Skip to content

Cpride/pagination#1130

Merged
corypride merged 5 commits into
CK-7vn/total-refactorfrom
cpride/pagination
May 18, 2026
Merged

Cpride/pagination#1130
corypride merged 5 commits into
CK-7vn/total-refactorfrom
cpride/pagination

Conversation

@corypride

@corypride corypride commented May 14, 2026

Copy link
Copy Markdown
Contributor

Pre-Submission PR Checklist

  • No debug/console/fmt.Println statements
  • Unnecessary development comments removed
  • All acceptance criteria verified
  • Functions according to ticket specifications
  • Tested manually where applicable
  • Branch rebased with latest main
  • No business logic exists within the database layer

Description of the change

Centralizes pagination state management site-wide by migrating all paginated pages from local useState to the useUrlPagination hook, so pagination state is preserved in the URL and survives browser refresh and back navigation.

  • Deleted the degraded components/shared/Pagination.tsx duplicate; re-exported components/Pagination.tsx from the shared index so all consumers get the canonical component
  • Added an optional prefix param to useUrlPagination for pages with multiple independent paginators (e.g. activity_page, history_page, audit_page)
  • Added instanceId prop to Pagination.tsx to keep select element IDs unique when multiple instances appear on one page
  • Replaced bare prev/next buttons in ClassEnrollmentDetails and AddClassEnrollments with the full Pagination component

Related issues: Asana — Split 4: Pagination

Screenshot(s)

test_3_7_history_page2_persists test_3_8_audit_page2_persists test_3_4_kcm_tab_switching test_2_4_add_class_enrollments_pagination test_2_3_class_enrollment_details_pagination test_2_2_dark_mode_active_page test_2_1_admin_management_pagination

Additional context

Bug found and fixed during testing — per_page dropped from URL when changing items-per-page: The Pagination.tsx items-per-page onChange handler was calling both onItemsPerPageChange(value) and onPageChange(1). Both closures captured stale searchParams, so the second setSearchParams overwrote the first, silently dropping per_page from the URL. Fixed by removing the redundant onPageChange(1)setPerPage in useUrlPagination already resets page to 1 internally.

Bug found and fixed during testing — activity_page resets to 1 on every mount: ActivityHistoryCard had a useEffect(() => setPage(1), [filterQuery]) that fired on every mount. Before this PR it reset a local useState value harmlessly; after migration it called setSearchParams, overwriting activity_page back to 1 on every page load. Fixed by adding a useRef(true) first-render guard so the reset only fires when filterQuery actually changes from a user action.


@corypride corypride requested a review from a team as a code owner May 14, 2026 16:21
@corypride corypride requested review from carddev81 and removed request for a team May 14, 2026 16:21
@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Pagination is now URL-driven across multiple pages, letting users bookmark/share specific page and page-size states.
  • Improvements

    • Changing items-per-page no longer forces the view back to page 1.
    • Pagination is rendered more consistently for results (shows when any results exist), improving navigation predictability.

Walkthrough

This PR migrates pagination state from component-local React state to URL-synchronized state across the application. The Pagination component gains DOM ID customization via instanceId prop and moves to a top-level location, while the useUrlPagination hook adds prefix-based query parameter namespacing to support multiple independent pagination instances per page. Ten pages are updated to use the enhanced hook and simplified component behavior.

Changes

Pagination Refactor

