'use client' import { useState, useEffect } from 'react' import type { VVTOrganizationHeader } from '@/lib/sdk/vvt-types' // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- interface VendorForProcessor { id: string name: string role: string serviceDescription: string country: string processingLocations: { country: string; region?: string; isEU: boolean; isAdequate: boolean }[] transferMechanisms: string[] certifications: { type: string; expirationDate?: string }[] status: string primaryContact: { name: string; email: string; phone?: string } dpoContact?: { name: string; email: string } contractTypes: string[] inherentRiskScore: number residualRiskScore: number nextReviewDate?: string processingActivityIds: string[] notes?: string createdAt: string updatedAt: string } // --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- const VENDOR_STATUS_LABELS: Record = { ACTIVE: 'Aktiv', PENDING_REVIEW: 'In Pruefung', APPROVED: 'Genehmigt', SUSPENDED: 'Ausgesetzt', ARCHIVED: 'Archiviert', DRAFT: 'Entwurf', REVIEW: 'In Pruefung', } const VENDOR_STATUS_COLORS: Record = { ACTIVE: 'bg-green-100 text-green-700', PENDING_REVIEW: 'bg-yellow-100 text-yellow-700', APPROVED: 'bg-green-100 text-green-800', SUSPENDED: 'bg-red-100 text-red-700', ARCHIVED: 'bg-gray-100 text-gray-600', DRAFT: 'bg-gray-100 text-gray-600', REVIEW: 'bg-yellow-100 text-yellow-700', } const ROLE_LABELS: Record = { PROCESSOR: 'Auftragsverarbeiter', SUB_PROCESSOR: 'Unterauftragsverarbeiter', } function riskColor(score: number): string { if (score <= 3) return 'bg-green-100 text-green-700' if (score <= 6) return 'bg-yellow-100 text-yellow-700' return 'bg-red-100 text-red-700' } // --------------------------------------------------------------------------- // API // --------------------------------------------------------------------------- async function apiListProcessorVendors(): Promise { const res = await fetch('/api/sdk/v1/vendor-compliance/vendors?limit=500') if (!res.ok) throw new Error(`Vendor API error: ${res.status}`) const data = await res.json() const items: any[] = data?.data?.items ?? [] return items .filter((v: any) => v.role === 'PROCESSOR' || v.role === 'SUB_PROCESSOR') .map((v: any) => ({ id: v.id, name: v.name ?? '', role: v.role ?? '', serviceDescription: v.serviceDescription ?? v.service_description ?? '', country: v.country ?? '', processingLocations: (v.processingLocations ?? v.processing_locations ?? []).map((l: any) => ({ country: l.country ?? '', region: l.region, isEU: l.isEU ?? l.is_eu ?? false, isAdequate: l.isAdequate ?? l.is_adequate ?? false, })), transferMechanisms: v.transferMechanisms ?? v.transfer_mechanisms ?? [], certifications: (v.certifications ?? []).map((c: any) => ({ type: c.type ?? '', expirationDate: c.expirationDate ?? c.expiration_date, })), status: v.status ?? 'ACTIVE', primaryContact: { name: v.primaryContact?.name ?? v.primary_contact?.name ?? '', email: v.primaryContact?.email ?? v.primary_contact?.email ?? '', phone: v.primaryContact?.phone ?? v.primary_contact?.phone, }, dpoContact: (v.dpoContact ?? v.dpo_contact) ? { name: (v.dpoContact ?? v.dpo_contact).name ?? '', email: (v.dpoContact ?? v.dpo_contact).email ?? '', } : undefined, contractTypes: v.contractTypes ?? v.contract_types ?? [], inherentRiskScore: v.inherentRiskScore ?? v.inherent_risk_score ?? 0, residualRiskScore: v.residualRiskScore ?? v.residual_risk_score ?? 0, nextReviewDate: v.nextReviewDate ?? v.next_review_date, processingActivityIds: v.processingActivityIds ?? v.processing_activity_ids ?? [], notes: v.notes, createdAt: v.createdAt ?? v.created_at ?? '', updatedAt: v.updatedAt ?? v.updated_at ?? '', })) } // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- export function TabProcessor({ orgHeader }: { orgHeader: VVTOrganizationHeader }) { const [vendors, setVendors] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const loadVendors = () => { setLoading(true) setError(null) apiListProcessorVendors() .then(data => setVendors(data)) .catch(err => setError(err.message ?? 'Fehler beim Laden der Auftragsverarbeiter')) .finally(() => setLoading(false)) } useEffect(() => { let cancelled = false setLoading(true) setError(null) apiListProcessorVendors() .then(data => { if (!cancelled) setVendors(data) }) .catch(err => { if (!cancelled) setError(err.message ?? 'Fehler beim Laden der Auftragsverarbeiter') }) .finally(() => { if (!cancelled) setLoading(false) }) return () => { cancelled = true } }, []) const handlePrintProcessorDoc = () => { const today = new Date().toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }) const activeVendors = vendors.filter(v => v.status !== 'ARCHIVED') const subProcessors = vendors.filter(v => v.role === 'SUB_PROCESSOR') let html = ` Verzeichnis Auftragsverarbeiter — Art. 30 Abs. 2 DSGVO

