Files
restic-manager/web/templates/pages/host_detail.html
T
steve 94441a5371 ui: update chip + per-host button
- Surface UpdateAvailable + TargetVersion on the dashboard host row,
  the host_chrome header, and the JSON Host shape.
- New host_update_chip partial renders an amber out-of-date pill
  next to the agent-version display when the host's agent trails
  the server.
- Host detail right-rail gains an admin-only Update agent button
  (disabled when host is offline or already updating).
- New .update-chip and .btn-amber CSS tokens; tailwind output
  refreshed.
2026-05-06 22:20:40 +01:00

156 lines
7.3 KiB
HTML

{{define "title"}}{{.Title}}{{end}}
{{define "content"}}
{{template "host_chrome" .}}
{{$page := .Page}}
{{$host := $page.Host}}
<div class="max-w-[1280px] mx-auto px-8 pb-14">
{{/* ---------- snapshots tab ---------- */}}
<div class="grid grid-cols-12 gap-6 pt-6 items-start">
<div class="col-span-9">
<div class="flex items-center justify-between mb-3">
<div class="flex items-center gap-3">
<h2 class="text-[13px] font-semibold tracking-[0.01em]">Snapshots</h2>
<div class="text-xs text-ink-fade">showing {{$page.SnapshotsShown}} of {{comma $host.SnapshotCount}}</div>
</div>
</div>
<div class="panel rounded-[7px] overflow-hidden">
{{if eq (len $page.Snapshots) 0}}
<div class="empty-state" style="border: none; background: var(--panel);">
<h3 class="text-base font-medium tracking-[-0.005em]">No snapshots yet.</h3>
<p class="text-pretty text-ink-mute text-[13px] mt-2 mx-auto max-w-[440px] leading-[1.65]">
Once a backup completes, the agent will refresh this list automatically.
</p>
<div class="mt-5">
<a href="/hosts/{{$host.ID}}/sources" class="btn">Open Sources →</a>
</div>
</div>
{{else}}
<div class="hairline grid items-baseline px-4 py-2.5 text-[11px] text-ink-fade uppercase tracking-[0.08em]"
style="grid-template-columns: 0.8fr 1fr 2fr 0.7fr 0.7fr 0.7fr; column-gap: 18px;">
<div>Snapshot id</div>
<div>Time</div>
<div>Paths</div>
<div class="text-right{{if $page.LegacyRestic}} cursor-help{{end}}"
{{if $page.LegacyRestic}}title="Needs restic 0.17+ on the agent host. This host runs {{$host.ResticVersion}}."{{end}}>Size</div>
<div class="text-right{{if $page.LegacyRestic}} cursor-help{{end}}"
{{if $page.LegacyRestic}}title="Needs restic 0.17+ on the agent host. This host runs {{$host.ResticVersion}}."{{end}}>Files</div>
<div></div>
</div>
{{range $i, $s := $page.Snapshots}}
<div class="grid items-center px-4 py-2.5 text-[13px] hairline"
style="grid-template-columns: 0.8fr 1fr 2fr 0.7fr 0.7fr 0.7fr; column-gap: 18px;">
<div class="mono text-ink font-medium">{{$s.ShortID}}</div>
<div class="mono text-ink-mid text-[12px]">{{absTime $s.Time}}</div>
<div class="mono text-ink-mid text-[12px] truncate" title="{{joinDot $s.Paths}}">{{joinDot $s.Paths}}</div>
<div class="text-right mono text-ink">{{bytes $s.SizeBytes}}</div>
<div class="text-right mono text-ink-mid">
{{if eq $s.FileCount 0}}<span class="text-ink-fade"></span>{{else}}{{comma $s.FileCount}}{{end}}
</div>
<div class="text-right">
<a href="/hosts/{{$host.ID}}/snapshots/{{$s.ID}}/restore" class="btn">Restore →</a>
</div>
</div>
{{end}}
{{end}}
</div>
{{if eq $host.SnapshotCount 0}}
{{else if gt $host.SnapshotCount $page.SnapshotsShown}}
<div class="text-xs text-ink-mute mt-3 px-1">showing the {{$page.SnapshotsShown}} most-recent snapshots — full pagination lands later</div>
{{end}}
</div>
{{/* ---------- right rail ---------- */}}
<aside class="col-span-3 flex flex-col gap-4">
<div class="panel rounded-[7px] px-4 py-3.5">
<div class="text-[11px] text-ink-fade uppercase tracking-[0.1em] mb-2.5">Run-now</div>
<p class="text-[12px] text-ink-mute leading-[1.55] mb-2">
Run-now lives on individual source groups now —
<a href="/hosts/{{$host.ID}}/sources" class="underline">open Sources →</a>
</p>
</div>
{{if and $page.CanAdmin $page.UpdateAvailable}}
<div class="panel rounded-[7px] px-4 py-3.5">
<div class="text-[11px] text-ink-fade uppercase tracking-[0.1em] mb-2.5">Agent update</div>
<p class="text-[12px] text-ink-mute leading-[1.55] mb-3">
Agent at <span class="mono text-ink-mid">{{$host.AgentVersion}}</span> ·
server at <span class="mono text-ink-mid">{{$page.TargetVersion}}</span>.
Pushes a self-update command; the agent re-launches into the new binary
and reconnects.
</p>
<form hx-post="/hosts/{{$host.ID}}/update" hx-swap="none">
<button class="btn btn-amber btn-block"
{{if not $page.Online}}disabled title="Agent must be online"
{{else if $page.UpdateInProgress}}disabled title="Update already in progress"
{{end}}>
Update agent
</button>
</form>
</div>
{{end}}
<div class="panel rounded-[7px] px-4 py-3.5">
<div class="text-[11px] text-ink-fade uppercase tracking-[0.1em] mb-2.5">Restore</div>
<p class="text-[12px] text-ink-mute leading-[1.55] mb-3">
Pick a snapshot, choose paths, dispatch. Live progress streams once the
agent starts.
</p>
<a href="/hosts/{{$host.ID}}/restore"
class="btn btn-block">Restore from snapshot…</a>
</div>
{{if gt $host.SnapshotCount 1}}
<div class="panel rounded-[7px] px-4 py-3.5">
<div class="text-[11px] text-ink-fade uppercase tracking-[0.1em] mb-2.5">Compare snapshots</div>
<p class="text-[12px] text-ink-mute leading-[1.55] mb-3">
Diff two snapshots to see what changed. Output streams to a live
job page like a regular run.
</p>
<form method="post" action="/hosts/{{$host.ID}}/snapshots/diff"
hx-post="/hosts/{{$host.ID}}/snapshots/diff" hx-swap="none"
class="space-y-2">
<input type="text" name="snapshot_a" placeholder="snapshot A id"
class="field mono text-[11.5px]" />
<input type="text" name="snapshot_b" placeholder="snapshot B id"
class="field mono text-[11.5px]" />
<button type="submit" class="btn btn-block">Diff →</button>
</form>
</div>
{{end}}
<div class="panel rounded-[7px] px-4 py-3.5">
<div class="text-[11px] text-bad uppercase tracking-[0.1em] font-semibold mb-2.5">Danger zone</div>
<p class="text-pretty text-[12px] text-ink-mute leading-[1.55] mb-3">
Removes the host record and everything attached to it
(schedules, source groups, jobs, snapshots metadata, alerts).
The agent's bearer is revoked, so a re-installed instance
comes back through the normal pending-host accept flow.
The repo data on the rest-server is left intact — you delete
that yourself.
</p>
<form method="post" action="/hosts/{{$host.ID}}/delete"
class="space-y-2"
onsubmit="return confirm('Remove host &quot;{{$host.Name}}&quot;? This cascades to every dependent row and cannot be undone.');">
<input type="text" name="confirm_hostname" required autocomplete="off"
placeholder="type hostname to confirm"
class="field mono text-[12px]" />
<button type="submit" class="btn btn-danger w-full justify-center">Remove host…</button>
</form>
</div>
</aside>
</div>
</div>
{{end}}