feat(search): print the school page's type and flags in search rows

Both rows now take their type label and tags from lib/schoolFacts, so a
parent reads the same words in the list and on the page: "State school"
instead of "Academy converter", "Girls' school" instead of "Girls", the
religious character instead of "Faith priority", and a Nursery class flag
on primaries. Who-can-apply tags are outlined with an inset ring, which
keeps them the size of their neighbours; the Selective tag no longer
borrows the status colour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
TudorandClaude Opus 5.5 committed 2026-10-02 22:59:45 +01:00
1 parent ca47d08186
commit ad9d3b67f7
6 files changed
+88 -42

No files matched your search

+13 -29
View File
@@ -3,7 +3,7 @@
* Four-line row for secondary school search results
*
* Line 1: School name · Ofsted badge
* Line 2: School type · Age range · Gender · Sixth form · Admissions tag
* Line 2: Phase · Type · Age range · the school page's flags (lib/schoolFacts)
* Line 3: Attainment 8 (large) · ±LA avg delta · Pupils
* Line 4: LA name · distance
*/
@@ -11,22 +11,10 @@
'use client';
import type { School } from '@/lib/types';
import { buildOfstedListBadge, getPhaseStyle, schoolUrl, formatAgeRange, hasReligiousCharacter, isProposedToClose, isSpecialSchool } from '@/lib/utils';
import { buildOfstedListBadge, getPhaseStyle, schoolUrl, formatAgeRange, isProposedToClose, isSpecialSchool } from '@/lib/utils';
import { schoolFlags, schoolTypeLabel } from '@/lib/schoolFacts';
import styles from './SecondarySchoolRow.module.css';
function detectAdmissionsTag(school: School): string | null {
// Exact match: "Non-selective" contains "selective", so a substring test
// tagged every comprehensive as Selective.
if (school.admissions_policy?.trim().toLowerCase() === 'selective') return 'Selective';
if (hasReligiousCharacter(school.religious_denomination)) return 'Faith priority';
return null;
}
function hasSixthForm(school: School): boolean {
// GIAS OfficialSixthForm flag; missing (pipeline not yet re-run) => false.
return school.has_sixth_form ?? false;
}
interface SecondarySchoolRowProps {
school: School;
isLocationSearch?: boolean;
@@ -64,9 +52,8 @@ export function SecondarySchoolRow({
? att8 - laAvgAttainment8
: null;
const admissionsTag = detectAdmissionsTag(school);
const sixthForm = hasSixthForm(school);
const showGender = school.gender && school.gender.toLowerCase() !== 'mixed';
const typeLabel = schoolTypeLabel(school);
const flags = schoolFlags(school);
return (
<div className={`${styles.row} ${phase.key ? styles[`phase${phase.key}`] : ''} ${isInCompare ? styles.rowInCompare : ''}`}>
@@ -90,19 +77,16 @@ export function SecondarySchoolRow({
{phase.label}
</span>
)}
{school.school_type && <span className={styles.attr}>{school.school_type}</span>}
{typeLabel && <span className={styles.attr}>{typeLabel}</span>}
{school.age_range && <span className={styles.attr}>{formatAgeRange(school.age_range)}</span>}
{showGender && (
<span className={styles.provisionTag}>{school.gender}</span>
)}
{sixthForm && (
<span className={styles.provisionTag}>Sixth form</span>
)}
{admissionsTag && (
<span className={`${styles.provisionTag} ${admissionsTag === 'Selective' ? styles.selectiveTag : ''}`}>
{admissionsTag}
{flags.map((flag) => (
<span
key={flag.label}
className={flag.kind === 'condition' ? styles.conditionTag : styles.provisionTag}
>
{flag.label}
</span>
)}
))}
{isProposedToClose(school) && (
<span className={`${styles.provisionTag} ${styles.closingTag}`}>⚠ Proposed to close</span>
)}