"use client"; import { useState } from "react"; import type { OrderItemData } from "@/lib/types"; // 6 color presets for customer grouping const CUSTOMER_COLORS = [ { border: "border-l-blue-500", bg: "bg-blue-50", text: "text-blue-800" }, { border: "border-l-emerald-500", bg: "bg-emerald-50", text: "text-emerald-800" }, { border: "border-l-violet-500", bg: "bg-violet-50", text: "text-violet-800" }, { border: "border-l-orange-500", bg: "bg-orange-50", text: "text-orange-800" }, { border: "border-l-teal-500", bg: "bg-teal-50", text: "text-teal-800" }, { border: "border-l-pink-500", bg: "bg-pink-50", text: "text-pink-800" }, ]; function getColor(index: number) { return CUSTOMER_COLORS[index % CUSTOMER_COLORS.length]; } // Build a color map from customer list function buildColorMap(customers: { id: string; name: string }[]) { const map: Record = {}; customers.forEach((c, i) => { map[c.id] = i; }); return map; } // Group items by customerId function groupItems(items: OrderItemData[]) { const groups: Record = {}; const ungrouped: OrderItemData[] = []; for (const item of items) { if (item.customerId) { if (!groups[item.customerId]) groups[item.customerId] = []; groups[item.customerId].push(item); } else { ungrouped.push(item); } } return { groups, ungrouped }; } // Inline edit form for an item function ItemEditForm({ item, customers, onSave, onCancel, }: { item: OrderItemData; customers: { id: string; name: string }[]; onSave: (item: OrderItemData) => void; onCancel: () => void; }) { const [form, setForm] = useState({ ...item }); function update(field: string, value: string | number) { const next = { ...form, [field]: value }; // Recompute prices next.netPrice = (next.initPrice + next.initPrice * next.taxRatio) * next.quantity; next.myNetPrice = (next.myPrice + next.myPrice * next.taxRatio) * next.quantity; next.finalPrice = next.netPrice; setForm(next); } const inputClass = "border-2 border-border rounded-lg px-2 py-1.5 text-sm bg-bg text-fg focus:outline-none focus:border-accent w-full"; const labelClass = "text-xs text-muted font-medium mb-0.5 block"; return (
update("itemName", e.target.value)} className={inputClass} />
update("initPrice", parseFloat(e.target.value) || 0)} className={inputClass} />
update("myPrice", parseFloat(e.target.value) || 0)} className={inputClass} />
update("taxRatio", parseFloat(e.target.value) || 0)} className={inputClass} />
update("quantity", parseInt(e.target.value) || 1)} className={inputClass} />
); } // Add item form function AddItemForm({ customers, onAdd, disabled, }: { customers: { id: string; name: string }[]; onAdd: (item: OrderItemData) => void; disabled: boolean; }) { const [form, setForm] = useState({ customerId: customers[0]?.id ?? "", itemName: "", initPrice: 0, myPrice: 0, taxRatio: 0.16, quantity: 1, }); function update(field: string, value: string | number) { setForm((prev) => ({ ...prev, [field]: value })); } function handleAdd() { if (!form.itemName.trim()) return; const netPrice = (form.initPrice + form.initPrice * form.taxRatio) * form.quantity; const myNetPrice = (form.myPrice + form.myPrice * form.taxRatio) * form.quantity; onAdd({ id: crypto.randomUUID(), orderId: "", customerId: form.customerId || null, customerName: customers.find((c) => c.id === form.customerId)?.name ?? null, itemName: form.itemName, initPrice: form.initPrice, myPrice: form.myPrice, taxRatio: form.taxRatio, quantity: form.quantity, netPrice, myNetPrice, finalPrice: netPrice, }); setForm({ customerId: customers[0]?.id ?? "", itemName: "", initPrice: 0, myPrice: 0, taxRatio: 0.16, quantity: 1 }); } const inputClass = "border-2 border-border rounded-lg px-2 py-1.5 text-sm bg-bg text-fg focus:outline-none focus:border-accent w-full"; const labelClass = "text-xs text-muted font-medium mb-0.5 block"; return (

