diff --git a/admin-compliance/app/(sdk)/sdk/obligations/page.tsx b/admin-compliance/app/(sdk)/sdk/obligations/page.tsx index f3aa4f1..f2ce136 100644 --- a/admin-compliance/app/(sdk)/sdk/obligations/page.tsx +++ b/admin-compliance/app/(sdk)/sdk/obligations/page.tsx @@ -1,11 +1,10 @@ 'use client' -import React, { useState, useEffect } from 'react' -import { useSDK } from '@/lib/sdk' +import React, { useState, useEffect, useCallback } from 'react' import { StepHeader, STEP_EXPLANATIONS } from '@/components/sdk/StepHeader' // ============================================================================= -// TYPES +// Types // ============================================================================= interface Obligation { @@ -13,364 +12,777 @@ interface Obligation { title: string description: string source: string - sourceArticle: string - deadline: Date | null + source_article: string + deadline: string | null status: 'pending' | 'in-progress' | 'completed' | 'overdue' priority: 'critical' | 'high' | 'medium' | 'low' responsible: string - linkedSystems: string[] + linked_systems: string[] + assessment_id?: string + rule_code?: string + notes?: string + created_at?: string + updated_at?: string +} + +interface ObligationStats { + pending: number + in_progress: number + overdue: number + completed: number + total: number + critical: number + high: number +} + +interface ObligationFormData { + title: string + description: string + source: string + source_article: string + deadline: string + status: string + priority: string + responsible: string + linked_systems: string + notes: string +} + +const EMPTY_FORM: ObligationFormData = { + title: '', + description: '', + source: 'DSGVO', + source_article: '', + deadline: '', + status: 'pending', + priority: 'medium', + responsible: '', + linked_systems: '', + notes: '', +} + +const API = '/api/sdk/v1/compliance/obligations' + +// ============================================================================= +// Status helpers +// ============================================================================= + +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 PRIORITY_LABELS: Record = { + critical: 'Kritisch', + high: 'Hoch', + medium: 'Mittel', + low: 'Niedrig', +} + +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', +} + +const STATUS_LABELS: Record = { + pending: 'Ausstehend', + 'in-progress':'In Bearbeitung', + completed: 'Abgeschlossen', + overdue: 'Ueberfaellig', +} + +const STATUS_NEXT: Record = { + pending: 'in-progress', + 'in-progress':'completed', + completed: 'pending', + overdue: 'in-progress', } // ============================================================================= -// COMPONENTS +// Create/Edit Modal // ============================================================================= -function ObligationCard({ obligation }: { obligation: Obligation }) { - const priorityColors = { - 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', - } +function ObligationModal({ + initial, + onClose, + onSave, +}: { + initial?: Partial + onClose: () => void + onSave: (data: ObligationFormData) => Promise +}) { + const [form, setForm] = useState({ ...EMPTY_FORM, ...initial }) + const [saving, setSaving] = useState(false) + const [error, setError] = useState(null) - const statusColors = { - pending: 'bg-gray-100 text-gray-600 border-gray-200', - 'in-progress': 'bg-blue-100 text-blue-700 border-blue-200', - completed: 'bg-green-100 text-green-700 border-green-200', - overdue: 'bg-red-100 text-red-700 border-red-200', - } + const update = (field: keyof ObligationFormData, value: string) => + setForm(prev => ({ ...prev, [field]: value })) - const statusLabels = { - pending: 'Ausstehend', - 'in-progress': 'In Bearbeitung', - completed: 'Abgeschlossen', - overdue: 'Ueberfaellig', + const handleSave = async () => { + if (!form.title.trim()) { setError('Titel ist erforderlich'); return } + setSaving(true) + setError(null) + try { + await onSave(form) + onClose() + } catch (e) { + setError(e instanceof Error ? e.message : 'Fehler beim Speichern') + } finally { + setSaving(false) + } } - const daysUntilDeadline = obligation.deadline - ? Math.ceil((obligation.deadline.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24)) - : null - return ( -
-
-
-
- - {obligation.priority === 'critical' ? 'Kritisch' : - obligation.priority === 'high' ? 'Hoch' : - obligation.priority === 'medium' ? 'Mittel' : 'Niedrig'} - - - {statusLabels[obligation.status]} - - - {obligation.source} {obligation.sourceArticle} - +
e.target === e.currentTarget && onClose()}> +
+
+

+ {initial?.title ? 'Pflicht bearbeiten' : 'Neue Pflicht erstellen'} +

+ +
+
+ {error &&
{error}
} + +
+ + update('title', 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="z.B. Datenschutz-Folgenabschaetzung durchfuehren" + />
-

{obligation.title}

-

{obligation.description}

-
-
-
-
- Verantwortlich: - {obligation.responsible} -
- {obligation.deadline && ( -
- Frist: - - {obligation.deadline.toLocaleDateString('de-DE')} - {daysUntilDeadline !== null && ( - - ({daysUntilDeadline < 0 ? `${Math.abs(daysUntilDeadline)} Tage ueberfaellig` : `${daysUntilDeadline} Tage`}) - - )} - +
+ +