A change Steward can't see happen (a config edited, a disk swapped) is recorded by whoever made it: what was done, its area, when (in the reader's own zone), the hosts, what it was done to and a detail. Its author or an admin edits or deletes it; recording, editing and deleting are audited and those audit rows stay off the timeline. log_change records one as "<user> via MCP", editable by the token's user. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
56 lines
2.7 KiB
HTML
56 lines
2.7 KiB
HTML
{# timeline/list.html — everything that happened to the infrastructure, newest
|
|
first, one list: alerts, playbook runs, changes to Steward's settings.
|
|
Filters are by host, area (what was affected) and kind (how it came about);
|
|
Settings → Timeline decides each type's area and whether it is shown, and
|
|
"Include hidden" shows the types it hides. #}
|
|
{% extends "base.html" %}
|
|
{% from "_macros.html" import page_header, empty_state %}
|
|
{% block title %}Timeline — Steward{% endblock %}
|
|
{% block content %}
|
|
{% call page_header("Timeline", "What happened, newest first: alerts, playbook runs and changes, on every host.") %}
|
|
{% if session.user_role in ('operator', 'admin') %}<a class="btn btn-sm" href="/timeline/changes/new?{{ ({'host': filters.host, 'back': back} if filters.host else {'back': back})|urlencode }}">Record a change</a>{% endif %}
|
|
{% if session.user_role == 'admin' %}<a class="btn btn-ghost btn-sm" href="/settings/timeline/">Areas and types</a>{% endif %}
|
|
{% endcall %}
|
|
|
|
<form class="filter-bar" method="get" action="/timeline/" role="search">
|
|
<select name="host" aria-label="Host" onchange="this.form.submit()">
|
|
<option value="">All hosts</option>
|
|
{% for h in hosts %}<option value="{{ h }}"{% if filters.host == h %} selected{% endif %}>{{ h }}</option>{% endfor %}
|
|
</select>
|
|
<select name="area" aria-label="Area" onchange="this.form.submit()">
|
|
<option value="">All areas</option>
|
|
{% for key, label in areas %}<option value="{{ key }}"{% if filters.area == key %} selected{% endif %}>{{ label }}</option>{% endfor %}
|
|
</select>
|
|
<select name="kind" aria-label="Kind" onchange="this.form.submit()">
|
|
<option value="">All kinds</option>
|
|
{% for key, label in kinds.items() %}<option value="{{ key }}"{% if filters.kind == key %} selected{% endif %}>{{ label }}</option>{% endfor %}
|
|
</select>
|
|
<label class="field-hint" style="display:flex;align-items:center;gap:0.35rem;">
|
|
<input type="checkbox" name="hidden" value="1"{% if filters.hidden %} checked{% endif %} onchange="this.form.submit()">
|
|
Include hidden types
|
|
</label>
|
|
<noscript><button type="submit" class="btn btn-ghost btn-sm">Filter</button></noscript>
|
|
</form>
|
|
|
|
{% if items %}
|
|
<div class="card-flush">
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>When ({{ tz_label() }})</th>
|
|
<th>What happened</th>
|
|
<th>Where</th>
|
|
<th>Who</th>
|
|
<th>Area</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% include "timeline/_rows.html" %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="card">{{ empty_state("Nothing on the timeline matches these filters." if keep else "Nothing has been recorded on the timeline yet.") }}</div>
|
|
{% endif %}
|
|
{% endblock %}
|