diff --git a/src/components/workflows/WorkflowCard.vue b/src/components/workflows/WorkflowCard.vue index 61f2beb88232deac2326c7a36efedb60c38f3074..50c8ddc00ff1848343aaed427b788496ee3dcabe 100644 --- a/src/components/workflows/WorkflowCard.vue +++ b/src/components/workflows/WorkflowCard.vue @@ -42,6 +42,9 @@ onMounted(() => { workflowId: workflow.workflow_id, versionId: latestVersion?.git_commit_hash, }, + query: { + workflowModeId: latestVersion?.modes?.[0] ?? undefined, + }, }" >{{ props.workflow.name }} </router-link> @@ -77,6 +80,9 @@ onMounted(() => { workflowId: workflow.workflow_id, versionId: latestVersion?.git_commit_hash, }, + query: { + workflowModeId: latestVersion?.modes?.[0] ?? undefined, + }, }" class="btn btn-outline-success" role="button" diff --git a/src/components/workflows/WorkflowWithVersionsCard.vue b/src/components/workflows/WorkflowWithVersionsCard.vue index 6d814c88dc91846178375cc42d836c2d91257363..9dab5779c0c0d2e2130c687f41b4730b1ea74aad 100644 --- a/src/components/workflows/WorkflowWithVersionsCard.vue +++ b/src/components/workflows/WorkflowWithVersionsCard.vue @@ -178,6 +178,9 @@ onMounted(() => { workflowId: props.workflow.workflow_id, versionId: version.git_commit_hash, }, + query: { + workflowModeId: version.modes?.[0] ?? undefined, + }, }" >View </router-link> @@ -216,6 +219,7 @@ td > img { .add-icon-hover:hover { color: var(--bs-success) !important; } + .text-align-center { text-align: center; } diff --git a/src/router/index.ts b/src/router/index.ts index cd6d25646eb4b2ff556af43ba1b667106b4e973a..3d0e6ef4d85a16d1ecaee96f5b674648cdc9dde1 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -67,7 +67,11 @@ const router = createRouter({ path: "workflows/:workflowId", name: "workflow", component: () => import("../views/workflows/WorkflowView.vue"), - props: true, + props: (route) => ({ + versionId: route.params.versionId ?? undefined, + workflowId: route.params.workflowId, + workflowModeId: route.query.workflowModeId ?? undefined, + }), children: [ { path: "version/:versionId", @@ -78,6 +82,7 @@ const router = createRouter({ versionId: route.params.versionId, workflowId: route.params.workflowId, activeTab: route.query.tab ?? "description", + workflowModeId: route.query.workflowModeId ?? undefined, }), }, { @@ -85,7 +90,11 @@ const router = createRouter({ name: "workflow-start", component: () => import("../views/workflows/StartWorkflowView.vue"), - props: true, + props: (route) => ({ + versionId: route.params.versionId, + workflowId: route.params.workflowId, + workflowModeId: route.query.workflowModeId ?? undefined, + }), }, ], }, diff --git a/src/views/workflows/StartWorkflowView.vue b/src/views/workflows/StartWorkflowView.vue index 6a6dba2545d4b2bbb27f325e2c5d124932f2ec46..d5eeae04f238353553951c7748d830057ea7a96e 100644 --- a/src/views/workflows/StartWorkflowView.vue +++ b/src/views/workflows/StartWorkflowView.vue @@ -7,13 +7,14 @@ import { WorkflowExecutionService, WorkflowVersionService, } from "@/client/workflow"; -import { onMounted, ref, reactive } from "vue"; +import { onMounted, ref, reactive, watch } from "vue"; import { useRouter } from "vue-router"; import { Toast } from "bootstrap"; const props = defineProps<{ versionId: string; workflowId: string; + workflowModeId?: string; }>(); const parameterSchema = ref(undefined); @@ -33,26 +34,29 @@ const versionState = reactive<{ workflowExecutionError: undefined, }); -function downloadVersion() { - WorkflowVersionService.workflowVersionGetWorkflowVersion( - props.versionId, - props.workflowId, - ) - .then((version) => { - versionState.workflowVersion = version; - return version; - }) - .then(downloadVersionFiles); -} +watch( + () => props.workflowModeId, + (newModeId, oldModeId) => { + if (newModeId !== oldModeId) { + downloadVersionFiles(); + } + }, +); -function downloadVersionFiles(version: WorkflowVersion) { +function downloadVersionFiles() { + versionState.loading = true; WorkflowVersionService.workflowVersionDownloadWorkflowDocumentation( - version.workflow_id, - version.git_commit_hash, + props.workflowId, + props.versionId, DocumentationEnum.PARAMETER_SCHEMA, - ).then((markdown) => { - parameterSchema.value = markdown; - }); + props.workflowModeId, + ) + .then((markdown) => { + parameterSchema.value = markdown; + }) + .finally(() => { + versionState.loading = false; + }); } function startWorkflow( @@ -90,7 +94,7 @@ function startWorkflow( onMounted(() => { errorToast = new Toast("#workflowExecutionViewErrorToast"); - downloadVersion(); + downloadVersionFiles(); }); </script> diff --git a/src/views/workflows/WorkflowVersionView.vue b/src/views/workflows/WorkflowVersionView.vue index 42de6e1fddad6a8e27da3cfbb69136abbc90b946..e815705d6df515463f850a4a7066a44f94978b6e 100644 --- a/src/views/workflows/WorkflowVersionView.vue +++ b/src/views/workflows/WorkflowVersionView.vue @@ -7,6 +7,7 @@ import WorkflowDocumentationTabs from "@/components/workflows/WorkflowDocumentat const props = defineProps<{ versionId: string; workflowId: string; + workflowModeId?: string; }>(); const versionState = reactive<{ @@ -41,6 +42,22 @@ watch( }, ); +watch( + () => props.workflowModeId, + (newModeId, oldModeId) => { + if (newModeId !== oldModeId) { + WorkflowVersionService.workflowVersionDownloadWorkflowDocumentation( + props.workflowId, + props.versionId, + DocumentationEnum.PARAMETER_SCHEMA, + newModeId, + ).then((schema) => { + versionState.parameterSchema = schema; + }); + } + }, +); + function updateVersion(versionId: string, workflowId: string) { versionState.loading = true; versionState.fileLoading = true; @@ -84,8 +101,9 @@ function downloadVersionFiles(version: WorkflowVersion) { version.workflow_id, version.git_commit_hash, DocumentationEnum.PARAMETER_SCHEMA, - ).then((markdown) => { - versionState.parameterSchema = markdown; + props.workflowModeId, + ).then((schema) => { + versionState.parameterSchema = schema; }); const usagePromise = WorkflowVersionService.workflowVersionDownloadWorkflowDocumentation( diff --git a/src/views/workflows/WorkflowView.vue b/src/views/workflows/WorkflowView.vue index 05e3ee2cfe6507f6fc40e63604ab74e8071ba375..ddf9b3fe57e738f9e73d584a61ba8d091b10effd 100644 --- a/src/views/workflows/WorkflowView.vue +++ b/src/views/workflows/WorkflowView.vue @@ -4,10 +4,12 @@ import type { WorkflowOut, WorkflowStatistic, WorkflowVersion, + WorkflowModeOut, } from "@/client/workflow"; import WorkflowStatisticsChart from "@/components/workflows/WorkflowStatisticsChart.vue"; import { Status, + WorkflowModeService, WorkflowService, WorkflowVersionService, } from "@/client/workflow"; @@ -27,6 +29,7 @@ const userRepository = useAuthStore(); const props = defineProps<{ workflowId: string; versionId?: string; + workflowModeId?: string; }>(); // Constants @@ -42,12 +45,16 @@ const workflowState = reactive<{ activeVersionId: string; initialOpen: boolean; stats: WorkflowStatistic[]; + modeMapping: Record<string, WorkflowModeOut>; + activeModeId?: string; }>({ loading: true, workflow: undefined, - activeVersionId: "", + activeVersionId: props.versionId ?? "", initialOpen: true, stats: [], + modeMapping: {}, + activeModeId: props.workflowModeId, }); // Watchers @@ -68,6 +75,15 @@ watch( }, ); +watch( + () => props.workflowModeId, + (newModeId) => { + if (newModeId) { + workflowState.activeModeId = newModeId; + } + }, +); + watch( () => workflowState.activeVersionId, (newVersionId, oldVersionId) => { @@ -76,10 +92,38 @@ watch( newVersionId !== oldVersionId && route.name !== "workflow-start" ) { + // If mode does not exist in other version, select another mode + if ( + activeVersionModeIds.value.length > 0 && + activeVersionModeIds.value.findIndex( + (modeId) => modeId === workflowState.activeModeId, + ) == -1 + ) { + workflowState.activeModeId = activeVersionModeIds.value[0]; + } else if (activeVersionModeIds.value.length == 0) { + // If new version does not has any modes, then set mode id to none + workflowState.activeModeId = undefined; + } router.push({ name: "workflow-version", params: { versionId: newVersionId }, - query: { tab: route.query.tab }, + query: { + tab: route.query.tab, + workflowModeId: workflowState.activeModeId, + }, + }); + } + }, +); + +watch( + () => workflowState.activeModeId, + (newModeId, oldModeId) => { + if (newModeId != oldModeId) { + router.push({ + name: route.name ?? undefined, + params: { versionId: workflowState.activeVersionId }, + query: { tab: route.query.tab, workflowModeId: newModeId }, }); } }, @@ -101,6 +145,10 @@ const activeVersion = computed<WorkflowVersion | undefined>( ), ); +const activeVersionModeIds = computed<string[]>( + () => activeVersion.value?.modes ?? [], +); + const activeVersionString = computed<string>( () => activeVersion.value?.version ?? "", ); @@ -144,6 +192,35 @@ function updateWorkflow(workflowId: string) { } else { workflowState.activeVersionId = route.params.versionId as string; } + workflowState.activeModeId = activeVersionModeIds.value[0] ?? undefined; + return workflow; + }) + .then((workflow) => + workflow.versions.map((version) => version.modes).flat(), + ) + .then((modeIds) => + modeIds + .filter((modeId) => modeId != null) // filter null modes + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + .filter((modeId) => !workflowState.modeMapping[modeId!]) + .filter( + // filter unique workflow versions + (modeId, index, array) => + array.findIndex((val) => val === modeId) === index, + ), + ) + .then((modeIds) => + Promise.all( + modeIds.map((modeId) => + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + WorkflowModeService.workflowModeGetWorkflowMode(modeId!), + ), + ), + ) + .then((modes) => { + for (const mode of modes) { + workflowState.modeMapping[mode.mode_id] = mode; + } }) .catch(() => { workflowState.workflow = undefined; @@ -214,7 +291,24 @@ onMounted(() => { alt="Workflow icon" /> </div> - <p class="fs-4 mb-5 mt-3">{{ workflowState.workflow.short_description }}</p> + <p class="fs-4 mt-3">{{ workflowState.workflow.short_description }}</p> + <div + v-if="activeVersionModeIds.length > 0" + class="row align-items-center mb-3 fs-5" + > + <label class="col-sm-1 col-form-label">Mode:</label> + <div class="col-sm-11"> + <select class="form-select w-fit" v-model="workflowState.activeModeId"> + <option + v-for="modeId of activeVersionModeIds" + :key="modeId" + :value="modeId" + > + {{ workflowState.modeMapping[modeId].name }} + </option> + </select> + </div> + </div> <template v-if="route.name !== 'workflow-start'"> <div v-if="!versionLaunchable" @@ -256,6 +350,9 @@ onMounted(() => { versionId: props.versionId, workflowId: props.workflowId, }, + query: { + workflowModeId: workflowState.activeModeId, + }, }" > <font-awesome-icon icon="fa-solid fa-rocket" class="me-2" />