import React, { useState, useMemo } 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 { Switch } from "@/components/ui/switch"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Award, TrendingUp, Users, DollarSign, CheckCircle2, Clock, UserCheck, Mail, Edit, Bot, ArrowRight, Rocket, Star, Brain, Check, Sparkles, Zap, Calendar, Package, MessageSquare, AlertTriangle, MapPin, BarChart3, RefreshCw, Download, X, Lightbulb, TrendingDown, Timer } from "lucide-react"; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, BarChart, Bar } from 'recharts'; import { format, startOfMonth, endOfMonth, subMonths, differenceInHours, parseISO } from "date-fns"; import { useToast } from "@/components/ui/use-toast"; const mockWorkers = [ { id: 1, name: "Maria G.", skill: "Server", rating: 4.9, distance: 3.2, reliability: 0.99, pro: 2 }, { id: 2, name: "Andre P.", skill: "Culinary", rating: 4.8, distance: 5.5, reliability: 0.96, pro: 3 }, { id: 3, name: "Jae L.", skill: "Dishwasher", rating: 4.6, distance: 1.1, reliability: 0.93, pro: 1 }, { id: 4, name: "Rita C.", skill: "Server", rating: 4.7, distance: 4.0, reliability: 0.95, pro: 2 }, { id: 5, name: "Luis M.", skill: "Culinary", rating: 4.5, distance: 7.4, reliability: 0.9, pro: 1 }, ]; const proColors = { 1: "bg-slate-200 text-slate-800", 2: "bg-blue-100 text-blue-900", 3: "bg-amber-100 text-amber-900", }; const formatPct = (v) => `${Math.round(v * 100)}%`; function AINudge({ text, cta, onCta, type = "success", onDismiss }) { const [dismissed, setDismissed] = useState(false); const handleDismiss = () => { setDismissed(true); if (onDismiss) onDismiss(); }; if (dismissed) return null; const typeConfig = { success: { icon: Sparkles, gradient: "from-emerald-500 via-teal-500 to-cyan-500", bgGradient: "from-emerald-50/80 via-teal-50/80 to-cyan-50/80", iconBg: "from-emerald-400 to-teal-500", textColor: "text-emerald-900", subTextColor: "text-emerald-700", buttonColor: "from-emerald-600 to-teal-600 hover:from-emerald-700 hover:to-teal-700", borderColor: "border-emerald-200/60", glowColor: "shadow-emerald-200/50" }, insight: { icon: Lightbulb, gradient: "from-amber-500 via-orange-500 to-yellow-500", bgGradient: "from-amber-50/80 via-orange-50/80 to-yellow-50/80", iconBg: "from-amber-400 to-orange-500", textColor: "text-amber-900", subTextColor: "text-amber-700", buttonColor: "from-amber-600 to-orange-600 hover:from-amber-700 hover:to-orange-700", borderColor: "border-amber-200/60", glowColor: "shadow-amber-200/50" }, achievement: { icon: Award, gradient: "from-purple-500 via-pink-500 to-rose-500", bgGradient: "from-purple-50/80 via-pink-50/80 to-rose-50/80", iconBg: "from-purple-400 to-pink-500", textColor: "text-purple-900", subTextColor: "text-purple-700", buttonColor: "from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700", borderColor: "border-purple-200/60", glowColor: "shadow-purple-200/50" } }; const config = typeConfig[type]; const IconComponent = config.icon; return (
AI INSIGHT

{text}

{cta && ( )}
); } export default function VendorDashboard() { const navigate = useNavigate(); const queryClient = useQueryClient(); const { toast } = useToast(); const { data: user } = useQuery({ queryKey: ['current-user-vendor'], queryFn: () => base44.auth.me(), }); 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 }); // Calculations const todayOrders = events.filter(e => { const eventDate = new Date(e.date); const today = new Date(); return eventDate.toDateString() === today.toDateString(); }); const activeOrders = 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 avgRating = staff.reduce((sum, s) => sum + (s.rating || 0), 0) / (staff.length || 1); const reliabilityScore = staff.reduce((sum, s) => sum + (s.reliability_score || 0), 0) / (staff.length || 1); // Rapid Orders (urgent orders within 24 hours) 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"); }) .sort((a, b) => new Date(a.date) - new Date(b.date)) .slice(0, 3); // 6-month revenue and payroll trend const last6Months = Array.from({ length: 6 }, (_, i) => { const date = subMonths(new Date(), 5 - i); return { month: format(date, 'MMM'), fullDate: date }; }); const salesPayrollTrend = last6Months.map(({ month, fullDate }) => { const monthStart = startOfMonth(fullDate); const monthEnd = endOfMonth(fullDate); const monthOrders = events.filter(e => { const eventDate = new Date(e.date); return eventDate >= monthStart && eventDate <= monthEnd && e.status === "Completed"; }); const sales = monthOrders.reduce((sum, e) => sum + (e.total || 0), 0); const payroll = sales * 0.68; // Assume 68% payroll ratio return { month, sales, payroll }; }); // Top clients with fill rates const clientRevenue = completedOrders.reduce((acc, event) => { const client = event.business_name || "Unknown"; if (!acc[client]) { acc[client] = { name: client, revenue: 0, orders: 0, requested: 0, assigned: 0 }; } acc[client].revenue += (event.total || 0); acc[client].orders++; acc[client].requested += (event.requested || 0); acc[client].assigned += (event.assigned_staff?.length || 0); return acc; }, {}); const topClients = Object.values(clientRevenue) .map(c => ({ ...c, fillRate: c.requested > 0 ? (c.assigned / c.requested) * 100 : 0 })) .sort((a, b) => b.revenue - a.revenue) .slice(0, 4); // Top performers with shift counts 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 })); // Staff assigned today const staffAssignedToday = todayOrders.reduce((sum, e) => sum + (e.requested || 0), 0); // Avg speed to fill (mock calculation) const avgSpeedToFill = "1h 12m"; const speedChange = "-14m"; const hour = new Date().getHours(); const greeting = hour < 12 ? "Good morning" : hour < 18 ? "Good afternoon" : "Good evening"; return (
{/* Header */}

