mirror of
https://github.com/moghtech/komodo.git
synced 2026-09-11 16:01:27 +00:00
implement pie chart summary
This commit is contained in:
@@ -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%" }}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,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";
|
||||
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
Reference in New Issue
Block a user