diff --git a/lib/api/routes/listingsRouter.js b/lib/api/routes/listingsRouter.js
index 96c0570..c91314f 100644
--- a/lib/api/routes/listingsRouter.js
+++ b/lib/api/routes/listingsRouter.js
@@ -1,6 +1,8 @@
import restana from 'restana';
import * as listingStorage from '../../services/storage/listingsStorage.js';
import { isAdmin as isAdminFn } from '../security.js';
+import logger from '../../services/logger.js';
+
const service = restana();
const listingsRouter = service.newRouter();
@@ -8,7 +10,7 @@ const listingsRouter = service.newRouter();
listingsRouter.get('/table', async (req, res) => {
const { page, pageSize = 50, filter, sortfield = null, sortdir = 'asc' } = req.query || {};
- const result = listingStorage.queryListings({
+ res.body = listingStorage.queryListings({
page: page ? parseInt(page, 10) : 1,
pageSize: pageSize ? parseInt(pageSize, 10) : 50,
filter: filter || undefined,
@@ -17,7 +19,18 @@ listingsRouter.get('/table', async (req, res) => {
userId: req.session.currentUser,
isAdmin: isAdminFn(req),
});
- res.body = result;
res.send();
});
+
+listingsRouter.delete('/', async (req, res) => {
+ const { jobId } = req.body;
+ try {
+ listingStorage.deleteListings(jobId);
+ } catch (error) {
+ res.send(new Error(error));
+ logger.error(error);
+ }
+ res.send();
+});
+
export { listingsRouter };
diff --git a/lib/services/storage/listingsStorage.js b/lib/services/storage/listingsStorage.js
index 4bcdc78..b142de2 100755
--- a/lib/services/storage/listingsStorage.js
+++ b/lib/services/storage/listingsStorage.js
@@ -251,3 +251,14 @@ export const queryListings = ({
return { totalNumber, page: safePage, result: rows };
};
+
+/**
+ * Delete all listings for a given job id.
+ *
+ * @param {string} jobId - The job identifier whose listings should be removed.
+ * @returns {any} The result from SqliteConnection.execute (may contain changes count).
+ */
+export const deleteListings = (jobId) => {
+ if (!jobId) return;
+ return SqliteConnection.execute(`DELETE FROM listings WHERE job_id = @jobId`, { jobId });
+};
diff --git a/ui/src/components/table/JobTable.jsx b/ui/src/components/table/JobTable.jsx
index ba32fc7..28fcde4 100644
--- a/ui/src/components/table/JobTable.jsx
+++ b/ui/src/components/table/JobTable.jsx
@@ -1,7 +1,7 @@
import React from 'react';
-import { Button, Empty, Table, Switch } from '@douyinfe/semi-ui';
-import { IconDelete, IconEdit, IconHistogram } from '@douyinfe/semi-icons';
+import { Button, Empty, Table, Switch, Popover } from '@douyinfe/semi-ui';
+import { IconDelete, IconDescend2, IconEdit, IconHistogram } from '@douyinfe/semi-icons';
import { IllustrationNoResult, IllustrationNoResultDark } from '@douyinfe/semi-illustrations';
import './JobTable.less';
@@ -14,7 +14,16 @@ const empty = (
/>
);
-export default function JobTable({ jobs = {}, onJobRemoval, onJobStatusChanged, onJobEdit, onJobInsight } = {}) {
+const getPopoverContent = (text) =>