refactor(app): move site routes into a (frontend) route group
Payload's admin panel ships its own root layout rendering html/body. Next allows multiple root layouts only when no app/layout.tsx exists, so the site's routes move into their own group. Route groups are invisible to routing: every public URL is unchanged, verified against the build's route table. The metadata file conventions deliberately stay at the app/ root. Moving them into the group renamed /icon.png to /icon-4usi79.png (likewise apple-icon and opengraph-image) and dropped /robots.txt altogether, which would have broken the /icon.png cache-control rule, the outputFileTracingIncludes entry for the share card, and robots.txt. darkThemeSafety reads app/globals.css off disk rather than importing it, so it needed its own path fix — a grep for import specifiers misses it, and it fails as an unrunnable suite rather than a failed assertion. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017YmbBhr8s7GusjDE12hrZM
This commit is contained in:
1 parent
eb648f3f76
commit
2437ffce42
21 files changed
+57
-26
No files matched your search
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Postcode district pages.
|
||||
*
|
||||
* No phase variants: nobody searches "primary schools in SW11", so the
|
||||
* variants would be pages without demand. These exist to catch
|
||||
* "schools near <postcode>" and to give London districts a geographic page
|
||||
* where the GIAS town field cannot.
|
||||
*/
|
||||
import { notFound } from 'next/navigation';
|
||||
import type { Metadata } from 'next';
|
||||
import { fetchPlace, fetchPlaces } from '@/lib/places';
|
||||
import { fetchNationalAverages } from '@/lib/api';
|
||||
import { PlaceView } from '@/components/places/PlaceView';
|
||||
import { absoluteUrl } from '@/lib/site';
|
||||
|
||||
interface Props { params: Promise<{ outcode: string }> }
|
||||
|
||||
export const revalidate = 604800;
|
||||
export const dynamicParams = true;
|
||||
|
||||
export async function generateStaticParams(): Promise<Array<{ outcode: string }>> {
|
||||
// 1,760 of these; same CI budget argument as the town routes.
|
||||
if (process.env.PRERENDER_PLACES !== '1') return [];
|
||||
try {
|
||||
return (await fetchPlaces())
|
||||
.filter((p) => p.kind === 'outcode')
|
||||
.map((p) => ({ outcode: p.slug }));
|
||||
} catch (error) {
|
||||
console.warn('generateStaticParams: API unreachable, falling back to on-demand ISR.', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
const { outcode } = await params;
|
||||
const detail = await fetchPlace('outcode', outcode);
|
||||
if (!detail) return { title: 'Place Not Found' };
|
||||
|
||||
const { name, count } = detail.place;
|
||||
return {
|
||||
title: { absolute: `Schools near ${name} | schoolcompare` },
|
||||
description:
|
||||
`${count} schools in the ${name} postcode district, with results, Ofsted grades `
|
||||
+ `and how close you had to live to get a place.`,
|
||||
alternates: { canonical: absoluteUrl(`/schools/near/${outcode}`) },
|
||||
};
|
||||
}
|
||||
|
||||
export default async function OutcodePage({ params }: Props) {
|
||||
const { outcode } = await params;
|
||||
const detail = await fetchPlace('outcode', outcode);
|
||||
if (!detail) notFound();
|
||||
|
||||
const national = await fetchNationalAverages().catch(() => null);
|
||||
return (
|
||||
<PlaceView
|
||||
detail={detail}
|
||||
englandAverage={national?.primary?.rwm_expected_pct ?? null}
|
||||
neighbours={[]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user