import React, { useState, useEffect } from "react"; import { base44 } from "@/api/base44Client"; import { useQuery } from "@tanstack/react-query"; import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, } from "@/components/ui/dialog"; import { BarChart3, TrendingUp, TrendingDown, AlertTriangle, CheckCircle2, DollarSign, Award, Shield, Target, Coffee, ArrowRight, Clock, AlertCircle, Activity, Users, FileText, ChevronRight, Edit, X, Calendar, Package, RefreshCw, Printer, Mail, Download, User, Menu, HelpCircle } from "lucide-react"; import { format } from "date-fns"; import { createPageUrl } from "@/utils"; import { Link } from "react-router-dom"; import { LineChart, Line, BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, AreaChart, Area, PieChart, Pie, Cell } from 'recharts'; const MARKET_AVERAGES = { "Banquet Captain": 47.76, "Server": 41.56, "Cook": 44.58, "Line Cook": 44.58, "Full Bartender": 47.76, "Sous Chef": 59.75, "Executive Chef": 70.60, "Dishwasher/ Steward": 38.38, "Prep Cook": 37.98, "Barback": 36.13, "Busser": 39.23, }; const COLORS = ['#0A39DF', '#10b981', '#f59e0b', '#8b5cf6', '#ec4899', '#ef4444', '#3b82f6']; // Safe date formatter const safeFormatDate = (dateString, formatStr) => { if (!dateString) return "N/A"; try { const date = new Date(dateString); if (isNaN(date.getTime())) return "N/A"; return format(date, formatStr); } catch { return "N/A"; } }; export default function Reports() { const [greeting, setGreeting] = useState(""); const [reviewVendor, setReviewVendor] = useState(null); const [showReviewDialog, setShowReviewDialog] = useState(false); useEffect(() => { const hour = new Date().getHours(); if (hour < 12) { setGreeting("Good Morning"); } else if (hour < 18) { setGreeting("Good Afternoon"); } else { setGreeting("Good Evening"); } }, []); const { data: user } = useQuery({ queryKey: ['current-user-reports'], queryFn: () => base44.auth.me(), }); const { data: rates = [] } = useQuery({ queryKey: ['vendor-rates-analysis'], queryFn: () => base44.entities.VendorRate.list(), initialData: [], }); const { data: events = [] } = useQuery({ queryKey: ['events-analysis'], queryFn: () => base44.entities.Event.list('-date'), initialData: [], }); const { data: staff = [] } = useQuery({ queryKey: ['staff-analysis'], queryFn: () => base44.entities.Staff.list(), initialData: [], }); const userRole = user?.user_role || user?.role || "admin"; // Filter data based on user role const getFilteredEvents = () => { if (userRole === "client") { return events.filter(e => e.client_email === user?.email || e.business_name === user?.company_name || e.created_by === user?.email ); } if (userRole === "vendor") { return events.filter(e => e.vendor_name === user?.company_name); } // For admin, operator, sector (to see own shifts), return all or adjust based on specific needs // For workforce, events might not be directly relevant unless they are assigned to them // For now, return all for admin/operator/sector if no specific event filtering return events; }; const getFilteredStaff = () => { if (userRole === "vendor") { return staff.filter(s => s.vendor_name === user?.company_name); } if (userRole === "workforce") { // Assuming employee_name matches full_name or created_by matches email return staff.filter(s => s.employee_name === user?.full_name || s.created_by === user?.email); } // For admin, client (to see all staff for their events), operator, sector return staff; }; const filteredEvents = getFilteredEvents(); const filteredStaff = getFilteredStaff(); // Analyze vendor performance (used by admin/procurement) const analyzeVendors = () => { const vendorStats = {}; rates.forEach(rate => { if (!vendorStats[rate.vendor_name]) { vendorStats[rate.vendor_name] = { name: rate.vendor_name, totalServices: 0, activeServices: 0, avgMarkup: 0, avgVendorFee: 0, underpriced: 0, overpriced: 0, optimal: 0, complianceIssues: 0, markups: [], fees: [], underpricedServices: [], complianceIssueServices: [] }; } const vendor = vendorStats[rate.vendor_name]; vendor.totalServices++; if (rate.is_active) vendor.activeServices++; vendor.markups.push(rate.markup_percentage); vendor.fees.push(rate.vendor_fee_percentage); // Check minimum wage compliance if (rate.employee_wage < 16.50) { vendor.complianceIssues++; vendor.complianceIssueServices.push({ ...rate, issue: 'Below minimum wage', currentWage: rate.employee_wage, requiredWage: 16.50, difference: (16.50 - rate.employee_wage).toFixed(2) }); } // Price analysis const marketAvg = MARKET_AVERAGES[rate.role_name]; if (marketAvg) { const diff = ((rate.client_rate - marketAvg) / marketAvg) * 100; if (diff < -15) { vendor.underpriced++; vendor.underpricedServices.push({ ...rate, issue: 'Underpriced', marketAverage: marketAvg, currentRate: rate.client_rate, percentageBelow: Math.abs(diff).toFixed(1), potentialRevenue: ((marketAvg - rate.client_rate) * 160).toFixed(0) // Assuming 160 hours/month }); } else if (diff > 20) { vendor.overpriced++; } else if (diff >= -5 && diff <= 10) { vendor.optimal++; } } }); // Calculate averages Object.values(vendorStats).forEach(vendor => { vendor.avgMarkup = vendor.markups.length > 0 ? (vendor.markups.reduce((a, b) => a + b, 0) / vendor.markups.length).toFixed(1) : 0; vendor.avgVendorFee = vendor.fees.length > 0 ? (vendor.fees.reduce((a, b) => a + b, 0) / vendor.fees.length).toFixed(1) : 0; }); return Object.values(vendorStats); }; const vendorAnalysis = analyzeVendors(); const topPerformers = vendorAnalysis .filter(v => v.optimal > 0) .sort((a, b) => b.optimal - a.optimal) .slice(0, 3); const underperformers = vendorAnalysis .filter(v => v.complianceIssues > 0 || v.underpriced > v.optimal) .sort((a, b) => (b.complianceIssues + b.underpriced) - (a.complianceIssues + a.underpriced)) .slice(0, 3); const underchargingVendors = vendorAnalysis .filter(v => v.underpriced > 2) .sort((a, b) => b.underpriced - a.underpriced) .slice(0, 3); const complianceIssues = vendorAnalysis .filter(v => v.complianceIssues > 0) .sort((a, b) => b.complianceIssues - a.complianceIssues); const totalVendors = vendorAnalysis.length; const totalServices = rates.length; const activeServices = rates.filter(r => r.is_active).length; const totalCompliance = rates.filter(r => r.employee_wage >= 16.50).length; const complianceRate = totalServices > 0 ? ((totalCompliance / totalServices) * 100).toFixed(1) : 0; const revenueOpportunities = underchargingVendors.reduce((sum, v) => sum + (v.underpriced * 5000), 0); // Placeholder revenue opportunity const handleReviewVendor = (vendor) => { setReviewVendor(vendor); setShowReviewDialog(true); }; const renderAdminProcurementReports = () => { return ( <> {/* Key Metrics */}
{totalVendors}
Active Vendors
{activeServices}
Active Services
{complianceRate}%
Wage Compliance
${(revenueOpportunities / 1000).toFixed(0)}K
Revenue Opportunity
{topPerformers[0].name} is performing excellently with {topPerformers[0].optimal} optimally-priced services, maintaining competitive market rates while ensuring profitability.
{underperformers[0].name} requires attention {underperformers[0].complianceIssues > 0 ? ( — {underperformers[0].complianceIssues} compliance {underperformers[0].complianceIssues === 1 ? 'issue' : 'issues'} detected and must be addressed immediately. ) : ( for pricing strategy optimization to improve market competitiveness. )}
{complianceIssues.length} {complianceIssues.length === 1 ? 'vendor has' : 'vendors have'} wage compliance issues below the California minimum wage ($16.50/hr) requiring immediate correction.
${(revenueOpportunities / 1000).toFixed(0)}K in potential annual revenue identified through price optimization across {underchargingVendors.length} vendors with {underchargingVendors.reduce((sum, v) => sum + v.underpriced, 0)} underpriced services.
Network health is {complianceRate >= 95 ? "excellent" : "stable"} with {activeServices} active services across {totalVendors} vendors. {complianceRate >= 95 ? "Continue monitoring for sustained performance." : "Recommend focusing on compliance corrections and pricing optimization."}
A+
Performance
${(revenueOpportunities / 1000).toFixed(0)}K
Annual Potential
{vendor.underpriced} services below market rate
Adjusting {underchargingVendors.reduce((sum, v) => sum + v.underpriced, 0)} underpriced services to competitive market rates could generate ${(revenueOpportunities / 1000).toFixed(0)}K in additional annual revenue without impacting client competitiveness.
{vendor.complianceIssues} {vendor.complianceIssues === 1 ? 'service' : 'services'} below minimum wage ($16.50/hr)
These services must be corrected immediately to comply with California minimum wage laws ($16.50/hr). Non-compliance may result in penalties, lawsuits, and reputational damage.
{filteredEvents.length}
Total Orders
${(totalSpend / 1000).toFixed(1)}K
Total Spend
${avgOrderSize.toFixed(0)}
Avg Order Size
{completedOrders}
Completed Orders
{totalOrders}
Total Orders
{fillRate}%
Fill Rate
${(totalRevenue / 1000).toFixed(1)}K
Total Revenue
{avgRating}
Avg Staff Rating
{totalShifts}
Total Shifts
{myRating}
My Rating
{coverage}%
Coverage Rate
{cancellations}
Cancellations
{totalOrders}
Total Orders
{activeOrders}
Active Orders
{completedOrders}
Completed
{totalStaff}
Total Staff
Data as of {safeFormatDate(new Date(), 'MMM dd, yyyy, h:mm a')}