import { useEffect, useState } from "react"; import { AdminButton, AdminSearchInput } from "@/components/admin/design-system"; import { openHtmlInPopup } from "@/lib/safe-window"; import { type WholesaleOrder, type WholesaleCustomer, bulkFulfillWholesaleOrders, bulkRecordWholesaleDeposit, markWholesaleOrderFulfilled, recordWholesaleDeposit, updateWholesaleOrderStatus, deleteWholesaleOrder, getWholesaleSettings, enqueueWholesaleNotification, } from "@/actions/wholesale"; import OrderDetailsModal from "@/components/wholesale/OrderDetailsModal"; import StatusBadge from "./StatusBadge"; import type { MsgFn } from "./types"; interface OrdersTabProps { orders: WholesaleOrder[]; customers: WholesaleCustomer[]; brandId: string; onMsg: MsgFn; onRefresh: () => void; } // Orders tab — list/filter orders, fulfill, record deposits, bulk operations, // cancel/delete, generate manifests, and dispatch notification emails on fulfillment. export default function OrdersTab({ orders, customers, brandId, onMsg, onRefresh }: OrdersTabProps) { const [showDepForm, setShowDepForm] = useState(null); const [depAmount, setDepAmount] = useState(""); const [depMethod, setDepMethod] = useState("cash"); const [fulfilling, setFulfilling] = useState(null); const [manifestLoading, setManifestLoading] = useState(false); const [selected, setSelected] = useState>(new Set()); const [statusFilter, setStatusFilter] = useState("all"); const [dateFrom, setDateFrom] = useState(""); const [dateTo, setDateTo] = useState(""); const [searchQuery, setSearchQuery] = useState(""); const [bulkDepAmount, setBulkDepAmount] = useState(""); const [bulkDepMethod, setBulkDepMethod] = useState("cash"); const [showBulkDep, setShowBulkDep] = useState(false); const [bulkLoading, setBulkLoading] = useState(false); const [openActions, setOpenActions] = useState(null); const [showViewOrder, setShowViewOrder] = useState(null); const [deleting, setDeleting] = useState(null); const filtered = orders.filter(o => { if (statusFilter !== "all" && o.status !== statusFilter) return false; if (dateFrom && (!o.anticipated_pickup_date || o.anticipated_pickup_date < dateFrom)) return false; if (dateTo && (!o.anticipated_pickup_date || o.anticipated_pickup_date > dateTo)) return false; if (searchQuery) { const q = searchQuery.toLowerCase(); if (!(o.company_name?.toLowerCase().includes(q) || o.invoice_number?.toLowerCase().includes(q) || o.customer_email?.toLowerCase().includes(q))) return false; } return true; }); function toggleSelect(id: string) { setSelected(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; }); } function toggleAll() { if (selected.size === filtered.length) { setSelected(new Set()); } else { setSelected(new Set(filtered.map(o => o.id))); } } async function handleBulkFulfill() { if (selected.size === 0) return; setBulkLoading(true); const result = await bulkFulfillWholesaleOrders(Array.from(selected)); setBulkLoading(false); if (result.success) { onMsg("success", `${result.count} order(s) marked as fulfilled.`); setSelected(new Set()); onRefresh(); } else { onMsg("error", result.error ?? "Bulk fulfill failed."); } } async function handleBulkDeposit() { if (selected.size === 0 || !bulkDepAmount) return; setBulkLoading(true); const result = await bulkRecordWholesaleDeposit(Array.from(selected), Number(bulkDepAmount), bulkDepMethod); setBulkLoading(false); if (result.success) { onMsg("success", `Deposit recorded on ${result.count} order(s).`); setSelected(new Set()); setShowBulkDep(false); setBulkDepAmount(""); onRefresh(); } else { onMsg("error", result.error ?? "Bulk deposit failed."); } } async function handleFulfill(orderId: string) { setFulfilling(orderId); const result = await markWholesaleOrderFulfilled(orderId); setFulfilling(null); if (result.success) { onMsg("success", "Order marked as fulfilled."); onRefresh(); // Enqueue order fulfilled notification const order = orders.find(o => o.id === orderId); const customer = order ? customers.find(c => c.email === order.customer_email) : null; if (order && customer) { enqueueWholesaleNotification({ brandId: brandId, customerId: customer.id, orderId, type: "order_fulfilled", emailTo: customer.email, subject: `Order ${order.invoice_number ?? orderId.slice(0, 8)} Ready for Pickup`, bodyHtml: `

Your Order is Ready!

Hi ${customer.company_name},

Your wholesale order ${order.invoice_number ?? orderId.slice(0, 8)} has been fulfilled and is ready for pickup${order.anticipated_pickup_date ? ` on ${order.anticipated_pickup_date}` : ""}.

Thank you for your business!

`, bodyText: `Order ${order.invoice_number ?? orderId.slice(0, 8)} has been fulfilled and is ready for pickup${order.anticipated_pickup_date ? ` on ${order.anticipated_pickup_date}` : ""}. Thank you!`, }); // Also notify the admin const settings = await getWholesaleSettings(brandId); const adminEmail = settings?.notification_email ?? settings?.from_email ?? settings?.invoice_business_email ?? null; if (adminEmail) { enqueueWholesaleNotification({ brandId: brandId, customerId: customer.id, orderId, type: "order_fulfilled", emailTo: adminEmail, subject: `[Admin] Order Fulfilled — ${order.invoice_number ?? orderId.slice(0, 8)}`, bodyHtml: `

Order Fulfilled

Order ${order.invoice_number ?? orderId.slice(0, 8)} for ${customer.company_name} has been marked as fulfilled.

