import React, { useState, useEffect, useMemo } from 'react'; import { Table, Popover, Input, Descriptions, Tag, Image, Empty, Button, Toast, Divider, Space, Select, } from '@douyinfe/semi-ui'; import { useActions, useSelector } from '../../../services/state/store.js'; import { IconClose, IconDelete, IconSearch, IconStar, IconStarStroked, 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'; import './ListingsTable.less'; import { format } from '../../../services/time/timeService.js'; import { IllustrationNoResult, IllustrationNoResultDark } from '@douyinfe/semi-illustrations'; import { xhrDelete, xhrPost } from '../../../services/xhr.js'; import { useNavigate } from 'react-router-dom'; import { useFeature } from '../../../hooks/featureHook.js'; const getColumns = (provider, setProviderFilter, jobs, setJobNameFilter) => { return [ { title: 'Watchlist', width: 133, dataIndex: 'isWatched', sorter: true, filters: [ { text: 'Show only watched listings', value: 'watchList', }, ], render: (id, row) => { return (
); }, }, { title: 'Active', dataIndex: 'is_active', width: 110, sorter: true, filters: [ { text: 'Show only active listings', value: 'activityStatus', }, ], render: (value) => { return value ? (
) : (
); }, }, { title: 'Job-Name', sorter: true, ellipsis: true, dataIndex: 'job_name', width: 150, onFilter: () => true, renderFilterDropdown: () => { return ( ); }, }, { title: 'Listing date', width: 130, dataIndex: 'created_at', sorter: true, render: (text) => timeService.format(text, false), }, { title: 'Provider', width: 130, dataIndex: 'provider', sorter: true, render: (text) => text.charAt(0).toUpperCase() + text.slice(1), onFilter: () => true, renderFilterDropdown: () => { return ( ); }, }, { title: 'Price', width: 110, dataIndex: 'price', sorter: true, render: (text) => text + ' €', }, { title: 'Address', width: 150, dataIndex: 'address', sorter: true, }, { title: 'Title', dataIndex: 'title', sorter: true, ellipsis: true, render: (text, row) => { return ( {text} ); }, }, ]; }; const empty = ( } darkModeImage={} description="No listings found." /> ); export default function ListingsTable() { const tableData = useSelector((state) => state.listingsTable); const provider = useSelector((state) => state.provider); const jobs = useSelector((state) => state.jobs.jobs); const navigate = useNavigate(); const watchlistFeature = useFeature('WATCHLIST_MANAGEMENT') || false; const actions = useActions(); const [page, setPage] = useState(1); const pageSize = 10; const [sortData, setSortData] = useState({}); const [freeTextFilter, setFreeTextFilter] = useState(null); const [watchListFilter, setWatchListFilter] = useState(null); const [jobNameFilter, setJobNameFilter] = useState(null); const [activityFilter, setActivityFilter] = useState(null); const [providerFilter, setProviderFilter] = useState(null); const [allFilters, setAllFilters] = useState([]); const [imageWidth, setImageWidth] = useState('100%'); const handlePageChange = (_page) => { setPage(_page); }; const columns = getColumns(provider, setProviderFilter, jobs, setJobNameFilter); const loadTable = () => { let sortfield = null; let sortdir = null; if (sortData != null && Object.keys(sortData).length > 0) { sortfield = sortData.field; sortdir = sortData.direction; } actions.listingsTable.getListingsTable({ page, pageSize, sortfield, sortdir, freeTextFilter, filter: { watchListFilter, jobNameFilter, activityFilter, providerFilter }, }); }; useEffect(() => { loadTable(); }, [page, sortData, freeTextFilter, providerFilter, activityFilter, jobNameFilter, watchListFilter]); const handleFilterChange = useMemo(() => debounce((value) => setFreeTextFilter(value), 500), []); const diffArrays = (primary, secondary) => { const result = {}; for (const item of secondary) { if (!primary.includes(item)) result[item] = true; } for (const item of primary) { if (!secondary.includes(item)) result[item] = false; } return [result]; }; useEffect(() => { return () => { // cleanup debounced handler to avoid memory leaks handleFilterChange.cancel && handleFilterChange.cancel(); }; }, [handleFilterChange]); const expandRowRender = (record) => { return (
{record.image_url == null ? ( ) : ( { setImageWidth('180px'); }} fallback={} /> )}
{record.is_active ? 'Yes' : 'No'} Link to Listing {format(record.created_at)} {record.price} € {record.title}

{record.description == null ? 'No description available' : record.description}

); }; return (
} showClear className="listingsTable__search" placeholder="Search" onChange={handleFilterChange} /> {watchlistFeature && ( )} { return { ...row, reloadTable: loadTable, }; })} onChange={(changeSet) => { if (changeSet?.extra?.changeType === 'filter') { const transformed = changeSet.filters.map((f) => f.dataIndex); const diff = diffArrays(allFilters, transformed); setAllFilters(transformed); diff.forEach((filter) => { switch (Object.keys(filter)[0]) { case 'isWatched': setWatchListFilter(Object.values(filter)[0]); break; case 'is_active': setActivityFilter(Object.values(filter)[0]); break; default: console.error('Unknown filter: ', filter.dataIndex); } }); } else if (changeSet?.extra?.changeType === 'sorter') { setSortData({ field: changeSet.sorter.dataIndex, direction: changeSet.sorter.sortOrder === 'ascend' ? 'asc' : 'desc', }); } }} pagination={{ currentPage: page, //for now fixed pageSize, total: tableData?.totalNumber || 0, onPageChange: handlePageChange, }} /> ); }