"use client"; import { useReducer, useMemo, useCallback, useEffect, useTransition, useRef } from "react"; import Link from "next/link"; import { useSearchParams } from "next/navigation"; import { Check, ChevronDown, ChevronLeft, ChevronRight, MapPin, Package, X, } from "lucide-react"; import { markPickupComplete } from "@/actions/pickup"; import { createAdminOrder } from "@/actions/orders/create-admin-order"; import { formatDate } from "@/lib/format-date"; import AdminBadge from "./design-system/AdminBadge"; import AdminButton, { AdminIconButton } from "./design-system/AdminButton"; import AdminSearchInput from "./design-system/AdminSearchInput"; import AdminFilterTabs from "./design-system/AdminFilterTabs"; import { useToast } from "./Toast"; import { AdminInput, AdminTextInput, AdminSelect } from "./design-system/AdminFormElements"; import EmptyState from "./EmptyState"; import KPIStat from "./KPIStat"; import { Skeleton } from "./design-system/Skeleton"; type OrderItem = { id: string; product_id: string; quantity: number; price: number; products: { name: string } | null; }; type Order = { id: string; customer_name: string; customer_email: string | null; customer_phone: string | null; stop_id: string | null; status: string; subtotal: number; pickup_complete: boolean; pickup_completed_at: string | null; pickup_completed_by: string | null; created_at: string; payment_processor: string | null; stops: { id?: string; city: string; state: string; date: string; brand_id?: string; } | null; order_items?: OrderItem[]; }; type Product = { id: string; name: string; price: number; type?: string | null; active?: boolean; }; type Stop = { id: string; city: string; state: string; date: string; brand_id?: string; }; type AdminOrdersPanelProps = { initialOrders: Order[]; initialStops: Stop[]; initialProducts?: Array<{ id: string; name: string; price: number; type?: string | null; active?: boolean }>; brandId: string | null; }; type StatusTab = "all" | "pending" | "picked_up"; type NewOrderItem = { product_id: string; quantity: number; price: number; fulfillment: "pickup" | "ship" }; function shortId(id: string) { return id.slice(0, 8).toUpperCase(); } const currencyFormatter = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }); function formatCurrency(amount: number) { return currencyFormatter.format(amount); } // Icon wrappers (lucide-react) — kept compact for inline use. const MapPinIcon = () => ; const CheckIcon = () => ; const XIcon = () => ; const ChevronDownIcon = () => ; const ChevronLeftIcon = () => ; const ChevronRightIcon = () => ; const EMPTY_PRODUCTS: Product[] = []; // ── State / Reducer ──────────────────────────────────────────────────────────── type State = { orders: Order[]; search: string; activeTab: StatusTab; selectedStops: string[]; showNewOrderModal: boolean; newOrderCustomerName: string; newOrderCustomerEmail: string; newOrderCustomerPhone: string; newOrderStopId: string; newOrderItems: NewOrderItem[]; newOrderSubmitting: boolean; newOrderError: string | null; showStopDropdown: boolean; pickingUp: string | null; page: number; selectedOrders: Set; bulkMarkingUp: boolean; }; type Action = | { type: "SET_ORDERS"; value: Order[] } | { type: "MARK_PICKUP"; orderId: string; completedAt: string | null; completedBy: string | null } | { type: "SET_SEARCH"; value: string } | { type: "SET_ACTIVE_TAB"; value: StatusTab } | { type: "RESET_PAGE" } | { type: "TOGGLE_STOP"; stopId: string } | { type: "CLEAR_STOPS" } | { type: "OPEN_NEW_ORDER_MODAL" } | { type: "CLOSE_NEW_ORDER_MODAL" } | { type: "SET_NEW_ORDER_CUSTOMER_NAME"; value: string } | { type: "SET_NEW_ORDER_CUSTOMER_EMAIL"; value: string } | { type: "SET_NEW_ORDER_CUSTOMER_PHONE"; value: string } | { type: "SET_NEW_ORDER_STOP_ID"; value: string } | { type: "SET_NEW_ORDER_ERROR"; value: string | null } | { type: "ADD_NEW_ORDER_ITEM"; item: NewOrderItem } | { type: "UPDATE_NEW_ORDER_ITEM"; index: number; patch: Partial } | { type: "REMOVE_NEW_ORDER_ITEM"; index: number } | { type: "SET_NEW_ORDER_SUBMITTING"; value: boolean } | { type: "TOGGLE_STOP_DROPDOWN"; value: boolean } | { type: "SET_PICKING_UP"; value: string | null } | { type: "SET_PAGE"; value: number } | { type: "TOGGLE_SELECT_ALL"; paginatedIds: string[] } | { type: "TOGGLE_ORDER_SELECTION"; orderId: string } | { type: "CLEAR_ORDER_SELECTION" } | { type: "SET_BULK_MARKING_UP"; value: boolean }; const initialState: State = { orders: [], search: "", activeTab: "all", selectedStops: [], showNewOrderModal: false, newOrderCustomerName: "", newOrderCustomerEmail: "", newOrderCustomerPhone: "", newOrderStopId: "", newOrderItems: [], newOrderSubmitting: false, newOrderError: null, showStopDropdown: false, pickingUp: null, page: 0, selectedOrders: new Set(), bulkMarkingUp: false, }; function reducer(state: State, action: Action): State { switch (action.type) { case "SET_ORDERS": return { ...state, orders: action.value }; case "MARK_PICKUP": return { ...state, orders: state.orders.map((o) => o.id === action.orderId ? { ...o, pickup_complete: true, pickup_completed_at: action.completedAt, pickup_completed_by: action.completedBy, } : o ), }; case "SET_SEARCH": return { ...state, search: action.value }; case "SET_ACTIVE_TAB": return { ...state, activeTab: action.value }; case "RESET_PAGE": return { ...state, page: 0 }; case "TOGGLE_STOP": return { ...state, selectedStops: state.selectedStops.includes(action.stopId) ? state.selectedStops.filter((id) => id !== action.stopId) : [...state.selectedStops, action.stopId], page: 0, }; case "CLEAR_STOPS": return { ...state, selectedStops: [], page: 0 }; case "OPEN_NEW_ORDER_MODAL": return { ...state, showNewOrderModal: true, newOrderError: null }; case "CLOSE_NEW_ORDER_MODAL": return { ...state, showNewOrderModal: false, newOrderCustomerName: "", newOrderCustomerEmail: "", newOrderCustomerPhone: "", newOrderStopId: "", newOrderItems: [], newOrderError: null, }; case "SET_NEW_ORDER_CUSTOMER_NAME": return { ...state, newOrderCustomerName: action.value }; case "SET_NEW_ORDER_CUSTOMER_EMAIL": return { ...state, newOrderCustomerEmail: action.value }; case "SET_NEW_ORDER_CUSTOMER_PHONE": return { ...state, newOrderCustomerPhone: action.value }; case "SET_NEW_ORDER_STOP_ID": return { ...state, newOrderStopId: action.value }; case "SET_NEW_ORDER_ERROR": return { ...state, newOrderError: action.value }; case "ADD_NEW_ORDER_ITEM": return { ...state, newOrderItems: [...state.newOrderItems, action.item] }; case "UPDATE_NEW_ORDER_ITEM": return { ...state, newOrderItems: state.newOrderItems.map((it, i) => (i === action.index ? { ...it, ...action.patch } : it)), }; case "REMOVE_NEW_ORDER_ITEM": return { ...state, newOrderItems: state.newOrderItems.filter((_, i) => i !== action.index), }; case "SET_NEW_ORDER_SUBMITTING": return { ...state, newOrderSubmitting: action.value }; case "TOGGLE_STOP_DROPDOWN": return { ...state, showStopDropdown: action.value }; case "SET_PICKING_UP": return { ...state, pickingUp: action.value }; case "SET_PAGE": return { ...state, page: action.value }; case "TOGGLE_SELECT_ALL": return { ...state, selectedOrders: state.selectedOrders.size === action.paginatedIds.length ? new Set() : new Set(action.paginatedIds), }; case "TOGGLE_ORDER_SELECTION": { const next = new Set(state.selectedOrders); if (next.has(action.orderId)) { next.delete(action.orderId); } else { next.add(action.orderId); } return { ...state, selectedOrders: next }; } case "CLEAR_ORDER_SELECTION": return { ...state, selectedOrders: new Set() }; case "SET_BULK_MARKING_UP": return { ...state, bulkMarkingUp: action.value }; } } const PAGE_SIZE = 20; export default function AdminOrdersPanel({ initialOrders, initialStops, initialProducts = EMPTY_PRODUCTS, brandId, }: AdminOrdersPanelProps) { const { success: showSuccess, error: showError } = useToast(); const searchParams = useSearchParams(); const [state, dispatch] = useReducer(reducer, { ...initialState, orders: initialOrders }); const { orders, search, activeTab, selectedStops, showNewOrderModal, newOrderCustomerName, newOrderCustomerEmail, newOrderCustomerPhone, newOrderStopId, newOrderItems, newOrderSubmitting, newOrderError, showStopDropdown, pickingUp, page, selectedOrders, bulkMarkingUp, } = state; const stops = initialStops; const products = initialProducts; const [isPending, startTransition] = useTransition(); const filterVersionRef = useRef(0); // Open the New Order modal when dashboard links here with ?new=true useEffect(() => { const init = async () => { if (searchParams?.get("new") === "true") { dispatch({ type: "OPEN_NEW_ORDER_MODAL" }); } }; void init(); }, [searchParams]); // Mark the filter operation as a transition whenever the input // dependencies change, so React can defer the heavy list work and // give us an `isPending` flag instead of a fake useState round-trip. useEffect(() => { startTransition(() => { filterVersionRef.current = filterVersionRef.current + 1; }); }, [page, activeTab, search, selectedStops.length, startTransition]); const filteredOrders = useMemo(() => { return orders.filter((order) => { if (activeTab === "pending" && order.pickup_complete) return false; if (activeTab === "picked_up" && !order.pickup_complete) return false; if (selectedStops.length > 0 && order.stop_id && !selectedStops.includes(order.stop_id)) return false; if (search.trim()) { const s = search.toLowerCase(); if (!order.customer_name.toLowerCase().includes(s) && !(order.customer_phone ?? "").toLowerCase().includes(s) && !order.id.toLowerCase().includes(s)) return false; } return true; }); }, [orders, activeTab, selectedStops, search]); const totalPages = Math.ceil(filteredOrders.length / PAGE_SIZE); const paginatedOrders = filteredOrders.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE); const pendingCount = orders.filter((o) => !o.pickup_complete).length; const pickedUpCount = orders.filter((o) => o.pickup_complete).length; const newOrderTotal = newOrderItems.reduce((sum, it) => sum + it.price * it.quantity, 0); // Bulk selection const toggleSelectAll = useCallback(() => { dispatch({ type: "TOGGLE_SELECT_ALL", paginatedIds: paginatedOrders.map((o) => o.id) }); }, [paginatedOrders]); const toggleOrderSelection = useCallback((orderId: string) => { dispatch({ type: "TOGGLE_ORDER_SELECTION", orderId }); }, []); // --- New Order (admin manual create) handlers --- function openNewOrderModal() { dispatch({ type: "OPEN_NEW_ORDER_MODAL" }); } function closeNewOrderModal() { dispatch({ type: "CLOSE_NEW_ORDER_MODAL" }); } function addNewOrderItem(productId: string) { const prod = products.find((p) => p.id === productId); if (!prod) return; dispatch({ type: "ADD_NEW_ORDER_ITEM", item: { product_id: prod.id, quantity: 1, price: Number(prod.price), fulfillment: "pickup", }, }); } function updateNewOrderItem(index: number, patch: Partial) { dispatch({ type: "UPDATE_NEW_ORDER_ITEM", index, patch }); } function removeNewOrderItem(index: number) { dispatch({ type: "REMOVE_NEW_ORDER_ITEM", index }); } function toggleStop(stopId: string) { dispatch({ type: "TOGGLE_STOP", stopId }); } function clearStops() { dispatch({ type: "CLEAR_STOPS" }); } async function handleCreateAdminOrder() { if (!newOrderCustomerName.trim()) { dispatch({ type: "SET_NEW_ORDER_ERROR", value: "Customer name is required" }); return; } if (newOrderItems.length === 0) { dispatch({ type: "SET_NEW_ORDER_ERROR", value: "Add at least one product" }); return; } dispatch({ type: "SET_NEW_ORDER_SUBMITTING", value: true }); dispatch({ type: "SET_NEW_ORDER_ERROR", value: null }); const result = await createAdminOrder(brandId, { customer_name: newOrderCustomerName.trim(), customer_email: newOrderCustomerEmail.trim() || null, customer_phone: newOrderCustomerPhone.trim() || null, stop_id: newOrderStopId || null, items: newOrderItems.map((i) => ({ product_id: i.product_id, quantity: i.quantity, price: i.price, fulfillment: i.fulfillment, })), }); dispatch({ type: "SET_NEW_ORDER_SUBMITTING", value: false }); if (result.success) { showSuccess(`Order created: ${result.orderId.slice(0, 8).toUpperCase()}`); closeNewOrderModal(); // Navigate to clean orders list (removes ?new=true and shows the new order after refresh) window.location.href = "/admin/orders"; } else { dispatch({ type: "SET_NEW_ORDER_ERROR", value: result.error ?? "Failed to create order" }); } } async function handleMarkPickup(orderId: string) { dispatch({ type: "SET_PICKING_UP", value: orderId }); const result = await markPickupComplete(orderId, brandId); if (result.success) { showSuccess("Pickup confirmed", `${result.pickup_completed_at ? 'Order marked as picked up' : 'Success'}`); dispatch({ type: "MARK_PICKUP", orderId, completedAt: result.pickup_completed_at, completedBy: result.pickup_completed_by, }); } else { showError("Failed to mark pickup", result.error ?? "Please try again"); } dispatch({ type: "SET_PICKING_UP", value: null }); } async function handleBulkMarkPickup() { if (selectedOrders.size === 0) return; dispatch({ type: "SET_BULK_MARKING_UP", value: true }); const orderIds = Array.from(selectedOrders); const results = await Promise.all( orderIds.map(async (orderId) => { try { const result = await markPickupComplete(orderId, brandId); if (result.success) { dispatch({ type: "MARK_PICKUP", orderId, completedAt: result.pickup_completed_at, completedBy: result.pickup_completed_by, }); return true; } return false; } catch { return false; } }) ); const successCount = results.filter((r) => r).length; const failCount = results.length - successCount; dispatch({ type: "SET_BULK_MARKING_UP", value: false }); dispatch({ type: "CLEAR_ORDER_SELECTION" }); if (failCount === 0) { showSuccess(`${successCount} order${successCount !== 1 ? 's' : ''} marked as picked up`); } else { showError("Some orders failed", `${successCount} succeeded, ${failCount} failed`); } } return ( { dispatch({ type: "SET_ACTIVE_TAB", value }); dispatch({ type: "RESET_PAGE" }); }} onSearchChange={(value) => { dispatch({ type: "SET_SEARCH", value }); dispatch({ type: "RESET_PAGE" }); }} onSearchClear={() => { dispatch({ type: "SET_SEARCH", value: "" }); dispatch({ type: "RESET_PAGE" }); }} onPageChange={(p) => dispatch({ type: "SET_PAGE", value: p })} onToggleSelectAll={toggleSelectAll} onToggleOrderSelection={toggleOrderSelection} onClearOrderSelection={() => dispatch({ type: "CLEAR_ORDER_SELECTION" })} onBulkMarkPickup={handleBulkMarkPickup} onMarkPickup={handleMarkPickup} onToggleStopDropdown={(value) => dispatch({ type: "TOGGLE_STOP_DROPDOWN", value })} onToggleStop={toggleStop} onClearStops={clearStops} onOpenNewOrderModal={openNewOrderModal} onCloseNewOrderModal={closeNewOrderModal} onSetNewOrderCustomerName={(value) => dispatch({ type: "SET_NEW_ORDER_CUSTOMER_NAME", value })} onSetNewOrderCustomerEmail={(value) => dispatch({ type: "SET_NEW_ORDER_CUSTOMER_EMAIL", value })} onSetNewOrderCustomerPhone={(value) => dispatch({ type: "SET_NEW_ORDER_CUSTOMER_PHONE", value })} onSetNewOrderStopId={(value) => dispatch({ type: "SET_NEW_ORDER_STOP_ID", value })} onAddNewOrderItem={addNewOrderItem} onUpdateNewOrderItem={updateNewOrderItem} onRemoveNewOrderItem={removeNewOrderItem} onCreateAdminOrder={handleCreateAdminOrder} /> ); } // ── OrdersAdminView (presentation) ───────────────────────────────────────────── function OrdersAdminView({ brandId, isPending, orders, filteredOrders, paginatedOrders, pendingCount, pickedUpCount, totalPages, page, search, activeTab, selectedStops, selectedOrders, bulkMarkingUp, showStopDropdown, pickingUp, stops, products, showNewOrderModal, newOrderCustomerName, newOrderCustomerEmail, newOrderCustomerPhone, newOrderStopId, newOrderItems, newOrderSubmitting, newOrderError, newOrderTotal, onActiveTabChange, onSearchChange, onSearchClear, onPageChange, onToggleSelectAll, onToggleOrderSelection, onClearOrderSelection, onBulkMarkPickup, onMarkPickup, onToggleStopDropdown, onToggleStop, onClearStops, onOpenNewOrderModal, onCloseNewOrderModal, onSetNewOrderCustomerName, onSetNewOrderCustomerEmail, onSetNewOrderCustomerPhone, onSetNewOrderStopId, onAddNewOrderItem, onUpdateNewOrderItem, onRemoveNewOrderItem, onCreateAdminOrder, }: { brandId: string | null; isPending: boolean; orders: Order[]; filteredOrders: Order[]; paginatedOrders: Order[]; pendingCount: number; pickedUpCount: number; totalPages: number; page: number; search: string; activeTab: StatusTab; selectedStops: string[]; selectedOrders: Set; bulkMarkingUp: boolean; showStopDropdown: boolean; pickingUp: string | null; stops: Stop[]; products: Product[]; showNewOrderModal: boolean; newOrderCustomerName: string; newOrderCustomerEmail: string; newOrderCustomerPhone: string; newOrderStopId: string; newOrderItems: NewOrderItem[]; newOrderSubmitting: boolean; newOrderError: string | null; newOrderTotal: number; onActiveTabChange: (value: StatusTab) => void; onSearchChange: (value: string) => void; onSearchClear: () => void; onPageChange: (p: number) => void; onToggleSelectAll: () => void; onToggleOrderSelection: (orderId: string) => void; onClearOrderSelection: () => void; onBulkMarkPickup: () => void; onMarkPickup: (orderId: string) => void; onToggleStopDropdown: (value: boolean) => void; onToggleStop: (stopId: string) => void; onClearStops: () => void; onOpenNewOrderModal: () => void; onCloseNewOrderModal: () => void; onSetNewOrderCustomerName: (value: string) => void; onSetNewOrderCustomerEmail: (value: string) => void; onSetNewOrderCustomerPhone: (value: string) => void; onSetNewOrderStopId: (value: string) => void; onAddNewOrderItem: (productId: string) => void; onUpdateNewOrderItem: (index: number, patch: Partial) => void; onRemoveNewOrderItem: (index: number) => void; onCreateAdminOrder: () => void; }) { return ( <>
{showNewOrderModal && ( )} ); } // ── Page Header ──────────────────────────────────────────────────────────────── function OrdersPageHeader({ filteredCount, brandId, onNewOrder, }: { filteredCount: number; brandId: string | null; onNewOrder: () => void; }) { return (

