forked from galaxyproject/galaxy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add job duration,
WorkflowInvocationJob
component
remove `JobStepTabs.vue`
- Loading branch information
1 parent
d09f9cc
commit 9d5b1a1
Showing
6 changed files
with
146 additions
and
282 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
215
client/src/components/WorkflowInvocationState/JobStep.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
110
client/src/components/WorkflowInvocationState/JobStepTabs.vue
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.