new version frontend-webpage
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
|
||||
import React, { useState, useMemo } from "react";
|
||||
import { base44 } from "@/api/base44Client";
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
@@ -24,10 +23,11 @@ import {
|
||||
} from "@/components/ui/tabs"; // New import
|
||||
import {
|
||||
Search, Calendar, MapPin, Users, Eye, Edit, X, Trash2, FileText, // Edit instead of Edit2
|
||||
Clock, DollarSign, Package, CheckCircle, AlertTriangle, Grid, List, Zap, Plus
|
||||
Clock, DollarSign, Package, CheckCircle, AlertTriangle, Grid, List, Zap, Plus, Building2, Bell, Edit3
|
||||
} 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;
|
||||
@@ -94,6 +94,8 @@ export default function ClientOrders() {
|
||||
const [statusFilter, setStatusFilter] = useState("all"); // Updated values for Tabs
|
||||
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 { data: user } = useQuery({
|
||||
queryKey: ['current-user-client-orders'],
|
||||
@@ -180,6 +182,11 @@ export default function ClientOrders() {
|
||||
setCancelDialogOpen(true); // Updated
|
||||
};
|
||||
|
||||
const handleViewOrder = (order) => {
|
||||
setSelectedOrder(order);
|
||||
setViewOrderModal(true);
|
||||
};
|
||||
|
||||
const confirmCancel = () => {
|
||||
if (orderToCancel) { // Updated
|
||||
cancelOrderMutation.mutate(orderToCancel.id); // Updated
|
||||
@@ -332,118 +339,115 @@ export default function ClientOrders() {
|
||||
<CardContent className="p-0"> {/* CardContent padding updated */}
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="bg-slate-50 hover:bg-slate-50"> {/* TableRow class updated */}
|
||||
<TableHead className="font-semibold text-slate-700">Order</TableHead> {/* Updated */}
|
||||
<TableHead className="font-semibold text-slate-700">Date</TableHead> {/* Updated */}
|
||||
<TableHead className="font-semibold text-slate-700">Location</TableHead> {/* Updated */}
|
||||
<TableHead className="font-semibold text-slate-700">Time</TableHead> {/* Updated */}
|
||||
<TableHead className="font-semibold text-slate-700">Status</TableHead> {/* Updated */}
|
||||
<TableHead className="font-semibold text-slate-700 text-center">Staff</TableHead> {/* Updated */}
|
||||
<TableHead className="font-semibold text-slate-700 text-center">Invoice</TableHead> {/* Updated */}
|
||||
<TableHead className="font-semibold text-slate-700 text-center">Actions</TableHead> {/* Updated */}
|
||||
<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 ? ( // Using filteredOrders
|
||||
{filteredOrders.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={8} className="text-center py-12 text-slate-500"> {/* Colspan updated */}
|
||||
<Package className="w-12 h-12 mx-auto mb-3 text-slate-300" /> {/* Icon updated */}
|
||||
<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) => { // Using filteredOrders, renamed event to order
|
||||
const assignment = getAssignmentStatus(order);
|
||||
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);
|
||||
const invoiceReady = order.status === "Completed";
|
||||
// const eventDate = safeParseDate(order.date); // Not directly used here, safeFormatDate handles it.
|
||||
|
||||
return (
|
||||
<TableRow key={order.id} className="hover:bg-slate-50">
|
||||
<TableCell> {/* Order cell */}
|
||||
<div>
|
||||
<p className="font-semibold text-slate-900">{order.event_name}</p>
|
||||
<p className="text-xs text-slate-500">{order.business_name || "—"}</p>
|
||||
<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> {/* Date cell */}
|
||||
<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-semibold text-slate-900">
|
||||
{safeFormatDate(order.date, 'MMM dd, yyyy')}
|
||||
</p>
|
||||
<p className="text-xs text-slate-500">
|
||||
{safeFormatDate(order.date, 'EEEE')}
|
||||
<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> {/* Location cell */}
|
||||
<div className="flex items-center gap-1.5 text-sm text-slate-600">
|
||||
<MapPin className="w-3.5 h-3.5 text-slate-400" />
|
||||
{order.hub || order.event_location || "—"}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell> {/* Time cell */}
|
||||
<div className="flex items-center gap-1 text-sm text-slate-600">
|
||||
<Clock className="w-3.5 h-3.5 text-slate-400" />
|
||||
{startTime} - {endTime}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell> {/* Status cell */}
|
||||
<TableCell>
|
||||
{getStatusBadge(order)}
|
||||
</TableCell>
|
||||
<TableCell className="text-center"> {/* Staff cell */}
|
||||
<TableCell>
|
||||
<span className="text-lg font-bold text-slate-900">{requestedCount}</span>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<Badge className={assignment.badgeClass}>
|
||||
{assignment.assigned} / {assignment.requested}
|
||||
</Badge>
|
||||
<span className="text-[10px] text-slate-500 font-medium">
|
||||
{assignment.percentage}%
|
||||
</span>
|
||||
<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 className="text-center"> {/* Invoice cell */}
|
||||
<div className="flex items-center justify-center">
|
||||
<Button // Changed from a div to a Button for better accessibility
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => invoiceReady && navigate(createPageUrl('Invoices'))}
|
||||
className={`w-10 h-10 rounded-full flex items-center justify-center ${invoiceReady ? 'bg-blue-100' : 'bg-slate-100'} ${invoiceReady ? 'cursor-pointer hover:bg-blue-200' : 'cursor-not-allowed opacity-50'}`}
|
||||
disabled={!invoiceReady}
|
||||
title={invoiceReady ? "View Invoice" : "Invoice not available"}
|
||||
>
|
||||
<FileText className={`w-5 h-5 ${invoiceReady ? 'text-blue-600' : 'text-slate-400'}`} />
|
||||
</Button>
|
||||
</div>
|
||||
<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> {/* Actions cell */}
|
||||
<div className="flex items-center justify-center gap-1">
|
||||
<TableCell>
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => navigate(createPageUrl(`EventDetail?id=${order.id}`))}
|
||||
className="hover:bg-slate-100"
|
||||
title="View details"
|
||||
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="hover:bg-slate-100"
|
||||
title="Edit order"
|
||||
className="h-8 w-8 p-0"
|
||||
title="Edit"
|
||||
>
|
||||
<Edit className="w-4 h-4" /> {/* Changed from Edit2 */}
|
||||
<Edit3 className="w-4 h-4" />
|
||||
</Button>
|
||||
)}
|
||||
{canCancelOrder(order) && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => handleCancelOrder(order)} // Updated
|
||||
className="hover:bg-red-50 hover:text-red-600"
|
||||
title="Cancel order"
|
||||
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>
|
||||
@@ -460,6 +464,13 @@ export default function ClientOrders() {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<OrderDetailModal
|
||||
open={viewOrderModal}
|
||||
onClose={() => setViewOrderModal(false)}
|
||||
order={selectedOrder}
|
||||
onCancel={handleCancelOrder}
|
||||
/>
|
||||
|
||||
<Dialog open={cancelDialogOpen} onOpenChange={setCancelDialogOpen}> {/* Updated open and onOpenChange */}
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
@@ -505,4 +516,4 @@ export default function ClientOrders() {
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user