export with one error
This commit is contained in:
@@ -21,9 +21,29 @@ 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
|
||||
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";
|
||||
@@ -92,10 +112,18 @@ export default function ClientOrders() {
|
||||
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'],
|
||||
@@ -135,6 +163,28 @@ export default function ClientOrders() {
|
||||
},
|
||||
});
|
||||
|
||||
// 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;
|
||||
|
||||
@@ -166,8 +216,61 @@ export default function ClientOrders() {
|
||||
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]);
|
||||
}, [clientEvents, searchTerm, statusFilter, dateFilter, specificDate, locationFilter, managerFilter]);
|
||||
|
||||
const activeOrders = clientEvents.filter(e =>
|
||||
e.status !== "Completed" && e.status !== "Canceled"
|
||||
@@ -316,23 +419,261 @@ export default function ClientOrders() {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl p-4 flex items-center gap-4 border shadow-sm">
|
||||
<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" /> {/* Icon size updated */}
|
||||
<Input
|
||||
placeholder="Search orders..." // Placeholder text updated
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-10 border-slate-300 h-10" // Class updated
|
||||
/>
|
||||
<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>
|
||||
<Tabs value={statusFilter} onValueChange={setStatusFilter} className="w-fit"> {/* Replaced Select with Tabs */}
|
||||
<TabsList>
|
||||
<TabsTrigger value="all">All</TabsTrigger>
|
||||
<TabsTrigger value="active">Active</TabsTrigger>
|
||||
<TabsTrigger value="completed">Completed</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
<Card className="border-slate-200 shadow-sm"> {/* Card class updated */}
|
||||
|
||||
Reference in New Issue
Block a user