V2 — authentification et administration :
- Authentification par email + mot de passe (email confirmé, un compte
par email), abandon de l'email interne dérivé du pseudo.
- Rôles public/judge sur profiles, section Administration (juges
uniquement, vérifiée côté serveur) pour gérer les membres.
- Verrou de pseudo : modifiable une fois par son propriétaire puis figé,
contournable par un juge.
- RLS étendue par un trigger BEFORE UPDATE (enforce_profile_update) pour
verrouiller les colonnes sensibles (role, points, pseudo_locked, pseudo
figé) — la RLS seule ne peut pas exprimer une règle par colonne.
V3 — points, journal, podium, progression, photo :
- RPC award_points() (SECURITY DEFINER) : seul point d'écriture de la
colonne points, vérifie le rôle juge côté serveur, delta signé sans
plancher à 0, trace chaque opération dans points_log.
- Leaderboard temps réel (Supabase Realtime) avec podium top 3
(égalités gérées), flèches de progression (previous_rank), et
contrôles de points juges avec confirmation explicite (plus de
debounce auto) avant envoi.
- Page /journal ("le crieur") : fil live et public des attributions de
points.
- Recadrage photo carré + compression client (react-easy-crop + canvas)
au signup et sur le profil.
- Passe de polish visuel : cartes/boutons cohérents, lien actif dans la
nav, podium retravaillé.
schema.sql, README.md et CLAUDE.md mis à jour en conséquence (schéma
idempotent, instructions SMTP/rôles/migration, conventions RLS+trigger
documentées pour les futures colonnes sensibles).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Le Tribunal
Application web pour animer et scorer le jeu « Le Tribunal » entre amis pendant une semaine de vacances : comptes, rôles, leaderboard en direct, attribution de points par les juges et journal public des événements. Le socle est ré-thématisable par édition (un thème différent chaque année).
Fonctionnalités
- 🔐 Création de compte par email + mot de passe (email confirmé, un compte par email)
- 👤 Connexion / déconnexion, pseudo affiché partout (jamais l'email)
- 🖼️ Profil éditable : pseudo modifiable une seule fois puis figé (sauf par un juge), photo recadrée en rond, redimensionnée et compressée côté client
- 🏆 Leaderboard en direct (Supabase Realtime) : podium top 3 (or/argent/bronze, gestion des égalités), flèches ▲▼ de progression depuis le dernier « round »
- ⚖️ Rôles :
public/judge. Les juges attribuent des points (+/- avec montant personnalisé et motif, confirmation explicite avant envoi) et gèrent les membres depuis une section Administration - 📣 Le crieur : journal public et en direct de toutes les attributions de points
- 📱 Responsive, pensé mobile-first et propre en vidéoprojection
Stack technique
| Rôle | Techno |
|---|---|
| Framework | Next.js 16 (App Router) + TypeScript |
| Style | Tailwind CSS v4 |
| Auth · Base · Stockage · Temps réel | Supabase (Auth, Postgres, Storage, Realtime) |
| Recadrage photo | react-easy-crop + canvas (resize/compression client) |
| Email transactionnel | SMTP custom (ex. Resend) branché sur Supabase Auth |
| Déploiement | Vercel + Supabase (managé) |
Prérequis
- Node.js 20.19+ (Next.js 16 / React 19)
- Un compte Supabase (offre gratuite suffisante)
- Un fournisseur SMTP gratuit (ex. Resend, 100 emails/jour) — le service email intégré de Supabase a un rate limit trop bas pour un usage réel
- Un compte Vercel pour le déploiement (optionnel en local)
Installation
1. Cloner et installer
git clone ssh://git@git.alxczl.fr:7878/valentin/tribunal-app.git
cd tribunal-app
npm install
2. Créer le projet Supabase
- Créez un nouveau projet sur supabase.com.
- Dans SQL Editor, exécutez tout le contenu de
supabase/schema.sql(tablesprofiles/points_log, policies RLS, triggers, RPC, bucketavatars, Realtime). Le script est idempotent, vous pouvez le ré-exécuter sans risque après une mise à jour. - Récupérez l'URL du projet et la clé
anondans Project Settings → API. - Dans Authentication → Providers → Email, activez "Confirm email" (un vrai email par personne, condition d'unicité des comptes).
- Dans Authentication → Settings → SMTP Settings, branchez un fournisseur SMTP custom (ex. Resend) — le service email par défaut de Supabase est bridé à quelques emails/heure. Voir Email transactionnel ci-dessous.
- Une fois votre propre compte créé et confirmé via
/signup, désignez-vous juge (voir Rôles & administration).
3. Variables d'environnement
cp .env.local.example .env.local
NEXT_PUBLIC_SUPABASE_URL=https://xxxxxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=votre_cle_anon
⚠️ Ne mettez jamais la clé
service_roledans le front ni dans le dépôt.
4. Lancer en local
npm run dev
L'application tourne sur http://localhost:3000.
Scripts
| Commande | Description |
|---|---|
npm run dev |
Serveur de développement |
npm run build |
Build de production |
npm run start |
Lance le build de production |
npm run lint |
Vérification du code (ESLint) |
Déploiement (Vercel)
- Importez le dépôt dans Vercel.
- Renseignez les variables d'environnement (
NEXT_PUBLIC_SUPABASE_URL,NEXT_PUBLIC_SUPABASE_ANON_KEY). - Déployez. Vercel rebuild automatiquement à chaque push sur la branche principale.
Structure du projet
tribunal-app/
├─ src/
│ ├─ app/
│ │ ├─ login/page.tsx # Connexion (email + mot de passe)
│ │ ├─ signup/page.tsx # Création de compte + photo recadrée
│ │ ├─ leaderboard/
│ │ │ ├─ page.tsx # Fetch initial + rôle de l'utilisateur
│ │ │ └─ leaderboard-view.tsx # Realtime, podium, liste, contrôles juges
│ │ ├─ journal/ # « Le crieur » : fil d'événements en direct
│ │ ├─ admin/ # Gestion des membres + reset du repère (juges)
│ │ ├─ profile/ # Édition pseudo (verrou) / photo, déconnexion
│ │ ├─ layout.tsx # Layout + navigation
│ │ └─ page.tsx # Redirection selon l'état de connexion
│ ├─ components/ # Avatar, AvatarPicker (recadrage), NavBar, Podium,
│ │ │ # JudgePointControls
│ ├─ lib/
│ │ ├─ ranking.ts # Classement avec égalités + calcul des flèches
│ │ ├─ image.ts # Recadrage/compression canvas côté client
│ │ └─ supabase/ # Clients Supabase (browser / server / proxy)
│ └─ proxy.ts # Protection des routes + rafraîchissement session
├─ supabase/schema.sql # Schéma complet + policies RLS + triggers + RPC (versionné)
├─ .env.local.example
└─ README.md
Authentification
La connexion se fait par email + mot de passe ; Supabase impose l'unicité de l'email, ce qui empêche la création de comptes multiples. Le pseudo est purement un nom d'affichage (leaderboard, profil, journal) : l'email n'est jamais montré aux autres membres. La confirmation d'email est obligatoire (voir SMTP).
Le profil (public.profiles) est créé automatiquement à l'inscription via un trigger sur auth.users (fonctionne même si l'email n'est pas encore confirmé).
Verrou de pseudo
Chaque membre peut changer son pseudo une seule fois depuis /profile. Dès l'enregistrement, pseudo_locked passe à true et le champ devient en lecture seule (« pseudo figé — contacte un juge pour le changer »). Un juge peut toujours modifier le pseudo de quiconque et, s'il le souhaite, redéverrouiller un compte depuis Administration.
Rôles & administration
Deux rôles : public (défaut) et judge. Les 2 premiers juges sont désignés manuellement via le SQL Editor :
select id, pseudo from public.profiles;
update public.profiles set role = 'judge' where id = '<uuid-du-membre>';
Ensuite, un juge peut promouvoir/rétrograder d'autres membres et éditer leur pseudo depuis la page Administration (visible uniquement par les juges, et vérifié côté serveur — pas seulement caché en front).
Email transactionnel
Le service email intégré de Supabase (sans SMTP custom) limite l'envoi à quelques emails par heure — largement insuffisant dès qu'on teste à plusieurs. Configurez un SMTP custom dans Authentication → Settings → SMTP Settings, par exemple avec Resend (gratuit, 100 emails/jour) :
| Champ | Valeur |
|---|---|
| Sender email | onboarding@resend.dev (domaine de test Resend) ou une adresse d'un domaine que vous avez vérifié |
| Host | smtp.resend.com |
| Port | 465 |
| Username | resend (en minuscules) |
| Password | votre clé API Resend |
Avec le domaine de test
onboarding@resend.dev, Resend ne livre qu'à l'adresse email du compte Resend lui-même. Pour envoyer à tous vos amis, vérifiez votre propre domaine dans Resend.
Points, journal et classement
- Attribution de points : depuis le leaderboard, un juge clique
+/−(montant personnalisable, motif optionnel) — le delta reste en attente (affiché en couleur) jusqu'à un clic sur Confirmer (envoie la RPCaward_points) ou Annuler. Le client n'écrit jamais directement la colonnepoints: la RPC vérifie le rôle juge côté serveur, applique le delta (négatif autorisé, pas de plancher à 0) et journalise l'opération. - Le crieur (
/journal) : fil de tous les événements (« Untel a donné +5 à Untel — motif »), visible par tous les authentifiés, alimenté par Supabase Realtime. - Podium : top 3 mis en avant (or/argent/bronze), gère proprement les égalités (rangs partagés) et les groupes de moins de 3 membres.
- Flèches de progression : ▲/▼/= comparent le rang actuel au dernier repère. Un juge fige un nouveau repère via Nouveau round dans Administration.
Sécurité
- Mots de passe hachés et gérés par Supabase Auth (jamais stockés en clair).
- Row Level Security activée sur
profilesetpoints_log. La RLS étant au niveau ligne, les colonnes sensibles (role,points,pseudo_locked, pseudo figé) sont verrouillées par un triggerBEFORE UPDATEqui ne fait jamais confiance à ce qu'envoie le client. - Les points ne sont modifiables que via la RPC
award_points(SECURITY DEFINER), jamais par une écriture directe — même un juge ne peut pas modifier la colonnepointsà la main. points_logest en lecture seule pour les clients ; seule la RPC peut y écrire.- La page
/adminvérifie le rôle côté serveur (Server Component) avant de rendre quoi que ce soit — pas de simple masquage front.
Ré-thématisation
Les couleurs sont définies comme design tokens (variables CSS), afin de pouvoir changer l'habillage d'une édition à l'autre sans toucher aux composants. Palette par défaut : navy #0F2748, or #C9A227, ivoire #F4ECD8, encre #2A2116.
Roadmap
- Éditions & thèmes — année, nom du thème, mood board, vocabulaire configurable
- Le jeu du Tribunal — le Char, batailles de cul sec, ostracisme, roulette, timer du Gardien
- Mode grand écran — vue leaderboard optimisée vidéoprojecteur
Licence
Projet personnel et privé, destiné à un usage entre amis.