From 78f7125f627612b0235581ea7663072542621452 Mon Sep 17 00:00:00 2001 From: AvatarMiiDe Date: Thu, 30 Jul 2026 04:20:36 +0100 Subject: [PATCH 1/3] perf(search): paginate SearchResults to cap mounted cards per page --- src/app/components/search/SearchResults.tsx | 99 ++++++- .../search/__tests__/SearchResults.test.tsx | 262 ++++++++++++++++++ 2 files changed, 354 insertions(+), 7 deletions(-) create mode 100644 src/app/components/search/__tests__/SearchResults.test.tsx diff --git a/src/app/components/search/SearchResults.tsx b/src/app/components/search/SearchResults.tsx index f74260b3..210b53ec 100644 --- a/src/app/components/search/SearchResults.tsx +++ b/src/app/components/search/SearchResults.tsx @@ -1,7 +1,7 @@ 'use client'; -import React from 'react'; -import { Star, Clock, User, ArrowRight, SearchX } from 'lucide-react'; +import React, { useState, useEffect, useCallback } from 'react'; +import { Star, Clock, User, ArrowRight, SearchX, ChevronLeft, ChevronRight } from 'lucide-react'; import Link from 'next/link'; import clsx from 'clsx'; import { EmptyState } from '@/components'; @@ -26,6 +26,10 @@ interface SearchResultsProps { isLoading?: boolean; sortBy?: string; onSortChange?: (sort: string) => void; + /** Number of cards per page. Defaults to 12. */ + pageSize?: number; + /** Initial page number. Defaults to 1. */ + initialPage?: number; } export const SearchResults: React.FC = ({ @@ -33,7 +37,28 @@ export const SearchResults: React.FC = ({ isLoading = false, sortBy = 'relevance', onSortChange, + pageSize = 12, + initialPage = 1, }) => { + const [currentPage, setCurrentPage] = useState(initialPage); + + // Reset to page 1 whenever the result set changes (new search / filter) + useEffect(() => { + setCurrentPage(1); + }, [results]); + + const totalPages = Math.max(1, Math.ceil(results.length / pageSize)); + const safeCurrentPage = Math.min(currentPage, totalPages); + const startIndex = (safeCurrentPage - 1) * pageSize; + const endIndex = Math.min(startIndex + pageSize, results.length); + const visibleResults = results.slice(startIndex, endIndex); + + const goToPrev = useCallback(() => setCurrentPage((p) => Math.max(1, p - 1)), []); + const goToNext = useCallback( + () => setCurrentPage((p) => Math.min(totalPages, p + 1)), + [totalPages], + ); + if (isLoading) { return (
@@ -91,9 +116,17 @@ export const SearchResults: React.FC = ({ return (
- {/* Sort Controls */} + {/* Sort Controls / Result Count */}
-

{results.length} results found

+

+ Showing{' '} + + {results.length === 0 ? 0 : startIndex + 1}–{endIndex} + {' '} + of{' '} + {results.length}{' '} + results +

{onSortChange && (