Skip to content

Commit

Permalink
feat: add playbooks side panel for configs and topology
Browse files Browse the repository at this point in the history
  • Loading branch information
mainawycliffe committed Oct 3, 2023
1 parent 595c069 commit 5991538
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 3 deletions.
23 changes: 22 additions & 1 deletion src/api/services/playbooks.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { PlaybookRunAction } from "../../components/Playbooks/Runs/PlaybookRunsSidePanel";
import { SubmitPlaybookRunFormValues } from "../../components/Playbooks/Runs/SubmitPlaybookRunForm";
import {
NewPlaybookSpec,
PlaybookSpec,
UpdatePlaybookSpec
} from "../../components/Playbooks/Settings/PlaybookSpecsTable";
import { AVATAR_INFO } from "../../constants";
import { IncidentCommander, PlaybookAPI } from "../axios";
import { ConfigDB, IncidentCommander, PlaybookAPI } from "../axios";
import { GetPlaybooksToRunParams } from "../query-hooks/playbooks";
import { resolve } from "../resolve";

export async function getAllPlaybooksSpecs() {
const res = await IncidentCommander.get<PlaybookSpec[] | null>(
Expand Down Expand Up @@ -62,3 +64,22 @@ export async function getPlaybookRun(params: GetPlaybooksToRunParams) {
);
return res.data ?? [];
}

export async function getPlaybookRuns(componentId?: string, configId?: string) {
const componentParamString = componentId
? `&component_id=eq.${componentId}`
: "";
const configParamString = configId ? `&config_id=eq.${configId}` : "";

const res = await resolve(
ConfigDB.get<PlaybookRunAction[] | null>(
`/playbook_runs?select=*,playbooks(id,name)&order=created_at.desc${componentParamString}&${configParamString}`,
{
headers: {
Prefer: "count=exact"
}
}
)
);
return res;
}
12 changes: 11 additions & 1 deletion src/components/ConfigSidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import SlidingSideBar from "../SlidingSideBar";
import { ConfigDetails } from "./ConfigDetails";
import ConfigActionBar from "./ConfigActionBar";
import { useCallback, useState } from "react";
import { PlaybookRunsSidePanel } from "../Playbooks/Runs/PlaybookRunsSidePanel";

type SidePanels =
| "ConfigDetails"
| "Configs"
| "Incidents"
| "Costs"
| "ConfigChanges"
| "Insights";
| "Insights"
| "PlaybookRuns";

export default function ConfigSidebar() {
const [openedPanel, setOpenedPanel] = useState<SidePanels | undefined>(
Expand Down Expand Up @@ -70,6 +72,14 @@ export default function ConfigSidebar() {
panelCollapsedStatusChange(status, "Costs")
}
/>
<PlaybookRunsSidePanel
panelType="config"
configId={id}
isCollapsed={openedPanel !== "PlaybookRuns"}
onCollapsedStateChange={(status) =>
panelCollapsedStatusChange(status, "PlaybookRuns")
}
/>
<ConfigChanges
configID={id}
isCollapsed={openedPanel !== "ConfigChanges"}
Expand Down
172 changes: 172 additions & 0 deletions src/components/Playbooks/Runs/PlaybookRunsSidePanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { useQuery } from "@tanstack/react-query";
import { ColumnDef } from "@tanstack/react-table";
import { useAtom } from "jotai";
import { useEffect, useMemo, useState } from "react";
import { AiOutlineTeam } from "react-icons/ai";
import { getPlaybookRuns } from "../../../api/services/playbooks";
import { relativeDateTime } from "../../../utils/date";
import PillBadge from "../../Badge/PillBadge";
import CollapsiblePanel from "../../CollapsiblePanel";
import EmptyState from "../../EmptyState";
import { InfiniteTable } from "../../InfiniteTable/InfiniteTable";
import TextSkeletonLoader from "../../SkeletonLoader/TextSkeletonLoader";
import { refreshButtonClickedTrigger } from "../../SlidingSideBar";
import Title from "../../Title/title";
import { PlaybookSpec } from "../Settings/PlaybookSpecsTable";

type TopologySidePanelProps = {
panelType: "topology";
componentId: string;
} & Props;

type ConfigSidePanelProps = {
panelType: "config";
configId: string;
} & Props;

type Props = {
isCollapsed?: boolean;
onCollapsedStateChange?: (isClosed: boolean) => void;
};

export type PlaybookRunStatus =
| "scheduled"
| "running"
| "cancelled"
| "completed"
| "failed"
| "pending";

export type PlaybookRunAction = {
id: string;
name: string;
status: PlaybookRunStatus;
playbook_run_id: string;
start_time?: string;
end_time?: string;
result?: {
stdout?: string;
};
error?: string;
playbooks?: PlaybookSpec;
};

const runsColumns: ColumnDef<PlaybookRunAction, any>[] = [
{
header: "Name",
id: "name",
accessorKey: "name",
size: 60,
cell: ({ row }) => {
const name = row.original.name ?? row.original.playbooks?.name;
return <span>{name}</span>;
}
},
{
header: "Status",
id: "status",
accessorKey: "status",
size: 60
},
{
header: "Duration",
id: "duration",
cell: ({ row }) => {
const { start_time, end_time } = row.original;
const value = relativeDateTime(end_time!, start_time);
return <span className="whitespace-nowrap">{value}</span>;
},
size: 20
}
];

export function PlaybookRunsSidePanel({
isCollapsed,
onCollapsedStateChange,
...props
}: ConfigSidePanelProps | TopologySidePanelProps) {
const { data, isLoading, refetch, isFetching } = useQuery(
["componentTeams", props],
() =>
getPlaybookRuns(
props.panelType === "topology" ? props.componentId : undefined,
props.panelType === "config" ? props.configId : undefined
)
);

const totalEntries = data?.totalEntries ?? 0;

const playbookRuns = data?.data ?? [];

const [{ pageIndex, pageSize }, setPageState] = useState({
pageIndex: 0,
pageSize: 50
});

const canGoNext = () => {
const pageCount = Math.ceil(totalEntries / pageSize);
return pageCount >= pageIndex + 1;
};

const [triggerRefresh] = useAtom(refreshButtonClickedTrigger);

const columns = useMemo(() => runsColumns, []);

useEffect(() => {
if (!isLoading && !isFetching) {
refetch();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [triggerRefresh]);

return (
<CollapsiblePanel
isCollapsed={isCollapsed}
onCollapsedStateChange={onCollapsedStateChange}
Header={
<div className="flex flex-row w-full items-center space-x-2">
<Title
title="Playbooks"
icon={<AiOutlineTeam className="w-6 h-auto" />}
/>
<PillBadge>{playbookRuns?.length ?? 0}</PillBadge>
</div>
}
dataCount={playbookRuns?.length}
>
<div className="flex flex-col space-y-4 py-2 w-full">
{isLoading ? (
<TextSkeletonLoader />
) : playbookRuns && playbookRuns.length > 0 ? (
<div className="flex flex-col overflow-y-hidden">
<InfiniteTable
isLoading={isLoading}
allRows={playbookRuns}
columns={columns}
isFetching={isFetching}
loaderView={<TextSkeletonLoader className="w-full my-2" />}
totalEntries={totalEntries}
fetchNextPage={() => {
if (canGoNext()) {
setPageState({
pageIndex: pageIndex + 1,
pageSize
});
}
}}
stickyHead
virtualizedRowEstimatedHeight={40}
columnsClassName={{
name: "",
status: "fit-content",
duration: "fit-content"
}}
/>
</div>
) : (
<EmptyState />
)}
</div>
</CollapsiblePanel>
);
}
15 changes: 14 additions & 1 deletion src/components/TopologySidebar/TopologySidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import TopologyActionBar from "./TopologyActionBar";
import TopologyCost from "./TopologyCost";
import TopologyInsights from "./TopologyInsights";
import { useCallback, useState } from "react";
import { PlaybookRunsSidePanel } from "../Playbooks/Runs/PlaybookRunsSidePanel";

type Props = {
topology?: Topology;
Expand All @@ -24,7 +25,8 @@ type SidePanels =
| "Costs"
| "ConfigChanges"
| "Teams"
| "Insights";
| "Insights"
| "PlaybookRuns";

export default function TopologySidebar({
topology,
Expand Down Expand Up @@ -77,6 +79,7 @@ export default function TopologySidebar({
panelCollapsedStatusChange(status, "Incidents")
}
/>

<TopologyCost
topology={topology}
isCollapsed={openedPanel !== "Costs"}
Expand All @@ -91,6 +94,16 @@ export default function TopologySidebar({
panelCollapsedStatusChange(status, "ConfigChanges")
}
/>

<PlaybookRunsSidePanel
panelType="topology"
componentId={id}
isCollapsed={openedPanel !== "PlaybookRuns"}
onCollapsedStateChange={(status) =>
panelCollapsedStatusChange(status, "PlaybookRuns")
}
/>

<ComponentTeams
componentId={id}
isCollapsed={openedPanel !== "Teams"}
Expand Down

0 comments on commit 5991538

Please sign in to comment.