51 lines
1.9 KiB
TypeScript
51 lines
1.9 KiB
TypeScript
import Link from 'next/link';
|
|||
|
|
import type { SchoolPlace } from '@/lib/jsonld';
|
||
|
|
import styles from './NearbyPlaces.module.css';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Links from a school page into the location layer.
|
||
|
|
*
|
||
|
|
* This exists for a structural reason rather than a decorative one. Before
|
||
|
|
* it, the only anchor on a school page pointed at the school's own website,
|
||
|
|
* so the ~27k pages that carry most of the site's inbound authority passed it
|
||
|
|
* straight off-site and none of it reached the place pages. These links are
|
||
|
|
* what circulate it instead.
|
||
|
|
*
|
||
|
|
* Every entry comes from the place registry via the API, so a link is only
|
||
|
|
* ever offered for a page that exists: a place below the publish threshold is
|
||
|
|
* absent from the registry and therefore absent here.
|
||
|
|
*/
|
||
|
|
|
||
|
|
/** Narrowest first: a reader on a school page wants its town before its
|
||
|
|
* county. The API orders widest-first because that is what the breadcrumb
|
||
|
|
* reads, so the two orders are deliberately different. */
|
||
|
|
const ORDER: Record<string, number> = {
|
||
|
|
town: 0, locality: 0, outcode: 1, authority: 2,
|
||
|
|
};
|
||
|
|
|
||
|
|
function label(place: SchoolPlace): string {
|
||
|
|
const noun = place.count === 1 ? 'school' : 'schools';
|
||
|
|
const preposition = place.kind === 'outcode' ? 'near' : 'in';
|
||
|
|
return `${place.count} ${noun} ${preposition} ${place.name}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function NearbyPlaces({ places }: { places: SchoolPlace[] }) {
|
||
|
|
if (places.length === 0) return null;
|
||
|
|
|
||
|
|
const sorted = [...places].sort(
|
||
|
|
(a, b) => (ORDER[a.kind] ?? 9) - (ORDER[b.kind] ?? 9),
|
||
|
|
);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<section className={styles.section} aria-labelledby="nearby-places">
|
||
|
|
<h2 id="nearby-places" className={styles.heading}>More schools near here</h2>
|
||
|
|
<ul className={styles.list}>
|
||
|
|
{sorted.map((place) => (
|
||
|
|
<li key={`${place.kind}:${place.slug}`}>
|
||
|
|
<Link href={place.url} className={styles.link}>{label(place)}</Link>
|
||
|
|
</li>
|
||
|
|
))}
|
||
|
|
</ul>
|
||
|
|
</section>
|
||
|
|
);
|
||
|
|
}
|