'use client' import React, { useState, useEffect } from 'react' import { useSDK, Evidence as SDKEvidence, EvidenceType } from '@/lib/sdk' import { StepHeader, STEP_EXPLANATIONS } from '@/components/sdk/StepHeader' // ============================================================================= // TYPES // ============================================================================= type DisplayEvidenceType = 'document' | 'screenshot' | 'log' | 'audit-report' | 'certificate' type DisplayFormat = 'pdf' | 'image' | 'text' | 'json' type DisplayStatus = 'valid' | 'expired' | 'pending-review' interface DisplayEvidence { id: string name: string description: string displayType: DisplayEvidenceType format: DisplayFormat controlId: string linkedRequirements: string[] linkedControls: string[] uploadedBy: string uploadedAt: Date validFrom: Date validUntil: Date | null status: DisplayStatus fileSize: string fileUrl: string | null } // ============================================================================= // HELPER FUNCTIONS // ============================================================================= function mapEvidenceTypeToDisplay(type: EvidenceType): DisplayEvidenceType { switch (type) { case 'DOCUMENT': return 'document' case 'SCREENSHOT': return 'screenshot' case 'LOG': return 'log' case 'CERTIFICATE': return 'certificate' case 'AUDIT_REPORT': return 'audit-report' default: return 'document' } } function mapDisplayTypeToEvidence(type: DisplayEvidenceType): EvidenceType { switch (type) { case 'document': return 'DOCUMENT' case 'screenshot': return 'SCREENSHOT' case 'log': return 'LOG' case 'certificate': return 'CERTIFICATE' case 'audit-report': return 'AUDIT_REPORT' default: return 'DOCUMENT' } } function getEvidenceStatus(validUntil: Date | null): DisplayStatus { if (!validUntil) return 'pending-review' const now = new Date() if (validUntil < now) return 'expired' return 'valid' } // ============================================================================= // EVIDENCE TEMPLATES // ============================================================================= interface EvidenceTemplate { id: string name: string description: string type: EvidenceType displayType: DisplayEvidenceType format: DisplayFormat controlId: string linkedRequirements: string[] linkedControls: string[] uploadedBy: string validityDays: number fileSize: string } const evidenceTemplates: EvidenceTemplate[] = [ { id: 'ev-dse-001', name: 'Datenschutzerklaerung v2.3', description: 'Aktuelle Datenschutzerklaerung fuer Website und App', type: 'DOCUMENT', displayType: 'document', format: 'pdf', controlId: 'ctrl-org-001', linkedRequirements: ['req-gdpr-13', 'req-gdpr-14'], linkedControls: ['ctrl-org-001'], uploadedBy: 'DSB', validityDays: 365, fileSize: '245 KB', }, { id: 'ev-pentest-001', name: 'Penetrationstest Report Q4/2024', description: 'Externer Penetrationstest durch Security-Partner', type: 'AUDIT_REPORT', displayType: 'audit-report', format: 'pdf', controlId: 'ctrl-tom-001', linkedRequirements: ['req-gdpr-32', 'req-iso-a12'], linkedControls: ['ctrl-tom-001', 'ctrl-tom-002', 'ctrl-det-001'], uploadedBy: 'IT Security Team', validityDays: 365, fileSize: '2.1 MB', }, { id: 'ev-iso-cert', name: 'ISO 27001 Zertifikat', description: 'Zertifizierung des ISMS', type: 'CERTIFICATE', displayType: 'certificate', format: 'pdf', controlId: 'ctrl-tom-001', linkedRequirements: ['req-iso-4.1', 'req-iso-5.1'], linkedControls: [], uploadedBy: 'QM Abteilung', validityDays: 365, fileSize: '156 KB', }, { id: 'ev-schulung-001', name: 'Schulungsnachweis Datenschutz 2024', description: 'Teilnehmerliste und Schulungsinhalt', type: 'DOCUMENT', displayType: 'document', format: 'pdf', controlId: 'ctrl-org-001', linkedRequirements: ['req-gdpr-39'], linkedControls: ['ctrl-org-001'], uploadedBy: 'HR Team', validityDays: 365, fileSize: '890 KB', }, { id: 'ev-rbac-001', name: 'Access Control Screenshot', description: 'Nachweis der RBAC-Konfiguration', type: 'SCREENSHOT', displayType: 'screenshot', format: 'image', controlId: 'ctrl-tom-001', linkedRequirements: ['req-gdpr-32'], linkedControls: ['ctrl-tom-001'], uploadedBy: 'Admin', validityDays: 0, fileSize: '1.2 MB', }, { id: 'ev-log-001', name: 'Audit Log Export', description: 'Monatlicher Audit-Log Export', type: 'LOG', displayType: 'log', format: 'json', controlId: 'ctrl-det-001', linkedRequirements: ['req-gdpr-32'], linkedControls: ['ctrl-det-001'], uploadedBy: 'System', validityDays: 90, fileSize: '4.5 MB', }, ] // ============================================================================= // COMPONENTS // ============================================================================= function EvidenceCard({ evidence, onDelete }: { evidence: DisplayEvidence; onDelete: () => void }) { const typeIcons = { document: ( ), screenshot: ( ), log: ( ), 'audit-report': ( ), certificate: ( ), } const statusColors = { valid: 'bg-green-100 text-green-700 border-green-200', expired: 'bg-red-100 text-red-700 border-red-200', 'pending-review': 'bg-yellow-100 text-yellow-700 border-yellow-200', } const statusLabels = { valid: 'Gueltig', expired: 'Abgelaufen', 'pending-review': 'Pruefung ausstehend', } return (
{typeIcons[evidence.displayType]}