Add New Item

update("itemName", e.target.value)} placeholder="Product name" className={inputClass} disabled={disabled} />
update("initPrice", parseFloat(e.target.value) || 0)} className={inputClass} disabled={disabled} />
update("myPrice", parseFloat(e.target.value) || 0)} className={inputClass} disabled={disabled} />
update("taxRatio", parseFloat(e.target.value) || 0)} className={inputClass} disabled={disabled} /> update("quantity", parseInt(e.target.value) || 1)} className={inputClass} disabled={disabled} />
); } // Main ItemTable component export function ItemTable({ items, customers, onItemsChange, disabled = false, }: { items: OrderItemData[]; customers: { id: string; name: string }[]; onItemsChange: (items: OrderItemData[]) => void; disabled?: boolean; }) { const [editingId, setEditingId] = useState(null); const colorMap = buildColorMap(customers); const { groups, ungrouped } = groupItems(items); function handleAdd(newItem: OrderItemData) { onItemsChange([...items, newItem]); } function handleRemove(id: string) { onItemsChange(items.filter((i) => i.id !== id)); if (editingId === id) setEditingId(null); } function handleEditSave(updated: OrderItemData) { onItemsChange(items.map((i) => (i.id === updated.id ? updated : i))); setEditingId(null); } // Get customer name by id function getCustomerName(id: string) { return customers.find((c) => c.id === id)?.name ?? "Unknown"; } // Render a single item row (desktop table row) function TableRow({ item, colorIndex }: { item: OrderItemData; colorIndex: number | null }) { const color = colorIndex !== null ? getColor(colorIndex) : null; const isEditing = editingId === item.id; return ( <> {item.itemName || "—"} ${item.initPrice.toFixed(2)} ${item.myPrice.toFixed(2)} {item.quantity} ${item.netPrice.toFixed(2)} ${item.finalPrice.toFixed(2)} {!disabled && (
)} {isEditing && ( setEditingId(null)} /> )} ); } // Render a single item card (mobile) function MobileCard({ item, colorIndex }: { item: OrderItemData; colorIndex: number | null }) { const color = colorIndex !== null ? getColor(colorIndex) : null; const isEditing = editingId === item.id; return (

{item.itemName || "—"}

{colorIndex !== null && (

{getCustomerName(item.customerId!)}

)}

${item.finalPrice.toFixed(2)}

Init: ${item.initPrice.toFixed(2)}
My: ${item.myPrice.toFixed(2)}
Qty: {item.quantity}
{!disabled && (
)}
{isEditing && (
setEditingId(null)} />
)}
); } // Render items for a customer group function CustomerGroup({ customerId, groupItems: gi }: { customerId: string; groupItems: OrderItemData[] }) { const colorIndex = colorMap[customerId] ?? 0; const color = getColor(colorIndex); const name = getCustomerName(customerId); return (
{name} ({gi.length} item{gi.length !== 1 ? "s" : ""})
{/* Desktop table */}
{gi.map((item) => )}
Item Init Price My Price Qty Net Final Actions
{/* Mobile cards */}
{gi.map((item) => )}
); } return (
{/* Add item form */} {!disabled && ( )} {/* Items grouped by customer */} {items.length === 0 ? (
No items yet. Add one above.
) : ( <> {/* Customer groups */} {Object.entries(groups).map(([customerId, gi]) => ( ))} {/* Ungrouped items */} {ungrouped.length > 0 && (
Unassigned ({ungrouped.length} item{ungrouped.length !== 1 ? "s" : ""})
{ungrouped.map((item) => )}
Item Init Price My Price Qty Net Final Actions
{ungrouped.map((item) => )}
)} )}
); }