/** * NearbySchoolsSection — the nearest eligible schools, closest first. Server * component; only the carousel, the compare bar and the add-to-compare button * are client-side. * * "Other schools nearby", not "similar" ones: the order is distance and only * distance. The hard filters upstream still guarantee the set is comparable — * same phase, same selectivity, mainstream never beside special — but nothing * here ranks by how alike two schools are, so the heading does not say it does. * * The chips report what a school shares, and may be absent entirely. That is * information for the reader to weigh, not a verdict this section has already * reached on their behalf. * * There is deliberately no "how these are chosen" panel: the method is already * visible in the chips and the distances. The single caption line is not a * method note — it is the one thing a card cannot self-correct. * * Nor is there a lede: "Other primary schools near X" only restated the heading. */ import Link from 'next/link'; import type { NearbySchool } from '@/lib/types'; import { schoolUrl } from '@/lib/utils'; import { AddToCompareButton } from './AddToCompareButton'; import { NearbySchoolsCarousel } from './NearbySchoolsCarousel'; import { NearbySchoolsCompareBar } from './NearbySchoolsCompareBar'; import { Section } from './sectionShared'; import styles from './NearbySchools.module.css'; const MINIMUM = 2; export function shouldRenderNearby(nearby?: NearbySchool[] | null): boolean { return (nearby?.length ?? 0) >= MINIMUM; } function metricLabel(key: string): string { return key === 'attainment_8_score' ? 'Attainment 8' : 'Reading, writing & maths'; } function formatMetric(value: number | null, key: string): string { if (value == null) return 'Not published'; return key === 'attainment_8_score' ? value.toFixed(1) : `${Math.round(value)}%`; } export function NearbySchoolsSection({ urn, thisMetricValue, nearby, }: { urn: number; thisMetricValue: number | null; nearby?: NearbySchool[] | null; }) { if (!shouldRenderNearby(nearby)) return null; const schools = nearby as NearbySchool[]; const metricKey = schools[0].metric_key; return (
Other schools nearby } > {schools.map((school) => (
  • {school.distance_miles} miles away

    {school.school_name}

    {[school.school_type, school.age_range && `Ages ${school.age_range}`] .filter(Boolean) .join(' · ')}

    {school.shared.length > 0 && (
      {school.shared.map((label) => (
    • {label}
    • ))}
    )}

    {formatMetric(school.metric_value, school.metric_key)}

    {metricLabel(school.metric_key)}

    {thisMetricValue != null && (

    {formatMetric(thisMetricValue, metricKey)} at this school

    )}
  • ))}
    {/* The one caveat the cards cannot make on their own: a reader who takes "0.6 miles away" for the walk has been misled, and nothing else here corrects that. */}

    Distances are straight-line from this school, not road distance.

    ); }