[
  {
    "id": "creer-composant",
    "titre": "Créer un composant",
    "categorie": "composants",
    "icone": "🧩",
    "description": "La base : déclarer un composant fonctionnel qui retourne du JSX ou du template HTML.",
    "react": "function Salutation({ nom }) {\n  return <h1>Bonjour {nom}</h1>;\n}\n\nexport default Salutation;",
    "nextjs": "// pages/salutation.js\nexport default function PageSalutation() {\n  return <h1>Bonjour!</h1>;\n}",
    "vuejs": "<template>\n  <h1>Bonjour {{ nom }}</h1>\n</template>\n\n<script setup>\ndefineProps(['nom']);\n</script>"
  },
  {
    "id": "passer-props",
    "titre": "Passer des props",
    "categorie": "composants",
    "icone": "📦",
    "description": "Transmettre des données du parent vers l'enfant via les propriétés (props).",
    "react": "// Parent\n<Carte titre=\"React\" couleur=\"blue\" />\n\n// Enfant\nfunction Carte({ titre, couleur }) {\n  return (\n    <div style={{ color: couleur }}>\n      {titre}\n    </div>\n  );\n}",
    "nextjs": "// Identique à React\n<Carte titre=\"Next\" couleur=\"black\" />\n\nfunction Carte({ titre, couleur }) {\n  return (\n    <div style={{ color: couleur }}>\n      {titre}\n    </div>\n  );\n}",
    "vuejs": "<!-- Parent -->\n<Carte titre=\"Vue\" couleur=\"green\" />\n\n<!-- Enfant (Carte.vue) -->\n<script setup>\nconst props = defineProps({\n  titre: String,\n  couleur: String\n});\n</script>\n\n<template>\n  <div :style=\"{ color: couleur }\">\n    {{ titre }}\n  </div>\n</template>"
  },
  {
    "id": "rendu-conditionnel",
    "titre": "Rendu conditionnel",
    "categorie": "composants",
    "icone": "🔀",
    "description": "Afficher ou masquer un élément selon une condition booléenne.",
    "react": "function Alerte({ visible, message }) {\n  return (\n    <div>\n      {visible && <p>{message}</p>}\n      {visible\n        ? <span>Ouvert</span>\n        : <span>Fermé</span>\n      }\n    </div>\n  );\n}",
    "nextjs": "// Identique à React\n{visible && <Composant />}\n\n{estActif\n  ? <ModeEdition />\n  : <ModeLecture />\n}",
    "vuejs": "<template>\n  <p v-if=\"visible\">{{ message }}</p>\n  <p v-else>Rien à afficher</p>\n\n  <!-- Reste dans le DOM -->\n  <div v-show=\"actif\">Caché par CSS</div>\n</template>"
  },
  {
    "id": "liste-cle",
    "titre": "Liste et clé",
    "categorie": "composants",
    "icone": "📋",
    "description": "Afficher une liste d'éléments avec une clé unique pour optimiser le rendu.",
    "react": "function ListeVilles({ villes }) {\n  return (\n    <ul>\n      {villes.map((ville) => (\n        <li key={ville.id}>\n          {ville.nom}\n        </li>\n      ))}\n    </ul>\n  );\n}",
    "nextjs": "// Identique à React\n{donnees.map((item) => (\n  <Carte key={item.id} {...item} />\n))}",
    "vuejs": "<template>\n  <ul>\n    <li\n      v-for=\"ville in villes\"\n      :key=\"ville.id\"\n    >\n      {{ ville.nom }}\n    </li>\n  </ul>\n</template>"
  },
  {
    "id": "enfants-slots",
    "titre": "Enfants / Slots",
    "categorie": "composants",
    "icone": "🪆",
    "description": "Passer du contenu HTML à l'intérieur d'un composant (composition).",
    "react": "function Boite({ children }) {\n  return (\n    <div className=\"boite\">\n      {children}\n    </div>\n  );\n}\n\n// Utilisation\n<Boite>\n  <p>Mon contenu ici</p>\n</Boite>",
    "nextjs": "// Identique à React\nfunction MiseEnPage({ children }) {\n  return <main>{children}</main>;\n}\n\n// _app.js\n<MiseEnPage>\n  <Component {...pageProps} />\n</MiseEnPage>",
    "vuejs": "<!-- Boite.vue -->\n<template>\n  <div class=\"boite\">\n    <slot />\n  </div>\n</template>\n\n<!-- Utilisation -->\n<Boite>\n  <p>Mon contenu ici</p>\n</Boite>"
  },
  {
    "id": "etat-local",
    "titre": "État local",
    "categorie": "etat",
    "icone": "💾",
    "description": "Déclarer une variable réactive qui déclenche un re-rendu quand elle change.",
    "react": "import { useState } from 'react';\n\nfunction Compteur() {\n  const [compte, definirCompte] =\n    useState(0);\n\n  return <p>Compte : {compte}</p>;\n}",
    "nextjs": "// Identique à React\nimport { useState } from 'react';\n\nconst [compte, definirCompte] =\n  useState(0);\nconst [texte, definirTexte] =\n  useState('');",
    "vuejs": "<script setup>\nimport { ref } from 'vue';\n\nconst compte = ref(0);\n// Accès via compte.value\n</script>\n\n<template>\n  <p>Compte : {{ compte }}</p>\n</template>"
  },
  {
    "id": "modifier-etat",
    "titre": "Modifier l'état",
    "categorie": "etat",
    "icone": "🔄",
    "description": "Mettre à jour une variable d'état pour déclencher un nouveau rendu du composant.",
    "react": "const [compte, definirCompte] =\n  useState(0);\n\n// Direct\ndefinirCompte(5);\n\n// Basé sur l'ancien état\ndefinirCompte(\n  (ancien) => ancien + 1\n);",
    "nextjs": "// Identique à React\ndefinirCompte(ancien => ancien + 1);\n\n// Avec un objet (spread)\ndefinirUtilisateur({\n  ...utilisateur,\n  nom: 'Alice'\n});",
    "vuejs": "<script setup>\nimport { ref, reactive } from 'vue';\n\nconst compte = ref(0);\ncompte.value++;\n\nconst user = reactive({ nom: 'Bob' });\nuser.nom = 'Alice'; // Réactif!\n</script>"
  },
  {
    "id": "evenement-clic",
    "titre": "Événement clic",
    "categorie": "evenements",
    "icone": "🖱️",
    "description": "Réagir au clic de l'utilisateur sur un élément interactif.",
    "react": "function MonAction() {\n  function gererClic() {\n    alert('Cliqué!');\n  }\n\n  return (\n    <button onClick={gererClic}>\n      Cliquer\n    </button>\n  );\n}",
    "nextjs": "// Identique à React\n<button onClick={\n  () => definirActif(!actif)\n}>\n  Basculer\n</button>",
    "vuejs": "<template>\n  <button @click=\"gererClic\">\n    Cliquer\n  </button>\n</template>\n\n<script setup>\nfunction gererClic() {\n  alert('Cliqué!');\n}\n</script>"
  },
  {
    "id": "evenement-saisie",
    "titre": "Événement saisie",
    "categorie": "evenements",
    "icone": "⌨️",
    "description": "Capturer ce que l'utilisateur tape dans un champ de formulaire.",
    "react": "const [texte, definirTexte] =\n  useState('');\n\n<input\n  value={texte}\n  onChange={\n    (e) => definirTexte(e.target.value)\n  }\n/>",
    "nextjs": "// Identique à React\n<input\n  value={recherche}\n  onChange={\n    (e) => definirRecherche(\n      e.target.value\n    )\n  }\n/>",
    "vuejs": "<template>\n  <input v-model=\"texte\" />\n  <p>Écrit : {{ texte }}</p>\n</template>\n\n<script setup>\nimport { ref } from 'vue';\nconst texte = ref('');\n</script>"
  },
  {
    "id": "lien-spa",
    "titre": "Lien SPA",
    "categorie": "routage",
    "icone": "🔗",
    "description": "Naviguer entre les pages sans rechargement grâce aux liens du cadriciel.",
    "react": "import { Link } from\n  'react-router-dom';\n\n<Link to=\"/profil\">\n  Mon profil\n</Link>\n<Link to={`/item/${id}`}>\n  Voir\n</Link>",
    "nextjs": "import Link from 'next/link';\n\n<Link href=\"/profil\">\n  Mon profil\n</Link>\n<Link href={`/item/${id}`}>\n  Voir\n</Link>",
    "vuejs": "<template>\n  <router-link to=\"/profil\">\n    Mon profil\n  </router-link>\n  <router-link\n    :to=\"`/item/${id}`\"\n  >\n    Voir\n  </router-link>\n</template>"
  },
  {
    "id": "route-dynamique",
    "titre": "Route dynamique",
    "categorie": "routage",
    "icone": "🛤️",
    "description": "Page dont le contenu change selon le paramètre dans l'URL (/item/42).",
    "react": "// Configuration du router\n<Route\n  path=\"/item/:id\"\n  element={<DetailItem />}\n/>\n\n// Dans le composant\nimport { useParams } from\n  'react-router-dom';\nconst { id } = useParams();",
    "nextjs": "// Fichier : pages/item/[id].js\nexport async function getStaticPaths() {\n  return {\n    paths: [...],\n    fallback: false\n  };\n}\n\nexport async function getStaticProps(\n  { params }\n) {\n  // params.id = le paramètre\n}",
    "vuejs": "// router/index.js\n{\n  path: '/item/:id',\n  component: DetailItem\n}\n\n// Dans le composant\nimport { useRoute } from 'vue-router';\nconst route = useRoute();\nconst id = route.params.id;"
  },
  {
    "id": "effet-montage",
    "titre": "Effet au montage",
    "categorie": "cycle-vie",
    "icone": "🚀",
    "description": "Exécuter du code quand le composant apparaît (chargement, timer, abonnement).",
    "react": "import { useEffect } from 'react';\n\nuseEffect(() => {\n  console.log('Monté!');\n\n  // Nettoyage (optionnel)\n  return () => {\n    console.log('Démonté');\n  };\n}, []); // [] = une seule fois",
    "nextjs": "// Client : identique à React\nuseEffect(() => { ... }, []);\n\n// Serveur (pré-rendu) :\nexport async function\n  getStaticProps() {\n  const data = await charger();\n  return { props: { data } };\n}",
    "vuejs": "<script setup>\nimport {\n  onMounted,\n  onUnmounted\n} from 'vue';\n\nonMounted(() => {\n  console.log('Monté!');\n});\n\nonUnmounted(() => {\n  console.log('Démonté');\n});\n</script>"
  },
  {
    "id": "style-conditionnel",
    "titre": "Style conditionnel",
    "categorie": "styles",
    "icone": "🎨",
    "description": "Appliquer des classes CSS ou des styles inline selon l'état du composant.",
    "react": "// Classe conditionnelle\n<div className={\n  actif ? 'carte actif' : 'carte'\n}>\n\n// Style inline\n<div style={{\n  color: erreur ? 'red' : 'green',\n  opacity: visible ? 1 : 0\n}}>",
    "nextjs": "// Identique à React\n<div className={\n  `carte ${actif ? 'actif' : ''}`\n}>\n\n// CSS Modules\nimport s from './Carte.module.css';\n<div className={s.carte}>",
    "vuejs": "<template>\n  <!-- Objet de classes -->\n  <div :class=\"{\n    actif: estActif,\n    erreur: aErreur\n  }\">\n\n  <!-- Style inline -->\n  <div :style=\"{\n    color: couleur,\n    fontSize: taille + 'px'\n  }\">\n</template>"
  },
  {
    "id": "charger-json",
    "titre": "Charger du JSON",
    "categorie": "donnees",
    "icone": "📂",
    "description": "Importer et utiliser des données depuis un fichier JSON local.",
    "react": "import villes from\n  './donnees/villes.json';\n\nfunction Liste() {\n  return (\n    <ul>\n      {villes.map((v) => (\n        <li key={v.id}>{v.nom}</li>\n      ))}\n    </ul>\n  );\n}",
    "nextjs": "import villes from\n  '../donnees/villes.json';\n\nexport async function\n  getStaticProps() {\n  return {\n    props: { villes }\n  };\n}\n\nexport default function Page(\n  { villes }\n) { ... }",
    "vuejs": "<script setup>\nimport villes from\n  './donnees/villes.json';\n// Directement disponible\n</script>\n\n<template>\n  <li\n    v-for=\"v in villes\"\n    :key=\"v.id\"\n  >\n    {{ v.nom }}\n  </li>\n</template>"
  }
]
