From 2da1b150ed160d726f1ba8d2b488160dd10d72d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Jurmanovi=C4=87?= Date: Sun, 1 Mar 2026 14:28:31 +0100 Subject: [PATCH] Move leaderboard fetching to server-side MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create src/lib/api.ts with fetchLeaderboard function for server use - Update page.tsx to async component that fetches data at build/request time - Pass fetched data to ClientPage as initialAppData prop - Remove client-side useEffect and state management from ClientPage - Reduce client bundle size (4.07kb → 2.99kb) - Improve security by keeping API credentials server-only --- src/app/ClientPage.tsx | 39 +++++---------------------------------- src/app/page.tsx | 9 +++++++-- src/lib/api.ts | 28 ++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 36 deletions(-) create mode 100644 src/lib/api.ts diff --git a/src/app/ClientPage.tsx b/src/app/ClientPage.tsx index ab637be..0d1cbf6 100644 --- a/src/app/ClientPage.tsx +++ b/src/app/ClientPage.tsx @@ -1,9 +1,8 @@ 'use client'; -import { useState, useEffect, useMemo } from 'react'; +import { 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'; @@ -11,40 +10,12 @@ 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; - } +interface ClientPageProps { + initialAppData: AppData; } -export default function ClientPage() { - const [appData, setAppData] = useState(defaultAppData); - - useEffect(() => { - const loadData = async () => { - const savedData = await fetchLeaderboard(); - if (savedData) { - setAppData(savedData); - } - }; - loadData(); - }, []); +export default function ClientPage({ initialAppData }: ClientPageProps) { + const appData = initialAppData; const standingsData = useMemo(() => calculateStandingsData(appData), [appData]); diff --git a/src/app/page.tsx b/src/app/page.tsx index becd00d..bdaba21 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,5 +1,10 @@ +import { fetchLeaderboard } from '@/lib/api'; +import { defaultAppData } from '@/lib/defaultData'; import ClientPage from './ClientPage'; -export default function Page() { - return ; +export default async function Page() { + const leaderboardData = await fetchLeaderboard(); + const initialAppData = leaderboardData || defaultAppData; + + return ; } diff --git a/src/lib/api.ts b/src/lib/api.ts new file mode 100644 index 0000000..ac973aa --- /dev/null +++ b/src/lib/api.ts @@ -0,0 +1,28 @@ +import { AppData } from './types'; + +export async function fetchLeaderboard(): Promise { + 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 || ''; + + if (!API_BASE || !SERVER_ID || !ACCESS_TOKEN) { + console.warn('Missing API configuration'); + return null; + } + + 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; + } +}