# 🏁 Énoncé FINAL — « Boss de fin » 😈 Dernier jour ! 🎉 Tu as déjà tout l'arsenal : composants, props, state, lifting state up, `useEffect`, formulaire, tri, filtre, **React Router**. Aujourd'hui on **s'amuse** : 4 modules courts et stylés, du plus utile au plus fun. Fais-en autant que tu veux, dans l'ordre que tu veux — mais le **Module L** est le vrai boss, attaque-le en premier. 💪 | Module | Tu fais | Niveau | Fun | |---|---|---|---| | L | La page détail **complète** (state remonté au routeur) | ⭐⭐⭐ | 🔑 | | M | Bouton **🎲 « Je tente ma chance »** (page au hasard) | ⭐⭐ | 😄 | | N | **🏆 Le Podium** des trilogies les plus likées | ⭐⭐ | 🥇 | | O | **🌗 Thème sombre** qui se souvient de ton choix | ⭐⭐ | 🌙 | --- ## 🔑 Module L — Le boss : faire suivre TOUT jusqu'au détail Dans `CORRECTION_page_detail.md` tu as fait une version simple avec `data.js`. Le souci : les trilogies **ajoutées au formulaire** et les **likes** n'arrivent pas dans la page détail. On corrige ça **proprement**. **L'idée :** la donnée doit vivre **au-dessus** des deux pages, donc dans `App`. `App` la passe ensuite **en props** aux deux routes. ### L.1 — Remonter le state dans `App.jsx` ```jsx import { useState } from "react"; import { Routes, Route } from "react-router-dom"; import { donneesInitiales } from "./data"; import Accueil from "./pages/Accueil"; import Details from "./pages/Details"; function App() { const [trilogies, setTrilogies] = useState(donneesInitiales); const [likes, setLikes] = useState({}); return ( } /> } /> ); } ``` > 🧩 **Le déclic :** une route peut recevoir des props comme n'importe quel composant — `element={
}`. C'est ça qui « branche » la donnée sur les deux pages. ### L.2 — `Accueil` lit/écrit via les props (au lieu de son state local) Enlève les `useState` de `trilogies` et `likes` dans `Accueil` : ils viennent maintenant de `props`. Garde en local ce qui est **propre à la page d'accueil** (`recherche`, `tri`, les champs du formulaire). ```jsx function Accueil({ trilogies, setTrilogies, likes, setLikes }) { // recherche, tri, nom, niveaux restent en useState local // ajouter() fait toujours setTrilogies([...trilogies, nouvelle]) // liker() fait toujours setLikes(prev => ({...prev, [id]: (prev[id]||0)+1})) } ``` ### L.3 — `Details` affiche la vraie trilogie **avec ses likes** ```jsx function Details({ trilogies, likes }) { const { id } = useParams(); const trilogie = trilogies.find((t) => t.id === Number(id)); if (!trilogie) return

Introuvable 🤷 ← retour

; return (
← Retour

{trilogie.nom}

🎮 Niveaux : {trilogie.niveaux}

❤️ Likes : {likes[trilogie.id] || 0}

); } ``` **✅ Test du boss :** ajoute une trilogie au formulaire → clique « détails » dessus → **elle s'affiche** (alors qu'avec `data.js` elle était introuvable). Like-la 3 fois sur l'accueil → son détail affiche bien « ❤️ Likes : 3 ». 🏆 > 🧠 **Ce que tu viens de faire** s'appelle « **lifting state up** » poussé jusqu'au routeur : l'état monte au plus petit ancêtre commun des composants qui en ont besoin. C'est **le** réflexe d'archi en React. --- ## 🎲 Module M — « Je tente ma chance » Un bouton qui t'envoie sur la page détail d'une trilogie **au hasard**. On découvre `useNavigate` (naviguer en JS, pas avec un clic sur un ``). ```jsx import { useNavigate } from "react-router-dom"; function Accueil({ trilogies, /* ... */ }) { const navigate = useNavigate(); function hasard() { const i = Math.floor(Math.random() * trilogies.length); navigate(`/trilogie/${trilogies[i].id}`); } // ... return ( <> {/* ... reste de la page ... */} ); } ``` > 🔎 `` = naviguer **au clic** sur un lien. `useNavigate()` = naviguer **depuis du code** (après un calcul, un envoi de formulaire…). Deux outils, deux usages. **✅ Test :** clique 🎲 plusieurs fois → tu tombes sur des trilogies différentes. --- ## 🏆 Module N — Le Podium En haut de l'accueil, affiche le **Top 3** des trilogies les plus likées, avec des médailles. ```jsx const medailles = ["🥇", "🥈", "🥉"]; const podium = [...trilogies] .sort((a, b) => (likes[b.id] || 0) - (likes[a.id] || 0)) .slice(0, 3); // on ne garde que les 3 premières // dans le JSX :

🏆 Podium

{podium.map((t, index) => (

{medailles[index]} {t.nom} — {likes[t.id] || 0} ❤️

))}
``` > 🔎 `.slice(0, 3)` = « garde les éléments d'indice 0, 1, 2 ». Et le **2ᵉ paramètre** de `.map((t, index) => ...)`, c'est la **position** dans la liste → parfait pour choisir la médaille. **✅ Test :** like beaucoup une trilogie → elle grimpe sur le podium en temps réel. --- ## 🌗 Module O — Thème sombre (qui se souvient) Un bouton 🌙 / ☀️ qui bascule toute la page en sombre, **et garde ton choix** même après un refresh (`localStorage`, comme les likes du Module H). ### O.1 — L'état + la mémoire ```jsx const [sombre, setSombre] = useState( () => localStorage.getItem("theme") === "sombre" ); useEffect(() => { localStorage.setItem("theme", sombre ? "sombre" : "clair"); }, [sombre]); ``` ### O.2 — Appliquer une classe sur le conteneur ```jsx
{/* ... */}
``` ### O.3 — Le CSS (dans `App.css`) ```css .sombre { background-color: #1a1a2e; color: #eaeaea; } .sombre .carte { background-color: #16213e; color: #eaeaea; } ``` > 🧩 Tu fais piloter du **CSS par du state** : `className` change selon `sombre`, et le navigateur applique les bonnes règles. C'est comme ça que marchent **tous** les dark modes que tu connais. **✅ Test :** passe en sombre → recharge la page (F5) → c'est **toujours** sombre. 🌙 --- ## ✅ Checklist du dernier jour - [ ] L — state remonté dans `App`, page détail complète (ajouts + likes inclus) 🔑 - [ ] M — bouton 🎲 avec `useNavigate` - [ ] N — podium Top 3 (`sort` + `slice` + `index` dans `map`) - [ ] O — thème sombre persistant (`localStorage` + classe CSS pilotée par state) ## 🚀 Si tu finis tout (true endgame) - 🎉 **Confetti au like** : une petite animation CSS quand le compteur monte. - ⚔️ **Mode Versus** : choisis 2 trilogies dans 2 `