'use client'; import React, { useState, useEffect } from 'react'; import { ShoppingBag, Truck, CheckCircle2, Clock, Phone, MapPin, Search, MessageCircle, Send, ShieldCheck, Building2, Navigation, ExternalLink, DollarSign, Layers, Sparkles, Compass, Check, AlertCircle, Edit3, Trash2, Plus, X, CreditCard, User, FileText } from 'lucide-react'; import { Order, OrderItem, Supplier, City } from '@/types'; import { formatCOP } from '@/lib/pricing-engine'; import { initClientTemplates, renderClientTemplate } from '@/lib/templates-client'; export default function AdminOrdersPage() { const [orders, setOrders] = useState([]); const [suppliers, setSuppliers] = useState([]); const [cities, setCities] = useState([]); const [activeTab, setActiveTab] = useState<'individual' | 'suppliers' | 'routes'>('individual'); const [search, setSearch] = useState(''); const [statusFilter, setStatusFilter] = useState('all'); const [toastMessage, setToastMessage] = useState(null); // Edit Order Modal State const [isEditModalOpen, setIsEditModalOpen] = useState(false); const [editingOrder, setEditingOrder] = useState(null); const [editCustomerName, setEditCustomerName] = useState(''); const [editCustomerPhone, setEditCustomerPhone] = useState(''); const [editAddress, setEditAddress] = useState(''); const [editCityId, setEditCityId] = useState('city-cali'); const [editCityName, setEditCityName] = useState('Cali'); const [editStatus, setEditStatus] = useState('pending'); const [editPaymentMethodName, setEditPaymentMethodName] = useState(''); const [editTotalAmount, setEditTotalAmount] = useState(0); const [editDepositAmount, setEditDepositAmount] = useState(10000); const [editShippingCost, setEditShippingCost] = useState(0); const [editNotes, setEditNotes] = useState(''); const [editItems, setEditItems] = useState([]); const [isSavingOrder, setIsSavingOrder] = useState(false); // Delete Order Confirmation Modal State const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [orderToDelete, setOrderToDelete] = useState(null); useEffect(() => { fetchOrders(); fetchSuppliers(); fetchCities(); initClientTemplates(); }, []); const fetchOrders = async () => { try { const r = await fetch('/api/orders'); const d = await r.json(); if (d.success && d.orders) { setOrders(d.orders); } } catch (e) { console.error('Error loading orders:', e); } }; const fetchSuppliers = async () => { try { const r = await fetch('/api/suppliers?all=true'); const d = await r.json(); if (d.success && d.suppliers) { setSuppliers(d.suppliers); } } catch (e) { console.error('Error loading suppliers:', e); } }; const fetchCities = async () => { try { const r = await fetch('/api/cities'); const d = await r.json(); if (d.success && d.cities) { setCities(d.cities); } } catch (e) { console.error('Error loading cities:', e); } }; const handleStatusChange = async (orderId: string, newStatus: Order['status']) => { setOrders((prev) => prev.map((o) => (o.id === orderId ? { ...o, status: newStatus } : o)) ); try { await fetch('/api/orders', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: orderId, status: newStatus }), }); setToastMessage(`Estado de la orden #${orderId} actualizado a "${newStatus}".`); setTimeout(() => setToastMessage(null), 4000); } catch (e) { console.error(e); } }; // Open Edit Order Modal const handleOpenEditModal = (order: Order) => { setEditingOrder(order); setEditCustomerName(order.customerName); setEditCustomerPhone(order.customerPhone); setEditAddress(order.address); setEditCityId(order.cityId); setEditCityName(order.cityName); setEditStatus(order.status); setEditPaymentMethodName(order.paymentMethodName || 'Contraentrega'); setEditTotalAmount(order.totalAmount); setEditDepositAmount(order.depositAmount); setEditShippingCost(order.shippingCost || 0); setEditNotes(order.notes || ''); setEditItems([...order.items]); setIsEditModalOpen(true); }; // Handle Edit Item in Modal const handleItemChange = (index: number, field: keyof OrderItem, value: any) => { const updated = [...editItems]; updated[index] = { ...updated[index], [field]: value }; // If changing supplierId, auto update supplierName and supplierPhone if (field === 'supplierName') { const foundSup = suppliers.find((s) => s.name === value || s.id === value); if (foundSup) { updated[index].supplierName = foundSup.name; updated[index].supplierPhone = foundSup.phone; } } setEditItems(updated); // Auto update total amount if prices/quantities change if (field === 'price' || field === 'quantity') { const newSubtotal = updated.reduce((acc, curr) => acc + (curr.price || 0) * (curr.quantity || 1), 0); setEditTotalAmount(newSubtotal + editShippingCost); } }; const handleRemoveItemFromEdit = (index: number) => { const updated = editItems.filter((_, idx) => idx !== index); setEditItems(updated); const newSubtotal = updated.reduce((acc, curr) => acc + (curr.price || 0) * (curr.quantity || 1), 0); setEditTotalAmount(newSubtotal + editShippingCost); }; const handleAddItemToEdit = () => { const defaultSup = suppliers[0] || { name: 'DIFUSIÓN JOVIC STORE', phone: '573167455877' }; const newItem: OrderItem = { productId: `prod-manual-${Date.now()}`, productName: 'Nuevo Modelo / Zapatilla', brand: 'Nike', size: '39', quantity: 1, price: 125000, supplierPrice: 90000, supplierName: defaultSup.name, supplierPhone: defaultSup.phone, image: 'https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?auto=format&fit=crop&w=800&q=80', }; const updated = [...editItems, newItem]; setEditItems(updated); const newSubtotal = updated.reduce((acc, curr) => acc + (curr.price || 0) * (curr.quantity || 1), 0); setEditTotalAmount(newSubtotal + editShippingCost); }; // Save Order Updates const handleSaveOrderEdits = async (e: React.FormEvent) => { e.preventDefault(); if (!editingOrder) return; setIsSavingOrder(true); const balanceDue = editTotalAmount - editDepositAmount; const payload: Partial = { id: editingOrder.id, customerName: editCustomerName, customerPhone: editCustomerPhone, address: editAddress, cityId: editCityId, cityName: editCityName, status: editStatus, paymentMethodName: editPaymentMethodName, totalAmount: editTotalAmount, depositAmount: editDepositAmount, shippingCost: editShippingCost, balanceDue: balanceDue > 0 ? balanceDue : 0, items: editItems, notes: editNotes, }; try { const res = await fetch('/api/orders', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); const data = await res.json(); if (data.success) { setOrders((prev) => prev.map((o) => (o.id === editingOrder.id ? { ...o, ...payload } as Order : o)) ); setToastMessage(`✅ Orden #${editingOrder.id} modificada y guardada correctamente.`); setTimeout(() => setToastMessage(null), 4000); setIsEditModalOpen(false); } } catch (err) { console.error('Error saving order edits:', err); } finally { setIsSavingOrder(false); } }; // Delete Order const handleOpenDeleteModal = (order: Order) => { setOrderToDelete(order); setIsDeleteModalOpen(true); }; const handleConfirmDeleteOrder = async () => { if (!orderToDelete) return; const targetId = orderToDelete.id; try { const res = await fetch(`/api/orders?id=${targetId}`, { method: 'DELETE', }); const data = await res.json(); if (data.success) { setOrders((prev) => prev.filter((o) => o.id !== targetId)); setToastMessage(`🗑️ Orden #${targetId} eliminada correctamente.`); setTimeout(() => setToastMessage(null), 4000); } } catch (err) { console.error('Error deleting order:', err); } finally { setIsDeleteModalOpen(false); setOrderToDelete(null); } }; // 1. Notificar separación individual a proveedor const handleNotifySingleSupplier = (order: Order, item: OrderItem) => { const supplierPhone = item.supplierPhone || '573167455877'; const supplierName = item.supplierName || 'Proveedor Mayorista'; const supplierCost = item.supplierPrice ? formatCOP(item.supplierPrice) : 'Precio Mayorista'; const message = renderClientTemplate('supplier_single_separation', { proveedor_nombre: supplierName, zapatilla_nombre: item.productName, talla: item.size, cantidad: item.quantity, costo_proveedor: supplierCost, orden_id: order.id, ciudad_destino: order.cityName, }); const url = `https://wa.me/${supplierPhone}?text=${encodeURIComponent(message)}`; window.open(url, '_blank'); if (order.status === 'pending' || order.status === 'deposit_received') { handleStatusChange(order.id, 'supplier_notified'); } }; // 2. Enviar Manifiesto Consolidado por Proveedor vía WhatsApp const handleSendSupplierBatchManifest = (supplierName: string, supplierPhone: string, itemsList: { order: Order; item: OrderItem }[]) => { const totalPairs = itemsList.reduce((acc, curr) => acc + curr.item.quantity, 0); const totalCost = itemsList.reduce((acc, curr) => acc + (curr.item.supplierPrice || 0) * curr.item.quantity, 0); const itemsSummary = itemsList.map((entry, idx) => `${idx + 1}. *${entry.item.productName}*\n • Talla: *${entry.item.size}* (x${entry.item.quantity})\n • Costo Base: ${formatCOP(entry.item.supplierPrice || 0)}\n • Orden Cliente: #${entry.order.id} (${entry.order.cityName})` ).join('\n\n'); const text = renderClientTemplate('supplier_batch_manifest', { proveedor_nombre: supplierName, fecha: new Date().toLocaleDateString('es-CO'), total_pares: totalPairs, total_costo_proveedor: formatCOP(totalCost), lista_pares_separacion: itemsSummary, }); const cleanPhone = supplierPhone.startsWith('57') ? supplierPhone : `57${supplierPhone}`; window.open(`https://wa.me/${cleanPhone}?text=${encodeURIComponent(text)}`, '_blank'); setToastMessage(`📲 Manifiesto de ${totalPairs} pares enviado a ${supplierName}.`); }; const handleOpenWaze = (address: string, city: string) => { const query = encodeURIComponent(`${address}, ${city}, Colombia`); const wazeUrl = `https://waze.com/ul?q=${query}&navigate=yes`; window.open(wazeUrl, '_blank'); }; const handleOpenGoogleMaps = (address: string, city: string) => { const query = encodeURIComponent(`${address}, ${city}, Colombia`); const mapsUrl = `https://www.google.com/maps/search/?api=1&query=${query}`; window.open(mapsUrl, '_blank'); }; const handleNotifyCustomerOnTheWay = (order: Order) => { const cleanPhone = order.customerPhone.startsWith('57') ? order.customerPhone : `57${order.customerPhone}`; const text = renderClientTemplate('order_on_the_way', { cliente_nombre: order.customerName, orden_id: order.id, cliente_direccion: order.address, cliente_ciudad: order.cityName, saldo_contraentrega: formatCOP(order.balanceDue), metodo_pago: order.paymentMethodName || 'Contraentrega', tienda_nombre: 'DripZone Luxury', }); window.open(`https://wa.me/${cleanPhone}?text=${encodeURIComponent(text)}`, '_blank'); handleStatusChange(order.id, 'dispatched'); }; const getStatusBadge = (status: Order['status']) => { switch (status) { case 'pending': return Pendiente Confirmar; case 'deposit_received': return Separación $10k Pagada; case 'supplier_notified': return Proveedor Notificado; case 'dispatched': return En Ruta con Mensajero; case 'delivered': return Entregado & Cobrado; case 'cancelled': return Cancelado; } }; const filteredOrders = orders.filter((o) => { const matchesSearch = o.customerName.toLowerCase().includes(search.toLowerCase()) || o.customerPhone.includes(search) || o.address.toLowerCase().includes(search.toLowerCase()) || o.cityName.toLowerCase().includes(search.toLowerCase()) || o.id.toLowerCase().includes(search.toLowerCase()); if (!matchesSearch) return false; if (statusFilter !== 'all' && o.status !== statusFilter) return false; return true; }); // Consolidado por Proveedor const supplierGroups = orders.reduce((acc, order) => { order.items.forEach((item) => { const suppName = item.supplierName || '💎 DIFUSIÓN JOVIC STORE COLOMBIA'; const suppPhone = item.supplierPhone || '573167455877'; const matchingSupplier = suppliers.find((s) => s.name.toLowerCase() === suppName.toLowerCase()); const isActiveSup = matchingSupplier ? matchingSupplier.isActive !== false : true; if (!acc[suppName]) { acc[suppName] = { name: suppName, phone: suppPhone, isActive: isActiveSup, entries: [], totalPairs: 0, totalCost: 0, }; } acc[suppName].entries.push({ order, item }); acc[suppName].totalPairs += item.quantity; acc[suppName].totalCost += (item.supplierPrice || 0) * item.quantity; }); return acc; }, {} as Record); const pendingDeliveries = orders.filter((o) => o.status !== 'delivered' && o.status !== 'cancelled'); const totalBalanceToCollect = pendingDeliveries.reduce((acc, curr) => acc + curr.balanceDue, 0); return (
{/* Header */}

Centro Logístico: Pedidos, Proveedores & Rutas Waze

Gestión completa de pedidos (editar datos, cambiar tallas, precios o eliminar), consolidación con proveedores y navegación Waze en 1 clic.

{toastMessage && (
{toastMessage}
)} {/* Main Tabs Navigation */}
{/* TAB 1: INDIVIDUAL ORDERS LIST */} {activeTab === 'individual' && (
{['all', 'pending', 'deposit_received', 'supplier_notified', 'dispatched', 'delivered'].map((st) => ( ))}
setSearch(e.target.value)} className="w-full bg-neutral-900 border border-neutral-800 rounded-xl px-3.5 py-2 text-xs text-white outline-hidden focus:border-purple-400" />
{filteredOrders.length === 0 ? (
No hay pedidos que coincidan con los filtros.
) : ( filteredOrders.map((order) => (
{/* Top bar */}
Orden #{order.id} {getStatusBadge(order.status)} {order.createdAt}

Cliente & Destino

{order.customerName}

{order.customerPhone}

{order.address} ({order.cityName})

{order.notes && (

Nota: {order.notes}

)}

Resumen de Cobro

Total Venta: {formatCOP(order.totalAmount)}
Anticipo / Separación: -{formatCOP(order.depositAmount)}
Saldo Contraentrega: {formatCOP(order.balanceDue)}

Pares en esta orden:

{order.items.map((item, idx) => (

{item.productName}

Talla: {item.size} | Cantidad: {item.quantity} | Costo: {formatCOP(item.supplierPrice || 0)} | Proveedor: {item.supplierName || 'Bodega Principal'}

))}
)) )}
)} {/* TAB 2: CONSOLIDADO AGRUPADO POR PROVEEDOR */} {activeTab === 'suppliers' && (
Consolidado Automático de Compras & Separaciones

Agrupación automática por Proveedor Mayorista. Si un proveedor ha sido desactivado, sus pedidos y cuentas permanecen 100% calculados e intactos en el historial.

{Object.values(supplierGroups).map((group) => (
Proveedor Mayorista {!group.isActive && ( ⚪ Inactivo / Histórico )}

{group.name}

WhatsApp Proveedor: {group.phone}

Total Pares

{group.totalPairs} par(es)

Total a Pagar

{formatCOP(group.totalCost)}

{group.entries.map((entry, idx) => ( ))}
Modelo / Zapatilla Talla Cant. Costo Base Orden Cliente Destino Estado Orden
{entry.item.productName} {entry.item.size} {entry.item.quantity} {formatCOP(entry.item.supplierPrice || 0)} #{entry.order.id} {entry.order.cityName} {getStatusBadge(entry.order.status)}
))}
)} {/* TAB 3: RUTAS DE ENTREGA ORGANIZADAS POR DIRECCIONES & WAZE */} {activeTab === 'routes' && (
Hoja de Ruta & Navegación GPS (Waze / Google Maps)

Entregas contraentrega secuenciadas por zona geográfica. Presiona "Abrir en Waze" para iniciar la navegación instantánea.

Paradas / Entregas

{pendingDeliveries.length} Paradas

Total a Recaudar

{formatCOP(totalBalanceToCollect)}

{pendingDeliveries.length === 0 ? (
🎉 No hay entregas pendientes por despachar en este momento.
) : ( pendingDeliveries.map((delivery, index) => (
Parada #{index + 1}
{delivery.customerName} • Orden #{delivery.id} {getStatusBadge(delivery.status)}

{delivery.address} ({delivery.cityName})

{delivery.customerPhone} • {delivery.items.map((i) => `${i.productName} (Talla ${i.size})`).join(', ')}
{delivery.notes && (

Nota: {delivery.notes}

)}

Cobrar Contraentrega

{formatCOP(delivery.balanceDue)}

)) )}
)} {/* MODAL: EDITAR / MODIFICAR PEDIDO */} {isEditModalOpen && editingOrder && (

Modificar Orden #{editingOrder.id}

{/* Customer details */}
setEditCustomerName(e.target.value)} className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white outline-hidden focus:border-purple-400 font-bold" />
setEditCustomerPhone(e.target.value)} className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-emerald-300 outline-hidden focus:border-emerald-400 font-mono font-bold" />
setEditAddress(e.target.value)} className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white outline-hidden focus:border-purple-400" />
{/* Items Management */}

