diff --git a/src/routes/delete-account/DeleteAccount.test.tsx b/src/routes/delete-account/DeleteAccount.test.tsx index 98f8bb38..ca7703d8 100644 --- a/src/routes/delete-account/DeleteAccount.test.tsx +++ b/src/routes/delete-account/DeleteAccount.test.tsx @@ -22,7 +22,10 @@ describe("DeleteAccount", () => { test("renders page title and account deletion steps", () => { setup(); expect( - screen.getByRole("heading", { name: /delete your account/i }), + screen.getByRole("heading", { + level: 1, + name: /delete your account/i, + }), ).toBeInTheDocument(); expect(screen.getByText(/open the webuddhist app/i)).toBeInTheDocument(); expect(screen.getByText(/go to profile/i)).toBeInTheDocument(); diff --git a/src/routes/planviewer/Planviewer.tsx b/src/routes/planviewer/Planviewer.tsx index 4e9d414c..2930393a 100644 --- a/src/routes/planviewer/Planviewer.tsx +++ b/src/routes/planviewer/Planviewer.tsx @@ -52,13 +52,15 @@ const Planviewer = () => { ); const handleSelectPlan = useCallback( - (planId: string) => { + (planId: string, date?: string) => { if (!selectedSeriesId) return; - setSearchParams({ + const params: Record = { series: selectedSeriesId, plan: planId, lang: apiLanguage, - }); + }; + if (date) params.date = date; + setSearchParams(params); }, [apiLanguage, selectedSeriesId, setSearchParams], ); diff --git a/src/routes/planviewer/components/SeriesDetailView.tsx b/src/routes/planviewer/components/SeriesDetailView.tsx index a15db20a..51c6fe34 100644 --- a/src/routes/planviewer/components/SeriesDetailView.tsx +++ b/src/routes/planviewer/components/SeriesDetailView.tsx @@ -16,7 +16,10 @@ import { resolveImageUrl, type PlanLanguageCode, } from "../utils/seriesUtils.ts"; -import { getPlanRowStatus } from "../utils/planStatusUtils.ts"; +import { + getPlanRowStatus, + resolvePlanInitialDate, +} from "../utils/planStatusUtils.ts"; import { getEarlyReturn, getLanguageClass, @@ -28,7 +31,7 @@ type SeriesDetailViewProps = { apiLanguage: string; isAuthenticated: boolean; onBack: () => void; - onSelectPlan: (planId: string) => void; + onSelectPlan: (planId: string, date?: string) => void; }; const SeriesDetailView = ({ @@ -187,7 +190,12 @@ const SeriesDetailView = ({ enrollment?.current_plan_id, )} contentFontClass={contentFontClass} - onSelect={onSelectPlan} + onSelect={(planId) => + onSelectPlan( + planId, + resolvePlanInitialDate(plan, today) ?? undefined, + ) + } /> )) )} diff --git a/src/routes/planviewer/utils/planStatusUtils.test.ts b/src/routes/planviewer/utils/planStatusUtils.test.ts index fd9611a9..2f049e04 100644 --- a/src/routes/planviewer/utils/planStatusUtils.test.ts +++ b/src/routes/planviewer/utils/planStatusUtils.test.ts @@ -1,5 +1,8 @@ import { describe, expect, test } from "vitest"; -import { resolveTodaysPlanId } from "./planStatusUtils.ts"; +import { + resolvePlanInitialDate, + resolveTodaysPlanId, +} from "./planStatusUtils.ts"; import type { SeriesPlanDTO } from "../types.ts"; const plans: SeriesPlanDTO[] = [ @@ -40,3 +43,29 @@ describe("resolveTodaysPlanId", () => { expect(resolveTodaysPlanId([], "EN")).toBeNull(); }); }); + +describe("resolvePlanInitialDate", () => { + // plan-1: 2026-05-14 .. 2026-05-19 (6 days) + // plan-2: 2026-05-20 .. 2026-06-19 (31 days) + test("returns today when today is within the plan window", () => { + const today = new Date("2026-06-10T12:00:00"); + expect(resolvePlanInitialDate(plans[1], today)).toBe("2026-06-10"); + }); + + test("returns the plan's first day when today is after the window", () => { + const today = new Date("2026-06-25T12:00:00"); + expect(resolvePlanInitialDate(plans[0], today)).toBe("2026-05-14"); + }); + + test("returns the plan's first day when today is before the window", () => { + const today = new Date("2026-05-01T12:00:00"); + expect(resolvePlanInitialDate(plans[1], today)).toBe("2026-05-20"); + }); + + test("returns null when the plan has no start_date", () => { + const today = new Date("2026-06-10T12:00:00"); + expect( + resolvePlanInitialDate({ ...plans[0], start_date: null }, today), + ).toBeNull(); + }); +}); diff --git a/src/routes/planviewer/utils/planStatusUtils.ts b/src/routes/planviewer/utils/planStatusUtils.ts index c6a1a055..60d5e76e 100644 --- a/src/routes/planviewer/utils/planStatusUtils.ts +++ b/src/routes/planviewer/utils/planStatusUtils.ts @@ -17,6 +17,22 @@ function addDays(date: Date, days: number): Date { return next; } +function toIsoDate(date: Date): string { + const pad = (n: number) => String(n).padStart(2, "0"); + return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`; +} + +export function resolvePlanInitialDate( + plan: SeriesPlanDTO, + today: Date = new Date(), +): string | null { + if (!plan.start_date) return null; + const start = toDateOnly(plan.start_date); + const end = addDays(start, Math.max(plan.total_days, 1) - 1); + if (today >= start && today <= end) return toIsoDate(today); + return toIsoDate(start); +} + export function formatPlanDateRange( startDate: string | null | undefined, totalDays: number,