5cb19caec2
Boutons de validation des points : - Les icônes ✓/✕ étaient trop grandes (40px) et dupliquées entre le podium et le reste du classement. Factorisées dans un composant partagé PointsConfirmControls (32px, icône 16px, olive/oxblood, aria-label + tooltip), utilisé à l'identique par JudgePointControls sur le podium ET dans la liste. Nouvelle page /calendrier — "Le Calendrier des Dieux" : - Modèle de données : days (date, dieu, domaine), events (titre, type, heure, lieu, created_by forcé par trigger), settings (ligne unique, date du Tribunal). RLS : lecture ouverte à tout authentifié, écriture (insert/update/delete) réservée au rôle judge par policies dédiées — jamais un simple masquage des boutons côté client. - 8 préréglages de divinités (Dionysos, Arès, Athéna, Aphrodite, Hermès, Poséidon, Hadès, Zeus) avec domaine, couleur d'accent et emblème SVG ; un Archonte peut aussi saisir une divinité libre. - Bannière avec compte à rebours avant la date du Tribunal, éditable par les Archontes. Journées en cartes (jour courant mis en évidence, jours passés estompés, jour du Tribunal en accent oxblood), liste d'événements avec icône par type (activité/défi/épreuve/tribunal). Mode édition (ajout/modification/suppression avec confirmation) réservé aux Archontes. Realtime sur les trois tables. - Ajouté au menu déroulant du header, route protégée par le proxy. Rendu des icônes dynamiques (GodEmblem, EventTypeIcon) via branchement JSX explicite plutôt que variable de composant résolue à l'exécution, pour respecter react-hooks/static-components. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
243 lines
14 KiB
Markdown
243 lines
14 KiB
Markdown
# 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 = '<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](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.
|