Files
craftvia/src/app/(marketing)/sicherheit/page.tsx
T
msolarczekandClaude Opus 5 4815a957b0 Produktseite ausgebaut: Unterseiten und Bildschirmfotos aus der App
Neue öffentliche Routen unter src/app/(marketing): Übersicht /funktionen, sechs Detailseiten
(planung, baustelle, zeiterfassung, berichte, abrechnung, lotse), /sicherheit und /preise,
gemeinsames Gerüst mit Kopf- und Fußzeile.

scripts/marketing-shots.ts nimmt die Bilder direkt aus der laufenden App auf (Playwright über
den installierten Chrome, Session wie im Smoke-Test, ohne Passworteingabe); 14 Aufnahmen in
public/marketing ersetzen die gezeichneten Vorschauen.

Plantafel: „Vorschläge" hängt jetzt als Fußzeile in der Auftragskarte statt frei zwischen zwei
Karten zu stehen (neue Prop footer an OrderCard, Schlüssel unplanned.suggestionsFor).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:35:04 +02:00

68 lines
2.9 KiB
TypeScript

import type { Metadata } from "next";
import { getTranslations } from "next-intl/server";
import { Download, EyeOff, KeyRound, Layers, ScrollText, UsersRound, type LucideIcon } from "lucide-react";
import { CtaBand, PageHero, ShotFrame } from "@/components/marketing/blocks";
/** Sicherheit und Datenschutz — Mandantentrennung, Rollen, Protokoll, Export. */
export async function generateMetadata(): Promise<Metadata> {
const t = await getTranslations("marketing.securityPage");
return { title: t("title"), description: t("lead") };
}
const ITEMS: { key: string; icon: LucideIcon }[] = [
{ key: "item1", icon: Layers },
{ key: "item2", icon: UsersRound },
{ key: "item3", icon: KeyRound },
{ key: "item4", icon: ScrollText },
{ key: "item5", icon: Download },
{ key: "item6", icon: EyeOff },
];
export default async function SecurityPage() {
const t = await getTranslations("marketing");
return (
<>
<PageHero eyebrow={t("security.eyebrow")} title={t("securityPage.title")} lead={t("securityPage.lead")} />
<section aria-label={t("securityPage.title")} className="mx-auto max-w-6xl px-4 pb-12 sm:px-6">
<ul className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{ITEMS.map(({ key, icon: Icon }) => (
<li key={key} className="shadow-card rounded-xl border bg-card p-5">
<span className="flex size-10 items-center justify-center rounded-lg bg-[var(--ui-primary-soft)] text-[var(--ui-primary)]">
<Icon className="size-5" aria-hidden />
</span>
<h2 className="mt-3 font-heading text-[16px] font-semibold">{t(`security.${key}.title`)}</h2>
<p className="mt-1.5 text-[14px] leading-relaxed text-muted-foreground">{t(`security.${key}.text`)}</p>
</li>
))}
</ul>
</section>
<section aria-labelledby="security-detail" className="border-y bg-card">
<div className="mx-auto grid max-w-6xl gap-8 px-4 py-12 sm:px-6 lg:grid-cols-[0.9fr_1.1fr] lg:items-center">
<div>
<h2 id="security-detail" className="font-heading text-[24px] font-semibold">{t("securityPage.detailTitle")}</h2>
<dl className="mt-5 grid gap-4">
{(["d1", "d2", "d3"] as const).map((k) => (
<div key={k}>
<dt className="font-heading text-[16px] font-semibold">{t(`securityPage.${k}title`)}</dt>
<dd className="mt-1 text-[14px] leading-relaxed text-muted-foreground">{t(`securityPage.${k}text`)}</dd>
</div>
))}
</dl>
</div>
<ShotFrame shot={{ src: "audit", kind: "desktop" }} caption={t("securityPage.shot1")} />
</div>
</section>
<section aria-label={t("securityPage.shot2")} className="mx-auto max-w-5xl px-4 py-12 sm:px-6">
<ShotFrame shot={{ src: "teams", kind: "desktop" }} caption={t("securityPage.shot2")} />
</section>
<CtaBand />
</>
);
}