Repository navigation
feat(alerts): live coverage preview in the schedule editor - #2435
Conversation
- Kit: rightSheet over the Dialog Submodel (672px form size, scrolling body, sticky footer, slide dropped under reduced motion) with followUrl/isDismissed; page/kit/sheet-param holds the ?sheet= helpers (push to open, replace to close). - Sidebar group labels fold their rows (sidebarGroupToggle, aria-expanded), saved to localStorage; the group holding the current page and the collapsed rail always show every row. - Footer on-call pill from the schedules list's on_call_now and the session user: on call until the handoff, not on call, or hidden without schedules. Re-read at each handoff; a dot with a tooltip on the rail. - /lab/shell shows all three, with a demo form in the sheet.
- Sheet: opening pushes a history entry marked as this tab's; dismissing goes back past it, and only a deep link replaces the param. Kit renamed to sidePanelSheet, footer receives close; sheet-param tests sit beside it. - On-call pill: a shift past its end reads as unknown; reads again at the handoff, hourly while off call, on focus and visibility, and with backoff (60s doubling, capped) after a stale or failed answer, keeping the last good data. Schedule and override writes announce a re-read. - An org with no schedules is cached per org for a day and skips the request; a schedule write clears it. - "until" uses the viewer's time zone with a short zone name. - Sidebar: the collapsed rail renders plain labels (no tab stops); the group holding the current page can fold and keeps only that row. The pill uses aria-label on the rail instead of a title.
… side panel
- Each hub section has its own path (/alerts/rules, /alerts/uptime, /alerts/slos,
/alerts/silences, /alerts/maintenance, /alerts/on-call, /alerts/routing); /alerts
stays the Inbox. One page instance serves them all, so a section change reads
nothing it already has.
- Old /alerts?tab= links replace themselves with the section's path and keep
their other params ("oncall" and "settings" map to on-call and routing).
- The sidebar's Alerts row lists the sections when active and replaces the hub's
own section rail. Sub-item aria-current now follows the most specific match.
- New silence opens in a URL-held side panel (?sheet=silence) with a sticky
Cancel / Save footer; Back, Cancel, Escape and a save close it.
sidePanelSheet gains onSubmit so the panel is a form.
- The silence panel clears ?sheet= in the model as soon as it closes itself, so a clock tick before the URL change can't reopen it. It opens only on the Silences section, and a viewer who can't manage loses a linked ?sheet= once the grants load (replaced away). - A rule lives at /alerts/rules/<ruleId>; /alerts/<ruleId> (notifications, the apps) replaces itself with it, keeping its tab and window. The rule page and editor go back to Rules and their crumbs go through it. - Settings automation and setup health link to the new section paths. - The sidebar reveals the open section, every sub-item past the edge fades, on each route change; sub-rows are tighter, thin rules split the former groups, and the Inbox shows its unacknowledged count from the alert_incidents collection, read only while an alerts page is open. - One AlertsTab schema next to AlertsSection; the sheet commands answer with a clock read instead of a no-op message.
… page The schedule editor renders the draft with the server's renderOnCall (alerting-core/oncall) on every edit: a lane per layer, the final lane with No one gap bars, a gap summary with links that focus each gap, weekend shading, a now line and a 1/2/4 week range. The detail page draws the same timeline from the server's shifts.
… fit, narrow day headers A gap is a hollow, hatched bar in the error tone instead of a warning wash. A bar hides its label under 3.75rem and shows initials when two characters fit. Layer rows can leave the tab order. Day headers stack and thin out their dates as days narrow, and the label column sizes to the timeline's own width.
- The detail calendar draws a skeleton until the shifts for the picked range land, instead of old shifts read as one long gap. - A wholly past range says its gaps are not checked; a draft cannot step back from today. - A layer with a half-typed window does not preview as always active. - The preview redraws only when the zone, layers, members, minute or range change. - An uncovered schedule says nobody is on call; a gap cut off by the range's end has no end time. - The start note shows only for a new schedule started on an earlier day and names who is on call now. - The editor is two columns from 46rem of room, the preview held in view while the form scrolls. - One shared focus command for gap links.
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
This comment has been minimized.
This comment has been minimized.
…size - A `scrim-panel` token (12% black, 24% in dark) with no blur sits beside a side panel, so the page it edits stays readable. Dialogs and other sheets keep `scrim`. - `sidePanelSheet` takes `size`: `compact` (28rem) for a few short fields, `form` (42rem, the default) or `wide`.
- The on-call pill is one 32px row of the footer list: a status dot and "On call until Mon 09:00" in the viewer's zone (the date instead of the weekday once the handoff is over six days out), or a muted "Not on call". The zone, date and schedules are its tooltip and accessible name. On the collapsed rail it is a calendar glyph badged with the dot. - Group chevrons show faintly at rest; the toggle is 28px tall. - A folded group holding the current page shows that page's parent row alone, highlighted, without its sub-items. - Hover is a neutral wash. When a sub-item is the current page only it is tinted; its parent keeps the medium weight on a plain background. - Sub-item rows are 28px on desktop and 36px in the mobile sheet. - /lab/shell gains Services and Logs pages, a far handoff and both panel sizes.
… the silence panel
- Each section's header has one primary action: New rule (split) on the Inbox
and Rules, New monitor, New SLO, New silence, New window, New schedule and
Add destination on their own sections. A section whose empty state already
offers it shows none until it has rows.
- The Alerts sub-list sets its groups apart with a 4px gap instead of rules,
and its rows take the kit's height again.
- The silence panel's Services, Environments and Tags are chips inputs that
suggest what the org's rules name (and the services Uptime listed); a typed
name can still be added. Its labels share one style.
- A picked end is on the viewer's clock and the field names the zone
("Ends, CET"); a scheduled row names its day once. The lab reads the
viewer's zone instead of UTC.
- The duration choice uses the larger segmented size.
# Conflicts: # apps/web/src/shell/oncall.scene.test.ts # apps/web/src/shell/view-nav.ts
This comment has been minimized.
This comment has been minimized.
|
F4 won't fix: the range runs from the start of today for 14 days and gaps are only checked from now, so "the next 14 days" names the range shown on the timeline; a clock-exact window would move the axis every minute. F1 to F3 are being fixed in a follow-up commit (the editor will load the schedule's overrides). |
Editing a saved schedule now reads its overrides and renders the draft with them, so the preview's gaps match paging and the detail page. The timeline is a skeleton until they land; a failed read previews without them, says so and offers another try. A new schedule asks for none. The timeline's day header keeps each day's full name for screen readers when its drawn label is shortened, and durationText's comment says minutes show only under a day.
|
F1 to F3 fixed in 7e5000c. F1: editing a saved schedule now loads its overrides (same |
This comment has been minimized.
This comment has been minimized.
|
@maple-review-bot won't fix F3 and F4. F3: dropping minutes past a day is intended ("3d 4h 30m" is noise in a summary line); the doc comment now says so and a test pins "2d 10h" for 2d 10h 30m. F4: "the next 14 days" names the range drawn on the timeline, which starts at the beginning of today; a clock-exact window would move the axis every minute. |
|
Both can be dismissed — your reasoning matches what the code actually does. F3 — F4 — confirmed the wording describes the drawn range, not a clock-exact window: The only thing I'd add, not as a blocker: because gaps are checked over |
# Conflicts: # apps/web/src/shell/command.ts # apps/web/src/shell/flags.ts
…read within the hour whoever holds it
# Conflicts: # apps/web/src/shell/view-nav.ts
This comment has been minimized.
This comment has been minimized.
|
@maple-review-bot won't fix F3. Past a day the length is a headline figure and the exact start and end are printed beside it in the same line, so the minutes are dropped on purpose (the function's comment says so). |
|
You're right, and F3 can be dismissed. I checked the head commit: the only production caller of `... longest ${durationText(longest.endMs - longest.startMs)}${isOpenGap(...) ? " or longer" : ""} (${gapRangeText(longest, scope.timeZone, scope.checkedToMs)})`
|
# Conflicts: # apps/web/src/shell/message.ts # apps/web/src/shell/model.ts # apps/web/src/shell/oncall.scene.test.ts # apps/web/src/shell/update.ts # apps/web/src/shell/view-nav.ts # packages/views/src/ui/sheet.ts
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
F5: fixed in |
This comment has been minimized.
This comment has been minimized.
|
F6: fixed in @maple-review-bot won't fix F3 (dismissed earlier: the exact start and end are printed beside the rounded length). |
|
Checked the head ( F6 is most of the way fixed — the 304px squeeze and the
So the split wants Your 1024px and 1280px checks don't contradict this; 1280px just lands above 59rem. To make it exact: On F3 I agree — |
…editor's two-column breakpoint
|
Right, the arithmetic was one rem short. |
This comment has been minimized.
This comment has been minimized.
|
F7: fixed in |
Maple review🟢 Confidence 10/10 · safe to merge. The gap and lane logic is pinned by tests that sample the preview against Adds a live coverage preview to the on-call schedule editor (from the draft, overrides included) and reuses it on the detail page, with gap bars, a gap summary and a 30-second clock.
Still open from earlier reviews
Fixed since the last review
|
Stacked on #2426. The on-call schedule editor now shows who is on call while you edit, and where nobody is.
What changed
renderOnCall(now exported as@maple/alerting-core/oncall; it only depends oneffectand domain types) on the draft, so nothing is duplicated. A test samples the preview againstonCallAtevery 15 to 30 minutes, including across a DST change.shift-timelinegains agaptone, weekend shading, a zone label, labels that hide when a bar is too narrow, and bar ids.formSectionnow stacks by its container's width.Before / after
Editing a saved schedule with an override:
Lab:
/lab/oncall?state=editor-gaps|editor-covered|editor-early|editor-override.Verification
apps/web:bunx vitest run src/page/alerts src/lab548 pass, rootsrc/*.test.ts132 pass. New: 14 coverage tests (layering, overrides, handoffs, DST, empty layers, server match), scene tests for live updates, the gap summary, range switching, the stale-range skeleton and the preview not re-rendering while typing the name.packages/viewstimeline tests 9 pass;alerting-coreoncall 25 pass.@maple/web,packages/views);oxlint -c .oxlintrc.effect.jsonclean on changed files;ui:index,ui:patterns:checkpass.Not verified: gap-link focus in a real browser (the command is tested), the uptime form's look after the
formSectionchange, and a live org (preview test pending).listOverridesreturns only overrides that have not ended, so one that ended earlier today is not drawn in the past part of today (gaps are checked from now, so the summary is unaffected).🤖 Generated with Claude Code
Summary by CodeRabbit