345 lines
11 KiB
Markdown
345 lines
11 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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 :**
|
|
```typescript
|
|
if (plan === 'premium') { ... }
|
|
if (user.plan !== 'free') { ... }
|
|
if (PLANS[plan].exam_mode) { ... }
|
|
```
|
|
|
|
**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()
|
|
}
|
|
```
|
|
|
|
Pourquoi ? Voir ADR 005.
|
|
|
|
### Règle B — Appels API
|
|
|
|
**Interdit :**
|
|
```typescript
|
|
fetch('https://api.expria.app/simulations') // oubli du token, pas de retry
|
|
supabase.from('productions').select() // contourne le backend
|
|
```
|
|
|
|
**Obligatoire :**
|
|
```typescript
|
|
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.
|
|
|
|
```typescript
|
|
// ❌ Dans une page
|
|
{user.plan === 'free' && productions.length >= 5 && <Paywall />}
|
|
```
|
|
|
|
**Obligatoire :** extraire dans `entities/` :
|
|
|
|
```typescript
|
|
// 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()` (via `auth-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
|
|
|
|
1. **Lire** `ARCHITECTURE.md` + `DEVELOPMENT_PRINCIPLES.md` + `PLANS_TARIFAIRES.md`
|
|
2. **Plan** avant code — tu produis un plan détaillé, tu attends validation
|
|
3. **Code** — maximum 3 fichiers par étape
|
|
4. **Test** — `npm run typecheck && npm run test` vert
|
|
5. **Golden Dataset** — rejouer les tests manuels concernés
|
|
6. **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é :
|
|
|
|
| Email | 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
|
|
|
|
1. Lire le document de référence concerné (`ARCHITECTURE.md`, `DEVELOPMENT_PRINCIPLES.md`, ADRs)
|
|
2. Chercher un pattern existant dans le code (grep sur le nom de la feature)
|
|
3. Vérifier les parcours utilisateurs (`PARCOURS_UTILISATEURS.md`)
|
|
4. 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 technique
|
|
- `docs/DEVELOPMENT_PRINCIPLES.md` — règles de dev
|
|
- `docs/SECURITY.md` — sécurité + patterns interdits
|
|
- `docs/PLANS_TARIFAIRES.md` — source de vérité des plans
|
|
- `docs/PARCOURS_UTILISATEURS.md` — parcours détaillés par plan
|
|
- `docs/GOLDEN_DATASET.md` — tests manuels
|
|
- `docs/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
|
|
|
|
- [React Router v6](https://reactrouter.com/en/main)
|
|
- [TanStack Query](https://tanstack.com/query/latest)
|
|
- [shadcn/ui](https://ui.shadcn.com/)
|
|
- [Tailwind CSS](https://tailwindcss.com/)
|
|
- [Vitest](https://vitest.dev/)
|
|
- [Vite](https://vitejs.dev/)
|
|
|
|
---
|
|
|
|
## 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 et `simulations_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 |
|