Web dashboard step 7: docs, users page, polish

Users page, README and config.example updates, implementation notes, the
rollout runbook, site-layout 404/500 pages, human-readable download sizes,
dark-mode and narrow-screen polish, and a smoke test over every dashboard
route; also removes zdiff3 ancestor markers left by earlier merges.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NHyYupFdBiR4VfoUM7NjSM
This commit is contained in:
2026-09-03 18:19:08 +00:00
co-authored by Claude Fable 5.1
parent 10cbe9ed79
commit eb960f1b57
16 changed files with 599 additions and 50 deletions
+41
View File
@@ -1185,4 +1185,45 @@ pub(crate) mod tests {
assert!(body.contains("2 runs · max $0.11"), "{body}");
assert!(!body.contains("style=\""), "no inline styles under the CSP");
}
#[tokio::test]
async fn every_dashboard_route_template_renders_with_fixture_data() {
let seed = seed().await;
sqlx::query(
"INSERT INTO jobs
(id, name, unit, requested_at, started_at, finished_at, status, message, run_id)
VALUES (99, 'features-prune', 'daily-epub-job@features-prune.service',
'2026-09-02T06:00:00Z', '2026-09-02T06:00:01Z',
'2026-09-02T06:00:02Z', 'ok', 'pruned fixture rows', ?)",
)
.bind(seed.run_id)
.execute(seed.db.pool())
.await
.unwrap();
let app = app_with_users(&seed.db).await;
let admin = login_cookie(&app, "admin", "correct horse battery").await;
let routes = vec![
"/dashboard".to_string(),
"/dashboard/runs".to_string(),
format!("/dashboard/runs/{}", seed.run_id),
"/dashboard/articles".to_string(),
"/dashboard/articles/1".to_string(),
"/dashboard/ratings".to_string(),
"/dashboard/ratings?tab=events".to_string(),
"/dashboard/profile".to_string(),
"/dashboard/stats?days=14".to_string(),
"/dashboard/settings".to_string(),
"/dashboard/settings/history".to_string(),
"/dashboard/jobs".to_string(),
"/dashboard/jobs/99".to_string(),
"/dashboard/users".to_string(),
];
for uri in routes {
let response = get(&app, &uri, Some(&admin)).await;
assert_eq!(response.status(), StatusCode::OK, "{uri}");
let body = response_text(response).await;
assert!(body.contains("<!doctype html>"), "{uri}: {body}");
assert!(body.contains("The Daily EPUB"), "{uri}: {body}");
}
}
}