Skip to content

fix Add Responsive Pagination to People Page Contributors List#258

Open
kiransatdive wants to merge 5 commits into
CircuitVerse:mainfrom
kiransatdive:feat/kiran-satdive
Open

fix Add Responsive Pagination to People Page Contributors List#258
kiransatdive wants to merge 5 commits into
CircuitVerse:mainfrom
kiransatdive:feat/kiran-satdive

Conversation

@kiransatdive

@kiransatdive kiransatdive commented Feb 11, 2026

Copy link
Copy Markdown

Description

This PR fixes issues related to the People Page Contributors List by adding responsive, client-side pagination and improving contributor interactions. The pagination is optimized for different screen sizes and keeps the page state in sync with the URL for better navigation and sharing.

fixes Issue #255

Type of change

  • Bug fix
  • Feature

What Was Fixed / Improved

-Added responsive pagination to the contributors list

@netlify

netlify Bot commented Feb 11, 2026

Copy link
Copy Markdown

Deploy Preview for cv-community-dashboard failed.

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

@coderabbitai

coderabbitai Bot commented Feb 11, 2026

Copy link
Copy Markdown

Walkthrough

Adds internal helpers to LeaderboardCard and ContributorCard to build and open GitHub search URLs, wires onClick handlers to activity chips (making them clickable) and adds cursor-pointer/hover/transition styles. Tweaks Clear/Reset button hover/active styling in LeaderboardView and removes an explicit color class from the GitFork icon in home-dashboard. Implements client-side, URL-synced pagination in app/people/page.tsx including page-size selector, navigation helpers, and dynamic pagination UI; preserves existing data fetching and exported signatures.

Possibly related PRs

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
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.
Description check ❓ Inconclusive The PR description provides a brief overview and references issue #255, but lacks completion of several template sections including Type of change selection and comprehensive checklist items. Complete the PR description by: (1) Selecting checkboxes for Type of change (Bug fix vs Feature), (2) Filling out the full Checklist with confirmations for code style, local testing, unnecessary files check, and PR title clarity, (3) Adding screenshots if applicable, (4) Providing more detailed explanation of pagination improvements in the 'What Was Fixed / Improved' section.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix Add Responsive Pagination to People Page Contributors List' partially describes the changeset. While pagination was added to the People page, the PR also includes interactive activity search functionality in LeaderboardCard and ContributorCard, plus styling updates in LeaderboardView and home-dashboard. The title focuses narrowly on pagination rather than the main functional additions.

