pagination curseur, layout 3 colonnes article, sidebar fixe

This commit is contained in:
Cedric Abonnel
2026-05-12 00:42:51 +02:00
parent d774042be9
commit be09fad48f
91 changed files with 8152 additions and 816 deletions
+394
View File
@@ -0,0 +1,394 @@
<?php
ob_start();
$now = time();
function adminStatusBadge(array $a, int $now): string
{
if (!$a['published']) {
return '<span class="badge bg-secondary">Brouillon</span>';
}
if (strtotime((string)($a['published_at'] ?? '')) > $now) {
return '<span class="badge bg-warning text-dark">Avant-première</span>';
}
return '<span class="badge bg-success">Publié</span>';
}
?>
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">Administration</h1>
<a href="/?action=create" class="btn btn-primary btn-sm">+ Nouvel article</a>
</div>
<!-- Onglets -->
<ul class="nav nav-tabs mb-4">
<?php if (isAdmin()): ?>
<li class="nav-item">
<a class="nav-link <?= $tab === 'dashboard' ? 'active' : '' ?>"
href="/?action=admin&tab=dashboard">Tableau de bord</a>
</li>
<?php endif; ?>
<li class="nav-item">
<a class="nav-link <?= $tab === 'articles' ? 'active' : '' ?>"
href="/?action=admin&tab=articles"><?= isAdmin() ? 'Articles' : 'Mes articles' ?></a>
</li>
<?php if (isAdmin()): ?>
<li class="nav-item">
<a class="nav-link <?= $tab === 'users' ? 'active' : '' ?>"
href="/?action=admin&tab=users">Utilisateurs</a>
</li>
<li class="nav-item">
<a class="nav-link <?= $tab === 'roles' ? 'active' : '' ?>"
href="/?action=admin&tab=roles">Rôles</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/?action=categories">Catégories</a>
</li>
<?php endif; ?>
</ul>
<!-- ─────────────────────────── DASHBOARD ─────────────────────────── -->
<?php if ($tab === 'dashboard' && isAdmin()): ?>
<div class="row g-3 mb-4">
<?php
$stats = [
['label' => 'Publiés', 'value' => $adminData['published'], 'color' => 'success'],
['label' => 'Avant-premières', 'value' => $adminData['previews'], 'color' => 'warning'],
['label' => 'Brouillons', 'value' => $adminData['drafts'], 'color' => 'secondary'],
['label' => 'Total', 'value' => $adminData['total'], 'color' => 'primary'],
];
foreach ($stats as $s): ?>
<div class="col-6 col-md-3">
<div class="card text-center">
<div class="card-body">
<div class="display-6 fw-bold text-<?= $s['color'] ?>"><?= $s['value'] ?></div>
<div class="text-muted small"><?= $s['label'] ?></div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<h5>Activité récente</h5>
<table class="table table-sm table-hover">
<thead>
<tr>
<th>Titre</th>
<th>Auteur</th>
<th>Statut</th>
<th>Modifié le</th>
</tr>
</thead>
<tbody>
<?php foreach ($adminData['recent'] as $a): ?>
<tr>
<td>
<a href="/post/<?= htmlspecialchars($a['slug'] ?? '') ?>">
<?= htmlspecialchars($a['title']) ?>
</a>
</td>
<td class="text-muted small"><?= htmlspecialchars($a['author'] ?? '') ?></td>
<td><?= adminStatusBadge($a, $now) ?></td>
<td class="text-muted small">
<?= htmlspecialchars(date('d/m/Y H:i', strtotime((string)($a['updated_at'] ?? $a['created_at'] ?? '')))) ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<!-- ─────────────────────────── ARTICLES ─────────────────────────── -->
<?php elseif ($tab === 'articles'): ?>
<?php if (empty($adminData['articles'])): ?>
<p class="text-muted">Aucun article.</p>
<?php else: ?>
<table class="table table-sm table-hover align-middle">
<thead>
<tr>
<th>Titre</th>
<?php if (isAdmin()): ?><th>Auteur</th><?php endif; ?>
<th>Catégorie</th>
<th>Statut</th>
<th>Date</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($adminData['articles'] as $a): ?>
<tr>
<td>
<a href="/post/<?= htmlspecialchars($a['slug'] ?? '') ?>">
<?= htmlspecialchars($a['title']) ?>
</a>
</td>
<?php if (isAdmin()): ?>
<td class="text-muted small"><?= htmlspecialchars($a['author'] ?? '') ?></td>
<?php endif; ?>
<td class="text-muted small"><?= htmlspecialchars($a['category'] ?? '') ?></td>
<td><?= adminStatusBadge($a, $now) ?></td>
<td class="text-muted small text-nowrap">
<?= htmlspecialchars(date('d/m/Y', strtotime((string)($a['published_at'] ?? $a['created_at'] ?? '')))) ?>
</td>
<td class="text-end text-nowrap">
<a href="/?action=edit&uuid=<?= htmlspecialchars($a['uuid']) ?>"
class="btn btn-outline-secondary btn-sm">Modifier</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<!-- ─────────────────────────── UTILISATEURS ─────────────────────────── -->
<?php elseif ($tab === 'users' && isAdmin()): ?>
<!-- Ajouter / attribuer un rôle -->
<div class="card mb-4">
<div class="card-header">Attribuer un rôle</div>
<div class="card-body">
<form method="post" action="/?action=admin_grant_role" class="row g-2 align-items-end">
<div class="col-md-5">
<label class="form-label small">Email</label>
<input type="email" name="email" class="form-control form-control-sm"
placeholder="utilisateur@exemple.fr" required>
</div>
<div class="col-md-4">
<label class="form-label small">Rôle</label>
<select name="role" class="form-select form-select-sm" required>
<?php foreach ($adminData['roles'] as $r): ?>
<option value="<?= htmlspecialchars($r['name']) ?>">
<?= htmlspecialchars($r['label']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3">
<button type="submit" class="btn btn-primary btn-sm w-100">Attribuer</button>
</div>
</form>
</div>
</div>
<!-- Liste des utilisateurs -->
<?php if (empty($adminData['users'])): ?>
<p class="text-muted">Aucun utilisateur.</p>
<?php else: ?>
<table class="table table-sm table-hover align-middle">
<thead>
<tr>
<th>Email</th>
<th>Statut</th>
<th>Rôles</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($adminData['users'] as $u): ?>
<tr>
<td class="small"><?= htmlspecialchars($u['email']) ?></td>
<td>
<?php if ($u['is_active'] === null): ?>
<span class="badge bg-light text-muted">Pré-inscrit</span>
<?php elseif ($u['is_active']): ?>
<span class="badge bg-success">Actif</span>
<?php else: ?>
<span class="badge bg-danger">Inactif</span>
<?php endif; ?>
</td>
<td>
<?php foreach ($u['roles'] as $roleName): ?>
<span class="badge bg-primary me-1"><?= htmlspecialchars($roleName) ?></span>
<form method="post" action="/?action=admin_revoke_role"
class="d-inline"
data-confirm="Retirer le rôle <?= htmlspecialchars($roleName) ?> à <?= htmlspecialchars($u['email']) ?> ?">
<input type="hidden" name="email" value="<?= htmlspecialchars($u['email']) ?>">
<input type="hidden" name="role" value="<?= htmlspecialchars($roleName) ?>">
<button type="submit" class="btn btn-link btn-sm p-0 text-danger" title="Retirer">×</button>
</form>
<?php endforeach; ?>
<?php if (empty($u['roles'])): ?>
<span class="text-muted small"></span>
<?php endif; ?>
</td>
<td class="text-end">
<!-- Ajout rapide d'un rôle existant -->
<?php
$currentRoles = $u['roles'];
$missing = array_filter($adminData['roles'], fn ($r) => !in_array($r['name'], $currentRoles, true));
?>
<?php if ($missing): ?>
<form method="post" action="/?action=admin_grant_role" class="d-inline-flex gap-1">
<input type="hidden" name="email" value="<?= htmlspecialchars($u['email']) ?>">
<select name="role" class="form-select form-select-sm" style="width:auto">
<?php foreach ($missing as $r): ?>
<option value="<?= htmlspecialchars($r['name']) ?>">
<?= htmlspecialchars($r['label']) ?>
</option>
<?php endforeach; ?>
</select>
<button type="submit" class="btn btn-outline-primary btn-sm">+</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<!-- ─────────────────────────── RÔLES ─────────────────────────── -->
<?php elseif ($tab === 'roles' && isAdmin()): ?>
<div class="row g-4">
<!-- Liste des rôles existants -->
<div class="col-lg-8">
<h5 class="mb-3">Rôles existants</h5>
<?php if (empty($adminData['roles'])): ?>
<p class="text-muted">Aucun rôle défini.</p>
<?php else: ?>
<table class="table table-sm table-hover align-middle">
<thead>
<tr>
<th style="width:160px">Nom technique</th>
<th>Label affiché</th>
<th class="text-center" style="width:90px">Utilisateurs</th>
<th style="width:100px"></th>
</tr>
</thead>
<tbody>
<?php foreach ($adminData['roles'] as $r): ?>
<tr>
<td><code class="text-body"><?= htmlspecialchars($r['name']) ?></code></td>
<td>
<form method="post" action="/?action=admin_update_role"
class="d-flex gap-2 align-items-center">
<input type="hidden" name="id" value="<?= (int)$r['id'] ?>">
<input type="text" name="label"
value="<?= htmlspecialchars($r['label']) ?>"
class="form-control form-control-sm" required>
<button type="submit" class="btn btn-outline-secondary btn-sm text-nowrap">
Sauver
</button>
</form>
</td>
<td class="text-center">
<span class="badge bg-secondary"><?= (int)$r['user_count'] ?></span>
</td>
<td class="text-end">
<form method="post" action="/?action=admin_delete_role"
data-confirm="Supprimer le rôle «<?= htmlspecialchars($r['name']) ?>» ?<?= (int)$r['user_count'] > 0 ? ' ' . (int)$r['user_count'] . ' utilisateur(s) perdront ce rôle.' : '' ?>">
<input type="hidden" name="id" value="<?= (int)$r['id'] ?>">
<button type="submit" class="btn btn-outline-danger btn-sm">
Supprimer
</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<!-- Créer un rôle -->
<div class="col-lg-4">
<div class="card">
<div class="card-header">Nouveau rôle</div>
<div class="card-body">
<form method="post" action="/?action=admin_create_role">
<div class="mb-3">
<label class="form-label small fw-semibold">Nom technique</label>
<input type="text" name="name" class="form-control form-control-sm"
placeholder="ex : moderator"
pattern="[a-z0-9_-]+"
title="Lettres minuscules, chiffres, tirets et underscores uniquement"
required>
<div class="form-text">Utilisé dans le code — ne change pas.</div>
</div>
<div class="mb-3">
<label class="form-label small fw-semibold">Label affiché</label>
<input type="text" name="label" class="form-control form-control-sm"
placeholder="ex : Modérateur" required>
</div>
<button type="submit" class="btn btn-primary btn-sm w-100">Créer</button>
</form>
</div>
</div>
</div>
<!-- Permissions par rôle -->
<?php if (!empty($adminData['roles'])): ?>
<div class="col-12 mt-2">
<h5 class="mb-3">Permissions par rôle</h5>
<p class="text-muted small mb-3">Le rôle <code>admin</code> a toutes les permissions implicitement.</p>
<div class="row g-3">
<?php foreach ($adminData['roles'] as $r):
if ($r['name'] === 'admin') {
continue;
} ?>
<div class="col-md-6 col-lg-4">
<div class="card h-100">
<div class="card-header py-2 d-flex align-items-center justify-content-between">
<span class="fw-semibold"><?= htmlspecialchars($r['label']) ?></span>
<code class="text-muted small"><?= htmlspecialchars($r['name']) ?></code>
</div>
<div class="card-body py-3">
<form method="post" action="/?action=admin_update_role_caps">
<input type="hidden" name="role_id" value="<?= (int)$r['id'] ?>">
<?php foreach (CAPABILITY_GROUPS as $group): ?>
<?php if (isset($group['single'])): ?>
<?php $cap = $group['single']; ?>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox"
name="caps[]" value="<?= htmlspecialchars($cap) ?>"
id="cap_<?= (int)$r['id'] ?>_<?= htmlspecialchars($cap) ?>"
<?= in_array($cap, $r['capabilities'], true) ? 'checked' : '' ?>>
<label class="form-check-label small fw-semibold"
for="cap_<?= (int)$r['id'] ?>_<?= htmlspecialchars($cap) ?>">
<?= htmlspecialchars($group['label']) ?>
</label>
</div>
<?php else: ?>
<div class="mb-3">
<div class="small fw-semibold mb-1"><?= htmlspecialchars($group['label']) ?></div>
<div class="d-flex gap-3 ps-1">
<?php foreach (['own' => 'Propres articles', 'all' => 'Tous'] as $scope => $scopeLabel):
$cap = $group[$scope]; ?>
<div class="form-check">
<input class="form-check-input" type="checkbox"
name="caps[]" value="<?= htmlspecialchars($cap) ?>"
id="cap_<?= (int)$r['id'] ?>_<?= htmlspecialchars($cap) ?>"
<?= in_array($cap, $r['capabilities'], true) ? 'checked' : '' ?>>
<label class="form-check-label small"
for="cap_<?= (int)$r['id'] ?>_<?= htmlspecialchars($cap) ?>">
<?= $scopeLabel ?>
</label>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<?php endforeach; ?>
<button type="submit" class="btn btn-outline-secondary btn-sm mt-1 w-100">
Enregistrer
</button>
</form>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<?php
$content = ob_get_clean();
$title = 'Administration — varlog';
include __DIR__ . '/layout.php';