Skip to content

Commit

Permalink
feat: time viewer for config changes
Browse files Browse the repository at this point in the history
Fixes #93
  • Loading branch information
mainawycliffe committed Oct 29, 2024
1 parent 171bb82 commit 2e52653
Show file tree
Hide file tree
Showing 8 changed files with 1,277 additions and 112 deletions.
1,071 changes: 1,015 additions & 56 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"react-use-size": "^3.0.3",
"react-windowed-select": "^5.2.0",
"reactflow": "^11.11.3",
"reaviz": "^15.18.5",
"recharts": "2.1.12",
"strip-ansi": "^7.1.0",
"tailwindcss": "^3.4.1",
Expand Down
57 changes: 14 additions & 43 deletions src/components/Configs/Changes/ConfigChangeTable.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useGetConfigChangesById } from "@flanksource-ui/api/query-hooks/useGetConfigChangesByConfigChangeIdQuery";
import { ConfigChange } from "@flanksource-ui/api/types/configs";
import GetUserAvatar from "@flanksource-ui/components/Users/GetUserAvatar";
import { Age } from "@flanksource-ui/ui/Age";
Expand All @@ -7,10 +6,8 @@ import { ChangeIcon } from "@flanksource-ui/ui/Icons/ChangeIcon";
import MRTDataTable from "@flanksource-ui/ui/MRTDataTable/MRTDataTable";
import { CellContext } from "@tanstack/react-table";
import { MRT_ColumnDef } from "mantine-react-table";
import { useState } from "react";
import ConfigLink from "../ConfigLink/ConfigLink";
import MRTConfigListTagsCell from "../ConfigList/Cells/MRTConfigListTagsCell";
import { ConfigDetailChangeModal } from "./ConfigDetailsChanges/ConfigDetailsChanges";

export const paramsToReset = {
configChanges: ["pageIndex", "pageSize"]
Expand Down Expand Up @@ -190,53 +187,27 @@ type ConfigChangeTableProps = {
isLoading?: boolean;
totalRecords: number;
numberOfPages: number;
onRowClick?: (row: ConfigChange) => void;
};

export function ConfigChangeTable({
data,
isLoading,
totalRecords,
numberOfPages
numberOfPages,
onRowClick = () => {}
}: ConfigChangeTableProps) {
const [selectedConfigChange, setSelectedConfigChange] =
useState<ConfigChange>();
const [modalIsOpen, setModalIsOpen] = useState(false);

const { data: configChange, isLoading: changeLoading } =
useGetConfigChangesById(
selectedConfigChange?.id!,
selectedConfigChange?.config_id!,
{
enabled: !!selectedConfigChange
}
);

return (
<>
<MRTDataTable
columns={configChangesColumn}
data={data}
isLoading={isLoading}
enableServerSideSorting
totalRowCount={totalRecords}
manualPageCount={numberOfPages}
enableServerSidePagination
disableHiding
onRowClick={(row) => {
setSelectedConfigChange(row);
setModalIsOpen(true);
}}
/>
{configChange && (
<ConfigDetailChangeModal
isLoading={changeLoading}
open={modalIsOpen}
setOpen={(open) => {
setModalIsOpen(open);
}}
changeDetails={configChange}
/>
)}
</>
<MRTDataTable
columns={configChangesColumn}
data={data}
isLoading={isLoading}
enableServerSideSorting
totalRowCount={totalRecords}
manualPageCount={numberOfPages}
enableServerSidePagination
disableHiding
onRowClick={onRowClick}
/>
);
}
147 changes: 147 additions & 0 deletions src/components/Configs/Changes/ConfigChangesGraph.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { ConfigChange } from "@flanksource-ui/api/types/configs";
import { Age } from "@flanksource-ui/ui/Age";
import { ChangeIcon } from "@flanksource-ui/ui/Icons/ChangeIcon";
import { relativeDateTime } from "@flanksource-ui/utils/date";
import dayjs from "dayjs";
import { useMemo } from "react";
import {
ChartShallowDataShape,
ChartTooltip,
LinearXAxis,
LinearXAxisTickLabel,
LinearXAxisTickLine,
LinearXAxisTickSeries,
LinearYAxis,
LinearYAxisTickLabel,
LinearYAxisTickSeries,
ScatterPlot,
ScatterPoint,
ScatterSeries
} from "reaviz";
import ConfigsTypeIcon from "../ConfigsTypeIcon";

type ConfigChangesGraphProps = {
changes: ConfigChange[];
onItemClicked?: (change: ConfigChange) => void;
};

export default function ConfigChangesGraph({
changes,
onItemClicked = () => {}
}: ConfigChangesGraphProps) {
const data: ChartShallowDataShape[] = useMemo(() => {
// group changes by config_id, then map to a scatter plot point
return changes.map((change) => {
return {
key: dayjs(change.first_observed).toDate(),
data: change.config?.name!,
metadata: change
};
});
}, [changes]);

return (
<div className="h-full w-full">
<ScatterPlot
data={data}
yAxis={
<LinearYAxis
type="category"
tickSeries={
<LinearYAxisTickSeries
label={
<LinearYAxisTickLabel
padding={3}
fontSize={12}
format={(v) => {
return v;
}}
/>
}
/>
}
/>
}
xAxis={
<LinearXAxis
type="value"
scale={{ type: "time" }}
tickSeries={
<LinearXAxisTickSeries
line={<LinearXAxisTickLine position="center" />}
label={
<LinearXAxisTickLabel
padding={3}
format={(v) => {
return relativeDateTime(v);
}}
/>
}
/>
}
/>
}
series={
<ScatterSeries
point={
<ScatterPoint
tooltip={
<ChartTooltip
followCursor={true}
content={(data: any) => {
const count = (data.metadata as ConfigChange).count;
const firstObserved = (data.metadata as ConfigChange)
.first_observed;
const summary = (data.metadata as ConfigChange).summary;

return (
<div className="flex flex-col gap-1 rounded-lg bg-gray-100 p-2 text-black shadow-sm">
<ConfigsTypeIcon
config={(data.metadata as ConfigChange).config}
>
{(data.metadata as ConfigChange).config?.name}
</ConfigsTypeIcon>
<div className="flex flex-col gap-1">
<div className="flex flex-row items-center justify-center gap-2 text-xs">
<span className="flex flex-row items-center gap-1 font-semibold">
<ChangeIcon change={data.metadata} />
{(data.metadata as ConfigChange).change_type}
</span>
<span className="font-semibold">
<Age
from={
(data.metadata as ConfigChange)
.first_observed
}
/>
{(count || 1) > 1 && (
<span className="inline-block pl-1 text-gray-500">
(x{count} over <Age from={firstObserved} />)
</span>
)}
</span>
</div>
<p>{summary}</p>
</div>
</div>
);
}}
/>
}
className={"bg-gray-500"}
size={20}
symbol={(data) => {
const change = data.metadata;
return <ChangeIcon change={change} />;
}}
onClick={(data) => {
onItemClicked(data.metadata as ConfigChange);
}}
/>
}
/>
}
/>
</div>
);
}
34 changes: 34 additions & 0 deletions src/components/Configs/Changes/ConfigChangesViewToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Switch } from "@flanksource-ui/ui/FormControls/Switch";
import { useAtom } from "jotai";
import { atomWithStorage } from "jotai/utils";

