96 lines
3.5 KiB
TypeScript
96 lines
3.5 KiB
TypeScript
import type { Prisma } from "@prisma/client";
|
||
import { prisma } from "@/lib/prisma";
|
||
|
||
export type LandingContentV1 = {
|
||
version: 1;
|
||
heroTitle: string;
|
||
heroLead: string;
|
||
primaryCta: { label: string; href: string };
|
||
secondaryCta?: { label: string; href: string };
|
||
benefitSectionTitle: string;
|
||
benefits: { title: string; body: string }[];
|
||
};
|
||
|
||
export const defaultLandingContent = (): LandingContentV1 => ({
|
||
version: 1,
|
||
heroTitle: "Motorrad fahren von A–Z",
|
||
heroLead:
|
||
"Von der Fahrschule zur ersten Serpentine. Tipps, Übungen und Kurse – strukturiert auf einer Plattform.",
|
||
primaryCta: { label: "Zu den Kursen", href: "/kurse" },
|
||
secondaryCta: { label: "Mitgliederbereich", href: "/portal" },
|
||
benefitSectionTitle: "Deine Vorteile",
|
||
benefits: [
|
||
{
|
||
title: "Praxisorientiert",
|
||
body: "Inhalte, die du auf den Platz und in den Alltag übernehmen kannst – Schritt für Schritt.",
|
||
},
|
||
{
|
||
title: "Von A bis Z",
|
||
body: "Klare Module statt Wildwuchs: vom ersten Gedanken ans Motorradfahren bis zu gezielten Übungen.",
|
||
},
|
||
{
|
||
title: "Fortschritt & Zertifikat",
|
||
body: "Behalte deinen Lernstand im Blick und sichere dir nach Abschluss eine Teilnahmebestätigung.",
|
||
},
|
||
],
|
||
});
|
||
|
||
function isRecord(v: unknown): v is Record<string, unknown> {
|
||
return typeof v === "object" && v !== null;
|
||
}
|
||
|
||
export function parseLandingContent(raw: unknown): LandingContentV1 {
|
||
if (!isRecord(raw) || raw.version !== 1) return defaultLandingContent();
|
||
const heroTitle = typeof raw.heroTitle === "string" ? raw.heroTitle : "";
|
||
const heroLead = typeof raw.heroLead === "string" ? raw.heroLead : "";
|
||
const benefitSectionTitle =
|
||
typeof raw.benefitSectionTitle === "string" ? raw.benefitSectionTitle : "Deine Vorteile";
|
||
const primaryCta = isRecord(raw.primaryCta)
|
||
? {
|
||
label: typeof raw.primaryCta.label === "string" ? raw.primaryCta.label : "Mehr",
|
||
href: typeof raw.primaryCta.href === "string" ? raw.primaryCta.href : "/kurse",
|
||
}
|
||
: { label: "Zu den Kursen", href: "/kurse" };
|
||
let secondaryCta: LandingContentV1["secondaryCta"];
|
||
if (isRecord(raw.secondaryCta)) {
|
||
const l = typeof raw.secondaryCta.label === "string" ? raw.secondaryCta.label : "";
|
||
const h = typeof raw.secondaryCta.href === "string" ? raw.secondaryCta.href : "";
|
||
if (l && h) secondaryCta = { label: l, href: h };
|
||
}
|
||
const benefitsRaw = Array.isArray(raw.benefits) ? raw.benefits : [];
|
||
const benefits = benefitsRaw
|
||
.map((b) => {
|
||
if (!isRecord(b)) return null;
|
||
const title = typeof b.title === "string" ? b.title : "";
|
||
const body = typeof b.body === "string" ? b.body : "";
|
||
if (!title && !body) return null;
|
||
return { title, body };
|
||
})
|
||
.filter(Boolean) as { title: string; body: string }[];
|
||
|
||
return {
|
||
version: 1,
|
||
heroTitle: heroTitle || defaultLandingContent().heroTitle,
|
||
heroLead: heroLead || defaultLandingContent().heroLead,
|
||
primaryCta,
|
||
secondaryCta,
|
||
benefitSectionTitle,
|
||
benefits: benefits.length ? benefits : defaultLandingContent().benefits,
|
||
};
|
||
}
|
||
|
||
export async function getLandingContent(): Promise<LandingContentV1> {
|
||
const row = await prisma.landingPage.findUnique({ where: { id: "default" } });
|
||
if (!row) return defaultLandingContent();
|
||
return parseLandingContent(row.content);
|
||
}
|
||
|
||
export async function saveLandingContent(content: LandingContentV1) {
|
||
const json = content as unknown as Prisma.InputJsonValue;
|
||
await prisma.landingPage.upsert({
|
||
where: { id: "default" },
|
||
create: { id: "default", content: json },
|
||
update: { content: json },
|
||
});
|
||
}
|