diff --git a/client/src/Utils/debounce.jsx b/client/src/Hooks/useDebounce.ts similarity index 68% rename from client/src/Utils/debounce.jsx rename to client/src/Hooks/useDebounce.ts index 11d639f14..57a7d4079 100644 --- a/client/src/Utils/debounce.jsx +++ b/client/src/Hooks/useDebounce.ts @@ -1,7 +1,7 @@ import { useState, useEffect } from "react"; -const useDebounce = (value, delay) => { - const [debouncedValue, setDebouncedValue] = useState(value); +const useDebounce = (value: T, delay: number): T => { + const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const handler = setTimeout(() => { @@ -12,6 +12,7 @@ const useDebounce = (value, delay) => { clearTimeout(handler); }; }, [value, delay]); + return debouncedValue; }; diff --git a/client/src/Pages/Infrastructure/Monitors/index.tsx b/client/src/Pages/Infrastructure/Monitors/index.tsx index 507ecb1ef..1d38dbdd3 100644 --- a/client/src/Pages/Infrastructure/Monitors/index.tsx +++ b/client/src/Pages/Infrastructure/Monitors/index.tsx @@ -20,6 +20,7 @@ import { useSelector, useDispatch } from "react-redux"; import { setRowsPerPage } from "@/Features/UI/uiSlice.js"; import type { RootState } from "@/Types/state"; import { InfraMonitorsTable } from "./Components/MonitorsTable"; +import useDebounce from "@/Hooks/useDebounce"; const InfrastructureMonitors = () => { const { t } = useTranslation(); @@ -43,6 +44,8 @@ const InfrastructureMonitors = () => { const [selectedMonitor, setSelectedMonitor] = useState(null); const isDialogOpen = Boolean(selectedMonitor); + const debouncedSearch = useDebounce(search, 300); + const handleClearFilters = useCallback(() => { setSelectedStatus(""); setSelectedState(""); @@ -69,8 +72,8 @@ const InfrastructureMonitors = () => { [toFilterActive, "isActive"], ]); const activeFilter = [...filterLookup].find(([key]) => key !== undefined); - const field = activeFilter?.[1] || (search ? "name" : sortField || undefined); - const filter = activeFilter?.[0] || search || undefined; + const field = activeFilter?.[1] || (debouncedSearch ? "name" : sortField || undefined); + const filter = activeFilter?.[0] || debouncedSearch || undefined; // Build URL for monitors with checks const monitorsWithChecksUrl = useMemo(() => { diff --git a/client/src/Pages/Uptime/Monitors/index.tsx b/client/src/Pages/Uptime/Monitors/index.tsx index 8aaed16d2..702bbd928 100644 --- a/client/src/Pages/Uptime/Monitors/index.tsx +++ b/client/src/Pages/Uptime/Monitors/index.tsx @@ -20,6 +20,7 @@ import { setRowsPerPage } from "@/Features/UI/uiSlice.js"; import { useIsAdmin } from "@/Hooks/useIsAdmin"; import type { RootState } from "@/Types/state"; import { useTheme } from "@mui/material"; +import useDebounce from "@/Hooks/useDebounce"; const UptimeMonitorsPage = () => { const { t } = useTranslation(); @@ -43,6 +44,8 @@ const UptimeMonitorsPage = () => { const [selectedMonitor, setSelectedMonitor] = useState(null); const isDialogOpen = Boolean(selectedMonitor); + const debouncedSearch = useDebounce(search, 300); + // Convert filter selections to API filter values // Status: "up" -> true, "down" -> false // State: "active" -> true, "paused" -> false @@ -65,8 +68,8 @@ const UptimeMonitorsPage = () => { [toFilterActive, "isActive"], ]); const activeFilter = [...filterLookup].find(([key]) => key !== undefined); - const field = activeFilter?.[1] || (search ? "name" : sortField || undefined); - const filter = activeFilter?.[0] || search; + const field = activeFilter?.[1] || (debouncedSearch ? "name" : sortField || undefined); + const filter = activeFilter?.[0] || debouncedSearch; // Default to all types when none selected const effectiveTypes =