'use client' import React, { useReducer, useMemo, useRef, useState, } from 'react' import { ComplianceClient, sdkReducer, initialState, StateSyncManager, createDSGVOModule, createComplianceModule, createRAGModule, createSecurityModule, } from '@breakpilot/compliance-sdk-core' import type { SyncState } from '@breakpilot/compliance-sdk-types' import { getStepById, getNextStep, getPreviousStep, getCompletionPercentage, getPhaseCompletionPercentage, } from '@breakpilot/compliance-sdk-types' import { ComplianceContext, type ComplianceContextValue, type ComplianceProviderProps, } from './provider-context' import { useSyncManagerEffect, useLoadInitialStateEffect, useAutoSaveEffect, useKeyboardShortcutsEffect, } from './provider-effects' import { useValidateCheckpoint, useOverrideCheckpoint, useGetCheckpointStatus, useUpdateUseCase, useAddRisk, useUpdateControl, useSaveState, useLoadState, useResetState, useForceSyncToServer, useExportState, } from './provider-callbacks' import { useCallback, useMemo as useMemoReact } from 'react' export { ComplianceContext, type ComplianceContextValue, type ComplianceProviderProps, } from './provider-context' // Re-export useCompliance so legacy component imports (`from '../provider'`) // keep resolving. Pre-existing cross-file import that was broken in baseline. export { useCompliance } from './hooks' // ============================================================================= // PROVIDER // ============================================================================= export function ComplianceProvider({ children, apiEndpoint, apiKey, tenantId, userId = 'default', enableBackendSync = true, onNavigate, onError, }: ComplianceProviderProps) { const [state, dispatch] = useReducer(sdkReducer, { ...initialState, tenantId, userId, }) const [isCommandBarOpen, setCommandBarOpenRaw] = useState(false) const [isInitialized, setIsInitialized] = useState(false) const [isLoading, setIsLoading] = useState(true) const [error, setError] = useState(null) const [syncState, setSyncState] = useState({ status: 'idle', lastSyncedAt: null, localVersion: 0, serverVersion: 0, pendingChanges: 0, error: null, }) const [isOnline, setIsOnline] = useState(true) // Refs const clientRef = useRef(null) const syncManagerRef = useRef(null) // Initialize client (once) if (!clientRef.current) { clientRef.current = new ComplianceClient({ apiEndpoint, apiKey, tenantId, onError: err => { setError(err) onError?.(err) }, }) } const client = clientRef.current // Modules const dsgvo = useMemo(() => createDSGVOModule(client, () => state), [client, state]) const compliance = useMemo(() => createComplianceModule(client, () => state), [client, state]) const rag = useMemo(() => createRAGModule(client), [client]) const security = useMemo(() => createSecurityModule(client, () => state), [client, state]) // ------------------------------------------------------------------------- // Effects (extracted to provider-effects.ts) // ------------------------------------------------------------------------- const syncStateSetters = useMemo( () => ({ setSyncState, setIsOnline, setError, dispatch }), // eslint-disable-next-line react-hooks/exhaustive-deps [] ) useSyncManagerEffect(enableBackendSync, tenantId, client, state, syncManagerRef, syncStateSetters) useLoadInitialStateEffect(tenantId, enableBackendSync, syncManagerRef, { setIsLoading, setIsInitialized, setError, dispatch, onError, }) useAutoSaveEffect(state, tenantId, isInitialized, enableBackendSync, syncManagerRef) const setCommandBarOpen = useCallback( (fn: boolean | ((prev: boolean) => boolean)) => { setCommandBarOpenRaw(typeof fn === 'function' ? fn : () => fn) }, [] ) useKeyboardShortcutsEffect(isCommandBarOpen, setCommandBarOpen as (fn: (prev: boolean) => boolean) => void) // ------------------------------------------------------------------------- // Navigation // ------------------------------------------------------------------------- const currentStep = useMemo(() => getStepById(state.currentStep), [state.currentStep]) const goToStep = useCallback( (stepId: string) => { const step = getStepById(stepId) if (step) { dispatch({ type: 'SET_CURRENT_STEP', payload: stepId }) onNavigate?.(step.url) } }, [onNavigate] ) const goToNextStep = useCallback(() => { const nextStep = getNextStep(state.currentStep) if (nextStep) goToStep(nextStep.id) }, [state.currentStep, goToStep]) const goToPreviousStep = useCallback(() => { const prevStep = getPreviousStep(state.currentStep) if (prevStep) goToStep(prevStep.id) }, [state.currentStep, goToStep]) const canGoNext = useMemo(() => getNextStep(state.currentStep) !== undefined, [state.currentStep]) const canGoPrevious = useMemo( () => getPreviousStep(state.currentStep) !== undefined, [state.currentStep] ) const completionPercentage = useMemo(() => getCompletionPercentage(state), [state]) const phase1Completion = useMemo(() => getPhaseCompletionPercentage(state, 1), [state]) const phase2Completion = useMemo(() => getPhaseCompletionPercentage(state, 2), [state]) // ------------------------------------------------------------------------- // Callbacks (extracted to provider-callbacks.ts) // ------------------------------------------------------------------------- const validateCheckpoint = useValidateCheckpoint(state, enableBackendSync, client, dispatch) const overrideCheckpoint = useOverrideCheckpoint(state, dispatch) const getCheckpointStatus = useGetCheckpointStatus(state) const updateUseCase = useUpdateUseCase(dispatch) const addRisk = useAddRisk(dispatch) const updateControl = useUpdateControl(dispatch) const saveState = useSaveState(state, tenantId, enableBackendSync, syncManagerRef, e => setError(e)) const loadState = useLoadState(tenantId, enableBackendSync, syncManagerRef, setIsLoading, dispatch, e => setError(e)) const resetState = useResetState(tenantId, dispatch) const forceSyncToServer = useForceSyncToServer(state, enableBackendSync, syncManagerRef) const exportState = useExportState(state, client) // ------------------------------------------------------------------------- // Context value // ------------------------------------------------------------------------- const value: ComplianceContextValue = { state, dispatch, client, dsgvo, compliance, rag, security, currentStep, goToStep, goToNextStep, goToPreviousStep, canGoNext, canGoPrevious, completionPercentage, phase1Completion, phase2Completion, validateCheckpoint, overrideCheckpoint, getCheckpointStatus, updateUseCase, addRisk, updateControl, saveState, loadState, resetState, syncState, forceSyncToServer, isOnline, exportState, isCommandBarOpen, setCommandBarOpen: setCommandBarOpenRaw, isInitialized, isLoading, error, } return {children} }