'use client' import React, { useReducer, useMemo, useEffect, useState, } from 'react' import { VendorComplianceContextValue, VendorStatistics, ComplianceStatistics, RiskOverview, VendorStatus, VendorRole, RiskLevel, FindingType, FindingSeverity, getRiskLevelFromScore, } from './types' import { initialState, vendorComplianceReducer } from './reducer' import { VendorComplianceContext } from './hooks' import { useVendorComplianceActions } from './use-actions' // Re-export hooks and selectors for barrel export { useVendorCompliance, useVendor, useProcessingActivity, useVendorContracts, useVendorFindings, useContractFindings, useControlInstancesForEntity, } from './hooks' // ========================================== // 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) const actions = useVendorComplianceActions(state, dispatch) // ========================================== // COMPUTED VALUES // ========================================== const vendorStats = useMemo(() => { const vendors = state.vendors const byStatus = vendors.reduce( (acc, v) => { acc[v.status] = (acc[v.status] || 0) + 1 return acc }, {} as Record ) const byRole = vendors.reduce( (acc, v) => { acc[v.role] = (acc[v.role] || 0) + 1 return acc }, {} as Record ) const byRiskLevel = vendors.reduce( (acc, v) => { const level = getRiskLevelFromScore(v.residualRiskScore / 4) acc[level] = (acc[level] || 0) + 1 return acc }, {} as Record ) const now = new Date() const pendingReviews = vendors.filter( (v) => v.nextReviewDate && new Date(v.nextReviewDate) <= now ).length const withExpiredContracts = vendors.filter((v) => state.contracts.some( (c) => c.vendorId === v.id && c.expirationDate && new Date(c.expirationDate) <= now && c.status === 'ACTIVE' ) ).length return { total: vendors.length, byStatus, byRole, byRiskLevel, pendingReviews, withExpiredContracts, } }, [state.vendors, state.contracts]) const complianceStats = useMemo(() => { const findings = state.findings const contracts = state.contracts const controlInstances = state.controlInstances const averageComplianceScore = contracts.length > 0 ? contracts.reduce((sum, c) => sum + (c.complianceScore || 0), 0) / contracts.filter((c) => c.complianceScore !== undefined).length || 0 : 0 const findingsByType = findings.reduce( (acc, f) => { acc[f.type] = (acc[f.type] || 0) + 1 return acc }, {} as Record ) const findingsBySeverity = findings.reduce( (acc, f) => { acc[f.severity] = (acc[f.severity] || 0) + 1 return acc }, {} as Record ) const openFindings = findings.filter( (f) => f.status === 'OPEN' || f.status === 'IN_PROGRESS' ).length const resolvedFindings = findings.filter( (f) => f.status === 'RESOLVED' || f.status === 'FALSE_POSITIVE' ).length const passedControls = controlInstances.filter( (ci) => ci.status === 'PASS' ).length const applicableControls = controlInstances.filter( (ci) => ci.status !== 'NOT_APPLICABLE' ).length const controlPassRate = applicableControls > 0 ? (passedControls / applicableControls) * 100 : 0 return { averageComplianceScore, findingsByType, findingsBySeverity, openFindings, resolvedFindings, controlPassRate, } }, [state.findings, state.contracts, state.controlInstances]) const riskOverview = useMemo(() => { const vendors = state.vendors const findings = state.findings const averageInherentRisk = vendors.length > 0 ? vendors.reduce((sum, v) => sum + v.inherentRiskScore, 0) / vendors.length : 0 const averageResidualRisk = vendors.length > 0 ? vendors.reduce((sum, v) => sum + v.residualRiskScore, 0) / vendors.length : 0 const highRiskVendors = vendors.filter( (v) => v.residualRiskScore >= 60 ).length const criticalFindings = findings.filter( (f) => f.severity === 'CRITICAL' && f.status === 'OPEN' ).length const transfersToThirdCountries = vendors.filter((v) => v.processingLocations.some((pl) => !pl.isEU && !pl.isAdequate) ).length return { averageInherentRisk, averageResidualRisk, highRiskVendors, criticalFindings, transfersToThirdCountries, } }, [state.vendors, state.findings]) // ========================================== // INITIALIZATION // ========================================== useEffect(() => { if (!isInitialized) { actions.loadData() setIsInitialized(true) } }, [isInitialized, actions]) // ========================================== // CONTEXT VALUE // ========================================== const contextValue = useMemo( () => ({ ...state, dispatch, vendorStats, complianceStats, riskOverview, ...actions, }), [state, vendorStats, complianceStats, riskOverview, actions] ) return ( {children} ) }