Show similar earlier incidents and let notes be marked as the fix

The incident view gets a "Seen before" section from the server's new
/similar endpoint; an older server without it just shows nothing. C adds a
note as the resolution note, alongside c for a plain note. Needs the
server release that adds /similar.

Claude-Session: https://claude.ai/code/session_01MMados3BD1oSjevHxbmVqU
This commit is contained in:
Niklas Ye
2026-09-25 15:42:25 +02:00
parent 451ce99a62
commit 057302cb39
7 changed files with 104 additions and 24 deletions
+29 -5
View File
@@ -90,7 +90,11 @@ func (m Model) renderBody() string {
case modeIncidentDetail, modeAlertDetail:
return m.renderDetail()
case modeNote:
return m.renderPrompt(m.styles.Header.Render("Note: ") + m.noteInput.View())
label := "Note: "
if m.notePinned {
label = "What fixed it: "
}
return m.renderPrompt(m.styles.Header.Render(label) + m.noteInput.View())
case modeSnooze:
return m.renderPrompt(m.styles.Header.Render("Snooze for: ") + m.snoozeInput.View())
case modeConfirm:
@@ -135,7 +139,7 @@ func (m Model) renderFooter() string {
switch m.mode {
case modeIncidentDetail:
if !m.selectedIncident.IsOpen() {
return withStatus(" x·archive c·note [/]·select d·del esc·back")
return withStatus(" x·archive c·note C·fix note [/]·select d·del esc·back")
}
return withStatus(" a·ack A·unack R·resolve s·assign z·snooze Z·unsnooze c·note [/]·select d·del esc·back")
@@ -485,7 +489,7 @@ func line(style lipgloss.Style, s string) string {
// ── Content builders ───────────────────────────────────────────────────────
func buildIncidentDetailContent(s Styles, inc api.Incident, timeline []api.IncidentEvent, cursor, width int) string {
func buildIncidentDetailContent(s Styles, inc api.Incident, timeline []api.IncidentEvent, similar []api.SimilarIncident, cursor, width int) string {
now := time.Now()
var b strings.Builder
contentW := width - 4
@@ -579,6 +583,22 @@ func buildIncidentDetailContent(s Styles, inc api.Incident, timeline []api.Incid
}
b.WriteString("\n")
// Seen before: earlier incidents of the same kind that someone left notes on.
if len(similar) > 0 {
b.WriteString(divider(s, "Seen before", width))
for _, sim := range similar {
b.WriteString(fmt.Sprintf(" #%-6d %-44s %s\n", sim.ID, truncate(sim.Title, 44),
s.Muted.Render("resolved "+humanAgo(now, sim.ResolvedAt))))
for _, n := range sim.ResolutionNotes {
b.WriteString(" " + s.Resolved.Render("fixed: ") + n.Detail + "\n")
}
if len(sim.ResolutionNotes) == 0 {
b.WriteString(line(s.Muted, fmt.Sprintf(" %d note(s), no resolution note", sim.NoteCount)))
}
}
b.WriteString("\n")
}
// Timeline — the only history the server keeps.
notes := noteEvents(timeline)
b.WriteString(divider(s, fmt.Sprintf("Timeline (%d events, %d notes)", len(timeline), len(notes)), width))
@@ -588,7 +608,7 @@ func buildIncidentDetailContent(s Styles, inc api.Incident, timeline []api.Incid
noteIndex := 0
for _, e := range timeline {
when := s.Muted.Render(humanAgo(now, e.CreatedAt))
if e.Type != api.EventNote {
if e.Type != api.EventNote && e.Type != api.EventResolutionNote {
b.WriteString(fmt.Sprintf(" %-52s %s\n", eventLabel(e), when))
continue
}
@@ -598,7 +618,11 @@ func buildIncidentDetailContent(s Styles, inc api.Incident, timeline []api.Incid
marker = s.Selected.Render("> ")
author = s.Selected.Render(e.Username)
}
b.WriteString(fmt.Sprintf("%s%-50s %s\n", marker, author+" wrote", when))
verb := " wrote"
if e.Type == api.EventResolutionNote {
verb = " noted the fix"
}
b.WriteString(fmt.Sprintf("%s%-50s %s\n", marker, author+verb, when))
b.WriteString(" " + e.Detail + "\n")
noteIndex++
}