Orders

{filteredCount} order{filteredCount !== 1 ? "s" : ""}

{brandId && ( Brand scoped )} + New Order
); } // ── Stats Cards ─────────────────────────────────────────────────────────────── function OrdersStatsCards({ isPending, ordersCount, pendingCount, pickedUpCount, }: { isPending: boolean; ordersCount: number; pendingCount: number; pickedUpCount: number; }) { return (
: ordersCount} tone="default" /> : pendingCount} tone="warning" /> : pickedUpCount} tone="primary" />
); } // ── Filter Bar ──────────────────────────────────────────────────────────────── function OrdersFilterBar({ activeTab, onActiveTabChange, filteredCount, pendingCount, pickedUpCount, search, onSearchChange, onSearchClear, stops, selectedStops, showStopDropdown, onToggleStopDropdown, onToggleStop, onClearStops, }: { activeTab: StatusTab; onActiveTabChange: (value: StatusTab) => void; filteredCount: number; pendingCount: number; pickedUpCount: number; search: string; onSearchChange: (value: string) => void; onSearchClear: () => void; stops: Stop[]; selectedStops: string[]; showStopDropdown: boolean; onToggleStopDropdown: (value: boolean) => void; onToggleStop: (stopId: string) => void; onClearStops: () => void; }) { return (
onActiveTabChange(value as StatusTab)} tabs={[ { value: "all", label: "All", count: filteredCount }, { value: "pending", label: "Pending", count: pendingCount }, { value: "picked_up", label: "Picked Up", count: pickedUpCount }, ]} size="md" /> onSearchChange(e.target.value)} onClear={onSearchClear} showClear={true} />
); } function StopFilterDropdown({ stops, selectedStops, showStopDropdown, onToggleStopDropdown, onToggleStop, onClearStops, }: { stops: Stop[]; selectedStops: string[]; showStopDropdown: boolean; onToggleStopDropdown: (value: boolean) => void; onToggleStop: (stopId: string) => void; onClearStops: () => void; }) { return (
{showStopDropdown && ( <>
{stops.map((stop) => ( ))}
)} ); } // ── Selected Stop Chips ──────────────────────────────────────────────────────── function SelectedStopChips({ stops, selectedStops, onToggleStop, }: { stops: Stop[]; selectedStops: string[]; onToggleStop: (stopId: string) => void; }) { if (selectedStops.length === 0) return null; return (
{selectedStops.map((stopId) => { const stop = stops.find((s) => s.id === stopId); if (!stop) return null; return ( {stop.city}, {stop.state} ); })}
); } // ── Bulk Actions Bar ────────────────────────────────────────────────────────── function BulkActionsBar({ selectedCount, bulkMarkingUp, onClear, onMarkAll, }: { selectedCount: number; bulkMarkingUp: boolean; onClear: () => void; onMarkAll: () => void; }) { if (selectedCount === 0) return null; return (
{selectedCount} order{selectedCount !== 1 ? 's' : ''} selected
} > Mark All as Picked Up
); } // ── Orders Table Section ────────────────────────────────────────────────────── function OrdersTableSection({ isPending, ordersCount, paginatedOrders, selectedOrders, pickingUp, onToggleSelectAll, onToggleOrderSelection, onMarkPickup, }: { isPending: boolean; ordersCount: number; paginatedOrders: Order[]; selectedOrders: Set; pickingUp: string | null; onToggleSelectAll: () => void; onToggleOrderSelection: (orderId: string) => void; onMarkPickup: (orderId: string) => void; }) { if (isPending) { return (
{Array.from({ length: 5 }).map((_, i) => (
))}
); } if (paginatedOrders.length === 0) { return (
} title={ordersCount === 0 ? "No orders yet" : "No orders match your filters"} description={ ordersCount === 0 ? "Create your first order to get started." : "Try adjusting your filters or clearing your search." } action={ ordersCount === 0 ? { label: "Create your first order", href: "/admin/orders?new=true" } : undefined } />
); } return ( ); } function OrdersTable({ paginatedOrders, selectedOrders, pickingUp, onToggleSelectAll, onToggleOrderSelection, onMarkPickup, }: { paginatedOrders: Order[]; selectedOrders: Set; pickingUp: string | null; onToggleSelectAll: () => void; onToggleOrderSelection: (orderId: string) => void; onMarkPickup: (orderId: string) => void; }) { return (
{paginatedOrders.map((order) => ( onToggleOrderSelection(order.id)} onMarkPickup={() => onMarkPickup(order.id)} /> ))}
0} onChange={onToggleSelectAll} className="h-4 w-4 rounded cursor-pointer" style={{ borderColor: "var(--admin-border-strong)", accentColor: "var(--admin-accent)", }} /> Order Customer Stop Date Items Total Status
); } function OrderRow({ order, isSelected, isPickingUp, onToggleSelect, onMarkPickup, }: { order: Order; isSelected: boolean; isPickingUp: boolean; onToggleSelect: () => void; onMarkPickup: () => void; }) { return ( {shortId(order.id)}
{order.customer_name}
{order.customer_phone && (
{order.customer_phone}
)} {order.stops ? ( {order.stops.city}, {order.stops.state} ) : ( )} {formatDate(order.created_at)} {order.order_items?.length ?? 0} {formatCurrency(order.subtotal)}
{order.pickup_complete ? ( Picked Up ) : ( Pending )} {order.payment_processor === "square" && ( Square )} {!order.pickup_complete && ( {isPickingUp ? "..." : "Pick Up"} )}
); } // ── Pagination ──────────────────────────────────────────────────────────────── function OrdersPagination({ page, totalPages, totalCount, onPageChange, }: { page: number; totalPages: number; totalCount: number; onPageChange: (p: number) => void; }) { if (totalPages <= 1) return null; return (

