import React, { useState } from "react"; import { base44 } from "@/api/base44Client"; import { useQuery } from "@tanstack/react-query"; import { Link, useNavigate } from "react-router-dom"; import { createPageUrl } from "@/utils"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Plus, Search, Calendar as CalendarIcon, Eye, Edit, Copy, X, RefreshCw } from "lucide-react"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import StatusCard from "../components/events/StatusCard"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Badge } from "@/components/ui/badge"; import { format, isSameDay, parseISO, isWithinInterval, startOfDay, endOfDay, isValid } from "date-fns"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import EventHoverCard from "../components/events/EventHoverCard"; import QuickAssignPopover from "../components/events/QuickAssignPopover"; import { Calendar } from "@/components/ui/calendar"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { useToast } from "@/components/ui/use-toast"; import PageHeader from "../components/common/PageHeader"; const statusColors = { Draft: "bg-gray-100 text-gray-800", Active: "bg-green-100 text-green-800", Pending: "bg-purple-100 text-purple-800", Confirmed: "bg-blue-100 text-blue-800", Assigned: "bg-yellow-100 text-yellow-800", Completed: "bg-slate-100 text-slate-800", Canceled: "bg-red-100 text-red-800", Cancelled: "bg-red-100 text-red-800" }; // Date range presets const datePresets = [ { label: "Today", getValue: () => { const today = new Date(); return { from: startOfDay(today), to: endOfDay(today) }; } }, { label: "This week", getValue: () => { const today = new Date(); const start = startOfDay(new Date(today.setDate(today.getDate() - today.getDay()))); const end = endOfDay(new Date(today.setDate(today.getDate() - today.getDay() + 6))); return { from: start, to: end }; } }, { label: "This month", getValue: () => { const today = new Date(); const start = startOfDay(new Date(today.getFullYear(), today.getMonth(), 1)); const end = endOfDay(new Date(today.getFullYear(), today.getMonth() + 1, 0)); return { from: start, to: end }; } }, { label: "This quarter", getValue: () => { const today = new Date(); const quarter = Math.floor(today.getMonth() / 3); const start = startOfDay(new Date(today.getFullYear(), quarter * 3, 1)); const end = endOfDay(new Date(today.getFullYear(), quarter * 3 + 3, 0)); return { from: start, to: end }; } }, { label: "This year", getValue: () => { const today = new Date(); const start = startOfDay(new Date(today.getFullYear(), 0, 1)); const end = endOfDay(new Date(today.getFullYear(), 11, 31)); return { from: start, to: end }; } }, { label: "Last week", getValue: () => { const today = new Date(); const lastWeekStart = new Date(today); lastWeekStart.setDate(today.getDate() - today.getDay() - 7); const lastWeekEnd = new Date(today); lastWeekEnd.setDate(today.getDate() - today.getDay() - 1); return { from: startOfDay(lastWeekStart), to: endOfDay(lastWeekEnd) }; } }, { label: "Last month", getValue: () => { const today = new Date(); const start = startOfDay(new Date(today.getFullYear(), today.getMonth() - 1, 1)); const end = endOfDay(new Date(today.getFullYear(), today.getMonth(), 0)); return { from: start, to: end }; } }, { label: "Last quarter", getValue: () => { const today = new Date(); const quarter = Math.floor(today.getMonth() / 3); const prevQuarterMonth = quarter * 3 - 3; const year = prevQuarterMonth < 0 ? today.getFullYear() - 1 : today.getFullYear(); const adjustedMonth = prevQuarterMonth < 0 ? prevQuarterMonth + 12 : prevQuarterMonth; const start = startOfDay(new Date(year, adjustedMonth, 1)); const end = endOfDay(new Date(year, adjustedMonth + 3, 0)); return { from: start, to: end }; } }, { label: "Last year", getValue: () => { const today = new Date(); const start = startOfDay(new Date(today.getFullYear() - 1, 0, 1)); const end = endOfDay(new Date(today.getFullYear() - 1, 11, 31)); return { from: start, to: end }; } }, { label: "Last 30 days", getValue: () => { const today = new Date(); const start = startOfDay(new Date(today.setDate(today.getDate() - 30))); const end = endOfDay(new Date()); return { from: start, to: end }; } }, { label: "Last 90 days", getValue: () => { const today = new Date(); const start = startOfDay(new Date(today.setDate(today.getDate() - 90))); const end = endOfDay(new Date()); return { from: start, to: end }; } }, { label: "Last 365 days", getValue: () => { const today = new Date(); const start = startOfDay(new Date(today.setDate(today.getDate() - 365))); const end = endOfDay(new Date()); return { from: start, to: end }; } }, { label: "All time", getValue: () => null }, ]; // Helper function to safely parse dates const safeParseDate = (dateString) => { if (!dateString) return null; try { const date = typeof dateString === 'string' ? parseISO(dateString) : new Date(dateString); return isValid(date) ? date : null; } catch { return null; } }; // Helper function to safely format dates const safeFormatDate = (dateString, formatStr) => { const date = safeParseDate(dateString); if (!date) return "-"; try { return format(date, formatStr); } catch { return "-"; } }; // Safely format date range for presets const safeFormatDateRange = (date, formatStr) => { if (!date) return ""; try { const validDate = date instanceof Date ? date : new Date(date); if (!isValid(validDate)) return ""; return format(validDate, formatStr); } catch { return ""; } }; export default function Events() { const navigate = useNavigate(); const [activeTab, setActiveTab] = useState("all"); const [searchTerm, setSearchTerm] = useState(""); const [selectedDates, setSelectedDates] = useState([]); const [dateRange, setDateRange] = useState(null); const [selectionMode, setSelectionMode] = useState("multiple"); const [calendarOpen, setCalendarOpen] = useState(false); const [showAlert, setShowAlert] = useState(true); const { toast } = useToast(); const [selectedPreset, setSelectedPreset] = useState(null); const [compareMode, setCompareMode] = useState(false); const { data: events, isLoading } = useQuery({ queryKey: ['events'], queryFn: () => base44.entities.Event.list('-date'), initialData: [], }); const getStatusCounts = () => { const total = events.length; const active = events.filter(e => e.status === "Active").length; const pending = events.filter(e => e.status === "Pending" || e.status === "Assigned").length; const confirmed = events.filter(e => e.status === "Confirmed").length; const completed = events.filter(e => e.status === "Completed").length; return { active: { count: active, percentage: total ? Math.round((active / total) * 100) : 0 }, pending: { count: pending, percentage: total ? Math.round((pending / total) * 100) : 0 }, confirmed: { count: confirmed, percentage: total ? Math.round((confirmed / total) * 100) : 0 }, completed: { count: completed, percentage: total ? Math.round((completed / total) * 100) : 0 }, }; }; const getFilteredEvents = () => { let filtered = events; if (selectionMode === "range" && dateRange?.from) { filtered = filtered.filter(e => { const eventDate = safeParseDate(e.date); if (!eventDate) return false; if (dateRange.to) { try { return isWithinInterval(eventDate, { start: startOfDay(dateRange.from), end: endOfDay(dateRange.to) }); } catch { return false; } } else { try { return isSameDay(eventDate, dateRange.from); } catch { return false; } } }); } else if (selectionMode === "multiple" && selectedDates.length > 0) { filtered = filtered.filter(e => { const eventDate = safeParseDate(e.date); if (!eventDate) return false; return selectedDates.some(selectedDate => { try { return isSameDay(eventDate, selectedDate); } catch { return false; } }); }); } if (activeTab === "last_minute") { filtered = filtered.filter(e => e.event_type === "Last Minute Request"); } else if (activeTab === "upcoming") { filtered = filtered.filter(e => { const eventDate = safeParseDate(e.date); return eventDate && eventDate > new Date(); }); } else if (activeTab === "active") { filtered = filtered.filter(e => e.status === "Active"); } else if (activeTab === "canceled") { filtered = filtered.filter(e => e.status === "Canceled" || e.status === "Cancelled"); } else if (activeTab === "past") { filtered = filtered.filter(e => e.status === "Completed"); } if (searchTerm) { filtered = filtered.filter(e => e.event_name?.toLowerCase().includes(searchTerm.toLowerCase()) || e.hub?.toLowerCase().includes(searchTerm.toLowerCase()) || e.business_name?.toLowerCase().includes(searchTerm.toLowerCase()) || e.id?.toLowerCase().includes(searchTerm.toLowerCase()) ); } return filtered; }; const statusCounts = getStatusCounts(); const filteredEvents = getFilteredEvents(); const getTabCount = (tab) => { if (tab === "all") return events.length; if (tab === "last_minute") return events.filter(e => e.event_type === "Last Minute Request").length; if (tab === "upcoming") { return events.filter(e => { const eventDate = safeParseDate(e.date); return eventDate && eventDate > new Date(); }).length; } if (tab === "active") return events.filter(e => e.status === "Active").length; if (tab === "canceled") return events.filter(e => e.status === "Canceled" || e.status === "Cancelled").length; if (tab === "past") return events.filter(e => e.status === "Completed").length; return 0; }; const handleDateSelect = (date) => { if (selectionMode === "multiple") { setSelectedDates(prev => { const exists = prev.some(d => { try { return isSameDay(d, date); } catch { return false; } }); if (exists) { return prev.filter(d => { try { return !isSameDay(d, date); } catch { return true; } }); } else { return [...prev, date]; } }); } }; const handleRangeSelect = (range) => { setDateRange(range); }; const clearDates = () => { setSelectedDates([]); setDateRange(null); setSelectedPreset(null); setShowAlert(true); }; const getDateSelectionText = () => { try { if (selectedPreset) { if (selectedPreset === "All time") return "All time"; const preset = datePresets.find(p => p.label === selectedPreset); if (preset) { const range = preset.getValue(); if (range?.from && range?.to) { return `${safeFormatDateRange(range.from, 'MMM d')} - ${safeFormatDateRange(range.to, 'MMM d, yyyy')}`; } else if (range?.from) { return safeFormatDateRange(range.from, 'MMM d, yyyy'); } } } if (selectionMode === "range" && dateRange?.from) { if (dateRange.to) { return `${safeFormatDateRange(dateRange.from, 'MMM d')} - ${safeFormatDateRange(dateRange.to, 'MMM d, yyyy')}`; } return safeFormatDateRange(dateRange.from, 'MMM d, yyyy'); } else if (selectionMode === "multiple" && selectedDates.length > 0) { if (selectedDates.length === 1) { return safeFormatDateRange(selectedDates[0], 'MMM d, yyyy'); } return `${selectedDates.length} dates selected`; } } catch { return "Select dates"; } return "Select dates"; }; const getEventCountForDate = (date) => { return events.filter(e => { const eventDate = safeParseDate(e.date); if (!eventDate) return false; try { return isSameDay(eventDate, date); } catch { return false; } }).length; }; const handlePresetSelect = (preset) => { setSelectedPreset(preset.label); setCalendarOpen(false); if (preset.label === "All time") { setDateRange(null); setSelectedDates([]); setSelectionMode("range"); } else { const range = preset.getValue(); setDateRange(range); setSelectedDates([]); setSelectionMode("range"); } setShowAlert(true); }; const getPresetDateRange = (preset) => { if (preset.label === "All time") return "All dates"; const range = preset.getValue(); if (!range?.from) return ""; try { if (range.to) { return `${safeFormatDateRange(range.from, 'd MMM')} - ${safeFormatDateRange(range.to, 'd MMM, yyyy')}`; } return safeFormatDateRange(range.from, 'd MMM, yyyy'); } catch { return ""; } }; React.useEffect(() => { if (showAlert && (filteredEvents.length > 0 && (selectedDates.length > 0 || dateRange?.from || selectedPreset === "All time"))) { const timer = setTimeout(() => { setShowAlert(false); }, 5000); return () => clearTimeout(timer); } }, [showAlert, filteredEvents.length, selectedDates.length, dateRange, selectedPreset]); const handleReorder = (event) => { const reorderData = { event_name: event.event_name, business_id: event.business_id, business_name: event.business_name, hub: event.hub, event_location: event.event_location, event_type: event.event_type, requested: event.requested, client_name: event.client_name, client_email: event.client_email, client_phone: event.client_phone, client_address: event.client_address, notes: event.notes, }; sessionStorage.setItem('reorderData', JSON.stringify(reorderData)); toast({ title: "Reordering Event", description: `Creating new order based on "${event.event_name}"`, }); navigate(createPageUrl("CreateEvent") + "?reorder=true"); }; return (
} /> {/* Enhanced Date Selection Section */}

