Files
breakpilot-lehrer/admin-lehrer/lib/sdk/vendor-compliance/context.tsx
Benjamin Admin b681ddb131 [split-required] Split 58 monoliths across Python, Go, TypeScript (Phases 1-3)
Phase 1 — Python (klausur-service): 5 monoliths → 36 files
- dsfa_corpus_ingestion.py (1,828 LOC → 5 files)
- cv_ocr_engines.py (2,102 LOC → 7 files)
- cv_layout.py (3,653 LOC → 10 files)
- vocab_worksheet_api.py (2,783 LOC → 8 files)
- grid_build_core.py (1,958 LOC → 6 files)

Phase 2 — Go (edu-search-service, school-service): 8 monoliths → 19 files
- staff_crawler.go (1,402 → 4), policy/store.go (1,168 → 3)
- policy_handlers.go (700 → 2), repository.go (684 → 2)
- search.go (592 → 2), ai_extraction_handlers.go (554 → 2)
- seed_data.go (591 → 2), grade_service.go (646 → 2)

Phase 3 — TypeScript (admin-lehrer): 45 monoliths → 220+ files
- sdk/types.ts (2,108 → 16 domain files)
- ai/rag/page.tsx (2,686 → 14 files)
- 22 page.tsx files split into _components/ + _hooks/
- 11 component files split into sub-components
- 10 SDK data catalogs added to loc-exceptions
- Deleted dead backup index_original.ts (4,899 LOC)

All original public APIs preserved via re-export facades.
Zero new errors: Python imports verified, Go builds clean,
TypeScript tsc --noEmit shows only pre-existing errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 17:28:57 +02:00

377 lines
8.7 KiB
TypeScript

