add pagination

This commit is contained in:
Aditya Siregar 2025-08-15 23:33:58 +07:00
parent 4ebe6da793
commit 2fcc239808

View File

@ -138,6 +138,12 @@ function MembersPageContent() {
const [searchTerm, setSearchTerm] = useState("")
const [statusFilter, setStatusFilter] = useState("all")
// Pagination states
const [currentPage, setCurrentPage] = useState(1)
const [pageSize, setPageSize] = useState(10)
const [totalUsers, setTotalUsers] = useState(0)
const [paginationLoading, setPaginationLoading] = useState(false)
// Bulk upload states
const [showBulkUpload, setShowBulkUpload] = useState(false)
const [bulkUsers, setBulkUsers] = useState<BulkUserRequest[]>([])
@ -153,19 +159,30 @@ function MembersPageContent() {
useEffect(() => {
if (user) {
fetchUsers()
fetchUsers(1, pageSize) // Always start from first page
loadStoredJob()
}
}, [user])
const fetchUsers = async () => {
// Reset to first page when filters change
useEffect(() => {
if (user && (searchTerm || statusFilter !== "all")) {
setCurrentPage(1)
fetchUsers(1, pageSize)
}
}, [searchTerm, statusFilter])
const fetchUsers = async (page: number = currentPage, size: number = pageSize) => {
try {
setLoading(true)
const response = await apiClient.get('/api/v1/users')
const response = await apiClient.get(`/api/v1/users?page=${page}&limit=${size}`)
const data: ApiResponse = response.data
if (data.success) {
setUsers(data.data.users)
setTotalUsers(data.data.pagination.total_count)
setCurrentPage(page)
toast({
title: "Success",
description: `Fetched ${data.data.users.length} users`,
@ -186,6 +203,7 @@ function MembersPageContent() {
})
} finally {
setLoading(false)
setPaginationLoading(false)
}
}
@ -352,7 +370,7 @@ function MembersPageContent() {
// If job is completed, refresh users list
if (jobResult.status === 'completed' || jobResult.status === 'failed') {
await fetchUsers()
await fetchUsers(currentPage, pageSize)
// Show completion message
if (jobResult.status === 'completed') {
@ -395,6 +413,24 @@ function MembersPageContent() {
}
}
// Pagination functions
const handlePageChange = (page: number) => {
setPaginationLoading(true)
setCurrentPage(page)
fetchUsers(page, pageSize)
}
const handlePageSizeChange = (size: number) => {
setPaginationLoading(true)
setPageSize(size)
setCurrentPage(1) // Reset to first page when changing page size
fetchUsers(1, size)
}
const totalPages = Math.ceil(totalUsers / pageSize)
const startIndex = (currentPage - 1) * pageSize + 1
const endIndex = Math.min(currentPage * pageSize, totalUsers)
const filteredUsers = users.filter((user) => {
const matchesSearch =
user.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
@ -513,8 +549,30 @@ function MembersPageContent() {
</Select>
</div>
<div className="w-full md:w-32">
<Label htmlFor="pageSize">Page Size</Label>
<Select value={pageSize.toString()} onValueChange={(value) => handlePageSizeChange(parseInt(value))}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="5">5 per page</SelectItem>
<SelectItem value="10">10 per page</SelectItem>
<SelectItem value="20">20 per page</SelectItem>
<SelectItem value="50">50 per page</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex items-end gap-2">
<Button onClick={fetchUsers} disabled={loading} className="gap-2">
<Button
onClick={() => {
setCurrentPage(1)
fetchUsers(1, pageSize)
}}
disabled={loading}
className="gap-2"
>
{loading ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
@ -564,7 +622,8 @@ function MembersPageContent() {
<CardHeader>
<CardTitle>Users List</CardTitle>
<CardDescription>
Showing {filteredUsers.length} of {users.length} users
Showing {startIndex}-{endIndex} of {totalUsers} total users
{filteredUsers.length !== users.length && ` (${filteredUsers.length} filtered)`}
</CardDescription>
</CardHeader>
<CardContent>
@ -615,6 +674,105 @@ function MembersPageContent() {
</Table>
</div>
)}
{/* Pagination Controls */}
{totalPages > 1 && (
<div className="relative flex items-center justify-between px-6 py-4 border-t">
{paginationLoading && (
<div className="absolute inset-0 bg-white/80 flex items-center justify-center">
<Loader2 className="h-6 w-6 animate-spin text-blue-600" />
</div>
)}
<div className="flex items-center gap-2 text-sm text-gray-600">
<span>
Page {currentPage} of {totalPages}
</span>
<span></span>
<span>
{startIndex}-{endIndex} of {totalUsers} results
</span>
</div>
<div className="flex items-center gap-2">
{/* Previous Page */}
<Button
variant="outline"
size="sm"
onClick={() => handlePageChange(currentPage - 1)}
disabled={currentPage === 1}
>
<ArrowLeft className="h-4 w-4" />
Previous
</Button>
{/* Page Numbers */}
<div className="flex items-center gap-1">
{/* First Page */}
{currentPage > 3 && (
<>
<Button
variant="outline"
size="sm"
onClick={() => handlePageChange(1)}
className="w-8 h-8 p-0"
>
1
</Button>
{currentPage > 4 && (
<span className="px-2 text-gray-400">...</span>
)}
</>
)}
{/* Page Range */}
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
const page = Math.max(1, Math.min(totalPages - 4, currentPage - 2)) + i
if (page > totalPages) return null
return (
<Button
key={page}
variant={page === currentPage ? "default" : "outline"}
size="sm"
onClick={() => handlePageChange(page)}
className="w-8 h-8 p-0"
>
{page}
</Button>
)
})}
{/* Last Page */}
{currentPage < totalPages - 2 && (
<>
{currentPage < totalPages - 3 && (
<span className="px-2 text-gray-400">...</span>
)}
<Button
variant="outline"
size="sm"
onClick={() => handlePageChange(totalPages)}
className="w-8 h-8 p-0"
>
{totalPages}
</Button>
</>
)}
</div>
{/* Next Page */}
<Button
variant="outline"
size="sm"
onClick={() => handlePageChange(currentPage + 1)}
disabled={currentPage === totalPages}
>
Next
<ArrowLeft className="h-4 w-4 rotate-180" />
</Button>
</div>
</div>
)}
</CardContent>
</Card>