Files
breakpilot-compliance/admin-compliance/lib/sdk/tom-generator/reducer.ts
Sharang Parnerkar 58e95d5e8e refactor(admin): split 9 more oversized lib/ files into focused modules
Files split by agents before rate limit:
  - dsr/api.ts (669 → barrel + helpers)
  - einwilligungen/context.tsx (669 → barrel + hooks/reducer)
  - export.ts (753 → barrel + domain exporters)
  - incidents/api.ts (845 → barrel + api-helpers)
  - tom-generator/context.tsx (720 → barrel + hooks/reducer)
  - vendor-compliance/context.tsx (1010 → 234 provider + hooks/reducer)
  - api-docs/endpoints.ts — partially split (3 domain files created)
  - academy/api.ts — partially split (helpers extracted)
  - whistleblower/api.ts — partially split (helpers extracted)

next build passes. api-client.ts (885) deferred to next session.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 19:12:09 +02:00

239 lines
7.6 KiB
TypeScript

// =============================================================================
// TOM Generator Reducer
// Action types and state reducer for the TOM Generator Wizard
// =============================================================================
import {
TOMGeneratorState,
TOMGeneratorStepId,
CompanyProfile,
DataProfile,
ArchitectureProfile,
SecurityProfile,
RiskProfile,
EvidenceDocument,
DerivedTOM,
GapAnalysisResult,
ExportRecord,
WizardStep,
createInitialTOMGeneratorState,
calculateProtectionLevel,
isDSFARequired,
hasSpecialCategories,
} from './types'
// =============================================================================
// ACTION TYPES
// =============================================================================
export type TOMGeneratorAction =
| { type: 'INITIALIZE'; payload: { tenantId: string; state?: TOMGeneratorState } }
| { type: 'RESET'; payload: { tenantId: string } }
| { type: 'SET_CURRENT_STEP'; payload: TOMGeneratorStepId }
| { type: 'SET_COMPANY_PROFILE'; payload: CompanyProfile }
| { type: 'UPDATE_COMPANY_PROFILE'; payload: Partial<CompanyProfile> }
| { type: 'SET_DATA_PROFILE'; payload: DataProfile }
| { type: 'UPDATE_DATA_PROFILE'; payload: Partial<DataProfile> }
| { type: 'SET_ARCHITECTURE_PROFILE'; payload: ArchitectureProfile }
| { type: 'UPDATE_ARCHITECTURE_PROFILE'; payload: Partial<ArchitectureProfile> }
| { type: 'SET_SECURITY_PROFILE'; payload: SecurityProfile }
| { type: 'UPDATE_SECURITY_PROFILE'; payload: Partial<SecurityProfile> }
| { type: 'SET_RISK_PROFILE'; payload: RiskProfile }
| { type: 'UPDATE_RISK_PROFILE'; payload: Partial<RiskProfile> }
| { type: 'COMPLETE_STEP'; payload: { stepId: TOMGeneratorStepId; data: unknown } }
| { type: 'UNCOMPLETE_STEP'; payload: TOMGeneratorStepId }
| { type: 'ADD_EVIDENCE'; payload: EvidenceDocument }
| { type: 'UPDATE_EVIDENCE'; payload: { id: string; data: Partial<EvidenceDocument> } }
| { type: 'DELETE_EVIDENCE'; payload: string }
| { type: 'SET_DERIVED_TOMS'; payload: DerivedTOM[] }
| { type: 'UPDATE_DERIVED_TOM'; payload: { id: string; data: Partial<DerivedTOM> } }
| { type: 'SET_GAP_ANALYSIS'; payload: GapAnalysisResult }
| { type: 'ADD_EXPORT'; payload: ExportRecord }
| { type: 'BULK_UPDATE_TOMS'; payload: { updates: Array<{ id: string; data: Partial<DerivedTOM> }> } }
| { type: 'LOAD_STATE'; payload: TOMGeneratorState }
// =============================================================================
// REDUCER
// =============================================================================
export function tomGeneratorReducer(
state: TOMGeneratorState,
action: TOMGeneratorAction
): TOMGeneratorState {
const updateState = (updates: Partial<TOMGeneratorState>): TOMGeneratorState => ({
...state,
...updates,
updatedAt: new Date(),
})
switch (action.type) {
case 'INITIALIZE': {
if (action.payload.state) {
return action.payload.state
}
return createInitialTOMGeneratorState(action.payload.tenantId)
}
case 'RESET': {
return createInitialTOMGeneratorState(action.payload.tenantId)
}
case 'SET_CURRENT_STEP': {
return updateState({ currentStep: action.payload })
}
case 'SET_COMPANY_PROFILE': {
return updateState({ companyProfile: action.payload })
}
case 'UPDATE_COMPANY_PROFILE': {
if (!state.companyProfile) return state
return updateState({
companyProfile: { ...state.companyProfile, ...action.payload },
})
}
case 'SET_DATA_PROFILE': {
const profile: DataProfile = {
...action.payload,
hasSpecialCategories: hasSpecialCategories(action.payload.categories),
}
return updateState({ dataProfile: profile })
}
case 'UPDATE_DATA_PROFILE': {
if (!state.dataProfile) return state
const updatedProfile = { ...state.dataProfile, ...action.payload }
if (action.payload.categories) {
updatedProfile.hasSpecialCategories = hasSpecialCategories(
action.payload.categories
)
}
return updateState({ dataProfile: updatedProfile })
}
case 'SET_ARCHITECTURE_PROFILE': {
return updateState({ architectureProfile: action.payload })
}
case 'UPDATE_ARCHITECTURE_PROFILE': {
if (!state.architectureProfile) return state
return updateState({
architectureProfile: { ...state.architectureProfile, ...action.payload },
})
}
case 'SET_SECURITY_PROFILE': {
return updateState({ securityProfile: action.payload })
}
case 'UPDATE_SECURITY_PROFILE': {
if (!state.securityProfile) return state
return updateState({
securityProfile: { ...state.securityProfile, ...action.payload },
})
}
case 'SET_RISK_PROFILE': {
const profile: RiskProfile = {
...action.payload,
protectionLevel: calculateProtectionLevel(action.payload.ciaAssessment),
dsfaRequired: isDSFARequired(state.dataProfile, action.payload),
}
return updateState({ riskProfile: profile })
}
case 'UPDATE_RISK_PROFILE': {
if (!state.riskProfile) return state
const updatedProfile = { ...state.riskProfile, ...action.payload }
if (action.payload.ciaAssessment) {
updatedProfile.protectionLevel = calculateProtectionLevel(
action.payload.ciaAssessment
)
}
updatedProfile.dsfaRequired = isDSFARequired(state.dataProfile, updatedProfile)
return updateState({ riskProfile: updatedProfile })
}
case 'COMPLETE_STEP': {
const updatedSteps = state.steps.map((step) =>
step.id === action.payload.stepId
? {
...step,
completed: true,
data: action.payload.data,
validatedAt: new Date(),
}
: step
)
return updateState({ steps: updatedSteps })
}
case 'UNCOMPLETE_STEP': {
const updatedSteps = state.steps.map((step) =>
step.id === action.payload
? { ...step, completed: false, validatedAt: null }
: step
)
return updateState({ steps: updatedSteps })
}
case 'ADD_EVIDENCE': {
return updateState({
documents: [...state.documents, action.payload],
})
}
case 'UPDATE_EVIDENCE': {
const updatedDocuments = state.documents.map((doc) =>
doc.id === action.payload.id ? { ...doc, ...action.payload.data } : doc
)
return updateState({ documents: updatedDocuments })
}
case 'DELETE_EVIDENCE': {
return updateState({
documents: state.documents.filter((doc) => doc.id !== action.payload),
})
}
case 'SET_DERIVED_TOMS': {
return updateState({ derivedTOMs: action.payload })
}
case 'UPDATE_DERIVED_TOM': {
const updatedTOMs = state.derivedTOMs.map((tom) =>
tom.id === action.payload.id ? { ...tom, ...action.payload.data } : tom
)
return updateState({ derivedTOMs: updatedTOMs })
}
case 'SET_GAP_ANALYSIS': {
return updateState({ gapAnalysis: action.payload })
}
case 'ADD_EXPORT': {
return updateState({
exports: [...state.exports, action.payload],
})
}
case 'BULK_UPDATE_TOMS': {
let updatedTOMs = [...state.derivedTOMs]
for (const update of action.payload.updates) {
updatedTOMs = updatedTOMs.map((tom) =>
tom.id === update.id ? { ...tom, ...update.data } : tom
)
}
return updateState({ derivedTOMs: updatedTOMs })
}
case 'LOAD_STATE': {
return action.payload
}
default:
return state
}
}