- tom-generator/export/zip.ts: extract private helpers to zip-helpers.ts (544→342 LOC) - tom-generator/export/docx.ts: extract private helpers to docx-helpers.ts (525→378 LOC) - tom-generator/export/pdf.ts: extract private helpers to pdf-helpers.ts (517→446 LOC) - tom-generator/demo-data/index.ts: extract DEMO_RISK_PROFILES + DEMO_EVIDENCE_DOCUMENTS to demo-data-part2.ts (518→360 LOC) - einwilligungen/generator/privacy-policy-sections.ts: extract sections 5-7 to part2 (559→313 LOC) - einwilligungen/export/pdf.ts: extract HTML/CSS helpers to pdf-helpers.ts (505→296 LOC) - vendor-compliance/context.tsx: extract API action hooks to context-actions.tsx (509→286 LOC) All originals re-export from sibling files — zero consumer import changes needed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
249 lines
7.1 KiB
TypeScript
249 lines
7.1 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* Vendor Compliance Context - API Actions
|
|
*
|
|
* Extracted from context.tsx to stay under 500 LOC hard cap.
|
|
* Contains loadData, refresh, and all CRUD API action hooks.
|
|
*/
|
|
|
|
import { useCallback } from 'react'
|
|
import type { Dispatch } from 'react'
|
|
import type { ProcessingActivity, VendorComplianceAction } from './types'
|
|
|
|
const API_BASE = '/api/sdk/v1/vendor-compliance'
|
|
|
|
export function useContextApiActions(
|
|
state: { processingActivities: ProcessingActivity[]; contracts: Array<{ id: string; vendorId: string; expirationDate?: Date | null; status: string }>; vendors: Array<{ id: string; contracts: string[] }> },
|
|
dispatch: Dispatch<VendorComplianceAction>
|
|
) {
|
|
const loadData = useCallback(async () => {
|
|
dispatch({ type: 'SET_LOADING', payload: true })
|
|
dispatch({ type: 'SET_ERROR', payload: null })
|
|
|
|
try {
|
|
const [
|
|
activitiesRes,
|
|
vendorsRes,
|
|
contractsRes,
|
|
findingsRes,
|
|
controlsRes,
|
|
controlInstancesRes,
|
|
] = await Promise.all([
|
|
fetch(`${API_BASE}/processing-activities`),
|
|
fetch(`${API_BASE}/vendors`),
|
|
fetch(`${API_BASE}/contracts`),
|
|
fetch(`${API_BASE}/findings`),
|
|
fetch(`${API_BASE}/controls`),
|
|
fetch(`${API_BASE}/control-instances`),
|
|
])
|
|
|
|
if (activitiesRes.ok) {
|
|
const data = await activitiesRes.json()
|
|
dispatch({ type: 'SET_PROCESSING_ACTIVITIES', payload: data.data || [] })
|
|
}
|
|
|
|
if (vendorsRes.ok) {
|
|
const data = await vendorsRes.json()
|
|
dispatch({ type: 'SET_VENDORS', payload: data.data || [] })
|
|
}
|
|
|
|
if (contractsRes.ok) {
|
|
const data = await contractsRes.json()
|
|
dispatch({ type: 'SET_CONTRACTS', payload: data.data || [] })
|
|
}
|
|
|
|
if (findingsRes.ok) {
|
|
const data = await findingsRes.json()
|
|
dispatch({ type: 'SET_FINDINGS', payload: data.data || [] })
|
|
}
|
|
|
|
if (controlsRes.ok) {
|
|
const data = await controlsRes.json()
|
|
dispatch({ type: 'SET_CONTROLS', payload: data.data || [] })
|
|
}
|
|
|
|
if (controlInstancesRes.ok) {
|
|
const data = await controlInstancesRes.json()
|
|
dispatch({ type: 'SET_CONTROL_INSTANCES', payload: data.data || [] })
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to load vendor compliance data:', error)
|
|
dispatch({
|
|
type: 'SET_ERROR',
|
|
payload: 'Fehler beim Laden der Daten',
|
|
})
|
|
} finally {
|
|
dispatch({ type: 'SET_LOADING', payload: false })
|
|
}
|
|
}, [dispatch])
|
|
|
|
const refresh = useCallback(async () => {
|
|
await loadData()
|
|
}, [loadData])
|
|
|
|
const createProcessingActivity = useCallback(
|
|
async (
|
|
data: Omit<ProcessingActivity, 'id' | 'tenantId' | 'createdAt' | 'updatedAt'>
|
|
): Promise<ProcessingActivity> => {
|
|
const response = await fetch(`${API_BASE}/processing-activities`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(data),
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json()
|
|
throw new Error(error.error || 'Fehler beim Erstellen der Verarbeitungstätigkeit')
|
|
}
|
|
|
|
const result = await response.json()
|
|
const activity = result.data
|
|
|
|
dispatch({ type: 'ADD_PROCESSING_ACTIVITY', payload: activity })
|
|
|
|
return activity
|
|
},
|
|
[dispatch]
|
|
)
|
|
|
|
const deleteProcessingActivity = useCallback(
|
|
async (id: string): Promise<void> => {
|
|
const response = await fetch(`${API_BASE}/processing-activities/${id}`, {
|
|
method: 'DELETE',
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json()
|
|
throw new Error(error.error || 'Fehler beim Löschen der Verarbeitungstätigkeit')
|
|
}
|
|
|
|
dispatch({ type: 'DELETE_PROCESSING_ACTIVITY', payload: id })
|
|
},
|
|
[dispatch]
|
|
)
|
|
|
|
const duplicateProcessingActivity = useCallback(
|
|
async (id: string): Promise<ProcessingActivity> => {
|
|
const original = state.processingActivities.find((a) => a.id === id)
|
|
if (!original) {
|
|
throw new Error('Verarbeitungstätigkeit nicht gefunden')
|
|
}
|
|
|
|
const { id: _id, vvtId: _vvtId, createdAt: _createdAt, updatedAt: _updatedAt, tenantId: _tenantId, ...rest } = original
|
|
|
|
const newActivity = await createProcessingActivity({
|
|
...rest,
|
|
vvtId: '',
|
|
name: {
|
|
de: `${original.name.de} (Kopie)`,
|
|
en: `${original.name.en} (Copy)`,
|
|
},
|
|
status: 'DRAFT',
|
|
})
|
|
|
|
return newActivity
|
|
},
|
|
[state.processingActivities, createProcessingActivity]
|
|
)
|
|
|
|
const deleteVendor = useCallback(
|
|
async (id: string): Promise<void> => {
|
|
const response = await fetch(`${API_BASE}/vendors/${id}`, {
|
|
method: 'DELETE',
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json()
|
|
throw new Error(error.error || 'Fehler beim Löschen des Vendors')
|
|
}
|
|
|
|
dispatch({ type: 'DELETE_VENDOR', payload: id })
|
|
},
|
|
[dispatch]
|
|
)
|
|
|
|
const deleteContract = useCallback(
|
|
async (id: string): Promise<void> => {
|
|
const contract = state.contracts.find((c) => c.id === id)
|
|
|
|
const response = await fetch(`${API_BASE}/contracts/${id}`, {
|
|
method: 'DELETE',
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json()
|
|
throw new Error(error.error || 'Fehler beim Löschen des Vertrags')
|
|
}
|
|
|
|
dispatch({ type: 'DELETE_CONTRACT', payload: id })
|
|
|
|
if (contract) {
|
|
const vendor = state.vendors.find((v) => v.id === contract.vendorId)
|
|
if (vendor) {
|
|
dispatch({
|
|
type: 'UPDATE_VENDOR',
|
|
payload: {
|
|
id: vendor.id,
|
|
data: { contracts: vendor.contracts.filter((cId) => cId !== id) },
|
|
},
|
|
})
|
|
}
|
|
}
|
|
},
|
|
[dispatch, state.contracts, state.vendors]
|
|
)
|
|
|
|
const startContractReview = useCallback(
|
|
async (contractId: string): Promise<void> => {
|
|
dispatch({
|
|
type: 'UPDATE_CONTRACT',
|
|
payload: { id: contractId, data: { reviewStatus: 'IN_PROGRESS' } },
|
|
})
|
|
|
|
const response = await fetch(`${API_BASE}/contracts/${contractId}/review`, {
|
|
method: 'POST',
|
|
})
|
|
|
|
if (!response.ok) {
|
|
dispatch({
|
|
type: 'UPDATE_CONTRACT',
|
|
payload: { id: contractId, data: { reviewStatus: 'FAILED' } },
|
|
})
|
|
const error = await response.json()
|
|
throw new Error(error.error || 'Fehler beim Starten der Vertragsprüfung')
|
|
}
|
|
|
|
const result = await response.json()
|
|
|
|
dispatch({
|
|
type: 'UPDATE_CONTRACT',
|
|
payload: {
|
|
id: contractId,
|
|
data: {
|
|
reviewStatus: 'COMPLETED',
|
|
reviewCompletedAt: new Date(),
|
|
complianceScore: result.data.complianceScore,
|
|
},
|
|
},
|
|
})
|
|
|
|
if (result.data.findings && result.data.findings.length > 0) {
|
|
dispatch({ type: 'ADD_FINDINGS', payload: result.data.findings })
|
|
}
|
|
},
|
|
[dispatch]
|
|
)
|
|
|
|
return {
|
|
loadData,
|
|
refresh,
|
|
createProcessingActivity,
|
|
deleteProcessingActivity,
|
|
duplicateProcessingActivity,
|
|
deleteVendor,
|
|
deleteContract,
|
|
startContractReview,
|
|
}
|
|
}
|