Files
school_compare/nextjs-app/components/HomeView.tsx
Tudor ff7f5487e6
Some checks failed
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 1m26s
Build and Push Docker Images / Build Frontend (Next.js) (push) Failing after 1m48s
Build and Push Docker Images / Trigger Portainer Update (push) Has been skipped
Complete Next.js migration with SSR and Docker deployment
- 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>
2026-02-02 20:34:35 +00:00

111 lines
3.3 KiB
TypeScript

/**
* HomeView Component
* Client-side home page view with search and filtering
*/
'use client';
import { useSearchParams } from 'next/navigation';
import { FilterBar } from './FilterBar';
import { SchoolCard } from './SchoolCard';
import { Pagination } from './Pagination';
import { EmptyState } from './EmptyState';
import { useComparisonContext } from '@/context/ComparisonContext';
import type { SchoolsResponse, Filters } from '@/lib/types';
import styles from './HomeView.module.css';
interface HomeViewProps {
initialSchools: SchoolsResponse;
filters: Filters;
}
export function HomeView({ initialSchools, filters }: HomeViewProps) {
const searchParams = useSearchParams();
const { addSchool } = useComparisonContext();
const hasSearch = searchParams.get('search') || searchParams.get('postcode');
const isLocationSearch = !!searchParams.get('postcode');
return (
<div className={styles.homeView}>
{/* Hero Section */}
<section className={styles.hero}>
<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>
</section>
{/* Search and Filters */}
<FilterBar filters={filters} showLocationSearch />
{/* Location Info Banner */}
{isLocationSearch && initialSchools.location_info && (
<div className={styles.locationBanner}>
<span className={styles.locationIcon}>📍</span>
<span>
Showing schools within {initialSchools.location_info.radius}km of{' '}
<strong>{initialSchools.location_info.postcode}</strong>
</span>
</div>
)}
{/* Results Section */}
<section className={styles.results}>
{!hasSearch && initialSchools.schools.length > 0 && (
<div className={styles.sectionHeader}>
<h2>Featured Schools</h2>
<p className={styles.sectionDescription}>
Explore schools from across England
</p>
</div>
)}
{hasSearch && (
<div className={styles.resultsHeader}>
<h2>
{initialSchools.total.toLocaleString()} school
{initialSchools.total !== 1 ? 's' : ''} found
</h2>
</div>
)}
{initialSchools.schools.length === 0 ? (
<EmptyState
title="No schools found"
message="Try adjusting your search criteria or filters to find schools."
action={{
label: 'Clear Filters',
onClick: () => {
window.location.href = '/';
},
}}
/>
) : (
<>
<div className={styles.grid}>
{initialSchools.schools.map((school) => (
<SchoolCard
key={school.urn}
school={school}
onAddToCompare={addSchool}
/>
))}
</div>
{initialSchools.total_pages > 1 && (
<Pagination
currentPage={initialSchools.page}
totalPages={initialSchools.total_pages}
total={initialSchools.total}
/>
)}
</>
)}
</section>
</div>
);
}