"use client"; import { useState, useEffect } from "react"; import { useParams, useRouter } from "next/navigation"; import useSWR from "swr"; import { Card, CardContent, CardHeader, CardTitle, } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Badge } from "@/components/ui/badge"; import { Textarea } from "@/components/ui/textarea"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Progress } from "@/components/ui/progress"; import { ArrowLeft, Plus, Trash2, Package, Save, Euro, Layers, Truck, FileDown } from "lucide-react"; import { toast } from "sonner"; import { formatCurrency } from "@/lib/format"; import type { Product, PurchaseOrder, PurchaseOrderItem } from "@/lib/types"; const fetcher = (url: string) => fetch(url).then((r) => r.json()); interface OrderLineItem { product_id: string; quantity: string; unit_price: string; notes: string; } const statusConfig: Record = { draft: { label: "Draft", variant: "secondary" }, confirmed: { label: "Confirmed", variant: "default" }, in_production: { label: "In Production", variant: "outline" }, shipped: { label: "Shipped", variant: "default" }, completed: { label: "Completed", variant: "default" }, cancelled: { label: "Cancelled", variant: "destructive" }, }; export default function OrderDetailPage() { const params = useParams(); const router = useRouter(); const customerId = params.id as string; const orderId = params.orderId as string; const [loading, setLoading] = useState(false); const [editing, setEditing] = useState(false); const [form, setForm] = useState({ po_number: "", customer_ref: "", status: "draft", order_date: "", due_date: "", notes: "", }); const [items, setItems] = useState([]); const { data: orderData, mutate } = useSWR< PurchaseOrder & { items: PurchaseOrderItem[] } >(`/api/orders/${orderId}`, fetcher); const { data: products = [] } = useSWR("/api/products", fetcher); useEffect(() => { if (orderData) { setForm({ po_number: orderData.po_number || "", customer_ref: orderData.customer_ref || "", status: orderData.status || "draft", order_date: orderData.order_date ? new Date(orderData.order_date).toISOString().slice(0, 10) : "", due_date: orderData.due_date ? new Date(orderData.due_date).toISOString().slice(0, 10) : "", notes: orderData.notes || "", }); setItems( (orderData.items || []).map((item) => ({ product_id: String(item.product_id), quantity: String(item.quantity), unit_price: String(item.unit_price), notes: item.notes || "", })) ); } }, [orderData]); function addLine() { setItems([ ...items, { product_id: "", quantity: "1", unit_price: "", notes: "" }, ]); } function removeLine(index: number) { if (items.length <= 1) return; setItems(items.filter((_, i) => i !== index)); } function updateLine(index: number, field: keyof OrderLineItem, value: string) { const updated = [...items]; updated[index] = { ...updated[index], [field]: value }; setItems(updated); } function getLineTotal(item: OrderLineItem) { return (Number(item.quantity) || 0) * (Number(item.unit_price) || 0); } const orderTotal = items.reduce((sum, item) => sum + getLineTotal(item), 0); async function handleDownloadPO() { if (!orderData) return; const { generatePoPdf } = await import("@/lib/generate-po-pdf"); const doc = generatePoPdf({ po_number: orderData.po_number, order_date: orderData.order_date, due_date: orderData.due_date, customer_ref: orderData.customer_ref, status: orderData.status, customer_name: orderData.customer_name || "", customer_code: orderData.customer_code || "", customer_email: (orderData as Record).customer_email as string | null, customer_phone: (orderData as Record).customer_phone as string | null, customer_address: (orderData as Record).customer_address as string | null, customer_contact: (orderData as Record).customer_contact as string | null, notes: orderData.notes, items: (orderData.items || []).map((item) => ({ product_name: item.product_name || "", product_sku: item.product_sku || "", quantity: Number(item.quantity), unit_price: Number(item.unit_price), line_total: Number(item.line_total || 0), notes: item.notes, })), }); doc.save(`${orderData.po_number}.pdf`); } async function handleSave() { setLoading(true); try { const validItems = items.filter((item) => item.product_id); const res = await fetch(`/api/orders/${orderId}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ customer_id: Number(customerId), ...form, due_date: form.due_date || null, items: validItems.map((item) => ({ product_id: Number(item.product_id), quantity: Number(item.quantity) || 1, unit_price: Number(item.unit_price) || 0, notes: item.notes || null, })), }), }); if (!res.ok) throw new Error("Failed to update"); toast.success("Purchase order updated"); mutate(); setEditing(false); } catch { toast.error("Failed to update order"); } finally { setLoading(false); } } if (!orderData) { return (

Loading order...

); } const sc = statusConfig[orderData.status] || statusConfig.draft; // VIEW MODE if (!editing) { return (

{orderData.po_number}

{sc.label}

Order for {orderData.customer_name} ({orderData.customer_code})

{/* Summary Cards */} {(() => { const itemsList = orderData.items || []; const totalOrdered = itemsList.reduce((s, i) => s + Number(i.quantity), 0); const totalShipped = itemsList.reduce((s, i) => s + Number(i.shipped_quantity || 0), 0); const totalRemaining = totalOrdered - totalShipped; const fulfillmentPct = totalOrdered > 0 ? Math.round((totalShipped / totalOrdered) * 100) : 0; const isOverdue = orderData.due_date && new Date(orderData.due_date) < new Date() && fulfillmentPct < 100; return (
Order Date

{new Date(orderData.order_date).toLocaleDateString()}

Due Date

{orderData.due_date ? new Date(orderData.due_date).toLocaleDateString() : "Not set"} {isOverdue && ( (overdue) )}

Order Total

{formatCurrency( itemsList.reduce((s, i) => s + Number(i.line_total || 0), 0) )}

{itemsList.length} line item{itemsList.length !== 1 ? "s" : ""}

Units Remaining

0 ? "text-foreground" : "text-accent"}> {totalRemaining} / {totalOrdered}

{totalShipped} shipped

Fulfillment

{fulfillmentPct}%

); })()} {orderData.customer_ref && (
Customer Reference:{" "} {orderData.customer_ref}
)} {orderData.notes && ( {orderData.notes} )} {/* Line Items Table */} Order Items # Product SKU Ordered Shipped Remaining Fulfillment Unit Price Line Total Notes {(orderData.items || []).map( (item: PurchaseOrderItem, idx: number) => { const ordered = Number(item.quantity); const shipped = Number(item.shipped_quantity || 0); const remaining = ordered - shipped; const pct = ordered > 0 ? Math.round((shipped / ordered) * 100) : 0; return ( {idx + 1} {item.product_name} {item.product_sku} {ordered.toLocaleString()} {shipped.toLocaleString()} 0 ? "text-foreground" : "text-accent"}> {remaining.toLocaleString()}
{shipped}/{ordered} {pct}%
{formatCurrency(Number(item.unit_price))} {formatCurrency(Number(item.line_total || 0))} {item.notes || "-"}
); } )}
); } // EDIT MODE return (

Edit Purchase Order

{orderData.po_number} - {orderData.customer_name}

Order Details
setForm({ ...form, po_number: e.target.value }) } className="font-mono" />
setForm({ ...form, customer_ref: e.target.value }) } />
setForm({ ...form, order_date: e.target.value }) } />
setForm({ ...form, due_date: e.target.value }) } />