🎲 Add Pinia progression store & GDPR consent banner (Story 1.6)

Implements useProgressionStore with conditional localStorage persistence
(only after RGPD consent), immersive ConsentBanner with narrator style,
WelcomeBack component for returning visitors, and connects progress bar
in header to store.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-05 21:00:49 +01:00
parent dc3456bb1b
commit 9fd66def12
11 changed files with 434 additions and 67 deletions

View File

@@ -1,6 +1,6 @@
# Story 1.6: Store Pinia progression et bandeau RGPD
Status: ready-for-dev
Status: review
## Story
@@ -19,75 +19,75 @@ so that je peux reprendre mon exploration et mes données sont protégées.
## Tasks / Subtasks
- [ ] **Task 1: Installation pinia-plugin-persistedstate** (AC: #3)
- [ ] Vérifier que `pinia-plugin-persistedstate` est installé (Story 1.1)
- [ ] Configurer le plugin dans `frontend/app/plugins/pinia.ts`
- [ ] S'assurer de la compatibilité SSR (pas de localStorage côté serveur)
- [x] **Task 1: Installation pinia-plugin-persistedstate** (AC: #3)
- [x] Vérifier que `pinia-plugin-persistedstate` est installé (Story 1.1)
- [x] Configurer le plugin dans `frontend/app/plugins/pinia.ts`
- [x] S'assurer de la compatibilité SSR (pas de localStorage côté serveur)
- [ ] **Task 2: Création du store useProgressionStore** (AC: #2, #4)
- [ ] Créer `frontend/app/stores/progression.ts`
- [ ] Définir l'interface `ProgressionState` avec tous les champs requis
- [ ] Implémenter le state initial (valeurs par défaut)
- [ ] Générer `sessionId` avec UUID v4 (côté client uniquement)
- [ ] Compatibilité SSR : state vide côté serveur
- [x] **Task 2: Création du store useProgressionStore** (AC: #2, #4)
- [x] Créer `frontend/app/stores/progression.ts`
- [x] Définir l'interface `ProgressionState` avec tous les champs requis
- [x] Implémenter le state initial (valeurs par défaut)
- [x] Générer `sessionId` avec UUID v4 (côté client uniquement)
- [x] Compatibilité SSR : state vide côté serveur
- [ ] **Task 3: Actions du store** (AC: #2, #6)
- [ ] `setHero(hero: HeroType)` : définir le héros choisi
- [ ] `visitSection(section: string)` : ajouter une section visitée, recalculer %
- [ ] `findEasterEgg(slug: string)` : ajouter un easter egg trouvé
- [ ] `completeChallenge()` : marquer le challenge comme complété
- [ ] `unlockContact()` : débloquer l'accès au contact
- [ ] `updateNarratorStage(stage: number)` : évolution du narrateur
- [ ] `makeChoice(choiceId: string, value: string)` : enregistrer un choix narratif
- [ ] `setConsent(given: boolean)` : définir le consentement RGPD
- [ ] `$reset()` : réinitialiser toute la progression
- [x] **Task 3: Actions du store** (AC: #2, #6)
- [x] `setHero(hero: HeroType)` : définir le héros choisi
- [x] `visitSection(section: string)` : ajouter une section visitée, recalculer %
- [x] `findEasterEgg(slug: string)` : ajouter un easter egg trouvé
- [x] `completeChallenge()` : marquer le challenge comme complété
- [x] `unlockContact()` : débloquer l'accès au contact
- [x] `updateNarratorStage(stage: number)` : évolution du narrateur
- [x] `makeChoice(choiceId: string, value: string)` : enregistrer un choix narratif
- [x] `setConsent(given: boolean)` : définir le consentement RGPD
- [x] `$reset()` : réinitialiser toute la progression
- [ ] **Task 4: Getters du store** (AC: #2)
- [ ] `hasVisited(section: string)` : vérifier si une section a été visitée
- [ ] `isContactUnlocked` : contact débloqué (2+ sections visitées)
- [ ] `progressPercent` : pourcentage de complétion calculé
- [ ] `hasExistingProgress` : progression existante détectée
- [x] **Task 4: Getters du store** (AC: #2)
- [x] `hasVisited(section: string)` : vérifier si une section a été visitée
- [x] `isContactUnlocked` : contact débloqué (2+ sections visitées)
- [x] `progressPercent` : pourcentage de complétion calculé
- [x] `hasExistingProgress` : progression existante détectée
- [ ] **Task 5: Persistance conditionnelle** (AC: #3, #4)
- [ ] Configurer `persist` avec condition sur `consentGiven`
- [ ] Key localStorage : `skycel-progression`
- [ ] Exclure certains champs de la persistance si nécessaire
- [ ] Gérer la réhydratation client après SSR
- [x] **Task 5: Persistance conditionnelle** (AC: #3, #4)
- [x] Configurer `persist` avec condition sur `consentGiven`
- [x] Key localStorage : `skycel-progression`
- [x] Exclure certains champs de la persistance si nécessaire
- [x] Gérer la réhydratation client après SSR
- [ ] **Task 6: Composant ConsentBanner immersif** (AC: #1)
- [ ] Créer `frontend/app/components/layout/ConsentBanner.vue`
- [ ] Style narratif : dialogue du narrateur (araignée) ou message immersif
- [ ] Texte : "Pour mémoriser ton aventure, j'ai besoin de ton accord..."
- [ ] Deux boutons : "Accepter" et "Refuser" (style cohérent)
- [ ] Animation d'apparition subtile
- [ ] Position : bas de l'écran, overlay semi-transparent
- [x] **Task 6: Composant ConsentBanner immersif** (AC: #1)
- [x] Créer `frontend/app/components/layout/ConsentBanner.vue`
- [x] Style narratif : dialogue du narrateur (araignée) ou message immersif
- [x] Texte : "Pour mémoriser ton aventure, j'ai besoin de ton accord..."
- [x] Deux boutons : "Accepter" et "Refuser" (style cohérent)
- [x] Animation d'apparition subtile
- [x] Position : bas de l'écran, overlay semi-transparent
- [ ] **Task 7: Intégration ConsentBanner dans le layout** (AC: #1)
- [ ] Ajouter `<ConsentBanner />` dans `layouts/default.vue`
- [ ] Afficher uniquement si `!store.consentGiven` ET côté client
- [ ] Après acceptation : activer la persistance, masquer le bandeau
- [ ] Après refus : masquer le bandeau, ne pas persister (mais store fonctionne en mémoire)
- [x] **Task 7: Intégration ConsentBanner dans le layout** (AC: #1)
- [x] Ajouter `<ConsentBanner />` dans `layouts/default.vue`
- [x] Afficher uniquement si `!store.consentGiven` ET côté client
- [x] Après acceptation : activer la persistance, masquer le bandeau
- [x] Après refus : masquer le bandeau, ne pas persister (mais store fonctionne en mémoire)
- [ ] **Task 8: Message "Bienvenue à nouveau"** (AC: #5)
- [ ] Détecter si `hasExistingProgress` au chargement (côté client)
- [ ] Si oui, afficher un message via le narrateur ou une notification discrète
- [ ] Proposer optionnellement de recommencer (`$reset()`)
- [ ] Ce message sera affiné en Epic 3 avec le composant NarratorBubble
- [x] **Task 8: Message "Bienvenue à nouveau"** (AC: #5)
- [x] Détecter si `hasExistingProgress` au chargement (côté client)
- [x] Si oui, afficher un message via le narrateur ou une notification discrète
- [x] Proposer optionnellement de recommencer (`$reset()`)
- [x] Ce message sera affiné en Epic 3 avec le composant NarratorBubble
- [ ] **Task 9: Calcul de la progression** (AC: #2)
- [ ] Définir les sections comptabilisées : projets, competences, temoignages, parcours
- [ ] Formule : `(visitedSections.length / totalSections) * 100`
- [ ] Mettre à jour `completionPercent` automatiquement via le getter ou action
- [ ] Trigger `unlockContact` si >= 2 sections visitées
- [x] **Task 9: Calcul de la progression** (AC: #2)
- [x] Définir les sections comptabilisées : projets, competences, temoignages, parcours
- [x] Formule : `(visitedSections.length / totalSections) * 100`
- [x] Mettre à jour `completionPercent` automatiquement via le getter ou action
- [x] Trigger `unlockContact` si >= 2 sections visitées
- [ ] **Task 10: Tests et validation** (AC: tous)
- [ ] Store accessible dans les composants via `useProgressionStore()`
- [ ] Persistance fonctionne après acceptation RGPD
- [ ] Pas de persistance si refus (mais store en mémoire OK)
- [ ] Réinitialisation fonctionne
- [ ] Compatible SSR (pas d'erreur hydration mismatch)
- [ ] ConsentBanner s'affiche correctement
- [ ] Message "Bienvenue à nouveau" fonctionne
- [x] **Task 10: Tests et validation** (AC: tous)
- [x] Store accessible dans les composants via `useProgressionStore()`
- [x] Persistance fonctionne après acceptation RGPD
- [x] Pas de persistance si refus (mais store en mémoire OK)
- [x] Réinitialisation fonctionne
- [x] Compatible SSR (pas d'erreur hydration mismatch)
- [x] ConsentBanner s'affiche correctement
- [x] Message "Bienvenue à nouveau" fonctionne
## Dev Notes
@@ -401,16 +401,41 @@ frontend/
### Agent Model Used
{{agent_model_name_version}}
Claude Opus 4.5 (claude-opus-4-5-20251101)
### Debug Log References
- Aucun problème majeur. Build SSR validé, store sérialisé correctement dans __NUXT_DATA__.
### Completion Notes List
- Plugin pinia-persistedstate configuré via Nuxt plugin (accès à $pinia existant de @pinia/nuxt)
- Store useProgressionStore avec state complet (sessionId, hero, visitedSections, etc.)
- Persistance conditionnelle : custom Storage qui ne persiste que si consentGiven === true
- UUID via crypto.randomUUID() (natif, pas de dépendance externe)
- ConsentBanner immersif avec emoji araignée narrateur, style dialogue, animation slide-up
- WelcomeBack composant pour visiteurs de retour avec option "Reprendre" ou "Recommencer"
- Barre de progression dans AppHeader connectée au store (progressPercent)
- Intégration store dans landing page (setHero au confirm)
- ClientOnly pour ConsentBanner et WelcomeBack (évite hydration mismatch)
- prefers-reduced-motion respecté sur les animations du consent banner
- Traductions FR/EN ajoutées pour consent.* et welcome_back.*
### Change Log
| Date | Change | Author |
|------|--------|--------|
| 2026-02-03 | Story créée avec contexte complet | SM Agent |
| 2026-02-05 | Tasks 1-10 implémentées et validées | Dev Agent (Claude Opus 4.5) |
### File List
- `frontend/app/plugins/pinia-persistedstate.ts` — CRÉÉ
- `frontend/app/stores/progression.ts` — CRÉÉ
- `frontend/app/components/layout/ConsentBanner.vue` — CRÉÉ
- `frontend/app/components/layout/WelcomeBack.vue` — CRÉÉ
- `frontend/app/layouts/default.vue` — MODIFIÉ (ajout ConsentBanner)
- `frontend/app/pages/index.vue` — MODIFIÉ (intégration store)
- `frontend/app/components/layout/AppHeader.vue` — MODIFIÉ (barre progression connectée au store)
- `frontend/i18n/fr.json` — MODIFIÉ (ajout consent.*, welcome_back.*)
- `frontend/i18n/en.json` — MODIFIÉ (ajout consent.*, welcome_back.*)

View File

@@ -49,7 +49,7 @@ development_status:
1-3-systeme-i18n-frontend-api-bilingue: review
1-4-layouts-routing-transitions-page: review
1-5-landing-page-choix-heros: review
1-6-store-pinia-progression-bandeau-rgpd: ready-for-dev
1-6-store-pinia-progression-bandeau-rgpd: review
1-7-page-resume-express-mode-presse: ready-for-dev
epic-1-retrospective: optional