"use client" import { useEffect, useState } from "react" import { AlertCircle } from "lucide-react" 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 { Switch } from "@/components/ui/switch" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { MarkdownInput } from "@/components/markdown-input" import { createPolicy, updatePolicy } from "@/hooks/use-privacy-policies" import { ApiError } from "@/lib/api" import { FormData, PrivacyPolicy } from "@/types/privacy-policy" const initialFormData: FormData = { title: "", responsible: "", contactDPO: "", dataSubjectsRights: "", complaintRight: "", withdrawalRight: "", processingDescription: "", processingPurposes: "", legalBasis: "", recipients: "", storageDuration: "", dataSource: "", provisionObligation: "", provisionConsequences: "", isPublic: true, } type TabId = "base" | "processing" | "rights" const requiredFields: { field: keyof FormData; label: string; tab: TabId }[] = [ { field: "title", label: "Titel", tab: "base" }, { field: "responsible", label: "Verantwortlicher", tab: "base" }, { field: "contactDPO", label: "Datenschutzbeauftragte/r", tab: "base" }, { field: "processingDescription", label: "Verarbeitungstätigkeit", tab: "processing" }, { field: "recipients", label: "Empfänger", tab: "processing" }, { field: "dataSubjectsRights", label: "Betroffenen-Rechte", tab: "rights" }, { field: "complaintRight", label: "Beschwerderecht", tab: "rights" }, { field: "withdrawalRight", label: "Widerrufsrecht", tab: "rights" }, ] const placeholders = { responsible: `**Firmenname** Musterstraße 123 12345 Musterstadt *Vertretungsberechtigt:* Max Mustermann`, contactDPO: `**Datenschutzbeauftragte/r:** Max Mustermann **E-Mail:** datenschutz@beispiel.de **Telefon:** +49 123 456789`, dataSubjectsRights: `- **Auskunftsrecht** (Art. 15 DSGVO) - **Recht auf Berichtigung** (Art. 16 DSGVO) - **Recht auf Löschung** (Art. 17 DSGVO) - **Recht auf Einschränkung** (Art. 18 DSGVO) - **Recht auf Widerspruch** (Art. 21 DSGVO)`, complaintRight: `Sie haben das **Recht zur Beschwerde** bei einer Aufsichtsbehörde. **Zuständige Aufsichtsbehörde:** [Landesdatenschutzbehörde](https://www.datenschutz.de)`, withdrawalRight: `Soweit die Verarbeitung auf einer *Einwilligung* beruht, können Sie diese **jederzeit widerrufen** – per E-Mail an datenschutz@beispiel.de.`, processingDescription: `**Website-Betrieb und Nutzerinteraktion** Verarbeitung von Nutzerdaten zum Betrieb der Website und zur Bereitstellung unserer Dienstleistungen.`, processingPurposes: `1. **Website-Betrieb** – Bereitstellung der Funktionalität 2. **Kommunikation** – Beantwortung von Anfragen 3. **Verbesserung** – Analyse der Nutzung`, legalBasis: `- **Art. 6 Abs. 1 lit. a** – *Einwilligung* - **Art. 6 Abs. 1 lit. b** – *Vertragserfüllung* - **Art. 6 Abs. 1 lit. f** – *Berechtigte Interessen*`, recipients: `### Interne Empfänger - Kundenservice, IT-Abteilung ### Externe Dienstleister - **Hosting:** [Anbieter] - **E-Mail:** [Service]`, storageDuration: `- **Verträge:** 10 Jahre nach Vertragsende - **Newsletter:** bis zum Widerruf - **Website-Logs:** 30 Tage`, provisionObligation: `Wer ist zur Bereitstellung der Daten verpflichtet, z. B.: Eigentümer der Immobilie / Verbrauchsstelle`, provisionConsequences: `Was passiert, wenn die Daten nicht angegeben werden, z. B.: Verbrauchsschätzung`, dataSource: `- **Direkte Eingabe:** Kontaktformulare, Registrierung - **Website-Nutzung:** Cookies, Logs - **Öffentliche Quellen:** Handelsregister`, } interface PolicyFormProps { open: boolean onOpenChange: (open: boolean) => void policy: PrivacyPolicy | null } export function PolicyForm({ open, onOpenChange, policy }: PolicyFormProps) { const [formData, setFormData] = useState(initialFormData) const [errors, setErrors] = useState([]) const [invalidFields, setInvalidFields] = useState>(new Set()) const [isSubmitting, setIsSubmitting] = useState(false) const [tab, setTab] = useState("base") useEffect(() => { if (!open) return setErrors([]) setInvalidFields(new Set()) setTab("base") setFormData( policy ? { title: policy.title, responsible: policy.responsible, contactDPO: policy.contactDPO, dataSubjectsRights: policy.dataSubjectsRights, complaintRight: policy.complaintRight, withdrawalRight: policy.withdrawalRight, processingDescription: policy.processingDescription, processingPurposes: policy.processingPurposes || "", legalBasis: policy.legalBasis || "", recipients: policy.recipients, storageDuration: policy.storageDuration || "", dataSource: policy.dataSource || "", provisionObligation: policy.provisionObligation || "", provisionConsequences: policy.provisionConsequences || "", isPublic: policy.isPublic, } : initialFormData ) }, [open, policy]) const set = (key: K, value: FormData[K]) => { setFormData((prev) => ({ ...prev, [key]: value })) setInvalidFields((prev) => { if (!prev.has(key as string)) return prev const next = new Set(prev) next.delete(key as string) return next }) } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() const missing = requiredFields.filter( ({ field }) => !String(formData[field] ?? "").trim() ) if (missing.length > 0) { setErrors(missing.map(({ label }) => `${label} ist erforderlich`)) setInvalidFields(new Set(missing.map(({ field }) => field as string))) setTab(missing[0].tab) return } setErrors([]) setInvalidFields(new Set()) setIsSubmitting(true) try { if (policy) { await updatePolicy(policy.id, formData) toast.success("Datenschutzerklärung aktualisiert") } else { await createPolicy(formData) toast.success("Datenschutzerklärung erstellt") } 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) } } const isInvalid = (field: keyof FormData) => invalidFields.has(field as string) return ( {policy ? "Datenschutzerklärung bearbeiten" : "Neue Datenschutzerklärung"} Alle Pflichtfelder (*) ausfüllen. Die Felder unterstützen Markdown.
setTab(value as TabId)} className="min-h-0 flex-1 gap-3" >
Stammdaten Verarbeitung Rechte
set("isPublic", checked)} />
{errors.length > 0 && (
    {errors.map((error, index) => (
  • {error}
  • ))}
)}
set("title", e.target.value)} placeholder="z. B. Datenschutzerklärung für die Website" aria-invalid={isInvalid("title")} />
set("responsible", value)} placeholder={placeholders.responsible} invalid={isInvalid("responsible")} required /> set("contactDPO", value)} placeholder={placeholders.contactDPO} invalid={isInvalid("contactDPO")} required />
set("processingDescription", value)} placeholder={placeholders.processingDescription} invalid={isInvalid("processingDescription")} required /> set("processingPurposes", value)} placeholder={placeholders.processingPurposes} /> set("legalBasis", value)} placeholder={placeholders.legalBasis} /> set("recipients", value)} placeholder={placeholders.recipients} invalid={isInvalid("recipients")} required /> set("storageDuration", value)} placeholder={placeholders.storageDuration} /> set("dataSource", value)} placeholder={placeholders.dataSource} /> set("dataSubjectsRights", value)} placeholder={placeholders.dataSubjectsRights} invalid={isInvalid("dataSubjectsRights")} rows={9} required />
set("complaintRight", value)} placeholder={placeholders.complaintRight} invalid={isInvalid("complaintRight")} required /> set("withdrawalRight", value)} placeholder={placeholders.withdrawalRight} invalid={isInvalid("withdrawalRight")} required /> set("provisionObligation", value)} placeholder={placeholders.provisionObligation} rows={4} /> set("provisionConsequences", value)} placeholder={placeholders.provisionConsequences} rows={4} />
) }