✨ 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:
@@ -18,6 +18,7 @@ class DatabaseSeeder extends Seeder
|
||||
SkillProjectSeeder::class,
|
||||
TestimonialSeeder::class,
|
||||
NarratorTextSeeder::class,
|
||||
EasterEggSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
155
api/database/seeders/EasterEggSeeder.php
Normal file
155
api/database/seeders/EasterEggSeeder.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\EasterEgg;
|
||||
use App\Models\Translation;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class EasterEggSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$easterEggs = [
|
||||
// 1. Konami code sur la landing
|
||||
[
|
||||
'slug' => 'konami-master',
|
||||
'location' => 'landing',
|
||||
'trigger_type' => 'konami',
|
||||
'reward_type' => 'badge',
|
||||
'reward_key' => 'easter.konami.reward',
|
||||
'difficulty' => 3,
|
||||
],
|
||||
// 2. Clic sur l'araignée cachée (header)
|
||||
[
|
||||
'slug' => 'hidden-spider',
|
||||
'location' => 'header',
|
||||
'trigger_type' => 'click',
|
||||
'reward_type' => 'anecdote',
|
||||
'reward_key' => 'easter.spider.reward',
|
||||
'difficulty' => 2,
|
||||
],
|
||||
// 3. Hover sur un caractère spécial dans le code (page projets)
|
||||
[
|
||||
'slug' => 'secret-comment',
|
||||
'location' => 'projects',
|
||||
'trigger_type' => 'hover',
|
||||
'reward_type' => 'snippet',
|
||||
'reward_key' => 'easter.comment.reward',
|
||||
'difficulty' => 2,
|
||||
],
|
||||
// 4. Scroll jusqu'en bas de la page parcours
|
||||
[
|
||||
'slug' => 'journey-end',
|
||||
'location' => 'journey',
|
||||
'trigger_type' => 'scroll',
|
||||
'reward_type' => 'anecdote',
|
||||
'reward_key' => 'easter.journey_end.reward',
|
||||
'difficulty' => 1,
|
||||
],
|
||||
// 5. Séquence de clics sur les compétences (Vue, Laravel, TypeScript)
|
||||
[
|
||||
'slug' => 'tech-sequence',
|
||||
'location' => 'skills',
|
||||
'trigger_type' => 'sequence',
|
||||
'reward_type' => 'snippet',
|
||||
'reward_key' => 'easter.tech_seq.reward',
|
||||
'difficulty' => 4,
|
||||
],
|
||||
// 6. Clic sur le logo Skycel 5 fois
|
||||
[
|
||||
'slug' => 'logo-clicks',
|
||||
'location' => 'global',
|
||||
'trigger_type' => 'click',
|
||||
'reward_type' => 'image',
|
||||
'reward_key' => 'easter.logo.reward',
|
||||
'difficulty' => 2,
|
||||
],
|
||||
// 7. Hover sur la date "2022" dans le parcours
|
||||
[
|
||||
'slug' => 'founding-date',
|
||||
'location' => 'journey',
|
||||
'trigger_type' => 'hover',
|
||||
'reward_type' => 'anecdote',
|
||||
'reward_key' => 'easter.founding.reward',
|
||||
'difficulty' => 2,
|
||||
],
|
||||
// 8. Console.log dans les devtools
|
||||
[
|
||||
'slug' => 'dev-console',
|
||||
'location' => 'global',
|
||||
'trigger_type' => 'sequence',
|
||||
'reward_type' => 'badge',
|
||||
'reward_key' => 'easter.console.reward',
|
||||
'difficulty' => 3,
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($easterEggs as $egg) {
|
||||
EasterEgg::firstOrCreate(['slug' => $egg['slug']], $egg);
|
||||
}
|
||||
|
||||
// Traductions des récompenses
|
||||
$translations = [
|
||||
// Konami
|
||||
[
|
||||
'key' => 'easter.konami.reward',
|
||||
'fr' => "Badge 'Gamer' debloque ! Tu connais les classiques.",
|
||||
'en' => "'Gamer' badge unlocked! You know the classics.",
|
||||
],
|
||||
// Spider
|
||||
[
|
||||
'key' => 'easter.spider.reward',
|
||||
'fr' => "Tu m'as trouve ! Je me cache partout sur ce site... Le Bug te surveille toujours.",
|
||||
'en' => "You found me! I hide everywhere on this site... The Bug is always watching.",
|
||||
],
|
||||
// Comment
|
||||
[
|
||||
'key' => 'easter.comment.reward',
|
||||
'fr' => "// Premier code ecrit : console.log('Hello World'); // Tout a commence la...",
|
||||
'en' => "// First code written: console.log('Hello World'); // It all started there...",
|
||||
],
|
||||
// Journey end
|
||||
[
|
||||
'key' => 'easter.journey_end.reward',
|
||||
'fr' => "Tu as lu jusqu'au bout ? Respect. Le prochain chapitre s'ecrit peut-etre avec toi.",
|
||||
'en' => "You read all the way? Respect. The next chapter might be written with you.",
|
||||
],
|
||||
// Tech sequence
|
||||
[
|
||||
'key' => 'easter.tech_seq.reward',
|
||||
'fr' => "const stack = ['Vue', 'Laravel', 'TypeScript'];\n// La sainte trinite du dev moderne",
|
||||
'en' => "const stack = ['Vue', 'Laravel', 'TypeScript'];\n// The holy trinity of modern dev",
|
||||
],
|
||||
// Logo
|
||||
[
|
||||
'key' => 'easter.logo.reward',
|
||||
'fr' => "Image secrete debloquee : La premiere version du logo Skycel (spoiler: c'etait moche)",
|
||||
'en' => "Secret image unlocked: The first version of the Skycel logo (spoiler: it was ugly)",
|
||||
],
|
||||
// Founding
|
||||
[
|
||||
'key' => 'easter.founding.reward',
|
||||
'fr' => "2022 : l'annee ou Le Bug est ne. Litteralement un bug dans le code qui m'a donne l'idee de la mascotte.",
|
||||
'en' => "2022: the year The Bug was born. Literally a bug in the code that gave me the mascot idea.",
|
||||
],
|
||||
// Console
|
||||
[
|
||||
'key' => 'easter.console.reward',
|
||||
'fr' => "Badge 'Developpeur' debloque ! Tu as verifie la console comme un vrai dev.",
|
||||
'en' => "'Developer' badge unlocked! You checked the console like a real dev.",
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($translations as $t) {
|
||||
Translation::firstOrCreate(
|
||||
['lang' => 'fr', 'key_name' => $t['key']],
|
||||
['value' => $t['fr']]
|
||||
);
|
||||
Translation::firstOrCreate(
|
||||
['lang' => 'en', 'key_name' => $t['key']],
|
||||
['value' => $t['en']]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,18 @@ class NarratorTextSeeder extends Seeder
|
||||
['context' => 'intro', 'text_key' => 'narrator.intro.dev.1', 'variant' => 1, 'hero_type' => 'dev'],
|
||||
['context' => 'intro', 'text_key' => 'narrator.intro.dev.2', 'variant' => 2, 'hero_type' => 'dev'],
|
||||
|
||||
// INTRO SEQUENCE 1 - Recruteur
|
||||
['context' => 'intro_sequence_1', 'text_key' => 'narrator.intro_seq.1.recruteur', 'variant' => 1, 'hero_type' => 'recruteur'],
|
||||
// INTRO SEQUENCE 1 - Client/Dev
|
||||
['context' => 'intro_sequence_1', 'text_key' => 'narrator.intro_seq.1.casual', 'variant' => 1, 'hero_type' => 'client'],
|
||||
['context' => 'intro_sequence_1', 'text_key' => 'narrator.intro_seq.1.casual', 'variant' => 1, 'hero_type' => 'dev'],
|
||||
|
||||
// INTRO SEQUENCE 2 - Tous
|
||||
['context' => 'intro_sequence_2', 'text_key' => 'narrator.intro_seq.2', 'variant' => 1, 'hero_type' => null],
|
||||
|
||||
// INTRO SEQUENCE 3 - Tous
|
||||
['context' => 'intro_sequence_3', 'text_key' => 'narrator.intro_seq.3', 'variant' => 1, 'hero_type' => null],
|
||||
|
||||
// TRANSITIONS
|
||||
['context' => 'transition_projects', 'text_key' => 'narrator.transition.projects.1', 'variant' => 1, 'hero_type' => null],
|
||||
['context' => 'transition_projects', 'text_key' => 'narrator.transition.projects.2', 'variant' => 2, 'hero_type' => null],
|
||||
@@ -214,6 +226,31 @@ class NarratorTextSeeder extends Seeder
|
||||
'fr' => "Tiens, un visage familier ! Content de te revoir, voyageur.",
|
||||
'en' => "Well, a familiar face! Good to see you again, traveler.",
|
||||
],
|
||||
|
||||
// Intro Sequence 1 - Recruteur
|
||||
[
|
||||
'key' => 'narrator.intro_seq.1.recruteur',
|
||||
'fr' => "Bienvenue dans mon domaine, voyageur... Je suis Le Bug, et je vais vous guider dans cette aventure.",
|
||||
'en' => "Welcome to my domain, traveler... I am The Bug, and I will guide you through this adventure.",
|
||||
],
|
||||
// Intro Sequence 1 - Casual
|
||||
[
|
||||
'key' => 'narrator.intro_seq.1.casual',
|
||||
'fr' => "Hey ! Bienvenue chez moi. Je suis Le Bug, ton guide pour cette aventure.",
|
||||
'en' => "Hey! Welcome to my place. I'm The Bug, your guide for this adventure.",
|
||||
],
|
||||
// Intro Sequence 2
|
||||
[
|
||||
'key' => 'narrator.intro_seq.2',
|
||||
'fr' => "Il y a quelqu'un ici que tu cherches... Un développeur mystérieux qui a créé tout ce que tu vois autour de toi.",
|
||||
'en' => "There's someone here you're looking for... A mysterious developer who created everything you see around you.",
|
||||
],
|
||||
// Intro Sequence 3
|
||||
[
|
||||
'key' => 'narrator.intro_seq.3',
|
||||
'fr' => "Pour le trouver, tu devras explorer ce monde. Chaque zone cache une partie de son histoire. Es-tu prêt ?",
|
||||
'en' => "To find them, you'll have to explore this world. Each zone hides a part of their story. Are you ready?",
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($translations as $t) {
|
||||
|
||||
Reference in New Issue
Block a user