Files
restic-manager/web/templates/pages/host_schedules.html
T
steve 8d993ac77c P2R-02 slice 3: Schedules tab — slim list, new/edit form, delete, Run-now
Schedules list: status (enabled/paused) + cron + source-group tags +
actions (Run-now when enabled+online, Edit, Delete). Run-now reuses
dispatchScheduledJob — same path real cron fires take, so each
referenced source group runs as its own backup with its own tag.
Falls back to a 409 if the agent is offline.

Schedule new/edit form: cron input with five preset chips
(quick-pick @hourly / nightly / 6h / weekly / monthly), source-group
multi-pick rendered as styled checkbox cards (visual state tracks
the underlying box via a tiny inline script), enabled toggle. No
paths/excludes/retention/kind on the schedule itself — those live on
source groups now.

Server-side validation re-renders with the operator's input + ticked
groups intact. Every successful mutation calls pushScheduleSetAsync.

Adds .schd-row, .preset-chip, .picker styles.
2026-05-03 11:55:16 +01:00

74 lines
3.3 KiB
HTML

{{define "title"}}{{.Title}}{{end}}
{{define "content"}}
{{template "host_chrome" .}}
{{$page := .Page}}
{{$host := $page.Host}}
{{$groupNames := $page.GroupNames}}
<div class="max-w-[1280px] mx-auto px-8 pb-14 pt-6">
<div class="flex items-center justify-between mb-4">
<p class="text-pretty text-[12.5px] text-ink-mute leading-[1.6] max-w-[760px]">
A schedule is a cron expression pointing at one or more source groups. When it fires, the agent runs a separate
<span class="mono text-ink-mid">restic backup</span> per chosen group — independent jobs, independent snapshots,
independent retention. Failure of one group doesn't fail the others.
</p>
<a href="/hosts/{{$host.ID}}/schedules/new" class="btn btn-primary whitespace-nowrap">+ New schedule</a>
</div>
{{if eq (len $page.Schedules) 0}}
<div class="panel rounded-[7px] empty-state" style="border-radius: 7px;">
<h3 class="text-base font-medium tracking-[-0.005em]">No schedules yet.</h3>
<p class="text-pretty text-ink-mute text-[13px] mt-2 mx-auto max-w-[480px] leading-[1.65]">
Add one and the agent will start running backups on whatever cron expression you give it.
Until then, Run-now from the Sources tab is the only way to trigger a backup.
</p>
<div class="mt-5">
<a href="/hosts/{{$host.ID}}/schedules/new" class="btn btn-primary">+ New schedule</a>
</div>
</div>
{{else}}
<div class="panel rounded-[7px] overflow-hidden">
<div class="schd-row head hairline">
<div>Status</div>
<div>Cron</div>
<div>Sources</div>
<div></div>
</div>
{{range $i, $sc := $page.Schedules}}
<div class="schd-row {{if not (eq $i 0)}}hairline{{end}}">
<div>
{{if $sc.Enabled}}
<span class="mono text-[11px] text-ok">enabled</span>
{{else}}
<span class="mono text-[11px] text-ink-fade">paused</span>
{{end}}
</div>
<div class="mono {{if $sc.Enabled}}text-ink{{else}}text-ink-mute{{end}}">{{$sc.CronExpr}}</div>
<div class="flex gap-1.5 flex-wrap">
{{range $sc.SourceGroupIDs}}
{{$name := index $groupNames .}}
<span class="tag" style="border-color: color-mix(in oklch, var(--accent), transparent 60%); color: var(--accent); {{if not $sc.Enabled}}opacity: 0.6;{{end}}">{{if $name}}{{$name}}{{else}}<span class="text-ink-fade">unknown</span>{{end}}</span>
{{end}}
</div>
<div class="flex gap-1.5 justify-end">
{{if and $sc.Enabled (eq $host.Status "online")}}
<button class="btn btn-primary"
hx-post="/hosts/{{$host.ID}}/schedules/{{$sc.ID}}/run"
hx-swap="none"
hx-disabled-elt="this">Run now</button>
{{end}}
<a href="/hosts/{{$host.ID}}/schedules/{{$sc.ID}}/edit" class="btn">Edit</a>
<form method="post" action="/hosts/{{$host.ID}}/schedules/{{$sc.ID}}/delete" style="display: inline;"
onsubmit="return confirm('Delete this schedule? Existing snapshots are not affected.');">
<button type="submit" class="btn btn-danger">Delete</button>
</form>
</div>
</div>
{{end}}
</div>
{{end}}
</div>
{{end}}