diff --git a/CORRECTION_page_detail.md b/CORRECTION_page_detail.md new file mode 100644 index 0000000..0486687 --- /dev/null +++ b/CORRECTION_page_detail.md @@ -0,0 +1,98 @@ +# 🔎 Correction — faire (enfin) vivre la page Détail + +Salut ! 👋 Ton routeur marche **parfaitement** : quand tu cliques « Voir les détails », l'URL devient `/trilogie/3` et la page change **sans recharger**. Bravo, c'est exactement le but de React Router. 🎉 + +Mais ta page `Details.jsx` ressemble encore à ça : + +```jsx +function Details() { + const { id } = useParams(); + return

Détails de la trilogie {id}

; +} +``` + +Elle affiche juste le **numéro**. On voudrait afficher la **vraie trilogie** : son nom, ses niveaux, ses likes. Et là, une question piège… + +--- + +## 🧠 Le vrai problème : « où vivent les données ? » + +`Details` reçoit `id` (genre `"3"`). Mais le **tableau des trilogies**, lui, vit dans le `useState` de… `Accueil`. 😬 + +`Details` et `Accueil` sont **deux pages sœurs** : aucune des deux n'est dans l'autre, donc `Details` **ne peut pas** lire l'état de `Accueil`. C'est le mur classique. + +> 💡 **La règle :** un composant ne voit que **ses props** et **son propre state**. Pour qu'une donnée soit partagée par deux pages, elle doit vivre **au-dessus des deux** (chez leur parent commun), ou dans un endroit que les deux savent lire. + +Il y a 2 niveaux de solution. Prends la **simple** d'abord, garde la **pro** pour l'énoncé final. + +--- + +## ✅ Solution simple — sortir les données dans un fichier à part + +Tes 5 trilogies de départ sont fixes. On les sort de `Accueil` dans un fichier que **les deux pages** peuvent importer. + +### 1. Crée `src/data.js` +```js +export const donneesInitiales = [ + { id: 1, nom: "The RGB Trilogy", niveaux: "Yatagarasu - Erebus - Sonic Wave" }, + { id: 2, nom: "The Greek Letter Trilogy", niveaux: "Omega - Sigma - Gamma" }, + { id: 3, nom: "The Techno Trilogy", niveaux: "Artificial Ascent - Digital Descent - Cybernetic Crescent" }, + { id: 4, nom: "The Riot's Trilogy", niveaux: "Tartarus - Acheron - Aeternus" }, + { id: 5, nom: "The Apocalytic Trilogy", niveaux: "Cataclysm - Bloodbath - Aftermath" }, +]; +``` + +### 2. Dans `Accueil.jsx`, importe-le au lieu de recopier le tableau +```jsx +import { donneesInitiales } from "../data"; +// ... +const [trilogies, setTrilogies] = useState(donneesInitiales); +``` + +### 3. Dans `Details.jsx`, retrouve la bonne trilogie +```jsx +import { useParams, Link } from "react-router-dom"; +import { donneesInitiales } from "../data"; + +function Details() { + const { id } = useParams(); + + // ⚠️ id de l'URL = une CHAÎNE ("3"), ton id de données = un NOMBRE (3). + // On compare en convertissant : Number(id) + const trilogie = donneesInitiales.find((t) => t.id === Number(id)); + + if (!trilogie) return

Trilogie introuvable 🤷 ← retour

; + + return ( +
+ ← Retour à la liste +

{trilogie.nom}

+

🎮 Niveaux : {trilogie.niveaux}

+
+ ); +} + +export default Details; +``` + +> 🐛 **Le piège n°1 ici, c'est `Number(id)`.** `useParams()` te donne **toujours une chaîne**. Si tu écris `t.id === id`, tu compares `3 === "3"` → `false`, et `find` ne trouve **jamais** rien. Convertis l'un des deux ! + +**✅ Test :** clique « Voir les détails » sur la trilogie Techno → tu vois son nom et ses niveaux, pas juste « 3 ». Le lien « ← Retour » te ramène à la liste. + +--- + +## ⚠️ La limite de cette solution (et c'est voulu) + +Avec le fichier `data.js`, la page détail connaît les **5 trilogies de départ**. Mais celles que tu **ajoutes avec le formulaire** ne seront **pas** dans `data.js` → leur page détail affichera « introuvable ». Et les **likes** non plus n'apparaissent pas dans le détail. + +C'est normal : `data.js` est figé, alors que le state vit dans `Accueil`. Pour que **tout** (ajouts + likes) suive jusqu'à la page détail, il faut faire **remonter l'état au niveau du routeur**. C'est le **Module L** de l'énoncé final 👇 — le vrai « boss ». 😈 + +--- + +## 🧹 Pendant qu'on y est, 3 petits nettoyages dans `Accueil.jsx` + +1. `import { useParams } ...` n'est pas utilisé dans `Accueil` → à supprimer (c'est dans `Details` qu'il sert). +2. `console.log("test")` → on enlève les logs de debug avant de livrer. 🧼 +3. Le `<>...` autour du seul ``. + +> 💡 **À retenir :** une page ne peut pas piocher dans le state d'une autre page. On sort la donnée commune **plus haut** (fichier partagé, ou state remonté chez le parent). Et `useParams()` renvoie **toujours une chaîne** → pense à `Number()` pour comparer aux ids. diff --git a/ENONCE_final_boss.md b/ENONCE_final_boss.md new file mode 100644 index 0000000..0d2641b --- /dev/null +++ b/ENONCE_final_boss.md @@ -0,0 +1,204 @@ +# 🏁 É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 `