From aa8d25df13af2bda895074a6048ac9831158d62b Mon Sep 17 00:00:00 2001 From: Maina Wycliffe Date: Tue, 4 Jun 2024 14:46:19 +0300 Subject: [PATCH 1/5] refactor: stop memoizing the filters component --- src/components/Configs/ConfigsListFilters/index.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/Configs/ConfigsListFilters/index.tsx b/src/components/Configs/ConfigsListFilters/index.tsx index 48290a0af..8155bd33a 100644 --- a/src/components/Configs/ConfigsListFilters/index.tsx +++ b/src/components/Configs/ConfigsListFilters/index.tsx @@ -1,6 +1,5 @@ import FormikFilterForm from "@flanksource-ui/components/Forms/FormikFilterForm"; import { debounce } from "lodash"; -import React from "react"; import { useSearchParams } from "react-router-dom"; import { TextInputClearable } from "../../../ui/FormControls/TextInputClearable"; import { ConfigListToggledDeletedItems } from "../ConfigListToggledDeletedItems/ConfigListToggledDeletedItems"; @@ -10,7 +9,7 @@ import { ConfigLabelsDropdown } from "./ConfigLabelsDropdown"; import { ConfigStatusDropdown } from "./ConfigStatusDropdown"; import { ConfigTypesDropdown } from "./ConfigTypesDropdown"; -function ConfigsListFilterControls() { +export default function ConfigsListFilters() { const [params, setParams] = useSearchParams(); return ( @@ -52,6 +51,3 @@ function ConfigsListFilterControls() { ); } - -const ConfigsListFilters = React.memo(ConfigsListFilterControls); -export default ConfigsListFilters; From 908772b8d025f5e368761addb4ecbb83978eef92 Mon Sep 17 00:00:00 2001 From: Maina Wycliffe Date: Tue, 4 Jun 2024 15:01:39 +0300 Subject: [PATCH 2/5] fix: move config search field to use formik --- .../Configs/ConfigsListFilters/index.tsx | 16 ++------ .../Formik/FormikSearchInputClearable.tsx | 41 +++++++++++++++++++ 2 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 src/components/Forms/Formik/FormikSearchInputClearable.tsx diff --git a/src/components/Configs/ConfigsListFilters/index.tsx b/src/components/Configs/ConfigsListFilters/index.tsx index 8155bd33a..a2719484d 100644 --- a/src/components/Configs/ConfigsListFilters/index.tsx +++ b/src/components/Configs/ConfigsListFilters/index.tsx @@ -1,7 +1,5 @@ +import FormikSearchInputClearable from "@flanksource-ui/components/Forms/Formik/FormikSearchInputClearable"; import FormikFilterForm from "@flanksource-ui/components/Forms/FormikFilterForm"; -import { debounce } from "lodash"; -import { useSearchParams } from "react-router-dom"; -import { TextInputClearable } from "../../../ui/FormControls/TextInputClearable"; import { ConfigListToggledDeletedItems } from "../ConfigListToggledDeletedItems/ConfigListToggledDeletedItems"; import ConfigGroupByDropdown from "./ConfigGroupByDropdown"; import { ConfigHealthyDropdown } from "./ConfigHealthyDropdown"; @@ -10,8 +8,6 @@ import { ConfigStatusDropdown } from "./ConfigStatusDropdown"; import { ConfigTypesDropdown } from "./ConfigTypesDropdown"; export default function ConfigsListFilters() { - const [params, setParams] = useSearchParams(); - return ( - { - const query = e.target.value || ""; - params.set("search", query); - setParams(params); - }, 200)} - className="w-80" + diff --git a/src/components/Forms/Formik/FormikSearchInputClearable.tsx b/src/components/Forms/Formik/FormikSearchInputClearable.tsx new file mode 100644 index 000000000..c98edb3ae --- /dev/null +++ b/src/components/Forms/Formik/FormikSearchInputClearable.tsx @@ -0,0 +1,41 @@ +import { TextInputClearable } from "@flanksource-ui/ui/FormControls/TextInputClearable"; +import { useField } from "formik"; +import { debounce } from "lodash"; +import { ComponentProps } from "react"; + +type FormikSearchInputClearableProps = { + name: string; + className?: string; +} & ComponentProps; + +export default function FormikSearchInputClearable({ + name, + required = false, + className = "flex flex-col w-80", + type = "text", + ...props +}: FormikSearchInputClearableProps) { + const [field] = useField({ + name, + type: type, + required, + validate: (value) => { + if (required && !value) { + return "This field is required"; + } + } + }); + + const onChange = debounce((value) => { + field.onChange({ + target: { + value, + name + } + }); + }, 400); + + return ( + + ); +} From 658ad83b08d6654facda343afc3070736c816a2b Mon Sep 17 00:00:00 2001 From: Maina Wycliffe Date: Mon, 3 Jun 2024 15:29:46 +0300 Subject: [PATCH 3/5] fix: fix incorrect name for health filter in catalog name Fixes #1992 --- .../Configs/ConfigsListFilters/ConfigHealthyDropdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Configs/ConfigsListFilters/ConfigHealthyDropdown.tsx b/src/components/Configs/ConfigsListFilters/ConfigHealthyDropdown.tsx index 467bb32ea..561c25759 100644 --- a/src/components/Configs/ConfigsListFilters/ConfigHealthyDropdown.tsx +++ b/src/components/Configs/ConfigsListFilters/ConfigHealthyDropdown.tsx @@ -36,7 +36,7 @@ export function ConfigHealthyDropdown({ paramsKey = "health" }: ConfigTypesDropdownProps) { const [field] = useField({ - name: "configType" + name: paramsKey }); return ( From a3053272e6014a8a5aefc1f30c41f0710f96cea6 Mon Sep 17 00:00:00 2001 From: Maina Wycliffe Date: Tue, 4 Jun 2024 14:34:52 +0300 Subject: [PATCH 4/5] fix: fix filter form syncing issues from url --- src/components/Forms/FormikFilterForm.tsx | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/components/Forms/FormikFilterForm.tsx b/src/components/Forms/FormikFilterForm.tsx index a8a6690cf..b127d6ee2 100644 --- a/src/components/Forms/FormikFilterForm.tsx +++ b/src/components/Forms/FormikFilterForm.tsx @@ -31,23 +31,17 @@ function FormikChangesListener({ paramsToReset.forEach((param) => searchParams.delete(param)); setSearchParams(searchParams); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [values]); + }, [values, setFieldValue]); // reset form values, if the query params change useEffect(() => { filterFields.forEach((field) => { const value = searchParams.get(field); const defaultValue = defaultFieldValues?.[field] ?? undefined; - if (!value) { - if (defaultValue) { - setFieldValue(field, defaultValue); - } else { - setFieldValue(field, undefined); - } - } + setFieldValue(field, value ?? defaultValue); }, []); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [searchParams]); + }, [defaultFieldValues, filterFields, searchParams]); // eslint-disable-next-line react/jsx-no-useless-fragment return <>{children}; From 2e9a590c7f2970e2f2a7d23567b212a94a3227fc Mon Sep 17 00:00:00 2001 From: Maina Wycliffe Date: Tue, 4 Jun 2024 14:46:49 +0300 Subject: [PATCH 5/5] fix: filter config list by health on click of the config item fix: fix issue with config summary chore: fix health filter link fix: fix filters not working --- .../Cells/ConfigSummaryHealthCells.tsx | 95 +++++++++++++++++++ .../ConfigSummary/ConfigSummaryList.tsx | 75 ++------------- 2 files changed, 101 insertions(+), 69 deletions(-) create mode 100644 src/components/Configs/ConfigSummary/Cells/ConfigSummaryHealthCells.tsx diff --git a/src/components/Configs/ConfigSummary/Cells/ConfigSummaryHealthCells.tsx b/src/components/Configs/ConfigSummary/Cells/ConfigSummaryHealthCells.tsx new file mode 100644 index 000000000..66a9188b3 --- /dev/null +++ b/src/components/Configs/ConfigSummary/Cells/ConfigSummaryHealthCells.tsx @@ -0,0 +1,95 @@ +import { ConfigSummary } from "@flanksource-ui/api/types/configs"; +import { + StatusInfo, + StatusLine +} from "@flanksource-ui/components/StatusLine/StatusLine"; +import { CellContext } from "@tanstack/react-table"; +import { useMemo } from "react"; +import { useSearchParams } from "react-router-dom"; +import { getConfigStatusColor } from "../ConfigSummaryList"; + +export function ConfigSummaryHealthCell({ + getValue, + row +}: CellContext) { + const [searchParams] = useSearchParams(); + + const value = getValue(); + const type = row.original.type; + const groupBy = searchParams.get("groupBy"); + + const urlBase = useMemo(() => { + if (groupBy) { + return `/catalog?groupBy=${groupBy}&configType=${type}`; + } + return `/catalog?configType=${type}`; + }, [groupBy, type]); + + const statusLines = useMemo(() => { + const data: StatusInfo[] = Object.entries(value ?? {}).map( + ([key, value]) => { + return { + label: value, + url: `${urlBase}&health=${key}:1`, + color: getConfigStatusColor({ + [key]: value + } as ConfigSummary["health"]) + }; + } + ); + return data; + }, [urlBase, value]); + + if (!value) { + return null; + } + + return ( +
{ + e.preventDefault(); + e.stopPropagation(); + }} + > + +
+ ); +} + +export function ConfigSummaryHealthAggregateCell({ + row +}: CellContext) { + const value = row.subRows.reduce((acc, row) => { + const health = row.original.health; + if (health) { + Object.entries(health).forEach(([key, value]) => { + acc[key] = (acc[key] || 0) + value; + }); + } + return acc; + }, {} as Record); + + const statusLines = useMemo(() => { + const data: StatusInfo[] = Object.entries(value ?? {}).map( + ([key, value]) => { + return { + label: value.toString(), + color: getConfigStatusColor({ + [key]: value + } as ConfigSummary["health"]) + }; + } + ); + return data; + }, [value]); + + if (!value) { + return null; + } + return ( +
+ +
+ ); +} diff --git a/src/components/Configs/ConfigSummary/ConfigSummaryList.tsx b/src/components/Configs/ConfigSummary/ConfigSummaryList.tsx index 4f1dcfe97..d6935f408 100644 --- a/src/components/Configs/ConfigSummary/ConfigSummaryList.tsx +++ b/src/components/Configs/ConfigSummary/ConfigSummaryList.tsx @@ -1,8 +1,4 @@ import { ConfigSummary } from "@flanksource-ui/api/types/configs"; -import { - StatusInfo, - StatusLine -} from "@flanksource-ui/components/StatusLine/StatusLine"; import { Badge } from "@flanksource-ui/ui/Badge/Badge"; import { CountBadge } from "@flanksource-ui/ui/Badge/CountBadge"; import { DataTable } from "@flanksource-ui/ui/DataTable"; @@ -15,6 +11,10 @@ import ConfigListCostCell from "../ConfigList/Cells/ConfigListCostCell"; import ConfigListDateCell from "../ConfigList/Cells/ConfigListDateCell"; import ConfigsTypeIcon from "../ConfigsTypeIcon"; import ConfigInsightsIcon from "../Insights/ConfigInsightsIcon"; +import { + ConfigSummaryHealthAggregateCell, + ConfigSummaryHealthCell +} from "./Cells/ConfigSummaryHealthCells"; export function getConfigStatusColor(health?: ConfigSummary["health"]) { if (!health) { @@ -111,71 +111,8 @@ const configSummaryColumns: ColumnDef[] = [ accessorKey: "health", minSize: 50, maxSize: 100, - cell: ({ getValue }: CellContext) => { - const value = getValue(); - - // eslint-disable-next-line react-hooks/rules-of-hooks - const statusLines = useMemo(() => { - const data: StatusInfo[] = Object.entries(value ?? {}).map( - ([key, value]) => { - return { - label: value, - // @ts-ignore - color: getConfigStatusColor({ - [key]: value - }) - }; - } - ); - return data; - }, [value]); - - if (!value) { - return null; - } - - return ( -
- -
- ); - }, - aggregatedCell: ({ row }: CellContext) => { - const value = row.subRows.reduce((acc, row) => { - const health = row.original.health; - if (health) { - Object.entries(health).forEach(([key, value]) => { - acc[key] = (acc[key] || 0) + value; - }); - } - return acc; - }, {} as Record); - - // eslint-disable-next-line react-hooks/rules-of-hooks - const statusLines = useMemo(() => { - const data: StatusInfo[] = Object.entries(value ?? {}).map( - ([key, value]) => { - return { - label: value.toString(), - // @ts-ignore - color: getConfigStatusColor({ - [key]: value - }) - }; - } - ); - return data; - }, [value]); - - if (!value) { - return null; - } - return ( -
- -
- ); - } + cell: ConfigSummaryHealthCell, + aggregatedCell: ConfigSummaryHealthAggregateCell }, { header: "analysis",