diff --git a/ENONCE_final_boss.md b/ENONCE_final_boss.md
index 0d2641b..18aac3d 100644
--- a/ENONCE_final_boss.md
+++ b/ENONCE_final_boss.md
@@ -1,21 +1,30 @@
-# đ ĂnoncĂ© FINAL â « Boss de fin » đ
+# đ Ă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. đȘ
+Dernier jour. Tu as tout l'arsenal : composants, props, state, lifting state up, `useEffect`, formulaire, tri, filtre, React Router. Aujourd'hui, place Ă la pratique libre.
-| 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 | ââ | đ |
+Un seul module est **obligatoire** : le **Module L** (la page détail, le vrai boss). Les autres forment un **menu** : pioche dans l'ordre que tu veux, fais-en le plus possible. Chacun est court et indépendant.
+
+| Module | Tu fais | Niveau |
+|---|---|---|
+| **L** | Page dĂ©tail **complĂšte** (state remontĂ© au routeur) â *obligatoire* | âââ |
+| M | Bouton « au hasard » (`useNavigate`) | ââ |
+| N | Le podium des trilogies les plus likĂ©es | ââ |
+| O | Bouton « supprimer » une trilogie | ââ |
+| P | ThĂšme sombre qui se souvient du choix | ââ |
+| Q | Mode Versus : deux trilogies s'affrontent | ââ |
+| R | Statistiques (total, moyenne, championne) | ââ |
+| S | Page « 404 » pour les URL inconnues | ââ |
+| T | Tri ascendant â descendant | â |
+| U | Custom hook `useLocalStorage` (refacto) | âââ |
+| V | Ăditer une trilogie | âââ |
---
-## đ Module L â Le boss : faire suivre TOUT jusqu'au dĂ©tail
+## đ Module L â Le boss : faire suivre TOUT jusqu'au dĂ©tail (obligatoire)
-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**.
+Dans `CORRECTION_page_detail.md` tu as fait une version simple avec `data.js`. Sa limite : 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'idée : la donnée doit vivre **au-dessus** des deux pages, donc dans `App`, qui la passe ensuite **en props** aux deux routes.
### L.1 â Remonter le state dans `App.jsx`
```jsx
@@ -31,174 +40,266 @@ function App() {
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.
+> Le dĂ©clic : une route reçoit 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).
+### L.2 â `Accueil` lit/Ă©crit via les props
+EnlĂšve les `useState` de `trilogies` et `likes` dans `Accueil` : ils viennent maintenant des props. Garde en local ce qui est propre Ă l'accueil (`recherche`, `tri`, 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**
+### 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
;
+ if (!trilogie) return Introuvable â retour
;
return (
â Retour
{trilogie.nom}
-
đź Niveaux : {trilogie.niveaux}
+
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 ». đ
+**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 « â€ïž 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.
+> 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 central en React.
---
-## đČ Module M â « Je tente ma chance »
+## Module M â Bouton « au hasard »
-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 ``).
+Un bouton qui t'envoie sur la page détail d'une trilogie au hasard. On découvre `useNavigate` (naviguer en JS, pas via 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 ... */}
- >
- );
+const navigate = useNavigate();
+function hasard() {
+ const i = Math.floor(Math.random() * trilogies.length);
+ navigate(`/trilogie/${trilogies[i].id}`);
}
+//
```
-> đ `` = naviguer **au clic** sur un lien. `useNavigate()` = naviguer **depuis du code** (aprĂšs un calcul, un envoi de formulaireâŠ). Deux outils, deux usages.
+> `` = naviguer au clic sur un lien. `useNavigate()` = naviguer depuis du code (aprĂšs un calcul, un envoi de formulaireâŠ).
-**â
Test :** clique đČ plusieurs fois â tu tombes sur des trilogies diffĂ©rentes.
+**Test :** clique plusieurs fois â tu tombes sur des trilogies diffĂ©rentes.
---
-## đ Module N â Le Podium
+## Module N â Le podium
-En haut de l'accueil, affiche le **Top 3** des trilogies les plus likées, avec des médailles.
+En haut de l'accueil, 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
+ .slice(0, 3);
-// dans le JSX :
-
-
đ Podium
- {podium.map((t, index) => (
-
{medailles[index]} {t.nom} â {likes[t.id] || 0} â€ïž
- ))}
-
+//
+// {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.
+> `.slice(0, 3)` garde les indices 0, 1, 2. Le 2á” paramĂštre de `.map((t, index) => ...)`, c'est la position dans la liste â parfait pour la mĂ©daille.
-**â
Test :** like beaucoup une trilogie â elle grimpe sur le podium en temps rĂ©el.
+**Test :** like beaucoup une trilogie â elle grimpe sur le podium en direct.
---
-## đ Module O â ThĂšme sombre (qui se souvient)
+## Module O â Supprimer une trilogie
-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).
+Un bouton « supprimer » sur chaque carte. La clé : l'immutabilité (un nouveau tableau **sans** l'élément).
+
+```jsx
+function supprimer(id) {
+ setTrilogies(trilogies.filter((t) => t.id !== id));
+}
+// sur la Carte :
+// et dans Accueil : onDelete={() => supprimer(trilo.id)}
+```
+
+> `filter` renvoie un **nouveau** tableau contenant tout sauf l'élément visé. On ne touche jamais l'ancien (immutabilité, comme pour l'ajout).
+
+**Test :** clique đïž â la carte disparaĂźt, les autres restent.
+
+---
+
+## Module P â ThĂšme sombre (qui se souvient)
+
+Un bouton qui bascule la page en sombre et garde le choix aprĂšs un refresh (`localStorage`, comme tes likes).
-### 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;
-}
+.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.
+> Tu fais piloter du CSS par du state : `className` change selon `sombre`, le navigateur applique les bonnes rÚgles. C'est comme ça que marchent tous les dark modes.
-**â
Test :** passe en sombre â recharge la page (F5) â c'est **toujours** sombre. đ
+**Test :** passe en sombre â recharge (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)
+## Module Q â Mode Versus
-## đ Si tu finis tout (true endgame)
-- đ **Confetti au like** : une petite animation CSS quand le compteur monte.
-- âïž **Mode Versus** : choisis 2 trilogies dans 2 `