Files
Krow-workspace/frontend-web/src/pages/ClientOrders.jsx
2025-11-26 13:05:42 -05:00

860 lines
35 KiB
JavaScript

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 (
<div className="inline-flex items-center gap-2 bg-red-500 text-white px-4 py-2 rounded-lg font-semibold text-xs shadow-md">
<Zap className="w-3.5 h-3.5 fill-white" />
RAPID
</div>
);
}
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 (
<div className={`inline-flex items-center gap-2 ${config.bg} text-white px-4 py-2 rounded-lg font-semibold text-xs shadow-md`}>
<Icon className="w-3.5 h-3.5" />
{event.status}
</div>
);
};
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 (
<div className="p-4 md:p-8 bg-slate-50 min-h-screen">
<div className="max-w-[1800px] mx-auto space-y-6">
<div className=""> {/* Removed mb-6 */}
<h1 className="text-2xl font-bold text-slate-900">My Orders</h1>
<p className="text-sm text-slate-500 mt-1">View and manage all your orders</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4"> {/* Removed mb-6 from here as it's now part of space-y-6 */}
<Card className="border border-blue-200 bg-blue-50">
<CardContent className="p-5">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-blue-500 rounded-lg flex items-center justify-center">
<Package className="w-5 h-5 text-white" />
</div>
<div>
<p className="text-xs text-blue-600 font-semibold uppercase">TOTAL</p>
<p className="text-2xl font-bold text-blue-700">{clientEvents.length}</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border border-orange-200 bg-orange-50">
<CardContent className="p-5">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-orange-500 rounded-lg flex items-center justify-center">
<Clock className="w-5 h-5 text-white" />
</div>
<div>
<p className="text-xs text-orange-600 font-semibold uppercase">ACTIVE</p>
<p className="text-2xl font-bold text-orange-700">{activeOrders}</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border border-green-200 bg-green-50">
<CardContent className="p-5">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-green-500 rounded-lg flex items-center justify-center">
<CheckCircle className="w-5 h-5 text-white" />
</div>
<div>
<p className="text-xs text-green-600 font-semibold uppercase">COMPLETED</p>
<p className="text-2xl font-bold text-green-700">{completedOrders}</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border border-purple-200 bg-purple-50">
<CardContent className="p-5">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-purple-500 rounded-lg flex items-center justify-center">
<DollarSign className="w-5 h-5 text-white" />
</div>
<div>
<p className="text-xs text-purple-600 font-semibold uppercase">TOTAL SPENT</p>
<p className="text-2xl font-bold text-purple-700">${Math.round(totalSpent / 1000)}k</p>
</div>
</div>
</CardContent>
</Card>
</div>
<div className="bg-white rounded-xl p-4 border shadow-sm">
<div className="flex items-center gap-4 mb-4">
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-slate-400" />
<Input
placeholder="Search orders..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10 border-slate-300 h-10"
/>
</div>
<Tabs value={statusFilter} onValueChange={setStatusFilter} className="w-fit">
<TabsList>
<TabsTrigger value="all">All</TabsTrigger>
<TabsTrigger value="active">Active</TabsTrigger>
<TabsTrigger value="completed">Completed</TabsTrigger>
</TabsList>
</Tabs>
</div>
<div className="flex items-center gap-3">
<div className="flex items-center gap-2">
<Filter className="w-4 h-4 text-slate-500" />
<span className="text-sm font-medium text-slate-700">Filters:</span>
</div>
<Popover open={calendarOpen} onOpenChange={setCalendarOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
className={`h-9 w-[160px] justify-start text-left font-normal ${specificDate ? 'bg-blue-50 border-blue-300' : ''}`}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{specificDate ? format(specificDate, 'MMM dd, yyyy') : 'Pick a date'}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<div className="p-6">
<CalendarComponent
mode="single"
selected={tempDate || specificDate}
onSelect={(date) => setTempDate(date)}
numberOfMonths={2}
initialFocus
/>
<div className="mt-6 pt-6 border-t border-slate-200">
<div className="flex items-center justify-center gap-3 mb-6">
<Button
variant="ghost"
size="sm"
onClick={() => setTempDate(new Date())}
className={`px-6 h-10 font-medium ${!tempDate && !specificDate ? 'border-b-2 border-blue-600 rounded-none' : ''}`}
>
Today
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => {
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
setTempDate(yesterday);
}}
className="px-6 h-10 font-medium"
>
Yesterday
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => {
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
setTempDate(tomorrow);
}}
className="px-6 h-10 font-medium"
>
Tomorrow
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => {
setTempDate(null);
setDateFilter("week");
}}
className="px-6 h-10 font-medium"
>
This Week
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => {
setTempDate(null);
setDateFilter("month");
}}
className="px-6 h-10 font-medium"
>
This Month
</Button>
</div>
<div className="flex items-center justify-end gap-3">
<Button
variant="outline"
size="sm"
onClick={() => {
setTempDate(null);
setSpecificDate(null);
setDateFilter("all");
setCalendarOpen(false);
}}
className="px-8 h-10 text-red-600 border-red-300 hover:bg-red-50 font-medium"
>
Reset
</Button>
<Button
variant="outline"
size="sm"
onClick={() => {
setTempDate(null);
setCalendarOpen(false);
}}
className="px-8 h-10 font-medium"
>
Cancel
</Button>
<Button
size="sm"
onClick={() => {
if (tempDate) {
setSpecificDate(tempDate);
setDateFilter("all");
}
setTempDate(null);
setCalendarOpen(false);
}}
className="px-10 h-10 bg-blue-600 hover:bg-blue-700 font-medium"
>
Apply
</Button>
</div>
</div>
</div>
</PopoverContent>
</Popover>
<Popover open={locationOpen} onOpenChange={setLocationOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={locationOpen}
className="w-[200px] h-9 justify-between text-sm"
>
<span className="truncate">{locationFilter === "all" ? "All Locations" : locationFilter}</span>
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[200px] p-0">
<Command shouldFilter={true}>
<CommandInput placeholder="Type to search..." className="h-9" />
<CommandEmpty>No location found.</CommandEmpty>
<CommandGroup className="max-h-64 overflow-auto">
<CommandItem
value="all"
onSelect={() => {
setLocationFilter("all");
setLocationOpen(false);
}}
>
<Check className={`mr-2 h-4 w-4 ${locationFilter === "all" ? "opacity-100" : "opacity-0"}`} />
All Locations
</CommandItem>
{uniqueLocations.map((location) => (
<CommandItem
key={location}
value={location}
onSelect={(currentValue) => {
setLocationFilter(currentValue);
setLocationOpen(false);
}}
>
<Check className={`mr-2 h-4 w-4 ${locationFilter === location ? "opacity-100" : "opacity-0"}`} />
{location}
</CommandItem>
))}
</CommandGroup>
</Command>
</PopoverContent>
</Popover>
<Popover open={managerOpen} onOpenChange={setManagerOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={managerOpen}
className="w-[200px] h-9 justify-between text-sm"
>
<span className="truncate">{managerFilter === "all" ? "All Managers" : managerFilter}</span>
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[200px] p-0">
<Command shouldFilter={true}>
<CommandInput placeholder="Type to search..." className="h-9" />
<CommandEmpty>No manager found.</CommandEmpty>
<CommandGroup className="max-h-64 overflow-auto">
<CommandItem
value="all"
onSelect={() => {
setManagerFilter("all");
setManagerOpen(false);
}}
>
<Check className={`mr-2 h-4 w-4 ${managerFilter === "all" ? "opacity-100" : "opacity-0"}`} />
All Managers
</CommandItem>
{uniqueManagers.map((manager) => (
<CommandItem
key={manager}
value={manager}
onSelect={(currentValue) => {
setManagerFilter(currentValue);
setManagerOpen(false);
}}
>
<Check className={`mr-2 h-4 w-4 ${managerFilter === manager ? "opacity-100" : "opacity-0"}`} />
{manager}
</CommandItem>
))}
</CommandGroup>
</Command>
</PopoverContent>
</Popover>
{(dateFilter !== "all" || specificDate || locationFilter !== "all" || managerFilter !== "all") && (
<Button
variant="ghost"
size="sm"
onClick={() => {
setDateFilter("all");
setSpecificDate(null);
setLocationFilter("all");
setManagerFilter("all");
}}
className="text-slate-600 hover:text-slate-900"
>
Clear Filters
</Button>
)}
</div>
</div>
<Card className="border-slate-200 shadow-sm"> {/* Card class updated */}
<CardContent className="p-0"> {/* CardContent padding updated */}
<Table>
<TableHeader>
<TableRow className="bg-slate-50 hover:bg-slate-50">
<TableHead className="font-semibold text-slate-700 text-xs uppercase">Business</TableHead>
<TableHead className="font-semibold text-slate-700 text-xs uppercase">Hub</TableHead>
<TableHead className="font-semibold text-slate-700 text-xs uppercase">Event</TableHead>
<TableHead className="font-semibold text-slate-700 text-xs uppercase">Date & Time</TableHead>
<TableHead className="font-semibold text-slate-700 text-xs uppercase">Status</TableHead>
<TableHead className="font-semibold text-slate-700 text-xs uppercase">Requested</TableHead>
<TableHead className="font-semibold text-slate-700 text-xs uppercase">Assigned</TableHead>
<TableHead className="font-semibold text-slate-700 text-xs uppercase">Invoice</TableHead>
<TableHead className="font-semibold text-slate-700 text-xs uppercase text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{filteredOrders.length === 0 ? (
<TableRow>
<TableCell colSpan={9} className="text-center py-12 text-slate-500">
<Package className="w-12 h-12 mx-auto mb-3 text-slate-300" />
<p className="font-medium">No orders found</p>
</TableCell>
</TableRow>
) : (
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 (
<TableRow key={order.id} className="hover:bg-slate-50">
<TableCell>
<div className="flex items-center gap-2">
<Building2 className="w-4 h-4 text-blue-600" />
<span className="text-sm font-medium text-slate-900">{order.business_name || "Primary Location"}</span>
</div>
</TableCell>
<TableCell>
<div className="flex items-center gap-2">
<MapPin className="w-4 h-4 text-purple-600" />
<span className="text-sm text-slate-700">{order.hub || "Main Hub"}</span>
</div>
</TableCell>
<TableCell>
<p className="font-semibold text-slate-900">{order.event_name || "Untitled Event"}</p>
</TableCell>
<TableCell>
<div>
<p className="font-medium text-slate-900">{safeFormatDate(order.date, 'MM.dd.yyyy')}</p>
<p className="text-xs text-slate-500 flex items-center gap-1">
<Clock className="w-3 h-3" />
{startTime} - {endTime}
</p>
</div>
</TableCell>
<TableCell>
{getStatusBadge(order)}
</TableCell>
<TableCell>
<span className="text-lg font-bold text-slate-900">{requestedCount}</span>
</TableCell>
<TableCell>
<div className="flex flex-col items-center gap-1">
<div className="w-10 h-10 bg-emerald-500 rounded-full flex items-center justify-center">
<span className="text-white font-bold text-sm">{assignedCount}</span>
</div>
<span className="text-xs text-emerald-600 font-semibold">{assignmentProgress}%</span>
</div>
</TableCell>
<TableCell>
<button className="w-8 h-8 flex items-center justify-center hover:bg-slate-100 rounded transition-colors">
<FileText className="w-5 h-5 text-slate-400" />
</button>
</TableCell>
<TableCell>
<div className="flex items-center justify-end gap-2">
<Button
variant="ghost"
size="icon"
onClick={() => handleViewOrder(order)}
className="h-8 w-8 p-0"
title="View"
>
<Eye className="w-4 h-4" />
</Button>
<Button
variant="ghost"
size="icon"
className="h-8 w-8 p-0"
title="Notifications"
>
<Bell className="w-4 h-4" />
</Button>
{canEditOrder(order) && (
<Button
variant="ghost"
size="icon"
onClick={() => navigate(createPageUrl(`EditEvent?id=${order.id}`))}
className="h-8 w-8 p-0"
title="Edit"
>
<Edit3 className="w-4 h-4" />
</Button>
)}
{canCancelOrder(order) && (
<Button
variant="ghost"
size="icon"
onClick={() => handleCancelOrder(order)}
className="h-8 w-8 p-0 text-red-600 hover:text-red-700 hover:bg-red-50"
title="Cancel"
>
<X className="w-4 h-4" />
</Button>
)}
</div>
</TableCell>
</TableRow>
);
})
)}
</TableBody>
</Table>
</CardContent>
</Card>
</div>
<OrderDetailModal
open={viewOrderModal}
onClose={() => setViewOrderModal(false)}
order={selectedOrder}
onCancel={handleCancelOrder}
/>
<Dialog open={cancelDialogOpen} onOpenChange={setCancelDialogOpen}> {/* Updated open and onOpenChange */}
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-2 text-red-600">
<AlertTriangle className="w-5 h-5" />
Cancel Order?
</DialogTitle>
<DialogDescription>
Are you sure you want to cancel this order? This action cannot be undone.
</DialogDescription>
</DialogHeader>
{orderToCancel && ( // Using orderToCancel
<div className="bg-slate-50 rounded-lg p-4 space-y-2">
<p className="font-bold text-slate-900">{orderToCancel.event_name}</p>
<div className="flex items-center gap-2 text-sm text-slate-600">
<Calendar className="w-4 h-4" />
{orderToCancel.date ? format(new Date(orderToCancel.date), "MMMM d, yyyy") : "—"}
</div>
<div className="flex items-center gap-2 text-sm text-slate-600">
<MapPin className="w-4 h-4" />
{orderToCancel.hub || orderToCancel.event_location}
</div>
</div>
)}
<DialogFooter>
<Button
variant="outline"
onClick={() => setCancelDialogOpen(false)} // Updated
>
Keep Order
</Button>
<Button
variant="destructive"
onClick={confirmCancel}
disabled={cancelOrderMutation.isPending}
>
{cancelOrderMutation.isPending ? "Canceling..." : "Yes, Cancel Order"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
);
}