import { useState, useCallback, useEffect, ReactNode, createElement, Fragment } from 'react'; import { useSearchParams } from 'next/navigation'; function parseLocalDate(dateStr: string): Date | null { const [year, month, day] = dateStr.split('-'); if (!year || !month || !day) return null; return new Date(Number(year), Number(month) - 1, Number(day), 12, 0, 0); } function formatLocalDate(date: Date): string { const y = date.getFullYear(); const m = String(date.getMonth() + 1).padStart(2, '0'); const d = String(date.getDate()).padStart(2, '0'); return `${y}-${m}-${d}`; } /** * Hook that handles date synchronization with URL parameters * Should only be used within a component wrapped in Suspense */ export function useSyncedDateWithParams() { const searchParams = useSearchParams(); // Initialize with null, then set in useEffect to ensure client-side only const [date, setDate] = useState(null); // Get date from search params useEffect(() => { const dateStr = searchParams?.get('date') || null; const parsedDate = dateStr ? parseLocalDate(dateStr) : null; const validDate = parsedDate && !isNaN(parsedDate.getTime()) ? parsedDate : new Date(); setDate(validDate); }, [searchParams]); const handleDateChange = useCallback((newDate: Date | null) => { setDate(newDate); // Update URL if (typeof window !== 'undefined') { const params = new URLSearchParams(window.location.search); if (newDate) { params.set('date', formatLocalDate(newDate)); } else { params.delete('date'); } window.history.replaceState({}, '', `${window.location.pathname}?${params}`); } }, []); return { date, handleDateChange }; } /** * Client component that safely uses useSearchParams * Must be wrapped in a Suspense boundary */ export function DateParamHandler({ children }: { children: (props: { date: Date | null; handleDateChange: (newDate: Date | null) => void }) => ReactNode }) { const { date, handleDateChange } = useSyncedDateWithParams(); // Use React.createElement to avoid JSX syntax issues return createElement(Fragment, null, children({ date, handleDateChange })); } /** * @deprecated Use DateParamHandler with Suspense instead */ export function useSyncedDate() { const searchParams = useSearchParams(); // Initialize with null, then set in useEffect to ensure client-side only const [date, setDate] = useState(null); // Get date from search params useEffect(() => { const dateStr = searchParams?.get('date') || null; const parsedDate = dateStr ? parseLocalDate(dateStr) : null; const validDate = parsedDate && !isNaN(parsedDate.getTime()) ? parsedDate : new Date(); setDate(validDate); }, [searchParams]); const handleDateChange = useCallback((newDate: Date | null) => { setDate(newDate); // Update URL if (typeof window !== 'undefined') { const params = new URLSearchParams(window.location.search); if (newDate) { params.set('date', formatLocalDate(newDate)); } else { params.delete('date'); } window.history.replaceState({}, '', `${window.location.pathname}?${params}`); } }, []); return [date, handleDateChange] as const; }