diff --git a/backend/app.py b/backend/app.py index 94be390..5f4ba0e 100644 --- a/backend/app.py +++ b/backend/app.py @@ -293,9 +293,9 @@ async def get_schools( school_type: Optional[str] = Query(None, description="Filter by school type", max_length=100), phase: Optional[str] = Query(None, description="Filter by phase: primary, secondary, all-through", max_length=50), postcode: Optional[str] = Query(None, description="Search near postcode", max_length=10), - radius: float = Query(5.0, ge=0.1, le=50, description="Search radius in miles"), + radius: float = Query(5.0, ge=0.1, le=5, description="Search radius in miles"), page: int = Query(1, ge=1, le=1000, description="Page number"), - page_size: int = Query(25, ge=1, le=100, description="Results per page"), + page_size: int = Query(25, ge=1, le=500, description="Results per page"), gender: Optional[str] = Query(None, description="Filter by gender (Mixed/Boys/Girls)", max_length=50), admissions_policy: Optional[str] = Query(None, description="Filter by admissions policy", max_length=100), has_sixth_form: Optional[str] = Query(None, description="Filter by sixth form presence: yes/no", max_length=3), diff --git a/nextjs-app/components/FilterBar.tsx b/nextjs-app/components/FilterBar.tsx index 01c320b..53e668a 100644 --- a/nextjs-app/components/FilterBar.tsx +++ b/nextjs-app/components/FilterBar.tsx @@ -145,7 +145,6 @@ export function FilterBar({ filters, isHero, resultFilters }: FilterBarProps) { - )} diff --git a/nextjs-app/components/HomeView.tsx b/nextjs-app/components/HomeView.tsx index c6dad29..c81c6cd 100644 --- a/nextjs-app/components/HomeView.tsx +++ b/nextjs-app/components/HomeView.tsx @@ -35,6 +35,8 @@ export function HomeView({ initialSchools, filters, totalSchools }: HomeViewProp const [hasMore, setHasMore] = useState(initialSchools.total_pages > 1); const [isLoadingMore, setIsLoadingMore] = useState(false); const [laAverages, setLaAverages] = useState>({}); + const [mapSchools, setMapSchools] = useState([]); + const [isLoadingMap, setIsLoadingMap] = useState(false); const prevSearchParamsRef = useRef(searchParams.toString()); const hasSearch = searchParams.get('search') || searchParams.get('postcode'); @@ -52,6 +54,7 @@ export function HomeView({ initialSchools, filters, totalSchools }: HomeViewProp setAllSchools(initialSchools.schools); setCurrentPage(initialSchools.page); setHasMore(initialSchools.total_pages > 1); + setMapSchools([]); } }, [searchParams, initialSchools]); @@ -60,6 +63,20 @@ export function HomeView({ initialSchools, filters, totalSchools }: HomeViewProp setSelectedMapSchool(null); }, [resultsView, searchParams]); + // Fetch all schools within radius when map view is active + useEffect(() => { + if (resultsView !== 'map' || !isLocationSearch) return; + setIsLoadingMap(true); + const params: Record = {}; + searchParams.forEach((value, key) => { params[key] = value; }); + params.page = 1; + params.page_size = 500; + fetchSchools(params, { cache: 'no-store' }) + .then(r => setMapSchools(r.schools)) + .catch(() => setMapSchools(initialSchools.schools)) + .finally(() => setIsLoadingMap(false)); + }, [resultsView, searchParams]); + // Fetch LA averages when secondary schools are visible useEffect(() => { if (!isSecondaryView) return; @@ -214,13 +231,14 @@ export function HomeView({ initialSchools, filters, totalSchools }: HomeViewProp
- {initialSchools.schools.map((school) => ( + {(isLoadingMap ? initialSchools.schools : mapSchools).map((school) => (
void; } -export default function LeafletMapInner({ schools, center, zoom, onMarkerClick }: LeafletMapInnerProps) { +export default function LeafletMapInner({ schools, center, zoom, referencePoint, onMarkerClick }: LeafletMapInnerProps) { const mapRef = useRef(null); const mapContainerRef = useRef(null); + const refMarkerRef = useRef(null); useEffect(() => { if (!mapContainerRef.current) return; @@ -43,13 +45,36 @@ export default function LeafletMapInner({ schools, center, zoom, onMarkerClick } }).addTo(mapRef.current); } - // Clear existing markers + // Clear existing school markers (not the reference pin) mapRef.current.eachLayer((layer) => { - if (layer instanceof L.Marker) { + if (layer instanceof L.Marker && layer !== refMarkerRef.current) { mapRef.current!.removeLayer(layer); } }); + // Add reference pin (search location) + if (refMarkerRef.current) { + refMarkerRef.current.remove(); + refMarkerRef.current = null; + } + if (referencePoint && mapRef.current) { + const refIcon = L.divIcon({ + html: `
`, + iconSize: [20, 20], + iconAnchor: [10, 10], + className: '', + }); + refMarkerRef.current = L.marker(referencePoint, { icon: refIcon, zIndexOffset: 1000 }) + .addTo(mapRef.current) + .bindPopup('Search location'); + } + // Add markers for schools schools.forEach((school) => { if (school.latitude && school.longitude && mapRef.current) { @@ -89,7 +114,7 @@ export default function LeafletMapInner({ schools, center, zoom, onMarkerClick } return () => { // Don't destroy map on every update, just clean markers }; - }, [schools, center, zoom, onMarkerClick]); + }, [schools, center, zoom, referencePoint, onMarkerClick]); // Cleanup map on unmount useEffect(() => { diff --git a/nextjs-app/components/SchoolMap.tsx b/nextjs-app/components/SchoolMap.tsx index 7a60bac..fa33d2f 100644 --- a/nextjs-app/components/SchoolMap.tsx +++ b/nextjs-app/components/SchoolMap.tsx @@ -24,10 +24,11 @@ interface SchoolMapProps { schools: School[]; center?: [number, number]; zoom?: number; + referencePoint?: [number, number]; onMarkerClick?: (school: School) => void; } -export function SchoolMap({ schools, center, zoom = 13, onMarkerClick }: SchoolMapProps) { +export function SchoolMap({ schools, center, zoom = 13, referencePoint, onMarkerClick }: SchoolMapProps) { // Calculate center if not provided const mapCenter: [number, number] = center || (() => { if (schools.length === 0) return [51.5074, -0.1278]; // Default to London @@ -50,6 +51,7 @@ export function SchoolMap({ schools, center, zoom = 13, onMarkerClick }: SchoolM schools={schools} center={mapCenter} zoom={zoom} + referencePoint={referencePoint} onMarkerClick={onMarkerClick} />