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

1774-Config-changes---filter-by-cell #1931

Merged
merged 2 commits into from
May 22, 2024
Merged
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
17 changes: 14 additions & 3 deletions src/api/services/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ export const getAllConfigs = () =>
resolve<ConfigItem[]>(ConfigDB.get(`/configs`));

export const getConfigsTags = async () => {
const res =
await ConfigDB.get<{ key: string; value: string }[]>("/config_tags");
const res = await ConfigDB.get<{ key: string; value: string }[]>(
"/config_tags"
);
return res.data ?? [];
};

Expand Down Expand Up @@ -209,6 +210,7 @@ export type GetConfigsRelatedChangesParams = {
pageSize?: string;
sortBy?: string;
sortOrder?: "asc" | "desc";
arbitraryFilter?: Record<string, string>;
};

export async function getConfigsChanges({
Expand All @@ -224,7 +226,8 @@ export async function getConfigsChanges({
page,
pageSize,
sortBy,
sortOrder
sortOrder,
arbitraryFilter
}: GetConfigsRelatedChangesParams) {
const queryParams = new URLSearchParams();
if (id) {
Expand All @@ -248,6 +251,14 @@ export async function getConfigsChanges({
queryParams.set("type", value);
}
}
if (arbitraryFilter) {
Object.entries(arbitraryFilter).forEach(([key, value]) => {
const filterExpression = tristateOutputToQueryParamValue(value);
if (filterExpression) {
queryParams.set(key, filterExpression);
}
});
}
if (from) {
queryParams.set("from", from);
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Agents/Add/AddAgentForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
useUpdateAgentMutations
} from "../../../api/query-hooks/mutations/useUpsertAgentMutations";
import { GenerateAgent, GeneratedAgent } from "../../../api/services/agents";
import { Button } from "../../../ui/Button";
import { Button } from "../../../ui/Buttons/Button";
import { Modal } from "../../../ui/Modal";
import FormikAutocompleteDropdown from "../../Forms/Formik/FormikAutocompleteDropdown";
import FormikKeyValueMapField from "../../Forms/Formik/FormikKeyValueMapField";
Expand Down
6 changes: 3 additions & 3 deletions src/components/Agents/AgentName.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { getAgentByID, Local } from "@flanksource-ui/api/services/agents";
import { AgentItem } from "@flanksource-ui/api/types/common";
import { Badge } from "@flanksource-ui/ui/Badge/Badge";
import { useQuery } from "@tanstack/react-query";
import { ComponentProps } from "react";
import { getAgentByID, Local } from "../../api/services/agents";
import { AgentItem } from "../../api/types/common";
import { Badge } from "../../ui/Badge";

type TopologyCardAgentProps = {
agent?: AgentItem;
Expand Down
2 changes: 1 addition & 1 deletion src/components/Agents/DeleteAgentButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useCallback, useState } from "react";
import { FaCircleNotch, FaTrash } from "react-icons/fa";
import { useDeleteAgentMutations } from "../../api/query-hooks/mutations/useUpsertAgentMutations";
import { ConfirmationPromptDialog } from "../../ui/AlertDialog/ConfirmationPromptDialog";
import { Button } from "../../ui/Button";
import { Button } from "../../ui/Buttons/Button";
import { toastError, toastSuccess } from "../Toast/toast";

type DeleteAgentButtonProps = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useState } from "react";
import { GeneratedAgent } from "../../../api/services/agents";
import { Button } from "../../../ui/Button";
import { Button } from "../../../ui/Buttons/Button";
import { Modal } from "../../../ui/Modal";
import { Tab, Tabs } from "../../Tabs/Tabs";
import { AgentFormValues } from "../Add/AddAgentForm";
Expand Down
22 changes: 11 additions & 11 deletions src/components/Canary/CanaryPopup/CheckDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { useCanaryGraphQuery } from "@flanksource-ui/api/query-hooks/health";
import { HealthCheck } from "@flanksource-ui/api/types/health";
import { isCanaryUI } from "@flanksource-ui/context/Environment";
import { Age } from "@flanksource-ui/ui/Age";
import { Badge } from "@flanksource-ui/ui/Badge/Badge";
import { Stat } from "@flanksource-ui/ui/stats";
import {
capitalizeFirstLetter,
toFixedIfNecessary
} from "@flanksource-ui/utils/common";
import mixins from "@flanksource-ui/utils/mixins.module.css";
import { useAtom } from "jotai";
import React, { Suspense, useEffect, useMemo, useRef } from "react";
import { useMediaQuery } from "react-responsive";
import { useSearchParams } from "react-router-dom";
import { useCanaryGraphQuery } from "../../../api/query-hooks/health";
import { HealthCheck } from "../../../api/types/health";
import { isCanaryUI } from "../../../context/Environment";
import { Age } from "../../../ui/Age";
import { Badge } from "../../../ui/Badge";
import { Stat } from "../../../ui/stats";
import {
capitalizeFirstLetter,
toFixedIfNecessary
} from "../../../utils/common";
import mixins from "../../../utils/mixins.module.css";
import { DropdownStandaloneWrapper } from "../../Dropdown/StandaloneWrapper";
import { TimeRange, timeRanges } from "../../Dropdown/TimeRange";
import { Head } from "../../Head/Head";
Expand Down
2 changes: 1 addition & 1 deletion src/components/Canary/CanaryPopup/CheckRunNow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
} from "@flanksource-ui/api/services/topology";
import { HealthCheck } from "@flanksource-ui/api/types/health";
import { useUserAccessStateContext } from "@flanksource-ui/context/UserAccessContext/UserAccessContext";
import { Button } from "@flanksource-ui/ui/Button";
import { Button } from "@flanksource-ui/ui/Buttons/Button";
import { useMutation } from "@tanstack/react-query";
import { FaSpinner } from "react-icons/fa";
import { VscDebugRerun } from "react-icons/vsc";
Expand Down
4 changes: 2 additions & 2 deletions src/components/Canary/CanaryPopup/CheckTitle.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { HealthCheck } from "@flanksource-ui/api/types/health";
import { Badge } from "@flanksource-ui/ui/Badge/Badge";
import clsx from "clsx";
import React from "react";
import { HealthCheck } from "../../../api/types/health";
import { Badge } from "../../../ui/Badge";
import AgentName from "../../Agents/AgentName";
import { Icon } from "../../Icon";

Expand Down
4 changes: 2 additions & 2 deletions src/components/Canary/Columns/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { HealthCheck } from "@flanksource-ui/api/types/health";
import { Badge } from "@flanksource-ui/ui/Badge/Badge";
import { CellContext } from "@tanstack/react-table";
import clsx from "clsx";
import dayjs from "dayjs";
import { HealthCheck } from "../../../api/types/health";
import { Badge } from "../../../ui/Badge";
import { Status } from "../../Status";
import { GetName } from "../data";
import style from "../index.module.css";
Expand Down
82 changes: 69 additions & 13 deletions src/components/Configs/Changes/ConfigChangeHistory/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useGetConfigChangesById } from "@flanksource-ui/api/query-hooks/useGetC
import { ConfigChange } from "@flanksource-ui/api/types/configs";
import GetUserAvatar from "@flanksource-ui/components/Users/GetUserAvatar";
import { PaginationOptions } from "@flanksource-ui/ui/DataTable";
import FilterByCellValue from "@flanksource-ui/ui/DataTable/FilterByCellValue";
import { DateCell } from "@flanksource-ui/ui/table";
import { SortingState, Updater } from "@tanstack/react-table";
import { ColumnDef } from "@tanstack/table-core";
Expand All @@ -11,6 +12,10 @@ import { DataTable } from "../../../index";
import ConfigLink from "../../ConfigLink/ConfigLink";
import { ConfigDetailChangeModal } from "../ConfigDetailsChanges/ConfigDetailsChanges";

export const paramsToReset = {
configChanges: ["pageIndex", "pageSize"]
};

const columns: ColumnDef<ConfigChange>[] = [
{
header: "Created",
Expand All @@ -30,11 +35,18 @@ const columns: ColumnDef<ConfigChange>[] = [
enableHiding: true,
enableSorting: false,
cell: function ConfigLinkCell({ row }) {
const configId = row.original.config_id;
return (
<ConfigLink
config={row.original.config}
configId={row.original.config_id}
/>
<FilterByCellValue
filterValue={configId}
paramKey="id"
paramsToReset={paramsToReset.configChanges}
>
<ConfigLink
config={row.original.config}
configId={row.original.config_id}
/>
</FilterByCellValue>
);
},
size: 84
Expand All @@ -45,10 +57,16 @@ const columns: ColumnDef<ConfigChange>[] = [
cell: function ConfigChangeTypeCell({ row, column }) {
const changeType = row?.getValue(column.id) as string;
return (
<div className="text-ellipsis overflow-hidden space-x-1">
<ChangeIcon change={row.original} />
<span>{changeType}</span>
</div>
<FilterByCellValue
filterValue={changeType}
paramKey="changeType"
paramsToReset={paramsToReset.configChanges}
>
<div className="text-ellipsis overflow-hidden space-x-1">
<ChangeIcon change={row.original} />
<span>{changeType}</span>
</div>
</FilterByCellValue>
);
},
maxSize: 70
Expand All @@ -59,7 +77,20 @@ const columns: ColumnDef<ConfigChange>[] = [
meta: {
cellClassName: "text-ellipsis overflow-hidden"
},
maxSize: 150
maxSize: 150,
cell: ({ getValue }) => {
const summary = getValue<string>();

return (
<FilterByCellValue
filterValue={summary}
paramKey="summary"
paramsToReset={paramsToReset.configChanges}
>
{summary}
</FilterByCellValue>
);
}
},
{
header: "Created By",
Expand All @@ -68,18 +99,43 @@ const columns: ColumnDef<ConfigChange>[] = [
cell: ({ row }) => {
const userID = row.original.created_by;
if (userID) {
return <GetUserAvatar userID={userID} />;
return (
<FilterByCellValue
filterValue={userID}
paramKey="created_by"
paramsToReset={paramsToReset.configChanges}
>
<GetUserAvatar userID={userID} />
</FilterByCellValue>
);
}

const externalCreatedBy = row.original.external_created_by;
if (externalCreatedBy) {
return <span>{externalCreatedBy}</span>;
return (
<FilterByCellValue
filterValue={externalCreatedBy}
paramKey="external_created_by"
paramsToReset={paramsToReset.configChanges}
>
<span>{externalCreatedBy}</span>
</FilterByCellValue>
);
}

const source = row.original.source;
if (source) {
return <span>{source}</span>;
return (
<FilterByCellValue
filterValue={source}
paramKey="source"
paramsToReset={paramsToReset.configChanges}
>
<span>{source}</span>
</FilterByCellValue>
);
}


return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,89 @@
import ClosableBadge from "@flanksource-ui/ui/Badge/ClosableBadge";
import clsx from "clsx";
import { useCallback } from "react";
import { FaBan } from "react-icons/fa";
import { useSearchParams } from "react-router-dom";
import { paramsToReset } from "../ConfigChangeHistory";
import { ChangesTypesDropdown } from "./ChangeTypesDropdown";
import { ConfigChangeSeverity } from "./ConfigChangeSeverity";
import ConfigChangesDateRangeFilter from "./ConfigChangesDateRangeFIlter";
import ConfigTypesTristateDropdown from "./ConfigTypesTristateDropdown";

type FilterBadgeProps = {
filters: string;
paramKey: string;
};

function FilterBadge({ filters, paramKey }: FilterBadgeProps) {
const [params, setParams] = useSearchParams();

const onRemove = useCallback(
(key: string, value: string) => {
const currentValue = params.get(key);
const arrayValue = currentValue?.split(",") || [];
const newValues = arrayValue.filter(
(v) => decodeURIComponent(v) !== decodeURIComponent(value)
);
if (newValues.length === 0) {
params.delete(key);
} else {
const updateValue = newValues.join(",");
params.set(key, updateValue);
}
paramsToReset.configChanges.forEach((param) => params.delete(param));
setParams(params);
},
[params, setParams]
);

const filtersArray = filters.split(",");

return (
<>
{filtersArray.map((filter) => (
<ClosableBadge
color="gray"
key={filter}
className="flex flex-row gap-1 px-2 text-xs"
onRemove={() => onRemove(paramKey, filter)}
>
<span className="text-gray-500 font-semibold">{paramKey}:</span>
<span className="text-gray-500">
{decodeURIComponent(filter.split(":")[0])}
</span>
<span>{filter.split(":")[1] === "-1" && <FaBan />}</span>
</ClosableBadge>
))}
</>
);
}

type ConfigChangeFiltersProps = React.HTMLProps<HTMLDivElement> & {
paramsToReset?: string[];
arbitraryFilters?: Record<string, string>;
};

export function ConfigChangeFilters({
className,
paramsToReset = [],
arbitraryFilters,
...props
}: ConfigChangeFiltersProps) {
return (
<div className={clsx("flex flex-row gap-2", className)} {...props}>
<ConfigTypesTristateDropdown
paramsToReset={[...paramsToReset, "configType"]}
/>
<ChangesTypesDropdown paramsToReset={paramsToReset} />
<ConfigChangeSeverity paramsToReset={paramsToReset} />
<ConfigChangesDateRangeFilter paramsToReset={paramsToReset} />
<div className="flex flex-col gap-2">
<div className={clsx("flex flex-row gap-1", className)} {...props}>
<ConfigTypesTristateDropdown
paramsToReset={[...paramsToReset, "configType"]}
/>
<ChangesTypesDropdown paramsToReset={paramsToReset} />
<ConfigChangeSeverity paramsToReset={paramsToReset} />
<ConfigChangesDateRangeFilter paramsToReset={paramsToReset} />
</div>
<div className="flex flex-wrap gap-2">
{Object.entries(arbitraryFilters ?? {}).map(([key, value]) => (
<FilterBadge filters={value} key={value} paramKey={key} />
))}
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button } from "../../ui/Button";
import { Button } from "../../ui/Buttons/Button";

type Props = {
selectedCount: number;
Expand Down
2 changes: 1 addition & 1 deletion src/components/Configs/ConfigList/ConfigListColumn.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Status } from "@flanksource-ui/components/Status";
import { Badge } from "@flanksource-ui/ui/Badge";
import { Badge } from "@flanksource-ui/ui/Badge/Badge";
import { CellContext, ColumnDef, Row } from "@tanstack/react-table";
import React from "react";
import { FaTrash } from "react-icons/fa";
Expand Down
2 changes: 1 addition & 1 deletion src/components/Configs/ConfigSummary/ConfigSummaryList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
StatusInfo,
StatusLine
} from "@flanksource-ui/components/StatusLine/StatusLine";
import { Badge } from "@flanksource-ui/ui/Badge";
import { Badge } from "@flanksource-ui/ui/Badge/Badge";
import { CountBadge } from "@flanksource-ui/ui/Badge/CountBadge";
import { DataTable } from "@flanksource-ui/ui/DataTable";
import { CellContext, ColumnDef, Row } from "@tanstack/react-table";
Expand Down
Loading
Loading