${order.anticipated_pickup_date ? `

Pickup Date: ${order.anticipated_pickup_date}

` : ""} `, bodyText: `Order ${order.invoice_number ?? orderId.slice(0, 8)} for ${customer.company_name} has been fulfilled.`, }); } // Trigger notification send (fire-and-forget) fetch(`/api/wholesale/notifications/send`, { method: "POST" }).catch(() => {}); } } else { onMsg("error", result.error ?? "Failed to fulfill order."); } } // Close actions dropdown when clicking outside useEffect(() => { function handleClick(e: MouseEvent) { if (!(e.target as Element).closest(".actions-cell")) { setOpenActions(null); } } document.addEventListener("click", handleClick); return () => document.removeEventListener("click", handleClick); }, []); function toggleActions(orderId: string, e: React.MouseEvent) { e.stopPropagation(); setOpenActions(prev => prev === orderId ? null : orderId); } async function handleRecordDeposit(orderId: string) { const result = await recordWholesaleDeposit(orderId, Number(depAmount), depMethod); setShowDepForm(null); setDepAmount(""); if (result.success) { onMsg("success", "Deposit recorded."); onRefresh(); } else { onMsg("error", result.error ?? "Failed to record deposit."); } } async function handleCancelOrder(orderId: string) { if (!confirm("Cancel this order? This cannot be undone.")) return; const result = await updateWholesaleOrderStatus(orderId, "cancelled"); if (result.success) { onMsg("success", "Order cancelled."); onRefresh(); } else { onMsg("error", result.error ?? "Failed to cancel order."); } } async function handleDeleteOrder(orderId: string) { if (!confirm("Delete this order? Fulfilled and paid orders cannot be deleted. This cannot be undone.")) return; setDeleting(orderId); const result = await deleteWholesaleOrder(orderId); setDeleting(null); if (result.success) { onMsg("success", "Order deleted."); onRefresh(); } else { onMsg("error", result.error ?? "Failed to delete order."); } } return (

Wholesale Orders ({orders.length})

{/* Filter bar */}
setDateFrom(e.target.value)} className="rounded-lg border border-[var(--admin-border)] px-3 py-1.5 text-sm outline-none focus:border-[var(--admin-accent)]" />
setDateTo(e.target.value)} className="rounded-lg border border-[var(--admin-border)] px-3 py-1.5 text-sm outline-none focus:border-[var(--admin-accent)]" />
setSearchQuery(e.target.value)} onClear={() => setSearchQuery("")} placeholder="Invoice, customer, email..." />
{filtered.length !== orders.length && (
{filtered.length} of {orders.length}
)}
{/* Bulk actions */} {selected.size > 0 && (
{selected.size} selected {bulkLoading ? "..." : "Bulk Fulfill"} setShowBulkDep(true)}> Bulk Record Deposit
)} {/* Manifest section */}

Load Manifest

Generate a printable manifest for pending/active orders.

{ setManifestLoading(true); const pending = filtered.filter(o => o.fulfillment_status !== "fulfilled"); if (pending.length === 0) { setManifestLoading(false); return; } const html = await fetch("/api/wholesale/manifest", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ brandId, orders: pending }), }).then(r => r.text()); openHtmlInPopup(html); setManifestLoading(false); }} disabled={manifestLoading} isLoading={manifestLoading} > {manifestLoading ? "Generating..." : "Generate Manifest"}
{filtered.length === 0 ? ( ) : filtered.map(o => ( ))}
0} onChange={toggleAll} className="rounded" /> Invoice Customer Pickup Date Subtotal Deposit Balance Status Payment Actions
No orders match the current filters.
toggleSelect(o.id)} className="rounded" /> {o.invoice_number ?? "—"} {o.company_name} {o.anticipated_pickup_date ?? "—"} ${Number(o.subtotal).toFixed(2)} ${Number(o.deposit_paid).toFixed(2)} / ${Number(o.deposit_required).toFixed(2)} 0 ? "text-[var(--admin-warning)] font-medium" : "text-[var(--admin-accent)]"}> ${Number(o.balance_due).toFixed(2)} {o.payment_status}
{/* Primary: Mark Fulfilled — only when order is not yet fulfilled */} {o.fulfillment_status !== "fulfilled" && ( !fulfilling && handleFulfill(o.id)} disabled={fulfilling === o.id} isLoading={fulfilling === o.id} > {fulfilling === o.id ? "..." : "Fulfill"} )} {/* Primary: Record Deposit — when there's a balance due */} {Number(o.balance_due) > 0 && o.fulfillment_status !== "fulfilled" && ( setShowDepForm(o.id)} > Deposit )} {/* Invoice download — always visible as icon button */} {/* ⋮ Actions dropdown — opens upward to avoid table cutoff */}
{openActions === o.id && (
e.stopPropagation()} >
{o.status !== "cancelled" && o.fulfillment_status !== "fulfilled" && ( )} {o.fulfillment_status !== "fulfilled" && ( )}
)}
{/* Deposit modal */} {showDepForm && (

Record Deposit

setDepAmount(e.target.value)} className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" step="0.01" />
handleRecordDeposit(showDepForm)} variant="primary"> Record Deposit setShowDepForm(null)} variant="secondary"> Cancel
)} {/* Bulk Deposit modal */} {showBulkDep && (

Bulk Record Deposit ({selected.size} orders)

setBulkDepAmount(e.target.value)} className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" step="0.01" placeholder="0.00" />
{bulkLoading ? "Recording..." : "Record on All"} setShowBulkDep(false)} variant="secondary"> Cancel
)} {/* Order Details modal */} {showViewOrder && ( setShowViewOrder(null)} onFulfill={handleFulfill} onRecordDeposit={(id) => { setShowViewOrder(null); setShowDepForm(id); }} fulfilling={fulfilling} /> )}
); }