diff --git a/apps/web/src/features/operations/orders/OrderDetail.tsx b/apps/web/src/features/operations/orders/OrderDetail.tsx
index 9a9db41a..8c3c0388 100644
--- a/apps/web/src/features/operations/orders/OrderDetail.tsx
+++ b/apps/web/src/features/operations/orders/OrderDetail.tsx
@@ -1,9 +1,451 @@
-import React from 'react'
+import React, { useMemo } from "react";
+import { useNavigate, useParams } from "react-router-dom";
+import { format } from "date-fns";
+import { useSelector } from "react-redux";
+import { Calendar, MapPin, Users, DollarSign, Edit3, X, Copy, Clock } from "lucide-react";
+
+import { Card, CardContent, CardHeader, CardTitle } from "@/common/components/ui/card";
+import { Button } from "@/common/components/ui/button";
+import { Badge } from "@/common/components/ui/badge";
+import DashboardLayout from "@/features/layouts/DashboardLayout";
+import { useGetOrderById, useUpdateOrder } from "@/dataconnect-generated/react";
+import { OrderStatus } from "@/dataconnect-generated";
+import { dataConnect } from "@/features/auth/firebase";
+import { useToast } from "@/common/components/ui/use-toast";
+import type { RootState } from "@/store/store";
+
+const safeFormatDate = (value?: string | null): string => {
+ if (!value) return "—";
+ try {
+ const d = new Date(value);
+ if (Number.isNaN(d.getTime())) return "—";
+ return format(d, "MMM d, yyyy");
+ } catch {
+ return "—";
+ }
+};
+
+const safeFormatDateTime = (value?: string | null): string => {
+ if (!value) return "—";
+ try {
+ const d = new Date(value);
+ if (Number.isNaN(d.getTime())) return "—";
+ return format(d, "MMM d, yyyy • h:mm a");
+ } catch {
+ return "—";
+ }
+};
+
+const getStatusBadge = (status: OrderStatus) => {
+ switch (status) {
+ case OrderStatus.FULLY_STAFFED:
+ case OrderStatus.FILLED:
+ return (
+
+ Fully Staffed
+
+ );
+ case OrderStatus.PARTIAL_STAFFED:
+ return (
+
+ Partial Staffed
+
+ );
+ case OrderStatus.PENDING:
+ case OrderStatus.POSTED:
+ return (
+
+ {status}
+
+ );
+ case OrderStatus.CANCELLED:
+ return (
+
+ Cancelled
+
+ );
+ case OrderStatus.COMPLETED:
+ return (
+
+ Completed
+
+ );
+ case OrderStatus.DRAFT:
+ default:
+ return (
+
+ {status}
+
+ );
+ }
+};
+
+export default function OrderDetail() {
+ const navigate = useNavigate();
+ const { id } = useParams<{ id: string }>();
+ const { toast } = useToast();
+ const { user } = useSelector((state: RootState) => state.auth);
+
+ const {
+ data,
+ isLoading,
+ } = useGetOrderById(
+ dataConnect,
+ { id: id || "" },
+ {
+ enabled: !!id,
+ },
+ );
+
+ const order = data?.order;
+
+ const cancelMutation = useUpdateOrder(dataConnect, {
+ onSuccess: () => {
+ toast({
+ title: "Order cancelled",
+ description: "The order status has been updated to Cancelled.",
+ });
+ },
+ onError: () => {
+ toast({
+ title: "Failed to cancel order",
+ description: "Please try again or contact support.",
+ variant: "destructive",
+ });
+ },
+ });
+
+ const canModify = useMemo(() => {
+ if (!order) return false;
+ const status = order.status as OrderStatus;
+ return status !== OrderStatus.CANCELLED && status !== OrderStatus.COMPLETED;
+ }, [order]);
+
+ const handleCancel = () => {
+ if (!order || !id || !canModify) return;
+ cancelMutation.mutate({
+ id,
+ status: OrderStatus.CANCELLED,
+ });
+ };
+
+ const handleEdit = () => {
+ if (!order || !id) return;
+ // Placeholder: route can later be wired to an edit form
+ navigate(`/orders/create?edit=${id}`);
+ };
+
+ const handleDuplicate = () => {
+ if (!order || !id) return;
+ // Placeholder: route can later pre-fill a new order from this one
+ navigate(`/orders/create?duplicate=${id}`);
+ };
+
+ const shifts: any[] = Array.isArray(order?.shifts) ? (order!.shifts as any[]) : [];
+
+ const totalRequested = order?.requested ?? 0;
+ const totalAssigned = Array.isArray(order?.assignedStaff) ? order!.assignedStaff.length : 0;
+
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
+ if (!order) {
+ return (
+
+
+
This order may have been deleted or the link is invalid.
+
+
+
+ );
+ }
+
+ const isClient = user?.userRole === "client";
+
+ const clientName = order.business?.businessName || "Unknown client";
+ const eventDateLabel = safeFormatDate(order.date as string | null);
+ const locationLabel = order.business?.businessName || "—";
+
+ const timelineItems = [
+ {
+ label: "Order created",
+ value: safeFormatDateTime(order.createdAt as string | null),
+ },
+ {
+ label: "Event date",
+ value: eventDateLabel,
+ },
+ {
+ label: "Current status",
+ value: (order.status as string) || "—",
+ },
+ ];
-const OrderDetail = () => {
return (
-
OrderDetail
- )
+
+ {getStatusBadge(order.status as OrderStatus)}
+
+
+
+
+ }
+ >
+
+ {/* Header / Key Info */}
+
+
+ Order Overview
+
+
+
+
+
+
+
+
+
+ Client Name
+
+
{clientName}
+
+
+
+
+
+
+
+
+
+ Event Date
+
+
{eventDateLabel}
+
+
+
+
+
+
+
+
+
+ Location
+
+
{locationLabel}
+
+
+
+
+
+
+
+
+
+ Staffed / Requested
+
+
+ {totalAssigned} / {totalRequested}
+
+
+
+
+
+
+
+ {/* Shifts Section */}
+
+
+ Shifts
+
+
+ {shifts.length === 0 ? (
+
+
+
No shifts defined for this order.
+
+ Add shifts when creating or editing the order to see them here.
+
+
+ ) : (
+
+ {shifts.map((shift: any, index: number) => {
+ const start = safeFormatDateTime(shift.startTime || shift.start || shift.date);
+ const end = safeFormatDateTime(shift.endTime || shift.end);
+ const title = shift.title || shift.positionName || shift.roleName || `Shift #${index + 1}`;
+ const workersNeeded = shift.workersNeeded ?? shift.requested ?? 0;
+ const filled =
+ typeof shift.filled === "number"
+ ? shift.filled
+ : Array.isArray(shift.assignedStaff)
+ ? shift.assignedStaff.length
+ : 0;
+ const vacancies = Math.max(workersNeeded - filled, 0);
+
+ return (
+
+
+
{title}
+
+
+
+ {start} {end !== "—" && `→ ${end}`}
+
+
+
+
+
+
+
+ Required
+
+ {workersNeeded || "—"}
+
+
+
+ Assigned
+
+ {filled}
+
+
+
+ Vacancies
+
+ {vacancies}
+
+
+
+ );
+ })}
+
+ )}
+
+
+
+ {/* Timeline Section */}
+
+
+
+ Order Status Timeline
+
+
+
+
+ {timelineItems.map((item, idx) => (
+
+
+
+ {item.label}
+
+
{item.value}
+
+ ))}
+
+
+
+
+ {/* Financial Summary (optional helper, derived from existing fields) */}
+
+
+ Summary
+
+
+
+
+
+
+
+
+ Estimated Total
+
+
+ {typeof order.total === "number" ? `$${order.total.toLocaleString()}` : "—"}
+
+
+
+
+
+
+
+
+
+
+ Total Positions
+
+
{totalRequested}
+
+
+
+
+
+
+
+
+
+ Order Type
+
+
+ {(order.orderType as string)?.replace("_", " ") || "—"}
+
+
+
+
+
+
+
+ );
}
-export default OrderDetail
\ No newline at end of file
+const FileTextIcon: React.FC = () => (
+
+);