@contractspec/example.workflow-system 3.7.7 → 3.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/browser/handlers/index.js +43 -43
- package/dist/browser/handlers/workflow.handlers.js +43 -43
- package/dist/browser/index.js +572 -183
- package/dist/browser/shared/demo-scenario.js +213 -0
- package/dist/browser/ui/WorkflowDashboard.visualizations.js +239 -0
- package/dist/browser/ui/hooks/index.js +0 -47
- package/dist/browser/ui/hooks/useWorkflowList.js +5 -3
- package/dist/browser/ui/index.js +5 -3
- package/dist/browser/ui/renderers/index.js +409 -73
- package/dist/browser/ui/renderers/workflow.markdown.js +409 -73
- package/dist/browser/visualizations/catalog.js +132 -0
- package/dist/browser/visualizations/index.js +133 -0
- package/dist/browser/visualizations/selectors.js +195 -0
- package/dist/example.test.d.ts +1 -0
- package/dist/handlers/index.js +43 -43
- package/dist/handlers/workflow.handlers.js +43 -43
- package/dist/index.d.ts +1 -0
- package/dist/index.js +572 -183
- package/dist/shared/demo-scenario.d.ts +43 -0
- package/dist/shared/demo-scenario.js +214 -0
- package/dist/ui/WorkflowDashboard.visualizations.d.ts +4 -0
- package/dist/ui/WorkflowDashboard.visualizations.js +240 -0
- package/dist/ui/hooks/index.js +0 -47
- package/dist/ui/hooks/useWorkflowList.d.ts +2 -1
- package/dist/ui/hooks/useWorkflowList.js +5 -3
- package/dist/ui/index.js +5 -3
- package/dist/ui/renderers/index.js +409 -73
- package/dist/ui/renderers/workflow.markdown.js +409 -73
- package/dist/visualizations/catalog.d.ts +11 -0
- package/dist/visualizations/catalog.js +133 -0
- package/dist/visualizations/index.d.ts +2 -0
- package/dist/visualizations/index.js +134 -0
- package/dist/visualizations/selectors.d.ts +11 -0
- package/dist/visualizations/selectors.js +196 -0
- package/dist/visualizations/selectors.test.d.ts +1 -0
- package/package.json +69 -8
package/dist/browser/index.js
CHANGED
|
@@ -715,9 +715,19 @@ function rowToApproval(row) {
|
|
|
715
715
|
};
|
|
716
716
|
}
|
|
717
717
|
function createWorkflowHandlers(db) {
|
|
718
|
+
function normalizeSql(sql) {
|
|
719
|
+
let placeholderIndex = 0;
|
|
720
|
+
return sql.replace(/\?/g, () => `$${++placeholderIndex}`);
|
|
721
|
+
}
|
|
722
|
+
async function queryRows(sql, params = []) {
|
|
723
|
+
return (await db.query(normalizeSql(sql), params)).rows;
|
|
724
|
+
}
|
|
725
|
+
async function execute(sql, params = []) {
|
|
726
|
+
await db.execute(normalizeSql(sql), params);
|
|
727
|
+
}
|
|
718
728
|
async function listDefinitions(input) {
|
|
719
729
|
const { projectId, status, search, limit = 20, offset = 0 } = input;
|
|
720
|
-
let whereClause =
|
|
730
|
+
let whereClause = 'WHERE "projectId" = ?';
|
|
721
731
|
const params = [projectId];
|
|
722
732
|
if (status && status !== "all") {
|
|
723
733
|
whereClause += " AND status = ?";
|
|
@@ -727,9 +737,9 @@ function createWorkflowHandlers(db) {
|
|
|
727
737
|
whereClause += " AND name LIKE ?";
|
|
728
738
|
params.push(`%${search}%`);
|
|
729
739
|
}
|
|
730
|
-
const countResult =
|
|
740
|
+
const countResult = await queryRows(`SELECT COUNT(*) as count FROM workflow_definition ${whereClause}`, params);
|
|
731
741
|
const total = countResult[0]?.count ?? 0;
|
|
732
|
-
const rows =
|
|
742
|
+
const rows = await queryRows(`SELECT * FROM workflow_definition ${whereClause} ORDER BY "updatedAt" DESC LIMIT ? OFFSET ?`, [...params, limit, offset]);
|
|
733
743
|
return {
|
|
734
744
|
definitions: rows.map(rowToDefinition),
|
|
735
745
|
total
|
|
@@ -738,7 +748,7 @@ function createWorkflowHandlers(db) {
|
|
|
738
748
|
async function createDefinition(input, context) {
|
|
739
749
|
const id = generateId("wfdef");
|
|
740
750
|
const now = new Date().toISOString();
|
|
741
|
-
await
|
|
751
|
+
await execute(`INSERT INTO workflow_definition (id, "projectId", "organizationId", name, description, type, status, "createdAt", "updatedAt")
|
|
742
752
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
743
753
|
id,
|
|
744
754
|
context.projectId,
|
|
@@ -750,15 +760,15 @@ function createWorkflowHandlers(db) {
|
|
|
750
760
|
now,
|
|
751
761
|
now
|
|
752
762
|
]);
|
|
753
|
-
const rows =
|
|
763
|
+
const rows = await queryRows(`SELECT * FROM workflow_definition WHERE id = ?`, [id]);
|
|
754
764
|
return rowToDefinition(rows[0]);
|
|
755
765
|
}
|
|
756
766
|
async function addStep(input) {
|
|
757
767
|
const id = generateId("wfstep");
|
|
758
768
|
const now = new Date().toISOString();
|
|
759
|
-
const maxOrderResult =
|
|
769
|
+
const maxOrderResult = await queryRows(`SELECT MAX("stepOrder") as maxOrder FROM workflow_step WHERE "definitionId" = ?`, [input.definitionId]);
|
|
760
770
|
const nextOrder = (maxOrderResult[0]?.maxOrder ?? 0) + 1;
|
|
761
|
-
await
|
|
771
|
+
await execute(`INSERT INTO workflow_step (id, "definitionId", name, description, "stepOrder", type, "requiredRoles", "autoApproveCondition", "timeoutHours", "createdAt")
|
|
762
772
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
763
773
|
id,
|
|
764
774
|
input.definitionId,
|
|
@@ -771,11 +781,11 @@ function createWorkflowHandlers(db) {
|
|
|
771
781
|
input.timeoutHours ?? null,
|
|
772
782
|
now
|
|
773
783
|
]);
|
|
774
|
-
const rows =
|
|
784
|
+
const rows = await queryRows(`SELECT * FROM workflow_step WHERE id = ?`, [id]);
|
|
775
785
|
return rowToStep(rows[0]);
|
|
776
786
|
}
|
|
777
787
|
async function getSteps(definitionId) {
|
|
778
|
-
const rows =
|
|
788
|
+
const rows = await queryRows(`SELECT * FROM workflow_step WHERE "definitionId" = ? ORDER BY "stepOrder"`, [definitionId]);
|
|
779
789
|
return rows.map(rowToStep);
|
|
780
790
|
}
|
|
781
791
|
async function listInstances(input) {
|
|
@@ -787,10 +797,10 @@ function createWorkflowHandlers(db) {
|
|
|
787
797
|
limit = 20,
|
|
788
798
|
offset = 0
|
|
789
799
|
} = input;
|
|
790
|
-
let whereClause =
|
|
800
|
+
let whereClause = 'WHERE "projectId" = ?';
|
|
791
801
|
const params = [projectId];
|
|
792
802
|
if (definitionId) {
|
|
793
|
-
whereClause +=
|
|
803
|
+
whereClause += ' AND "definitionId" = ?';
|
|
794
804
|
params.push(definitionId);
|
|
795
805
|
}
|
|
796
806
|
if (status && status !== "all") {
|
|
@@ -798,12 +808,12 @@ function createWorkflowHandlers(db) {
|
|
|
798
808
|
params.push(status);
|
|
799
809
|
}
|
|
800
810
|
if (requestedBy) {
|
|
801
|
-
whereClause +=
|
|
811
|
+
whereClause += ' AND "requestedBy" = ?';
|
|
802
812
|
params.push(requestedBy);
|
|
803
813
|
}
|
|
804
|
-
const countResult =
|
|
814
|
+
const countResult = await queryRows(`SELECT COUNT(*) as count FROM workflow_instance ${whereClause}`, params);
|
|
805
815
|
const total = countResult[0]?.count ?? 0;
|
|
806
|
-
const rows =
|
|
816
|
+
const rows = await queryRows(`SELECT * FROM workflow_instance ${whereClause} ORDER BY "startedAt" DESC LIMIT ? OFFSET ?`, [...params, limit, offset]);
|
|
807
817
|
return {
|
|
808
818
|
instances: rows.map(rowToInstance),
|
|
809
819
|
total
|
|
@@ -812,9 +822,9 @@ function createWorkflowHandlers(db) {
|
|
|
812
822
|
async function startInstance(input, context) {
|
|
813
823
|
const id = generateId("wfinst");
|
|
814
824
|
const now = new Date().toISOString();
|
|
815
|
-
const steps =
|
|
825
|
+
const steps = await queryRows(`SELECT * FROM workflow_step WHERE "definitionId" = ? ORDER BY "stepOrder" LIMIT 1`, [input.definitionId]);
|
|
816
826
|
const firstStepId = steps[0]?.id ?? null;
|
|
817
|
-
await
|
|
827
|
+
await execute(`INSERT INTO workflow_instance (id, "projectId", "definitionId", status, "currentStepId", data, "requestedBy", "startedAt")
|
|
818
828
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
819
829
|
id,
|
|
820
830
|
context.projectId,
|
|
@@ -826,36 +836,32 @@ function createWorkflowHandlers(db) {
|
|
|
826
836
|
now
|
|
827
837
|
]);
|
|
828
838
|
if (firstStepId) {
|
|
829
|
-
await
|
|
839
|
+
await execute(`INSERT INTO workflow_approval (id, "instanceId", "stepId", status, "createdAt")
|
|
830
840
|
VALUES (?, ?, ?, ?, ?)`, [generateId("wfappr"), id, firstStepId, "PENDING", now]);
|
|
831
841
|
}
|
|
832
|
-
const rows =
|
|
842
|
+
const rows = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [id]);
|
|
833
843
|
return rowToInstance(rows[0]);
|
|
834
844
|
}
|
|
835
845
|
async function approveStep(input, context) {
|
|
836
846
|
const now = new Date().toISOString();
|
|
837
|
-
const instances =
|
|
838
|
-
input.instanceId
|
|
839
|
-
])).rows;
|
|
847
|
+
const instances = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
|
|
840
848
|
if (!instances[0]) {
|
|
841
849
|
throw new Error("NOT_FOUND");
|
|
842
850
|
}
|
|
843
851
|
const instance = instances[0];
|
|
844
|
-
await
|
|
845
|
-
WHERE instanceId = ? AND stepId = ? AND status = 'PENDING'`, [
|
|
852
|
+
await execute(`UPDATE workflow_approval SET status = 'APPROVED', "actorId" = ?, comment = ?, "decidedAt" = ?
|
|
853
|
+
WHERE "instanceId" = ? AND "stepId" = ? AND status = 'PENDING'`, [
|
|
846
854
|
context.actorId,
|
|
847
855
|
input.comment ?? null,
|
|
848
856
|
now,
|
|
849
857
|
input.instanceId,
|
|
850
858
|
instance.currentStepId
|
|
851
859
|
]);
|
|
852
|
-
const currentStep =
|
|
853
|
-
|
|
854
|
-
])).rows;
|
|
855
|
-
const nextSteps = (await db.query(`SELECT * FROM workflow_step WHERE definitionId = ? AND stepOrder > ? ORDER BY stepOrder LIMIT 1`, [instance.definitionId, currentStep[0]?.stepOrder ?? 0])).rows;
|
|
860
|
+
const currentStep = await queryRows(`SELECT * FROM workflow_step WHERE id = ?`, [instance.currentStepId]);
|
|
861
|
+
const nextSteps = await queryRows(`SELECT * FROM workflow_step WHERE "definitionId" = ? AND "stepOrder" > ? ORDER BY "stepOrder" LIMIT 1`, [instance.definitionId, currentStep[0]?.stepOrder ?? 0]);
|
|
856
862
|
if (nextSteps[0]) {
|
|
857
|
-
await
|
|
858
|
-
await
|
|
863
|
+
await execute(`UPDATE workflow_instance SET "currentStepId" = ? WHERE id = ?`, [nextSteps[0].id, input.instanceId]);
|
|
864
|
+
await execute(`INSERT INTO workflow_approval (id, "instanceId", "stepId", status, "createdAt")
|
|
859
865
|
VALUES (?, ?, ?, ?, ?)`, [
|
|
860
866
|
generateId("wfappr"),
|
|
861
867
|
input.instanceId,
|
|
@@ -864,37 +870,31 @@ function createWorkflowHandlers(db) {
|
|
|
864
870
|
now
|
|
865
871
|
]);
|
|
866
872
|
} else {
|
|
867
|
-
await
|
|
873
|
+
await execute(`UPDATE workflow_instance SET status = 'COMPLETED', "currentStepId" = NULL, "completedAt" = ? WHERE id = ?`, [now, input.instanceId]);
|
|
868
874
|
}
|
|
869
|
-
const updated =
|
|
870
|
-
input.instanceId
|
|
871
|
-
])).rows;
|
|
875
|
+
const updated = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
|
|
872
876
|
return rowToInstance(updated[0]);
|
|
873
877
|
}
|
|
874
878
|
async function rejectStep(input, context) {
|
|
875
879
|
const now = new Date().toISOString();
|
|
876
|
-
const instances =
|
|
877
|
-
input.instanceId
|
|
878
|
-
])).rows;
|
|
880
|
+
const instances = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
|
|
879
881
|
if (!instances[0]) {
|
|
880
882
|
throw new Error("NOT_FOUND");
|
|
881
883
|
}
|
|
882
|
-
await
|
|
883
|
-
WHERE instanceId = ? AND stepId = ? AND status = 'PENDING'`, [
|
|
884
|
+
await execute(`UPDATE workflow_approval SET status = 'REJECTED', "actorId" = ?, comment = ?, "decidedAt" = ?
|
|
885
|
+
WHERE "instanceId" = ? AND "stepId" = ? AND status = 'PENDING'`, [
|
|
884
886
|
context.actorId,
|
|
885
887
|
input.reason,
|
|
886
888
|
now,
|
|
887
889
|
input.instanceId,
|
|
888
890
|
instances[0].currentStepId
|
|
889
891
|
]);
|
|
890
|
-
await
|
|
891
|
-
const updated =
|
|
892
|
-
input.instanceId
|
|
893
|
-
])).rows;
|
|
892
|
+
await execute(`UPDATE workflow_instance SET status = 'REJECTED', "completedAt" = ? WHERE id = ?`, [now, input.instanceId]);
|
|
893
|
+
const updated = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
|
|
894
894
|
return rowToInstance(updated[0]);
|
|
895
895
|
}
|
|
896
896
|
async function getApprovals(instanceId) {
|
|
897
|
-
const rows =
|
|
897
|
+
const rows = await queryRows(`SELECT * FROM workflow_approval WHERE "instanceId" = ? ORDER BY "createdAt"`, [instanceId]);
|
|
898
898
|
return rows.map(rowToApproval);
|
|
899
899
|
}
|
|
900
900
|
return {
|
|
@@ -1992,8 +1992,9 @@ var WorkflowSystemPresentations = {
|
|
|
1992
1992
|
import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
|
|
1993
1993
|
import { useCallback, useEffect, useState } from "react";
|
|
1994
1994
|
"use client";
|
|
1995
|
-
function useWorkflowList(
|
|
1996
|
-
const { handlers } = useTemplateRuntime();
|
|
1995
|
+
function useWorkflowList(projectIdOverride) {
|
|
1996
|
+
const { handlers, projectId: runtimeProjectId } = useTemplateRuntime();
|
|
1997
|
+
const projectId = projectIdOverride ?? runtimeProjectId;
|
|
1997
1998
|
const workflow = handlers.workflow;
|
|
1998
1999
|
const [definitions, setDefinitions] = useState([]);
|
|
1999
2000
|
const [instances, setInstances] = useState([]);
|
|
@@ -2014,7 +2015,7 @@ function useWorkflowList(projectId = "local-project") {
|
|
|
2014
2015
|
} finally {
|
|
2015
2016
|
setLoading(false);
|
|
2016
2017
|
}
|
|
2017
|
-
}, [
|
|
2018
|
+
}, [projectId, workflow]);
|
|
2018
2019
|
useEffect(() => {
|
|
2019
2020
|
fetchData();
|
|
2020
2021
|
}, [fetchData]);
|
|
@@ -2023,6 +2024,7 @@ function useWorkflowList(projectId = "local-project") {
|
|
|
2023
2024
|
activeDefinitions: definitions.filter((d) => d.status === "ACTIVE").length,
|
|
2024
2025
|
totalInstances: instances.length,
|
|
2025
2026
|
pendingInstances: instances.filter((i) => i.status === "PENDING").length,
|
|
2027
|
+
inProgressInstances: instances.filter((i) => i.status === "IN_PROGRESS").length,
|
|
2026
2028
|
completedInstances: instances.filter((i) => i.status === "COMPLETED").length,
|
|
2027
2029
|
rejectedInstances: instances.filter((i) => i.status === "REJECTED").length
|
|
2028
2030
|
};
|
|
@@ -2039,115 +2041,450 @@ function useWorkflowList(projectId = "local-project") {
|
|
|
2039
2041
|
// src/ui/hooks/index.ts
|
|
2040
2042
|
"use client";
|
|
2041
2043
|
|
|
2042
|
-
// src/
|
|
2043
|
-
var
|
|
2044
|
+
// src/shared/demo-scenario.ts
|
|
2045
|
+
var WORKFLOW_SYSTEM_DEMO_PROJECT_ID = "workflow-system-demo";
|
|
2046
|
+
var WORKFLOW_SYSTEM_DEMO_ORGANIZATION_ID = "org_demo";
|
|
2047
|
+
var WORKFLOW_SYSTEM_DEMO_DEFINITIONS = [
|
|
2044
2048
|
{
|
|
2045
|
-
id: "
|
|
2046
|
-
name: "
|
|
2049
|
+
id: "wf_expense",
|
|
2050
|
+
name: "Expense Approval",
|
|
2051
|
+
description: "Approve non-trivial spend before finance releases budget.",
|
|
2047
2052
|
type: "APPROVAL",
|
|
2053
|
+
status: "ACTIVE",
|
|
2054
|
+
createdAt: "2026-03-10T09:00:00.000Z",
|
|
2055
|
+
updatedAt: "2026-03-20T08:15:00.000Z",
|
|
2048
2056
|
steps: [
|
|
2049
2057
|
{
|
|
2050
|
-
id: "
|
|
2058
|
+
id: "wfstep_expense_manager",
|
|
2051
2059
|
name: "Manager Review",
|
|
2052
|
-
|
|
2053
|
-
|
|
2060
|
+
description: "Validate business value and team budget.",
|
|
2061
|
+
stepOrder: 1,
|
|
2062
|
+
type: "APPROVAL",
|
|
2063
|
+
requiredRoles: ["manager"],
|
|
2064
|
+
createdAt: "2026-03-10T09:00:00.000Z"
|
|
2054
2065
|
},
|
|
2055
2066
|
{
|
|
2056
|
-
id: "
|
|
2067
|
+
id: "wfstep_expense_finance",
|
|
2057
2068
|
name: "Finance Review",
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2069
|
+
description: "Confirm ledger coding and spending threshold.",
|
|
2070
|
+
stepOrder: 2,
|
|
2071
|
+
type: "APPROVAL",
|
|
2072
|
+
requiredRoles: ["finance"],
|
|
2073
|
+
createdAt: "2026-03-10T09:10:00.000Z"
|
|
2074
|
+
}
|
|
2075
|
+
]
|
|
2064
2076
|
},
|
|
2065
2077
|
{
|
|
2066
|
-
id: "
|
|
2067
|
-
name: "
|
|
2068
|
-
|
|
2078
|
+
id: "wf_vendor",
|
|
2079
|
+
name: "Vendor Onboarding",
|
|
2080
|
+
description: "Sequence security, procurement, and legal before activation.",
|
|
2081
|
+
type: "SEQUENTIAL",
|
|
2082
|
+
status: "ACTIVE",
|
|
2083
|
+
createdAt: "2026-03-08T11:00:00.000Z",
|
|
2084
|
+
updatedAt: "2026-03-19T13:10:00.000Z",
|
|
2069
2085
|
steps: [
|
|
2070
2086
|
{
|
|
2071
|
-
id: "
|
|
2072
|
-
name: "
|
|
2073
|
-
|
|
2074
|
-
|
|
2087
|
+
id: "wfstep_vendor_security",
|
|
2088
|
+
name: "Security Check",
|
|
2089
|
+
description: "Review data access and integration scope.",
|
|
2090
|
+
stepOrder: 1,
|
|
2091
|
+
type: "APPROVAL",
|
|
2092
|
+
requiredRoles: ["security"],
|
|
2093
|
+
createdAt: "2026-03-08T11:00:00.000Z"
|
|
2075
2094
|
},
|
|
2076
|
-
{
|
|
2077
|
-
|
|
2078
|
-
|
|
2095
|
+
{
|
|
2096
|
+
id: "wfstep_vendor_procurement",
|
|
2097
|
+
name: "Procurement Check",
|
|
2098
|
+
description: "Validate pricing, procurement policy, and owner.",
|
|
2099
|
+
stepOrder: 2,
|
|
2100
|
+
type: "APPROVAL",
|
|
2101
|
+
requiredRoles: ["procurement"],
|
|
2102
|
+
createdAt: "2026-03-08T11:05:00.000Z"
|
|
2103
|
+
},
|
|
2104
|
+
{
|
|
2105
|
+
id: "wfstep_vendor_legal",
|
|
2106
|
+
name: "Legal Sign-off",
|
|
2107
|
+
description: "Approve terms before the vendor goes live.",
|
|
2108
|
+
stepOrder: 3,
|
|
2109
|
+
type: "APPROVAL",
|
|
2110
|
+
requiredRoles: ["legal"],
|
|
2111
|
+
createdAt: "2026-03-08T11:10:00.000Z"
|
|
2112
|
+
}
|
|
2113
|
+
]
|
|
2079
2114
|
},
|
|
2080
2115
|
{
|
|
2081
|
-
id: "
|
|
2082
|
-
name: "
|
|
2083
|
-
|
|
2116
|
+
id: "wf_policy_exception",
|
|
2117
|
+
name: "Policy Exception",
|
|
2118
|
+
description: "Escalate a temporary exception through team lead and compliance.",
|
|
2119
|
+
type: "APPROVAL",
|
|
2120
|
+
status: "DRAFT",
|
|
2121
|
+
createdAt: "2026-03-15T07:30:00.000Z",
|
|
2122
|
+
updatedAt: "2026-03-18T11:20:00.000Z",
|
|
2084
2123
|
steps: [
|
|
2085
|
-
{
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2124
|
+
{
|
|
2125
|
+
id: "wfstep_policy_lead",
|
|
2126
|
+
name: "Team Lead Review",
|
|
2127
|
+
description: "Check urgency and expected blast radius.",
|
|
2128
|
+
stepOrder: 1,
|
|
2129
|
+
type: "APPROVAL",
|
|
2130
|
+
requiredRoles: ["team-lead"],
|
|
2131
|
+
createdAt: "2026-03-15T07:30:00.000Z"
|
|
2132
|
+
},
|
|
2133
|
+
{
|
|
2134
|
+
id: "wfstep_policy_compliance",
|
|
2135
|
+
name: "Compliance Review",
|
|
2136
|
+
description: "Accept or reject the exception request.",
|
|
2137
|
+
stepOrder: 2,
|
|
2138
|
+
type: "APPROVAL",
|
|
2139
|
+
requiredRoles: ["compliance"],
|
|
2140
|
+
createdAt: "2026-03-15T07:40:00.000Z"
|
|
2141
|
+
}
|
|
2142
|
+
]
|
|
2090
2143
|
}
|
|
2091
2144
|
];
|
|
2092
|
-
var
|
|
2145
|
+
var WORKFLOW_SYSTEM_DEMO_INSTANCES = [
|
|
2093
2146
|
{
|
|
2094
|
-
id: "
|
|
2095
|
-
definitionId: "
|
|
2096
|
-
definitionName: "Purchase Approval",
|
|
2147
|
+
id: "wfinst_expense_open",
|
|
2148
|
+
definitionId: "wf_expense",
|
|
2097
2149
|
status: "IN_PROGRESS",
|
|
2098
|
-
currentStepId: "
|
|
2099
|
-
|
|
2100
|
-
|
|
2150
|
+
currentStepId: "wfstep_expense_finance",
|
|
2151
|
+
data: {
|
|
2152
|
+
amount: 4200,
|
|
2153
|
+
currency: "EUR",
|
|
2154
|
+
vendor: "Nimbus AI"
|
|
2155
|
+
},
|
|
2156
|
+
requestedBy: "sarah@contractspec.io",
|
|
2157
|
+
startedAt: "2026-03-20T08:00:00.000Z",
|
|
2158
|
+
approvals: [
|
|
2159
|
+
{
|
|
2160
|
+
id: "wfappr_expense_manager",
|
|
2161
|
+
stepId: "wfstep_expense_manager",
|
|
2162
|
+
status: "APPROVED",
|
|
2163
|
+
actorId: "manager.demo",
|
|
2164
|
+
comment: "Approved for the Q2 automation budget.",
|
|
2165
|
+
decidedAt: "2026-03-20T08:15:00.000Z",
|
|
2166
|
+
createdAt: "2026-03-20T08:05:00.000Z"
|
|
2167
|
+
},
|
|
2168
|
+
{
|
|
2169
|
+
id: "wfappr_expense_finance",
|
|
2170
|
+
stepId: "wfstep_expense_finance",
|
|
2171
|
+
status: "PENDING",
|
|
2172
|
+
createdAt: "2026-03-20T08:15:00.000Z"
|
|
2173
|
+
}
|
|
2174
|
+
]
|
|
2101
2175
|
},
|
|
2102
2176
|
{
|
|
2103
|
-
id: "
|
|
2104
|
-
definitionId: "
|
|
2105
|
-
definitionName: "Purchase Approval",
|
|
2177
|
+
id: "wfinst_vendor_done",
|
|
2178
|
+
definitionId: "wf_vendor",
|
|
2106
2179
|
status: "COMPLETED",
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2180
|
+
data: {
|
|
2181
|
+
vendor: "Acme Cloud",
|
|
2182
|
+
riskTier: "medium"
|
|
2183
|
+
},
|
|
2184
|
+
requestedBy: "leo@contractspec.io",
|
|
2185
|
+
startedAt: "2026-03-19T09:30:00.000Z",
|
|
2186
|
+
completedAt: "2026-03-19T13:10:00.000Z",
|
|
2187
|
+
approvals: [
|
|
2188
|
+
{
|
|
2189
|
+
id: "wfappr_vendor_security",
|
|
2190
|
+
stepId: "wfstep_vendor_security",
|
|
2191
|
+
status: "APPROVED",
|
|
2192
|
+
actorId: "security.demo",
|
|
2193
|
+
comment: "SOC2 scope is acceptable.",
|
|
2194
|
+
decidedAt: "2026-03-19T10:10:00.000Z",
|
|
2195
|
+
createdAt: "2026-03-19T09:35:00.000Z"
|
|
2196
|
+
},
|
|
2197
|
+
{
|
|
2198
|
+
id: "wfappr_vendor_procurement",
|
|
2199
|
+
stepId: "wfstep_vendor_procurement",
|
|
2200
|
+
status: "APPROVED",
|
|
2201
|
+
actorId: "procurement.demo",
|
|
2202
|
+
comment: "Commercial terms match the preferred vendor tier.",
|
|
2203
|
+
decidedAt: "2026-03-19T11:25:00.000Z",
|
|
2204
|
+
createdAt: "2026-03-19T10:15:00.000Z"
|
|
2205
|
+
},
|
|
2206
|
+
{
|
|
2207
|
+
id: "wfappr_vendor_legal",
|
|
2208
|
+
stepId: "wfstep_vendor_legal",
|
|
2209
|
+
status: "APPROVED",
|
|
2210
|
+
actorId: "legal.demo",
|
|
2211
|
+
comment: "MSA redlines are complete.",
|
|
2212
|
+
decidedAt: "2026-03-19T13:05:00.000Z",
|
|
2213
|
+
createdAt: "2026-03-19T11:30:00.000Z"
|
|
2214
|
+
}
|
|
2215
|
+
]
|
|
2111
2216
|
},
|
|
2112
2217
|
{
|
|
2113
|
-
id: "
|
|
2114
|
-
definitionId: "
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2218
|
+
id: "wfinst_policy_rejected",
|
|
2219
|
+
definitionId: "wf_policy_exception",
|
|
2220
|
+
status: "REJECTED",
|
|
2221
|
+
currentStepId: "wfstep_policy_compliance",
|
|
2222
|
+
data: {
|
|
2223
|
+
policy: "Model rollout freeze",
|
|
2224
|
+
durationDays: 14
|
|
2225
|
+
},
|
|
2226
|
+
requestedBy: "maya@contractspec.io",
|
|
2227
|
+
startedAt: "2026-03-18T10:00:00.000Z",
|
|
2228
|
+
completedAt: "2026-03-18T11:20:00.000Z",
|
|
2229
|
+
approvals: [
|
|
2230
|
+
{
|
|
2231
|
+
id: "wfappr_policy_lead",
|
|
2232
|
+
stepId: "wfstep_policy_lead",
|
|
2233
|
+
status: "APPROVED",
|
|
2234
|
+
actorId: "lead.demo",
|
|
2235
|
+
comment: "Escalation justified for the release train.",
|
|
2236
|
+
decidedAt: "2026-03-18T10:30:00.000Z",
|
|
2237
|
+
createdAt: "2026-03-18T10:05:00.000Z"
|
|
2238
|
+
},
|
|
2239
|
+
{
|
|
2240
|
+
id: "wfappr_policy_compliance",
|
|
2241
|
+
stepId: "wfstep_policy_compliance",
|
|
2242
|
+
status: "REJECTED",
|
|
2243
|
+
actorId: "compliance.demo",
|
|
2244
|
+
comment: "Exception exceeds the allowed blast radius.",
|
|
2245
|
+
decidedAt: "2026-03-18T11:15:00.000Z",
|
|
2246
|
+
createdAt: "2026-03-18T10:35:00.000Z"
|
|
2247
|
+
}
|
|
2248
|
+
]
|
|
2249
|
+
}
|
|
2250
|
+
];
|
|
2251
|
+
|
|
2252
|
+
// src/visualizations/catalog.ts
|
|
2253
|
+
import {
|
|
2254
|
+
defineVisualization,
|
|
2255
|
+
VisualizationRegistry
|
|
2256
|
+
} from "@contractspec/lib.contracts-spec/visualizations";
|
|
2257
|
+
var INSTANCE_LIST_REF = {
|
|
2258
|
+
key: "workflow.instance.list",
|
|
2259
|
+
version: "1.0.0"
|
|
2260
|
+
};
|
|
2261
|
+
var META = {
|
|
2262
|
+
version: "1.0.0",
|
|
2263
|
+
domain: "workflow",
|
|
2264
|
+
stability: "experimental",
|
|
2265
|
+
owners: ["@example.workflow-system"],
|
|
2266
|
+
tags: ["workflow", "visualization", "operations"]
|
|
2267
|
+
};
|
|
2268
|
+
var WorkflowInstanceStatusVisualization = defineVisualization({
|
|
2269
|
+
meta: {
|
|
2270
|
+
...META,
|
|
2271
|
+
key: "workflow-system.visualization.instance-status",
|
|
2272
|
+
title: "Instance Status Breakdown",
|
|
2273
|
+
description: "Distribution of workflow instance states.",
|
|
2274
|
+
goal: "Surface the current workload mix.",
|
|
2275
|
+
context: "Workflow operations overview."
|
|
2276
|
+
},
|
|
2277
|
+
source: { primary: INSTANCE_LIST_REF, resultPath: "data" },
|
|
2278
|
+
visualization: {
|
|
2279
|
+
kind: "pie",
|
|
2280
|
+
nameDimension: "status",
|
|
2281
|
+
valueMeasure: "instances",
|
|
2282
|
+
dimensions: [
|
|
2283
|
+
{ key: "status", label: "Status", dataPath: "status", type: "category" }
|
|
2284
|
+
],
|
|
2285
|
+
measures: [
|
|
2286
|
+
{
|
|
2287
|
+
key: "instances",
|
|
2288
|
+
label: "Instances",
|
|
2289
|
+
dataPath: "instances",
|
|
2290
|
+
format: "number"
|
|
2291
|
+
}
|
|
2292
|
+
],
|
|
2293
|
+
table: { caption: "Workflow instance counts by status." }
|
|
2294
|
+
}
|
|
2295
|
+
});
|
|
2296
|
+
var WorkflowThroughputVisualization = defineVisualization({
|
|
2297
|
+
meta: {
|
|
2298
|
+
...META,
|
|
2299
|
+
key: "workflow-system.visualization.throughput",
|
|
2300
|
+
title: "Run Throughput",
|
|
2301
|
+
description: "Daily workflow instance starts.",
|
|
2302
|
+
goal: "Show operational throughput over time.",
|
|
2303
|
+
context: "Workflow trend monitoring."
|
|
2304
|
+
},
|
|
2305
|
+
source: { primary: INSTANCE_LIST_REF, resultPath: "data" },
|
|
2306
|
+
visualization: {
|
|
2307
|
+
kind: "cartesian",
|
|
2308
|
+
variant: "line",
|
|
2309
|
+
xDimension: "day",
|
|
2310
|
+
yMeasures: ["instances"],
|
|
2311
|
+
dimensions: [{ key: "day", label: "Day", dataPath: "day", type: "time" }],
|
|
2312
|
+
measures: [
|
|
2313
|
+
{
|
|
2314
|
+
key: "instances",
|
|
2315
|
+
label: "Instances",
|
|
2316
|
+
dataPath: "instances",
|
|
2317
|
+
format: "number",
|
|
2318
|
+
color: "#0f766e"
|
|
2319
|
+
}
|
|
2320
|
+
],
|
|
2321
|
+
table: { caption: "Daily workflow instance starts." }
|
|
2322
|
+
}
|
|
2323
|
+
});
|
|
2324
|
+
var WorkflowActiveMetricVisualization = defineVisualization({
|
|
2325
|
+
meta: {
|
|
2326
|
+
...META,
|
|
2327
|
+
key: "workflow-system.visualization.active-work",
|
|
2328
|
+
title: "Active Work",
|
|
2329
|
+
description: "Current in-flight or pending workflow instances.",
|
|
2330
|
+
goal: "Expose active operational workload.",
|
|
2331
|
+
context: "Workflow workload comparison."
|
|
2332
|
+
},
|
|
2333
|
+
source: { primary: INSTANCE_LIST_REF, resultPath: "data" },
|
|
2334
|
+
visualization: {
|
|
2335
|
+
kind: "metric",
|
|
2336
|
+
measure: "value",
|
|
2337
|
+
measures: [
|
|
2338
|
+
{ key: "value", label: "Instances", dataPath: "value", format: "number" }
|
|
2339
|
+
],
|
|
2340
|
+
table: { caption: "Active workflow count." }
|
|
2120
2341
|
}
|
|
2342
|
+
});
|
|
2343
|
+
var WorkflowCompletedMetricVisualization = defineVisualization({
|
|
2344
|
+
meta: {
|
|
2345
|
+
...META,
|
|
2346
|
+
key: "workflow-system.visualization.completed-work",
|
|
2347
|
+
title: "Completed Work",
|
|
2348
|
+
description: "Completed workflow instances in the current sample.",
|
|
2349
|
+
goal: "Show output against active workload.",
|
|
2350
|
+
context: "Workflow workload comparison."
|
|
2351
|
+
},
|
|
2352
|
+
source: { primary: INSTANCE_LIST_REF, resultPath: "data" },
|
|
2353
|
+
visualization: {
|
|
2354
|
+
kind: "metric",
|
|
2355
|
+
measure: "value",
|
|
2356
|
+
measures: [
|
|
2357
|
+
{ key: "value", label: "Instances", dataPath: "value", format: "number" }
|
|
2358
|
+
],
|
|
2359
|
+
table: { caption: "Completed workflow count." }
|
|
2360
|
+
}
|
|
2361
|
+
});
|
|
2362
|
+
var WorkflowVisualizationSpecs = [
|
|
2363
|
+
WorkflowInstanceStatusVisualization,
|
|
2364
|
+
WorkflowThroughputVisualization,
|
|
2365
|
+
WorkflowActiveMetricVisualization,
|
|
2366
|
+
WorkflowCompletedMetricVisualization
|
|
2121
2367
|
];
|
|
2368
|
+
var WorkflowVisualizationRegistry = new VisualizationRegistry([
|
|
2369
|
+
...WorkflowVisualizationSpecs
|
|
2370
|
+
]);
|
|
2371
|
+
var WorkflowVisualizationRefs = WorkflowVisualizationSpecs.map((spec) => ({
|
|
2372
|
+
key: spec.meta.key,
|
|
2373
|
+
version: spec.meta.version
|
|
2374
|
+
}));
|
|
2375
|
+
|
|
2376
|
+
// src/visualizations/selectors.ts
|
|
2377
|
+
function toDayKey(value) {
|
|
2378
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
2379
|
+
return date.toISOString().slice(0, 10);
|
|
2380
|
+
}
|
|
2381
|
+
function createWorkflowVisualizationSections(instances) {
|
|
2382
|
+
const statusCounts = new Map;
|
|
2383
|
+
const throughput = new Map;
|
|
2384
|
+
let activeCount = 0;
|
|
2385
|
+
let completedCount = 0;
|
|
2386
|
+
for (const instance of instances) {
|
|
2387
|
+
statusCounts.set(instance.status, (statusCounts.get(instance.status) ?? 0) + 1);
|
|
2388
|
+
const day = toDayKey(instance.startedAt);
|
|
2389
|
+
throughput.set(day, (throughput.get(day) ?? 0) + 1);
|
|
2390
|
+
if (instance.status === "PENDING" || instance.status === "IN_PROGRESS") {
|
|
2391
|
+
activeCount += 1;
|
|
2392
|
+
}
|
|
2393
|
+
if (instance.status === "COMPLETED") {
|
|
2394
|
+
completedCount += 1;
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
const primaryItems = [
|
|
2398
|
+
{
|
|
2399
|
+
key: "workflow-status",
|
|
2400
|
+
spec: WorkflowInstanceStatusVisualization,
|
|
2401
|
+
data: {
|
|
2402
|
+
data: Array.from(statusCounts.entries()).map(([status, count]) => ({
|
|
2403
|
+
status,
|
|
2404
|
+
instances: count
|
|
2405
|
+
}))
|
|
2406
|
+
},
|
|
2407
|
+
title: "Instance Status Breakdown",
|
|
2408
|
+
description: "Status mix across workflow instances.",
|
|
2409
|
+
height: 260
|
|
2410
|
+
},
|
|
2411
|
+
{
|
|
2412
|
+
key: "workflow-throughput",
|
|
2413
|
+
spec: WorkflowThroughputVisualization,
|
|
2414
|
+
data: {
|
|
2415
|
+
data: Array.from(throughput.entries()).sort(([left], [right]) => left.localeCompare(right)).map(([day, count]) => ({ day, instances: count }))
|
|
2416
|
+
},
|
|
2417
|
+
title: "Run Throughput",
|
|
2418
|
+
description: "Daily workflow starts from current instances."
|
|
2419
|
+
}
|
|
2420
|
+
];
|
|
2421
|
+
const comparisonItems = [
|
|
2422
|
+
{
|
|
2423
|
+
key: "workflow-active",
|
|
2424
|
+
spec: WorkflowActiveMetricVisualization,
|
|
2425
|
+
data: { data: [{ value: activeCount }] },
|
|
2426
|
+
title: "Active Work",
|
|
2427
|
+
description: "Pending and in-progress workflows.",
|
|
2428
|
+
height: 200
|
|
2429
|
+
},
|
|
2430
|
+
{
|
|
2431
|
+
key: "workflow-completed",
|
|
2432
|
+
spec: WorkflowCompletedMetricVisualization,
|
|
2433
|
+
data: { data: [{ value: completedCount }] },
|
|
2434
|
+
title: "Completed Work",
|
|
2435
|
+
description: "Completed workflows in the current sample.",
|
|
2436
|
+
height: 200
|
|
2437
|
+
}
|
|
2438
|
+
];
|
|
2439
|
+
return {
|
|
2440
|
+
primaryItems,
|
|
2441
|
+
comparisonItems
|
|
2442
|
+
};
|
|
2443
|
+
}
|
|
2444
|
+
// src/ui/renderers/workflow.markdown.ts
|
|
2445
|
+
var workflowDefinitions = WORKFLOW_SYSTEM_DEMO_DEFINITIONS;
|
|
2446
|
+
var workflowInstances = WORKFLOW_SYSTEM_DEMO_INSTANCES;
|
|
2447
|
+
var workflowDefinitionById = new Map(workflowDefinitions.map((definition) => [definition.id, definition]));
|
|
2448
|
+
function formatDate(value) {
|
|
2449
|
+
return new Date(value).toISOString().slice(0, 10);
|
|
2450
|
+
}
|
|
2122
2451
|
var workflowDashboardMarkdownRenderer = {
|
|
2123
2452
|
target: "markdown",
|
|
2124
2453
|
render: async (desc) => {
|
|
2125
2454
|
if (desc.source.type !== "component" || desc.source.componentKey !== "WorkflowDashboard") {
|
|
2126
2455
|
throw new Error("workflowDashboardMarkdownRenderer: not WorkflowDashboard");
|
|
2127
2456
|
}
|
|
2128
|
-
const definitions =
|
|
2129
|
-
const instances =
|
|
2457
|
+
const definitions = workflowDefinitions;
|
|
2458
|
+
const instances = workflowInstances;
|
|
2459
|
+
const visualizations = createWorkflowVisualizationSections(instances);
|
|
2130
2460
|
const activeDefinitions = definitions.filter((d) => d.status === "ACTIVE");
|
|
2131
|
-
const
|
|
2132
|
-
const inProgressInstances = instances.filter((i) => i.status === "IN_PROGRESS");
|
|
2133
|
-
const completedInstances = instances.filter((i) => i.status === "COMPLETED");
|
|
2461
|
+
const awaitingActionInstances = instances.filter((i) => i.status === "PENDING" || i.status === "IN_PROGRESS");
|
|
2134
2462
|
const lines = [
|
|
2135
2463
|
"# Workflow Dashboard",
|
|
2136
2464
|
"",
|
|
2137
|
-
">
|
|
2465
|
+
"> Seeded workflow and approval overview for the sandbox demo.",
|
|
2138
2466
|
"",
|
|
2139
2467
|
"## Summary",
|
|
2140
2468
|
"",
|
|
2141
2469
|
"| Metric | Value |",
|
|
2142
2470
|
"|--------|-------|",
|
|
2143
2471
|
`| Active Workflows | ${activeDefinitions.length} |`,
|
|
2144
|
-
`|
|
|
2145
|
-
`|
|
|
2146
|
-
`|
|
|
2147
|
-
"",
|
|
2148
|
-
"## Active Workflow Definitions",
|
|
2472
|
+
`| Awaiting Action | ${awaitingActionInstances.length} |`,
|
|
2473
|
+
`| Completed | ${instances.filter((i) => i.status === "COMPLETED").length} |`,
|
|
2474
|
+
`| Rejected | ${instances.filter((i) => i.status === "REJECTED").length} |`,
|
|
2149
2475
|
""
|
|
2150
2476
|
];
|
|
2477
|
+
lines.push("## Visualization Overview");
|
|
2478
|
+
lines.push("");
|
|
2479
|
+
for (const item of [
|
|
2480
|
+
...visualizations.primaryItems,
|
|
2481
|
+
...visualizations.comparisonItems
|
|
2482
|
+
]) {
|
|
2483
|
+
lines.push(`- **${item.title}** via \`${item.spec.meta.key}\``);
|
|
2484
|
+
}
|
|
2485
|
+
lines.push("");
|
|
2486
|
+
lines.push("## Active Workflow Definitions");
|
|
2487
|
+
lines.push("");
|
|
2151
2488
|
if (activeDefinitions.length === 0) {
|
|
2152
2489
|
lines.push("_No active workflow definitions._");
|
|
2153
2490
|
} else {
|
|
@@ -2166,8 +2503,9 @@ var workflowDashboardMarkdownRenderer = {
|
|
|
2166
2503
|
lines.push("| Workflow | Requested By | Status | Started |");
|
|
2167
2504
|
lines.push("|----------|--------------|--------|---------|");
|
|
2168
2505
|
for (const inst of instances.slice(0, 10)) {
|
|
2169
|
-
const startedDate =
|
|
2170
|
-
|
|
2506
|
+
const startedDate = formatDate(inst.startedAt);
|
|
2507
|
+
const definitionName = workflowDefinitionById.get(inst.definitionId)?.name ?? inst.definitionId;
|
|
2508
|
+
lines.push(`| ${definitionName} | ${inst.requestedBy} | ${inst.status} | ${startedDate} |`);
|
|
2171
2509
|
}
|
|
2172
2510
|
}
|
|
2173
2511
|
return {
|
|
@@ -2183,7 +2521,7 @@ var workflowDefinitionListMarkdownRenderer = {
|
|
|
2183
2521
|
if (desc.source.type !== "component" || desc.source.componentKey !== "WorkflowDefinitionList") {
|
|
2184
2522
|
throw new Error("workflowDefinitionListMarkdownRenderer: not WorkflowDefinitionList");
|
|
2185
2523
|
}
|
|
2186
|
-
const definitions =
|
|
2524
|
+
const definitions = workflowDefinitions;
|
|
2187
2525
|
const lines = [
|
|
2188
2526
|
"# Workflow Definitions",
|
|
2189
2527
|
"",
|
|
@@ -2198,7 +2536,7 @@ var workflowDefinitionListMarkdownRenderer = {
|
|
|
2198
2536
|
lines.push("### Steps");
|
|
2199
2537
|
lines.push("");
|
|
2200
2538
|
for (const step of def.steps) {
|
|
2201
|
-
lines.push(`${step.
|
|
2539
|
+
lines.push(`${step.stepOrder}. **${step.name}** - Roles: ${step.requiredRoles.join(", ")}`);
|
|
2202
2540
|
}
|
|
2203
2541
|
lines.push("");
|
|
2204
2542
|
}
|
|
@@ -2215,7 +2553,7 @@ var workflowInstanceDetailMarkdownRenderer = {
|
|
|
2215
2553
|
if (desc.source.type !== "component" || desc.source.componentKey !== "WorkflowInstanceDetail") {
|
|
2216
2554
|
throw new Error("workflowInstanceDetailMarkdownRenderer: not WorkflowInstanceDetail");
|
|
2217
2555
|
}
|
|
2218
|
-
const instance =
|
|
2556
|
+
const instance = workflowInstances.find((workflowInstance) => workflowInstance.status === "IN_PROGRESS") ?? workflowInstances[0];
|
|
2219
2557
|
if (!instance) {
|
|
2220
2558
|
return {
|
|
2221
2559
|
mimeType: "text/markdown",
|
|
@@ -2224,14 +2562,14 @@ var workflowInstanceDetailMarkdownRenderer = {
|
|
|
2224
2562
|
No workflow instances available.`
|
|
2225
2563
|
};
|
|
2226
2564
|
}
|
|
2227
|
-
const definition =
|
|
2565
|
+
const definition = workflowDefinitions.find((d) => d.id === instance.definitionId);
|
|
2228
2566
|
const lines = [
|
|
2229
|
-
`# Workflow: ${instance.
|
|
2567
|
+
`# Workflow: ${definition?.name ?? instance.definitionId}`,
|
|
2230
2568
|
"",
|
|
2231
2569
|
`**Instance ID:** ${instance.id}`,
|
|
2232
2570
|
`**Status:** ${instance.status}`,
|
|
2233
2571
|
`**Requested By:** ${instance.requestedBy}`,
|
|
2234
|
-
`**Started:** ${
|
|
2572
|
+
`**Started:** ${formatDate(instance.startedAt)}`,
|
|
2235
2573
|
"",
|
|
2236
2574
|
"## Steps Progress",
|
|
2237
2575
|
""
|
|
@@ -2252,7 +2590,7 @@ No workflow instances available.`
|
|
|
2252
2590
|
lines.push("## Actions");
|
|
2253
2591
|
lines.push("");
|
|
2254
2592
|
lines.push("- **Approve** - Move to next step");
|
|
2255
|
-
lines.push("- **Reject** -
|
|
2593
|
+
lines.push("- **Reject** - End the workflow with a rejection outcome");
|
|
2256
2594
|
lines.push("- **Delegate** - Assign to another approver");
|
|
2257
2595
|
return {
|
|
2258
2596
|
mimeType: "text/markdown",
|
|
@@ -2261,6 +2599,51 @@ No workflow instances available.`
|
|
|
2261
2599
|
};
|
|
2262
2600
|
}
|
|
2263
2601
|
};
|
|
2602
|
+
// src/ui/WorkflowDashboard.visualizations.tsx
|
|
2603
|
+
import {
|
|
2604
|
+
ComparisonView,
|
|
2605
|
+
VisualizationCard,
|
|
2606
|
+
VisualizationGrid
|
|
2607
|
+
} from "@contractspec/lib.design-system";
|
|
2608
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
2609
|
+
"use client";
|
|
2610
|
+
function WorkflowVisualizationOverview({
|
|
2611
|
+
instances
|
|
2612
|
+
}) {
|
|
2613
|
+
const { primaryItems, comparisonItems } = createWorkflowVisualizationSections(instances);
|
|
2614
|
+
return /* @__PURE__ */ jsxDEV("section", {
|
|
2615
|
+
className: "space-y-4",
|
|
2616
|
+
children: [
|
|
2617
|
+
/* @__PURE__ */ jsxDEV("div", {
|
|
2618
|
+
children: [
|
|
2619
|
+
/* @__PURE__ */ jsxDEV("h3", {
|
|
2620
|
+
className: "font-semibold text-lg",
|
|
2621
|
+
children: "Workflow Visualizations"
|
|
2622
|
+
}, undefined, false, undefined, this),
|
|
2623
|
+
/* @__PURE__ */ jsxDEV("p", {
|
|
2624
|
+
className: "text-muted-foreground text-sm",
|
|
2625
|
+
children: "Contract-backed charts for workflow health and throughput."
|
|
2626
|
+
}, undefined, false, undefined, this)
|
|
2627
|
+
]
|
|
2628
|
+
}, undefined, true, undefined, this),
|
|
2629
|
+
/* @__PURE__ */ jsxDEV(VisualizationGrid, {
|
|
2630
|
+
children: primaryItems.map((item) => /* @__PURE__ */ jsxDEV(VisualizationCard, {
|
|
2631
|
+
data: item.data,
|
|
2632
|
+
description: item.description,
|
|
2633
|
+
height: item.height,
|
|
2634
|
+
spec: item.spec,
|
|
2635
|
+
title: item.title
|
|
2636
|
+
}, item.key, false, undefined, this))
|
|
2637
|
+
}, undefined, false, undefined, this),
|
|
2638
|
+
/* @__PURE__ */ jsxDEV(ComparisonView, {
|
|
2639
|
+
description: "Shared comparison surface for active versus completed work.",
|
|
2640
|
+
items: comparisonItems,
|
|
2641
|
+
title: "Workload Comparison"
|
|
2642
|
+
}, undefined, false, undefined, this)
|
|
2643
|
+
]
|
|
2644
|
+
}, undefined, true, undefined, this);
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2264
2647
|
// src/ui/WorkflowDashboard.tsx
|
|
2265
2648
|
import {
|
|
2266
2649
|
Button,
|
|
@@ -2270,7 +2653,7 @@ import {
|
|
|
2270
2653
|
StatCardGroup
|
|
2271
2654
|
} from "@contractspec/lib.design-system";
|
|
2272
2655
|
import { useState as useState2 } from "react";
|
|
2273
|
-
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
2656
|
+
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
2274
2657
|
"use client";
|
|
2275
2658
|
var STATUS_COLORS = {
|
|
2276
2659
|
ACTIVE: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400",
|
|
@@ -2290,151 +2673,148 @@ function WorkflowDashboard() {
|
|
|
2290
2673
|
{ id: "instances", label: "Instances", icon: "\uD83D\uDD04" }
|
|
2291
2674
|
];
|
|
2292
2675
|
if (loading) {
|
|
2293
|
-
return /* @__PURE__ */
|
|
2676
|
+
return /* @__PURE__ */ jsxDEV2(LoaderBlock, {
|
|
2294
2677
|
label: "Loading Workflows..."
|
|
2295
2678
|
}, undefined, false, undefined, this);
|
|
2296
2679
|
}
|
|
2297
2680
|
if (error) {
|
|
2298
|
-
return /* @__PURE__ */
|
|
2681
|
+
return /* @__PURE__ */ jsxDEV2(ErrorState, {
|
|
2299
2682
|
title: "Failed to load Workflows",
|
|
2300
2683
|
description: error.message,
|
|
2301
2684
|
onRetry: refetch,
|
|
2302
2685
|
retryLabel: "Retry"
|
|
2303
2686
|
}, undefined, false, undefined, this);
|
|
2304
2687
|
}
|
|
2305
|
-
return /* @__PURE__ */
|
|
2688
|
+
return /* @__PURE__ */ jsxDEV2("div", {
|
|
2306
2689
|
className: "space-y-6",
|
|
2307
2690
|
children: [
|
|
2308
|
-
/* @__PURE__ */
|
|
2691
|
+
/* @__PURE__ */ jsxDEV2("div", {
|
|
2309
2692
|
className: "flex items-center justify-between",
|
|
2310
2693
|
children: [
|
|
2311
|
-
/* @__PURE__ */
|
|
2694
|
+
/* @__PURE__ */ jsxDEV2("h2", {
|
|
2312
2695
|
className: "font-bold text-2xl",
|
|
2313
2696
|
children: "Workflow System"
|
|
2314
2697
|
}, undefined, false, undefined, this),
|
|
2315
|
-
/* @__PURE__ */
|
|
2316
|
-
onClick: () =>
|
|
2317
|
-
children:
|
|
2318
|
-
|
|
2319
|
-
className: "mr-2",
|
|
2320
|
-
children: "+"
|
|
2321
|
-
}, undefined, false, undefined, this),
|
|
2322
|
-
" New Workflow"
|
|
2323
|
-
]
|
|
2324
|
-
}, undefined, true, undefined, this)
|
|
2698
|
+
/* @__PURE__ */ jsxDEV2(Button, {
|
|
2699
|
+
onClick: () => void refetch(),
|
|
2700
|
+
children: "Refresh"
|
|
2701
|
+
}, undefined, false, undefined, this)
|
|
2325
2702
|
]
|
|
2326
2703
|
}, undefined, true, undefined, this),
|
|
2327
|
-
/* @__PURE__ */
|
|
2704
|
+
/* @__PURE__ */ jsxDEV2(StatCardGroup, {
|
|
2328
2705
|
children: [
|
|
2329
|
-
/* @__PURE__ */
|
|
2706
|
+
/* @__PURE__ */ jsxDEV2(StatCard, {
|
|
2330
2707
|
label: "Workflows",
|
|
2331
2708
|
value: stats.totalDefinitions,
|
|
2332
2709
|
hint: `${stats.activeDefinitions} active`
|
|
2333
2710
|
}, undefined, false, undefined, this),
|
|
2334
|
-
/* @__PURE__ */
|
|
2711
|
+
/* @__PURE__ */ jsxDEV2(StatCard, {
|
|
2335
2712
|
label: "Instances",
|
|
2336
2713
|
value: stats.totalInstances,
|
|
2337
2714
|
hint: "total runs"
|
|
2338
2715
|
}, undefined, false, undefined, this),
|
|
2339
|
-
/* @__PURE__ */
|
|
2340
|
-
label: "
|
|
2341
|
-
value: stats.pendingInstances,
|
|
2342
|
-
hint: "
|
|
2716
|
+
/* @__PURE__ */ jsxDEV2(StatCard, {
|
|
2717
|
+
label: "Awaiting Action",
|
|
2718
|
+
value: stats.pendingInstances + stats.inProgressInstances,
|
|
2719
|
+
hint: "open approvals"
|
|
2343
2720
|
}, undefined, false, undefined, this),
|
|
2344
|
-
/* @__PURE__ */
|
|
2721
|
+
/* @__PURE__ */ jsxDEV2(StatCard, {
|
|
2345
2722
|
label: "Completed",
|
|
2346
2723
|
value: stats.completedInstances,
|
|
2347
2724
|
hint: "finished"
|
|
2348
2725
|
}, undefined, false, undefined, this)
|
|
2349
2726
|
]
|
|
2350
2727
|
}, undefined, true, undefined, this),
|
|
2351
|
-
/* @__PURE__ */
|
|
2728
|
+
/* @__PURE__ */ jsxDEV2(WorkflowVisualizationOverview, {
|
|
2729
|
+
instances
|
|
2730
|
+
}, undefined, false, undefined, this),
|
|
2731
|
+
/* @__PURE__ */ jsxDEV2("nav", {
|
|
2352
2732
|
className: "flex gap-1 rounded-lg bg-muted p-1",
|
|
2353
2733
|
role: "tablist",
|
|
2354
|
-
children: tabs.map((tab) => /* @__PURE__ */
|
|
2734
|
+
children: tabs.map((tab) => /* @__PURE__ */ jsxDEV2(Button, {
|
|
2355
2735
|
type: "button",
|
|
2356
2736
|
role: "tab",
|
|
2357
2737
|
"aria-selected": activeTab === tab.id,
|
|
2358
2738
|
onClick: () => setActiveTab(tab.id),
|
|
2359
2739
|
className: `flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 font-medium text-sm transition-colors ${activeTab === tab.id ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
|
|
2360
2740
|
children: [
|
|
2361
|
-
/* @__PURE__ */
|
|
2741
|
+
/* @__PURE__ */ jsxDEV2("span", {
|
|
2362
2742
|
children: tab.icon
|
|
2363
2743
|
}, undefined, false, undefined, this),
|
|
2364
2744
|
tab.label
|
|
2365
2745
|
]
|
|
2366
2746
|
}, tab.id, true, undefined, this))
|
|
2367
2747
|
}, undefined, false, undefined, this),
|
|
2368
|
-
/* @__PURE__ */
|
|
2748
|
+
/* @__PURE__ */ jsxDEV2("div", {
|
|
2369
2749
|
className: "min-h-[400px]",
|
|
2370
2750
|
role: "tabpanel",
|
|
2371
2751
|
children: [
|
|
2372
|
-
activeTab === "definitions" && /* @__PURE__ */
|
|
2752
|
+
activeTab === "definitions" && /* @__PURE__ */ jsxDEV2("div", {
|
|
2373
2753
|
className: "rounded-lg border border-border",
|
|
2374
|
-
children: /* @__PURE__ */
|
|
2754
|
+
children: /* @__PURE__ */ jsxDEV2("table", {
|
|
2375
2755
|
className: "w-full",
|
|
2376
2756
|
children: [
|
|
2377
|
-
/* @__PURE__ */
|
|
2757
|
+
/* @__PURE__ */ jsxDEV2("thead", {
|
|
2378
2758
|
className: "border-border border-b bg-muted/30",
|
|
2379
|
-
children: /* @__PURE__ */
|
|
2759
|
+
children: /* @__PURE__ */ jsxDEV2("tr", {
|
|
2380
2760
|
children: [
|
|
2381
|
-
/* @__PURE__ */
|
|
2761
|
+
/* @__PURE__ */ jsxDEV2("th", {
|
|
2382
2762
|
className: "px-4 py-3 text-left font-medium text-sm",
|
|
2383
2763
|
children: "Name"
|
|
2384
2764
|
}, undefined, false, undefined, this),
|
|
2385
|
-
/* @__PURE__ */
|
|
2765
|
+
/* @__PURE__ */ jsxDEV2("th", {
|
|
2386
2766
|
className: "px-4 py-3 text-left font-medium text-sm",
|
|
2387
2767
|
children: "Type"
|
|
2388
2768
|
}, undefined, false, undefined, this),
|
|
2389
|
-
/* @__PURE__ */
|
|
2769
|
+
/* @__PURE__ */ jsxDEV2("th", {
|
|
2390
2770
|
className: "px-4 py-3 text-left font-medium text-sm",
|
|
2391
2771
|
children: "Status"
|
|
2392
2772
|
}, undefined, false, undefined, this),
|
|
2393
|
-
/* @__PURE__ */
|
|
2773
|
+
/* @__PURE__ */ jsxDEV2("th", {
|
|
2394
2774
|
className: "px-4 py-3 text-left font-medium text-sm",
|
|
2395
2775
|
children: "Created"
|
|
2396
2776
|
}, undefined, false, undefined, this)
|
|
2397
2777
|
]
|
|
2398
2778
|
}, undefined, true, undefined, this)
|
|
2399
2779
|
}, undefined, false, undefined, this),
|
|
2400
|
-
/* @__PURE__ */
|
|
2780
|
+
/* @__PURE__ */ jsxDEV2("tbody", {
|
|
2401
2781
|
className: "divide-y divide-border",
|
|
2402
2782
|
children: [
|
|
2403
|
-
definitions.map((def) => /* @__PURE__ */
|
|
2783
|
+
definitions.map((def) => /* @__PURE__ */ jsxDEV2("tr", {
|
|
2404
2784
|
className: "hover:bg-muted/50",
|
|
2405
2785
|
children: [
|
|
2406
|
-
/* @__PURE__ */
|
|
2786
|
+
/* @__PURE__ */ jsxDEV2("td", {
|
|
2407
2787
|
className: "px-4 py-3",
|
|
2408
2788
|
children: [
|
|
2409
|
-
/* @__PURE__ */
|
|
2789
|
+
/* @__PURE__ */ jsxDEV2("div", {
|
|
2410
2790
|
className: "font-medium",
|
|
2411
2791
|
children: def.name
|
|
2412
2792
|
}, undefined, false, undefined, this),
|
|
2413
|
-
/* @__PURE__ */
|
|
2793
|
+
/* @__PURE__ */ jsxDEV2("div", {
|
|
2414
2794
|
className: "text-muted-foreground text-sm",
|
|
2415
2795
|
children: def.description
|
|
2416
2796
|
}, undefined, false, undefined, this)
|
|
2417
2797
|
]
|
|
2418
2798
|
}, undefined, true, undefined, this),
|
|
2419
|
-
/* @__PURE__ */
|
|
2799
|
+
/* @__PURE__ */ jsxDEV2("td", {
|
|
2420
2800
|
className: "px-4 py-3 font-mono text-sm",
|
|
2421
2801
|
children: def.type
|
|
2422
2802
|
}, undefined, false, undefined, this),
|
|
2423
|
-
/* @__PURE__ */
|
|
2803
|
+
/* @__PURE__ */ jsxDEV2("td", {
|
|
2424
2804
|
className: "px-4 py-3",
|
|
2425
|
-
children: /* @__PURE__ */
|
|
2805
|
+
children: /* @__PURE__ */ jsxDEV2("span", {
|
|
2426
2806
|
className: `inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${STATUS_COLORS[def.status] ?? ""}`,
|
|
2427
2807
|
children: def.status
|
|
2428
2808
|
}, undefined, false, undefined, this)
|
|
2429
2809
|
}, undefined, false, undefined, this),
|
|
2430
|
-
/* @__PURE__ */
|
|
2810
|
+
/* @__PURE__ */ jsxDEV2("td", {
|
|
2431
2811
|
className: "px-4 py-3 text-muted-foreground text-sm",
|
|
2432
2812
|
children: def.createdAt.toLocaleDateString()
|
|
2433
2813
|
}, undefined, false, undefined, this)
|
|
2434
2814
|
]
|
|
2435
2815
|
}, def.id, true, undefined, this)),
|
|
2436
|
-
definitions.length === 0 && /* @__PURE__ */
|
|
2437
|
-
children: /* @__PURE__ */
|
|
2816
|
+
definitions.length === 0 && /* @__PURE__ */ jsxDEV2("tr", {
|
|
2817
|
+
children: /* @__PURE__ */ jsxDEV2("td", {
|
|
2438
2818
|
colSpan: 4,
|
|
2439
2819
|
className: "px-4 py-8 text-center text-muted-foreground",
|
|
2440
2820
|
children: "No workflow definitions found"
|
|
@@ -2445,63 +2825,63 @@ function WorkflowDashboard() {
|
|
|
2445
2825
|
]
|
|
2446
2826
|
}, undefined, true, undefined, this)
|
|
2447
2827
|
}, undefined, false, undefined, this),
|
|
2448
|
-
activeTab === "instances" && /* @__PURE__ */
|
|
2828
|
+
activeTab === "instances" && /* @__PURE__ */ jsxDEV2("div", {
|
|
2449
2829
|
className: "rounded-lg border border-border",
|
|
2450
|
-
children: /* @__PURE__ */
|
|
2830
|
+
children: /* @__PURE__ */ jsxDEV2("table", {
|
|
2451
2831
|
className: "w-full",
|
|
2452
2832
|
children: [
|
|
2453
|
-
/* @__PURE__ */
|
|
2833
|
+
/* @__PURE__ */ jsxDEV2("thead", {
|
|
2454
2834
|
className: "border-border border-b bg-muted/30",
|
|
2455
|
-
children: /* @__PURE__ */
|
|
2835
|
+
children: /* @__PURE__ */ jsxDEV2("tr", {
|
|
2456
2836
|
children: [
|
|
2457
|
-
/* @__PURE__ */
|
|
2837
|
+
/* @__PURE__ */ jsxDEV2("th", {
|
|
2458
2838
|
className: "px-4 py-3 text-left font-medium text-sm",
|
|
2459
2839
|
children: "Instance ID"
|
|
2460
2840
|
}, undefined, false, undefined, this),
|
|
2461
|
-
/* @__PURE__ */
|
|
2841
|
+
/* @__PURE__ */ jsxDEV2("th", {
|
|
2462
2842
|
className: "px-4 py-3 text-left font-medium text-sm",
|
|
2463
2843
|
children: "Status"
|
|
2464
2844
|
}, undefined, false, undefined, this),
|
|
2465
|
-
/* @__PURE__ */
|
|
2845
|
+
/* @__PURE__ */ jsxDEV2("th", {
|
|
2466
2846
|
className: "px-4 py-3 text-left font-medium text-sm",
|
|
2467
2847
|
children: "Requested By"
|
|
2468
2848
|
}, undefined, false, undefined, this),
|
|
2469
|
-
/* @__PURE__ */
|
|
2849
|
+
/* @__PURE__ */ jsxDEV2("th", {
|
|
2470
2850
|
className: "px-4 py-3 text-left font-medium text-sm",
|
|
2471
2851
|
children: "Started"
|
|
2472
2852
|
}, undefined, false, undefined, this)
|
|
2473
2853
|
]
|
|
2474
2854
|
}, undefined, true, undefined, this)
|
|
2475
2855
|
}, undefined, false, undefined, this),
|
|
2476
|
-
/* @__PURE__ */
|
|
2856
|
+
/* @__PURE__ */ jsxDEV2("tbody", {
|
|
2477
2857
|
className: "divide-y divide-border",
|
|
2478
2858
|
children: [
|
|
2479
|
-
instances.map((inst) => /* @__PURE__ */
|
|
2859
|
+
instances.map((inst) => /* @__PURE__ */ jsxDEV2("tr", {
|
|
2480
2860
|
className: "hover:bg-muted/50",
|
|
2481
2861
|
children: [
|
|
2482
|
-
/* @__PURE__ */
|
|
2862
|
+
/* @__PURE__ */ jsxDEV2("td", {
|
|
2483
2863
|
className: "px-4 py-3 font-mono text-sm",
|
|
2484
2864
|
children: inst.id
|
|
2485
2865
|
}, undefined, false, undefined, this),
|
|
2486
|
-
/* @__PURE__ */
|
|
2866
|
+
/* @__PURE__ */ jsxDEV2("td", {
|
|
2487
2867
|
className: "px-4 py-3",
|
|
2488
|
-
children: /* @__PURE__ */
|
|
2868
|
+
children: /* @__PURE__ */ jsxDEV2("span", {
|
|
2489
2869
|
className: `inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${STATUS_COLORS[inst.status] ?? ""}`,
|
|
2490
2870
|
children: inst.status
|
|
2491
2871
|
}, undefined, false, undefined, this)
|
|
2492
2872
|
}, undefined, false, undefined, this),
|
|
2493
|
-
/* @__PURE__ */
|
|
2873
|
+
/* @__PURE__ */ jsxDEV2("td", {
|
|
2494
2874
|
className: "px-4 py-3 text-sm",
|
|
2495
2875
|
children: inst.requestedBy
|
|
2496
2876
|
}, undefined, false, undefined, this),
|
|
2497
|
-
/* @__PURE__ */
|
|
2877
|
+
/* @__PURE__ */ jsxDEV2("td", {
|
|
2498
2878
|
className: "px-4 py-3 text-muted-foreground text-sm",
|
|
2499
2879
|
children: inst.startedAt.toLocaleDateString()
|
|
2500
2880
|
}, undefined, false, undefined, this)
|
|
2501
2881
|
]
|
|
2502
2882
|
}, inst.id, true, undefined, this)),
|
|
2503
|
-
instances.length === 0 && /* @__PURE__ */
|
|
2504
|
-
children: /* @__PURE__ */
|
|
2883
|
+
instances.length === 0 && /* @__PURE__ */ jsxDEV2("tr", {
|
|
2884
|
+
children: /* @__PURE__ */ jsxDEV2("td", {
|
|
2505
2885
|
colSpan: 4,
|
|
2506
2886
|
className: "px-4 py-8 text-center text-muted-foreground",
|
|
2507
2887
|
children: "No workflow instances found"
|
|
@@ -3026,6 +3406,7 @@ var WorkflowSystemFeature = defineFeature({
|
|
|
3026
3406
|
targets: ["react", "markdown"]
|
|
3027
3407
|
}
|
|
3028
3408
|
],
|
|
3409
|
+
visualizations: WorkflowVisualizationRefs,
|
|
3029
3410
|
capabilities: {
|
|
3030
3411
|
requires: [
|
|
3031
3412
|
{ key: "identity", version: "1.0.0" },
|
|
@@ -3060,11 +3441,16 @@ export {
|
|
|
3060
3441
|
workflowDashboardMarkdownRenderer,
|
|
3061
3442
|
useWorkflowList,
|
|
3062
3443
|
mockDataStore,
|
|
3444
|
+
createWorkflowVisualizationSections,
|
|
3063
3445
|
createWorkflowHandlers,
|
|
3064
3446
|
createStateMachineEngine,
|
|
3065
3447
|
createInitialState,
|
|
3066
3448
|
buildStateMachineDefinition,
|
|
3449
|
+
WorkflowVisualizationSpecs,
|
|
3450
|
+
WorkflowVisualizationRegistry,
|
|
3451
|
+
WorkflowVisualizationRefs,
|
|
3067
3452
|
WorkflowUpdatedEvent,
|
|
3453
|
+
WorkflowThroughputVisualization,
|
|
3068
3454
|
WorkflowSystemPresentations,
|
|
3069
3455
|
WorkflowSystemFeature,
|
|
3070
3456
|
WorkflowStepModel,
|
|
@@ -3072,12 +3458,15 @@ export {
|
|
|
3072
3458
|
WorkflowPublishedEvent,
|
|
3073
3459
|
WorkflowMetricsPresentation,
|
|
3074
3460
|
WorkflowListPresentation,
|
|
3461
|
+
WorkflowInstanceStatusVisualization,
|
|
3075
3462
|
WorkflowInstanceModel,
|
|
3076
3463
|
WorkflowDetailPresentation,
|
|
3077
3464
|
WorkflowDesignerPresentation,
|
|
3078
3465
|
WorkflowDefinitionModel,
|
|
3079
3466
|
WorkflowDashboard,
|
|
3080
3467
|
WorkflowCreatedEvent,
|
|
3468
|
+
WorkflowCompletedMetricVisualization,
|
|
3469
|
+
WorkflowActiveMetricVisualization,
|
|
3081
3470
|
UpdateWorkflowInputModel,
|
|
3082
3471
|
UpdateWorkflowContract,
|
|
3083
3472
|
TriggerTypeEnum,
|