Showing {(page * PAGE_SIZE) + 1} to {Math.min((page + 1) * PAGE_SIZE, totalCount)} of {totalCount}

onPageChange(Math.max(0, page - 1))} disabled={page === 0} className="!rounded-lg" > {page + 1} / {totalPages} onPageChange(Math.min(totalPages - 1, page + 1))} disabled={page >= totalPages - 1} className="!rounded-lg" >
); } // ── New Order Modal ─────────────────────────────────────────────────────────── function NewOrderModal({ stops, products, customerName, customerEmail, customerPhone, stopId, items, submitting, error, total, onClose, onSetCustomerName, onSetCustomerEmail, onSetCustomerPhone, onSetStopId, onAddItem, onUpdateItem, onRemoveItem, onSubmit, }: { stops: Stop[]; products: Product[]; customerName: string; customerEmail: string; customerPhone: string; stopId: string; items: NewOrderItem[]; submitting: boolean; error: string | null; total: number; onClose: () => void; onSetCustomerName: (value: string) => void; onSetCustomerEmail: (value: string) => void; onSetCustomerPhone: (value: string) => void; onSetStopId: (value: string) => void; onAddItem: (productId: string) => void; onUpdateItem: (index: number, patch: Partial) => void; onRemoveItem: (index: number) => void; onSubmit: () => void; }) { return (

