Files
internet_use_control/internal/web/templates/schedule-editor.html
Sam Rolfe b58993d994 Phase 7: telemetry and usage (daily rollup + week dashboard)
Build on Phases 1–6. Telemetry already arrived on
restrict/<device>/telemetry; this phase stores it in a daily rollup and
shows it.

- store: migration 003 adds telemetry_daily (device, day, app seconds,
  site seconds). InsertTelemetry maintains the affected days in the same
  transaction; RebuildRollups backfills and repairs it. UsageRange and
  TopUsage serve the week grid and top lists. CapAudit enforces the
  size-capped append-only audit log from AGENTS.md §7.
- web: GET /devices/{id}/usage — Monday-to-Sunday totals plus the top 10
  apps and top 10 sites, using CSP-safe native <progress> bars.
- theme.js: light/dark from the OS preference, with a nav toggle, loaded
  as an external script so the strict CSP still holds.
- cmd/control: `rollup` subcommand; rollup rebuild and audit cap at
  start-up and every 15 minutes.
- Version 0.5.0-phase7 (compose, Makefile, docs).

Tests: go test ./... and go vet ./... pass in golang:1.25-alpine.

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

94 lines
3.9 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "schedule-editor.html"}}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>{{.Title}} — Internet Use Control</title>
<link rel="stylesheet" href="/static/tokens.css">
<link rel="stylesheet" href="/static/app.css">
<script src="/static/theme.js" defer></script>
</head>
<body class="app">
<nav class="nav-bar">
<a class="nav-bar__brand" href="/">
<span class="nav-bar__mark" aria-hidden="true"></span>
Internet Use Control
</a>
<div class="nav-bar__right">
<button class="button-utility" type="button" data-theme-toggle>Dark</button>
<span class="nav-bar__user">{{.Username}}</span>
</div>
</nav>
<main class="container">
<header class="page-head">
<p class="page-head__eyebrow"><a href="/">← Dashboard</a></p>
<h1 class="heading-1">{{.Heading}}</h1>
<p class="page-head__sub">{{.Sub}}</p>
</header>
{{if .Flash}}<p class="flash flash--ok" role="status">{{.Flash}}</p>{{end}}
{{if .Error}}<p class="flash flash--error" role="alert">{{.Error}}</p>{{end}}
<section class="schedule-note">
{{if .ActiveNow}}
<p class="schedule-note__now"><span class="dot dot--off"></span> A window is active now{{if .ActiveUntil}} until {{.ActiveUntil}}{{end}}.</p>
{{else}}
<p class="schedule-note__now"><span class="dot dot--faint"></span> No window is active now.</p>
{{end}}
{{if .NextBoundary}}<p class="schedule-note__next">Next change: {{.NextBoundary}}.</p>{{end}}
<p class="schedule-note__hint">Each row is a day. A window means restrictions are ON during that time.
Leave both boxes empty to remove a window. An end time earlier than the start crosses midnight.</p>
</section>
<section class="card-panel">
<form method="post" action="/devices/{{.DeviceID}}/schedule" class="stack-form">
<input type="hidden" name="csrf" value="{{.CSRF}}">
<div class="schedule-scroll">
<table class="schedule-grid">
<caption class="visually-hidden">Weekly blocking windows for {{.DeviceName}}</caption>
<thead>
<tr>
<th scope="col">Day</th>
{{range index .Rows 0}}{{range .Slots}}<th scope="col">Window</th>{{end}}{{end}}
</tr>
</thead>
<tbody>
{{range $row := .Rows}}
<tr>
<th scope="row">{{$row.Name}}</th>
{{range $i, $slot := $row.Slots}}
<td class="schedule-grid__slot">
<input class="text-input time-input" type="time" name="start_{{$row.Weekday}}_{{$i}}"
value="{{$slot.Start}}" aria-label="{{$row.Name}} window {{$i}} start">
<span class="schedule-grid__dash" aria-hidden="true">–</span>
<input class="text-input time-input" type="time" name="end_{{$row.Weekday}}_{{$i}}"
value="{{$slot.End}}" aria-label="{{$row.Name}} window {{$i}} end">
</td>
{{end}}
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="form-actions">
<button class="button-primary" type="submit">Save schedule</button>
<button class="button-utility" type="submit" name="clear" value="1" formnovalidate>Clear all</button>
<a class="button-utility" href="/devices/{{.DeviceID}}/lists">Blocklist / allowlist</a>
<a class="button-utility" href="/">Done</a>
</div>
</form>
</section>
</main>
<footer class="app-foot">
<span>Control Service · {{.Version}} · {{.AppEnv}}</span>
<span class="app-foot__sep">·</span>
<span>{{year}} Family Home Lab</span>
</footer>
</body>
</html>{{end}}