refactor(admin): split audit-checklist, cookie-banner, escalations pages

Each page.tsx was 750-780 LOC. Extracted React components to _components/
and custom hooks to _hooks/ next to each page.tsx. All three pages are now
under 215 LOC (well within the 500 LOC hard cap). Zero behavior changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-04-16 13:06:45 +02:00
parent 9f2224efc4
commit 9096aad693
21 changed files with 1892 additions and 1949 deletions

View File

@@ -0,0 +1,266 @@
'use client'
import { useState, useEffect, useRef, useCallback } from 'react'
import { useSDK, ChecklistItem as SDKChecklistItem } from '@/lib/sdk'
import { PastSession } from '../_components/types'
import { checklistTemplates } from '../_components/checklistTemplates'
import { mapSDKStatusToDisplay, mapDisplayStatusToSDK } from '../_components/statusHelpers'
import { DisplayStatus, DisplayChecklistItem } from '../_components/types'
export function useAuditChecklist() {
const { state, dispatch } = useSDK()
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [activeSessionId, setActiveSessionId] = useState<string | null>(null)
const notesTimerRef = useRef<Record<string, ReturnType<typeof setTimeout>>>({})
const [pastSessions, setPastSessions] = useState<PastSession[]>([])
const [pdfLanguage, setPdfLanguage] = useState<'de' | 'en'>('de')
const [generatingPdf, setGeneratingPdf] = useState(false)
useEffect(() => {
const fetchChecklist = async () => {
try {
setLoading(true)
const sessionsRes = await fetch('/api/sdk/v1/compliance/audit/sessions?status=in_progress')
if (sessionsRes.ok) {
const sessionsData = await sessionsRes.json()
const sessions = sessionsData.sessions || sessionsData
if (Array.isArray(sessions) && sessions.length > 0) {
const session = sessions[0]
setActiveSessionId(session.id)
const checklistRes = await fetch(`/api/sdk/v1/compliance/audit/checklist/${session.id}`)
if (checklistRes.ok) {
const checklistData = await checklistRes.json()
const items = checklistData.items || checklistData.checklist || checklistData
if (Array.isArray(items) && items.length > 0) {
const mapped: SDKChecklistItem[] = items.map((item: Record<string, unknown>) => ({
id: (item.id || item.requirement_id || '') as string,
requirementId: (item.requirement_id || '') as string,
title: (item.title || item.question || '') as string,
description: (item.category || item.description || '') as string,
status: ((item.status || 'PENDING') as string).toUpperCase() as SDKChecklistItem['status'],
notes: (item.notes || item.auditor_notes || '') as string,
verifiedBy: (item.verified_by || item.signed_off_by || null) as string | null,
verifiedAt: item.verified_at || item.signed_off_at ? new Date((item.verified_at || item.signed_off_at) as string) : null,
}))
dispatch({ type: 'SET_STATE', payload: { checklist: mapped } })
setError(null)
return
}
}
}
}
loadFromTemplates()
} catch {
loadFromTemplates()
} finally {
setLoading(false)
}
}
const loadFromTemplates = () => {
if (state.checklist.length > 0) return
const templatesToLoad = state.requirements.length > 0
? checklistTemplates.filter(t =>
state.requirements.some(r => r.id === t.requirementId)
)
: checklistTemplates
const items: SDKChecklistItem[] = templatesToLoad.map(template => ({
id: template.id,
requirementId: template.requirementId,
title: template.question,
description: template.category,
status: 'PENDING',
notes: '',
verifiedBy: null,
verifiedAt: null,
}))
if (items.length > 0) {
dispatch({ type: 'SET_STATE', payload: { checklist: items } })
}
}
fetchChecklist()
const fetchAllSessions = async () => {
try {
const res = await fetch('/api/sdk/v1/compliance/audit/sessions')
if (res.ok) {
const data = await res.json()
const sessions = data.sessions || []
setPastSessions(sessions)
}
} catch {
// Silently fail
}
}
fetchAllSessions()
}, []) // eslint-disable-line react-hooks/exhaustive-deps
const displayItems: DisplayChecklistItem[] = state.checklist.map(item => {
const template = checklistTemplates.find(t => t.id === item.id)
return {
id: item.id,
requirementId: item.requirementId,
question: item.title,
category: item.description || template?.category || 'Allgemein',
status: mapSDKStatusToDisplay(item.status),
notes: item.notes,
evidence: [],
priority: template?.priority || 'medium',
verifiedBy: item.verifiedBy,
verifiedAt: item.verifiedAt,
}
})
const handleStatusChange = async (itemId: string, status: DisplayStatus) => {
const sdkStatus = mapDisplayStatusToSDK(status)
const updatedChecklist = state.checklist.map(item =>
item.id === itemId
? {
...item,
status: sdkStatus,
verifiedBy: status !== 'not-reviewed' ? 'Aktueller Benutzer' : null,
verifiedAt: status !== 'not-reviewed' ? new Date() : null,
}
: item
)
dispatch({ type: 'SET_STATE', payload: { checklist: updatedChecklist } })
if (activeSessionId) {
try {
const item = state.checklist.find(i => i.id === itemId)
await fetch(`/api/sdk/v1/compliance/audit/checklist/${activeSessionId}/items/${item?.requirementId || itemId}/sign-off`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
status: sdkStatus === 'PASSED' ? 'compliant' : sdkStatus === 'FAILED' ? 'non_compliant' : sdkStatus === 'NOT_APPLICABLE' ? 'partially_compliant' : 'not_assessed',
auditor_notes: item?.notes || '',
}),
})
} catch {
// Silently fail
}
}
}
const handleNotesChange = useCallback((itemId: string, notes: string) => {
const updatedChecklist = state.checklist.map(item =>
item.id === itemId ? { ...item, notes } : item
)
dispatch({ type: 'SET_STATE', payload: { checklist: updatedChecklist } })
if (notesTimerRef.current[itemId]) {
clearTimeout(notesTimerRef.current[itemId])
}
notesTimerRef.current[itemId] = setTimeout(async () => {
if (activeSessionId) {
const item = state.checklist.find(i => i.id === itemId)
try {
await fetch(`/api/sdk/v1/compliance/audit/checklist/${activeSessionId}/items/${item?.requirementId || itemId}/sign-off`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ notes }),
})
} catch {
// Silently fail
}
}
}, 1000)
}, [state.checklist, activeSessionId, dispatch])
const handleExport = () => {
const exportData = displayItems.map(item => ({
id: item.id,
requirementId: item.requirementId,
question: item.question,
category: item.category,
status: item.status,
notes: item.notes,
priority: item.priority,
verifiedBy: item.verifiedBy,
verifiedAt: item.verifiedAt?.toISOString() || null,
}))
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `audit-checklist-${new Date().toISOString().split('T')[0]}.json`
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
}
const handlePdfDownload = async () => {
if (!activeSessionId) {
setError('Kein aktives Audit vorhanden. Erstellen Sie zuerst eine Checkliste.')
return
}
setGeneratingPdf(true)
setError(null)
try {
const res = await fetch(`/api/sdk/v1/compliance/audit/sessions/${activeSessionId}/report/pdf?language=${pdfLanguage}`)
if (!res.ok) throw new Error('Fehler bei der PDF-Generierung')
const blob = await res.blob()
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `audit-checklist-${activeSessionId}.pdf`
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(url)
document.body.removeChild(a)
} catch (err) {
setError(err instanceof Error ? err.message : 'PDF-Download fehlgeschlagen')
} finally {
setGeneratingPdf(false)
}
}
const handleNewChecklist = async () => {
try {
setError(null)
const res = await fetch('/api/sdk/v1/compliance/audit/sessions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: `Compliance Audit ${new Date().toLocaleDateString('de-DE')}`,
auditor_name: 'Aktueller Benutzer',
regulation_codes: ['GDPR'],
}),
})
if (res.ok) {
window.location.reload()
} else {
setError('Fehler beim Erstellen der neuen Checkliste')
}
} catch {
setError('Verbindung zum Backend fehlgeschlagen')
}
}
return {
state,
loading,
error,
setError,
activeSessionId,
pastSessions,
pdfLanguage,
setPdfLanguage,
generatingPdf,
displayItems,
handleStatusChange,
handleNotesChange,
handleExport,
handlePdfDownload,
handleNewChecklist,
}
}