Skip to content

Commit 44bb7d5

Browse files
add a dummy, hoverable stop button to JobState; add it to JobInformation
Also removes the existing `JobState` component rendered in the `JobStepJobs` modal header.
1 parent 262aa10 commit 44bb7d5

3 files changed

Lines changed: 125 additions & 16 deletions

File tree

client/src/components/JobInformation/JobInformation.vue

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { getJobDuration } from "./utilities";
1111
1212
import Heading from "../Common/Heading.vue";
1313
import DecodedId from "../DecodedId.vue";
14+
import JobState from "../JobStates/JobState.vue";
1415
import CodeRow from "./CodeRow.vue";
1516
import CopyToClipboard from "@/components/CopyToClipboard.vue";
1617
import HelpText from "@/components/Help/HelpText.vue";
@@ -144,7 +145,10 @@ watch(
144145
:stderr_position="stderr_position"
145146
:stderr_length="stderr_length"
146147
@update:result="updateConsoleOutputs" />
147-
<Heading id="job-information-heading" h1 separator inline size="md"> Job Information </Heading>
148+
<Heading id="job-information-heading" h1 separator inline size="md">
149+
Job Information
150+
<JobState v-if="job" class="job-information-state-badge" :job="job" />
151+
</Heading>
148152
<table id="job-information" class="tabletip info_data_table">
149153
<tbody>
150154
<tr v-if="job && job.tool_id">
@@ -261,9 +265,15 @@ watch(
261265
</table>
262266
</div>
263267
</template>
264-
<style scoped>
268+
<style scoped lang="scss">
269+
@import "@/style/scss/theme/blue.scss";
270+
265271
.tooltipJobInfo {
266272
text-decoration-line: underline;
267273
text-decoration-style: dashed;
268274
}
275+
276+
.job-information-state-badge {
277+
font-size: $h5-font-size;
278+
}
269279
</style>

client/src/components/JobStates/JobState.vue

Lines changed: 107 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
<script setup lang="ts">
2+
import { faSquare } from "@fortawesome/free-solid-svg-icons";
23
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
3-
import { computed } from "vue";
4+
import { computed, ref } from "vue";
45
5-
import type { JobBaseModel } from "@/api/jobs";
6+
import { type JobBaseModel, NON_TERMINAL_STATES } from "@/api/jobs";
7+
import { useToast } from "@/composables/toast";
68
import { getHeaderClass, iconClasses } from "@/composables/useInvocationGraph";
79
10+
import GButton from "../BaseComponents/GButton.vue";
11+
812
const props = defineProps<{
913
job: JobBaseModel;
1014
}>();
@@ -17,11 +21,110 @@ const badgeClass = computed(() => {
1721
});
1822
1923
const stateIcon = computed(() => iconClasses[props.job.state] || null);
24+
25+
const Toast = useToast();
26+
27+
/** Whether the current user owns the job (can stop it) */
28+
const userOwnsJob = computed(() => {
29+
return true; // TODO: For testing i have true, will have proper implementation later
30+
});
31+
32+
/** Whether to render this button
33+
* 1. If the job is not the user's own
34+
* 2. Job is already in a terminal state
35+
*/
36+
const canStopJob = computed(() => !userOwnsJob.value || NON_TERMINAL_STATES.includes(props.job.state));
37+
38+
/** Whether the stop job action is currently being performed */
39+
const stopping = ref(false);
40+
41+
async function stopJob() {
42+
stopping.value = true;
43+
try {
44+
// TODO: To be implemented: Call the API to stop the job, and handle any errors that may occur.
45+
// Ideally, we would want a job object returned from the API after stopping,
46+
// and we would want to update the job state in the UI accordingly. For now, I am just
47+
// using a Toast.
48+
49+
Toast.success("Job stopped successfully.");
50+
51+
// TODO: Handle job state returned by the API
52+
} catch (error) {
53+
// TODO: Handle string errorMessageAsString, just casting to string for now
54+
Toast.error(error as string, "Failed to stop the job.");
55+
} finally {
56+
stopping.value = false;
57+
}
58+
}
2059
</script>
2160

2261
<template>
23-
<span class="rounded px-2 py-1" :class="badgeClass">
24-
<FontAwesomeIcon v-if="stateIcon" :icon="stateIcon.icon" :spin="stateIcon.spin" />
62+
<span class="job-state-badge rounded px-2 py-1" :class="badgeClass">
63+
<FontAwesomeIcon
64+
v-if="stateIcon"
65+
:icon="stateIcon.icon"
66+
:spin="stateIcon.spin"
67+
:class="{ 'hoverable-icon': canStopJob }" />
68+
<GButton
69+
v-if="canStopJob"
70+
transparent
71+
inline
72+
icon-only
73+
pill
74+
size="small"
75+
class="stop-job-btn"
76+
title="Stop the execution of this job"
77+
:disabled="stopping"
78+
disabled-title="Stopping job..."
79+
@click="stopJob">
80+
<FontAwesomeIcon :icon="faSquare" />
81+
</GButton>
2582
{{ props.job.state }}
2683
</span>
2784
</template>
85+
86+
<style lang="scss" scoped>
87+
// If the job is in a stoppable state, we want to make the default state
88+
// icon hide on hover, and show the stop button instead.
89+
.job-state-badge {
90+
position: relative;
91+
92+
&:hover {
93+
.hoverable-icon {
94+
visibility: hidden;
95+
}
96+
.stop-job-btn {
97+
display: inline-block;
98+
}
99+
}
100+
}
101+
102+
.stop-job-btn {
103+
display: none;
104+
position: absolute;
105+
left: 0.5rem;
106+
top: 50%;
107+
transform: translateY(-50%);
108+
height: 1em;
109+
width: 1em;
110+
line-height: 1;
111+
padding: 0 !important;
112+
min-height: unset !important;
113+
}
114+
115+
.hoverable-icon {
116+
display: inline-block;
117+
height: 1em;
118+
width: 1em;
119+
line-height: 1;
120+
}
121+
122+
// When canStopJob is true, ensure only one shows at a time
123+
.job-state-badge:not(:hover) .stop-job-btn {
124+
display: none;
125+
}
126+
127+
.job-state-badge:hover .hoverable-icon {
128+
visibility: hidden;
129+
}
130+
</style>

client/src/components/WorkflowInvocationState/JobStepJobs.vue

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -163,16 +163,12 @@ watch(
163163
<GModal :show.sync="showModal" fixed-height size="medium" @close="viewedJob = null">
164164
<template v-slot:header>
165165
<div v-if="viewedJob" class="w-100 d-flex justify-content-between align-items-center">
166-
<div class="d-flex flex-gapx-1 align-items-center">
167-
<Heading h2 size="sm" style="margin-bottom: 0">
168-
Job
169-
<code>
170-
{{ viewedJob.id }}
171-
</code>
172-
</Heading>
173-
174-
<JobState :job="viewedJob" />
175-
</div>
166+
<Heading h2 size="sm">
167+
Job
168+
<code>
169+
{{ viewedJob.id }}
170+
</code>
171+
</Heading>
176172

177173
<div class="d-flex align-items-center">
178174
<GButtonGroup>

0 commit comments

Comments
 (0)