diff --git a/app/[[...slug]]/page.tsx b/app/[[...slug]]/page.tsx new file mode 100644 index 0000000..7505816 --- /dev/null +++ b/app/[[...slug]]/page.tsx @@ -0,0 +1,50 @@ +import { notFound } from 'next/navigation'; +import type { SectionRecord } from '$entities/Section'; +import { getCollection } from '$shared/api'; +import { SectionFactory } from '$widgets/SectionFactory'; +import { SectionsAccordion } from '$widgets/SectionsAccordion'; + +/** + * Optional catchall: `/` → first section, `/:slug` → that section. + */ +export async function generateStaticParams() { + const { items: sections } = await getCollection('sections', { + sort: 'order', + }); + return [{}, ...sections.map((s) => ({ slug: [s.slug] }))]; +} + +type Props = { + params: Promise<{ slug?: string[] }>; +}; + +/** + * Portfolio page — one route per section, sections list always visible. + */ +export default async function SectionPage({ params }: Props) { + const { slug } = await params; + + const { items: sections } = await getCollection('sections', { + sort: 'order', + }); + + if (sections.length === 0) { + notFound(); + } + + const activeSlug = slug?.[0] ?? sections[0].slug; + + if (!sections.some((s) => s.slug === activeSlug)) { + notFound(); + } + + return ( +
+ + {sections.map((s) => ( + + ))} + +
+ ); +} diff --git a/app/api/collections/[collection]/records/route.ts b/app/api/collections/[collection]/records/route.ts index 8fb18ed..2c24ab6 100644 --- a/app/api/collections/[collection]/records/route.ts +++ b/app/api/collections/[collection]/records/route.ts @@ -1,6 +1,6 @@ import { NextResponse } from 'next/server'; -export const dynamic = 'force-dynamic'; +export const dynamic = 'force-static'; const base = { created: '', updated: '' }; @@ -241,6 +241,10 @@ const FIXTURES: Record = { ], }; +export function generateStaticParams() { + return Object.keys(FIXTURES).map((collection) => ({ collection })); +} + /** * Mock API route handler for PocketBase collection records. * Returns fixture data shaped as a PocketBase list response. diff --git a/next.config.ts b/next.config.ts index 31d6a9f..9bbdb8d 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,10 +1,11 @@ import type { NextConfig } from 'next'; -const isExport = process.env.NODE_ENV === 'production'; +/* output: 'export' is opt-in via STATIC_EXPORT=true. + * Set this in CI/deploy — not locally — so the mock API route works + * during development and local builds. */ +const isExport = process.env.STATIC_EXPORT === 'true'; const nextConfig: NextConfig = { - /* output: 'export' only applies at build time — enabling it in dev mode - * breaks route handlers (incompatible with force-dynamic in Next.js 16) */ ...(isExport ? { output: 'export' } : {}), images: { unoptimized: true }, }; diff --git a/package.json b/package.json index 70adbe8..8ec288a 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "scripts": { "dev": "next dev", "build": "next build", + "export": "STATIC_EXPORT=true next build", "start": "next start", "lint": "biome lint --write .", "format": "biome format --write .",