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:
@@ -157,10 +157,21 @@ export default function OrderDetailsModal({ orderId, open, onOpenChange }: Order
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
// Validate orderId before making request
|
||||
if (!orderId || orderId === 'undefined' || orderId === 'null') {
|
||||
throw new Error('Order ID is required');
|
||||
}
|
||||
|
||||
// Ensure orderId is a valid number or string
|
||||
const orderIdStr = String(orderId).trim();
|
||||
if (!orderIdStr || orderIdStr === 'undefined' || orderIdStr === 'null') {
|
||||
throw new Error('Invalid order ID');
|
||||
}
|
||||
|
||||
// Fetch full order details from admin endpoint
|
||||
// Use /admin/orders/:orderId (fetchClient will add /api prefix and backend URL)
|
||||
console.log(`Fetching order details for order #${orderId}`);
|
||||
const orderData = await fetchClient<OrderDetails>(`/admin/orders/${orderId}`);
|
||||
console.log(`Fetching order details for order #${orderIdStr}`);
|
||||
const orderData = await fetchClient<OrderDetails>(`/admin/orders/${orderIdStr}`);
|
||||
|
||||
console.log("Order data received:", orderData);
|
||||
|
||||
|
||||
@@ -10,18 +10,37 @@ interface Vendor {
|
||||
lastLogin?: string;
|
||||
}
|
||||
|
||||
interface PaginationResponse {
|
||||
success: boolean;
|
||||
vendors: Vendor[];
|
||||
pagination: {
|
||||
page: number;
|
||||
limit: number;
|
||||
total: number;
|
||||
totalPages: number;
|
||||
hasNextPage: boolean;
|
||||
hasPrevPage: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export default function VendorsCard() {
|
||||
const [vendors, setVendors] = useState<Vendor[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [resetTokens, setResetTokens] = useState<Record<string, string>>({});
|
||||
const [page, setPage] = useState(1);
|
||||
const [pagination, setPagination] = useState<PaginationResponse['pagination'] | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
(async () => {
|
||||
try {
|
||||
const data = await fetchClient<Vendor[]>("/admin/vendors");
|
||||
if (mounted) setVendors(data);
|
||||
setLoading(true);
|
||||
const data = await fetchClient<PaginationResponse>(`/admin/vendors?page=${page}&limit=10`);
|
||||
if (mounted) {
|
||||
setVendors(data.vendors);
|
||||
setPagination(data.pagination);
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (mounted) setError(e?.message || "Failed to load vendors");
|
||||
} finally {
|
||||
@@ -29,7 +48,7 @@ export default function VendorsCard() {
|
||||
}
|
||||
})();
|
||||
return () => { mounted = false; };
|
||||
}, []);
|
||||
}, [page]);
|
||||
|
||||
async function generateResetToken(vendorId: string) {
|
||||
try {
|
||||
@@ -55,8 +74,9 @@ export default function VendorsCard() {
|
||||
) : vendors.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground mt-3">No vendors found</p>
|
||||
) : (
|
||||
<div className="mt-3 space-y-2 max-h-64 overflow-y-auto">
|
||||
{vendors.map((vendor) => (
|
||||
<>
|
||||
<div className="mt-3 space-y-2 max-h-64 overflow-y-auto">
|
||||
{vendors.map((vendor) => (
|
||||
<div key={vendor._id} className="rounded border border-border/50 p-3 text-sm">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
@@ -88,7 +108,31 @@ export default function VendorsCard() {
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{pagination && pagination.totalPages > 1 && (
|
||||
<div className="mt-3 flex items-center justify-between text-xs text-muted-foreground">
|
||||
<span>
|
||||
Page {pagination.page} of {pagination.totalPages} ({pagination.total} total)
|
||||
</span>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => setPage(p => Math.max(1, p - 1))}
|
||||
disabled={!pagination.hasPrevPage}
|
||||
className="px-2 py-1 rounded border border-border hover:bg-muted/40 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Previous
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setPage(p => p + 1)}
|
||||
disabled={!pagination.hasNextPage}
|
||||
className="px-2 py-1 rounded border border-border hover:bg-muted/40 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user