// The alert feed: Alertmanager's own records, read-only. Each row leads to the // incident it belongs to, which is where anything can be done about it. import * as api from './api.js'; import { h, clear, badge, emptyState, spinner } from './ui.js'; import { age, severityClass, labelSummary } from './format.js'; const FILTERS = [ { id: 'firing', label: 'Firing', query: { status: 'firing' } }, { id: 'resolved', label: 'Resolved', query: { status: 'resolved' } }, { id: 'all', label: 'All', query: {} }, { id: 'archived', label: 'Archived', query: { archived: 'true' } }, ]; const view = () => document.getElementById('view-alerts'); let filter = 'firing'; let items = null; let error = null; export function show() { render(); refresh(); } export async function refresh() { const requested = filter; const f = FILTERS.find((x) => x.id === filter); try { const result = await api.alerts({ ...f.query, limit: 200 }); if (requested !== filter) return; items = result; error = null; } catch (err) { if (requested !== filter) return; error = err.message; } render(); } function setFilter(id) { if (id === filter) return; filter = id; items = null; render(); refresh(); } function render() { const chips = h('div', { class: 'chips', role: 'tablist', 'aria-label': 'Filter' }, FILTERS.map((f) => h('button', { class: 'chip', type: 'button', role: 'tab', 'aria-selected': String(f.id === filter), onclick: () => setFilter(f.id), text: f.label, }))); let body; if (error && !items) body = h('div', { class: 'load-error', text: error }); else if (!items) body = spinner(); else if (!items.length) body = emptyState(filter === 'firing' ? 'Nothing firing' : 'No alerts', '', filter === 'firing' ? 'checkCircle' : null); else { body = h('div', { class: 'list' }, error && h('div', { class: 'load-error', text: `Showing older data: ${error}` }), items.map(row)); } clear(view(), h('div', {}, chips, body)); } function row(a) { const summary = (a.annotations && a.annotations.summary) || ''; const sev = a.labels && a.labels.severity; const labels = labelSummary(Object.fromEntries( Object.entries(a.labels || {}).filter(([k]) => k !== 'severity'))); const linked = a.incident_id != null; return h(linked ? 'a' : 'div', { class: `row st-${a.status} ${linked ? '' : 'no-link'}`, href: linked ? `/incidents/${a.incident_id}` : null, }, h('div', { class: 'row-title', text: a.name }), h('div', { class: 'row-age', title: a.starts_at, text: age(a.status === 'firing' ? a.starts_at : a.received_at) }), h('div', { class: 'row-meta' }, badge(a.status === 'firing' ? 'Firing' : 'Resolved', `st-${a.status}`), sev && badge(sev, `plain ${severityClass(sev)}`), summary && h('span', { text: summary }), labels && h('span', { class: 'labels', text: labels }), ), ); }