'use client' import { useState, useEffect, useRef } from 'react' import { useSDK, Evidence as SDKEvidence, EvidenceType } from '@/lib/sdk' import { DisplayEvidence, mapEvidenceTypeToDisplay, getEvidenceStatus, evidenceTemplates, } from '../_components/EvidenceTypes' export function useEvidence() { const { state, dispatch } = useSDK() const [filter, setFilter] = useState('all') const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [uploading, setUploading] = useState(false) const fileInputRef = useRef(null) const [page, setPage] = useState(1) const [pageSize] = useState(20) const [total, setTotal] = useState(0) useEffect(() => { const fetchEvidence = async () => { try { setLoading(true) const res = await fetch(`/api/sdk/v1/compliance/evidence?page=${page}&limit=${pageSize}`) if (res.ok) { const data = await res.json() if (data.total !== undefined) setTotal(data.total) const backendEvidence = data.evidence || data if (Array.isArray(backendEvidence) && backendEvidence.length > 0) { const mapped: SDKEvidence[] = backendEvidence.map((e: Record) => ({ id: (e.id || '') as string, controlId: (e.control_id || '') as string, type: ((e.evidence_type || 'DOCUMENT') as string).toUpperCase() as EvidenceType, name: (e.title || e.name || '') as string, description: (e.description || '') as string, fileUrl: (e.artifact_url || null) as string | null, validFrom: e.valid_from ? new Date(e.valid_from as string) : new Date(), validUntil: e.valid_until ? new Date(e.valid_until as string) : null, uploadedBy: (e.uploaded_by || 'System') as string, uploadedAt: e.created_at ? new Date(e.created_at as string) : new Date(), })) dispatch({ type: 'SET_STATE', payload: { evidence: mapped } }) setError(null) return } } loadFromTemplates() } catch { loadFromTemplates() } finally { setLoading(false) } } const loadFromTemplates = () => { if (state.evidence.length > 0) return if (state.controls.length === 0) return 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) 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 }) }) } fetchEvidence() }, [page, pageSize]) // eslint-disable-line react-hooks/exhaustive-deps 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 = async (evidenceId: string) => { if (!confirm('Moechten Sie diesen Nachweis wirklich loeschen?')) return dispatch({ type: 'DELETE_EVIDENCE', payload: evidenceId }) try { await fetch(`/api/sdk/v1/compliance/evidence/${evidenceId}`, { method: 'DELETE' }) } catch { // Silently fail — SDK state is already updated } } const handleUpload = async (file: File) => { setUploading(true) setError(null) try { const controlId = state.controls.length > 0 ? state.controls[0].id : 'GENERIC' const params = new URLSearchParams({ control_id: controlId, evidence_type: 'document', title: file.name, }) const formData = new FormData() formData.append('file', file) const res = await fetch(`/api/sdk/v1/compliance/evidence/upload?${params}`, { method: 'POST', body: formData, }) if (!res.ok) { const errData = await res.json().catch(() => ({ error: 'Upload fehlgeschlagen' })) throw new Error(errData.error || errData.detail || 'Upload fehlgeschlagen') } const data = await res.json() const newEvidence: SDKEvidence = { id: data.id || `ev-${Date.now()}`, controlId: controlId, type: 'DOCUMENT', name: file.name, description: `Hochgeladen am ${new Date().toLocaleDateString('de-DE')}`, fileUrl: data.artifact_url || null, validFrom: new Date(), validUntil: null, uploadedBy: 'Aktueller Benutzer', uploadedAt: new Date(), } dispatch({ type: 'ADD_EVIDENCE', payload: newEvidence }) } catch (err) { setError(err instanceof Error ? err.message : 'Upload fehlgeschlagen') } finally { setUploading(false) } } const handleView = (ev: DisplayEvidence) => { if (ev.fileUrl) { window.open(ev.fileUrl, '_blank') } else { alert('Keine Datei vorhanden') } } const handleDownload = (ev: DisplayEvidence) => { if (!ev.fileUrl) return const a = document.createElement('a') a.href = ev.fileUrl a.download = ev.name document.body.appendChild(a) a.click() document.body.removeChild(a) } const handleUploadClick = () => { fileInputRef.current?.click() } const handleFileChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (file) { handleUpload(file) e.target.value = '' } } return { state, filter, setFilter, loading, error, setError, uploading, fileInputRef, page, setPage, pageSize, total, displayEvidence, filteredEvidence, validCount, expiredCount, pendingCount, handleDelete, handleView, handleDownload, handleUploadClick, handleFileChange, } }