'use client'
import {
ART9_CATEGORIES, STATUS_COLORS, STATUS_LABELS,
BUSINESS_FUNCTION_LABELS, PROTECTION_LEVEL_LABELS, DEPLOYMENT_LABELS,
REVIEW_INTERVAL_LABELS,
} from '@/lib/sdk/vvt-types'
import type { VVTActivity, VVTOrganizationHeader } from '@/lib/sdk/vvt-types'
import {
DATA_SUBJECT_CATEGORY_META, PERSONAL_DATA_CATEGORY_META,
LEGAL_BASIS_META, TRANSFER_MECHANISM_META,
} from '@/lib/sdk/vvt-types'
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function resolveDataSubjects(cats: string[]) {
return cats.map(c => DATA_SUBJECT_CATEGORY_META[c as keyof typeof DATA_SUBJECT_CATEGORY_META]?.de || c).join(', ')
}
function resolveDataCategories(cats: string[]) {
return cats.map(c => PERSONAL_DATA_CATEGORY_META[c as keyof typeof PERSONAL_DATA_CATEGORY_META]?.label?.de || c).join(', ')
}
function resolveLegalBasis(lb: { type: string; description?: string; reference?: string }) {
const meta = LEGAL_BASIS_META[lb.type as keyof typeof LEGAL_BASIS_META]
const label = meta ? `${meta.label.de} (${meta.article})` : lb.type
return lb.reference ? `${label} — ${lb.reference}` : label
}
function resolveTransferMechanism(m: string) {
const meta = TRANSFER_MECHANISM_META[m as keyof typeof TRANSFER_MECHANISM_META]
return meta?.de || m
}
function buildDocumentHtml(activities: VVTActivity[], orgHeader: VVTOrganizationHeader, today: string): string {
const approvedActivities = activities.filter(a => a.status !== 'ARCHIVED')
let html = `
Verzeichnis von Verarbeitungstaetigkeiten — ${orgHeader.organizationName || 'Organisation'}
Verzeichnis von Verarbeitungstaetigkeiten
gemaess Art. 30 Abs. 1 DSGVO
${orgHeader.organizationName || '(Organisation eintragen)'}
${orgHeader.industry ? `Branche: ${orgHeader.industry} ` : ''}
${orgHeader.employeeCount ? `Mitarbeiter: ${orgHeader.employeeCount} ` : ''}
${orgHeader.locations && orgHeader.locations.length > 0 ? `Standorte: ${orgHeader.locations.join(', ')} ` : ''}
${orgHeader.dpoName ? ` Datenschutzbeauftragter: ${orgHeader.dpoName} ` : ''}
${orgHeader.dpoContact ? `Kontakt DSB: ${orgHeader.dpoContact} ` : ''}
VVT-Version: ${orgHeader.vvtVersion} | Stand: ${today}
${orgHeader.lastReviewDate ? ` | Letzte Pruefung: ${new Date(orgHeader.lastReviewDate).toLocaleDateString('de-DE')}` : ''}
${orgHeader.nextReviewDate ? ` | Naechste Pruefung: ${new Date(orgHeader.nextReviewDate).toLocaleDateString('de-DE')}` : ''}
| Pruefintervall: ${REVIEW_INTERVAL_LABELS[orgHeader.reviewInterval] || orgHeader.reviewInterval}
Inhaltsverzeichnis
${approvedActivities.length} Verarbeitungstaetigkeiten in ${[...new Set(approvedActivities.map(a => a.businessFunction))].length} Geschaeftsbereichen
${approvedActivities.map((a) => `
${a.vvtId} ${a.name || '(Ohne Namen)'}
${BUSINESS_FUNCTION_LABELS[a.businessFunction]} — ${STATUS_LABELS[a.status]}
`).join('')}
`
for (const a of approvedActivities) {
const hasArt9 = a.personalDataCategories.some(c => ART9_CATEGORIES.includes(c))
const hasThirdCountry = a.thirdCountryTransfers.length > 0
html += `
${a.description ? `
Beschreibung
${a.description}
` : ''}
Pflichtfeld (Art. 30) Inhalt
Verantwortlicher ${a.responsible || `nicht angegeben `}
Geschaeftsbereich ${BUSINESS_FUNCTION_LABELS[a.businessFunction]}
Zwecke der Verarbeitung ${a.purposes.length > 0 ? a.purposes.join('; ') : `nicht angegeben `}
Rechtsgrundlage(n) ${a.legalBases.length > 0 ? a.legalBases.map(resolveLegalBasis).join(' ') : `nicht angegeben `}
Kategorien betroffener Personen ${a.dataSubjectCategories.length > 0 ? resolveDataSubjects(a.dataSubjectCategories) : `nicht angegeben `}
Kategorien personenbezogener Daten ${a.personalDataCategories.length > 0 ? resolveDataCategories(a.personalDataCategories) : `nicht angegeben `}${hasArt9 ? 'Enthalt besondere Kategorien nach Art. 9 DSGVO ' : ''}
Empfaengerkategorien ${a.recipientCategories.length > 0 ? a.recipientCategories.map(r => `${r.name} (${r.type})`).join('; ') : `keine `}
Uebermittlung an Drittlaender ${hasThirdCountry ? a.thirdCountryTransfers.map(t => `${t.country}: ${t.recipient} — ${resolveTransferMechanism(t.transferMechanism)}`).join(' ') : 'Keine Drittlanduebermittlung'}
Loeschfristen ${a.retentionPeriod.description || `nicht angegeben `}${a.retentionPeriod.legalBasis ? `Rechtsgrundlage: ${a.retentionPeriod.legalBasis} ` : ''}${a.retentionPeriod.deletionProcedure ? `Verfahren: ${a.retentionPeriod.deletionProcedure} ` : ''}
TOM (Art. 32 DSGVO) ${a.tomDescription || `nicht beschrieben `}
${a.structuredToms && (a.structuredToms.accessControl.length > 0 || a.structuredToms.confidentiality.length > 0 || a.structuredToms.integrity.length > 0 || a.structuredToms.availability.length > 0 || a.structuredToms.separation.length > 0) ? `
Strukturierte TOMs
Kategorie Massnahmen
${a.structuredToms.accessControl.length > 0 ? `Zugriffskontrolle ${a.structuredToms.accessControl.join(', ')} ` : ''}
${a.structuredToms.confidentiality.length > 0 ? `Vertraulichkeit ${a.structuredToms.confidentiality.join(', ')} ` : ''}
${a.structuredToms.integrity.length > 0 ? `Integritaet ${a.structuredToms.integrity.join(', ')} ` : ''}
${a.structuredToms.availability.length > 0 ? `Verfuegbarkeit ${a.structuredToms.availability.join(', ')} ` : ''}
${a.structuredToms.separation.length > 0 ? `Trennbarkeit ${a.structuredToms.separation.join(', ')} ` : ''}
` : ''}
Erstellt: ${new Date(a.createdAt).toLocaleDateString('de-DE')} | Aktualisiert: ${new Date(a.updatedAt).toLocaleDateString('de-DE')}
${a.dpiaRequired ? ' | DSFA erforderlich' : ''}
| Schutzniveau: ${PROTECTION_LEVEL_LABELS[a.protectionLevel]}
| Deployment: ${DEPLOYMENT_LABELS[a.deploymentModel]}
`
}
html += `
`
return html
}
// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------
export function TabDokument({ activities, orgHeader }: { activities: VVTActivity[]; orgHeader: VVTOrganizationHeader }) {
const today = new Date().toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' })
const nonArchivedActivities = activities.filter(a => a.status !== 'ARCHIVED')
const handlePrintDocument = () => {
const htmlContent = buildDocumentHtml(activities, orgHeader, today)
const printWindow = window.open('', '_blank')
if (printWindow) {
printWindow.document.write(htmlContent)
printWindow.document.close()
printWindow.focus()
setTimeout(() => printWindow.print(), 300)
}
}
const handleDownloadHtml = () => {
const htmlContent = buildDocumentHtml(activities, orgHeader, today)
const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `vvt-dokument-${new Date().toISOString().split('T')[0]}.html`
a.click()
URL.revokeObjectURL(url)
}
return (
VVT-Dokument (Art. 30 DSGVO)
Druckfertiges Verarbeitungsverzeichnis mit Deckblatt, Inhaltsverzeichnis und allen {nonArchivedActivities.length} Verarbeitungstaetigkeiten.
HTML herunterladen
Als PDF drucken
{nonArchivedActivities.length === 0 && (
Keine Verarbeitungstaetigkeiten vorhanden. Erstellen Sie zuerst Eintraege im Tab "Verzeichnis".
)}
{nonArchivedActivities.length > 0 && (
Vorschau — Inhalt des Dokuments
Deckblatt
Verzeichnis von Verarbeitungstaetigkeiten
gemaess Art. 30 Abs. 1 DSGVO
{orgHeader.organizationName || '(Organisation eintragen)'}
{orgHeader.dpoName && <> DSB: {orgHeader.dpoName}>}
{orgHeader.dpoContact && <> ({orgHeader.dpoContact})>}
Version {orgHeader.vvtVersion} | Stand: {today}
{nonArchivedActivities.map((a) => {
const hasArt9 = a.personalDataCategories.some(c => ART9_CATEGORIES.includes(c))
return (
{a.vvtId}
{a.name || '(Ohne Namen)'}
{STATUS_LABELS[a.status]}
{hasArt9 && Art. 9 }
Zweck: {a.purposes.join(', ') || '—'}
Rechtsgrundlage: {a.legalBases.map(lb => lb.type).join(', ') || '—'}
Betroffene: {a.dataSubjectCategories.length || 0} Kategorien
Datenkategorien: {a.personalDataCategories.length || 0}
Empfaenger: {a.recipientCategories.length || 0}
Loeschfrist: {a.retentionPeriod.description || '—'}
)
})}
Tipp: Klicken Sie auf "Als PDF drucken" fuer das vollstaendige, formatierte Dokument mit allen
Pflichtfeldern nach Art. 30 DSGVO — inklusive Deckblatt und Inhaltsverzeichnis.
)}
)
}