Skip to content
Snippets Groups Projects
Verified Commit 63f75925 authored by Daniel Göbel's avatar Daniel Göbel
Browse files

Add page for starting a workflow

#39
parent 65e9b158
No related branches found
No related tags found
2 merge requests!84Remove development branch,!41Resolve "Create Start Workflow Execution Page"
Pipeline #27657 passed
......@@ -3,6 +3,10 @@ body {
background: #181818;
}
.fs-0 {
font-size: 3.5rem;
}
.top-toast {
top: 4rem;
}
......
......@@ -14,7 +14,6 @@ import { Toast } from "bootstrap";
const props = defineProps({
schema: {
type: Object,
required: true,
},
workflowVersionId: {
type: String,
......@@ -66,7 +65,7 @@ const formState = reactive<{
// Computed Properties
// =============================================================================
const parameterGroups = computed<Record<string, never>>(
() => props.schema["definitions"]
() => props.schema?.["definitions"]
);
// Create a list with the names of all parameter groups
......@@ -94,7 +93,9 @@ const navParameterGroups = computed<ParameterGroup[]>(() =>
watch(
() => props.schema,
(newValue) => {
updateSchema(newValue);
if (newValue) {
updateSchema(newValue);
}
}
);
......@@ -102,7 +103,7 @@ watch(
// =============================================================================
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/ban-ts-comment, @typescript-eslint/no-unused-vars */
function updateSchema(schema: Record<string, any>) {
validateSchema = schemaCompiler.compile(props.schema);
validateSchema = schemaCompiler.compile(schema);
const b = Object.keys(schema["definitions"]).map((groupName) => [
groupName,
Object.fromEntries(
......@@ -122,6 +123,7 @@ function updateSchema(schema: Record<string, any>) {
function startWorkflow() {
formState.validated = true;
formState.errorType = undefined;
if (launchForm.value?.checkValidity()) {
const realInput = Object.values(formState.formInput).reduce((acc, val) => {
return { ...acc, ...val };
......@@ -132,7 +134,6 @@ function startWorkflow() {
console.error(validateSchema.errors);
errorToast?.show();
} else {
formState.errorType = undefined;
formState.loading = true;
WorkflowExecutionService.workflowExecutionStartWorkflow({
workflow_version_id: props.workflowVersionId,
......@@ -154,13 +155,16 @@ function startWorkflow() {
formState.loading = false;
});
}
} else {
formState.errorType = "form";
errorToast?.show();
}
}
// Lifecycle Events
// =============================================================================
onMounted(() => {
updateSchema(props.schema);
if (props.schema) updateSchema(props.schema);
errorToast = new Toast("#workflowExecutionErrorToast");
});
</script>
......@@ -175,17 +179,22 @@ onMounted(() => {
data-bs-autohide="true"
id="workflowExecutionErrorToast"
>
<div class="d-flex p-2">
<div v-if="formState.errorType === 'limit'" class="toast-body">
You have too many active workflow executions to start a new one
</div>
<div v-else>
There was an error with starting the workflow execution. Look in the
console for more information
<div class="d-flex p-2 justify-content-between align-items-center">
<div class="toast-body">
<template v-if="formState.errorType === 'limit'">
You have too many active workflow executions to start a new one.
</template>
<template v-else-if="formState.errorType === 'form'">
Some inputs are not valid.
</template>
<template v-else>
There was an error with starting the workflow execution. Look in the
console for more information.
</template>
</div>
<button
type="button"
class="btn-close btn-close-white m-auto"
class="btn-close btn-close-white"
data-bs-dismiss="toast"
aria-label="Close"
></button>
......@@ -194,10 +203,13 @@ onMounted(() => {
</div>
<div class="row mb-5 align-items-start">
<form
v-if="props.schema"
class="col-9"
id="launchWorkflowForm"
ref="launchForm"
:class="{ 'was-validated': formState.validated }"
@submit.prevent="startWorkflow"
novalidate
>
<div class="card bg-dark mb-3">
<h2 class="card-header" id="pipelineGeneralOptions">
......@@ -218,7 +230,7 @@ onMounted(() => {
</span>
<textarea
class="form-control"
rows="2"
rows="1"
v-model="formState.pipelineNotes"
/>
</div>
......@@ -260,6 +272,27 @@ onMounted(() => {
/>
</template>
</form>
<!-- Loading card -->
<div v-else class="col-9">
<div class="card bg-dark mb-3">
<h2 class="card-header placeholder-glow">
<span class="placeholder col-6"></span>
</h2>
<div class="card-body">
<h5 class="card-title placeholder-glow">
<span class="placeholder col-5"> </span>
</h5>
<template v-for="n in 4" :key="n">
<div class="placeholder-glow fs-5">
<span class="placeholder w-100"> </span>
</div>
<div class="mb-3 placeholder-glow">
<span class="placeholder col-3"> </span>
</div>
</template>
</div>
</div>
</div>
<div
class="col-3 sticky-top bg-dark rounded-1 px-0"
style="top: 70px !important; max-height: calc(100vh - 150px)"
......@@ -268,9 +301,8 @@ onMounted(() => {
<button
type="submit"
form="launchWorkflowForm"
@click.prevent="startWorkflow"
class="btn btn-success w-50 mx-2"
:disabled="formState.loading"
:disabled="formState.loading || !props.schema"
>
<font-awesome-icon icon="fa-solid fa-rocket" class="me-2" />
Launch
......@@ -286,7 +318,7 @@ onMounted(() => {
</router-link>
</div>
<nav class="h-100">
<nav class="nav">
<nav v-if="props.schema" class="nav">
<ul class="ps-0">
<li class="nav-link">
<a href="#pipelineGeneralOptions"
......@@ -309,6 +341,14 @@ onMounted(() => {
</li>
</ul>
</nav>
<!-- Loading nav links -->
<div v-else class="placeholder-glow ps-3 pt-3">
<span
v-for="n in 5"
:key="n"
class="placeholder col-8 mt-2 mb-3"
></span>
</div>
</nav>
</div>
</div>
......
......@@ -46,7 +46,7 @@ const router = createRouter({
meta: { requiresReviewerRole: true },
},
{
path: "workflows/:workflowId/",
path: "workflows/:workflowId",
name: "workflow",
component: () => import("../views/workflows/WorkflowView.vue"),
props: true,
......@@ -62,6 +62,13 @@ const router = createRouter({
activeTab: route.query.tab ?? "description",
}),
},
{
path: "version/:versionId/start",
name: "workflow-start",
component: () =>
import("../views/workflows/StartWorkflowView.vue"),
props: true,
},
],
},
],
......
<script setup lang="ts">
import ParameterSchemaFormComponent from "@/components/parameter-schema/ParameterSchemaFormComponent.vue";
import type { WorkflowVersionFull } from "@/client/workflow";
import { WorkflowVersionService } from "@/client/workflow";
import axios from "axios";
import { onMounted, ref, reactive } from "vue";
import type { JSONSchemaType } from "ajv";
const props = defineProps<{
versionId: string;
workflowId: string;
}>();
const parameterSchema = ref(undefined);
const versionState = reactive<{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
parameterSchema?: JSONSchemaType<any>;
workflowVersion?: WorkflowVersionFull;
}>({
parameterSchema: undefined,
workflowVersion: undefined,
});
function downloadVersion() {
WorkflowVersionService.workflowVersionGetWorkflowVersion(
props.versionId,
props.workflowId
)
.then((version) => {
versionState.workflowVersion = version;
return version;
})
.then(downloadVersionFiles);
}
function downloadVersionFiles(version: WorkflowVersionFull) {
axios.get(version.parameter_schema_url).then((response) => {
parameterSchema.value = response.data;
});
}
onMounted(() => {
downloadVersion();
});
</script>
<template>
<parameter-schema-form-component
:workflow-version-id="versionId"
:schema="parameterSchema"
/>
</template>
<style scoped></style>
......@@ -57,7 +57,11 @@ watch(
watch(
() => workflowState.activeVersionId,
(newVersionId, oldVersionId) => {
if (newVersionId !== oldVersionId) {
if (
newVersionId &&
newVersionId !== oldVersionId &&
route.name !== "workflow-start"
) {
router.push({
name: "workflow-version",
params: { versionId: newVersionId },
......@@ -101,7 +105,7 @@ function updateWorkflow(workflowId: string) {
WorkflowService.workflowGetWorkflow(workflowId)
.then((workflow) => {
workflowState.workflow = workflow;
if (!workflowState.initialOpen || route.params.versionId == null) {
if (!workflowState.initialOpen || !route.params.versionId) {
workflowState.activeVersionId =
workflow.versions[workflow.versions.length - 1].git_commit_hash;
} else {
......@@ -145,7 +149,10 @@ onMounted(() => {
</div>
<div v-else-if="workflowState.workflow != null">
<div class="d-flex justify-content-between align-items-center">
<span class="fs-0 w-fit">{{ workflowState.workflow.name }}</span>
<div class="fs-0 w-fit text-light">
{{ workflowState.workflow.name }}
<span v-if="activeVersionString">@{{ activeVersionString }}</span>
</div>
<a :href="workflowState.workflow.repository_url" target="_blank">
<img
v-if="activeVersionIcon != null"
......@@ -155,70 +162,80 @@ onMounted(() => {
/></a>
</div>
<p class="fs-4 mb-5 mt-3">{{ workflowState.workflow.short_description }}</p>
<div
v-if="!versionLaunchable"
class="alert alert-warning w-fit mx-auto"
role="alert"
>
This version can not be used.
<router-link
v-if="latestVersion != null"
class="alert-link"
:to="{
name: 'workflow-version',
params: {
versionId: latestVersion.git_commit_hash,
},
query: { tab: route.query.tab },
}"
>Try the latest version {{ latestVersion?.version }}.</router-link
>
</div>
<div class="row align-items-center">
<a
role="button"
class="btn btn-success btn-lg w-fit mx-auto"
:class="{ disabled: !versionLaunchable }"
href="#"
>
<font-awesome-icon icon="fa-solid fa-rocket" class="me-2" />
<span class="align-middle">Launch {{ activeVersionString }}</span>
</a>
<template v-if="route.name !== 'workflow-start'">
<div
v-if="latestVersion != null"
class="input-group w-fit position-absolute end-0"
v-if="!versionLaunchable"
class="alert alert-warning w-fit mx-auto"
role="alert"
>
<span class="input-group-text px-2" id="workflow-version-wrapping"
><font-awesome-icon icon="fa-solid fa-tags" class="text-secondary"
/></span>
<select
class="form-select form-select-sm"
aria-label="Workflow version selection"
aria-describedby="workflow-version-wrapping"
v-model="workflowState.activeVersionId"
This version can not be used.
<router-link
v-if="latestVersion != null"
class="alert-link"
:to="{
name: 'workflow-version',
params: {
versionId: latestVersion.git_commit_hash,
},
query: { tab: route.query.tab },
}"
>Try the latest version {{ latestVersion?.version }}.</router-link
>
</div>
<div class="row align-items-center">
<router-link
role="button"
class="btn btn-success btn-lg w-fit mx-auto"
:class="{ disabled: !versionLaunchable }"
:to="{
name: 'workflow-start',
params: {
versionId: props.versionId,
workflowId: props.workflowId,
},
}"
>
<font-awesome-icon icon="fa-solid fa-rocket" class="me-2" />
<span class="align-middle">Launch {{ activeVersionString }}</span>
</router-link>
<div
v-if="latestVersion != null"
class="input-group w-fit position-absolute end-0"
>
<option
v-for="version in sortedVersions(workflowState.workflow?.versions)"
:key="version.git_commit_hash"
:value="version.git_commit_hash"
<span class="input-group-text px-2" id="workflow-version-wrapping"
><font-awesome-icon icon="fa-solid fa-tags" class="text-secondary"
/></span>
<select
class="form-select form-select-sm"
aria-label="Workflow version selection"
aria-describedby="workflow-version-wrapping"
v-model="workflowState.activeVersionId"
>
{{ version.version }}
</option>
</select>
<option
v-for="version in sortedVersions(
workflowState.workflow?.versions
)"
:key="version.git_commit_hash"
:value="version.git_commit_hash"
>
{{ version.version }}
</option>
</select>
</div>
</div>
</div>
<div class="row w-100 mb-4 mt-2 mx-0">
<a
:href="workflowState.workflow.repository_url"
target="_blank"
class="text-secondary text-decoration-none mx-auto w-fit p-0"
>
<font-awesome-icon :icon="gitIcon" class="me-1" />
<span class="align-middle">
{{ workflowState.workflow.repository_url }}</span
></a
>
</div>
<div class="row w-100 mb-4 mt-2 mx-0">
<a
:href="workflowState.workflow.repository_url"
target="_blank"
class="text-secondary text-decoration-none mx-auto w-fit p-0"
>
<font-awesome-icon :icon="gitIcon" class="me-1" />
<span class="align-middle">
{{ workflowState.workflow.repository_url }}</span
></a
>
</div>
</template>
</div>
<router-view v-if="workflowState.loading || workflowState.workflow != null" />
<div v-else class="text-center fs-1 mt-5">
......@@ -235,10 +252,6 @@ onMounted(() => {
</template>
<style scoped>
.fs-0 {
font-size: 4em;
}
.icon:hover {
opacity: 0.8;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment