feat(ux): implement UX audit recommendations
All checks were successful
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 1m10s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 1m12s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 1s

- Redesign landing page with unified Omnibox search

- Add ComparisonToast for better comparison flow visibility

- Add visual 'Added' state to SchoolCard

- Add info tooltips to educational metrics

- Optimize mobile map view with Bottom Sheet

- Standardize distance display to miles
This commit is contained in:
Tudor
2026-03-05 09:33:47 +00:00
parent 6a95445f5e
commit ad7380dba5
9 changed files with 430 additions and 279 deletions

View File

@@ -5,7 +5,7 @@
'use client';
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { useSearchParams } from 'next/navigation';
import { FilterBar } from './FilterBar';
import { SchoolCard } from './SchoolCard';
@@ -25,18 +25,26 @@ export function HomeView({ initialSchools, filters }: HomeViewProps) {
const searchParams = useSearchParams();
const { addSchool, selectedSchools } = useComparisonContext();
const [resultsView, setResultsView] = useState<'list' | 'map'>('list');
const [selectedMapSchool, setSelectedMapSchool] = useState<School | null>(null);
const hasSearch = searchParams.get('search') || searchParams.get('postcode');
const isLocationSearch = !!searchParams.get('postcode');
// Close bottom sheet if we change views or search
useEffect(() => {
setSelectedMapSchool(null);
}, [resultsView, searchParams]);
return (
<div className={styles.homeView}>
{/* Combined Hero + Search and Filters */}
<div className={styles.heroSection}>
<h1 className={styles.heroTitle}>Compare Primary School Performance</h1>
<p className={styles.heroDescription}>Search and compare KS2 results for thousands of schools across England</p>
</div>
<FilterBar
filters={filters}
showLocationSearch
heroTitle="Compare Primary School Performance"
heroDescription="Search and compare KS2 results for thousands of schools across England"
/>
{/* Location Info Banner with View Toggle */}
@@ -110,7 +118,7 @@ export function HomeView({ initialSchools, filters }: HomeViewProps) {
action={
hasSearch
? {
label: 'Clear Filters',
label: 'Clear all filters and show featured schools',
onClick: () => {
window.location.href = '/';
},
@@ -125,18 +133,37 @@ export function HomeView({ initialSchools, filters }: HomeViewProps) {
<SchoolMap
schools={initialSchools.schools}
center={initialSchools.location_info?.coordinates}
onMarkerClick={setSelectedMapSchool}
/>
</div>
<div className={styles.compactList}>
{initialSchools.schools.map((school) => (
<CompactSchoolItem
key={school.urn}
school={school}
onAddToCompare={addSchool}
isInCompare={selectedSchools.some(s => s.urn === school.urn)}
/>
<div
key={school.urn}
className={`${styles.listItemWrapper} ${selectedMapSchool?.urn === school.urn ? styles.highlightedItem : ''}`}
>
<CompactSchoolItem
school={school}
onAddToCompare={addSchool}
isInCompare={selectedSchools.some(s => s.urn === school.urn)}
/>
</div>
))}
</div>
{/* Mobile Bottom Sheet for Selected Map Pin */}
{selectedMapSchool && (
<div className={styles.bottomSheetWrapper}>
<div className={styles.bottomSheet}>
<button className={styles.closeSheetBtn} onClick={() => setSelectedMapSchool(null)}>×</button>
<CompactSchoolItem
school={selectedMapSchool}
onAddToCompare={addSchool}
isInCompare={selectedSchools.some(s => s.urn === selectedMapSchool.urn)}
/>
</div>
</div>
)}
</div>
) : (
/* List View Layout */
@@ -147,6 +174,7 @@ export function HomeView({ initialSchools, filters }: HomeViewProps) {
key={school.urn}
school={school}
onAddToCompare={addSchool}
isInCompare={selectedSchools.some(s => s.urn === school.urn)}
/>
))}
</div>
@@ -182,7 +210,7 @@ function CompactSchoolItem({ school, onAddToCompare, isInCompare }: CompactSchoo
</a>
{school.distance !== undefined && school.distance !== null && (
<span className={styles.distanceBadge}>
{school.distance.toFixed(1)} mi
{(school.distance / 1.60934).toFixed(1)} mi
</span>
)}
</div>