feat(ux): 8 UX improvements — simpler home, advanced filters, phase tabs, 4-line rows
All checks were successful
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 48s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 1m13s
Build and Push Docker Images / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 32s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 1s

1. Simpler home page: only search box on landing, no filter dropdowns
2. Advanced filters: hidden behind toggle on results page, auto-open if active
3. Per-school phase rendering: each row renders based on its own data
4. Taller 4-line rows with context line (type, age range, denomination, gender)
5. Result-scoped filters: dropdown values reflect current search results
6. Fix blank filter values: exclude empty strings and "Not applicable"
7. Rankings: Primary/Secondary phase tabs with phase-specific metrics
8. Compare: Primary/Secondary tabs with school counts and phase metrics

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-29 08:57:06 +01:00
parent e8175561d5
commit 1d22877aec
14 changed files with 735 additions and 408 deletions

View File

@@ -1,10 +1,11 @@
/**
* SchoolRow Component
* Three-line row for school search results
* Four-line row for primary school search results
*
* Line 1: School name · School type
* Line 2: R,W&M % · Progress score · Pupil count
* Line 3: Local authority · Distance
* Line 1: School name · Ofsted badge
* Line 2: School type · Age range · Denomination · Gender
* Line 3: R,W&M % · Progress score · Pupil count
* Line 4: Local authority · Distance
*/
import type { School } from '@/lib/types';
@@ -48,28 +49,43 @@ export function SchoolRow({
}
};
const showGender = school.gender && school.gender.toLowerCase() !== 'mixed';
const showDenomination =
school.religious_denomination &&
school.religious_denomination !== 'Does not apply';
return (
<div className={`${styles.row} ${isInCompare ? styles.rowInCompare : ''}`}>
{/* Left: three content lines */}
{/* Left: four content lines */}
<div className={styles.rowContent}>
{/* Line 1: School name + type + Ofsted badge */}
{/* Line 1: School name + Ofsted badge */}
<div className={styles.line1}>
<a href={`/school/${school.urn}`} className={styles.schoolName}>
{school.school_name}
</a>
{school.school_type && (
<span className={styles.schoolType}>{school.school_type}</span>
)}
{school.ofsted_grade && (
<span className={`${styles.ofstedBadge} ${styles[`ofsted${school.ofsted_grade}`]}`}>
{OFSTED_LABELS[school.ofsted_grade]}
{school.ofsted_date && (
<span className={styles.ofstedDate}>
{' '}({new Date(school.ofsted_date).getFullYear()})
</span>
)}
</span>
)}
</div>
{/* Line 2: Key stats */}
{/* Line 2: Context tags */}
<div className={styles.line2}>
{school.school_type && <span>{school.school_type}</span>}
{school.age_range && <span>{school.age_range}</span>}
{showDenomination && <span>{school.religious_denomination}</span>}
{showGender && <span>{school.gender}</span>}
</div>
{/* Line 3: Key stats */}
<div className={styles.line3}>
{school.rwm_expected_pct != null ? (
<span className={styles.stat}>
<strong className={styles.statValue}>
@@ -123,8 +139,8 @@ export function SchoolRow({
)}
</div>
{/* Line 3: Location + distance */}
<div className={styles.line3}>
{/* Line 4: Location + distance */}
<div className={styles.line4}>
{school.local_authority && (
<span>{school.local_authority}</span>
)}
@@ -133,11 +149,6 @@ export function SchoolRow({
{school.distance.toFixed(1)} mi
</span>
)}
{!isLocationSearch &&
school.religious_denomination &&
school.religious_denomination !== 'Does not apply' && (
<span>{school.religious_denomination}</span>
)}
</div>
</div>