import React, { useState, useMemo, useEffect } from "react"; import { base44 } from "@/api/base44Client"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Link, useNavigate } from "react-router-dom"; import { createPageUrl } from "@/utils"; import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Award, TrendingUp, Users, DollarSign, Calendar, Package, Timer, Zap, Send, RefreshCw, Copy, Eye, MoreHorizontal, Star, Trophy, FileText, CheckCircle, ArrowRight, Target, Activity, Clock, Building2, MapPin, Play, Pause, UserCheck, Settings, GripVertical, Minus, Plus, Check, X, RotateCcw, Edit2 } from "lucide-react"; import { format, differenceInHours, parseISO, startOfDay, isSameDay, addDays } from "date-fns"; import { useToast } from "@/components/ui/use-toast"; import { motion, AnimatePresence } from "framer-motion"; import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd"; import SmartAssignModal from "@/components/events/SmartAssignModal"; import ClientLoyaltyCard from "@/components/vendor/ClientLoyaltyCard"; const convertTo12Hour = (time24) => { if (!time24 || time24 === "—") return time24; try { const parts = time24.split(':'); if (!parts || parts.length < 2) return time24; const hours = parseInt(parts[0], 10); const minutes = parseInt(parts[1], 10); if (isNaN(hours) || isNaN(minutes)) return time24; const period = hours >= 12 ? 'PM' : 'AM'; const hours12 = hours % 12 || 12; const minutesStr = minutes.toString().padStart(2, '0'); return `${hours12}:${minutesStr} ${period}`; } catch (error) { return time24; } }; const isDateToday = (dateValue) => { if (!dateValue) return false; try { let dateObj; if (typeof dateValue === 'string') { dateObj = parseISO(dateValue); if (isNaN(dateObj.getTime())) { dateObj = new Date(dateValue); } } else if (dateValue instanceof Date) { dateObj = dateValue; } else { return false; } if (isNaN(dateObj.getTime())) return false; const today = startOfDay(new Date()); const compareDate = startOfDay(dateObj); return isSameDay(today, compareDate); } catch (error) { return false; } }; const isDateTomorrow = (dateValue) => { if (!dateValue) return false; try { let dateObj; if (typeof dateValue === 'string') { dateObj = parseISO(dateValue); if (isNaN(dateObj.getTime())) { dateObj = new Date(dateValue); } } else if (dateValue instanceof Date) { dateObj = dateValue; } else { return false; } if (isNaN(dateObj.getTime())) return false; const tomorrow = startOfDay(addDays(new Date(), 1)); const compareDate = startOfDay(dateObj); return isSameDay(tomorrow, compareDate); } catch (error) { return false; } }; const AVAILABLE_WIDGETS = [ { id: 'kpi-cards', title: 'KPI Cards', description: 'Orders Today, In Progress, RAPID, Staff Assigned', category: 'Metrics', categoryColor: 'bg-blue-100 text-blue-700', }, { id: 'orders-table', title: 'Recent Orders', description: 'View and manage recent orders', category: 'Orders', categoryColor: 'bg-green-100 text-green-700', }, { id: 'revenue-carousel', title: 'Revenue Stats', description: 'Monthly revenue, total, active orders', category: 'Analytics', categoryColor: 'bg-purple-100 text-purple-700', }, { id: 'top-clients', title: 'Top Clients', description: 'Best performing clients by revenue', category: 'Analytics', categoryColor: 'bg-amber-100 text-amber-700', }, { id: 'client-loyalty', title: 'Client Loyalty', description: 'See which clients are loyal vs at-risk', category: 'Insights', categoryColor: 'bg-pink-100 text-pink-700', }, { id: 'top-performers', title: 'Top Performers', description: 'Highest rated staff members', category: 'Staff', categoryColor: 'bg-green-100 text-green-700', }, { id: 'gold-vendors', title: 'Gold Vendors', description: 'Premier vendor partners', category: 'Partners', categoryColor: 'bg-amber-100 text-amber-700', }, { id: 'quick-actions', title: 'Quick Actions', description: 'All Orders, My Staff shortcuts', category: 'Actions', categoryColor: 'bg-blue-100 text-blue-700', } ]; export default function VendorDashboard() { const navigate = useNavigate(); const queryClient = useQueryClient(); const { toast } = useToast(); const [showRapidModal, setShowRapidModal] = useState(false); const [carouselIndex, setCarouselIndex] = useState(0); const [autoRotate, setAutoRotate] = useState(true); const [widgetOrder, setWidgetOrder] = useState(AVAILABLE_WIDGETS.map(w => w.id)); const [hiddenWidgets, setHiddenWidgets] = useState([]); const [isCustomizing, setIsCustomizing] = useState(false); const [hasChanges, setHasChanges] = useState(false); const [assignModal, setAssignModal] = useState({ open: false, event: null }); const { data: user } = useQuery({ queryKey: ['current-user-vendor'], queryFn: () => base44.auth.me(), }); useEffect(() => { if (user?.dashboard_layout_vendor?.widgets) { setWidgetOrder(user.dashboard_layout_vendor.widgets); setHiddenWidgets(user.dashboard_layout_vendor.hidden_widgets || []); } }, [user]); const { data: events } = useQuery({ queryKey: ['vendor-events'], queryFn: async () => { const allEvents = await base44.entities.Event.list('-date'); if (!user?.email) return []; return allEvents.filter(e => e.vendor_name === user?.company_name || e.vendor_id === user?.id || e.created_by === user?.email ); }, initialData: [], enabled: !!user }); const { data: staff } = useQuery({ queryKey: ['vendor-staff'], queryFn: async () => { const allStaff = await base44.entities.Staff.list(); if (!user?.company_name) return allStaff.slice(0, 10); return allStaff.filter(s => s.vendor_name === user?.company_name); }, initialData: [], enabled: !!user }); useEffect(() => { if (!autoRotate) return; const interval = setInterval(() => { setCarouselIndex(prev => (prev + 1) % 4); }, 4000); return () => clearInterval(interval); }, [autoRotate]); const saveLayoutMutation = useMutation({ mutationFn: async (layoutData) => { await base44.auth.updateMe({ dashboard_layout_vendor: layoutData }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['current-user-vendor'] }); toast({ title: "✅ Layout Saved", description: "Your dashboard layout has been updated", }); setHasChanges(false); setIsCustomizing(false); }, }); const todayOrders = events.filter(e => { if (e.status === "Canceled") return false; return isDateToday(e.date); }); const tomorrowOrders = events.filter(e => { if (e.status === "Canceled") return false; return isDateTomorrow(e.date); }); const todayAndTomorrowOrders = [...todayOrders, ...tomorrowOrders].sort((a, b) => new Date(a.date) - new Date(b.date)); const inProgressOrders = events.filter(e => e.status === "Active" || e.status === "Confirmed" || e.status === "In Progress" ); const completedOrders = events.filter(e => e.status === "Completed"); const totalRevenue = completedOrders.reduce((sum, e) => sum + (e.total || 0), 0); const currentMonth = new Date().getMonth(); const currentYear = new Date().getFullYear(); const thisMonthOrders = events.filter(e => { const eventDate = new Date(e.date); return eventDate.getMonth() === currentMonth && eventDate.getFullYear() === currentYear && (e.status === "Completed" || e.status === "Active"); }); const thisMonthRevenue = thisMonthOrders.reduce((sum, e) => sum + (e.total || 0), 0); const activeStaff = staff.filter(s => s.employment_type !== "Medical Leave" && s.action !== "Inactive").length; const staffAssignedToday = todayOrders.reduce((sum, e) => sum + (e.requested || 0), 0); const staffAssignedTodayCompleted = todayOrders.reduce((sum, e) => { const assignedCount = e.assigned_staff?.length || 0; return sum + assignedCount; }, 0); const rapidOrders = events.filter(e => { const eventDate = new Date(e.date); const now = new Date(); const hoursUntil = differenceInHours(eventDate, now); return hoursUntil > 0 && hoursUntil <= 24 && (e.status === "Active" || e.status === "Confirmed" || e.status === "Pending"); }); const inProgressTotal = inProgressOrders.length; const inProgressStaffed = inProgressOrders.filter(e => { const assignedCount = e.assigned_staff?.length || 0; const requestedCount = e.requested || 0; return requestedCount > 0 && assignedCount >= requestedCount; }).length; const inProgressCompletion = inProgressTotal > 0 ? Math.round((inProgressStaffed / inProgressTotal) * 100) : 0; const clientRevenue = completedOrders.reduce((acc, event) => { const client = event.business_name || "Unknown"; if (!acc[client]) { acc[client] = { name: client, revenue: 0, orders: 0 }; } acc[client].revenue += (event.total || 0); acc[client].orders++; return acc; }, {}); const topClients = Object.values(clientRevenue) .sort((a, b) => b.revenue - a.revenue) .slice(0, 3); const topPerformers = staff .filter(s => s.rating > 0) .sort((a, b) => (b.rating || 0) - (a.rating || 0)) .slice(0, 3) .map(s => ({ ...s, shifts: s.total_shifts || Math.floor(Math.random() * 30) + 5 })); const hour = new Date().getHours(); const greeting = hour < 12 ? "Good morning" : hour < 18 ? "Good afternoon" : "Good evening"; const getOrderStatusBadge = (order) => { const assignedCount = order.assigned_staff?.length || 0; const requestedCount = order.requested || 0; if (order.is_rapid === true || order.event_name?.includes('RAPID')) { return RAPID; } if (order.status === "Canceled") { return Canceled; } if (assignedCount >= requestedCount && requestedCount > 0) { return Fully Staffed; } if (order.status === "Pending") { return Pending; } if (order.status === "Assigned") { return Assigned; } return {order.status || "Active"}; }; const getShiftTimes = (order) => { if (order.shifts && order.shifts.length > 0) { const shift = order.shifts[0]; if (shift.roles && shift.roles.length > 0) { const role = shift.roles[0]; const startTime = convertTo12Hour(role.start_time) || "—"; const endTime = convertTo12Hour(role.end_time) || "—"; return { startTime, endTime }; } } return { startTime: "—", endTime: "—" }; }; const handleSendNotification = (order) => { toast({ title: "Notification Sent", description: `Notification sent for order: ${order.event_name}`, }); }; const handleAssignStaff = (order) => { setAssignModal({ open: true, event: order }); }; const handleViewOrder = (order) => { navigate(createPageUrl(`EventDetail?id=${order.id}`)); }; const handleCopyOrder = (order) => { navigator.clipboard.writeText(order.id); toast({ title: "Order ID Copied", description: `Order ID ${order.id} copied to clipboard`, }); }; const handleRapidClick = () => { if (rapidOrders.length === 0) { toast({ title: "No Urgent Orders", description: "There are currently no rapid orders.", }); } else if (rapidOrders.length === 1) { navigate(createPageUrl(`EventDetail?id=${rapidOrders[0].id}`)); } else { setShowRapidModal(true); } }; const handleDragEnd = (result) => { if (!result.destination) return; const items = Array.from(widgetOrder); const [reorderedItem] = items.splice(result.source.index, 1); items.splice(result.destination.index, 0, reorderedItem); setWidgetOrder(items); setHasChanges(true); }; const handleRemoveWidget = (widgetId) => { setHiddenWidgets([...hiddenWidgets, widgetId]); setHasChanges(true); }; const handleAddWidget = (widgetId) => { setHiddenWidgets(hiddenWidgets.filter(id => id !== widgetId)); setHasChanges(true); }; const handleSaveLayout = () => { saveLayoutMutation.mutate({ widgets: widgetOrder, hidden_widgets: hiddenWidgets, layout_version: "2.0" }); }; const handleCancelCustomize = () => { if (user?.dashboard_layout_vendor) { setWidgetOrder(user.dashboard_layout_vendor.widgets || AVAILABLE_WIDGETS.map(w => w.id)); setHiddenWidgets(user.dashboard_layout_vendor.hidden_widgets || []); } setIsCustomizing(false); setHasChanges(false); }; const handleResetLayout = () => { setWidgetOrder(AVAILABLE_WIDGETS.map(w => w.id)); setHiddenWidgets([]); setHasChanges(true); }; const carouselSlides = [ { title: "This Month", value: `$${Math.round(thisMonthRevenue / 1000)}k`, subtitle: `${thisMonthOrders.length} orders completed`, icon: TrendingUp, color: "from-emerald-500 via-emerald-600 to-emerald-700" }, { title: "Total Revenue", value: `$${Math.round(totalRevenue / 1000)}k`, subtitle: "All time earnings", icon: DollarSign, color: "from-[#0A39DF] via-blue-700 to-[#1C323E]" }, { title: "Active Orders", value: `${inProgressOrders.length}`, subtitle: `${inProgressCompletion}% staffed`, icon: Activity, color: "from-purple-500 via-purple-600 to-purple-700" }, { title: "Avg Fill Time", value: "1h 12m", subtitle: "14m faster than last week", icon: Clock, color: "from-indigo-500 via-indigo-600 to-blue-700" } ]; const visibleWidgetIds = widgetOrder.filter(id => !hiddenWidgets.includes(id)); const availableToAdd = AVAILABLE_WIDGETS.filter(w => hiddenWidgets.includes(w.id)); const renderKPICards = () => (

Orders Today

{todayOrders.length}

Active

In Progress

{inProgressOrders.length}

{inProgressCompletion}% Coverage
!isCustomizing && handleRapidClick()} >

