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