'use client'
import React, {
createContext,
useContext,
useReducer,
useCallback,
useMemo,
useEffect,
useState,
} from 'react'
import type {
VendorComplianceContextValue,
ProcessingActivity,
Vendor,
ContractDocument,
Finding,
ControlInstance,
ExportFormat,
} from './types'
import {
vendorComplianceReducer,
initialState,
computeVendorStats,
computeComplianceStats,
computeRiskOverview,
} from './context-reducer'
import {
loadAllData,
apiCreateProcessingActivity,
apiUpdateProcessingActivity,
apiDeleteProcessingActivity,
apiDuplicateProcessingActivity,
apiCreateVendor,
apiUpdateVendor,
apiDeleteVendor,
apiUploadContract,
apiUpdateContract,
apiDeleteContract,
apiStartContractReview,
apiUpdateFinding,
apiResolveFinding,
apiUpdateControlInstance,
apiExportVVT,
apiExportVendorAuditPack,
apiExportRoPA,
} from './context-actions'
// ==========================================
// CONTEXT
// ==========================================
const VendorComplianceContext = createContext<VendorComplianceContextValue | null>(null)
// ==========================================
// PROVIDER
// ==========================================
interface VendorComplianceProviderProps {
children: React.ReactNode
tenantId?: string
}
export function VendorComplianceProvider({
children,
tenantId,
}: VendorComplianceProviderProps) {
const [state, dispatch] = useReducer(vendorComplianceReducer, initialState)
const [isInitialized, setIsInitialized] = useState(false)
// ==========================================
// COMPUTED VALUES
// ==========================================
const vendorStats = useMemo(
() => computeVendorStats(state),
[state.vendors, state.contracts]
)
const complianceStats = useMemo(
() => computeComplianceStats(state),
[state.findings, state.contracts, state.controlInstances]
)
const riskOverview = useMemo(
() => computeRiskOverview(state),
[state.vendors, state.findings]
)
// ==========================================
// ACTION WRAPPERS
// ==========================================
const loadData = useCallback(async () => {
await loadAllData(dispatch)
}, [])
const refresh = useCallback(async () => {
await loadData()
}, [loadData])
const createProcessingActivity = useCallback(
async (data: Omit<ProcessingActivity, 'id' | 'tenantId' | 'createdAt' | 'updatedAt'>) => {
return apiCreateProcessingActivity(dispatch, data)
},
[]
)
const updateProcessingActivity = useCallback(
async (id: string, data: Partial<ProcessingActivity>) => {
await apiUpdateProcessingActivity(dispatch, id, data)
},
[]
)
const deleteProcessingActivity = useCallback(
async (id: string) => {
await apiDeleteProcessingActivity(dispatch, id)
},
[]
)
const duplicateProcessingActivity = useCallback(
async (id: string) => {
return apiDuplicateProcessingActivity(dispatch, state, id)
},
[state]
)
const createVendor = useCallback(
async (data: Omit<Vendor, 'id' | 'tenantId' | 'createdAt' | 'updatedAt'>) => {
return apiCreateVendor(dispatch, data)
},
[]
)
const updateVendor = useCallback(
async (id: string, data: Partial<Vendor>) => {
await apiUpdateVendor(dispatch, id, data)
},
[]
)
const deleteVendor = useCallback(
async (id: string) => {
await apiDeleteVendor(dispatch, id)
},
[]
)
const uploadContract = useCallback(
async (vendorId: string, file: File, metadata: Partial<ContractDocument>) => {
return apiUploadContract(dispatch, state, vendorId, file, metadata)
},
[state]
)
const updateContract = useCallback(
async (id: string, data: Partial<ContractDocument>) => {
await apiUpdateContract(dispatch, id, data)
},
[]
)
const deleteContract = useCallback(
async (id: string) => {
await apiDeleteContract(dispatch, state, id)
},
[state]
)
const startContractReview = useCallback(
async (contractId: string) => {
await apiStartContractReview(dispatch, contractId)
},
[]
)
const updateFinding = useCallback(
async (id: string, data: Partial<Finding>) => {
await apiUpdateFinding(dispatch, id, data)
},
[]
)
const resolveFinding = useCallback(
async (id: string, resolution: string) => {
await apiResolveFinding(dispatch, id, resolution)
},
[]
)
const updateControlInstance = useCallback(
async (id: string, data: Partial<ControlInstance>) => {
await apiUpdateControlInstance(dispatch, id, data)
},
[]
)
const exportVVT = useCallback(
async (format: ExportFormat, activityIds?: string[]) => {
return apiExportVVT(format, activityIds)
},
[]
)
const exportVendorAuditPack = useCallback(
async (vendorId: string, format: ExportFormat) => {
return apiExportVendorAuditPack(vendorId, format)
},
[]
)
const exportRoPA = useCallback(
async (format: ExportFormat) => {
return apiExportRoPA(format)
},
[]
)
// ==========================================
// INITIALIZATION
// ==========================================
useEffect(() => {
if (!isInitialized) {
loadData()
setIsInitialized(true)
}
}, [isInitialized, loadData])
// ==========================================
// CONTEXT VALUE
// ==========================================
const contextValue = useMemo<VendorComplianceContextValue>(
() => ({
...state,
dispatch,
vendorStats,
complianceStats,
riskOverview,
createProcessingActivity,
updateProcessingActivity,
deleteProcessingActivity,
duplicateProcessingActivity,
createVendor,
updateVendor,
deleteVendor,
uploadContract,
updateContract,
deleteContract,
startContractReview,
updateFinding,
resolveFinding,
updateControlInstance,
exportVVT,
exportVendorAuditPack,
exportRoPA,
loadData,
refresh,
}),
[
state,
vendorStats,
complianceStats,
riskOverview,
createProcessingActivity,
updateProcessingActivity,
deleteProcessingActivity,
duplicateProcessingActivity,
createVendor,
updateVendor,
deleteVendor,
uploadContract,
updateContract,
deleteContract,
startContractReview,
updateFinding,
resolveFinding,
updateControlInstance,
exportVVT,
exportVendorAuditPack,
exportRoPA,
loadData,
refresh,
]
)
return (
<VendorComplianceContext.Provider value={contextValue}>
{children}
</VendorComplianceContext.Provider>
)
}
// ==========================================
// HOOK
// ==========================================
export function useVendorCompliance(): VendorComplianceContextValue {
const context = useContext(VendorComplianceContext)
if (!context) {
throw new Error(
'useVendorCompliance must be used within a VendorComplianceProvider'
)
}
return context
}
// ==========================================
// SELECTORS
// ==========================================
export function useVendor(vendorId: string | null) {
const { vendors } = useVendorCompliance()
return useMemo(
() => vendors.find((v) => v.id === vendorId) ?? null,
[vendors, vendorId]
)
}
export function useProcessingActivity(activityId: string | null) {
const { processingActivities } = useVendorCompliance()
return useMemo(
() => processingActivities.find((a) => a.id === activityId) ?? null,
[processingActivities, activityId]
)
}
export function useVendorContracts(vendorId: string | null) {
const { contracts } = useVendorCompliance()
return useMemo(
() => contracts.filter((c) => c.vendorId === vendorId),
[contracts, vendorId]
)
}
export function useVendorFindings(vendorId: string | null) {
const { findings } = useVendorCompliance()
return useMemo(
() => findings.filter((f) => f.vendorId === vendorId),
[findings, vendorId]
)
}
export function useContractFindings(contractId: string | null) {
const { findings } = useVendorCompliance()
return useMemo(
() => findings.filter((f) => f.contractId === contractId),
[findings, contractId]
)
}
export function useControlInstancesForEntity(
entityType: 'VENDOR' | 'PROCESSING_ACTIVITY',
entityId: string | null
) {
const { controlInstances, controls } = useVendorCompliance()
return useMemo(() => {
if (!entityId) return []
return controlInstances
.filter((ci) => ci.entityType === entityType && ci.entityId === entityId)
.map((ci) => ({
...ci,
control: controls.find((c) => c.id === ci.controlId),
}))
}, [controlInstances, controls, entityType, entityId])
}