11 KiB
ONBOARDING.md — Expria Frontend
Temps de lecture : 25 minutes. Ce document te permet de prendre en main le projet et de commencer à contribuer. Si tu ne l'as pas encore lu, commence par
ARCHITECTURE.md.
1. Contexte produit (2 min)
Expria est une application web de coaching pour l'examen TCF Canada, destinée aux francophones qui préparent leur immigration au Canada via Express Entry. Les utilisateurs viennent principalement d'Algérie, du Maroc, du Cameroun et sont quasi exclusivement sur mobile.
Fonctionnalités clés :
- Simulation des 6 tâches de l'examen (EE T1/T2/T3 + EO T1/T2/T3)
- Correction automatique par IA (DeepSeek pour les textes, Gemini pour l'audio)
- Rapport détaillé selon le plan de l'utilisateur (free / standard / premium)
- Dialogue oral temps réel avec un examinateur IA (feature Premium exclusive)
- Mode examen chronométré (Premium)
Monétisation : abonnement via Stripe (Standard 19,90€/4 semaines, Premium 39,90€/4 semaines).
2. Installation (5 min)
Prérequis
- Node.js 20 LTS ou supérieur
- npm 10+ (ou pnpm 9+ si tu préfères)
- Git
- Un compte GitHub avec accès au dépôt
germannoff/expria-frontend
Étapes
git clone https://github.com/germannoff/expria-frontend.git
cd expria-frontend
npm install
cp .env.example .env
# Remplis .env avec les vraies valeurs (voir section 3)
npm run dev
L'application devrait être accessible sur http://localhost:5173.
Commandes principales
npm run dev # dev server avec HMR
npm run build # build production (sortie dans dist/)
npm run preview # preview du build de production
npm run typecheck # vérification TypeScript stricte
npm run test # tests Vitest (une passe)
npm run test:watch # tests Vitest en mode watch
npm run lint # ESLint
npm run format # Prettier (écriture)
3. Variables d'environnement
Crée un fichier .env à la racine en copiant .env.example :
VITE_API_URL=https://api.expria.app
VITE_SUPABASE_URL=https://<project>.supabase.co
VITE_SUPABASE_ANON_KEY=<clé publique Supabase>
VITE_ENABLE_T2_LIVE=false
VITE_SENTRY_DSN= (optionnel)
Les valeurs de développement te seront transmises par Hermann ou via un coffre-fort (1Password, Bitwarden).
Ne JAMAIS committer .env. Le fichier est dans .gitignore.
Ne JAMAIS mettre de clé privée (Supabase Service Role, Stripe Secret, Gemini API Key) côté frontend. Ces clés n'existent que dans le backend.
4. Structure du projet (5 min)
La règle essentielle : trois couches hiérarchiques, et les dépendances ne remontent jamais.
app/ ← peut importer de : entities, features, shared
features/ ← peut importer de : entities, shared
entities/ ← peut importer de : shared
shared/ ← ne doit RIEN importer d'autre
Où mettre quoi ?
| Tu écris... | Ça va dans... |
|---|---|
Un type métier (Plan, Production, Report) |
src/entities/<domaine>/types.ts |
Une fonction pure métier (hasAccess, canSimulate, applyFloutage) |
src/entities/<domaine>/lib.ts |
| Un appel API vers le backend | src/entities/<domaine>/api.ts |
| Un composant de page | src/features/<feature>/pages/ |
| Un composant UI spécifique à une feature | src/features/<feature>/components/ |
| Un hook React spécifique à une feature | src/features/<feature>/hooks/ |
Un composant UI générique (Button, Modal) |
src/shared/components/ui/ |
Un helper technique (apiFetch, getAccessToken) |
src/shared/lib/ |
Un hook utilitaire (useDebounce) |
src/shared/hooks/ |
Exemple concret
Tu veux ajouter une feature "historique des paiements". Tu crées :
src/entities/billing/types.ts # type Payment
src/entities/billing/api.ts # GET /billing/payments
src/features/billing/pages/PaymentHistoryPage.tsx
src/features/billing/hooks/usePaymentHistory.ts
Tu n'importes rien de features/ dans entities/. Jamais. C'est la règle d'or.
5. Règles absolues (5 min)
Règle A — Permissions
Interdit :
if (plan === 'premium') { ... }
if (user.plan !== 'free') { ... }
if (PLANS[plan].exam_mode) { ... }
Obligatoire :
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()
}
Pourquoi ? Voir ADR 005.
Règle B — Appels API
Interdit :
fetch('https://api.expria.app/simulations') // oubli du token, pas de retry
supabase.from('productions').select() // contourne le backend
Obligatoire :
import { apiFetch } from '@/shared/lib/api-client'
const response = await apiFetch<Production[]>('/simulations', { method: 'GET' })
if (response.error) {
// gérer l'erreur selon response.error.code
}
Règle C — Logique métier
Interdit : mettre des règles métier dans un composant React.
// ❌ Dans une page
{user.plan === 'free' && productions.length >= 5 && <Paywall />}
Obligatoire : extraire dans entities/ :
// entities/user/lib.ts
export function canSimulate(plan: Plan, used: number) { ... }
// features/dashboard/pages/DashboardPage.tsx
const { allowed } = canSimulate(plan, simulationsUsed)
{!allowed && <Paywall reason={reason} />}
Règle D — Clés privées
Aucune clé privée dans le frontend. Aucune. Si tu as un doute, c'est que c'est privé.
Règle E — Supabase côté frontend
Supabase est utilisé uniquement pour l'auth :
supabase.auth.signInWithPassword()supabase.auth.signOut()supabase.auth.getSession()(viaauth-client.ts)
Toute lecture/écriture de données métier passe par le backend Hono. Jamais de supabase.from('productions') côté frontend.
Règle F — Workflow de dev
- Lire
ARCHITECTURE.md+DEVELOPMENT_PRINCIPLES.md+PLANS_TARIFAIRES.md - Plan avant code — tu produis un plan détaillé, tu attends validation
- Code — maximum 3 fichiers par étape
- Test —
npm run typecheck && npm run testvert - Golden Dataset — rejouer les tests manuels concernés
- Commit — message clair (
feat:,fix:,refactor:)
6. Points d'entrée à connaître (5 min)
Fichiers que tu liras en premier
| Fichier | Rôle |
|---|---|
src/app/main.tsx |
Entry point React |
src/app/providers.tsx |
Wrappers globaux (QueryClient, Router, etc.) |
src/app/router.tsx |
Toutes les routes de l'app |
src/entities/user/access.ts |
Source de vérité des plans — copie exacte du backend |
src/entities/user/lib.ts |
hasAccess() et canSimulate() |
src/shared/lib/api-client.ts |
Wrapper fetch avec retry, timeout, logging |
src/shared/lib/auth-client.ts |
Gestion du token Supabase |
src/features/dashboard/hooks/usePlan.ts |
Hook central pour le plan utilisateur |
Comptes de test
Voir docs/TEST_ENVIRONMENT.md (copie du backend). Résumé :
| Plan | Usage | |
|---|---|---|
| test.free@gmail.com | free | Parcours Free normal |
| test.standard@gmail.com | standard | Parcours Standard |
| test.premium@gmail.com | premium | Parcours Premium |
| test.quota@gmail.com | free (5/5) | Blocage quota |
Mot de passe commun : Expria2025!test.
7. Workflow Claude Code (3 min)
Le projet est développé avec assistance IA (Claude Code, Cursor). Le fondateur Hermann est non-technique — le workflow est strict pour éviter les dérives.
Début de session
"Je commence une session sur expria-frontend.
Lis dans l'ordre :
1. docs/ARCHITECTURE.md
2. docs/DEVELOPMENT_PRINCIPLES.md
3. docs/PLANS_TARIFAIRES.md
4. docs/PARCOURS_UTILISATEURS.md (section du plan concerné)
Puis annonce : 'Documents lus, voici mon plan pour cette session.'
Ne commence à coder qu'après validation du plan."
Pendant la session
- L'IA propose un plan → Hermann valide → l'IA code → l'IA lance les tests → l'IA présente un résumé.
- Maximum 3 fichiers par étape. Si plus : découper.
- Tests rouges = on arrête et on corrige avant de continuer.
Fin de session
- Résumé des modifications
- Tests Golden Dataset manuels rejoués
- Mise à jour de
docs/CHANGELOG.md - Commit Git avec message clair
8. Quand tu ne sais pas quoi faire
Ordre de recherche
- Lire le document de référence concerné (
ARCHITECTURE.md,DEVELOPMENT_PRINCIPLES.md, ADRs) - Chercher un pattern existant dans le code (grep sur le nom de la feature)
- Vérifier les parcours utilisateurs (
PARCOURS_UTILISATEURS.md) - Demander à Hermann avant d'inventer une solution
Les pièges classiques
| Piège | Solution |
|---|---|
| "Je vais mettre cette règle dans le composant, c'est plus simple" | Non. Ça va dans entities/<domaine>/lib.ts. |
| "Je vais appeler Supabase directement pour éviter l'appel backend" | Non. Le backend est l'autorité. |
"Je vais tester avec if (plan === 'premium') juste pour déboguer" |
Non, même temporairement. Utilise hasAccess. |
| "Je vais ajouter Zustand, ce sera plus propre" | Non, voir ADR 003. Discute-en avant. |
"Je vais modifier access.ts sans toucher au backend" |
Non. access.ts doit rester identique des deux côtés. |
9. Ressources
Documents de référence (dans ce dépôt)
docs/ARCHITECTURE.md— architecture techniquedocs/DEVELOPMENT_PRINCIPLES.md— règles de devdocs/SECURITY.md— sécurité + patterns interditsdocs/PLANS_TARIFAIRES.md— source de vérité des plansdocs/PARCOURS_UTILISATEURS.md— parcours détaillés par plandocs/GOLDEN_DATASET.md— tests manuelsdocs/adr/— décisions architecturales motivées
Documents backend (dans expria-backend/docs/)
Les documents de référence du backend existent en miroir. Les règles y sont les mêmes, adaptées au contexte serveur.
Docs externes
10. Premier ticket pour te faire la main
Quand tu arrives sur le projet, voici une suggestion de premier ticket pour te familiariser :
Ajouter un affichage du nombre de simulations restantes pour un utilisateur Free dans le header du Dashboard.
Critères d'acceptation :
- Utiliser
usePlan()pour obtenir le plan etsimulations_used- Utiliser
canSimulate()pour déterminer s'il en reste- Afficher "X/5 simulations restantes" pour les free, rien pour les standard/premium
- Pas de
if (plan === 'free')dans le code- Test unitaire de la logique
Ce ticket touche :
entities/user/lib.ts,features/dashboard/components/,features/dashboard/pages/. Trois fichiers — parfait pour une session.
Bienvenue dans Expria. Bonne route !
11. Historique
| Version | Date | Changements |
|---|---|---|
| 1.0 | 2026-04-17 | Création initiale |