From 38777796aa626e1f90c78df6563ca88203ec21ab Mon Sep 17 00:00:00 2001 From: Hermann_Kitio Date: Sat, 18 Apr 2026 02:09:46 +0300 Subject: [PATCH] =?UTF-8?q?feat(auth):=20useAuth=20+=20ProtectedRoute=20+?= =?UTF-8?q?=20signUp=20dans=20auth-client=20(Sprint=201=20=C3=A9tape=202)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/skills/frontend-design/SKILL.md | 42 + .gitignore | 3 + design-reference/direction-h-dark.html | 927 ++++++++++++++++++ .../direction-h-juste-milieu.html | 921 +++++++++++++++++ docs/DESIGN_SYSTEM.md | 343 +++++++ docs/adr/006-stack-versions-2026.md | 104 +- index.html | 6 + .../auth/components/ProtectedRoute.tsx | 42 + src/features/auth/hooks/useAuth.ts | 54 + .../design-system/DesignSystemPage.tsx | 74 +- src/index.css | 96 +- src/shared/components/Logo.tsx | 4 +- src/shared/components/ui/avatar.tsx | 61 +- src/shared/components/ui/dialog.tsx | 54 +- src/shared/components/ui/input.tsx | 14 +- src/shared/components/ui/label.tsx | 15 +- src/shared/components/ui/progress.tsx | 11 +- src/shared/components/ui/separator.tsx | 12 +- src/shared/lib/auth-client.ts | 28 +- 19 files changed, 2620 insertions(+), 191 deletions(-) create mode 100644 .cursor/skills/frontend-design/SKILL.md create mode 100644 design-reference/direction-h-dark.html create mode 100644 design-reference/direction-h-juste-milieu.html create mode 100644 docs/DESIGN_SYSTEM.md create mode 100644 src/features/auth/components/ProtectedRoute.tsx create mode 100644 src/features/auth/hooks/useAuth.ts diff --git a/.cursor/skills/frontend-design/SKILL.md b/.cursor/skills/frontend-design/SKILL.md new file mode 100644 index 0000000..600b6db --- /dev/null +++ b/.cursor/skills/frontend-design/SKILL.md @@ -0,0 +1,42 @@ +--- +name: frontend-design +description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics. +license: Complete terms in LICENSE.txt +--- + +This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices. + +The user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints. + +## Design Thinking + +Before coding, understand the context and commit to a BOLD aesthetic direction: +- **Purpose**: What problem does this interface solve? Who uses it? +- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction. +- **Constraints**: Technical requirements (framework, performance, accessibility). +- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember? + +**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity. + +Then implement working code (HTML/CSS/JS, React, Vue, etc.) that is: +- Production-grade and functional +- Visually striking and memorable +- Cohesive with a clear aesthetic point-of-view +- Meticulously refined in every detail + +## Frontend Aesthetics Guidelines + +Focus on: +- **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font. +- **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes. +- **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise. +- **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density. +- **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic. Apply creative forms like gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, and grain overlays. + +NEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts and component patterns, and cookie-cutter design that lacks context-specific character. + +Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices (Space Grotesk, for example) across generations. + +**IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well. + +Remember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision. \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4591520..1862d22 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ dist-ssr # Claude Code local config .claude/ + +# Exploration DA temporaire — supprimer une fois la direction choisie +design-exploration/ diff --git a/design-reference/direction-h-dark.html b/design-reference/direction-h-dark.html new file mode 100644 index 0000000..4a52f4a --- /dev/null +++ b/design-reference/direction-h-dark.html @@ -0,0 +1,927 @@ + + + + + +Expria — Tableau de bord · Direction H : Mode sombre + + + + + +
+
+
+
Direction H — Mode sombre
+
Inversion réfléchie de la version claire. Fond bleu-nuit désaturé, cards qui ressortent, bleu Expria remonté en luminance pour rester lisible.
+
+
Mode sombre
+
+
+ +
+ + + + + +
+ + + + +
+ +
+
Simulations restantes
+
3 / 5
+
+
Plan Découverte — renouvellement à chaque upgrade
+
+ +
+
Niveau estimé
+
NCLC 8
+
+
Moyenne des 3 dernières simulations · Objectif NCLC 9
+
+ +
+
Plan actuel
+
Découverte
+
Débloquez des simulations illimitées, le Mode Examen et le T2 Live avec Standard ou Premium.
+ + Passer à Standard + + +
+ +
+ + +
+ + +
+
Simulations récentes
+
Vos 3 dernières corrections
+ +
+ +
+
+ +
+
+
Expression écrite — Tâche 2
+
Aujourd'hui · 09:42
+
+
NCLC 9
+
16/20
+
+ +
+
+ +
+
+
Expression orale — Tâche 1
+
Il y a 2 jours
+
+
NCLC 8
+
14/20
+
+ +
+
+ +
+
+
Expression écrite — Tâche 3
+
Il y a 5 jours
+
+
NCLC 9
+
15/20
+
+ +
+
+ + +
+
Prochaine étape recommandée
+
Travaillez la tâche 2 à l'oral
+
+ Votre dernier score à l'EO T2 (14/20) est en dessous de votre moyenne. Une simulation de 10 minutes suffit pour consolider. +
+ +
+
+
Durée
+
10 min
+
+
+
Difficulté
+
Modérée
+
+
+ + +
+ +
+ + +
+
Palette de la direction H — mode sombre
+
+
+
+
#0D1220
+
Fond principal
+
+
+
+
#182238
+
Cards surface
+
+
+
+
#5B7FFF
+
Bleu Expria — remonté
+
+
+
+
#27324B
+
Hairlines
+
+
+
+
#F1F4FA
+
Titres
+
+
+
+
#A8B2C7
+
Corps secondaire
+
+
+
+
#3DD68C
+
Succès
+
+
+
+
#F5B849
+
Attention
+
+
+
+ +
+ +
+ + + diff --git a/design-reference/direction-h-juste-milieu.html b/design-reference/direction-h-juste-milieu.html new file mode 100644 index 0000000..87a062d --- /dev/null +++ b/design-reference/direction-h-juste-milieu.html @@ -0,0 +1,921 @@ + + + + + +Expria — Tableau de bord · Direction H : Juste milieu + + + + + +
+
+
+
Direction H — Juste milieu
+
Entre Boréal (trop blanc) et Cadence (trop sombre). Fond gris-bleuté, cards blanches en relief, bleu Expria pivot, accents bleu-nuit.
+
+
Version recommandée
+
+
+ +
+ + + + + +
+ + + + +
+ +
+
Simulations restantes
+
3 / 5
+
+
Plan Découverte — renouvellement à chaque upgrade
+
+ +
+
Niveau estimé
+
NCLC 8
+
+
Moyenne des 3 dernières simulations · Objectif NCLC 9
+
+ +
+
Plan actuel
+
Découverte
+
Débloquez des simulations illimitées, le Mode Examen et le T2 Live avec Standard ou Premium.
+ + Passer à Standard + + +
+ +
+ + +
+ + +
+
Simulations récentes
+
Vos 3 dernières corrections
+ +
+ +
+
+ +
+
+
Expression écrite — Tâche 2
+
Aujourd'hui · 09:42
+
+
NCLC 9
+
16/20
+
+ +
+
+ +
+
+
Expression orale — Tâche 1
+
Il y a 2 jours
+
+
NCLC 8
+
14/20
+
+ +
+
+ +
+
+
Expression écrite — Tâche 3
+
Il y a 5 jours
+
+
NCLC 9
+
15/20
+
+ +
+
+ + +
+
Prochaine étape recommandée
+
Travaillez la tâche 2 à l'oral
+
+ Votre dernier score à l'EO T2 (14/20) est en dessous de votre moyenne. Une simulation de 10 minutes suffit pour consolider. +
+ +
+
+
Durée
+
10 min
+
+
+
Difficulté
+
Modérée
+
+
+ + +
+ +
+ + +
+
Palette de la direction H
+
+
+
+
#EEF2F8
+
Fond principal
+
+
+
+
#FFFFFF
+
Cards (blanc franc)
+
+
+
+
#1B4FD8
+
Bleu Expria — pivot
+
+
+
+
#0B1F5C
+
Bleu nuit — premium
+
+
+
+
#0F172A
+
Titres
+
+
+
+
#475569
+
Corps
+
+
+
+
#0E9F6E
+
Succès
+
+
+
+
#C77A00
+
Attention
+
+
+
+ +
+ +
+ + + diff --git a/docs/DESIGN_SYSTEM.md b/docs/DESIGN_SYSTEM.md new file mode 100644 index 0000000..3a16386 --- /dev/null +++ b/docs/DESIGN_SYSTEM.md @@ -0,0 +1,343 @@ +# DESIGN_SYSTEM.md — Expria Frontend + +> **Document de référence — Version 1.0 — Sprint 1** +> Source de vérité unique pour l'identité visuelle, les tokens de design et les primitives UI. +> Toute décision de DA doit être consignée ici avant d'être implémentée. + +--- + +## 1. Direction artistique — verrouillée + +**Nom :** Boréal +**Positionnement :** institutionnel chaleureux, premium sans flashy, sérieux sans austère. +**Référence mentale :** Stripe Dashboard, Linear, Notion Desktop — mais réchauffé d'un cran. + +### Parti pris fondateurs + +| Principe | Décision | +|---|---| +| Mode canonique Sprint 1 | **Clair uniquement** (light chaud) | +| Mode sombre | Prévu Sprint 2+ (tokens écrits dual-theme-ready dès J1) | +| Fond principal | `#F4F2EC` (off-white calibré, ni froid ni saturé) | +| Surfaces élevées | Blanc pur `#FFFFFF` pour contraste subtil avec le fond | +| Bleu de marque | `#1B4FD8` **sacro-saint** en mode clair — aucune variation | +| Bleu mode sombre | `#7C9BFF` **prévu** pour Sprint 2+ (pattern Apple system colors) | +| Accent chaleureux | Aucun en Sprint 1 — le bleu porte toute l'intentionnalité | +| Angles | Rayons généreux mais retenus : 8 / 12 / 16 px | +| Ombres | Minimales. 1 ombre-card unique, très subtile. Hairlines 1px privilégiées. | +| Animations | 150–200 ms, `ease-out`, respect de `prefers-reduced-motion` | +| Icônes | SVG inline dans `shared/ui/icons/` — aucune dépendance externe | +| Typographie | Plus Jakarta Sans (via `font-family`, fallback système) | + +### Ce qu'on refuse explicitement + +- Gradients criards (le seul acceptable : aucun). +- Glassmorphism ou `backdrop-filter` généralisé — réservé à la bottom nav mobile si besoin. +- Emojis dans les éléments interactifs ou les labels fonctionnels. +- Ombres lourdes, "drop shadows" style Material Design 2. +- Plus de 2 niveaux d'élévation visuelle (fond → card → modal). +- Toute police de display fantaisiste, serif décorative ou condensée. +- Les motifs SaaS génériques : illustrations 3D, dégradés violet-rose, glass blobs. + +--- + +## 2. Tokens — `src/index.css` + +Remplacer intégralement le contenu actuel (`@import 'tailwindcss';`) par le bloc ci-dessous. Tailwind 4 lit automatiquement les tokens déclarés dans `@theme`. + +```css +@import 'tailwindcss'; + +@theme { + /* ----- Brand ------------------------------------------------------- */ + --color-brand: #1B4FD8; + --color-brand-hover: #1744B8; + --color-brand-active: #13379C; + --color-brand-soft: #E7EDFC; + --color-brand-ink: #FFFFFF; + + /* ----- Surfaces (light — Sprint 1) --------------------------------- */ + --color-bg: #F4F2EC; + --color-surface: #FBFAF6; + --color-surface-raised: #FFFFFF; + --color-surface-sunken: #EEECE4; + + /* ----- Ink (texte) ------------------------------------------------- */ + --color-ink-primary: #0F1220; + --color-ink-secondary: #4A4F5E; + --color-ink-tertiary: #8A8F9E; + --color-ink-inverse: #FBFAF6; + + /* ----- Borders & dividers ------------------------------------------ */ + --color-border: #E3E0D6; + --color-border-strong: #C9C5B7; + --color-border-focus: #1B4FD8; + + /* ----- Feedback ---------------------------------------------------- */ + --color-success: #1F7A4C; + --color-success-soft: #E3F2EA; + --color-warning: #B8741A; + --color-warning-soft: #F7EEDF; + --color-danger: #B8322D; + --color-danger-soft: #F7E1DF; + + /* ----- Typographie ------------------------------------------------- */ + --font-sans: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", sans-serif; + --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace; + + /* Échelle : mobile-first, les tailles desktop se gèrent via utilities Tailwind */ + --text-xs: 11px; + --text-sm: 13px; + --text-base: 14px; + --text-md: 15px; + --text-lg: 17px; + --text-xl: 20px; + --text-2xl: 24px; + --text-3xl: 32px; + --text-display: 40px; + + /* ----- Rayons ------------------------------------------------------ */ + --radius-xs: 6px; + --radius-sm: 8px; + --radius-md: 12px; + --radius-lg: 16px; + --radius-xl: 20px; + --radius-pill: 999px; + + /* ----- Ombres ------------------------------------------------------ */ + --shadow-card: 0 1px 2px rgba(15, 18, 32, 0.04), 0 1px 8px rgba(15, 18, 32, 0.03); + --shadow-raised: 0 4px 16px rgba(15, 18, 32, 0.06), 0 1px 2px rgba(15, 18, 32, 0.04); + --shadow-focus: 0 0 0 3px rgba(27, 79, 216, 0.18); +} + +/* --------------------------------------------------------------------- */ +/* Globals — reset minimal, fond chaud par défaut */ +/* --------------------------------------------------------------------- */ + +html, body { + background: var(--color-bg); + color: var(--color-ink-primary); + font-family: var(--font-sans); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-variant-numeric: tabular-nums; +} + +*:focus-visible { + outline: none; + box-shadow: var(--shadow-focus); + border-radius: var(--radius-xs); +} + +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0ms !important; + transition-duration: 0ms !important; + } +} + +/* --------------------------------------------------------------------- */ +/* TODO Sprint 2+ — Dark theme (Cadence recalibré) */ +/* --------------------------------------------------------------------- */ +/* +@theme { + --color-bg-dark: #0F1320; + --color-surface-dark: #171B2B; + --color-surface-raised-dark: #1E2338; + --color-ink-primary-dark: #E6E4DB; + --color-ink-secondary-dark: #9CA0AC; + --color-brand-dark: #7C9BFF; + --color-brand-hover-dark: #9AB3FF; + --color-border-dark: #2A3048; +} +*/ +``` + +### Règles d'usage des tokens + +1. **Aucune valeur hexadécimale en dur** dans les composants. Toute couleur passe par un token. +2. **Nommage sémantique obligatoire.** On écrit `bg-surface`, pas `bg-gray-50`. +3. Si un cas d'usage exige une teinte hors charte, **le documenter ici avant de l'ajouter**. Pas de token orphelin. +4. Les tokens marqués `*-dark` ne sont **pas utilisés en Sprint 1**. Leur présence en commentaire est intentionnelle pour faciliter la reprise Sprint 2+. + +--- + +## 3. Typographie + +| Usage | Taille | Poids | Tracking | Ligne | Token | +|---|---|---|---|---|---| +| Display (NCLC hero) | 40px | 700 | -0.02em | 1.0 | `text-display` | +| H1 page | 32px | 700 | -0.02em | 1.1 | `text-3xl` | +| H2 section | 24px | 700 | -0.015em | 1.2 | `text-2xl` | +| H3 card title | 20px | 700 | -0.01em | 1.3 | `text-xl` | +| Lead / intro | 17px | 500 | -0.005em | 1.5 | `text-lg` | +| Body | 14px | 400 | 0 | 1.6 | `text-base` | +| Body renforcé | 15px | 500 | 0 | 1.55 | `text-md` | +| Small / meta | 13px | 400 | 0 | 1.5 | `text-sm` | +| Eyebrow / label | 11px | 600 | 0.1em (uppercase) | 1.4 | `text-xs` | + +**Règles :** +- Tout nombre métier (score 16/20, NCLC 7,5, compteur 3/5) est rendu en `font-variant-numeric: tabular-nums`. Hérité par le body, mais à re-spécifier explicitement sur les tables et listes. +- `Plus Jakarta Sans` est déclarée en `font-family` avec fallback système. **Aucune webfont chargée** tant qu'on n'a pas validé la stratégie self-hosting (décision reportée). +- Les chiffres français utilisent la **virgule** comme séparateur décimal (`7,5`, jamais `7.5`). + +--- + +## 4. Primitives UI — Sprint 1 + +À créer dans `src/shared/ui/` en FSD, une primitive par dossier (`button/`, `card/`, etc.) avec `index.ts` pour l'export. + +### Inventaire minimal + +| Composant | Variants | Usage dashboard | +|---|---|---| +| `Button` | `primary` / `secondary` / `ghost` / `upgrade` | CTA "Nouvelle simulation", "Passer au plan Standard", actions tertiaires | +| `Card` | `default` / `raised` / `interactive` | Cadre métriques, item simulation, recommandation | +| `MetricCard` | `default` / `hero` (pour le NCLC) | Bloc NCLC, compteur simulations, dernier score | +| `ProgressBar` | `default` | Progression vers NCLC 9 | +| `Badge` | `plan` / `nclc` / `neutral` | Plan actuel dans header, niveau NCLC par simulation | +| `Sidebar` | — | Nav desktop (≥ 1024px) | +| `BottomNav` | — | Nav mobile (< 1024px), 4 items max | +| `PageHeader` | — | Greeting + plan pill | +| `SectionHeader` | — | Titre de section + action optionnelle | + +### Règles d'implémentation + +- Chaque primitive **accepte `className`** en plus de ses props typées, pour overrides ponctuels. +- Chaque primitive **expose ses props via un type exporté** (`ButtonProps`, `CardProps`, etc.). +- Aucune primitive ne contient de logique métier ou d'appel API. Elles reçoivent tout par props. +- Les icônes sont passées par une prop `icon` acceptant un `ReactNode`, jamais par nom de string. + +--- + +## 5. Layout dashboard — spécification + +Les primitives ci-dessus s'assemblent dans `src/features/dashboard/` et `src/pages/dashboard/`. + +### Structure sémantique + +``` + + (≥ 1024px) +
+ (greeting + plan) +
+ (NCLC estimé + progression) + (simulations restantes) + (dernier score) +
+
+ (< 1024px) + +``` + +### Breakpoints + +| Breakpoint | Comportement | +|---|---| +| `< 1024px` | Mono-colonne, `BottomNav` fixe en bas, padding horizontal 20px | +| `≥ 1024px` | Sidebar 240px + contenu centré 860px max, padding horizontal 32px | +| `≥ 1440px` | Contenu centré 920px max (pas d'élargissement excessif) | + +### Densité verticale + +- Padding vertical section : 24px mobile, 32px desktop. +- Gap inter-cards : 12px mobile, 16px desktop. +- Marge sous `PageHeader` : 20px mobile, 28px desktop. + +--- + +## 6. Données mock — Sprint 1 + +Avant branchement API, fournir les données via `src/shared/api/mock/dashboard.ts`. Données crédibles, françaises, alignées sur l'audience réelle. + +```typescript +export const mockDashboard = { + user: { + firstName: 'Yacine', + plan: 'decouverte' as const, + planLabel: 'Plan Découverte', + }, + metrics: { + nclcEstimated: 7.5, + nclcTarget: 9, + simulationsUsed: 2, + simulationsQuota: 5, + lastScore: { value: 16, max: 20, type: 'ecrit' as const }, + }, + recentSimulations: [ + { id: 's-001', type: 'ecrit', relativeDate: 'il y a 2 jours', score: 16, max: 20, nclc: 8 }, + { id: 's-002', type: 'oral', relativeDate: 'il y a 5 jours', score: 14, max: 20, nclc: 7 }, + { id: 's-003', type: 'ecrit', relativeDate: 'il y a 1 semaine', score: 15, max: 20, nclc: 7 }, + ], + nextStep: { + title: 'Cible une simulation orale cette semaine', + body: 'Ton écrit est solide (NCLC 8). L\'oral reste à consolider pour sécuriser ton NCLC 9.', + action: { label: 'Démarrer Expression Orale', to: '/simulation/orale' }, + }, +} as const; +``` + +**Règles contenu :** +- Aucun "Lorem ipsum", aucune date absolue — relatif uniquement (`il y a X jours`). +- Les prénoms mocks reflètent l'audience : Yacine, Aminata, Kenza, Bilal, Fatou, Kévin (Canada). +- Les scores suivent une progression crédible (pas de 20/20 ni de 5/20). + +--- + +## 7. Accessibilité — plancher Sprint 1 + +- Contraste minimum **WCAG AA** sur tous les couples texte/fond (vérifié pour la palette ci-dessus). +- Tous les éléments interactifs ont un `:focus-visible` avec `--shadow-focus` (halo bleu 3px). +- Les icônes purement décoratives portent `aria-hidden="true"`. +- Les icônes fonctionnelles (sans label visible) portent `aria-label`. +- Les landmarks sémantiques sont utilisés : `
`, `