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>
239 lines
7.6 KiB
TypeScript
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
|
|
}
|
|
}
|