Plugin content-pages : - Modèle Prisma ContentPage (slug PK, title, body markdown, category, published) - lib/content-pages.ts : helpers upsert/get/list/unpublish - lib/markdown.ts : mini-renderer markdown server-side sans deps externe (h1-h3, paragraphes, gras/italique, liens, listes ul/ol, hr, blockquote, échappement HTML) - ContentPageRenderer server component, applique le theme Guyane (font-serif) - 5 pages seedées : /a-propos, /faq, /comment-ca-marche, /pour-comites-entreprise, /devenir-loueur - Routes publiques + force-dynamic + guard requirePluginOr404 Plugin legal-pages : - Réutilise le même modèle ContentPage, catégorie 'legal' - 3 pages seedées : /cgv, /mentions-legales, /politique-de-confidentialite (contenu de base, à valider par avocat avant prod réelle) Admin : - /admin/content-pages : table par catégorie, statut publié/dépublié - /admin/content-pages/[slug] : éditeur markdown + toggle publié - PATCH /api/admin/content-pages/[slug] Hooks plugin : - onEnable seed + republish toutes les pages - onDisable dépublie toute la catégorie sans la supprimer (preserve les edits)
31 lines
1.1 KiB
TypeScript
31 lines
1.1 KiB
TypeScript
import { renderMarkdown } from "@/lib/markdown";
|
|
import type { ContentPage } from "@/lib/content-pages";
|
|
|
|
/**
|
|
* Rend une ContentPage en HTML markdown. Server component pur.
|
|
* Pas de "use client" — le markdown est rendu côté serveur, pas de hydration.
|
|
*/
|
|
export function ContentPageRenderer({ page }: { page: ContentPage }) {
|
|
const html = renderMarkdown(page.body);
|
|
return (
|
|
<article className="mx-auto max-w-3xl px-6 py-12 sm:px-8 lg:px-12">
|
|
<header className="mb-8">
|
|
<h1 className="font-serif text-4xl font-medium tracking-tight text-[var(--color-karbe-ink)] sm:text-5xl">
|
|
{page.title}
|
|
</h1>
|
|
<p className="mt-2 text-xs uppercase tracking-wider text-[var(--color-karbe-canopy-700)]/70">
|
|
Mis à jour le{" "}
|
|
{new Date(page.updatedAt).toLocaleDateString("fr-FR", {
|
|
day: "2-digit",
|
|
month: "long",
|
|
year: "numeric",
|
|
})}
|
|
</p>
|
|
</header>
|
|
<div
|
|
className="text-base leading-relaxed text-[var(--color-karbe-ink)]/90"
|
|
dangerouslySetInnerHTML={{ __html: html }}
|
|
/>
|
|
</article>
|
|
);
|
|
}
|