Files
FabledSteward/steward/templates/timeline/change_form.html
T
bvandeusenandClaude Opus 5.5 329aa15e3d
CI / lint (push) Successful in 2s
CI / integration (push) Failing after 1m20s
CI / unit (push) Failing after 1m33s
CI / publish (push) Skipped
feat(timeline): record a change by hand from the Timeline, a host's page or MCP log_change (#6350)
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>
2026-10-24 21:26:02 -04:00

84 lines
4.1 KiB
HTML

{# timeline/change_form.html — record a change by hand, or edit one (timeline.changes):
what was done, the area it is about, the hosts, when, and any detail. Saving
returns to `back`, the page the form was opened from. #}
{% extends "base.html" %}
{% from "_macros.html" import crumbs, page_header %}
{% set title = "Edit a recorded change" if entry else "Record a change" %}
{% block title %}{{ title }} — Timeline — Steward{% endblock %}
{% block breadcrumb %}{{ crumbs([("Timeline", "/timeline/"), (title, "")]) }}{% endblock %}
{% block content %}
{{ page_header(title, "Something Steward can't see happen: a config edited, a disk swapped, a service moved.") }}
{% if error %}<div class="alert alert-error" role="alert">{{ error }}</div>{% endif %}
<form method="post" action="{{ '/timeline/changes/' ~ entry.id if entry else '/timeline/changes' }}" class="form-page">{{ csrf_field() }}
<input type="hidden" name="back" value="{{ back }}">
<section class="card form-section" aria-labelledby="chg-what">
<h2 id="chg-what" class="section-title">The change</h2>
<div class="field">
<label class="field-label" for="chg-summary">What was done</label>
<input class="input" id="chg-summary" type="text" name="summary" value="{{ values.summary }}"
maxlength="300" required autofocus placeholder="e.g. Moved Postgres data to the new SSD">
</div>
<div class="field">
<label class="field-label" for="chg-area">Area</label>
<select class="input" id="chg-area" name="area" required>
<option value="">— pick one —</option>
{% for key, label in areas %}<option value="{{ key }}"{% if values.area == key %} selected{% endif %}>{{ label }}</option>{% endfor %}
</select>
<span class="field-hint">What part of the infrastructure it is about.</span>
</div>
<div class="field">
<label class="field-label" for="chg-at">When ({{ tz_label() }})</label>
<input class="input" id="chg-at" type="datetime-local" name="at" value="{{ values.at }}" required>
</div>
<div class="field">
<label class="field-label" for="chg-subject">What it was done to <span class="tone-dim">(optional)</span></label>
<input class="input" id="chg-subject" type="text" name="subject" value="{{ values.subject }}"
maxlength="255" placeholder="e.g. postgres, the core switch, /etc/fstab">
</div>
<div class="field">
<label class="field-label" for="chg-note">Detail <span class="tone-dim">(optional)</span></label>
<textarea class="input" id="chg-note" name="note" rows="4" maxlength="4000"
placeholder="Why, how, and anything to know if it goes wrong.">{{ values.note }}</textarea>
</div>
</section>
<section class="card form-section" aria-labelledby="chg-hosts">
<h2 id="chg-hosts" class="section-title">Hosts</h2>
<p class="field-hint">The change shows in each ticked host's history. Tick none for a change not on any one host.</p>
{% if hosts %}
<div class="check-list">
{% for h in hosts %}
<label class="field-check">
<input type="checkbox" name="hosts" value="{{ h }}"{% if h in values.hosts %} checked{% endif %}>
<span>{{ h }}</span>
</label>
{% endfor %}
</div>
{% else %}
<p class="tone-muted">No hosts yet.</p>
{% endif %}
</section>
<div class="form-actions">
<button type="submit" class="btn">{{ "Save" if entry else "Record the change" }}</button>
<a href="{{ back }}" class="btn btn-ghost">Cancel</a>
</div>
</form>
{% if entry %}
<section class="card danger-zone" aria-labelledby="chg-delete">
<div>
<h2 id="chg-delete" class="section-title">Delete this change</h2>
<p class="field-hint">Recorded by {{ entry.actor }}. Deleting takes it off the timeline; the audit log keeps that it was.</p>
</div>
<form method="post" action="/timeline/changes/{{ entry.id }}/delete"
data-confirm="Delete “{{ entry.summary }}” from the timeline?">{{ csrf_field() }}
<input type="hidden" name="back" value="{{ back }}">
<button class="btn btn-danger" type="submit">Delete</button>
</form>
</section>
{% endif %}
{% endblock %}