import React, { useState } 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 { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Calendar, Plus, Clock, DollarSign, MessageSquare, RefreshCw, ArrowRight, Users, CloudOff, MapPin, AlertTriangle, Package, Download, TrendingUp, TrendingDown, BarChart3, Sparkles } from "lucide-react"; import { format, parseISO, differenceInDays, startOfMonth, endOfMonth, subMonths } from "date-fns"; import { useToast } from "@/components/ui/use-toast"; import { LineChart, Line, BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; export default function ClientDashboard() { const navigate = useNavigate(); const queryClient = useQueryClient(); const { toast } = useToast(); const [reorderingId, setReorderingId] = useState(null); const { data: user } = useQuery({ queryKey: ['current-user'], queryFn: () => base44.auth.me(), }); const { data: events } = useQuery({ queryKey: ['client-events'], queryFn: async () => { const allEvents = await base44.entities.Event.list('-date'); const clientEvents = allEvents.filter(e => e.client_email === user?.email || e.business_name === user?.company_name || e.created_by === user?.email ); if (clientEvents.length === 0) { return allEvents.filter(e => e.status === "Completed"); } return clientEvents; }, initialData: [], enabled: !!user }); const createEventMutation = useMutation({ mutationFn: (eventData) => base44.entities.Event.create(eventData), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['client-events'] }); toast({ title: "✅ Order Created", description: "Your reorder has been created successfully", }); setReorderingId(null); }, onError: () => { toast({ title: "❌ Failed to Create Order", description: "Please try again", variant: "destructive", }); setReorderingId(null); }, }); // Enhanced Analytics 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").length; const completedOrders = events.filter(e => e.status === "Completed"); const totalSpending = completedOrders.reduce((sum, e) => sum + (e.total || 0), 0); const needsAttention = events.filter(e => e.status === "Pending" || e.status === "Draft").length; // Monthly spending trend (last 6 months) const last6Months = Array.from({ length: 6 }, (_, i) => { const date = subMonths(new Date(), 5 - i); return { month: format(date, 'MMM'), fullDate: date }; }); const spendingTrend = 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 spend = monthOrders.reduce((sum, e) => sum + (e.total || 0), 0); const orderCount = monthOrders.length; const staffCount = monthOrders.reduce((sum, e) => sum + (e.requested || 0), 0); return { month, spend, orderCount, staffCount }; }); // Cost breakdown by category 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"; }); const lastMonthOrders = events.filter(e => { const eventDate = new Date(e.date); const lastMonth = subMonths(new Date(), 1); return eventDate.getMonth() === lastMonth.getMonth() && eventDate.getFullYear() === lastMonth.getFullYear() && e.status === "Completed"; }); const thisMonthSpend = thisMonthOrders.reduce((sum, e) => sum + (e.total || 0), 0); const lastMonthSpend = lastMonthOrders.reduce((sum, e) => sum + (e.total || 0), 0); const spendChange = lastMonthSpend > 0 ? ((thisMonthSpend - lastMonthSpend) / lastMonthSpend) * 100 : 0; // Labor analytics const totalStaffHired = completedOrders.reduce((sum, e) => sum + (e.requested || 0), 0); const thisMonthStaff = thisMonthOrders.reduce((sum, e) => sum + (e.requested || 0), 0); const lastMonthStaff = lastMonthOrders.reduce((sum, e) => sum + (e.requested || 0), 0); const staffChange = lastMonthStaff > 0 ? ((thisMonthStaff - lastMonthStaff) / lastMonthStaff) * 100 : 0; const avgStaffPerOrder = completedOrders.length > 0 ? totalStaffHired / completedOrders.length : 0; const costPerStaff = totalStaffHired > 0 ? totalSpending / totalStaffHired : 0; // Sales analytics const avgOrderValue = completedOrders.length > 0 ? totalSpending / completedOrders.length : 0; // Upcoming const upcomingEvents = events .filter(e => { const eventDate = new Date(e.date); const today = new Date(); return eventDate > today && e.status !== "Canceled"; }) .sort((a, b) => new Date(a.date) - new Date(b.date)) .slice(0, 5); // Frequent orders const pastOrders = events.filter(e => e.status === "Completed"); const orderFrequency = pastOrders.reduce((acc, event) => { const key = event.event_name; if (!acc[key]) { acc[key] = { event, count: 0, lastOrdered: event.date, totalSpent: 0, avgCost: 0, totalStaff: 0 }; } acc[key].count++; acc[key].totalSpent += (event.total || 0); acc[key].totalStaff += (event.requested || 0); if (new Date(event.date) > new Date(acc[key].lastOrdered)) { acc[key].lastOrdered = event.date; acc[key].event = event; } return acc; }, {}); Object.values(orderFrequency).forEach(item => { item.avgCost = item.count > 0 ? item.totalSpent / item.count : 0; }); const frequentOrders = Object.values(orderFrequency) .sort((a, b) => b.count - a.count) .slice(0, 3); const totalReorders = frequentOrders.reduce((sum, item) => sum + item.count, 0); const timeSavedMinutes = totalReorders * 15; const timeSavedHours = Math.floor(timeSavedMinutes / 60); const remainingMinutes = timeSavedMinutes % 60; const handleQuickReorder = (event) => { setReorderingId(event.id); const reorderData = { event_name: event.event_name, business_id: event.business_id, business_name: event.business_name, hub: event.hub, status: "Draft", requested: event.requested, shifts: event.shifts, notes: `Reorder of: ${event.event_name}`, }; createEventMutation.mutate(reorderData); }; const handleRefresh = () => { window.location.reload(); }; 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] || 'Client'}

