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:
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")}
>
{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)
| Vendor Name |
Region |
State |
Specialty |
Total Staff |
Software/Platform |
Actions |
{paginatedVendors.length > 0 ? (
paginatedVendors.map((vendor, idx) => (
{
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) */}
{/* Review & Approve Modal */}
);
}