Valentin ROBIN e6f4463cb0 Design responsive, logo, et correctif RLS upload photo
Design & UX :
- Renomme "Administration" en "Admin" (nav, page, docs).
- Logo original (marteau de juge stylisé navy/or) en favicon (src/app/icon.svg,
  remplace le favicon.ico par défaut de create-next-app) et dans la navbar
  (components/logo.tsx).
- Navbar : menu hamburger sur mobile (les liens débordaient à partir de
  ~4 entrées sur petit écran).
- Leaderboard : lignes en deux niveaux sur mobile (infos membre / contrôles
  juges) au lieu d'un seul flex-wrap qui devenait illisible.
- Contrôles de points juges (JudgePointControls) : boutons et champ montant
  réduits en mode compact pour tenir dans les colonnes du podium sur petit
  écran.
- Podium : scroll horizontal de secours (overflow-x-auto) si le contenu
  dépasse malgré tout sur très petits écrans.
- Table admin : pseudo/avatar sur une ligne, rôle/statut/actions sur une
  autre en mobile (le flex-1 sur l'input pseudo n'avait aucun effet en
  layout colonne).

Correctif sécurité/fonctionnel :
- storage.objects n'avait que des policies INSERT et UPDATE pour le bucket
  avatars, pas de policy SELECT. Postgres a besoin de lire la ligne
  existante pour évaluer la clause USING d'un UPDATE : sans SELECT, tout
  remplacement de photo (upsert) échouait avec "new row violates row-level
  security policy", même si les policies INSERT/UPDATE étaient correctes.
  Ajout d'une policy SELECT publique sur le bucket avatars (cohérent avec
  le fait que le bucket est déjà public en lecture).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 16:47:43 +02:00

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. Le socle est ré-thématisable par édition (un thème différent chaque année).


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 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 Admin
  • 📣 Le crieur : journal public et en direct de toutes les attributions de points
  • 📱 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
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

  1. Créez un nouveau projet sur supabase.com.
  2. Dans SQL Editor, exécutez tout le contenu de 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, 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 ci-dessous.
  6. Une fois votre propre compte créé et confirmé via /signup, désignez-vous juge (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_role dans 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)

  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 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/
│  │  ├─ 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

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 juge pour le changer »). Un juge peut toujours modifier le pseudo de quiconque et, s'il le souhaite, redéverrouiller un compte depuis Admin.

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 juge peut promouvoir/rétrograder d'autres membres et éditer leur pseudo depuis la page Admin (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 (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 Admin.

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 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.

Ré-thématisation

Les couleurs sont définies comme design tokens (variables CSS), afin de pouvoir changer l'habillage d'une édition à l'autre sans toucher aux composants. Palette par défaut : navy #0F2748, or #C9A227, ivoire #F4ECD8, encre #2A2116.


Roadmap

  • É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
  • Mode grand écran — vue leaderboard optimisée vidéoprojecteur

Licence

Projet personnel et privé, destiné à un usage entre amis.

S
Description
Application web pour animer et scorer le jeu « Le Tribunal » entre amis : comptes, profils et leaderboard.
Readme 2.1 MiB
Languages
TypeScript 76.3%
PLpgSQL 21.5%
CSS 1.8%
Python 0.2%
JavaScript 0.1%