import { NavLink, useLocation } from 'react-router-dom' import { useEffect, useMemo, useState } from 'react' import { getInitiativeExecutionGraphState } from '../api/executionPlan.js' import { useProgramScope } from '../context/ProgramScopeContext.jsx' import { useOptionalInitiativeOperations } from '../context/InitiativeOperationsContext.jsx' import { useMethodUiContext } from '../hooks/useMethodUiContext.js' import { buildProjectsByParent } from '../utils/projectTree.js' import { projectPath } from '../utils/routes.js' import { resolvePlanOutlineActiveKey, resolvePlanOutlineProjectId, resolvePlanOutlineActionId, } from '../plan/planOutlineNodes.js' import { resolvePlanOutlineNodesFromProfile } from '../registry/resolveOperatingProfile.js' import { PlanOutlineWorkTree } from './PlanOutlineWorkTree.jsx' function PlanOutlineProjectTree({ projects, parentId, activeProjectId, depth = 0 }) { const byParent = useMemo(() => buildProjectsByParent(projects), [projects]) const nodes = byParent.get(parentId || '') || [] if (nodes.length === 0) { return null } return ( ) } export function PlanOutlineNav() { const location = useLocation() const { initiativeId, initiativeTitle, hrefWithScope } = useProgramScope() const { operatingContext, hasActiveSprint } = useMethodUiContext() const ops = useOptionalInitiativeOperations() const activeKey = resolvePlanOutlineActiveKey(location.pathname) const activeProjectId = resolvePlanOutlineProjectId(location.pathname) const activeActionId = resolvePlanOutlineActionId(location.pathname) const [executionGraph, setExecutionGraph] = useState(null) const [structureOpen, setStructureOpen] = useState( activeKey === 'structure' || Boolean(activeProjectId), ) const [workOpen, setWorkOpen] = useState( activeKey === 'work' || Boolean(activeActionId), ) const contextReady = Boolean(initiativeId && ops?.initiativeId === initiativeId) const projects = contextReady ? ops.projects : [] const backlogItems = contextReady ? ops.backlogItems : [] const actions = contextReady ? ops.actions : [] const openActions = useMemo( () => actions.filter( (action) => action.status !== 'done' && action.status !== 'discarded', ), [actions], ) const counts = useMemo( () => ({ inbox: contextReady ? backlogItems.filter((item) => item.status !== 'converted').length : null, work: contextReady ? openActions.length : null, }), [contextReady, backlogItems, openActions], ) useEffect(() => { setStructureOpen(activeKey === 'structure' || Boolean(activeProjectId)) }, [activeKey, activeProjectId]) useEffect(() => { setWorkOpen(activeKey === 'work' || Boolean(activeActionId)) }, [activeKey, activeActionId]) const outlineNodes = useMemo( () => resolvePlanOutlineNodesFromProfile({ operatingContext, hasActiveSprint, }), [operatingContext, hasActiveSprint], ) useEffect(() => { if (!initiativeId) { setExecutionGraph(null) return undefined } let cancelled = false getInitiativeExecutionGraphState(initiativeId) .then((graphState) => { if (!cancelled) setExecutionGraph(graphState) }) .catch(() => { if (!cancelled) setExecutionGraph(null) }) return () => { cancelled = true } }, [initiativeId, location.pathname]) function nodeLabel(node) { if (node.key === 'inbox' && counts.inbox != null) { return `${node.label} (${counts.inbox})` } if (node.key === 'work' && counts.work != null) { return `${node.label} (${counts.work})` } if (node.key === 'structure' && projects.length > 0) { return `${node.label} (${projects.length})` } return node.label } return ( ) }