# CURSORRULES — Projet Next.js / Sanity / Vercel
# Stack : Figma Make → Cursor → Sanity → Vercel
# Auteur : Florian

---

## FICHIERS PROTÉGÉS — NE JAMAIS SUPPRIMER NI ÉCRASER

Le dossier `/config/` et son contenu sont des fichiers de référence du projet. Ils ne font pas partie du code généré.
- Ne jamais supprimer, déplacer ou écraser `/config/architecture.yaml`
- Ne jamais supprimer, déplacer ou écraser `/config/redirects.json`
- Ne jamais supprimer, déplacer ou écraser `/config/context-stack.md`
- Ne jamais supprimer, déplacer ou écraser `/config/navigation.yaml`
- Ne jamais supprimer, déplacer ou écraser `/config/navigation-footer.yaml`
- Ne jamais supprimer, déplacer ou écraser `.cursorrules`
- Ne jamais supprimer, déplacer ou écraser `README.md`
Ces fichiers doivent rester intacts même lors de l'initialisation Next.js, de restructurations ou de refactorisations.

---

## STACK TECHNIQUE
- Framework : Next.js (App Router)
- CMS : Sanity (headless)
- Hébergement : Vercel
- Styling : Tailwind CSS
- Génération maquettes : Figma Make

---

## CONVENTIONS GÉNÉRALES

- Toujours utiliser le App Router de Next.js (dossier /app)
- Composants en TypeScript (.tsx)
- Un composant par fichier
- Nommage des composants en PascalCase
- Nommage des fichiers en kebab-case
- Imports absolus avec alias @/

---

## ARBORESCENCE

- Lire le fichier /config/architecture.yaml avant de créer des pages
- Créer les routes Next.js en respectant la hiérarchie du YAML
- Ne pas créer de page marquée published: false
- Chaque page doit avoir son slug correspondant à l'URL finale
- En cas de suppression d'une page : TOUJOURS ajouter la redirection dans /config/redirects.json

---

## SEO — OBLIGATOIRE SUR CHAQUE PAGE

### Métadonnées
- Récupérer metaTitle et metaDescription depuis Sanity ou architecture.yaml
- Injecter via la fonction generateMetadata() de Next.js
- Title : entre 50 et 60 caractères
- Description : entre 140 et 160 caractères

### Structure HTML
- Une seule balise <h1> par page
- Hiérarchie stricte : h1 → h2 → h3 (pas de saut de niveau)
- Vérifier la hiérarchie avant de finaliser le composant

### Images
- Toujours ajouter un attribut alt descriptif et contextualisé
- Nommer les fichiers images en kebab-case avec mots-clés : ex. creation-site-web-paris.jpg
- Utiliser le composant <Image> de Next.js (jamais de balise <img> brute)
- Définir width et height sur chaque image

### Source des images — règle absolue : une seule source par image

Chaque image a UNE seule source. Ne jamais mélanger les deux dans un même composant.

- **Image depuis Sanity** → le composant lit uniquement depuis Sanity. L'alt, le titre et la légende viennent aussi de Sanity. Fallback `/public/` autorisé uniquement pendant le développement avant connexion Sanity.
- **Image depuis `/public/`** → référencée en dur dans le JSX. Réservée aux images UI qui ne changent jamais (logo fixe, icônes, illustrations décoratives). Ne pas exposer dans Sanity.

**Cas concret à ne pas créer :** un logo dans `/public/images/ui/logo.svg` ET un champ logo dans Sanity. Si le client change le logo dans Studio, le site affiche toujours l'ancien — parce que le composant lit `/public/`, pas Sanity. Ce bug silencieux est à éviter absolument.

**Avant de coder tout composant qui affiche une image**, demander ou vérifier explicitement : "cette image vient-elle de Sanity ou de `/public/` ?" et ne coder qu'une seule source.

### URLs et navigation
- Slugs en minuscules, kebab-case, sans caractères spéciaux
- Pas de paramètres inutiles dans les URLs
- Ajouter des liens internes pertinents entre les pages

### Données structurées
- Ajouter du JSON-LD sur chaque page (au minimum : WebPage ou Service)
- Pour les pages avec FAQ : ajouter le schema FAQPage
- Pour les pages de service : ajouter le schema Service

---

## SANITY — INTÉGRATION

- Chaque page récupère son contenu depuis Sanity via GROQ
- Utiliser next-sanity pour les requêtes

### Champs gérés par Sanity (éditables sans toucher au code)
- `published` : publier / dépublier une page
- `noindex` : indexer ou non par Google
- `excludeFromSitemap` : inclure ou exclure du sitemap et du plan du site
- `slug` : URL de la page — si modifié, générer automatiquement une redirection 301 dans redirects.json
- `metaTitle` : titre SEO
- `metaDescription` : description SEO

