Diagnostic et plan - Découplage du contenu /blog via Sanity
Diagnostic et plan - Découplage du contenu /blog via Sanity
Diagnostic approfondi et plan de conception pour assurer que 100% du contenu éditorial de la page /blog soit piloté par Sanity CMS (Astro ne gérant que la présentation).
Diagnostic & Plan d’exécution — Découplage du Contenu /blog via Sanity
Date : 2026-10-18
Auteurs : Pair-programming Antigravity IDE & Anderson
Référence : Spécification utilisateur (plan/description.md), ADR-001
Dépôts cibles :
audyssee/amn-studio(Gestion des schémas & contenu CMS)audyssee/amn-blog(Présentation Astro SSR)
1. Diagnostic de l’existant sur /blog1/2
L’analyse du code source d’amn-blog et du dataset Sanity development (projet 4wa7mvsz) révèle une séparation incomplète entre contenu éditorial et présentation :
1. Diagnostic de l’existant sur /blog2/2
Éléments de la page /blog |
Composant Astro | État actuel | Piloté par Sanity ? | Problème constaté |
|---|---|---|---|---|
| Liste des articles & pagination | index.astro & BlogCard.astro |
Requête GROQ SSR dynamique : *[_type == "post"] paginée par 5 |
OUI (avec fallback Markdown local) | Conforme à l’architecture. |
| Hero (Titre & Sous-titre) | BlogHero.astro |
Valeurs codées en dur dans index.astro : title="Welcome, world", subtitle="Get all the latest news from the Amnworld." |
NON (Hardcodé dans Astro) | Tout changement de slogan ou de titre nécessite un commit de code Astro. |
| Sidebar — Popular posts | PopularPosts.astro |
Prend les 4 premiers posts récents, mais a une liste de 4 articles en dur (defaultPopularPosts) si vide. Pas de sélection éditoriale dans Sanity. |
PARTIELLEMENT | L’éditeur ne peut pas choisir manuellement les articles mis en avant dans la sidebar. |
| Sidebar — Callout plateforme (“AMN platform”) | MerchCallout.astro |
Titre, description, URL vers les docs et badges codés en dur dans le composant. | NON (100% hardcodé) | L’encart publicitaire/plateforme ne peut pas être modifié sans déployer le front. |
| Sidebar — Inscription RSS | SubscribeRss.astro |
Titre “Subscribe” et sous-titre codés en dur. | NON (Hardcodé) | Textes figés dans le code Astro. |
| Métadonnées SEO du Blog | index.astro -> MainLayout.astro |
title="Blog — Latest news & insights", description="..." passés en dur dans index.astro. |
NON (Hardcodé) | Les métadonnées du blog ne sont pas configurables depuis Sanity. |
Diagnostic de Sanity Studio (amn-studio)1/2
Dans amn-studio/schemaTypes/, les schémas existants sont :
post,author,category,blockContentpage(avecpageBuilder: hero, features, testimonials, cta, richText, imageGallery)amn(document de test minimal avec message + status)submission(formulaires)
Diagnostic de Sanity Studio (amn-studio)2/2
👉 Constat clé : Il n’existe aucun schéma blogSettings (ou réglages de page de blog) dans Sanity. C’est la raison pour laquelle les textes du hero, de la sidebar et du SEO avaient été initialement renseignés en dur dans Astro.
2. Architecture cible (Séparation stricte CMS / Présentation)1/2
Conformément à l’ADR-001 :
- Sanity (
amn-studio) = Contenu brut & Gouvernance éditoriale (titres, textes, slogans, liens, sélections d’articles populaires, encarts promo). - Astro (
amn-blog) = Moteur de présentation pur (balisage HTML5 sémantique, design Tailwind CSS, responsive, accessibilité, rendu SSR).
2. Architecture cible (Séparation stricte CMS / Présentation)2/2
+-----------------------------------------------------------------------+| SANITY STUDIO (amn-studio) || || Document Singleton "blogSettings" : || • heroTitle: "Welcome, world" || • heroSubtitle: "Get all the latest news from the Amnworld." || • popularPosts: [ Ref(post1), Ref(post2), Ref(post3) ] || • callout: { title, description, linkUrl, badgeText } || • subscribe: { title, subtitle, rssUrl } || • seo: { metaTitle, metaDescription, ogImage } |+-----------------------------------▲-----------------------------------+ │ Requête GROQ SSR │ (loadQuery)+-----------------------------------▼-----------------------------------+| ASTRO SSR (amn-blog) || || 1. index.astro fetch posts + blogSettings en une seule passe || 2. Injecte dynamiquement les données dans : || • <BlogHero title={settings.heroTitle} ... /> || • <BlogSidebar popularPosts={settings.popularPosts} ... /> || • <MainLayout title={settings.seo.metaTitle} ... /> || 3. Aucun texte de contenu en dur dans le code Astro |+-----------------------------------------------------------------------+3. Plan d’exécution par étapes
Étape 1 : Création du schéma blogSettings.ts dans amn-studio1/3
Ajouter le schéma singleton blogSettings avec les champs :
- Hero :
heroTitle(string, titre d’accueil)heroSubtitle(text, sous-titre)
- Popular posts :
popularPosts(array de références verspost, avec prévisualisation)
Étape 1 : Création du schéma blogSettings.ts dans amn-studio2/3
- Callout Encart Plateforme :
calloutTitle(string)calloutDescription(text)calloutLink(url / string)calloutBadgeText(string, ex: “Audyssée architecture”)
Étape 1 : Création du schéma blogSettings.ts dans amn-studio3/3
- Subscribe RSS :
subscribeTitle(string)subscribeSubtitle(text)
- SEO & Réseaux :
metaTitle(string)metaDescription(text)ogImage(image)
Enregistrer blogSettings dans amn-studio/schemaTypes/index.ts.
Étape 2 : Création & initialisation du document blogSettings dans le dataset Sanity
- Via les outils MCP Sanity (
create_documents) ou script, créer le document initialblogSettingsavec les valeurs actuelles pour assurer une continuité parfaite sans rupture.
Étape 3 : Mise à jour de la requête GROQ dans amn-blog/src/pages/blog/index.astro
Adapter la requête pour récupérer posts, total ET settings en un seul appel :
{ "posts": *[_type == "post"] | order(publishedAt desc)[$start...$end] { _id, title, slug, excerpt, publishedAt, mainImage { asset->{ url }, alt }, author->{ name, image { asset->{ url } } }, categories[]->{ _id, title } }, "total": count(*[_type == "post"]), "settings": *[_type == "blogSettings"][0] { heroTitle, heroSubtitle, metaTitle, metaDescription, calloutTitle, calloutDescription, calloutLink, calloutBadgeText, subscribeTitle, subscribeSubtitle, "selectedPopularPosts": popularPosts[]->{ title, "href": "/blog/" + slug.current + "/" } }}Étape 4 : Injection dynamique dans les composants Astro1/2
BlogHero.astro: Reçoittitleetsubtitledepuissettings(avec fallback élégant si non défini).BlogSidebar.astro: Transmet les données dynamiquescallout,subscribeetpopularPostsà ses sous-composants.PopularPosts.astro: Utilise les articles sélectionnés dans Sanity (ou à défaut les articles les plus récents de Sanity).
Étape 4 : Injection dynamique dans les composants Astro2/2
MerchCallout.astro: Affiche le titre, la description, le badge et le lien issus desettings.SubscribeRss.astro: Affiche les libellés issus desettings.MainLayout.astro: ReçoitmetaTitleetmetaDescriptionissus desettings.
Étape 5 : Validation & Traitement des retours utilisateurs (Feedback)1/3
Suite aux retours utilisateurs, les éléments complémentaires suivants ont été entièrement découplés et confiés à Sanity :
-
Titre de la section “Popular posts” : Ajout du champ
popularPostsTitle(string) dans le schémablogSettings, récupéré dynamiquement et transmis à<PopularPosts title={popularPostsTitle} />. -
Visuel de l’encart callout (“Audyssée architecture”) & lien cliquable : Ajout du champ
calloutImage(image avec hotspot) dansblogSettings. Le visuel est rendu sous forme de lien cliquable (<a href={href}>) avec l’image Sanity si fournie, ou avec le visuel vectoriel par défaut si aucune image n’est téléversée.
Étape 5 : Validation & Traitement des retours utilisateurs (Feedback)2/3
-
Bouton & lien RSS Feed : Ajout des champs
subscribeButtonLabel(“RSS Feed”) etsubscribeRssLink(“/rss.xml”) dansblogSettings, transmis dynamiquement à<SubscribeRss />.
Étape 5 : Validation & Traitement des retours utilisateurs (Feedback)3/3
-
Refonte visuelle du Merch Callout (conforme au style Astro) :
- Élimination de la boîte/carte imbriquée interne autour du visuel.
- Suppression du contour de carte (
border border-purple-500/20, fond boîte) : la section s’intègre directement et harmonieusement dans la sidebar, délimitée uniquement par la bordure inférieure de section. - L’image occupe toute la largeur utile sous le titre et la description, avec coins arrondis (
rounded-xl), intégrée dans un lien cliquable global (<a href={href}>).
4. Statut de réalisation1/2
| Élément | Propriété Sanity | Statut |
|---|---|---|
| Titre du hero | blogSettings.heroTitle |
✅ Découplé & En ligne |
| Sous-titre du hero | blogSettings.heroSubtitle |
✅ Découplé & En ligne |
| Titre “Popular posts” | blogSettings.popularPostsTitle |
✅ Découplé & En ligne |
| Liste des articles populaires | blogSettings.popularPosts[] |
✅ Découplé & En ligne |
| Titre callout | blogSettings.calloutTitle |
✅ Découplé & En ligne |
| Description callout | blogSettings.calloutDescription |
✅ Découplé & En ligne |
4. Statut de réalisation2/2
| Élément | Propriété Sanity | Statut |
|---|---|---|
| Badge callout | blogSettings.calloutBadgeText |
✅ Découplé & En ligne |
| Image callout cliquable | blogSettings.calloutImage + calloutLink |
✅ Découplé & En ligne |
| Titre & Sous-titre subscribe | blogSettings.subscribeTitle / subscribeSubtitle |
✅ Découplé & En ligne |
| Libellé & URL bouton RSS | blogSettings.subscribeButtonLabel / subscribeRssLink |
✅ Découplé & En ligne |
| Métadonnées SEO | blogSettings.metaTitle / metaDescription |
✅ Découplé & En ligne |