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) =>
{text}
; + +export default function JobTable({ + jobs = {}, + onJobRemoval, + onJobStatusChanged, + onJobEdit, + onJobInsight, + onListingRemoval, +} = {}) { return ( { return (
-
); }, diff --git a/ui/src/components/table/JobTable.less b/ui/src/components/table/JobTable.less index eb9eb8b..7d7d9ee 100644 --- a/ui/src/components/table/JobTable.less +++ b/ui/src/components/table/JobTable.less @@ -5,6 +5,11 @@ gap: 1rem; } +.jobPopoverContent { + padding: 1rem; + color: white; +} + @media (min-width: 768px) { .interactions { flex-direction: initial; diff --git a/ui/src/views/jobs/Jobs.jsx b/ui/src/views/jobs/Jobs.jsx index a3e61aa..30365b3 100644 --- a/ui/src/views/jobs/Jobs.jsx +++ b/ui/src/views/jobs/Jobs.jsx @@ -16,7 +16,17 @@ export default function Jobs() { const onJobRemoval = async (jobId) => { try { await xhrDelete('/api/jobs', { jobId }); - Toast.success('Job successfully remove'); + Toast.success('Job successfully removed'); + await actions.jobs.getJobs(); + } catch (error) { + Toast.error(error); + } + }; + + const onListingRemoval = async (jobId) => { + try { + await xhrDelete('/api/listings', { jobId }); + Toast.success('Listings successfully removed'); await actions.jobs.getJobs(); } catch (error) { Toast.error(error); @@ -49,6 +59,7 @@ export default function Jobs() { navigate(`/jobs/insights/${jobId}`)} onJobEdit={(jobId) => navigate(`/jobs/edit/${jobId}`)}