Skip to content

feat: add responsive pagination to contributors list#256

Open
Omkarchavan2527 wants to merge 2 commits into
CircuitVerse:mainfrom
Omkarchavan2527:feat/add-people-pagination
Open

feat: add responsive pagination to contributors list#256
Omkarchavan2527 wants to merge 2 commits into
CircuitVerse:mainfrom
Omkarchavan2527:feat/add-people-pagination

Conversation

@Omkarchavan2527

@Omkarchavan2527 Omkarchavan2527 commented Feb 8, 2026

Copy link
Copy Markdown

Description

This PR adds responsive pagination to the People page contributors list. Instead of rendering all contributor cards in a single long list, contributors are now displayed in smaller pages to improve performance, reduce excessive scrolling, and provide a more structured browsing experience on both desktop and mobile.
Pagination state is also synced with the page URL so users can reload or share the same view.

Related Issue

Fixes #255

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation

Checklist

  • Code follows project style
  • Tested locally
  • No unnecessary files added
  • PR title is clear and descriptive

Screenshots (if applicable)

before :

people-CircuitVerse.mp4

After:

Screenshot (57) Screenshot (58)

Summary by CodeRabbit

  • New Features

    • Unified people search and browse experience with overview and contributor detail panels; the people page now delegates to this single search-driven view.
    • Search filtering by name/username and URL-shareable pagination with selectable page sizes (24, 48, 96, 192, all).
  • UX Improvements

    • Loading skeletons, dismissible retry on errors, smooth scroll restoration.
    • Accessibility and responsive enhancements for keyboard and screen-reader navigation.

@netlify

netlify Bot commented Feb 8, 2026

Copy link
Copy Markdown

Deploy Preview for cv-community-dashboard failed.

Name Link
🔨 Latest commit 785e4d2
🔍 Latest deploy log https://app.netlify.com/projects/cv-community-dashboard/deploys/698e78db61396300085f5388

@coderabbitai

coderabbitai Bot commented Feb 8, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The People page was simplified: all client-side data fetching, local state, effects, per-contributor navigation, and inline rendering for Core Team, Alumni, and Contributors were removed. The page now renders a single client-side PeopleSearch component inside Suspense; PeopleSearch encapsulates fetching, search/filtering, URL-synced pagination (page and limit), loading/error states, scroll restoration, and contributor detail view logic.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding responsive pagination to the contributors list, which matches the primary focus of the changeset.
Linked Issues check ✅ Passed The PR fully addresses issue #255 requirements: pagination is implemented with responsive page sizes, state is synced with URL parameters, and UX is improved for both desktop and mobile.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing pagination: refactored page.tsx to use new PeopleSearch component and added PeopleSearch with full pagination, search, and URL-sync functionality.
Description check ✅ Passed The PR description is complete and well-structured, covering all required template sections with clear, specific details about the feature, related issue, type of change, checklist confirmations, and visual before/after screenshots.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@app/people/page.tsx`:
- Around line 224-233: The current updatePage function (and similar functions
updatePageSize and the reset effect) uses window.history.replaceState which
bypasses Next.js routing; import useRouter from "next/navigation", call const
router = useRouter() in the component, and replace window.history.replaceState
calls with router.replace(...) so Next's internal navigation state updates
correctly; when building the replacement URL use params.toString() and omit the
trailing "?" when it's an empty string (i.e., use pathname alone when
params.toString() === "") to avoid URLs like "/people?".
- Around line 197-207: The effect that resets currentPage (useEffect checking
currentPage > totalPages) causes a render where paginatedPeople is empty;
instead ensure the effective page is computed or reset synchronously when the
search changes: in the search onChange handler (where
filteredPeople/searchParams are updated) call setCurrentPageState(1) immediately
(or update the URL params to remove page) so filteredPeople, totalPages and
currentPage remain consistent within the same render; alternatively compute an
effectivePage variable used by pagination logic (e.g., const effectivePage =
Math.min(currentPage, Math.max(1, totalPages)) or default to 1) and use
effectivePage for deriving paginatedPeople so you avoid the intermediate empty
render—apply the change around the search input handler and pagination code that
derives paginatedPeople.
- Line 74: The page currently calls useSearchParams() inside the "use client"
PeoplePage which forces full client rendering; extract the search-params logic
into a small Client Component (e.g., create a new component named PeopleSearch
that includes "use client" and calls useSearchParams()), then import and render
<PeopleSearch/> inside the server component and wrap it with React.Suspense
(e.g., <Suspense fallback={...}><PeopleSearch/></Suspense>); update PeoplePage
to remove useSearchParams() usage so the rest of PeoplePage remains a
server/static component and only PeopleSearch is client-side.
🧹 Nitpick comments (2)
app/people/page.tsx (2)

81-81: Move PAGE_SIZE_OPTIONS to module scope.

This array is recreated on every render. Since it's a constant, declare it outside the component.

+const PAGE_SIZE_OPTIONS = [24, 48, 96, 192] as const;
+
 export default function PeoplePage() {
-  const PAGE_SIZE_OPTIONS = [24, 48, 96, 192] as const;

207-207: Non-idiomatic dependency array expression: pathname ?? "".

Inline expressions in useEffect deps can confuse the React exhaustive-deps lint rule and other developers. pathname from usePathname() is always a string in a page component—just use pathname directly.

-  }, [totalPages, currentPage, searchParamsString, pathname ?? ""]);
+  }, [totalPages, currentPage, searchParamsString, pathname]);

Comment thread app/people/page.tsx Outdated
Comment thread app/people/page.tsx Outdated
Comment thread app/people/page.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@app/people/PeopleSearch.tsx`:
- Around line 52-63: The fetchPeople function currently returns res.json()
without awaiting it, which allows JSON parsing errors to escape the local
try/catch; update fetchPeople so you await the JSON parse (await res.json())
before returning, ensuring any exceptions from res.json() are caught by the
try/catch and the fallback result ({ updatedAt: 0, people: [], coreTeam: [],
alumni: [] }) is returned on error.
🧹 Nitpick comments (4)
app/people/PeopleSearch.tsx (3)

