Skip to content

Commit

Permalink
add job duration, WorkflowInvocationJob component
Browse files Browse the repository at this point in the history
remove `JobStepTabs.vue`
  • Loading branch information
ahmedhamidawan committed Sep 3, 2024
1 parent d09f9cc commit 9d5b1a1
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 282 deletions.
7 changes: 3 additions & 4 deletions client/src/components/JobInformation/JobInformation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import HelpText from "components/Help/HelpText";
import { JobDetailsProvider } from "components/providers/JobProvider";
import UtcDate from "components/UtcDate";
import { NON_TERMINAL_STATES } from "components/WorkflowInvocationState/util";
import { formatDuration, intervalToDuration } from "date-fns";
import { computed, ref } from "vue";
import { GalaxyApi } from "@/api";
import { rethrowSimple } from "@/utils/simple-error";
import { getJobDuration } from "./utilities";
import DecodedId from "../DecodedId.vue";
import CodeRow from "./CodeRow.vue";
Expand All @@ -27,9 +28,7 @@ const props = defineProps({
},
});
const runTime = computed(() =>
formatDuration(intervalToDuration({ start: new Date(job.value.create_time), end: new Date(job.value.update_time) }))
);
const runTime = computed(() => getJobDuration(job.value));
const jobIsTerminal = computed(() => job.value && !NON_TERMINAL_STATES.includes(job.value.state));
Expand Down
7 changes: 7 additions & 0 deletions client/src/components/JobInformation/utilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { formatDuration, intervalToDuration } from "date-fns";

import type { JobBaseModel } from "@/api/jobs";

