Files
xamxam/public/admin/index.php
Pontoporeia 77bfd2f8e3 Extract status-badge.php partial; replace inline badge markup in index.php and account.php
Add templates/partials/status-badge.php — a single reusable partial that
renders the <span class="status-badge …"> element for three badge types:

  'publish'  — Publié / En attente derived from a boolean is_published value
  'access'   — access-type label (Libre / Interne / Interdit) with slug-based
               CSS modifier class and appropriate symbol (○ ◑ ●)
  'ok'       — generic green/yellow boolean badge with caller-supplied labels
               (used for 'Active'/'Non configurée' and 'Présent'/'Absent' in
               account.php)

All three variants emit aria-label with a context prefix and wrap the
decorative symbol in aria-hidden="true" — behaviour identical to the
inline code they replace.

Callers set $badgeType + $badgeValue (+ optional $badgeOkLabel /
$badgeWarnLabel / $badgeContext) before the include; the partial unsets
all working variables after rendering so they do not bleed into the
including scope.

Files changed:
  templates/partials/status-badge.php  — new partial
  public/admin/index.php               — table status column now uses partial
                                         (removes 15 lines of inline if/else/php)
  public/admin/account.php             — two credential status rows now use partial
                                         (removes 8 lines of inline if/else)
2026-04-02 12:50:46 +02:00

221 lines
11 KiB
PHP

