P2R-02 follow-up: clickable rows on Sources/Schedules + cron-preset tooltips
CI / Test (linux/amd64) (pull_request) Successful in 1m57s
CI / Lint (pull_request) Failing after 15s
CI / Build (windows/amd64) (pull_request) Successful in 22s
CI / Build (linux/amd64) (pull_request) Successful in 22s
CI / Build (linux/arm64) (pull_request) Successful in 22s

Aligns Sources and Schedules tab rows with the dashboard's row-click
UX: whole-row click navigates to the row's edit page (mirroring
.host-row.clickable). Drops the redundant Edit buttons; Run-now and
Delete remain in .row-action cells that sit above the row-link
overlay via z-index.

Schedule edit form's cron preset chips now carry human-readable
title= tooltips ("Every day at 03:00", "Every Sunday at 03:00", etc).

tasks.md gets a binding row-design rule covering all current and
future list-row templates, and the P2R-02 entry is split into the
six slices already agreed with the operator (slices 1–3 marked
done, 4 next).
This commit is contained in:
2026-05-03 12:01:55 +01:00
parent 67ca769686
commit 64d2fcf7a3
8 changed files with 74 additions and 20 deletions
File diff suppressed because one or more lines are too long
+22
View File
@@ -193,6 +193,18 @@
column-gap: 18px;
padding: 14px 18px;
}
/* Whole-row click → edit page, mirroring .host-row.clickable on the
dashboard. Action cells sit above via z-index so their buttons
keep working. */
.src-row.clickable { position: relative; }
.src-row.clickable .row-link {
position: absolute; inset: 0; z-index: 0;
text-indent: -9999px; overflow: hidden;
}
.src-row.clickable:hover { background: var(--panel-hi); cursor: pointer; }
.src-row.clickable > * { position: relative; z-index: 1; pointer-events: none; }
.src-row.clickable > .row-link { pointer-events: auto; }
.src-row.clickable > .row-action { pointer-events: auto; }
/* ---------- schedule rows (Schedules tab) ---------- */
.schd-row {
@@ -206,6 +218,16 @@
font-size: 11px; color: var(--ink-fade);
text-transform: uppercase; letter-spacing: 0.08em;
}
/* Whole-row click → edit page (matches .host-row.clickable). */
.schd-row.clickable { position: relative; }
.schd-row.clickable .row-link {
position: absolute; inset: 0; z-index: 0;
text-indent: -9999px; overflow: hidden;
}
.schd-row.clickable:hover { background: var(--panel-hi); cursor: pointer; }
.schd-row.clickable > * { position: relative; z-index: 1; pointer-events: none; }
.schd-row.clickable > .row-link { pointer-events: auto; }
.schd-row.clickable > .row-action { pointer-events: auto; }
/* ---------- cron preset chips ---------- */
.preset-chip {
+3 -3
View File
@@ -36,7 +36,8 @@
<div></div>
</div>
{{range $i, $sc := $page.Schedules}}
<div class="schd-row {{if not (eq $i 0)}}hairline{{end}}">
<div class="schd-row clickable {{if not (eq $i 0)}}hairline{{end}}">
<a href="/hosts/{{$host.ID}}/schedules/{{$sc.ID}}/edit" class="row-link" aria-label="Edit schedule">edit</a>
<div>
{{if $sc.Enabled}}
<span class="mono text-[11px] text-ok">enabled</span>
@@ -51,14 +52,13 @@
<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">
<div class="flex gap-1.5 justify-end row-action">
{{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>
+3 -3
View File
@@ -29,7 +29,8 @@
<div class="panel rounded-[7px] overflow-hidden">
{{range $i, $row := $page.Groups}}
{{$g := $row.Group}}
<div class="src-row {{if not (eq $i 0)}}hairline{{end}}">
<div class="src-row clickable {{if not (eq $i 0)}}hairline{{end}}">
<a href="/hosts/{{$host.ID}}/sources/{{$g.ID}}/edit" class="row-link" aria-label="Edit {{$g.Name}}">{{$g.Name}}</a>
<div>
<div class="flex items-center" style="gap: 10px;">
<span class="tag mono" style="border-color: color-mix(in oklch, var(--accent), transparent 60%); color: var(--accent);">{{$g.Name}}</span>
@@ -52,7 +53,7 @@
{{if gt $row.SnapshotCount 0}} · <span class="mono">{{$row.SnapshotCount}}</span> snapshot{{if ne $row.SnapshotCount 1}}s{{end}}{{end}}
</div>
</div>
<div class="flex justify-end" style="gap: 6px;">
<div class="flex justify-end row-action" style="gap: 6px;">
{{if and (gt (len $g.Includes) 0) (eq $host.Status "online")}}
<button class="btn btn-primary"
hx-post="/hosts/{{$host.ID}}/source-groups/{{$g.ID}}/run"
@@ -62,7 +63,6 @@
<button class="btn" disabled
title="{{if eq (len $g.Includes) 0}}add at least one include path before running{{else}}host is offline{{end}}">Run now</button>
{{end}}
<a href="/hosts/{{$host.ID}}/sources/{{$g.ID}}/edit" class="btn">Edit</a>
{{if gt $row.UsedBy 0}}
<button class="btn btn-danger" disabled
title="remove this group from {{$row.UsedBy}} schedule{{if ne $row.UsedBy 1}}s{{end}} first">Delete</button>
+10 -3
View File
@@ -26,9 +26,16 @@
<label class="field-label" for="cron">Cron expression</label>
<input type="text" id="cron" name="cron" class="field mono" value="{{$f.CronExpr}}" required autofocus />
<div class="flex flex-wrap gap-1.5 mt-2.5" id="cron-presets">
{{range list "0 3 * * *" "@hourly" "0 */6 * * *" "0 3 * * 0" "0 3 1 * *"}}
<span class="preset-chip" data-cron="{{.}}">{{.}}</span>
{{end}}
<span class="preset-chip" data-cron="0 3 * * *"
title="Every day at 03:00">0 3 * * *</span>
<span class="preset-chip" data-cron="@hourly"
title="Every hour, on the hour (00 minutes)">@hourly</span>
<span class="preset-chip" data-cron="0 */6 * * *"
title="Every 6 hours, on the hour (00:00, 06:00, 12:00, 18:00)">0 */6 * * *</span>
<span class="preset-chip" data-cron="0 3 * * 0"
title="Every Sunday at 03:00">0 3 * * 0</span>
<span class="preset-chip" data-cron="0 3 1 * *"
title="The 1st of every month at 03:00">0 3 1 * *</span>
</div>
<div class="field-help mt-2.5">
Standard 5-field cron with descriptors. Server validates with the same parser the agent uses to fire — what saves here is what runs.