mirror of
https://github.com/orangecoding/fredy.git
synced 2026-06-16 12:31:07 +00:00
remove listings from listingstable when clicked
This commit is contained in:
@@ -22,10 +22,23 @@ listingsRouter.get('/table', async (req, res) => {
|
|||||||
res.send();
|
res.send();
|
||||||
});
|
});
|
||||||
|
|
||||||
listingsRouter.delete('/', async (req, res) => {
|
listingsRouter.delete('/job', async (req, res) => {
|
||||||
const { jobId } = req.body;
|
const { jobId } = req.body;
|
||||||
try {
|
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) {
|
} catch (error) {
|
||||||
res.send(new Error(error));
|
res.send(new Error(error));
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
|
|||||||
@@ -258,7 +258,19 @@ export const queryListings = ({
|
|||||||
* @param {string} jobId - The job identifier whose listings should be removed.
|
* @param {string} jobId - The job identifier whose listings should be removed.
|
||||||
* @returns {any} The result from SqliteConnection.execute (may contain changes count).
|
* @returns {any} The result from SqliteConnection.execute (may contain changes count).
|
||||||
*/
|
*/
|
||||||
export const deleteListings = (jobId) => {
|
export const deleteListingsByJobId = (jobId) => {
|
||||||
if (!jobId) return;
|
if (!jobId) return;
|
||||||
return SqliteConnection.execute(`DELETE FROM listings WHERE job_id = @jobId`, { jobId });
|
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);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useEffect, useMemo } from 'react';
|
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 { 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 * as timeService from '../../services/time/timeService.js';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import no_image from '../../assets/no_image.jpg';
|
import no_image from '../../assets/no_image.jpg';
|
||||||
@@ -9,6 +9,7 @@ import no_image from '../../assets/no_image.jpg';
|
|||||||
import './ListingsTable.less';
|
import './ListingsTable.less';
|
||||||
import { format } from '../../services/time/timeService.js';
|
import { format } from '../../services/time/timeService.js';
|
||||||
import { IllustrationNoResult, IllustrationNoResultDark } from '@douyinfe/semi-illustrations';
|
import { IllustrationNoResult, IllustrationNoResultDark } from '@douyinfe/semi-illustrations';
|
||||||
|
import { xhrDelete } from '../../services/xhr.js';
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
@@ -81,6 +82,7 @@ const columns = [
|
|||||||
title: 'Title',
|
title: 'Title',
|
||||||
dataIndex: 'title',
|
dataIndex: 'title',
|
||||||
sorter: true,
|
sorter: true,
|
||||||
|
ellipsis: true,
|
||||||
render: (text, row) => {
|
render: (text, row) => {
|
||||||
return (
|
return (
|
||||||
<a href={row.url} target="_blank" rel="noopener noreferrer">
|
<a href={row.url} target="_blank" rel="noopener noreferrer">
|
||||||
@@ -106,12 +108,13 @@ export default function ListingsTable() {
|
|||||||
const pageSize = 10;
|
const pageSize = 10;
|
||||||
const [sortData, setSortData] = useState({});
|
const [sortData, setSortData] = useState({});
|
||||||
const [filter, setFilter] = useState(null);
|
const [filter, setFilter] = useState(null);
|
||||||
|
const [selectedKeys, setSelectedKeys] = useState([]);
|
||||||
|
|
||||||
const handlePageChange = (_page) => {
|
const handlePageChange = (_page) => {
|
||||||
setPage(_page);
|
setPage(_page);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const loadTable = () => {
|
||||||
let sortfield = null;
|
let sortfield = null;
|
||||||
let sortdir = null;
|
let sortdir = null;
|
||||||
|
|
||||||
@@ -120,10 +123,20 @@ export default function ListingsTable() {
|
|||||||
sortdir = sortData.direction;
|
sortdir = sortData.direction;
|
||||||
}
|
}
|
||||||
actions.listingsTable.getListingsTable({ page, pageSize, sortfield, sortdir, filter });
|
actions.listingsTable.getListingsTable({ page, pageSize, sortfield, sortdir, filter });
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadTable();
|
||||||
}, [page, sortData, filter]);
|
}, [page, sortData, filter]);
|
||||||
|
|
||||||
const handleFilterChange = useMemo(() => debounce((value) => setFilter(value), 500), []);
|
const handleFilterChange = useMemo(() => debounce((value) => setFilter(value), 500), []);
|
||||||
|
|
||||||
|
const rowSelection = {
|
||||||
|
onChange: (selectedRowKeys) => {
|
||||||
|
setSelectedKeys(selectedRowKeys);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const expandRowRender = (record) => {
|
const expandRowRender = (record) => {
|
||||||
return (
|
return (
|
||||||
<div className="listingsTable__expanded">
|
<div className="listingsTable__expanded">
|
||||||
@@ -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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<Input
|
||||||
@@ -165,12 +190,20 @@ export default function ListingsTable() {
|
|||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
onChange={handleFilterChange}
|
onChange={handleFilterChange}
|
||||||
/>
|
/>
|
||||||
|
{selectedKeys != null && selectedKeys.length > 0 && (
|
||||||
|
<Card className="listingsTable__toolbar">
|
||||||
|
<Button type="danger" icon={<IconDelete />} onClick={() => onRemoveSelectedListings()}>
|
||||||
|
Remove selected Listings
|
||||||
|
</Button>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
<Table
|
<Table
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
empty={empty}
|
empty={empty}
|
||||||
hideExpandedColumn={false}
|
hideExpandedColumn={false}
|
||||||
sticky={{ top: 5 }}
|
sticky={{ top: 5 }}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
rowSelection={rowSelection}
|
||||||
expandedRowRender={expandRowRender}
|
expandedRowRender={expandRowRender}
|
||||||
dataSource={tableData?.result || []}
|
dataSource={tableData?.result || []}
|
||||||
onChange={(changeSet) => {
|
onChange={(changeSet) => {
|
||||||
|
|||||||
@@ -7,4 +7,8 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__toolbar {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ export default function Jobs() {
|
|||||||
|
|
||||||
const onListingRemoval = async (jobId) => {
|
const onListingRemoval = async (jobId) => {
|
||||||
try {
|
try {
|
||||||
await xhrDelete('/api/listings', { jobId });
|
await xhrDelete('/api/listings/job', { jobId });
|
||||||
Toast.success('Listings successfully removed');
|
Toast.success('Listings successfully removed');
|
||||||
await actions.jobs.getJobs();
|
await actions.jobs.getJobs();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user