From 50717d1bafe29ebdad31b325098562a9e100b349 Mon Sep 17 00:00:00 2001 From: ryan Date: Sun, 12 Jul 2026 18:07:43 +0800 Subject: [PATCH] fix(frontend): support static export dynamic routes compliance via client useParams --- .../(main)/websites/[zoneId]/page-client.tsx | 21 +++++++++++++++---- .../app/(main)/websites/[zoneId]/page.tsx | 21 ++++++------------- frontend/tests/zone/zone-page.test.tsx | 8 +++++-- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/frontend/app/(main)/websites/[zoneId]/page-client.tsx b/frontend/app/(main)/websites/[zoneId]/page-client.tsx index 726d1171..20d92caa 100644 --- a/frontend/app/(main)/websites/[zoneId]/page-client.tsx +++ b/frontend/app/(main)/websites/[zoneId]/page-client.tsx @@ -1,7 +1,7 @@ 'use client'; -import {usePathname, useRouter, useSearchParams} from 'next/navigation'; -import {useCallback, useMemo, useState} from 'react'; +import {useParams, usePathname, useRouter, useSearchParams} from 'next/navigation'; +import {useCallback, useEffect, useMemo, useState} from 'react'; import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'; import {ArrowLeft, Globe, Pencil, Trash2} from 'lucide-react'; import {toast} from 'sonner'; @@ -40,7 +40,14 @@ function getZonePageTab(value: string | null | undefined): ZonePageTab { return zoneTabs.includes(value as ZonePageTab) ? (value as ZonePageTab) : 'overview'; } -export function ZonePageClient({zoneId}: {zoneId: number}) { +export function ZonePageClient() { + const params = useParams(); + const zoneId = Number(params?.zoneId); + + const [mounted, setMounted] = useState(false); + useEffect(() => { + setMounted(true); + }, []); const router = useRouter(); const pathname = usePathname(); @@ -96,7 +103,13 @@ export function ZonePageClient({zoneId}: {zoneId: number}) { onError: (error) => toast.error(getErrorMessage(error)), }); - + if (!mounted) { + return ( +
+ +
+ ); + } if (!Number.isInteger(zoneId) || zoneId <= 0) { return ( diff --git a/frontend/app/(main)/websites/[zoneId]/page.tsx b/frontend/app/(main)/websites/[zoneId]/page.tsx index 9b0e26e2..da807a05 100644 --- a/frontend/app/(main)/websites/[zoneId]/page.tsx +++ b/frontend/app/(main)/websites/[zoneId]/page.tsx @@ -1,29 +1,20 @@ import {Suspense} from 'react' import {Globe} from 'lucide-react' -import dynamic from 'next/dynamic' import {LoadingStateWithBorder} from '@/components/layout/loading' +import {ZonePageClient} from './page-client' -const ZonePageClient = dynamic( - () => import('./page-client').then((mod) => mod.ZonePageClient), - { - ssr: false, - loading: () => ( -
- -
- ), - } -) +export async function generateStaticParams() { + return [{zoneId: '1'}]; +} -export default async function ZonePage({params}: PageProps<'/websites/[zoneId]'>) { - const {zoneId} = await params +export default async function ZonePage() { return ( }> - + ) } diff --git a/frontend/tests/zone/zone-page.test.tsx b/frontend/tests/zone/zone-page.test.tsx index 37fee031..0c6528ce 100644 --- a/frontend/tests/zone/zone-page.test.tsx +++ b/frontend/tests/zone/zone-page.test.tsx @@ -13,6 +13,8 @@ class ResizeObserverMock { vi.stubGlobal('ResizeObserver', ResizeObserverMock); +let mockZoneId = '42'; + const replaceMock = vi.fn(); vi.mock('next/link', () => ({ @@ -23,8 +25,9 @@ vi.mock('next/link', () => ({ vi.mock('next/navigation', () => ({ useRouter: () => ({replace: replaceMock, back: vi.fn()}), - usePathname: () => '/websites/42', + usePathname: () => `/websites/${mockZoneId}`, useSearchParams: () => new URLSearchParams(), + useParams: () => ({zoneId: mockZoneId}), })); vi.mock('@/lib/services/openflare', async (importOriginal) => { @@ -47,6 +50,7 @@ vi.mock('@/lib/services/openflare', async (importOriginal) => { }); function renderPage(zoneId: number) { + mockZoneId = String(zoneId); const client = new QueryClient({ defaultOptions: { queries: {retry: false, gcTime: 0}, @@ -54,7 +58,7 @@ function renderPage(zoneId: number) { }); return render( - + , ); }