feat(webapp): the recap for players, three missing controls, and explained gates (phase 7b of v4.3.0) #531
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/phase7b-self-service"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #388, closes #389, closes #382. Phase 7b of the v4.3.0 build order (#514). No migration, no contract change.
#388 recap for players.
GET /api/sessions/{id}/shelf/cold-openmoves from the GM gate to the member gate, andSessionTableandSessionDetailmount the existingColdOpenPanel. The payload was not entirely player-safe:open_loopsincluded plot threads withplayer_visible: false, an invariant three other places enforce, so the widening is a narrowing on the way out in the same service call (gm_viewflag):previously_onunchanged (the summary is already member-readable),open_loopsfiltered to visible threads for non-GMs,carried_countreported as 0 (it counts cards on the GM's private shelf). The panel gains a collapsible mode on its own local-storage key for the two player surfaces;SessionTablefetches its session once and declares the campaign so the nav bar gets its links there (#378's gap).#389 three controls. Un-reveal is a "Shown to the table" strip on the shelf rather than a per-card button, since a reveal can outlive its card. XP awards are now listed on the ledger (they were totals only, which is why
deleteXphad no caller) with a delete that names the award. The.icsendpoint already existed (GET /api/sessions/{id}/calendar.ics, shared code with the feed and invites) with tests and no caller; it is now a download link beside Google Calendar, and its eligibility is widened from "status is confirmed" to "has a scheduled time" so a completed session no longer offers a download that answers 400; an unscheduled session stays 400, documented and tested.#382 explained gates. A shared
BlockedStatecomponent (theCampaignHighlightspattern) on the four wiki hard-blocks andWikiArticle's error state;CampaignPlanningexplains instead of redirecting; the article edit URL for a non-editor renders the read view with a note;AddToShelfexplains its empty state with a link; the highlights draft badge says it publishes when the session summary is approved (there is no per-highlight approve endpoint and none was added); the archived banner names the instance admin as the restore path.Backend full 2504 passed / 13 skipped (+10); frontend 787 passed (64 files, +42); ruff and eslint clean apart from the pre-existing warning;
vite buildgreen.🤖 Generated with Claude Code
The cold-open strip — last session's recap, the quests still open — has been mounted only by SessionShelf, which is GM-only, so the one piece of catch-up a player most wants was the one surface they could not reach. `GET /sessions/{id}/shelf/cold-open` moves from `get_session_for_gm` to `get_session_for_member`, with the players' cut computed by the same service call rather than a second endpoint: - the recap is unchanged; `Session.summary` is already returned to every member by the session detail endpoint, so nothing new is exposed; - `open_loops` drops threads that are not `player_visible`, the same filter the Discord pre-session brief and the bot's thread endpoint already apply — that flag is exactly for the rumour the party has not heard; - `carried_count` reports 0, because it counts cards on the GM's private shelf. On the front end SessionTable and SessionDetail mount the existing ColdOpenPanel — no copy — in a new `collapsible` mode: a fold/unfold toggle on its own localStorage key instead of the shelf's permanent ×, because a player who puts the recap aside before the game wants it back during it. SessionTable also fetches its session for the campaign id and declares it, so the nav bar gains that campaign's links there (#378). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>Three backend capabilities with no way to reach them. `revokeSessionReveal` had no caller, so revealing a card was one-way: it stayed on the players' table view for the rest of the session. The shelf now carries a "Shown to the table" strip listing the live reveals with an un-reveal on each — a list rather than a control on the card, because a reveal outlives its card and an orphan would otherwise be unreachable. The player table drops it on its own poll, exactly as it picks up a reveal; nothing here talks to it. `fetchXp`/`deleteXp` had no caller either, and for the same underlying reason: the ledger rendered XP totals only, so an individual award was never on screen to act on. Awards are now listed like loot and gold, each with a delete whose confirm names the award ("Delete 250 XP to Alice — for the dragon?"). "Add to Calendar" offered Google alone. `GET /api/sessions/{id}/calendar.ics` already existed, member-gated and unused by the front end, so this is a link rather than a new endpoint — no second copy of the date arithmetic, and none of it client-side. Its eligibility widens from "status is confirmed" to "has a confirmed_time", which is the condition the calendar section itself renders on; gating the two differently meant a completed session showed a download that answered 400. A session with no scheduled time stays a 400, not a 404 — it exists, there is just nothing to put in a calendar yet. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>Six places where the app answered "you may not do that here" by removing the evidence that there was anything to do, which is indistinguishable from a broken link: - CampaignPlanning `navigate(..., {replace: true})`d a non-GM with no message. Prep is hidden from players in the nav bar, so anyone landing here followed a link a GM shared — it now says Prep is the GM's workspace and offers the way back. - WikiProposals, WikiNewEntry and WikiDraftReview ended navigation with a bare sentence on an empty page. They now use a shared BlockedState (the pattern CampaignHighlights already had), which says what the page is for and links back to the article, the wiki, the campaign and the dashboard. WikiArticle's full-page error state gets the same links. - WikiArticle's `/edit` URL for a non-editor silently rendered the read view. It still renders the read view — that is the useful thing to show — but says why. - AddToShelf `return null`ed with no upcoming session; it now says there is none and links to where you schedule one. - SessionHighlights' draft badge named the state without naming what changes it. There is no per-highlight approve endpoint and this does not invent one: approving the session summary publishes every highlight in one UPDATE, and that control is already on this page, so the badge and a count line say so. - The archived-campaign banner named no restore path. Archiving is a GM action, restoring is an instance admin's, so the banner now says so to the GM who cannot — and says scheduled sessions stay put and stop sending reminders. The count of those sessions is left to the post-archive confirmation, since `silenced_future_sessions` (#479) is on CampaignArchiveResponse only, not CampaignResponse. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>