diff --git a/.vscode/solid.code-snippets b/.vscode/solid.code-snippets index a701071bd..2ded946e2 100644 --- a/.vscode/solid.code-snippets +++ b/.vscode/solid.code-snippets @@ -38,7 +38,7 @@ "scope": "typescriptreact,javascriptreact", "prefix": "provider", "body": [ - "import { Component, createContext, useContext } from \"solid-js\";", + "import { ParentComponent, createContext, useContext } from \"solid-js\";", "", "const value = () => {", "\treturn {};", @@ -48,7 +48,7 @@ "", "const context = createContext();", "", - "export const Provider: Component<{}> = (p) => {", + "export const Provider: ParentComponent<{}> = (p) => {", "\treturn (", "\t\t", "\t\t\t{p.children}", diff --git a/frontend/src/components/shared/HeatBar.tsx b/frontend/src/components/shared/HeatBar.tsx index 69d42348e..2c7be965d 100644 --- a/frontend/src/components/shared/HeatBar.tsx +++ b/frontend/src/components/shared/HeatBar.tsx @@ -18,6 +18,7 @@ const HeatBar: Component<{ containerClass?: string; containerStyle?: JSX.CSSProperties; barHeight?: string; + onClick?: () => void; }> = (p) => { let el: HTMLDivElement; const [width, setWidth] = createSignal(); @@ -32,18 +33,26 @@ const HeatBar: Component<{ removeEventListener("resize", handleResize); }); return ( - + {(index) => (
)} diff --git a/frontend/src/components/shared/LightweightChart.tsx b/frontend/src/components/shared/LightweightChart.tsx index cbd6b62cc..1576b9c45 100644 --- a/frontend/src/components/shared/LightweightChart.tsx +++ b/frontend/src/components/shared/LightweightChart.tsx @@ -26,9 +26,12 @@ type LineDataPoint = { }; const LightweightChart: Component<{ - style?: JSX.CSSProperties; - class?: string; lines?: () => LinesData[]; + class?: string; + style?: JSX.CSSProperties; + width?: string; + height?: string; + disableScroll?: boolean }> = (p) => { let el: HTMLDivElement; const [chart, setChart] = createSignal(); @@ -49,8 +52,8 @@ const LightweightChart: Component<{ vertLines: { color: "#3f454d" }, }, timeScale: { timeVisible: true }, - // handleScroll: false, - // handleScale: false, + handleScroll: p.disableScroll ? false : true, + handleScale: p.disableScroll ? false : true, }); chart.timeScale().fitContent(); setChart(chart); @@ -64,11 +67,12 @@ const LightweightChart: Component<{ const series = chart()!.addLineSeries({ color: line.color, title: line.title, - priceLineVisible: line.priceLineVisible || false + priceLineVisible: line.priceLineVisible || false, }); series.setData(line.line as any); return series; }); + chart()!.timeScale().fitContent(); lineSeries = series; } }); @@ -86,7 +90,11 @@ const LightweightChart: Component<{
); }; diff --git a/frontend/src/components/stats/Charts.tsx b/frontend/src/components/stats/Charts.tsx new file mode 100644 index 000000000..ef2d28a3a --- /dev/null +++ b/frontend/src/components/stats/Charts.tsx @@ -0,0 +1,359 @@ +import { Accessor, Component, createSignal, For, Show } from "solid-js"; +import { SystemStats, SystemStatsRecord } from "../../types"; +import { + convertTsMsToLocalUnixTsInSecs, + get_to_one_sec_divisor, +} from "../../util/helpers"; +import Flex from "../shared/layout/Flex"; +import Grid from "../shared/layout/Grid"; +import LightweightChart 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 = "200px"; +const SMALL_CHART_HEIGHT = "150px"; + +export const CpuChart: Component<{ + stats: Accessor<(SystemStatsRecord | SystemStats)[] | undefined>; + small?: boolean; + disableScroll?: boolean; +}> = (p) => { + const line = () => { + return p.stats()?.map((s) => { + return { + time: convertTsMsToLocalUnixTsInSecs( + (s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts + ), + value: s.cpu_perc, + }; + }); + }; + return ( + + + +

cpu

+
+ [{ title: "%", color: COLORS.blue, line: line()! }]} + disableScroll={p.disableScroll} + /> +
+
+ ); +}; + +export const MemChart: Component<{ + stats: Accessor<(SystemStatsRecord | SystemStats)[] | undefined>; + small?: boolean; + disableScroll?: boolean; +}> = (p) => { + const line = () => { + return p.stats()?.map((s) => { + return { + time: convertTsMsToLocalUnixTsInSecs( + (s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts + ), + value: (100 * s.mem_used_gb) / s.mem_total_gb, + }; + }); + }; + return ( + + + +

memory

+
+ [{ title: "mem %", color: COLORS.blue, line: line()! }]} + disableScroll={p.disableScroll} + /> +
+
+ ); +}; + +export const DiskChart: Component<{ + stats: Accessor<(SystemStatsRecord | SystemStats)[] | undefined>; + small?: boolean; + disableScroll?: boolean; +}> = (p) => { + const line = () => { + return p.stats()?.map((s) => { + return { + time: convertTsMsToLocalUnixTsInSecs( + (s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts + ), + value: (100 * s.disk.used_gb) / s.disk.total_gb, + }; + }); + }; + return ( + + + +

disk

+
+ [{ title: "disk %", color: "#184e9f", line: line()! }]} + disableScroll={p.disableScroll} + /> +
+
+ ); +}; + +export const NetworkIoCharts: Component<{ + stats: Accessor<(SystemStatsRecord | SystemStats)[] | undefined>; + small?: boolean; + disableScroll?: boolean; +}> = (p) => { + const recv_line = () => { + return p.stats()?.map((s) => { + return { + time: convertTsMsToLocalUnixTsInSecs( + (s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts + ), + value: + s.networks?.length || 0 > 0 + ? s.networks!.map((n) => n.recieved_kb).reduce((p, c) => p + c) / + get_to_one_sec_divisor(s.polling_rate)! + : 0, + }; + }); + }; + const trans_line = () => { + return p.stats()?.map((s) => { + return { + time: convertTsMsToLocalUnixTsInSecs( + (s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts + ), + value: + s.networks?.length || 0 > 0 + ? s.networks!.map((n) => n.transmitted_kb).reduce((p, c) => p + c) / + get_to_one_sec_divisor(s.polling_rate)! + : 0, + }; + }); + }; + return ( + + + +

network sent kb/s

+
+ [ + { title: "sent kb/s", color: "#184e9f", line: trans_line()! }, + ]} + /> +
+ + +

network received kb/s

+
+ [ + { title: "recv kb/s", color: "#184e9f", line: recv_line()! }, + ]} + disableScroll={p.disableScroll} + /> +
+
+ ); +}; + +export const DiskIoCharts: Component<{ + stats: Accessor<(SystemStatsRecord | SystemStats)[] | undefined>; + small?: boolean; + disableScroll?: boolean; +}> = (p) => { + const read_line = () => { + return p.stats()?.map((s) => { + return { + time: convertTsMsToLocalUnixTsInSecs( + (s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts + ), + value: + s.disk.disks?.length || 0 > 0 + ? s.disk.read_kb / get_to_one_sec_divisor(s.polling_rate)! + : 0, + }; + }); + }; + const write_line = () => { + return p.stats()?.map((s) => { + return { + time: convertTsMsToLocalUnixTsInSecs( + (s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts + ), + value: + s.disk.disks?.length || 0 > 0 + ? s.disk.write_kb / get_to_one_sec_divisor(s.polling_rate)! + : 0, + }; + }); + }; + return ( + + + +

disk read kb/s

+
+ [ + { title: "kb/s", color: "#184e9f", line: read_line()! }, + ]} + /> +
+ +

disk write kb/s

+ [ + { title: "kb/s", color: "#184e9f", line: write_line()! }, + ]} + disableScroll={p.disableScroll} + /> +
+
+ ); +}; + +export const TempuratureChart: Component<{ + stats: Accessor<(SystemStatsRecord | SystemStats)[] | undefined>; + small?: boolean; + disableScroll?: boolean; +}> = (p) => { + // const [selected, setSelected] = createSignal(p.stats()![p.stats()!.length - 1].components![0].label); + const labels = () => { + return p.stats()![p.stats()!.length - 1].components!.map((c) => c.label); + }; + const line = (component: string) => { + return p.stats()?.map((s) => { + const temp = s.components!.find((c) => c.label === component)?.temp; + return { + time: convertTsMsToLocalUnixTsInSecs( + (s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts + ), + value: temp || 0, + }; + }); + }; + return ( + + {(label) => ( + + +

{label}

+ {/* */} +
+ [ + { title: "temp", color: "#184e9f", line: line(label)! }, + ]} + disableScroll={p.disableScroll} + /> +
+ )} +
+ ); +}; diff --git a/frontend/src/components/stats/CurrentStats.tsx b/frontend/src/components/stats/CurrentStats.tsx index e7c96549d..998c2eb3b 100644 --- a/frontend/src/components/stats/CurrentStats.tsx +++ b/frontend/src/components/stats/CurrentStats.tsx @@ -8,18 +8,32 @@ import { Setter, Show, } from "solid-js"; +import { createStore } from "solid-js/store"; import { client, URL } from "../.."; import { SystemStats } from "../../types"; -import { combineClasses, generateQuery } from "../../util/helpers"; +import { generateQuery } from "../../util/helpers"; +import { useLocalStorage } from "../../util/hooks"; import HeatBar from "../shared/HeatBar"; -import Flex from "../shared/layout/Flex"; import Grid from "../shared/layout/Grid"; import Loading from "../shared/loading/Loading"; +import { CpuChart, DiskChart, MemChart } from "./Charts"; import s from "./stats.module.scss"; const CurrentStats: Component<{}> = (p) => { const params = useParams(); - const [stats, setStats] = createSignal(); + const [showCpuGraph, setShowCpuGraph] = useLocalStorage( + false, + "current-stats-cpu-graph-v1" + ); + const [showMemGraph, setShowMemGraph] = useLocalStorage( + false, + "current-stats-mem-graph-v1" + ); + const [showDiskGraph, setShowDiskGraph] = useLocalStorage( + false, + "current-stats-disk-graph-v1" + ); + const [stats, setStats] = createSignal([]); const open = useStatsWs(params, setStats); createEffect(() => { client @@ -28,54 +42,82 @@ const CurrentStats: Component<{}> = (p) => { components: true, processes: true, }) - .then(setStats); + .then((stats) => setStats([stats])); }); const mem_perc = () => { - return (100 * stats()!.mem_used_gb) / stats()!.mem_total_gb; + return ( + (100 * stats()[stats()!.length - 1].mem_used_gb) / + stats()![stats().length - 1].mem_total_gb + ); }; const disk_perc = () => { - return (100 * stats()!.disk.used_gb) / stats()!.disk.total_gb; + return ( + (100 * stats()[stats().length - 1].disk.used_gb) / + stats()![stats().length - 1].disk.total_gb + ); }; return ( - }> + 0} fallback={}> +
+

