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, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import {
Tabs, // New import
TabsList, // New import
TabsTrigger, // New import
} from "@/components/ui/tabs"; // New import
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Calendar as CalendarComponent } from "@/components/ui/calendar";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
} from "@/components/ui/command";
import {
Search, Calendar, MapPin, Users, Eye, Edit, X, Trash2, FileText, // Edit instead of Edit2
Clock, DollarSign, Package, CheckCircle, AlertTriangle, Grid, List, Zap, Plus, Building2, Bell, Edit3, Filter, CalendarIcon, Check, ChevronsUpDown
} from "lucide-react";
import { useToast } from "@/components/ui/use-toast";
import { format, parseISO, isValid } from "date-fns";
import OrderDetailModal from "@/components/orders/OrderDetailModal";
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; }
};
const safeFormatDate = (dateString, formatString) => {
const date = safeParseDate(dateString);
return date ? format(date, formatString) : '—';
};
const convertTo12Hour = (time24) => {
if (!time24) return "-";
try {
const [hours, minutes] = time24.split(':');
const hour = parseInt(hours);
const ampm = hour >= 12 ? 'PM' : 'AM';
const hour12 = hour % 12 || 12;
return `${hour12}:${minutes} ${ampm}`;
} catch {
return time24;
}
};
const getStatusBadge = (event) => {
if (event.is_rapid) {
return (
RAPID
);
}
const statusConfig = {
'Draft': { bg: 'bg-slate-500', icon: FileText },
'Pending': { bg: 'bg-amber-500', icon: Clock },
'Partial Staffed': { bg: 'bg-orange-500', icon: AlertTriangle },
'Fully Staffed': { bg: 'bg-emerald-500', icon: CheckCircle },
'Active': { bg: 'bg-blue-500', icon: Users },
'Completed': { bg: 'bg-slate-400', icon: CheckCircle },
'Canceled': { bg: 'bg-red-500', icon: X },
};
const config = statusConfig[event.status] || { bg: 'bg-slate-400', icon: Clock };
const Icon = config.icon;
return (
{event.status}
);
};
export default function ClientOrders() {
const navigate = useNavigate();
const queryClient = useQueryClient();
const { toast } = useToast();
const [searchTerm, setSearchTerm] = useState("");
const [statusFilter, setStatusFilter] = useState("all"); // Updated values for Tabs
const [dateFilter, setDateFilter] = useState("all");
const [specificDate, setSpecificDate] = useState(null);
const [tempDate, setTempDate] = useState(null);
const [locationFilter, setLocationFilter] = useState("all");
const [managerFilter, setManagerFilter] = useState("all");
const [locationOpen, setLocationOpen] = useState(false);
const [managerOpen, setManagerOpen] = useState(false);
const [cancelDialogOpen, setCancelDialogOpen] = useState(false); // Changed from cancelDialog.open
const [orderToCancel, setOrderToCancel] = useState(null); // Changed from cancelDialog.order
const [viewOrderModal, setViewOrderModal] = useState(false);
const [selectedOrder, setSelectedOrder] = useState(null);
const [calendarOpen, setCalendarOpen] = useState(false);
const { data: user } = useQuery({
queryKey: ['current-user-client-orders'],
queryFn: () => base44.auth.me(),
});
const { data: allEvents = [] } = useQuery({
queryKey: ['all-events-client'],
queryFn: () => base44.entities.Event.list('-date'),
});
const clientEvents = useMemo(() => {
return allEvents.filter(e =>
e.client_email === user?.email ||
e.business_name === user?.company_name ||
e.created_by === user?.email
);
}, [allEvents, user]);
const cancelOrderMutation = useMutation({
mutationFn: (orderId) => base44.entities.Event.update(orderId, { status: "Canceled" }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['all-events-client'] });
toast({
title: "✅ Order Canceled",
description: "Your order has been canceled successfully",
});
setCancelDialogOpen(false); // Updated
setOrderToCancel(null); // Updated
},
onError: () => {
toast({
title: "❌ Failed to Cancel",
description: "Could not cancel order. Please try again.",
variant: "destructive",
});
},
});
// Get unique locations and managers for filters
const uniqueLocations = useMemo(() => {
const locations = new Set();
clientEvents.forEach(e => {
if (e.hub) locations.add(e.hub);
if (e.event_location) locations.add(e.event_location);
});
return Array.from(locations).sort();
}, [clientEvents]);
const uniqueManagers = useMemo(() => {
const managers = new Set();
clientEvents.forEach(e => {
if (e.manager_name) managers.add(e.manager_name);
// Also check in shifts for manager names
e.shifts?.forEach(shift => {
if (shift.manager_name) managers.add(shift.manager_name);
});
});
return Array.from(managers).sort();
}, [clientEvents]);
const filteredOrders = useMemo(() => { // Renamed from filteredEvents
let filtered = clientEvents;
if (searchTerm) {
const lower = searchTerm.toLowerCase();
filtered = filtered.filter(e =>
e.event_name?.toLowerCase().includes(lower) ||
e.business_name?.toLowerCase().includes(lower) ||
e.hub?.toLowerCase().includes(lower) ||
e.event_location?.toLowerCase().includes(lower) // Added event_location to search
);
}
const now = new Date();
// Reset time for comparison to only compare dates
now.setHours(0, 0, 0, 0);
filtered = filtered.filter(e => {
const eventDate = safeParseDate(e.date);
const isCompleted = e.status === "Completed";
const isCanceled = e.status === "Canceled";
const isFutureOrPresent = eventDate && eventDate >= now;
if (statusFilter === "active") {
return !isCompleted && !isCanceled && isFutureOrPresent;
} else if (statusFilter === "completed") {
return isCompleted;
}
return true; // For "all" or other statuses
});
// Specific date filter (from calendar)
if (specificDate) {
filtered = filtered.filter(e => {
const eventDate = safeParseDate(e.date);
if (!eventDate) return false;
const selectedDateNormalized = new Date(specificDate);
selectedDateNormalized.setHours(0, 0, 0, 0);
eventDate.setHours(0, 0, 0, 0);
return eventDate.getTime() === selectedDateNormalized.getTime();
});
}
// Date range filter
else if (dateFilter !== "all") {
filtered = filtered.filter(e => {
const eventDate = safeParseDate(e.date);
if (!eventDate) return false;
const now = new Date();
now.setHours(0, 0, 0, 0);
if (dateFilter === "today") {
return eventDate.toDateString() === now.toDateString();
} else if (dateFilter === "week") {
const weekFromNow = new Date(now);
weekFromNow.setDate(now.getDate() + 7);
return eventDate >= now && eventDate <= weekFromNow;
} else if (dateFilter === "month") {
const monthFromNow = new Date(now);
monthFromNow.setMonth(now.getMonth() + 1);
return eventDate >= now && eventDate <= monthFromNow;
} else if (dateFilter === "past") {
return eventDate < now;
}
return true;
});
}
// Location filter
if (locationFilter !== "all") {
filtered = filtered.filter(e =>
e.hub === locationFilter || e.event_location === locationFilter
);
}
// Manager filter
if (managerFilter !== "all") {
filtered = filtered.filter(e => {
if (e.manager_name === managerFilter) return true;
// Check shifts for manager
return e.shifts?.some(shift => shift.manager_name === managerFilter);
});
}
return filtered;
}, [clientEvents, searchTerm, statusFilter, dateFilter, specificDate, locationFilter, managerFilter]);
const activeOrders = clientEvents.filter(e =>
e.status !== "Completed" && e.status !== "Canceled"
).length;
const completedOrders = clientEvents.filter(e => e.status === "Completed").length;
const totalSpent = clientEvents
.filter(e => e.status === "Completed")
.reduce((sum, e) => sum + (e.total || 0), 0);
const handleCancelOrder = (order) => {
setOrderToCancel(order); // Updated
setCancelDialogOpen(true); // Updated
};
const handleViewOrder = (order) => {
setSelectedOrder(order);
setViewOrderModal(true);
};
const confirmCancel = () => {
if (orderToCancel) { // Updated
cancelOrderMutation.mutate(orderToCancel.id); // Updated
}
};
const canEditOrder = (order) => {
const eventDate = safeParseDate(order.date);
const now = new Date();
return order.status !== "Completed" &&
order.status !== "Canceled" &&
eventDate && eventDate > now; // Ensure eventDate is valid before comparison
};
const canCancelOrder = (order) => {
return order.status !== "Completed" && order.status !== "Canceled";
};
const getAssignmentStatus = (event) => {
const totalRequested = event.shifts?.reduce((accShift, shift) => {
return accShift + (shift.roles?.reduce((accRole, role) => accRole + (role.count || 0), 0) || 0);
}, 0) || 0;
const assigned = event.assigned_staff?.length || 0;
const percentage = totalRequested > 0 ? Math.round((assigned / totalRequested) * 100) : 0;
let badgeClass = 'bg-slate-100 text-slate-600'; // Default: no staff, or no roles requested
if (assigned > 0 && assigned < totalRequested) {
badgeClass = 'bg-orange-500 text-white'; // Partial Staffed
} else if (assigned >= totalRequested && totalRequested > 0) {
badgeClass = 'bg-emerald-500 text-white'; // Fully Staffed
} else if (assigned === 0 && totalRequested > 0) {
badgeClass = 'bg-red-500 text-white'; // Requested but 0 assigned
} else if (assigned > 0 && totalRequested === 0) {
badgeClass = 'bg-blue-500 text-white'; // Staff assigned but no roles explicitly requested (e.g., event set up, staff assigned, but roles not detailed or count is 0)
}
return {
badgeClass,
assigned,
requested: totalRequested,
percentage,
};
};
const getEventTimes = (event) => {
const firstShift = event.shifts?.[0];
const rolesInFirstShift = firstShift?.roles || [];
let startTime = null;
let endTime = null;
if (rolesInFirstShift.length > 0) {
startTime = rolesInFirstShift[0].start_time || null;
endTime = rolesInFirstShift[0].end_time || null;
}
return {
startTime: startTime ? convertTo12Hour(startTime) : "-",
endTime: endTime ? convertTo12Hour(endTime) : "-"
};
};
return (
{/* Removed mb-6 */}
My Orders
View and manage all your orders
{/* Removed mb-6 from here as it's now part of space-y-6 */}
TOTAL
{clientEvents.length}
COMPLETED
{completedOrders}
TOTAL SPENT
${Math.round(totalSpent / 1000)}k
Filters:
setTempDate(date)}
numberOfMonths={2}
initialFocus
/>
No location found.
{
setLocationFilter("all");
setLocationOpen(false);
}}
>
All Locations
{uniqueLocations.map((location) => (
{
setLocationFilter(currentValue);
setLocationOpen(false);
}}
>
{location}
))}
No manager found.
{
setManagerFilter("all");
setManagerOpen(false);
}}
>
All Managers
{uniqueManagers.map((manager) => (
{
setManagerFilter(currentValue);
setManagerOpen(false);
}}
>
{manager}
))}
{(dateFilter !== "all" || specificDate || locationFilter !== "all" || managerFilter !== "all") && (
)}
{/* Card class updated */}
{/* CardContent padding updated */}
Business
Hub
Event
Date & Time
Status
Requested
Assigned
Invoice
Actions
{filteredOrders.length === 0 ? (
No orders found
) : (
filteredOrders.map((order) => {
const assignedCount = order.assigned_staff?.length || 0;
const requestedCount = order.requested || 0;
const assignmentProgress = requestedCount > 0 ? Math.round((assignedCount / requestedCount) * 100) : 0;
const { startTime, endTime } = getEventTimes(order);
return (
{order.business_name || "Primary Location"}
{order.hub || "Main Hub"}
{order.event_name || "Untitled Event"}
{safeFormatDate(order.date, 'MM.dd.yyyy')}
{startTime} - {endTime}
{getStatusBadge(order)}
{requestedCount}
{assignedCount}
{assignmentProgress}%
{canEditOrder(order) && (
)}
{canCancelOrder(order) && (
)}
);
})
)}
setViewOrderModal(false)}
order={selectedOrder}
onCancel={handleCancelOrder}
/>
);
}