Here's what's happening with your orders

{/* Hero Stats - 4 Cards */}

Create New

Order Now

{todayOrders.length > 0 ? 'Active' : 'None'}

Today's Orders

{todayOrders.length}

Active

In Progress

{activeOrders}

{needsAttention > 0 ? (

Needs Attention

{needsAttention}

) : (
All Clear

Status

All Good!

)}
{/* Main Content Grid */}
{/* Left Column - Analytics (2 cols) */}
{/* Big Analytics Cards */}
{/* Cost Analysis */}

Cost

Analysis

This Month

${(thisMonthSpend / 1000).toFixed(1)}k

{spendChange !== 0 && (
0 ? 'text-green-600' : 'text-red-600'}`}> {spendChange > 0 ? : } {Math.abs(spendChange).toFixed(0)}%
)}
Avg Order ${Math.round(avgOrderValue).toLocaleString()}
Cost/Staff ${Math.round(costPerStaff)}
{/* Labor Summary */}

Labor

Summary

This Month

{thisMonthStaff}

{staffChange !== 0 && (
0 ? 'text-blue-600' : 'text-red-600'}`}> {staffChange > 0 ? : } {Math.abs(staffChange).toFixed(0)}%
)}
Total Staff {totalStaffHired}
Avg/Order {Math.round(avgStaffPerOrder)}
{/* Sales Analytics */}

Sales

Analytics

This Month

{thisMonthOrders.length}

orders
Total Orders {events.length}
Completed {completedOrders.length}
{/* 6-Month Trend */}

Spending Trend

Last 6 months overview

6 Months
`$${(value / 1000).toFixed(0)}k`} /> [`$${Math.round(value).toLocaleString()}`, 'Spend']} />
{/* Quick Reorder */}

Order it again

One tap to reorder • Saved {timeSavedHours}h {remainingMinutes}m

Top 3
{frequentOrders.length > 0 ? (
{frequentOrders.map((item, index) => { const { event, count, avgCost, totalStaff, lastOrdered } = item; const isReordering = reorderingId === event.id; const medals = [ { icon: "🥇", color: "from-yellow-400 to-amber-500" }, { icon: "🥈", color: "from-slate-400 to-slate-500" }, { icon: "🥉", color: "from-amber-600 to-orange-700" } ]; const medal = medals[index]; return (
{medal.icon}
Ordered {count}x

{event.event_name}

{event.hub && (
{event.hub}
)}

Avg Cost

${(avgCost / 1000).toFixed(1)}k

Staff

{Math.round(totalStaff / count)}

Last ordered {format(parseISO(lastOrdered), "MMM d")}
); })}
) : (

No previous orders yet

Complete your first order to see recommendations

)}
{/* Right Column - Quick Access (1 col) */}
{/* Financial Card */}

Total Spending

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

Lifetime value • {completedOrders.length} orders

{/* Quick Actions */}

All Orders

{events.length} total

Messages

Get support

{/* Coming Up */}

Coming Up

{upcomingEvents.length > 0 ? (
{upcomingEvents.map((event) => { const daysUntil = differenceInDays(new Date(event.date), new Date()); const isUrgent = daysUntil <= 3; const isToday = daysUntil === 0; return (

{event.event_name}

{(isToday || isUrgent) && ( {isToday ? "Today" : `${daysUntil}d`} )}
{format(new Date(event.date), "MMM d, h:mm a")} {event.requested || 0} staff
); })}
) : (

No upcoming orders

)}
); }