Zapatillas & Proveedores en la Orden

{editItems.map((item, idx) => (
Par #{idx + 1} {editItems.length > 1 && ( )}
handleItemChange(idx, 'productName', e.target.value)} className="w-full bg-neutral-950 border border-neutral-800 rounded-lg px-2.5 py-1.5 text-white outline-hidden" />
handleItemChange(idx, 'size', e.target.value)} className="w-full bg-neutral-950 border border-neutral-800 rounded-lg px-2.5 py-1.5 text-white font-mono font-bold outline-hidden" />
handleItemChange(idx, 'quantity', parseInt(e.target.value, 10) || 1)} className="w-full bg-neutral-950 border border-neutral-800 rounded-lg px-2.5 py-1.5 text-white outline-hidden" />
handleItemChange(idx, 'price', parseInt(e.target.value, 10) || 0)} className="w-full bg-neutral-950 border border-neutral-800 rounded-lg px-2.5 py-1.5 text-white font-mono outline-hidden" />
handleItemChange(idx, 'supplierPrice', parseInt(e.target.value, 10) || 0)} className="w-full bg-neutral-950 border border-neutral-800 rounded-lg px-2.5 py-1.5 text-emerald-300 font-mono outline-hidden" />
))}
{/* Financial calculations */}
setEditTotalAmount(parseInt(e.target.value, 10) || 0)} className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white font-mono font-bold outline-hidden focus:border-purple-400" />
setEditDepositAmount(parseInt(e.target.value, 10) || 0)} className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-purple-300 font-mono font-bold outline-hidden focus:border-purple-400" />
{formatCOP(editTotalAmount - editDepositAmount)}