mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 19:19:19 +02:00
admin/index.php showed "TFE total / Publiés / En attente" by running array_filter() over the already-filtered $theses array returned by getThesesList(). When any search or year filter was active the three numbers reflected only the matching subset, making the stats misleading (e.g. searching for a single student would show "1 total, 0 publiés"). Add Database::getThesesStats(): array — a single SQL aggregation query: SELECT COUNT(*), SUM(is_published), SUM(NOT is_published) FROM theses This runs against the raw theses table with no filters, so the counters always display the true whole-database figures regardless of what filter the admin has active. admin/index.php now calls getThesesStats() and reads $stats['total'], $stats['published'], $stats['pending'] instead of the array_filter expressions.
226 lines
11 KiB
PHP
226 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 require_once APP_ROOT . '/templates/admin/head.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 class="admin-main" id="main-content">
|
|
<h1 class="admin-page-title">Liste des TFE</h1>
|
|
|
|
<?php if (isset($_SESSION['error'])): ?>
|
|
<div class="admin-alert admin-alert--error">⚠ <?= htmlspecialchars($_SESSION['error']); unset($_SESSION['error']); ?></div>
|
|
<?php endif; ?>
|
|
<?php if (isset($_SESSION['success'])): ?>
|
|
<div class="admin-alert admin-alert--success">✓ <?= htmlspecialchars($_SESSION['success']); unset($_SESSION['success']); ?></div>
|
|
<?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) -->
|
|
<div class="admin-stats">
|
|
<div class="admin-stat">
|
|
<div class="admin-stat__number"><?= $stats['total'] ?></div>
|
|
<div class="admin-stat__label">TFE total</div>
|
|
</div>
|
|
<div class="admin-stat">
|
|
<div class="admin-stat__number"><?= $stats['published'] ?></div>
|
|
<div class="admin-stat__label">Publiés</div>
|
|
</div>
|
|
<div class="admin-stat">
|
|
<div class="admin-stat__number"><?= $stats['pending'] ?></div>
|
|
<div class="admin-stat__label">En attente</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 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">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 class="admin-table">
|
|
<thead>
|
|
<tr>
|
|
<th><input type="checkbox" onchange="toggleAll(this)"></th>
|
|
<th>ID</th>
|
|
<th>Titre</th>
|
|
<th>Auteur(s)</th>
|
|
<th>Année</th>
|
|
<th>Orientation</th>
|
|
<th>AP</th>
|
|
<th>Statut</th>
|
|
<th>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 if ($thesis['is_published']): ?>
|
|
<span class="status-badge status-published">Publié</span>
|
|
<?php else: ?>
|
|
<span class="status-badge status-pending">En attente</span>
|
|
<?php endif; ?>
|
|
<?php if (!empty($thesis['access_type'])): ?>
|
|
<br><span class="status-badge status-access status-access--<?= strtolower(preg_replace('/[^a-z]/i', '', $thesis['access_type'])) ?>">
|
|
<?= htmlspecialchars($thesis['access_type']) ?>
|
|
</span>
|
|
<?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'; ?>
|