Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NHyYupFdBiR4VfoUM7NjSM
3.0 KiB
3.0 KiB
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
issue_full.html(§8): masthead + dateline, The Brief (front_page_html|safe), download buttons (fromIssueView.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.article.htmlat/issues/{date}/articles/{article_id}(§8): header (title → source, byline, meta line, Why, social line viachapters::social_line, summary), body fromammonia::cleanwith the same configuration the EPUB uses (find it insrc/html.rs/src/epub; do not runto_xhtml),<img>keep remotesrcand getloading="lazy" referrerpolicy="no-referrer", the discussion viacomments::render_xhtmlwhen present, prev/next in issue order, "Read online ↗", rating widget for admins. 404 (site error page) when the article is not in that issue.world.html(/issues/{date}/world,world::render_xhtml, 404 when absent) andbehind.html(/issues/{date}/behind, the same lines as the EPUB chapter viachapters::behind_*_line; near misses link to/dashboard/articles/{id}for admins).- These three routes sit under the
login_required!layer (§3). - The HMAC confirmation page (
server::confirmation_page) gains the "Open this issue on the site" link (§8). - Rating widget partial
_rating_widget.html+src/web/rate.rs(§8.1):POST /rateunder the admin layer and the origin check; form and JSON variants;RatingEvent { source: "dashboard", user_id: Some(viewer.id) };clearedwrites value 0.0 with labelcleared;issue_dateoptional with the dashboard fallback (db::latest_issue_date_for_article); JSON →200 {"article_id","label","event_id"}, form → flash + 303 to a validated same-sitenext. Check how the CLI (main.rscmd_ratings) writes theclearedevent and match it exactly. app.js: intercept.ratingforms,fetchwithcredentials: "same-origin"andAccept: application/json, update the active button; keep the no-JS form path working.- Tests (§17 "Full rendering", "Rating"): signed-in
/issues/{date}contains Brief, summaries and why; article page contains body and discussion; the fallback loader (noissue_json) renders without world/discussion links; downloads listed only for files that exist;POST /rateas admin appends adashboardevent withuser_id, as user → 403, anonymous → redirect/401, JSON and form variants,nextvalidated,clearedvalue 0.