2026-07-12 19:26:08 +02:00
import { useEffect , useState } from 'react'
import { Link } from 'react-router-dom'
import { getInitiativeExecutionGraphState } from '../api/executionPlan.js'
import { LoadingState } from './LoadingState.jsx'
import { ErrorState } from './ErrorState.jsx'
import { EmptyState } from './EmptyState.jsx'
import { StatusBadge } from './StatusBadge.jsx'
import { ExecutionFlowBadge } from './ExecutionFlowBadge.jsx'
import {
buildCriticalPathSteps ,
summarizeCriticalPath ,
} from '../utils/executionGraph.js'
import { actionPath , scopedPath } from '../utils/routes.js'
export function CriticalPathPanel ( {
initiativeId ,
actions = [ ] ,
2026-07-27 13:34:13 +02:00
graphState : kernelGraphState = null ,
graphLoading = false ,
scopeRoadmapItemId = null ,
2026-07-12 19:26:08 +02:00
embedded = true ,
} ) {
2026-07-27 13:34:13 +02:00
const useKernelGraph = kernelGraphState != null || graphLoading
const [ fallbackGraphState , setFallbackGraphState ] = useState ( null )
const [ fallbackLoading , setFallbackLoading ] = useState ( ! useKernelGraph )
2026-07-12 19:26:08 +02:00
const [ error , setError ] = useState ( null )
useEffect ( ( ) => {
2026-07-27 13:34:13 +02:00
if ( ! initiativeId || useKernelGraph ) {
setFallbackGraphState ( null )
setFallbackLoading ( false )
2026-07-12 19:26:08 +02:00
return undefined
}
let cancelled = false
2026-07-27 13:34:13 +02:00
setFallbackLoading ( true )
2026-07-12 19:26:08 +02:00
setError ( null )
2026-07-27 13:34:13 +02:00
getInitiativeExecutionGraphState ( initiativeId , {
scopeRoadmapItemId : scopeRoadmapItemId || undefined ,
} )
2026-07-12 19:26:08 +02:00
. then ( ( data ) => {
2026-07-27 13:34:13 +02:00
if ( ! cancelled ) setFallbackGraphState ( data )
2026-07-12 19:26:08 +02:00
} )
. catch ( ( err ) => {
if ( ! cancelled ) setError ( err . message )
} )
. finally ( ( ) => {
2026-07-27 13:34:13 +02:00
if ( ! cancelled ) setFallbackLoading ( false )
2026-07-12 19:26:08 +02:00
} )
return ( ) => {
cancelled = true
}
2026-07-27 13:34:13 +02:00
} , [ initiativeId , scopeRoadmapItemId , useKernelGraph ] )
const graphState = useKernelGraph ? kernelGraphState : fallbackGraphState
const loading = useKernelGraph ? graphLoading : fallbackLoading
2026-07-12 19:26:08 +02:00
const steps = buildCriticalPathSteps ( graphState , actions )
const summary = summarizeCriticalPath ( graphState , actions )
const body = (
< >
{ loading && < LoadingState message = "Lade kritischer Pfad …" / > }
{ ! loading && error && (
< ErrorState message = { error } onRetry = { ( ) => window . location . reload ( ) } / >
) }
{ ! loading && ! error && steps . length === 0 && (
2026-07-27 13:34:13 +02:00
< EmptyState message = "Noch kein kritischer Pfad im aktiven Gate-Horizont — Arbeitspakete mit Abhängigkeiten unter Plan → Zielzustände / Durchführungsplan anlegen." / >
2026-07-12 19:26:08 +02:00
) }
{ ! loading && ! error && steps . length > 0 && (
< >
< p className = "critical-path-summary muted" > { summary . message } < / p >
< ol className = "critical-path-steps item-list" >
{ steps . map ( ( step ) => (
< li
key = { step . actionId }
className = {
'list-item card-list-item critical-path-step' +
( step . isNextReady ? ' critical-path-step--next' : '' ) +
( step . status === 'done' ? ' critical-path-step--done' : '' )
}
>
< span className = "critical-path-step__index muted" > { step . index } < / span >
< div className = "list-item-main" >
< Link to = { actionPath ( step . actionId ) } className = "link-inline" >
< strong > { step . title } < / strong >
< / Link >
< p className = "list-item-desc muted" > { step . reason } < / p >
< / div >
< div className = "list-item-meta" >
< StatusBadge status = { step . status } / >
< ExecutionFlowBadge meta = { step . meta } actionStatus = { step . status } / >
< / div >
< / li >
) ) }
< / ol >
< p className = "muted critical-path-hint" >
Graph unter { ' ' }
< Link to = { scopedPath ( '/plan/gates' , { initiativeId } ) } className = "link-inline" >
Plan → Zielzustände
< / Link >
{ ' ' } pflegen .
< / p >
< / >
) }
< / >
)
if ( embedded ) {
return (
< section className = "card critical-path-panel" >
< div className = "section-header" >
< div >
< h2 > Kritischer Pfad < / h2 >
< p className = "section-lead muted" >
2026-07-27 13:34:13 +02:00
Abhängigkeitspfad im aktiven Gate - Horizont — Steuerungsantwort für lineare
Vorhaben ( A2 ) .
2026-07-12 19:26:08 +02:00
< / p >
< / div >
< / div >
{ body }
< / section >
)
}
return body
}