mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-06 08:22:24 +01:00
feature (admin): activity graph
This commit is contained in:
parent
dce68b81a3
commit
79aaf52dbd
4 changed files with 83 additions and 3 deletions
|
|
@ -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(`
|
||||
<div class="component_logpage sticky">
|
||||
<h2>Events</h2>
|
||||
<h2>System Logs</h2>
|
||||
<div class="component_logviewer"></div>
|
||||
<div class="component_stats"></div>
|
||||
<div class="component_logger"></div>
|
||||
|
||||
<h2>Activity Report</h2>
|
||||
<h2>Audit Report</h2>
|
||||
<div class="component_audit"></div>
|
||||
<div>
|
||||
`);
|
||||
|
|
@ -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")));
|
||||
});
|
||||
|
|
|
|||
29
public/assets/pages/adminpage/ctrl_activity_graph.css
Normal file
29
public/assets/pages/adminpage/ctrl_activity_graph.css
Normal file
|
|
@ -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;
|
||||
}
|
||||
48
public/assets/pages/adminpage/ctrl_activity_graph.js
Normal file
48
public/assets/pages/adminpage/ctrl_activity_graph.js
Normal file
|
|
@ -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(`<div class="chart"></div>`);
|
||||
for (let i = 0; i < bars.length; i++) {
|
||||
const $bar = createElement(`<div class="bar" title="${bars[i]}"></div>`)
|
||||
$bar.style.height = Math.sqrt(bars[i]) / Math.sqrt(max) * 100 + "%";
|
||||
$chart.appendChild($bar);
|
||||
}
|
||||
$root.appendChild($chart);
|
||||
$root.appendChild(createElement(`
|
||||
<div class="legend">
|
||||
<span>${start}</span>
|
||||
<span>${end}</span>
|
||||
</div>
|
||||
`));
|
||||
render($root);
|
||||
}),
|
||||
rxjs.catchError(() => rxjs.EMPTY),
|
||||
));
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ export default async function(render) {
|
|||
<a href="${getLogUrl()}" download="${logname()}">
|
||||
<button class="component_button primary">Download</button>
|
||||
</a>
|
||||
<br/><br/>
|
||||
<br><br>
|
||||
</div>
|
||||
`);
|
||||
const $log = qs($page, "pre");
|
||||
|
|
|
|||
Loading…
Reference in a new issue