Skip to content

Commit e30c36a

Browse files
committed
feat: enhance job modal functionality and update plugin options
1 parent 47e4439 commit e30c36a

4 files changed

Lines changed: 63 additions & 10 deletions

File tree

custom/JobsList.vue

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
<template>
22
<div class="w-1vw md:w-64 bg-white border border-gray-200 rounded-md">
3-
<Modal v-for="job in props.jobs" :key="job.id" :beforeOpenFunction="props.closeDropdown">
3+
<Modal
4+
v-for="job in props.jobs" :key="job.id"
5+
:beforeCloseFunction="onBeforeOpen"
6+
:beforeOpenFunction="onBeforeClose"
7+
removeFromDomOnClose
8+
>
49
<template #trigger>
510
<div class="flex items-center w-full px-4 py-3 border-b border-gray-200 hover:bg-gray-50 transition-colors">
611
<div class="flex flex-col w-full max-w-48">
@@ -22,12 +27,10 @@
2227
<StateToIcon :job="job" />
2328
</div>
2429
</template>
25-
<div>
26-
<JobInfoPopup
27-
:job="job"
28-
:meta="meta"
29-
/>
30-
</div>
30+
<JobInfoPopup
31+
:job="job"
32+
:meta="meta"
33+
/>
3134
</Modal>
3235

3336
</div>
@@ -40,6 +43,7 @@ import { getTimeAgoString } from '@/utils';
4043
import { ProgressBar, Modal } from '@/afcl';
4144
import JobInfoPopup from './JobInfoPopup.vue';
4245
import StateToIcon from './StateToIcon.vue';
46+
import { ref } from 'vue';
4347
4448
const props = defineProps<{
4549
jobs: IJob[];
@@ -49,5 +53,16 @@ const props = defineProps<{
4953
};
5054
}>();
5155
56+
57+
const isModalOpen = ref(false);
58+
59+
function onBeforeOpen() {
60+
props.closeDropdown();
61+
}
62+
63+
function onBeforeClose() {
64+
isModalOpen.value = false;
65+
}
66+
5267
5368
</script>

custom/NavbarJobs.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
import type { AdminUser } from 'adminforth';
4242
import { onMounted, onUnmounted, ref, computed } from 'vue';
4343
import { IconCheckCircleOutline } from '@iconify-prerendered/vue-flowbite';
44-
import { Tooltip, Modal } from '@/afcl';
44+
import { Tooltip } from '@/afcl';
4545
import { useI18n } from 'vue-i18n';
4646
import JobsList from './JobsList.vue';
4747
import type { IJob } from './utils';

index.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { PluginOptions } from './types.js';
44
import { afLogger } from "adminforth";
55
import pLimit from 'p-limit';
66
import { Level } from 'level';
7+
import fs from 'fs/promises';
78

89
type TaskStatus = 'SCHEDULED' | 'IN_PROGRESS' | 'DONE' | 'FAILED';
910
type setStateFieldParams = (state: Record<string, any>) => void;
@@ -48,6 +49,35 @@ export default class extends AdminForthPlugin {
4849
pluginInstanceId: this.pluginInstanceId,
4950
}
5051
});
52+
53+
if (!this.resourceConfig.hooks) {
54+
this.resourceConfig.hooks = {};
55+
}
56+
if (!this.resourceConfig.hooks.delete) {
57+
this.resourceConfig.hooks.delete = {};
58+
}
59+
if (!this.resourceConfig.hooks.delete.beforeSave) {
60+
this.resourceConfig.hooks.delete.beforeSave = [];
61+
}
62+
this.resourceConfig.hooks.delete.beforeSave.push(async ({record, recordId}: {record: any, recordId: any}) => {
63+
64+
const levelDbPath = `${this.options.levelDbPath || './background-jobs-dbs/'}job_${recordId}`;
65+
const jobLevelDb = this.levelDbInstances[recordId];
66+
67+
//close level db instance if it's open and delete the level db folder for the job
68+
if (jobLevelDb) {
69+
await jobLevelDb.close();
70+
delete this.levelDbInstances[recordId];
71+
}
72+
73+
//delete level db folder for the job
74+
await fs.rm(levelDbPath, {
75+
recursive: true,
76+
force: true,
77+
});
78+
79+
return {ok: true};
80+
})
5181
}
5282

