feat(marketing): add real application screenshots

This commit is contained in:
t.indorf
2026-10-13 13:13:14 +02:00
parent abe73bb9ac
commit d4aa90422f
12 changed files with 307 additions and 177 deletions
+10
View File
@@ -1,3 +1,5 @@
import { ScreenshotPreview } from "@/components/marketing/product-screenshots";
import { getProductScreenshot } from "@/lib/marketing/screenshots";
import { StructuredData } from "@/components/marketing/structured-data";
import { featureStructuredData, publicPageMetadata } from "@/lib/marketing/seo";
import type { Metadata } from "next";
@@ -66,6 +68,7 @@ export default async function FeaturePage({ params }: FeaturePageProps) {
}
const Icon = feature.icon;
const screenshot = getProductScreenshot(slug);
return (
<div className="min-h-screen bg-[#f8faf8] text-slate-950">
@@ -169,6 +172,13 @@ export default async function FeaturePage({ params }: FeaturePageProps) {
</div>
</section>
{screenshot && (
<section aria-label={`${feature.shortTitle} in der App`} className="mx-auto w-full max-w-7xl px-5 pt-16 sm:px-6">
<h2 className="mb-6 text-2xl font-semibold tracking-tight">Ein Blick in die Anwendung</h2>
<ScreenshotPreview screenshot={screenshot} />
</section>
)}
<section className="mx-auto grid w-full max-w-7xl gap-6 px-5 py-20 sm:px-6 lg:grid-cols-2">
<Card className="border-slate-200 bg-white">
<CardHeader>
+8 -177
View File
@@ -2,10 +2,8 @@ import Image from "next/image";
import Link from "next/link";
import {
ArrowRight,
Bell,
CheckCircle2,
ClipboardList,
Clock,
DatabaseZap,
FileStack,
Fingerprint,
@@ -15,7 +13,6 @@ import {
MessageSquareText,
MessagesSquare,
PhoneCall,
Search,
Route,
ShieldCheck,
Table2,
@@ -41,6 +38,8 @@ import {
marketingTitle,
publicPageMetadata,
} from "@/lib/marketing/seo";
import { ProductScreenshots, ScreenshotPreview } from "@/components/marketing/product-screenshots";
import { getProductScreenshot } from "@/lib/marketing/screenshots";
import { ContactForm } from "./contact-form";
export const metadata = publicPageMetadata(
@@ -284,14 +283,14 @@ export default function LandingPage() {
</h2>
</div>
<p className="max-w-2xl text-sm leading-6 text-slate-600 lg:justify-self-end">
Der Notdienst-Planer sammelt Verfügbarkeiten, erinnert an fehlende
Einträge und macht aus Antworten einen veröffentlichbaren
Monatsplan. Bei manuellen Änderungen bleibt sichtbar, welche
Haushalte für den Tag verfügbar sind.
Entdeckt die sechs Bereiche direkt in der App: vom Notdienst
über Kalender und Mitteilungen bis zur Arbeitszeit. Die Ansichten
zeigen Beispieldaten und lassen sich für einen genaueren Blick
vergrößern.
</p>
</div>
<ProductBrowserMock />
<ProductScreenshots />
</section>
<section className="mx-auto w-full max-w-7xl px-5 py-20 sm:px-6 lg:py-24">
@@ -442,7 +441,7 @@ export default function LandingPage() {
</div>
</CardHeader>
<CardContent className="pt-0">
<FeaturePreview slug={feature.slug} />
<ScreenshotPreview screenshot={getProductScreenshot(feature.slug)!} compact />
</CardContent>
</Card>
);
@@ -628,171 +627,3 @@ export default function LandingPage() {
</div>
);
}
function ProductBrowserMock() {
return (
<div className="overflow-hidden rounded-2xl border border-emerald-100 bg-white shadow-[0_24px_70px_rgba(15,23,42,0.12)]">
<div className="flex items-center justify-between border-b border-slate-200 bg-[#fbf8ef] px-4 py-3">
<div className="flex items-center gap-2">
<span className="h-3 w-3 rounded-full bg-red-300" />
<span className="h-3 w-3 rounded-full bg-amber-300" />
<span className="h-3 w-3 rounded-full bg-emerald-300" />
</div>
<div className="rounded-full border border-slate-200 bg-white px-3 py-1 text-[11px] font-medium text-slate-500 sm:px-4 sm:text-xs">
Kita-Planer · Notdienst
</div>
<div className="hidden w-16 sm:block" />
</div>
<div className="bg-[#fbf8ef] p-2 sm:p-4">
<Image
src="/landing/notdienst-calendar-linen.png"
alt="Notdienst-Kalender im Kita-Planer mit ausgewählten Tagen im Juni 2026"
width={2262}
height={1640}
sizes="(min-width: 1280px) 1184px, calc(100vw - 40px)"
className="h-auto w-full rounded-xl border border-slate-200 bg-[#fbf8ef]"
/>
</div>
</div>
);
}
function FeaturePreview({ slug }: { slug: string }) {
if (slug === "notdienst-planer") {
return (
<div className="rounded-xl border border-red-100 bg-[#fbf8ef] p-3">
<div className="mb-3 flex items-center justify-between text-xs">
<span className="font-semibold text-slate-700">Juni</span>
<span className="rounded-full bg-red-50 px-2 py-1 text-red-700">
12/16
</span>
</div>
<div className="grid grid-cols-5 gap-1.5">
{[
"",
"bg-red-100",
"",
"bg-red-100",
"",
"bg-red-100",
"",
"",
"bg-red-100",
"",
].map((cell, index) => (
<span
key={index}
className={`h-7 rounded-md border border-red-100 bg-white ${cell}`}
/>
))}
</div>
</div>
);
}
if (slug === "kita-kalender") {
return (
<div className="rounded-xl border border-sky-100 bg-[#fbf8ef] p-3">
{[
["10", "Schließtag", "bg-red-400"],
["18", "Elternabend", "bg-indigo-400"],
["29", "Sommerfest", "bg-emerald-500"],
].map(([date, title, color]) => (
<div
key={title}
className="mb-2 last:mb-0 flex items-center gap-3 rounded-lg bg-white px-3 py-2 text-xs"
>
<span className="font-semibold tabular-nums text-slate-950">
{date}
</span>
<span className={`h-5 w-1 rounded-full ${color}`} />
<span className="text-slate-600">{title}</span>
</div>
))}
</div>
);
}
if (slug === "eltern-adressbuch") {
return (
<div className="rounded-xl border border-emerald-100 bg-[#fbf8ef] p-3">
<div className="mb-3 flex items-center gap-2 rounded-lg border border-slate-200 bg-white px-3 py-2 text-xs text-slate-400">
<Search className="h-3.5 w-3.5" />
Familie suchen
</div>
{["Meier", "Mustermann"].map((name) => (
<div key={name} className="mb-2 last:mb-0 flex items-center gap-2">
<span className="h-7 w-7 rounded-lg bg-emerald-50" />
<span className="h-2 flex-1 rounded-full bg-slate-200" />
<span className="h-2 w-10 rounded-full bg-emerald-100" />
</div>
))}
</div>
);
}
if (slug === "krankmeldung") {
return (
<div className="rounded-xl border border-amber-100 bg-[#fbf8ef] p-3">
<div className="mb-3 flex items-center gap-2 text-xs font-semibold text-slate-700">
<Clock className="h-4 w-4 text-amber-700" />
Heute fehlen 2 Kinder
</div>
{["Kai · krank", "Mia · Arzttermin"].map((item) => (
<div
key={item}
className="mb-2 last:mb-0 rounded-lg bg-white px-3 py-2 text-xs text-slate-600"
>
{item}
</div>
))}
</div>
);
}
if (slug === "personal-arbeitszeit") {
return (
<div className="rounded-xl border border-teal-100 bg-[#fbf8ef] p-3">
<div className="mb-3 flex items-center justify-between gap-2 text-xs">
<span className="font-semibold text-slate-700">
Meine Arbeitswoche
</span>
<span className="rounded-full bg-teal-50 px-2 py-1 text-teal-700">
Eingereicht
</span>
</div>
<div className="grid grid-cols-3 gap-2 text-center text-xs">
{[
["Mo", "7:00 Std."],
["Di", "6:30 Std."],
["Mi", "Urlaub"],
].map(([day, hours]) => (
<div key={day} className="rounded-lg bg-white p-2">
<span className="block text-slate-500">{day}</span>
<span className="mt-1 block font-medium text-slate-700">
{hours}
</span>
</div>
))}
</div>
</div>
);
}
return (
<div className="rounded-xl border border-indigo-100 bg-[#fbf8ef] p-3">
<div className="mb-3 flex items-center gap-2 text-xs font-semibold text-slate-700">
<Bell className="h-4 w-4 text-indigo-700" />
Aktualisierte Mitteilung
</div>
<div className="space-y-2">
<span className="block h-2.5 w-4/5 rounded-full bg-slate-200" />
<span className="block h-2.5 w-3/5 rounded-full bg-slate-200" />
<span className="inline-flex rounded-full bg-indigo-50 px-2 py-1 text-xs text-indigo-700">
PDF angehängt
</span>
</div>
</div>
);
}