11<script setup lang="ts">
22import { FontAwesomeIcon } from " @fortawesome/vue-fontawesome" ;
3- import { BAlert , BTab , BTabs } from " bootstrap-vue" ;
4- import { computed } from " vue" ;
3+ import { BAlert , BNav , BNavItem , BTab , BTabs } from " bootstrap-vue" ;
4+ import { computed , ref } from " vue" ;
55
6- import type { JobBaseModel } from " @/api/jobs" ;
7- import { getHeaderClass , iconClasses } from " @/composables/useInvocationGraph" ;
6+ import type { JobBaseModel , JobState } from " @/api/jobs" ;
7+ import { getHeaderClass , iconClasses , statePlaceholders } from " @/composables/useInvocationGraph" ;
88
99import JobDetailsDisplayed from " ../JobInformation/JobDetails.vue" ;
10+ import InvocationStepStateDisplay from " ./InvocationStepStateDisplay.vue" ;
1011
1112interface Props {
1213 jobs: JobBaseModel [];
@@ -19,6 +20,21 @@ const props = withDefaults(defineProps<Props>(), {
1920const firstJob = computed (() => props .jobs [0 ]);
2021const jobCount = computed (() => props .jobs .length );
2122
23+ /** Jobs grouped by their state */
24+ const jobsByState = computed (() => {
25+ const jobsMap: { [key : string ]: JobBaseModel [] } = {};
26+ props .jobs .forEach ((job ) => {
27+ if (! jobsMap [job .state ]) {
28+ jobsMap [job .state ] = [];
29+ }
30+ jobsMap [job .state ]?.push (job );
31+ });
32+ return jobsMap as Record <JobState , JobBaseModel []>;
33+ });
34+
35+ /** Currently selected/filtered job state */
36+ const currentState = ref <JobState | null >(firstJob .value ?.state || null );
37+
2238function getIcon(job : JobBaseModel ) {
2339 return iconClasses [job .state ];
2440}
@@ -37,24 +53,39 @@ function getTabClass(job: JobBaseModel) {
3753 <div v-else-if =" jobCount === 1 && firstJob" >
3854 <JobDetailsDisplayed :job-id =" firstJob.id" />
3955 </div >
40- <BTabs v-else lazy vertical pills card nav-class =" p-0" active-tab-class =" p-0" >
41- <BTab
42- v-for =" job in jobs"
43- :key =" job.id"
44- data-description =" workflow invocation job"
45- :title-item-class =" getTabClass(job)"
46- title-link-class =" w-100" >
47- <template v-slot :title >
48- {{ job.state }}
49- <FontAwesomeIcon
50- v-if =" getIcon(job)"
51- :class =" getIcon(job)?.class"
52- :icon =" getIcon(job)?.icon"
53- :spin =" getIcon(job)?.spin" />
54- </template >
55- <div >
56- <JobDetailsDisplayed :job-id =" job.id" />
57- </div >
58- </BTab >
59- </BTabs >
56+ <div v-else >
57+ <BNav justified pills class =" mb-2 p-2" >
58+ <BNavItem
59+ v-for =" (stateJobs, state) in jobsByState"
60+ :key =" state"
61+ :title =" `Click to view ${statePlaceholders[state] || state} jobs`"
62+ :active =" currentState === state"
63+ link-classes =" d-flex justify-content-center"
64+ @click =" currentState = state" >
65+ <InvocationStepStateDisplay :state =" state" :job-count =" stateJobs.length" />
66+ </BNavItem >
67+ </BNav >
68+
69+ <BAlert v-if =" !currentState" variant =" info" show > Please select a job state to view jobs. </BAlert >
70+ <BTabs v-else lazy vertical pills card nav-class =" p-0" active-tab-class =" p-0" >
71+ <BTab
72+ v-for =" job in jobsByState[currentState]"
73+ :key =" job.id"
74+ data-description =" workflow invocation job"
75+ :title-item-class =" getTabClass(job)"
76+ title-link-class =" w-100" >
77+ <template v-slot :title >
78+ {{ job.state }}
79+ <FontAwesomeIcon
80+ v-if =" getIcon(job)"
81+ :class =" getIcon(job)?.class"
82+ :icon =" getIcon(job)?.icon"
83+ :spin =" getIcon(job)?.spin" />
84+ </template >
85+ <div class =" m-3" >
86+ <JobDetailsDisplayed :job-id =" job.id" />
87+ </div >
88+ </BTab >
89+ </BTabs >
90+ </div >
6091</template >
0 commit comments