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 (
Here's what's happening with your orders
Create New
Order Now
Today's Orders
{todayOrders.length}
In Progress
{activeOrders}
Needs Attention
{needsAttention}
Status
All Good!
Cost
Analysis
This Month
${(thisMonthSpend / 1000).toFixed(1)}k
{spendChange !== 0 && (Labor
Summary
This Month
{thisMonthStaff}
{staffChange !== 0 && (Sales
Analytics
This Month
{thisMonthOrders.length}
ordersLast 6 months overview
One tap to reorder • Saved {timeSavedHours}h {remainingMinutes}m
Avg Cost
${(avgCost / 1000).toFixed(1)}k
Staff
{Math.round(totalStaff / count)}
No previous orders yet
Complete your first order to see recommendations
Total Spending
${Math.round(totalSpending / 1000)}k
Lifetime value • {completedOrders.length} orders
All Orders
{events.length} total
Messages
Get support
{event.event_name}
{(isToday || isUrgent) && (No upcoming orders