From a096075d8cb16027af16847bc2d40af27b6d360b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Jurmanovi=C4=87?= Date: Sun, 1 Mar 2026 14:26:15 +0100 Subject: [PATCH] Extract client logic to separate ClientPage component - Create ClientPage.tsx with 'use client' and all stateful logic - Simplify page.tsx to just import and render ClientPage - Allows easy switching between client/server rendering strategies - Maintains same functionality with cleaner separation of concerns --- src/app/ClientPage.tsx | 84 +++++++++++++++++++++++++++++++++++++++++ src/app/page.tsx | 85 ++---------------------------------------- 2 files changed, 87 insertions(+), 82 deletions(-) create mode 100644 src/app/ClientPage.tsx diff --git a/src/app/ClientPage.tsx b/src/app/ClientPage.tsx new file mode 100644 index 0000000..ab637be --- /dev/null +++ b/src/app/ClientPage.tsx @@ -0,0 +1,84 @@ +'use client'; + +import { useState, useEffect, useMemo } from 'react'; +import dynamic from 'next/dynamic'; +import { AppData } from '@/lib/types'; +import { defaultAppData } from '@/lib/defaultData'; +import { calculateStandingsData } from '@/lib/leaderboard'; +import RaceResultsTable from './components/RaceResultsTable'; +import DriverStandings from './components/DriverStandings'; +import PointsKey from './components/PointsKey'; + +const CumulativeChart = dynamic(() => import('./components/CumulativeChart'), { ssr: false }); + +const API_BASE = process.env.NEXT_PUBLIC_API_BASE || ''; +const SERVER_ID = process.env.NEXT_PUBLIC_SERVER_ID || ''; +const ACCESS_TOKEN = process.env.NEXT_PUBLIC_ACCESS_TOKEN || ''; + +async function fetchLeaderboard(): Promise { + try { + const res = await fetch(`${API_BASE}/server/${SERVER_ID}/leaderboard`, { + headers: { + "Access-Key": ACCESS_TOKEN + } + }); + if (!res.ok) return null; + const data = await res.json(); + // Treat empty leaderboard (no drivers) as null to fall back to defaults + if (!data || !data.drivers || data.drivers.length === 0) return null; + return data; + } catch (e) { + console.error("Failed to load leaderboard from API:", e); + return null; + } +} + +export default function ClientPage() { + const [appData, setAppData] = useState(defaultAppData); + + useEffect(() => { + const loadData = async () => { + const savedData = await fetchLeaderboard(); + if (savedData) { + setAppData(savedData); + } + }; + loadData(); + }, []); + + const standingsData = useMemo(() => calculateStandingsData(appData), [appData]); + + return ( +
+
+

+ Grid Gladiators League Leaderboard +

+
+ +
+
+

Race Results

+ +
+ +
+
+

Driver Standings

+ +
+ +
+

Points Table:

+ +
+
+
+ +
+

Cumulative Points per Race

+ +
+
+ ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 4c1b1be..becd00d 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,84 +1,5 @@ -'use client'; +import ClientPage from './ClientPage'; -import { useState, useEffect, useMemo } from 'react'; -import dynamic from 'next/dynamic'; -import { AppData } from '@/lib/types'; -import { defaultAppData } from '@/lib/defaultData'; -import { calculateStandingsData } from '@/lib/leaderboard'; -import RaceResultsTable from './components/RaceResultsTable'; -import DriverStandings from './components/DriverStandings'; -import PointsKey from './components/PointsKey'; - -const CumulativeChart = dynamic(() => import('./components/CumulativeChart'), { ssr: false }); - -const API_BASE = process.env.NEXT_PUBLIC_API_BASE || ''; -const SERVER_ID = process.env.NEXT_PUBLIC_SERVER_ID || ''; -const ACCESS_TOKEN = process.env.NEXT_PUBLIC_ACCESS_TOKEN || ''; - -async function fetchLeaderboard(): Promise { - try { - const res = await fetch(`${API_BASE}/server/${SERVER_ID}/leaderboard`, { - headers: { - "Access-Key": ACCESS_TOKEN - } - }); - if (!res.ok) return null; - const data = await res.json(); - // Treat empty leaderboard (no drivers) as null to fall back to defaults - if (!data || !data.drivers || data.drivers.length === 0) return null; - return data; - } catch (e) { - console.error("Failed to load leaderboard from API:", e); - return null; - } -} - -export default function Home() { - const [appData, setAppData] = useState(defaultAppData); - - useEffect(() => { - const loadData = async () => { - const savedData = await fetchLeaderboard(); - if (savedData) { - setAppData(savedData); - } - }; - loadData(); - }, []); - - const standingsData = useMemo(() => calculateStandingsData(appData), [appData]); - - return ( -
-
-

- Grid Gladiators League Leaderboard -

-
- -
-
-

Race Results

- -
- -
-
-

Driver Standings

- -
- -
-

Points Table:

- -
-
-
- -
-

Cumulative Points per Race

- -
-
- ); +export default function Page() { + return ; }