# 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](#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](https://nodejs.org/) 20.19+ (Next.js 16 / React 19) - Un compte [Supabase](https://supabase.com/) (offre gratuite suffisante) - Un fournisseur SMTP gratuit (ex. [Resend](https://resend.com/), 100 emails/jour) — le service email intégré de Supabase a un rate limit trop bas pour un usage réel - Un compte [Vercel](https://vercel.com/) pour le déploiement (optionnel en local) --- ## Installation ### 1. Cloner et installer ```bash git clone ssh://git@git.alxczl.fr:7878/valentin/tribunal-app.git cd tribunal-app npm install ``` ### 2. Créer le projet Supabase 1. Créez un nouveau projet sur [supabase.com](https://supabase.com/). 2. Dans **SQL Editor**, exécutez tout le contenu de [`supabase/schema.sql`](supabase/schema.sql) (tables `profiles`/`points_log`/`days`/`events`/`settings`, policies RLS, triggers, RPC, bucket `avatars`, Realtime). Le script est idempotent, vous pouvez le ré-exécuter sans risque après une mise à jour. 3. Récupérez l'URL du projet et la clé `anon` dans **Project Settings → API**. 4. Dans **Authentication → Providers → Email**, **activez "Confirm email"** (un vrai email par personne, condition d'unicité des comptes). 5. 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](#email-transactionnel) ci-dessous. 6. Une fois votre propre compte créé et confirmé via `/signup`, désignez-vous Archonte (voir [Rôles & administration](#rôles--administration)). ### 3. Variables d'environnement ```bash cp .env.local.example .env.local ``` ```env NEXT_PUBLIC_SUPABASE_URL=https://xxxxxxxx.supabase.co NEXT_PUBLIC_SUPABASE_ANON_KEY=votre_cle_anon ``` > ⚠️ Ne mettez **jamais** la clé `service_role` dans le front ni dans le dépôt. ### 4. Lancer en local ```bash npm run dev ``` L'application tourne sur [http://localhost:3000](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) 1. Importez le dépôt dans Vercel. 2. Renseignez les variables d'environnement (`NEXT_PUBLIC_SUPABASE_URL`, `NEXT_PUBLIC_SUPABASE_ANON_KEY`). 3. 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](#email-transactionnel)). 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 : ```sql select id, pseudo from public.profiles; update public.profiles set role = 'judge' where id = ''; ``` 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](https://resend.com/) (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 RPC `award_points`) ou **Annuler**. Le client n'écrit jamais directement la colonne `points` : 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 `tribunal` reç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 `profiles` et `points_log`. La RLS étant au niveau ligne, les colonnes sensibles (`role`, `points`, `pseudo_locked`, pseudo figé) sont verrouillées par un **trigger `BEFORE UPDATE`** qui 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 colonne `points` à la main. - `points_log` est en lecture seule pour les clients ; seule la RPC peut y écrire. - La page `/admin` vé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ôle `judge` par 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.