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>
- Hide empty state placeholder on initial load
- Add prominent hero mode to FilterBar when no search is active
- Fix SchoolCard test TypeScript and assertion errors
- 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
- Reduced section padding from 2rem to 1rem-1.25rem
- Reduced margin-bottom from 2rem to 1rem
- Smaller chart height (400px → 280px) and map height (400px → 250px)
- Detailed metrics now in 3-column grid layout
- Condensed font sizes and spacing throughout
- Applied design system colors consistently
- Shortened metric labels (e.g., "Expected Standard" → "Expected")
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Increased header z-index from 100 to 1000 to ensure it stays above
Leaflet map elements (which typically use z-index 400-600).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changed 'equity' to 'disadvantaged' and 'trends' to '3yr' to match
the MetricDefinition category type.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added optgroup elements to organize metrics into logical categories:
- Expected Standard
- Higher Standard
- Progress Scores
- Average Scores
- Gender Performance
- Equity (Disadvantaged)
- School Context
- 3-Year Trends
Also added CSS styling for optgroup labels to match the design system.
Note: School names in rankings are already clickable links to school details.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implemented split-view map layout for postcode searches:
- List/Map toggle appears when doing location search
- Map view shows interactive map with school markers on left
- Compact school list on right with distance badges, stats, actions
- Mobile responsive: stacks vertically with map on top
- Updated School type to include distance and total_pupils fields
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Merged the hero title/description into FilterBar component to save
vertical space. The combined block has a gradient background flowing
from cream to white with the search controls below the header.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Reduced padding and title size to eliminate empty feeling, added
decorative coral underline bar for visual interest, and subtle
fade-in animation on page load.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add flex layout to schoolCard for proper content distribution
- Use flex: 1 on schoolMeta to fill available space
- Change margin-top to auto on latestValue to push to bottom
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Move comparison state from hook to shared context provider
- All components now share the same state instance
- Badge count updates immediately when schools are added/removed
- Add key prop to badge to re-trigger animation on count change
- Add storage event listener for cross-tab synchronization
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove padding from Modal's .content wrapper (let children control)
- Remove conflicting width/max-width from SchoolSearchModal
- Modal size classes now properly control the width
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- ComparisonView now uses fetchComparison from lib/api
- SchoolSearchModal now uses fetchSchools from lib/api
- Fixed bug in fetcher function that incorrectly sliced URLs
(url.slice(4) was removing '/com' from '/compare')
This fixes the malformed URL issue where '/api/compare' became '/apipare'.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added better UX and debugging for the comparison screen:
1. Loading state for chart section
- Shows "Loading comparison data..." when schools are selected
but data hasn't loaded yet
- Provides visual feedback to users
2. Enhanced debugging logs
- Log URNs being fetched
- Log API response status
- Log received comparison data
- Better error handling with null state on failure
3. Improved conditional rendering
- Chart shows when data is available
- Loading message shows when waiting for data
- Nothing shows when no schools selected
This helps diagnose any API issues and provides better user feedback
during data loading.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The compare screen and school search modal were not working because
they were fetching from '/api' directly instead of using the
NEXT_PUBLIC_API_URL environment variable that points to the backend.
Fixed client-side fetch calls in:
- ComparisonView: Fetch comparison data with correct API URL
- SchoolSearchModal: Search schools with correct API URL
This ensures client-side requests go to the FastAPI backend at
the configured URL (e.g., http://localhost:8000/api) rather than
trying to hit non-existent Next.js API routes.
Fixes comparison screen showing no data when schools are selected.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1. Added original favicon
- Copied favicon.svg from original frontend
- Added favicon reference to layout metadata
- Professional icon with brand colors
2. Updated logo in navigation
- Replaced emoji with proper SVG logo from original design
- Uses circular target design with crosshairs
- Matches brand identity with coral accent color
3. Removed emoji icons throughout app for professional look
- Removed 📍 (location pin) from school locations
- Removed 🏫 (school building) from school types
- Removed 🔢 from URN labels and section headings
- Kept meaningful symbols (✓, +) in buttons only
- Updated map popup button color to brand coral (#e07256)
Components updated:
- Navigation: Professional SVG logo
- HomeView: Clean location banner
- SchoolDetailView: No decorative emojis in metadata
- ComparisonView: Text-only school information
- SchoolSearchModal: Clean school listings
- LeafletMapInner: Professional map popups
Result: More polished, professional appearance suitable for
educational data platform
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1. Show empty state by default on home page
- Don't fetch or display schools until user searches
- Show helpful message prompting users to search
- Only fetch schools when search params are present
2. Change distance search to miles
- Display 0.5, 1, and 2 mile options instead of km
- Convert miles to km when sending to API (backend expects km)
- Convert km back to miles for display in location banner
- Maintains backend compatibility while improving UX
3. Fix metric labels in rankings dropdown
- Backend returns 'name' and 'type' fields
- Frontend expects 'label' and 'format' fields
- Added transformation in fetchMetrics to map fields
- Dropdown now shows proper labels like "RWM Combined %"
instead of technical codes like "rwm_expected_pct"
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Migrate from vanilla JavaScript SPA to Next.js 16 with App Router
- Add server-side rendering for all pages (Home, Compare, Rankings)
- Create individual school pages with dynamic routing (/school/[urn])
- Implement Chart.js and Leaflet map integrations
- Add comprehensive SEO with sitemap, robots.txt, and JSON-LD
- Set up Docker multi-service architecture (PostgreSQL, FastAPI, Next.js)
- Update CI/CD pipeline to build both backend and frontend images
- Fix Dockerfile to include devDependencies for TypeScript compilation
- Add Jest testing configuration
- Implement performance optimizations (code splitting, caching)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>