From ee97a5800b88ed3fffec983f8eff037a209f43fb Mon Sep 17 00:00:00 2001 From: david Date: Wed, 1 Apr 2026 20:04:03 +0200 Subject: [PATCH] =?UTF-8?q?Mejorada=20la=20b=C3=BAsqueda=20r=C3=A1pida?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/form/simple-search-input.tsx | 109 +++++++++++------- .../use-list-customers.controller.ts | 26 ++++- .../datatable/data-table-pagination.tsx | 72 ++++-------- .../src/components/datatable/data-table.tsx | 29 ++--- 4 files changed, 131 insertions(+), 105 deletions(-) diff --git a/modules/core/src/web/components/form/simple-search-input.tsx b/modules/core/src/web/components/form/simple-search-input.tsx index c9646da3..4e42f456 100644 --- a/modules/core/src/web/components/form/simple-search-input.tsx +++ b/modules/core/src/web/components/form/simple-search-input.tsx @@ -20,6 +20,8 @@ type SimpleSearchInputProps = { const SEARCH_HISTORY_KEY = "search_history"; +const normalizeSearchValue = (value: string) => value.trim().replace(/\s+/g, " "); + export const SimpleSearchInput = ({ value, onSearchChange, @@ -33,84 +35,102 @@ export const SimpleSearchInput = ({ const [open, setOpen] = useState(false); const inputRef = useRef(null); + // Evita que el siguiente debounce pise una búsqueda inmediata + const skipNextDebouncedEmitRef = useRef(false); + const debouncedValue = useDebounce(searchValue, 300); - // Load from localStorage on mount + useEffect(() => { + setSearchValue(value); + }, [value]); + useEffect(() => { const stored = localStorage.getItem(SEARCH_HISTORY_KEY); - if (stored) setHistory(JSON.parse(stored)); + if (!stored) return; + + try { + const parsed = JSON.parse(stored) as string[]; + setHistory(Array.isArray(parsed) ? parsed : []); + } catch { + setHistory([]); + } }, []); - // Emit changes after debounce useEffect(() => { - onSearchChange(debouncedValue); + if (skipNextDebouncedEmitRef.current) { + skipNextDebouncedEmitRef.current = false; + return; + } + + onSearchChange(normalizeSearchValue(debouncedValue)); }, [debouncedValue, onSearchChange]); - // Save history to localStorage const saveHistory = (term: string) => { - if (!term.trim()) return; - const cleaned = term.trim(); - const newHistory = [cleaned, ...history.filter((h) => h !== cleaned)].slice(0, maxHistory); - setHistory(newHistory); - localStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(newHistory)); + const cleaned = normalizeSearchValue(term); + if (!cleaned) return; + + setHistory((prev) => { + const nextHistory = [cleaned, ...prev.filter((item) => item !== cleaned)].slice( + 0, + maxHistory + ); + localStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(nextHistory)); + return nextHistory; + }); }; - const clearHistory = () => { - setHistory([]); - localStorage.removeItem(SEARCH_HISTORY_KEY); - }; - - // Input handlers const handleInputChange = (e: React.ChangeEvent) => { - const cleaned = e.target.value.trimStart().replace(/\s+/g, " "); - setSearchValue(cleaned); + setSearchValue(e.target.value); + }; + + const handleImmediateSearch = (rawValue: string) => { + const nextValue = normalizeSearchValue(rawValue); + + skipNextDebouncedEmitRef.current = true; + setSearchValue(nextValue); + setLastSearch(nextValue); + onSearchChange(nextValue); + saveHistory(nextValue); + setOpen(false); }; const handleClear = () => { - setSearchValue(""); - onSearchChange(""); + handleImmediateSearch(""); + inputRef.current?.focus(); }; const handleKeyDown = (e: React.KeyboardEvent) => { - // Enter → búsqueda inmediata + const currentValue = e.currentTarget.value; + if (e.key === "Enter" && !e.shiftKey && !e.metaKey && !e.ctrlKey) { e.preventDefault(); - onSearchChange(searchValue); - setLastSearch(searchValue); - saveHistory(searchValue); - setOpen(false); + handleImmediateSearch(currentValue); + return; } - // Shift+Enter → repetir última búsqueda if (e.key === "Enter" && e.shiftKey) { e.preventDefault(); - if (lastSearch) { - onSearchChange(lastSearch); - setSearchValue(lastSearch); - } + if (!lastSearch) return; + + handleImmediateSearch(lastSearch); + return; } - // Ctrl/Cmd+Enter → limpiar if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { e.preventDefault(); handleClear(); - inputRef.current?.focus(); } }; const handleSelectHistory = (term: string) => { - setSearchValue(term); - onSearchChange(term); - setLastSearch(term); - setOpen(false); + handleImmediateSearch(term); }; return (
- + history.length > 0 && setOpen(true)} @@ -120,26 +140,33 @@ export const SimpleSearchInput = ({ spellCheck={false} value={searchValue} /> + - + {loading && ( - + )} + {!(searchValue || loading) && ( - )} + {searchValue && !loading && (