import { NavLink, useLocation } from 'react-router-dom'
import { useEffect, useMemo, useState } from 'react'
import { listInitiativeProjects } from '../api/projects.js'
import { listInitiativeActions } from '../api/initiatives.js'
import { listInitiativeBacklog } from '../api/backlog.js'
import { useProgramScope } from '../context/ProgramScopeContext.jsx'
import { buildProjectsByParent } from '../utils/projectTree.js'
import { projectPath } from '../utils/routes.js'
import {
PLAN_OUTLINE_NODES,
resolvePlanOutlineActiveKey,
resolvePlanOutlineProjectId,
} from '../plan/planOutlineNodes.js'
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 (
{nodes.map((project) => (
-
'analysis-split__nav-link analysis-split__nav-link--nested' +
(isActive || activeProjectId === project.id
? ' analysis-split__nav-link--active'
: '')
}
>
{project.title}
))}
)
}
export function PlanOutlineNav() {
const location = useLocation()
const { initiativeId, initiativeTitle, hrefWithScope } = useProgramScope()
const activeKey = resolvePlanOutlineActiveKey(location.pathname)
const activeProjectId = resolvePlanOutlineProjectId(location.pathname)
const [counts, setCounts] = useState({ inbox: null, work: null })
const [projects, setProjects] = useState([])
const [structureOpen, setStructureOpen] = useState(
activeKey === 'structure' || Boolean(activeProjectId),
)
useEffect(() => {
setStructureOpen(activeKey === 'structure' || Boolean(activeProjectId))
}, [activeKey, activeProjectId])
useEffect(() => {
if (!initiativeId) {
setProjects([])
return undefined
}
let cancelled = false
listInitiativeProjects(initiativeId)
.then((items) => {
if (!cancelled) setProjects(Array.isArray(items) ? items : [])
})
.catch(() => {
if (!cancelled) setProjects([])
})
return () => {
cancelled = true
}
}, [initiativeId])
useEffect(() => {
if (!initiativeId) {
setCounts({ inbox: null, work: null })
return undefined
}
let cancelled = false
Promise.all([
listInitiativeBacklog(initiativeId).catch(() => []),
listInitiativeActions(initiativeId).catch(() => []),
]).then(([backlog, actions]) => {
if (cancelled) return
setCounts({
inbox: backlog.filter((item) => item.status !== 'converted').length,
work: actions.filter(
(action) => action.status !== 'done' && action.status !== 'discarded',
).length,
})
})
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 (
)
}