Layer / File(s) Summary
Pagination component enhancement and migration
frontend/src/components/Pagination.tsx, frontend/src/components/shared/Pagination.tsx, frontend/src/components/shared/index.ts
Pagination moves from shared/ to top-level components/, gains optional instanceId prop for custom DOM ID prefixes via useId(), updates label/select id wiring, and stops resetting the current page when items-per-page changes. The old shared file and its barrel re-export were removed.
useUrlPagination hook enhancement
frontend/src/hooks/useUrlPagination.ts
Hook adds an optional prefix parameter to namespace query keys (pageKey/perPageKey), validates parsed values (>= 1) with fallbacks, and memoizes setPage/setPerPage via useCallback.
Admin and core pages pagination migration
frontend/src/pages/ClassesPage.tsx, frontend/src/pages/ProgramsPage.tsx, frontend/src/pages/admin/AdminManagement.tsx, frontend/src/pages/admin/FacilityManagement.tsx
These pages replace local useState pagination with useUrlPagination, update paginated slice and API query construction to use hook values, reset page via the hook setter on filter/search/sort changes, and wire Pagination directly to setPage/setPerPage.
Activity history and audit pages pagination migration
frontend/src/components/student/ActivityHistoryCard.tsx, frontend/src/pages/class-detail/AuditTab.tsx
ActivityHistoryCard and AuditTab adopt useUrlPagination with prefixes ('activity', 'audit'), add a first-render guard for filter-driven page resets, change pagination render conditions to total > 0, and simplify items-per-page handlers to call setPerPage.
Knowledge center pages pagination migration
frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx, frontend/src/pages/knowledge-center/ResidentKnowledgeCenter.tsx
Knowledge center pages switch to useUrlPagination, update Pagination import paths to the new component, and remove page-reset logic from items-per-page handlers.
Program enrollment pages pagination migration
frontend/src/pages/programs/AddClassEnrollments.tsx, frontend/src/pages/programs/ClassEnrollmentDetails.tsx, frontend/src/pages/programs/ProgramOverviewFacilityAdmin.tsx
Enrollment pages replace custom Previous/Next pagination UI and local state with useUrlPagination and the shared Pagination component, use optional prefixes for multi-pagination scenarios, remove computed totalPages, and stop resetting page when items-per-page changes.
🚥 Pre-merge checks | ✅ 3 | ❌ 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.
Title check ❓ Inconclusive The title 'Cpride/pagination' is a branch name or generic identifier that does not clearly describe the main change in the changeset. Use a descriptive title like 'Centralize pagination state management with useUrlPagination hook' to clearly convey the main change.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description is comprehensive and directly related to the changeset, explaining the centralization of pagination state management and the specific changes made throughout the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/components/Pagination.tsx`:
- Around line 19-20: The default prop instanceId = 'pagination' causes duplicate
id/htmlFor collisions; update the Pagination component to generate a unique
fallback when instanceId is not provided (e.g., use React's useId or a short
unique generator) and use that generated value wherever instanceId is referenced
(look for the instanceId prop usage and any id/htmlFor attributes in
Pagination). Ensure the prop signature (instanceId) remains optional but falls
back to the generated id, and apply the same fix to the other occurrences noted
around lines 76–80 where the hardcoded 'pagination' is used.

In `@frontend/src/hooks/useUrlPagination.ts`:
- Around line 9-16: In useUrlPagination, parsing page and perPage from
searchParams with parseInt can produce NaN or non-positive values; update the
logic that reads searchParams.get(pageKey)/get(perPageKey) so that after
parseInt you validate and normalize values: if parsed page is NaN or < 1 use
defaultPage, and if parsed perPage is NaN or <= 0 use defaultPerPage (optionally
clamp perPage to a sensible max); reference the variables searchParams, pageKey,
perPageKey, defaultPage and defaultPerPage and replace the direct parseInt usage
with this guarded normalization so pagination math always receives safe positive
integers.
- Around line 18-29: setPage and setPerPage build URLSearchParams from the
render-time searchParams which can cause race/stale overwrites; change both to
use the functional updater form of setSearchParams(prev => ...) so each call
builds a new URLSearchParams from the current prev value, then set the
pageKey/perPageKey as needed (setPerPage should also reset pageKey to '1'), and
pass the appropriate replace option to setSearchParams({ replace: ... }) so
updates always merge against the latest params rather than a stale snapshot.

In `@frontend/src/pages/ClassesPage.tsx`:
- Line 97: The mount effect in ClassesPage that unconditionally calls
setCurrentPage(1) is clobbering the URL-backed pagination provided by
useUrlPagination; remove or guard that effect so it does not run on initial
render. Locate the useEffect in ClassesPage that calls setCurrentPage(1) (and
any related setItemsPerPage) and either delete the effect entirely or wrap it so
it only runs on real dependency changes (e.g., track an isMounted ref or check
previous values) rather than on mount, ensuring useUrlPagination's currentPage
from the URL is preserved.

In `@frontend/src/pages/ProgramsPage.tsx`:
- Line 179: The page URL state isn't reset when search/sort/filter criteria
change, causing out-of-range empty results; in ProgramsPage add a useEffect that
calls setPage(1) whenever any query criteria change (e.g., searchTerm,
selectedFilters, sortBy, category, etc.) so the URL-backed page resets to the
first page; locate useUrlPagination usage (const { page, perPage, setPage,
setPerPage } = useUrlPagination(1, 10)) and add the effect referencing setPage
and the specific criterion state variables used elsewhere in this component
(also apply the same fix where similar pagination is used around lines 289-290).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3f40077e-ac33-4225-b14a-e4fdd74f9147

📥 Commits

Reviewing files that changed from the base of the PR and between 96c36e4 and 5635518.

📒 Files selected for processing (16)
  • frontend/src/components/Pagination.tsx
  • frontend/src/components/shared/Pagination.tsx
  • frontend/src/components/shared/index.ts
  • frontend/src/components/student/ActivityHistoryCard.tsx
  • frontend/src/hooks/useUrlPagination.ts
  • frontend/src/pages/ClassesPage.tsx
  • frontend/src/pages/ProgramsPage.tsx
  • frontend/src/pages/admin/AdminManagement.tsx
  • frontend/src/pages/admin/FacilityManagement.tsx
  • frontend/src/pages/admin/ProviderUserManagement.tsx
  • frontend/src/pages/class-detail/AuditTab.tsx
  • frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx
  • frontend/src/pages/knowledge-center/ResidentKnowledgeCenter.tsx
  • frontend/src/pages/programs/AddClassEnrollments.tsx
  • frontend/src/pages/programs/ClassEnrollmentDetails.tsx
  • frontend/src/pages/programs/ProgramOverviewFacilityAdmin.tsx
💤 Files with no reviewable changes (1)
  • frontend/src/components/shared/Pagination.tsx

Comment thread frontend/src/components/Pagination.tsx Outdated
Comment thread frontend/src/hooks/useUrlPagination.ts Outdated
Comment thread frontend/src/hooks/useUrlPagination.ts Outdated
Comment thread frontend/src/pages/ClassesPage.tsx
Comment thread frontend/src/pages/ProgramsPage.tsx
@corypride corypride force-pushed the cpride/pagination branch from 5635518 to 707ea41 Compare May 14, 2026 16:32

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

Caution

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

⚠️ Outside diff range comments (1)
frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx (1)

339-344: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider adding setter functions to the dependency array.

While React guarantees that setCurrentPage and setCategoryFilter are stable, the exhaustive-deps ESLint rule would flag these missing dependencies. Adding them improves code clarity and satisfies linting rules.

📋 Suggested dependency array update
 useEffect(() => {
     if (currentTab !== 'libraries') {
         setCategoryFilter('all');
     }
     setCurrentPage(1);
-}, [currentTab]);
+}, [currentTab, setCategoryFilter, setCurrentPage]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx` around
lines 339 - 344, The useEffect that resets category and page when currentTab
changes should include the setter functions in its dependency array to satisfy
exhaustive-deps and clarify intent: update the effect watching currentTab so it
lists setCategoryFilter and setCurrentPage along with currentTab (i.e.,
useEffect(..., [currentTab, setCategoryFilter, setCurrentPage])), leaving the
existing body that calls setCategoryFilter('all') when currentTab !==
'libraries' and setCurrentPage(1).
♻️ Duplicate comments (2)
frontend/src/hooks/useUrlPagination.ts (1)

