Every generateStaticParams is gated behind PRERENDER_PLACES and wrapped in the same try/catch the school route uses. The plan claimed authority pages were 'few enough to always prebuild' — but few enough still means the API must be reachable at build time, and in CI it is not: the build failed with ECONNREFUSED rather than degrading to ISR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015mWQnpye9F299NVRCCSRvj
68 lines
2.5 KiB
TypeScript
68 lines
2.5 KiB
TypeScript
/**
|
|
* Local authority pages.
|
|
*
|
|
* A separate namespace from /schools/[place] because 67 town names collide
|
|
* with an authority name and neither set contains the other — Bedford the
|
|
* town holds 104 schools, Bedford the authority 86, because postal towns
|
|
* cross authority boundaries. The title says "Local Authority" so a reader
|
|
* landing on both knows which set each covers.
|
|
*/
|
|
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<{ la: string }> }
|
|
|
|
export const revalidate = 604800;
|
|
export const dynamicParams = true;
|
|
|
|
export async function generateStaticParams(): Promise<Array<{ la: string }>> {
|
|
// Gated like every other prerender in this app. There are only ~154
|
|
// authorities, but "few enough to always build" still means the API must be
|
|
// reachable at build time, and in CI it is not — the build fails with
|
|
// ECONNREFUSED rather than degrading. The catch is the same fallback the
|
|
// school route uses.
|
|
if (process.env.PRERENDER_PLACES !== '1') return [];
|
|
try {
|
|
return (await fetchPlaces())
|
|
.filter((p) => p.kind === 'authority')
|
|
.map((p) => ({ la: 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 { la } = await params;
|
|
const detail = await fetchPlace('authority', la);
|
|
if (!detail) return { title: 'Place Not Found' };
|
|
|
|
const { name, count } = detail.place;
|
|
return {
|
|
title: `Schools in ${name} — Local Authority | schoolcompare`,
|
|
description:
|
|
`All ${count} schools in the ${name} local authority, ranked by SATs and GCSE `
|
|
+ `results, with Ofsted grades and the authority average against England.`,
|
|
alternates: { canonical: absoluteUrl(`/schools/authority/${la}`) },
|
|
};
|
|
}
|
|
|
|
export default async function AuthorityPage({ params }: Props) {
|
|
const { la } = await params;
|
|
const detail = await fetchPlace('authority', la);
|
|
if (!detail) notFound();
|
|
|
|
const national = await fetchNationalAverages().catch(() => null);
|
|
return (
|
|
<PlaceView
|
|
detail={detail}
|
|
englandAverage={national?.primary?.rwm_expected_pct ?? null}
|
|
neighbours={[]}
|
|
/>
|
|
);
|
|
}
|