-
Daniel Göbel authored
#68
Daniel Göbel authored#68
WorkflowView.vue 11.24 KiB
<script setup lang="ts">
import { computed, onMounted, reactive, watch } from "vue";
import type {
WorkflowOut,
WorkflowStatistic,
WorkflowVersion,
} from "@/client/workflow";
import { Status, WorkflowService } from "@/client/workflow";
import WorkflowStatisticsChart from "@/components/workflows/WorkflowStatisticsChart.vue";
import { useRoute, useRouter } from "vue-router";
import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
import {
latestVersion as calculateLatestVersion,
sortedVersions,
} from "@/utils/Workflow";
import { determineGitIcon } from "@/utils/GitRepository";
import { useAuthStore } from "@/stores/users";
import { useWorkflowStore } from "@/stores/workflows";
const workflowRepository = useWorkflowStore();
const userRepository = useAuthStore();
// Props
// =============================================================================
const props = defineProps<{
workflowId: string;
versionId?: string;
workflowModeId?: string;
developerView: boolean;
}>();
// Constants
// =============================================================================
const router = useRouter();
const route = useRoute();
// Reactive State
// =============================================================================
const workflowState = reactive<{
loading: boolean;
activeVersionId: string;
initialOpen: boolean;
stats: WorkflowStatistic[];
activeModeId?: string;
}>({
loading: true,
activeVersionId: props.versionId ?? "",
initialOpen: true,
stats: [],
activeModeId: props.workflowModeId,
});
// Watchers
// =============================================================================
watch(
() => props.workflowId,
(newWorkflowId, oldWorkflowId) => {
if (newWorkflowId !== oldWorkflowId) {
updateWorkflow(newWorkflowId);
}
},
);
watch(
() => props.versionId,
(newWorkflowId) => {
workflowState.activeVersionId = newWorkflowId ?? "";
},
);
watch(
() => props.workflowModeId,
(newModeId) => {
if (newModeId) {
workflowState.activeModeId = newModeId;
}
},
);
watch(
() => workflowState.activeVersionId,
(newVersionId, oldVersionId) => {
if (
newVersionId &&
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,
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 },
});
}
},
);
// Computed Properties
// =============================================================================
const workflow = computed<WorkflowOut | undefined>(() =>
props.developerView
? workflowRepository.comprehensiveWorkflowMapping[props.workflowId]
: workflowRepository.workflowMapping[props.workflowId],
);
const latestVersion = computed<WorkflowVersion | undefined>(() =>
calculateLatestVersion(
workflow.value?.versions?.filter(
(version) => version.status == Status.PUBLISHED,
) || [],
),
);
const activeVersion = computed<WorkflowVersion | undefined>(
() =>
workflow.value?.versions.find(
(w) => w.git_commit_hash === workflowState.activeVersionId,
),
);
const activeVersionModeIds = computed<string[]>(
() => activeVersion.value?.modes ?? [],
);
const activeVersionString = computed<string>(
() => activeVersion.value?.version ?? "",
);
const activeVersionIcon = computed<string | undefined>(
() => activeVersion.value?.icon_url ?? undefined,
);
const versionLaunchable = computed<boolean>(
() => activeVersion.value?.status == Status.PUBLISHED ?? false,
);
const gitIcon = computed<string>(() =>
determineGitIcon(workflow.value?.repository_url),
);
const allowVersionDeprecation = computed<boolean>(() => {
if (activeVersion.value?.status === Status.PUBLISHED) {
if (userRepository.workflowReviewer || userRepository.admin) {
return true;
} else if (
userRepository.workflowDev &&
workflow.value?.developer_id === userRepository.currentUID
) {
return true;
}
}
return false;
});
// Functions
// =============================================================================
function updateWorkflow(workflowId: string) {
workflowState.loading = true;
workflowRepository
.fetchWorkflow(props.workflowId, props.developerView, () => {
workflowState.loading = false;
workflowState.initialOpen = false;
})
.then((workflow) => {
if (!workflowState.initialOpen || !route.params.versionId) {
workflowState.activeVersionId =
workflow.versions[workflow.versions.length - 1].git_commit_hash;
} else {
workflowState.activeVersionId = route.params.versionId as string;
}
workflowState.activeModeId = activeVersionModeIds.value[0] ?? undefined;
});
WorkflowService.workflowGetWorkflowStatistics(workflowId).then((stats) => {
workflowState.stats = stats;
});
}
function deprecateCurrentWorkflowVersion() {
if (props.versionId) {
workflowRepository.deprecateWorkflowVersion(
props.workflowId,
props.versionId,
);
}
}
// Lifecycle Events
// =============================================================================
onMounted(() => {
updateWorkflow(props.workflowId);
});
</script>
<template>
<div v-if="workflowState.loading">
<div
class="d-flex mt-5 justify-content-between align-items-center placeholder-glow"
>
<span class="fs-0 placeholder col-6"></span>
<span class="fs-0 placeholder col-1"></span>
</div>
<div class="fs-4 mb-5 mt-4 placeholder-glow">
<span class="placeholder col-10"></span>
</div>
<div class="row align-items-center placeholder-glow my-1">
<span class="mx-auto col-2 placeholder bg-success fs-0"></span>
<span class="position-absolute end-0 col-1 placeholder fs-2"></span>
</div>
<div class="row w-100 mb-4 mt-3 mx-0 placeholder-glow">
<span class="placeholder col-3 mx-auto"></span>
</div>
</div>
<div v-else-if="workflow">
<div class="d-flex justify-content-between align-items-center">
<h2 class="w-fit">
{{ workflow.name }}
<span v-if="activeVersionString">@{{ activeVersionString }}</span>
</h2>
<img
v-if="activeVersionIcon != null"
:src="activeVersionIcon"
class="img-fluid icon"
alt="Workflow icon"
/>
</div>
<p class="fs-4 mt-3">{{ 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"
>
{{ workflowRepository.modeMapping[modeId]?.name }}
</option>
</select>
</div>
</div>
<template v-if="route.name !== 'workflow-start'">
<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"
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">
<div class="w-fit position-absolute start-0">
<button
v-if="props.versionId && allowVersionDeprecation"
type="button"
class="btn btn-warning"
@click="deprecateCurrentWorkflowVersion"
>
Deprecate version
</button>
</div>
<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,
},
query: {
workflowModeId: workflowState.activeModeId,
},
}"
>
<font-awesome-icon icon="fa-solid fa-rocket" class="me-2" />
<span class="align-middle">Launch {{ activeVersionString }}</span>
</router-link>
<div
v-if="latestVersion"
class="input-group w-fit position-absolute end-0"
>
<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"
>
<option
v-for="version in sortedVersions(workflow.versions)"
:key="version.git_commit_hash"
:value="version.git_commit_hash"
>
{{ version.version }}
</option>
</select>
</div>
</div>
<div class="row w-100 mb-4 mt-2 mx-0 border-bottom pb-2">
<a
:href="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"> {{ workflow.repository_url }}</span>
<font-awesome-icon
v-if="workflow?.private"
icon="fa-solid fa-lock"
class="ms-1"
/>
</a>
</div>
<workflow-statistics-chart
:stats="workflowState.stats"
v-if="workflowState.stats"
/>
</template>
</div>
<router-view v-if="workflowState.loading || workflow" />
<div v-else class="text-center fs-1 mt-5">
<font-awesome-icon
icon="fa-solid fa-magnifying-glass"
class="my-5 fs-0"
style="color: var(--bs-secondary)"
/>
<p class="my-5">
Could not find any Workflow with ID <br />'{{ workflowId }}'
</p>
<router-link :to="{ name: 'workflows' }" class="mt-5">Back</router-link>
</div>
</template>
<style scoped>
.icon {
max-width: 64px;
max-height: 64px;
min-width: 50px;
min-height: 50px;
}
</style>