diff --git a/public/assets/pages/adminpage/ctrl_activity.js b/public/assets/pages/adminpage/ctrl_activity.js
index 2c245cde..a86e68bb 100644
--- a/public/assets/pages/adminpage/ctrl_activity.js
+++ b/public/assets/pages/adminpage/ctrl_activity.js
@@ -3,6 +3,7 @@ import { createElement, createRender } from "../../lib/skeleton/index.js";
import { initConfig } from "./model_config.js";
import componentLogForm from "./ctrl_activity_form.js";
import componentLogViewer from "./ctrl_activity_viewer.js";
+import componentLogGraph from "./ctrl_activity_graph.js";
import componentAuditor from "./ctrl_activity_audit.js";
import transition from "./animate.js";
import AdminHOC from "./decorator.js";
@@ -10,11 +11,12 @@ import AdminHOC from "./decorator.js";
export default AdminHOC(async function(render) {
const $page = createElement(`
-
Events
+
System Logs
+
-
Activity Report
+
Audit Report
`);
@@ -23,5 +25,6 @@ export default AdminHOC(async function(render) {
componentLogViewer(createRender($page.querySelector(".component_logviewer")));
componentLogForm(createRender($page.querySelector(".component_logger")));
+ componentLogGraph(createRender($page.querySelector(".component_stats")));
componentAuditor(createRender($page.querySelector(".component_audit")));
});
diff --git a/public/assets/pages/adminpage/ctrl_activity_graph.css b/public/assets/pages/adminpage/ctrl_activity_graph.css
new file mode 100644
index 00000000..52f41f08
--- /dev/null
+++ b/public/assets/pages/adminpage/ctrl_activity_graph.css
@@ -0,0 +1,29 @@
+.component_stats {
+ clear: both;
+}
+.component_stats .chart {
+ display: flex;
+ align-items: flex-end;
+ height: 100px;
+}
+.component_stats .chart .bar {
+ display: flex;
+ flex: 1;
+ cursor: pointer;
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
+ border-bottom: 1px solid var(--light);
+
+ background: var(--border);
+ border: 2px solid var(--border);
+}
+.component_stats .chart .bar[title="0"], .component_stats .chart .bar[title="1"], .component_stats .chart .bar[title="2"] {
+ border-top-left-radius: 0px;
+ border-top-right-radius: 0px;
+}
+.component_stats .legend {
+ display: flex;
+ justify-content: space-between;
+ color: var(--light);
+ font-size: 0.8rem;
+}
diff --git a/public/assets/pages/adminpage/ctrl_activity_graph.js b/public/assets/pages/adminpage/ctrl_activity_graph.js
new file mode 100644
index 00000000..684669e9
--- /dev/null
+++ b/public/assets/pages/adminpage/ctrl_activity_graph.js
@@ -0,0 +1,48 @@
+import { createElement } from "../../lib/skeleton/index.js";
+import rxjs, { effect } from "../../lib/rx.js";
+import { get as getLogs } from "./model_log.js";
+import { loadCSS } from "../../helpers/loader.js";
+
+export default async function(render) {
+ await loadCSS(import.meta.url, "./ctrl_activity_graph.css");
+
+ effect(getLogs().pipe(
+ rxjs.map((log) => {
+ const times = log.trim().split("\n").map((line) => new Date(line.substring(0, 19)).getTime());
+ const start = times[0];
+ const end = times[times.length - 1];
+
+ const size = 30
+ const bars = Array(size).fill(0);
+ const width = (end - start) / size;
+ for (const t of times) {
+ const idx = Math.min(size - 1, Math.max(0, Math.floor((t - start) / width)));
+ bars[idx] += 1;
+ }
+ return {
+ bars,
+ start: new Date(start).toLocaleTimeString(),
+ end: new Date(end).toLocaleTimeString(),
+ };
+ }),
+ rxjs.tap(({ bars, start, end }) => {
+ const max = Math.max(1, ...bars);
+ const $root = document.createDocumentFragment();
+ const $chart = createElement(`
`);
+ for (let i = 0; i < bars.length; i++) {
+ const $bar = createElement(`
`)
+ $bar.style.height = Math.sqrt(bars[i]) / Math.sqrt(max) * 100 + "%";
+ $chart.appendChild($bar);
+ }
+ $root.appendChild($chart);
+ $root.appendChild(createElement(`
+
+ ${start}
+ ${end}
+
+ `));
+ render($root);
+ }),
+ rxjs.catchError(() => rxjs.EMPTY),
+ ));
+}
diff --git a/public/assets/pages/adminpage/ctrl_activity_viewer.js b/public/assets/pages/adminpage/ctrl_activity_viewer.js
index 01057f35..25abe402 100644
--- a/public/assets/pages/adminpage/ctrl_activity_viewer.js
+++ b/public/assets/pages/adminpage/ctrl_activity_viewer.js
@@ -13,7 +13,7 @@ export default async function(render) {
-
+
`);
const $log = qs($page, "pre");