(ui) Group host detail actions by context

Move host-level actions out of the page header and into the panels where
operators expect them: configuration, connection preflight, and snapshot
storage. This keeps the host control page calmer while preserving the same
actions.

Refs #26
This commit is contained in:
2026-05-21 13:53:10 +02:00
parent 9e75273fc5
commit 1929196287
2 changed files with 58 additions and 56 deletions

View File

@@ -241,6 +241,13 @@
.stack { display: grid; gap: 5px; } .stack { display: grid; gap: 5px; }
.stack.spaced { margin-bottom: 14px; } .stack.spaced { margin-bottom: 14px; }
.two-col { display: grid; gap: 18px; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } .two-col { display: grid; gap: 18px; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.panel-grid {
display: grid;
gap: 18px;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
margin-bottom: 18px;
}
.panel-grid .panel { margin-bottom: 0; }
.actions { .actions {
align-items: center; align-items: center;
display: flex; display: flex;
@@ -676,7 +683,8 @@
display: grid; display: grid;
} }
.page-header .actions { justify-content: flex-start; } .page-header .actions { justify-content: flex-start; }
.two-col { grid-template-columns: 1fr; } .two-col,
.panel-grid { grid-template-columns: 1fr; }
.dashboard-priority-grid { grid-template-columns: 1fr; } .dashboard-priority-grid { grid-template-columns: 1fr; }
.host-control-grid { grid-template-columns: 1fr; } .host-control-grid { grid-template-columns: 1fr; }
.schedule-row { grid-template-columns: 1fr; } .schedule-row { grid-template-columns: 1fr; }

View File

@@ -9,27 +9,6 @@
<h1>{{ host.host }}</h1> <h1>{{ host.host }}</h1>
<div class="page-subtitle">{{ host.address }} · {{ host.enabled|yesno:"enabled,disabled" }}</div> <div class="page-subtitle">{{ host.address }} · {{ host.enabled|yesno:"enabled,disabled" }}</div>
</div> </div>
<section class="actions" aria-label="Host actions">
<a class="button-link" href="{% url 'edit_host_config' host.host %}">Edit config</a>
<form method="post" action="{% url 'discover_host_snapshots' host.host %}">
{% csrf_token %}
<button type="submit">Discover snapshots</button>
</form>
<a class="button-link" href="{% url 'host_retention_plan' host.host %}">Plan retention</a>
<a class="button-link" href="{% url 'edit_host_schedule' host.host %}">Edit schedule</a>
<form method="post" action="{% url 'prepare_host_directories' host.host %}">
{% csrf_token %}
<button type="submit" class="secondary">Prepare directories</button>
</form>
<form method="post" action="{% url 'scan_host_known_key' host.host %}">
{% csrf_token %}
<button type="submit" class="secondary">Scan SSH host key</button>
</form>
<form method="post" action="{% url 'run_host_preflight' host.host %}">
{% csrf_token %}
<button type="submit" class="secondary">Run connection preflight</button>
</form>
</section>
</header> </header>
{% if retention_warning.has_warning %} {% if retention_warning.has_warning %}
@@ -144,7 +123,7 @@
</article> </article>
<article class="panel host-control-panel"> <article class="panel host-control-panel">
<h2>Schedule <a class="button-link secondary compact" href="{% url 'edit_host_schedule' host.host %}">Edit</a></h2> <h2>Schedule <a class="button-link secondary compact" href="{% url 'edit_host_schedule' host.host %}">Edit schedule</a></h2>
{% if schedule %} {% if schedule %}
<div class="host-control-meta"> <div class="host-control-meta">
<div><span class="label">Schedule expression</span><strong>{{ schedule.cron_expr }}</strong></div> <div><span class="label">Schedule expression</span><strong>{{ schedule.cron_expr }}</strong></div>
@@ -272,39 +251,7 @@
</table> </table>
</section> </section>
{% if last_preflight %} <div class="panel-grid">
<section class="panel">
<h2>Connection Preflight</h2>
<div class="stack spaced">
<div><strong>Status:</strong> <span class="status {% if last_preflight.ok %}ok{% else %}failed{% endif %}">{% if last_preflight.ok %}ok{% else %}failed{% endif %}</span></div>
<div><strong>Target:</strong> {{ last_preflight.target }}</div>
<div><strong>Backup source:</strong> {{ last_preflight.source_root }}</div>
<div><strong>Remote rsync:</strong> {{ last_preflight.rsync_binary }}</div>
</div>
<table>
<thead>
<tr>
<th>Status</th>
<th>Check</th>
<th>Message</th>
<th>Detail</th>
</tr>
</thead>
<tbody>
{% for check in last_preflight.checks %}
<tr>
<td><span class="status {% if check.ok %}ok{% else %}failed{% endif %}">{% if check.ok %}ok{% else %}failed{% endif %}</span></td>
<td>{{ check.name }}</td>
<td>{{ check.message }}</td>
<td class="muted">{{ check.detail }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
{% endif %}
<div class="two-col">
<section class="panel"> <section class="panel">
<h2>Configuration</h2> <h2>Configuration</h2>
<div class="host-control-meta"> <div class="host-control-meta">
@@ -316,9 +263,56 @@
</div> </div>
<div class="actions inline"> <div class="actions inline">
<a class="button-link secondary compact" href="{% url 'edit_host_config' host.host %}">Edit config</a> <a class="button-link secondary compact" href="{% url 'edit_host_config' host.host %}">Edit config</a>
<a class="button-link secondary compact" href="{% url 'host_retention_plan' host.host %}">Plan retention</a>
</div> </div>
</section> </section>
<section class="panel">
<h2>Connection Preflight &amp; SSH</h2>
{% if last_preflight %}
<div class="host-control-meta">
<div>
<span class="label">Preflight</span>
<strong>
<span class="status {% if last_preflight.ok %}ok{% else %}failed{% endif %}">
{% if last_preflight.ok %}ok{% else %}failed{% endif %}
</span>
</strong>
</div>
<div><span class="label">Target</span><strong>{{ last_preflight.target }}</strong></div>
<div><span class="label">Backup source</span><strong>{{ last_preflight.source_root }}</strong></div>
<div><span class="label">Remote rsync</span><strong>{{ last_preflight.rsync_binary }}</strong></div>
</div>
{% else %}
<p class="muted">No connection preflight recorded yet.</p>
{% endif %}
<div class="actions inline">
<form method="post" action="{% url 'run_host_preflight' host.host %}">
{% csrf_token %}
<button type="submit" class="secondary compact">Run connection preflight</button>
</form>
<form method="post" action="{% url 'scan_host_known_key' host.host %}">
{% csrf_token %}
<button type="submit" class="secondary compact">Scan SSH host key</button>
</form>
</div>
{% if last_preflight.checks %}
<div class="activity-list">
{% for check in last_preflight.checks %}
<div class="activity-row">
<span class="status {% if check.ok %}ok{% else %}failed{% endif %}">
{% if check.ok %}ok{% else %}failed{% endif %}
</span>
<span>
<strong>{{ check.name }}</strong>
<span class="muted">{{ check.message }}{% if check.detail %} · {{ check.detail }}{% endif %}</span>
</span>
</div>
{% endfor %}
</div>
{% endif %}
</section>
<section class="panel"> <section class="panel">
<h2>Snapshot Storage</h2> <h2>Snapshot Storage</h2>
<div class="host-control-meta"> <div class="host-control-meta">