Add pagination to admin user, vendor, and ban lists

Introduces pagination controls and server-side paginated fetching for blocked users, users, and vendors in the admin dashboard. Improves error handling in server API responses and validates order ID in OrderDetailsModal. Updates git-info.json with latest commit metadata.
This commit is contained in:
g
2025-12-31 05:46:24 +00:00
parent 0062aa2dfe
commit 5f1e294091
6 changed files with 194 additions and 20 deletions

View File

@@ -33,6 +33,15 @@ export default function AdminBanPage() {
const [blockedUsers, setBlockedUsers] = useState<BlockedUser[]>([]);
const [searchQuery, setSearchQuery] = useState("");
const [banDialogOpen, setBanDialogOpen] = useState(false);
const [page, setPage] = useState(1);
const [pagination, setPagination] = useState<{
page: number;
limit: number;
total: number;
totalPages: number;
hasNextPage: boolean;
hasPrevPage: boolean;
} | null>(null);
const [banData, setBanData] = useState({
telegramUserId: "",
reason: "",
@@ -41,13 +50,25 @@ export default function AdminBanPage() {
useEffect(() => {
fetchBlockedUsers();
}, []);
}, [page]);
const fetchBlockedUsers = async () => {
try {
setLoading(true);
const data = await fetchClient<BlockedUser[]>("/admin/blocked-users");
setBlockedUsers(data);
const data = await fetchClient<{
success: boolean;
blockedUsers: BlockedUser[];
pagination: {
page: number;
limit: number;
total: number;
totalPages: number;
hasNextPage: boolean;
hasPrevPage: boolean;
};
}>(`/admin/blocked-users?page=${page}&limit=25`);
setBlockedUsers(data.blockedUsers);
setPagination(data.pagination);
} catch (error) {
console.error("Failed to fetch blocked users:", error);
toast({
@@ -395,6 +416,31 @@ export default function AdminBanPage() {
</TableBody>
</Table>
)}
{pagination && pagination.totalPages > 1 && (
<div className="mt-4 flex items-center justify-between px-6 pb-6">
<div className="text-sm text-muted-foreground">
Showing page {pagination.page} of {pagination.totalPages} ({pagination.total} total)
</div>
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
onClick={() => setPage(p => Math.max(1, p - 1))}
disabled={!pagination.hasPrevPage}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => setPage(p => p + 1)}
disabled={!pagination.hasNextPage}
>
Next
</Button>
</div>
</div>
)}
</CardContent>
</Card>
</div>

View File

@@ -25,6 +25,19 @@ interface TelegramUser {
createdAt?: string;
}
interface PaginationResponse {
success: boolean;
users: TelegramUser[];
pagination: {
page: number;
limit: number;
total: number;
totalPages: number;
hasNextPage: boolean;
hasPrevPage: boolean;
};
}
function formatCurrency(amount: number): string {
return new Intl.NumberFormat('en-GB', {
style: 'currency',
@@ -37,16 +50,19 @@ export default function AdminUsersPage() {
const [loading, setLoading] = useState(true);
const [users, setUsers] = useState<TelegramUser[]>([]);
const [searchQuery, setSearchQuery] = useState("");
const [page, setPage] = useState(1);
const [pagination, setPagination] = useState<PaginationResponse['pagination'] | null>(null);
useEffect(() => {
fetchUsers();
}, []);
}, [page]);
const fetchUsers = async () => {
try {
setLoading(true);
const data = await fetchClient<TelegramUser[]>("/admin/users");
setUsers(data);
const data = await fetchClient<PaginationResponse>(`/admin/users?page=${page}&limit=25`);
setUsers(data.users);
setPagination(data.pagination);
} catch (error: any) {
console.error("Failed to fetch users:", error);
toast({
@@ -300,6 +316,31 @@ export default function AdminUsersPage() {
</TableBody>
</Table>
)}
{pagination && pagination.totalPages > 1 && (
<div className="mt-4 flex items-center justify-between">
<div className="text-sm text-muted-foreground">
Showing page {pagination.page} of {pagination.totalPages} ({pagination.total} total users)
</div>
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
onClick={() => setPage(p => Math.max(1, p - 1))}
disabled={!pagination.hasPrevPage}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => setPage(p => p + 1)}
disabled={!pagination.hasNextPage}
>
Next
</Button>
</div>
</div>
)}
</CardContent>
</Card>
</div>