export function getJobDuration(job: JobBaseModel): string {
return formatDuration(intervalToDuration({ start: new Date(job.create_time), end: new Date(job.update_time) }));
}
215 changes: 80 additions & 135 deletions client/src/components/WorkflowInvocationState/JobStep.vue
Original file line number Diff line number Diff line change
@@ -1,144 +1,89 @@
<template>
<b-card v-if="jobs">
<b-table
small
caption-top
:items="jobsProvider"
:fields="fields"
primary-key="id"
:tbody-tr-class="showingJobCls"
:striped="!invocationGraph"
@row-clicked="rowClicked">
<template v-slot:cell(showing_job)="data">
<span v-if="showingJobId === data.item.id || data.item._showDetails">
<FontAwesomeIcon v-if="!invocationGraph" icon="caret-down" size="lg" />
<span v-else>
<FontAwesomeIcon class="text-primary" icon="fa-eye" />
</span>
</span>
<span v-else>
<FontAwesomeIcon v-if="!invocationGraph" icon="caret-right" size="lg" />
<span v-else>
<FontAwesomeIcon icon="fa-eye" />
</span>
</span>
</template>
<template v-slot:row-details="row">
<JobProvider :id="row.item.id" v-slot="{ item, loading }">
<div v-if="loading"><b-spinner label="Loading Job..."></b-spinner></div>
<div v-else>
<b-card>
<JobInformation v-if="item" :job_id="item.id" />
<p></p>
<JobParameters v-if="item" :job-id="item.id" :include-title="false" />
</b-card>
</div>
</JobProvider>
</template>
<template v-slot:cell(create_time)="data">
<UtcDate :date="data.value" mode="elapsed" />
</template>
<template v-slot:cell(update_time)="data">
<UtcDate :date="data.value" mode="elapsed" />
</template>
</b-table>
</b-card>
</template>
<script>
import { library } from "@fortawesome/fontawesome-svg-core";
import { faCaretDown, faCaretRight } from "@fortawesome/free-solid-svg-icons";
<script setup lang="ts">
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import BootstrapVue from "bootstrap-vue";
import JobInformation from "components/JobInformation/JobInformation";
import JobParameters from "components/JobParameters/JobParameters";
import { JobProvider } from "components/providers";
import UtcDate from "components/UtcDate";
import Vue from "vue";
import { BAlert, BTab, BTabs } from "bootstrap-vue";
import Vue, { computed, ref, watch } from "vue";
Vue.use(BootstrapVue);
library.add(faCaretDown, faCaretRight);
import { GalaxyApi } from "@/api";
import { type JobBaseModel, type JobDetails } from "@/api/jobs";
import { getHeaderClass, iconClasses } from "@/composables/useInvocationGraph";
import { rethrowSimple } from "@/utils/simple-error";
export default {
components: {
FontAwesomeIcon,
UtcDate,
JobProvider,
JobParameters,
JobInformation,
},
props: {
jobs: { type: Array, required: true },
invocationGraph: { type: Boolean, default: false },
showingJobId: { type: String, default: null },
},
data() {
return {
fields: [
{ key: "showing_job", label: "", sortable: false },
{ key: "state", sortable: true },
{ key: "update_time", label: "Updated", sortable: true },
{ key: "create_time", label: "Created", sortable: true },
],
toggledItems: {},
};
},
methods: {
jobsProvider(ctx, callback) {
// It may seem unnecessary to use a provider here, since the jobs prop
// is being updated externally. However we need to keep track of the
// _showDetails attribute which determines whether the row is shown as expanded
this.$watch(
"jobs",
function () {
// update new jobs array with current state
const toggledJobs = this.jobs.map((e) => {
return { ...e, _showDetails: !!this.toggledItems[e.id] };
});
callback(toggledJobs);
import LoadingSpan from "../LoadingSpan.vue";
import WorkflowInvocationJob from "../WorkflowInvocationState/WorkflowInvocationJob.vue";
interface Props {
jobs: JobBaseModel[];
}
const props = withDefaults(defineProps<Props>(), {
jobs: () => [],
});
const loading = ref(true);
const initialLoading = ref(true);
const jobsDetails = ref<{ [key: string]: JobDetails }>({});
watch(
() => props.jobs,
async (propJobs: JobBaseModel[]) => {
loading.value = true;
for (const job of propJobs) {
const { data, error } = await GalaxyApi().GET("/api/jobs/{job_id}", {
params: {
path: { job_id: job.id },
query: { full: true },
},
{ immediate: true }
);
return null;
},
toggleDetails(item) {
// toggle item
item._showDetails = !item._showDetails;
// update state
this.toggledItems[item.id] = item._showDetails;
},
rowClicked(item) {
if (this.invocationGraph) {
this.$emit("row-clicked", item.id);
} else {
this.toggleDetails(item);
}
},
showingJobCls(item, type) {
let cls = "job-tr-class cursor-pointer unselectable";
if (this.showingJobId === item.id) {
cls += " showing-job";
});
Vue.set(jobsDetails.value, job.id, data);
if (error) {
rethrowSimple(error);
}
return cls;
},
}
if (initialLoading.value) {
initialLoading.value = false;
}
loading.value = false;
},
};
</script>
{ immediate: true }
);
const firstJob = computed(() => Object.values(jobsDetails.value)[0]);
const jobCount = computed(() => Object.keys(jobsDetails.value).length);
<style lang="scss">
// NOTE: Couldn't use scoped style due to it not working for the BTable class rows
@import "theme/blue.scss";
@import "base.scss";
function getIcon(job: JobDetails) {
return iconClasses[job.state];
}
// Table row class
.job-tr-class {
&:hover {
background-color: $brand-secondary;
}
&.showing-job {
background-color: darken($brand-secondary, 10%);
}
&:not(.showing-job) {
border-top: 0.2rem solid $brand-secondary;
}
function getTabClass(job: JobDetails) {
return {
...getHeaderClass(job.state),
"d-flex": true,
"text-center": true,
};
}
</style>
</script>

<template>
<BAlert v-if="initialLoading" variant="info" show>
<LoadingSpan message="Loading Jobs" />
</BAlert>
<BAlert v-else-if="!jobsDetails || !jobCount" variant="info" show> No jobs found for this step. </BAlert>
<div v-else-if="jobCount === 1 && firstJob">
<WorkflowInvocationJob :job="firstJob" />
</div>
<BTabs v-else vertical pills card nav-class="p-0">
<BTab v-for="job in jobsDetails" :key="job.id" :title-item-class="getTabClass(job)" title-link-class="w-100">
<template v-slot:title>
{{ job.state }}
<FontAwesomeIcon
v-if="getIcon(job)"
:class="getIcon(job)?.class"
:icon="getIcon(job)?.icon"
:spin="getIcon(job)?.spin" />
</template>
<div>
<WorkflowInvocationJob :job="job" />
</div>
</BTab>
</BTabs>
</template>
110 changes: 0 additions & 110 deletions client/src/components/WorkflowInvocationState/JobStepTabs.vue

This file was deleted.

Loading

0 comments on commit 9d5b1a1

Please sign in to comment.