Files
breakpilot-compliance/admin-compliance/app/sdk/einwilligungen/privacy-policy/page.tsx
Benjamin Admin 215b95adfa
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 32s
CI / test-python-backend-compliance (push) Successful in 31s
CI / test-python-document-crawler (push) Successful in 21s
CI / test-python-dsms-gateway (push) Successful in 19s
refactor: Admin-Layout komplett entfernt — SDK als einziges Layout
Kaputtes (admin) Layout geloescht (Role-Selection, 404-Sidebar, localhost-Dashboard).
SDK-Flow nach /sdk/sdk-flow verschoben. Route-Gruppe (sdk) aufgeloest.
Root-Seite redirected auf /sdk. ~25 ungenutzte Dateien/Verzeichnisse entfernt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 11:43:00 +01:00

415 lines
14 KiB
TypeScript

'use client'
/**
* Privacy Policy Generator Seite
*
* Generiert Datenschutzerklaerungen aus dem Datenpunktkatalog.
*/
import { useState, useEffect } from 'react'
import { useSDK } from '@/lib/sdk'
import { StepHeader, STEP_EXPLANATIONS } from '@/components/sdk/StepHeader'
import { PrivacyPolicyPreview } from '@/components/sdk/einwilligungen'
import {
EinwilligungenProvider,
useEinwilligungen,
} from '@/lib/sdk/einwilligungen/context'
import {
PREDEFINED_DATA_POINTS,
} from '@/lib/sdk/einwilligungen/catalog/loader'
import {
generatePrivacyPolicy,
} from '@/lib/sdk/einwilligungen/generator/privacy-policy'
import {
CompanyInfo,
SupportedLanguage,
ExportFormat,
GeneratedPrivacyPolicy,
} from '@/lib/sdk/einwilligungen/types'
import {
Building2,
Mail,
Phone,
Globe,
User,
Save,
ArrowLeft,
} from 'lucide-react'
import Link from 'next/link'
// =============================================================================
// COMPANY INFO FORM
// =============================================================================
interface CompanyInfoFormProps {
companyInfo: CompanyInfo | null
onChange: (info: CompanyInfo) => void
}
function CompanyInfoForm({ companyInfo, onChange }: CompanyInfoFormProps) {
const [formData, setFormData] = useState<CompanyInfo>(
companyInfo || {
name: '',
address: '',
city: '',
postalCode: '',
country: 'Deutschland',
email: '',
phone: '',
website: '',
dpoName: '',
dpoEmail: '',
dpoPhone: '',
registrationNumber: '',
vatId: '',
}
)
const handleChange = (field: keyof CompanyInfo, value: string) => {
const updated = { ...formData, [field]: value }
setFormData(updated)
onChange(updated)
}
return (
<div className="bg-white rounded-xl border border-slate-200 p-6 space-y-6">
<div className="flex items-center gap-3 border-b border-slate-200 pb-4">
<Building2 className="w-5 h-5 text-slate-400" />
<h3 className="font-semibold text-slate-900">Unternehmensdaten</h3>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Company Name */}
<div className="md:col-span-2">
<label className="block text-sm font-medium text-slate-700 mb-1">
Firmenname *
</label>
<input
type="text"
value={formData.name}
onChange={(e) => handleChange('name', e.target.value)}
placeholder="Muster GmbH"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
required
/>
</div>
{/* Address */}
<div className="md:col-span-2">
<label className="block text-sm font-medium text-slate-700 mb-1">
Strasse & Hausnummer *
</label>
<input
type="text"
value={formData.address}
onChange={(e) => handleChange('address', e.target.value)}
placeholder="Musterstrasse 123"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
required
/>
</div>
{/* Postal Code */}
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">
PLZ *
</label>
<input
type="text"
value={formData.postalCode}
onChange={(e) => handleChange('postalCode', e.target.value)}
placeholder="12345"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
required
/>
</div>
{/* City */}
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">
Stadt *
</label>
<input
type="text"
value={formData.city}
onChange={(e) => handleChange('city', e.target.value)}
placeholder="Musterstadt"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
required
/>
</div>
{/* Country */}
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">
Land
</label>
<input
type="text"
value={formData.country}
onChange={(e) => handleChange('country', e.target.value)}
placeholder="Deutschland"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
</div>
{/* Email */}
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">
E-Mail *
</label>
<input
type="email"
value={formData.email}
onChange={(e) => handleChange('email', e.target.value)}
placeholder="datenschutz@example.de"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
required
/>
</div>
{/* Phone */}
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">
Telefon
</label>
<input
type="tel"
value={formData.phone || ''}
onChange={(e) => handleChange('phone', e.target.value)}
placeholder="+49 123 456789"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
</div>
{/* Website */}
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">
Website
</label>
<input
type="url"
value={formData.website || ''}
onChange={(e) => handleChange('website', e.target.value)}
placeholder="https://example.de"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
</div>
{/* Registration Number */}
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">
Handelsregister
</label>
<input
type="text"
value={formData.registrationNumber || ''}
onChange={(e) => handleChange('registrationNumber', e.target.value)}
placeholder="HRB 12345 Amtsgericht Musterstadt"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
</div>
{/* VAT ID */}
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">
USt-IdNr.
</label>
<input
type="text"
value={formData.vatId || ''}
onChange={(e) => handleChange('vatId', e.target.value)}
placeholder="DE123456789"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
</div>
</div>
{/* DPO Section */}
<div className="border-t border-slate-200 pt-6">
<div className="flex items-center gap-3 mb-4">
<User className="w-5 h-5 text-slate-400" />
<h4 className="font-medium text-slate-900">Datenschutzbeauftragter (optional)</h4>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">
Name
</label>
<input
type="text"
value={formData.dpoName || ''}
onChange={(e) => handleChange('dpoName', e.target.value)}
placeholder="Max Mustermann"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">
E-Mail
</label>
<input
type="email"
value={formData.dpoEmail || ''}
onChange={(e) => handleChange('dpoEmail', e.target.value)}
placeholder="dsb@example.de"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">
Telefon
</label>
<input
type="tel"
value={formData.dpoPhone || ''}
onChange={(e) => handleChange('dpoPhone', e.target.value)}
placeholder="+49 123 456780"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
</div>
</div>
</div>
</div>
)
}
// =============================================================================
// PRIVACY POLICY CONTENT
// =============================================================================
function PrivacyPolicyContent() {
const { state } = useSDK()
const {
allDataPoints,
state: einwilligungenState,
} = useEinwilligungen()
const [companyInfo, setCompanyInfo] = useState<CompanyInfo | null>(null)
const [language, setLanguage] = useState<SupportedLanguage>('de')
const [format, setFormat] = useState<ExportFormat>('HTML')
const [policy, setPolicy] = useState<GeneratedPrivacyPolicy | null>(null)
const [isGenerating, setIsGenerating] = useState(false)
const handleGenerate = async () => {
if (!companyInfo || !companyInfo.name || !companyInfo.email || !companyInfo.address) {
alert('Bitte fuellen Sie zuerst die Pflichtfelder (Firmenname, Adresse, E-Mail) aus.')
return
}
setIsGenerating(true)
try {
// Generate locally (could also call API)
const generatedPolicy = generatePrivacyPolicy(
state.tenantId || 'demo',
allDataPoints,
companyInfo,
language,
format
)
setPolicy(generatedPolicy)
} catch (error) {
console.error('Error generating policy:', error)
alert('Fehler beim Generieren der Datenschutzerklaerung')
} finally {
setIsGenerating(false)
}
}
const handleDownload = (downloadFormat: ExportFormat) => {
if (!policy?.content) return
const blob = new Blob([policy.content], {
type: downloadFormat === 'HTML' ? 'text/html' : 'text/markdown',
})
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `datenschutzerklaerung-${language}.${downloadFormat === 'HTML' ? 'html' : 'md'}`
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
}
return (
<div className="space-y-6">
{/* Back Link */}
<Link
href="/sdk/einwilligungen/catalog"
className="inline-flex items-center gap-2 text-sm text-slate-600 hover:text-slate-900"
>
<ArrowLeft className="w-4 h-4" />
Zurueck zum Katalog
</Link>
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-slate-900">
Datenschutzerklaerung Generator
</h1>
<p className="text-slate-600 mt-1">
Generieren Sie eine DSGVO-konforme Datenschutzerklaerung aus Ihrem Datenpunktkatalog.
</p>
</div>
</div>
{/* Stats */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div className="bg-white rounded-xl border border-slate-200 p-4">
<div className="text-sm text-slate-500">Datenpunkte</div>
<div className="text-2xl font-bold text-slate-900">{allDataPoints.length}</div>
</div>
<div className="bg-white rounded-xl border border-slate-200 p-4">
<div className="text-sm text-slate-500">Kategorien</div>
<div className="text-2xl font-bold text-indigo-600">8</div>
</div>
<div className="bg-white rounded-xl border border-slate-200 p-4">
<div className="text-sm text-slate-500">Abschnitte</div>
<div className="text-2xl font-bold text-purple-600">9</div>
</div>
<div className="bg-white rounded-xl border border-slate-200 p-4">
<div className="text-sm text-slate-500">Sprachen</div>
<div className="text-2xl font-bold text-green-600">2</div>
</div>
</div>
{/* Two Column Layout */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Left: Company Info */}
<div>
<CompanyInfoForm companyInfo={companyInfo} onChange={setCompanyInfo} />
</div>
{/* Right: Preview */}
<div>
<PrivacyPolicyPreview
policy={policy}
isLoading={isGenerating}
language={language}
format={format}
onLanguageChange={setLanguage}
onFormatChange={setFormat}
onGenerate={handleGenerate}
onDownload={handleDownload}
/>
</div>
</div>
</div>
)
}
// =============================================================================
// MAIN PAGE
// =============================================================================
export default function PrivacyPolicyPage() {
return (
<EinwilligungenProvider>
<PrivacyPolicyContent />
</EinwilligungenProvider>
)
}