+ {graphState?.graph_profile?.emphasize_fulfillment && fulfillmentPct != null && (
+
+ Erfüllungsgrad (Plan): {fulfillmentPct}% — Reifegrad zählt, kein
+ Gate-Zwang.
+
+ )}
+ {graphState?.graph_profile?.enforce_gate_blocking === false &&
+ !graphState?.graph_profile?.emphasize_fulfillment && (
+
+ Gates sind optional — der Graph blockiert nicht hart; Reihenfolge über Kanten oder
+ Liste.
+
+ )}
+
) : (
-
+
)}
)
diff --git a/frontend/src/components/RoadmapPlanSection.jsx b/frontend/src/components/RoadmapPlanSection.jsx
index ce8af86..b7d22f1 100644
--- a/frontend/src/components/RoadmapPlanSection.jsx
+++ b/frontend/src/components/RoadmapPlanSection.jsx
@@ -2,11 +2,10 @@ import { useEffect, useMemo, useState } from 'react'
import { Link } from 'react-router-dom'
import { gatePath } from '../utils/routes.js'
import { listInitiativeRoadmapCriteriaProgress, listInitiativeRoadmapGraphState } from '../api/roadmap.js'
-import {
- ROADMAP_ITEM_TYPE_LABELS,
-} from '../constants/status.js'
+import { ROADMAP_ITEM_TYPE_LABELS } from '../constants/status.js'
import { StatusBadge } from './StatusBadge.jsx'
import { GateGraphStateBadge } from './GateGraphStateBadge.jsx'
+import { GateFulfillmentBadge } from './GateFulfillmentBadge.jsx'
import { EmptyState } from './EmptyState.jsx'
import { Modal } from './Modal.jsx'
import { RoadmapItemForm } from './RoadmapItemForm.jsx'
@@ -33,42 +32,46 @@ export function RoadmapPlanSection({
onReorder,
onDelete,
busy,
+ graphState: graphStateProp,
}) {
const [showCreate, setShowCreate] = useState(false)
const [dragItemId, setDragItemId] = useState('')
const [dropTargetId, setDropTargetId] = useState('')
const [criteriaProgress, setCriteriaProgress] = useState({})
- const [graphState, setGraphState] = useState(null)
+ const [graphStateLocal, setGraphStateLocal] = useState(null)
+ const graphState = graphStateProp ?? graphStateLocal
const isDesktop = useMinWidth(1024)
const canReorder = canManage && typeof onReorder === 'function'
useEffect(() => {
if (!initiativeId) {
setCriteriaProgress({})
- setGraphState(null)
+ if (!graphStateProp) setGraphStateLocal(null)
return undefined
}
let cancelled = false
- Promise.all([
- listInitiativeRoadmapCriteriaProgress(initiativeId),
- listInitiativeRoadmapGraphState(initiativeId),
- ])
- .then(([progressData, graphData]) => {
- if (!cancelled) {
- setCriteriaProgress(progressData || {})
- setGraphState(graphData || null)
+ const loaders = [listInitiativeRoadmapCriteriaProgress(initiativeId)]
+ if (!graphStateProp) {
+ loaders.push(listInitiativeRoadmapGraphState(initiativeId))
+ }
+ Promise.all(loaders)
+ .then((results) => {
+ if (cancelled) return
+ setCriteriaProgress(results[0] || {})
+ if (!graphStateProp && results[1]) {
+ setGraphStateLocal(results[1] || null)
}
})
.catch(() => {
if (!cancelled) {
setCriteriaProgress({})
- setGraphState(null)
+ if (!graphStateProp) setGraphStateLocal(null)
}
})
return () => {
cancelled = true
}
- }, [initiativeId, items])
+ }, [initiativeId, items, graphStateProp])
const sortedItems = useMemo(() => sortByOrder(items), [items])
@@ -204,6 +207,7 @@ export function RoadmapPlanSection({
itemId={item.id}
siblingItems={sortedItems}
/>
+
{canReorder && !isDesktop && (