Files
compass_teams_nhs/docs/phase-5-dashboard-notes.md
Sam Rolfe 37d82b1a0a Phase 5: per-child dashboard (overdue first), item detail, delete
- internal/dashboard: classify items into overdue / due today / due this week /
  no due date / messages; render the plain-text brief (FR-6, FR-7, FR-9).
- internal/store: ItemsForChild, ItemByID, DeleteItemByID, ScanSummaryForChild
  and the stored-brief CRUD helpers (FR-10).
- internal/web: routes /?child=, /items/{id}, POST /items/{id}/delete,
  POST /summaries/build, POST /summaries/{id}/delete, plus a per-child scan
  banner (FR-12) and a phone-first server-rendered template.
- Calendar events are never "overdue": a past lesson is history, not work.
- cmd/school brief build|list; scripts/screenshot-phase5.mjs for phone evidence.
- Tests: bucket classification, stored-brief round-trip, and HTTP routes
  including the NO SCAN / SCAN FAILED / healthy banner behaviour.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-15 17:50:29 +11:00

115 lines
4.5 KiB
Markdown
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.
# Phase 5 — the per-child dashboard
Status: **done.** Sam can see, per child and on a phone, what is overdue, what is
due today, what is due this week, and the messages. See AUD-109.
## What the dashboard shows
One tab per child (Harry, Finn). The selected tab shows, in order (FR-7):
1. **Overdue** — overdue first, with a red marker.
2. **Due today**.
3. **Due this week** — the next seven days.
4. **No due date** — only when the child has such tasks.
5. **Messages** — the Compass news feed, newest first.
A task is **overdue** when Compass says so (`Status: Overdue` from the JSON
service, `Submitted: Overdue` from the grid), or when it is not submitted and its
due date is before today. **Submitted** work (On Time, Received Late) is not
"due", so it stays out of the lists; the count is shown in the panel footer.
A **calendar event** is never overdue: a past lesson is history, not work to do.
Events appear under due today and due this week.
The page is server-rendered HTML with HTMX. It is phone-first: one column, 44px
touch targets, and the Delete button steps below the title under 520px.
## Routes
| Method | Path | Shows |
|---|---|---|
| `GET` | `/?child=<id>` | the per-child dashboard (first child when `child` is absent) |
| `GET` | `/items/{id}` | item detail: subject, teacher, due date, link, full text (§5 screen 3) |
| `POST` | `/items/{id}/delete` | the Delete action on an item (FR-10) |
| `POST` | `/summaries/build` | build a draft brief for one child (FR-10 summary half) |
| `POST` | `/summaries/{id}/delete` | the Delete action on a stored brief (FR-10) |
| `GET` | `/partials/scan-status` | the scan banner fragment (polled every 60s) |
Every `POST` checks the session CSRF token. An HTMX request gets `204 No
Content`, so the row is removed in place; a plain form post gets `303 See Other`
back to the dashboard, so the page works without JavaScript.
A deleted item returns on the next parse if the school still lists it. A deleted
brief is gone.
## Fail loudly (FR-12)
The banner reads the scan log. It is never an empty day:
- **NO SCAN HAS RUN YET** — amber. A child with no scan shows
"Unknown — the last scan did not complete." instead of "Nothing overdue.".
- **SCAN FAILED** — red, with the recorded error. The child panel repeats the
warning.
- **Last scan completed** — green. Only then may an empty section say
"Nothing overdue.".
The banner is per child (`ScanSummaryForChild`), so a failed scan for Finn is
never hidden by a successful scan for Harry.
## Where the items come from
No new scan code: Phase 3/4 store the items. Phase 5 adds the reads and the
classification (`internal/dashboard`).
- `internal/dashboard/dashboard.go` — classify items into buckets; render the
plain-text brief.
- `internal/store/dashboard.go` — `ItemsForChild`, `ItemByID`, `DeleteItemByID`,
`ScanSummaryForChild`, and the summary create/list/get/delete helpers.
- `internal/web/server.go` — the routes and handlers.
- `internal/web/templates/dashboard.html`, `item.html` — the screens.
## The stored brief (FR-10, delete half)
A brief is a row in `notifications` (AGENTS.md §7). `sent_at` stays NULL for a
draft. Building a brief stores the text; **sending** it is Phase 7.
```bash
docker exec compass-teams-nhs /app/school brief build --child Harry
docker exec compass-teams-nhs /app/school brief list
```
## Verification (2026-10-15, live)
```bash
make test # go test ./...
```
The tests cover the buckets, the brief text, the stored brief round-trip, and the
HTTP routes. `internal/web/server_test.go` logs in and proves:
- the selected child's items render and the other child's do not,
- the overdue item renders before the due-today item,
- the item detail carries subject, teacher and body,
- delete removes the row and a tokenless delete is refused (403),
- the banner shows `NO SCAN HAS RUN YET`, then `SCAN FAILED` with the error, then
`Last scan completed`, and only a healthy scan allows "Nothing overdue.".
Live counts on `.13` (Compass data of 2026-10-15):
| Child | Overdue | Due today | This week | No due date | Messages |
|---|---|---|---|---|---|
| Harry | 6 | 8 | 7 | 18 | 1 |
| Finn | 2 | 0 | 0 | 8 | 1 |
The overdue counts match the home-page reconciliation from Phase 4a (Harry 6,
Finn 2).
Phone-width screenshots (390 × 844) are attached to AUD-109:
`scripts/screenshot-phase5.mjs`.
## Known gaps
- Teams (FR-15) is a separate source; the dashboard shows Compass only.
- Sending a brief is Phase 7. Phase 5 stores and deletes a draft.
- The calendar write (Phase 6) and the Hub API (Phase 8) are not built yet.