{greeting}, {user?.full_name?.split(' ')[0] || 'Partner'}

Here's your performance overview

{/* Top KPI Metrics - NEW DESIGN */}
{/* Sales */}

Sales (M1D)

${Math.round(thisMonthRevenue / 1000)},000

+5% vs last month

{/* Payroll */}

Payroll (M1D)

${Math.round(thisMonthRevenue * 0.68 / 1000)},000

68% of sales

{/* Active Workforce */}

Active Workforce

{activeStaff}

+3 this week

{/* Avg Speed to Fill */}

Avg Speed-to-Fill

{avgSpeedToFill}

{speedChange} vs last week

{/* AI Insights */}
{}} /> {}} />
{/* Main Content Grid */}
{/* Left Column (2 cols) */}
{/* RAPID ORDERS - NEW SECTION */}
Rapid Orders

Urgent orders within 24 hours

{rapidOrders.length > 0 && ( {rapidOrders.length} Urgent )}
{rapidOrders.length > 0 ? (
{rapidOrders.map((order) => { const eventDate = order.date ? parseISO(order.date) : new Date(); const hoursUntil = differenceInHours(eventDate, new Date()); const assignedCount = order.assigned_staff?.length || 0; const requestedCount = order.requested || 0; const fillPercentage = requestedCount > 0 ? Math.round((assignedCount / requestedCount) * 100) : 0; return (

{order.business_name || "Client"} – {order.event_name}

{hoursUntil}h left
{assignedCount}/{requestedCount} filled
{format(eventDate, "h:mm a")}
{fillPercentage}%
); })}
) : (

No urgent orders

Orders within 24h will appear here

)} {/* Sales vs Payroll Chart */} Sales vs Payroll `${(value / 1000).toFixed(0)}k`} /> [`$${Math.round(value).toLocaleString()}`, '']} /> {/* Client Analysis */} Client Analysis {topClients.length > 0 ? (
{topClients.map((client) => (

{client.name}

Fill

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

{Math.round(client.fillRate)}%

))}
) : (

No client data

)}
{/* Right Column (1 col) */}
{/* Total Revenue Card - NEW DESIGN */}

Total Revenue

${Math.round(totalRevenue / 1000)}k

All time earnings

{/* Quick Actions - NEW DESIGN */}

All Orders

View & manage

My Orders

Manage staff

{/* Today's Metrics */}

{todayOrders.length}

Orders Today

Active

{activeOrders.length}

In Progress

{/* Staff Assigned Today */}
Today

{staffAssignedToday}

Staff Assigned

{/* Top Clients - NEW DESIGN */} Top Clients {topClients.slice(0, 3).length > 0 ? (
{topClients.slice(0, 3).map((client) => (

{client.name}

+{client.orders}%

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

))}
) : (

No client data

)}
{/* Top Performers - NEW DESIGN */} Top Performers {topPerformers.length > 0 ? (
{topPerformers.map((member) => (

{member.employee_name} - {member.position || "Staff"}

{member.shifts} shifts

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

No staff data

)}
{/* Gold Vendors - NEW SECTION */} Gold Vendors

Legendary Staffing

Score

98

Epic Workforce

Score

96

); }