New Order (Admin)

Manual entry for testing / phone orders

{error && (
{error}
)}
onSetCustomerName(e.target.value)} placeholder="Jane Doe" /> onSetCustomerEmail(e.target.value)} placeholder="jane@example.com" type="email" /> onSetCustomerPhone(e.target.value)} placeholder="(555) 123-4567" />
onSetStopId(e.target.value)} options={[ { value: "", label: "— No stop (ship / manual fulfillment) —" }, ...stops.map((s) => ({ value: s.id, label: `${s.city}, ${s.state} — ${formatDate(s.date)}`, })), ]} />
Cancel {submitting ? "Creating..." : `Create Order — $${total.toFixed(2)}`}
); } function NewOrderItemsEditor({ products, items, total, onAdd, onUpdate, onRemove, }: { products: Product[]; items: NewOrderItem[]; total: number; onAdd: (productId: string) => void; onUpdate: (index: number, patch: Partial) => void; onRemove: (index: number) => void; }) { return (
Total: ${total.toFixed(2)}
{products.length > 0 ? (
{ if (e.target.value) onAdd(e.target.value); }} options={[ { value: "", label: "— Add a product —" }, ...products.map((p) => ({ value: p.id, label: `${p.name} — $${Number(p.price).toFixed(2)}` })), ]} />
) : (

No products loaded for this brand. You can still enter custom items if the action supports it.

)} {items.length === 0 && (

No items yet. Use the selector above.

)} {items.length > 0 && (
{items.map((item, idx) => { const prod = products.find((p) => p.id === item.product_id); return (
{prod?.name ?? item.product_id}
onUpdate(idx, { quantity: Math.max(1, parseInt(e.target.value) || 1) })} className="w-full rounded border px-2 py-1 text-sm" style={{ borderColor: "var(--admin-border)", backgroundColor: "var(--admin-card-bg)", color: "var(--admin-text-primary)", }} />
onUpdate(idx, { price: parseFloat(e.target.value) || 0 })} className="w-full rounded border px-2 py-1 text-sm" style={{ borderColor: "var(--admin-border)", backgroundColor: "var(--admin-card-bg)", color: "var(--admin-text-primary)", }} />
); })}
)}
); }