feat(places): place page client and view component
One component for all four families: they differ in what fills the registry, not in what the page shows, so a second would be a second place to forget the same change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015mWQnpye9F299NVRCCSRvj
This commit is contained in:
1 parent
42138fc402
commit
de5e790112
4 files changed
+346
No files matched your search
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Client for the places API.
|
||||
*
|
||||
* Two namespaces, matching the backend: towns and localities share
|
||||
* /schools/[place]; authorities take /schools/authority/[la]. 67 town names
|
||||
* collide with an authority name and neither set contains the other, so one
|
||||
* namespace would publish near-duplicate pages.
|
||||
*/
|
||||
import type { School } from '@/lib/types';
|
||||
|
||||
export interface PlaceSummary {
|
||||
kind: string;
|
||||
slug: string;
|
||||
name: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface PlaceDetail {
|
||||
place: PlaceSummary & { parent_authority: string | null };
|
||||
schools: School[];
|
||||
averages: {
|
||||
rwm_expected_pct: number | null;
|
||||
attainment_8_score: number | null;
|
||||
};
|
||||
}
|
||||
|
||||
export function placeUrl(kind: string, slug: string, phase?: string): string {
|
||||
const base =
|
||||
kind === 'authority' ? `/schools/authority/${slug}`
|
||||
: kind === 'outcode' ? `/schools/near/${slug}`
|
||||
: `/schools/${slug}`;
|
||||
return phase ? `${base}/${phase}` : base;
|
||||
}
|
||||
|
||||
/** An authority name as it appears in a URL. */
|
||||
export function authoritySlug(name: string): string {
|
||||
return name.toLowerCase().trim().replace(/[^\w\s-]/g, '').replace(/\s+/g, '-');
|
||||
}
|
||||
|
||||
const API = process.env.FASTAPI_URL || process.env.NEXT_PUBLIC_API_URL
|
||||
|| 'http://localhost:8000/api';
|
||||
|
||||
export async function fetchPlaces(): Promise<PlaceSummary[]> {
|
||||
const res = await fetch(`${API}/places`, { next: { revalidate: 604800 } });
|
||||
if (!res.ok) return [];
|
||||
return (await res.json()).places ?? [];
|
||||
}
|
||||
|
||||
export async function fetchPlace(
|
||||
kind: string, slug: string, phase?: string,
|
||||
): Promise<PlaceDetail | null> {
|
||||
const q = phase ? `?phase=${encodeURIComponent(phase)}` : '';
|
||||
const res = await fetch(`${API}/places/${kind}/${slug}${q}`,
|
||||
{ next: { revalidate: 604800 } });
|
||||
if (!res.ok) return null;
|
||||
return res.json();
|
||||
}
|
||||
Reference in new issue
Block a user