Skip to content
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
10 changes: 4 additions & 6 deletions frontend/src/components/ActionCacheMissMetrics/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,30 @@ import type {
Maybe,
MissDetail,
} from "@/graphql/__generated__/graphql";
import ActionsPieChart, { type ActionsChartItem } from "../ActionsPieChart";
import { chartColor } from "../ActionsPieChart/utils";
import SummaryPieChart, { type SummaryChartItem } from "../SummaryPieChart";
import { nullPercent } from "../Utilities/nullPercent";

interface Props {
acStatistics?: Maybe<ActionCacheStatistics>;
}

const ActionCacheMissMetrics: React.FC<Props> = ({ acStatistics }) => {
const chartItems: ActionsChartItem[] = [];
const chartItems: SummaryChartItem[] = [];

if (acStatistics) {
acStatistics?.missDetails?.forEach((item: MissDetail, index: number) => {
const chartItem: ActionsChartItem = {
const chartItem: SummaryChartItem = {
key: index,
count: item.count ?? 0,
percent: nullPercent(item.count, acStatistics?.misses, 0),
color: chartColor(index),
value: item.reason ?? "",
type: "square",
};
chartItems.push(chartItem);
});
}

return <ActionsPieChart items={chartItems} />;
return <SummaryPieChart items={chartItems} />;
};

export default ActionCacheMissMetrics;
8 changes: 4 additions & 4 deletions frontend/src/components/ActionRunnerMetrics/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { RunnerCount } from "@/graphql/__generated__/graphql";
import ActionsPieChart, { type ActionsChartItem } from "../ActionsPieChart";
import SummaryPieChart, { type SummaryChartItem } from "../SummaryPieChart";
import { nullPercent } from "../Utilities/nullPercent";

interface Props {
Expand All @@ -18,12 +18,12 @@ function colorSwitchOnExecStrat(exec: string) {
}

const ActionRunnerMetrics: React.FC<Props> = ({ runnerMetrics }) => {
const chartItems: ActionsChartItem[] = [];
const chartItems: SummaryChartItem[] = [];
const totalCount: number =
runnerMetrics.find((i) => i.name === "total")?.actionsExecuted ?? 0;

runnerMetrics.forEach((item: RunnerCount, index: number) => {
const chartItem: ActionsChartItem = {
const chartItem: SummaryChartItem = {
key: index,
value: item.name ?? "",
count: item.actionsExecuted ?? 0,
Expand All @@ -36,7 +36,7 @@ const ActionRunnerMetrics: React.FC<Props> = ({ runnerMetrics }) => {
}
});

return <ActionsPieChart items={chartItems} />;
return <SummaryPieChart items={chartItems} />;
};

export default ActionRunnerMetrics;
12 changes: 5 additions & 7 deletions frontend/src/components/ActionTypeMetrics/index.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,33 @@
import type { ActionData } from "@/graphql/__generated__/graphql";
import type { Maybe } from "graphql/jsutils/Maybe";
import ActionsPieChart, { type ActionsChartItem } from "../ActionsPieChart";
import { chartColor } from "../ActionsPieChart/utils";
import type { ActionData } from "@/graphql/__generated__/graphql";
import SummaryPieChart, { type SummaryChartItem } from "../SummaryPieChart";
import { nullPercent } from "../Utilities/nullPercent";

interface Props {
actionData?: Maybe<ActionData[]>;
}

const ActionTypeMetrics: React.FC<Props> = ({ actionData }) => {
const actions: ActionsChartItem[] = [];
const actions: SummaryChartItem[] = [];
const totalActionsExecuted = actionData?.reduce(
(accumulator, item) => accumulator + (item.actionsExecuted ?? 0),
0,
);

if (actionData) {
actionData.forEach((item: ActionData, index: number) => {
const chartItem: ActionsChartItem = {
const chartItem: SummaryChartItem = {
key: index,
value: item.mnemonic ?? "",
percent: nullPercent(item.actionsExecuted, totalActionsExecuted, 0),
count: item.actionsExecuted ?? 0,
color: chartColor(index),
type: "square",
};
actions.push(chartItem);
});
}

return <ActionsPieChart items={actions} />;
return <SummaryPieChart items={actions} />;
};

export default ActionTypeMetrics;
87 changes: 0 additions & 87 deletions frontend/src/components/ActionsPieChart/index.tsx

This file was deleted.

26 changes: 0 additions & 26 deletions frontend/src/components/ActionsPieChart/utils.ts

This file was deleted.

3 changes: 3 additions & 0 deletions frontend/src/components/SummaryPieChart/index.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.summaryPieChartsWrapper ul li {
break-inside: avoid-column;
}
114 changes: 114 additions & 0 deletions frontend/src/components/SummaryPieChart/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { Flex, theme } from "antd";
import Link from "next/link";
import { useCallback, useState } from "react";
import { Cell, Legend, type LegendType, Pie, PieChart } from "recharts";
import type { Payload } from "recharts/types/component/DefaultLegendContent";
import { renderActiveShapeCompact } from "../Utilities/renderShape";
import styles from "./index.module.css";
import { themeColor } from "./utils";

// The `Legend` component uses `value`, `color`,
// and `type` by default to render the data.
export interface SummaryChartItem {
key: React.Key;
value: string;
percent: string;
color?: string;
count: number;
type?: LegendType;
href?: string;
}

const { useToken } = theme;

interface Props {
items: SummaryChartItem[];
chartWidth?: number;
}

const INNER_RADIUS = 30;
const OUTER_RADIUS = 50;

const SummaryPieChart: React.FC<Props> = ({
items,
chartWidth = 600,
}: Props) => {
const { token } = useToken();

const renderLegendText = (value: string) => {
const item = coloredItems.find((i) => i.value === value);
if (value === coloredItems[activeIndexRunner].value) {
return (
<span>
<u>
<b>{item?.count ?? 0}</b>{" "}
<span style={{ color: token.colorText }}>
{item?.href ? <Link href={item.href}>{value}</Link> : value} (
{item?.percent})
</span>
</u>
</span>
);
}
return (
<span>
<b>{item?.count ?? 0}</b>{" "}
<span style={{ color: token.colorText }}>
{item?.href ? <Link href={item.href}>{value}</Link> : value} (
{item?.percent})
</span>
</span>
);
};

const [activeIndexRunner, setActiveIndexRunner] = useState<number>(0);
const onRunnerPieEnter = useCallback((_: Payload, index: number) => {
setActiveIndexRunner(index);
}, []);

// Items are sorted to display the highest count first in the legend
items.sort((a, b) => {
return b.count - a.count;
});

const coloredItems = items.map((item, index) => ({
...item,
color: item?.color || themeColor(token, index),
}));

return (
<Flex vertical gap="middle" style={{ width: chartWidth }}>
<PieChart height={OUTER_RADIUS * 3} width={chartWidth}>
<Pie
activeIndex={activeIndexRunner}
activeShape={renderActiveShapeCompact}
dataKey="count"
data={coloredItems}
innerRadius={INNER_RADIUS}
outerRadius={OUTER_RADIUS}
onMouseEnter={onRunnerPieEnter}
>
{coloredItems.map((value: SummaryChartItem) => {
return <Cell key={value.key} fill={value.color} />;
})}
</Pie>
</PieChart>
<div className={styles.summaryPieChartsWrapper}>
<Legend
payload={coloredItems}
chartWidth={chartWidth}
layout="vertical"
align="center"
wrapperStyle={{
position: "static",
columns: 2,
}}
formatter={renderLegendText}
onMouseEnter={onRunnerPieEnter}
/>
</div>
</Flex>
);
};

export default SummaryPieChart;
25 changes: 25 additions & 0 deletions frontend/src/components/SummaryPieChart/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { GlobalToken } from "antd";

export const themeColor = (token: GlobalToken, index: number): string => {
const colors: string[] = [
token["lime-8"],
token["purple-8"],
token["red-8"],
token["cyan-8"],
token["yellow-8"],
token["red-6"],
token["blue-8"],
token["magenta-8"],
token["volcano-6"],
token["green-8"],
token["volcano-8"],
token["blue-6"],
token["orange-8"],
token["magenta-6"],
token["green-6"],
token["orange-6"],
token["cyan-6"],
];

return colors[index % colors.length];
};