From 6b6bcf5585fdbcb069805523ddb6c30c9f9c6a30 Mon Sep 17 00:00:00 2001 From: Ahmed Awan Date: Thu, 11 Apr 2024 13:02:36 -0500 Subject: [PATCH 01/41] Add an invocation graph view to the invocation summary This graph view utilizes the same workflow editor canvas, and instead of showing step details on each step node, it shows the job states for the steps. Clicking on a step expands the details for the invocation step. The `useInvocationGraph` composable loads the graph based on the original workflow id and invocation object; and the step info is loaded via the `step_jobs_summary` api route. --- .../src/components/Workflow/Editor/Node.vue | 59 +++- .../components/Workflow/Editor/NodeInput.vue | 38 ++- .../Workflow/Editor/NodeInvocationText.vue | 39 +++ .../components/Workflow/Editor/NodeOutput.vue | 3 +- .../Workflow/Editor/WorkflowGraph.vue | 16 +- .../Invocation/Graph/InvocationGraph.vue | 300 ++++++++++++++++++ .../components/Workflow/InvocationsList.vue | 12 +- .../Workflow/{constants.js => constants.ts} | 2 +- .../WorkflowInvocationState/JobStep.vue | 62 +++- .../WorkflowInvocationState.vue | 5 + .../WorkflowInvocationStep.vue | 68 +++- .../WorkflowInvocationSummary.vue | 142 +++++---- client/src/composables/useInvocationGraph.ts | 271 ++++++++++++++++ client/src/style/scss/base.scss | 12 + 14 files changed, 937 insertions(+), 92 deletions(-) create mode 100644 client/src/components/Workflow/Editor/NodeInvocationText.vue create mode 100644 client/src/components/Workflow/Invocation/Graph/InvocationGraph.vue rename client/src/components/Workflow/{constants.js => constants.ts} (68%) create mode 100644 client/src/composables/useInvocationGraph.ts diff --git a/client/src/components/Workflow/Editor/Node.vue b/client/src/components/Workflow/Editor/Node.vue index 3b29ed433f27..144191a60d7e 100644 --- a/client/src/components/Workflow/Editor/Node.vue +++ b/client/src/components/Workflow/Editor/Node.vue @@ -12,8 +12,10 @@ :disabled="readonly" @move="onMoveTo" @pan-by="onPanBy"> +
@@ -74,6 +76,12 @@ >{{ step.id + 1 }}: {{ title }} + + +
{{ errors }} -
+ +
-
+
+ , default: null }, name: { type: String as PropType, default: null }, - step: { type: Object as PropType, required: true }, + step: { type: Object as PropType, required: true }, datatypesMapper: { type: DatatypesMapperModel, required: true }, activeNodeId: { type: null as unknown as PropType, @@ -164,6 +185,7 @@ const props = defineProps({ scroll: { type: Object as PropType, required: true }, scale: { type: Number, default: 1 }, highlight: { type: Boolean, default: false }, + isInvocation: { type: Boolean, default: false }, readonly: { type: Boolean, default: false }, }); @@ -219,6 +241,23 @@ const style = computed(() => { return { top: props.step.position!.top + "px", left: props.step.position!.left + "px" }; }); const errors = computed(() => props.step.errors || stateStore.getStepLoadingState(props.id)?.error); +const headerClass = computed(() => { + let cls; + if (props.isInvocation) { + cls = "cursor-pointer"; + if (invocationStep.value.headerClass) { + cls += ` ${invocationStep.value.headerClass}`; + } else { + cls += " node-header"; + } + } else { + cls = "node-header"; + if (!props.readonly && !props.isInvocation) { + cls += " cursor-move"; + } + } + return cls; +}); const inputs = computed(() => { const connections = connectionStore.getConnectionsForStep(props.id); const extraStepInputs = stepStore.getStepExtraInputs(props.id); @@ -247,6 +286,7 @@ const invalidOutputs = computed(() => { return { name, optional: false, datatypes: [], valid: false }; }); }); +const invocationStep = computed(() => props.step as GraphStep); const outputs = computed(() => { return [...props.step.outputs, ...invalidOutputs.value]; }); @@ -317,12 +357,21 @@ function makeActive() { } .node-header { - cursor: move; background: $brand-primary; color: $white; + &.cursor-move { + cursor: move; + } } .node-body { + .invocation-node-output { + position: absolute; + right: 0; + top: 0; + width: 100%; + height: 100%; + } .rule { height: 0; border: none; diff --git a/client/src/components/Workflow/Editor/NodeInput.vue b/client/src/components/Workflow/Editor/NodeInput.vue index b4ab0ac1962b..d3b26a85a40d 100644 --- a/client/src/components/Workflow/Editor/NodeInput.vue +++ b/client/src/components/Workflow/Editor/NodeInput.vue @@ -70,6 +70,10 @@ const props = defineProps({ type: Boolean, default: false, }, + blank: { + type: Boolean, + default: false, + }, }); onBeforeUnmount(() => { @@ -145,7 +149,7 @@ const label = computed(() => props.input.label || props.input.name); const hasConnections = computed(() => connections.value.length > 0); const rowClass = computed(() => { const classes = ["form-row", "dataRow", "input-data-row"]; - if (props.input?.valid === false) { + if (!props.blank && props.input?.valid === false) { classes.push("form-row-error"); } return classes; @@ -234,21 +238,23 @@ watch(
- - {{ label }} - - * + + + {{ label }} + + * +
diff --git a/client/src/components/Workflow/Editor/NodeInvocationText.vue b/client/src/components/Workflow/Editor/NodeInvocationText.vue new file mode 100644 index 000000000000..acc3dd31f555 --- /dev/null +++ b/client/src/components/Workflow/Editor/NodeInvocationText.vue @@ -0,0 +1,39 @@ + + diff --git a/client/src/components/Workflow/Editor/NodeOutput.vue b/client/src/components/Workflow/Editor/NodeOutput.vue index cac8dba4a5ac..9e16d7fb236f 100644 --- a/client/src/components/Workflow/Editor/NodeOutput.vue +++ b/client/src/components/Workflow/Editor/NodeOutput.vue @@ -51,6 +51,7 @@ const props = defineProps<{ datatypesMapper: DatatypesMapperModel; parentNode: HTMLElement | null; readonly: boolean; + blank: boolean; }>(); const emit = defineEmits(["pan-by", "stopDragging", "onDragConnector"]); @@ -341,7 +342,7 @@ const removeTagsAction = computed(() => {