### Champs NON gérés par Sanity (YAML uniquement)
- `type` : type de page — architectural, ne pas exposer dans Sanity
- `template` : template utilisé — implique du code, ne pas exposer dans Sanity
- `children` : hiérarchie des pages — structure, ne pas exposer dans Sanity
- `archive` : archive parente d'un article — structure, ne pas exposer dans Sanity

### Règle slug dans Sanity
- Si le slug est modifié dans Sanity → générer automatiquement une redirection 301 dans redirects.json
- Mettre à jour tous les liens internes et le fil d'ariane automatiquement
- Ne jamais laisser un ancien slug sans redirection
- RÈGLE ABSOLUE : si Sanity est connecté au projet, TOUT le contenu visible sur le site doit être éditable dans Sanity Studio — sans exception
- Cela inclut : titres, paragraphes, boutons, labels, images, liens, metaTitle, metaDescription, textes alternatifs des images, textes de navigation, footer, mentions légales
- Ne jamais hard-coder du contenu éditorial dans les composants, même pour un test
- Ne jamais laisser un texte en dur dans le JSX si Sanity est connecté
- Chaque composant qui affiche du texte ou une image doit recevoir ses données depuis Sanity
- Les champs minimum par page dans Sanity : titre, contenu, metaTitle, metaDescription, slug, image principale avec alt

---

## CODE GÉNÉRÉ PAR FIGMA MAKE

- Quand tu intègres du code venant de Figma Make :
  - Vérifier la cohérence avec la stack (Tailwind, App Router)
  - Remplacer les balises <img> par <Image>
  - Ajouter les balises alt manquantes
  - Vérifier la hiérarchie des titres
  - Nettoyer les styles inline si présents

---

## REDIRECTIONS

- Fichier : /config/redirects.json
- Format :
  [
    { "source": "/ancienne-url", "destination": "/nouvelle-url", "permanent": true }
  ]
- Rappel : à chaque suppression de page, ajouter la redirection correspondante
- Importer les redirections dans next.config.js

---

## DÉPLOIEMENT VERCEL

- Variables d'environnement Sanity dans .env.local (jamais dans le code)
- Vérifier que les redirections sont bien déclarées dans next.config.js avant deploy

---

## NOINDEX ET EXCLUSION SITEMAP

- Les trois champs suivants sont OBLIGATOIRES sur chaque page de architecture.yaml : published, noindex, excludeFromSitemap
- Ne jamais laisser ces champs implicites — toujours les déclarer explicitement
- Si `published: false` → ne pas générer la page (noindex et excludeFromSitemap sont ignorés)
- Si `noindex: true` → injecter `<meta name="robots" content="noindex, nofollow">` via generateMetadata()
- Si `noindex: false` → injecter `<meta name="robots" content="index, follow">` via generateMetadata()
- Si `excludeFromSitemap: true` → exclure la page du sitemap.xml ET de la page /plan-du-site
- Pages avec noindex: true et excludeFromSitemap: true par défaut : contact-merci, 404
- Toute page de remerciement ou de confirmation → toujours noindex: true + excludeFromSitemap: true
- Pas de statut brouillon dans le YAML — le brouillon est géré par les branches Git (dev → prévisualisation Vercel)

---

## SITEMAP — GÉNÉRATION AUTOMATIQUE

### Page HTML Sitemap
- Route : /sitemap-page
- Générer automatiquement depuis architecture.yaml
- Lister toutes les pages avec published: true
- Respecter la hiérarchie des children
- Ajouter des liens cliquables vers chaque page