Select Event Dates

{/* Date Presets Sidebar */}

DATE RANGE

{datePresets.map((preset) => ( ))}
{/* Compare Toggle */}
{/* Calendar Section */}

{selectionMode === "range" ? "Select Date Range" : "Select Multiple Dates"}

{selectionMode === "range" ? "Click start date, then end date" : "Click dates to select/deselect"}

getEventCountForDate(date) > 0 }} modifiersClassNames={{ hasEvents: 'has-events' }} className="rounded-md border-0" />

Dates with dot indicators have events

{(selectedDates.length > 0 || dateRange?.from || selectedPreset === "All time") && showAlert && filteredEvents.length > 0 && ( {filteredEvents.length} event{filteredEvents.length !== 1 ? 's' : ''} found for selected date{selectionMode === "multiple" && selectedDates.length > 1 ? 's' : ''} )}
Total Events {getTabCount("all")} Last Minute {getTabCount("last_minute")} Upcoming {getTabCount("upcoming")} Active {getTabCount("active")} Canceled {getTabCount("canceled")} Past {getTabCount("past")}
setSearchTerm(e.target.value)} className="pl-10 border-slate-300" />
M
ID Company Name Hub Status Event Date Event Name PO Requested Assigned Actions {filteredEvents.length === 0 ? (

No events found

Try selecting different dates or adjusting your filters

) : ( filteredEvents.map((event) => { const assignedCount = event.assigned_staff?.length || 0; const confirmedCount = event.assigned_staff?.filter(s => s.confirmed).length || 0; return ( {event.id?.slice(-4).toUpperCase()} {event.business_name || "Company Name"} {event.hub || "-"} {event.status} {event.status === "Assigned" && confirmedCount > 0 && ( {confirmedCount}/{assignedCount} ✓ )} {safeFormatDate(event.date, "MM/dd/yyyy")} {event.event_name} {event.po || event.po_number || "-"} {event.requested || 0}
); }) )}
); }