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. Habillée sur le thème d'un tribunal de la Grèce antique (voir Direction artistique).
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 Archonte), 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 Archontes (juges) attribuent des points (+/- avec montant personnalisé et motif, confirmation explicite avant envoi) et gèrent les Citoyens depuis Le Conseil des Archontes - 📣 Le crieur : journal public et en direct de toutes les attributions de points
- 🎡 La Roulette : tirage au sort animé (avatars) parmi tous les membres ou les Citoyens uniquement
- 🗓️ Le Calendrier des Dieux : agenda de la semaine sous le patronage du Panthéon, date du Tribunal avec compte à rebours, édition réservée aux Archontes
- 📱 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 (tokens Grèce antique, voir globals.css) |
| Typographie | Cinzel (titres), Cormorant Garamond (citations), Manrope (interface) — next/font/google |
| 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/days/events/settings, 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 Archonte (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 des décrets en direct
│ │ ├─ roulette/ # « La Roulette » : tirage au sort animé
│ │ ├─ calendrier/ # « Le Calendrier des Dieux » : agenda, panthéon, date du Tribunal
│ │ ├─ admin/ # Le Conseil des Archontes : gestion des membres, reset du repère
│ │ ├─ profile/ # Édition pseudo (verrou) / photo, déconnexion
│ │ ├─ layout.tsx # Layout + header global
│ │ ├─ icon.svg # Favicon (couronne de laurier)
│ │ └─ page.tsx # Redirection selon l'état de connexion
│ ├─ components/ # Header (logo + chip utilisateur + menu), Avatar, AvatarPicker,
│ │ │ # Podium, JudgePointControls, PointsConfirmControls, icons,
│ │ │ # LaurelWreath, DiamondDivider
│ ├─ lib/
│ │ ├─ ranking.ts # Classement avec égalités + calcul des flèches
│ │ ├─ pantheon.ts # Préréglages des dieux + icônes par type d'événement
│ │ ├─ 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 Archonte pour le changer »). Un Archonte peut toujours modifier le pseudo de quiconque et, s'il le souhaite, redéverrouiller un compte depuis Le Conseil des Archontes.
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 Archonte peut promouvoir/rétrograder d'autres membres et éditer leur pseudo depuis Le Conseil des Archontes (visible uniquement par les Archontes, 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 L'Agora, un Archonte 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 Archonte fige un nouveau repère via Nouveau round dans Le Conseil des Archontes.
Le Calendrier des Dieux
Agenda de la semaine (/calendrier) : chaque journée est placée sous le patronage d'une divinité (nom, domaine, emblème SVG, couleur d'accent — 8 préréglages fournis dans lib/pantheon.ts : Dionysos, Arès, Athéna, Aphrodite, Hermès, Poséidon, Hadès, Zeus ; un Archonte peut en saisir d'autres librement). Chaque journée liste ses événements (activité/défi/épreuve/tribunal, heure, lieu, description).
- Lecture : tous les authentifiés.
- Écriture (créer/modifier/supprimer une journée ou un événement, définir la date du Tribunal) : réservée aux Archontes, imposée par policies RLS sur
days/events/settings— jamais par un simple masquage des boutons côté client. - Bannière : compte à rebours avant la date du Tribunal (
settings.tribunal_date), éditable par les Archontes. - Le jour courant est mis en valeur, les jours passés sont estompés, le jour contenant un événement de type
tribunalreçoit un accent oxblood distinct. - Realtime activé sur les trois tables : toute modification est visible immédiatement par tous.
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 Archonte 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. days/events/settings(Calendrier) : lecture ouverte à tous les authentifiés, écriture (insert/update/delete) réservée au rôlejudgepar policies RLS dédiées — un Citoyen qui appellerait l'API directement se ferait rejeter, pas seulement masquer les boutons.
Direction artistique
Thème fixe : un tribunal égéen de nuit — marbre, or, mer sombre, sceaux de cire. Tokens couleur (globals.css) :
| Token | Valeur | Usage |
|---|---|---|
--ink / --ink-2 |
#0A1B33 / #0F2748 |
Fond de l'app (mer de nuit), header, boutons primaires |
--marble / --marble-2 |
#F4ECD8 / #E9DEC2 |
Surfaces claires (cartes, formulaires) |
--gold / --gold-bright |
#C9A227 / #E7C560 |
Accents, bordures, 1ʳᵉ place |
--oxblood |
#A5342A |
Blâmes (points négatifs), sceaux, erreurs |
--olive |
#5E6B3B |
Honneurs (points positifs), succès |
--sea |
#2E6E7E |
Accent secondaire |
--silver / --bronze |
#C7CDD6 / #B08D57 |
Accents 2ᵉ/3ᵉ place du podium |
--text-marble / --text-mut |
#2A2116 / #7A6A4C |
Texte sur surfaces claires (jamais de doré sur marbre pour du texte courant — contraste insuffisant) |
Ornements en SVG/CSS inline (pas d'images bitmap) : couronne de laurier (components/laurel-wreath.tsx, calculée par trigonométrie), frise à méandre (.meander-divider), séparateur losange (components/diamond-divider.tsx), texture marbre (.marble-surface).
Vocabulaire (toujours accompagné du terme fonctionnel) : juges → Archontes, membres → Citoyens, leaderboard → L'Agora, journal → Le Crieur, points positifs/négatifs → Honneurs/Blâmes, section admin → Le Conseil des Archontes.
Ce thème est codé en dur (pas de système de re-thématisation par édition) — changer l'apparence pour une prochaine édition demande d'éditer directement les tokens et le vocabulaire.
Roadmap
- Éditions — table
editions(année, mood board) pour rattacher les points à une édition donnée - Le jeu du Tribunal — le Char, batailles de cul sec, ostracisme, timer du Gardien (la Roulette est livrée)
- Mode grand écran — vue leaderboard optimisée vidéoprojecteur
Licence
Projet personnel et privé, destiné à un usage entre amis.