basic

+
+

cpu:

setShowCpuGraph((curr) => !curr)} /> -

{stats()!.cpu_perc.toFixed(1)}%

+

{stats()[stats().length - 1].cpu_perc.toFixed(1)}%

+ +
+ +
+

mem:

setShowMemGraph((curr) => !curr)} />

{mem_perc().toFixed(1)}%

- {stats()!.mem_used_gb.toFixed()}GB of{" "} - {stats()!.mem_total_gb.toFixed()}GB + {stats()[stats().length - 1].mem_used_gb.toFixed()}GB of{" "} + {stats()[stats().length - 1].mem_total_gb.toFixed()}GB
+ +
+ +
+

disk:

setShowDiskGraph(curr => !curr)} />

{disk_perc().toFixed(1)}%

- {stats()!.disk.used_gb.toFixed()}GB of{" "} - {stats()!.disk.total_gb.toFixed()}GB + {stats()[stats().length - 1].disk.used_gb.toFixed()}GB of{" "} + {stats()[stats().length - 1].disk.total_gb.toFixed()}GB
+ +
+ +
+ @@ -84,7 +126,7 @@ const CurrentStats: Component<{}> = (p) => { export default CurrentStats; -function useStatsWs(params: Params, setStats: Setter) { +function useStatsWs(params: Params, setStats: Setter) { const ws = new ReconnectingWebSocket( `${URL.replace("http", "ws")}/ws/stats/${params.id}${generateQuery({ networks: "true", @@ -105,7 +147,10 @@ function useStatsWs(params: Params, setStats: Setter) { } const stats = JSON.parse(data) as SystemStats; // console.log(stats); - setStats(stats); + setStats((stats_arr) => [ + ...(stats_arr.length > 200 ? stats_arr.slice(1) : stats_arr), + stats, + ]); }); ws.addEventListener("close", () => { console.log("stats connection closed"); diff --git a/frontend/src/components/stats/HistoricalStats.tsx b/frontend/src/components/stats/HistoricalStats.tsx index caff71fe6..381663da8 100644 --- a/frontend/src/components/stats/HistoricalStats.tsx +++ b/frontend/src/components/stats/HistoricalStats.tsx @@ -1,15 +1,14 @@ import { useParams } from "@solidjs/router"; -import { Accessor, Component, createEffect, createSignal, For, Match, Show, Switch } from "solid-js"; +import { Component, createEffect, createSignal, Match, Show, Switch } from "solid-js"; import { client } from "../.."; import { SystemStatsRecord, Timelength } from "../../types"; -import { convertTsMsToLocalUnixTsInSecs, get_to_one_sec_divisor } from "../../util/helpers"; import { useLocalStorage } from "../../util/hooks"; import Icon from "../shared/Icon"; import Flex from "../shared/layout/Flex"; import Grid from "../shared/layout/Grid"; -import LightweightChart from "../shared/LightweightChart"; import Loading from "../shared/loading/Loading"; import Selector from "../shared/menu/Selector"; +import { CpuChart, DiskChart, DiskIoCharts, MemChart, NetworkIoCharts, TempuratureChart } from "./Charts"; import s from "./stats.module.scss"; const TIMELENGTHS = [ @@ -22,13 +21,7 @@ const TIMELENGTHS = [ Timelength.OneDay, ]; -const COLORS = { - blue: "#184e9f", - orange: "#ac5c36", - purple: "#5A0B4D", - green: "#41764c", - red: "#952E23", -}; + const VIEWS = [ "basic", @@ -58,7 +51,7 @@ const HistoricalStats: Component<{}> = (p) => { }); return ( - +