'use client' /** * Audit Checklist Page - 476+ Requirements Interactive Checklist * * Features: * - Session management (create, start, complete) * - Paginated checklist with search & filters * - Sign-off workflow with digital signatures * - Progress tracking with statistics */ import { useState, useEffect } from 'react' import { PagePurpose } from '@/components/common/PagePurpose' // Types interface AuditSession { id: string name: string auditor_name: string auditor_email?: string auditor_organization?: string status: 'draft' | 'in_progress' | 'completed' | 'archived' regulation_ids?: string[] total_items: number completed_items: number compliant_count: number non_compliant_count: number completion_percentage: number created_at: string started_at?: string completed_at?: string } interface ChecklistItem { requirement_id: string regulation_code: string article: string paragraph?: string title: string description?: string current_result: string notes?: string is_signed: boolean signed_at?: string signed_by?: string evidence_count: number controls_mapped: number implementation_status: string priority: number } interface AuditStatistics { total: number compliant: number compliant_with_notes: number non_compliant: number not_applicable: number pending: number completion_percentage: number } // Haupt-/Nebenabweichungen aus ISMS interface FindingsData { major_count: number // Hauptabweichungen (blockiert Zertifizierung) minor_count: number // Nebenabweichungen (erfordert CAPA) ofi_count: number // Verbesserungspotenziale total: number open_majors: number // Offene Hauptabweichungen open_minors: number // Offene Nebenabweichungen } const RESULT_COLORS: Record = { compliant: { bg: 'bg-green-100', text: 'text-green-700', label: 'Konform' }, compliant_notes: { bg: 'bg-green-50', text: 'text-green-600', label: 'Konform (mit Anm.)' }, non_compliant: { bg: 'bg-red-100', text: 'text-red-700', label: 'Nicht konform' }, not_applicable: { bg: 'bg-slate-100', text: 'text-slate-600', label: 'N/A' }, pending: { bg: 'bg-yellow-100', text: 'text-yellow-700', label: 'Ausstehend' }, } export default function AuditChecklistPage() { const [sessions, setSessions] = useState([]) const [selectedSession, setSelectedSession] = useState(null) const [checklist, setChecklist] = useState([]) const [statistics, setStatistics] = useState(null) const [loading, setLoading] = useState(true) const [checklistLoading, setChecklistLoading] = useState(false) const [error, setError] = useState(null) const [findings, setFindings] = useState(null) // Filters const [search, setSearch] = useState('') const [statusFilter, setStatusFilter] = useState('') const [regulationFilter, setRegulationFilter] = useState('') const [page, setPage] = useState(1) const [totalPages, setTotalPages] = useState(1) // Modal states const [showCreateModal, setShowCreateModal] = useState(false) const [showSignOffModal, setShowSignOffModal] = useState(false) const [selectedItem, setSelectedItem] = useState(null) // New session form const [newSession, setNewSession] = useState({ name: '', auditor_name: '', auditor_email: '', auditor_organization: '', regulation_codes: [] as string[], }) useEffect(() => { loadSessions() loadFindings() }, []) const loadFindings = async () => { try { const res = await fetch('/api/admin/compliance/isms/findings/summary') if (res.ok) { const data = await res.json() setFindings(data) } } catch (err) { console.error('Failed to load findings:', err) } } useEffect(() => { if (selectedSession) { loadChecklist() } }, [selectedSession, page, statusFilter, regulationFilter, search]) const loadSessions = async () => { setLoading(true) try { const res = await fetch('/api/admin/audit/sessions') if (res.ok) { const data = await res.json() setSessions(data) } } catch (err) { console.error('Failed to load sessions:', err) setError('Sessions konnten nicht geladen werden') } finally { setLoading(false) } } const loadChecklist = async () => { if (!selectedSession) return setChecklistLoading(true) try { const params = new URLSearchParams({ page: page.toString(), page_size: '50', }) if (statusFilter) params.set('status_filter', statusFilter) if (regulationFilter) params.set('regulation_filter', regulationFilter) if (search) params.set('search', search) const res = await fetch(`/api/admin/compliance/audit/checklist/${selectedSession.id}?${params}`) if (res.ok) { const data = await res.json() setChecklist(data.items || []) setStatistics(data.statistics) setTotalPages(data.pagination?.total_pages || 1) } } catch (err) { console.error('Failed to load checklist:', err) } finally { setChecklistLoading(false) } } const createSession = async () => { try { const res = await fetch('/api/admin/audit/sessions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newSession), }) if (res.ok) { const session = await res.json() setSessions([session, ...sessions]) setSelectedSession(session) setShowCreateModal(false) setNewSession({ name: '', auditor_name: '', auditor_email: '', auditor_organization: '', regulation_codes: [], }) } } catch (err) { console.error('Failed to create session:', err) } } const startSession = async (sessionId: string) => { try { const res = await fetch(`/api/admin/audit/sessions/${sessionId}/start`, { method: 'PUT', }) if (res.ok) { loadSessions() if (selectedSession?.id === sessionId) { setSelectedSession({ ...selectedSession, status: 'in_progress' }) } } } catch (err) { console.error('Failed to start session:', err) } } const completeSession = async (sessionId: string) => { try { const res = await fetch(`/api/admin/audit/sessions/${sessionId}/complete`, { method: 'PUT', }) if (res.ok) { loadSessions() if (selectedSession?.id === sessionId) { setSelectedSession({ ...selectedSession, status: 'completed' }) } } } catch (err) { console.error('Failed to complete session:', err) } } const signOffItem = async (result: string, notes: string, sign: boolean) => { if (!selectedSession || !selectedItem) return try { const res = await fetch( `/api/admin/compliance/audit/checklist/${selectedSession.id}/items/${selectedItem.requirement_id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ result, notes, sign }), } ) if (res.ok) { loadChecklist() loadSessions() setShowSignOffModal(false) setSelectedItem(null) } } catch (err) { console.error('Failed to sign off:', err) } } const downloadPdf = async (sessionId: string) => { window.open(`/api/admin/audit/sessions/${sessionId}/pdf`, '_blank') } return (
{error && (

{error}

)} {/* Haupt-/Nebenabweichungen Uebersicht */} {findings && (

Audit Findings (ISMS)

0 ? 'bg-red-100 text-red-700' : 'bg-green-100 text-green-700' }`}> {findings.open_majors > 0 ? 'Zertifizierung blockiert' : 'Zertifizierungsfaehig'}

{findings.major_count}

Hauptabweichungen

(MAJOR)

{findings.open_majors > 0 && (

{findings.open_majors} offen

)}

{findings.minor_count}

Nebenabweichungen

(MINOR)

{findings.open_minors > 0 && (

{findings.open_minors} offen

)}

{findings.ofi_count}

Verbesserungen

(OFI)

{findings.total}

Gesamt Findings

{findings.open_majors === 0 ? ( ) : ( )}

Zertifizierung

{findings.open_majors === 0 ? 'Moeglich' : 'Blockiert'}

Hauptabweichung (MAJOR): Signifikante Abweichung von Anforderungen - blockiert Zertifizierung bis zur Behebung.{' '} Nebenabweichung (MINOR): Kleinere Abweichung - erfordert CAPA (Corrective Action) innerhalb 90 Tagen.

)}
{/* Sessions Sidebar */}

Audit Sessions

{loading ? (
) : sessions.length === 0 ? (

Keine Sessions vorhanden

) : (
{sessions.map((session) => (
setSelectedSession(session)} className={`p-4 rounded-lg border cursor-pointer transition-colors ${ selectedSession?.id === session.id ? 'border-purple-500 bg-purple-50' : 'border-slate-200 hover:border-slate-300' }`} >
{session.status === 'completed' ? 'Abgeschlossen' : session.status === 'in_progress' ? 'In Bearbeitung' : session.status === 'archived' ? 'Archiviert' : 'Entwurf'} {session.completion_percentage.toFixed(0)}%

{session.name}

{session.auditor_name}

))}
)}
{/* Checklist Content */}
{!selectedSession ? (

Waehlen Sie eine Session

Waehlen Sie eine Audit-Session aus der Liste oder erstellen Sie eine neue.

) : ( <> {/* Session Header */}

{selectedSession.name}

{selectedSession.auditor_name} {selectedSession.auditor_organization && `- ${selectedSession.auditor_organization}`}

{selectedSession.status === 'draft' && ( )} {selectedSession.status === 'in_progress' && ( )} {selectedSession.status === 'completed' && ( )}
{/* Statistics */} {statistics && (

{statistics.total}

Gesamt

{statistics.compliant + statistics.compliant_with_notes}

Konform

{statistics.non_compliant}

Nicht konform

{statistics.not_applicable}

N/A

{statistics.pending}

Ausstehend

{statistics.completion_percentage.toFixed(0)}%

Fortschritt

)}
{/* Filters */}
{ setSearch(e.target.value); setPage(1) }} placeholder="Suche..." className="flex-1 px-4 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500" />
{/* Checklist Table */}
{checklistLoading ? (
) : checklist.length === 0 ? (
Keine Eintraege gefunden
) : ( {checklist.map((item) => { const resultConfig = RESULT_COLORS[item.current_result] || RESULT_COLORS.pending return ( ) })}
Regulation Artikel Titel Controls Status Aktion
{item.regulation_code} {item.article} {item.paragraph && {item.paragraph}}

{item.title}

0 ? 'bg-green-100 text-green-700' : 'bg-slate-100 text-slate-500' }`}> {item.controls_mapped} {resultConfig.label} {item.is_signed && ( )}
)} {/* Pagination */} {totalPages > 1 && (
Seite {page} von {totalPages}
)}
)}
{/* Create Session Modal */} {showCreateModal && (

Neue Audit Session

setNewSession({ ...newSession, name: e.target.value })} className="w-full px-3 py-2 border rounded-lg" placeholder="z.B. Q1 2026 DSGVO Audit" />
setNewSession({ ...newSession, auditor_name: e.target.value })} className="w-full px-3 py-2 border rounded-lg" placeholder="Dr. Max Mustermann" />
setNewSession({ ...newSession, auditor_organization: e.target.value })} className="w-full px-3 py-2 border rounded-lg" placeholder="TÜV Rheinland" />
)} {/* Sign Off Modal */} {showSignOffModal && selectedItem && ( { setShowSignOffModal(false); setSelectedItem(null) }} onSignOff={signOffItem} /> )}
) } // Sign Off Modal Component function SignOffModal({ item, onClose, onSignOff, }: { item: ChecklistItem onClose: () => void onSignOff: (result: string, notes: string, sign: boolean) => void }) { const [result, setResult] = useState(item.current_result === 'pending' ? '' : item.current_result) const [notes, setNotes] = useState(item.notes || '') const [sign, setSign] = useState(false) return (

Anforderung bewerten

{item.regulation_code} {item.article}: {item.title}

{[ { value: 'compliant', label: 'Konform', color: 'green' }, { value: 'compliant_notes', label: 'Konform (mit Anm.)', color: 'green' }, { value: 'non_compliant', label: 'Nicht konform', color: 'red' }, { value: 'not_applicable', label: 'Nicht anwendbar', color: 'slate' }, ].map((opt) => ( ))}