docs: ajouter ONBOARDING, SECURITY, CHANGELOG, ADR 001-006
This commit is contained in:
parent
28c9c08d31
commit
52b8e9d011
9 changed files with 1380 additions and 0 deletions
68
docs/adr/001-cloudflare-pages-vs-vercel.md
Normal file
68
docs/adr/001-cloudflare-pages-vs-vercel.md
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# ADR 001 — Hébergement frontend : Cloudflare Pages
|
||||
|
||||
**Statut :** Accepté
|
||||
**Date :** 2026-04-17
|
||||
**Décideur :** Hermann
|
||||
|
||||
---
|
||||
|
||||
## Contexte
|
||||
|
||||
Le frontend Expria V2 doit être hébergé sur une plateforme de distribution statique. Trois options étaient envisagées : Vercel, Cloudflare Pages, et un VPS.
|
||||
|
||||
Contraintes à prendre en compte :
|
||||
|
||||
- L'audience cible est majoritairement en Afrique du Nord (Algérie, Maroc) et en Afrique centrale (Cameroun), avec une part importante au Canada. Presque 100% mobile.
|
||||
- Le DNS de `expria.app` est actuellement géré chez Vercel.
|
||||
- Le backend tourne déjà sur Render Frankfurt (EU).
|
||||
- Le projet est maintenu par un fondateur non-technique, avec assistance IA.
|
||||
- Les revenus sont encore nuls ou quasi nuls — les coûts d'infrastructure doivent rester à zéro.
|
||||
|
||||
## Options envisagées
|
||||
|
||||
### Option A — Vercel
|
||||
|
||||
- Avantages : DNS déjà configuré chez Vercel, auto-deploy GitHub natif sans config, edge nodes à Paris et Francfort.
|
||||
- Inconvénients : tarif gratuit limité en bande passante si le produit décolle, verrouillage progressif dans l'écosystème Vercel (edge functions, middleware), fondateur a déjà exprimé un veto personnel contre Vercel suite à frictions passées.
|
||||
|
||||
### Option B — Cloudflare Pages
|
||||
|
||||
- Avantages : CDN mondial (287+ points de présence, y compris Casablanca et Le Caire), tier gratuit généreux (builds illimités, bande passante illimitée), cohérent avec la recommandation initiale d'`ARCHITECTURE.md` backend §2, découplé du DNS (on peut migrer le DNS sans changer l'hébergement).
|
||||
- Inconvénients : déploiement via Wrangler CLI légèrement moins transparent que Vercel, auto-deploy GitHub nécessite une config initiale.
|
||||
|
||||
### Option C — VPS (dans la continuité du backend)
|
||||
|
||||
- Avantages : un seul fournisseur, contrôle total.
|
||||
- Inconvénients : fait perdre tout le bénéfice CDN (les utilisateurs africains rapatrient les assets depuis Frankfurt), charge opérationnelle (nginx, certificats, mise à jour OS), gaspille les ressources VPS.
|
||||
|
||||
## Décision
|
||||
|
||||
**Cloudflare Pages** pour l'hébergement frontend.
|
||||
|
||||
Configuration cible :
|
||||
- Source : dépôt GitHub `germannoff/expria-frontend`
|
||||
- Build command : `npm run build`
|
||||
- Output directory : `dist`
|
||||
- Domaine : `expria.app` (CNAME depuis le DNS Vercel vers Cloudflare Pages, le DNS reste chez Vercel jusqu'à nouvel ordre)
|
||||
- Déploiement : auto-deploy à chaque push sur `main` (configuration Cloudflare Pages ↔ GitHub)
|
||||
|
||||
## Conséquences
|
||||
|
||||
**Positives :**
|
||||
- Coût zéro quelle que soit la montée en charge initiale.
|
||||
- Latence optimisée pour l'audience cible (CDN proche des utilisateurs africains).
|
||||
- Cohérent avec le document de référence backend `ARCHITECTURE.md` §2.
|
||||
- Indépendance vis-à-vis de Vercel (DNS reste séparé de l'hébergement).
|
||||
|
||||
**Négatives :**
|
||||
- Une commande CLI supplémentaire (`wrangler`) à connaître pour les déploiements manuels d'urgence. Mitigation : l'auto-deploy GitHub résout ce point à 95%.
|
||||
- Si le fondateur doit un jour déployer manuellement sans accès au terminal, il devra passer par le dashboard Cloudflare Pages.
|
||||
|
||||
**À revisiter si :**
|
||||
- L'audience bascule massivement vers un continent non couvert par Cloudflare (improbable).
|
||||
- Cloudflare Pages impose des limites qui contraignent le produit (scénarios edge à étudier si on ajoute du server-side rendering plus tard).
|
||||
|
||||
## Références
|
||||
|
||||
- `ARCHITECTURE.md` backend §2 et §9 (recommandation initiale Cloudflare Pages)
|
||||
- `TECH_DEBT.md` TD-04 (déploiement manuel — rendu caduc par la réactivation du compte GitHub le 2026-04-17)
|
||||
78
docs/adr/002-auth-api-decoupling.md
Normal file
78
docs/adr/002-auth-api-decoupling.md
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
# ADR 002 — Découplage auth-client et api-client
|
||||
|
||||
**Statut :** Accepté
|
||||
**Date :** 2026-04-17
|
||||
**Décideur :** Hermann
|
||||
|
||||
---
|
||||
|
||||
## Contexte
|
||||
|
||||
Le frontend doit :
|
||||
1. Obtenir un token JWT auprès de Supabase (authentification).
|
||||
2. Envoyer ce token au backend Hono sur chaque requête API (autorisation).
|
||||
|
||||
L'approche initiale proposée était un seul fichier `api-client.ts` contenant un intercepteur qui appelle directement le SDK Supabase pour récupérer le token avant chaque requête. Cette approche couple fortement le client HTTP à Supabase.
|
||||
|
||||
## Options envisagées
|
||||
|
||||
### Option A — API client avec intercepteur Supabase intégré
|
||||
|
||||
```typescript
|
||||
// api-client.ts
|
||||
import { supabase } from './supabase'
|
||||
|
||||
async function apiFetch(path: string) {
|
||||
const { data } = await supabase.auth.getSession()
|
||||
return fetch(`${API_URL}${path}`, {
|
||||
headers: { Authorization: `Bearer ${data.session?.access_token}` }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
- Avantages : code court, un seul fichier, familier aux développeurs juniors.
|
||||
- Inconvénients : couple définitivement l'HTTP layer à Supabase, rend impossible le test du client API sans mocker Supabase entier, bloque toute migration future d'auth (ex : passage à Auth0, Clerk, auth maison).
|
||||
|
||||
### Option B — Deux fichiers distincts
|
||||
|
||||
```typescript
|
||||
// auth-client.ts (connaît Supabase, rien d'autre)
|
||||
export async function getAccessToken(): Promise<string | null> { ... }
|
||||
|
||||
// api-client.ts (connaît HTTP, rien d'autre)
|
||||
export async function apiFetch<T>(path: string, options: RequestOptions = {}): Promise<T> {
|
||||
const token = options.token ?? await getAccessToken()
|
||||
// fetch avec Bearer token
|
||||
}
|
||||
```
|
||||
|
||||
- Avantages : séparation des responsabilités claire (auth vs HTTP), chaque fichier testable indépendamment, migration d'auth = modifier un seul fichier, respecte le principe d'inversion de dépendance.
|
||||
- Inconvénients : deux fichiers au lieu d'un (impact négligeable).
|
||||
|
||||
## Décision
|
||||
|
||||
**Option B** — deux fichiers séparés :
|
||||
|
||||
- `src/shared/lib/auth-client.ts` : gère exclusivement Supabase Auth (login, logout, register, récupération du token, refresh de session).
|
||||
- `src/shared/lib/api-client.ts` : gère exclusivement les appels HTTP vers `api.expria.app` (fetch, retry, timeout, logging, parsing d'erreurs).
|
||||
|
||||
L'api-client appelle `getAccessToken()` depuis l'auth-client, mais n'importe aucun type ni fonction Supabase directement.
|
||||
|
||||
## Conséquences
|
||||
|
||||
**Positives :**
|
||||
- Un dev qui arrive comprend en 30 secondes : "ici l'auth, ici l'HTTP".
|
||||
- Le test de `api-client.ts` ne nécessite pas de mocker Supabase — on injecte un token factice.
|
||||
- Si Supabase est remplacé un jour (peu probable mais possible), seul `auth-client.ts` change. Aucun fichier feature n'est touché.
|
||||
- Cohérent avec la Règle 1 de `DEVELOPMENT_PRINCIPLES.md` : séparation stricte des responsabilités.
|
||||
|
||||
**Négatives :**
|
||||
- Un fichier supplémentaire. Coût négligeable.
|
||||
|
||||
**À revisiter si :**
|
||||
- Un besoin de couplage fort émerge (ex : un token custom généré par le backend plutôt que Supabase). Dans ce cas, la séparation reste valide — on change juste l'implémentation interne de `getAccessToken()`.
|
||||
|
||||
## Références
|
||||
|
||||
- `DEVELOPMENT_PRINCIPLES.md` Règle 1 (séparation stricte)
|
||||
- `ARCHITECTURE.md` §10 Règle 1 (le frontend ne contient aucune logique métier)
|
||||
62
docs/adr/003-no-zustand.md
Normal file
62
docs/adr/003-no-zustand.md
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# ADR 003 — Pas de Zustand pour la V2
|
||||
|
||||
**Statut :** Accepté
|
||||
**Date :** 2026-04-17
|
||||
**Décideur :** Hermann
|
||||
|
||||
---
|
||||
|
||||
## Contexte
|
||||
|
||||
Question posée : faut-il introduire un store global (Zustand, Jotai, Redux Toolkit) dès le scaffold du frontend, en prévision de la complexité future du produit ?
|
||||
|
||||
Arguments en faveur d'une intégration précoce :
|
||||
- La T2 Live a une state machine complexe (idle → connecting → listening → speaking → processing → ended/error).
|
||||
- Plusieurs composants pourraient devoir lire le plan utilisateur actuel.
|
||||
- Un produit "scalable" justifierait une gestion d'état "moderne".
|
||||
|
||||
## Options envisagées
|
||||
|
||||
### Option A — Zustand dès le départ
|
||||
|
||||
- Avantages : prêt le jour où on en aura besoin, un seul endroit pour l'état global.
|
||||
- Inconvénients : dépendance supplémentaire à maintenir, code plus abstrait pour des cas qui ne la nécessitent pas, risque que les devs "poussent" de l'état dans le store par réflexe même quand `useState` suffit.
|
||||
|
||||
### Option B — Pas de store global, stratégie en trois couches
|
||||
|
||||
- **État serveur** → TanStack Query (cache, refetch, invalidation après webhook Stripe).
|
||||
- **État T2 Live** → `useReducer` local avec une state machine explicite, dans `features/t2-live/hooks/useT2LiveSession.ts`.
|
||||
- **État UI local** → `useState` / `useContext` pour les cas simples (ex : ouverture d'une modal).
|
||||
|
||||
- Avantages : zéro dépendance supplémentaire, chaque composant gère son propre état, TanStack Query couvre 90% des besoins (tout ce qui vient du serveur).
|
||||
- Inconvénients : si plusieurs composants non-parents ont besoin de partager un état client complexe (non-serveur), il faudra passer par React Context ou migrer vers Zustand à ce moment-là.
|
||||
|
||||
## Décision
|
||||
|
||||
**Option B** — pas de store global pour la V2.
|
||||
|
||||
Règle d'introduction future : Zustand (ou équivalent) ne sera introduit que si un cas concret apparaît où plusieurs composants non-parents partagent un état client complexe qui ne peut pas être géré par TanStack Query ni par un Context React local. Jusque là, on s'en passe.
|
||||
|
||||
## Conséquences
|
||||
|
||||
**Positives :**
|
||||
- Une dépendance en moins dans `package.json`.
|
||||
- Un nouveau dev ne se demande pas "pourquoi Zustand pour un produit aussi simple ?".
|
||||
- Évite le piège classique : dès qu'un store global existe, les devs y poussent tout, y compris ce qui devrait rester local. Le résultat est un store-spaghetti.
|
||||
- TanStack Query gère déjà la synchronisation serveur → UI, ce qui couvre le plan utilisateur, les productions, les rapports, l'historique.
|
||||
|
||||
**Négatives :**
|
||||
- Si un besoin complexe de partage d'état client apparaît tôt, il faudra migrer. Mitigation : TanStack Query + Context suffisent dans 95% des cas prévisibles.
|
||||
|
||||
**À revisiter si :**
|
||||
- Un composant du header doit réagir en temps réel à un événement du composant de simulation (sans passer par le serveur).
|
||||
- La state machine T2 Live devient partagée entre plusieurs écrans (peu probable).
|
||||
- L'application a plus de 10 écrans avec de l'état client partagé complexe.
|
||||
|
||||
Dans ces cas, introduire Zustand sera une session dédiée avec migration progressive — pas une réécriture.
|
||||
|
||||
## Références
|
||||
|
||||
- ADR 002 (API client découplé)
|
||||
- TanStack Query docs — stratégie de cache
|
||||
- `ARCHITECTURE.md` §4 (structure `features/` + `shared/`)
|
||||
93
docs/adr/004-types-duplication.md
Normal file
93
docs/adr/004-types-duplication.md
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
# ADR 004 — Types partagés par duplication contrôlée
|
||||
|
||||
**Statut :** Accepté
|
||||
**Date :** 2026-04-17
|
||||
**Décideur :** Hermann
|
||||
|
||||
---
|
||||
|
||||
## Contexte
|
||||
|
||||
Le frontend Expria et le backend Expria sont dans deux dépôts GitHub séparés (`expria-frontend` et `expria-backend`). Plusieurs types TypeScript doivent exister à l'identique des deux côtés :
|
||||
|
||||
- `Plan` (`'free' | 'standard' | 'premium'`)
|
||||
- `PlanPermissions` (l'objet de l'objet `PLANS`)
|
||||
- `Production` (enregistrement d'une simulation)
|
||||
- `Report` (rapport de correction)
|
||||
- `ApiResponse<T>` (contrat de réponse normalisé)
|
||||
|
||||
Sans synchronisation, ces types divergent, ce qui cause des bugs subtils (ex : un champ renommé dans le backend mais pas dans le frontend).
|
||||
|
||||
## Options envisagées
|
||||
|
||||
### Option A — Monorepo avec pnpm workspaces ou Turborepo
|
||||
|
||||
- Avantages : un seul dépôt, types partagés via un package `@expria/types`, refactor global en une seule PR.
|
||||
- Inconvénients : restructuration massive (les dépôts actuels sont indépendants, chacun avec son propre CI/CD sur Render et Cloudflare Pages), courbe d'apprentissage pour un fondateur non-technique, complexité des commandes (`pnpm --filter frontend run dev`), incompatibilité avec le workflow Claude Code actuel (une session = un dépôt).
|
||||
|
||||
### Option B — Package npm publié (`@expria/types`)
|
||||
|
||||
- Avantages : types versionnés, installable comme n'importe quelle dépendance.
|
||||
- Inconvénients : publication sur npm (privé = $7/mois/dev, public = expose la structure du produit), versioning à gérer, friction de workflow (publier une nouvelle version à chaque changement).
|
||||
|
||||
### Option C — Génération automatique via `tsc --declaration`
|
||||
|
||||
- Avantages : les types sont dérivés directement du code backend, pas de divergence possible.
|
||||
- Inconvénients : pipeline de build à maintenir entre les deux dépôts, chaque modification backend nécessite un commit dans frontend pour régénérer les types, fragile au démarrage.
|
||||
|
||||
### Option D — Duplication manuelle avec commentaire source
|
||||
|
||||
- Avantages : zéro infrastructure, chaque dépôt reste autonome, compatible avec le workflow Claude Code actuel.
|
||||
- Inconvénients : discipline humaine requise pour synchroniser à chaque changement backend.
|
||||
|
||||
## Décision
|
||||
|
||||
**Option D** — duplication manuelle, avec règle de discipline stricte.
|
||||
|
||||
### Format imposé
|
||||
|
||||
Chaque fichier de types dupliqué commence par :
|
||||
|
||||
```typescript
|
||||
// SOURCE OF TRUTH: expria-backend/src/types/<fichier>.ts
|
||||
// Synchronisé le : YYYY-MM-DD
|
||||
// Si ce fichier diverge du backend, le frontend doit être mis à jour immédiatement.
|
||||
```
|
||||
|
||||
### Règle de synchronisation
|
||||
|
||||
Tout changement de type dans le backend **doit** être répercuté dans le frontend dans le même commit logique (même jour, même session de travail). Cette règle est intégrée à `DEVELOPMENT_PRINCIPLES.md` frontend.
|
||||
|
||||
### Liste des fichiers concernés
|
||||
|
||||
- `src/entities/user/types.ts` ← `expria-backend/src/types/plan.ts`
|
||||
- `src/entities/production/types.ts` ← `expria-backend/src/types/production.ts`
|
||||
- `src/entities/report/types.ts` ← `expria-backend/src/types/report.ts`
|
||||
- `src/shared/types/api.ts` ← `expria-backend/src/types/api.ts`
|
||||
|
||||
### Fichier critique particulier
|
||||
|
||||
`src/entities/user/lib/access.ts` doit être **identique au bit près** à `expria-backend/src/lib/access.ts`. Cette règle est inscrite dans `ARCHITECTURE.md` §10 Règle 2 backend — on l'étend au frontend.
|
||||
|
||||
## Conséquences
|
||||
|
||||
**Positives :**
|
||||
- Zéro infrastructure, zéro outillage à maintenir.
|
||||
- Chaque dépôt reste autonome (cohérent avec le choix de dépôts séparés — `ARCHITECTURE.md` backend §3).
|
||||
- Workflow Claude Code inchangé (une session = un dépôt).
|
||||
- Coût zéro.
|
||||
|
||||
**Négatives :**
|
||||
- Discipline humaine requise. Si la règle n'est pas respectée, divergence silencieuse possible.
|
||||
- Mitigation : ajouter dans `GOLDEN_DATASET.md` frontend un test de cohérence minimal — appeler `/plans/status` et vérifier que la structure retournée correspond aux types frontend.
|
||||
|
||||
**À revisiter si :**
|
||||
- Un dev senior rejoint l'équipe et préfère un monorepo pnpm (décision à prendre avec lui).
|
||||
- Le projet stabilise ses types (6-12 mois de production sans changement de schéma). À ce moment, un package `@expria/types` devient viable.
|
||||
- Les divergences silencieuses causent plus de 2 bugs en production.
|
||||
|
||||
## Références
|
||||
|
||||
- `ARCHITECTURE.md` backend §3 (dépôts séparés)
|
||||
- `ARCHITECTURE.md` backend §10 Règle 2 (source de vérité unique pour `access.ts`)
|
||||
- `DEVELOPMENT_PRINCIPLES.md` frontend (règle de synchronisation à ajouter)
|
||||
149
docs/adr/005-has-access-typed-strict.md
Normal file
149
docs/adr/005-has-access-typed-strict.md
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
# ADR 005 — Fonctions d'accès : alias frontend-idiomatiques sur l'API backend
|
||||
|
||||
**Statut :** Accepté
|
||||
**Date :** 2026-04-17
|
||||
**Décideur :** Hermann
|
||||
**Révision :** 2 (après audit backend du 2026-04-17)
|
||||
|
||||
---
|
||||
|
||||
## Contexte
|
||||
|
||||
La vérification des permissions selon le plan utilisateur est la logique la plus critique du frontend. Une erreur ici = une feature Premium accessible gratuitement, ou à l'inverse un utilisateur payant bloqué.
|
||||
|
||||
La Règle D de `DEVELOPMENT_PRINCIPLES.md` l'énonce clairement : **jamais de `if (plan === 'premium')` dans le code**. Toute vérification passe par un helper centralisé.
|
||||
|
||||
L'audit backend du 2026-04-17 a confirmé que `src/lib/access.ts` expose trois fonctions :
|
||||
|
||||
```typescript
|
||||
export function getPlanPermissions(plan: Plan): Permissions
|
||||
export function canUserSimulate(user: { plan: string; simulations_used: number }): { allowed, reason? }
|
||||
export function checkFeatureAccess(plan: Plan, feature: Feature): boolean
|
||||
```
|
||||
|
||||
## Contrainte dictée par l'ADR 004
|
||||
|
||||
Le fichier `src/entities/user/access.ts` côté frontend **doit être identique au bit près** à `expria-backend/src/lib/access.ts`. Cette règle est non négociable : elle garantit qu'un changement de permission dans le backend est impossible à rater côté frontend.
|
||||
|
||||
## Options envisagées
|
||||
|
||||
### Option 1 — Noms strictement identiques partout
|
||||
|
||||
Frontend utilise directement `canUserSimulate()` et `checkFeatureAccess()` dans tout le code.
|
||||
|
||||
- **Avantages :** un seul nom par fonction dans tout le projet, pas d'indirection, grep universel.
|
||||
- **Inconvénients :** `checkFeatureAccess` est verbeux en JSX, le nom `canUserSimulate` est backend-coloré (prend un `user`, pas un plan), ces noms ne sont pas les standards React (`hasRole`, `hasPermission`, `hasAccess`).
|
||||
|
||||
### Option 2 — Alias frontend-idiomatiques dans `entities/user/lib.ts`
|
||||
|
||||
`access.ts` reste identique au backend. Un fichier séparé `lib.ts` ré-exporte les fonctions sous des noms plus courts et idiomatiques React.
|
||||
|
||||
```typescript
|
||||
// src/entities/user/access.ts — IDENTIQUE au backend
|
||||
export function getPlanPermissions(plan) { /* ... */ }
|
||||
export function canUserSimulate(user) { /* ... */ }
|
||||
export function checkFeatureAccess(plan, feature) { /* ... */ }
|
||||
|
||||
// src/entities/user/lib.ts — alias pour le frontend
|
||||
import {
|
||||
canUserSimulate,
|
||||
checkFeatureAccess,
|
||||
getPlanPermissions,
|
||||
} from './access'
|
||||
|
||||
/**
|
||||
* Alias frontend-idiomatique de checkFeatureAccess.
|
||||
* Vérifie si un plan a accès à une feature booléenne donnée.
|
||||
*/
|
||||
export const hasAccess = checkFeatureAccess
|
||||
|
||||
/**
|
||||
* Alias frontend-idiomatique de canUserSimulate.
|
||||
* Signature ergonomique (plan, used) au lieu de ({ plan, simulations_used }).
|
||||
*/
|
||||
export function canSimulate(plan: Plan, simulationsUsed: number) {
|
||||
return canUserSimulate({ plan, simulations_used: simulationsUsed })
|
||||
}
|
||||
|
||||
/**
|
||||
* Ré-export direct — même nom qu'au backend.
|
||||
*/
|
||||
export { getPlanPermissions }
|
||||
```
|
||||
|
||||
- **Avantages :** `access.ts` strictement identique au backend (ADR 004 respecté à 100%), le code frontend utilise `hasAccess` et `canSimulate` (standards React), signature ergonomique côté frontend.
|
||||
- **Inconvénients :** une légère indirection (`Ctrl+click` sur `hasAccess` mène à un ré-export). Bénéfice net : la lisibilité des composants React.
|
||||
|
||||
## Décision
|
||||
|
||||
**Option 2** — alias frontend-idiomatiques dans `entities/user/lib.ts`, par-dessus un `access.ts` strictement identique au backend.
|
||||
|
||||
### Emplacement du code
|
||||
|
||||
```
|
||||
src/entities/user/
|
||||
├── access.ts # COPIE BIT-À-BIT de expria-backend/src/lib/access.ts
|
||||
└── lib.ts # Alias et helpers frontend-spécifiques
|
||||
```
|
||||
|
||||
### Règle d'utilisation dans le code frontend
|
||||
|
||||
**Interdit :**
|
||||
```typescript
|
||||
if (plan === 'premium') { ... }
|
||||
if (PLANS[plan].exam_mode) { ... }
|
||||
if (!user.plan === 'free' && simulations_used > 5) { ... }
|
||||
```
|
||||
|
||||
**Obligatoire :**
|
||||
```typescript
|
||||
import { hasAccess, canSimulate } from '@/entities/user/lib'
|
||||
|
||||
if (hasAccess(plan, 'exam_mode')) { ... }
|
||||
|
||||
const { allowed, reason } = canSimulate(plan, simulationsUsed)
|
||||
if (!allowed) {
|
||||
if (reason === 'quota_reached') openUpgradeModal()
|
||||
}
|
||||
```
|
||||
|
||||
### Règle de maintenance
|
||||
|
||||
Toute modification de `access.ts` doit être faite **simultanément** dans les deux dépôts (backend et frontend) dans le même commit logique. Si une fonction est ajoutée ou renommée côté backend, un alias équivalent peut être ajouté dans `lib.ts` **seulement si** le nom backend est inconfortable en contexte React. Sinon, import direct.
|
||||
|
||||
### Tests associés (obligatoires)
|
||||
|
||||
```
|
||||
src/entities/user/__tests__/access.test.ts # teste les 3 fonctions backend (parité)
|
||||
src/entities/user/__tests__/lib.test.ts # teste hasAccess et canSimulate (alias)
|
||||
```
|
||||
|
||||
Au minimum :
|
||||
- `checkFeatureAccess` / `hasAccess` : 14+ assertions
|
||||
- `canUserSimulate` / `canSimulate` : 7+ assertions
|
||||
- `getPlanPermissions` : 4 assertions (3 plans + plan invalide)
|
||||
|
||||
Ces tests calquent leur structure sur `TESTS_AUTOMATISES.md` backend pour garantir la parité des comportements.
|
||||
|
||||
## Conséquences
|
||||
|
||||
**Positives :**
|
||||
- `access.ts` strictement identique au backend : impossible de diverger par accident.
|
||||
- Code frontend lisible et idiomatique : `hasAccess(plan, 'exam_mode')` dans un composant React se lit naturellement.
|
||||
- Un dev React qui arrive trouve les noms qu'il cherche.
|
||||
- Extension automatique : ajouter une permission dans `PLANS` la rend immédiatement accessible via `hasAccess`.
|
||||
- `canSimulate` a une signature plus ergonomique côté frontend.
|
||||
|
||||
**Négatives :**
|
||||
- Indirection légère : `Ctrl+click` sur `hasAccess` mène à `lib.ts`, puis un deuxième clic est nécessaire pour arriver à `access.ts`. Mitigation : documenté dans `ONBOARDING.md` + commentaire JSDoc sur chaque alias.
|
||||
|
||||
**À revisiter si :**
|
||||
- Une troisième catégorie de valeurs apparaît dans `PLANS` (ni booléen ni quota).
|
||||
- Un dev senior trouve que les alias ajoutent plus de friction que de valeur.
|
||||
|
||||
## Références
|
||||
|
||||
- `PLANS_TARIFAIRES.md` §3 (objet `PLANS` source de vérité)
|
||||
- ADR 004 (règle de duplication de `access.ts`)
|
||||
- `TESTS_AUTOMATISES.md` backend §3, §4, §5 (tests de parité)
|
||||
- Audit backend du 2026-04-17 (confirmation du contenu de `access.ts`)
|
||||
160
docs/adr/006-stack-versions-2026.md
Normal file
160
docs/adr/006-stack-versions-2026.md
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
# ADR 006 — Stack frontend : versions 2026 (React 19, Vite 8, TypeScript 6, Tailwind 4, RR7)
|
||||
|
||||
**Statut :** Accepté
|
||||
**Date :** 2026-04-17
|
||||
**Décideur :** Hermann
|
||||
**Contexte :** Révélé par l'état des lieux Claude Code au démarrage du Sprint 0 frontend
|
||||
|
||||
---
|
||||
|
||||
## Contexte
|
||||
|
||||
La première version d'`ARCHITECTURE.md` §2 listait une stack basée sur les versions "connues stables" :
|
||||
|
||||
- React 18
|
||||
- Vite 5
|
||||
- TypeScript 5
|
||||
- Tailwind 3
|
||||
- React Router v6
|
||||
|
||||
L'état des lieux effectué par Claude Code au démarrage du Sprint 0 (2026-04-17) a révélé que le scaffold installé plusieurs semaines auparavant utilisait des versions plus récentes :
|
||||
|
||||
- React 19.2.4
|
||||
- Vite 8.0.4
|
||||
- TypeScript 6.0.2
|
||||
- Tailwind 4.2.2
|
||||
- React Router v7.14.1
|
||||
|
||||
Cette divergence doit être résolue : soit downgrader le scaffold, soit mettre à jour la documentation.
|
||||
|
||||
## Options envisagées
|
||||
|
||||
### Option A — Downgrader vers les versions de la doc originale
|
||||
|
||||
Aligner le scaffold sur React 18, Vite 5, TypeScript 5, Tailwind 3, React Router v6.
|
||||
|
||||
- **Avantages :** documentation historique respectée, versions "éprouvées" en production.
|
||||
- **Inconvénients :**
|
||||
- Casse un `node_modules` qui fonctionne
|
||||
- Perd l'optimisation du compilateur React 19 (Actions, useOptimistic)
|
||||
- Perd le moteur Oxide de Tailwind 4 (builds 3,5x plus rapides)
|
||||
- Perd le typage strict amélioré de TypeScript 6
|
||||
- Downgrade effectué pour des raisons qui n'existent plus (les versions récentes sont matures en avril 2026)
|
||||
|
||||
### Option B — Mettre à jour la documentation
|
||||
|
||||
Accepter les versions installées et mettre à jour `ARCHITECTURE.md §2` pour refléter la réalité.
|
||||
|
||||
- **Avantages :**
|
||||
- Préserve le travail de scaffold déjà fait
|
||||
- Bénéficie des améliorations de performance des versions récentes
|
||||
- Écosystème mature : shadcn/ui supporte complètement Tailwind 4 et React 19 depuis début 2025
|
||||
- Alignement avec l'écosystème 2026 (les nouveaux tutoriels, docs, et ressources communautaires supposent ces versions)
|
||||
- **Inconvénients :**
|
||||
- Versions légèrement plus récentes = moins de StackOverflow disponible pour les cas exotiques
|
||||
- Mitigation : Claude Opus 4.7 connaît bien ces versions (cf. knowledge cutoff janvier 2026)
|
||||
|
||||
### Option C — Hybride
|
||||
|
||||
Garder React 19, Vite 8, TypeScript 6 mais downgrader Tailwind 4 → 3 pour "compatibilité shadcn/ui classique".
|
||||
|
||||
- **Avantages :** apparemment plus prudent.
|
||||
- **Inconvénients :** injustifié depuis que shadcn/ui supporte complètement Tailwind 4 avec configuration CSS-first via `@theme`. Ajoute de la complexité sans bénéfice.
|
||||
|
||||
## Décision
|
||||
|
||||
**Option B** — accepter les versions installées et mettre à jour la documentation.
|
||||
|
||||
### Stack frontend officielle au 2026-04-17
|
||||
|
||||
| Couche | Version | Notes |
|
||||
|---|---|---|
|
||||
| React | 19.2.x | Server Components N/A (SPA pur), Actions et useOptimistic disponibles |
|
||||
| React DOM | 19.2.x | |
|
||||
| Vite | 8.0.x | Moteur Rolldown stable, config simplifiée |
|
||||
| TypeScript | 6.0.x | Typage strict activé (voir tsconfig.app.json) |
|
||||
| Tailwind CSS | 4.2.x | Configuration CSS-first via `@theme`, pas de `tailwind.config.ts` |
|
||||
| `@tailwindcss/vite` | 4.2.x | Plugin Vite officiel (préféré au plugin PostCSS) |
|
||||
| React Router | v7.14.x | Compatible API v6, data loaders disponibles |
|
||||
| Supabase JS | 2.103.x | |
|
||||
|
||||
### Dépendances à ajouter lors du scaffold
|
||||
|
||||
| Package | Rôle | Cf. ADR |
|
||||
|---|---|---|
|
||||
| `@tanstack/react-query` | Cache serveur, refetch, mutations | ARCHITECTURE.md §2 |
|
||||
| `zod` | Validation des inputs formulaires | SECURITY.md SEC-04 |
|
||||
| `react-markdown` | Rendu sécurisé des rapports IA | SECURITY.md SEC-05 |
|
||||
| `class-variance-authority`, `clsx`, `tailwind-merge` | Utilitaires shadcn/ui | — |
|
||||
| `lucide-react` | Icônes (standard shadcn/ui) | — |
|
||||
| Packages `@radix-ui/react-*` | Primitives shadcn/ui (installés à la demande) | — |
|
||||
| `@sentry/react` | Monitoring | TECH_DEBT.md FTD-07 (après MVP) |
|
||||
|
||||
### Dépendances de développement
|
||||
|
||||
| Package | Rôle |
|
||||
|---|---|
|
||||
| `vitest` | Tests unitaires |
|
||||
| `@vitest/coverage-v8` | Couverture |
|
||||
| `@testing-library/react` | Tests React |
|
||||
| `@testing-library/jest-dom` | Matchers DOM |
|
||||
| `@testing-library/user-event` | Simulation user |
|
||||
| `jsdom` | Environnement DOM pour Vitest |
|
||||
| `prettier` | Formatage |
|
||||
| `eslint-config-prettier` | Intégration ESLint ↔ Prettier |
|
||||
|
||||
### Configuration Tailwind 4
|
||||
|
||||
Pas de `tailwind.config.ts`. La configuration se fait exclusivement dans `src/index.css` via les directives :
|
||||
|
||||
```css
|
||||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
--color-primary: #1B4FD8; /* Couleur brand Expria */
|
||||
--font-sans: "Plus Jakarta Sans", system-ui, sans-serif;
|
||||
/* Autres variables de thème */
|
||||
}
|
||||
```
|
||||
|
||||
### shadcn/ui avec Tailwind 4
|
||||
|
||||
La CLI shadcn/ui supporte Tailwind 4 depuis début 2025 :
|
||||
|
||||
```bash
|
||||
npx shadcn@latest init
|
||||
npx shadcn@latest add button dialog form
|
||||
```
|
||||
|
||||
Les composants générés utilisent les conventions Tailwind 4 (pas de `forwardRef`, attributs `data-slot`). Le fichier de configuration reste `components.json` à la racine.
|
||||
|
||||
## Conséquences
|
||||
|
||||
**Positives :**
|
||||
- Pas de perte de travail sur le scaffold existant
|
||||
- Performances optimales (build Tailwind 4 : microsecondes sur builds incrémentaux)
|
||||
- Stack aligné sur l'écosystème 2026 — facile pour un dev externe qui arrivera
|
||||
- Compilateur React 19 apporte des optimisations gratuites
|
||||
|
||||
**Négatives :**
|
||||
- Les versions récentes peuvent avoir quelques bugs non encore découverts. Mitigation : mise à jour ponctuelle vers la dernière version patch en cas de bug signalé (ex : 19.2.4 → 19.2.5).
|
||||
- Si un dev arrive avec une expertise sur React 17/18 uniquement, courbe d'apprentissage légère. Mitigation : `ONBOARDING.md` liste les ressources officielles pour React 19 et Tailwind 4.
|
||||
|
||||
**À revisiter si :**
|
||||
- Une faille de sécurité critique apparaît dans une version spécifique
|
||||
- Une incompatibilité bloquante est découverte entre deux packages (peu probable en avril 2026)
|
||||
|
||||
## Actions de mise en cohérence
|
||||
|
||||
1. Mettre à jour `ARCHITECTURE.md §2` avec les versions ci-dessus (réalisé en session actuelle)
|
||||
2. Mettre à jour `ONBOARDING.md` pour référencer React 19 et Tailwind 4 dans les ressources (à faire)
|
||||
3. Aucune action sur `TESTS_AUTOMATISES.md` — Vitest fonctionne identiquement
|
||||
4. Aucune action sur les ADRs 001-005 — ils ne référencent pas de versions précises
|
||||
|
||||
## Références
|
||||
|
||||
- État des lieux Claude Code du 2026-04-17
|
||||
- [shadcn/ui Tailwind v4](https://ui.shadcn.com/docs/tailwind-v4) — support officiel confirmé
|
||||
- [React 19 Upgrade Guide](https://react.dev/blog/2024/04/25/react-19)
|
||||
- [Tailwind CSS v4.0](https://tailwindcss.com/blog/tailwindcss-v4)
|
||||
- `ARCHITECTURE.md` §2 (mis à jour en parallèle)
|
||||
Loading…
Add table
Add a link
Reference in a new issue