Files
FabledSteward/steward/templates/status/widget.html
T
bvandeusenandClaude Opus 5.5 b29b160baa
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 1m23s
CI / unit (push) Successful in 1m33s
CI / publish (push) Successful in 1m16s
feat(widgets): Overview verdict, pending monitors' age, fullest filesystems first (#5810)
- Overview: a verdict line when something is wrong ('1 alert firing, 1
  monitor down.'); its picker description now says what it lists (alerts,
  monitors down, anomalies). Host figures have their home in Hosts.
  Comments no longer name the Status page that was folded into Monitors.
- Monitors widget: a monitor not checked yet says when it was added.
- Host page: filesystems fullest first, by the worse of space and inodes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 10:11:37 -04:00

94 lines
6.5 KiB
HTML

{# status/widget.html — the dashboard Overview (#5213): everything that needs
attention, across hosts, monitors and alerts, with since-times. The Monitors
widget is the full list of checks; this is the summary. #}
{% from "_macros.html" import status_badge %}
{% set firing = alerts | selectattr("label", "in", ["firing", "no_data"]) | list %}
{% set anomalies = anomalies or [] %}
<div style="display:flex;flex-wrap:wrap;gap:0.4rem 1.5rem;align-items:baseline;margin-bottom:0.6rem;">
<span>
{% if session.user_id %}<a href="/hosts/" style="color:inherit;text-decoration:none;">{% endif %}
<span class="stat-val" style="font-size:1.5rem;">{{ total_hosts }}</span><span class="stat-label">host{{ '' if total_hosts == 1 else 's' }}</span>
{% if session.user_id %}</a>{% endif %}
</span>
<span title="Uptime monitors by their latest check">
<span class="stat-label" style="margin-right:0.35rem;">Monitors</span>
<span class="stat-val tone-ok" style="font-size:1.5rem;">{{ up }}</span><span class="stat-label">up</span>
<span class="stat-val {{ 'tone-crit' if down else 'tone-dim' }}" style="font-size:1.5rem;margin-left:0.6rem;">{{ down }}</span><span class="stat-label">down</span>
{% if pending %}<span class="stat-val tone-warn" style="font-size:1.5rem;margin-left:0.6rem;">{{ pending }}</span><span class="stat-label">pending</span>{% endif %}
</span>
<span title="Alert rules firing or gone silent">
{% if session.user_id %}<a href="/alerts/" style="color:inherit;text-decoration:none;">{% endif %}
<span class="stat-label" style="margin-right:0.35rem;">Alerts</span>
<span class="stat-val {{ 'tone-crit' if firing else 'tone-dim' }}" style="font-size:1.5rem;">{{ firing|length }}</span><span class="stat-label">firing</span>
{% if session.user_id %}</a>{% endif %}
</span>
</div>
{% set problems = entries | rejectattr("status", "equalto", "up") | list %}
{% if not problems and not alerts and not anomalies %}
<div class="tone-ok" style="font-size:0.85rem;padding:0.3rem 0;">
{% if entries %}✓ All {{ up }} monitors are up and no alert is firing.{% else %}No monitors configured, and no alert is firing.{% endif %}
</div>
{% else %}
{# The verdict when something is wrong (#5810): what, in counts, worst first. #}
{% set said = [] %}
{% if firing %}{% set _ = said.append(firing|length ~ " alert" ~ ('' if firing|length == 1 else 's') ~ " firing") %}{% endif %}
{% if down %}{% set _ = said.append(down ~ " monitor" ~ ('' if down == 1 else 's') ~ " down") %}{% endif %}
{% if anomalies %}{% set _ = said.append(anomalies|length ~ " metric" ~ ('' if anomalies|length == 1 else 's') ~ " unusual") %}{% endif %}
{% if pending %}{% set _ = said.append(pending ~ " monitor" ~ ('' if pending == 1 else 's') ~ " waiting for a first check") %}{% endif %}
{% if said %}<div class="w-verdict {{ 'tone-crit' if firing or down else 'tone-warn' }}">{{ said|join(', ')|capitalize }}.</div>{% endif %}
<div style="display:grid;gap:1px;">
{% for a in alerts[:6] %}
<div style="display:flex;align-items:center;gap:0.5rem;padding:0.2rem 0;font-size:0.82rem;">
{{ status_badge(a.label) }}
{% set nm_style = "flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;" %}
{% if session.user_id %}
<a href="/alerts/history?rule={{ a.rule.id }}" title="{{ a.rule.name }}" style="{{ nm_style }}color:inherit;text-decoration:none;">{{ a.rule.name }}</a>
{% else %}<span title="{{ a.rule.name }}" style="{{ nm_style }}">{{ a.rule.name }}</span>{% endif %}
<span style="color:var(--text-dim);font-size:0.75rem;white-space:nowrap;">{{ a.since|ago }}</span>
</div>
{% endfor %}
{% for e in problems[:8] %}
<div style="display:flex;align-items:center;gap:0.5rem;padding:0.2rem 0;font-size:0.82rem;">
{{ status_badge(e.status) }}
{% set nm_style = "flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;" %}
{# Link to the monitor's own detail view for logged-in users; plain text on the public share view. #}
{% if session.user_id and e.detail_url %}
<a href="{{ e.detail_url }}" title="{{ e.name }}" style="{{ nm_style }}color:inherit;text-decoration:none;">{{ e.name }}</a>
{% else %}
<span title="{{ e.name }}" style="{{ nm_style }}">{{ e.name }}</span>
{% endif %}
<span style="color:var(--text-dim);font-size:0.7rem;">{{ e.kind|monitor_type }}</span>
{% if e.since %}<span style="color:var(--text-dim);font-size:0.75rem;white-space:nowrap;">{% if e.since_at_least %}over {% endif %}{{ e.since|ago }}</span>{% endif %}
</div>
{% endfor %}
{# Metric anomalies going on now (#5740): a warning each. #}
{% for v in anomalies[:4] %}
<div style="display:flex;align-items:center;gap:0.5rem;padding:0.2rem 0;font-size:0.82rem;">
{{ status_badge("anomaly") }}
{% set nm_style = "flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;" %}
{% set nm = v.anomaly.resource_name ~ " · " ~ metric_label(v.anomaly.metric_name) %}
{% if session.user_id %}
<a href="/alerts/anomalies" title="{{ nm }}: {{ metric_value(v.anomaly.metric_name, v.anomaly.peak_level) }}, usual {{ metric_value(v.anomaly.metric_name, v.anomaly.usual_hold) }}" style="{{ nm_style }}color:inherit;text-decoration:none;">{{ nm }}</a>
{% else %}<span title="{{ nm }}" style="{{ nm_style }}">{{ nm }}</span>{% endif %}
<span style="color:var(--text-dim);font-size:0.75rem;white-space:nowrap;">{{ v.anomaly.last_started_at|ago }}</span>
</div>
{% endfor %}
{# What didn't fit, each linking to its own list (#5510). #}
{% set more_alerts, more_monitors = alerts|length - 6, problems|length - 8 %}
{% if more_alerts > 0 or more_monitors > 0 %}
<div style="color:var(--text-dim);font-size:0.75rem;padding-top:0.2rem;">
{%- if more_alerts > 0 %}{% if session.user_id %}<a href="/alerts/">+{{ more_alerts }} more alerts</a>{% else %}+{{ more_alerts }} more alerts{% endif %}{% endif %}
{%- if more_alerts > 0 and more_monitors > 0 %} · {% endif %}
{%- if more_monitors > 0 %}{% if session.user_id %}<a href="/monitors/">+{{ more_monitors }} more monitors</a>{% else %}+{{ more_monitors }} more monitors{% endif %}{% endif -%}
</div>
{% endif %}
</div>
{% endif %}
{% if to_review %}
<div style="font-size:0.78rem;color:var(--text-muted);padding-top:0.3rem;">
{% if session.user_id %}<a href="/alerts/anomalies">{{ to_review }} metric anomal{{ "y" if to_review == 1 else "ies" }} to review</a>
{% else %}{{ to_review }} metric anomal{{ "y" if to_review == 1 else "ies" }} to review{% endif %}
</div>
{% endif %}