'use client' import React, { useState, useEffect, useRef } from 'react' import { useParams } from 'next/navigation' interface EvidenceFile { id: string filename: string file_type: string file_size: number description: string uploaded_at: string uploaded_by: string linked_mitigation_ids: string[] linked_mitigation_names: string[] linked_verification_ids: string[] linked_verification_names: string[] } interface Linkable { id: string name: string } function formatFileSize(bytes: number): string { if (bytes === 0) return '0 B' const k = 1024 const sizes = ['B', 'KB', 'MB', 'GB'] const i = Math.floor(Math.log(bytes) / Math.log(k)) return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i] } function FileIcon({ type }: { type: string }) { const isPdf = type.includes('pdf') const isImage = type.includes('image') const isDoc = type.includes('word') || type.includes('document') const isSpreadsheet = type.includes('sheet') || type.includes('excel') const color = isPdf ? 'text-red-500' : isImage ? 'text-blue-500' : isDoc ? 'text-blue-600' : isSpreadsheet ? 'text-green-600' : 'text-gray-500' return ( ) } function LinkBadges({ names, type }: { names: string[]; type: 'mitigation' | 'verification' }) { if (names.length === 0) return null const color = type === 'mitigation' ? 'bg-blue-50 text-blue-700' : 'bg-green-50 text-green-700' return (
{names.map((name, i) => ( {name} ))}
) } function LinkModal({ evidence, mitigations, verifications, onSave, onClose, }: { evidence: EvidenceFile mitigations: Linkable[] verifications: Linkable[] onSave: (evidenceId: string, mitIds: string[], verIds: string[]) => void onClose: () => void }) { const [selectedMitigations, setSelectedMitigations] = useState(evidence.linked_mitigation_ids) const [selectedVerifications, setSelectedVerifications] = useState(evidence.linked_verification_ids) function toggle(list: string[], setList: (v: string[]) => void, id: string) { setList(list.includes(id) ? list.filter((x) => x !== id) : [...list, id]) } return (

Nachweis verknuepfen: {evidence.filename}

{mitigations.length > 0 && (

Massnahmen

{mitigations.map((m) => ( ))}
)} {verifications.length > 0 && (

Verifikationen

{verifications.map((v) => ( ))}
)}
) } export default function EvidencePage() { const params = useParams() const projectId = params.projectId as string const [files, setFiles] = useState([]) const [mitigations, setMitigations] = useState([]) const [verifications, setVerifications] = useState([]) const [loading, setLoading] = useState(true) const [uploading, setUploading] = useState(false) const [dragging, setDragging] = useState(false) const [linkingFile, setLinkingFile] = useState(null) const fileInputRef = useRef(null) useEffect(() => { fetchData() }, [projectId]) async function fetchData() { try { const [evRes, mitRes, verRes] = await Promise.all([ fetch(`/api/sdk/v1/iace/projects/${projectId}/evidence`), fetch(`/api/sdk/v1/iace/projects/${projectId}/mitigations`), fetch(`/api/sdk/v1/iace/projects/${projectId}/verifications`), ]) if (evRes.ok) { const json = await evRes.json() setFiles(json.evidence || json || []) } if (mitRes.ok) { const json = await mitRes.json() setMitigations((json.mitigations || json || []).map((m: { id: string; title: string }) => ({ id: m.id, name: m.title }))) } if (verRes.ok) { const json = await verRes.json() setVerifications((json.verifications || json || []).map((v: { id: string; title: string }) => ({ id: v.id, name: v.title }))) } } catch (err) { console.error('Failed to fetch data:', err) } finally { setLoading(false) } } async function handleUpload(fileList: FileList) { setUploading(true) try { for (let i = 0; i < fileList.length; i++) { const file = fileList[i] const formData = new FormData() formData.append('file', file) formData.append('description', '') await fetch(`/api/sdk/v1/iace/projects/${projectId}/evidence`, { method: 'POST', body: formData, }) } await fetchData() } catch (err) { console.error('Failed to upload:', err) } finally { setUploading(false) } } function handleDrop(e: React.DragEvent) { e.preventDefault() setDragging(false) if (e.dataTransfer.files.length > 0) { handleUpload(e.dataTransfer.files) } } function handleDragOver(e: React.DragEvent) { e.preventDefault() setDragging(true) } function handleDragLeave() { setDragging(false) } async function handleLink(evidenceId: string, mitIds: string[], verIds: string[]) { try { await fetch(`/api/sdk/v1/iace/projects/${projectId}/evidence/${evidenceId}/link`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ linked_mitigation_ids: mitIds, linked_verification_ids: verIds, }), }) setLinkingFile(null) await fetchData() } catch (err) { console.error('Failed to link evidence:', err) } } async function handleDelete(id: string) { if (!confirm('Nachweis wirklich loeschen?')) return try { await fetch(`/api/sdk/v1/iace/projects/${projectId}/evidence/${id}`, { method: 'DELETE' }) await fetchData() } catch (err) { console.error('Failed to delete evidence:', err) } } if (loading) { return (
) } return (
{/* Header */}

Nachweise

Laden Sie Nachweisdokumente hoch und verknuepfen Sie diese mit Massnahmen und Verifikationen.

{/* Upload Area */}
fileInputRef.current?.click()} className={`border-2 border-dashed rounded-xl p-8 text-center cursor-pointer transition-colors ${ dragging ? 'border-purple-400 bg-purple-50 dark:bg-purple-900/20' : 'border-gray-300 hover:border-purple-300 hover:bg-gray-50 dark:hover:bg-gray-800' }`} > e.target.files && handleUpload(e.target.files)} /> {uploading ? (
Wird hochgeladen...
) : ( <>

Dateien auswaehlen oder hierher ziehen

PDF, Word, Excel, Bilder und andere Dokumente

)}
{/* Link Modal */} {linkingFile && ( setLinkingFile(null)} /> )} {/* File List */} {files.length > 0 ? (

Hochgeladene Nachweise ({files.length})

{files.map((file) => (
{file.filename} {formatFileSize(file.file_size)}
{file.description && (

{file.description}

)}
Hochgeladen am {new Date(file.uploaded_at).toLocaleDateString('de-DE')}
))}
) : (

Keine Nachweise vorhanden

Laden Sie Testberichte, Zertifikate, Analyseergebnisse und andere Nachweisdokumente hoch und verknuepfen Sie diese mit den entsprechenden Massnahmen.

)}
) }