Files
xamxam/app/templates/admin/index.php

281 lines
16 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script>
function toggleAll(src){document.querySelectorAll('input[name="selected_theses[]"]').forEach(cb=>cb.checked=src.checked);updateBulk();}
function updateBulk(){const n=document.querySelectorAll('input[name="selected_theses[]"]:checked').length;const b=document.getElementById('bulk-actions');document.getElementById('selected-count').textContent=n;b.style.display=n>0?'flex':'none';}
function confirmBulk(act){const n=document.querySelectorAll('input[name="selected_theses[]"]:checked').length;if(!n){document.getElementById('no-selection-dialog').showModal();return;}document.getElementById('bulk-action-input').value=act;if(act==='delete'){document.getElementById('bulk-delete-count').textContent=n;document.getElementById('bulk-delete-dialog').showModal();}else{document.getElementById('bulk-confirm-word').textContent=act==='publish'?'Publier':'Dépublier';document.getElementById('bulk-confirm-count').textContent=n;document.getElementById('bulk-confirm-dialog').showModal();}}
function execBulk(){const a=document.getElementById('bulk-action-input').value;const f=document.getElementById('bulk-form');f.action = a==='delete' ? 'actions/delete.php' : 'actions/publish.php';const c=document.getElementById('bulk-checkboxes');c.innerHTML='';document.querySelectorAll('input[name="selected_theses[]"]:checked').forEach(cb=>{const inp=document.createElement('input');inp.type='hidden';inp.name='selected_theses[]';inp.value=cb.value;c.appendChild(inp);});f.submit();}
function confirmDelete(id,title){document.getElementById('delete-thesis-title').textContent=title;document.getElementById('delete-thesis-dialog').showModal();document.getElementById('delete-dialog-confirm').onclick=function(){document.getElementById('delete-form-'+id).submit();};}
document.addEventListener('DOMContentLoaded',()=>{document.querySelectorAll('input[name="selected_theses[]"]').forEach(cb=>cb.addEventListener('change',updateBulk));});
document.addEventListener('htmx:afterSwap',()=>{document.querySelectorAll('input[name="selected_theses[]"]').forEach(cb=>cb.addEventListener('change',updateBulk));updateBulk();});
</script>
<main id="main-content" class="admin-main--list">
<!-- Title + filters + stats + import all in one toolbar row -->
<div class="admin-list-toolbar admin-list-toolbar--list">
<div class="admin-toolbar-top">
<div class="admin-toolbar-title-row">
<h1>Liste des TFE</h1>
<div class="admin-stats">
<fieldset class="admin-stat">
<legend class="admin-stat__label">Total</legend>
<span class="admin-stat__number"><?= $stats['total'] ?></span>
</fieldset>
<fieldset class="admin-stat admin-stat--pub">
<legend class="admin-stat__label">Publiés</legend>
<span class="admin-stat__number"><?= $stats['published'] ?></span>
</fieldset>
<fieldset class="admin-stat admin-stat--pend">
<legend class="admin-stat__label">Attente</legend>
<span class="admin-stat__number"><?= $stats['pending'] ?></span>
</fieldset>
</div>
</div>
<div class="admin-btn-group">
<a href="/admin/add.php" class="btn btn--primary btn--sm">+ Ajouter un TFE</a>
<a href="/admin/tags.php" class="btn btn--primary btn--sm">Mots-clés</a>
<?php if ($trashCount > 0): ?>
<a href="/admin/index.php?tab=trash" class="btn btn--sm <?= $tab === 'trash' ? 'btn--primary' : 'btn--secondary' ?>">
Corbeille (<?= $trashCount ?>)
</a>
<?php endif; ?>
<button type="button" class="btn btn--primary btn--sm" id="import-dialog-btn"
onclick="document.getElementById('import-dialog').showModal()">
Importer
</button>
<button type="button" class="btn btn--primary btn--sm" id="export-dialog-btn"
onclick="document.getElementById('export-dialog').showModal()">
Exporter
</button>
</div>
</div>
<form id="admin-filter-form" class="admin-filters" method="get" action="/admin/"
hx-get="/admin/"
hx-trigger="change from:select, input changed delay:300ms from:input[name=search], keyup[key=='Enter'] from:input[name=search]"
hx-target="#admin-table-wrap"
hx-swap="innerHTML"
hx-indicator="#admin-search-indicator"
hx-include="#admin-filter-form, #admin-table-wrap input[name=sort], #admin-table-wrap input[name=dir]"
hx-push-url="true"
hx-sync="#admin-filter-form:replace">
<input type="text" name="search" placeholder="Titre, auteur..."
value="<?= htmlspecialchars($searchQuery) ?>">
<select name="year">
<option value="">Année</option>
<?php foreach ($years as $y): ?>
<option value="<?= $y ?>" <?= $yearFilter == $y ? 'selected' : '' ?>><?= $y ?></option>
<?php endforeach; ?>
</select>
<select name="orientation">
<option value="">Orientation</option>
<?php foreach ($orientations as $o): ?>
<option value="<?= $o['id'] ?>" <?= $orientationFilter == $o['id'] ? 'selected' : '' ?>>
<?= htmlspecialchars($o['name']) ?>
</option>
<?php endforeach; ?>
</select>
<select name="ap">
<option value="">AP</option>
<?php foreach ($apPrograms as $ap): ?>
<option value="<?= $ap['id'] ?>" <?= $apFilter == $ap['id'] ? 'selected' : '' ?>>
<?= htmlspecialchars($ap['code'] ?? $ap['name']) ?>
</option>
<?php endforeach; ?>
</select>
<?php if ($searchQuery || $yearFilter || $orientationFilter || $apFilter): ?>
<a href="/admin/" class="btn btn--secondary btn--sm admin-filters-reset">&#x2715; Réinitialiser</a>
<?php endif; ?>
</form>
<!-- Loading indicator bar -->
<div id="admin-search-indicator" class="admin-search-indicator"></div>
</div>
<?php include APP_ROOT . '/templates/admin/index-table.php'; ?>
</main>
<!-- ══════════════════════════════════════════════════════════════
CONFIRM DIALOGS (replacing browser alert/confirm)
══════════════════════════════════════════════════════════════ -->
<!-- No-selection alert -->
<dialog id="no-selection-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="no-sel-title">
<div class="admin-dialog__header">
<h2 id="no-sel-title">Aucune sélection</h2>
<button type="button" class="admin-dialog__close" aria-label="Fermer"
onclick="this.closest('dialog').close()">&#x2715;</button>
</div>
<div class="admin-dialog__alert">
<p>Sélectionnez au moins un TFE avant d'effectuer une action groupée.</p>
</div>
<div class="admin-dialog__footer">
<button type="button" class="btn btn--primary" onclick="this.closest('dialog').close()">OK</button>
</div>
</dialog>
<!-- Bulk publish/unpublish confirm -->
<dialog id="bulk-confirm-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="bulk-confirm-title">
<div class="admin-dialog__header">
<h2 id="bulk-confirm-title">Confirmation</h2>
<button type="button" class="admin-dialog__close" aria-label="Fermer"
onclick="this.closest('dialog').close()">&#x2715;</button>
</div>
<div class="admin-dialog__alert">
<p><span id="bulk-confirm-word"></span> <span id="bulk-confirm-count"></span> TFE(s) ?</p>
</div>
<div class="admin-dialog__footer">
<button type="button" class="btn btn--primary" onclick="this.closest('dialog').close(); execBulk()">Confirmer</button>
<button type="button" class="btn btn--secondary" onclick="this.closest('dialog').close()">Annuler</button>
</div>
</dialog>
<!-- Bulk delete confirm -->
<dialog id="bulk-delete-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="bulk-delete-title">
<div class="admin-dialog__header">
<h2 id="bulk-delete-title">Supprimer des TFE</h2>
<button type="button" class="admin-dialog__close" aria-label="Fermer"
onclick="this.closest('dialog').close()">&#x2715;</button>
</div>
<div class="admin-dialog__alert">
<p>Supprimer définitivement <strong><span id="bulk-delete-count"></span> TFE(s)</strong> ? Cette action est irréversible.</p>
</div>
<div class="admin-dialog__footer">
<button type="button" class="btn btn--danger" onclick="this.closest('dialog').close(); execBulk()">Supprimer</button>
<button type="button" class="btn btn--secondary" onclick="this.closest('dialog').close()">Annuler</button>
</div>
</dialog>
<!-- Single thesis delete confirm -->
<dialog id="delete-thesis-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="delete-thesis-title-label">
<div class="admin-dialog__header">
<h2 id="delete-thesis-title-label">Supprimer ce TFE</h2>
<button type="button" class="admin-dialog__close" aria-label="Fermer"
onclick="this.closest('dialog').close()">&#x2715;</button>
</div>
<div class="admin-dialog__alert">
<p>Supprimer « <strong id="delete-thesis-title"></strong> » ? Cette action est irréversible.</p>
</div>
<div class="admin-dialog__footer">
<button type="button" class="btn btn--danger" id="delete-dialog-confirm" onclick="this.closest('dialog').close()">Supprimer</button>
<button type="button" class="btn btn--secondary" onclick="this.closest('dialog').close()">Annuler</button>
</div>
</dialog>
<!-- ══════════════════════════════════════════════════════════════
IMPORT DIALOG
══════════════════════════════════════════════════════════════ -->
<dialog id="import-dialog" class="admin-dialog" aria-labelledby="import-dialog-title">
<div class="admin-dialog__header">
<h2 id="import-dialog-title">Importer une liste de TFE</h2>
<button type="button" class="admin-dialog__close" aria-label="Fermer"
onclick="document.getElementById('import-dialog').close()">&#x2715;</button>
</div>
<?php if ($importMessage || !empty($importErrors)): ?>
<div class="admin-import-status-card">
<?php if (!empty($importErrors)): ?>
<div class="toast admin-import-status-card__errors" role="alert" data-type="error">
<strong>⚠ Erreurs :</strong>
<ul class="admin-error-list">
<?php foreach ($importErrors as $err): ?>
<li><?= htmlspecialchars($err) ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<?php if ($importMessage): ?>
<p class="admin-import-status-card__success" role="status">✓ <?= htmlspecialchars($importMessage) ?></p>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ($importMessage): ?>
<?php if (!empty($importResults)): ?>
<details class="admin-import-log-details">
<summary>Logs d'importation (<?= count($importResults) ?> entrées)</summary>
<ul class="admin-import-log">
<?php foreach ($importResults as $r): ?>
<li class="admin-import-log__item admin-import-log__item--<?= $r['type'] ?>"><?= htmlspecialchars($r['msg']) ?></li>
<?php endforeach; ?>
</ul>
</details>
<?php endif; ?>
<div class="admin-form-footer">
<button type="button" class="btn btn--primary"
onclick="document.getElementById('import-dialog').close(); window.location.href = window.location.pathname">Terminé</button>
</div>
<?php else: ?>
<form method="post" enctype="multipart/form-data" class="admin-form">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
<div>
<label for="csv_file">Fichier CSV</label>
<div class="admin-file-input">
<input type="file" id="csv_file" name="csv_file" accept=".csv" required>
<small class="admin-file-hint">
Colonnes : Identifiant, Titre, Sous-titre, Auteur·ice(s), Contact, Promoteur·ice(s), Format, Année, AP, Orientation, Finalité, Mots-clés, Synopsis, Contexte, Remarques, Langue, Autorisation, License, taille, Points sur 20, lien BAIU<br>
Quatre premières lignes ignorées — Séparateur : virgule — UTF-8
</small>
</div>
</div>
<div class="admin-form-footer">
<button type="submit" class="btn btn--primary">Importer</button>
<button type="button" class="btn btn--secondary"
onclick="document.getElementById('import-dialog').close()">Annuler</button>
</div>
</form>
<?php if (!empty($importResults)): ?>
<details class="admin-import-log-details">
<summary>Logs d'importation (<?= count($importResults) ?> entrées)</summary>
<ul class="admin-import-log">
<?php foreach ($importResults as $r): ?>
<li class="admin-import-log__item admin-import-log__item--<?= $r['type'] ?>"><?= htmlspecialchars($r['msg']) ?></li>
<?php endforeach; ?>
</ul>
</details>
<?php endif; ?>
<?php endif; ?>
</dialog>
<!-- ══════════════════════════════════════════════════════════════
EXPORT DIALOG
══════════════════════════════════════════════════════════════ -->
<dialog id="export-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="export-dialog-title">
<div class="admin-dialog__header">
<h2 id="export-dialog-title">Exporter</h2>
<button type="button" class="admin-dialog__close" aria-label="Fermer"
onclick="document.getElementById('export-dialog').close()">&#x2715;</button>
</div>
<form method="get" action="/admin/actions/export.php" class="admin-form">
<div class="admin-form-group">
<label class="admin-checkbox-label">
<input type="checkbox" name="csv" value="1" checked>
CSV (tableau des métadonnées)
</label>
</div>
<div class="admin-form-group">
<label class="admin-checkbox-label">
<input type="checkbox" name="files" value="1" checked>
Fichiers (archive ZIP)
</label>
</div>
<div class="admin-form-group">
<label class="admin-checkbox-label">
<input type="checkbox" name="db" value="1">
Base de données (SQLite)
</label>
</div>
<div class="admin-form-footer">
<button type="submit" class="btn btn--primary">Exporter</button>
<button type="button" class="btn btn--secondary"
onclick="document.getElementById('export-dialog').close()">Annuler</button>
</div>
</form>
</dialog>
<?php if ($importMessage || !empty($importErrors)): ?>
<script>document.getElementById('import-dialog').showModal();</script>
<?php endif; ?>