# Story 4.3: Chemins narratifs différenciés Status: ready-for-dev ## Story As a visiteur, I want que mes choix aient un impact visible sur mon parcours, so that je sens que mon expérience est vraiment personnalisée. ## Acceptance Criteria 1. **Given** le visiteur fait des choix tout au long de l'aventure **When** il navigue entre les zones **Then** 2-3 points de choix binaires créent 4-8 parcours possibles 2. **And** chaque choix est enregistré dans `choices` du store 3. **And** l'ordre suggéré des zones varie selon le chemin choisi 4. **And** les textes du narrateur s'adaptent au chemin (transitions contextuelles) 5. **And** tous les chemins permettent de visiter tout le contenu 6. **And** tous les chemins mènent au contact (pas de "mauvais" choix) 7. **And** le `currentPath` du store reflète le chemin actuel 8. **And** à la fin de chaque zone, le narrateur propose un choix vers la suite ## Tasks / Subtasks - [ ] **Task 1: Définir l'arbre des chemins** (AC: #1, #5, #6) - [ ] Créer `frontend/app/data/narrativePaths.ts` - [ ] Définir 2-3 points de choix créant 4-8 parcours - [ ] S'assurer que tous les chemins visitent toutes les zones - [ ] S'assurer que tous les chemins mènent au contact - [ ] **Task 2: Créer le composable useNarrativePath** (AC: #2, #3, #7) - [ ] Créer `frontend/app/composables/useNarrativePath.ts` - [ ] Calculer le chemin actuel basé sur les choix - [ ] Exposer la prochaine zone suggérée - [ ] Exposer les zones restantes dans l'ordre - [ ] **Task 3: Ajouter les textes de transition contextuels** (AC: #4) - [ ] Créer des contextes spécifiques : `transition_after_projects_to_skills`, etc. - [ ] Variantes selon le chemin pris - [ ] Commentaires du narrateur sur les choix précédents - [ ] **Task 4: Intégrer les choix après chaque zone** (AC: #8) - [ ] Composant `ZoneEndChoice.vue` affiché à la fin de chaque page de zone - [ ] Proposer les options de destination selon le chemin - [ ] Utiliser ChoiceCards pour la présentation - [ ] **Task 5: Mettre à jour le store** (AC: #2, #7) - [ ] Ajouter `currentPath` computed au store - [ ] Ajouter `suggestedNextZone` computed - [ ] Méthode pour obtenir le choix à un point donné - [ ] **Task 6: Créer l'API pour les transitions contextuelles** (AC: #4) - [ ] Endpoint `/api/narrator/transition-contextual` - [ ] Paramètres : from_zone, to_zone, path_choices - [ ] Retourner un texte adapté au contexte - [ ] **Task 7: Tests et validation** - [ ] Tester tous les chemins possibles (4-8) - [ ] Vérifier que tous mènent au contact - [ ] Valider les textes contextuels - [ ] Tester la suggestion de zone suivante ## Dev Notes ### Arbre des chemins narratifs ```typescript // frontend/app/data/narrativePaths.ts // Points de choix dans l'aventure export const NARRATIVE_CHOICE_POINTS = { // Point 1 : Après l'intro intro: { id: 'intro', options: ['projects', 'skills'], }, // Point 2 : Après la première zone after_first_zone: { id: 'after_first_zone', options: ['testimonials', 'journey'], }, // Point 3 : Après la deuxième zone after_second_zone: { id: 'after_second_zone', // Les options dépendent de ce qui reste }, } // Chemins possibles (4-8 combinaisons) // Format : intro_choice -> after_first -> after_second -> contact export const NARRATIVE_PATHS = [ // Chemin 1 : Projets → Témoignages → Compétences → Parcours → Contact ['projects', 'testimonials', 'skills', 'journey', 'contact'], // Chemin 2 : Projets → Témoignages → Parcours → Compétences → Contact ['projects', 'testimonials', 'journey', 'skills', 'contact'], // Chemin 3 : Projets → Parcours → Témoignages → Compétences → Contact ['projects', 'journey', 'testimonials', 'skills', 'contact'], // Chemin 4 : Projets → Parcours → Compétences → Témoignages → Contact ['projects', 'journey', 'skills', 'testimonials', 'contact'], // Chemin 5 : Compétences → Témoignages → Projets → Parcours → Contact ['skills', 'testimonials', 'projects', 'journey', 'contact'], // Chemin 6 : Compétences → Témoignages → Parcours → Projets → Contact ['skills', 'testimonials', 'journey', 'projects', 'contact'], // Chemin 7 : Compétences → Parcours → Témoignages → Projets → Contact ['skills', 'journey', 'testimonials', 'projects', 'contact'], // Chemin 8 : Compétences → Parcours → Projets → Témoignages → Contact ['skills', 'journey', 'projects', 'testimonials', 'contact'], ] // Mapper zone key -> route export const ZONE_ROUTES: Record = { projects: { fr: '/projets', en: '/en/projects' }, skills: { fr: '/competences', en: '/en/skills' }, testimonials: { fr: '/temoignages', en: '/en/testimonials' }, journey: { fr: '/parcours', en: '/en/journey' }, contact: { fr: '/contact', en: '/en/contact' }, } ``` ### Composable useNarrativePath ```typescript // frontend/app/composables/useNarrativePath.ts import { NARRATIVE_PATHS, ZONE_ROUTES } from '~/data/narrativePaths' export function useNarrativePath() { const progressionStore = useProgressionStore() const { locale } = useI18n() // Déterminer le chemin actuel basé sur les choix const currentPath = computed(() => { const choices = progressionStore.choices // Trouver le premier choix (intro) const introChoice = choices.find(c => c.id === 'intro_first_choice') if (!introChoice) return null const startZone = introChoice.value === 'choice_projects_first' ? 'projects' : 'skills' // Filtrer les chemins qui commencent par cette zone let possiblePaths = NARRATIVE_PATHS.filter(path => path[0] === startZone) // Affiner avec les choix suivants const afterFirstChoice = choices.find(c => c.id === 'after_first_zone') if (afterFirstChoice && possiblePaths.length > 1) { const secondZone = afterFirstChoice.value.includes('testimonials') ? 'testimonials' : 'journey' possiblePaths = possiblePaths.filter(path => path[1] === secondZone) } return possiblePaths[0] || null }) // Zone actuelle basée sur la route const currentZone = computed(() => { const route = useRoute() const path = route.path.toLowerCase() for (const [zone, routes] of Object.entries(ZONE_ROUTES)) { if (path.includes(routes.fr.slice(1)) || path.includes(routes.en.slice(4))) { return zone } } return null }) // Index de la zone actuelle dans le chemin const currentZoneIndex = computed(() => { if (!currentPath.value || !currentZone.value) return -1 return currentPath.value.indexOf(currentZone.value) }) // Prochaine zone suggérée const suggestedNextZone = computed(() => { if (!currentPath.value || currentZoneIndex.value === -1) return null const nextIndex = currentZoneIndex.value + 1 if (nextIndex >= currentPath.value.length) return null return currentPath.value[nextIndex] }) // Zones restantes à visiter const remainingZones = computed(() => { if (!currentPath.value) return [] const visited = progressionStore.visitedSections return currentPath.value.filter(zone => zone !== 'contact' && !visited.includes(zone as any) ) }) // Obtenir la route pour une zone function getZoneRoute(zone: string): string { const routes = ZONE_ROUTES[zone] if (!routes) return '/' return locale.value === 'fr' ? routes.fr : routes.en } // Générer le choix pour après la zone actuelle function getNextChoicePoint() { if (!remainingZones.value.length) { // Plus de zones, aller au contact return { id: 'go_to_contact', choices: [ { id: 'contact', textFr: 'Rencontrer le développeur', textEn: 'Meet the developer', icon: '📧', destination: getZoneRoute('contact'), zoneColor: '#fa784f', }, ], } } // Proposer les 2 prochaines zones const nextTwo = remainingZones.value.slice(0, 2) return { id: `after_${currentZone.value}`, questionFr: 'Où vas-tu ensuite ?', questionEn: 'Where to next?', choices: nextTwo.map(zone => ({ id: `choice_${zone}`, textFr: getZoneLabel(zone, 'fr'), textEn: getZoneLabel(zone, 'en'), icon: getZoneIcon(zone), destination: getZoneRoute(zone), zoneColor: getZoneColor(zone), })), } } return { currentPath, currentZone, suggestedNextZone, remainingZones, getZoneRoute, getNextChoicePoint, } } // Helpers function getZoneLabel(zone: string, locale: string): string { const labels: Record = { projects: { fr: 'Découvrir les créations', en: 'Discover the creations' }, skills: { fr: 'Explorer les compétences', en: 'Explore the skills' }, testimonials: { fr: 'Écouter les témoignages', en: 'Listen to testimonials' }, journey: { fr: 'Suivre le parcours', en: 'Follow the journey' }, } return labels[zone]?.[locale] || zone } function getZoneIcon(zone: string): string { const icons: Record = { projects: '💻', skills: '⚡', testimonials: '💬', journey: '📍', } return icons[zone] || '?' } function getZoneColor(zone: string): string { const colors: Record = { projects: '#3b82f6', skills: '#10b981', testimonials: '#f59e0b', journey: '#8b5cf6', } return colors[zone] || '#fa784f' } ``` ### Composant ZoneEndChoice ```vue ``` ### Schéma des chemins narratifs ``` ┌─────────────┐ │ INTRO │ └──────┬──────┘ │ ┌────────────┴────────────┐ ▼ ▼ ┌──────────┐ ┌──────────┐ │ PROJETS │ │ COMPÉT. │ └────┬─────┘ └────┬─────┘ │ │ ┌───────┴───────┐ ┌───────┴───────┐ ▼ ▼ ▼ ▼ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │TÉMOIGN.│ │PARCOURS│ │TÉMOIGN.│ │PARCOURS│ └───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘ │ │ │ │ ▼ ▼ ▼ ▼ (suite) (suite) (suite) (suite) │ │ │ │ └──────┬──────┴───────────┴──────┬──────┘ │ │ ▼ ▼ ┌──────────────────────────────────────┐ │ CONTACT │ │ (tous les chemins y mènent) │ └──────────────────────────────────────┘ ``` ### Dépendances **Cette story nécessite :** - Story 4.1 : ChoiceCards - Story 4.2 : Intro narrative (premier choix) - Story 3.5 : Store de progression (choices) **Cette story prépare pour :** - Story 4.7 : Révélation (fin des chemins) - Story 4.8 : Page contact (destination finale) ### Project Structure Notes **Fichiers à créer :** ``` frontend/app/ ├── data/ │ └── narrativePaths.ts # CRÉER ├── composables/ │ └── useNarrativePath.ts # CRÉER └── components/feature/ └── ZoneEndChoice.vue # CRÉER ``` **Fichiers à modifier :** ``` frontend/app/pages/projets.vue # AJOUTER ZoneEndChoice frontend/app/pages/competences.vue # AJOUTER ZoneEndChoice frontend/app/pages/temoignages.vue # AJOUTER ZoneEndChoice frontend/app/pages/parcours.vue # AJOUTER ZoneEndChoice ``` ### References - [Source: docs/planning-artifacts/epics.md#Story-4.3] - [Source: docs/planning-artifacts/ux-design-specification.md#Narrative-Paths] - [Source: docs/brainstorming-gamification-2026-01-26.md#Parcours-Narratifs] ### Technical Requirements | Requirement | Value | Source | |-------------|-------|--------| | Points de choix | 2-3 | Epics | | Parcours possibles | 4-8 | Epics | | Toutes zones visitables | Oui | Epics | | Tous chemins → contact | Oui | Epics | ## Dev Agent Record ### Agent Model Used {{agent_model_name_version}} ### Debug Log References ### Completion Notes List ### Change Log | Date | Change | Author | |------|--------|--------| | 2026-02-04 | Story créée avec contexte complet | SM Agent | ### File List