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>
This commit is contained in:
126
nextjs-app/components/Pagination.tsx
Normal file
126
nextjs-app/components/Pagination.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Pagination Component
|
||||
* Navigate through pages of results
|
||||
*/
|
||||
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams, usePathname } from 'next/navigation';
|
||||
import styles from './Pagination.module.css';
|
||||
|
||||
interface PaginationProps {
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
export function Pagination({ currentPage, totalPages, total }: PaginationProps) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
if (totalPages <= 1) return null;
|
||||
|
||||
const goToPage = (page: number) => {
|
||||
const params = new URLSearchParams(searchParams);
|
||||
params.set('page', page.toString());
|
||||
router.push(`${pathname}?${params.toString()}`);
|
||||
};
|
||||
|
||||
const handlePrevious = () => {
|
||||
if (currentPage > 1) {
|
||||
goToPage(currentPage - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
if (currentPage < totalPages) {
|
||||
goToPage(currentPage + 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Generate page numbers to show
|
||||
const getPageNumbers = () => {
|
||||
const pages: (number | string)[] = [];
|
||||
const maxVisible = 7;
|
||||
|
||||
if (totalPages <= maxVisible) {
|
||||
// Show all pages
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
} else {
|
||||
// Show first, last, and pages around current
|
||||
pages.push(1);
|
||||
|
||||
if (currentPage > 3) {
|
||||
pages.push('...');
|
||||
}
|
||||
|
||||
const start = Math.max(2, currentPage - 1);
|
||||
const end = Math.min(totalPages - 1, currentPage + 1);
|
||||
|
||||
for (let i = start; i <= end; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
|
||||
if (currentPage < totalPages - 2) {
|
||||
pages.push('...');
|
||||
}
|
||||
|
||||
pages.push(totalPages);
|
||||
}
|
||||
|
||||
return pages;
|
||||
};
|
||||
|
||||
const pageNumbers = getPageNumbers();
|
||||
|
||||
return (
|
||||
<div className={styles.pagination}>
|
||||
<div className={styles.info}>
|
||||
Showing page {currentPage} of {totalPages} ({total.toLocaleString()} total schools)
|
||||
</div>
|
||||
|
||||
<div className={styles.controls}>
|
||||
<button
|
||||
onClick={handlePrevious}
|
||||
disabled={currentPage === 1}
|
||||
className={styles.navButton}
|
||||
aria-label="Previous page"
|
||||
>
|
||||
← Previous
|
||||
</button>
|
||||
|
||||
<div className={styles.pages}>
|
||||
{pageNumbers.map((page, index) => (
|
||||
typeof page === 'number' ? (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => goToPage(page)}
|
||||
className={page === currentPage ? styles.pageButtonActive : styles.pageButton}
|
||||
aria-label={`Go to page ${page}`}
|
||||
aria-current={page === currentPage ? 'page' : undefined}
|
||||
>
|
||||
{page}
|
||||
</button>
|
||||
) : (
|
||||
<span key={index} className={styles.ellipsis}>
|
||||
{page}
|
||||
</span>
|
||||
)
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleNext}
|
||||
disabled={currentPage === totalPages}
|
||||
className={styles.navButton}
|
||||
aria-label="Next page"
|
||||
>
|
||||
Next →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user