Files
old.cms.fellies.org/apps/web/src/components/public-page-view.tsx

141 lines
4.8 KiB
TypeScript

import { parsePageBlocks } from "@cms/content"
import Image from "next/image"
type PageEntity = {
title: string
status: string
summary: string | null
content: string
}
type PublicPageViewProps = {
page: PageEntity
}
export function PublicPageView({ page }: PublicPageViewProps) {
const blocks = (() => {
try {
return parsePageBlocks(page.content)
} catch {
return [
{
id: "fallback-rich-text",
type: "rich_text" as const,
body: page.content,
},
]
}
})()
return (
<article className="mx-auto flex w-full max-w-4xl flex-col gap-6 px-6 py-16">
<header className="space-y-3">
<p className="text-sm uppercase tracking-[0.2em] text-neutral-500">{page.status}</p>
<h1 className="text-4xl font-semibold tracking-tight">{page.title}</h1>
{page.summary ? <p className="text-neutral-600">{page.summary}</p> : null}
</header>
<section className="space-y-4 rounded-xl border border-neutral-200 bg-white p-6 text-neutral-800">
{blocks.map((block) => {
if (block.type === "hero") {
return (
<section key={block.id} className="space-y-2 rounded border border-neutral-200 p-4">
<h2 className="text-2xl font-semibold">{block.heading}</h2>
{block.subheading ? <p className="text-neutral-600">{block.subheading}</p> : null}
{block.ctaLabel && block.ctaHref ? (
<a
href={block.ctaHref}
className="inline-flex rounded bg-neutral-900 px-3 py-1.5 text-sm text-white"
>
{block.ctaLabel}
</a>
) : null}
</section>
)
}
if (block.type === "rich_text") {
return (
<section
key={block.id}
className="prose prose-neutral max-w-none whitespace-pre-wrap"
>
{block.body}
</section>
)
}
if (block.type === "gallery") {
return (
<section key={block.id} className="space-y-3">
{block.title ? <h3 className="text-lg font-medium">{block.title}</h3> : null}
<div className="grid gap-3 sm:grid-cols-2">
{block.imageIds.length === 0 ? (
<p className="text-sm text-neutral-500">No media linked yet.</p>
) : (
block.imageIds.map((imageId) => (
<Image
key={imageId}
src={`/api/media/file/${imageId}`}
alt=""
width={1200}
height={800}
className="h-48 w-full rounded border border-neutral-200 object-cover"
/>
))
)}
</div>
</section>
)
}
if (block.type === "cta") {
return (
<a
key={block.id}
href={block.href}
className={`inline-flex rounded px-3 py-2 text-sm ${
block.variant === "secondary"
? "border border-neutral-300 text-neutral-800"
: "bg-neutral-900 text-white"
}`}
>
{block.label}
</a>
)
}
if (block.type === "form") {
return (
<section key={block.id} className="space-y-2 rounded border border-neutral-200 p-4">
<h3 className="text-lg font-medium">{block.title || "Form block"}</h3>
<p className="text-sm text-neutral-600">
{block.description || "Form integration pending."}
</p>
<p className="text-xs text-neutral-500">formKey: {block.formKey}</p>
</section>
)
}
return (
<section key={block.id} className="space-y-2 rounded border border-neutral-200 p-4">
{block.title ? <h3 className="text-lg font-medium">{block.title}</h3> : null}
<div className="grid gap-3 md:grid-cols-2">
{block.cards.map((card) => (
<article key={card.id} className="rounded border border-neutral-200 p-3">
<h4 className="font-medium">{card.name}</h4>
{card.price ? <p className="text-sm text-neutral-700">{card.price}</p> : null}
{card.description ? (
<p className="text-sm text-neutral-600">{card.description}</p>
) : null}
</article>
))}
</div>
</section>
)
})}
</section>
</article>
)
}