add pagination
This commit is contained in:
parent
4ebe6da793
commit
2fcc239808
@ -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>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user