mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 19:19:19 +02:00
Redesign UI to match target design images
- Flat purple-gradient nav bar with POSTERG/RÉPERTOIRE/À PROPOS links - Full-width search bar with icon, bottom-border only, below nav - Home: white bg, media card grid (thumbnail + author/title label below) - Répertoire: 4-column index (Années/Catégories/Étudiantes/Mots-clés) - TFE: 2-column layout (large text left, media right) - À Propos: 2-column, large monospace text, new apropos.php page - Admin: dark theme (#1a1a1a), purple gradient nav, bottom-border inputs - New shared partials: templates/nav.php, templates/search-bar.php - Rewrote all CSS: common, main, search, tfe, apropos, admin
This commit is contained in:
@@ -1,254 +1,196 @@
|
||||
<?php
|
||||
// Bootstrap application
|
||||
require_once __DIR__ . "/../../config/bootstrap.php";
|
||||
require_once __DIR__ . '/../../src/AdminAuth.php';
|
||||
|
||||
// PHP-level auth guard (defence-in-depth behind nginx Basic Auth)
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
// Generate CSRF token
|
||||
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();
|
||||
|
||||
// Get filter parameters
|
||||
$searchQuery = isset($_GET['search']) ? trim($_GET['search']) : '';
|
||||
$yearFilter = isset($_GET['year']) ? intval($_GET['year']) : null;
|
||||
$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;
|
||||
}
|
||||
if ($searchQuery) $filters['search'] = $searchQuery;
|
||||
if ($yearFilter) $filters['year'] = $yearFilter;
|
||||
if ($orientationFilter) $filters['orientation'] = $orientationFilter;
|
||||
|
||||
$theses = $db->getThesesList($filters);
|
||||
$years = $db->getAllYears();
|
||||
$theses = $db->getThesesList($filters);
|
||||
$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(source) {
|
||||
const checkboxes = document.querySelectorAll('input[name="selected_theses[]"]');
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.checked = source.checked;
|
||||
});
|
||||
updateBulkActionsVisibility();
|
||||
}
|
||||
|
||||
function updateBulkActionsVisibility() {
|
||||
const checkboxes = document.querySelectorAll('input[name="selected_theses[]"]:checked');
|
||||
const bulkActions = document.getElementById('bulk-actions');
|
||||
const selectedCount = document.getElementById('selected-count');
|
||||
|
||||
if (checkboxes.length > 0) {
|
||||
bulkActions.style.display = 'flex';
|
||||
selectedCount.textContent = checkboxes.length;
|
||||
} else {
|
||||
bulkActions.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function bulkAction(action) {
|
||||
const checkboxes = document.querySelectorAll('input[name="selected_theses[]"]:checked');
|
||||
if (checkboxes.length === 0) {
|
||||
alert('Veuillez sélectionner au moins un TFE.');
|
||||
return false;
|
||||
}
|
||||
|
||||
const actionText = action === 'publish' ? 'publier' : 'dépublier';
|
||||
if (!confirm(`Voulez-vous vraiment ${actionText} ${checkboxes.length} TFE(s) ?`)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set action
|
||||
document.getElementById('bulk-action-input').value = action;
|
||||
|
||||
// Copy selected thesis IDs to hidden form
|
||||
const bulkCheckboxesContainer = document.getElementById('bulk-checkboxes');
|
||||
bulkCheckboxesContainer.innerHTML = '';
|
||||
checkboxes.forEach(checkbox => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'selected_theses[]';
|
||||
input.value = checkbox.value;
|
||||
bulkCheckboxesContainer.appendChild(input);
|
||||
});
|
||||
|
||||
// Submit the form
|
||||
document.getElementById('bulk-form').submit();
|
||||
return false;
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Add change listeners to all checkboxes
|
||||
document.querySelectorAll('input[name="selected_theses[]"]').forEach(checkbox => {
|
||||
checkbox.addEventListener('change', updateBulkActionsVisibility);
|
||||
});
|
||||
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>
|
||||
<main class="admin-main">
|
||||
<h1 class="admin-page-title">Liste des TFE</h1>
|
||||
|
||||
<?php if (isset($_SESSION['error'])): ?>
|
||||
<div class="alert-error">
|
||||
<strong>⚠️ Erreur:</strong> <?php echo htmlspecialchars($_SESSION['error']);
|
||||
unset($_SESSION['error']); ?>
|
||||
</div>
|
||||
<div class="admin-alert admin-alert--error">⚠ <?= htmlspecialchars($_SESSION['error']); unset($_SESSION['error']); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_SESSION['success'])): ?>
|
||||
<div class="alert-success">
|
||||
<strong>✓ <?php echo htmlspecialchars($_SESSION['success']);
|
||||
unset($_SESSION['success']); ?></strong>
|
||||
</div>
|
||||
<div class="admin-alert admin-alert--success">✓ <?= htmlspecialchars($_SESSION['success']); unset($_SESSION['success']); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div id="bulk-actions" class="bulk-actions">
|
||||
<!-- Stats -->
|
||||
<div class="admin-stats">
|
||||
<div class="admin-stat">
|
||||
<div class="admin-stat__number"><?= count($theses) ?></div>
|
||||
<div class="admin-stat__label">TFE total</div>
|
||||
</div>
|
||||
<div class="admin-stat">
|
||||
<div class="admin-stat__number"><?= count(array_filter($theses, fn($t) => $t['is_published'])) ?></div>
|
||||
<div class="admin-stat__label">Publiés</div>
|
||||
</div>
|
||||
<div class="admin-stat">
|
||||
<div class="admin-stat__number"><?= count(array_filter($theses, fn($t) => !$t['is_published'])) ?></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="bulk-actions-buttons">
|
||||
<button type="button" class="btn-bulk-publish" onclick="bulkAction('publish')">Publier la sélection</button>
|
||||
<button type="button" class="btn-bulk-unpublish" onclick="bulkAction('unpublish')">Dépublier la sélection</button>
|
||||
<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="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
|
||||
<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>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number"><?php echo count($theses); ?></div>
|
||||
<div class="stat-label">TFE total</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number"><?php echo count(array_filter($theses, fn($t) => $t['is_published'])); ?></div>
|
||||
<div class="stat-label">Publiés</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number"><?php echo count(array_filter($theses, fn($t) => !$t['is_published'])); ?></div>
|
||||
<div class="stat-label">En attente</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filters">
|
||||
<form method="get" action="/admin/">
|
||||
<fieldset>
|
||||
<label for="search">Rechercher</label>
|
||||
<input type="text" id="search" name="search" placeholder="Titre, auteur..." value="<?php echo htmlspecialchars($searchQuery); ?>">
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label for="year">Année</label>
|
||||
<select id="year" name="year">
|
||||
<option value="">Toutes</option>
|
||||
<?php foreach ($years as $year): ?>
|
||||
<option value="<?php echo $year; ?>" <?php echo $yearFilter == $year ? 'selected' : ''; ?>>
|
||||
<?php echo $year; ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label for="orientation">Orientation</label>
|
||||
<select id="orientation" name="orientation">
|
||||
<option value="">Toutes</option>
|
||||
<?php foreach ($orientations as $orientation): ?>
|
||||
<option value="<?php echo $orientation['id']; ?>" <?php echo $orientationFilter == $orientation['id'] ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($orientation['name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</fieldset>
|
||||
|
||||
<button type="submit">Filtrer</button>
|
||||
<?php if ($searchQuery || $yearFilter || $orientationFilter): ?>
|
||||
<a href="/admin/">Réinitialiser</a>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Table -->
|
||||
<?php if (empty($theses)): ?>
|
||||
<p>Aucun TFE trouvé.</p>
|
||||
<p style="color:var(--admin-text-muted);padding:1rem 0;">Aucun TFE trouvé.</p>
|
||||
<?php else: ?>
|
||||
<table class="thesis-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><input type="checkbox" class="select-all-checkbox" onchange="toggleAll(this)" title="Tout sélectionner"></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" class="select-checkbox" name="selected_theses[]" value="<?php echo $thesis['id']; ?>"></td>
|
||||
<td><?php echo htmlspecialchars($thesis['identifier'] ?? $thesis['id']); ?></td>
|
||||
<td>
|
||||
<div class="thesis-title"><?php echo htmlspecialchars($thesis['title']); ?></div>
|
||||
<?php if ($thesis['subtitle']): ?>
|
||||
<div class="thesis-subtitle"><?php echo htmlspecialchars($thesis['subtitle']); ?></div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo htmlspecialchars($thesis['authors'] ?? 'N/A'); ?></td>
|
||||
<td><?php echo $thesis['year']; ?></td>
|
||||
<td><?php echo htmlspecialchars($thesis['orientation'] ?? 'N/A'); ?></td>
|
||||
<td><?php echo htmlspecialchars($thesis['ap_program'] ?? 'N/A'); ?></td>
|
||||
<td>
|
||||
<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; ?>
|
||||
</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']): ?>
|
||||
<span class="status-badge status-published">Publié</span>
|
||||
<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: ?>
|
||||
<span class="status-badge status-pending">En attente</span>
|
||||
<input type="hidden" name="action" value="publish">
|
||||
<button type="submit" class="admin-btn-sm admin-btn-publish">Publier</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<div class="actions">
|
||||
<a href="/admin/thanks.php?id=<?php echo $thesis['id']; ?>" class="btn btn-view">Voir</a>
|
||||
<a href="/admin/edit.php?id=<?php echo $thesis['id']; ?>" class="btn btn-edit">Éditer</a>
|
||||
<form method="post" action="actions/publish.php" class="publish-form">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
|
||||
<input type="hidden" name="thesis_id" value="<?php echo $thesis['id']; ?>">
|
||||
<?php if ($thesis['is_published']): ?>
|
||||
<input type="hidden" name="action" value="unpublish">
|
||||
<button type="submit" class="btn btn-unpublish" onclick="return confirm('Retirer ce TFE de la publication ?');">Dépublier</button>
|
||||
<?php else: ?>
|
||||
<input type="hidden" name="action" value="publish">
|
||||
<button type="submit" class="btn btn-publish">Publier</button>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</main>
|
||||
|
||||
<?php require_once APP_ROOT . '/templates/admin/footer.php'; ?>
|
||||
<?php require_once APP_ROOT . '/templates/admin/footer.php'; ?>
|
||||
|
||||
Reference in New Issue
Block a user