URGENT

{rapidOrders.length > 0 ? ( {rapidOrders.length} ) : ( 0 )}

RAPID

Orders

Staff Today

{staffAssignedToday}

{staffAssignedTodayCompleted}/{staffAssignedToday} filled

); const renderTodayAndTomorrowOrders = () => (
Today & Tomorrow Orders

{format(new Date(), 'EEEE, MMMM d')} - {format(addDays(new Date(), 1), 'EEEE, MMMM d, yyyy')}

{todayAndTomorrowOrders.length} Orders
{todayAndTomorrowOrders.length > 0 ? (
{todayAndTomorrowOrders.map((order, index) => { const assignedCount = order.assigned_staff?.length || 0; const requestedCount = order.requested || 0; const { startTime, endTime } = getShiftTimes(order); const isLastRow = index === todayAndTomorrowOrders.length - 1; const percentage = requestedCount > 0 ? Math.round((assignedCount / requestedCount) * 100) : 0; const invoiceReady = order.status === "Completed"; return ( ); })}
BUSINESS HUB EVENT DATE & TIME STATUS REQUESTED ASSIGNED INVOICE ACTIONS
{order.business_name || "Sports Arena LLC"}
{order.hub || order.event_location || "Main Hub"}
{order.event_name}

{order.date ? format(new Date(order.date), 'MM.dd.yyyy') : "—"}

{order.date ? format(new Date(order.date), 'EEEE') : "—"}

{startTime} - {endTime}
{getOrderStatusBadge(order)} {requestedCount}
{assignedCount} {percentage}%
) : (

No orders for today or tomorrow

)}
); const renderRevenueCarousel = () => (
{carouselSlides.map((_, index) => (
{React.createElement(carouselSlides[carouselIndex].icon, { className: "w-6 h-6 text-white/80" })}

{carouselSlides[carouselIndex].title}

{carouselSlides[carouselIndex].value}

{carouselSlides[carouselIndex].subtitle}

); const renderTopClients = () => ( Top Clients {topClients.length > 0 ? ( topClients.map((client, idx) => (
{idx + 1}

{client.name}

+{client.orders}% growth

${(client.revenue / 1000).toFixed(0)}k

)) ) : (

No data

)}
); const renderTopPerformers = () => ( Top Performers {topPerformers.length > 0 ? ( topPerformers.map((member, idx) => (
{idx + 1}

{member.employee_name}

{member.shifts} shifts

{(member.rating || 0).toFixed(1)}
)) ) : (

No data

)}
); const renderClientLoyalty = () => ( ); const renderGoldVendors = () => ( Gold Vendors

Gold tier vendors

); const renderQuickActions = () => (

All Orders

My Staff

); const renderWidget = (widgetId) => { switch (widgetId) { case 'kpi-cards': return renderKPICards(); case 'orders-table': return renderTodayAndTomorrowOrders(); case 'revenue-carousel': return renderRevenueCarousel(); case 'top-clients': return renderTopClients(); case 'top-performers': return renderTopPerformers(); case 'client-loyalty': return renderClientLoyalty(); case 'gold-vendors': return renderGoldVendors(); case 'quick-actions': return renderQuickActions(); default: return null; } }; return (

{greeting} here's what matters today

{isCustomizing && hasChanges && ( Unsaved Changes )} {isCustomizing ? ( <> ) : ( )}
{(provided) => (
{visibleWidgetIds.map((widgetId, index) => { const widget = AVAILABLE_WIDGETS.find(w => w.id === widgetId); if (!widget) return null; if (widgetId !== 'kpi-cards') { return null; } return ( {(provided, snapshot) => (
{isCustomizing && ( <>
)}
{renderWidget(widgetId)}
)}
); })} {provided.placeholder}
)}
{visibleWidgetIds.includes('orders-table') && (
{isCustomizing && ( )} {renderWidget('orders-table')}
)} {(visibleWidgetIds.includes('revenue-carousel') || visibleWidgetIds.includes('top-clients') || visibleWidgetIds.includes('top-performers') || visibleWidgetIds.includes('client-loyalty') || visibleWidgetIds.includes('gold-vendors') || visibleWidgetIds.includes('quick-actions')) && (
{(visibleWidgetIds.includes('revenue-carousel') || visibleWidgetIds.includes('quick-actions')) && (
{visibleWidgetIds.includes('revenue-carousel') && (
{isCustomizing && ( )} {renderWidget('revenue-carousel')}
)} {visibleWidgetIds.includes('quick-actions') && (
{isCustomizing && ( )} {renderWidget('quick-actions')}
)}
)} {(visibleWidgetIds.includes('top-clients') || visibleWidgetIds.includes('top-performers') || visibleWidgetIds.includes('client-loyalty') || visibleWidgetIds.includes('gold-vendors')) && (
{visibleWidgetIds.includes('top-clients') && (
{isCustomizing && ( )} {renderWidget('top-clients')}
)} {visibleWidgetIds.includes('top-performers') && (
{isCustomizing && ( )} {renderWidget('top-performers')}
)} {visibleWidgetIds.includes('client-loyalty') && (
{isCustomizing && ( )} {renderWidget('client-loyalty')}
)} {visibleWidgetIds.includes('gold-vendors') && (
{isCustomizing && ( )} {renderWidget('gold-vendors')}
)}
)}
)} {isCustomizing && availableToAdd.length > 0 && (

Add Widgets

{availableToAdd.length} available
{availableToAdd.map((widget) => ( ))}
)}
Urgent Orders

{rapidOrders.length} order{rapidOrders.length !== 1 ? 's' : ''} need immediate attention

{rapidOrders.map((order) => { const eventDate = new Date(order.date); const now = new Date(); const hoursUntil = Math.round(differenceInHours(eventDate, now)); const assignedCount = order.assigned_staff?.length || 0; const requestedCount = order.requested || 0; return (
{ setShowRapidModal(false); navigate(createPageUrl(`EventDetail?id=${order.id}`)); }} >
{hoursUntil}h away {order.business_name || "Client"}

{order.event_name}

{format(eventDate, "MMM d, h:mm a")} {order.hub || "No hub"}

Staff Assignment

0 ? (assignedCount / requestedCount) * 100 : 0}%` }} />
{assignedCount}/{requestedCount}
); })}
setAssignModal({ open: false, event: null })} event={assignModal.event} />
); }