[Frontend] Give the dashboard a "needs you" attention surface #368

Closed
opened 2026-08-25 20:42:08 +00:00 by claude-bot · 2 comments
Contributor

Impact: CRITICAL

Found in the August 2026 session lifecycle review (#319).

What the user experiences

A GM or player who logs into the web app sees nothing but a list of campaigns. There is no way to tell, at a glance, that a vote is waiting on you, a session needs an RSVP, a transcript is ready for GM approval, or a recording is in progress right now. Every one of those facts only reaches the user through Discord, or not at all. The only in-app hint of anything pending is a non-clickable "Next session in 3d 4h" line on each campaign card.

Evidence

  • webapp/frontend/src/pages/Dashboard.jsx:234-446 — the dashboard renders a campaign list and nothing else; no unread state, no inbox, no pending-action count anywhere.
  • webapp/frontend/src/pages/Dashboard.jsx:67-71 — the countdown line ("Next session in Nd Nh") is plain text, not itself a link to the session; the whole card links to /campaigns/:id instead (:76-78).
  • Grep across webapp/frontend/src for notification|unread|inbox|badge returns only admin settings, status pills, and one badge at CampaignDetail.jsx:2599-2603 (on a tab that opens a new browser tab — see the nav-bar issue in this milestone).

Why it matters for a hosted product

The product never surfaces work that is waiting, in either channel. Every "is it done?" and "did my vote count?" becomes a support message to the owner instead of something the app already knows and could show.

Proposed fix

Add a "Needs you" block above the campaign list on Dashboard.jsx, driven by one new aggregate endpoint that returns: open votes the current user hasn't voted in, confirmed sessions they haven't RSVP'd to (once the web RSVP control ships — see the companion RSVP issue), transcripts in ready status awaiting GM approval, lore proposals pending count, and any session currently in_progress/recording. Every row deep-links to the relevant page. Make the campaign countdown line (Dashboard.jsx:67-71) itself a clickable link to the session while this is built.

Acceptance criteria

  • A new backend aggregate endpoint returns the current user's pending-action items across their campaigns.
  • Dashboard.jsx renders a "Needs you" section above the campaign list when the user has at least one pending item.
  • Each item type (open vote, missing RSVP, transcript awaiting approval, pending lore proposals, in-progress session) deep-links to the correct destination.
  • The section is absent (not an empty box) when there is nothing pending.
  • The existing "Next session in Nd Nh" countdown text becomes a link to the session.
**Impact: CRITICAL** Found in the August 2026 session lifecycle review (#319). ## What the user experiences A GM or player who logs into the web app sees nothing but a list of campaigns. There is no way to tell, at a glance, that a vote is waiting on you, a session needs an RSVP, a transcript is ready for GM approval, or a recording is in progress right now. Every one of those facts only reaches the user through Discord, or not at all. The only in-app hint of anything pending is a non-clickable "Next session in 3d 4h" line on each campaign card. ## Evidence - `webapp/frontend/src/pages/Dashboard.jsx:234-446` — the dashboard renders a campaign list and nothing else; no unread state, no inbox, no pending-action count anywhere. - `webapp/frontend/src/pages/Dashboard.jsx:67-71` — the countdown line ("Next session in Nd Nh") is plain text, not itself a link to the session; the whole card links to `/campaigns/:id` instead (`:76-78`). - Grep across `webapp/frontend/src` for `notification|unread|inbox|badge` returns only admin settings, status pills, and one badge at `CampaignDetail.jsx:2599-2603` (on a tab that opens a new browser tab — see the nav-bar issue in this milestone). ## Why it matters for a hosted product The product never surfaces work that is waiting, in either channel. Every "is it done?" and "did my vote count?" becomes a support message to the owner instead of something the app already knows and could show. ## Proposed fix Add a "Needs you" block above the campaign list on `Dashboard.jsx`, driven by one new aggregate endpoint that returns: open votes the current user hasn't voted in, confirmed sessions they haven't RSVP'd to (once the web RSVP control ships — see the companion RSVP issue), transcripts in `ready` status awaiting GM approval, lore proposals pending count, and any session currently `in_progress`/recording. Every row deep-links to the relevant page. Make the campaign countdown line (`Dashboard.jsx:67-71`) itself a clickable link to the session while this is built. ## Acceptance criteria - [ ] A new backend aggregate endpoint returns the current user's pending-action items across their campaigns. - [ ] `Dashboard.jsx` renders a "Needs you" section above the campaign list when the user has at least one pending item. - [ ] Each item type (open vote, missing RSVP, transcript awaiting approval, pending lore proposals, in-progress session) deep-links to the correct destination. - [ ] The section is absent (not an empty box) when there is nothing pending. - [ ] The existing "Next session in Nd Nh" countdown text becomes a link to the session.
Author
Contributor

Picking this up as v4.3.0 phase 4 (#514), last on the lane so it can use #370's RSVP data. Decision: one aggregate endpoint for the signed-in user returning typed items (open vote without your vote, confirmed session without your RSVP, transcript awaiting GM approval, pending suggested wiki updates, session in progress), each with a deep link, computed in one query per kind scoped to your memberships. The dashboard renders a "Needs you" block above the campaign list only when there is something in it, and the countdown line becomes a link to the session.

Picking this up as v4.3.0 phase 4 (#514), last on the lane so it can use #370's RSVP data. Decision: one aggregate endpoint for the signed-in user returning typed items (open vote without your vote, confirmed session without your RSVP, transcript awaiting GM approval, pending suggested wiki updates, session in progress), each with a deep link, computed in one query per kind scoped to your memberships. The dashboard renders a "Needs you" block above the campaign list only when there is something in it, and the countdown line becomes a link to the session.
Author
Contributor

Done in PR #526 (auto-merging on green); ships with v4.3.0.

GET /api/me/attention returns all five kinds in one response: in_progress, vote, rsvp, approve_transcript, wiki_suggestions. An aggregate rather than five client calls per campaign, because the dashboard has to know whether anything is pending before it can decide to render a section at all.

Six round trips regardless of how many campaigns you are in: memberships once, then one query per kind scoped by campaign_id IN (...), with "has this user already answered?" as a correlated EXISTS and the vote's date as a correlated min(proposed_time). No N+1. The two GM-only kinds are skipped entirely when you GM nothing. Archived and trashed campaigns contribute nothing: archiving is how a group says it has stopped playing (#479), and a dashboard that kept nagging about its open votes would undo that.

The section is absent, not empty, when nothing is pending; a block that is there every day is a block people stop reading. Each row deep-links to its destination, and the kind labels come from vocabulary.js, so a suggested wiki update is called the same thing here as on the page it links to (#379). The rsvp kind uses #370's nullable rsvp_status, so a GM marking somebody present does not count as that person having answered.

The "Next session in Nd Nh" line is now a link to the session. That required making the campaign card a container with the campaign link inside it rather than one big link, since an anchor cannot nest in an anchor.

Tests: 22 backend (test_attention.py: each kind for the right user and not for others, empty-list shape, scoping), 7 component, 3 page tests. One discrepancy found and filed as #524: count_bot_proposals on the campaign page does not filter soft-deleted proposals, so its number can exceed this endpoint's.

Done in PR #526 (auto-merging on green); ships with v4.3.0. `GET /api/me/attention` returns all five kinds in one response: `in_progress`, `vote`, `rsvp`, `approve_transcript`, `wiki_suggestions`. An aggregate rather than five client calls per campaign, because the dashboard has to know whether *anything* is pending before it can decide to render a section at all. Six round trips regardless of how many campaigns you are in: memberships once, then one query per kind scoped by `campaign_id IN (...)`, with "has this user already answered?" as a correlated `EXISTS` and the vote's date as a correlated `min(proposed_time)`. No N+1. The two GM-only kinds are skipped entirely when you GM nothing. Archived and trashed campaigns contribute nothing: archiving is how a group says it has stopped playing (#479), and a dashboard that kept nagging about its open votes would undo that. The section is **absent, not empty**, when nothing is pending; a block that is there every day is a block people stop reading. Each row deep-links to its destination, and the kind labels come from `vocabulary.js`, so a suggested wiki update is called the same thing here as on the page it links to (#379). The `rsvp` kind uses #370's nullable `rsvp_status`, so a GM marking somebody present does not count as that person having answered. The "Next session in Nd Nh" line is now a link to the session. That required making the campaign card a container with the campaign link inside it rather than one big link, since an anchor cannot nest in an anchor. Tests: 22 backend (`test_attention.py`: each kind for the right user and not for others, empty-list shape, scoping), 7 component, 3 page tests. One discrepancy found and filed as #524: `count_bot_proposals` on the campaign page does not filter soft-deleted proposals, so its number can exceed this endpoint's.
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Quest-Board#368
No description provided.