feat(sdk): add global seq numbering and visibleWhen for SDK flow navigation
Fix interleaved step ordering by introducing global sequence numbers (100-4700) instead of package-relative order. Add conditional visibility (visibleWhen) for optional steps like Import and DSFA. Fix TOM/workflow prerequisite bugs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -99,6 +99,9 @@ const initialState: SDKState = {
|
||||
dsrConfig: null,
|
||||
escalationWorkflows: [],
|
||||
|
||||
// IACE (Industrial AI Compliance Engine)
|
||||
iaceProjects: [],
|
||||
|
||||
// Security
|
||||
sbom: null,
|
||||
securityIssues: [],
|
||||
@@ -705,26 +708,26 @@ export function SDKProvider({
|
||||
)
|
||||
|
||||
const goToNextStep = useCallback(() => {
|
||||
const nextStep = getNextStep(state.currentStep)
|
||||
const nextStep = getNextStep(state.currentStep, state)
|
||||
if (nextStep) {
|
||||
goToStep(nextStep.id)
|
||||
}
|
||||
}, [state.currentStep, goToStep])
|
||||
}, [state, goToStep])
|
||||
|
||||
const goToPreviousStep = useCallback(() => {
|
||||
const prevStep = getPreviousStep(state.currentStep)
|
||||
const prevStep = getPreviousStep(state.currentStep, state)
|
||||
if (prevStep) {
|
||||
goToStep(prevStep.id)
|
||||
}
|
||||
}, [state.currentStep, goToStep])
|
||||
}, [state, goToStep])
|
||||
|
||||
const canGoNext = useMemo(() => {
|
||||
return getNextStep(state.currentStep) !== undefined
|
||||
}, [state.currentStep])
|
||||
return getNextStep(state.currentStep, state) !== undefined
|
||||
}, [state])
|
||||
|
||||
const canGoPrevious = useMemo(() => {
|
||||
return getPreviousStep(state.currentStep) !== undefined
|
||||
}, [state.currentStep])
|
||||
return getPreviousStep(state.currentStep, state) !== undefined
|
||||
}, [state])
|
||||
|
||||
// Progress
|
||||
const completionPercentage = useMemo(() => getCompletionPercentage(state), [state])
|
||||
|
||||
@@ -62,6 +62,86 @@ export type LegalForm =
|
||||
| 'stiftung' // Foundation
|
||||
| 'other' // Other
|
||||
|
||||
// =============================================================================
|
||||
// MACHINE BUILDER PROFILE (IACE - Industrial AI Compliance Engine)
|
||||
// =============================================================================
|
||||
|
||||
export type MachineProductType = 'test_stand' | 'robot_cell' | 'special_machine' | 'production_line' | 'other'
|
||||
|
||||
export type AIIntegrationType = 'vision' | 'predictive_maintenance' | 'quality_control' | 'robot_control' | 'process_optimization' | 'other'
|
||||
|
||||
export type HumanOversightLevel = 'full' | 'partial' | 'minimal' | 'none'
|
||||
|
||||
export type CriticalSector = 'energy' | 'water' | 'transport' | 'health' | 'pharma' | 'automotive' | 'defense'
|
||||
|
||||
export interface MachineBuilderProfile {
|
||||
// Produkt
|
||||
productTypes: MachineProductType[]
|
||||
productDescription: string
|
||||
productPride: string
|
||||
containsSoftware: boolean
|
||||
containsFirmware: boolean
|
||||
containsAI: boolean
|
||||
aiIntegrationType: AIIntegrationType[]
|
||||
|
||||
// Sicherheit
|
||||
hasSafetyFunction: boolean
|
||||
safetyFunctionDescription: string
|
||||
autonomousBehavior: boolean
|
||||
humanOversightLevel: HumanOversightLevel
|
||||
|
||||
// Konnektivitaet
|
||||
isNetworked: boolean
|
||||
hasRemoteAccess: boolean
|
||||
hasOTAUpdates: boolean
|
||||
updateMechanism: string
|
||||
|
||||
// Markt & Kunden
|
||||
exportMarkets: string[]
|
||||
criticalSectorClients: boolean
|
||||
criticalSectors: CriticalSector[]
|
||||
oemClients: boolean
|
||||
|
||||
// CE
|
||||
ceMarkingRequired: boolean
|
||||
existingCEProcess: boolean
|
||||
hasRiskAssessment: boolean
|
||||
}
|
||||
|
||||
export const MACHINE_PRODUCT_TYPE_LABELS: Record<MachineProductType, string> = {
|
||||
test_stand: 'Pruefstand',
|
||||
robot_cell: 'Roboterzelle',
|
||||
special_machine: 'Sondermaschine',
|
||||
production_line: 'Produktionslinie',
|
||||
other: 'Sonstige',
|
||||
}
|
||||
|
||||
export const AI_INTEGRATION_TYPE_LABELS: Record<AIIntegrationType, string> = {
|
||||
vision: 'Bildverarbeitung / Machine Vision',
|
||||
predictive_maintenance: 'Predictive Maintenance',
|
||||
quality_control: 'Qualitaetskontrolle',
|
||||
robot_control: 'Robotersteuerung',
|
||||
process_optimization: 'Prozessoptimierung',
|
||||
other: 'Sonstige',
|
||||
}
|
||||
|
||||
export const HUMAN_OVERSIGHT_LABELS: Record<HumanOversightLevel, string> = {
|
||||
full: 'Vollstaendig (Mensch entscheidet immer)',
|
||||
partial: 'Teilweise (Mensch ueberwacht)',
|
||||
minimal: 'Minimal (Mensch greift nur bei Stoerung ein)',
|
||||
none: 'Keine (vollautonomer Betrieb)',
|
||||
}
|
||||
|
||||
export const CRITICAL_SECTOR_LABELS: Record<CriticalSector, string> = {
|
||||
energy: 'Energie',
|
||||
water: 'Wasser',
|
||||
transport: 'Transport / Verkehr',
|
||||
health: 'Gesundheit',
|
||||
pharma: 'Pharma',
|
||||
automotive: 'Automotive',
|
||||
defense: 'Verteidigung',
|
||||
}
|
||||
|
||||
export interface CompanyProfile {
|
||||
// Basic Info
|
||||
companyName: string
|
||||
@@ -102,6 +182,9 @@ export interface CompanyProfile {
|
||||
legalContactName: string | null
|
||||
legalContactEmail: string | null
|
||||
|
||||
// Machine Builder (IACE)
|
||||
machineBuilder?: MachineBuilderProfile
|
||||
|
||||
// Completion Status
|
||||
isComplete: boolean
|
||||
completedAt: Date | null
|
||||
@@ -284,6 +367,7 @@ export type CommandType = 'ACTION' | 'NAVIGATION' | 'SEARCH' | 'GENERATE' | 'HEL
|
||||
|
||||
export interface SDKStep {
|
||||
id: string
|
||||
seq: number // Globale Sequenznummer (100, 200, 300, ...)
|
||||
phase: SDKPhase
|
||||
package: SDKPackageId
|
||||
order: number
|
||||
@@ -294,6 +378,7 @@ export interface SDKStep {
|
||||
checkpointId: string
|
||||
prerequisiteSteps: string[]
|
||||
isOptional: boolean
|
||||
visibleWhen?: (state: SDKState) => boolean // Konditionale Sichtbarkeit
|
||||
}
|
||||
|
||||
export const SDK_STEPS: SDKStep[] = [
|
||||
@@ -302,6 +387,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
// =============================================================================
|
||||
{
|
||||
id: 'company-profile',
|
||||
seq: 100,
|
||||
phase: 1,
|
||||
package: 'vorbereitung',
|
||||
order: 1,
|
||||
@@ -315,6 +401,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'compliance-scope',
|
||||
seq: 200,
|
||||
phase: 1,
|
||||
package: 'vorbereitung',
|
||||
order: 2,
|
||||
@@ -328,6 +415,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'use-case-assessment',
|
||||
seq: 300,
|
||||
phase: 1,
|
||||
package: 'vorbereitung',
|
||||
order: 3,
|
||||
@@ -341,6 +429,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'import',
|
||||
seq: 400,
|
||||
phase: 1,
|
||||
package: 'vorbereitung',
|
||||
order: 4,
|
||||
@@ -350,10 +439,12 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
url: '/sdk/import',
|
||||
checkpointId: 'CP-IMP',
|
||||
prerequisiteSteps: ['use-case-assessment'],
|
||||
isOptional: true, // Nur für Bestandskunden
|
||||
isOptional: true,
|
||||
visibleWhen: (state) => state.customerType === 'existing',
|
||||
},
|
||||
{
|
||||
id: 'screening',
|
||||
seq: 500,
|
||||
phase: 1,
|
||||
package: 'vorbereitung',
|
||||
order: 5,
|
||||
@@ -367,6 +458,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'modules',
|
||||
seq: 600,
|
||||
phase: 1,
|
||||
package: 'vorbereitung',
|
||||
order: 6,
|
||||
@@ -380,6 +472,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'source-policy',
|
||||
seq: 700,
|
||||
phase: 1,
|
||||
package: 'vorbereitung',
|
||||
order: 7,
|
||||
@@ -397,6 +490,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
// =============================================================================
|
||||
{
|
||||
id: 'requirements',
|
||||
seq: 1000,
|
||||
phase: 1,
|
||||
package: 'analyse',
|
||||
order: 1,
|
||||
@@ -410,6 +504,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'controls',
|
||||
seq: 1100,
|
||||
phase: 1,
|
||||
package: 'analyse',
|
||||
order: 2,
|
||||
@@ -423,6 +518,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'evidence',
|
||||
seq: 1200,
|
||||
phase: 1,
|
||||
package: 'analyse',
|
||||
order: 3,
|
||||
@@ -436,6 +532,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'risks',
|
||||
seq: 1300,
|
||||
phase: 1,
|
||||
package: 'analyse',
|
||||
order: 4,
|
||||
@@ -449,6 +546,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'ai-act',
|
||||
seq: 1400,
|
||||
phase: 1,
|
||||
package: 'analyse',
|
||||
order: 5,
|
||||
@@ -462,6 +560,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'audit-checklist',
|
||||
seq: 1500,
|
||||
phase: 1,
|
||||
package: 'analyse',
|
||||
order: 6,
|
||||
@@ -475,6 +574,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'audit-report',
|
||||
seq: 1600,
|
||||
phase: 1,
|
||||
package: 'analyse',
|
||||
order: 7,
|
||||
@@ -492,6 +592,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
// =============================================================================
|
||||
{
|
||||
id: 'obligations',
|
||||
seq: 2000,
|
||||
phase: 2,
|
||||
package: 'dokumentation',
|
||||
order: 1,
|
||||
@@ -505,6 +606,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'dsfa',
|
||||
seq: 2100,
|
||||
phase: 2,
|
||||
package: 'dokumentation',
|
||||
order: 2,
|
||||
@@ -514,10 +616,17 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
url: '/sdk/dsfa',
|
||||
checkpointId: 'CP-DSFA',
|
||||
prerequisiteSteps: ['obligations'],
|
||||
isOptional: true, // Only if dsfa_recommended
|
||||
isOptional: true,
|
||||
visibleWhen: (state) => {
|
||||
const level = state.complianceScope?.decision?.determinedLevel
|
||||
if (level && ['L2', 'L3', 'L4'].includes(level)) return true
|
||||
const triggers = state.complianceScope?.decision?.triggeredHardTriggers || []
|
||||
return triggers.some(t => t.rule.dsfaRequired)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'tom',
|
||||
seq: 2200,
|
||||
phase: 2,
|
||||
package: 'dokumentation',
|
||||
order: 3,
|
||||
@@ -526,11 +635,12 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
description: 'Technische & Org. Maßnahmen',
|
||||
url: '/sdk/tom',
|
||||
checkpointId: 'CP-TOM',
|
||||
prerequisiteSteps: ['dsfa'],
|
||||
prerequisiteSteps: ['obligations'],
|
||||
isOptional: false,
|
||||
},
|
||||
{
|
||||
id: 'loeschfristen',
|
||||
seq: 2300,
|
||||
phase: 2,
|
||||
package: 'dokumentation',
|
||||
order: 4,
|
||||
@@ -544,6 +654,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'vvt',
|
||||
seq: 2400,
|
||||
phase: 2,
|
||||
package: 'dokumentation',
|
||||
order: 5,
|
||||
@@ -561,6 +672,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
// =============================================================================
|
||||
{
|
||||
id: 'einwilligungen',
|
||||
seq: 3000,
|
||||
phase: 2,
|
||||
package: 'rechtliche-texte',
|
||||
order: 1,
|
||||
@@ -574,6 +686,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'consent',
|
||||
seq: 3100,
|
||||
phase: 2,
|
||||
package: 'rechtliche-texte',
|
||||
order: 2,
|
||||
@@ -587,6 +700,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'cookie-banner',
|
||||
seq: 3200,
|
||||
phase: 2,
|
||||
package: 'rechtliche-texte',
|
||||
order: 3,
|
||||
@@ -600,6 +714,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'document-generator',
|
||||
seq: 3300,
|
||||
phase: 2,
|
||||
package: 'rechtliche-texte',
|
||||
order: 4,
|
||||
@@ -610,9 +725,11 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
checkpointId: 'CP-DOCGEN',
|
||||
prerequisiteSteps: ['cookie-banner'],
|
||||
isOptional: true,
|
||||
visibleWhen: () => true,
|
||||
},
|
||||
{
|
||||
id: 'workflow',
|
||||
seq: 3400,
|
||||
phase: 2,
|
||||
package: 'rechtliche-texte',
|
||||
order: 5,
|
||||
@@ -621,7 +738,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
description: 'Versionierung & Freigabe-Workflow',
|
||||
url: '/sdk/workflow',
|
||||
checkpointId: 'CP-WRKF',
|
||||
prerequisiteSteps: ['document-generator'],
|
||||
prerequisiteSteps: ['cookie-banner'],
|
||||
isOptional: false,
|
||||
},
|
||||
|
||||
@@ -630,6 +747,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
// =============================================================================
|
||||
{
|
||||
id: 'dsr',
|
||||
seq: 4000,
|
||||
phase: 2,
|
||||
package: 'betrieb',
|
||||
order: 1,
|
||||
@@ -643,6 +761,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'escalations',
|
||||
seq: 4100,
|
||||
phase: 2,
|
||||
package: 'betrieb',
|
||||
order: 2,
|
||||
@@ -656,6 +775,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'vendor-compliance',
|
||||
seq: 4200,
|
||||
phase: 2,
|
||||
package: 'betrieb',
|
||||
order: 3,
|
||||
@@ -669,6 +789,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'consent-management',
|
||||
seq: 4300,
|
||||
phase: 2,
|
||||
package: 'betrieb',
|
||||
order: 4,
|
||||
@@ -682,6 +803,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'notfallplan',
|
||||
seq: 4400,
|
||||
phase: 2,
|
||||
package: 'betrieb',
|
||||
order: 5,
|
||||
@@ -693,6 +815,48 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
prerequisiteSteps: ['consent-management'],
|
||||
isOptional: false,
|
||||
},
|
||||
{
|
||||
id: 'incidents',
|
||||
seq: 4500,
|
||||
phase: 2,
|
||||
package: 'betrieb',
|
||||
order: 6,
|
||||
name: 'Incident Management',
|
||||
nameShort: 'Incidents',
|
||||
description: 'Datenpannen erfassen, bewerten und melden (Art. 33/34 DSGVO)',
|
||||
url: '/sdk/incidents',
|
||||
checkpointId: 'CP-INC',
|
||||
prerequisiteSteps: ['notfallplan'],
|
||||
isOptional: false,
|
||||
},
|
||||
{
|
||||
id: 'whistleblower',
|
||||
seq: 4600,
|
||||
phase: 2,
|
||||
package: 'betrieb',
|
||||
order: 7,
|
||||
name: 'Hinweisgebersystem',
|
||||
nameShort: 'Whistleblower',
|
||||
description: 'Anonymes Meldesystem gemaess HinSchG',
|
||||
url: '/sdk/whistleblower',
|
||||
checkpointId: 'CP-WB',
|
||||
prerequisiteSteps: ['incidents'],
|
||||
isOptional: false,
|
||||
},
|
||||
{
|
||||
id: 'academy',
|
||||
seq: 4700,
|
||||
phase: 2,
|
||||
package: 'betrieb',
|
||||
order: 8,
|
||||
name: 'Compliance Academy',
|
||||
nameShort: 'Academy',
|
||||
description: 'Mitarbeiter-Schulungen & Zertifikate',
|
||||
url: '/sdk/academy',
|
||||
checkpointId: 'CP-ACAD',
|
||||
prerequisiteSteps: ['whistleblower'],
|
||||
isOptional: false,
|
||||
},
|
||||
]
|
||||
|
||||
// =============================================================================
|
||||
@@ -1323,6 +1487,9 @@ export interface SDKState {
|
||||
dsrConfig: DSRConfig | null
|
||||
escalationWorkflows: EscalationWorkflow[]
|
||||
|
||||
// IACE (Industrial AI Compliance Engine)
|
||||
iaceProjects: IACEProjectSummary[]
|
||||
|
||||
// Security
|
||||
sbom: SBOM | null
|
||||
securityIssues: SecurityIssue[]
|
||||
@@ -1334,6 +1501,28 @@ export interface SDKState {
|
||||
preferences: UserPreferences
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// IACE PROJECT TYPES
|
||||
// =============================================================================
|
||||
|
||||
export type IACEProjectStatus = 'draft' | 'onboarding' | 'classification' | 'hazard_analysis' | 'mitigation' | 'verification' | 'tech_file' | 'completed' | 'archived'
|
||||
|
||||
export interface IACEProjectSummary {
|
||||
id: string
|
||||
machineName: string
|
||||
machineType: MachineProductType
|
||||
status: IACEProjectStatus
|
||||
completenessScore: number
|
||||
riskSummary: {
|
||||
critical: number
|
||||
high: number
|
||||
medium: number
|
||||
low: number
|
||||
}
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// SDK ACTIONS
|
||||
// =============================================================================
|
||||
@@ -1406,48 +1595,59 @@ export function getStepByUrl(url: string): SDKStep | undefined {
|
||||
}
|
||||
|
||||
export function getStepsForPhase(phase: SDKPhase): SDKStep[] {
|
||||
return SDK_STEPS.filter(s => s.phase === phase).sort((a, b) => a.order - b.order)
|
||||
return SDK_STEPS.filter(s => s.phase === phase).sort((a, b) => a.seq - b.seq)
|
||||
}
|
||||
|
||||
export function getNextStep(currentStepId: string): SDKStep | undefined {
|
||||
const currentStep = getStepById(currentStepId)
|
||||
if (!currentStep) return undefined
|
||||
// Alle Steps global nach seq sortiert
|
||||
function getAllStepsSorted(): SDKStep[] {
|
||||
return [...SDK_STEPS].sort((a, b) => a.seq - b.seq)
|
||||
}
|
||||
|
||||
const stepsInPhase = getStepsForPhase(currentStep.phase)
|
||||
const currentIndex = stepsInPhase.findIndex(s => s.id === currentStepId)
|
||||
|
||||
if (currentIndex < stepsInPhase.length - 1) {
|
||||
return stepsInPhase[currentIndex + 1]
|
||||
}
|
||||
|
||||
// Move to next phase
|
||||
if (currentStep.phase === 1) {
|
||||
return getStepsForPhase(2)[0]
|
||||
}
|
||||
// Sichtbare Steps (state-abhaengig)
|
||||
export function getVisibleSteps(state: SDKState): SDKStep[] {
|
||||
return getAllStepsSorted().filter(step => {
|
||||
if (step.visibleWhen) return step.visibleWhen(state)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// Naechster sichtbarer Step
|
||||
export function getNextVisibleStep(currentStepId: string, state: SDKState): SDKStep | undefined {
|
||||
const visible = getVisibleSteps(state)
|
||||
const idx = visible.findIndex(s => s.id === currentStepId)
|
||||
if (idx >= 0 && idx < visible.length - 1) return visible[idx + 1]
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function getPreviousStep(currentStepId: string): SDKStep | undefined {
|
||||
const currentStep = getStepById(currentStepId)
|
||||
if (!currentStep) return undefined
|
||||
|
||||
const stepsInPhase = getStepsForPhase(currentStep.phase)
|
||||
const currentIndex = stepsInPhase.findIndex(s => s.id === currentStepId)
|
||||
|
||||
if (currentIndex > 0) {
|
||||
return stepsInPhase[currentIndex - 1]
|
||||
}
|
||||
|
||||
// Move to previous phase
|
||||
if (currentStep.phase === 2) {
|
||||
const phase1Steps = getStepsForPhase(1)
|
||||
return phase1Steps[phase1Steps.length - 1]
|
||||
}
|
||||
|
||||
// Vorheriger sichtbarer Step
|
||||
export function getPreviousVisibleStep(currentStepId: string, state: SDKState): SDKStep | undefined {
|
||||
const visible = getVisibleSteps(state)
|
||||
const idx = visible.findIndex(s => s.id === currentStepId)
|
||||
if (idx > 0) return visible[idx - 1]
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function getNextStep(currentStepId: string, state?: SDKState): SDKStep | undefined {
|
||||
if (!state) {
|
||||
// Fallback: seq-sortiert ohne Sichtbarkeitspruefung
|
||||
const sorted = getAllStepsSorted()
|
||||
const idx = sorted.findIndex(s => s.id === currentStepId)
|
||||
if (idx >= 0 && idx < sorted.length - 1) return sorted[idx + 1]
|
||||
return undefined
|
||||
}
|
||||
return getNextVisibleStep(currentStepId, state)
|
||||
}
|
||||
|
||||
export function getPreviousStep(currentStepId: string, state?: SDKState): SDKStep | undefined {
|
||||
if (!state) {
|
||||
const sorted = getAllStepsSorted()
|
||||
const idx = sorted.findIndex(s => s.id === currentStepId)
|
||||
if (idx > 0) return sorted[idx - 1]
|
||||
return undefined
|
||||
}
|
||||
return getPreviousVisibleStep(currentStepId, state)
|
||||
}
|
||||
|
||||
export function calculateRiskScore(likelihood: RiskLikelihood, impact: RiskImpact): number {
|
||||
return likelihood * impact
|
||||
}
|
||||
@@ -1490,7 +1690,7 @@ export function getPackageById(packageId: SDKPackageId): SDKPackage | undefined
|
||||
}
|
||||
|
||||
export function getStepsForPackage(packageId: SDKPackageId): SDKStep[] {
|
||||
return SDK_STEPS.filter(s => s.package === packageId).sort((a, b) => a.order - b.order)
|
||||
return SDK_STEPS.filter(s => s.package === packageId).sort((a, b) => a.seq - b.seq)
|
||||
}
|
||||
|
||||
export function getPackageCompletionPercentage(state: SDKState, packageId: SDKPackageId): number {
|
||||
@@ -1545,9 +1745,9 @@ export function isPackageUnlocked(state: SDKState, packageId: SDKPackageId): boo
|
||||
return getPackageCompletionPercentage(state, prevPackage.id) === 100
|
||||
}
|
||||
|
||||
/** @deprecated Use getVisibleSteps(state) instead */
|
||||
export function getVisibleStepsForCustomerType(customerType: CustomerType): SDKStep[] {
|
||||
return SDK_STEPS.filter(step => {
|
||||
// Import step is only for existing customers
|
||||
return getAllStepsSorted().filter(step => {
|
||||
if (step.id === 'import') {
|
||||
return customerType === 'existing'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user