Working...
@@ -775,9 +916,17 @@ export default function WorkflowPage({
open={runsOpen}
settings={settings}
definition={selectedDefinition}
+ initialInstanceId={requestedRunId}
canStart={canStart}
canTransition={canTransition}
- onClose={() => setRunsOpen(false)}
+ onClose={() => {
+ setRunsOpen(false);
+ if (requestedRunId) {
+ const next = new URLSearchParams(searchParams);
+ next.delete("run");
+ setSearchParams(next, { replace: true });
+ }
+ }}
/>
);
@@ -799,6 +948,7 @@ function WorkflowDefinitionSettingsDialog({
onClose: () => void;
}) {
const provenance = draft?.governance?.actions.edit?.source_path ?? [];
+ const effectiveView = useEffectiveView();
const referenceScopeType = draft?.scopeType === "group" ? "group" : "user";
const scopeProvider = useMemo(
() => workflowScopeReferenceProvider(settings, referenceScopeType),
@@ -881,6 +1031,61 @@ function WorkflowDefinitionSettingsDialog({
+
+
+
+
+
+
onChange({ allowAutomation: value })}
/>
@@ -1094,6 +1299,14 @@ function startPaletteDrag(
event.dataTransfer.effectAllowed = "copy";
}
+function fileName(value: string): string {
+ return value
+ .normalize("NFKD")
+ .replace(/[^\w.-]+/g, "-")
+ .replace(/^-+|-+$/g, "")
+ .toLowerCase() || "workflow";
+}
+
function apiErrorMessage(error: unknown): string {
if (!isApiError(error)) {
return error instanceof Error ? error.message : "The request failed.";
diff --git a/webui/src/features/workflow/WorkflowRunsDialog.tsx b/webui/src/features/workflow/WorkflowRunsDialog.tsx
index b90f18b..9bc4ae7 100644
--- a/webui/src/features/workflow/WorkflowRunsDialog.tsx
+++ b/webui/src/features/workflow/WorkflowRunsDialog.tsx
@@ -23,8 +23,13 @@ import {
FormField,
IconButton,
LoadingFrame,
+ StageRail,
StatusBadge,
- type ApiSettings
+ dispatchWorkflowViewChanged,
+ usePlatformUiCapability,
+ type StageRailTone,
+ type ApiSettings,
+ type ViewsRuntimeUiCapability
} from "@govoplan/core-webui";
import {
cancelWorkflowInstance,
@@ -50,6 +55,7 @@ export default function WorkflowRunsDialog({
open,
settings,
definition,
+ initialInstanceId,
canStart,
canTransition,
onClose
@@ -57,6 +63,7 @@ export default function WorkflowRunsDialog({
open: boolean;
settings: ApiSettings;
definition: WorkflowDefinition | null;
+ initialInstanceId?: string | null;
canStart: boolean;
canTransition: boolean;
onClose: () => void;
@@ -69,6 +76,9 @@ export default function WorkflowRunsDialog({
const [comment, setComment] = useState("");
const [evidence, setEvidence] = useState("");
const [cancelOpen, setCancelOpen] = useState(false);
+ const viewsRuntime = usePlatformUiCapability
(
+ "views.runtime"
+ );
const selected = useMemo(
() => instances.find((item) => item.id === selectedId) ?? instances[0] ?? null,
@@ -99,7 +109,10 @@ export default function WorkflowRunsDialog({
const items = await listWorkflowInstances(settings, definition.id);
setInstances(items);
setSelectedId((current) => (
- items.some((item) => item.id === current)
+ initialInstanceId
+ && items.some((item) => item.id === initialInstanceId)
+ ? initialInstanceId
+ : items.some((item) => item.id === current)
? current
: items[0]?.id ?? null
));
@@ -108,7 +121,7 @@ export default function WorkflowRunsDialog({
} finally {
setLoading(false);
}
- }, [definition?.id, open, settings]);
+ }, [definition?.id, initialInstanceId, open, settings]);
useEffect(() => {
if (!open) return;
@@ -117,6 +130,39 @@ export default function WorkflowRunsDialog({
void load();
}, [load, open]);
+ useEffect(() => {
+ if (!open || !selected) return;
+ const context = selected.view_context;
+ if (!context || !viewsRuntime) {
+ dispatchWorkflowViewChanged(null);
+ return;
+ }
+ let cancelled = false;
+ void viewsRuntime.resolveWorkflowView(settings, {
+ viewId: context.view_id,
+ revisionId: context.revision_id,
+ visibleSurfaceIds: context.visible_surface_ids
+ }).then((projection) => {
+ if (!cancelled) {
+ dispatchWorkflowViewChanged(projection, selected.id);
+ }
+ }).catch((viewError) => {
+ if (!cancelled) {
+ dispatchWorkflowViewChanged(null);
+ setError(errorMessage(viewError));
+ }
+ });
+ return () => {
+ cancelled = true;
+ };
+ }, [
+ open,
+ selected?.id,
+ selected?.updated_at,
+ settings,
+ viewsRuntime
+ ]);
+
useEffect(() => {
if (
!open
@@ -236,6 +282,10 @@ export default function WorkflowRunsDialog({
const actionUrl = typeof currentStep?.handoff.action_url === "string"
? currentStep.handoff.action_url
: "";
+ const close = () => {
+ dispatchWorkflowViewChanged(null);
+ onClose();
+ };
return (
<>
@@ -244,8 +294,8 @@ export default function WorkflowRunsDialog({
title={`Runs${definition ? ` · ${definition.name}` : ""}`}
className="workflow-runs-dialog"
bodyClassName="workflow-runs-dialog-body"
- onClose={onClose}
- footer={}
+ onClose={close}
+ footer={}
>
@@ -419,40 +469,26 @@ export default function WorkflowRunsDialog({
) : null}
Progress
-
- {selected.steps.map((step) => (
-
-
- {step.status === "completed" ? (
-
- ) : step.status === "failed" ? (
-
- ) : ["running", "waiting"].includes(step.status) ? (
-
- ) : (
-
- )}
-
-
- {step.node_id}
-
- {step.node_type} · attempt {step.attempt}
-
-
-
-
- ))}
-
+ ({
+ id: step.id,
+ label: step.node_id,
+ detail: `${step.node_type} · attempt ${step.attempt}`,
+ statusLabel: step.status,
+ current: step.id === selected.current_step_id,
+ tone: stepTone(step.status),
+ icon: step.status === "completed" ? (
+
+ ) : step.status === "failed" ? (
+
+ ) : ["running", "waiting"].includes(step.status) ? (
+
+ ) : (
+
+ )
+ }))}
+ />
Evidence trail
@@ -530,6 +566,15 @@ function actionLabel(action: WorkflowAction): string {
}[action];
}
+function stepTone(
+ status: WorkflowInstanceStep["status"]
+): StageRailTone {
+ if (status === "completed") return "success";
+ if (status === "running" || status === "waiting") return "active";
+ if (status === "failed" || status === "cancelled") return "danger";
+ return "neutral";
+}
+
function formatDateTime(value: string): string {
return new Intl.DateTimeFormat(undefined, {
dateStyle: "medium",
diff --git a/webui/src/features/workflow/model.ts b/webui/src/features/workflow/model.ts
index 433cec7..2035030 100644
--- a/webui/src/features/workflow/model.ts
+++ b/webui/src/features/workflow/model.ts
@@ -7,6 +7,7 @@ import type {
WorkflowGovernance,
WorkflowGraph,
WorkflowGraphNode,
+ WorkflowExecutionMode,
WorkflowNodeType,
WorkflowStatus
} from "../../api/workflow";
@@ -27,44 +28,53 @@ export type WorkflowDraft = {
allowStart: boolean;
allowReuse: boolean;
allowAutomation: boolean;
+ executionMode: WorkflowExecutionMode;
+ viewId: string;
+ viewRevisionId: string;
governance: WorkflowGovernance | null;
};
const inputPort = [{
- id: "input",
- label: "Input",
+ id: "incoming",
+ label: "Incoming",
required: true,
- multiple: false,
+ multiple: true,
minimum_connections: 1
}];
const outputPort = [{
- id: "output",
- label: "Output",
- required: true,
- multiple: false,
- minimum_connections: 1
+ id: "outgoing",
+ label: "Outgoing",
+ required: false,
+ multiple: true,
+ minimum_connections: 0
}];
export const FALLBACK_WORKFLOW_LIBRARY: WorkflowNodeType[] = [
{
- type: "workflow.start.manual",
- category: "trigger",
- category_label: "Start",
- label: "Manual start",
- description: "Start through an explicit action.",
+ type: "bpmn.startEvent",
+ category: "bpmn_event",
+ category_label: "Events",
+ label: "Start event",
+ description: "Start a BPMN process.",
icon: "circle-play",
input_ports: [],
output_ports: outputPort,
config_fields: [],
- default_config: { input_schema_ref: "" }
+ default_config: {
+ event_definition: "none",
+ start_kind: "manual",
+ input_schema_ref: "",
+ documentation: ""
+ },
+ metadata: { notation: "bpmn-2.0", shape: "event-start" }
},
{
- type: "workflow.activity",
- category: "activity",
+ type: "bpmn.userTask",
+ category: "bpmn_activity",
category_label: "Activities",
- label: "Activity",
- description: "A governed unit of work.",
- icon: "square-check-big",
+ label: "User task",
+ description: "A governed user task.",
+ icon: "user-round-check",
input_ports: inputPort,
output_ports: outputPort,
config_fields: [
@@ -80,20 +90,29 @@ export const FALLBACK_WORKFLOW_LIBRARY: WorkflowNodeType[] = [
title: "",
instructions: "",
assignee: "",
- due_after: ""
- }
+ due_after: "",
+ task_mode: "activity",
+ documentation: ""
+ },
+ metadata: { notation: "bpmn-2.0", shape: "activity" }
},
{
- type: "workflow.end.completed",
- category: "outcome",
- category_label: "Outcomes",
- label: "Completed",
- description: "Complete successfully.",
- icon: "circle-check-big",
+ type: "bpmn.endEvent",
+ category: "bpmn_event",
+ category_label: "Events",
+ label: "End event",
+ description: "End the BPMN process.",
+ icon: "circle-stop",
input_ports: [{ ...inputPort[0], multiple: true }],
output_ports: [],
config_fields: [],
- default_config: { output_mapping: {} }
+ default_config: {
+ event_definition: "none",
+ outcome: "completed",
+ output_mapping: {},
+ documentation: ""
+ },
+ metadata: { notation: "bpmn-2.0", shape: "event-end" }
}
];
@@ -110,53 +129,98 @@ export function sampleWorkflowDraft(): WorkflowDraft {
allowStart: true,
allowReuse: false,
allowAutomation: false,
+ executionMode: "hybrid",
+ viewId: "",
+ viewRevisionId: "",
governance: null,
graph: {
schema_version: 1,
nodes: [
{
id: "start",
- type: "workflow.start.manual",
+ type: "bpmn.startEvent",
label: "Start",
position: { x: 60, y: 150 },
- config: { input_schema_ref: "" }
+ size: { width: 36, height: 36 },
+ process_id: "Process_1",
+ config: {
+ event_definition: "none",
+ start_kind: "manual",
+ input_schema_ref: "",
+ documentation: ""
+ }
},
{
id: "activity",
- type: "workflow.activity",
+ type: "bpmn.userTask",
label: "Activity",
position: { x: 320, y: 150 },
+ size: { width: 120, height: 80 },
+ process_id: "Process_1",
config: {
title: "Complete activity",
instructions: "",
assignee: "",
- due_after: ""
+ due_after: "",
+ task_mode: "activity",
+ documentation: ""
}
},
{
id: "complete",
- type: "workflow.end.completed",
+ type: "bpmn.endEvent",
label: "Completed",
position: { x: 580, y: 150 },
- config: { output_mapping: {} }
+ size: { width: 36, height: 36 },
+ process_id: "Process_1",
+ config: {
+ event_definition: "none",
+ outcome: "completed",
+ output_mapping: {},
+ documentation: ""
+ }
}
],
edges: [
{
id: "start-activity",
+ type: "bpmn.sequenceFlow",
+ label: "",
source: "start",
target: "activity",
- source_port: "output",
- target_port: "input"
+ source_port: "outgoing",
+ target_port: "incoming",
+ config: {},
+ waypoints: []
},
{
id: "activity-complete",
+ type: "bpmn.sequenceFlow",
+ label: "",
source: "activity",
target: "complete",
- source_port: "output",
- target_port: "input"
+ source_port: "outgoing",
+ target_port: "incoming",
+ config: {},
+ waypoints: []
}
- ]
+ ],
+ metadata: {
+ notation: "bpmn-2.0",
+ bpmn: {
+ definitions_id: "Definitions_1",
+ target_namespace: "urn:govoplan:workflow",
+ processes: [{
+ id: "Process_1",
+ name: "",
+ is_executable: true,
+ attributes: {}
+ }],
+ collaborations: [],
+ choreographies: [],
+ root_elements_xml: []
+ }
+ }
}
};
}
@@ -180,6 +244,9 @@ export function draftFromDefinition(
allowStart: definition.governance.allow_start,
allowReuse: definition.governance.allow_reuse,
allowAutomation: definition.governance.allow_automation,
+ executionMode: definition.revision.execution_mode,
+ viewId: definition.revision.view_id ?? "",
+ viewRevisionId: definition.revision.view_revision_id ?? "",
governance: definition.governance
};
}
@@ -198,7 +265,10 @@ export function workflowPayload(
inherit_to_lower_scopes: draft.inheritToLowerScopes,
allow_start: draft.allowStart,
allow_reuse: draft.allowReuse,
- allow_automation: draft.allowAutomation
+ allow_automation: draft.allowAutomation,
+ execution_mode: draft.executionMode,
+ view_id: draft.viewId || null,
+ view_revision_id: draft.viewRevisionId || null
};
}
@@ -217,7 +287,10 @@ export function workflowFingerprint(
inheritToLowerScopes: draft.inheritToLowerScopes,
allowStart: draft.allowStart,
allowReuse: draft.allowReuse,
- allowAutomation: draft.allowAutomation
+ allowAutomation: draft.allowAutomation,
+ executionMode: draft.executionMode,
+ viewId: draft.viewId,
+ viewRevisionId: draft.viewRevisionId
});
}
@@ -226,9 +299,25 @@ export function newWorkflowNode(
position: { x: number; y: number },
library: WorkflowNodeType[]
): WorkflowGraphNode {
- return createDefinitionGraphNode(
+ const node = createDefinitionGraphNode(
type,
position,
library
);
+ const shape = library.find((item) => item.type === type)?.metadata?.shape;
+ return {
+ ...node,
+ process_id: "Process_1",
+ size: defaultNodeSize(String(shape ?? "activity"))
+ };
+}
+
+function defaultNodeSize(shape: string): { width: number; height: number } {
+ if (shape.startsWith("event")) return { width: 36, height: 36 };
+ if (shape === "gateway") return { width: 50, height: 50 };
+ if (shape === "participant") return { width: 600, height: 180 };
+ if (shape === "lane") return { width: 560, height: 140 };
+ if (shape === "data-object") return { width: 36, height: 50 };
+ if (shape === "data-store") return { width: 50, height: 50 };
+ return { width: 120, height: 80 };
}
diff --git a/webui/src/module.ts b/webui/src/module.ts
index 967a524..f7ac8a9 100644
--- a/webui/src/module.ts
+++ b/webui/src/module.ts
@@ -1,13 +1,67 @@
import { createElement, lazy } from "react";
-import type { PlatformWebModule } from "@govoplan/core-webui";
+import type {
+ DashboardWidgetsUiCapability,
+ PlatformWebModule
+} from "@govoplan/core-webui";
import "@xyflow/react/dist/style.css";
import "./styles/workflow.css";
const WorkflowPage = lazy(() => import("./features/workflow/WorkflowPage"));
+const WorkflowOpenWorkWidget = lazy(
+ () => import("./features/workflow/WorkflowOpenWorkWidget")
+);
const readScopes = [
"workflow:definition:read",
"workflow:instance:admin"
];
+const instanceReadScopes = [
+ "workflow:instance:read",
+ "workflow:instance:admin"
+];
+const workflowDashboardWidgets: DashboardWidgetsUiCapability = {
+ widgets: [
+ {
+ id: "workflow.open-work",
+ surfaceId: "workflow.widget.open-work",
+ title: "Open workflow work",
+ description: "Running workflows and steps that require attention.",
+ moduleId: "workflow",
+ category: "Work",
+ order: 42,
+ defaultVisible: false,
+ defaultSize: "medium",
+ supportedSizes: ["medium", "wide"],
+ anyOf: instanceReadScopes,
+ refreshIntervalMs: 60_000,
+ defaultConfiguration: {
+ maxItems: 6,
+ includeRunning: true
+ },
+ configurationFields: [
+ {
+ id: "maxItems",
+ label: "Maximum items",
+ kind: "number",
+ min: 1,
+ max: 20,
+ step: 1,
+ required: true
+ },
+ {
+ id: "includeRunning",
+ label: "Include running steps",
+ kind: "boolean"
+ }
+ ],
+ render: ({ settings, refreshKey, configuration }) =>
+ createElement(WorkflowOpenWorkWidget, {
+ settings,
+ refreshKey,
+ configuration
+ })
+ }
+ ]
+};
export const workflowModule: PlatformWebModule = {
id: "workflow",
@@ -22,6 +76,15 @@ export const workflowModule: PlatformWebModule = {
"policy",
"tasks"
],
+ viewSurfaces: [
+ {
+ id: "workflow.widget.open-work",
+ moduleId: "workflow",
+ kind: "section",
+ label: "Open workflow work widget",
+ order: 76
+ }
+ ],
navItems: [
{
to: "/workflow",
@@ -39,7 +102,10 @@ export const workflowModule: PlatformWebModule = {
render: ({ settings, auth }) =>
createElement(WorkflowPage, { settings, auth })
}
- ]
+ ],
+ uiCapabilities: {
+ "dashboard.widgets": workflowDashboardWidgets
+ }
};
export default workflowModule;
diff --git a/webui/src/styles/workflow.css b/webui/src/styles/workflow.css
index 5ae32e6..491d6d3 100644
--- a/webui/src/styles/workflow.css
+++ b/webui/src/styles/workflow.css
@@ -274,6 +274,10 @@
overflow: hidden;
}
+.workflow-bpmn-file-input {
+ display: none;
+}
+
.workflow-palette,
.workflow-inspector,
.workflow-inspector-column {
@@ -457,38 +461,101 @@
display: flex;
align-items: center;
gap: 9px;
- width: 190px;
+ width: 100%;
+ height: 100%;
min-height: 56px;
+ box-sizing: border-box;
border: 1px solid var(--line-dark);
- border-left: 4px solid #3d6f9e;
border-radius: 6px;
background: var(--panel);
box-shadow: var(--shadow-xs);
padding: 8px 10px;
}
-.workflow-node-trigger {
- border-left-color: #2f7d6d;
+.workflow-node-bpmn_activity {
+ border-width: 2px;
}
-.workflow-node-activity {
- border-left-color: #3d6f9e;
+.workflow-node-bpmn_collaboration {
+ border-width: 2px;
+ background: color-mix(in srgb, var(--panel) 92%, transparent);
}
-.workflow-node-decision {
- border-left-color: #b7791f;
+.workflow-node-shape-participant,
+.workflow-node-shape-lane {
+ align-items: flex-start;
+ padding: 12px;
}
-.workflow-node-wait {
- border-left-color: #8a6d3b;
+.workflow-node-shape-lane {
+ border-width: 1px;
}
-.workflow-node-integration {
- border-left-color: #76569b;
+.workflow-node-shape-group {
+ align-items: flex-start;
+ border: 2px dashed var(--line-dark);
+ background: transparent;
+ box-shadow: none;
}
-.workflow-node-outcome {
- border-left-color: #9d4e63;
+.workflow-node-shape-text-annotation {
+ border: 0;
+ border-left: 2px solid var(--line-dark);
+ border-radius: 0;
+ background: transparent;
+ box-shadow: none;
+}
+
+.workflow-node[class*="workflow-node-shape-event"],
+.workflow-node-shape-gateway {
+ justify-content: flex-start;
+ border: 0;
+ background: transparent;
+ box-shadow: none;
+ padding: 4px;
+}
+
+.workflow-node[class*="workflow-node-shape-event"] .workflow-node-icon {
+ width: 42px;
+ height: 42px;
+ flex-basis: 42px;
+ border: 2px solid var(--text-strong);
+ border-radius: 50%;
+ background: var(--panel);
+}
+
+.workflow-node-shape-event-intermediate-catch .workflow-node-icon,
+.workflow-node-shape-event-intermediate-throw .workflow-node-icon {
+ box-shadow: inset 0 0 0 3px var(--panel), inset 0 0 0 4px var(--text-strong);
+}
+
+.workflow-node-shape-event-boundary .workflow-node-icon {
+ border-style: dashed;
+}
+
+.workflow-node-shape-event-end .workflow-node-icon {
+ border-width: 4px;
+}
+
+.workflow-node-shape-gateway .workflow-node-icon {
+ width: 42px;
+ height: 42px;
+ flex-basis: 42px;
+ border: 2px solid var(--text-strong);
+ border-radius: 2px;
+ background: var(--panel);
+ transform: rotate(45deg);
+}
+
+.workflow-node-shape-gateway .workflow-node-icon svg {
+ transform: rotate(-45deg);
+}
+
+.workflow-node-shape-data-object,
+.workflow-node-shape-data-store,
+.workflow-node-shape-conversation,
+.workflow-node-shape-choreography {
+ border-width: 2px;
}
.workflow-node.is-selected {
@@ -498,10 +565,26 @@
var(--shadow-xs);
}
+.workflow-node[class*="workflow-node-shape-event"].is-selected,
+.workflow-node-shape-gateway.is-selected {
+ box-shadow: none;
+}
+
+.workflow-node[class*="workflow-node-shape-event"].is-selected .workflow-node-icon,
+.workflow-node-shape-gateway.is-selected .workflow-node-icon {
+ border-color: var(--accent);
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 24%, transparent);
+}
+
.workflow-node.has-error {
border-color: var(--danger-text);
}
+.workflow-node[class*="workflow-node-shape-event"].has-error .workflow-node-icon,
+.workflow-node-shape-gateway.has-error .workflow-node-icon {
+ border-color: var(--danger-text);
+}
+
.workflow-node-icon {
display: grid;
width: 28px;
@@ -552,6 +635,25 @@
background: var(--accent);
}
+.workflow-edge-bpmn-messageFlow .react-flow__edge-path,
+.workflow-edge-bpmn-association .react-flow__edge-path,
+.workflow-edge-bpmn-conversationLink .react-flow__edge-path {
+ stroke-width: 1.5;
+}
+
+.workflow-inspector-checkbox {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ color: var(--text);
+ font-size: 12px;
+}
+
+.workflow-inspector-checkbox input {
+ width: auto;
+ margin: 0;
+}
+
.workflow-inspector-fields {
display: grid;
grid-auto-rows: max-content;
@@ -899,100 +1001,10 @@
padding: 6px 2px;
}
-.workflow-run-history > div {
- grid-template-columns: repeat(
- auto-fit,
- minmax(min(150px, 100%), 1fr)
- );
- overflow-x: auto;
+.workflow-run-history .stage-rail {
padding: 2px 0 4px;
}
-.workflow-run-stage {
- --workflow-stage-color: var(--line-dark);
- position: relative;
- display: grid;
- min-width: 140px;
- grid-template-columns: 32px minmax(0, 1fr);
- gap: 7px;
- padding: 4px 12px 4px 0;
-}
-
-.workflow-run-stage[data-state="completed"] {
- --workflow-stage-color: var(--green);
-}
-
-.workflow-run-stage[data-state="running"],
-.workflow-run-stage[data-state="waiting"] {
- --workflow-stage-color: var(--blue);
-}
-
-.workflow-run-stage[data-state="failed"],
-.workflow-run-stage[data-state="cancelled"] {
- --workflow-stage-color: var(--red);
-}
-
-.workflow-run-stage[data-state="superseded"] {
- --workflow-stage-color: var(--muted);
-}
-
-.workflow-run-stage:not(:last-child)::after {
- position: absolute;
- z-index: 0;
- top: 18px;
- left: 27px;
- width: calc(100% - 17px);
- height: 2px;
- background: linear-gradient(
- 90deg,
- var(--workflow-stage-color),
- var(--line-dark)
- );
- content: "";
-}
-
-.workflow-run-stage-marker {
- z-index: 1;
- display: grid;
- width: 30px;
- height: 30px;
- place-items: center;
- border: 1px solid var(--workflow-stage-color);
- border-radius: 50%;
- background: var(--panel);
- color: var(--workflow-stage-color);
-}
-
-.workflow-run-stage[data-current="true"] .workflow-run-stage-marker {
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--workflow-stage-color) 18%, transparent);
-}
-
-.workflow-run-stage-copy {
- z-index: 1;
- display: grid;
- min-width: 0;
- align-content: start;
- gap: 3px;
- padding-top: 2px;
-}
-
-.workflow-run-stage-copy strong,
-.workflow-run-stage-copy small {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.workflow-run-stage-copy strong {
- color: var(--text-strong);
- font-size: 11px;
-}
-
-.workflow-run-stage-copy .status-badge {
- width: fit-content;
- margin-top: 2px;
-}
-
.workflow-run-history small,
.workflow-run-events small {
color: var(--muted);
@@ -1025,6 +1037,7 @@
.workflow-editor {
grid-template-columns: 180px minmax(0, 1fr) 270px;
}
+
}
@media (max-width: 900px) {
@@ -1042,6 +1055,7 @@
border-top: var(--border-line);
border-left: 0;
}
+
}
@media (max-width: 680px) {
diff --git a/webui/tsconfig.json b/webui/tsconfig.json
index 0bd5c1f..493d211 100644
--- a/webui/tsconfig.json
+++ b/webui/tsconfig.json
@@ -26,7 +26,8 @@
"@xyflow/react": ["../../govoplan-core/webui/node_modules/@xyflow/react/dist/esm/index.d.ts"],
"lucide-react": ["../../govoplan-core/webui/node_modules/lucide-react/dist/lucide-react.d.ts"],
"react": ["../../govoplan-core/webui/node_modules/@types/react/index.d.ts"],
- "react/jsx-runtime": ["../../govoplan-core/webui/node_modules/@types/react/jsx-runtime.d.ts"]
+ "react/jsx-runtime": ["../../govoplan-core/webui/node_modules/@types/react/jsx-runtime.d.ts"],
+ "react-router": ["../../govoplan-core/webui/node_modules/react-router/dist/production/index.d.ts"]
}
},
"include": ["src"]