Kairo-Jinkendo/frontend/src/components/CriticalPathPanel.jsx
Lars 00567bb097
All checks were successful
Deploy Development / deploy (push) Successful in 47s
Test Suite / pytest-backend (push) Successful in 2m57s
Test Suite / lint-backend (push) Successful in 2s
Test Suite / compose-smoke (push) Has been skipped
Test Suite / k6 /api/health Baseline (push) Successful in 18s
Test Suite / playwright-smoke (push) Successful in 20s
AP2.2b: Kritischer Pfad in Kontrolle für A2 Linear.
CriticalPathPanel aus Execution Graph, Next-Action-Begruendung und Integrationstest fuer sequential_dependency.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 19:26:08 +02:00

135 lines
4.5 KiB
JavaScript

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,
findNextReadyOnCriticalPath,
summarizeCriticalPath,
} from '../utils/executionGraph.js'
import { actionPath, scopedPath } from '../utils/routes.js'
export function CriticalPathPanel({
initiativeId,
actions = [],
embedded = true,
}) {
const [graphState, setGraphState] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
if (!initiativeId) {
setGraphState(null)
setLoading(false)
return undefined
}
let cancelled = false
setLoading(true)
setError(null)
getInitiativeExecutionGraphState(initiativeId)
.then((data) => {
if (!cancelled) setGraphState(data)
})
.catch((err) => {
if (!cancelled) setError(err.message)
})
.finally(() => {
if (!cancelled) setLoading(false)
})
return () => {
cancelled = true
}
}, [initiativeId])
const steps = buildCriticalPathSteps(graphState, actions)
const summary = summarizeCriticalPath(graphState, actions)
const nextReady = findNextReadyOnCriticalPath(graphState, actions)
const body = (
<>
{loading && <LoadingState message="Lade kritischer Pfad …" />}
{!loading && error && (
<ErrorState message={error} onRetry={() => window.location.reload()} />
)}
{!loading && !error && steps.length === 0 && (
<EmptyState message="Noch kein kritischer Pfad — Arbeitspakete mit Abhängigkeiten unter Plan → Zielzustände / Durchführungsplan anlegen." />
)}
{!loading && !error && steps.length > 0 && (
<>
<p className="critical-path-summary muted">{summary.message}</p>
{nextReady && (
<div className="critical-path-next card-list-item">
<span className="badge status-badge status-active">Nächster Schritt</span>
<strong>{nextReady.action.title}</strong>
<p className="muted">
Kritischer Pfad ausführungsbereit, keine blockierenden Vorgänger.
</p>
<Link
to={actionPath(nextReady.actionId)}
className="btn btn-primary btn-sm"
>
Arbeitspaket öffnen
</Link>
</div>
)}
<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">
Längster Abhängigkeitspfad aus dem Execution Graph Steuerungsantwort für
lineare Vorhaben (A2).
</p>
</div>
</div>
{body}
</section>
)
}
return body
}