Found by the live walk, signed in as an admin: /account/events redirected to the dashboard. `GET /player/events/history` is behind requireAuth alone and self-scoped on req.user.id -- staff are a superset of players -- but the WEB has two logged-in shells, and RequirePlayer sends anyone who is not a `player` out of /account. A single mount there is a screen the reviewing admin can never open. Engagement Phase 7 hit this exact wall with the inbox and answered it with two routes, one pair of components and one mapping. `eventHistoryPath` joins `inboxPath` and `notificationSettingsPath` in notificationPaths.js rather than starting a second file with the same comment at the top of it. The staff path is /admin/events/mine, in the Events section of the sidebar, and it is the one row in that group with no `roles`. Also: the eventAnnounce fixture carried no slug, state or `listed`, so `eventUrl` answered undefined in every test in that file and the new code was exercised by none of them. The fixture now looks like a definition row, and three tests cover the link, the unlisted case and the draft case. The run.failed assertion that came with them was reading the wrong layer: `baseFor` assembles eventUrl for every trigger and the SEAM drops the keys a trigger does not declare, so the declaration test is what proves it. Removed, with a note saying where the rule actually lives. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
36 lines
1.8 KiB
JavaScript
36 lines
1.8 KiB
JavaScript
// Where a given account's notification screens live.
|
|
//
|
|
// **Staff and players reach the same two screens at different paths, and that is
|
|
// this file's whole reason to exist.** `/auth/me/notifications` is role-agnostic
|
|
// — behind `requireAuth` only, like every other `/auth/me` route — but the WEB
|
|
// has two logged-in shells: `RequirePlayer` sends anyone who is not a player to
|
|
// the admin area, where staff manage their own account under `/admin/account`.
|
|
// So a bell that always pointed at `/account/notifications` would, for every
|
|
// staff member, point at a page that redirects.
|
|
//
|
|
// Discovered in the Phase 7 rig: signed in as an admin, the inbox was simply
|
|
// unreachable on the web. Two routes, one pair of components, one mapping here.
|
|
|
|
export const isStaff = (user) => !!(user && user.role && user.role !== 'player')
|
|
|
|
/** The inbox — what the bell opens. */
|
|
export const inboxPath = (user) => (isStaff(user) ? '/admin/notifications' : '/account/notifications')
|
|
|
|
/** The per-channel preferences screen. */
|
|
export const notificationSettingsPath = (user) =>
|
|
isStaff(user) ? '/admin/notifications/settings' : '/account/notifications/settings'
|
|
|
|
/**
|
|
* This account's own event participation (events Phase 14a).
|
|
*
|
|
* The third screen to need this mapping, and it needed it for exactly the reason
|
|
* the two above did: `GET /player/events/history` is behind `requireAuth` alone,
|
|
* self-scoped on `req.user.id` — a staff member has a participation history like
|
|
* anyone else, and the group's own header says staff are a superset of players.
|
|
* The WEB is what disagrees, because `RequirePlayer` sends them to the login
|
|
* page. Found the same way the notifications pair was: signed in as an admin,
|
|
* the screen simply redirected.
|
|
*/
|
|
export const eventHistoryPath = (user) =>
|
|
isStaff(user) ? '/admin/events/mine' : '/account/events'
|