Complete Next.js migration with SSR and Docker deployment
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

- 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>
This commit is contained in:
Tudor
2026-02-02 20:34:35 +00:00
parent f4919db3b9
commit ff7f5487e6
72 changed files with 18636 additions and 20 deletions

View File

@@ -0,0 +1,57 @@
/**
* Compare Page (SSR)
* Side-by-side comparison of schools with metrics
*/
import { fetchComparison, fetchMetrics } from '@/lib/api';
import { ComparisonView } from '@/components/ComparisonView';
import type { Metadata } from 'next';
interface ComparePageProps {
searchParams: Promise<{
urns?: string;
metric?: string;
}>;
}
export const metadata: Metadata = {
title: 'Compare Schools',
description: 'Compare KS2 performance across multiple primary schools in England',
keywords: 'school comparison, compare schools, KS2 comparison, primary school performance',
};
// Force dynamic rendering
export const dynamic = 'force-dynamic';
export default async function ComparePage({ searchParams }: ComparePageProps) {
const { urns: urnsParam, metric: metricParam } = await searchParams;
const urns = urnsParam?.split(',').map(Number).filter(Boolean) || [];
const selectedMetric = metricParam || 'rwm_expected_pct';
// Fetch comparison data if URNs provided
let comparisonData = null;
if (urns.length > 0) {
try {
const response = await fetchComparison(urnsParam!);
comparisonData = response.comparison;
} catch (error) {
console.error('Failed to fetch comparison:', error);
}
}
// Fetch available metrics
const metricsResponse = await fetchMetrics();
// Convert metrics object to array
const metricsArray = Object.values(metricsResponse.metrics);
return (
<ComparisonView
initialData={comparisonData}
initialUrns={urns}
metrics={metricsArray}
selectedMetric={selectedMetric}
/>
);
}