'use client' import React, { useState, useEffect, useCallback } from 'react' import { useSDK } from '@/lib/sdk' // ============================================================================= // Types // ============================================================================= interface ProcessTask { id: string tenant_id: string project_id: string | null task_code: string title: string description: string | null category: string priority: string frequency: string assigned_to: string | null responsible_team: string | null linked_control_ids: string[] linked_module: string | null last_completed_at: string | null next_due_date: string | null due_reminder_days: number status: string completion_date: string | null completion_result: string | null completion_evidence_id: string | null follow_up_actions: string[] is_seed: boolean notes: string | null tags: string[] created_at: string updated_at: string } interface TaskStats { total: number by_status: Record by_category: Record overdue_count: number due_7_days: number due_14_days: number due_30_days: number } interface TaskFormData { task_code: string title: string description: string category: string priority: string frequency: string assigned_to: string responsible_team: string linked_module: string next_due_date: string due_reminder_days: number notes: string } interface CompleteFormData { completed_by: string result: string notes: string } interface HistoryEntry { id: string task_id: string completed_by: string | null completed_at: string result: string | null evidence_id: string | null notes: string | null status: string } const EMPTY_FORM: TaskFormData = { task_code: '', title: '', description: '', category: 'dsgvo', priority: 'medium', frequency: 'yearly', assigned_to: '', responsible_team: '', linked_module: '', next_due_date: '', due_reminder_days: 14, notes: '', } const EMPTY_COMPLETE: CompleteFormData = { completed_by: '', result: '', notes: '', } const API = '/api/sdk/v1/compliance/process-tasks' // ============================================================================= // Constants // ============================================================================= const CATEGORY_LABELS: Record = { dsgvo: 'DSGVO', nis2: 'NIS2', bsi: 'BSI', iso27001: 'ISO 27001', ai_act: 'AI Act', internal: 'Intern', } const CATEGORY_COLORS: Record = { dsgvo: 'bg-blue-100 text-blue-700', nis2: 'bg-purple-100 text-purple-700', bsi: 'bg-green-100 text-green-700', iso27001: 'bg-indigo-100 text-indigo-700', ai_act: 'bg-orange-100 text-orange-700', internal: 'bg-gray-100 text-gray-600', } const PRIORITY_LABELS: Record = { critical: 'Kritisch', high: 'Hoch', medium: 'Mittel', low: 'Niedrig', } const PRIORITY_COLORS: Record = { critical: 'bg-red-100 text-red-700', high: 'bg-orange-100 text-orange-700', medium: 'bg-yellow-100 text-yellow-700', low: 'bg-green-100 text-green-700', } const STATUS_LABELS: Record = { pending: 'Ausstehend', in_progress: 'In Bearbeitung', completed: 'Erledigt', overdue: 'Ueberfaellig', skipped: 'Uebersprungen', } const STATUS_COLORS: Record = { pending: 'bg-gray-100 text-gray-600', in_progress: 'bg-blue-100 text-blue-700', completed: 'bg-green-100 text-green-700', overdue: 'bg-red-100 text-red-700', skipped: 'bg-yellow-100 text-yellow-700', } const STATUS_ICONS: Record = { pending: '\u25CB', in_progress: '\u25D4', completed: '\u2714', overdue: '\u26A0', skipped: '\u2192', } const FREQUENCY_LABELS: Record = { weekly: 'Woechentlich', monthly: 'Monatlich', quarterly: 'Quartalsweise', semi_annual: 'Halbjaehrlich', yearly: 'Jaehrlich', once: 'Einmalig', } // ============================================================================= // Helpers // ============================================================================= function formatDate(d: string | null): string { if (!d) return '\u2014' const dt = new Date(d) return dt.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }) } function daysUntil(d: string | null): number | null { if (!d) return null return Math.ceil((new Date(d).getTime() - Date.now()) / 86400000) } function dueLabel(d: string | null): string { const days = daysUntil(d) if (days === null) return '\u2014' if (days < 0) return `${Math.abs(days)} Tage ueberfaellig` if (days === 0) return 'Heute faellig' if (days === 1) return 'Morgen faellig' return `In ${days} Tagen` } function dueLabelColor(d: string | null): string { const days = daysUntil(d) if (days === null) return 'text-gray-400' if (days < 0) return 'text-red-600 font-semibold' if (days <= 7) return 'text-orange-600' if (days <= 30) return 'text-yellow-600' return 'text-green-600' } // ============================================================================= // Toast // ============================================================================= function Toast({ message, onClose }: { message: string; onClose: () => void }) { useEffect(() => { const t = setTimeout(onClose, 3000) return () => clearTimeout(t) }, [onClose]) return (
{message}
) } // ============================================================================= // Complete Modal // ============================================================================= function CompleteModal({ task, onClose, onComplete, }: { task: ProcessTask onClose: () => void onComplete: (data: CompleteFormData) => Promise }) { const [form, setForm] = useState({ ...EMPTY_COMPLETE }) const [saving, setSaving] = useState(false) const handleSave = async () => { setSaving(true) try { await onComplete(form) onClose() } catch { setSaving(false) } } return (
e.target === e.currentTarget && onClose()}>

Aufgabe erledigen

{task.title}

setForm(prev => ({ ...prev, completed_by: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500" placeholder="Name / Rolle" />