5383
private checkIfFieldInResource(resourceConfig: AdminForthResource, fieldName: string, fieldString?: string) {
@@ -198,6 +228,7 @@ export default class extends AdminForthPlugin {
198228

199229
//define the setTaskStateField and getTaskStateField functions to pass to the task
200230
const setTaskStateField = async (state: Record<string, any>) => {
231+
this.adminforth.websocket.publish(`/background-jobs-task-update/${jobId}`, { taskIndex, state });
201232
await this.setLevelDbTaskStateField(jobLevelDb, taskIndex.toString(), state);
202233
}
203234
const getTaskStateField = async () => {
@@ -242,11 +273,13 @@ export default class extends AdminForthPlugin {
242273
if (lastJobStatus !== 'CANCELLED' && failedTasks === 0) {
243274
await this.adminforth.resource(this.getResourceId()).update(jobId, {
244275
[this.options.statusField]: 'DONE',
276+
[this.options.finishedAtField]: (new Date()).toISOString(),
245277
})
246278
this.adminforth.websocket.publish('/background-jobs', { jobId, status: 'DONE' });
247279
} else if (failedTasks > 0) {
248280
await this.adminforth.resource(this.getResourceId()).update(jobId, {
249281
[this.options.statusField]: 'DONE_WITH_ERRORS',
282+
[this.options.finishedAtField]: (new Date()).toISOString(),
250283
})
251284
this.adminforth.websocket.publish('/background-jobs', { jobId, status: 'DONE_WITH_ERRORS' });
252285
}
@@ -349,11 +382,13 @@ export default class extends AdminForthPlugin {
349382
async validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource) {
350383
// optional method where you can safely check field types after database discovery was performed
351384
this.checkIfFieldInResource(resourceConfig, this.options.createdAtField, 'createdAtField');
385+
this.checkIfFieldInResource(resourceConfig, this.options.finishedAtField, 'finishedAtField');
352386
this.checkIfFieldInResource(resourceConfig, this.options.startedByField, 'startedByField');
353387
this.checkIfFieldInResource(resourceConfig, this.options.stateField, 'stateField');
354388
this.checkIfFieldInResource(resourceConfig, this.options.progressField, 'progressField');
355389
this.checkIfFieldInResource(resourceConfig, this.options.statusField, 'statusField');
356390
this.checkIfFieldInResource(resourceConfig, this.options.nameField, 'nameField');
391+
this.checkIfFieldInResource(resourceConfig, this.options.jobHandlerField, 'jobHandlerField');
357392

358393

359394
//Add temp delay to make sure, that all resources active. Probably should be fixed
@@ -402,6 +437,7 @@ export default class extends AdminForthPlugin {
402437
try {
403438
await this.adminforth.resource(this.getResourceId()).update(jobId, {
404439
[this.options.statusField]: 'CANCELLED',
440+
[this.options.finishedAtField]: (new Date()).toISOString(),
405441
});
406442
this.adminforth.websocket.publish('/background-jobs', {
407443
jobId,
@@ -426,6 +462,7 @@ export default class extends AdminForthPlugin {
426462
} else {
427463
try {
428464
jobLevelDb = new Level(levelDbPath, { valueEncoding: 'json' });
465+
this.levelDbInstances[jobId] = jobLevelDb;
429466
} catch (error) {
430467
return { ok: false, message: `Failed to access tasks for job with id ${jobId}.` };
431468
}

types.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11

22
export interface PluginOptions {
33
createdAtField: string;
4+
finishedAtField: string;
45
startedByField: string;
56
stateField: string;
67
progressField: string;
78
statusField: string;
8-
nameField?: string;
9-
jobHandlerField?: string;
9+
nameField: string;
10+
jobHandlerField: string;
1011

1112
/**
1213
* Path to the level db folder. If not provided, a default path is ./background-jobs-dbs/

0 commit comments

Comments
 (0)