Chaque rencontre dispose d'un bouton « ✉ Email » dans l'administration
(admins et modérateurs) qui génère un email HTML prêt à envoyer, aux
couleurs du site avec le logo du club : titre, description, encadré
date/heure/lieu/places restantes et bouton d'inscription.
- Nouvelle route GET /api/admin/rencontres/:id/email : construit l'email
(mise en page en tables + styles inline, compatible clients mail) avec
des URL absolues dérivées de l'hôte de la requête.
- Le bouton de l'email pointe vers /?inscription=<id> : la page d'accueil
ouvre alors automatiquement la modale d'inscription de la rencontre.
- Modale d'aperçu dans l'admin : copie de l'email (HTML enrichi presse-
papiers pour coller directement dans un logiciel de messagerie), copie
de l'objet et du lien d'inscription, téléchargement du fichier .html.
- Les images publiques (logo, uploads) sont maintenant servies avec
Cross-Origin-Resource-Policy: cross-origin pour que le logo se charge
dans les webmails et l'aperçu.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
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
- Nouveau rôle users.role='moderator' (contrainte CHECK migrée via
DROP+ADD CONSTRAINT, idempotent) : accès à Membres, Rencontres,
Inscriptions uniquement. Dashboard, Catégories (écriture), Contenu du
site, Demandes d'adhésion et gestion des comptes restent admin-only,
imposé côté serveur (requireAuth accepte désormais un tableau de rôles)
- Nouvel onglet « Administrateurs » (admin-only) : créer un compte admin
ou modérateur (mot de passe temporaire généré, même mécanique que pour
les membres — visible tant que non changé, changement forcé à la
première connexion), réinitialiser l'accès, promouvoir/rétrograder,
supprimer. Garde-fous : impossible de se supprimer ou de se rétrograder
soi-même, impossible de supprimer le dernier administrateur
- users.full_name (colonne migrée) pour l'affichage des comptes staff
- AccessCell/CredentialsModal extraits dans AccessControls.jsx, partagés
entre la gestion des membres et celle des comptes admin/modérateur
- AdminShell filtre ses onglets par rôle ; Espace.jsx route admin et
modérateur vers le back-office
Corrigé en cours de route : GET /api/admin/categories doit rester lisible
par les modérateurs (nécessaire au formulaire membre) même si sa gestion
en écriture reste admin-only — sans quoi le Promise.all du frontend
échouait silencieusement et vidait la liste des membres.
Vérifié : 34 + 21 tests existants toujours au vert, 26 nouveaux tests de
permissions par rôle, parcours navigateur complet (création modérateur,
connexion, changement forcé, menu restreint, accès aux membres).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
- Nouveau membre créé avec email : un mot de passe temporaire aléatoire
(~69 bits, sans caractères ambigus) est généré et haché ; il est retourné
à l'admin et reste lisible en clair (users.temp_password) tant que le
membre ne l'a pas changé
- Colonnes users.temp_password / must_change_password (migration
ALTER TABLE IF NOT EXISTS, appliquée au démarrage par le bootstrap)
- POST /api/admin/members/:id/reset-access : régénère l'accès (création
s'il n'existait pas, ou réinitialisation si le membre a perdu son mot
de passe) ; login/me exposent mustChangePassword ; change-password
efface systématiquement le mot de passe temporaire et lève le blocage
- Frontend : colonne « Accès » dans la liste des membres (mot de passe
visible + copier + réinitialiser, ou « Défini », ou « Créer l'accès »),
modal de confirmation après création/réinitialisation, écran de
changement de mot de passe obligatoire avant tout accès au portail ou
au back-office
Vérifié : cycle complet (création → mot de passe visible côté admin →
connexion → changement forcé → effacement automatique → réinitialisation
admin en cas de perte) via 21 tests e2e dédiés + parcours navigateur ;
34 tests existants toujours au vert.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o