"use client" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Checkbox } from "@/components/ui/checkbox" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/custom-tabs" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Separator } from "@/components/ui/separator" import { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, } from "@/components/ui/sheet" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { closestCenter, DndContext, type DragEndEvent, KeyboardSensor, MouseSensor, TouchSensor, type UniqueIdentifier, useSensor, useSensors, } from "@dnd-kit/core" import { restrictToVerticalAxis } from "@dnd-kit/modifiers" import { arrayMove, SortableContext, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable" import { CSS } from "@dnd-kit/utilities" import { type ColumnDef, type ColumnFiltersState, flexRender, getCoreRowModel, getFacetedRowModel, getFacetedUniqueValues, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, type Row, type SortingState, useReactTable, type VisibilityState, } from "@tanstack/react-table" import { CheckCircle2Icon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronsLeftIcon, ChevronsRightIcon, ClockIcon, ColumnsIcon, CreditCardIcon, FilterIcon, GripVerticalIcon, HomeIcon, MailIcon, MapPinIcon, MoreVerticalIcon, PackageIcon, PhoneIcon, PlusIcon, SearchIcon, TruckIcon, UserIcon, XCircleIcon, } from "lucide-react" import * as React from "react" import { toast } from "sonner" import { z } from "zod" import { formatPrice } from "../../orders-page-2/lib/utils" export const orderItemSchema = z.object({ id: z.number(), name: z.string(), price: z.number(), quantity: z.number(), total: z.number(), }) export const orderAddressSchema = z.object({ street: z.string(), city: z.string(), state: z.string(), zipCode: z.string(), country: z.string(), }) export const orderCustomerSchema = z.object({ name: z.string(), email: z.string(), phone: z.string().optional(), }) export const orderSchema = z.object({ id: z.number(), orderNumber: z.string(), customer: z.string(), customerDetails: orderCustomerSchema.optional(), date: z.string(), status: z.string(), total: z.number(), items: z.number(), orderItems: z.array(orderItemSchema).optional(), paymentMethod: z.string(), shippingAddress: orderAddressSchema.optional(), billingAddress: orderAddressSchema.optional(), notes: z.string().optional(), }) function DragHandle({ id }: { id: number }) { const { attributes, listeners } = useSortable({ id, }) return ( ) } const getStatusIcon = (status: string) => { switch (status) { case "Completed": return case "Processing": return case "Shipped": return case "Cancelled": return default: return } } const getStatusColor = (status: string) => { switch (status) { case "Completed": return "text-green-500 dark:text-green-400" case "Processing": return "text-amber-500 dark:text-amber-400 " case "Shipped": return "text-blue-500 dark:text-blue-400" case "Cancelled": return "text-red-500 dark:text-red-400" default: return "text-muted-foreground" } } function OrderDetails({ order, onClose }: { order: z.infer; onClose: () => void }) { return ( Order #{order.orderNumber} Placed on {new Date(order.date).toLocaleDateString()} • {order.status} Details Items Customer
Status
{getStatusIcon(order.status)} {order.status}
Order Date
{new Date(order.date).toLocaleDateString()}
Payment Method
{order.paymentMethod}
Total Amount
{formatPrice(order.total)}
{order.shippingAddress && (

Shipping Address

{order.shippingAddress.street}

{order.shippingAddress.city}, {order.shippingAddress.state} {order.shippingAddress.zipCode}

{order.shippingAddress.country}

)} {order.notes && (

Order Notes

{order.notes}

)}
{order.orderItems && order.orderItems.length > 0 ? (
Item Qty Price Total {order.orderItems.map((item) => ( {item.name} {item.quantity} {formatPrice(item.price)} {formatPrice(item.total)} ))}
) : (

No items available

)}
Subtotal: {formatPrice(order.total * 0.9)}
Tax: {formatPrice(order.total * 0.1)}
Total: {formatPrice(order.total)}
{order.customerDetails ? (

{order.customerDetails.name}

Customer

{order.customerDetails.email}
{order.customerDetails.phone && (
{order.customerDetails.phone}
)}
{order.billingAddress && (

Billing Address

{order.billingAddress.street}

{order.billingAddress.city}, {order.billingAddress.state} {order.billingAddress.zipCode}

{order.billingAddress.country}

)}
) : (

No customer details available

)}
) } function UpdateStatusDialog({ order, isOpen, onClose, onUpdateStatus, }: { order: z.infer | null isOpen: boolean onClose: () => void onUpdateStatus: (orderId: number, newStatus: string) => void }) { const [status, setStatus] = React.useState(order?.status || "") React.useEffect(() => { if (order) { setStatus(order.status) } }, [order]) const handleSubmit = (e: React.FormEvent) => { e.preventDefault() if (order) { onUpdateStatus(order.id, status) onClose() } } return ( !open && onClose()}> Update Order Status Change the status for order #{order?.orderNumber}. This will be reflected immediately.
) } function DraggableRow({ row }: { row: Row> }) { const { transform, transition, setNodeRef, isDragging } = useSortable({ id: row.original.id, }) return ( {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} ) } export function OrdersTable({ data: initialData, }: { data: z.infer[] }) { // Create a mutable copy of initialData to work with const [allOrders, setAllOrders] = React.useState(() => [...initialData]) const [data, setData] = React.useState(() => [...initialData]) const [rowSelection, setRowSelection] = React.useState({}) const [columnVisibility, setColumnVisibility] = React.useState({}) const [columnFilters, setColumnFilters] = React.useState([]) const [sorting, setSorting] = React.useState([]) const [pagination, setPagination] = React.useState({ pageIndex: 0, pageSize: 10, }) const [searchQuery, setSearchQuery] = React.useState("") const [statusFilter, setStatusFilter] = React.useState("all") const [dateFilter, setDateFilter] = React.useState("all") const [selectedOrder, setSelectedOrder] = React.useState | null>(null) const [isUpdateStatusOpen, setIsUpdateStatusOpen] = React.useState(false) const [isOrderDetailsOpen, setIsOrderDetailsOpen] = React.useState(false) const sortableId = React.useId() const sensors = useSensors(useSensor(MouseSensor, {}), useSensor(TouchSensor, {}), useSensor(KeyboardSensor, {})) const dataIds = React.useMemo(() => data?.map(({ id }) => id) || [], [data]) const columns = React.useMemo>[]>( () => [ { id: "drag", header: () => null, cell: ({ row }) => , }, { id: "select", header: ({ table }) => (
table.toggleAllPageRowsSelected(!!value)} aria-label="Select all" />
), cell: ({ row }) => (
row.toggleSelected(!!value)} aria-label="Select row" />
), enableSorting: false, enableHiding: false, }, { accessorKey: "orderNumber", header: "Order #", cell: ({ row }) => { return ( ) }, }, { accessorKey: "customer", header: "Customer", cell: ({ row }) =>
{row.original.customer}
, }, { accessorKey: "date", header: "Date", cell: ({ row }) => { const date = new Date(row.original.date) return (
{date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", })}
) }, }, { accessorKey: "status", header: "Status", cell: ({ row }) => ( {getStatusIcon(row.original.status)} {row.original.status} ), }, { accessorKey: "total", header: () =>
Total
, cell: ({ row }) =>
{formatPrice(row.original.total)}
, }, { accessorKey: "items", header: () =>
Items
, cell: ({ row }) =>
{row.original.items}
, }, { accessorKey: "paymentMethod", header: "Payment", cell: ({ row }) =>
{row.original.paymentMethod}
, }, { id: "actions", cell: ({ row }) => ( handleViewOrderDetails(row.original)}>View details handleUpdateStatus(row.original)}>Update status Contact customer handleQuickStatusUpdate(row.original.id, "Cancelled")} > Cancel order ), }, ], [], ) // Apply filters when search query, status filter, or date filter changes React.useEffect(() => { const filteredData = allOrders.filter((order) => { const matchesSearch = searchQuery === "" || order.orderNumber.toLowerCase().includes(searchQuery.toLowerCase()) || order.customer.toLowerCase().includes(searchQuery.toLowerCase()) || order.paymentMethod.toLowerCase().includes(searchQuery.toLowerCase()) const matchesStatus = statusFilter === "all" || order.status.toLowerCase() === statusFilter.toLowerCase() let matchesDate = true const orderDate = new Date(order.date) const today = new Date() today.setHours(0, 0, 0, 0) if (dateFilter === "today") { const todayString = today.toISOString().split("T")[0] matchesDate = order.date === todayString } else if (dateFilter === "yesterday") { const yesterday = new Date(today) yesterday.setDate(yesterday.getDate() - 1) const yesterdayString = yesterday.toISOString().split("T")[0] matchesDate = order.date === yesterdayString } else if (dateFilter === "week") { const weekAgo = new Date(today) weekAgo.setDate(weekAgo.getDate() - 7) matchesDate = orderDate >= weekAgo } return matchesSearch && matchesStatus && matchesDate }) setData(filteredData) }, [searchQuery, statusFilter, dateFilter, allOrders]) const handleViewOrderDetails = (order: z.infer) => { setSelectedOrder(order) setIsOrderDetailsOpen(true) } const handleUpdateStatus = (order: z.infer) => { setSelectedOrder(order) setIsUpdateStatusOpen(true) } const handleQuickStatusUpdate = (orderId: number, newStatus: string) => { updateOrderStatus(orderId, newStatus) } const updateOrderStatus = (orderId: number, newStatus: string) => { // Update both the filtered data and the full data set setData((prevData) => prevData.map((order) => { if (order.id === orderId) { return { ...order, status: newStatus } } return order }), ) // Update the allOrders state instead of mutating initialData setAllOrders((prevOrders) => prevOrders.map((order) => { if (order.id === orderId) { return { ...order, status: newStatus } } return order }), ) // Find the order to show in the toast notification const orderToUpdate = allOrders.find((order) => order.id === orderId) if (orderToUpdate) { toast.success("Order status updated", { description: `Order #${orderToUpdate.orderNumber} is now ${newStatus}`, }) } } function handleDragEnd(event: DragEndEvent) { const { active, over } = event if (active && over && active.id !== over.id) { setData((data) => { const oldIndex = dataIds.indexOf(active.id) const newIndex = dataIds.indexOf(over.id) return arrayMove(data, oldIndex, newIndex) }) } } const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), getSortedRowModel: getSortedRowModel(), getFilteredRowModel: getFilteredRowModel(), getFacetedRowModel: getFacetedRowModel(), getFacetedUniqueValues: getFacetedUniqueValues(), onColumnVisibilityChange: setColumnVisibility, onColumnFiltersChange: setColumnFilters, onSortingChange: setSorting, onRowSelectionChange: setRowSelection, onPaginationChange: setPagination, state: { columnVisibility, columnFilters, sorting, rowSelection, pagination, }, }) return ( <>
setSearchQuery(e.target.value)} />

Status

Date

{table .getAllColumns() .filter((column) => typeof column.accessorFn !== "undefined" && column.getCanHide()) .map((column) => { return ( column.toggleVisibility(!!value)} > {column.id} ) })}
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { return ( {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} ) })} ))} {table.getRowModel().rows?.length ? ( {table.getRowModel().rows.map((row) => ( ))} ) : ( No orders found. )}
{table.getFilteredSelectedRowModel().rows.length} of {table.getFilteredRowModel().rows.length} order(s) selected.
per page
Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
{selectedOrder && setIsOrderDetailsOpen(false)} />} setIsUpdateStatusOpen(false)} onUpdateStatus={updateOrderStatus} /> ) }