Put a team's own settings in the web UI
Closes #17. Everything a team owner configures was API-only: escalation, integrations, dead man's switches, membership, and the rota -- which the on-call view still described as the TUI's job, and the TUI has been broken against this server since teams landed. Setting up the feature this whole line of work exists for meant using curl. A Team tab now holds all of it, one team at a time, with a picker for somebody in more than one. An owner edits; a member sees the same page without the controls, because the server refuses their writes anyway -- hiding a button is a courtesy to the reader, not the thing enforcing anything. The escalation editor holds a draft and sends the whole ladder, because the API replaces it wholesale: the levels are an order, and patching one rung leaves the numbering of the others undecided. Adding a level defaults to five minutes and the rota, which is the shape almost every ladder starts as. An integration key is returned exactly once, so creating one opens a panel that says so, shows the URL large with a copy button, and renders the Alertmanager receiver snippet with the URL already in it -- the next thing anybody does with that key is paste it into a config. The panel stays until it is dismissed rather than disappearing on the next re-render. The incident view gains where an incident is on the ladder and when the next page is due, which is the question somebody looking at an unacknowledged incident actually has. The API carries it: the incident payload now includes escalation_level and escalation_due_at, the latter computed in the incident SELECT by joining the level's timeout, so a list costs no extra queries. Verified against a live server by making every call the page makes, including the writes: the six reads the Team tab issues, a two-level ladder saved and read back, an integration created and its key returned once, three days of rota assigned, switches set, and an incident showing level 1 with a due time five minutes out. Claude-Session: https://claude.ai/code/session_01RHPj4ggeFdEjKKfm4SHbD7
This commit is contained in:
@@ -52,6 +52,13 @@ type querier interface {
|
||||
|
||||
const incidentSelectFrom = `
|
||||
SELECT i.id, i.team_id, t.name, i.group_key, i.title, i.group_labels, i.status, i.severity,
|
||||
i.escalation_level,
|
||||
-- When this level runs out. Computed here rather than in Go because
|
||||
-- the timeout lives beside the level in the policy, and one join is
|
||||
-- cheaper than a second query per incident in a list.
|
||||
(SELECT i.escalation_level_at + el.timeout_seconds
|
||||
FROM escalation_levels el
|
||||
WHERE el.team_id = i.team_id AND el.position = i.escalation_level),
|
||||
i.triggered_at,
|
||||
i.acknowledged_by, i.acknowledged_at, ack.username,
|
||||
i.assigned_to, asg.username, i.snoozed_until,
|
||||
@@ -65,10 +72,11 @@ func scanIncident(s scanner) (models.Incident, error) {
|
||||
var i models.Incident
|
||||
var groupLabelsJSON string
|
||||
var triggeredAt int64
|
||||
var ackAt, snoozedUntil, resolvedAt, archivedAt *int64
|
||||
var ackAt, snoozedUntil, resolvedAt, archivedAt, escalationDue *int64
|
||||
|
||||
if err := s.Scan(
|
||||
&i.ID, &i.TeamID, &i.TeamName, &i.GroupKey, &i.Title, &groupLabelsJSON, &i.Status, &i.Severity,
|
||||
&i.EscalationLevel, &escalationDue,
|
||||
&triggeredAt,
|
||||
&i.AcknowledgedByID, &ackAt, &i.AcknowledgedByUser,
|
||||
&i.AssignedToID, &i.AssignedToUser, &snoozedUntil,
|
||||
@@ -83,6 +91,7 @@ func scanIncident(s scanner) (models.Incident, error) {
|
||||
i.SnoozedUntil = unixPtr(snoozedUntil)
|
||||
i.ResolvedAt = unixPtr(resolvedAt)
|
||||
i.ArchivedAt = unixPtr(archivedAt)
|
||||
i.EscalationDueAt = unixPtr(escalationDue)
|
||||
return i, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user