✏️ 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 `@components/Leaderboard/LeaderboardCard.tsx`:
- Around line 165-198: getGitHubSearchUrl and openGitHubSearch are duplicated
across LeaderboardCard.tsx and ContributorCard.tsx and are recreated on every
render; extract them to a shared module (e.g., lib/github-search.ts) at module
scope, move the username regex (USERNAME_RE) there, implement and export
getGitHubSearchUrl(activityName: string, username: string): string | null and
openGitHubSearch(activityName: string, username: string): void, ensure the same
activityName -> query mappings are used in the shared function (including "PR
merged", "PR opened", "Issue opened", "Issue closed", "Issue assigned", "Issue
labeled", "Review submitted"), update both LeaderboardCard and ContributorCard
to import and call these exported functions instead of defining them inline, and
keep window.open behavior in openGitHubSearch so callers simply invoke it.
- Around line 287-294: The clickable activity items in LeaderboardCard use
onClick on a <div> (the element that calls openGitHubSearch with activityName
and entry.username and uses cn/style.bgColor/style.borderColor) which is not
keyboard-accessible; replace each such interactive <div> with a semantic
<button> (respecting existing classes/styling) or add role="button",
tabIndex={0}, and an onKeyDown handler that calls the same openGitHubSearch when
Enter or Space is pressed, and ensure focus styles remain visible; update all
similar occurrences in this component where onClick is used for interactivity.
- Around line 184-185: In the LeaderboardCard component's switch handling for
case "Issue labeled", replace the invalid GitHub search qualifier `labeler:`
with the correct `label:` if the goal is to search by label name (update the
returned URL in the case "Issue labeled" inside LeaderboardCard.tsx), or if the
real intent is to find issues labeled by a specific user (actor), change the
implementation to call the GitHub API (e.g., via REST search/issues or GraphQL)
and filter by events/actor instead, since GitHub search does not support
filtering by the user who applied a label.
🧹 Nitpick comments (1)
components/people/ContributorCard.tsx (1)

52-77: Subset of activity types handled vs LeaderboardCard.

This version of getGitHubSearchUrl only handles 3 activity types while the copy in LeaderboardCard.tsx handles 7. If new activity types are displayed here in the future, the search links will silently fail. This is another reason to consolidate into a shared utility as suggested in the LeaderboardCard review.

Comment thread components/Leaderboard/LeaderboardCard.tsx
Comment thread components/Leaderboard/LeaderboardCard.tsx
Comment thread components/Leaderboard/LeaderboardCard.tsx

@Git-HimanshuRathi Git-HimanshuRathi 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.

@kiransatdive can u fill the PR template and also mention which issue your are fixing ?

- Implement pagination for contributor cards (12, 24, 48, 96, All options)
- Add URL parameter handling for page and limit (?page=2&limit=48)
- Add pagination controls with previous/next buttons and page numbers
- Add intelligent ellipsis for large page counts
- Maintain search functionality with page reset
- Ensure responsive design and accessibility
- Consistent with Leaderboard page pagination behavior

Fixes issue with ~278 contributors showing in single continuous list
- Resolved merge conflict in app/people/page.tsx
- Kept pagination implementation from main branch
- Ready to push pagination feature to feat/kiran-satdive branch

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/people/page.tsx (1)

52-63: ⚠️ Potential issue | 🟡 Minor

res.json() returned without await in an async function.

Line 59 returns the raw Promise from res.json() without awaiting it. While this works (the caller awaits the outer async function), if res.json() throws a parse error, it will reject the returned promise rather than being caught by the surrounding try/catch block. This means a malformed JSON response bypasses the graceful error handling and instead surfaces as an unhandled rejection in the loadData effect's catch.

Suggested fix
-    return res.json();
+    return await res.json();
🤖 Fix all issues with AI agents
In `@app/people/page.tsx`:
- Around line 397-411: The JSX block for the contributors section (the outer
<div className="mb-8"> containing the TITLE h2 and DESCRIPTION p and its closing
</div>) has inconsistent indentation; reformat that entire block to match the
surrounding file's JSX indentation style (use the same 2-space or 4-space
nesting used in the rest of app/people/page.tsx), ensuring each child element
(the inner <div className="mb-4">, <h2>, <span>s, and <p
className="text-lg...">) is indented consistently and the closing </div> aligns
with its opening tag.
- Around line 201-220: The pagination local state is being redundantly synced
with URL params causing divergence and extra renders; remove the two useEffect
syncs that read searchParams and call setCurrentPage/setPageSize, instead derive
currentPage and pageSize directly from searchParams using useMemo (or compute
inline) so the URL is the single source of truth, and update the helpers
updatePage and updatePageSize to only call router.replace(...) (or router.push)
to update searchParams without calling setState; keep references to
searchParams, setCurrentPage, setPageSize, updatePage, and updatePageSize so you
can locate and remove the state writes and replace them with URL-only updates
and memoized reads.
- Around line 229-234: The effect that currently resets to page 1 when
currentPage > totalPages should instead clamp the page to the last valid page;
update the useEffect (watching currentPage and totalPages) to call
updatePage(totalPages) when currentPage > totalPages && totalPages > 0 so that
pages bookmarked beyond the dataset are corrected to the last page rather than
silently jumping to page 1; keep the existing guards (totalPages > 0) and
reference useEffect, updatePage, currentPage, totalPages, and filteredPeople
when making the change.
- Around line 222-227: The useEffect that resets pagination uses updatePage,
currentPage, and pageSize but only lists searchQuery in its deps, causing a
stale closure; update the effect to either inline the URL-update logic (read and
set searchParams/pathname/router directly) or ensure updatePage is stable by
wrapping it in useCallback and include updatePage, currentPage, and pageSize
(and any values it closes over such as searchParams, pathname, router) in the
dependency array; similarly fix the other effect at lines 229–234 so all
referenced symbols (updatePage, currentPage, pageSize, searchParams, pathname,
router) are properly included or the logic is inlined to make the URL the single
source of truth.
🧹 Nitpick comments (1)
app/people/page.tsx (1)

413-482: Inconsistent indentation continues in the main content area.

Lines 413–414 jump to 12-space indentation while the surrounding contributor title/description block (lines 397–411) used a different indentation scheme. This suggests the two blocks were authored or pasted separately. Please normalize to a single consistent indent level throughout the returned JSX.

Comment thread app/people/page.tsx
Comment on lines +201 to +220
// Sync pagination state with URL params
useEffect(() => {
const page = searchParams.get('page');
const parsedPage = page ? parseInt(page, 10) : 1;
if (parsedPage > 0) {
setCurrentPage(parsedPage);
}
}, [searchParams]);

