{ "sourceRun": "ship-source-u-full-paid-20260909-0740", "initialImplementation": "# Plan: User Dashboard Page\n\n## Context\nWe're shipping a new user dashboard at `/dashboard` showing recent activity,\nnotifications panel, and quick-action buttons. Users land here after login.\n\n## UI Scope\n- New React page component `UserDashboard.tsx` at `src/pages/`\n- Three new sub-components: `ActivityFeed`, `NotificationsPanel`, `QuickActions`\n- Tailwind CSS for layout, mobile-first responsive (breakpoints: sm/md/lg)\n- Empty state, loading skeleton, error state for each panel\n- Hover states + focus-visible outlines on every interactive element\n- Modal dialog for \"Mark all as read\" on notifications panel\n- Toast notification system for action feedback\n\n## Backend\n- New REST endpoint `GET /api/dashboard` returns `{ activity, notifications, quickActions }`\n- Backed by existing PostgreSQL tables; no schema changes\n\n## Out of scope\n- Dark mode (separate plan)\n- Personalization / customization (separate plan)\n\n## Existing product and application contracts\n\nThis is the existing single-role member workspace, not a new product or a new\nonboarding flow. Members currently visit three separate pages after login to\nresume work, check alerts, and inspect recent changes. In the team's last task\nwalkthrough, finding the next item took a median 75 seconds. The dashboard's\nsuccess measure is login-to-first-completed-task time, targeting 45 seconds,\nwith completed-task rate and permission-error rate as guardrails. Existing\nanalytics records login, action start, action completion, and permission errors;\nthe new page still needs its own exposure and interaction instrumentation.\n\nActivity is the immutable audit history of workspace changes. Notifications are\nmember-specific alerts with persistent read state; acknowledging an alert does\nnot alter audit history. The existing action registry supplies three actions\n(create an item, resume assigned work, invite a member), with stable IDs, labels,\nroute targets, and server-side eligibility predicates. These are links into\nexisting workflows; action ranking and a new configuration service do not exist.\n\nThe application already uses cookie sessions and workspace membership middleware.\nIts request context supplies the authenticated member and workspace IDs. Existing\nrepository methods apply both IDs where appropriate; callers do not accept a\nworkspace ID from query parameters. Mutations already require CSRF tokens. The\nnew dashboard endpoint must compose these methods and follow the same boundaries;\nits handler, authorization integration, and failure paths have not been written.\n\nExisting list methods return the latest 20 records plus a cursor and have indexed\nworkspace/member and created-at access paths. The existing full activity and\nnotification pages own older-page navigation. The member-scoped bulk-read API is\nidempotent and marks only notifications at or before the supplied snapshot time,\nso later arrivals remain unread. Existing HTTP clients expose typed unauthenticated,\nforbidden, validation, retryable-service, and network errors. Each dashboard panel\nstill needs to map these results to its loading, empty, error, retry, and success\nstates; the aggregate endpoint's response composition and partial-failure behavior\nremain new implementation work. No schema migration or new mutation API is needed.\n\nThe app already has Tailwind spacing/color/type tokens, a responsive page shell,\nbuttons, links, and a dialog primitive with focus trapping, Escape dismissal, and\nfocus return. These primitives do not implement any dashboard panel, confirmation\nflow, or toast system. The new modal and toast feedback must also work with keyboard\nand screen readers; existing accessibility policy requires named controls, a live\nregion for nonblocking feedback, sufficient contrast, and reduced-motion support.\nThe dashboard still needs its own layout, content hierarchy, mobile behavior, and\nstate-specific copy at sm/md/lg breakpoints.\n\nVitest, React Testing Library, and Playwright already run in CI. Existing fixtures\ncover authenticated members, another workspace, empty lists, and service failures;\nthere are no dashboard-specific tests yet. Existing staging feature flags and\nrequest/error metrics support a member-cohort rollout and rollback to the current\nlanding page. The dashboard's rollout criteria, endpoint performance checks,\ninteraction tests, and accessibility verification must be specified and added.\n\nAll dashboard screen, panel, aggregate-endpoint, modal, and toast work listed above\nis new. The existing contracts describe dependencies to reuse, not completed work\nor prior approval of an implementation approach.\n", "activeAfterAmend": "# Plan: User Dashboard Page\n\n## Implementation plan\n\n### Original plan\n\n**Context:** Shipping a new user dashboard at `/dashboard` — activity feed, notifications panel, quick-action buttons. Users land here after login. Success metric: login-to-first-completed-task median from 75 seconds to 45 seconds.\n\n**UI Scope:**\n- `UserDashboard.tsx` at `src/pages/`\n- Sub-components: `ActivityFeed`, `NotificationsPanel`, `QuickActions`\n- Tailwind CSS, mobile-first (sm/md/lg breakpoints)\n- Loading skeleton, empty state, error state per panel\n- Hover + focus-visible on interactive elements\n- Modal: \"Mark all as read\" on notifications panel\n- Toast notification system for action feedback\n\n**Backend:**\n- `GET /api/dashboard` → `{ activity, notifications, quickActions }`\n- Existing PostgreSQL tables; no schema changes\n\n**Out of scope:** dark mode, personalization.\n\n### Amendments from CEO review (A1-A14)\n\n**A1 — Partial-failure contract (REQUIRED before implementation):**\nResponse schema:\n```\n{\n activity: ActivityData | null,\n notifications: NotificationData | null,\n quickActions: QuickActionData | null,\n panelErrors: {\n activity?: { code: string, message: string },\n notifications?: { code: string, message: string },\n quickActions?: { code: string, message: string }\n }\n}\n```\nEach panel renders based on its own data/error field independently.\n\n**A2 — Parallel backend queries:** Handler uses Promise.all (or equivalent) for all three data sources. Serial execution prohibited.\n\n**A3 — Rollout criteria (pre-specified, required before feature flag enables):**\n1. p95 GET /api/dashboard latency < 300ms (staging verification)\n2. Zero WCAG 2.1 AA violations (keyboard + screen reader paths)\n3. Permission-error rate at or below current baseline\n4. Login-to-first-completed-task median < 50s in first cohort (abort if > 65s)\n\n**A4 — Double-click guard:** \"Mark all as read\" confirm button disables immediately on click; re-enables on API response.\n\n**A5 — Toast system:**\n- Use existing installed toast library if present; otherwise install `sonner` (not hand-rolled)\n- Toast container at AppShell level (not inside UserDashboard) — `role=\"status\"` / `aria-live=\"polite\"`\n- Max 3 visible toasts; FIFO replacement; 4-second auto-dismiss\n- Suppress animations via `@media (prefers-reduced-motion)`\n\n**A6 — PanelWrapper:** Shared component handling loading skeleton, empty state, error state + retry button, success slot. All three panels use it.\n\n**A7 — QuickActions empty state:** When all 3 actions ineligible, render defined empty state (e.g., \"No actions available\").\n\n**A8 — Panel order:** QuickActions → NotificationsPanel → ActivityFeed (mobile and lg breakpoint).\n\n**A9 — Content rendering:** All user-generated content in ActivityFeed and NotificationsPanel rendered as plaintext (React's default `{value}`). No `dangerouslySetInnerHTML`.\n\n**A10 — QuickActions service layer:** Data fetching via `fetchQuickActions()` service function, not direct action registry import. Enables future ranking service substitution.\n\n**A11 — Analytics events (required):**\n- `dashboard_viewed` on page mount\n- `panel_loaded {panel, duration_ms, status}` per panel\n- `quick_action_clicked {action_id}`\n- `notification_marked_read {count}` on bulk-read success\n- `panel_error {panel, error_code}`\n\n**A12 — Alert:** Dashboard endpoint p95 > 500ms for 5 minutes → existing alert channel.\n\n**A13 — Runbook:** 1-page operational runbook: what to check when endpoint is slow/erroring, how to disable feature flag, escalation path.\n\n**A14 — Required tests:**\n- Playwright: login → all panels render (happy path)\n- Playwright: one panel API fails → other two panels still render\n- RTL: \"Mark all as read\" modal — confirm / cancel / error states\n- RTL: double-click guard on confirm button\n- RTL: all panel states (loading / empty / error)\n- Playwright: keyboard navigation through all panels (Tab, Enter, Escape)\n- Playwright: reduced-motion mode (no skeleton animations)\n- Vitest: GET /api/dashboard partial failure response structure\n- Playwright staging: p95 latency verification check\n\n**Deferred to TODOS.md:**\n- State-aware redirect for members with in-progress work (P2)\n- Notification badge in page `