import { notFound } from 'next/navigation' import Link from 'next/link' import { Flag, Sparkles, BookOpen, Clock } from 'lucide-react' import { format } from 'date-fns' import { fr } from 'date-fns/locale' import prisma from '@/lib/prisma' import { processNoteHtmlForPublish } from '@/lib/publish/process-note-html' import { REWRITE_SHARED_CSS, KATEX_PUBLISH_CSS } from '@/lib/publish/shared-css' export const revalidate = 60 async function getNotebookSite(slug: string) { const site = await prisma.notebookSite.findUnique({ where: { slug }, include: { notebook: { select: { id: true, name: true, icon: true, user: { select: { name: true, image: true } }, notes: { select: { id: true, title: true, content: true, order: true, updatedAt: true }, where: { trashedAt: null, isArchived: false }, orderBy: { order: 'asc' }, }, }, }, }, }) if (!site || !site.isPublic) return null return site } export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) { const { slug } = await params const site = await getNotebookSite(slug) if (!site) return { title: 'Site introuvable — Memento' } return { title: `${site.notebook.name} — Memento`, description: site.description || `Carnet publié sur Memento`, openGraph: { title: site.notebook.name, description: site.description || '' }, } } function wordCount(html: string) { return (html || '').replace(/<[^>]+>/g, ' ').trim().split(/\s+/).filter(Boolean).length } function readingTime(html: string) { return Math.max(1, Math.ceil(wordCount(html) / 200)) } function preview(html: string, len = 140) { return (html || '').replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim().slice(0, len) } export default async function NotebookSitePage({ params }: { params: Promise<{ slug: string }> }) { const { slug } = await params const site = await getNotebookSite(slug) if (!site) notFound() let selectedIds: string[] = [] try { selectedIds = JSON.parse(site.selectedNoteIds) } catch {} const allNotes = site.notebook.notes const orderedNotes = selectedIds.length > 0 ? selectedIds.map(id => allNotes.find(n => n.id === id)).filter(Boolean) as typeof allNotes : allNotes const notebook = site.notebook const totalWc = orderedNotes.reduce((s, n) => s + wordCount(n.content || ''), 0) const totalRt = Math.max(1, Math.ceil(totalWc / 200)) return ( <> {/* ── NAV ── */} {/* ── HERO ── */}
{notebook.icon &&
{notebook.icon}
}
CARNET · {orderedNotes.length} NOTES · {totalRt} MIN

{notebook.name}

{site.description &&

{site.description}

} {notebook.user?.name && (
{notebook.user.image && } par {notebook.user.name}
)}
{/* ── TABLE DES MATIÈRES ── */}

TABLE DES MATIÈRES

{orderedNotes.map((note, i) => ( {String(i + 1).padStart(2, '0')}
{note.title || 'Sans titre'}
{preview(note.content || '')}
{readingTime(note.content || '')} min
))}
{/* ── NOTES ── */}
{orderedNotes.map((note, i) => { const bodyHtml = processNoteHtmlForPublish(note.content || '') const rt = readingTime(note.content || '') return (
{String(i + 1).padStart(2, '0')} {rt} min de lecture

{note.title || 'Sans titre'}

) })}
{/* ── FOOTER ── */} ) }