useEffect(() => {
const limit = searchParams.get('limit');
if (limit === 'all') {
setPageSize(Infinity);
} else {
const parsedLimit = limit ? parseInt(limit, 10) : 24;
if ([12, 24, 48, 96].includes(parsedLimit)) {
setPageSize(parsedLimit);
}
}
}, [searchParams]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Redundant bidirectional sync between URL and local state is fragile.

There are two sources of truth for currentPage and pageSize: local useState and URL search params. The helpers (updatePage, updatePageSize) write to both, and these useEffect hooks read from URL back into local state. This creates:

  1. A brief window each render where local state and URL can diverge.
  2. Unnecessary re-renders (state is set in the helper, then the effect sets it again from the URL).
  3. Hard-to-trace bugs when external navigation (browser back/forward) changes the URL.

Consider deriving pagination values directly from searchParams via useMemo, making the URL the single source of truth:

Sketch: derive from URL instead of syncing
-  const [pageSize, setPageSize] = useState<number>(() => { ... });
-  const [currentPage, setCurrentPage] = useState<number>(() => { ... });
+  const pageSize = useMemo(() => {
+    const limit = searchParams.get('limit');
+    if (limit === 'all') return Infinity;
+    const parsed = limit ? parseInt(limit, 10) : 24;
+    return [12, 24, 48, 96].includes(parsed) ? parsed : 24;
+  }, [searchParams]);
+
+  const currentPage = useMemo(() => {
+    const page = searchParams.get('page');
+    const parsed = page ? parseInt(page, 10) : 1;
+    return parsed > 0 ? parsed : 1;
+  }, [searchParams]);

Then the updatePage / updatePageSize helpers only need to call router.replace(...) — no setState calls, no sync effects.

🤖 Prompt for AI Agents
In `@app/people/page.tsx` around lines 201 - 220, The pagination local state is
being redundantly synced with URL params causing divergence and extra renders;
remove the two useEffect syncs that read searchParams and call
setCurrentPage/setPageSize, instead derive currentPage and pageSize directly
from searchParams using useMemo (or compute inline) so the URL is the single
source of truth, and update the helpers updatePage and updatePageSize to only
call router.replace(...) (or router.push) to update searchParams without calling
setState; keep references to searchParams, setCurrentPage, setPageSize,
updatePage, and updatePageSize so you can locate and remove the state writes and
replace them with URL-only updates and memoized reads.

Comment thread app/people/page.tsx
Comment on lines +222 to +227
// Reset to page 1 when search query changes
useEffect(() => {
if (currentPage !== 1 && pageSize !== Infinity) {
updatePage(1);
}
}, [searchQuery]);

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 | 🟠 Major

Missing dependencies in useEffect — stale closure risk.

updatePage is called inside this effect but is not listed in the dependency array. Since updatePage closes over searchParams, pathname, and router, a stale closure can occur when URL state changes between renders without searchQuery changing. Additionally, currentPage and pageSize are read but also not declared as dependencies.

The React exhaustive-deps rule would flag this. The same issue applies to the effect at lines 229–234.

Suggested fix: inline the URL update or wrap helpers in useCallback
-  // Reset to page 1 when search query changes
-  useEffect(() => {
-    if (currentPage !== 1 && pageSize !== Infinity) {
-      updatePage(1);
-    }
-  }, [searchQuery]);
+  // Reset to page 1 when search query changes
+  useEffect(() => {
+    if (currentPage !== 1 && pageSize !== Infinity) {
+      const params = new URLSearchParams(searchParams.toString());
+      params.delete("page");
+      setCurrentPage(1);
+      router.replace(`${pathname}?${params.toString()}`, { scroll: false });
+    }
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [searchQuery]);

A cleaner long-term approach: derive currentPage and pageSize directly from searchParams (making the URL the single source of truth) instead of duplicating into local state and syncing via effects. This eliminates the entire class of stale-closure and out-of-sync bugs.

🤖 Prompt for AI Agents
In `@app/people/page.tsx` around lines 222 - 227, The useEffect that resets
pagination uses updatePage, currentPage, and pageSize but only lists searchQuery
in its deps, causing a stale closure; update the effect to either inline the
URL-update logic (read and set searchParams/pathname/router directly) or ensure
updatePage is stable by wrapping it in useCallback and include updatePage,
currentPage, and pageSize (and any values it closes over such as searchParams,
pathname, router) in the dependency array; similarly fix the other effect at
lines 229–234 so all referenced symbols (updatePage, currentPage, pageSize,
searchParams, pathname, router) are properly included or the logic is inlined to
make the URL the single source of truth.

Comment thread app/people/page.tsx
Comment on lines +229 to +234
// Reset to page 1 when filtered results change significantly
useEffect(() => {
if (currentPage > totalPages && totalPages > 0) {
updatePage(1);
}
}, [currentPage, totalPages]);

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

This safety-net effect can trigger an unnecessary navigation on initial load.

When data first loads, filteredPeople goes from [] to a populated array. Before data arrives, totalPages is 0 (from Math.ceil(0 / pageSize)), and if the URL had ?page=2, currentPage would be 2. The condition currentPage > totalPages && totalPages > 0 guards against totalPages === 0, so this specific scenario is safe. However, if a user bookmarks ?page=5 but the dataset only has 3 pages, this resets silently to page 1 instead of the last valid page — a minor UX concern.

Consider clamping to totalPages instead of resetting to 1:

Suggested fix
   useEffect(() => {
     if (currentPage > totalPages && totalPages > 0) {
-      updatePage(1);
+      updatePage(totalPages);
     }
   }, [currentPage, totalPages]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Reset to page 1 when filtered results change significantly
useEffect(() => {
if (currentPage > totalPages && totalPages > 0) {
updatePage(1);
}
}, [currentPage, totalPages]);
// Reset to page 1 when filtered results change significantly
useEffect(() => {
if (currentPage > totalPages && totalPages > 0) {
updatePage(totalPages);
}
}, [currentPage, totalPages]);
🤖 Prompt for AI Agents
In `@app/people/page.tsx` around lines 229 - 234, The effect that currently resets
to page 1 when currentPage > totalPages should instead clamp the page to the
last valid page; update the useEffect (watching currentPage and totalPages) to
call updatePage(totalPages) when currentPage > totalPages && totalPages > 0 so
that pages bookmarked beyond the dataset are corrected to the last page rather
than silently jumping to page 1; keep the existing guards (totalPages > 0) and
reference useEffect, updatePage, currentPage, totalPages, and filteredPeople
when making the change.

Comment thread app/people/page.tsx
Comment on lines +397 to +411
<div className="mb-8">
{/* TITLE */}
<div className="mb-4">
<h2 className="text-3xl font-bold">
<span className="text-black dark:text-white">Community </span>
<span className="text-[#42B883]">Contributors</span>
</h2>
</div>

<p className="text-lg text-muted-foreground max-w-3xl mb-6">
Amazing community members who contribute to CircuitVerse through
code, documentation, and more.
</p>
<h2 className="text-3xl font-bold">
<span className="text-black dark:text-white">Community </span>
<span className="text-[#42B883]">Contributors</span>
</h2>
</div>

<div className="flex flex-col gap-4">
<PeopleStats
contributors={filteredPeople}
allContributors={people}
onContributorClick={handleContributorClick}
/>

<div className="flex items-center justify-between gap-4 py-8">
<div className="flex items-center gap-2">
<Users className="w-5 h-5 text-muted-foreground" />
<span className="text-2xl font-bold text-foreground">
{filteredPeople.length}{' '}
<span className="text-[#42B883]">
{filteredPeople.length === 1 ? 'Contributor' : 'Contributors'}
</span>
{searchQuery && <span className="text-foreground"> found</span>}
</span>
</div>

<div className="relative w-full sm:w-72">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input
type="text"
placeholder="Search contributors..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-9 h-10"
/>
</div>
</div>

<PeopleGrid
contributors={filteredPeople}
onContributorClick={handleContributorClick}
viewMode="grid"
loading={false}
/>
</div>
</section>
{/* DESCRIPTION */}
<p className="text-lg text-muted-foreground max-w-3xl mb-6">
Amazing community members who contribute to CircuitVerse through
code, documentation, and more.
</p>
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Severely inconsistent indentation in the contributors section.

This block mixes 0, 2, 4, and 8-space indentation levels across siblings and parent-child elements. For example, the closing </div> at line 411 is at column 0 while its opening tag at line 397 is indented. This makes the JSX structure very hard to follow and maintain.

Please re-indent this block to match the surrounding code's indentation style (the rest of the file uses consistent 2-space or 4-space nesting within the JSX tree).

🤖 Prompt for AI Agents
In `@app/people/page.tsx` around lines 397 - 411, The JSX block for the
contributors section (the outer <div className="mb-8"> containing the TITLE h2
and DESCRIPTION p and its closing </div>) has inconsistent indentation; reformat
that entire block to match the surrounding file's JSX indentation style (use the
same 2-space or 4-space nesting used in the rest of app/people/page.tsx),
ensuring each child element (the inner <div className="mb-4">, <h2>, <span>s,
and <p className="text-lg...">) is indented consistently and the closing </div>
aligns with its opening tag.

@kiransatdive

Copy link
Copy Markdown
Author

@kiransatdive can u fill the PR template and also mention which issue your are fixing ?

okay

@kiransatdive kiransatdive marked this pull request as draft February 11, 2026 16:18
@kiransatdive kiransatdive marked this pull request as ready for review February 11, 2026 16:18
@kiransatdive kiransatdive changed the title Feat/kiran satdive Add Responsive Pagination to People Page Contributors List #255 Feb 11, 2026
@kiransatdive kiransatdive changed the title Add Responsive Pagination to People Page Contributors List #255 Potential issue Feb 11, 2026
@kiransatdive kiransatdive changed the title Potential issue fix Add Responsive Pagination to People Page Contributors List Feb 11, 2026
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.

3 participants