Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2165-Catalog---Add-includeexclude-shortcuts-in-Catalog-tag-list #2188

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/api/query-hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,16 +200,20 @@ export function prepareConfigListQuery({
}
if (labels) {
labels.split(",").forEach((label) => {
const [k, v] = label.split("__:__");
const [k, v] = label.split("____");
const [realValue, operand] = v.split(":");
const operator = parseInt(operand) === -1 ? "neq" : "eq";
if (!isNull(v)) {
filterQueries.push(`labels->>${k}=eq.${encodeURIComponent(v)}`);
filterQueries.push(
`labels->>${k}.${operator}.${encodeURIComponent(realValue)}`
);
} else {
filterQueries.push(`labels->>${k}=is.null`);
}
});
}
if (filterQueries.length) {
query = `${query}&${filterQueries.join("&")}`;
query = `${query}&or=(${filterQueries.join(",")})`;
}
}
if (sortBy && sortOrder) {
Expand Down
28 changes: 18 additions & 10 deletions src/api/query-hooks/useConfigSummaryQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,23 @@ import { useSearchParams } from "react-router-dom";
import { ConfigSummaryRequest, getConfigsSummary } from "../services/configs";
import { ConfigSummary } from "../types/configs";

export function useLabelFiltersFromParams() {
const [searchParams] = useSearchParams();
const labels = searchParams.get("labels") ?? undefined;

return useMemo(() => {
if (labels) {
return labels.split(",").reduce((acc, label) => {
const [filterValue, operand] = label.split(":");
const [key, value] = filterValue.split("____");
const symbol = parseInt(operand) === -1 ? "!" : "";
return { ...acc, [key]: `${symbol}${value}` };
}, {});
}
return undefined;
}, [labels]);
}

export function useConfigSummaryQuery({
enabled = true
}: UseQueryOptions<ConfigSummary[]> = {}) {
Expand All @@ -18,23 +35,14 @@ export function useConfigSummaryQuery({
groupBy: "config_class,type"
});
const hideDeletedConfigs = useHideDeletedConfigs();
const labels = searchParams.get("labels") ?? undefined;
const status = searchParams.get("status") ?? undefined;
const health = searchParams.get("health") ?? undefined;

const [favorites] = useAtom(configTypesFavorites);

const groupBy = useGroupBySearchParam();

const filterSummaryByLabel = useMemo(() => {
if (labels) {
return labels.split(",").reduce((acc, label) => {
const [key, value] = label.split("__:__");
return { ...acc, [key]: value };
}, {});
}
return undefined;
}, [labels]);
const filterSummaryByLabel = useLabelFiltersFromParams();

const req: ConfigSummaryRequest = {
// group by config_class is always done on the frontend
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type ConfigListTagsCellProps<
hideGroupByView?: boolean;
label?: string;
enableFilterByTag?: boolean;
filterByTagParamKey?: string;
};

export default function ConfigListTagsCell<
Expand All @@ -21,7 +22,8 @@ export default function ConfigListTagsCell<
row,
getValue,
hideGroupByView = false,
enableFilterByTag = false
enableFilterByTag = false,
filterByTagParamKey = "tags"
}: ConfigListTagsCellProps<T>): JSX.Element | null {
const [params, setParams] = useSearchParams();

Expand Down Expand Up @@ -67,10 +69,10 @@ export default function ConfigListTagsCell<
.join(",");

// Update the URL
params.set("tags", updatedValue);
params.set(filterByTagParamKey, updatedValue);
setParams(params);
},
[enableFilterByTag, params, setParams]
[enableFilterByTag, filterByTagParamKey, params, setParams]
);

