Files
internet_use_control/internal/web/templates/partials/device-cards.html
Paperclip 9af543c394 Phase 6: schedules, blocklists, allowlists (editors + effective state)
- Weekly schedule editor per device: a Monday-Sunday grid of blocking
  windows, including windows that cross midnight.
- Blocklist/allowlist editor per device: app (Android package / Windows
  process) and site (hostname) entries, tagged by the device platform.
- Effective desired state: a manual lock, an active schedule window, or
  neither. While blocked the allowlist is honoured (Spotify stays while
  other apps are blocked). Published retained with reason schedule/manual.
- Boundary-aware republish loop so a window turns on and off on time,
  and manual lock expiry is cleared promptly.
- CONTROL_TIMEZONE (default Australia/Sydney) with embedded tzdata, so
  wall-clock windows are evaluated in the family zone, not the container.
- Every change is written to the append-only audit log (FR-15).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 16:12:02 +11:00

52 lines
2.3 KiB
HTML

{{define "device-cards.html"}}<div class="device-grid">
{{range .Devices}}
<article class="device-card">
<header class="device-card__head">
<div class="device-card__id">
<h3 class="device-card__name">{{.Name}}</h3>
<p class="device-card__meta">{{.Child}} · {{.PlatformLabel}}</p>
</div>
<span class="status-pill {{if .Locked}}status-pill--off{{else}}status-pill--on{{end}}">
<span class="dot {{if .Locked}}dot--off{{else}}dot--on{{end}}"></span>{{.StateLabel}}
</span>
</header>
<p class="device-card__state">
{{if .Locked}}Blocked{{if .ReasonLabel}} {{.ReasonLabel}}{{end}}{{if .TimeLeft}} · {{.TimeLeft}}{{end}}{{else}}No block active{{end}}
</p>
{{if .Until}}<p class="device-card__until">Lifts {{.UntilLocal}}</p>{{end}}
<div class="device-card__actions">
<form method="post" action="/devices/{{.ID}}/lock" class="inline-form">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<select class="select-input" name="minutes" aria-label="Lock duration">
<option value="0">until I unlock</option>
<option value="15">15 min</option>
<option value="30" selected>30 min</option>
<option value="60">1 hour</option>
</select>
<button class="button-primary" type="submit">{{if .Locked}}Re-lock{{else}}Lock{{end}}</button>
</form>
{{if .Locked}}
<form method="post" action="/devices/{{.ID}}/unlock" class="inline-form">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<button class="button-utility" type="submit">Unlock</button>
</form>
{{end}}
<a class="button-utility" href="/devices/{{.ID}}/edit">Edit</a>
<a class="button-utility" href="/devices/{{.ID}}/schedule">Schedule</a>
<a class="button-utility" href="/devices/{{.ID}}/lists">Lists</a>
</div>
<p class="device-card__foot">
<span class="device-card__presence">
<span class="dot {{if .Online}}dot--on{{else}}dot--faint{{end}}"></span>
{{if .Online}}online{{else}}offline{{end}}
</span>
{{if .HasLastSeen}}<span>· last seen {{.LastSeen}}</span>{{else}}<span>· never connected</span>{{end}}
</p>
<p class="device-card__topic"><code>{{.Topic}}</code></p>
</article>
{{end}}
</div>{{end}}