diff --git a/src/pages/MonitoringPage/MonitoringPage.tsx b/src/pages/MonitoringPage/MonitoringPage.tsx index 9252448..cd7f95e 100644 --- a/src/pages/MonitoringPage/MonitoringPage.tsx +++ b/src/pages/MonitoringPage/MonitoringPage.tsx @@ -306,14 +306,17 @@ function PhaseDashboard({ setActiveIds: React.Dispatch>>, goalIndex: number }) { - const getGoals = useProgramStore((s) => s.getGoalsInPhase); + const getGoalsWithDepth = useProgramStore((s) => s.getGoalsWithDepth); const getTriggers = useProgramStore((s) => s.getTriggersInPhase); const getNorms = useProgramStore((s) => s.getNormsInPhase); // Prepare data view models - const goals = (getGoals(phaseId) as GoalNode[]).map(g => ({ + const goals = getGoalsWithDepth(phaseId).map((g) => ({ ...g, - achieved: activeIds[g.id] ?? false, + id: g.id as string, + name: g.name as string, + achieved: activeIds[g.id as string] ?? false, + level: g.level, // Pass this new property to the UI })); const triggers = (getTriggers(phaseId) as TriggerNode[]).map(t => ({ diff --git a/src/pages/MonitoringPage/MonitoringPageComponents.tsx b/src/pages/MonitoringPage/MonitoringPageComponents.tsx index 6efeab5..d1d2854 100644 --- a/src/pages/MonitoringPage/MonitoringPageComponents.tsx +++ b/src/pages/MonitoringPage/MonitoringPageComponents.tsx @@ -91,13 +91,14 @@ export const DirectSpeechInput: React.FC = () => { }; // --- interface for goals/triggers/norms/conditional norms --- -type StatusItem = { +export type StatusItem = { id?: string | number; achieved?: boolean; description?: string; label?: string; norm?: string; name?: string; + level?: number; }; interface StatusListProps { @@ -129,7 +130,7 @@ export const StatusList: React.FC = ({ const isCurrentGoal = type === 'goal' && idx === currentGoalIndex; const canOverride = (showIndicator && !isActive) || (type === 'cond_norm' && isActive); - + const indentation = (item.level || 0) * 20; const handleOverrideClick = () => { if (!canOverride) return; @@ -147,7 +148,10 @@ export const StatusList: React.FC = ({ }; return ( -
  • +
  • {showIndicator && ( [] }; +export type GoalWithDepth = Record & { level: number }; + /** * the type definition of the programStore */ @@ -18,6 +20,7 @@ export type ProgramState = { getPhaseNames: () => string[]; getNormsInPhase: (currentPhaseId: string) => Record[]; getGoalsInPhase: (currentPhaseId: string) => Record[]; + getGoalsWithDepth: (currentPhaseId: string) => GoalWithDepth[]; getTriggersInPhase: (currentPhaseId: string) => Record[]; // if more specific utility functions are needed they can be added here: } @@ -70,6 +73,51 @@ const useProgramStore = create((set, get) => ({ } throw new Error(`phase with id:"${currentPhaseId}" not found`) }, + + getGoalsWithDepth: (currentPhaseId: string) => { + const program = get().currentProgram; + const phase = program.phases.find(val => val["id"] === currentPhaseId); + + if (!phase) { + throw new Error(`phase with id:"${currentPhaseId}" not found`); + } + + const rootGoals = phase["goals"] as Record[]; + const flatList: GoalWithDepth[] = []; + + // Helper: Define this ONCE, outside the loop + const isGoal = (item: Record) => { + return item["plan"] !== undefined && item["plan"] !== null; + }; + + // Recursive helper function + const traverse = (goals: Record[], depth: number) => { + goals.forEach((goal) => { + // 1. Add the current goal to the list + flatList.push({ ...goal, level: depth }); + + // 2. Check for children + const plan = goal["plan"] as Record | undefined; + + if (plan && Array.isArray(plan["steps"])) { + const steps = plan["steps"] as Record[]; + + // 3. FILTER: Only recurse on steps that are actually goals + // If we just passed 'steps', we might accidentally add Actions/Speeches to the goal list + const childGoals = steps.filter(isGoal); + + if (childGoals.length > 0) { + traverse(childGoals, depth + 1); + } + } + }); + }; + + // Start traversal + traverse(rootGoals, 0); + + return flatList; + }, /** * gets the triggers for the provided phase */