15-25: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use functional setSearchParams updates to avoid stale query overwrites.

setPage/setPerPage are still based on render-time searchParams, so sequential updates can drop each other’s changes. This is the same stale-param issue previously seen with pagination URL state.

Proposed fix
     const setPage = (newPage: number, options?: { replace?: boolean }) => {
-        const params = new URLSearchParams(searchParams);
-        params.set(pageKey, newPage.toString());
-        setSearchParams(params, { replace: options?.replace ?? false });
+        setSearchParams((prev) => {
+            const params = new URLSearchParams(prev);
+            params.set(pageKey, newPage.toString());
+            return params;
+        }, { replace: options?.replace ?? false });
     };

     const setPerPage = (newPerPage: number) => {
-        const params = new URLSearchParams(searchParams);
-        params.set(perPageKey, newPerPage.toString());
-        params.set(pageKey, '1');
-        setSearchParams(params, { replace: false });
+        setSearchParams((prev) => {
+            const params = new URLSearchParams(prev);
+            params.set(perPageKey, newPerPage.toString());
+            params.set(pageKey, '1');
+            return params;
+        }, { replace: false });
     };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/hooks/useUrlPagination.ts` around lines 15 - 25, setPage and
setPerPage currently read from the render-time searchParams which can cause
stale overwrites; change both to use the functional form of setSearchParams so
you operate on the latest params (e.g., setSearchParams(prev => { const params =
new URLSearchParams(prev); params.set(pageKey, newPage.toString()); return
params; }, { replace: options?.replace ?? false }) for setPage, and likewise for
setPerPage set perPageKey and reset pageKey to '1' inside setSearchParams(prev
=> { ... }) with replace: false) so updates compose safely.
frontend/src/pages/ClassesPage.tsx (1)

217-219: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard the page-reset effect on initial mount.

Line 217 runs setCurrentPage(1) on first render, which overwrites URL-backed pagination and breaks refresh/back navigation persistence. Add a first-render guard before resetting.

Suggested fix
-import { useState, useMemo, useEffect } from 'react';
+import { useState, useMemo, useEffect, useRef } from 'react';
...
+const isFirstRender = useRef(true);
...
 useEffect(() => {
+    if (isFirstRender.current) {
+        isFirstRender.current = false;
+        return;
+    }
     setCurrentPage(1);
-}, [searchQuery, todayOnly, attendanceConcerns, facilityFilter, programFilter, statusFilter]);
+}, [searchQuery, todayOnly, attendanceConcerns, facilityFilter, programFilter, statusFilter, setCurrentPage]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/ClassesPage.tsx` around lines 217 - 219, The effect that
calls setCurrentPage(1) (inside the useEffect at top-level of ClassesPage) runs
on initial mount and clobbers URL-backed pagination; add a first-render guard
(e.g., a useRef isInitialMount or check if currentPage is undefined) so the
effect only resets the page when searchQuery, todayOnly, attendanceConcerns,
facilityFilter, programFilter, or statusFilter change after mount, not on the
initial render; update the useEffect to bail out if isInitialMount is true (or
if currentPage already exists) and then flip the ref to false so subsequent
dependency changes will call setCurrentPage(1).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx`:
- Around line 339-344: The useEffect that resets category and page when
currentTab changes should include the setter functions in its dependency array
to satisfy exhaustive-deps and clarify intent: update the effect watching
currentTab so it lists setCategoryFilter and setCurrentPage along with
currentTab (i.e., useEffect(..., [currentTab, setCategoryFilter,
setCurrentPage])), leaving the existing body that calls setCategoryFilter('all')
when currentTab !== 'libraries' and setCurrentPage(1).

---

Duplicate comments:
In `@frontend/src/hooks/useUrlPagination.ts`:
- Around line 15-25: setPage and setPerPage currently read from the render-time
searchParams which can cause stale overwrites; change both to use the functional
form of setSearchParams so you operate on the latest params (e.g.,
setSearchParams(prev => { const params = new URLSearchParams(prev);
params.set(pageKey, newPage.toString()); return params; }, { replace:
options?.replace ?? false }) for setPage, and likewise for setPerPage set
perPageKey and reset pageKey to '1' inside setSearchParams(prev => { ... }) with
replace: false) so updates compose safely.

In `@frontend/src/pages/ClassesPage.tsx`:
- Around line 217-219: The effect that calls setCurrentPage(1) (inside the
useEffect at top-level of ClassesPage) runs on initial mount and clobbers
URL-backed pagination; add a first-render guard (e.g., a useRef isInitialMount
or check if currentPage is undefined) so the effect only resets the page when
searchQuery, todayOnly, attendanceConcerns, facilityFilter, programFilter, or
statusFilter change after mount, not on the initial render; update the useEffect
to bail out if isInitialMount is true (or if currentPage already exists) and
then flip the ref to false so subsequent dependency changes will call
setCurrentPage(1).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a40a979a-270d-4140-aee2-7da95777d671

📥 Commits

Reviewing files that changed from the base of the PR and between 5635518 and 673a584.

📒 Files selected for processing (15)
  • frontend/src/components/Pagination.tsx
  • frontend/src/components/shared/Pagination.tsx
  • frontend/src/components/shared/index.ts
  • frontend/src/components/student/ActivityHistoryCard.tsx
  • frontend/src/hooks/useUrlPagination.ts
  • frontend/src/pages/ClassesPage.tsx
  • frontend/src/pages/ProgramsPage.tsx
  • frontend/src/pages/admin/AdminManagement.tsx
  • frontend/src/pages/admin/FacilityManagement.tsx
  • frontend/src/pages/class-detail/AuditTab.tsx
  • frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx
  • frontend/src/pages/knowledge-center/ResidentKnowledgeCenter.tsx
  • frontend/src/pages/programs/AddClassEnrollments.tsx
  • frontend/src/pages/programs/ClassEnrollmentDetails.tsx
  • frontend/src/pages/programs/ProgramOverviewFacilityAdmin.tsx
💤 Files with no reviewable changes (1)
  • frontend/src/components/shared/Pagination.tsx

@carddev81 carddev81 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When selecting to display more on the page it does not display what is selected. See page below, holding off on the review till this is corrected since this will reflect all the changes made.

To reproduce, just go to the residents listing and select 40 from the items to display

@corypride

Copy link
Copy Markdown
Contributor Author
test.mp4

@carddev81 Could not reproduce as you stated. Send me a video

@carddev81 carddev81 self-requested a review May 18, 2026 14:59

@carddev81 carddev81 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@corypride I don\t understand why I am not getting that error I posted, but here are the only findings I could spot. Great Job.

Audit History (Please check all locations) - See video--After selecting to display more than 20 records at a time the pagination controls disappear.

2026-05-18.10-09-40.mp4

Linting errors:

  1. ClassesPage.tsx (Line 219): error is -> React Hook useEffect has a missing dependency: 'setCurrentPage'. Either include it or remove the dependency array
  2. KnowledgeCenterManagement.tsx (344): error is -> React Hook useEffect has a missing dependency: 'setCurrentPage'. Either include it or remove the dependency array.

Inconsistency

The ClassesPage.tsxk (line 50) imports Pagination from @/components/shared while every other location it is needed imports from @/components/Pagination. The re-export in shared/index.ts makes it work, but it's inconsistent. Either update ClassesPage or delete the re-export. I say delete the re-export because it's not even being used by any other page.

Please fix these issues and merge your code into the total refactor branch please.

@corypride

corypride commented May 18, 2026 via email

Copy link
Copy Markdown
Contributor Author

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

Caution

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

⚠️ Outside diff range comments (1)
frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx (1)

339-344: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve URL page on initial mount.

Line 343 unconditionally calls setCurrentPage(1), which overwrites ?page=... on first render and breaks refresh/back pagination persistence.

Suggested fix
+    const hasInitializedTabReset = useRef(false);
+
     useEffect(() => {
+        if (!hasInitializedTabReset.current) {
+            hasInitializedTabReset.current = true;
+            return;
+        }
         if (currentTab !== 'libraries') {
             setCategoryFilter('all');
         }
         setCurrentPage(1);
     }, [currentTab, setCurrentPage]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx` around
