Skip to content

SPEC — Correction Composant 2 : Blog Listing + Typage

SPEC — Correction Composant 2 : Blog Listing + Typage

Documentation technique

Cycle : DOC → SPEC → CODE → TEST
Scope : 3 corrections ciblées, sans refonte complète
Statut : En attente d’approbation avant CODE

1. Problème 1 — blog/index.astro ne lit pas depuis Sanity [BLOQUANT]

1.1 Situation actuelle

// blog/index.astro — ACTUEL (INCORRECT)
const posts = (await getCollection('blog')).sort(...)
// → Lit des fichiers Markdown locaux, pas Sanity

1.2 Correction ciblée1/2

Fichier : apps/web/src/pages/blog/index.astro

Changements :

  • Remplacer getCollection('blog') par loadQuery() avec GROQ
  • Conserver export const prerender = false (SSR — même raison que index.astro : accès cookies pour Visual Editing)
  • Garder le même template HTML (juste changer la source de données)
  • Utiliser MainLayout à la place du layout inline actuel (cohérence avec le reste)

1.2 Correction ciblée2/2

Query GROQ (triée par publishedAt desc, même ordre que le tri actuel par date) :

*[_type == "post"] | order(publishedAt desc) {
_id,
_type,
title,
slug,
publishedAt,
mainImage {
asset->{ _id, url, metadata { lqip, dimensions } },
alt
},
author->{ _id, name },
categories[]->{ _id, title }
}

Type de retour : PostListItem[] (type local défini dans le fichier)

2. Problème 2 — Typage as any dans blog/[slug].astro [NON BLOQUANT]

2.1 Situation actuelle

// blog/[slug].astro — ACTUEL (INCORRECT)
const { data: post } = await loadQuery({...})
// → post est `unknown`, tout casté `as any`

2.2 Correction ciblée1/2

Fichier : apps/web/src/pages/blog/[slug].astro

Changements :

  • Définir un type local SanityPost qui reflète exactement la projection GROQ existante
  • Passer le type générique à loadQuery<SanityPost>({...})
  • Remplacer les (post as any)?.x par post?.x
  • Pas de changement fonctionnel — uniquement typage

2.2 Correction ciblée2/2

Note : On n’importe pas Post depuis apps/studio/sanity.types.ts car le type brut ne correspond pas à la projection GROQ (les références sont résolues via -> dans la query). On définit un type local correspondant exactement à la projection.

3. Problème 3 — SEO description = titre [NON BLOQUANT]

3.1 Situation actuelle

// blog/[slug].astro ligne 142 — ACTUEL (INCORRECT)
description={(post as any)?.title || contentPost?.data.title}
// → description SEO = titre de l'article

3.2 Correction ciblée1/2

Fichier : apps/web/src/pages/blog/[slug].astro

Changements :

  • Ajouter un champ excerpt (string, optionnel) au schéma Sanity post.ts
  • Ajouter excerpt à la query GROQ de [slug].astro
  • Utiliser post?.excerpt comme description, fallback sur le titre
  • Régénérer sanity.types.ts avec task typegen (si disponible) ou manuellement

3.2 Correction ciblée2/2

Schéma Sanity (apps/studio/schemaTypes/post.ts) :

defineField({
name: 'excerpt',
title: 'Excerpt',
type: 'text',
rows: 3,
description: 'Short description for SEO and listing previews',
}),

4. Ordre d’implémentation

  1. Problème 3d (schéma Sanity excerpt) — d’abord car ça touche le studio
  2. Problème 1 (blog/index.astro → Sanity) — correction principale
  3. Problème 2 (typage [slug].astro) — amélioration qualité

5. Ce qu’on ne touche PAS

  • loadQuery.ts — fonctionne parfaitement, pas de changement
  • MainLayout.astro — pas de changement
  • BlogPost.astro layout — conservé pour le fallback content collections
  • Le fallback content collections dans [slug].astro — conservé (utile en dev)
  • astro.config.mjs — pas de changement

6. Critères de validation (TEST)

  • curl http://localhost:4321/blog/ liste les posts Sanity (pas les Markdown)
  • Les posts Sanity ont titre, date, auteur, image visible dans le listing
  • curl http://localhost:4321/blog/<slug-sanity> affiche l’article complet
  • La <meta name="description"> contient l’excerpt, pas le titre
  • task typecheck → 0 erreurs
  • Pas de régression sur les posts Markdown (fallback dans [slug].astro)
Switch to normal mode

Contents