{evidence.name}

{statusLabels[evidence.status]}

{evidence.description}

Hochgeladen: {evidence.uploadedAt.toLocaleDateString('de-DE')} {evidence.validUntil && ( Gueltig bis: {evidence.validUntil.toLocaleDateString('de-DE')} )} {evidence.fileSize}
{evidence.linkedRequirements.map(req => ( {req} ))} {evidence.linkedControls.map(ctrl => ( {ctrl} ))}
Hochgeladen von: {evidence.uploadedBy}
) } // ============================================================================= // MAIN PAGE // ============================================================================= export default function EvidencePage() { const { state, dispatch } = useSDK() const [filter, setFilter] = useState('all') // Load evidence based on controls when controls exist useEffect(() => { if (state.controls.length > 0 && state.evidence.length === 0) { // Add relevant evidence based on controls const relevantEvidence = evidenceTemplates.filter(e => state.controls.some(c => c.id === e.controlId || e.linkedControls.includes(c.id)) ) const now = new Date() relevantEvidence.forEach(template => { const validFrom = new Date(now) validFrom.setMonth(validFrom.getMonth() - 1) // Uploaded 1 month ago const validUntil = template.validityDays > 0 ? new Date(validFrom.getTime() + template.validityDays * 24 * 60 * 60 * 1000) : null const sdkEvidence: SDKEvidence = { id: template.id, controlId: template.controlId, type: template.type, name: template.name, description: template.description, fileUrl: null, validFrom, validUntil, uploadedBy: template.uploadedBy, uploadedAt: validFrom, } dispatch({ type: 'ADD_EVIDENCE', payload: sdkEvidence }) }) } }, [state.controls, state.evidence.length, dispatch]) // Convert SDK evidence to display evidence const displayEvidence: DisplayEvidence[] = state.evidence.map(ev => { const template = evidenceTemplates.find(t => t.id === ev.id) return { id: ev.id, name: ev.name, description: ev.description, displayType: mapEvidenceTypeToDisplay(ev.type), format: template?.format || 'pdf', controlId: ev.controlId, linkedRequirements: template?.linkedRequirements || [], linkedControls: template?.linkedControls || [ev.controlId], uploadedBy: ev.uploadedBy, uploadedAt: ev.uploadedAt, validFrom: ev.validFrom, validUntil: ev.validUntil, status: getEvidenceStatus(ev.validUntil), fileSize: template?.fileSize || 'Unbekannt', fileUrl: ev.fileUrl, } }) const filteredEvidence = filter === 'all' ? displayEvidence : displayEvidence.filter(e => e.status === filter || e.displayType === filter) const validCount = displayEvidence.filter(e => e.status === 'valid').length const expiredCount = displayEvidence.filter(e => e.status === 'expired').length const pendingCount = displayEvidence.filter(e => e.status === 'pending-review').length const handleDelete = (evidenceId: string) => { if (confirm('Moechten Sie diesen Nachweis wirklich loeschen?')) { dispatch({ type: 'DELETE_EVIDENCE', payload: evidenceId }) } } const stepInfo = STEP_EXPLANATIONS['evidence'] return (
{/* Step Header */} {/* Controls Alert */} {state.controls.length === 0 && (

Keine Kontrollen definiert

Bitte definieren Sie zuerst Kontrollen, um die zugehoerigen Nachweise zu laden.

)} {/* Stats */}
Gesamt
{displayEvidence.length}
Gueltig
{validCount}
Abgelaufen
{expiredCount}
Pruefung ausstehend
{pendingCount}
{/* Filter */}
Filter: {['all', 'valid', 'expired', 'pending-review', 'document', 'certificate', 'audit-report'].map(f => ( ))}
{/* Evidence List */}
{filteredEvidence.map(ev => ( handleDelete(ev.id)} /> ))}
{filteredEvidence.length === 0 && state.controls.length > 0 && (

Keine Nachweise gefunden

Passen Sie den Filter an oder laden Sie neue Nachweise hoch.

)}
) }