feat(list): redesign primary school card — single metric, vs-national delta, fix label
This commit is contained in:
@@ -2,30 +2,23 @@
|
||||
* SchoolRow Component
|
||||
* Four-line row for primary school search results
|
||||
*
|
||||
* Line 1: School name · Ofsted badge
|
||||
* Line 1: School name · Ofsted badge (framework-aware)
|
||||
* Line 2: School type · Age range · Denomination · Gender
|
||||
* Line 3: R,W&M % · Progress score · Pupil count
|
||||
* Line 3: Reading, Writing & Maths % · trend arrow · vs-national delta · Pupils
|
||||
* Line 4: Local authority · Distance
|
||||
*/
|
||||
|
||||
import type { School } from '@/lib/types';
|
||||
import { formatPercentage, formatProgress, calculateTrend, getPhaseStyle, schoolUrl } from '@/lib/utils';
|
||||
import { progressBand } from '@/lib/metrics';
|
||||
import { formatPercentage, calculateTrend, getPhaseStyle, schoolUrl, buildOfstedListBadge } from '@/lib/utils';
|
||||
import styles from './SchoolRow.module.css';
|
||||
|
||||
const OFSTED_LABELS: Record<number, string> = {
|
||||
1: 'Outstanding',
|
||||
2: 'Good',
|
||||
3: 'Req. Improvement',
|
||||
4: 'Inadequate',
|
||||
};
|
||||
|
||||
interface SchoolRowProps {
|
||||
school: School;
|
||||
isLocationSearch?: boolean;
|
||||
isInCompare?: boolean;
|
||||
onAddToCompare?: (school: School) => void;
|
||||
onRemoveFromCompare?: (urn: number) => void;
|
||||
nationalAvgRwm?: number | null;
|
||||
}
|
||||
|
||||
export function SchoolRow({
|
||||
@@ -34,13 +27,22 @@ export function SchoolRow({
|
||||
isInCompare = false,
|
||||
onAddToCompare,
|
||||
onRemoveFromCompare,
|
||||
nationalAvgRwm,
|
||||
}: SchoolRowProps) {
|
||||
const trend = calculateTrend(school.rwm_expected_pct, school.prev_rwm_expected_pct);
|
||||
const phase = getPhaseStyle(school.phase);
|
||||
const ofstedBadge = buildOfstedListBadge(school);
|
||||
|
||||
// Use reading progress as representative; fall back to writing, then maths
|
||||
const progressScore =
|
||||
school.reading_progress ?? school.writing_progress ?? school.maths_progress ?? null;
|
||||
const showGender = school.gender && school.gender.toLowerCase() !== 'mixed';
|
||||
const showDenomination =
|
||||
school.religious_denomination &&
|
||||
school.religious_denomination !== 'Does not apply';
|
||||
|
||||
// vs-national delta
|
||||
const rwmDelta =
|
||||
school.rwm_expected_pct != null && nationalAvgRwm != null
|
||||
? Math.round(school.rwm_expected_pct - nationalAvgRwm)
|
||||
: null;
|
||||
|
||||
const handleCompareClick = () => {
|
||||
if (isInCompare) {
|
||||
@@ -50,11 +52,6 @@ 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} ${phase.key ? styles[`phase${phase.key}`] : ''} ${isInCompare ? styles.rowInCompare : ''}`}>
|
||||
{/* Left: four content lines */}
|
||||
@@ -65,16 +62,9 @@ export function SchoolRow({
|
||||
<a href={schoolUrl(school.urn, school.school_name)} className={styles.schoolName}>
|
||||
{school.school_name}
|
||||
</a>
|
||||
{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 className={`${styles.ofstedBadge} ${styles[ofstedBadge.cssClass]}`}>
|
||||
{ofstedBadge.label}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Line 2: Context tags */}
|
||||
@@ -92,10 +82,9 @@ export function SchoolRow({
|
||||
|
||||
{/* Line 3: Key stats */}
|
||||
<div className={styles.line3}>
|
||||
{school.rwm_expected_pct != null ? (
|
||||
<span className={styles.stat}>
|
||||
<strong className={styles.statValue}>
|
||||
{formatPercentage(school.rwm_expected_pct, 0)}
|
||||
{school.rwm_expected_pct != null ? formatPercentage(school.rwm_expected_pct, 0) : '—'}
|
||||
</strong>
|
||||
{school.prev_rwm_expected_pct != null && (
|
||||
<span
|
||||
@@ -119,23 +108,25 @@ export function SchoolRow({
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
<span className={styles.statLabel}>R, W & M</span>
|
||||
</span>
|
||||
) : (
|
||||
<span className={styles.stat}>
|
||||
<strong className={styles.statValue}>—</strong>
|
||||
<span className={styles.statLabel}>R, W & M</span>
|
||||
<span className={styles.statLabel}>Reading, Writing & Maths</span>
|
||||
{rwmDelta != null && (
|
||||
<span
|
||||
className={
|
||||
rwmDelta >= 2
|
||||
? styles.vsNational
|
||||
: rwmDelta <= -2
|
||||
? styles.vsNationalNeg
|
||||
: styles.vsNationalFlat
|
||||
}
|
||||
>
|
||||
{rwmDelta >= 2
|
||||
? `+${rwmDelta} pts vs national`
|
||||
: rwmDelta <= -2
|
||||
? `${rwmDelta} pts vs national`
|
||||
: '≈ national avg'}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{progressScore != null && (
|
||||
<span className={styles.stat}>
|
||||
<strong className={styles.statValue}>{formatProgress(progressScore)}</strong>
|
||||
<span className={styles.statLabel}>
|
||||
progress · {progressBand(progressScore)}
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{school.total_pupils != null && (
|
||||
<span className={styles.stat}>
|
||||
|
||||
Reference in New Issue
Block a user