diff --git a/apps/website/app/dashboard/page.tsx b/apps/website/app/dashboard/page.tsx index d262c3285..8c298baf2 100644 --- a/apps/website/app/dashboard/page.tsx +++ b/apps/website/app/dashboard/page.tsx @@ -1,6 +1,6 @@ import { notFound, redirect } from 'next/navigation'; import { DashboardPageQuery } from '@/lib/page-queries/DashboardPageQuery'; -import { DashboardTemplate } from '@/templates/DashboardTemplate'; +import { DashboardPageClient } from '@/client-wrapper/DashboardPageClient'; import { logger } from '@/lib/infrastructure/logging/logger'; export default async function DashboardPage() { @@ -23,5 +23,5 @@ export default async function DashboardPage() { // Success const viewData = result.unwrap(); - return ; -} \ No newline at end of file + return ; +} diff --git a/apps/website/app/leagues/create/page.tsx b/apps/website/app/leagues/create/page.tsx index 72a1af1bd..296e91570 100644 --- a/apps/website/app/leagues/create/page.tsx +++ b/apps/website/app/leagues/create/page.tsx @@ -1,7 +1,3 @@ -'use client'; - -import React from 'react'; -import { useRouter, useSearchParams } from 'next/navigation'; import { CreateLeagueWizard } from '@/client-wrapper/CreateLeagueWizard'; import { Section } from '@/ui/Section'; import { Container } from '@/ui/Container'; @@ -10,11 +6,13 @@ import { SearchParamBuilder } from '@/lib/routing/search-params/SearchParamBuild type StepName = 'basics' | 'visibility' | 'structure' | 'schedule' | 'scoring' | 'stewarding' | 'review'; -export default function CreateLeaguePage() { - const router = useRouter(); - const searchParams = useSearchParams(); +interface CreateLeaguePageProps { + searchParams: Promise<{ [key: string]: string | string[] | undefined }>; +} - const wizardParams = SearchParamParser.parseWizard(searchParams as unknown as URLSearchParams).unwrap(); +export default async function CreateLeaguePage({ searchParams }: CreateLeaguePageProps) { + const resolvedParams = await searchParams; + const wizardParams = SearchParamParser.parseWizard(resolvedParams as any).unwrap(); const rawStep = wizardParams.step; let currentStepName: StepName = 'basics'; @@ -28,23 +26,11 @@ export default function CreateLeaguePage() { currentStepName = rawStep; } - const handleStepChange = (stepName: StepName) => { - const builder = new SearchParamBuilder(); - // Copy existing params if needed, but here we just want to set the step - if (searchParams) { - searchParams.forEach((value, key) => { - if (key !== 'step') builder.set(key, value); - }); - } - builder.step(stepName); - router.push(`/leagues/create${builder.build()}`); - }; - return (
- +
); -} \ No newline at end of file +} diff --git a/apps/website/app/page.tsx b/apps/website/app/page.tsx index c56c8188e..e144db2eb 100644 --- a/apps/website/app/page.tsx +++ b/apps/website/app/page.tsx @@ -1,5 +1,4 @@ -import { PageWrapper } from '@/components/shared/state/PageWrapper'; -import { HomeTemplate, type HomeViewData } from '@/templates/HomeTemplate'; +import { HomePageClient } from '@/client-wrapper/HomePageClient'; import { PageDataFetcher } from '@/lib/page/PageDataFetcher'; import { HomePageQuery } from '@/lib/page-queries/HomePageQuery'; import { notFound, redirect } from 'next/navigation'; @@ -19,7 +18,5 @@ export default async function Page() { notFound(); } - const Template = ({ viewData }: { viewData: HomeViewData }) => ; - - return ; + return ; } diff --git a/apps/website/app/sponsor/settings/page.tsx b/apps/website/app/sponsor/settings/page.tsx index 20edcf390..f601a450f 100644 --- a/apps/website/app/sponsor/settings/page.tsx +++ b/apps/website/app/sponsor/settings/page.tsx @@ -3,7 +3,7 @@ import { useState } from 'react'; import { SponsorSettingsTemplate } from '@/templates/SponsorSettingsTemplate'; import { logoutAction } from '@/app/actions/logoutAction'; -import { ConfirmDialog } from '@/ui/ConfirmDialog'; +import { ConfirmDialog } from '@/components/shared/ConfirmDialog'; import { useRouter } from 'next/navigation'; import { routes } from '@/lib/routing/RouteConfig'; import { logger } from '@/lib/infrastructure/logging/logger'; diff --git a/apps/website/app/teams/create/page.tsx b/apps/website/app/teams/create/page.tsx index b79967070..6b25ece8e 100644 --- a/apps/website/app/teams/create/page.tsx +++ b/apps/website/app/teams/create/page.tsx @@ -1,37 +1,5 @@ -'use client'; +import { CreateTeamPageClient } from '@/client-wrapper/CreateTeamPageClient'; -import { CreateTeamForm } from '@/components/teams/CreateTeamForm'; -import { routes } from '@/lib/routing/RouteConfig'; -import { Container } from '@/ui/Container'; -import { Heading } from '@/ui/Heading'; -import { Stack } from '@/ui/Stack'; -import { Section } from '@/ui/Section'; -import { useRouter } from 'next/navigation'; - -export default function CreateTeamPage() { - const router = useRouter(); - - const handleNavigate = (teamId: string) => { - router.push(routes.team.detail(teamId)); - }; - - const handleCancel = () => { - router.back(); - }; - - return ( -
- - - - Create a Team - - - - -
- ); +export default async function CreateTeamPage() { + return ; } diff --git a/apps/website/client-wrapper/CreateLeagueWizard.tsx b/apps/website/client-wrapper/CreateLeagueWizard.tsx index 7ef2b437d..d8d79f7d9 100644 --- a/apps/website/client-wrapper/CreateLeagueWizard.tsx +++ b/apps/website/client-wrapper/CreateLeagueWizard.tsx @@ -12,6 +12,7 @@ import type { Weekday } from '@/lib/types/Weekday'; import type { WizardErrors } from '@/lib/types/WizardErrors'; import type { LeagueScoringPresetViewModel } from '@/lib/view-models/LeagueScoringPresetViewModel'; import { CreateLeagueWizardTemplate, Step } from '@/templates/CreateLeagueWizardTemplate'; +import { SearchParamBuilder } from '@/lib/routing/search-params/SearchParamBuilder'; import { Award, Calendar, @@ -91,7 +92,7 @@ type LeagueWizardFormModel = LeagueConfigFormModel & { interface CreateLeagueWizardProps { stepName: StepName; - onStepChange: (stepName: StepName) => void; + onStepChange?: (stepName: StepName) => void; } function stepNameToStep(stepName: StepName): Step { @@ -200,6 +201,16 @@ export function CreateLeagueWizard({ stepName, onStepChange }: CreateLeagueWizar const router = useRouter(); const { session } = useAuth(); + const handleStepChange = useCallback((newStepName: StepName) => { + if (onStepChange) { + onStepChange(newStepName); + } else { + const builder = new SearchParamBuilder(); + builder.step(newStepName); + router.push(`/leagues/create${builder.build()}`); + } + }, [onStepChange, router]); + const step = stepNameToStep(stepName); const [loading, setLoading] = useState(false); const [presetsLoading, setPresetsLoading] = useState(true); @@ -330,19 +341,19 @@ export function CreateLeagueWizard({ stepName, onStepChange }: CreateLeagueWizar const nextStep = (step < 7 ? ((step + 1) as Step) : step); saveHighestStep(nextStep); setHighestCompletedStep((prev) => Math.max(prev, nextStep)); - onStepChange(stepToStepName(nextStep)); + handleStepChange(stepToStepName(nextStep)); }; const goToPreviousStep = () => { const prevStep = (step > 1 ? ((step - 1) as Step) : step); - onStepChange(stepToStepName(prevStep)); + handleStepChange(stepToStepName(prevStep)); }; const goToStep = useCallback((targetStep: Step) => { if (targetStep <= highestCompletedStep) { - onStepChange(stepToStepName(targetStep)); + handleStepChange(stepToStepName(targetStep)); } - }, [highestCompletedStep, onStepChange]); + }, [highestCompletedStep, handleStepChange]); const handleSubmit = async (event: FormEvent) => { event.preventDefault(); @@ -413,7 +424,7 @@ export function CreateLeagueWizard({ stepName, onStepChange }: CreateLeagueWizar })); if (LeagueWizardCommandModel.hasWizardErrors(allErrors)) { - onStepChange('basics'); + handleStepChange('basics'); return; } diff --git a/apps/website/client-wrapper/CreateTeamPageClient.tsx b/apps/website/client-wrapper/CreateTeamPageClient.tsx new file mode 100644 index 000000000..5b2e6c042 --- /dev/null +++ b/apps/website/client-wrapper/CreateTeamPageClient.tsx @@ -0,0 +1,42 @@ +'use client'; + +import { CreateTeamForm } from '@/components/teams/CreateTeamForm'; +import { routes } from '@/lib/routing/RouteConfig'; +import { Container } from '@/ui/Container'; +import { Heading } from '@/ui/Heading'; +import { Stack } from '@/ui/Stack'; +import { Section } from '@/ui/Section'; +import { useRouter } from 'next/navigation'; + +/** + * CreateTeamPageClient + * + * Client wrapper for the Create Team page. + */ +export function CreateTeamPageClient() { + const router = useRouter(); + + const handleNavigate = (teamId: string) => { + router.push(routes.team.detail(teamId)); + }; + + const handleCancel = () => { + router.back(); + }; + + return ( +
+ + + + Create a Team + + + + +
+ ); +} diff --git a/apps/website/client-wrapper/DashboardPageClient.tsx b/apps/website/client-wrapper/DashboardPageClient.tsx new file mode 100644 index 000000000..6cc4ea8bb --- /dev/null +++ b/apps/website/client-wrapper/DashboardPageClient.tsx @@ -0,0 +1,26 @@ +'use client'; + +import { DashboardTemplate } from '@/templates/DashboardTemplate'; +import type { DashboardViewData } from '@/lib/view-data/DashboardViewData'; +import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts'; +import { useRouter } from 'next/navigation'; +import { routes } from '@/lib/routing/RouteConfig'; + +/** + * DashboardPageClient + * + * Client wrapper for the Dashboard page. + * Manages client-side interactions for the dashboard. + */ +export function DashboardPageClient({ viewData }: ClientWrapperProps) { + const router = useRouter(); + + const handleNavigateToRaces = () => router.push(routes.public.races); + + return ( + + ); +} diff --git a/apps/website/client-wrapper/HomePageClient.tsx b/apps/website/client-wrapper/HomePageClient.tsx new file mode 100644 index 000000000..2e0eccf79 --- /dev/null +++ b/apps/website/client-wrapper/HomePageClient.tsx @@ -0,0 +1,20 @@ +'use client'; + +import { HomeTemplate, type HomeViewData } from '@/templates/HomeTemplate'; +import { PageWrapper } from '@/components/shared/state/PageWrapper'; + +interface HomePageClientProps { + viewData: HomeViewData; +} + +/** + * HomePageClient - Client wrapper for the Home page. + * Manages state and handlers for the home page. + */ +export function HomePageClient({ viewData }: HomePageClientProps) { + const Template = ({ viewData }: { viewData: HomeViewData }) => ( + + ); + + return ; +} diff --git a/apps/website/components/AppWrapper.tsx b/apps/website/components/AppWrapper.tsx index 7a165b486..0c75834f4 100644 --- a/apps/website/components/AppWrapper.tsx +++ b/apps/website/components/AppWrapper.tsx @@ -8,7 +8,7 @@ import { NotificationProvider } from '@/components/notifications/NotificationPro import { NotificationIntegration } from '@/components/errors/NotificationIntegration'; import { EnhancedErrorBoundary } from '@/components/errors/EnhancedErrorBoundary'; import { DevToolbar } from '@/components/dev/DevToolbar'; -import { ThemeProvider } from '@/ui/theme/ThemeProvider'; +import { ThemeProvider } from '@/components/shared/ThemeProvider'; import React from 'react'; interface AppWrapperProps { diff --git a/apps/website/components/admin/AdminHeaderPanel.tsx b/apps/website/components/admin/AdminHeaderPanel.tsx index cde7c0fa8..f544a7bdb 100644 --- a/apps/website/components/admin/AdminHeaderPanel.tsx +++ b/apps/website/components/admin/AdminHeaderPanel.tsx @@ -1,6 +1,6 @@ 'use client'; -import { ProgressLine } from '@/ui/ProgressLine'; +import { ProgressLine } from '@/components/shared/ProgressLine'; import { SectionHeader } from '@/ui/SectionHeader'; import React from 'react'; diff --git a/apps/website/components/app/AppFooter.tsx b/apps/website/components/app/AppFooter.tsx index da2fddd42..e8fcb060e 100644 --- a/apps/website/components/app/AppFooter.tsx +++ b/apps/website/components/app/AppFooter.tsx @@ -1,5 +1,12 @@ import React from 'react'; -import { Footer } from '@/ui/Footer'; +import { Container } from '@/ui/Container'; +import { Grid } from '@/ui/Grid'; +import { Stack } from '@/ui/Stack'; +import { Text } from '@/ui/Text'; +import { Link } from '@/ui/Link'; +import { Surface } from '@/ui/Surface'; +import { BrandMark } from '@/ui/BrandMark'; +import { Box } from '@/ui/Box'; interface AppFooterProps { children?: React.ReactNode; @@ -7,9 +14,76 @@ interface AppFooterProps { /** * AppFooter is the bottom section of the application. + * It follows the "Telemetry Workspace" structure: matte surface, thin separators, minimal. + * Aligned with ControlBar (Header) design. */ export function AppFooter({ children }: AppFooterProps) { return ( -