V2 + V3 : rôles, points, journal, podium et recadrage photo
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>
This commit is contained in:
@@ -1,18 +1,18 @@
|
||||
# Le Tribunal
|
||||
|
||||
Application web pour animer et scorer le jeu **« Le Tribunal »** entre amis pendant une semaine de vacances : comptes, profils et leaderboard. Le socle est **ré-thématisable par édition** (un thème différent chaque année).
|
||||
|
||||
> **État actuel : V1** — le périmètre se limite à l'authentification, aux profils et au leaderboard. Le jeu lui-même (le bus, la roulette, l'ostracisme, etc.) arrivera dans des versions suivantes (voir la [roadmap](#roadmap)).
|
||||
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 (V1)
|
||||
## Fonctionnalités
|
||||
|
||||
- 🔐 **Création de compte** avec pseudo, mot de passe et photo de profil
|
||||
- 👤 **Connexion / déconnexion** par pseudo + mot de passe (pas d'email visible)
|
||||
- 🖼️ **Profil éditable** : changer son pseudo et sa photo
|
||||
- 🏆 **Leaderboard** de tous les inscrits, trié par points (rang · photo · pseudo · points)
|
||||
- 📱 **Responsive**, pensé mobile-first et propre sur ordinateur
|
||||
- 🔐 **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
|
||||
|
||||
---
|
||||
|
||||
@@ -20,9 +20,11 @@ Application web pour animer et scorer le jeu **« Le Tribunal »** entre amis pe
|
||||
|
||||
| Rôle | Techno |
|
||||
|--------------------------|---------------------------------|
|
||||
| Framework | Next.js (App Router) + TypeScript |
|
||||
| Style | Tailwind CSS |
|
||||
| Auth · Base · Stockage | Supabase (Auth, Postgres, Storage) |
|
||||
| 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é) |
|
||||
|
||||
---
|
||||
@@ -31,6 +33,7 @@ Application web pour animer et scorer le jeu **« Le Tribunal »** entre amis pe
|
||||
|
||||
- [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)
|
||||
|
||||
---
|
||||
@@ -48,14 +51,14 @@ 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 le contenu de [`supabase/schema.sql`](supabase/schema.sql) (table `profiles`, policies RLS, bucket `avatars`).
|
||||
2. Dans **SQL Editor**, exécutez tout le contenu de [`supabase/schema.sql`](supabase/schema.sql) (tables `profiles`/`points_log`, 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**, **désactivez "Confirm email"**. L'app utilise des adresses techniques fictives (`pseudo@letribunal.test`) qui ne peuvent recevoir aucun email : si la confirmation reste activée, personne ne peut se connecter après inscription.
|
||||
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 juge (voir [Rôles & administration](#rôles--administration)).
|
||||
|
||||
### 3. Variables d'environnement
|
||||
|
||||
Copiez le fichier d'exemple et renseignez vos valeurs :
|
||||
|
||||
```bash
|
||||
cp .env.local.example .env.local
|
||||
```
|
||||
@@ -102,18 +105,24 @@ L'application tourne sur [http://localhost:3000](http://localhost:3000).
|
||||
tribunal-app/
|
||||
├─ src/
|
||||
│ ├─ app/
|
||||
│ │ ├─ login/page.tsx # Connexion (pseudo + mot de passe)
|
||||
│ │ ├─ signup/page.tsx # Création de compte + upload photo
|
||||
│ │ ├─ leaderboard/page.tsx # Classement de tous les inscrits
|
||||
│ │ ├─ profile/ # Édition du profil + déconnexion
|
||||
│ │ ├─ layout.tsx # Layout + navigation
|
||||
│ │ └─ page.tsx # Redirection selon l'état de connexion
|
||||
│ ├─ components/ # Composants UI réutilisables (Avatar, NavBar)
|
||||
│ │ ├─ 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/
|
||||
│ │ ├─ auth.ts # slugify / email interne dérivé du pseudo
|
||||
│ │ └─ supabase/ # Clients Supabase (browser / server / proxy)
|
||||
│ └─ proxy.ts # Protection des routes + rafraîchissement session
|
||||
├─ supabase/schema.sql # Schéma + policies RLS + bucket avatars (versionné)
|
||||
│ │ ├─ 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
|
||||
```
|
||||
@@ -122,17 +131,57 @@ tribunal-app/
|
||||
|
||||
## Authentification
|
||||
|
||||
La connexion se fait par **pseudo + mot de passe**, sans email visible. En interne, un email technique est dérivé du pseudo (`slug@letribunal.test`) pour s'appuyer sur Supabase Auth — `.test` est un TLD réservé (RFC 2606), accepté par le validateur d'email de Supabase (`.local` est rejeté). Le pseudo affiché est stocké dans la table `profiles`, et son unicité est vérifiée à l'inscription.
|
||||
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)).
|
||||
|
||||
> Pas de réinitialisation de mot de passe par email en V1 (cercle d'amis, pas d'email réel collecté). Un administrateur peut réinitialiser un mot de passe depuis la console Supabase si nécessaire.
|
||||
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 :
|
||||
|
||||
```sql
|
||||
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](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 le leaderboard, un juge 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 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 : chacun ne peut modifier que son propre profil, et **les points ne sont pas modifiables** depuis l'application (gestion admin prévue plus tard).
|
||||
- Lecture des profils réservée aux utilisateurs authentifiés (nécessaire pour le leaderboard).
|
||||
- **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 juge 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.
|
||||
|
||||
---
|
||||
|
||||
@@ -144,13 +193,12 @@ Les couleurs sont définies comme **design tokens** (variables CSS), afin de pou
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [ ] **Rôles admin / juges** — attribution de points, modération
|
||||
- [ ] **É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
|
||||
- [ ] **Temps réel** — leaderboard et fil d'événements live, mode « grand écran » pour vidéoprojecteur
|
||||
- [ ] **Mode grand écran** — vue leaderboard optimisée vidéoprojecteur
|
||||
|
||||
---
|
||||
|
||||
## Licence
|
||||
|
||||
Projet personnel et privé, destiné à un usage entre amis.
|
||||
Projet personnel et privé, destiné à un usage entre amis.
|
||||
|
||||
Reference in New Issue
Block a user