Extract the monolithic SDK context provider into seven focused modules: - context-types.ts (203 LOC): SDKContextValue interface, initialState, ExtendedSDKAction - context-reducer.ts (353 LOC): sdkReducer with all action handlers - context-provider.tsx (495 LOC): SDKProvider component + SDKContext - context-hooks.ts (17 LOC): useSDK hook - context-validators.ts (94 LOC): local checkpoint validation logic - context-projects.ts (67 LOC): project management API helpers - context-sync-helpers.ts (145 LOC): sync infrastructure init/cleanup/callbacks - context.tsx (23 LOC): barrel re-export preserving existing import paths All files under the 500-line hard cap. Build verified with `npx next build`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
354 lines
9.6 KiB
TypeScript
354 lines
9.6 KiB
TypeScript
import {
|
|
SDKState,
|
|
getStepById,
|
|
} from './types'
|
|
import { ExtendedSDKAction, initialState } from './context-types'
|
|
|
|
// =============================================================================
|
|
// REDUCER
|
|
// =============================================================================
|
|
|
|
export function sdkReducer(state: SDKState, action: ExtendedSDKAction): SDKState {
|
|
const updateState = (updates: Partial<SDKState>): SDKState => ({
|
|
...state,
|
|
...updates,
|
|
lastModified: new Date(),
|
|
})
|
|
|
|
switch (action.type) {
|
|
case 'SET_STATE':
|
|
return updateState(action.payload)
|
|
|
|
case 'LOAD_DEMO_DATA':
|
|
// Load demo data while preserving user preferences
|
|
return {
|
|
...initialState,
|
|
...action.payload,
|
|
tenantId: state.tenantId,
|
|
userId: state.userId,
|
|
preferences: state.preferences,
|
|
lastModified: new Date(),
|
|
}
|
|
|
|
case 'SET_CURRENT_STEP': {
|
|
const step = getStepById(action.payload)
|
|
return updateState({
|
|
currentStep: action.payload,
|
|
currentPhase: step?.phase || state.currentPhase,
|
|
})
|
|
}
|
|
|
|
case 'COMPLETE_STEP':
|
|
if (state.completedSteps.includes(action.payload)) {
|
|
return state
|
|
}
|
|
return updateState({
|
|
completedSteps: [...state.completedSteps, action.payload],
|
|
})
|
|
|
|
case 'SET_CHECKPOINT_STATUS':
|
|
return updateState({
|
|
checkpoints: {
|
|
...state.checkpoints,
|
|
[action.payload.id]: action.payload.status,
|
|
},
|
|
})
|
|
|
|
case 'SET_CUSTOMER_TYPE':
|
|
return updateState({ customerType: action.payload })
|
|
|
|
case 'SET_COMPANY_PROFILE':
|
|
return updateState({ companyProfile: action.payload })
|
|
|
|
case 'UPDATE_COMPANY_PROFILE':
|
|
return updateState({
|
|
companyProfile: state.companyProfile
|
|
? { ...state.companyProfile, ...action.payload }
|
|
: null,
|
|
})
|
|
|
|
case 'SET_COMPLIANCE_SCOPE':
|
|
return updateState({ complianceScope: action.payload })
|
|
|
|
case 'UPDATE_COMPLIANCE_SCOPE':
|
|
return updateState({
|
|
complianceScope: state.complianceScope
|
|
? { ...state.complianceScope, ...action.payload }
|
|
: null,
|
|
})
|
|
|
|
case 'ADD_IMPORTED_DOCUMENT':
|
|
return updateState({
|
|
importedDocuments: [...state.importedDocuments, action.payload],
|
|
})
|
|
|
|
case 'UPDATE_IMPORTED_DOCUMENT':
|
|
return updateState({
|
|
importedDocuments: state.importedDocuments.map(doc =>
|
|
doc.id === action.payload.id ? { ...doc, ...action.payload.data } : doc
|
|
),
|
|
})
|
|
|
|
case 'DELETE_IMPORTED_DOCUMENT':
|
|
return updateState({
|
|
importedDocuments: state.importedDocuments.filter(doc => doc.id !== action.payload),
|
|
})
|
|
|
|
case 'SET_GAP_ANALYSIS':
|
|
return updateState({ gapAnalysis: action.payload })
|
|
|
|
case 'ADD_USE_CASE':
|
|
return updateState({
|
|
useCases: [...state.useCases, action.payload],
|
|
})
|
|
|
|
case 'UPDATE_USE_CASE':
|
|
return updateState({
|
|
useCases: state.useCases.map(uc =>
|
|
uc.id === action.payload.id ? { ...uc, ...action.payload.data } : uc
|
|
),
|
|
})
|
|
|
|
case 'DELETE_USE_CASE':
|
|
return updateState({
|
|
useCases: state.useCases.filter(uc => uc.id !== action.payload),
|
|
activeUseCase: state.activeUseCase === action.payload ? null : state.activeUseCase,
|
|
})
|
|
|
|
case 'SET_ACTIVE_USE_CASE':
|
|
return updateState({ activeUseCase: action.payload })
|
|
|
|
case 'SET_SCREENING':
|
|
return updateState({ screening: action.payload })
|
|
|
|
case 'ADD_MODULE':
|
|
return updateState({
|
|
modules: [...state.modules, action.payload],
|
|
})
|
|
|
|
case 'UPDATE_MODULE':
|
|
return updateState({
|
|
modules: state.modules.map(m =>
|
|
m.id === action.payload.id ? { ...m, ...action.payload.data } : m
|
|
),
|
|
})
|
|
|
|
case 'ADD_REQUIREMENT':
|
|
return updateState({
|
|
requirements: [...state.requirements, action.payload],
|
|
})
|
|
|
|
case 'UPDATE_REQUIREMENT':
|
|
return updateState({
|
|
requirements: state.requirements.map(r =>
|
|
r.id === action.payload.id ? { ...r, ...action.payload.data } : r
|
|
),
|
|
})
|
|
|
|
case 'ADD_CONTROL':
|
|
return updateState({
|
|
controls: [...state.controls, action.payload],
|
|
})
|
|
|
|
case 'UPDATE_CONTROL':
|
|
return updateState({
|
|
controls: state.controls.map(c =>
|
|
c.id === action.payload.id ? { ...c, ...action.payload.data } : c
|
|
),
|
|
})
|
|
|
|
case 'ADD_EVIDENCE':
|
|
return updateState({
|
|
evidence: [...state.evidence, action.payload],
|
|
})
|
|
|
|
case 'UPDATE_EVIDENCE':
|
|
return updateState({
|
|
evidence: state.evidence.map(e =>
|
|
e.id === action.payload.id ? { ...e, ...action.payload.data } : e
|
|
),
|
|
})
|
|
|
|
case 'DELETE_EVIDENCE':
|
|
return updateState({
|
|
evidence: state.evidence.filter(e => e.id !== action.payload),
|
|
})
|
|
|
|
case 'ADD_RISK':
|
|
return updateState({
|
|
risks: [...state.risks, action.payload],
|
|
})
|
|
|
|
case 'UPDATE_RISK':
|
|
return updateState({
|
|
risks: state.risks.map(r =>
|
|
r.id === action.payload.id ? { ...r, ...action.payload.data } : r
|
|
),
|
|
})
|
|
|
|
case 'DELETE_RISK':
|
|
return updateState({
|
|
risks: state.risks.filter(r => r.id !== action.payload),
|
|
})
|
|
|
|
case 'SET_AI_ACT_RESULT':
|
|
return updateState({ aiActClassification: action.payload })
|
|
|
|
case 'ADD_OBLIGATION':
|
|
return updateState({
|
|
obligations: [...state.obligations, action.payload],
|
|
})
|
|
|
|
case 'UPDATE_OBLIGATION':
|
|
return updateState({
|
|
obligations: state.obligations.map(o =>
|
|
o.id === action.payload.id ? { ...o, ...action.payload.data } : o
|
|
),
|
|
})
|
|
|
|
case 'SET_DSFA':
|
|
return updateState({ dsfa: action.payload })
|
|
|
|
case 'ADD_TOM':
|
|
return updateState({
|
|
toms: [...state.toms, action.payload],
|
|
})
|
|
|
|
case 'UPDATE_TOM':
|
|
return updateState({
|
|
toms: state.toms.map(t =>
|
|
t.id === action.payload.id ? { ...t, ...action.payload.data } : t
|
|
),
|
|
})
|
|
|
|
case 'ADD_RETENTION_POLICY':
|
|
return updateState({
|
|
retentionPolicies: [...state.retentionPolicies, action.payload],
|
|
})
|
|
|
|
case 'UPDATE_RETENTION_POLICY':
|
|
return updateState({
|
|
retentionPolicies: state.retentionPolicies.map(p =>
|
|
p.id === action.payload.id ? { ...p, ...action.payload.data } : p
|
|
),
|
|
})
|
|
|
|
case 'ADD_PROCESSING_ACTIVITY':
|
|
return updateState({
|
|
vvt: [...state.vvt, action.payload],
|
|
})
|
|
|
|
case 'UPDATE_PROCESSING_ACTIVITY':
|
|
return updateState({
|
|
vvt: state.vvt.map(p =>
|
|
p.id === action.payload.id ? { ...p, ...action.payload.data } : p
|
|
),
|
|
})
|
|
|
|
case 'ADD_DOCUMENT':
|
|
return updateState({
|
|
documents: [...state.documents, action.payload],
|
|
})
|
|
|
|
case 'UPDATE_DOCUMENT':
|
|
return updateState({
|
|
documents: state.documents.map(d =>
|
|
d.id === action.payload.id ? { ...d, ...action.payload.data } : d
|
|
),
|
|
})
|
|
|
|
case 'SET_COOKIE_BANNER':
|
|
return updateState({ cookieBanner: action.payload })
|
|
|
|
case 'SET_DSR_CONFIG':
|
|
return updateState({ dsrConfig: action.payload })
|
|
|
|
case 'ADD_ESCALATION_WORKFLOW':
|
|
return updateState({
|
|
escalationWorkflows: [...state.escalationWorkflows, action.payload],
|
|
})
|
|
|
|
case 'UPDATE_ESCALATION_WORKFLOW':
|
|
return updateState({
|
|
escalationWorkflows: state.escalationWorkflows.map(w =>
|
|
w.id === action.payload.id ? { ...w, ...action.payload.data } : w
|
|
),
|
|
})
|
|
|
|
case 'ADD_SECURITY_ISSUE':
|
|
return updateState({
|
|
securityIssues: [...state.securityIssues, action.payload],
|
|
})
|
|
|
|
case 'UPDATE_SECURITY_ISSUE':
|
|
return updateState({
|
|
securityIssues: state.securityIssues.map(i =>
|
|
i.id === action.payload.id ? { ...i, ...action.payload.data } : i
|
|
),
|
|
})
|
|
|
|
case 'ADD_BACKLOG_ITEM':
|
|
return updateState({
|
|
securityBacklog: [...state.securityBacklog, action.payload],
|
|
})
|
|
|
|
case 'UPDATE_BACKLOG_ITEM':
|
|
return updateState({
|
|
securityBacklog: state.securityBacklog.map(i =>
|
|
i.id === action.payload.id ? { ...i, ...action.payload.data } : i
|
|
),
|
|
})
|
|
|
|
case 'ADD_COMMAND_HISTORY':
|
|
return updateState({
|
|
commandBarHistory: [action.payload, ...state.commandBarHistory].slice(0, 50),
|
|
})
|
|
|
|
case 'SET_PREFERENCES':
|
|
return updateState({
|
|
preferences: { ...state.preferences, ...action.payload },
|
|
})
|
|
|
|
case 'ADD_CUSTOM_CATALOG_ENTRY': {
|
|
const entry = action.payload
|
|
const existing = state.customCatalogs[entry.catalogId] || []
|
|
return updateState({
|
|
customCatalogs: {
|
|
...state.customCatalogs,
|
|
[entry.catalogId]: [...existing, entry],
|
|
},
|
|
})
|
|
}
|
|
|
|
case 'UPDATE_CUSTOM_CATALOG_ENTRY': {
|
|
const { catalogId, entryId, data } = action.payload
|
|
const entries = state.customCatalogs[catalogId] || []
|
|
return updateState({
|
|
customCatalogs: {
|
|
...state.customCatalogs,
|
|
[catalogId]: entries.map(e =>
|
|
e.id === entryId ? { ...e, data: { ...e.data, ...data }, updatedAt: new Date().toISOString() } : e
|
|
),
|
|
},
|
|
})
|
|
}
|
|
|
|
case 'DELETE_CUSTOM_CATALOG_ENTRY': {
|
|
const { catalogId, entryId } = action.payload
|
|
const items = state.customCatalogs[catalogId] || []
|
|
return updateState({
|
|
customCatalogs: {
|
|
...state.customCatalogs,
|
|
[catalogId]: items.filter(e => e.id !== entryId),
|
|
},
|
|
})
|
|
}
|
|
|
|
case 'RESET_STATE':
|
|
return { ...initialState, lastModified: new Date() }
|
|
|
|
default:
|
|
return state
|
|
}
|
|
}
|