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