<?php
require_once __DIR__ . "/../../config/bootstrap.php";
require_once __DIR__ . '/../../src/AdminAuth.php';
AdminAuth::requireLogin();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$pageTitle = "Liste des TFE";
require_once __DIR__ . '/../../src/Database.php';
try {
$db = new Database();
$searchQuery = isset($_GET['search']) ? trim($_GET['search']) : '';
$yearFilter = isset($_GET['year']) ? intval($_GET['year']) : null;
$orientationFilter = isset($_GET['orientation']) ? intval($_GET['orientation']) : null;
$filters = [];
if ($searchQuery) $filters['search'] = $searchQuery;
if ($yearFilter) $filters['year'] = $yearFilter;
if ($orientationFilter) $filters['orientation'] = $orientationFilter;
$theses = $db->getThesesList($filters);
$stats = $db->getThesesStats();
$years = $db->getAllYears();
$orientations = $db->getAllOrientations();
} catch (Exception $e) {
error_log("Error loading theses list: " . $e->getMessage());
die("Erreur lors du chargement de la liste.");
}
?>
<?php $isAdmin = true; $bodyClass = 'admin-body'; require_once APP_ROOT . '/templates/head.php'; ?>
<?php include APP_ROOT . '/templates/header.php'; ?>
<script>
function toggleAll(src) {
document.querySelectorAll('input[name="selected_theses[]"]').forEach(cb => cb.checked = src.checked);
updateBulk();
}
function updateBulk() {
const checked = document.querySelectorAll('input[name="selected_theses[]"]:checked');
const bulk = document.getElementById('bulk-actions');
document.getElementById('selected-count').textContent = checked.length;
bulk.style.display = checked.length > 0 ? 'flex' : 'none';
}
function bulkAction(action) {
const checked = document.querySelectorAll('input[name="selected_theses[]"]:checked');
if (!checked.length) { alert('Sélectionnez au moins un TFE.'); return; }
const word = action === 'publish' ? 'publier' : 'dépublier';
if (!confirm(`${word.charAt(0).toUpperCase()+word.slice(1)} ${checked.length} TFE(s) ?`)) return;
document.getElementById('bulk-action-input').value = action;
const container = document.getElementById('bulk-checkboxes');
container.innerHTML = '';
checked.forEach(cb => {
const inp = document.createElement('input');
inp.type = 'hidden'; inp.name = 'selected_theses[]'; inp.value = cb.value;
container.appendChild(inp);
});
document.getElementById('bulk-form').submit();
}
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('input[name="selected_theses[]"]').forEach(cb => cb.addEventListener('change', updateBulk));
});
</script>
<main id="main-content">
<h1>Liste des TFE</h1>
<?php if (isset($_SESSION['error'])): ?>
<p role="alert" data-type="error">⚠ <?= htmlspecialchars($_SESSION['error']); unset($_SESSION['error']); ?></p>
<?php endif; ?>
<?php if (isset($_SESSION['success'])): ?>
<p role="status" data-type="success">✓ <?= htmlspecialchars($_SESSION['success']); unset($_SESSION['success']); ?></p>
<?php endif; ?>
<!-- Maintenance mode toggle -->
<?php $maintenanceOn = file_exists(APP_ROOT . '/storage/maintenance.flag'); ?>
<div class="admin-maintenance-bar <?= $maintenanceOn ? 'admin-maintenance-bar--active' : '' ?>">
<?php if ($maintenanceOn): ?>
<span>⚠ Mode maintenance <strong>activé</strong> — le site public est inaccessible.</span>
<form method="post" action="actions/maintenance.php" style="display:inline;">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
<input type="hidden" name="action" value="disable_maintenance">
<button type="submit" class="admin-btn admin-btn--sm">Désactiver la maintenance</button>
</form>
<?php else: ?>
<span>Site public : <strong>en ligne</strong></span>
<form method="post" action="actions/maintenance.php" style="display:inline;">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
<input type="hidden" name="action" value="enable_maintenance">
<button type="submit" class="admin-btn admin-btn--sm admin-btn--warning"
onclick="return confirm('Mettre le site en maintenance ? Les visiteurs verront une page 503.')">
Activer la maintenance
</button>
</form>
<?php endif; ?>
</div>
<!-- Stats (always reflects full DB, independent of active filters) -->
<dl class="admin-stats">
<div class="admin-stat">
<dt class="admin-stat__label">TFE total</dt>
<dd class="admin-stat__number"><?= $stats['total'] ?></dd>
</div>
<div class="admin-stat">
<dt class="admin-stat__label">Publiés</dt>
<dd class="admin-stat__number"><?= $stats['published'] ?></dd>
</div>
<div class="admin-stat">
<dt class="admin-stat__label">En attente</dt>
<dd class="admin-stat__number"><?= $stats['pending'] ?></dd>
</div>
</dl>
<!-- Filters -->
<form class="admin-filters" method="get" action="/admin/">
<input type="text" name="search" placeholder="Titre, auteur..."
value="<?= htmlspecialchars($searchQuery) ?>">
<select name="year">
<option value="">Toutes les années</option>
<?php foreach ($years as $y): ?>
<option value="<?= $y ?>" <?= $yearFilter == $y ? 'selected' : '' ?>><?= $y ?></option>
<?php endforeach; ?>
</select>
<select name="orientation">
<option value="">Toutes les orientations</option>
<?php foreach ($orientations as $o): ?>
<option value="<?= $o['id'] ?>" <?= $orientationFilter == $o['id'] ? 'selected' : '' ?>>
<?= htmlspecialchars($o['name']) ?>
</option>
<?php endforeach; ?>
</select>
<button type="submit" class="admin-filters-btn">Filtrer</button>
<?php if ($searchQuery || $yearFilter || $orientationFilter): ?>
<a href="/admin/" class="admin-filters-reset"><span aria-hidden="true">✕ </span>Réinitialiser</a>
<?php endif; ?>
</form>
<!-- Bulk actions bar -->
<div id="bulk-actions" class="admin-bulk-actions">
<strong><span id="selected-count">0</span> TFE(s) sélectionné(s)</strong>
<div class="admin-bulk-btns">
<button type="button" class="admin-btn-sm admin-btn-publish" onclick="bulkAction('publish')">Publier</button>
<button type="button" class="admin-btn-sm admin-btn-unpublish" onclick="bulkAction('unpublish')">Dépublier</button>
</div>
</div>
<form id="bulk-form" method="post" action="actions/publish.php">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
<input type="hidden" id="bulk-action-input" name="action" value="">
<input type="hidden" name="bulk" value="1">
<div id="bulk-checkboxes"></div>
</form>
<!-- Table -->
<?php if (empty($theses)): ?>
<p style="color:var(--admin-text-muted);padding:1rem 0;">Aucun TFE trouvé.</p>
<?php else: ?>
<table>
<thead>
<tr>
<th scope="col"><input type="checkbox" onchange="toggleAll(this)"></th>
<th scope="col">ID</th>
<th scope="col">Titre</th>
<th scope="col">Auteur(s)</th>
<th scope="col">Année</th>
<th scope="col">Orientation</th>
<th scope="col">AP</th>
<th scope="col">Statut</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($theses as $thesis): ?>
<tr>
<td><input type="checkbox" name="selected_theses[]" value="<?= $thesis['id'] ?>"></td>
<td style="color:var(--admin-text-muted);font-size:.8rem;"><?= htmlspecialchars($thesis['identifier'] ?? $thesis['id']) ?></td>
<td>
<div class="thesis-title"><?= htmlspecialchars($thesis['title']) ?></div>
<?php if ($thesis['subtitle']): ?>
<div class="thesis-subtitle"><?= htmlspecialchars($thesis['subtitle']) ?></div>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($thesis['authors'] ?? 'N/A') ?></td>
<td><?= $thesis['year'] ?></td>
<td><?= htmlspecialchars($thesis['orientation'] ?? 'N/A') ?></td>
<td><?= htmlspecialchars($thesis['ap_program'] ?? 'N/A') ?></td>
<td>
<?php $badgeType = 'publish'; $badgeValue = $thesis['is_published']; include APP_ROOT . '/templates/partials/status-badge.php'; ?>
<?php if (!empty($thesis['access_type'])): ?>
<br><?php $badgeType = 'access'; $badgeValue = $thesis['access_type']; include APP_ROOT . '/templates/partials/status-badge.php'; ?>
<?php endif; ?>
</td>
<td>
<div class="admin-actions">
<a href="/admin/thanks.php?id=<?= $thesis['id'] ?>" class="admin-btn-sm admin-btn-view">Voir</a>
<a href="/admin/edit.php?id=<?= $thesis['id'] ?>" class="admin-btn-sm admin-btn-edit">Éditer</a>
<form method="post" action="actions/publish.php" class="publish-form">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
<input type="hidden" name="thesis_id" value="<?= $thesis['id'] ?>">
<?php if ($thesis['is_published']): ?>
<input type="hidden" name="action" value="unpublish">
<button type="submit" class="admin-btn-sm admin-btn-unpublish"
onclick="return confirm('Retirer de la publication ?')">Dépublier</button>
<?php else: ?>
<input type="hidden" name="action" value="publish">
<button type="submit" class="admin-btn-sm admin-btn-publish">Publier</button>
<?php endif; ?>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</main>
<?php require_once APP_ROOT . '/templates/admin/footer.php'; ?>