import React, { useState } from "react"; import { base44 } from "@/api/base44Client"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Link } from "react-router-dom"; import { createPageUrl } from "@/utils"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Package, Plus, Search, Users, Edit, ChevronLeft, ChevronRight, Building2, DollarSign, Mail, CheckCircle2, XCircle, Clock, Eye, Archive, LayoutGrid, List as ListIcon } from "lucide-react"; import PageHeader from "../components/common/PageHeader"; import VendorScoreHoverCard from "../components/procurement/VendorScoreHoverCard"; import VendorDetailModal from "../components/procurement/VendorDetailModal"; import { useToast } from "@/components/ui/use-toast"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Textarea } from "@/components/ui/textarea"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; export default function VendorManagement() { const [searchTerm, setSearchTerm] = useState(""); const [regionFilter, setRegionFilter] = useState("all"); const [stateFilter, setStateFilter] = useState("all"); const [specialtyFilter, setSpecialtyFilter] = useState("all"); const [softwareFilter, setSoftwareFilter] = useState("all"); const [statusFilter, setStatusFilter] = useState("all"); const [currentPage, setCurrentPage] = useState(1); const [itemsPerPage, setItemsPerPage] = useState(10); const [editingVendor, setEditingVendor] = useState(null); const [showEditModal, setShowEditModal] = useState(false); const [reviewingVendor, setReviewingVendor] = useState(null); const [showReviewModal, setShowReviewModal] = useState(false); const [showRejectModal, setShowRejectModal] = useState(false); const [rejectionReason, setRejectionReason] = useState(""); const [viewMode, setViewMode] = useState("list"); // "list" or "grid" const { toast } = useToast(); const queryClient = useQueryClient(); const { data: user } = useQuery({ queryKey: ['current-user-vendor-mgmt'], queryFn: () => base44.auth.me(), }); const userRole = user?.user_role || user?.role; // Fetch pending vendors from database const { data: pendingVendors = [] } = useQuery({ queryKey: ['pending-vendors'], queryFn: () => base44.entities.Vendor.filter({ approval_status: 'pending' }, '-created_date'), initialData: [], }); // Fetch approved vendors const { data: approvedVendors = [] } = useQuery({ queryKey: ['approved-vendors'], queryFn: () => base44.entities.Vendor.list('-created_date'), initialData: [], }); // Fetch all vendor rates const { data: allVendorRates = [] } = useQuery({ queryKey: ['all-vendor-rates'], queryFn: () => base44.entities.VendorRate.list('-created_date'), initialData: [], }); // Approve vendor mutation const approveVendorMutation = useMutation({ mutationFn: async (vendorId) => { await base44.entities.Vendor.update(vendorId, { approval_status: 'approved', approved_date: new Date().toISOString(), approved_by: user?.email, is_active: true, }); // Activate all vendor rates const vendorRates = allVendorRates.filter(r => r.vendor_id === vendorId); await Promise.all( vendorRates.map(rate => base44.entities.VendorRate.update(rate.id, { is_active: true }) ) ); }, onSuccess: () => { queryClient.invalidateQueries(['pending-vendors']); queryClient.invalidateQueries(['approved-vendors']); queryClient.invalidateQueries(['all-vendor-rates']); toast({ title: "Vendor Approved", description: "Vendor has been approved and activated", }); setShowReviewModal(false); setReviewingVendor(null); }, }); // Reject vendor mutation const rejectVendorMutation = useMutation({ mutationFn: async ({ vendorId, reason }) => { await base44.entities.Vendor.update(vendorId, { approval_status: 'suspended', is_active: false, notes: `Rejected: ${reason}`, }); // Send rejection email const vendor = [...pendingVendors, ...approvedVendors].find(v => v.id === vendorId); if (vendor?.primary_contact_email) { await base44.integrations.Core.SendEmail({ from_name: "KROW Platform", to: vendor.primary_contact_email, subject: "KROW Vendor Application - Additional Information Required", body: `

Vendor Application Update

Dear ${vendor.primary_contact_name || 'Vendor'},

Thank you for your interest in joining the KROW vendor network.

We've reviewed your application and need some additional information before we can proceed:

Reason: ${reason}

Please address the above and resubmit your application.

Best regards,
KROW Procurement Team

` }); } }, onSuccess: () => { queryClient.invalidateQueries(['pending-vendors']); queryClient.invalidateQueries(['approved-vendors']); toast({ title: "Vendor Rejected", description: "Vendor has been notified and can resubmit", }); setShowReviewModal(false); setShowRejectModal(false); setReviewingVendor(null); setRejectionReason(""); }, }); // Archive vendor mutation const archiveVendorMutation = useMutation({ mutationFn: async (vendorId) => { await base44.entities.Vendor.update(vendorId, { approval_status: 'archived', is_active: false, }); }, onSuccess: () => { queryClient.invalidateQueries(['pending-vendors']); queryClient.invalidateQueries(['approved-vendors']); toast({ title: "Vendor Archived", description: "Vendor has been archived", }); }, }); // Mock vendors for existing list const mockApprovedVendors = [ { id: 'mock-1001', vendorNumber: "VN-1001", name: "Legendary Event Staffing", region: "Bay Area", state: "California", specialty: "Full Service Events", software: "Building platform (KROW)", softwareType: "building", employees: 450, fillRate: 97, onTimeRate: 98, csat: 4.7, spend: "$230k", approval_status: "approved", legal_name: "Legendary Event Staffing Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Jane Doe", primary_contact_email: "jane@legendary.com", w9_document: true, coi_document: true, coverage_regions: ["San Francisco", "Oakland", "San Jose"] }, { id: 'mock-1002', vendorNumber: "VN-1002", name: "Instawork", region: "National", state: "Multiple", specialty: "On-Demand Gig Platform", software: "Full Platform", softwareType: "platform", employees: 5000, fillRate: 95, onTimeRate: 96, csat: 4.5, spend: "$215k", approval_status: "approved", legal_name: "Instawork Corp.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "John Smith", primary_contact_email: "john@instawork.com", w9_document: true, coi_document: true, coverage_regions: ["New York", "Los Angeles", "Chicago"] }, { id: 'mock-1003', vendorNumber: "VN-1003", name: "The Party Staff", region: "Bay Area", state: "California", specialty: "Event Staffing", software: "Building platform (KROW)", softwareType: "building", employees: 320, fillRate: 94, onTimeRate: 97, csat: 4.6, spend: "$180k", approval_status: "approved", legal_name: "The Party Staff LLC", tax_id: "XX-XXXXXXX", business_type: "LLC", primary_contact_name: "Sarah Johnson", primary_contact_email: "sarah@thepartystaff.com", w9_document: true, coi_document: true, coverage_regions: ["San Francisco", "Oakland"] }, { id: 'mock-1004', vendorNumber: "VN-1004", name: "Elite Hospitality Staffing", region: "Bay Area", state: "California", specialty: "Hotels/Events", software: "Partial Tech", softwareType: "partial", employees: 280, fillRate: 92, onTimeRate: 95, csat: 4.4, spend: "$165k", approval_status: "approved", legal_name: "Elite Hospitality Staffing Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Michael Chen", primary_contact_email: "michael@elitehospitality.com", w9_document: true, coi_document: true, coverage_regions: ["Oakland", "San Jose"] }, { id: 'mock-1005', vendorNumber: "VN-1005", name: "Jeff Duerson Staffing", region: "Bay Area", state: "California", specialty: "Catering/Events", software: "Traditional", softwareType: "traditional", employees: 150, fillRate: 90, onTimeRate: 93, csat: 4.3, spend: "$140k", approval_status: "approved", legal_name: "Jeff Duerson Staffing LLC", tax_id: "XX-XXXXXXX", business_type: "LLC", primary_contact_name: "Jeff Duerson", primary_contact_email: "jeff@jdstaffing.com", w9_document: true, coi_document: true, coverage_regions: ["San Jose", "Palo Alto"] }, { id: 'mock-1006', vendorNumber: "VN-1006", name: "Flagship Culinary Services", region: "Bay Area", state: "California", specialty: "Culinary", software: "Traditional", softwareType: "traditional", employees: 200, fillRate: 91, onTimeRate: 94, csat: 4.5, spend: "$155k", approval_status: "approved", legal_name: "Flagship Culinary Services Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Amanda Rodriguez", primary_contact_email: "amanda@flagshipculinary.com", w9_document: true, coi_document: true, coverage_regions: ["Palo Alto", "San Francisco"] }, { id: 'mock-1007', vendorNumber: "VN-1007", name: "LA Event Professionals", region: "Southern California", state: "California", specialty: "Event Staffing", software: "Building platform (KROW)", softwareType: "building", employees: 380, fillRate: 93, onTimeRate: 96, csat: 4.5, spend: "$190k", approval_status: "approved", legal_name: "LA Event Professionals LLC", tax_id: "XX-XXXXXXX", business_type: "LLC", primary_contact_name: "Carlos Martinez", primary_contact_email: "carlos@laeventpros.com", w9_document: true, coi_document: true, coverage_regions: ["Los Angeles", "Long Beach"] }, { id: 'mock-1008', vendorNumber: "VN-1008", name: "Hollywood Hospitality", region: "Southern California", state: "California", specialty: "Full Service Events", software: "Full Platform", softwareType: "platform", employees: 420, fillRate: 94, onTimeRate: 97, csat: 4.6, spend: "$210k", approval_status: "approved", legal_name: "Hollywood Hospitality Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Jessica Lee", primary_contact_email: "jessica@hollywoodhospitality.com", w9_document: true, coi_document: true, coverage_regions: ["Los Angeles", "Beverly Hills"] }, { id: 'mock-1009', vendorNumber: "VN-1009", name: "San Diego Event Staff", region: "Southern California", state: "California", specialty: "Hospitality", software: "Partial Tech", softwareType: "partial", employees: 250, fillRate: 91, onTimeRate: 94, csat: 4.4, spend: "$145k", approval_status: "approved", legal_name: "San Diego Event Staff LLC", tax_id: "XX-XXXXXXX", business_type: "LLC", primary_contact_name: "David Kim", primary_contact_email: "david@sdeventstaff.com", w9_document: true, coi_document: true, coverage_regions: ["San Diego", "Chula Vista"] }, { id: 'mock-1010', vendorNumber: "VN-1010", name: "OC Staffing Solutions", region: "Southern California", state: "California", specialty: "Event-based staffing", software: "Traditional", softwareType: "traditional", employees: 180, fillRate: 89, onTimeRate: 92, csat: 4.2, spend: "$130k", approval_status: "approved", legal_name: "OC Staffing Solutions Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Emily Watson", primary_contact_email: "emily@ocstaffing.com", w9_document: true, coi_document: true, coverage_regions: ["Irvine", "Anaheim"] }, { id: 'mock-1011', vendorNumber: "VN-1011", name: "NorCal Staffing", region: "Northern California", state: "California", specialty: "Event-based staffing", software: "Partial Tech", softwareType: "partial", employees: 220, fillRate: 90, onTimeRate: 93, csat: 4.3, spend: "$150k", approval_status: "approved", legal_name: "NorCal Staffing LLC", tax_id: "XX-XXXXXXX", business_type: "LLC", primary_contact_name: "Robert Brown", primary_contact_email: "robert@norcalstaffing.com", w9_document: true, coi_document: true, coverage_regions: ["Sacramento", "Stockton"] }, { id: 'mock-1012', vendorNumber: "VN-1012", name: "Kitchen & Cater Staffing", region: "Northern California", state: "California", specialty: "Catering", software: "Traditional", softwareType: "traditional", employees: 160, fillRate: 88, onTimeRate: 91, csat: 4.1, spend: "$125k", approval_status: "approved", legal_name: "Kitchen & Cater Staffing Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Lisa Chang", primary_contact_email: "lisa@kitchencater.com", w9_document: true, coi_document: true, coverage_regions: ["Sacramento", "Davis"] }, { id: 'mock-1013', vendorNumber: "VN-1013", name: "NYC Event Masters", region: "East Coast", state: "New York", specialty: "Full Service Events", software: "Full Platform", softwareType: "platform", employees: 550, fillRate: 96, onTimeRate: 98, csat: 4.7, spend: "$240k", approval_status: "approved", legal_name: "NYC Event Masters Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "James Wilson", primary_contact_email: "james@nyceventmasters.com", w9_document: true, coi_document: true, coverage_regions: ["New York City", "Brooklyn", "Queens"] }, { id: 'mock-1014', vendorNumber: "VN-1014", name: "Manhattan Staffing Group", region: "East Coast", state: "New York", specialty: "Hospitality", software: "Building platform (KROW)", softwareType: "building", employees: 410, fillRate: 94, onTimeRate: 96, csat: 4.5, spend: "$200k", approval_status: "approved", legal_name: "Manhattan Staffing Group LLC", tax_id: "XX-XXXXXXX", business_type: "LLC", primary_contact_name: "Rachel Green", primary_contact_email: "rachel@manhattanstaffing.com", w9_document: true, coi_document: true, coverage_regions: ["Manhattan", "Bronx"] }, { id: 'mock-1015', vendorNumber: "VN-1015", name: "Brooklyn Event Pros", region: "East Coast", state: "New York", specialty: "Event Staffing", software: "Partial Tech", softwareType: "partial", employees: 290, fillRate: 92, onTimeRate: 95, csat: 4.4, spend: "$170k", approval_status: "approved", legal_name: "Brooklyn Event Pros Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Daniel Park", primary_contact_email: "daniel@brooklyneventpros.com", w9_document: true, coi_document: true, coverage_regions: ["Brooklyn", "Queens"] }, { id: 'mock-1016', vendorNumber: "VN-1016", name: "Upstate Event Services", region: "East Coast", state: "New York", specialty: "Regional Events", software: "Traditional", softwareType: "traditional", employees: 140, fillRate: 87, onTimeRate: 90, csat: 4.0, spend: "$115k", approval_status: "approved", legal_name: "Upstate Event Services LLC", tax_id: "XX-XXXXXXX", business_type: "LLC", primary_contact_name: "Karen Miller", primary_contact_email: "karen@upstateeventservices.com", w9_document: true, coi_document: true, coverage_regions: ["Buffalo", "Rochester"] }, { id: 'mock-1017', vendorNumber: "VN-1017", name: "Lone Star Staffing", region: "South", state: "Texas", specialty: "Full Service Events", software: "Building platform (KROW)", softwareType: "building", employees: 370, fillRate: 93, onTimeRate: 96, csat: 4.5, spend: "$185k", approval_status: "approved", legal_name: "Lone Star Staffing Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Austin Davis", primary_contact_email: "austin@lonestarstaffing.com", w9_document: true, coi_document: true, coverage_regions: ["Austin", "Round Rock"] }, { id: 'mock-1018', vendorNumber: "VN-1018", name: "Houston Hospitality Hub", region: "South", state: "Texas", specialty: "Hospitality", software: "Full Platform", softwareType: "platform", employees: 460, fillRate: 95, onTimeRate: 97, csat: 4.6, spend: "$220k", approval_status: "approved", legal_name: "Houston Hospitality Hub LLC", tax_id: "XX-XXXXXXX", business_type: "LLC", primary_contact_name: "Maria Garcia", primary_contact_email: "maria@houstonhospitality.com", w9_document: true, coi_document: true, coverage_regions: ["Houston", "Galveston"] }, { id: 'mock-1019', vendorNumber: "VN-1019", name: "Dallas Event Specialists", region: "South", state: "Texas", specialty: "Event Staffing", software: "Partial Tech", softwareType: "partial", employees: 340, fillRate: 92, onTimeRate: 95, csat: 4.4, spend: "$175k", approval_status: "approved", legal_name: "Dallas Event Specialists Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Brandon White", primary_contact_email: "brandon@dallaseventspecialists.com", w9_document: true, coi_document: true, coverage_regions: ["Dallas", "Fort Worth"] }, { id: 'mock-1020', vendorNumber: "VN-1020", name: "San Antonio Services", region: "South", state: "Texas", specialty: "Catering/Events", software: "Traditional", softwareType: "traditional", employees: 190, fillRate: 89, onTimeRate: 92, csat: 4.2, spend: "$135k", approval_status: "approved", legal_name: "San Antonio Services LLC", tax_id: "XX-XXXXXXX", business_type: "LLC", primary_contact_name: "Jennifer Lopez", primary_contact_email: "jennifer@sanantonioservices.com", w9_document: true, coi_document: true, coverage_regions: ["San Antonio"] }, { id: 'mock-1021', vendorNumber: "VN-1021", name: "Chicago Event Staffing", region: "Midwest", state: "Illinois", specialty: "Full Service Events", software: "Full Platform", softwareType: "platform", employees: 490, fillRate: 95, onTimeRate: 97, csat: 4.6, spend: "$225k", approval_status: "approved", legal_name: "Chicago Event Staffing Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Thomas Anderson", primary_contact_email: "thomas@chicagoeventstaffing.com", w9_document: true, coi_document: true, coverage_regions: ["Chicago", "Naperville"] }, { id: 'mock-1022', vendorNumber: "VN-1022", name: "Windy City Hospitality", region: "Midwest", state: "Illinois", specialty: "Hotels/Events", software: "Building platform (KROW)", softwareType: "building", employees: 380, fillRate: 93, onTimeRate: 96, csat: 4.5, spend: "$190k", approval_status: "approved", legal_name: "Windy City Hospitality LLC", tax_id: "XX-XXXXXXX", business_type: "LLC", primary_contact_name: "Nicole Taylor", primary_contact_email: "nicole@windycityhospitality.com", w9_document: true, coi_document: true, coverage_regions: ["Chicago", "Evanston"] }, { id: 'mock-1023', vendorNumber: "VN-1023", name: "Miami Event Solutions", region: "South", state: "Florida", specialty: "Full Service Events", software: "Full Platform", softwareType: "platform", employees: 440, fillRate: 94, onTimeRate: 96, csat: 4.5, spend: "$205k", approval_status: "approved", legal_name: "Miami Event Solutions Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Carlos Hernandez", primary_contact_email: "carlos@miamievertsolutions.com", w9_document: true, coi_document: true, coverage_regions: ["Miami", "Fort Lauderdale"] }, { id: 'mock-1024', vendorNumber: "VN-1024", name: "Orlando Hospitality Group", region: "South", state: "Florida", specialty: "Theme Park Events", software: "Building platform (KROW)", softwareType: "building", employees: 520, fillRate: 95, onTimeRate: 97, csat: 4.7, spend: "$235k", approval_status: "approved", legal_name: "Orlando Hospitality Group LLC", tax_id: "XX-XXXXXXX", business_type: "LLC", primary_contact_name: "Michelle Scott", primary_contact_email: "michelle@orlandohospitality.com", w9_document: true, coi_document: true, coverage_regions: ["Orlando", "Kissimmee"] }, { id: 'mock-1025', vendorNumber: "VN-1025", name: "Tampa Bay Staffing", region: "South", state: "Florida", specialty: "Event Staffing", software: "Partial Tech", softwareType: "partial", employees: 310, fillRate: 91, onTimeRate: 94, csat: 4.3, spend: "$160k", approval_status: "approved", legal_name: "Tampa Bay Staffing Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Kevin Moore", primary_contact_email: "kevin@tampabaystaffing.com", w9_document: true, coi_document: true, coverage_regions: ["Tampa", "St. Petersburg"] }, { id: 'mock-1026', vendorNumber: "VN-1026", name: "Seattle Event Pros", region: "West", state: "Washington", specialty: "Tech Events", software: "Full Platform", softwareType: "platform", employees: 400, fillRate: 94, onTimeRate: 96, csat: 4.6, spend: "$200k", approval_status: "approved", legal_name: "Seattle Event Pros LLC", tax_id: "XX-XXXXXXX", business_type: "LLC", primary_contact_name: "Amy Chen", primary_contact_email: "amy@seattleeventpros.com", w9_document: true, coi_document: true, coverage_regions: ["Seattle", "Bellevue"] }, { id: 'mock-1027', vendorNumber: "VN-1027", name: "Pacific Northwest Staffing", region: "West", state: "Washington", specialty: "Full Service Events", software: "Building platform (KROW)", softwareType: "building", employees: 350, fillRate: 93, onTimeRate: 95, csat: 4.5, spend: "$180k", approval_status: "approved", legal_name: "Pacific Northwest Staffing Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Brian Lee", primary_contact_email: "brian@pnwstaffing.com", w9_document: true, coi_document: true, coverage_regions: ["Seattle", "Tacoma"] }, { id: 'mock-1028', vendorNumber: "VN-1028", name: "Boston Event Services", region: "East Coast", state: "Massachusetts", specialty: "Corporate Events", software: "Full Platform", softwareType: "platform", employees: 370, fillRate: 93, onTimeRate: 96, csat: 4.5, spend: "$185k", approval_status: "approved", legal_name: "Boston Event Services LLC", tax_id: "XX-XXXXXXX", business_type: "LLC", primary_contact_name: "Patrick Sullivan", primary_contact_email: "patrick@bostoneventservices.com", w9_document: true, coi_document: true, coverage_regions: ["Boston", "Cambridge"] }, { id: 'mock-1029', vendorNumber: "VN-1029", name: "New England Hospitality", region: "East Coast", state: "Massachusetts", specialty: "Full Service Events", software: "Building platform (KROW)", softwareType: "building", employees: 320, fillRate: 92, onTimeRate: 95, csat: 4.4, spend: "$170k", approval_status: "approved", legal_name: "New England Hospitality Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Laura Murphy", primary_contact_email: "laura@nehospitality.com", w9_document: true, coi_document: true, coverage_regions: ["Boston", "Worcester"] }, { id: 'mock-1030', vendorNumber: "VN-1030", name: "Atlanta Event Staffing", region: "South", state: "Georgia", specialty: "Full Service Events", software: "Full Platform", softwareType: "platform", employees: 420, fillRate: 94, onTimeRate: 96, csat: 4.6, spend: "$210k", approval_status: "approved", legal_name: "Atlanta Event Staffing LLC", tax_id: "XX-XXXXXXX", business_type: "LLC", primary_contact_name: "Marcus Johnson", primary_contact_email: "marcus@atlantaeventstaffing.com", w9_document: true, coi_document: true, coverage_regions: ["Atlanta", "Marietta"] }, { id: 'mock-1031', vendorNumber: "VN-1031", name: "Peach State Hospitality", region: "South", state: "Georgia", specialty: "Hospitality", software: "Building platform (KROW)", softwareType: "building", employees: 340, fillRate: 92, onTimeRate: 95, csat: 4.4, spend: "$175k", approval_status: "approved", legal_name: "Peach State Hospitality Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Stephanie Wright", primary_contact_email: "stephanie@peachstatehospitality.com", w9_document: true, coi_document: true, coverage_regions: ["Atlanta", "Savannah"] }, { id: 'mock-1032', vendorNumber: "VN-1032", name: "Denver Event Solutions", region: "West", state: "Colorado", specialty: "Mountain Events", software: "Partial Tech", softwareType: "partial", employees: 280, fillRate: 91, onTimeRate: 94, csat: 4.3, spend: "$150k", approval_status: "approved", legal_name: "Denver Event Solutions LLC", tax_id: "XX-XXXXXXX", business_type: "LLC", primary_contact_name: "Jake Thompson", primary_contact_email: "jake@denvereventsolutions.com", w9_document: true, coi_document: true, coverage_regions: ["Denver", "Boulder"] }, { id: 'mock-1033', vendorNumber: "VN-1033", name: "Rocky Mountain Staffing", region: "West", state: "Colorado", specialty: "Event Staffing", software: "Traditional", softwareType: "traditional", employees: 230, fillRate: 89, onTimeRate: 92, csat: 4.2, spend: "$140k", approval_status: "approved", legal_name: "Rocky Mountain Staffing Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Hannah Davis", primary_contact_email: "hannah@rockymountainstaffing.com", w9_document: true, coi_document: true, coverage_regions: ["Denver", "Colorado Springs"] }, { id: 'mock-1034', vendorNumber: "VN-1034", name: "Hospitality Staffing Solutions (HSS)", region: "National", state: "Multiple", specialty: "Hospitality", software: "Full Platform", softwareType: "platform", employees: 4200, fillRate: 96, onTimeRate: 98, csat: 4.7, spend: "$195k", approval_status: "approved", legal_name: "HSS Corporation", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Richard Lawson", primary_contact_email: "richard@hss.com", w9_document: true, coi_document: true, coverage_regions: ["National Coverage"] }, { id: 'mock-1035', vendorNumber: "VN-1035", name: "Bluecrew", region: "National", state: "Multiple", specialty: "W-2 Staffing Platform", software: "Full Platform", softwareType: "platform", employees: 3800, fillRate: 95, onTimeRate: 97, csat: 4.6, spend: "$185k", approval_status: "approved", legal_name: "Bluecrew Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Chris Miller", primary_contact_email: "chris@bluecrew.com", w9_document: true, coi_document: true, coverage_regions: ["National Coverage"] }, { id: 'mock-1036', vendorNumber: "VN-1036", name: "Qwick", region: "National", state: "Multiple", specialty: "Hospitality Platform", software: "Full Platform", softwareType: "platform", employees: 3500, fillRate: 94, onTimeRate: 96, csat: 4.5, spend: "$175k", approval_status: "approved", legal_name: "Qwick Inc.", tax_id: "XX-XXXXXXX", business_type: "Corporation", primary_contact_name: "Sarah Mitchell", primary_contact_email: "sarah@qwick.com", w9_document: true, coi_document: true, coverage_regions: ["National Coverage"] }, ]; // Combine all vendors (mock + database approved + database pending) const allVendorsMap = new Map(); mockApprovedVendors.forEach(vendor => allVendorsMap.set(vendor.id, vendor)); approvedVendors.forEach(vendor => { if (!allVendorsMap.has(vendor.id)) { allVendorsMap.set(vendor.id, { ...vendor, name: vendor.legal_name, vendorNumber: vendor.vendor_number, }); } }); const allVendors = Array.from(allVendorsMap.values()); // Calculate status counts const statusCounts = { all: allVendors.length, approved: allVendors.filter(v => v.approval_status === "approved").length, pending: pendingVendors.length, suspended: allVendors.filter(v => v.approval_status === "suspended").length, terminated: allVendors.filter(v => v.approval_status === "terminated").length, archived: allVendors.filter(v => v.approval_status === "archived").length, }; // Get unique values for filters const uniqueRegions = [...new Set(allVendors.map(v => v.region).filter(Boolean))].sort(); const uniqueStates = [...new Set(allVendors.map(v => v.state).filter(Boolean))].sort(); const uniqueSpecialties = [...new Set(allVendors.map(v => v.specialty).filter(Boolean))].sort(); // Apply filters with proper logic const filteredVendors = allVendors.filter(vendor => { const matchesSearch = !searchTerm || vendor.name?.toLowerCase().includes(searchTerm.toLowerCase()) || vendor.vendorNumber?.toLowerCase().includes(searchTerm.toLowerCase()) || vendor.vendor_number?.toLowerCase().includes(searchTerm.toLowerCase()) || vendor.specialty?.toLowerCase().includes(searchTerm.toLowerCase()); const matchesRegion = regionFilter === "all" || vendor.region === regionFilter; const matchesSpecialty = specialtyFilter === "all" || vendor.specialty === specialtyFilter; const matchesState = stateFilter === "all" || vendor.state === stateFilter; const matchesSoftware = softwareFilter === "all" || vendor.softwareType === softwareFilter; const matchesStatus = statusFilter === "all" || vendor.approval_status === statusFilter; return matchesSearch && matchesRegion && matchesSpecialty && matchesState && matchesSoftware && matchesStatus; }); // Reset to page 1 when filters change React.useEffect(() => { setCurrentPage(1); }, [searchTerm, regionFilter, stateFilter, specialtyFilter, softwareFilter, statusFilter]); // Pagination const totalPages = Math.ceil(filteredVendors.length / itemsPerPage); const startIndex = (currentPage - 1) * itemsPerPage; const endIndex = startIndex + itemsPerPage; const paginatedVendors = filteredVendors.slice(startIndex, endIndex); const getSoftwareBadge = (type, label) => { if (type === "platform") { return ✅ {label}; } else if (type === "building") { return ⚙️ {label}; } else if (type === "partial") { return ⚙️ {label}; } else { return ❌ {label}; } }; const handleEditVendor = (vendor) => { setEditingVendor(vendor); setShowEditModal(true); }; const handleReviewVendor = (vendor) => { setReviewingVendor(vendor); setShowReviewModal(true); }; const handleRejectClick = () => { setShowRejectModal(true); }; const getStatusColor = (status) => { const colors = { all: "from-slate-600 to-slate-700", approved: "from-emerald-500 to-emerald-600", pending: "from-amber-500 to-amber-600", suspended: "from-orange-500 to-orange-600", terminated: "from-red-500 to-red-600", archived: "from-slate-400 to-slate-500" }; return colors[status] || colors.all; }; const getStatusIcon = (status) => { const icons = { all: Package, approved: CheckCircle2, pending: Clock, suspended: XCircle, terminated: XCircle, archived: Archive }; return icons[status] || Package; }; const getStatusLabel = (status) => { const labels = { all: "All", approved: "Active", pending: "Review", suspended: "Hold", terminated: "Closed", archived: "Archived" }; return labels[status] || status; }; return (
} /> {/* Status Cards */}
{/* All Vendors */} setStatusFilter("all")} >
{getStatusLabel("all")}
{statusCounts.all}

Total Vendors

{/* Approved */} setStatusFilter("approved")} >
{getStatusLabel("approved")}
{statusCounts.approved}

Approved

{/* Pending */} setStatusFilter("pending")} >
{getStatusLabel("pending")}
{statusCounts.pending}

Pending

{/* Suspended */} setStatusFilter("suspended")} >
{getStatusLabel("suspended")}
{statusCounts.suspended}

Suspended

{/* Terminated */} setStatusFilter("terminated")} >
{getStatusLabel("terminated")}
{statusCounts.terminated}

Terminated

{/* Archived */} setStatusFilter("archived")} >
{getStatusLabel("archived")}
{statusCounts.archived}

Archived

{/* Search and Filters */}
setSearchTerm(e.target.value)} className="pl-10" />
{/* View Mode Toggle */}
{/* Active Filters Display */} {(searchTerm || regionFilter !== "all" || stateFilter !== "all" || specialtyFilter !== "all" || softwareFilter !== "all" || statusFilter !== "all") && (
Active Filters: {searchTerm && ( Search: "{searchTerm}" )} {statusFilter !== "all" && ( Status: {statusFilter} )} {regionFilter !== "all" && ( Region: {regionFilter} )} {stateFilter !== "all" && ( State: {stateFilter} )} {specialtyFilter !== "all" && ( Specialty: {specialtyFilter} )} {softwareFilter !== "all" && ( Software: {softwareFilter} )}
)}
{/* Vendor Directory - Grid or List View */} {viewMode === "grid" ? ( // Grid View
{paginatedVendors.map((vendor, idx) => (
{vendor.approval_status === 'pending' ? ( ) : ( )}

{vendor.name || vendor.legal_name}

{vendor.vendorNumber || vendor.vendor_number}

{vendor.approval_status === 'pending' && ( Pending Review )}
Region: {vendor.region || '—'}
Specialty: {vendor.specialty || '—'}
Staff: {vendor.employees ? vendor.employees.toLocaleString() : 'N/A'}
{vendor.approval_status === 'pending' ? ( ) : ( <> archiveVendorMutation.mutate(vendor.id)}> Archive )}
))}
) : ( // List View (Table)
{paginatedVendors.length > 0 ? ( paginatedVendors.map((vendor, idx) => ( )) ) : ( )}
Vendor Name Region State Specialty Total Staff Software/Platform Actions
{ if (vendor.approval_status === 'pending') { handleReviewVendor(vendor); } else { handleEditVendor(vendor); } }} >
{vendor.approval_status === 'pending' ? ( ) : ( )}

{vendor.name || vendor.legal_name} {vendor.approval_status === 'pending' && ( Pending Review )}

{vendor.region || '—'} {vendor.state || '—'} {vendor.specialty || '—'} {vendor.employees ? vendor.employees.toLocaleString() : 'N/A'} {vendor.software ? getSoftwareBadge(vendor.softwareType, vendor.software) : '—'}
{vendor.approval_status === 'pending' ? ( ) : ( <> archiveVendorMutation.mutate(vendor.id)}> Archive )}

No vendors found

Try adjusting your search or filters

{/* Pagination */} {totalPages > 1 && (
Page {currentPage} of {totalPages}
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => { let pageNum; if (totalPages <= 5) { pageNum = i + 1; } else if (currentPage <= 3) { pageNum = i + 1; } else if (currentPage >= totalPages - 2) { pageNum = totalPages - 4 + i; } else { pageNum = currentPage - 2 + i; } return ( ); })}
)}
)}
{/* Edit Vendor Modal */} { setShowEditModal(false); setEditingVendor(null); }} onEdit={(vendor) => {}} /> {/* Quick Reject Modal (for reject button in header) */} Reject Pending Vendors

You have {pendingVendors.length} pending vendor{pendingVendors.length !== 1 ? 's' : ''}. Select vendors to reject:

{pendingVendors.map((vendor) => ( handleReviewVendor(vendor)}>

{vendor.legal_name}

{vendor.vendor_number}

))}
{/* Review & Approve Modal */} Review Vendor Application {reviewingVendor && (
{/* Vendor Info */}

{reviewingVendor.legal_name || reviewingVendor.name}

Vendor Number: {reviewingVendor.vendor_number || reviewingVendor.vendorNumber}
Business Type: {reviewingVendor.business_type || 'N/A'}
Tax ID: {reviewingVendor.tax_id || 'N/A'}
Contact: {reviewingVendor.primary_contact_name || 'N/A'}
{/* Documents */}

Documents

{reviewingVendor.w9_document ? (
W-9 Form
) : (
W-9 Form (Missing)
)} {reviewingVendor.coi_document ? (
COI
) : (
COI (Missing)
)}
{/* Service Coverage */}

Service Coverage

{reviewingVendor.coverage_regions && reviewingVendor.coverage_regions.length > 0 ? (
{reviewingVendor.coverage_regions.map((city, i) => ( {city} ))}
) : (

No coverage regions specified.

)}
{/* Rate Proposals */}

Rate Proposals ({allVendorRates.filter(r => r.vendor_id === reviewingVendor.id).length})

{allVendorRates.filter(r => r.vendor_id === reviewingVendor.id).length > 0 ? ( allVendorRates.filter(r => r.vendor_id === reviewingVendor.id).map((rate, i) => (
{rate.role_name} {rate.category}
Pay: ${rate.employee_wage} Markup: {rate.markup_percentage}% Bill: ${rate.client_rate}
)) ) : (

No rate proposals submitted yet.

)}
{/* Rejection Reason (optional) */}

Provide feedback to help the vendor improve their application