Files
breakpilot-compliance/admin-compliance/app/sdk/einwilligungen/_hooks/useConsents.ts
Sharang Parnerkar 92a730626d refactor(admin): split einwilligungen page.tsx into colocated components
Extract nav tabs, detail modal, table row, stats grid, search/filter,
records table, pagination, and data-loading hook into _components/ and
_hooks/. page.tsx reduced from 833 to 114 LOC.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 08:20:50 +02:00

172 lines
5.1 KiB
TypeScript

'use client'
import { useCallback, useEffect, useState } from 'react'
import {
ConsentRecord,
ConsentStatus,
ConsentType,
HistoryAction,
PAGE_SIZE,
} from '../_types'
interface GlobalStats {
total: number
active: number
revoked: number
}
export function useConsents() {
const [records, setRecords] = useState<ConsentRecord[]>([])
const [isLoading, setIsLoading] = useState(true)
const [currentPage, setCurrentPage] = useState(1)
const [totalRecords, setTotalRecords] = useState(0)
const [globalStats, setGlobalStats] = useState<GlobalStats>({ total: 0, active: 0, revoked: 0 })
const loadConsents = useCallback(async (page: number) => {
setIsLoading(true)
try {
const offset = (page - 1) * PAGE_SIZE
const listResponse = await fetch(
`/api/sdk/v1/einwilligungen/consent?limit=${PAGE_SIZE}&offset=${offset}`
)
if (listResponse.ok) {
const listData = await listResponse.json()
setTotalRecords(listData.total ?? 0)
if (listData.consents?.length > 0) {
const mapped: ConsentRecord[] = listData.consents.map((c: {
id: string
user_id: string
data_point_id: string
granted: boolean
granted_at: string
revoked_at?: string
consent_version?: string
source?: string
ip_address?: string
user_agent?: string
history?: Array<{
id: string
action: string
created_at: string
consent_version?: string
ip_address?: string
user_agent?: string
source?: string
}>
}) => ({
id: c.id,
identifier: c.user_id,
email: c.user_id,
consentType: (c.data_point_id as ConsentType) || 'privacy',
status: (c.revoked_at ? 'withdrawn' : 'granted') as ConsentStatus,
currentVersion: c.consent_version || '1.0',
grantedAt: c.granted_at ? new Date(c.granted_at) : null,
withdrawnAt: c.revoked_at ? new Date(c.revoked_at) : null,
source: c.source ?? null,
ipAddress: c.ip_address ?? '',
userAgent: c.user_agent ?? '',
history: (c.history ?? []).map(h => ({
id: h.id,
action: h.action as HistoryAction,
timestamp: new Date(h.created_at),
version: h.consent_version || '1.0',
ipAddress: h.ip_address ?? '',
userAgent: h.user_agent ?? '',
source: h.source ?? '',
})),
}))
setRecords(mapped)
} else {
setRecords([])
}
}
} catch {
// Backend nicht erreichbar, leere Liste anzeigen
} finally {
setIsLoading(false)
}
}, [])
const loadStats = useCallback(async () => {
try {
const res = await fetch('/api/sdk/v1/einwilligungen/consent?stats=true')
if (res.ok) {
const data = await res.json()
const s = data.statistics
if (s) {
setGlobalStats({
total: s.total_consents ?? 0,
active: s.active_consents ?? 0,
revoked: s.revoked_consents ?? 0,
})
setTotalRecords(s.total_consents ?? 0)
}
}
} catch {
// Statistiken nicht erreichbar — lokale Werte behalten
}
}, [])
useEffect(() => {
loadStats()
}, [loadStats])
useEffect(() => { loadConsents(currentPage) }, [currentPage, loadConsents])
const handleRevoke = useCallback(async (recordId: string) => {
try {
const response = await fetch('/api/sdk/v1/einwilligungen/consent', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ consentId: recordId, action: 'revoke' }),
})
if (response.ok) {
const now = new Date()
setRecords(prev => prev.map(r => {
if (r.id === recordId) {
return {
...r,
status: 'withdrawn' as ConsentStatus,
withdrawnAt: now,
history: [
...r.history,
{
id: `h-${recordId}-${r.history.length + 1}`,
action: 'withdrawn' as HistoryAction,
timestamp: now,
version: r.currentVersion,
ipAddress: 'Admin-Portal',
userAgent: 'Admin Action',
source: 'Manueller Widerruf durch Admin',
notes: 'Widerruf über Admin-Portal durchgeführt',
},
],
}
}
return r
}))
loadStats()
}
} catch {
// Fallback: update local state even if API call fails
const now = new Date()
setRecords(prev => prev.map(r => {
if (r.id === recordId) {
return { ...r, status: 'withdrawn' as ConsentStatus, withdrawnAt: now }
}
return r
}))
}
}, [loadStats])
return {
records,
isLoading,
currentPage,
setCurrentPage,
totalRecords,
globalStats,
handleRevoke,
}
}