"use client"; import React, { useState, useEffect, useCallback } from "react"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; import { Badge } from "@/components/common/badge"; import { Button } from "@/components/common/button"; import { Input } from "@/components/common/input"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/common/table"; import { Search, MoreHorizontal, UserCheck, UserX, Mail, Loader2, Store, Shield, ShieldAlert, Clock, Calendar, Pencil, Plus } from "lucide-react"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/common/dialog"; import { Label } from "@/components/common/label"; import { fetchClient } from "@/lib/api/api-client"; import { useToast } from "@/lib/hooks/use-toast"; import { motion, AnimatePresence } from "framer-motion"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/common/dropdown-menu"; interface Vendor { _id: string; username: string; storeId?: string; createdAt?: string; lastLogin?: string; isAdmin?: boolean; isActive: boolean; } interface PaginationResponse { success: boolean; vendors: Vendor[]; pagination: { page: number; limit: number; total: number; totalPages: number; hasNextPage: boolean; hasPrevPage: boolean; }; } export default function AdminVendorsPage() { const { toast } = useToast(); const [loading, setLoading] = useState(true); const [vendors, setVendors] = useState([]); // State for browser detection const [isFirefox, setIsFirefox] = useState(false); useEffect(() => { const ua = navigator.userAgent.toLowerCase(); setIsFirefox(ua.includes("firefox") && !ua.includes("chrome")); }, []); const [page, setPage] = useState(1); const [pagination, setPagination] = useState(null); const [searchQuery, setSearchQuery] = useState(""); const [isEditStoreOpen, setIsEditStoreOpen] = useState(false); const [editingVendor, setEditingVendor] = useState(null); const [newStoreId, setNewStoreId] = useState(""); const [updating, setUpdating] = useState(false); const handleToggleStatus = async (vendor: Vendor) => { try { await fetchClient(`/admin/vendors/${vendor._id}/status`, { method: 'PATCH', body: { isActive: !vendor.isActive } }); toast({ title: "Success", description: `Vendor ${vendor.isActive ? 'suspended' : 'activated'} successfully`, }); fetchVendors(); } catch (error: any) { toast({ title: "Error", description: error.message || "Failed to update vendor status", variant: "destructive", }); } }; const handleEditStore = (vendor: Vendor) => { setEditingVendor(vendor); setNewStoreId(vendor.storeId || ""); setIsEditStoreOpen(true); }; const saveStoreId = async () => { if (!editingVendor) return; try { setUpdating(true); await fetchClient(`/admin/vendors/${editingVendor._id}/store-id`, { method: 'PUT', body: { storeId: newStoreId } }); toast({ title: "Success", description: "Store ID updated successfully", }); setIsEditStoreOpen(false); fetchVendors(); // Refresh list } catch (error: any) { toast({ title: "Error", description: error.message || "Failed to update store ID", variant: "destructive", }); } finally { setUpdating(false); } }; const fetchVendors = useCallback(async () => { try { setLoading(true); const params = new URLSearchParams({ page: page.toString(), limit: '25' }); const data = await fetchClient(`/admin/vendors?${params.toString()}`); setVendors(data.vendors); setPagination(data.pagination); } catch (error: any) { console.error("Failed to fetch vendors:", error); toast({ title: "Error", description: error.message || "Failed to load vendors", variant: "destructive", }); } finally { setLoading(false); } }, [page, toast]); useEffect(() => { fetchVendors(); }, [fetchVendors]); const filteredVendors = searchQuery.trim() ? vendors.filter(v => v.username.toLowerCase().includes(searchQuery.toLowerCase()) || (v.storeId && v.storeId.toString().toLowerCase().includes(searchQuery.toLowerCase())) ) : vendors; const activeVendors = vendors.filter(v => v.isActive); const suspendedVendors = vendors.filter(v => !v.isActive); const adminVendors = vendors.filter(v => v.isAdmin); const totalVendors = pagination?.total || vendors.length; const stats = [ { title: "Total Vendors", value: totalVendors, description: "Registered vendors", icon: Store, }, { title: "Active Vendors", value: activeVendors.length, description: `${vendors.length > 0 ? Math.round((activeVendors.length / vendors.length) * 100) : 0}% active rate`, icon: UserCheck, }, { title: "Suspended", value: suspendedVendors.length, description: `${vendors.length > 0 ? Math.round((suspendedVendors.length / vendors.length) * 100) : 0}% suspension rate`, icon: UserX, }, { title: "Admin Users", value: adminVendors.length, description: "Administrative access", icon: ShieldAlert, }, ]; return (

All Vendors

Manage vendor accounts and permissions

{/* Stats Cards */}
{stats.map((stat, i) => ( {stat.title}
{stat.value}

{stat.description}

))}
{/* Search and Filters */}
Vendor Management View and manage all vendor accounts
setSearchQuery(e.target.value)} />
Vendor Store Status Join Date Last Login Actions {isFirefox ? ( loading ? (

Loading vendors...

) : filteredVendors.length === 0 ? ( {searchQuery.trim() ? "No vendors found matching your search" : "No vendors found"} ) : ( filteredVendors.map((vendor, index) => (
{vendor.username.substring(0, 2).toUpperCase()}
{vendor.username}
{vendor.storeId ? (
{vendor.storeId}
) : (
No store
)}
{vendor.isActive ? "Active" : "Suspended"} {vendor.isAdmin && ( Admin )}
{vendor.createdAt ? new Date(vendor.createdAt).toLocaleDateString() : 'N/A'}
{vendor.lastLogin ? new Date(vendor.lastLogin).toLocaleDateString() : 'Never'}
Actions navigator.clipboard.writeText(vendor._id)} > Copy Vendor ID handleToggleStatus(vendor)} > {vendor.isActive ? ( <> Suspend Vendor ) : ( <> Activate Vendor )}
)) ) ) : ( {loading ? (

Loading vendors...

) : filteredVendors.length === 0 ? ( {searchQuery.trim() ? "No vendors found matching your search" : "No vendors found"} ) : ( filteredVendors.map((vendor, index) => (
{vendor.username.substring(0, 2).toUpperCase()}
{vendor.username}
{vendor.storeId ? (
{vendor.storeId}
) : (
No store
)}
{vendor.isActive ? "Active" : "Suspended"} {vendor.isAdmin && ( Admin )}
{vendor.createdAt ? new Date(vendor.createdAt).toLocaleDateString() : 'N/A'}
{vendor.lastLogin ? new Date(vendor.lastLogin).toLocaleDateString() : 'Never'}
Actions navigator.clipboard.writeText(vendor._id)} > Copy Vendor ID handleToggleStatus(vendor)} > {vendor.isActive ? ( <> Suspend Vendor ) : ( <> Activate Vendor )}
)) )}
)}
{pagination && pagination.totalPages > 1 && (
Page {pagination.page} of {pagination.totalPages}
)}
Update Vendor Store Enter the Store ID to assign to vendor {editingVendor?.username}.
setNewStoreId(e.target.value)} placeholder="Enter 24-character Store ID" className="col-span-3 font-mono" />

Ensure the Store ID corresponds to an existing store in the system.

); }