admin: read/unread triage for load errors (unread by default, mark read/all)

The load-error log had no way to clear reviewed entries. Add a read_at column to
client_errors and a read/unread model mirroring the feedback inbox:
- GET /api/admin/client-errors?show=unread|read|all (default unread; returns id+read)
- POST /api/admin/client-errors/read-all  (mark all unread read)
- POST /api/admin/client-errors/{id}/read {read: bool}  (per-row toggle)
Headline stat is now "Unread load errors" (admin_stats.client_errors.unread), so the
red badge clears as you triage. Admin UI: Unread/Read/All tabs, a "Mark all read"
button, and a per-row ✓/↩ toggle; reading an entry drops it from the default view.
14-day auto-prune still bounds the table. Tests cover filter, toggle, mark-all,
404, and gating.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-29 10:38:22 -04:00
parent bddb8d22b0
commit d98cec9ded
5 changed files with 115 additions and 11 deletions
+4
View File
@@ -886,6 +886,10 @@ def admin_stats(conn: sqlite3.Connection, days: int = 30) -> dict:
f"SELECT COUNT(*) FROM client_errors WHERE created_at>=date('now',?) AND {_NOT_BOT_SQL}",
(since,),
),
# Drives the headline now: how many still need a look (clears as you mark them read).
"unread": scalar(
f"SELECT COUNT(*) FROM client_errors WHERE read_at IS NULL AND {_NOT_BOT_SQL}",
),
},
}