"use client" import { useEffect, useState } from "react" import { AlertCircle, 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).`, accessibility: `## Erklärung zur Barrierefreiheit Diese Erklärung gilt für die Website **datenschutz.beispiel.de**. ### Stand der Vereinbarkeit mit den Anforderungen Diese Website ist mit der BITV 2.0 *[vollständig / teilweise / nicht]* vereinbar. ### Nicht barrierefreie Inhalte Hier die bekannten Einschränkungen und deren Begründung auflisten. ### Erstellung dieser Erklärung Diese Erklärung wurde am TT.MM.JJJJ erstellt. Grundlage war eine *[Selbstbewertung / Prüfung durch Dritte]*. Letzte Überprüfung: TT.MM.JJJJ ### Barrieren melden – Kontakt Sie können uns Mängel bei der Barrierefreiheit mitteilen und Informationen in barrierefreier Form anfordern: Name, E-Mail, Telefon ### Schlichtungsverfahren Wenn wir auf Ihre Meldung nicht zufriedenstellend reagieren, können Sie die Schlichtungsstelle nach § 16 BGG anrufen: Anschrift und Kontakt der zuständigen Schlichtungsstelle`, easyLanguage: `## Willkommen Das hier ist die Internet-Seite vom Amt Musterstadt. Hier steht: Was macht das Amt mit Ihren Daten? ## Was finden Sie auf dieser Seite? Sie finden hier eine Liste. In der Liste stehen viele Themen. Zum Beispiel: Bau-Antrag. Oder: Wohn-Geld. Zu jedem Thema gibt es einen Text. In dem Text steht: Welche Daten braucht das Amt? Und: Wie lange speichert das Amt die Daten? ## Wie finden Sie ein Thema? Oben auf der Seite ist ein Such-Feld. Dort können Sie ein Wort eintippen. Dann zeigt die Seite nur noch passende Themen. Sie können auch auf ein Thema in der Liste klicken. ## Barriere-Freiheit Barriere-frei heißt: Alle Menschen können die Seite benutzen. Wir arbeiten daran, dass die Seite barriere-frei wird. Manche Sachen sind noch nicht barriere-frei. Sie können uns das melden. Schreiben Sie an: *[E-Mail-Adresse eintragen]* *Dieser Text muss vor der Veröffentlichung von einer Prüf-Gruppe gegengelesen werden. Das ist bei Leichter Sprache üblich.*`, signLanguage: `## Informationen in Deutscher Gebärdensprache Das Video oben erklärt: - die wesentlichen Inhalte dieses Portals - wie die Navigation funktioniert - die wesentlichen Inhalte der Erklärung zur Barrierefreiheit Die Adresse des Videos tragen Sie oben in das Feld ein; es erscheint dann über diesem Text. ### Kontakt Fragen zur Barrierefreiheit richten Sie bitte an: *[Kontaktdaten eintragen]*`, } interface SitePagesFormProps { open: boolean onOpenChange: (open: boolean) => void } /** * Pflegt Impressum, Datenschutzerklärung und Erklärung zur Barrierefreiheit. * Ein leer gelassener Text nimmt die betreffende Seite vom Netz. */ 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} ))} {errors.length > 0 && (
    {errors.map((error, index) => (
  • {error}
  • ))}
)} {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, nur bei einer eigenen Videodatei nötig (WCAG 1.2.2). Ein Player-Anbieter bringt seine Untertitel selbst mit.

)} setFormData((prev) => ({ ...prev, [key]: value })) } placeholder={placeholders[key]} rows={20} />
))}
) }