From 3aa30bc1e2909a7f7a9635014a53c0d32a00fa03 Mon Sep 17 00:00:00 2001 From: orangecoding Date: Fri, 3 Oct 2025 13:27:44 +0200 Subject: [PATCH] remove listings from listingstable when clicked --- lib/api/routes/listingsRouter.js | 17 ++++++++-- lib/services/storage/listingsStorage.js | 14 +++++++- ui/src/components/table/ListingsTable.jsx | 39 ++++++++++++++++++++-- ui/src/components/table/ListingsTable.less | 4 +++ ui/src/views/jobs/Jobs.jsx | 2 +- 5 files changed, 69 insertions(+), 7 deletions(-) diff --git a/lib/api/routes/listingsRouter.js b/lib/api/routes/listingsRouter.js index c91314f..5e6e1d5 100644 --- a/lib/api/routes/listingsRouter.js +++ b/lib/api/routes/listingsRouter.js @@ -22,10 +22,23 @@ listingsRouter.get('/table', async (req, res) => { res.send(); }); -listingsRouter.delete('/', async (req, res) => { +listingsRouter.delete('/job', async (req, res) => { const { jobId } = req.body; try { - listingStorage.deleteListings(jobId); + listingStorage.deleteListingsByJobId(jobId); + } catch (error) { + res.send(new Error(error)); + logger.error(error); + } + res.send(); +}); + +listingsRouter.delete('/', async (req, res) => { + const { ids } = req.body; + try { + if (Array.isArray(ids) && ids.length > 0) { + listingStorage.deleteListingsById(ids); + } } catch (error) { res.send(new Error(error)); logger.error(error); diff --git a/lib/services/storage/listingsStorage.js b/lib/services/storage/listingsStorage.js index b142de2..6ee648b 100755 --- a/lib/services/storage/listingsStorage.js +++ b/lib/services/storage/listingsStorage.js @@ -258,7 +258,19 @@ export const queryListings = ({ * @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) => { +export const deleteListingsByJobId = (jobId) => { if (!jobId) return; return SqliteConnection.execute(`DELETE FROM listings WHERE job_id = @jobId`, { jobId }); }; + +/** + * Delete listings by a list of listing IDs. + * + * @param {string[]} ids - Array of listing IDs to delete. + * @returns {any} The result from SqliteConnection.execute. + */ +export const deleteListingsById = (ids) => { + if (!Array.isArray(ids) || ids.length === 0) return; + const placeholders = ids.map(() => '?').join(','); + return SqliteConnection.execute(`DELETE FROM listings WHERE id IN (${placeholders})`, ids); +}; diff --git a/ui/src/components/table/ListingsTable.jsx b/ui/src/components/table/ListingsTable.jsx index e17504e..9ca717b 100644 --- a/ui/src/components/table/ListingsTable.jsx +++ b/ui/src/components/table/ListingsTable.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect, useMemo } from 'react'; -import { Table, Popover, Input, Descriptions, Tag, Image, Empty } from '@douyinfe/semi-ui'; +import { Table, Popover, Input, Descriptions, Tag, Image, Empty, Button, Card, Toast } from '@douyinfe/semi-ui'; import { useActions, useSelector } from '../../services/state/store.js'; -import { IconClose, IconSearch, IconTick } from '@douyinfe/semi-icons'; +import { IconClose, IconDelete, IconSearch, IconTick } from '@douyinfe/semi-icons'; import * as timeService from '../../services/time/timeService.js'; import debounce from 'lodash/debounce'; import no_image from '../../assets/no_image.jpg'; @@ -9,6 +9,7 @@ import no_image from '../../assets/no_image.jpg'; import './ListingsTable.less'; import { format } from '../../services/time/timeService.js'; import { IllustrationNoResult, IllustrationNoResultDark } from '@douyinfe/semi-illustrations'; +import { xhrDelete } from '../../services/xhr.js'; const columns = [ { @@ -81,6 +82,7 @@ const columns = [ title: 'Title', dataIndex: 'title', sorter: true, + ellipsis: true, render: (text, row) => { return ( @@ -106,12 +108,13 @@ export default function ListingsTable() { const pageSize = 10; const [sortData, setSortData] = useState({}); const [filter, setFilter] = useState(null); + const [selectedKeys, setSelectedKeys] = useState([]); const handlePageChange = (_page) => { setPage(_page); }; - useEffect(() => { + const loadTable = () => { let sortfield = null; let sortdir = null; @@ -120,10 +123,20 @@ export default function ListingsTable() { sortdir = sortData.direction; } actions.listingsTable.getListingsTable({ page, pageSize, sortfield, sortdir, filter }); + }; + + useEffect(() => { + loadTable(); }, [page, sortData, filter]); const handleFilterChange = useMemo(() => debounce((value) => setFilter(value), 500), []); + const rowSelection = { + onChange: (selectedRowKeys) => { + setSelectedKeys(selectedRowKeys); + }, + }; + const expandRowRender = (record) => { return (
@@ -156,6 +169,18 @@ export default function ListingsTable() { ); }; + const onRemoveSelectedListings = async () => { + if (selectedKeys != null && selectedKeys.length > 0) { + try { + await xhrDelete('/api/listings/', { ids: selectedKeys }); + Toast.success('Listing(s) successfully removed'); + loadTable(); + } catch (error) { + Toast.error(error); + } + } + }; + return (
+ {selectedKeys != null && selectedKeys.length > 0 && ( + + + + )} { diff --git a/ui/src/components/table/ListingsTable.less b/ui/src/components/table/ListingsTable.less index 015e85e..375f38b 100644 --- a/ui/src/components/table/ListingsTable.less +++ b/ui/src/components/table/ListingsTable.less @@ -7,4 +7,8 @@ display: flex; gap: 1rem; } + + &__toolbar { + margin-bottom: 1rem; + } } \ No newline at end of file diff --git a/ui/src/views/jobs/Jobs.jsx b/ui/src/views/jobs/Jobs.jsx index 30365b3..a663b10 100644 --- a/ui/src/views/jobs/Jobs.jsx +++ b/ui/src/views/jobs/Jobs.jsx @@ -25,7 +25,7 @@ export default function Jobs() { const onListingRemoval = async (jobId) => { try { - await xhrDelete('/api/listings', { jobId }); + await xhrDelete('/api/listings/job', { jobId }); Toast.success('Listings successfully removed'); await actions.jobs.getJobs(); } catch (error) {