merge: sync with origin/main, take upstream on conflicts
# Conflicts: # admin-compliance/lib/sdk/types.ts # admin-compliance/lib/sdk/vendor-compliance/types.ts
This commit is contained in:
@@ -9,6 +9,7 @@ import React, {
|
||||
|
||||
import {
|
||||
VendorComplianceContextValue,
|
||||
ProcessingActivity,
|
||||
VendorStatistics,
|
||||
ComplianceStatistics,
|
||||
RiskOverview,
|
||||
@@ -199,6 +200,245 @@ export function VendorComplianceProvider({
|
||||
}
|
||||
}, [state.vendors, state.findings])
|
||||
|
||||
// ==========================================
|
||||
// API CALLS
|
||||
// ==========================================
|
||||
|
||||
const apiBase = '/api/sdk/v1/vendor-compliance'
|
||||
|
||||
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(`${apiBase}/processing-activities`),
|
||||
fetch(`${apiBase}/vendors`),
|
||||
fetch(`${apiBase}/contracts`),
|
||||
fetch(`${apiBase}/findings`),
|
||||
fetch(`${apiBase}/controls`),
|
||||
fetch(`${apiBase}/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 })
|
||||
}
|
||||
}, [apiBase])
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
await loadData()
|
||||
}, [loadData])
|
||||
|
||||
// ==========================================
|
||||
// PROCESSING ACTIVITIES ACTIONS
|
||||
// ==========================================
|
||||
|
||||
const createProcessingActivity = useCallback(
|
||||
async (
|
||||
data: Omit<ProcessingActivity, 'id' | 'tenantId' | 'createdAt' | 'updatedAt'>
|
||||
): Promise<ProcessingActivity> => {
|
||||
const response = await fetch(`${apiBase}/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
|
||||
},
|
||||
[apiBase]
|
||||
)
|
||||
|
||||
const deleteProcessingActivity = useCallback(
|
||||
async (id: string): Promise<void> => {
|
||||
const response = await fetch(`${apiBase}/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 })
|
||||
},
|
||||
[apiBase]
|
||||
)
|
||||
|
||||
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: '', // Will be generated by backend
|
||||
name: {
|
||||
de: `${original.name.de} (Kopie)`,
|
||||
en: `${original.name.en} (Copy)`,
|
||||
},
|
||||
status: 'DRAFT',
|
||||
})
|
||||
|
||||
return newActivity
|
||||
},
|
||||
[state.processingActivities, createProcessingActivity]
|
||||
)
|
||||
|
||||
// ==========================================
|
||||
// VENDOR ACTIONS
|
||||
// ==========================================
|
||||
|
||||
const deleteVendor = useCallback(
|
||||
async (id: string): Promise<void> => {
|
||||
const response = await fetch(`${apiBase}/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 })
|
||||
},
|
||||
[apiBase]
|
||||
)
|
||||
|
||||
// ==========================================
|
||||
// CONTRACT ACTIONS
|
||||
// ==========================================
|
||||
|
||||
const deleteContract = useCallback(
|
||||
async (id: string): Promise<void> => {
|
||||
const contract = state.contracts.find((c) => c.id === id)
|
||||
|
||||
const response = await fetch(`${apiBase}/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 })
|
||||
|
||||
// Update vendor's contracts list
|
||||
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) },
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
[apiBase, 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(`${apiBase}/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()
|
||||
|
||||
// Update contract with review results
|
||||
dispatch({
|
||||
type: 'UPDATE_CONTRACT',
|
||||
payload: {
|
||||
id: contractId,
|
||||
data: {
|
||||
reviewStatus: 'COMPLETED',
|
||||
reviewCompletedAt: new Date(),
|
||||
complianceScore: result.data.complianceScore,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Add findings
|
||||
if (result.data.findings && result.data.findings.length > 0) {
|
||||
dispatch({ type: 'ADD_FINDINGS', payload: result.data.findings })
|
||||
}
|
||||
},
|
||||
[apiBase]
|
||||
)
|
||||
|
||||
// ==========================================
|
||||
// INITIALIZATION
|
||||
// ==========================================
|
||||
@@ -221,9 +461,27 @@ export function VendorComplianceProvider({
|
||||
vendorStats,
|
||||
complianceStats,
|
||||
riskOverview,
|
||||
...actions,
|
||||
deleteProcessingActivity,
|
||||
duplicateProcessingActivity,
|
||||
deleteVendor,
|
||||
deleteContract,
|
||||
startContractReview,
|
||||
loadData,
|
||||
refresh,
|
||||
}),
|
||||
[state, vendorStats, complianceStats, riskOverview, actions]
|
||||
[
|
||||
state,
|
||||
vendorStats,
|
||||
complianceStats,
|
||||
riskOverview,
|
||||
deleteProcessingActivity,
|
||||
duplicateProcessingActivity,
|
||||
deleteVendor,
|
||||
deleteContract,
|
||||
startContractReview,
|
||||
loadData,
|
||||
refresh,
|
||||
]
|
||||
)
|
||||
|
||||
return (
|
||||
@@ -232,3 +490,20 @@ export function VendorComplianceProvider({
|
||||
</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
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,6 @@ export * from './types'
|
||||
export {
|
||||
VendorComplianceProvider,
|
||||
useVendorCompliance,
|
||||
useVendor,
|
||||
useProcessingActivity,
|
||||
useVendorContracts,
|
||||
useVendorFindings,
|
||||
useContractFindings,
|
||||
useControlInstancesForEntity,
|
||||
} from './context'
|
||||
|
||||
// ==========================================
|
||||
|
||||
1190
admin-compliance/lib/sdk/vendor-compliance/types.ts
Normal file
1190
admin-compliance/lib/sdk/vendor-compliance/types.ts
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user