export type GraphType = "Table" | "Graph";
export const configChangesViewToggle = atomWithStorage<GraphType>(
"configChangesViewToggleState",
"Table",
undefined,
{
getOnInit: true
}
);

export function useConfigChangesViewToggleState() {
const [hideDeletedConfigs] = useAtom(configChangesViewToggle);
return hideDeletedConfigs;
}

export default function ConfigChangesViewToggle() {
const [toggleValue, setToggleValue] = useAtom(configChangesViewToggle);

return (
<div className="ml-auto flex flex-row items-center gap-2 px-2">
<Switch
options={["Table", "Graph"]}
onChange={(v) => {
setToggleValue(v as GraphType);
}}
value={toggleValue}
/>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { FilterBadge } from "../../ConfigChangesFilters/ConfigChangesFilters";
import { ConfigRelatedChangesToggles } from "../../ConfigChangesFilters/ConfigRelatedChangesToggles";
import { ConfigTagsDropdown } from "../../ConfigChangesFilters/ConfigTagsDropdown";
import ConfigTypesTristateDropdown from "../../ConfigChangesFilters/ConfigTypesTristateDropdown";
import ConfigChangesViewToggle from "../../ConfigChangesViewToggle";
import { ConfigChangesToggledDeletedItems } from "./ConfigChangesToggledDeletedItems";

type ConfigChangeFiltersProps = {
Expand All @@ -22,19 +23,27 @@ export function ConfigRelatedChangesFilters({
const arbitraryFilters = useConfigChangesArbitraryFilters();

return (
<div className="flex flex-col gap-2">
<div className="flex w-full flex-col gap-2">
<FormikFilterForm
paramsToReset={paramsToReset}
filterFields={["configTypes", "changeType", "severity", "tags"]}
>
<div className={clsx("flex flex-wrap items-center gap-2", className)}>
<div
className={clsx(
"flex w-full flex-wrap items-center gap-2",
className
)}
>
<ConfigTypesTristateDropdown />
<ChangesTypesDropdown />
<ConfigChangeSeverity />
<ConfigTagsDropdown />
<ConfigRelatedChangesToggles />
<ConfigChangesDateRangeFilter paramsToReset={paramsToReset} />
<ConfigChangesToggledDeletedItems />
<div className="ml-auto flex flex-row gap-2">
<ConfigChangesViewToggle />
</div>
</div>
</FormikFilterForm>
<div className="flex flex-wrap gap-2">
Expand Down
1 change: 1 addition & 0 deletions src/pages/config/ConfigChangesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export function ConfigChangesPage() {
) : (
<>
<ConfigChangeFilters paramsToReset={["page"]} />

<ConfigChangeTable
data={changes}
isLoading={isLoading || isRefetching}
Expand Down
Loading

0 comments on commit 2e52653

Please sign in to comment.