"use client" import { FormErrors } from "@/components/form-errors" import { sitePageTemplates } from "@/lib/site-page-templates" import { useEffect, useState } from "react" import { ExternalLink } from "lucide-react" import Link from "next/link" import { toast } from "sonner" import { Button } from "@/components/ui/button" import { Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { MarkdownInput } from "@/components/markdown-input" import { updateSitePages, useSitePages } from "@/hooks/use-site-pages" import { ApiError } from "@/lib/api" import { EMPTY_SITE_PAGES, SITE_PAGES, SITE_PAGE_META, SitePageKey, SitePagesContent, } from "@/lib/site-pages" /** * Vorlagen als Platzhalter. Sie werden nicht gespeichert, sondern zeigen nur, * welche Angaben die jeweilige Seite enthalten muss. */ const placeholders: Record = { imprint: `## Angaben gemäß § 5 DDG **Amt Musterstadt** Musterstraße 1 12345 Musterstadt **Vertreten durch:** Amtsvorsteher/in Vorname Nachname **Kontakt** Telefon: 01234 5678-0 E-Mail: info@amt-musterstadt.de **Zuständige Aufsichtsbehörde** Name und Anschrift der Rechtsaufsicht **Verantwortlich für den Inhalt:** Vorname Nachname, Anschrift`, privacy: `## Datenschutzerklärung für dieses Portal Diese Erklärung betrifft die Verarbeitung Ihrer Daten beim **Besuch dieses Portals**. Die Merkblätter zu den einzelnen Verwaltungsverfahren finden Sie in der Übersicht. ### Verantwortlicher Name, Anschrift, Kontakt ### Datenschutzbeauftragte/r Name, Anschrift, E-Mail ### Server-Protokolldaten Beim Aufruf werden IP-Adresse, Zeitpunkt, aufgerufene Seite, übertragene Datenmenge und Browserkennung protokolliert. *Zweck:* technischer Betrieb und Abwehr von Angriffen *Rechtsgrundlage:* Art. 6 Abs. 1 lit. e DSGVO i. V. m. Landesrecht *Speicherdauer:* z. B. 7 Tage ### Cookies Ohne Anmeldung setzt das Portal keine Cookies. Im Admin-Bereich wird ein technisch notwendiges Sitzungs-Cookie verwendet. ### Ihre Rechte Auskunft (Art. 15), Berichtigung (Art. 16), Löschung (Art. 17), Einschränkung (Art. 18), Widerspruch (Art. 21) sowie Beschwerde bei der Aufsichtsbehörde (Art. 77 DSGVO).`, ...sitePageTemplates, } interface SitePagesFormProps { open: boolean onOpenChange: (open: boolean) => void } /** * Pflegt Impressum, Datenschutzerklärung und Erklärung zur Barrierefreiheit. * Veröffentlichte Seiten dürfen nicht versehentlich geleert werden. */ export function SitePagesForm({ open, onOpenChange }: SitePagesFormProps) { const { pages, isLoading } = useSitePages() const [formData, setFormData] = useState(EMPTY_SITE_PAGES) const [errors, setErrors] = useState([]) const [isSubmitting, setIsSubmitting] = useState(false) const [tab, setTab] = useState("imprint") // Beim Öffnen den gespeicherten Stand laden useEffect(() => { if (!open || isLoading) return setErrors([]) setTab("imprint") setFormData({ ...pages }) // `pages` bewusst nicht als Abhängigkeit: sonst würden Eingaben beim // Hintergrund-Refresh von SWR überschrieben. // eslint-disable-next-line react-hooks/exhaustive-deps }, [open, isLoading]) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setErrors([]) setIsSubmitting(true) try { await updateSitePages(formData) toast.success("Seiteninhalte gespeichert") onOpenChange(false) } catch (error) { const details = error instanceof ApiError && error.details.length > 0 ? error.details : [error instanceof Error ? error.message : "Fehler beim Speichern"] setErrors(details) } finally { setIsSubmitting(false) } } return ( Rechtliche Seiten Die eigenständigen Seiten des Portals: drei rechtliche Pflichtseiten sowie Leichte Sprache und Gebärdensprache nach § 4 BITV 2.0. Sie erscheinen in der Fußzeile der öffentlichen Seiten – ein leer gelassener Text nimmt die betreffende Seite vom Netz.
setTab(value as SitePageKey)} className="min-h-0 flex-1 gap-3" > {SITE_PAGES.map((key) => ( {SITE_PAGE_META[key].short} ))} {SITE_PAGES.map((key) => (
{SITE_PAGE_META[key].hint} {formData[key].trim() && ( )}
{/* Das Video steht in eigenen Feldern, nicht im Markdown: Der Markdown-Prozessor filtert HTML weg. */} {key === "signLanguage" && (
setFormData((prev) => ({ ...prev, signLanguageVideo: e.target.value, })) } placeholder="/videos/gebaerdensprache.mp4" />

Videodatei auf dem eigenen Server (beginnt mit /) oder Player-Adresse eines freigegebenen Anbieters. Derzeit freigegeben: alangu.de. Andere Videoportale sind bewusst gesperrt – sie übertragen Besucherdaten an Dritte.

setFormData((prev) => ({ ...prev, signLanguageCaptions: e.target.value, })) } placeholder="/videos/gebaerdensprache.vtt" />

WebVTT-Datei, für eigene Videodateien (WCAG 1.2.2). Bei eingebetteten Playern müssen die Untertitel dort eingerichtet und geprüft werden.

)} {key in sitePageTemplates && !formData[key].trim() && } setFormData((prev) => ({ ...prev, [key]: value })) } placeholder={placeholders[key]} rows={20} />
))}
) }