'use client' import { useState, useEffect, useCallback, useMemo } from 'react' import type { TOMGeneratorState } from '@/lib/sdk/tom-generator/types' import type { TOMComplianceCheckResult } from '@/lib/sdk/tom-compliance' import { buildTOMDocumentHtml, createDefaultTOMDocumentOrgHeader, type TOMDocumentOrgHeader, type TOMDocumentRevision, } from '@/lib/sdk/tom-document' // ============================================================================= // TYPES // ============================================================================= interface TOMDocumentTabProps { state: TOMGeneratorState complianceResult: TOMComplianceCheckResult | null } // ============================================================================= // COMPONENT // ============================================================================= function TOMDocumentTab({ state, complianceResult }: TOMDocumentTabProps) { // --------------------------------------------------------------------------- // State // --------------------------------------------------------------------------- const [orgHeader, setOrgHeader] = useState(() => { if (typeof window !== 'undefined') { const saved = localStorage.getItem('bp_tom_document_orgheader') if (saved) { try { return JSON.parse(saved) } catch { /* ignore */ } } } return createDefaultTOMDocumentOrgHeader() }) const [revisions, setRevisions] = useState(() => { if (typeof window !== 'undefined') { const saved = localStorage.getItem('bp_tom_document_revisions') if (saved) { try { return JSON.parse(saved) } catch { /* ignore */ } } } return [] }) // --------------------------------------------------------------------------- // localStorage persistence // --------------------------------------------------------------------------- useEffect(() => { localStorage.setItem('bp_tom_document_orgheader', JSON.stringify(orgHeader)) }, [orgHeader]) useEffect(() => { localStorage.setItem('bp_tom_document_revisions', JSON.stringify(revisions)) }, [revisions]) // --------------------------------------------------------------------------- // Computed values // --------------------------------------------------------------------------- const tomCount = useMemo(() => { if (!state?.derivedTOMs) return 0 return Array.isArray(state.derivedTOMs) ? state.derivedTOMs.length : 0 }, [state?.derivedTOMs]) const applicableTOMs = useMemo(() => { if (!state?.derivedTOMs || !Array.isArray(state.derivedTOMs)) return [] return state.derivedTOMs.filter(t => t.applicability !== 'NOT_APPLICABLE') }, [state?.derivedTOMs]) const implementedCount = useMemo(() => { return applicableTOMs.filter(t => t.implementationStatus === 'IMPLEMENTED').length }, [applicableTOMs]) const [canonicalCount, setCanonicalCount] = useState(0) useEffect(() => { if (tomCount === 0) return fetch('/api/sdk/v1/compliance/tom-mappings/stats') .then(r => r.ok ? r.json() : null) .then(data => { if (data?.sync_state?.canonical_controls_matched) setCanonicalCount(data.sync_state.canonical_controls_matched) }) .catch(() => {}) }, [tomCount]) // --------------------------------------------------------------------------- // Handlers // --------------------------------------------------------------------------- const handlePrintTOMDocument = useCallback(() => { const html = buildTOMDocumentHtml( state?.derivedTOMs || [], orgHeader, state?.companyProfile || null, state?.riskProfile || null, complianceResult, revisions, ) const printWindow = window.open('', '_blank') if (printWindow) { printWindow.document.write(html) printWindow.document.close() setTimeout(() => printWindow.print(), 300) } }, [state, orgHeader, complianceResult, revisions]) const handleDownloadTOMDocumentHtml = useCallback(() => { const html = buildTOMDocumentHtml( state?.derivedTOMs || [], orgHeader, state?.companyProfile || null, state?.riskProfile || null, complianceResult, revisions, ) const blob = new Blob([html], { type: 'text/html;charset=utf-8' }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `TOM-Dokumentation-${orgHeader.organizationName || 'Organisation'}-${new Date().toISOString().split('T')[0]}.html` document.body.appendChild(a) a.click() document.body.removeChild(a) URL.revokeObjectURL(url) }, [state, orgHeader, complianceResult, revisions]) const handleAddRevision = useCallback(() => { setRevisions(prev => [...prev, { version: String(prev.length + 1) + '.0', date: new Date().toISOString().split('T')[0], author: '', changes: '', }]) }, []) const handleUpdateRevision = useCallback((index: number, field: keyof TOMDocumentRevision, value: string) => { setRevisions(prev => prev.map((r, i) => i === index ? { ...r, [field]: value } : r)) }, []) const handleRemoveRevision = useCallback((index: number) => { setRevisions(prev => prev.filter((_, i) => i !== index)) }, []) const updateOrgHeader = useCallback((field: keyof TOMDocumentOrgHeader, value: string | string[]) => { setOrgHeader(prev => ({ ...prev, [field]: value })) }, []) // --------------------------------------------------------------------------- // Render // --------------------------------------------------------------------------- return (
{/* 1. Action Bar */}