lines 339 - 344, The effect is resetting page on first render and overwriting
any ?page=... param; update the useEffect that reads currentTab to skip calling
setCurrentPage(1) on initial mount by adding a mounted ref (e.g., isInitialMount
using useRef) or tracking previous tab, and only call setCurrentPage(1) when
currentTab changes after mount (but still always apply the category reset logic
for setCategoryFilter('all')); tweak the existing useEffect (the one referencing
currentTab, setCurrentPage, setCategoryFilter) to guard the setCurrentPage call
with that isInitialMount check so URL page state is preserved on initial load.
♻️ Duplicate comments (2)
frontend/src/hooks/useUrlPagination.ts (1)

16-33: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use functional setSearchParams updates in both setters.

Line 18 and Line 27 clone closed-over searchParams; sequential updates can still drop keys (the same class of bug this PR hit with per_page). Use functional updates so each write merges from current params.

Proposed fix
     const setPage = useCallback(
         (newPage: number, options?: { replace?: boolean }) => {
-            const params = new URLSearchParams(searchParams);
-            params.set(pageKey, newPage.toString());
-            setSearchParams(params, { replace: options?.replace ?? false });
+            setSearchParams((prev) => {
+                const params = new URLSearchParams(prev);
+                params.set(pageKey, newPage.toString());
+                return params;
+            }, { replace: options?.replace ?? false });
         },
-        [searchParams, setSearchParams, pageKey]
+        [setSearchParams, pageKey]
     );
