Update public feature details and align SEO metadata
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { Metadata } from "next";
|
||||
import { publicPageMetadata } from "@/lib/marketing/seo";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
ArrowLeft,
|
||||
@@ -11,10 +11,11 @@ import {
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Datenschutz | Kita-Planer",
|
||||
description: "Datenschutzhinweise und Platzhalter für Kita-Planer.",
|
||||
};
|
||||
export const metadata = publicPageMetadata(
|
||||
"/datenschutz",
|
||||
"Datenschutz",
|
||||
"Datenschutzhinweise zu Konten, Kontaktfreigaben und der Nutzung von Kita-Planer.",
|
||||
);
|
||||
|
||||
const overviewItems = [
|
||||
{
|
||||
@@ -77,9 +78,7 @@ export default function DatenschutzPage() {
|
||||
<div className="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-emerald-50 text-emerald-700 ring-1 ring-emerald-100">
|
||||
<Icon className="h-5 w-5" />
|
||||
</div>
|
||||
<h2 className="font-semibold tracking-tight">
|
||||
{item.title}
|
||||
</h2>
|
||||
<h2 className="font-semibold tracking-tight">{item.title}</h2>
|
||||
<p className="mt-2 text-sm leading-6 text-slate-600">
|
||||
{item.text}
|
||||
</p>
|
||||
@@ -154,9 +153,9 @@ export default function DatenschutzPage() {
|
||||
<p className="mt-3 leading-7 text-slate-700">
|
||||
Zugriff auf Daten erhalten nur berechtigte Nutzerinnen und Nutzer
|
||||
innerhalb der jeweiligen Kita beziehungsweise des jeweiligen
|
||||
Elternvereins. Für Hosting, E-Mail-Versand oder technische
|
||||
Dienste können Auftragsverarbeiter eingesetzt werden:
|
||||
[Hosting-Anbieter], [E-Mail-Anbieter], [weitere Dienstleister].
|
||||
Elternvereins. Für Hosting, E-Mail-Versand oder technische Dienste
|
||||
können Auftragsverarbeiter eingesetzt werden: [Hosting-Anbieter],
|
||||
[E-Mail-Anbieter], [weitere Dienstleister].
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -180,9 +179,9 @@ export default function DatenschutzPage() {
|
||||
<p className="mt-3 leading-7 text-slate-700">
|
||||
Kita-Planer verwendet technisch notwendige Cookies oder
|
||||
vergleichbare Technologien, um Login-Sitzungen sicher
|
||||
bereitzustellen. Für die öffentliche Website kann zusätzlich
|
||||
Umami Analytics eingesetzt werden, um Seitenaufrufe anonymisiert
|
||||
und ohne Cookies auszuwerten.
|
||||
bereitzustellen. Für die öffentliche Website kann zusätzlich Umami
|
||||
Analytics eingesetzt werden, um Seitenaufrufe anonymisiert und
|
||||
ohne Cookies auszuwerten.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -220,9 +219,9 @@ export default function DatenschutzPage() {
|
||||
</h2>
|
||||
<p className="mt-3 leading-7 text-slate-700">
|
||||
Betroffene Personen können sich bei einer
|
||||
Datenschutzaufsichtsbehörde beschweren. Zuständig ist
|
||||
insbesondere die Aufsichtsbehörde am Sitz der verantwortlichen
|
||||
Stelle: [zuständige Datenschutzaufsichtsbehörde].
|
||||
Datenschutzaufsichtsbehörde beschweren. Zuständig ist insbesondere
|
||||
die Aufsichtsbehörde am Sitz der verantwortlichen Stelle:
|
||||
[zuständige Datenschutzaufsichtsbehörde].
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { StructuredData } from "@/components/marketing/structured-data";
|
||||
import { featureStructuredData, publicPageMetadata } from "@/lib/marketing/seo";
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
@@ -48,30 +50,11 @@ export async function generateMetadata({
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
title: feature.seoTitle,
|
||||
description: feature.seoDescription,
|
||||
openGraph: {
|
||||
title: feature.seoTitle,
|
||||
description: feature.seoDescription,
|
||||
type: "website",
|
||||
locale: "de_DE",
|
||||
images: [
|
||||
{
|
||||
url: "/og-image.png",
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: "Ein Kind spielt mit bunten Holzklötzen.",
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: feature.seoTitle,
|
||||
description: feature.seoDescription,
|
||||
images: ["/og-image.png"],
|
||||
},
|
||||
};
|
||||
return publicPageMetadata(
|
||||
`/features/${feature.slug}`,
|
||||
feature.seoTitle,
|
||||
feature.seoDescription,
|
||||
);
|
||||
}
|
||||
|
||||
export default async function FeaturePage({ params }: FeaturePageProps) {
|
||||
@@ -86,22 +69,30 @@ export default async function FeaturePage({ params }: FeaturePageProps) {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#f8faf8] text-slate-950">
|
||||
<StructuredData data={featureStructuredData(feature)} />
|
||||
<header className="border-b border-slate-200 bg-white/95">
|
||||
<div className="mx-auto flex h-20 w-full max-w-7xl items-center justify-between px-5 sm:px-6">
|
||||
<Link href="/" className="flex items-center gap-3 text-slate-950">
|
||||
<span className="flex h-9 w-9 items-center justify-center rounded-lg bg-slate-950 text-white">
|
||||
<Link
|
||||
href="/"
|
||||
className="flex shrink-0 items-center gap-2 text-slate-950 sm:gap-3"
|
||||
>
|
||||
<span className="flex h-8 w-8 items-center justify-center rounded-lg bg-slate-950 text-white sm:h-9 sm:w-9">
|
||||
<ShieldCheck className="h-5 w-5" />
|
||||
</span>
|
||||
<span className="text-base font-semibold tracking-tight">
|
||||
<span className="whitespace-nowrap text-sm font-semibold tracking-tight sm:text-base">
|
||||
Kita-Planer
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
<nav className="flex items-center gap-2">
|
||||
<Button asChild variant="ghost">
|
||||
<nav className="flex items-center gap-1 sm:gap-2">
|
||||
<Button
|
||||
asChild
|
||||
variant="ghost"
|
||||
className="px-3 text-xs sm:px-4 sm:text-sm"
|
||||
>
|
||||
<Link href="/login">Login</Link>
|
||||
</Button>
|
||||
<Button asChild>
|
||||
<Button asChild className="px-3 text-xs sm:px-4 sm:text-sm">
|
||||
<Link href="/#kontakt">Demo anfragen</Link>
|
||||
</Button>
|
||||
</nav>
|
||||
@@ -181,7 +172,7 @@ export default async function FeaturePage({ params }: FeaturePageProps) {
|
||||
<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>
|
||||
<CardTitle className="text-2xl tracking-tight">
|
||||
<CardTitle as="h2" className="text-2xl tracking-tight">
|
||||
{feature.problemTitle}
|
||||
</CardTitle>
|
||||
<CardDescription className="text-base leading-7 text-slate-600">
|
||||
@@ -192,7 +183,7 @@ export default async function FeaturePage({ params }: FeaturePageProps) {
|
||||
|
||||
<Card className="border-slate-200 bg-white">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-2xl tracking-tight">
|
||||
<CardTitle as="h2" className="text-2xl tracking-tight">
|
||||
{feature.solutionTitle}
|
||||
</CardTitle>
|
||||
<CardDescription className="text-base leading-7 text-slate-600">
|
||||
@@ -220,7 +211,7 @@ export default async function FeaturePage({ params }: FeaturePageProps) {
|
||||
<div className="mb-3 flex h-10 w-10 items-center justify-center rounded-lg bg-emerald-50 text-emerald-700 ring-1 ring-emerald-100">
|
||||
<CheckCircle2 className="h-5 w-5" />
|
||||
</div>
|
||||
<CardTitle className="text-xl tracking-tight">
|
||||
<CardTitle as="h3" className="text-xl tracking-tight">
|
||||
{benefit.title}
|
||||
</CardTitle>
|
||||
<CardDescription className="text-sm leading-6 text-slate-600">
|
||||
@@ -273,7 +264,7 @@ export default async function FeaturePage({ params }: FeaturePageProps) {
|
||||
<div className="mb-3 flex h-10 w-10 items-center justify-center rounded-lg bg-slate-100 text-slate-700">
|
||||
<HelpCircle className="h-5 w-5" />
|
||||
</div>
|
||||
<CardTitle className="text-lg leading-snug">
|
||||
<CardTitle as="h3" className="text-lg leading-snug">
|
||||
{faq.question}
|
||||
</CardTitle>
|
||||
<CardDescription className="text-sm leading-6 text-slate-600">
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import type { Metadata } from "next";
|
||||
import { publicPageMetadata } from "@/lib/marketing/seo";
|
||||
import Link from "next/link";
|
||||
import { ArrowLeft, Building2, Mail, MapPin, ShieldCheck } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Impressum | Kita-Planer",
|
||||
description: "Impressum und Anbieterkennzeichnung von Kita-Planer.",
|
||||
};
|
||||
export const metadata = publicPageMetadata(
|
||||
"/impressum",
|
||||
"Impressum",
|
||||
"Impressum und Anbieterkennzeichnung von Kita-Planer.",
|
||||
);
|
||||
|
||||
const contactItems = [
|
||||
{
|
||||
@@ -117,8 +118,8 @@ export default function ImpressumPage() {
|
||||
Umsatzsteuer-ID
|
||||
</h2>
|
||||
<p className="mt-3 leading-7 text-slate-700">
|
||||
Umsatzsteuer-Identifikationsnummer gemäß § 27a
|
||||
Umsatzsteuergesetz: -
|
||||
Umsatzsteuer-Identifikationsnummer gemäß § 27a Umsatzsteuergesetz:
|
||||
-
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -126,9 +127,7 @@ export default function ImpressumPage() {
|
||||
<h2 className="text-xl font-semibold tracking-tight">
|
||||
Verantwortlich für den Inhalt
|
||||
</h2>
|
||||
<p className="mt-3 leading-7 text-slate-700">
|
||||
Torben Indorf
|
||||
</p>
|
||||
<p className="mt-3 leading-7 text-slate-700">Torben Indorf</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
+23
-29
@@ -1,6 +1,12 @@
|
||||
import { getSession } from "@/lib/auth-utils";
|
||||
import { headers } from "next/headers";
|
||||
import type { Metadata } from "next";
|
||||
import {
|
||||
getPublicSiteOrigin,
|
||||
marketingDescription,
|
||||
marketingTitle,
|
||||
socialImage,
|
||||
} from "@/lib/marketing/seo";
|
||||
import { Manrope } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
@@ -13,36 +19,24 @@ const manrope = Manrope({
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL(
|
||||
process.env.NEXT_PUBLIC_SITE_URL ?? "https://mein-kitaplaner.de",
|
||||
),
|
||||
title: {
|
||||
default: "Der digitale Kita-Planer für Elternvereine",
|
||||
template: "%s | Kita-Planer",
|
||||
},
|
||||
description:
|
||||
"Kita-Planer bündelt Dienste, Termine, Notdienste, Abwesenheiten und sichere Kommunikation für Elternvereine an einem Ort.",
|
||||
metadataBase: new URL(getPublicSiteOrigin()),
|
||||
title: { default: marketingTitle, template: "%s | Kita-Planer" },
|
||||
description: marketingDescription,
|
||||
// Public marketing pages opt in explicitly; account and internal pages stay out.
|
||||
robots: { index: false, follow: false },
|
||||
openGraph: {
|
||||
title: "Der digitale Kita-Planer für Elternvereine",
|
||||
description:
|
||||
"Organisiert Dienste, teilt Neuigkeiten und behaltet Termine gemeinsam im Blick.",
|
||||
title: marketingTitle,
|
||||
description: marketingDescription,
|
||||
type: "website",
|
||||
locale: "de_DE",
|
||||
images: [
|
||||
{
|
||||
url: "/og-image.png",
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: "Ein Kind spielt mit bunten Holzklötzen.",
|
||||
},
|
||||
],
|
||||
siteName: "Kita-Planer",
|
||||
images: [socialImage],
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: "Der digitale Kita-Planer für Elternvereine",
|
||||
description:
|
||||
"Organisiert Dienste, teilt Neuigkeiten und behaltet Termine gemeinsam im Blick.",
|
||||
images: ["/og-image.png"],
|
||||
title: marketingTitle,
|
||||
description: marketingDescription,
|
||||
images: [{ url: socialImage.url, alt: socialImage.alt }],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -51,13 +45,13 @@ export default async function RootLayout({
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const [requestHeaders, session] = await Promise.all([headers(), getSession()]);
|
||||
const [requestHeaders, session] = await Promise.all([
|
||||
headers(),
|
||||
getSession(),
|
||||
]);
|
||||
const nonce = requestHeaders.get("x-nonce") ?? undefined;
|
||||
return (
|
||||
<html
|
||||
lang="de"
|
||||
className={`${manrope.variable} h-full antialiased`}
|
||||
>
|
||||
<html lang="de" className={`${manrope.variable} h-full antialiased`}>
|
||||
<body className="min-h-full flex flex-col">
|
||||
{children}
|
||||
<UmamiAnalytics nonce={nonce} enabled={!session?.user?.id} />
|
||||
|
||||
+111
-79
@@ -1,4 +1,3 @@
|
||||
import type { Metadata } from "next";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
@@ -35,38 +34,20 @@ import {
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { marketingFeatures } from "@/lib/marketing/features";
|
||||
import { StructuredData } from "@/components/marketing/structured-data";
|
||||
import {
|
||||
landingStructuredData,
|
||||
marketingDescription,
|
||||
marketingTitle,
|
||||
publicPageMetadata,
|
||||
} from "@/lib/marketing/seo";
|
||||
import { ContactForm } from "./contact-form";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Kita-Planer für Elterninitiativen und Kita-Vereine",
|
||||
description:
|
||||
"Die einfache Plattform für Elterninitiativen, freie Kitas und Kita-Vereine: organisiert Dienste, Termine, Krankmeldungen und offizielle Kommunikation an einem Ort.",
|
||||
alternates: {
|
||||
canonical: "/",
|
||||
},
|
||||
openGraph: {
|
||||
title: "Kita-Planer für Elterninitiativen und Kita-Vereine",
|
||||
description:
|
||||
"Weniger Chat-Chaos, weniger Excel-Listen, mehr Ruhe im ehrenamtlichen Kita-Alltag.",
|
||||
type: "website",
|
||||
locale: "de_DE",
|
||||
images: [
|
||||
{
|
||||
url: "/og-image.png",
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: "Ein Kind spielt mit bunten Holzklötzen.",
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: "Kita-Planer für Elterninitiativen und Kita-Vereine",
|
||||
description:
|
||||
"Organisiert Dienste, Termine, Krankmeldungen und offizielle Kommunikation an einem Ort.",
|
||||
images: ["/og-image.png"],
|
||||
},
|
||||
};
|
||||
export const metadata = publicPageMetadata(
|
||||
"/",
|
||||
marketingTitle,
|
||||
marketingDescription,
|
||||
);
|
||||
|
||||
const heroImage =
|
||||
"https://images.unsplash.com/photo-1503454537195-1dcabb73ffb9?auto=format&fit=crop&w=2400&q=86";
|
||||
@@ -74,13 +55,13 @@ const heroImage =
|
||||
const privacyPoints = [
|
||||
{
|
||||
icon: DatabaseZap,
|
||||
title: "Strikt isolierte Mandanten",
|
||||
text: "Jede Kita arbeitet technisch streng getrennt pro Kita. Rollen und Datenabfragen bleiben konsequent tenant-isoliert.",
|
||||
title: "Getrennte Daten je Kita",
|
||||
text: "Die Daten eurer Kita sind von anderen Einrichtungen getrennt. Zugriff erhalten nur die berechtigten Konten eurer Kita.",
|
||||
},
|
||||
{
|
||||
icon: Fingerprint,
|
||||
title: "Kontaktfreigabe per Opt-in",
|
||||
text: "Adressbuchdaten erscheinen nur nach aktivem Opt-In pro Elternteil. Nicht freigegebene Kontaktdaten verlassen den Server nicht.",
|
||||
text: "Jeder Elternteil entscheidet über die Freigabe der eigenen Kontaktdaten. Im internen Adressbuch erscheinen nur freigegebene Angaben.",
|
||||
},
|
||||
{
|
||||
icon: Trash2,
|
||||
@@ -90,23 +71,23 @@ const privacyPoints = [
|
||||
{
|
||||
icon: LockKeyhole,
|
||||
title: "Geschützte Anhänge",
|
||||
text: "Dokumente vom Schwarzen Brett liegen nicht öffentlich und werden nur nach Session- und Kita-Prüfung ausgeliefert.",
|
||||
text: "Anhänge sind nur nach Anmeldung und für die gewählte Empfängergruppe eurer Kita abrufbar.",
|
||||
},
|
||||
{
|
||||
icon: UserCog,
|
||||
title: "Rollenrechte pro Kita",
|
||||
text: "Vorstand, Koordination, Eltern und Team sehen nur die Bereiche, die zu ihrer Aufgabe und Kita-Zugehörigkeit passen.",
|
||||
text: "Kita-Admins vergeben Rechte für Verwaltung, Notdienst-Koordination, Eltern und Team. Ämter wie Vorstand sind von Zugriffsrechten getrennt.",
|
||||
},
|
||||
{
|
||||
icon: FileStack,
|
||||
title: "Klare Datenpfade",
|
||||
text: "Löschungen sind bewusst vorgesehen; Fragen zu Export und Übergabe werden im Setup sauber geklärt.",
|
||||
title: "Kontozugang selbst verwalten",
|
||||
text: "Aktivierte Konten können ihr Passwort im Profil ändern oder einen zeitlich begrenzten Wiederherstellungslink anfordern.",
|
||||
},
|
||||
];
|
||||
|
||||
const proofPoints = [
|
||||
"Für Elterninitiativen, freie Kitas und Kita-Vereine",
|
||||
"Dienste, Kalender, Abwesenheiten und offizielle Infos in einem System",
|
||||
"Dienste, Kalender, Arbeitszeit und offizielle Infos in einem System",
|
||||
"Gebaut für Vorstände, Eltern, Koordination und Team",
|
||||
];
|
||||
|
||||
@@ -114,13 +95,13 @@ const replacedTools = [
|
||||
{
|
||||
icon: MessagesSquare,
|
||||
title: "WhatsApp für offizielle Infos",
|
||||
text: "Ankündigungen, Anhänge und Lesestatus gehören in einen verbindlichen Kanal, nicht zwischen spontane Rückfragen.",
|
||||
text: "Ankündigungen mit Anhängen, sichtbaren Korrekturen und optionalen E-Mails bleiben auch später auffindbar.",
|
||||
accent: "bg-emerald-50 text-emerald-700 ring-emerald-100",
|
||||
},
|
||||
{
|
||||
icon: Table2,
|
||||
title: "Excel-Listen für Dienste",
|
||||
text: "Verfügbarkeiten, Mindesttage und Dienstpläne werden strukturierter als in Tabellen mit Version 17-final-final.",
|
||||
text: "Verfügbarkeiten, Erinnerungen und veröffentlichte Dienstpläne bleiben nachvollziehbar. Bei Bedarf lässt sich die Einteilung manuell anpassen.",
|
||||
accent: "bg-lime-50 text-lime-700 ring-lime-100",
|
||||
},
|
||||
{
|
||||
@@ -147,17 +128,22 @@ const audienceRoles = [
|
||||
{
|
||||
icon: UserCog,
|
||||
title: "Vorstand & Admin",
|
||||
text: "Kita, Familien, Termine, Einladungen und Rechte verwalten, ohne dass alles an einer privaten Ablage hängt.",
|
||||
text: "Familien einladen, Zugriffsrechte vergeben, Mitteilungen korrigieren und Versandfehler gezielt erneut versuchen.",
|
||||
},
|
||||
{
|
||||
icon: UsersRound,
|
||||
title: "Eltern",
|
||||
text: "Termine sehen, Kinder krankmelden, Verfügbarkeiten eintragen und freigegebene Kontakte finden.",
|
||||
text: "Verfügbarkeiten eintragen, zu Terminen zusagen, Mitbringbeiträge verwalten und zukünftige Abwesenheiten korrigieren.",
|
||||
},
|
||||
{
|
||||
icon: HeartHandshake,
|
||||
title: "ErzieherInnen & Koordination",
|
||||
text: "Tagesüberblick, Abwesenheiten, Dienste und relevante Informationen an einem Ort sehen.",
|
||||
title: "Kita-Team",
|
||||
text: "Arbeitszeit erfassen, Wochen einreichen und Urlaub oder Freizeitausgleich beantragen. Abwesenheiten und Teammitteilungen bleiben im Blick.",
|
||||
},
|
||||
{
|
||||
icon: HeartHandshake,
|
||||
title: "Notdienst-Koordination",
|
||||
text: "Verfügbarkeiten prüfen, Monatspläne veröffentlichen und bei Ausfällen mit Rückmeldungen aus den Haushalten weiterplanen.",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -190,28 +176,32 @@ const onboardingSteps = [
|
||||
export default function LandingPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-[#f8faf8] text-slate-950">
|
||||
<StructuredData data={landingStructuredData(marketingFeatures)} />
|
||||
<header className="absolute left-0 right-0 top-0 z-20">
|
||||
<div className="mx-auto flex min-h-20 w-full max-w-7xl items-center justify-between gap-3 px-5 py-4 sm:px-6">
|
||||
<Link href="/" className="flex items-center gap-3 text-white">
|
||||
<span className="flex h-9 w-9 items-center justify-center rounded-lg bg-white/12 ring-1 ring-white/30">
|
||||
<Link
|
||||
href="/"
|
||||
className="flex shrink-0 items-center gap-2 text-white sm:gap-3"
|
||||
>
|
||||
<span className="flex h-8 w-8 items-center justify-center rounded-lg bg-white/12 ring-1 ring-white/30 sm:h-9 sm:w-9">
|
||||
<ShieldCheck className="h-5 w-5" />
|
||||
</span>
|
||||
<span className="text-base font-semibold tracking-tight">
|
||||
<span className="whitespace-nowrap text-sm font-semibold tracking-tight sm:text-base">
|
||||
Kita-Planer
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
<nav className="flex flex-wrap items-center justify-end gap-2">
|
||||
<nav className="flex flex-wrap items-center justify-end gap-1 sm:gap-2">
|
||||
<Button
|
||||
asChild
|
||||
variant="ghost"
|
||||
className="text-white hover:bg-white/10 hover:text-white"
|
||||
className="px-3 text-xs text-white hover:bg-white/10 hover:text-white sm:px-4 sm:text-sm"
|
||||
>
|
||||
<Link href="/login">Login</Link>
|
||||
</Button>
|
||||
<Button
|
||||
asChild
|
||||
className="bg-white text-slate-950 shadow-sm hover:bg-white/90"
|
||||
className="bg-white px-3 text-xs text-slate-950 shadow-sm hover:bg-white/90 sm:px-4 sm:text-sm"
|
||||
>
|
||||
<Link href="/#kontakt">Demo anfragen</Link>
|
||||
</Button>
|
||||
@@ -245,8 +235,8 @@ export default function LandingPage() {
|
||||
</h1>
|
||||
<p className="mt-6 max-w-2xl text-lg leading-8 text-white/82 sm:text-xl">
|
||||
Für Elterninitiativen, freie Kitas und Kita-Vereine, die
|
||||
Dienste, Termine, Krankmeldungen und offizielle Kommunikation
|
||||
ehrenamtlich organisieren.
|
||||
Dienste, Termine, Abwesenheiten, Arbeitszeit und offizielle
|
||||
Kommunikation gemeinsam organisieren.
|
||||
</p>
|
||||
|
||||
<div className="mt-9 flex flex-col gap-3 sm:flex-row">
|
||||
@@ -294,9 +284,10 @@ 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, zeigt offene
|
||||
Familien und macht aus losen Antworten einen veröffentlichbaren
|
||||
Monatsplan.
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -386,11 +377,14 @@ export default function LandingPage() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mx-auto w-full max-w-7xl px-5 py-24 sm:px-6">
|
||||
<section
|
||||
id="funktionen"
|
||||
className="mx-auto w-full max-w-7xl px-5 py-24 sm:px-6"
|
||||
>
|
||||
<div className="mb-12 flex flex-col gap-5 lg:flex-row lg:items-end lg:justify-between">
|
||||
<div className="max-w-3xl">
|
||||
<p className="mb-3 text-sm font-semibold uppercase tracking-wide text-emerald-700">
|
||||
Die 5 Säulen
|
||||
Die {marketingFeatures.length} Bereiche
|
||||
</p>
|
||||
<h2 className="text-balance text-3xl font-semibold leading-tight sm:text-4xl">
|
||||
Plant Dienste, teilt Neuigkeiten und behaltet Termine gemeinsam
|
||||
@@ -405,15 +399,12 @@ export default function LandingPage() {
|
||||
</div>
|
||||
|
||||
<div className="grid gap-5 md:grid-cols-2 xl:grid-cols-6">
|
||||
{marketingFeatures.map((feature, index) => {
|
||||
{marketingFeatures.map((feature) => {
|
||||
const Icon = feature.icon;
|
||||
const isWide = index > 2;
|
||||
return (
|
||||
<Card
|
||||
key={feature.title}
|
||||
className={`group border-slate-200 bg-white/90 shadow-sm transition duration-200 hover:-translate-y-1 hover:border-slate-300 hover:shadow-lg ${
|
||||
isWide ? "xl:col-span-3" : "xl:col-span-2"
|
||||
}`}
|
||||
className="group border-slate-200 bg-white/90 shadow-sm transition duration-200 hover:-translate-y-1 hover:border-slate-300 hover:shadow-lg xl:col-span-2"
|
||||
>
|
||||
<CardHeader className="space-y-5">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
@@ -440,7 +431,10 @@ export default function LandingPage() {
|
||||
variant="link"
|
||||
className="mt-4 h-auto p-0 text-slate-950"
|
||||
>
|
||||
<Link href={`/features/${feature.slug}`}>
|
||||
<Link
|
||||
href={`/features/${feature.slug}`}
|
||||
aria-label={`Mehr über ${feature.shortTitle} erfahren`}
|
||||
>
|
||||
Mehr erfahren
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</Link>
|
||||
@@ -508,9 +502,9 @@ export default function LandingPage() {
|
||||
</h2>
|
||||
<p className="mt-5 max-w-xl text-base leading-7 text-slate-300">
|
||||
Kita-Vereine verwalten besonders sensible Daten. Deshalb ist
|
||||
Kita-Planer so gebaut, dass Sichtbarkeit, Rollenrechte,
|
||||
Löschung und Mandantentrennung nicht vom guten Willen einzelner
|
||||
Chats abhängen.
|
||||
Kita-Planer so gebaut, dass Sichtbarkeit, Rollenrechte, Löschung
|
||||
und Mandantentrennung nicht vom guten Willen einzelner Chats
|
||||
abhängen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -644,10 +638,10 @@ function ProductBrowserMock() {
|
||||
<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-4 py-1 text-xs font-medium text-slate-500">
|
||||
app.kita-planer.de/dashboard/notdienst
|
||||
<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="w-16" />
|
||||
<div className="hidden w-16 sm:block" />
|
||||
</div>
|
||||
|
||||
<div className="bg-[#fbf8ef] p-2 sm:p-4">
|
||||
@@ -675,14 +669,23 @@ function FeaturePreview({ slug }: { slug: string }) {
|
||||
</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}`}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
{[
|
||||
"",
|
||||
"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>
|
||||
);
|
||||
@@ -748,11 +751,40 @@ function FeaturePreview({ slug }: { slug: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
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" />
|
||||
Neue Mitteilung
|
||||
Aktualisierte Mitteilung
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<span className="block h-2.5 w-4/5 rounded-full bg-slate-200" />
|
||||
|
||||
+8
-10
@@ -1,28 +1,26 @@
|
||||
import type { MetadataRoute } from "next";
|
||||
|
||||
function getSiteUrl() {
|
||||
return process.env.NEXT_PUBLIC_SITE_URL ?? "https://mein-kitaplaner.de";
|
||||
}
|
||||
import { getPublicSiteOrigin, publicUrl } from "@/lib/marketing/seo";
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
const siteUrl = getSiteUrl();
|
||||
const siteUrl = getPublicSiteOrigin();
|
||||
|
||||
return {
|
||||
rules: {
|
||||
userAgent: "*",
|
||||
allow: ["/", "/features/", "/impressum", "/datenschutz"],
|
||||
// Login/registration/recovery forms stay crawlable so bots can read noindex.
|
||||
disallow: [
|
||||
"/api/",
|
||||
"/dashboard/",
|
||||
"/dashboard",
|
||||
"/admin",
|
||||
"/invite/",
|
||||
"/alert/",
|
||||
"/login",
|
||||
"/register",
|
||||
"/onboarding/",
|
||||
"/forbidden",
|
||||
"/onboarding",
|
||||
"/neues-passwort/",
|
||||
],
|
||||
},
|
||||
sitemap: `${siteUrl}/sitemap.xml`,
|
||||
sitemap: publicUrl("/sitemap.xml"),
|
||||
host: siteUrl,
|
||||
};
|
||||
}
|
||||
|
||||
+8
-19
@@ -2,36 +2,25 @@ import type { MetadataRoute } from "next";
|
||||
|
||||
import { marketingFeatures } from "@/lib/marketing/features";
|
||||
|
||||
function getSiteUrl() {
|
||||
return process.env.NEXT_PUBLIC_SITE_URL ?? "https://mein-kitaplaner.de";
|
||||
}
|
||||
import { marketingLastModified, publicUrl } from "@/lib/marketing/seo";
|
||||
|
||||
export default function sitemap(): MetadataRoute.Sitemap {
|
||||
const siteUrl = getSiteUrl();
|
||||
const lastModified = new Date();
|
||||
|
||||
return [
|
||||
{
|
||||
url: siteUrl,
|
||||
lastModified,
|
||||
changeFrequency: "weekly",
|
||||
url: publicUrl(),
|
||||
lastModified: marketingLastModified,
|
||||
changeFrequency: "monthly",
|
||||
priority: 1,
|
||||
},
|
||||
{ url: publicUrl("/impressum"), changeFrequency: "yearly", priority: 0.2 },
|
||||
{
|
||||
url: `${siteUrl}/impressum`,
|
||||
lastModified,
|
||||
changeFrequency: "yearly",
|
||||
priority: 0.2,
|
||||
},
|
||||
{
|
||||
url: `${siteUrl}/datenschutz`,
|
||||
lastModified,
|
||||
url: publicUrl("/datenschutz"),
|
||||
changeFrequency: "yearly",
|
||||
priority: 0.2,
|
||||
},
|
||||
...marketingFeatures.map((feature) => ({
|
||||
url: `${siteUrl}/features/${feature.slug}`,
|
||||
lastModified,
|
||||
url: publicUrl(`/features/${feature.slug}`),
|
||||
lastModified: marketingLastModified,
|
||||
changeFrequency: "monthly" as const,
|
||||
priority: 0.8,
|
||||
})),
|
||||
|
||||
Reference in New Issue
Block a user