The stats page could only say how many alerts fired. It now answers when
one alert fires, how often, and how fast it is picked up and fixed: each
row of the top-alerts list opens /stats/alert/{name}, with a timeline,
by-hour and by-weekday charts, time-to-resolve per occurrence and a table
of every occurrence linking to its incident. The overview gains a
weekday-by-hour heatmap and a few derived observations.
An occurrence is an incident, not an alert row. Alert rows are reused per
fingerprint and mutated in place, so they carry no history; incidents are
the only record of each time something fired and was answered. That means
/api/stats/alerts/top now counts incidents opened (filtered on
triggered_at) so the list and the drilldown agree. The response keeps name
and count and only adds fields, so existing clients keep working, but the
counts can differ from before.
No migration: the lookups use incidents_signature_idx. Label breakdown,
team and severity filters and period-over-period deltas from the design
draft are left for later.
Claude-Session: https://claude.ai/code/session_016mBLURvJoMuUEr9cB2RpUN
Addresses the screenshot-review feedback in #26. No framework or build
step added — all of this stays within the existing plain HTML/CSS/
vanilla-JS + go:embed architecture.
- Nav: re-enable the bottom tab bar that was already built and
switched off (Queue/On-call/Alerts/Team + a "More" sheet for
Stats/Admin/Account), replacing the hamburger on phone width.
- Queue: chip counts, a scroll fade on the filter row, a
"Triggered Xh ago" + severity label per row, a chevron on the team
switcher so it reads as a dropdown.
- On-call: collapse repeated same-person days into shift bars (week
view and "your shifts" both), show the week as a date range with
the ISO week number as secondary text, split "Current shift" out
from "Next shifts" with "ends in Nd", a pill badge + row highlight
for "you".
- Incident detail: fix the actual bug behind the duplicate
"acknowledged" timeline entries (acknowledgeIncident's UPDATE had no
guard on the incident's current status, so acknowledging an
already-acknowledged incident silently re-logged the event — now
idempotent, with regression tests on both the authenticated route
and the ntfy ack-button route). Relabel escalation re-pages so they
don't look like the same page landing twice. Copy the primary action
up near the top. Label the "···" button. Group the timeline by
phase (triggered/acknowledged/resolved). Add an "at a glance"
summary row (duration/severity/responsible) and collapse the group
labels by default.
- Team overview: reword the vague copy ("One owner." etc.) into plain
labels.
- Empty states: fill in missing icons/one-liners across queue,
alerts, stats and the incident timeline.
- CSS: fix card padding bugs, verify link contrast already passes AA,
introduce a --fs-* type-scale token set and migrate the few
genuinely isolated cases onto it (left sizes tied to a fixed shape,
a deliberately prominent display, or a non-negotiable constraint
like the iOS-zoom-prevention input size as documented exceptions
rather than guess at a render this change can't see).
Verified with the full fmt/lint/test/helm-lint gate, plus a live
instance against the test DB with seeded incidents and schedule data
to trace the on-call grouping and timeline phase-splitting logic
against real API responses.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Statistics used to live only in terdut-tui; the account page said so.
The page shows the same figures as the TUI's Stats tab -- incident
counts, MTTA and MTTR, top alerts, and alert frequency by hour (UTC) and
by day of week -- and adds a range picker (Today, 7d, 30d, 90d, All)
that the TUI does not have. The ranges are day-granular because the
server reads from/to as whole UTC dates, so there is no 24h chip.
No server change: the page uses the existing /api/stats/* endpoints,
which already scope to the caller's teams. Charts are inline SVG and
plain elements sized from script, because the CSP forbids inline styles,
inline scripts and CDN libraries.
Removes the "statistics are in terdut-tui" notes from the account page
and the README.