From 664558146df2a9eaed4888498978168383cdc3b8 Mon Sep 17 00:00:00 2001 From: estifanos Date: Fri, 28 Aug 2026 11:28:41 +0000 Subject: [PATCH] feat: add debounced search input and clear filter functionality to PersonalDocumentsCard --- .../components/PersonalDocumentsCard.tsx | 85 ++++++++++++++----- apps/backoffice/src/app/i18n/locales/am.ts | 3 +- apps/backoffice/src/app/i18n/locales/en.ts | 3 +- 3 files changed, 66 insertions(+), 25 deletions(-) diff --git a/apps/backoffice/src/app/features/certificate-requirements/components/PersonalDocumentsCard.tsx b/apps/backoffice/src/app/features/certificate-requirements/components/PersonalDocumentsCard.tsx index 220729868..dfaa16175 100644 --- a/apps/backoffice/src/app/features/certificate-requirements/components/PersonalDocumentsCard.tsx +++ b/apps/backoffice/src/app/features/certificate-requirements/components/PersonalDocumentsCard.tsx @@ -6,13 +6,16 @@ import { Button, Group, Modal, + Paper, Select, Stack, Text, + TextInput, Title, Tooltip, } from '@mantine/core'; -import { IconEdit, IconPlus, IconTrash } from '@tabler/icons-react'; +import { useDebouncedValue } from '@mantine/hooks'; +import { IconEdit, IconPlus, IconSearch, IconTrash, IconX } from '@tabler/icons-react'; import { useTranslation } from 'react-i18next'; import { AdvancedTable, @@ -41,6 +44,8 @@ type DraftRequirement = Omit = { 'application/pdf': 'PDF', @@ -93,14 +98,17 @@ export function PersonalDocumentsCard() { /** null = any licence, GLOBAL_ONLY = the all-licence ones, else a type id. */ const [licenseTypeFilter, setLicenseTypeFilter] = useState(null); - const { q, setQ, pageIndex, setPageIndex, pageSize, setPageSize } = useServerTable({ + const { pageIndex, setPageIndex, pageSize, setPageSize } = useServerTable({ pageSize: 10, }); + const [searchInput, setSearchInput] = useState(''); + // Typing must not fire a request per keystroke; same 300ms as the queue. + const [search] = useDebouncedValue(searchInput, SEARCH_DEBOUNCE_MS); // Every facet goes to the server: it filters and searches in SQL, groups the // rows into documents, then pages the documents. const { data, isFetching, refetch } = useGetPersonalDocumentsQuery({ - search: q.trim() || undefined, + search: search.trim() || undefined, licenseTypeId: licenseTypeFilter && licenseTypeFilter !== GLOBAL_ONLY ? licenseTypeFilter @@ -126,7 +134,13 @@ export function PersonalDocumentsCard() { ); /** Filters are the server's business now; an empty page is its answer. */ - const isFiltered = q.trim() !== '' || licenseTypeFilter !== null; + const isFiltered = search.trim() !== '' || licenseTypeFilter !== null; + + function clearFilters() { + setSearchInput(''); + setLicenseTypeFilter(null); + setPageIndex(0); + } const typeName = (id: string) => { const found = (licenseTypes?.items ?? []).find((lt) => lt.id === id); @@ -298,25 +312,24 @@ export function PersonalDocumentsCard() { - + + } + value={searchInput} + onChange={(e) => { + const { value } = e.currentTarget; + setSearchInput(value); + setPageIndex(0); + }} + w={240} + />