Web dashboard: briefs for steps 2-7

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 04:03:11 +00:00
co-authored by Claude Fable 5.1
parent bf2ab8441e
commit a70de4af06
6 changed files with 276 additions and 0 deletions
@@ -0,0 +1,47 @@
# Step 2 — Full issue views, rating widget, POST /rate
Read `00-shared.md`, then the plan `docs/plans/2026-09-03-web-dashboard.md`
(§3, §4.3–§4.4, §5.4, §6.3–§6.4, §8 including §8.1, §16, §17), then
`handoff-step1.md` for what the foundation actually shipped. This is plan §19
item 2. Build on the step 1 code as it exists; do not restructure it.
## Deliverables
1. `issue_full.html` (§8): masthead + dateline, The Brief (`front_page_html`
`|safe`), download buttons (from `IssueView.downloads`), the per-section
index like the EPUB's In-this-issue page (title → article page,
`source · N min read`, summary, Why it's here), the rating widget for admins,
links to World Briefing / Behind the paper (only when present), colophon
facts footer. `/` and `/issues/{date}` render this for any signed-in viewer.
2. `article.html` at `/issues/{date}/articles/{article_id}` (§8): header
(title → source, byline, meta line, Why, social line via
`chapters::social_line`, summary), body from `ammonia::clean` with the same
configuration the EPUB uses (find it in `src/html.rs`/`src/epub`; do not run
`to_xhtml`), `<img>` keep remote `src` and get `loading="lazy"
referrerpolicy="no-referrer"`, the discussion via `comments::render_xhtml`
when present, prev/next in issue order, "Read online ↗", rating widget for
admins. 404 (site error page) when the article is not in that issue.
3. `world.html` (`/issues/{date}/world`, `world::render_xhtml`, 404 when
absent) and `behind.html` (`/issues/{date}/behind`, the same lines as the
EPUB chapter via `chapters::behind_*_line`; near misses link to
`/dashboard/articles/{id}` for admins).
4. These three routes sit under the `login_required!` layer (§3).
5. The HMAC confirmation page (`server::confirmation_page`) gains the
"Open this issue on the site" link (§8).
6. Rating widget partial `_rating_widget.html` + `src/web/rate.rs` (§8.1):
`POST /rate` under the admin layer and the origin check; form and JSON
variants; `RatingEvent { source: "dashboard", user_id: Some(viewer.id) }`;
`cleared` writes value 0.0 with label `cleared`; `issue_date` optional with
the dashboard fallback (`db::latest_issue_date_for_article`); JSON →
`200 {"article_id","label","event_id"}`, form → flash + 303 to a validated
same-site `next`. Check how the CLI (`main.rs` `cmd_ratings`) writes the
`cleared` event and match it exactly.
7. `app.js`: intercept `.rating` forms, `fetch` with
`credentials: "same-origin"` and `Accept: application/json`, update the
active button; keep the no-JS form path working.
8. Tests (§17 "Full rendering", "Rating"): signed-in `/issues/{date}` contains
Brief, summaries and why; article page contains body and discussion; the
fallback loader (no `issue_json`) renders without world/discussion links;
downloads listed only for files that exist; `POST /rate` as admin appends a
`dashboard` event with `user_id`, as user → 403, anonymous → redirect/401,
JSON and form variants, `next` validated, `cleared` value 0.