Dashboard: every number leads somewhere, real counts, and new charts
- Cards and charts link to the page they're about: pending messages to the queue, bans to blocked IPs, report warnings to the reports, and so on, shown only to viewers who may open that page. - A one-line status under the greeting: what needs a look (failed tasks, messages retrying, recipients given up on) or, when nothing does, what's there. Each phrase is a link. - Counts from the server's own objects stand in for live metrics it can't report, and a live number with no source reads as unknown, not zero. - Who uses the space: a treemap of people sized by storage, colored by how near their quota they are, each tile opening the account. - Where mail is waiting: queued recipients by destination, split into waiting, retrying and given up, each row opening the filtered queue. - The weekly rhythm: messages by hour and weekday, shown once metric history exists. - Dashboard tabs are titled by their label.
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Coffey Labs
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { squarify } from './treemap';
|
||||
|
||||
describe('squarify', () => {
|
||||
it('fills the box, with areas in proportion to the values', () => {
|
||||
const tiles = squarify([6, 6, 4, 3, 2, 2, 1], (v) => v, 600, 400);
|
||||
expect(tiles).toHaveLength(7);
|
||||
const area = tiles.reduce((s, t) => s + t.w * t.h, 0);
|
||||
expect(area).toBeCloseTo(600 * 400, 3);
|
||||
for (const t of tiles) {
|
||||
expect(t.w * t.h).toBeCloseTo((t.item / 24) * 600 * 400, 3);
|
||||
expect(t.x).toBeGreaterThanOrEqual(-1e-9);
|
||||
expect(t.y).toBeGreaterThanOrEqual(-1e-9);
|
||||
expect(t.x + t.w).toBeLessThanOrEqual(600 + 1e-6);
|
||||
expect(t.y + t.h).toBeLessThanOrEqual(400 + 1e-6);
|
||||
}
|
||||
});
|
||||
|
||||
it('keeps tiles reasonably square', () => {
|
||||
const tiles = squarify([6, 6, 4, 3, 2, 2, 1], (v) => v, 600, 400);
|
||||
const worst = Math.max(...tiles.map((t) => Math.max(t.w / t.h, t.h / t.w)));
|
||||
expect(worst).toBeLessThan(4);
|
||||
});
|
||||
|
||||
it('gives nothing for nothing', () => {
|
||||
expect(squarify([0, 0], (v) => v, 100, 100)).toEqual([]);
|
||||
expect(squarify([], (v: number) => v, 100, 100)).toEqual([]);
|
||||
expect(squarify([1], (v) => v, 0, 100)).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user