From d130dfd94a818a0879f695bfc34f66f104ec2271 Mon Sep 17 00:00:00 2001 From: Daniel Sallai Date: Sun, 12 Apr 2026 11:52:09 +0200 Subject: [PATCH] =?UTF-8?q?fix(plan):=20fix=20getCurrentPlan=20response=20?= =?UTF-8?q?shape=20=E2=80=94=20nest=20plan=20inside=20{=20plan,=20latestVe?= =?UTF-8?q?rsion=20}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GET /api/workout-plans/current endpoint returned a flat WorkoutPlan & { latestVersion } merge, but the frontend's CurrentPlanResponse expected { plan: WorkoutPlan, latestVersion }. This caused planResponse?.plan to always be undefined, making the Plan page show an empty heading with no content. Fix: return { plan, latestVersion } (nested) from getCurrentPlan instead of { ...plan, latestVersion } (flat merge). Co-Authored-By: Claude Opus 4.6 (1M context) --- .../backend/src/db/queries/workout-plans.ts | 6 ++++-- .../src/routes/__tests__/workout-plans.test.ts | 18 ++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/backend/src/db/queries/workout-plans.ts b/packages/backend/src/db/queries/workout-plans.ts index 739e19d..1f81348 100644 --- a/packages/backend/src/db/queries/workout-plans.ts +++ b/packages/backend/src/db/queries/workout-plans.ts @@ -125,11 +125,13 @@ function mapAdjustmentRow(r: Record): PlanAdjustment { /** * Returns the single current plan for a user along with its latest version, * or null if the user has no plan yet. + * + * Response shape matches the frontend CurrentPlanResponse: { plan, latestVersion }. */ export async function getCurrentPlan( pool: pg.Pool, userId: string, -): Promise<(WorkoutPlan & { latestVersion: PlanVersion }) | null> { +): Promise<{ plan: WorkoutPlan; latestVersion: PlanVersion } | null> { // Get the most recent plan for the user const { rows: planRows } = await pool.query( `SELECT ${PLAN_COLUMNS} FROM workout_plans WHERE user_id = $1 @@ -149,7 +151,7 @@ export async function getCurrentPlan( if (versionRows.length === 0) return null; const latestVersion = mapVersionRow(versionRows[0] as Record); - return { ...plan, latestVersion }; + return { plan, latestVersion }; } /** Returns a plan by ID, or null if not found. */ diff --git a/packages/backend/src/routes/__tests__/workout-plans.test.ts b/packages/backend/src/routes/__tests__/workout-plans.test.ts index 37e5492..59c862e 100644 --- a/packages/backend/src/routes/__tests__/workout-plans.test.ts +++ b/packages/backend/src/routes/__tests__/workout-plans.test.ts @@ -178,13 +178,15 @@ describe('GET /api/workout-plans/current', () => { it('when user has a plan → 200 with plan + latestVersion', async () => { const { getCurrentPlan } = await import('../../db/queries/workout-plans.js'); vi.mocked(getCurrentPlan).mockResolvedValueOnce({ - id: 'plan-uuid', - userId: 'user-uuid', - name: 'My Plan', - splitType: 'Custom', - activeVersionId: 'version-uuid', - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), + plan: { + id: 'plan-uuid', + userId: 'user-uuid', + name: 'My Plan', + splitType: 'Custom', + activeVersionId: 'version-uuid', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, latestVersion: { id: 'version-uuid', planId: 'plan-uuid', @@ -204,7 +206,7 @@ describe('GET /api/workout-plans/current', () => { }); expect(response.statusCode).toBe(200); const body = JSON.parse(response.body); - expect(body.data.id).toBe('plan-uuid'); + expect(body.data.plan.id).toBe('plan-uuid'); expect(body.data.latestVersion).toBeDefined(); await app.close(); });