@@
     const setPerPage = useCallback(
         (newPerPage: number) => {
-            const params = new URLSearchParams(searchParams);
-            params.set(perPageKey, newPerPage.toString());
-            params.set(pageKey, '1');
-            setSearchParams(params, { replace: false });
+            setSearchParams((prev) => {
+                const params = new URLSearchParams(prev);
+                params.set(perPageKey, newPerPage.toString());
+                params.set(pageKey, '1');
+                return params;
+            }, { replace: false });
         },
-        [searchParams, setSearchParams, pageKey, perPageKey]
+        [setSearchParams, pageKey, perPageKey]
     );
For react-router-dom v6.23.0 `useSearchParams`, confirm:
1) functional updates (`setSearchParams(prev => ...)`) are supported, and
2) multiple `setSearchParams` calls in the same tick are not queued like React state updates.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/hooks/useUrlPagination.ts` around lines 16 - 33, The setters
setPage and setPerPage close over the stale searchParams and create new
URLSearchParams from that snapshot, which can drop concurrent query keys; change
both to use the functional form of setSearchParams (setSearchParams(prev => {
... })) so each update builds from the latest params, and in setPerPage ensure
you set perPageKey and reset pageKey inside that functional update; verify
useSearchParams' setSearchParams supports functional updates before applying.
frontend/src/pages/ClassesPage.tsx (1)

217-219: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent mount-time page reset from clobbering URL state.

Line 217 runs on initial render and forces page=1, which breaks refresh/back persistence for this page. Guard first render so reset only happens after real filter changes.

Proposed fix
-import { useState, useMemo, useEffect } from 'react';
+import { useState, useMemo, useEffect, useRef } from 'react';
@@
 export default function ClassesPage() {
+    const isFirstRender = useRef(true);
@@
     useEffect(() => {
+        if (isFirstRender.current) {
+            isFirstRender.current = false;
+            return;
+        }
         setCurrentPage(1);
     }, [searchQuery, todayOnly, attendanceConcerns, facilityFilter, programFilter, statusFilter, setCurrentPage]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/ClassesPage.tsx` around lines 217 - 219, The effect that
