UI: useDebouncedTermSearch hook

This commit is contained in:
mbecker20
2026-07-14 00:14:59 -07:00
parent 687440782d
commit 0593cc659c
7 changed files with 67 additions and 86 deletions
+24 -1
View File
@@ -13,7 +13,7 @@ import {
useQuery,
useQueryClient,
} from "@tanstack/react-query";
import { useEffect, useMemo } from "react";
import { useEffect, useMemo, useState } from "react";
import { atom, useAtom } from "jotai";
import { atomFamily } from "jotai-family";
import { atomWithHash } from "jotai-location";
@@ -25,6 +25,7 @@ import {
termMatchesTypeKeyword,
} from "@/lib/utils";
import { notifications } from "@mantine/notifications";
import { useDebounce } from "mogh_ui";
export function komodo_client() {
return KomodoClient(KOMODO_BASE_URL, {
@@ -182,6 +183,28 @@ export function atomWithStorage<T>(key: string, init: T) {
);
}
export function useDebouncedTermSearch(props?: {
debounce?: number;
onUpdate?: () => void;
}) {
const { debounce = 200, onUpdate } = props ?? {};
const [search, setSearch] = useState("");
const debouncedSearch = useDebounce(search, debounce);
const terms = useMemo(() => {
onUpdate?.();
return debouncedSearch
.toLowerCase()
.split(" ")
.map((term) => term.trim())
.filter((term) => term);
}, [debouncedSearch]);
return {
search,
setSearch,
terms,
};
}
export function useResourceParamType() {
const type = useParams().type;
if (!type) return undefined;
+7 -14
View File
@@ -1,11 +1,11 @@
import ContainerPorts from "@/components/docker/container-ports";
import DockerResourceLink from "@/components/docker/link";
import { containerStateIntention } from "@/lib/color";
import { useRead, useTagsFilter } from "@/lib/hooks";
import { useDebouncedTermSearch, useRead, useTagsFilter } from "@/lib/hooks";
import { keepPreviousData } from "@tanstack/react-query";
import { Types } from "komodo_client";
import { ICONS } from "@/lib/icons";
import { DataTable, SortableHeader, useDebounce } from "mogh_ui";
import { DataTable, SortableHeader } from "mogh_ui";
import { Page } from "mogh_ui";
import { StatusBadge } from "mogh_ui";
import { Group, Stack } from "@mantine/core";
@@ -23,17 +23,10 @@ export default function Containers() {
const [selectedServers, setSelectedServers] = useState<string[]>([]);
const [page, setPage] = useState(0);
const [search, setSearch] = useState("");
const debouncedSearch = useDebounce(search, 200);
const searchTerms = useMemo(() => {
// back to first page if search changes
setPage(0);
return debouncedSearch
.toLowerCase()
.split(" ")
.map((term) => term.trim())
.filter((term) => term);
}, [debouncedSearch]);
const { search, setSearch, terms } = useDebouncedTermSearch({
onUpdate: () => setPage(0),
});
const tags = useTagsFilter();
@@ -53,7 +46,7 @@ export default function Containers() {
useRead(
"ListAllContainers",
{
terms: searchTerms,
terms,
servers: selectedServers,
tags,
page,
+8 -11
View File
@@ -1,6 +1,11 @@
import TagsFilter from "@/components/tags/filter";
import { keepPreviousData } from "@tanstack/react-query";
import { useRead, useTagsFilter, useTemplatesQueryBehavior } from "@/lib/hooks";
import {
useDebouncedTermSearch,
useRead,
useTagsFilter,
useTemplatesQueryBehavior,
} from "@/lib/hooks";
import { usableResourcePath } from "@/lib/utils";
import {
RequiredResourceComponents,
@@ -8,7 +13,7 @@ import {
UsableResource,
} from "@/resources";
import { ICONS } from "@/lib/icons";
import { Section, useDebounce } from "mogh_ui";
import { Section } from "mogh_ui";
import { Group, Stack, Text } from "@mantine/core";
import { useEffect, useMemo, useState } from "react";
import ListPagination from "@/components/list-pagination";
@@ -18,15 +23,7 @@ import { ShowHideButton } from "mogh_ui";
import { SearchInput } from "mogh_ui";
export default function DashboardTables() {
const [search, setSearch] = useState("");
const debouncedSearch = useDebounce(search, 200);
const terms = useMemo(() => {
return debouncedSearch
.toLowerCase()
.split(" ")
.map((term) => term.trim())
.filter((term) => term);
}, [debouncedSearch]);
const { search, setSearch, terms } = useDebouncedTermSearch();
const Tables = useMemo(
() =>
+6 -12
View File
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import {
useDebouncedTermSearch,
useFilterByUpdateAvailable,
useRead,
useResourceParamType,
@@ -10,7 +11,7 @@ import {
} from "@/lib/hooks";
import { ResourceComponents, UsableResource } from "@/resources";
import { Types } from "komodo_client";
import { Page, useDebounce } from "mogh_ui";
import { Page } from "mogh_ui";
import { Group, Stack } from "@mantine/core";
import ListPagination from "@/components/list-pagination";
import { TableSkeleton } from "mogh_ui";
@@ -34,17 +35,10 @@ export default function Resources({ _type }: { _type?: UsableResource }) {
useSetTitle(name + "s");
const [page, setPage] = useState(0);
const [search, setSearch] = useState("");
const debouncedSearch = useDebounce(search, 200);
const terms = useMemo(() => {
// Set to page 0 whenever search changes
setPage(0);
return debouncedSearch
.toLowerCase()
.split(" ")
.map((term) => term.trim())
.filter((term) => term);
}, [debouncedSearch]);
const { search, setSearch, terms } = useDebouncedTermSearch({
onUpdate: () => setPage(0),
});
const [filterUpdateAvailable, toggleFilterUpdateAvailable] =
useFilterByUpdateAvailable();
+11 -15
View File
@@ -2,42 +2,38 @@ import TagsFilter from "@/components/tags/filter";
import TableTags from "@/components/tags/table";
import ListPagination from "@/components/list-pagination";
import {
useDebouncedTermSearch,
usePermissions,
useRead,
useSetTitle,
useTags,
useTagsFilter,
useWrite,
} from "@/lib/hooks";
import { UsableResource } from "@/resources";
import ResourceLink from "@/resources/link";
import { ICONS } from "@/lib/icons";
import { DataTable, SortableHeader, useDebounce } from "mogh_ui";
import { DataTable, SortableHeader } from "mogh_ui";
import { Page } from "mogh_ui";
import { SearchInput } from "mogh_ui";
import { Group, Stack, Switch } from "@mantine/core";
import { notifications } from "@mantine/notifications";
import { keepPreviousData } from "@tanstack/react-query";
import { Types } from "komodo_client";
import { useEffect, useMemo, useState } from "react";
import { useEffect, useState } from "react";
const SCHEDULE_SORT_KEYS = Object.values(Types.ScheduleSortBy);
export default function Schedules() {
useSetTitle("Schedules");
const [search, setSearch] = useState("");
const debouncedSearch = useDebounce(search, 200);
const terms = useMemo(
() =>
debouncedSearch
.toLowerCase()
.split(" ")
.map((term) => term.trim())
.filter((term) => term),
[debouncedSearch],
);
const { tags } = useTags();
const [page, setPage] = useState(0);
const { search, setSearch, terms } = useDebouncedTermSearch({
onUpdate: () => setPage(0),
});
const tags = useTagsFilter();
// Server side sort, passed up from the table.
const [sort, setSort] = useState<{
sort_by?: Types.ScheduleSortBy;
+8 -19
View File
@@ -1,19 +1,14 @@
import TerminalTargetLink from "@/pages/terminals/target-link";
import ListPagination from "@/components/list-pagination";
import { useRead, useSetTitle } from "@/lib/hooks";
import { useDebouncedTermSearch, useRead, useSetTitle } from "@/lib/hooks";
import { ICONS } from "@/lib/icons";
import { terminalLink } from "@/lib/utils";
import {
DataTable,
fmtDateWithMinutes,
SortableHeader,
useDebounce,
} from "mogh_ui";
import { DataTable, fmtDateWithMinutes, SortableHeader } from "mogh_ui";
import { Page } from "mogh_ui";
import { Group, Stack, Text } from "@mantine/core";
import { keepPreviousData } from "@tanstack/react-query";
import { Types } from "komodo_client";
import { useEffect, useMemo, useState } from "react";
import { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import DeleteTerminal from "./delete";
import BatchDeleteAllTerminals from "./batch-delete";
@@ -24,19 +19,13 @@ const TERMINAL_SORT_KEYS = Object.values(Types.TerminalSortBy);
export default function Terminals() {
useSetTitle("Terminals");
const [search, setSearch] = useState("");
const debouncedSearch = useDebounce(search, 200);
const terms = useMemo(
() =>
debouncedSearch
.toLowerCase()
.split(" ")
.map((term) => term.trim())
.filter((term) => term),
[debouncedSearch],
);
const [page, setPage] = useState(0);
const { search, setSearch, terms } = useDebouncedTermSearch({
onUpdate: () => setPage(0),
});
// Server side sort, passed up from the table.
const [sort, setSort] = useState<{
sort_by?: Types.TerminalSortBy;
+3 -14
View File
@@ -1,9 +1,8 @@
import { useRead } from "@/lib/hooks";
import { useDebouncedTermSearch, useRead } from "@/lib/hooks";
import { keepPreviousData } from "@tanstack/react-query";
import { MultiSelect } from "@mantine/core";
import { Types } from "komodo_client";
import { useDebounce } from "mogh_ui";
import { useMemo, useState } from "react";
import { useMemo } from "react";
import { UsableResource } from ".";
import { fmtResourceType } from "@/lib/formatting";
@@ -25,17 +24,7 @@ export default function ResourceMultiSelector({
templates?: Types.TemplatesQueryBehavior;
placeholder?: string;
}) {
const [search, setSearch] = useState("");
const debouncedSearch = useDebounce(search, 200);
const terms = useMemo(
() =>
debouncedSearch
.toLowerCase()
.split(" ")
.map((term) => term.trim())
.filter((term) => term),
[debouncedSearch],
);
const { search, setSearch, terms } = useDebouncedTermSearch();
const resources =
useRead(