feat(ux): implement comprehensive UX audit fixes across all pages
Addresses 28 issues identified in UX audit (P0–P3 severity): P0 — Critical: - Fix compare URL sharing: seed ComparisonContext from SSR initialData when localStorage is empty, making /compare?urns=... links shareable - Remove permanently broken "Avg. Scaled Score" column from school detail historical data table P1 — High priority: - Add radius selector (0.5–10 mi) to postcode search in FilterBar - Make Add to Compare a toggle (remove) on SchoolCards - Hide hero title/description once a search is active - Show school count + quick-search prompts on empty landing page - Compare empty state opens in-page school search modal directly - Remove URN from school detail header (irrelevant to end users) - Move map above performance chart in school detail page - Add ← Back navigation to school detail page - Add sort controls to search results (RWM%, distance, A–Z) - Show metric descriptions below metric selector - Expand ComparisonToast to list school names with per-school remove - Add progress score explainer (0 = national average) throughout P2 — Medium: - Remove console.log statements from ComparisonView - Colour-code comparison school cards to match chart line colours - Replace plain loading text with LoadingSkeleton in ComparisonView - Rankings empty state uses shared EmptyState component - Rankings year filter shows actual year e.g. "2023 (Latest)" - Rankings subtitle shows top-N count - Add View link alongside Add button in rankings table - Remove placeholder Privacy Policy / Terms links from footer - Replace untappable 10px info icons with visible metric hint text - Show active filter chips in search results header P3 — Polish: - Remove redundant "Home" nav link (logo already links home) - Add / and Ctrl+K keyboard shortcut to focus search input - Add Share button to compare page (copies URL to clipboard) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,12 +11,13 @@ import styles from './SchoolCard.module.css';
|
||||
interface SchoolCardProps {
|
||||
school: School;
|
||||
onAddToCompare?: (school: School) => void;
|
||||
onRemoveFromCompare?: (urn: number) => void;
|
||||
showDistance?: boolean;
|
||||
distance?: number;
|
||||
isInCompare?: boolean;
|
||||
}
|
||||
|
||||
export function SchoolCard({ school, onAddToCompare, showDistance, distance, isInCompare = false }: SchoolCardProps) {
|
||||
export function SchoolCard({ school, onAddToCompare, onRemoveFromCompare, showDistance, distance, isInCompare = false }: SchoolCardProps) {
|
||||
const trend = calculateTrend(school.rwm_expected_pct, school.prev_rwm_expected_pct);
|
||||
const trendColor = getTrendColor(trend);
|
||||
|
||||
@@ -51,9 +52,9 @@ export function SchoolCard({ school, onAddToCompare, showDistance, distance, isI
|
||||
<div className={styles.metrics}>
|
||||
{school.rwm_expected_pct !== null && (
|
||||
<div className={styles.metric}>
|
||||
<span className={styles.metricLabel} title="Percentage of pupils achieving the expected standard in Reading, Writing, and Maths">
|
||||
<span className={styles.metricLabel}>
|
||||
RWM Expected
|
||||
<svg className="info-icon" style={{ marginLeft: '4px', width: '10px', height: '10px', verticalAlign: 'middle', color: 'var(--text-muted)' }} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>
|
||||
<span className={styles.metricHint}>% meeting expected standard</span>
|
||||
</span>
|
||||
<div className={styles.metricValue}>
|
||||
<strong>{formatPercentage(school.rwm_expected_pct)}</strong>
|
||||
@@ -91,9 +92,9 @@ export function SchoolCard({ school, onAddToCompare, showDistance, distance, isI
|
||||
|
||||
{school.reading_progress !== null && (
|
||||
<div className={styles.metric}>
|
||||
<span className={styles.metricLabel} title="Progress score from KS1 to KS2 in Reading. >0 is above average.">
|
||||
<span className={styles.metricLabel}>
|
||||
Reading
|
||||
<svg className="info-icon" style={{ marginLeft: '4px', width: '10px', height: '10px', verticalAlign: 'middle', color: 'var(--text-muted)' }} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>
|
||||
<span className={styles.metricHint}>progress score (0 = avg)</span>
|
||||
</span>
|
||||
<strong>{formatProgress(school.reading_progress)}</strong>
|
||||
</div>
|
||||
@@ -101,9 +102,9 @@ export function SchoolCard({ school, onAddToCompare, showDistance, distance, isI
|
||||
|
||||
{school.writing_progress !== null && (
|
||||
<div className={styles.metric}>
|
||||
<span className={styles.metricLabel} title="Progress score from KS1 to KS2 in Writing. >0 is above average.">
|
||||
<span className={styles.metricLabel}>
|
||||
Writing
|
||||
<svg className="info-icon" style={{ marginLeft: '4px', width: '10px', height: '10px', verticalAlign: 'middle', color: 'var(--text-muted)' }} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>
|
||||
<span className={styles.metricHint}>progress score (0 = avg)</span>
|
||||
</span>
|
||||
<strong>{formatProgress(school.writing_progress)}</strong>
|
||||
</div>
|
||||
@@ -111,9 +112,9 @@ export function SchoolCard({ school, onAddToCompare, showDistance, distance, isI
|
||||
|
||||
{school.maths_progress !== null && (
|
||||
<div className={styles.metric}>
|
||||
<span className={styles.metricLabel} title="Progress score from KS1 to KS2 in Maths. >0 is above average.">
|
||||
<span className={styles.metricLabel}>
|
||||
Maths
|
||||
<svg className="info-icon" style={{ marginLeft: '4px', width: '10px', height: '10px', verticalAlign: 'middle', color: 'var(--text-muted)' }} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>
|
||||
<span className={styles.metricHint}>progress score (0 = avg)</span>
|
||||
</span>
|
||||
<strong>{formatProgress(school.maths_progress)}</strong>
|
||||
</div>
|
||||
@@ -122,16 +123,15 @@ export function SchoolCard({ school, onAddToCompare, showDistance, distance, isI
|
||||
)}
|
||||
|
||||
<div className={styles.actions}>
|
||||
<Link href={`/school/${school.urn}`} className={styles.btnSecondary}>
|
||||
<Link href={`/school/${school.urn}`} className={styles.btnPrimary}>
|
||||
View Details
|
||||
</Link>
|
||||
{onAddToCompare && (
|
||||
<button
|
||||
onClick={() => onAddToCompare(school)}
|
||||
className={`${styles.btnPrimary} ${isInCompare ? styles.btnAdded : ''}`}
|
||||
disabled={isInCompare}
|
||||
onClick={() => isInCompare ? onRemoveFromCompare?.(school.urn) : onAddToCompare(school)}
|
||||
className={isInCompare ? styles.btnRemove : styles.btnSecondary}
|
||||
>
|
||||
{isInCompare ? 'Added ✓' : 'Add to Compare'}
|
||||
{isInCompare ? '✓ Remove' : 'Add to Compare'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user