TOM-Dokument (Art. 32 DSGVO)

Auditfaehiges Dokument mit {applicableTOMs.length} Massnahmen generieren

{/* 2. Org Header Form */}

Organisationsdaten

updateOrgHeader('organizationName', e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-purple-500 focus:ring-1 focus:ring-purple-500 outline-none" />
updateOrgHeader('industry', e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-purple-500 focus:ring-1 focus:ring-purple-500 outline-none" />
updateOrgHeader('dpoName', e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-purple-500 focus:ring-1 focus:ring-purple-500 outline-none" />
updateOrgHeader('dpoContact', e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-purple-500 focus:ring-1 focus:ring-purple-500 outline-none" />
updateOrgHeader('responsiblePerson', e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-purple-500 focus:ring-1 focus:ring-purple-500 outline-none" />
updateOrgHeader('itSecurityContact', e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-purple-500 focus:ring-1 focus:ring-purple-500 outline-none" />
updateOrgHeader('employeeCount', e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-purple-500 focus:ring-1 focus:ring-purple-500 outline-none" />
updateOrgHeader('locations', e.target.value.split(',').map(s => s.trim()))} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-purple-500 focus:ring-1 focus:ring-purple-500 outline-none" />
updateOrgHeader('documentVersion', e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-purple-500 focus:ring-1 focus:ring-purple-500 outline-none" />
updateOrgHeader('reviewInterval', e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-purple-500 focus:ring-1 focus:ring-purple-500 outline-none" />
updateOrgHeader('lastReviewDate', e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-purple-500 focus:ring-1 focus:ring-purple-500 outline-none" />
updateOrgHeader('nextReviewDate', e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-purple-500 focus:ring-1 focus:ring-purple-500 outline-none" />
{/* 3. Revisions Manager */}

Aenderungshistorie

{revisions.length > 0 ? ( {revisions.map((revision, index) => ( ))}
Version Datum Autor Aenderungen
handleUpdateRevision(index, 'version', e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-1.5 text-sm focus:border-purple-500 focus:ring-1 focus:ring-purple-500 outline-none" /> handleUpdateRevision(index, 'date', e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-1.5 text-sm focus:border-purple-500 focus:ring-1 focus:ring-purple-500 outline-none" /> handleUpdateRevision(index, 'author', e.target.value)} placeholder="Name" className="w-full rounded-lg border border-gray-300 px-3 py-1.5 text-sm focus:border-purple-500 focus:ring-1 focus:ring-purple-500 outline-none" /> handleUpdateRevision(index, 'changes', e.target.value)} placeholder="Beschreibung der Aenderungen" className="w-full rounded-lg border border-gray-300 px-3 py-1.5 text-sm focus:border-purple-500 focus:ring-1 focus:ring-purple-500 outline-none" />
) : (

Noch keine Revisionen. Die erste Version wird automatisch im Dokument eingetragen.

)}
{/* 4. Document Preview */}

Dokument-Vorschau

{tomCount === 0 ? (

Starten Sie den TOM-Generator, um Massnahmen abzuleiten.

) : (
{/* Cover preview */}

TOM-Dokumentation

Art. 32 DSGVO — {orgHeader.organizationName || 'Organisation'}

Version {orgHeader.documentVersion} | Stand: {new Date().toLocaleDateString('de-DE')}

{/* Stats */}

{applicableTOMs.length}

Massnahmen

{implementedCount}

Umgesetzt

{canonicalCount || '-'}

Belegende Controls

12

Sektionen

{complianceResult ? complianceResult.score : '-'}

Compliance-Score

{/* 12 Sections list */}

12 Dokument-Sektionen:

  1. Ziel und Zweck
  2. Geltungsbereich
  3. Grundprinzipien Art. 32
  4. Schutzbedarf und Risikoanalyse
  5. Massnahmen-Uebersicht
  6. Detaillierte Massnahmen
  7. SDM Gewaehrleistungsziele
  8. Verantwortlichkeiten
  9. Pruef- und Revisionszyklus
  10. Compliance-Status
  11. Aenderungshistorie
)}
) } export { TOMDocumentTab }