calls setCurrentPage(1) runs on mount and clobbers URL/page state; change the
useEffect (the one referencing useEffect and setCurrentPage with dependencies
searchQuery, todayOnly, attendanceConcerns, facilityFilter, programFilter,
statusFilter) to skip its first invocation—e.g. add an isInitialMount useRef (or
track previous filter values) and only call setCurrentPage(1) when not the
initial render (i.e. when a real filter change occurs) so refresh/back preserve
the page query.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx`:
- Around line 339-344: The effect is resetting page on first render and
overwriting any ?page=... param; update the useEffect that reads currentTab to
skip calling setCurrentPage(1) on initial mount by adding a mounted ref (e.g.,
isInitialMount using useRef) or tracking previous tab, and only call
setCurrentPage(1) when currentTab changes after mount (but still always apply
the category reset logic for setCategoryFilter('all')); tweak the existing
useEffect (the one referencing currentTab, setCurrentPage, setCategoryFilter) to
guard the setCurrentPage call with that isInitialMount check so URL page state
is preserved on initial load.

---

Duplicate comments:
In `@frontend/src/hooks/useUrlPagination.ts`:
- Around line 16-33: The setters setPage and setPerPage close over the stale
searchParams and create new URLSearchParams from that snapshot, which can drop
concurrent query keys; change both to use the functional form of setSearchParams
(setSearchParams(prev => { ... })) so each update builds from the latest params,
and in setPerPage ensure you set perPageKey and reset pageKey inside that
functional update; verify useSearchParams' setSearchParams supports functional
updates before applying.

In `@frontend/src/pages/ClassesPage.tsx`:
- Around line 217-219: The effect that calls setCurrentPage(1) runs on mount and
clobbers URL/page state; change the useEffect (the one referencing useEffect and
setCurrentPage with dependencies searchQuery, todayOnly, attendanceConcerns,
facilityFilter, programFilter, statusFilter) to skip its first invocation—e.g.
add an isInitialMount useRef (or track previous filter values) and only call
setCurrentPage(1) when not the initial render (i.e. when a real filter change
occurs) so refresh/back preserve the page query.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 28968791-e299-4c16-aba6-ec29860dd40f

📥 Commits

Reviewing files that changed from the base of the PR and between 673a584 and 379feaf.

📒 Files selected for processing (8)
  • frontend/src/components/shared/index.ts
  • frontend/src/components/student/ActivityHistoryCard.tsx
  • frontend/src/hooks/useUrlPagination.ts
  • frontend/src/pages/ClassesPage.tsx
  • frontend/src/pages/ProgramsPage.tsx
  • frontend/src/pages/class-detail/AuditTab.tsx
  • frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx
  • frontend/src/pages/programs/ProgramOverviewFacilityAdmin.tsx
💤 Files with no reviewable changes (1)
  • frontend/src/components/shared/index.ts

@corypride corypride merged commit 18c37b9 into CK-7vn/total-refactor May 18, 2026
9 checks passed
@corypride corypride deleted the cpride/pagination branch May 18, 2026 17:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants