Gestion des rencontres passées + interdiction des actions destructrices aux modérateurs

Rencontres passées (section « Ils y étaient » de l'accueil) :
- Nouvel onglet « Rencontres passées » (admin + modérateur) : création,
  édition, jusqu'à 3 photos par rencontre (1 grande + 2 petites, upload
  après enregistrement — même schéma que la photo des rencontres à venir)
- Nouvelles colonnes rencontres_passees.image_path/_2/_3 (migration
  ALTER TABLE IF NOT EXISTS)
- Home.jsx affiche désormais les vraies photos uploadées (repli sur le
  motif placeholder si absentes) ; ordre passé de ORDER BY id à
  ORDER BY id DESC (les plus récentes en premier, seed réordonné en
  conséquence pour préserver l'affichage actuel)
- Suppression réservée aux administrateurs

Modérateurs : plus aucune action destructrice possible, imposé côté
serveur (adminOnly) et masqué côté interface :
- Validation/suspension d'un membre (toggle-valide)
- Suppression d'une rencontre, d'une rencontre passée, d'une inscription

Vérifié : 34 + 21 + 26 tests existants toujours au vert, 21 nouveaux
tests (CRUD rencontres passées, upload des 3 emplacements photo, ordre
d'affichage, restrictions modérateur sur chaque action désormais admin-
only), parcours navigateur complet (upload des 3 photos, rendu réel sur
la page d'accueil).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
This commit is contained in:
Claude committed 2026-07-13 14:58:32 +00:00
1 parent fab64bc38a
commit 214250bc39
11 files changed
+426 -55

No files matched your search

+14
View File
@@ -66,6 +66,20 @@ export const inscriptionAdminSchema = z.object({
statut: z.enum(['confirmee', 'en_attente']),
});
export const rencontrePasseeSchema = z.object({
date_label: trimmed(60, 1),
lieu: trimmed(200, 1),
titre: trimmed(200, 1),
texte: trimmed(2000, 1),
participants: z.coerce.number().int().min(0).max(100000),
nb_photos: z.coerce.number().int().min(0).max(100000),
});
export const pastEventImageParam = z.object({
id: z.coerce.number().int().positive(),
slot: z.coerce.number().int().min(1).max(3),
});
export const categorySchema = z.object({ name: trimmed(80, 1) });
export const staffRoleEnum = z.enum(['admin', 'moderator']);