implement pie chart summary

This commit is contained in:
mbecker20
2023-03-22 06:41:57 +00:00
parent 6c2d8a8494
commit c605b2f6fc
6 changed files with 257 additions and 99 deletions
+3 -2
View File
@@ -17,13 +17,14 @@ const Home: Component<{}> = (p) => {
const { servers } = useAppState();
return (
<>
<Grid
{/* <Grid
style={{ width: "100%" }}
gridTemplateColumns={isSemiMobile() ? "1fr" : "1fr 1fr"}
>
<Summary />
<Updates />
</Grid>
</Grid> */}
<Summary />
<TreeProvider>
<SimpleTabs
containerStyle={{ width: "100%" }}
+42 -70
View File
@@ -1,15 +1,28 @@
import { Component, createMemo, For, Show } from "solid-js";
import { Accessor, Component, createMemo, For, Show } from "solid-js";
import { useAppState } from "../../state/StateProvider";
import { DockerContainerState, ServerStatus } from "../../types";
import Grid from "../shared/layout/Grid";
import Flex from "../shared/layout/Flex";
import PieChart, { PieChartSection } from "../shared/PieChart";
import { COLORS } from "../../style/colors";
const PIE_CHART_HEIGHT = 250;
const Summary: Component<{}> = (p) => {
const deployentCount = useDeploymentCount();
const serverCount = useServerCount();
return (
<Grid class="card shadow" gridTemplateRows="auto 1fr 1fr 1fr">
<h1>summary</h1>
<DeploymentsSummary />
<ServersSummary />
<Grid
class="card shadow"
gridTemplateColumns="1fr 1fr 1fr"
style={{ width: "100%", "box-sizing": "border-box" }}
>
<div style={{ width: "100%", height: `${PIE_CHART_HEIGHT}px` }}>
<PieChart title="deployments" sections={deployentCount()} />
</div>
<div style={{ width: "100%", height: `${PIE_CHART_HEIGHT}px` }}>
<PieChart title="servers" sections={serverCount()} />
</div>
<BuildsSummary />
</Grid>
);
@@ -56,71 +69,17 @@ const BuildsSummary = () => {
);
};
const DeploymentsSummary = () => {
const deployentCount = useDeploymentCount();
return (
<SummaryItem
title="deployments"
metrics={[
{
title: "total",
class: "text-green",
count: deployentCount().total,
},
{
title: "running",
class: "text-green",
count: deployentCount().running,
},
{
title: "stopped",
class: "text-red",
count: deployentCount().stopped,
},
{
title: "not deployed",
class: "text-blue",
count: deployentCount().notDeployed,
},
{
title: "unknown",
class: "text-blue",
count: deployentCount().unknown,
},
]}
/>
);
};
const ServersSummary = () => {
const serverCount = useServerCount();
return (
<SummaryItem
title="servers"
metrics={[
{ title: "total", class: "text-green", count: serverCount().total },
{ title: "healthy", class: "text-green", count: serverCount().healthy },
{
title: "unhealthy",
class: "text-red",
count: serverCount().unhealthy,
},
{
title: "disabled",
class: "text-blue",
count: serverCount().disabled,
},
]}
/>
);
};
function useDeploymentCount() {
function useDeploymentCount(): Accessor<PieChartSection[]> {
const { deployments } = useAppState();
const count = createMemo(() => {
const ids = deployments.ids();
if (!ids)
return { total: 0, running: 0, stopped: 0, notDeployed: 0, unknown: 0 };
return [
{ title: "running", amount: 0, color: COLORS.textgreen },
{ title: "stopped", amount: 0, color: COLORS.textred },
{ title: "not deployed", amount: 0, color: COLORS.textblue },
{ title: "unknown", amount: 0, color: COLORS.textorange },
];
let running = 0;
let stopped = 0;
let notDeployed = 0;
@@ -137,16 +96,25 @@ function useDeploymentCount() {
unknown++;
}
}
return { total: ids.length, running, stopped, notDeployed, unknown };
return [
{ title: "running", amount: running, color: COLORS.textgreen },
{ title: "stopped", amount: stopped, color: COLORS.textred },
{ title: "not deployed", amount: notDeployed, color: COLORS.textblue },
{ title: "unknown", amount: unknown, color: COLORS.textorange },
];
});
return count;
}
function useServerCount() {
function useServerCount(): Accessor<PieChartSection[]> {
const { servers } = useAppState();
const count = createMemo(() => {
const ids = servers.ids();
if (!ids) return { total: 0, healthy: 0, unhealthy: 0, disabled: 0 };
if (!ids) return [
{ title: "healthy", amount: 0, color: COLORS.textgreen },
{ title: "unhealthy", amount: 0, color: COLORS.textred },
{ title: "disabled", amount: 0, color: COLORS.textblue },
];
let healthy = 0;
let unhealthy = 0;
let disabled = 0;
@@ -160,7 +128,11 @@ function useServerCount() {
unhealthy++;
}
}
return { total: ids.length, healthy, unhealthy, disabled };
return [
{ title: "healthy", amount: healthy, color: COLORS.textgreen },
{ title: "unhealthy", amount: unhealthy, color: COLORS.textred },
{ title: "disabled", amount: disabled, color: COLORS.textblue },
];
});
return count;
}
+166
View File
@@ -0,0 +1,166 @@
import { Component, createMemo, For, Show } from "solid-js";
import { useAppState } from "../../state/StateProvider";
import { DockerContainerState, ServerStatus } from "../../types";
import Grid from "../shared/layout/Grid";
import Flex from "../shared/layout/Flex";
const Summary: Component<{}> = (p) => {
return (
<Grid class="card shadow" gridTemplateRows="auto 1fr 1fr 1fr">
<h1>summary</h1>
<DeploymentsSummary />
<ServersSummary />
<BuildsSummary />
</Grid>
);
};
export default Summary;
const SummaryItem: Component<{
title: string;
metrics: Array<{ title: string; class: string; count?: number }>;
}> = (p) => {
return (
<Flex
class="card light shadow wrap"
justifyContent="space-between"
alignItems="center"
>
<h2>{p.title}</h2>
<Flex class="wrap">
<For each={p.metrics}>
{(metric) => (
<Show when={metric?.count && metric.count > 0}>
<Flex gap="0.4rem" alignItems="center">
<div>{metric.title}</div>
<h2 class={metric.class}>{metric.count}</h2>
</Flex>
</Show>
)}
</For>
</Flex>
</Flex>
);
};
const BuildsSummary = () => {
const { builds } = useAppState();
return (
<SummaryItem
title="builds"
metrics={[
{ title: "total", class: "text-green", count: builds.ids()?.length },
]}
/>
);
};
const DeploymentsSummary = () => {
const deployentCount = useDeploymentCount();
return (
<SummaryItem
title="deployments"
metrics={[
{
title: "total",
class: "text-green",
count: deployentCount().total,
},
{
title: "running",
class: "text-green",
count: deployentCount().running,
},
{
title: "stopped",
class: "text-red",
count: deployentCount().stopped,
},
{
title: "not deployed",
class: "text-blue",
count: deployentCount().notDeployed,
},
{
title: "unknown",
class: "text-blue",
count: deployentCount().unknown,
},
]}
/>
);
};
const ServersSummary = () => {
const serverCount = useServerCount();
return (
<SummaryItem
title="servers"
metrics={[
{ title: "total", class: "text-green", count: serverCount().total },
{ title: "healthy", class: "text-green", count: serverCount().healthy },
{
title: "unhealthy",
class: "text-red",
count: serverCount().unhealthy,
},
{
title: "disabled",
class: "text-blue",
count: serverCount().disabled,
},
]}
/>
);
};
function useDeploymentCount() {
const { deployments } = useAppState();
const count = createMemo(() => {
const ids = deployments.ids();
if (!ids)
return { total: 0, running: 0, stopped: 0, notDeployed: 0, unknown: 0 };
let running = 0;
let stopped = 0;
let notDeployed = 0;
let unknown = 0;
for (const id of ids) {
const state = deployments.get(id)!.state;
if (state === DockerContainerState.NotDeployed) {
notDeployed++;
} else if (state === DockerContainerState.Running) {
running++;
} else if (state === DockerContainerState.Exited) {
stopped++;
} else if (state === DockerContainerState.Unknown) {
unknown++;
}
}
return { total: ids.length, running, stopped, notDeployed, unknown };
});
return count;
}
function useServerCount() {
const { servers } = useAppState();
const count = createMemo(() => {
const ids = servers.ids();
if (!ids) return { total: 0, healthy: 0, unhealthy: 0, disabled: 0 };
let healthy = 0;
let unhealthy = 0;
let disabled = 0;
for (const id of ids) {
const server = servers.get(id)!;
if (server.status === ServerStatus.Disabled) {
disabled++;
} else if (server.status === ServerStatus.Ok) {
healthy++;
} else if (server.status === ServerStatus.NotOk) {
unhealthy++;
}
}
return { total: ids.length, healthy, unhealthy, disabled };
});
return count;
}
+14 -18
View File
@@ -7,6 +7,7 @@ import {
onCleanup,
onMount,
} from "solid-js";
import Grid from "./layout/Grid";
export type PieChartSection = {
title: string;
@@ -14,7 +15,7 @@ export type PieChartSection = {
color: string;
};
const Piechart: Component<{
const PieChart: Component<{
title: string;
sections: (PieChartSection | undefined)[];
donutProportion?: number;
@@ -27,7 +28,7 @@ const Piechart: Component<{
const sections = createMemo(
() =>
p.sections
.filter((s) => s)
.filter((s) => s && s.amount > 0)
.sort((a, b) => {
if (a!.amount > b!.amount) {
return -1;
@@ -58,26 +59,24 @@ const Piechart: Component<{
chart()?.draw();
});
return (
<div
<Grid
ref={ref!}
style={{
width: "100%",
height: "100%",
"box-sizing": "border-box",
display: "grid",
position: "relative",
}}
>
<div
<Grid
placeItems="center"
style={{
position: "absolute",
width: "100%",
height: "100%",
display: "grid",
"place-items": "center",
}}
>
<div style={{ display: "grid", gap: "0.2rem", "z-index": -1 }}>
<div style={{ display: "grid", gap: "0.2rem" }}>
<h2>{p.title}</h2>
<For each={sections()}>
{(section, index) => (
@@ -92,19 +91,19 @@ const Piechart: Component<{
: 0.5,
}}
>
{section.title}:
{section.title}:{" "}
<div style={{ color: section.color }}>{section.amount}</div>
</div>
)}
</For>
</div>
</div>
<canvas ref={canvas!} style={{ "z-index": 1 }} />
</div>
</Grid>
<canvas ref={canvas!} />
</Grid>
);
};
export default Piechart;
export default PieChart;
type InnerPieChartSection = PieChartSection & {
startAngle: number;
@@ -122,7 +121,7 @@ class PieChartCanvas {
private canvas: HTMLCanvasElement,
sections: PieChartSection[],
private onSelectedUpdate: (selected: number | undefined) => void,
private donutProportion = 0.3,
private donutProportion = 0.75,
private seperation = 0.02 // private initAngle = -Math.PI / 8
) {
this.sections = [];
@@ -221,13 +220,10 @@ class PieChartCanvas {
break;
}
}
// console.log("x", x);
// console.log("y", y);
// console.log(atan);
console.log(this.selected);
}
updateCanvasDim(width: number, height: number) {
if (width <= 0 || height <= 0) return;
this.canvas.width = width;
this.canvas.height = height;
this.cx = this.canvas.width / 2;
+1 -9
View File
@@ -1,5 +1,5 @@
import { LineData, SingleValueData } from "lightweight-charts";
import { Accessor, Component, For, ParentComponent, Show } from "solid-js";
import { COLORS } from "../../style/colors";
import { SystemStats, SystemStatsRecord } from "../../types";
import {
convertTsMsToLocalUnixTsInSecs,
@@ -9,14 +9,6 @@ import Grid from "../shared/layout/Grid";
import LightweightChart, { LightweightValue } from "../shared/LightweightChart";
import s from "./stats.module.scss";
export const COLORS = {
blue: "#184e9f",
orange: "#ac5c36",
purple: "#5A0B4D",
green: "#41764c",
red: "#952E23",
};
const CHART_HEIGHT = "250px";
const SMALL_CHART_HEIGHT = "150px";
+31
View File
@@ -0,0 +1,31 @@
export const COLORS = {
"app-color": "#fceade",
lightgrey: "#3f454d",
grey: "#25292e",
darkgrey: "#16181b",
textblue: "#5f9af4",
lightblue: "#1c63cd",
blue: "#184e9f",
darkblue: "#12366d",
textgreen: "#80ea97",
lightgreen: "#4f8d5c",
green: "#41764c",
darkgreen: "#2b4f33",
textred: "#f04633",
lightred: "#b13a2d",
red: "#952E23",
darkred: "#631F17",
textorange: "#e77e4e",
lightorange: "#d56b3a",
orange: "#ac5c36",
darkorange: "#984f2d",
lightpurple: "#720e61",
purple: "#5A0B4D",
darkpurple: "#3b0732",
};