From 6447db300811605b430133af02cdd6545fedd687 Mon Sep 17 00:00:00 2001 From: josh Date: Sat, 30 May 2026 08:39:20 -0400 Subject: [PATCH] refactor: store selected week in a cookie, not the URL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The home page no longer reads ?week=YYYY-MM-DD from the URL. Selected week lives in the tcWeek cookie, set via a server action that revalidates the home page so the next render reflects it. The URL stays at "/" regardless of which week the user is viewing. WeekNav prev/next/today buttons (and the arrow-key bindings) call the server action directly — no router.refresh dance, no client-side cookie write. BackToCalendarLink drops its localStorage-based href reconstruction and just links to "/" since the cookie already remembers the right week across navigations. Co-Authored-By: Claude Opus 4.7 --- README.md | 2 +- app/actions/week.ts | 29 +++++++++++++++++++++++++++++ app/page.tsx | 17 ++++++++--------- components/BackToCalendarLink.tsx | 15 ++++----------- components/HomePageClient.tsx | 5 ----- components/WeekNav.tsx | 10 ++++++---- docs/ARCHITECTURE.md | 8 +++++++- docs/DEVELOPMENT.md | 2 +- 8 files changed, 56 insertions(+), 32 deletions(-) create mode 100644 app/actions/week.ts diff --git a/README.md b/README.md index ddb9199..f03512f 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ The backend starts on port 3001, initializes the database, and begins the cron s npm run dev ``` -Open [http://localhost:3000](http://localhost:3000). Navigate weeks with the `←` / `→` buttons, or pass `?week=YYYY-MM-DD` directly. Click any park name to open its detail page. +Open [http://localhost:3000](http://localhost:3000). Navigate weeks with the `←` / `→` buttons (or arrow keys); your selected week persists across visits via the `tcWeek` cookie. Click any park name to open its detail page. ### Debug a specific park + date diff --git a/app/actions/week.ts b/app/actions/week.ts new file mode 100644 index 0000000..42784ce --- /dev/null +++ b/app/actions/week.ts @@ -0,0 +1,29 @@ +"use server"; + +import { cookies } from "next/headers"; +import { revalidatePath } from "next/cache"; + +const WEEK_COOKIE = "tcWeek"; +const MAX_AGE = 60 * 60 * 24 * 30; // 30 days + +/** + * Persist the selected week start (YYYY-MM-DD) in a server-readable cookie + * and revalidate the home page so the new week renders. + */ +export async function setWeek(weekStart: string): Promise { + if (!/^\d{4}-\d{2}-\d{2}$/.test(weekStart)) return; + const cookieStore = await cookies(); + cookieStore.set(WEEK_COOKIE, weekStart, { + path: "/", + maxAge: MAX_AGE, + sameSite: "lax", + }); + revalidatePath("/"); +} + +/** Clear the saved week — used by the "Today" button to jump back to current. */ +export async function clearWeek(): Promise { + const cookieStore = await cookies(); + cookieStore.delete(WEEK_COOKIE); + revalidatePath("/"); +} diff --git a/app/page.tsx b/app/page.tsx index 14d7545..423b82c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,15 +1,14 @@ +import { cookies } from "next/headers"; import { HomePageClient } from "@/components/HomePageClient"; import { getTodayLocal, formatDateLocal } from "@/lib/env"; const BACKEND_URL = process.env.BACKEND_URL ?? "http://localhost:3001"; -interface PageProps { - searchParams: Promise<{ week?: string }>; -} +const WEEK_COOKIE = "tcWeek"; -function getWeekStart(param: string | undefined): string { - if (param && /^\d{4}-\d{2}-\d{2}$/.test(param)) { - const d = new Date(param + "T00:00:00"); +function getWeekStart(saved: string | undefined): string { + if (saved && /^\d{4}-\d{2}-\d{2}$/.test(saved)) { + const d = new Date(saved + "T00:00:00"); if (!isNaN(d.getTime())) { d.setDate(d.getDate() - d.getDay()); return formatDateLocal(d); @@ -21,9 +20,9 @@ function getWeekStart(param: string | undefined): string { return formatDateLocal(d); } -export default async function HomePage({ searchParams }: PageProps) { - const params = await searchParams; - const weekStart = getWeekStart(params.week); +export default async function HomePage() { + const saved = (await cookies()).get(WEEK_COOKIE)?.value; + const weekStart = getWeekStart(saved); const data = await fetch( `${BACKEND_URL}/api/calendar/week?start=${weekStart}`, diff --git a/components/BackToCalendarLink.tsx b/components/BackToCalendarLink.tsx index 5fe3d03..557927b 100644 --- a/components/BackToCalendarLink.tsx +++ b/components/BackToCalendarLink.tsx @@ -1,19 +1,12 @@ -"use client"; - -import { useState, useEffect } from "react"; import Link from "next/link"; export function BackToCalendarLink() { - const [href, setHref] = useState("/"); - - useEffect(() => { - const saved = localStorage.getItem("lastWeek"); - if (saved) setHref(`/?week=${saved}`); - }, []); - + // The selected week is stored in a server-readable cookie (tcWeek), so the + // home page already renders the right week without us needing to pass it + // through the URL. return ( timeouts.forEach(clearTimeout); }, [isCurrentWeek, today, data, router]); - // Remember the current week so the park page back button returns here. - useEffect(() => { - localStorage.setItem("lastWeek", weekStart); - }, [weekStart]); - const toggle = () => { const next = !coastersOnly; setCoastersOnly(next); diff --git a/components/WeekNav.tsx b/components/WeekNav.tsx index 7437658..1fe192b 100644 --- a/components/WeekNav.tsx +++ b/components/WeekNav.tsx @@ -1,7 +1,7 @@ "use client"; import { useEffect } from "react"; -import { useRouter } from "next/navigation"; +import { setWeek, clearWeek } from "@/app/actions/week"; interface WeekNavProps { weekStart: string; // YYYY-MM-DD (Sunday) @@ -39,9 +39,11 @@ function shiftWeek(weekStart: string, delta: number): string { } export function WeekNav({ weekStart, weekDates, isCurrentWeek }: WeekNavProps) { - const router = useRouter(); const nav = (delta: number) => { - router.push(`/?week=${shiftWeek(weekStart, delta)}`); + void setWeek(shiftWeek(weekStart, delta)); + }; + const jumpToToday = () => { + void clearWeek(); }; useEffect(() => { @@ -68,7 +70,7 @@ export function WeekNav({ weekStart, weekDates, isCurrentWeek }: WeekNavProps) { {!isCurrentWeek && (