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

51
nextjs-app/app/layout.tsx Normal file
View File

@@ -0,0 +1,51 @@
import type { Metadata } from 'next';
import { Navigation } from '@/components/Navigation';
import { Footer } from '@/components/Footer';
import { ComparisonProvider } from '@/context/ComparisonProvider';
import './globals.css';
export const metadata: Metadata = {
title: {
default: 'SchoolCompare | Compare Primary School Performance',
template: '%s | SchoolCompare',
},
description: 'Compare primary school KS2 performance across England',
keywords: 'school comparison, KS2 results, primary school performance, England schools, SATs results',
authors: [{ name: 'SchoolCompare' }],
manifest: '/manifest.json',
openGraph: {
type: 'website',
title: 'SchoolCompare | Compare Primary School Performance',
description: 'Compare primary school KS2 performance across England',
url: 'https://schoolcompare.co.uk',
siteName: 'SchoolCompare',
},
twitter: {
card: 'summary',
title: 'SchoolCompare | Compare Primary School Performance',
description: 'Compare primary school KS2 performance across England',
},
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>
<ComparisonProvider>
<div className="app-container">
<div className="noise-overlay" />
<Navigation />
<main className="main-content">
{children}
</main>
<Footer />
</div>
</ComparisonProvider>
</body>
</html>
);
}