feat(epic-4): chemins narratifs, easter eggs, challenge et contact

Epic 4: Chemins Narratifs, Challenge & Contact

Stories implementees:
- 4.1: Composant ChoiceCards pour choix narratifs binaires
- 4.2: Sequence d'intro narrative avec Le Bug
- 4.3: Chemins narratifs differencies avec useNarrativePath
- 4.4: Table easter_eggs et systeme de detection (API + composable)
- 4.5: Easter eggs UI (popup, notification, collection)
- 4.6: Page challenge avec puzzle de code
- 4.7: Page revelation "Monde de Code"
- 4.8: Page contact avec formulaire et stats

Fichiers crees:
- Frontend: ChoiceCards, IntroSequence, ZoneEndChoice, EasterEggPopup,
  CodePuzzle, ChallengeSuccess, CodeWorld, et pages intro/challenge/revelation
- API: EasterEggController, Model, Migration, Seeder

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-08 13:35:12 +01:00
parent 64b1a33d10
commit 7e87a341a2
38 changed files with 3037 additions and 96 deletions

View File

@@ -0,0 +1,85 @@
export type ZoneKey = 'projects' | 'skills' | 'testimonials' | 'journey' | 'contact'
export interface ZoneInfo {
labelFr: string
labelEn: string
icon: string
color: string
routeFr: string
routeEn: string
}
export const ZONES: Record<ZoneKey, ZoneInfo> = {
projects: {
labelFr: 'Découvrir les créations',
labelEn: 'Discover the creations',
icon: '💻',
color: '#3b82f6',
routeFr: '/projets',
routeEn: '/en/projects',
},
skills: {
labelFr: 'Explorer les compétences',
labelEn: 'Explore the skills',
icon: '⚡',
color: '#10b981',
routeFr: '/competences',
routeEn: '/en/skills',
},
testimonials: {
labelFr: 'Écouter les témoignages',
labelEn: 'Listen to testimonials',
icon: '💬',
color: '#f59e0b',
routeFr: '/temoignages',
routeEn: '/en/testimonials',
},
journey: {
labelFr: 'Suivre le parcours',
labelEn: 'Follow the journey',
icon: '📍',
color: '#8b5cf6',
routeFr: '/parcours',
routeEn: '/en/journey',
},
contact: {
labelFr: 'Rencontrer le développeur',
labelEn: 'Meet the developer',
icon: '📧',
color: '#fa784f',
routeFr: '/contact',
routeEn: '/en/contact',
},
}
// Chemins possibles (8 combinaisons basées sur 3 points de choix)
// Tous les chemins passent par toutes les zones et mènent au contact
export const NARRATIVE_PATHS: ZoneKey[][] = [
// Chemin 1-4 : Commençant par Projets
['projects', 'testimonials', 'skills', 'journey', 'contact'],
['projects', 'testimonials', 'journey', 'skills', 'contact'],
['projects', 'journey', 'testimonials', 'skills', 'contact'],
['projects', 'journey', 'skills', 'testimonials', 'contact'],
// Chemin 5-8 : Commençant par Compétences
['skills', 'testimonials', 'projects', 'journey', 'contact'],
['skills', 'testimonials', 'journey', 'projects', 'contact'],
['skills', 'journey', 'testimonials', 'projects', 'contact'],
['skills', 'journey', 'projects', 'testimonials', 'contact'],
]
// Points de choix et leurs options
export const CHOICE_POINT_OPTIONS: Record<string, { optionA: ZoneKey; optionB: ZoneKey }> = {
intro_first_choice: { optionA: 'projects', optionB: 'skills' },
after_projects: { optionA: 'testimonials', optionB: 'journey' },
after_skills: { optionA: 'testimonials', optionB: 'journey' },
}
// Mapper choice ID vers zone
export function choiceIdToZone(choiceId: string): ZoneKey | null {
if (choiceId.includes('projects')) return 'projects'
if (choiceId.includes('skills')) return 'skills'
if (choiceId.includes('testimonials')) return 'testimonials'
if (choiceId.includes('journey')) return 'journey'
if (choiceId.includes('contact')) return 'contact'
return null
}