From 13e6d6287dc27009d4932d980012a2ec81c036f3 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 1 Nov 2026 16:38:56 +0800 Subject: [PATCH] fix(dashboard): put the traffic heatmap back under the briefing In the aside the hourly cells were too small to read, and when nothing is broken the briefing left the main column nearly empty. The heatmap now follows the briefing at full width with taller cells and an hour axis; the slowest transactions stay in the aside. On phones the cells shrink so all 24 hours still fit without scrolling sideways. --- web/src/views/DashboardView.vue | 13 ++++++-- web/src/views/__tests__/DashboardView.test.ts | 11 ++++--- .../dashboard/TransactionDensityWidget.vue | 31 +++++++++++++++++-- .../views/dashboard/__tests__/widgets.test.ts | 1 + 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/web/src/views/DashboardView.vue b/web/src/views/DashboardView.vue index 8b8b02f..edc7627 100644 --- a/web/src/views/DashboardView.vue +++ b/web/src/views/DashboardView.vue @@ -161,9 +161,14 @@ function finishSetup() {
- +
+ +
+ +
@@ -173,7 +178,6 @@ function finishSetup() { -
@@ -196,6 +200,10 @@ function finishSetup() { border-right: 1px solid var(--border-soft); } +.db-tail { + padding: 8px 16px 40px; +} + .db-aside { min-width: 0; padding: 32px 8px 40px; @@ -208,6 +216,7 @@ function finishSetup() { @media (max-width: 768px) { .db-body { grid-template-columns: 1fr; } .db-main { border-right: none; } + .db-tail { padding: 0 0 28px; } /* On phones the health summary follows the briefing. */ .db-aside { padding: 8px 0 28px; border-top: 1px solid var(--border-soft); } } diff --git a/web/src/views/__tests__/DashboardView.test.ts b/web/src/views/__tests__/DashboardView.test.ts index aa70af8..ff65825 100644 --- a/web/src/views/__tests__/DashboardView.test.ts +++ b/web/src/views/__tests__/DashboardView.test.ts @@ -405,14 +405,17 @@ describe('DashboardView', () => { }) describe('widget layout', () => { - it('lets the briefing end the main column and keeps health, then performance, beside it', () => { + it('follows the briefing with the week\'s traffic and keeps health, then performance, beside it', () => { const wrapper = makeWrapper({ projects: [{ id: 'p1', name: 'A', slug: 'a' }, { id: 'p2', name: 'B', slug: 'b' }], }) const titles = (column: string) => wrapper.findAll(`${column} .db-sec__title`).map(t => t.text()) - expect(wrapper.find('.db-main > .brief').exists()).toBe(true) - expect(wrapper.findAll('.db-main > *')).toHaveLength(1) - expect(titles('.db-aside')).toEqual(['Vitals · 24h', 'Release health', 'Projects', 'Recent alerts', 'Slowest transactions', 'Traffic · last 7 days']) + const main = wrapper.findAll('.db-main > *') + expect(main).toHaveLength(2) + expect(main[0].classes()).toContain('brief') + expect(main[1].classes()).toContain('db-tail') + expect(titles('.db-tail')).toEqual(['Traffic · last 7 days']) + expect(titles('.db-aside')).toEqual(['Vitals · 24h', 'Release health', 'Projects', 'Recent alerts', 'Slowest transactions']) }) it('fetches widget data only once the dashboard itself is shown', () => { diff --git a/web/src/views/dashboard/TransactionDensityWidget.vue b/web/src/views/dashboard/TransactionDensityWidget.vue index 00a44fd..c64aff8 100644 --- a/web/src/views/dashboard/TransactionDensityWidget.vue +++ b/web/src/views/dashboard/TransactionDensityWidget.vue @@ -100,6 +100,10 @@ function cellColor(v: number): string { :style="cellColor(cell) ? { background: cellColor(cell) } : undefined" /> + +
low
@@ -125,7 +129,7 @@ function cellColor(v: number): string { .db-heatmap__row { display: flex; align-items: center; - gap: 1px; + gap: 2px; margin-bottom: 2px; } @@ -142,7 +146,7 @@ function cellColor(v: number): string { .db-heatmap__cell { position: relative; flex: 1; - height: 9px; + height: 16px; border-radius: 2px; background: var(--surface-2); min-width: 0; @@ -170,6 +174,29 @@ function cellColor(v: number): string { .db-heatmap__cell--legend { flex: none; width: 11px; + height: 11px; +} + +/* Ticks sit over the cell area: the day labels take the first 28px. */ +.db-heatmap__axis { + position: relative; + height: 14px; + margin: 4px 0 0 28px; +} + +.db-heatmap__tick { + position: absolute; + top: 0; + font-size: 10px; + color: var(--text-3); + opacity: 0.5; + font-variant-numeric: tabular-nums; +} + +@media (max-width: 768px) { + .db-heatmap { padding: 8px 0 10px; } + .db-heatmap__row { gap: 1px; } + .db-heatmap__cell { height: 12px; } } .db-heatmap__foot { diff --git a/web/src/views/dashboard/__tests__/widgets.test.ts b/web/src/views/dashboard/__tests__/widgets.test.ts index 2ce873a..628648f 100644 --- a/web/src/views/dashboard/__tests__/widgets.test.ts +++ b/web/src/views/dashboard/__tests__/widgets.test.ts @@ -416,5 +416,6 @@ describe('TransactionDensityWidget', () => { expect(wrapper.findAll('.db-heatmap__row')).toHaveLength(7) expect(wrapper.findAll('.db-heatmap__row .db-heatmap__cell')).toHaveLength(7 * 24) expect(wrapper.find('.db-heatmap__row .db-heatmap__cell').attributes('data-tip')).toMatch(/00:00–01:00 · no events$/) + expect(wrapper.findAll('.db-heatmap__tick').map(t => t.text())).toEqual(['00:00', '06:00', '12:00', '18:00']) }) })