const groupByProp = decodeURIComponent(params.get("groupByProp") ?? "");
Expand Down
15 changes: 11 additions & 4 deletions src/components/Configs/ConfigList/ConfigListColumn.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Status } from "@flanksource-ui/components/Status";
import { Badge } from "@flanksource-ui/ui/Badge/Badge";
import ChangeCountIcon, {
CountBar
} from "@flanksource-ui/ui/Icons/ChangeCount";
import { CellContext, ColumnDef, Row } from "@tanstack/react-table";
import React from "react";
import { FaTrash } from "react-icons/fa";
Expand All @@ -17,9 +20,6 @@ import ConfigListCostCell, {
import ConfigListDateCell from "./Cells/ConfigListDateCell";
import ConfigListNameCell from "./Cells/ConfigListNameCell";
import ConfigListTagsCell from "./Cells/ConfigListTagsCell";
import ChangeCountIcon, {
CountBar
} from "@flanksource-ui/ui/Icons/ChangeCount";

export const configListColumns: ColumnDef<ConfigItem, any>[] = [
{
Expand Down Expand Up @@ -114,7 +114,14 @@ export const configListColumns: ColumnDef<ConfigItem, any>[] = [
{
header: "Tags",
accessorKey: "tags",
cell: React.memo(ConfigListTagsCell),
cell: React.memo((props) => (
<ConfigListTagsCell
{...props}
hideGroupByView
enableFilterByTag
filterByTagParamKey="labels"
/>
)),
aggregatedCell: "",
maxSize: 300,
minSize: 100
Expand Down
1 change: 1 addition & 0 deletions src/components/Configs/ConfigSummary/ConfigSummaryList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ export default function ConfigSummaryList({

const handleRowClick = useCallback(
(row: Row<ConfigSummary>) => {
params.delete("labels");
if (groupBy.includes("type")) {
const { type } = row.original;
params.set("configType", type);
Expand Down
52 changes: 25 additions & 27 deletions src/components/Configs/ConfigsListFilters/ConfigLabelsDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { useGetConfigLabelsListQuery } from "@flanksource-ui/api/query-hooks";
import TristateReactSelect, {
TriStateOptions
} from "@flanksource-ui/ui/Dropdowns/TristateReactSelect";
import { useField } from "formik";
import { useMemo } from "react";
import { ReactSelectDropdown } from "../../ReactSelectDropdown";

type Props = {
searchParamKey?: string;
Expand All @@ -16,30 +18,33 @@ export function ConfigLabelsDropdown({ searchParamKey = "labels" }: Props) {

const labelItems = useMemo(() => {
if (data && Array.isArray(data)) {
const options = data.map((tag) => ({
label: (
<div className="block space-x-1 text-sm">
<span className="w-auto text-gray-600">{tag.key}:</span>
<span className="w-full">{tag.value}</span>
</div>
),
value: `${tag.key}__:__${tag.value}`
}));
return [{ label: "All", value: "All" }, ...options];
return data.map(
(tag) =>
({
label: (
<span className="space-x-1 text-sm">
<span className="text-gray-600">{tag.key}:</span>
<span>{tag.value}</span>
</span>
),
value: `${tag.key}____${tag.value}`,
id: `${tag.key}____${tag.value}`
}) satisfies TriStateOptions
);
} else {
// Adding this console.error to help debug the issue I noticed happening
// inside the Saas, that's leading to the catalog page crashing
console.error("Invalid data for ConfigLabelsDropdown", data);
return [{ label: "All", value: "All" }];
return [];
}
}, [data]);

return (
<ReactSelectDropdown
items={labelItems}
name="type"
<TristateReactSelect
isLoading={isLoading}
options={labelItems}
onChange={(value) => {
if (value && value !== "All") {
if (value && value !== "all") {
field.onChange({
target: { name: searchParamKey, value: value }
});
Expand All @@ -49,17 +54,10 @@ export function ConfigLabelsDropdown({ searchParamKey = "labels" }: Props) {
});
}
}}
value={field.value ?? "All"}
className="w-auto max-w-[38rem]"
dropDownClassNames="w-auto max-w-[38rem] left-0"
hideControlBorder
isMulti
prefix={
<div className="mr-2 whitespace-nowrap text-xs text-gray-500">
Labels:
</div>
}
isLoading={isLoading}
minMenuWidth="400px"
value={field.value}
className="w-auto max-w-[400px]"
label={"Labels"}
/>
);
}
Loading