fix: broken filters (#294)

This commit is contained in:
Adrian Bach
2026-04-04 12:26:34 +02:00
committed by GitHub
parent d7f46d6c68
commit 7888c5b340
3 changed files with 70 additions and 25 deletions

View File

@@ -90,7 +90,14 @@ const ListingsGrid = () => {
loadData(); loadData();
}, [page, sortField, sortDir, freeTextFilter, providerFilter, activityFilter, jobNameFilter, watchListFilter]); }, [page, sortField, sortDir, freeTextFilter, providerFilter, activityFilter, jobNameFilter, watchListFilter]);
const handleFilterChange = useMemo(() => debounce((value) => setFreeTextFilter(value || null), 500), []); const handleFilterChange = useMemo(
() =>
debounce((value) => {
setFreeTextFilter(value || null);
setPage(1);
}, 500),
[],
);
useEffect(() => { useEffect(() => {
return () => { return () => {
@@ -152,6 +159,7 @@ const ListingsGrid = () => {
onChange={(e) => { onChange={(e) => {
const v = e.target.value; const v = e.target.value;
setActivityFilter(v === 'all' ? null : v === 'true'); setActivityFilter(v === 'all' ? null : v === 'true');
setPage(1);
}} }}
> >
<Radio value="all">All</Radio> <Radio value="all">All</Radio>
@@ -166,6 +174,7 @@ const ListingsGrid = () => {
onChange={(e) => { onChange={(e) => {
const v = e.target.value; const v = e.target.value;
setWatchListFilter(v === 'all' ? null : v === 'true'); setWatchListFilter(v === 'all' ? null : v === 'true');
setPage(1);
}} }}
> >
<Radio value="all">All</Radio> <Radio value="all">All</Radio>
@@ -176,7 +185,10 @@ const ListingsGrid = () => {
<Select <Select
placeholder="Provider" placeholder="Provider"
showClear showClear
onChange={(val) => setProviderFilter(val)} onChange={(val) => {
setProviderFilter(val);
setPage(1);
}}
value={providerFilter} value={providerFilter}
style={{ width: 130 }} style={{ width: 130 }}
> >
@@ -190,7 +202,10 @@ const ListingsGrid = () => {
<Select <Select
placeholder="Job" placeholder="Job"
showClear showClear
onChange={(val) => setJobNameFilter(val)} onChange={(val) => {
setJobNameFilter(val);
setPage(1);
}}
value={jobNameFilter} value={jobNameFilter}
style={{ width: 130 }} style={{ width: 130 }}
> >

View File

@@ -40,6 +40,12 @@ export const parseNullableBoolean = {
* @param {*} defaultValue - value when param is absent * @param {*} defaultValue - value when param is absent
* @param {{ parse: (s: string) => *, stringify: (v: *) => string|null }} [options] * @param {{ parse: (s: string) => *, stringify: (v: *) => string|null }} [options]
*/ */
// WeakMap to store pending batched updates per setSearchParams function.
// This lets multiple useSearchParamState hooks on the same component batch
// their changes into a single setSearchParams call, preventing them from
// overwriting each other.
const pendingUpdates = new WeakMap();
export function useSearchParamState([searchParams, setSearchParams], key, defaultValue, options = {}) { export function useSearchParamState([searchParams, setSearchParams], key, defaultValue, options = {}) {
const { parse = (v) => v, stringify = (v) => String(v) } = options; const { parse = (v) => v, stringify = (v) => String(v) } = options;
@@ -48,21 +54,42 @@ export function useSearchParamState([searchParams, setSearchParams], key, defaul
const setValue = useCallback( const setValue = useCallback(
(newValue) => { (newValue) => {
// Collect the change
if (!pendingUpdates.has(setSearchParams)) {
pendingUpdates.set(setSearchParams, new Map());
// Schedule a single flush at the end of the current microtask
queueMicrotask(() => {
const updates = pendingUpdates.get(setSearchParams);
pendingUpdates.delete(setSearchParams);
if (!updates || updates.size === 0) return;
setSearchParams( setSearchParams(
(prev) => { (prev) => {
const next = new URLSearchParams(prev); const next = new URLSearchParams(prev);
const serialized = stringify(newValue); for (const [k, entry] of updates) {
if (newValue === defaultValue || newValue === null || newValue === undefined || serialized === null) { if (entry.remove) {
next.delete(key); next.delete(k);
} else { } else {
next.set(key, serialized); next.set(k, entry.serialized);
}
} }
return next; return next;
}, },
{ replace: true }, { replace: true },
); );
});
}
const batch = pendingUpdates.get(setSearchParams);
const serialized = stringify(newValue);
if (newValue === defaultValue || newValue === null || newValue === undefined || serialized === null) {
batch.set(key, { remove: true });
} else {
batch.set(key, { remove: false, serialized });
}
}, },
[key, defaultValue, stringify], [key, defaultValue, stringify, setSearchParams],
); );
return [value, setValue]; return [value, setValue];

View File

@@ -207,14 +207,17 @@ export const useFredyState = create(
filter, filter,
}) { }) {
try { try {
const qryString = queryString.stringify({ const qryString = queryString.stringify(
{
page, page,
pageSize, pageSize,
freeTextFilter, freeTextFilter,
sortfield, sortfield,
sortdir, sortdir,
...filter, ...filter,
}); },
{ skipNull: true, skipEmptyString: true },
);
const response = await xhrGet(`/api/listings/table?${qryString}`); const response = await xhrGet(`/api/listings/table?${qryString}`);
set((state) => ({ set((state) => ({
listingsData: { ...state.listingsData, ...response.json }, listingsData: { ...state.listingsData, ...response.json },