65-178: Consider breaking this ~570-line component into smaller pieces.

PeopleSearch handles data fetching, URL sync, search/filter, pagination logic, scroll management, error/loading states, contributor detail view, and the full render tree. This is a lot of responsibility for a single component, making it harder to test and maintain.

Extracting focused subcomponents or custom hooks (e.g., a usePeoplePagination hook for pagination/URL sync, a ContributorsSection component for the grid + pagination UI) would improve readability without changing behavior.


25-27: Nit: Extra blank lines.

There are three consecutive blank lines (25–27) between the imports and the interface declaration. One blank line is sufficient.


140-151: Hash-based scroll effect doesn't clean up if loading never transitions.

The effect only runs when loading changes. If the component mounts with window.location.hash === "#contributors" but data loads very quickly (or is cached), the 50ms setTimeout races with the DOM being ready. This is a minor edge case since the setTimeout provides a small buffer, but worth noting.

app/people/page.tsx (1)

1-9: Provide a meaningful Suspense fallback instead of null.

fallback={null} renders a blank page while the client component suspends (e.g., during static pre-rendering or initial hydration when useSearchParams isn't available yet). Since PeopleSearch already has a detailed loading skeleton, consider extracting that skeleton into a shared component and using it here, or at minimum show a lightweight spinner:

Quick improvement
+import { Loader2 } from "lucide-react";
 import { Suspense } from "react";
 import PeopleSearch from "./PeopleSearch";

 export default function PeoplePage() {
   return (
-    <Suspense fallback={null}>
+    <Suspense fallback={
+      <div className="flex items-center justify-center min-h-screen">
+        <Loader2 className="w-12 h-12 animate-spin text-green-600" />
+      </div>
+    }>
       <PeopleSearch />
     </Suspense>
   );
 }

Comment thread app/people/PeopleSearch.tsx
@Omkarchavan2527
Omkarchavan2527 force-pushed the feat/add-people-pagination branch from ffe2b08 to 8e37c4a Compare February 10, 2026 01:27

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@app/people/page.tsx`:
- Around line 6-8: The Suspense fallback currently returns null, causing a blank
page while the PeopleSearch client bundle loads; extract the skeleton markup
(from PeopleSearch.tsx lines 302–394) into a new shared server component (e.g.,
PeopleSearchSkeleton), export it, import it into app/people/page.tsx, and
replace fallback={null} with <PeopleSearchSkeleton /> so the lightweight loading
UI displays until the client component mounts; ensure the skeleton contains only
server-renderable markup/props and no client-only hooks.
🧹 Nitpick comments (5)
app/people/PeopleSearch.tsx (5)

203-211: Missing router in the effect dependency array.

router.replace is called inside this effect but router isn't listed in the dependency array. While router is practically stable in Next.js, the exhaustive-deps lint rule will flag this. Same applies to buildUrl (which closes over pathname).

Proposed fix
-  }, [totalPages, currentPage, searchParamsString, pathname ?? ""]);
+  }, [totalPages, currentPage, searchParamsString, pathname, router]);

86-138: PAGE_SIZE_OPTIONS is recreated every render and referenced in effects — hoist it to module scope.

Since it's a constant array, defining it inside the component body means a new reference each render. Hoisting avoids this and makes the effects cleaner.

Proposed fix
+const PAGE_SIZE_OPTIONS = [24, 48, 96, 192] as const;
+
 export default function PeopleSearch() {
   ...
-  const PAGE_SIZE_OPTIONS = [24, 48, 96, 192] as const;

302-394: Consider extracting the loading skeleton into a separate component.

This ~90-line skeleton block adds significant visual noise to an already large component (~574 lines). Extracting it (e.g. PeopleLoadingSkeleton) would improve readability and make it reusable as the Suspense fallback in page.tsx (instead of fallback={null}).


509-551: Extract pagination page-number generation into a helper.

The inline IIFE that builds the truncated page list adds cognitive load. A small getPageNumbers(currentPage, totalPages) utility would be easier to read and unit-test independently.


228-234: updatePage reads searchParams directly — prefer the stable searchParamsString.

searchParams?.toString() on Line 229 reads the hook object, while searchParamsString (Line 84) was explicitly created as a stable primitive for effect deps. Using both patterns is inconsistent and can lead to subtle staleness bugs if searchParams reference hasn't updated yet. The same applies to updatePageSize (Line 238).

Proposed fix
   const updatePage = (page: number) => {
-    const params = new URLSearchParams(searchParams?.toString() ?? "");
+    const params = new URLSearchParams(searchParamsString);

Comment thread app/people/page.tsx
Comment on lines +6 to +8
<Suspense fallback={null}>
<PeopleSearch />
</Suspense>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

fallback={null} shows a blank page while the client bundle loads.

Until the PeopleSearch client component JS is downloaded, parsed, and mounted, the user sees nothing. Provide a lightweight loading skeleton as the fallback for better perceived performance. This is especially important on mobile / slow connections.

Proposed fix
+import { PeopleLoadingSkeleton } from "./PeopleLoadingSkeleton";
+
 export default function PeoplePage() {
   return (
-    <Suspense fallback={null}>
+    <Suspense fallback={<PeopleLoadingSkeleton />}>
       <PeopleSearch />
     </Suspense>
   );
 }

Extract the skeleton from PeopleSearch.tsx Lines 302–394 into a shared server component so it can be used here.

🤖 Prompt for AI Agents
In `@app/people/page.tsx` around lines 6 - 8, The Suspense fallback currently
returns null, causing a blank page while the PeopleSearch client bundle loads;
extract the skeleton markup (from PeopleSearch.tsx lines 302–394) into a new
shared server component (e.g., PeopleSearchSkeleton), export it, import it into
app/people/page.tsx, and replace fallback={null} with <PeopleSearchSkeleton />
so the lightweight loading UI displays until the client component mounts; ensure
the skeleton contains only server-renderable markup/props and no client-only
hooks.

@Heyy-Himanshuu Heyy-Himanshuu left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would suggest you to keep the number like 50 , 100 to maintain the consistency accross the website as other pages also uses the same format .

@Omkarchavan2527
Omkarchavan2527 force-pushed the feat/add-people-pagination branch from 8e37c4a to f96b65c Compare February 11, 2026 14:20

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@app/people/PeopleSearch.tsx`:
- Around line 86-126: The initializer for pageSize (useState for pageSize)
returns 10 when no limit param, but the sync useEffect sets pageSize to 50 by
default, causing a layout shift; make the default consistent (pick one, e.g.,
10) by updating the useEffect branch that currently does setPageSizeState(50)
and the else branch to set the same chosen default (or change the initializer
instead if you prefer 50), ensuring PAGE_SIZE_OPTIONS, pageSize, and
setPageSizeState are all using the same default value.
🧹 Nitpick comments (2)
app/people/PeopleSearch.tsx (2)

509-551: Consider extracting the page-number generation into a helper function.

The IIFE inside JSX (Lines 509–551) computes page numbers with ellipsis logic and renders buttons — this is ~40 lines of inline logic that would be easier to read and test as a standalone getPageNumbers(currentPage, totalPages) utility. Optional cleanup.


302-394: Extract loading skeleton into a shared component.

This ~90-line skeleton block is a good candidate for extraction into a standalone PeopleLoadingSkeleton server component. It could then also be used as the <Suspense fallback> in page.tsx (which currently passes null, showing a blank page while the client bundle loads).

Comment thread app/people/PeopleSearch.tsx
@Omkarchavan2527
Omkarchavan2527 force-pushed the feat/add-people-pagination branch from f96b65c to ec63ebf Compare February 11, 2026 14:27
@Heyy-Himanshuu

Copy link
Copy Markdown

CI is still red .

@Omkarchavan2527
Omkarchavan2527 force-pushed the feat/add-people-pagination branch from ec63ebf to b562bbc Compare February 12, 2026 00:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Responsive Pagination to People Page Contributors List

2 participants