From 91f599f1a26f4f8eed9aa782b72980d5e58bdda4 Mon Sep 17 00:00:00 2001 From: KevinBigham Date: Tue, 5 May 2026 12:20:41 -0500 Subject: [PATCH] feat(web): polish assistant release readiness Strengthens the V1 Assistant into a closed-playtest-ready layer stacked on PR #72 while keeping simulation behavior, RNG, and save compatibility untouched. Save schema remains v33; no migration required. --- apps/web/src/app/App.test.tsx | 28 +++ apps/web/src/app/App.tsx | 6 + apps/web/src/app/layout/AppLayout.tsx | 19 +- apps/web/src/app/layout/Sidebar.tsx | 11 +- apps/web/src/app/layout/SimControls.tsx | 6 +- .../assistant/components/AssistantAvatar.tsx | 59 +++++ .../components/AssistantPanel.test.tsx | 181 +++++++++++--- .../assistant/components/AssistantPanel.tsx | 228 +++++++++++++++--- .../assistant/data/assistantGuidance.test.ts | 72 ++++++ .../assistant/data/assistantGuidance.ts | 112 +++++++++ .../assistant/lib/assistantFeedback.test.ts | 38 +++ .../assistant/lib/assistantFeedback.ts | 43 ++++ .../features/finance/routes/FinancePage.tsx | 17 +- .../minors/components/PipelineView.tsx | 3 +- .../src/features/minors/routes/MinorsPage.tsx | 4 +- .../players/components/ProfileHeader.tsx | 14 +- .../src/features/roster/routes/RosterPage.tsx | 31 ++- .../features/scouting/routes/ScoutingPage.tsx | 10 +- .../src/features/setup/routes/SetupPage.tsx | 17 +- .../src/features/trade/routes/TradePage.tsx | 9 +- .../shared/components/RatingBadge.test.tsx | 45 ++++ .../web/src/shared/components/RatingBadge.tsx | 62 +++++ apps/web/src/workers/sim.worker.helpers.ts | 2 + apps/web/src/workers/sim.worker.pipeline.ts | 16 +- apps/web/src/workers/sim.worker.queries.ts | 26 +- ...BD_ASSISTANT_POLISH_RELEASE_V2_PROGRESS.md | 100 ++++++++ docs/tutorial-assistant/asset-plan.md | 12 + .../assistant-visual-system.md | 31 +++ docs/tutorial-assistant/coverage-matrix.md | 20 +- docs/tutorial-assistant/first-session-flow.md | 27 +++ .../mobile-validation-v2.md | 42 ++++ .../narrative-continuity-v2.md | 34 +++ .../playtest-feedback-loop.md | 27 +++ docs/tutorial-assistant/playtest-plan.md | 17 ++ .../ratings-visibility-changelog.md | 25 ++ docs/tutorial-assistant/release-gate.md | 34 ++- docs/tutorial-assistant/v2-scorecard.md | 33 +++ packages/design-tokens/src/tailwind-preset.ts | 11 + 38 files changed, 1340 insertions(+), 132 deletions(-) create mode 100644 apps/web/src/features/assistant/components/AssistantAvatar.tsx create mode 100644 apps/web/src/features/assistant/lib/assistantFeedback.test.ts create mode 100644 apps/web/src/features/assistant/lib/assistantFeedback.ts create mode 100644 apps/web/src/shared/components/RatingBadge.test.tsx create mode 100644 apps/web/src/shared/components/RatingBadge.tsx create mode 100644 docs/goals/MBD_ASSISTANT_POLISH_RELEASE_V2_PROGRESS.md create mode 100644 docs/tutorial-assistant/assistant-visual-system.md create mode 100644 docs/tutorial-assistant/first-session-flow.md create mode 100644 docs/tutorial-assistant/mobile-validation-v2.md create mode 100644 docs/tutorial-assistant/narrative-continuity-v2.md create mode 100644 docs/tutorial-assistant/playtest-feedback-loop.md create mode 100644 docs/tutorial-assistant/ratings-visibility-changelog.md create mode 100644 docs/tutorial-assistant/v2-scorecard.md diff --git a/apps/web/src/app/App.test.tsx b/apps/web/src/app/App.test.tsx index b42343a..2a661b9 100644 --- a/apps/web/src/app/App.test.tsx +++ b/apps/web/src/app/App.test.tsx @@ -12,6 +12,10 @@ const routeMockState = vi.hoisted(() => ({ error: null as unknown, })); +const toasterMockState = vi.hoisted(() => ({ + props: [] as Array>, +})); + vi.mock('./routes', () => ({ AppRoutes: () => { if (routeMockState.error) { @@ -25,6 +29,13 @@ vi.mock('./providers/ErrorBoundary', () => ({ ErrorBoundary: ({ children }: { children: ReactNode }) => <>{children}, })); +vi.mock('sonner', () => ({ + Toaster: (props: Record) => { + toasterMockState.props.push(props); + return null; + }, +})); + ( globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean } ).IS_REACT_ACT_ENVIRONMENT = true; @@ -70,6 +81,7 @@ describe('App', () => { }); usePreferencesStore.getState().reset(); routeMockState.error = null; + toasterMockState.props = []; document.documentElement.dataset.contrast = 'standard'; container = document.createElement('div'); document.body.appendChild(container); @@ -96,6 +108,22 @@ describe('App', () => { expect(document.documentElement.dataset.contrast).toBe('high'); }); + it('keeps persistent toasts dismissible and clear of mobile chrome', async () => { + await act(async () => { + root!.render(); + await Promise.resolve(); + }); + + expect(toasterMockState.props.at(-1)).toEqual(expect.objectContaining({ + closeButton: true, + mobileOffset: expect.objectContaining({ + bottom: 'calc(env(safe-area-inset-bottom) + 16rem)', + left: '0.75rem', + right: '0.75rem', + }), + })); + }); + it('routes save-load render failures into recovery instead of the generic app shell', async () => { const failure: Extract = { ok: false, diff --git a/apps/web/src/app/App.tsx b/apps/web/src/app/App.tsx index 0d44672..2729988 100644 --- a/apps/web/src/app/App.tsx +++ b/apps/web/src/app/App.tsx @@ -25,7 +25,13 @@ export function App() { standing.teamId === userTeamId) ?? null, + daysUntilTradeDeadline: seasonFlow.daysUntilTradeDeadline, + phaseLabel: seasonFlow.phaseLabel, + detailLabel: seasonFlow.detailLabel, + seasonSummary: seasonFlow.seasonSummary, + } + : null; const ambientMode = resolveAmbientMode( location.pathname, isInitialized, @@ -468,7 +481,7 @@ export function AppLayout() { {/* Main area: sidebar + content */}
-
+
<> {seasonFlow && ( - + {/* Command palette overlay */}
-
+
Navigation
) : null} -
+

{guidance.title}

-

{guidance.pagePurpose}

-

{guidance.whenToUse}

+

+ {guidance.firstSessionCue ?? guidance.pagePurpose} +

+

{guidance.whenToUse}

+
+ +
+
Story so far
+
    + {storySoFar.map((line) => ( +
  • + {line} +
  • + ))} +
@@ -254,6 +332,8 @@ export function AssistantPanel({ tickerFeed = [] }: AssistantPanelProps) {
{ratingsOpen ? ( -
+
Ratings read

{guidance.ratingsFocus}

) : null} {strategyOpen ? ( -
+
Bench coach note

{guidance.deeperStrategy}

) : null} -
+
{guidance.mobileTip}
-
+ {feedbackOpen ? ( +
+
+
Playtest feedback
+ {copiedFeedback ? ( + Copied + ) : null} +
+
+ + +
+