diff --git a/src/frontend/components/app/filter-drawer.tsx b/src/frontend/components/app/filter-drawer.tsx index f7e5f3765..322bdebb1 100644 --- a/src/frontend/components/app/filter-drawer.tsx +++ b/src/frontend/components/app/filter-drawer.tsx @@ -1,5 +1,5 @@ import { Box, Button, Drawer, DrawerContent, DrawerFooter, H3, Icon } from '@adminjs/design-system' -import identity from 'lodash/identity.js' +import isNil from 'lodash/isNil.js' import pickBy from 'lodash/pickBy.js' import React, { useEffect, useRef, useState } from 'react' import { useParams } from 'react-router-dom' @@ -29,7 +29,7 @@ const FilterDrawer: React.FC = (props) => { const { translateButton, translateLabel } = useTranslation() const initialLoad = useRef(true) const { isVisible, toggleFilter } = useFilterDrawer() - const { storeParams, filters } = useQueryParams() + const { storeParams, clearParams, filters } = useQueryParams() useEffect(() => { if (initialLoad.current) { @@ -41,12 +41,12 @@ const FilterDrawer: React.FC = (props) => { const handleSubmit = (event: SubmitEvent) => { event.preventDefault() - storeParams({ filters: pickBy(filter, identity) }) + storeParams({ filters: pickBy(filter, (v) => !isNil(v)) }) } const handleReset = (event: SubmitEvent) => { event.preventDefault() - storeParams({ filters: undefined }) + clearParams('filters') setFilter({}) } @@ -60,7 +60,10 @@ const FilterDrawer: React.FC = (props) => { if ((propertyName as RecordJSON).params) { throw new Error('you can not pass RecordJSON to filters') } - setFilter({ ...filter, [propertyName as string]: value }) + setFilter({ + ...filter, + [propertyName as string]: typeof value === 'string' && !value.length ? undefined : value, + }) } const contentTag = getResourceElementCss(resource.id, 'filter-drawer') diff --git a/src/frontend/hooks/use-query-params.ts b/src/frontend/hooks/use-query-params.ts index 001651494..8d78e7c78 100644 --- a/src/frontend/hooks/use-query-params.ts +++ b/src/frontend/hooks/use-query-params.ts @@ -2,8 +2,8 @@ import isEmpty from 'lodash/isEmpty.js' import pick from 'lodash/pick.js' import { parse, stringify } from 'qs' -import { useCallback, useMemo } from 'react' -import { useSearchParams } from 'react-router-dom' +import { useMemo } from 'react' +import { useLocation, useSearchParams } from 'react-router-dom' // eslint-disable-next-line no-shadow export enum QueryParams { @@ -21,21 +21,28 @@ export enum QueryListParams { Query = 'query', } -type Params> = { +type Params, FiltersT = Record> = ParamsT & { sortBy: string page: string tab: string redirectUrl: string direction: 'asc' | 'desc' filters: FiltersT + refresh: boolean } -export function useQueryParams>() { +export function useQueryParams< + ParamsT = Record, + FiltersT = Record, +>() { + const { pathname } = useLocation() const [searchParams, setSearchParams] = useSearchParams() const parsedQuery = useMemo( - () => parse(searchParams.toString()) as Params, - [searchParams], + () => parse(searchParams.toString(), { + allowDots: true, + }) as unknown as Params, + [searchParams, pathname], ) const { sortBy, direction, page, tab, filters, redirectUrl } = parsedQuery const showFilters = !isEmpty(filters) @@ -50,10 +57,33 @@ export function useQueryParams>() { [parsedQuery], ) - const storeParams = useCallback((params: Partial) => { - const newQuery = { sortBy, direction, page, tab, filters, redirectUrl, ...params } - return setSearchParams(stringify(newQuery, { skipNulls: true, allowDots: true })) - }, []) + function storeParams(params: Partial>) { + setSearchParams( + stringify( + { sortBy, direction, page, tab, filters, redirectUrl, ...params }, + { allowDots: true }, + ), + ) + } + + function clearParams(...params: string[]) { + const searchParamsKeys = Array.from(searchParams.keys()) + const clearCandidates = params.length ? params : searchParamsKeys + + for (const param of searchParamsKeys) { + for (const paramToClear of clearCandidates) { + if (param.startsWith(paramToClear) && searchParams.get(param)) { + searchParams.delete(param) + } + } + } + + setSearchParams(searchParams) + } + + function getParam(param: keyof Params & string) { + searchParams.get(param) + } return { showFilters, @@ -64,7 +94,9 @@ export function useQueryParams>() { direction, page, tab, - storeParams, redirectUrl, + storeParams, + clearParams, + getParam, } } diff --git a/src/frontend/utils/api-client.ts b/src/frontend/utils/api-client.ts index 56cc0ef8b..b029689e1 100644 --- a/src/frontend/utils/api-client.ts +++ b/src/frontend/utils/api-client.ts @@ -37,7 +37,7 @@ const checkResponse = (response: AxiosResponse): void => { if (response.request.responseURL && response.request.responseURL.match(loginUrl) ) { - // eslint-disable-next-line no-undef + // eslint-disable-next-line no-undef, no-alert alert('Your session expired. You will be redirected to login screen') globalAny.location.assign(loginUrl) }