Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/spider.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
## 2024-05-19 - Typechecking JSON-LD Date Properties
**Learning:** When passing potentially null date fields (like `publishedAt`, `updatedAt`) from Payload CMS to jsonLd generator functions, using the fields directly can cause strict TypeScript compilation failures since the schemas explicitly expect `string | undefined` and not `null`.
**Action:** Always use the logical OR operator with `undefined` (e.g., `datePublished: post.publishedAt || undefined`) when passing date properties to JSON-LD generator functions to satisfy strict type requirements and prevent build regressions.
## 2024-05-19 - Typechecking JSON-LD ItemList Implementations
**Learning:** Archive and listing pages (like `/actualites`) can be optimized with `ItemList` schema for search engines to better understand the collection of items. It requires generating absolute URLs dynamically using `getServerSideURL()` alongside the locale logic to ensure links inside the schema perfectly match the canonical URLs.
**Action:** When implementing `ItemList` JSON-LD schema on index pages, map over the payload documents to include valid `url` attributes and verify that `tsc` passes without breaking existing page rendering logic.
12 changes: 12 additions & 0 deletions src/app/(frontend)/[locale]/actualites/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import React from 'react'
import Link from 'next/link'
import { Media } from '@/components/Media'
import { ArrowUpRight, Clock } from 'lucide-react'
import { getItemListJsonLd } from '@/utilities/jsonLd'
import { getServerSideURL } from '@/utilities/getURL'

export const dynamic = 'force-static'
export const revalidate = 600
Expand All @@ -29,9 +31,19 @@ export default async function ActualitesPage(props: {
const featured = actualites.docs[0]
const rest = actualites.docs.slice(1)

const serverUrl = getServerSideURL()
const itemListJsonLd = getItemListJsonLd(
actualites.docs.map((doc) => ({
name: doc.title,
url: `${serverUrl}/${locale || 'fr'}/actualites/${doc.slug}`,
}))
)

return (
<main className="relative min-h-screen bg-smatch-black text-smatch-text-primary selection:bg-smatch-gold selection:text-smatch-black overflow-hidden pt-32 pb-24">
{/* SEO: Use semantic <main> tag to indicate the primary content of the document, improving crawlability and accessibility. */}
{/* SEO: Inject ItemList structured data to help search engines understand the collection of items. */}
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(itemListJsonLd) }} />
{/* Background Atmosphere */}
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-full max-w-[1200px] h-[600px] bg-smatch-surface opacity-30 blur-[150px] rounded-[100%] pointer-events-none -z-10" />

Expand Down
14 changes: 14 additions & 0 deletions src/utilities/jsonLd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,17 @@ export function getBlogPostingJsonLd(args: {
},
}
}

/** ItemList schema — use on archive/listing pages */
export function getItemListJsonLd(items: { name: string; url: string }[]) {
return {
'@context': 'https://schema.org',
'@type': 'ItemList',
itemListElement: items.map((item, i) => ({
'@type': 'ListItem',
position: i + 1,
name: item.name,
url: item.url,
})),
}
}