Verzeichnis aller Verarbeitungstaetigkeiten

als Auftragsverarbeiter gemaess Art. 30 Abs. 2 DSGVO
${orgHeader.organizationName || '(Organisation eintragen)'}
${orgHeader.dpoName ? `Datenschutzbeauftragter: ${orgHeader.dpoName}
` : ''}Stand: ${today}
` for (const v of activeVendors) { const thirdCountryLocations = v.processingLocations.filter(l => !l.isEU && !l.isAdequate) const thirdCountryHtml = thirdCountryLocations.length > 0 ? thirdCountryLocations.map(l => `${l.country}${l.region ? ` (${l.region})` : ''}`).join(', ') + (v.transferMechanisms.length > 0 ? `
Garantien: ${v.transferMechanisms.join(', ')}` : '') : 'Keine Drittlanduebermittlung' const subProcessorHtml = subProcessors.length > 0 ? subProcessors.map(s => `${s.name} — ${s.serviceDescription || s.country}`).join('
') : 'Keine' html += `
${ROLE_LABELS[v.role] ?? v.role}

${v.name}

Pflichtfeld (Art. 30 Abs. 2)Inhalt
Name/Kontaktdaten des Auftragsverarbeiters${orgHeader.organizationName}${orgHeader.dpoContact ? `
Kontakt: ${orgHeader.dpoContact}` : ''}
Name/Kontaktdaten des Verantwortlichen${v.name}${v.primaryContact.email ? `
Kontakt: ${v.primaryContact.email}` : ''}
Kategorien von Verarbeitungen${v.serviceDescription || 'nicht angegeben'}
Unterauftragsverarbeiter${subProcessorHtml}
Uebermittlung an Drittlaender${thirdCountryHtml}
TOM (Art. 32 DSGVO)Siehe TOM-Dokumentation im Vendor-Compliance-Modul
` } html += `` const printWindow = window.open('', '_blank') if (printWindow) { printWindow.document.write(html) printWindow.document.close() printWindow.focus() setTimeout(() => printWindow.print(), 300) } } return (

Dieses Verzeichnis zeigt alle Auftragsverarbeiter aus dem Vendor Register. Neue Auftragsverarbeiter hinzufuegen oder bestehende bearbeiten:

Zum Vendor Register

Auftragsverarbeiter-Verzeichnis (Art. 30 Abs. 2)

Auftragsverarbeiter und Unterauftragsverarbeiter aus dem Vendor-Compliance-Modul (nur lesen).

{vendors.length > 0 && ( )}
{loading && (

Auftragsverarbeiter werden geladen...

)} {!loading && error && (

{error}

)} {!loading && !error && vendors.length === 0 && (

Keine Auftragsverarbeiter im Vendor Register

Legen Sie Auftragsverarbeiter im Vendor Register an, damit sie hier automatisch erscheinen.

Zum Vendor Register
)} {!loading && !error && vendors.length > 0 && (
{vendors.map(v => { const thirdCountryLocations = v.processingLocations.filter(l => !l.isEU && !l.isAdequate) return (

{v.name}

{ROLE_LABELS[v.role] ?? v.role} {VENDOR_STATUS_LABELS[v.status] ?? v.status}
{v.serviceDescription &&

{v.serviceDescription}

}
{v.primaryContact.name && ( {v.primaryContact.name} )} {v.primaryContact.email && ( {v.primaryContact.email} )}
Inherent: {v.inherentRiskScore} Residual: {v.residualRiskScore} {v.updatedAt && Aktualisiert: {new Date(v.updatedAt).toLocaleDateString('de-DE')}}
{thirdCountryLocations.length > 0 && (
Drittlandtransfers: {thirdCountryLocations.map(l => `${l.country}${l.region ? ` (${l.region})` : ''}`).join(', ')}
)} {v.certifications.length > 0 && (
{v.certifications.map((c, i) => ( {c.type}{c.expirationDate ? ` (bis ${new Date(c.expirationDate).toLocaleDateString('de-DE')})` : ''} ))}
)}
) })}
)}

Art. 30 Abs. 2 DSGVO — Pflichtangaben

  • Name und Kontaktdaten des/der Auftragsverarbeiter(s) und jedes Verantwortlichen
  • Kategorien von Verarbeitungen, die im Auftrag jedes Verantwortlichen durchgefuehrt werden
  • Uebermittlungen an Drittlaender einschliesslich Dokumentierung geeigneter Garantien
  • Allgemeine Beschreibung der technischen und organisatorischen Massnahmen (Art. 32 Abs. 1)
) }