Le token OTP Supabase expire après 24h (max autorisé) mais l'invitation applicative reste valide 7 jours. Quand le lien est expiré, l'invité peut maintenant demander un nouveau lien en un clic au lieu d'être bloqué. L'email est inclus dans l'URL d'invitation (urlquery-encodé) pour éviter de le redemander.
Remplace les wizards multi-étapes par des formulaires créant une seule entité chacun.
Supprime les use cases composés (create-public-figure-with-statement, contribute-statement,
create-position-with-statement) et leurs actions/composants associés.
Ajoute FormSuccess, CSS partagé form-with-guide, types partagés domain/use-cases/types.
Ajoute le bouton « Ajouter une position » sur la page sujet.
Le use case createSubjectUseCase retournait un message générique « Données invalides » au lieu de FieldErrors par champ. Alignement sur le pattern des autres use cases (create-public-figure, create-position) avec propagation des erreurs dans l'action et le formulaire.
La création de personnalité accepte désormais soit une URL Wikipedia, soit 2+ sources de notoriété (URLs valides). Le formulaire affiche conditionnellement les champs de sources quand Wikipedia est vide, avec des guides UX sur chaque champ.
Remplace le findAll() + getStats() par personnalité (843 requêtes) par une vue SQL v_public_figure_activity_summary (2-3 requêtes). La page affiche maintenant 4 sections : recherche, top 10 actives, activité récente, et index alphabétique. Renomme aussi subject_activity_summary en v_subject_activity_summary pour uniformiser la convention de nommage des vues.
Le critère de notoriété accepte désormais soit une page Wikipedia, soit deux sources indépendantes (notoriety_sources). Cela permet d'ajouter des personnalités publiques notables qui n'ont pas de page Wikipedia.
- Identité (nom, email) avec bouton Inviter
- Carte réputation avec score, rang, barre de progression vers le prochain rang
- Lien vers l'historique de réputation
- Le nom dans le header devient un lien vers /me
- Ajout getNextRankThreshold dans permissions
Remplace le pattern addReputation (fetch+update non atomique) par un INSERT dans reputation_events avec trigger PostgreSQL qui maintient le cache contributors.reputation.
- Entité ReputationEvent avec types RewardableAction et RelatedEntityType
- Interface recordEvent + getHistory dans ReputationRepository
- Migration SQL : table, index, trigger, RLS
- Migration des 7 use cases vers recordEvent
- Ajout invitation_bonus dans les récompenses
Page protégée affichant le score, le rang et la liste chronologique des mouvements de réputation du contributeur connecté. Ajout d'un lien « Réputation » dans le header.
Pré-remplissage du formulaire de contribution depuis les pages /s/[slug] et /p/[slug] via searchParams (initialFigure, initialSubject). Le Combobox supporte un initialItem pour afficher la sélection par défaut.
Server actions et use cases pour le CRUD sujets (create, update, delete), page /s/ajouter avec NewSubjectForm. Mise à jour de CONTRIBUTING.md : une personnalité peut changer de position au fil du temps.
Permet aux contributeurs confirmés d'inviter de nouvelles personnes par email. L'invité reçoit un lien, choisit son mot de passe, et son compte est créé avec la moitié de la réputation de l'inviteur. L'inviteur reçoit +50 pts.
Domain : entité Invitation (Effect Schema), use cases inviteUser et acceptInvitation, permissions invite_user, ContributorRepository et InvitationRepository, extraction de DatabaseError dans errors.ts.
Infra : repositories Supabase, migration table invitations avec index unique partiel, RPC get_user_id_by_email, template email personnalisé.
UI : page /inviter avec formulaire, page /accepter-invitation avec choix de mot de passe (token consommé uniquement à la soumission), lien Inviter dans AuthSection, NoticeBanner pour les redirections avec message.
Correctifs issus de la revue de code : rollback de l'invitation si l'envoi d'email échoue, try/catch + Sentry dans inviteUserAction, ignoreDuplicates dans le callback auth pour ne pas écraser la réputation, exclusion de .direnv dans vitest.
- Upload photo via client admin Supabase (service role, bypass RLS) avec redimensionnement sharp (800px max, JPEG q85)
- Gestion d'erreurs propre dans les 5 repositories : helper dbError + Sentry.captureException
- Formulaire : try/catch/finally pour éviter UI bloquée, validation taille photo côté client (30 Mo max)
- Validation date avec date-fns (parseISO/isFuture) au lieu de construction manuelle
- bodySizeLimit 30mb pour les server actions
- Redirections UI vers /nouvelle-prise-de-position + bouton conditionnel sur /s
- Tests contribute-statement use case (21 tests) + findByWikipediaUrl dans les mocks existants
Formulaire unique /nouvelle-prise-de-position permettant de créer une prise
de position en sélectionnant ou créant à la volée une personnalité, un sujet
et une position. L'upload de la photo se fait AVANT la création des entités
pour éviter un état incohérent, et les erreurs d'upload remontent à Sentry.
Appel à l'API MediaWiki pour valider que la page existe et possède la
catégorie « Wikipédia:Article biographique ». Interface WikipediaValidator
dans domain/services (port hexagonal), implémentation dans infra/.
Même logique atomique que le wizard position : on crée personnalité →
statement → evidence → réputation en une seule opération. La permission
add_personality requiert le rang Éloquent (1000+ pts).
- Use case create-public-figure-with-statement en TDD (13 tests)
- Server actions utilitaires (search-subjects, get-positions-for-subject)
- Wizard 2 étapes (personnalité + première prise de position)
- Page /p/ajouter avec auth check Éloquent
- Bouton conditionnel sur /p via le composant Button du design system
- Ajout de size="small" au composant Button
- Ajout de onSelect au Combobox pour le chargement dynamique
- Ajout de typecheck (tsc --noEmit) au script npm check
- Correction des fakes incomplets dans les tests existants
Ajoute un parcours en 2 étapes pour créer une position et son premier
statement atomiquement : étape 1 (titre + description), étape 2
(personnalité + preuve sourcée). La soumission crée position, statement,
evidence et accorde 100 pts de réputation (2×50).
Ajoute get-authenticated-contributor, reputation-repository et son
implémentation Supabase qui n'avaient pas été commités. Corrige aussi
le renommage createServerSupabaseClient → createSSRSupabaseClient dans
toutes les pages. Ajoute le script npm run check (lint+format+build).
- Use case create-statement avec validation par champ (TDD, 8 tests)
- Server actions : création de statement et recherche de personnalités
- Page /s/[slug]/ajouter avec formulaire complet
- Combobox avec downshift pour la recherche async de personnalités
- PositionRepository (interface + implémentation Supabase)
- createEvidence et searchByName ajoutés aux repos existants
- Bouton conditionnel sur la page détail sujet (visible si connecté·e)
- Erreurs de validation affichées sous chaque champ concerné
- Conservation du state du formulaire en cas d'erreur
Remplace les N+1 requêtes (1 + 4×N) par une seule requête sur une vue
PostgreSQL qui agrège sujets, stats et figures. Les sujets sont triés
par date de dernière prise de position (taken_at) décroissante.
5 rangs (Sophiste, Métèque, Éloquent, Idéaliste, Fondateur), matrice
complète des 19 actions, barème de points pour 28 actions. Le Contributor
délègue maintenant au système de permissions via contributorCanPerform.
Hook useCurrentUser pour l'affichage conditionnel côté client.
Installe prettier, configure .prettierrc (singleQuote, no semi, trailingComma, LF) et .prettierignore. Ajoute les scripts format et format:check. Applique le formatage sur tous les fichiers sources.
Remplace les données en dur par une requête Supabase. Ajoute findLatest(limit) au StatementRepository avec le type LatestStatement. Le composant LastStatements devient un server component async avec Suspense, affiche les avatars et les liens vers les personnalités et sujets.
Crée les pages /p/[slug] et /subjects/[slug] qui étaient des liens morts
depuis les pages index. Ajoute le StatementRepository (domain + infra)
pour les requêtes jointes statements/positions/sujets/personnalités.
Composants partagés extraits : FigureAvatar, ErrorDisplay.
La page personnalité affiche les prises de position groupées par sujet
avec label explicite "Sa position :". La page sujet affiche les positions
avec les avatars des personnalités concernées.
- Configure le bucket avatars avec seeding automatique dans config.toml
- Ajoute rewrite Next.js /avatars/* vers Supabase Storage
- Supprime pictureUrl des entités (dérivé du slug)
- Corrige les erreurs TypeScript dans subject-repository-supabase
- Migre les repositories vers Effect avec types brandés