diff --git a/apps/fabro-web/app/components/stage-renderers/conditional-decision.tsx b/apps/fabro-web/app/components/stage-renderers/conditional-decision.tsx index 3ff6c8581..748f46853 100644 --- a/apps/fabro-web/app/components/stage-renderers/conditional-decision.tsx +++ b/apps/fabro-web/app/components/stage-renderers/conditional-decision.tsx @@ -1,39 +1,127 @@ +import { useMemo } from "react"; +import { Link } from "react-router"; import { + ArrowLongRightIcon, ArrowsRightLeftIcon, - InformationCircleIcon, } from "@heroicons/react/20/solid"; +import type { EventEnvelope } from "@qltysh/fabro-api-client"; import type { Stage } from "../stage-sidebar"; import { StageMetaBar } from "./meta-bar"; +import { findEdgeForNode } from "./helpers"; -// TODO: render the evaluated condition expression and the chosen edge once -// `ConditionalHandler` emits a `conditional.evaluated` event with that data. -// Right now the handler is a passthrough — engine-level edge selection is the -// only signal — so we only have notes/duration to display. +const REASON_LABEL: Record = { + condition: "Matched condition", + unconditional: "Default edge", + jump: "Jumped", + preferred_label: "Preferred label", +}; + +function reasonLabel(reason: string): string { + return REASON_LABEL[reason] ?? reason; +} + +export function ConditionalDecision({ + stage, + runEvents, + allStages, + runId, +}: { + stage: Stage; + runEvents: EventEnvelope[]; + allStages: Stage[]; + runId: string; +}) { + const edge = useMemo( + () => findEdgeForNode(runEvents, stage.nodeId), + [runEvents, stage.nodeId], + ); + const targetStage = useMemo(() => { + if (!edge) return null; + let pick: Stage | null = null; + for (const s of allStages) { + if (s.nodeId !== edge.toNode) continue; + if (!pick || s.visit > pick.visit) pick = s; + } + return pick; + }, [allStages, edge]); -export function ConditionalDecision({ stage }: { stage: Stage }) { return (
-
+
-

- The conditional {stage.nodeId}{" "} - evaluated and the workflow continued along its chosen edge. -

-

- +

+
+ From +
+
+ {stage.nodeId} +
+
+
+ + {edge && ( +
+
Reason
+
{reasonLabel(edge.reason)}
+ {edge.condition && ( + <> +
Condition
+
+ {edge.condition} +
+ + )} + {edge.isJump && ( + <> +
Edge type
+
Jump
+ + )} +
+ )} ); diff --git a/apps/fabro-web/app/components/stage-renderers/fan-in-results.tsx b/apps/fabro-web/app/components/stage-renderers/fan-in-results.tsx index ec650a863..a7d4b7f5c 100644 --- a/apps/fabro-web/app/components/stage-renderers/fan-in-results.tsx +++ b/apps/fabro-web/app/components/stage-renderers/fan-in-results.tsx @@ -1,6 +1,7 @@ import { useMemo } from "react"; import { CpuChipIcon, + SparklesIcon, TrophyIcon, } from "@heroicons/react/20/solid"; import type { EventEnvelope } from "@qltysh/fabro-api-client"; @@ -111,26 +112,36 @@ export function FanInResults({ } /> -
-
-