9f4e58019b
Remplace le thème générique (tokens navy/gold/ivory re-thématisables par édition) par une direction artistique fixe et codée en dur : tribunal égéen de nuit — marbre, or, mer sombre, sceaux de cire. Fondations (globals.css, layout.tsx) : - Polices next/font/google : Cinzel (titres, tracking large), Cormorant Garamond (citations italiques), Manrope (interface). - Nouveaux tokens couleur : ink/ink-2 (fond mer de nuit), marble/marble-2 (surfaces claires), gold/gold-bright (accents), oxblood (blâmes/erreurs), olive (honneurs/succès), sea, text-marble/text-mut (texte sur marbre — jamais de doré en texte courant, contraste insuffisant). - Classes utilitaires : .marble-surface (texture par dégradés, pas d'image de bruit), .meander-divider (frise à méandre grecque en SVG data-URI). Composants d'ornement (aucune image bitmap) : - components/laurel-wreath.tsx : couronne de laurier calculée par trigonométrie (deux arcs symétriques de feuilles), réutilisée en logo, favicon (icon.svg régénéré avec les mêmes coordonnées) et couronne du rang 1 du podium. - components/diamond-divider.tsx : séparateur ◆ avec filets dégradés. Header global (components/header.tsx, remplace nav-bar.tsx) : - Logo laurier + "LE TRIBUNAL" à gauche (cliquable vers le leaderboard). - Chip utilisateur à droite : avatar (bordure dorée), pseudo, badge de rôle (Archonte/Citoyen), points de gloire, menu déroulant (Mon profil, Le Conseil des Archontes si juge, Se déconnecter). - Onglets L'Agora / Le Crieur, frise à méandre en séparateur. - Responsive : nom du site et pseudo masqués sous le breakpoint sm, l'avatar reste toujours visible. Vocabulaire diégétique (toujours avec le terme fonctionnel à côté) : juges → Archontes, membres → Citoyens, leaderboard → L'Agora, journal → Le Crieur (décrets avec sceau de cire oxblood), points positifs/négatifs → Honneurs/Blâmes, points → gloires, section admin → Le Conseil des Archontes. Avatar : bordure dorée, couleur de fallback dérivée d'un hash du pseudo (au lieu d'une couleur fixe). Toutes les pages (login, signup, leaderboard/podium, journal, admin, profil) sont passées sur les nouveaux tokens/polices/vocabulaire. README.md et CLAUDE.md : section "Ré-thématisation" remplacée par "Direction artistique" documentant la palette, les polices et les ornements ; toute mention du système de re-thème par édition retirée. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
136 lines
10 KiB
Markdown
136 lines
10 KiB
Markdown
@AGENTS.md
|
|
|
|
# Le Tribunal
|
|
|
|
Application web pour un groupe d'amis (~12 personnes) qui anime un jeu et des olympiades pendant une semaine de vacances : comptes, rôles, leaderboard en direct, attribution de points par des juges, journal public des événements. Habillée sur le thème d'un tribunal de la Grèce antique (voir section 6).
|
|
|
|
## 1. État actuel
|
|
|
|
V1 à V4 sont livrées :
|
|
- **V1** : comptes, profils, leaderboard statique.
|
|
- **V2** : authentification par email réel (plus de pseudo+email interne), rôles `public`/`judge`, verrou de pseudo (modifiable une fois), section Admin.
|
|
- **V3** : attribution de points par les juges (RPC sécurisée, +/- avec confirmation explicite), journal public « le crieur », podium top 3, flèches de progression, recadrage/compression photo côté client.
|
|
- **V4** : direction artistique Grèce antique (marbre/or/mer de nuit, polices Cinzel/Cormorant Garamond/Manrope, vocabulaire Archontes/Citoyens/Agora/Crieur), header global avec chip utilisateur.
|
|
|
|
Hors périmètre pour l'instant (voir roadmap en fin de doc) : éditions/saisons, le jeu du Tribunal lui-même (Char, roulette, ostracisme, timer du Gardien), mode grand écran.
|
|
|
|
---
|
|
|
|
## 2. Stack technique
|
|
|
|
- **Framework** : Next.js 16 (App Router) + TypeScript
|
|
- **Style** : Tailwind CSS v4 (design tokens en variables CSS, voir `globals.css`)
|
|
- **Auth + Base + Stockage + Temps réel** : Supabase (Auth, Postgres, Storage, Realtime)
|
|
- **Recadrage photo** : `react-easy-crop` + canvas natif (resize/compression client, jamais d'upload de l'image brute)
|
|
- **Déploiement** : Vercel (front) + Supabase (managé) — pas encore déployé, tourne en local pour l'instant
|
|
|
|
Next.js 16 a renommé `middleware.ts` en `proxy.ts` (fichier `src/proxy.ts`, fonction `proxy`). `src/lib/supabase/middleware.ts` est un nom de fichier utilitaire interne, sans rapport avec cette convention.
|
|
|
|
---
|
|
|
|
## 3. Modèle de données (`supabase/schema.sql`, source de vérité)
|
|
|
|
Le script est **idempotent** : toujours le ré-exécuter en entier après une modification, jamais de migration incrémentale séparée.
|
|
|
|
### `public.profiles` (liée à `auth.users`, créée automatiquement par un trigger sur `auth.users`)
|
|
|
|
| Colonne | Notes |
|
|
|-----------------|-------------------------------------------------------------------|
|
|
| `id` | = `auth.users.id` |
|
|
| `pseudo` | affiché partout, unique (insensible à la casse) |
|
|
| `avatar_url` | URL publique Storage (`{user_id}.webp`) |
|
|
| `points` | jamais modifiable directement — uniquement via RPC `award_points` |
|
|
| `role` | `public` (défaut) ou `judge` |
|
|
| `pseudo_locked` | passe à `true` automatiquement au 1er changement de pseudo |
|
|
| `previous_rank` | repère de classement, mis à jour par `reset_rank_reference()` |
|
|
|
|
### `public.points_log` (journal, lecture seule côté client)
|
|
|
|
`id, target_id, judge_id, delta, reason, created_at` — écrit uniquement par la RPC `award_points`.
|
|
|
|
### Colonnes sensibles : RLS + trigger, jamais confiance au client
|
|
|
|
La RLS est **au niveau ligne** : elle ne peut pas exprimer « cette colonne seulement si tel rôle ». Le verrouillage fin des colonnes (`role`, `points`, `pseudo_locked`, pseudo figé, `previous_rank`) passe par le trigger `enforce_profile_update()` :
|
|
- `points` : bloqué pour **tout le monde**, y compris les juges, en écriture directe. Seule la RPC `award_points()` (SECURITY DEFINER) peut la modifier, via un flag de session (`app.bypass_points_lock`) qui autorise explicitement CETTE écriture précise.
|
|
- `role`, `previous_rank` : modifiables uniquement par un `judge`.
|
|
- `pseudo` : modifiable une fois par le propriétaire (auto-lock ensuite), ou à tout moment par un `judge`.
|
|
- `auth.uid() is null` (SQL Editor, migrations) bypass le trigger : ces accès sont déjà fiables par construction.
|
|
|
|
Ce pattern (RLS pour l'accès à la ligne + trigger `BEFORE UPDATE` pour l'accès aux colonnes) est la convention du projet — le reproduire pour toute nouvelle colonne sensible plutôt que d'inventer autre chose.
|
|
|
|
### RPC exposées
|
|
|
|
- `is_pseudo_taken(p_pseudo)` — anon + authenticated, ne renvoie qu'un booléen (l'anon ne peut pas lire `profiles`).
|
|
- `award_points(p_target_id, p_delta, p_reason)` — authenticated, vérifie `role = 'judge'` côté serveur, jamais côté client.
|
|
- `reset_rank_reference()` — authenticated, vérifie `role = 'judge'`, fige le classement courant dans `previous_rank`.
|
|
|
|
---
|
|
|
|
## 4. Authentification
|
|
|
|
Email + mot de passe (Supabase Auth impose l'unicité de l'email). Confirmation d'email **activée** — nécessite un SMTP custom configuré côté Supabase (le service intégré est trop limité en volume, voir README). Le pseudo est un simple nom d'affichage stocké dans `profiles`, jamais l'email n'est montré aux autres membres.
|
|
|
|
---
|
|
|
|
## 5. Pages & navigation
|
|
|
|
| Route | Contenu |
|
|
|----------------|---------------------------------------------------------------------------|
|
|
| `/` | Redirige vers `/leaderboard` si connecté, sinon `/login` |
|
|
| `/login` | Email + mot de passe |
|
|
| `/signup` | Email + mot de passe + pseudo + photo (recadrée) ; gère l'attente de confirmation email |
|
|
| `/leaderboard` | « L'Agora » — podium top 3 + liste, Realtime, contrôles de points pour les juges |
|
|
| `/journal` | « Le Crieur » — fil live des décrets (attributions de points), lecture pour tous |
|
|
| `/profile` | Pseudo (verrouillable), photo, déconnexion |
|
|
| `/admin` | « Le Conseil des Archontes » — juges uniquement, **vérifié côté serveur** (Server Component) — gestion des membres, reset du repère de classement |
|
|
|
|
`/leaderboard`, `/profile`, `/admin`, `/journal` sont protégées par `src/proxy.ts` (redirection `/login` si non connecté). L'accès juge-only de `/admin` n'est **pas** géré par le proxy (il n'a pas facilement le rôle) — c'est la page elle-même qui vérifie et redirige.
|
|
|
|
Le header global (`components/header.tsx`) est rendu dans `layout.tsx` pour tout utilisateur connecté : logo (couronne de laurier + « Le Tribunal ») à gauche, chip utilisateur (avatar, pseudo, rôle, points) avec menu déroulant à droite, onglets L'Agora/Le Crieur en dessous.
|
|
|
|
---
|
|
|
|
## 6. Look & feel — Grèce antique
|
|
|
|
Thème **fixe et codé en dur** (pas de système de re-thématisation par édition — voir roadmap). Un tribunal égéen de nuit : marbre, or, mer sombre, sceaux de cire.
|
|
|
|
Tokens couleur (`globals.css`, exposés comme utilities Tailwind via `@theme inline`) :
|
|
- `--ink #0A1B33` / `--ink-2 #0F2748` : fond de l'app (dégradés radiaux mer de nuit), header, boutons primaires.
|
|
- `--marble #F4ECD8` / `--marble-2 #E9DEC2` : surfaces claires (`.marble-surface`, classe utilitaire avec léger dégradé/grain).
|
|
- `--gold #C9A227` / `--gold-bright #E7C560` : accents, bordures, 1ʳᵉ place. **Jamais** pour du texte courant sur `marble` (contraste insuffisant) — utiliser `--text-marble` à la place.
|
|
- `--oxblood #A5342A` : blâmes (points négatifs), sceaux, erreurs.
|
|
- `--olive #5E6B3B` : honneurs (points positifs), succès.
|
|
- `--sea #2E6E7E` : accent secondaire.
|
|
- `--text-marble #2A2116` / `--text-mut #7A6A4C` : texte sur surfaces claires.
|
|
|
|
Polices (`next/font/google`, variables CSS, exposées comme `font-heading`/`font-serif`/`font-sans`) : **Cinzel** (titres, noms, chiffres de score — majuscules, `tracking-[0.1em]`), **Cormorant Garamond** (citations/sous-titres, italique), **Manrope** (interface, corps de texte).
|
|
|
|
Ornements en SVG/CSS inline (jamais d'image bitmap) :
|
|
- `components/laurel-wreath.tsx` — couronne de laurier calculée par trigonométrie (deux arcs symétriques de feuilles), utilisée en logo, sur le rang 1 du podium, etc. Le favicon (`src/app/icon.svg`) est une version statique des mêmes coordonnées.
|
|
- `.meander-divider` (classe globale) — frise à méandre grecque en `background-image` SVG data-URI, séparateur discret.
|
|
- `components/diamond-divider.tsx` — séparateur ◆ avec filets dégradés.
|
|
- `.marble-surface` (classe globale) — texture marbre via dégradés (pas d'image de bruit).
|
|
|
|
Convention de carte : `marble-surface rounded-2xl border border-gold/40 shadow-xl` (formulaires, podium) ou `rounded-lg border-gold/25` pour les lignes de liste. Bouton primaire : `bg-ink-2 text-gold-bright font-heading uppercase hover:bg-ink`. Bouton secondaire : `border border-gold/30 text-text-marble hover:bg-gold/10`.
|
|
|
|
Vocabulaire diégétique (toujours accompagné du terme fonctionnel dans le code/les commentaires, pour que ça reste maintenable) : juges/admins → **Archontes**, membres → **Citoyens**, leaderboard → **L'Agora**, journal des points → **Le Crieur** (décrets), points positifs/négatifs → **Honneurs**/**Blâmes**, points → **gloires**, section admin → **Le Conseil des Archontes**.
|
|
|
|
---
|
|
|
|
## 7. Conventions
|
|
|
|
- Toujours passer par les policies RLS + trigger ; ne jamais exposer la `service_role key` côté client.
|
|
- Toute nouvelle colonne sensible sur `profiles` (ou une future table) suit le pattern RLS+trigger de la section 3, pas un `grant`/`revoke` par colonne (qui ne peut pas distinguer les rôles applicatifs).
|
|
- Aperçus locaux d'image avant upload (crop, fichier choisi) = `blob:`/`data:` URL → toujours `unoptimized` sur `next/image` ou une balise `<img>` classique (`next/image` ne sait pas résoudre ces schémas côté serveur).
|
|
- Commandes : `npm run dev`, `npm run build`, `npm run lint`.
|
|
- Le schéma SQL (`supabase/schema.sql`) est la seule source de vérité pour la base — le tenir à jour à chaque changement de données/sécurité, et le garder idempotent.
|
|
|
|
---
|
|
|
|
## 8. Roadmap (pas encore codé)
|
|
|
|
- **Éditions** : table `editions` (année, mood board) ; les points deviendront rattachables à une édition donnée. Le thème visuel (Grèce antique) reste fixe, ce n'est pas un système de re-thématisation par édition.
|
|
- **Le jeu du Tribunal** : le Char (zone de pénalité), batailles de cul sec, ostracisme, roulette, timer du Gardien.
|
|
- **Mode grand écran** : vue leaderboard optimisée pour vidéoprojecteur.
|
|
- **Déploiement** : pas encore fait (Vercel + Supabase managé prévus, voir README).
|