Skip to content

Commit 7937ca6

Browse files
ulischultehzpz
andauthored
Fix scheduled tasks view (#4243)
Co-authored-by: Timo <mailantimo@gmx.de>
1 parent eb1c813 commit 7937ca6

4 files changed

Lines changed: 240 additions & 83 deletions

File tree

spring-boot-admin-server-ui/src/main/frontend/views/instances/scheduledtasks/i18n.en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
"scheduledtasks": {
44
"label": "Scheduled Tasks",
55
"no_scheduledtasks": "No scheduled tasks present.",
6+
"last_execution": "Last Execution",
7+
"last_execution_status": "Last Execution State",
8+
"next_execution": "Next Execution",
69
"cron": {
710
"expression": "Expression",
811
"runnable": "Runnable",

spring-boot-admin-server-ui/src/main/frontend/views/instances/scheduledtasks/index.vue

Lines changed: 119 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -24,101 +24,137 @@
2424
/>
2525
</div>
2626

27-
<template v-if="hasCronData">
28-
<table class="table-auto w-full">
29-
<thead>
30-
<tr>
31-
<th
32-
class="text-left"
33-
v-text="$t('instances.scheduledtasks.cron.runnable')"
34-
/>
35-
<th v-text="$t('instances.scheduledtasks.cron.expression')" />
36-
</tr>
37-
</thead>
38-
<tbody v-for="task in cron" :key="task.runnable.target">
39-
<tr>
40-
<td v-text="task.runnable.target" />
41-
<td
42-
class="font-mono text-center text-sm"
43-
v-text="task.expression"
44-
/>
45-
</tr>
46-
</tbody>
47-
</table>
48-
</template>
27+
<div class="flex flex-col">
28+
<template v-if="hasCronData">
29+
<sba-panel :title="$t('instances.scheduledtasks.cron.title')">
30+
<table class="table w-full">
31+
<thead>
32+
<tr>
33+
<th
34+
class="text-left"
35+
v-text="$t('instances.scheduledtasks.cron.runnable')"
36+
/>
37+
<th v-text="$t('instances.scheduledtasks.cron.expression')" />
38+
<th v-text="$t('instances.scheduledtasks.next_execution')" />
39+
<th v-text="$t('instances.scheduledtasks.last_execution')" />
40+
<th
41+
v-text="
42+
$t('instances.scheduledtasks.last_execution_status')
43+
"
44+
/>
45+
</tr>
46+
</thead>
47+
<tbody v-for="task in cron" :key="task.runnable.target">
48+
<tr>
49+
<td v-text="task.runnable.target" />
50+
<td class="font-mono text-sm" v-text="task.expression" />
51+
<scheduled-task-executions :task="task" />
52+
</tr>
53+
</tbody>
54+
</table>
55+
</sba-panel>
56+
</template>
4957

50-
<template v-if="hasFixedDelayData">
51-
<h3
52-
class="title"
53-
v-text="$t('instances.scheduledtasks.fixed_delay.title')"
54-
/>
55-
<table class="metrics table is-fullwidth">
56-
<thead>
57-
<tr>
58-
<th
59-
v-text="$t('instances.scheduledtasks.fixed_delay.runnable')"
60-
/>
61-
<th
62-
v-text="
63-
$t('instances.scheduledtasks.fixed_delay.initial_delay_ms')
64-
"
65-
/>
66-
<th
67-
v-text="$t('instances.scheduledtasks.fixed_delay.interval_ms')"
68-
/>
69-
</tr>
70-
</thead>
71-
<tbody v-for="task in fixedDelay" :key="task.runnable.target">
72-
<tr>
73-
<td v-text="task.runnable.target" />
74-
<td v-text="task.initialDelay" />
75-
<td v-text="task.interval" />
76-
</tr>
77-
</tbody>
78-
</table>
79-
</template>
58+
<template v-if="hasFixedDelayData">
59+
<sba-panel :title="$t('instances.scheduledtasks.fixed_delay.title')">
60+
<table class="table w-full">
61+
<thead>
62+
<tr>
63+
<th
64+
v-text="$t('instances.scheduledtasks.fixed_delay.runnable')"
65+
/>
66+
<th
67+
v-text="
68+
$t(
69+
'instances.scheduledtasks.fixed_delay.initial_delay_ms',
70+
)
71+
"
72+
/>
73+
<th
74+
v-text="
75+
$t('instances.scheduledtasks.fixed_delay.interval_ms')
76+
"
77+
/>
78+
<th v-text="$t('instances.scheduledtasks.next_execution')" />
79+
<th v-text="$t('instances.scheduledtasks.last_execution')" />
80+
<th
81+
v-text="
82+
$t('instances.scheduledtasks.last_execution_status')
83+
"
84+
/>
85+
</tr>
86+
</thead>
87+
<tbody v-for="task in fixedDelay" :key="task.runnable.target">
88+
<tr>
89+
<td v-text="task.runnable.target" />
90+
<td v-text="task.initialDelay" />
91+
<td v-text="task.interval" />
92+
<scheduled-task-executions :task="task" />
93+
</tr>
94+
</tbody>
95+
</table>
96+
</sba-panel>
97+
</template>
8098

81-
<template v-if="hasFixedRateData">
82-
<h3
83-
class="title"
84-
v-text="$t('instances.scheduledtasks.fixed_rate.title')"
85-
/>
86-
<table class="metrics table is-fullwidth">
87-
<thead>
88-
<tr>
89-
<th
90-
v-text="$t('instances.scheduledtasks.fixed_delay.runnable')"
91-
/>
92-
<th
93-
v-text="
94-
$t('instances.scheduledtasks.fixed_delay.initial_delay_ms')
95-
"
96-
/>
97-
<th
98-
v-text="$t('instances.scheduledtasks.fixed_delay.interval_ms')"
99-
/>
100-
</tr>
101-
</thead>
102-
<tbody v-for="task in fixedRate" :key="task.runnable.target">
103-
<tr>
104-
<td v-text="task.runnable.target" />
105-
<td v-text="task.initialDelay" />
106-
<td v-text="task.interval" />
107-
</tr>
108-
</tbody>
109-
</table>
110-
</template>
99+
<template v-if="hasFixedRateData">
100+
<sba-panel :title="$t('instances.scheduledtasks.fixed_rate.title')">
101+
<table class="table w-full">
102+
<thead>
103+
<tr>
104+
<th
105+
v-text="$t('instances.scheduledtasks.fixed_delay.runnable')"
106+
/>
107+
<th
108+
v-text="
109+
$t(
110+
'instances.scheduledtasks.fixed_delay.initial_delay_ms',
111+
)
112+
"
113+
/>
114+
<th
115+
v-text="
116+
$t('instances.scheduledtasks.fixed_delay.interval_ms')
117+
"
118+
/>
119+
<th v-text="$t('instances.scheduledtasks.next_execution')" />
120+
<th v-text="$t('instances.scheduledtasks.last_execution')" />
121+
<th
122+
v-text="
123+
$t('instances.scheduledtasks.last_execution_status')
124+
"
125+
/>
126+
</tr>
127+
</thead>
128+
<tbody v-for="task in fixedRate" :key="task.runnable.target">
129+
<tr>
130+
<td v-text="task.runnable.target" />
131+
<td v-text="task.initialDelay" />
132+
<td v-text="task.interval" />
133+
<scheduled-task-executions :task="task" />
134+
</tr>
135+
</tbody>
136+
</table>
137+
</sba-panel>
138+
</template>
139+
</div>
111140
</sba-panel>
112141
</sba-instance-section>
113142
</template>
114143

115144
<script>
145+
import SbaPanel from '@/components/sba-panel.vue';
146+
116147
import Instance from '@/services/instance';
117148
import { VIEW_GROUP } from '@/views/ViewGroup';
149+
import ScheduledTaskExecutions from '@/views/instances/scheduledtasks/scheduled-task-executions.vue';
118150
import SbaInstanceSection from '@/views/instances/shell/sba-instance-section';
119151
120152
export default {
121-
components: { SbaInstanceSection },
153+
components: {
154+
ScheduledTaskExecutions,
155+
SbaPanel,
156+
SbaInstanceSection,
157+
},
122158
props: {
123159
instance: {
124160
type: Instance,
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { screen, waitFor } from '@testing-library/vue';
2+
import { describe, expect, it } from 'vitest';
3+
4+
import { render } from '@/test-utils';
5+
import ScheduledTaskExecutions from '@/views/instances/scheduledtasks/scheduled-task-executions.vue';
6+
7+
describe('ScheduledTaskExecutions', () => {
8+
const baseTask = {
9+
nextExecution: { time: '2024-06-01T12:00:00Z' },
10+
lastExecution: { time: '2024-05-31T12:00:00Z', status: 'SUCCESS' },
11+
};
12+
13+
it('renders nextExecution time', async () => {
14+
render(ScheduledTaskExecutions, {
15+
props: { task: baseTask },
16+
});
17+
const nextExec = await screen.findByText('2024-06-01T12:00:00Z');
18+
expect(nextExec).toBeVisible();
19+
});
20+
21+
it('renders lastExecution time and status when lastExecution exists', async () => {
22+
render(ScheduledTaskExecutions, {
23+
props: { task: baseTask },
24+
});
25+
26+
const lastExec = await screen.findByText('2024-05-31T12:00:00Z');
27+
expect(lastExec).toBeVisible();
28+
29+
const statusBadge = await screen.findByRole('status');
30+
expect(statusBadge).toHaveTextContent('SUCCESS');
31+
expect(statusBadge).toHaveClass('status-badge success');
32+
});
33+
34+
it('does not render lastExecution time or status if lastExecution is missing', async () => {
35+
const task = { nextExecution: { time: '2024-06-01T12:00:00Z' } };
36+
render(ScheduledTaskExecutions, {
37+
props: { task },
38+
});
39+
40+
const nextExec = await screen.findByText('2024-06-01T12:00:00Z');
41+
expect(nextExec).toBeVisible();
42+
43+
await waitFor(() =>
44+
expect(screen.queryByRole('status')).not.toBeInTheDocument(),
45+
);
46+
47+
await waitFor(() =>
48+
expect(screen.queryByTestId('lastExecution')).not.toBeInTheDocument(),
49+
);
50+
});
51+
52+
it('applies classNames with lowercase status', async () => {
53+
const task = {
54+
nextExecution: { time: '2024-06-01T12:00:00Z' },
55+
lastExecution: { time: '2024-05-31T12:00:00Z', status: 'ERROR' },
56+
};
57+
render(ScheduledTaskExecutions, {
58+
props: { task },
59+
});
60+
61+
const statusBadge = await screen.findByRole('status');
62+
expect(statusBadge).toHaveTextContent('ERROR');
63+
expect(statusBadge).toHaveClass('status-badge error');
64+
});
65+
});
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<template>
2+
<td class="font-mono text-sm">
3+
<sba-formatted-obj
4+
v-if="task.nextExecution"
5+
:value="task.nextExecution.time"
6+
/>
7+
</td>
8+
<td class="font-mono text-sm">
9+
<sba-formatted-obj
10+
v-if="task.lastExecution"
11+
data-testid="lastExecution"
12+
:value="task.lastExecution.time"
13+
/>
14+
</td>
15+
<td class="font-mono text-sm">
16+
<span
17+
v-if="task.lastExecution"
18+
role="status"
19+
:class="`status-badge ${task.lastExecution.status.toLowerCase()}`"
20+
v-text="task.lastExecution.status"
21+
/>
22+
</td>
23+
</template>
24+
25+
<script>
26+
import SbaFormattedObj from '@/components/sba-formatted-obj.vue';
27+
28+
export default {
29+
name: 'ScheduledTaskExecutions',
30+
components: { SbaFormattedObj },
31+
props: {
32+
task: {
33+
type: Object,
34+
required: true,
35+
},
36+
},
37+
};
38+
</script>
39+
40+
<style scoped>
41+
.status-badge {
42+
@apply bg-gray-200 text-black text-xs inline-flex items-center uppercase rounded overflow-hidden px-3 py-1;
43+
}
44+
.success {
45+
@apply bg-green-200 text-green-700;
46+
}
47+
.error {
48+
@apply bg-red-200 text-red-700;
49+
}
50+
.started {
51+
@apply bg-yellow-200 text-yellow-700;
52+
}
53+
</style>

0 commit comments

Comments
 (0)