import { useEffect, useState } from 'react' import { Link } from 'react-router-dom' import { IconRoute, IconAlertTriangle } from '@tabler/icons-react' import RoadmapProgressBar from './roadmap/RoadmapProgressBar' import { fetchRoadmapByTicketId, fetchRoadmapNodes, computeProgress, isNodeOverdue, NODE_KIND, NODE_STATUS, ROADMAP_STATUS, } from '../lib/roadmaps' /** * Roadmap-Tracking im Projektticket: Fortschritt, Mini-Uebersicht und Link zum Board. * Rendert nichts, wenn zum Ticket keine Roadmap existiert. */ export default function TicketRoadmapPanel({ ticket }) { const [roadmap, setRoadmap] = useState(null) const [nodes, setNodes] = useState([]) const [loaded, setLoaded] = useState(false) useEffect(() => { let cancelled = false async function load() { try { const doc = await fetchRoadmapByTicketId(ticket.$id) if (cancelled) return setRoadmap(doc) if (doc) setNodes(await fetchRoadmapNodes(doc.$id)) } catch { /* Panel ist optional */ } finally { if (!cancelled) setLoaded(true) } } load() return () => { cancelled = true } }, [ticket.$id]) if (!loaded || !roadmap) return null const progress = computeProgress(nodes) const relevant = nodes.filter((n) => n.kind !== NODE_KIND.START) const counts = { open: relevant.filter((n) => (n.status || NODE_STATUS.OPEN) === NODE_STATUS.OPEN).length, inProgress: relevant.filter((n) => n.status === NODE_STATUS.IN_PROGRESS).length, done: relevant.filter((n) => n.status === NODE_STATUS.DONE).length, } const nextDue = relevant .filter((n) => n.deadline && n.status !== NODE_STATUS.DONE) .sort((a, b) => a.deadline.localeCompare(b.deadline)) .slice(0, 3) const closedButActive = ticket.status === 'Closed' && roadmap.status === ROADMAP_STATUS.ACTIVE return (