From 0f8f0dd27fae2edf89efd612a6a29cf5093cc48b Mon Sep 17 00:00:00 2001 From: Mohamed Marrouchi Date: Wed, 28 May 2025 17:06:23 +0100 Subject: [PATCH] fix: move debounce to text input and enhance --- .../app-components/inputs/FilterTextfield.tsx | 61 +++++++++++++++++-- frontend/src/components/inbox/index.tsx | 10 +-- frontend/src/hooks/useSearch.tsx | 13 ++-- 3 files changed, 66 insertions(+), 18 deletions(-) diff --git a/frontend/src/app-components/inputs/FilterTextfield.tsx b/frontend/src/app-components/inputs/FilterTextfield.tsx index cd58df06..bbd3af20 100644 --- a/frontend/src/app-components/inputs/FilterTextfield.tsx +++ b/frontend/src/app-components/inputs/FilterTextfield.tsx @@ -1,31 +1,80 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ +import ClearIcon from "@mui/icons-material/Clear"; import SearchIcon from "@mui/icons-material/Search"; -import { TextFieldProps } from "@mui/material"; +import { + debounce, + IconButton, + InputAdornment, + TextFieldProps, +} from "@mui/material"; +import { useCallback, useMemo, useState } from "react"; import { useTranslate } from "@/hooks/useTranslate"; import { Adornment } from "./Adornment"; import { Input } from "./Input"; -export const FilterTextfield = (props: TextFieldProps) => { - const { t } = useTranslate(); +export interface FilterTextFieldProps + extends Omit { + onChange: (value: string) => void; + delay?: number; + clearable: boolean; + defaultValue?: string; +} + +export const FilterTextfield = ({ + onChange: onSearch, + defaultValue = "", + delay = 500, + clearable = true, + ...props +}) => { + const { t } = useTranslate(); + const [inputValue, setInputValue] = useState(defaultValue); + const debouncedSearch = useMemo( + () => + debounce((value: string) => { + onSearch?.(value); + }, delay), + [onSearch, delay], + ); + const handleChange = useCallback( + (event: React.ChangeEvent) => { + const value = event.target.value; + + setInputValue(value); + debouncedSearch(value); + }, + [debouncedSearch], + ); + const handleClear = useCallback(() => { + setInputValue(""); + debouncedSearch(""); + }, [debouncedSearch]); - //TODO: replace the native delete text button by a styled custom button return ( , + endAdornment: clearable && ( + + + + + + ), }} placeholder={t("placeholder.keywords")} {...props} + value={inputValue} + onChange={handleChange} /> ); }; diff --git a/frontend/src/components/inbox/index.tsx b/frontend/src/components/inbox/index.tsx index a34d6d62..598b7461 100644 --- a/frontend/src/components/inbox/index.tsx +++ b/frontend/src/components/inbox/index.tsx @@ -6,12 +6,13 @@ * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ -import { MainContainer, Search, Sidebar } from "@chatscope/chat-ui-kit-react"; +import { MainContainer, Sidebar } from "@chatscope/chat-ui-kit-react"; import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css"; import { Grid, MenuItem } from "@mui/material"; import { useState } from "react"; import AutoCompleteEntitySelect from "@/app-components/inputs/AutoCompleteEntitySelect"; +import { FilterTextfield } from "@/app-components/inputs/FilterTextfield"; import { Input } from "@/app-components/inputs/Input"; import { useSearch } from "@/hooks/useSearch"; import { useTranslate } from "@/hooks/useTranslate"; @@ -46,13 +47,14 @@ export const Inbox = () => { - - + + {/* onSearch("")} className="changeColor" onChange={(v) => onSearch(v)} placeholder="Search..." - /> + /> */} ({ export const useSearch = (params: TParamItem) => { const [searchText, setSearchText] = useState(""); - const onSearch = debounce( - (e: ChangeEvent | string) => { - setSearchText(typeof e === "string" ? e : e.target.value); - }, - 300, - ); const { $eq: eqInitialParams, $iLike: iLikeParams, @@ -67,7 +60,11 @@ export const useSearch = (params: TParamItem) => { } = params; return { - onSearch, + onSearch: ( + e: ChangeEvent | string, + ) => { + setSearchText(typeof e === "string" ? e : e.target.value); + }, searchPayload: { where: { ...buildEqInitialParams({ initialParams: eqInitialParams }),