diff --git a/apps/backoffice/app/components/LiveSearchForm.tsx b/apps/backoffice/app/components/LiveSearchForm.tsx index 72f1e89..4dcf9de 100644 --- a/apps/backoffice/app/components/LiveSearchForm.tsx +++ b/apps/backoffice/app/components/LiveSearchForm.tsx @@ -39,12 +39,17 @@ interface LiveSearchFormProps { debounceMs?: number; } +const STORAGE_KEY_PREFIX = "filters:"; + export function LiveSearchForm({ fields, basePath, debounceMs = 300 }: LiveSearchFormProps) { const router = useRouter(); const searchParams = useSearchParams(); const { t } = useTranslation(); const timerRef = useRef | null>(null); const formRef = useRef(null); + const restoredRef = useRef(false); + + const storageKey = `${STORAGE_KEY_PREFIX}${basePath}`; const buildUrl = useCallback((): string => { if (!formRef.current) return basePath; @@ -58,16 +63,58 @@ export function LiveSearchForm({ fields, basePath, debounceMs = 300 }: LiveSearc return qs ? `${basePath}?${qs}` : basePath; }, [basePath]); + const saveFilters = useCallback(() => { + if (!formRef.current) return; + const formData = new FormData(formRef.current); + const filters: Record = {}; + for (const [key, value] of formData.entries()) { + const str = value.toString().trim(); + if (str) filters[key] = str; + } + try { + localStorage.setItem(storageKey, JSON.stringify(filters)); + } catch {} + }, [storageKey]); + const navigate = useCallback((immediate: boolean) => { if (timerRef.current) clearTimeout(timerRef.current); if (immediate) { + saveFilters(); router.replace(buildUrl() as any); } else { timerRef.current = setTimeout(() => { + saveFilters(); router.replace(buildUrl() as any); }, debounceMs); } - }, [router, buildUrl, debounceMs]); + }, [router, buildUrl, debounceMs, saveFilters]); + + // Restore filters from localStorage on mount if URL has no filters + useEffect(() => { + if (restoredRef.current) return; + restoredRef.current = true; + + const hasUrlFilters = fields.some((f) => { + const val = searchParams.get(f.name); + return val && val.trim() !== ""; + }); + if (hasUrlFilters) return; + + try { + const saved = localStorage.getItem(storageKey); + if (!saved) return; + const filters: Record = JSON.parse(saved); + const fieldNames = new Set(fields.map((f) => f.name)); + const params = new URLSearchParams(); + for (const [key, value] of Object.entries(filters)) { + if (fieldNames.has(key) && value) params.set(key, value); + } + const qs = params.toString(); + if (qs) { + router.replace(`${basePath}?${qs}` as any); + } + } catch {} + }, []); // eslint-disable-line react-hooks/exhaustive-deps useEffect(() => { return () => { @@ -89,6 +136,7 @@ export function LiveSearchForm({ fields, basePath, debounceMs = 300 }: LiveSearc onSubmit={(e) => { e.preventDefault(); if (timerRef.current) clearTimeout(timerRef.current); + saveFilters(); router.replace(buildUrl() as any); }} className="space-y-4" @@ -149,7 +197,11 @@ export function LiveSearchForm({ fields, basePath, debounceMs = 300 }: LiveSearc {hasFilters && (