### Fichier sitemap.xml
- Générer automatiquement depuis architecture.yaml
- Inclure toutes les pages avec published: true
- Format standard XML (https://www.sitemaps.org/)
- Inclure : loc, lastmod, changefreq, priority
- Utiliser la fonctionnalité native Next.js : /app/sitemap.ts

### Script de génération
- Créer /lib/sitemap-utils.ts qui lit architecture.yaml
- Exporter les fonctions : getSitemapPages(), generateSitemapXml()
- Utiliser dans /app/sitemap.ts pour le sitemap.xml automatique
- Utiliser dans /app/plan-du-site/page.tsx pour la page HTML
- Le sitemap.xml se met à jour automatiquement à chaque déploiement Vercel

---

## FIL D'ARIANE (BREADCRUMB)

- Présent sur toutes les pages SAUF la homepage
- Généré automatiquement depuis la hiérarchie de architecture.yaml
- Structure : Accueil → Parent → Page courante
- Exemples :
  - Accueil → Services → Ateliers Flash
  - Accueil → Blog → Nom de l'article
- Créer un composant réutilisable /components/Breadcrumb.tsx
- Le composant reçoit le slug de la page et remonte la hiérarchie depuis architecture.yaml
- Ajouter systématiquement le JSON-LD BreadcrumbList sur chaque page concernée
- Le fil d'Ariane doit être visible visuellement ET balisé en JSON-LD pour le SEO

---

## GIT — RÈGLES

- Ne jamais inclure .env.local dans les commits
- Toujours avoir un fichier .gitignore avec : .env.local, .env, node_modules/, .next/
- Branche main = production (connectée à Vercel)
- Branche dev = développement en cours
- Rappeler de commiter avant chaque déploiement important

---

## WPGRAPHQL — RÉCUPÉRATION DE DONNÉES WORDPRESS

- Utilisé quand le projet récupère du contenu depuis un site WordPress existant
- Le plugin WPGraphQL doit être installé et activé sur le WordPress AVANT de coder
- Point d'accès GraphQL : `https://tonsite-wordpress.com/graphql`
- Installer le client : `npm install graphql-request` ou `@apollo/client graphql`
- Ne jamais utiliser la REST API WordPress — toujours passer par WPGraphQL
- Les appels WPGraphQL sont générés par Cursor à partir des instructions en langage naturel
- Si le site utilise ACF : vérifier que WPGraphQL for ACF est aussi installé côté WordPress
- Stocker l'URL du point GraphQL WordPress dans `.env.local` : `WORDPRESS_GRAPHQL_URL=https://...`

---

## MISE À JOUR ARCHITECTURE.YAML DEPUIS WORDPRESS

- Quand tu récupères des pages depuis WordPress via WPGraphQL, toujours demander à l'utilisateur : "Veux-tu que je mette à jour architecture.yaml avec les pages récupérées depuis WordPress ?"
- Si oui : mettre à jour architecture.yaml en respectant la structure existante (type, template, slug, published, metaTitle, metaDescription)
- Ne jamais écraser architecture.yaml automatiquement sans confirmation explicite
- Si des pages existent déjà dans architecture.yaml, demander si on fusionne ou si on remplace

---

## INTERFACE D'ARBORESCENCE — /admin/arborescence

### Objectif
Page locale accessible sur `localhost:3000/admin/arborescence` permettant de trier et organiser les pages avant de connecter Sanity. C'est une étape de préparation — on nettoie l'arborescence en amont pour que Sanity reçoive uniquement du contenu validé et propre.

### Fonctionnalités à générer
- Affichage de toutes les pages récupérées depuis WPGraphQL ou architecture.yaml
- Drag and drop pour réorganiser les pages et définir la hiérarchie (niveau 1, niveau 2, niveau 3)
- Bouton supprimer : retire la page de l'arborescence ET injecte automatiquement la redirection dans redirects.json
- Bouton ajouter : permet d'ajouter une page manuellement avec ses métadonnées
- Bouton publier/dépublier : change le statut published dans architecture.yaml
- Sauvegarde : régénère architecture.yaml proprement avec les modifications

### Données récupérées depuis WPGraphQL pour l'arborescence
Ne récupérer QUE ces champs — pas le contenu complet des pages :
- title : titre de la page, de l'article ou de la catégorie
- slug : URL dédiée à la page
- metaTitle : meta title SEO (via plugin Yoast ou RankMath si présent)
- metaDescription : meta description SEO (via plugin Yoast ou RankMath si présent)
- status : statut de publication (publish, draft, private) → mapper vers published: true/false dans architecture.yaml
  - publish → published: true
  - draft → published: false
  - private → published: false

### Workflow recommandé
1. Récupérer les pages brutes depuis WPGraphQL (titres, slugs, métadonnées, statuts uniquement)
2. Afficher dans l'interface /admin/arborescence
3. Trier : garder, supprimer, réorganiser
4. Les suppressions alimentent redirects.json automatiquement
5. Valider → architecture.yaml est régénéré proprement
6. Connecter Sanity sur cette base nette

---

## BIBLIOTHÈQUE MÉDIA — RÈGLES

### Stockage
- Images dans /public/images/ organisé en sous-dossiers : /pages, /articles, /ui
- Nommage des fichiers : kebab-case avec mots-clés SEO (ex: creation-site-web-paris.jpg)
- Toujours utiliser le composant <Image> de Next.js, jamais de balise <img> brute

### Métadonnées images — RÈGLE ABSOLUE
- Ne JAMAIS écrire les balises alt en dur dans le JSX
- Chaque image récupère son alt, titre et légende depuis Sanity
- Sanity est la source de vérité unique pour toutes les métadonnées d'images
- Cette règle s'applique à toutes les images sans exception : pages, articles, UI

### Interface /admin/medias
- Générer une interface accessible sur localhost:3000/admin/medias
- Afficher : vignette, nom fichier, URL, pages d'utilisation, métadonnées SEO éditables
- Édition des métadonnées → mise à jour directe dans Sanity
- Bouton "Remplacer l'image" → deux modes : même URL (simple) ou nouvelle URL (avec script de mise à jour en masse et confirmation explicite)
- Ne pas déployer cette interface en production

### Remplacement d'image avec nouvelle URL
- Générer un script de remplacement qui met à jour toutes les références dans le code ET dans Sanity
- Toujours demander confirmation explicite avant d'exécuter le script
- Toujours ajouter une redirection dans redirects.json pour l'ancienne URL image

---

## SCRIPT DE TÉLÉCHARGEMENT DES MÉDIAS WORDPRESS

- Générer le script dans `/scripts/download-medias.ts`
- Lancement : `npx ts-node scripts/download-medias.ts`
- Toujours demander confirmation explicite avant de lancer
- Récupérer TOUS les médias depuis WPGraphQL (mediaItems) : images, PDF, documents, vidéos MP4, audio
- Trier par type MIME dans les bons dossiers :
  - Images (jpg, png, webp, gif, svg) → /public/images/
  - Documents (pdf, doc, docx, xls, xlsx, ppt, pptx) → /public/documents/
  - Vidéos (mp4, mov, webm) → /public/videos/
  - Audio (mp3, wav, ogg) → /public/audio/
  - Autres → /public/medias/
- Générer /config/medias-map.json avec : originalUrl, localUrl, altText, title, mimeType, width, height
- Après téléchargement : remplacer toutes les anciennes URLs WordPress par les nouvelles URLs locales dans le code ET dans Sanity
- Afficher un rapport : nombre de fichiers, taille totale, erreurs éventuelles
- Ce script est à lancer UNE SEULE FOIS lors de la migration
- Le fichier /config/medias-map.json généré est la source de données de l'interface /admin/medias
- L'interface /admin/medias lit ce fichier et affiche tous les médias : images, documents, vidéos, audio
- Toute modification dans /admin/medias met à jour medias-map.json ET Sanity en même temps

---

## CARDS, CATÉGORIES, EXTRAITS ET IMAGE À LA UNE

### Image à la une
- Champ `featuredImage` dans Sanity sur les pages, articles ET catégories
- Si pas d'image définie : utiliser l'image par défaut des paramètres globaux Sanity
- Option "ne pas afficher d'image" : champ booléen `hideImage` dans Sanity
- Utiliser comme og:image si pas d'image OpenGraph définie

### Catégories dans Sanity
- Objet Sanity avec : name, slug, featuredImage, excerpt, parent
- Assignables aux pages ET aux articles
- Une page ou article peut avoir plusieurs catégories

### Extraits
- Champ `excerpt` dans Sanity sur pages, articles ET catégories
- Si excerpt vide → fallback automatique sur les X premiers caractères du contenu
- X = valeur lue depuis les paramètres globaux Sanity (défaut : 160 caractères)

### Composants Cards — /components/cards/
- CardArticle, CardService, CardCategorie, CardRecherche
- Chaque card reçoit ses données depuis Sanity via props
- Structure d'une card : featuredImage, titre, extrait, badge type, bouton lire la suite
- Si hideImage: true → afficher la card sans image
- Si pas d'image et hideImage: false → utiliser l'image par défaut

### Template moteur de recherche — search-template
- Grille configurable 3 ou 4 colonnes (paramètre Sanity)
- Composant CardRecherche pour chaque résultat
- Badge type sur chaque card : Page / Article
- Barre de recherche avec loupe
- Filtres temps réel : par type (Page/Article) et par catégorie
- Indexer : titre, extrait, contenu, catégories
- Ajouter search-template à la liste des templates disponibles dans architecture.yaml

### Paramètres globaux Sanity
- Créer un document singleton "Paramètres globaux" dans Sanity
- Contient : image par défaut, nombre de caractères extrait, colonnes grille, logo, couleurs, contact

---

## JOBBOARD — FONCTIONNALITÉ OPTIONNELLE

### Activation
- Ne pas générer cette fonctionnalité par défaut
- L'activer UNIQUEMENT si :
  - Une page avec `template: jobboard-template` est présente dans architecture.yaml
  - OU si l'utilisateur le demande explicitement
- Si non activé : ne pas créer le schéma Sanity, ne pas créer les composants, ne pas créer le template

## JOBBOARD

### Objet Sanity — Offre d'emploi
Champs : title, company, location, contractType, jobCategory, excerpt, content, featuredImage, published, noindex, slug, publishedAt, expiresAt

### Composant CardJob — /components/cards/CardJob.tsx
- Affiche : logo/image, titre poste, entreprise, localisation, type contrat, date, extrait, bouton "Voir l'offre"

### Template jobboard-template
- Grille ou liste configurable
- Filtres temps réel : type de contrat, localisation, catégorie de métier
- Barre de recherche texte libre
- Tri par date (plus récent en premier)
- Masquer automatiquement les offres avec expiresAt dépassée
- Afficher le nombre de résultats
