The map view now follows option B of the results-controls mockups: - The list sits in a pane on the left, under the result count and sort, and the map fills the rest of the screen below the pinned toolbar. The toolbar's height is measured, so the split ends 1rem above the bottom of the screen however the controls wrap. - Pins are brand-teal dots and the selected one is coral. The search location is an ink dot, the search radius a dashed circle with its distance, and the view fits that circle. Tiles are muted and the zoom sits under the fullscreen button. - A school picked on the map or in the list opens a card on the map (View, + Compare, following the basket), and its list card is ringed and scrolled into view. Phones keep the bottom sheet. - The list cards show the full name, Ofsted and school type, the headline figure and pupils. The map cards now follow the list rows: no England benchmark for special schools, PRUs and AP, and no placeholder all-zero RWM (Greenmead showed "0% RWM -62 pts vs national"). That rule moves to a shared listRwmValue helper. A postcode search opens on the map for desktop browsers, chosen on the server from the user agent so the list never paints first. The client falls back to the list below 1024px, and follows the default through client-side navigation from the hero until the reader picks a view. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SchoolCompare
SchoolCompare compares schools across England: primary (KS2), secondary (KS4), all-through and post-16 provision, with coverage depending on the source dataset. It provides school search, postcode maps, comparisons, rankings, place pages, Ofsted information, admissions and destination measures. Editorial content lives in a Payload CMS blog.
Start here
- Architecture and data flow
- Development and validation
- Deployment and promotion
- Legacy and unused-code inventory
- Frontend conventions
- CMS publishing
Repository map
| Path | Responsibility |
|---|---|
backend/ |
FastAPI routes, cached school data, read-only SQLAlchemy mappings, feature flags |
nextjs-app/ |
Next.js App Router, React UI, Payload CMS, frontend tests |
pipeline/plugins/extractors/ |
Custom Singer taps for GIAS, EES, Ofsted and other datasets |
pipeline/transform/ |
dbt staging/intermediate models, marts, seeds and data tests |
pipeline/dags/ |
Airflow extraction, transformation and publication workflows |
pipeline/scripts/ |
Search indexing, code generation and operational diagnostics |
e2e/ |
Playwright journeys against a running environment |
.gitea/workflows/ |
PR checks, staging deployment and manual production promotion |
scripts/ |
CI review tooling and historical data utilities; see the legacy inventory |
docs/superpowers/, mockups/ |
Design history and prototypes, not application entry points |
Runtime
The public site is Next.js, not the FastAPI root page. Browser /api/*
requests pass through a Next.js route handler to FastAPI. Server-rendered pages
call FastAPI directly using FASTAPI_URL, including its /api suffix.
PostgreSQL/PostGIS stores school data. Meltano/Singer extracts source data;
dbt builds marts.*; FastAPI reads those tables. Typesense serves text search
and autocomplete. Payload runs inside Next.js and owns a separate payload
database schema and uploaded media.
There is no automatic CSV import or sample dataset on startup. A working school-data environment needs populated marts from the pipeline or an approved database snapshot. See development before choosing a setup.
Validation
cd nextjs-app
npm ci
npm run typecheck
npm test -- --runInBand
Backend checks, pipeline validation, runtime versions and E2E requirements are
listed in DEVELOPMENT.md. No npm run lint script is
currently defined.
Deployment
Work on a feature branch and open a PR. Merging to main builds images and
deploys staging. Production promotion is a separate, human-triggered Gitea
workflow. Use DEPLOY.md and the Portainer compose files as the
operational references. The generic compose examples still reference :latest,
which the current release workflow does not publish.