Compare commits

..

2 Commits

Author SHA1 Message Date
Niklas Ye 220eb97665 Set the chart's placeholder version to 0.47.1
Release / binaries (push) Successful in 24s
Release / image (push) Successful in 1m9s
Release / scan-image (push) Successful in 1s
CI / chart (push) Successful in 1s
CI / security (push) Successful in 40s
CI / test (push) Successful in 6m18s
Release / test (push) Successful in 10s
Release / chart (push) Successful in 2s
Cosmetic: make helm-package passes --version and --app-version from the
tag, so these fields decide nothing about what is published. They are kept
in step so the tree heading for v0.47.1 does not say 0.47.0, as in
fcd8f83 and a314083.

Claude-Session: https://claude.ai/code/session_016mBLURvJoMuUEr9cB2RpUN
2026-10-19 08:18:56 +02:00
Niklas Ye 8521bdcef7 Fold the stats pages to fit a phone
The stats pages were laid out for a desktop width and did not fit a phone.
Below 600px the top-alerts rows now take three lines (name, count and
sparkline, last seen and mean fix) instead of six columns, the heatmap
shrinks to the screen with fewer hour labels, and the drilldown's
time-to-resolve bars and occurrences table fold into stacked lines and
cards rather than scrolling sideways. Grid children on the stats pages may
now shrink below their content width, which is what let the heatmap push
the page wider than the screen.

CSS and one data-label attribute only: no API, schema or behaviour change
at wider widths.

Claude-Session: https://claude.ai/code/session_016mBLURvJoMuUEr9cB2RpUN
2026-10-19 08:18:56 +02:00
3 changed files with 50 additions and 7 deletions
+2 -2
View File
@@ -15,5 +15,5 @@ type: application
# appVersion and image.tag in values.yaml no longer agree, and that is not an oversight:
# image.tag stays "latest", which is what a local install actually pulls. appVersion is
# metadata and drives nothing.
version: 0.47.0
appVersion: "v0.47.0"
version: 0.47.1
appVersion: "v0.47.1"
+40
View File
@@ -1386,6 +1386,46 @@ a.top-row:hover { background: var(--surface-2); }
.stats-split { grid-template-columns: 3fr 1fr; align-items: stretch; }
}
/* Phones: nothing on the stats pages may be wider than the screen, so the
wide rows fold into two or three lines instead of scrolling sideways. */
.stats > *, .stats-split > * { min-width: 0; }
@media (max-width: 599px) {
.top-head { display: none; }
.toplist { overflow-x: visible; }
.top-row {
min-width: 0; grid-template-columns: minmax(0, 1fr) auto; row-gap: 4px; padding: 10px 4px;
}
.top-row > :nth-child(1) { grid-column: 1; grid-row: 1; font-weight: 600; }
.top-row > :nth-child(6) { grid-column: 2; grid-row: 1; }
.top-row > :nth-child(2) { grid-column: 1; grid-row: 2; }
.top-row > :nth-child(3) { grid-column: 2; grid-row: 2; }
.top-row > :nth-child(4) { grid-column: 1; grid-row: 3; font-size: 12px; }
.top-row > :nth-child(5) { grid-column: 2; grid-row: 3; font-size: 12px; text-align: right; }
.top-row > :nth-child(4)::before { content: "Last seen "; }
.top-row > :nth-child(5)::before { content: "Fix "; }
.heatmap > * { min-width: 0; }
.heat-row, .heat-cells { gap: 2px; }
.heat-day { width: 26px; }
.heat-cell { height: 18px; border-radius: 3px; }
.heat-hour { font-size: 9px; overflow: visible; white-space: nowrap; }
.heat-hour:not(:nth-child(6n+1)) { visibility: hidden; }
.fix-row { flex-wrap: wrap; row-gap: 2px; }
.fix-label { width: auto; flex: 1; }
.fix-total { width: auto; }
.fix-track { flex: 1 1 100%; order: 3; }
.occ-wrap { overflow-x: visible; }
.occ { min-width: 0; }
.occ thead { display: none; }
.occ, .occ tbody { display: block; }
.occ tr { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 12px; padding: 10px 0; border-top: 1px solid var(--border); }
.occ td { border-top: 0; padding: 0; overflow-wrap: anywhere; }
.occ td:first-child { grid-column: 1 / -1; font-weight: 600; }
.occ td[data-label]::before { content: attr(data-label) ": "; color: var(--muted); font-size: 12px; }
}
/* On-call, wide: the hero and the week on the left, your shifts on the right.
.view-page > * caps its children at 760px, so the grid lifts that. */
@media (min-width: 900px) {
+8 -5
View File
@@ -103,17 +103,20 @@ function fixBars(d) {
}));
}
// On a phone each row folds into a card; the labels come from data-label.
const OCC_COLS = ['Fired (UTC)', 'Time to ack', 'Duration', 'Acknowledged by', 'Status'];
function table(d) {
return h('div', { class: 'occ-wrap' },
h('table', { class: 'occ' },
h('thead', {}, h('tr', {}, ['Fired (UTC)', 'Time to ack', 'Duration', 'Acknowledged by', 'Status'].map((t) => h('th', { text: t })))),
h('thead', {}, h('tr', {}, OCC_COLS.map((t) => h('th', { text: t })))),
h('tbody', {}, d.occurrences.map((o) => h('tr', {},
h('td', {}, h('a', { href: `/incidents/${o.incident_id}`, text: fmtTime(o.triggered_at) }),
` ${WEEKDAYS[new Date(o.triggered_at * 1000).getUTCDay()]}`),
h('td', { text: sec(o.ack_seconds) }),
h('td', { text: sec(o.resolve_seconds) }),
h('td', { text: o.acknowledged_by || '—' }),
h('td', { text: o.status }))))));
h('td', { 'data-label': OCC_COLS[1], text: sec(o.ack_seconds) }),
h('td', { 'data-label': OCC_COLS[2], text: sec(o.resolve_seconds) }),
h('td', { 'data-label': OCC_COLS[3], text: o.acknowledged_by || '—' }),
h('td', { 'data-label': OCC_COLS[4], text: o.status }))))));
}
export function body(name, d, error, onRange) {