From 7d3b69b3f1511e3436f83eb806a189e67c465860 Mon Sep 17 00:00:00 2001 From: Cory Pride Date: Wed, 13 May 2026 11:22:48 -0500 Subject: [PATCH 1/5] refactor: Pagination component to be used site wide --- frontend/src/components/Pagination.tsx | 10 +- frontend/src/components/shared/Pagination.tsx | 136 ------------------ frontend/src/components/shared/index.ts | 2 +- .../student/ActivityHistoryCard.tsx | 9 +- frontend/src/hooks/useUrlPagination.ts | 15 +- frontend/src/pages/ClassesPage.tsx | 4 +- frontend/src/pages/ProgramsPage.tsx | 21 ++- frontend/src/pages/admin/AdminManagement.tsx | 27 ++-- .../src/pages/admin/FacilityManagement.tsx | 21 ++- frontend/src/pages/class-detail/AuditTab.tsx | 10 +- .../KnowledgeCenterManagement.tsx | 11 +- .../ResidentKnowledgeCenter.tsx | 11 +- .../pages/programs/AddClassEnrollments.tsx | 24 ++-- .../pages/programs/ClassEnrollmentDetails.tsx | 24 ++-- .../programs/ProgramOverviewFacilityAdmin.tsx | 9 +- 15 files changed, 89 insertions(+), 245 deletions(-) delete mode 100644 frontend/src/components/shared/Pagination.tsx diff --git a/frontend/src/components/Pagination.tsx b/frontend/src/components/Pagination.tsx index 9a6e12d13..42fcf7a70 100644 --- a/frontend/src/components/Pagination.tsx +++ b/frontend/src/components/Pagination.tsx @@ -6,7 +6,8 @@ interface PaginationProps { itemsPerPage: number; onPageChange: (page: number) => void; onItemsPerPageChange: (itemsPerPage: number) => void; - itemLabel?: string; // e.g., "programs", "classes", "residents" + itemLabel?: string; + instanceId?: string; } export function Pagination({ @@ -15,7 +16,8 @@ export function Pagination({ itemsPerPage, onPageChange, onItemsPerPageChange, - itemLabel = 'items' + itemLabel = 'items', + instanceId = 'pagination' }: PaginationProps) { const totalPages = Math.ceil(totalItems / itemsPerPage); const startItem = totalItems === 0 ? 0 : (currentPage - 1) * itemsPerPage + 1; @@ -71,11 +73,11 @@ export function Pagination({

-
- - -
- - -
- {getPageNumbers(currentPage, totalPages).map( - (page, index) => { - if (page === '...') { - return ( - - ... - - ); - } - const num = page as number; - return ( - - ); - } - )} -
- - -
- - - ); -} diff --git a/frontend/src/components/shared/index.ts b/frontend/src/components/shared/index.ts index d1abb2c24..ea6729b53 100644 --- a/frontend/src/components/shared/index.ts +++ b/frontend/src/components/shared/index.ts @@ -6,4 +6,4 @@ export { ConfirmDialog } from './ConfirmDialog'; export { FormModal } from './FormModal'; export { DataTable } from './DataTable'; export type { Column } from './DataTable'; -export { Pagination } from './Pagination'; +export { Pagination } from '../Pagination'; diff --git a/frontend/src/components/student/ActivityHistoryCard.tsx b/frontend/src/components/student/ActivityHistoryCard.tsx index 04908c85b..b48892c2e 100644 --- a/frontend/src/components/student/ActivityHistoryCard.tsx +++ b/frontend/src/components/student/ActivityHistoryCard.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from 'react'; +import { useUrlPagination } from '@/hooks/useUrlPagination'; import useSWR from 'swr'; import { Calendar } from 'lucide-react'; import { @@ -53,8 +54,7 @@ export default function ActivityHistoryCard({ programId, residentId }: ActivityHistoryCardProps) { - const [page, setPage] = useState(1); - const [perPage, setPerPage] = useState(20); + const { page, perPage, setPage, setPerPage } = useUrlPagination(1, 20, 'activity'); const [filterQuery, setFilterQuery] = useState(''); const endpoint = residentId @@ -152,10 +152,7 @@ export default function ActivityHistoryCard({ totalItems={total} itemsPerPage={perPage} onPageChange={setPage} - onItemsPerPageChange={(val) => { - setPerPage(val); - setPage(1); - }} + onItemsPerPageChange={setPerPage} itemLabel="entries" /> )} diff --git a/frontend/src/hooks/useUrlPagination.ts b/frontend/src/hooks/useUrlPagination.ts index 984db6c61..0c8e78a61 100644 --- a/frontend/src/hooks/useUrlPagination.ts +++ b/frontend/src/hooks/useUrlPagination.ts @@ -1,27 +1,30 @@ import { useSearchParams } from 'react-router-dom'; -export function useUrlPagination(defaultPage = 1, defaultPerPage = 20) { +export function useUrlPagination(defaultPage = 1, defaultPerPage = 20, prefix = '') { const [searchParams, setSearchParams] = useSearchParams(); + const pageKey = prefix ? `${prefix}_page` : 'page'; + const perPageKey = prefix ? `${prefix}_per_page` : 'per_page'; + const page = parseInt( - searchParams.get('page') ?? defaultPage.toString(), + searchParams.get(pageKey) ?? defaultPage.toString(), 10 ); const perPage = parseInt( - searchParams.get('per_page') ?? defaultPerPage.toString(), + searchParams.get(perPageKey) ?? defaultPerPage.toString(), 10 ); const setPage = (newPage: number, options?: { replace?: boolean }) => { const params = new URLSearchParams(searchParams); - params.set('page', newPage.toString()); + params.set(pageKey, newPage.toString()); setSearchParams(params, { replace: options?.replace ?? false }); }; const setPerPage = (newPerPage: number) => { const params = new URLSearchParams(searchParams); - params.set('per_page', newPerPage.toString()); - params.set('page', '1'); + params.set(perPageKey, newPerPage.toString()); + params.set(pageKey, '1'); setSearchParams(params, { replace: false }); }; diff --git a/frontend/src/pages/ClassesPage.tsx b/frontend/src/pages/ClassesPage.tsx index 59b252b0e..1a9e665ba 100644 --- a/frontend/src/pages/ClassesPage.tsx +++ b/frontend/src/pages/ClassesPage.tsx @@ -1,4 +1,5 @@ import { useState, useMemo, useEffect } from 'react'; +import { useUrlPagination } from '@/hooks/useUrlPagination'; import { useNavigate, Link } from 'react-router-dom'; import useSWR from 'swr'; import { useAuth, canSwitchFacility } from '@/auth/useAuth'; @@ -93,8 +94,7 @@ export default function ClassesPage() { const [programSearch, setProgramSearch] = useState(''); const [attendanceClass, setAttendanceClass] = useState(null); const [showBulkCancel, setShowBulkCancel] = useState(false); - const [currentPage, setCurrentPage] = useState(1); - const [itemsPerPage, setItemsPerPage] = useState(20); + const { page: currentPage, perPage: itemsPerPage, setPage: setCurrentPage, setPerPage: setItemsPerPage } = useUrlPagination(); const crossFacility = user ? canSwitchFacility(user) : false; diff --git a/frontend/src/pages/ProgramsPage.tsx b/frontend/src/pages/ProgramsPage.tsx index 451981738..f219c6a6b 100644 --- a/frontend/src/pages/ProgramsPage.tsx +++ b/frontend/src/pages/ProgramsPage.tsx @@ -1,4 +1,5 @@ import { useState, useMemo } from 'react'; +import { useUrlPagination } from '@/hooks/useUrlPagination'; import { useNavigate } from 'react-router-dom'; import useSWR from 'swr'; import { useAuth, canSwitchFacility } from '@/auth/useAuth'; @@ -175,8 +176,7 @@ export default function ProgramsPage() { } }; - const [currentPage, setCurrentPage] = useState(1); - const [itemsPerPage, setItemsPerPage] = useState(10); + const { page, perPage, setPage, setPerPage } = useUrlPagination(1, 10); const toggleTypeFilter = (type: ProgramType) => { setSelectedTypes((prev) => @@ -286,8 +286,8 @@ export default function ProgramsPage() { }, [resp?.data, search, sort, selectedTypes, selectedStatuses]); const paginatedPrograms = filtered.slice( - (currentPage - 1) * itemsPerPage, - currentPage * itemsPerPage + (page - 1) * perPage, + page * perPage ); const stats = useMemo(() => { @@ -989,17 +989,14 @@ export default function ProgramsPage() { )} {/* Pagination */} - {filtered.length > itemsPerPage && ( + {filtered.length > perPage && (
{ - setItemsPerPage(val); - setCurrentPage(1); - }} + itemsPerPage={perPage} + onPageChange={setPage} + onItemsPerPageChange={setPerPage} itemLabel="items" />
diff --git a/frontend/src/pages/admin/AdminManagement.tsx b/frontend/src/pages/admin/AdminManagement.tsx index 56dfd7380..289f4c3a4 100644 --- a/frontend/src/pages/admin/AdminManagement.tsx +++ b/frontend/src/pages/admin/AdminManagement.tsx @@ -1,4 +1,5 @@ import { useEffect, useMemo, useState } from 'react'; +import { useUrlPagination } from '@/hooks/useUrlPagination'; import useSWR from 'swr'; import { Search, @@ -162,8 +163,7 @@ export default function AdminManagement() { const [sortDirection, setSortDirection] = useState('asc'); const [facilityFilter, setFacilityFilter] = useState('all'); - const [currentPage, setCurrentPage] = useState(1); - const [itemsPerPage, setItemsPerPage] = useState(20); + const { page, perPage, setPage, setPerPage } = useUrlPagination(); const [selectedAdmins, setSelectedAdmins] = useState>(new Map()); @@ -190,8 +190,8 @@ export default function AdminManagement() { if (!user) return null; const params = new URLSearchParams({ search: searchQuery, - page: String(currentPage), - per_page: String(itemsPerPage), + page: String(page), + per_page: String(perPage), order_by: `${SORT_FIELD[sortColumn]} ${sortDirection}`, role: user.role }); @@ -199,7 +199,7 @@ export default function AdminManagement() { params.set('facility_id', facilityFilter); } return `/api/users?${params.toString()}`; - }, [user, searchQuery, currentPage, itemsPerPage, sortColumn, sortDirection, canSwitchFac, facilityFilter]); + }, [user, searchQuery, page, perPage, sortColumn, sortDirection, canSwitchFac, facilityFilter]); const { data, mutate, error, isLoading } = useSWR, Error>(usersUrl); const admins = data?.data ?? []; @@ -251,7 +251,7 @@ export default function AdminManagement() { setSortColumn(column); setSortDirection('asc'); } - setCurrentPage(1); + setPage(1); }; const allOnPageSelected = @@ -459,7 +459,7 @@ export default function AdminManagement() { value={searchQuery} onChange={(e) => { setSearchQuery(e.target.value); - setCurrentPage(1); + setPage(1); }} className="pl-10" /> @@ -469,7 +469,7 @@ export default function AdminManagement() { value={facilityFilter} onValueChange={(v) => { setFacilityFilter(v); - setCurrentPage(1); + setPage(1); }} > @@ -706,13 +706,10 @@ export default function AdminManagement() { { - setItemsPerPage(value); - setCurrentPage(1); - }} + itemsPerPage={perPage} + currentPage={page} + onPageChange={setPage} + onItemsPerPageChange={setPerPage} itemLabel="admins" /> diff --git a/frontend/src/pages/admin/FacilityManagement.tsx b/frontend/src/pages/admin/FacilityManagement.tsx index 9e8831553..118516bf5 100644 --- a/frontend/src/pages/admin/FacilityManagement.tsx +++ b/frontend/src/pages/admin/FacilityManagement.tsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { useUrlPagination } from '@/hooks/useUrlPagination'; import useSWR from 'swr'; import { toast } from 'sonner'; import { Search, Plus, Edit, ArrowUpDown, Building2 } from 'lucide-react'; @@ -68,8 +69,7 @@ export default function FacilityManagement() { const [searchQuery, setSearchQuery] = useState(''); const [sortColumn, setSortColumn] = useState('name'); const [sortDirection, setSortDirection] = useState('asc'); - const [currentPage, setCurrentPage] = useState(1); - const [itemsPerPage, setItemsPerPage] = useState(20); + const { page, perPage, setPage, setPerPage } = useUrlPagination(); const [showAddFacility, setShowAddFacility] = useState(false); const [showEditFacility, setShowEditFacility] = useState(false); @@ -120,8 +120,8 @@ export default function FacilityManagement() { }); const paginated = sorted.slice( - (currentPage - 1) * itemsPerPage, - currentPage * itemsPerPage + (page - 1) * perPage, + page * perPage ); function toggleSort(column: SortColumn) { @@ -212,7 +212,7 @@ export default function FacilityManagement() { value={searchQuery} onChange={(e) => { setSearchQuery(e.target.value); - setCurrentPage(1); + setPage(1); }} className="pl-10" /> @@ -346,13 +346,10 @@ export default function FacilityManagement() { { - setItemsPerPage(value); - setCurrentPage(1); - }} + itemsPerPage={perPage} + currentPage={page} + onPageChange={setPage} + onItemsPerPageChange={setPerPage} itemLabel="facilities" /> diff --git a/frontend/src/pages/class-detail/AuditTab.tsx b/frontend/src/pages/class-detail/AuditTab.tsx index 725fe06d9..f546a07df 100644 --- a/frontend/src/pages/class-detail/AuditTab.tsx +++ b/frontend/src/pages/class-detail/AuditTab.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useUrlPagination } from '@/hooks/useUrlPagination'; import useSWR from 'swr'; import { Calendar } from 'lucide-react'; import { Pagination } from '@/components/Pagination'; @@ -96,8 +96,7 @@ function formatEntry(entry: HistoryEntry): React.ReactNode { } export function AuditTab({ classId }: AuditTabProps) { - const [page, setPage] = useState(1); - const [perPage, setPerPage] = useState(20); + const { page, perPage, setPage, setPerPage } = useUrlPagination(1, 20, 'audit'); const { data: auditResp } = useSWR>( `/api/program-classes/${classId}/history?page=${page}&per_page=${perPage}` @@ -147,10 +146,7 @@ export function AuditTab({ classId }: AuditTabProps) { totalItems={total} itemsPerPage={perPage} onPageChange={setPage} - onItemsPerPageChange={(val) => { - setPerPage(val); - setPage(1); - }} + onItemsPerPageChange={setPerPage} itemLabel="entries" /> )} diff --git a/frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx b/frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx index 44ade8c2d..677ed250f 100644 --- a/frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx +++ b/frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx @@ -30,7 +30,8 @@ import { } from '@/components/ui/dialog'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; -import { Pagination } from '@/components/shared/Pagination'; +import { Pagination } from '@/components/Pagination'; +import { useUrlPagination } from '@/hooks/useUrlPagination'; import { useDebounceValue } from 'usehooks-ts'; import { useToast } from '@/contexts/ToastContext'; import { @@ -318,8 +319,7 @@ export default function KnowledgeCenterManagement() { const [visibilityFilter, setVisibilityFilter] = useState('all'); const [categoryFilter, setCategoryFilter] = useState('all'); const [currentTab, setCurrentTab] = useState('libraries'); - const [currentPage, setCurrentPage] = useState(1); - const [itemsPerPage, setItemsPerPage] = useState(20); + const { page: currentPage, perPage: itemsPerPage, setPage: setCurrentPage, setPerPage } = useUrlPagination(); const [showAddVideo, setShowAddVideo] = useState(false); const [showAddLink, setShowAddLink] = useState(false); @@ -465,10 +465,7 @@ export default function KnowledgeCenterManagement() { currentPage * itemsPerPage ); - const handleItemsPerPageChange = (v: number) => { - setItemsPerPage(v); - setCurrentPage(1); - }; + const handleItemsPerPageChange = setPerPage; const pollVideos = (delay: number) => { if (!pollingRef.current) return; diff --git a/frontend/src/pages/knowledge-center/ResidentKnowledgeCenter.tsx b/frontend/src/pages/knowledge-center/ResidentKnowledgeCenter.tsx index 816d18c04..bf0689958 100644 --- a/frontend/src/pages/knowledge-center/ResidentKnowledgeCenter.tsx +++ b/frontend/src/pages/knowledge-center/ResidentKnowledgeCenter.tsx @@ -1,4 +1,5 @@ import { useState, useMemo, useEffect } from 'react'; +import { useUrlPagination } from '@/hooks/useUrlPagination'; import { useNavigate } from 'react-router-dom'; import { useTourContext } from '@/contexts/TourContext'; import { targetToStepIndexMap } from '@/components/UnlockEdTour'; @@ -25,7 +26,7 @@ import { TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; -import { Pagination } from '@/components/shared/Pagination'; +import { Pagination } from '@/components/Pagination'; import { useDebounceValue } from 'usehooks-ts'; import { ScrollArea } from '@/components/ui/scroll-area'; import { @@ -75,8 +76,7 @@ export default function ResidentKnowledgeCenter() { const [searchQuery] = useDebounceValue(searchTerm, 500); const [contentTypeFilter, setContentTypeFilter] = useState('all'); const [categoryFilter, setCategoryFilter] = useState('all'); - const [currentPage, setCurrentPage] = useState(1); - const [itemsPerPage, setItemsPerPage] = useState(ITEMS_PER_PAGE); + const { page: currentPage, perPage: itemsPerPage, setPage: setCurrentPage, setPerPage } = useUrlPagination(1, ITEMS_PER_PAGE); const { data: tagsData } = useSWR>( '/api/tags' @@ -476,10 +476,7 @@ export default function ResidentKnowledgeCenter() { totalItems={filteredContent.length} itemsPerPage={itemsPerPage} onPageChange={setCurrentPage} - onItemsPerPageChange={(v) => { - setItemsPerPage(v); - setCurrentPage(1); - }} + onItemsPerPageChange={setPerPage} /> )} diff --git a/frontend/src/pages/programs/AddClassEnrollments.tsx b/frontend/src/pages/programs/AddClassEnrollments.tsx index c065d207d..667b7f25e 100644 --- a/frontend/src/pages/programs/AddClassEnrollments.tsx +++ b/frontend/src/pages/programs/AddClassEnrollments.tsx @@ -1,4 +1,6 @@ import { useState, useEffect } from 'react'; +import { useUrlPagination } from '@/hooks/useUrlPagination'; +import { Pagination } from '@/components/Pagination'; import { useNavigate, useParams, useLoaderData } from 'react-router-dom'; import useSWR from 'swr'; import { toast } from 'sonner'; @@ -65,8 +67,7 @@ export default function AddClassEnrollments() { const [selectedUsers, setSelectedUsers] = useState([]); const [errorMessage, setErrorMessage] = useState(''); - const [page, setPage] = useState(1); - const perPage = 20; + const { page, perPage, setPage, setPerPage } = useUrlPagination(); const [searchTerm, setSearchTerm] = useState(''); const [searchQuery] = useDebounceValue(searchTerm, 500); const [sortQuery, setSortQuery] = useState(FilterResidentNames['Resident Name (A-Z)']); @@ -273,16 +274,15 @@ export default function AddClassEnrollments() { - {totalPages > 1 && ( -
- - Page {page} of {totalPages} - -
+ {(meta?.total ?? 0) > 0 && ( + )} diff --git a/frontend/src/pages/programs/ClassEnrollmentDetails.tsx b/frontend/src/pages/programs/ClassEnrollmentDetails.tsx index 2730f5e8b..17a87f207 100644 --- a/frontend/src/pages/programs/ClassEnrollmentDetails.tsx +++ b/frontend/src/pages/programs/ClassEnrollmentDetails.tsx @@ -1,4 +1,6 @@ import { useState, startTransition } from 'react'; +import { useUrlPagination } from '@/hooks/useUrlPagination'; +import { Pagination } from '@/components/Pagination'; import { useNavigate, useParams, useLoaderData } from 'react-router-dom'; import useSWR from 'swr'; import { toast } from 'sonner'; @@ -73,8 +75,7 @@ export default function ClassEnrollmentDetails() { const [sortQuery, setSortQuery] = useState(FilterResidentNames['Resident Name (A-Z)']); const [viewMode, setViewMode] = useState<'enrolled' | 'other'>('enrolled'); const [filterStatus, setFilterStatus] = useState('all'); - const [page, setPage] = useState(1); - const perPage = 20; + const { page, perPage, setPage, setPerPage } = useUrlPagination(); const [selectedResidents, setSelectedResidents] = useState([]); const [changeStatusValue, setChangeStatusValue] = useState(); const [showConfirmDialog, setShowConfirmDialog] = useState(false); @@ -406,16 +407,15 @@ export default function ClassEnrollmentDetails() { - {totalPages > 1 && ( -
- - Page {page} of {totalPages} - -
+ {(meta?.total ?? 0) > 0 && ( + )} diff --git a/frontend/src/pages/programs/ProgramOverviewFacilityAdmin.tsx b/frontend/src/pages/programs/ProgramOverviewFacilityAdmin.tsx index 59ea1cf6e..608ab3640 100644 --- a/frontend/src/pages/programs/ProgramOverviewFacilityAdmin.tsx +++ b/frontend/src/pages/programs/ProgramOverviewFacilityAdmin.tsx @@ -1,4 +1,5 @@ import { useState, useMemo } from 'react'; +import { useUrlPagination } from '@/hooks/useUrlPagination'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; import useSWR from 'swr'; import { toast } from 'sonner'; @@ -109,8 +110,7 @@ export default function ProgramOverviewFacilityAdmin() { const [searchParams] = useSearchParams(); const { user } = useAuth(); const [activeTab, setActiveTab] = useState('classes'); - const [historyPage, setHistoryPage] = useState(1); - const [historyPerPage, setHistoryPerPage] = useState(20); + const { page: historyPage, perPage: historyPerPage, setPage: setHistoryPage, setPerPage: setHistoryPerPage } = useUrlPagination(1, 20, 'history'); const [statusModalOpen, setStatusModalOpen] = useState(false); const [selectedClass, setSelectedClass] = useState(null); const [selectedStatus, setSelectedStatus] = useState(''); @@ -667,10 +667,7 @@ export default function ProgramOverviewFacilityAdmin() { currentPage={historyPage} itemsPerPage={historyPerPage} onPageChange={setHistoryPage} - onItemsPerPageChange={(val) => { - setHistoryPerPage(val); - setHistoryPage(1); - }} + onItemsPerPageChange={setHistoryPerPage} /> From 707ea415ec7bba6db6b11248bab7caa761b672c3 Mon Sep 17 00:00:00 2001 From: Cory Pride Date: Wed, 13 May 2026 17:42:50 -0500 Subject: [PATCH 2/5] fix: linting errors --- frontend/src/components/Pagination.tsx | 1 - frontend/src/components/student/ActivityHistoryCard.tsx | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Pagination.tsx b/frontend/src/components/Pagination.tsx index 42fcf7a70..7032c8003 100644 --- a/frontend/src/components/Pagination.tsx +++ b/frontend/src/components/Pagination.tsx @@ -81,7 +81,6 @@ export function Pagination({ value={itemsPerPage} onChange={(e) => { onItemsPerPageChange(Number(e.target.value)); - onPageChange(1); // Reset to first page when changing items per page }} className="bg-white dark:bg-[#262626] text-gray-900 dark:text-white px-3 py-1.5 rounded border border-gray-200 dark:border-[#404040] text-sm focus:outline-none focus:ring-2 focus:ring-[#556830] dark:focus:ring-[#8fb55e]" > diff --git a/frontend/src/components/student/ActivityHistoryCard.tsx b/frontend/src/components/student/ActivityHistoryCard.tsx index b48892c2e..b5fe9153c 100644 --- a/frontend/src/components/student/ActivityHistoryCard.tsx +++ b/frontend/src/components/student/ActivityHistoryCard.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { useUrlPagination } from '@/hooks/useUrlPagination'; import useSWR from 'swr'; import { Calendar } from 'lucide-react'; @@ -67,7 +67,12 @@ export default function ActivityHistoryCard({ ServerResponseMany >(endpoint); + const isFirstRender = useRef(true); useEffect(() => { + if (isFirstRender.current) { + isFirstRender.current = false; + return; + } setPage(1); }, [filterQuery]); From 528b4ddcc55e0b1cb8ca827972a5eb71cdc11053 Mon Sep 17 00:00:00 2001 From: Cory Pride Date: Thu, 14 May 2026 11:35:22 -0500 Subject: [PATCH 3/5] fix: removed unused totalPages variable --- frontend/src/pages/programs/AddClassEnrollments.tsx | 2 -- frontend/src/pages/programs/ClassEnrollmentDetails.tsx | 2 -- 2 files changed, 4 deletions(-) diff --git a/frontend/src/pages/programs/AddClassEnrollments.tsx b/frontend/src/pages/programs/AddClassEnrollments.tsx index 667b7f25e..29a031f12 100644 --- a/frontend/src/pages/programs/AddClassEnrollments.tsx +++ b/frontend/src/pages/programs/AddClassEnrollments.tsx @@ -83,8 +83,6 @@ export default function AddClassEnrollments() { const users = data?.data ?? []; const meta = data?.meta; - const totalPages = meta ? meta.last_page : 1; - const enrolledCount = classInfo?.enrollments?.filter( (e) => e.enrollment_status === EnrollmentStatus.Enrolled diff --git a/frontend/src/pages/programs/ClassEnrollmentDetails.tsx b/frontend/src/pages/programs/ClassEnrollmentDetails.tsx index 17a87f207..85c2c8c33 100644 --- a/frontend/src/pages/programs/ClassEnrollmentDetails.tsx +++ b/frontend/src/pages/programs/ClassEnrollmentDetails.tsx @@ -102,8 +102,6 @@ export default function ClassEnrollmentDetails() { const meta = data?.meta; const enrolledCount = viewMode === 'enrolled' ? meta?.total ?? 0 : otherData?.meta?.total ?? 0; const otherCount = viewMode === 'enrolled' ? otherData?.meta?.total ?? 0 : meta?.total ?? 0; - const totalPages = meta ? meta.last_page : 1; - async function refreshEnrollments() { await Promise.all([mutate(), mutateOther()]); } From 673a58426298f9d8bad906ff58ee58c6bb676614 Mon Sep 17 00:00:00 2001 From: Cory Pride Date: Thu, 14 May 2026 13:11:44 -0500 Subject: [PATCH 4/5] fix: resolved code rabbit requested changes --- frontend/src/components/Pagination.tsx | 9 ++++++--- frontend/src/hooks/useUrlPagination.ts | 13 +++++-------- frontend/src/pages/ProgramsPage.tsx | 11 ++++++++++- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/Pagination.tsx b/frontend/src/components/Pagination.tsx index 7032c8003..ef2aa9d6f 100644 --- a/frontend/src/components/Pagination.tsx +++ b/frontend/src/components/Pagination.tsx @@ -1,3 +1,4 @@ +import { useId } from 'react'; import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline'; interface PaginationProps { @@ -17,8 +18,10 @@ export function Pagination({ onPageChange, onItemsPerPageChange, itemLabel = 'items', - instanceId = 'pagination' + instanceId }: PaginationProps) { + const generatedId = useId(); + const itemsPerPageId = `${instanceId ?? `pagination-${generatedId}`}-items-per-page`; const totalPages = Math.ceil(totalItems / itemsPerPage); const startItem = totalItems === 0 ? 0 : (currentPage - 1) * itemsPerPage + 1; const endItem = Math.min(currentPage * itemsPerPage, totalItems); @@ -73,11 +76,11 @@ export function Pagination({

-