'use client'; import { useEffect, useMemo, useState, type ReactNode } from 'react'; import { Plus, RefreshCw, Search, Eye, CheckCircle2, Trash2, ShoppingCart, Barcode, Printer, FileText, User, MapPin, } from 'lucide-react'; import { toast } from 'sonner'; import { format } from 'date-fns'; import { apiFetch } from '@/services/api/client'; import { SlideOver } from '@/components/ui/SlideOver'; import { CustomSelect } from '@/components/ui/CustomSelect'; import { TablePagination, useClientPagination } from '@/components/ui/TablePagination'; interface OrderItemDetail { product_id: string; variant_id: string; product_name: string; sku: string; unit_price: number; quantity: number; total_price: number; } interface StatusHistorySchema { previous_status: string | null; new_status: string; changed_by: string; reason: string | null; created_at: string; } interface AdminOrderDetailResponse { order_id: string; order_no: string; customer_id: string | null; customer_email: string | null; customer_name: string | null; total_amount: number; discount_amount: number; tax_amount: number; shipping_cost: number; final_amount: number; status: string; payment_status: string; fulfillment_status: string; shipping_address_json: string | null; billing_address_json: string | null; tracking_number: string | null; courier_name: string | null; created_at: string; items: OrderItemDetail[]; history: StatusHistorySchema[]; } function renderFormattedAddress(addressJsonStr: string | null | undefined): ReactNode { if (!addressJsonStr) return null; const str = String(addressJsonStr).trim(); if (str.startsWith('{') && str.endsWith('}')) { try { const parsed = JSON.parse(str); if (parsed && typeof parsed === 'object') { const { full_name, phone, street_address, address_line1, address_line2, city, state, pincode, postal_code, country, address_type } = parsed; const street = street_address || address_line1 || address_line2 || ''; const zip = pincode || postal_code || ''; const name = full_name || ''; const location = [street, city, state, zip, country].filter(Boolean).join(', '); return (
{name &&

{name}

} {phone &&

Phone: {phone}

} {location &&

{location}

} {address_type && ( {address_type} )}
); } } catch { // fallback } } return

{str}

; } interface AdminOrderListResponse { order_id: string; order_no: string; customer_email: string | null; customer_name?: string | null; customer_phone?: string | null; final_amount: number; status: string; payment_status: string; created_at: string; } type StatusFilter = 'all' | 'open' | 'shipped' | 'delivered' | 'cancelled'; function formatOrderDate(iso: string) { const normalized = iso.endsWith('Z') || iso.includes('+') ? iso : `${iso}Z`; const date = new Date(normalized); if (Number.isNaN(date.getTime())) return iso; return format(date, 'dd-MMM-yyyy HH:mm'); } function statusKey(status?: string) { return (status || '').toUpperCase(); } function isOpenStatus(status?: string) { const s = statusKey(status); return s === 'PENDING' || s === 'CONFIRMED' || s === 'PROCESSING'; } function isShippedStatus(status?: string) { const s = statusKey(status); return s === 'SHIPPED' || s === 'OUT_FOR_DELIVERY'; } function isDeliveredStatus(status?: string) { return statusKey(status) === 'DELIVERED'; } function isCancelledStatus(status?: string) { return statusKey(status) === 'CANCELLED'; } function statusBadgeClass(status?: string) { if (isDeliveredStatus(status)) return 'bg-success text-white'; if (isCancelledStatus(status)) return 'bg-destructive text-white'; if (isShippedStatus(status)) return 'bg-primary text-white'; return 'bg-warning text-white'; } export default function OrderIntelligencePage() { const [orders, setOrders] = useState([]); const [loading, setLoading] = useState(true); const [searchQuery, setSearchQuery] = useState(''); const [statusFilter, setStatusFilter] = useState('all'); const [selectedOrderId, setSelectedOrderId] = useState(null); const [detailData, setDetailData] = useState(null); const [detailLoading, setDetailLoading] = useState(false); const [barcodeInput, setBarcodeInput] = useState(''); const [verifiedItems, setVerifiedItems] = useState([]); const [showStatusForm, setShowStatusForm] = useState(false); const [newStatus, setNewStatus] = useState('PROCESSING'); const [trackingNo, setTrackingNo] = useState(''); const [courier, setCourier] = useState(''); const [changeReason, setChangeReason] = useState(''); const [updating, setUpdating] = useState(false); const [showCreateOrderModal, setShowCreateOrderModal] = useState(false); const [buyerName, setBuyerName] = useState(''); const [buyerEmail, setBuyerEmail] = useState(''); const [buyerPhone, setBuyerPhone] = useState(''); const [shippingAddress, setShippingAddress] = useState(''); const [productsList, setProductsList] = useState([]); const [categoriesList, setCategoriesList] = useState([]); const [brandsList, setBrandsList] = useState([]); const [selectedCategoryId, setSelectedCategoryId] = useState(''); const [selectedBrandId, setSelectedBrandId] = useState(''); const [selectedProductId, setSelectedProductId] = useState(''); const [skuOptions, setSkuOptions] = useState([]); const [skuLoading, setSkuLoading] = useState(false); const [tempSkuId, setTempSkuId] = useState(''); const [tempQty, setTempQty] = useState(1); const [tempPrice, setTempPrice] = useState(0); const [selectedItems, setSelectedItems] = useState([]); const [paymentStatus, setPaymentStatus] = useState('PAID'); const [paymentMethod, setPaymentMethod] = useState('CASH'); const [digitalMethod, setDigitalMethod] = useState<'UPI' | 'CARD'>('UPI'); const [cashPaidAmount, setCashPaidAmount] = useState(0); const [posSuccessData, setPosSuccessData] = useState(null); const effectiveItems = useMemo(() => { if (selectedItems.length > 0) return selectedItems; if (selectedProductId && tempSkuId) { const itemOpt = skuOptions.find((opt) => opt.variant_id === tempSkuId); if (itemOpt) { const prodObj = productsList.find((p) => p.product_id === selectedProductId); return [ { variant_id: itemOpt.variant_id, product_name: prodObj?.name || itemOpt.product_name || 'Product item', sku: itemOpt.sku, quantity: Number(tempQty || 1), unit_price: Number(tempPrice || itemOpt.price || 0), }, ]; } } return []; }, [selectedItems, selectedProductId, tempSkuId, skuOptions, tempQty, tempPrice, productsList]); const calculateOrderTotalWithTax = useMemo(() => { const subtotal = effectiveItems.reduce((acc, item) => acc + item.quantity * item.unit_price, 0); const tax = (subtotal * 18) / 100; return subtotal + tax; }, [effectiveItems]); const calculatedDigitalAmount = useMemo(() => { if (paymentMethod === 'MIXED_PAYMENT') { return Math.max(0, calculateOrderTotalWithTax - Number(cashPaidAmount || 0)); } if (paymentMethod === 'UPI' || paymentMethod === 'CARD') { return calculateOrderTotalWithTax; } return 0; }, [paymentMethod, calculateOrderTotalWithTax, cashPaidAmount]); const loadRazorpayScript = (): Promise => { return new Promise((resolve) => { if ((window as any).Razorpay) { resolve(true); return; } const script = document.createElement('script'); script.src = 'https://checkout.razorpay.com/v1/checkout.js'; script.onload = () => resolve(true); script.onerror = () => resolve(false); document.body.appendChild(script); }); }; const fetchCatalogProductsAndSkus = async () => { setSkuLoading(true); try { const [catRes, skuRes, categoriesRes, brandsRes] = await Promise.all([ apiFetch('/api/v1/catalog/products/all?limit=200').catch(() => []), apiFetch<{ items: any[] }>('/api/v1/inventory/skus?limit=150').catch(() => ({ items: [] })), apiFetch('/api/v1/catalog/categories/all').catch(() => []), apiFetch('/api/v1/catalog/brands/all').catch(() => []), ]); const prodsList = Array.isArray(catRes) ? catRes : catRes?.products || []; setProductsList(prodsList); const catsList = Array.isArray(categoriesRes) ? categoriesRes : categoriesRes?.items || []; setCategoriesList(catsList); const brsList = Array.isArray(brandsRes) ? brandsRes : brandsRes?.items || []; setBrandsList(brsList); const itemsMap = new Map(); if (skuRes && skuRes.items) { skuRes.items.forEach((item: any) => { itemsMap.set(item.variant_id, item); }); } prodsList.forEach((prod: any) => { if (prod.variants && prod.variants.length > 0) { prod.variants.forEach((v: any) => { if (!itemsMap.has(v.variant_id)) { const attrVals = (v.attributes || []).map((a: any) => a.attribute_value).join(' / '); itemsMap.set(v.variant_id, { variant_id: v.variant_id, product_id: prod.product_id, product_name: prod.name, variant_name: attrVals || v.sku, sku: v.sku, barcode: v.barcode || '', price: Number(v.price), available_stock: v.available_stock || 0, }); } }); } }); setSkuOptions(Array.from(itemsMap.values())); } catch { toast.error('Failed to load sellable products catalog'); } finally { setSkuLoading(false); } }; const availableCategoriesOptions = useMemo(() => { if (categoriesList.length > 0) { return categoriesList.map((c) => ({ value: c.category_id, label: c.name, })); } const map = new Map(); productsList.forEach((p) => { if (p.category_id) { map.set(p.category_id, p.category_name || p.category?.name || 'Category'); } }); return Array.from(map.entries()).map(([id, name]) => ({ value: id, label: name })); }, [categoriesList, productsList]); const availableBrandsOptions = useMemo(() => { const filteredProducts = selectedCategoryId ? productsList.filter((p) => p.category_id === selectedCategoryId) : productsList; const brandIdsInFilteredProducts = new Set(filteredProducts.map((p) => p.brand_id).filter(Boolean)); if (brandsList.length > 0) { return brandsList .filter((b) => !selectedCategoryId || brandIdsInFilteredProducts.has(b.brand_id)) .map((b) => ({ value: b.brand_id, label: b.name, })); } const map = new Map(); filteredProducts.forEach((p) => { if (p.brand_id) { map.set(p.brand_id, p.brand_name || p.brand?.name || 'Brand'); } }); return Array.from(map.entries()).map(([id, name]) => ({ value: id, label: name })); }, [brandsList, productsList, selectedCategoryId]); const availableProductsOptions = useMemo(() => { let filtered = productsList; if (selectedCategoryId) { filtered = filtered.filter((p) => p.category_id === selectedCategoryId); } if (selectedBrandId) { filtered = filtered.filter((p) => p.brand_id === selectedBrandId); } return filtered.map((p) => ({ value: p.product_id, label: p.name, })); }, [productsList, selectedCategoryId, selectedBrandId]); const availableVariantsForProduct = useMemo(() => { if (!selectedProductId) return []; return skuOptions.filter((opt) => opt.product_id === selectedProductId); }, [skuOptions, selectedProductId]); const handleAddOrderItem = () => { if (!selectedProductId) { toast.warning('Select a product first'); return; } if (!tempSkuId) { toast.warning('Select a product variant'); return; } const itemOpt = skuOptions.find((opt) => opt.variant_id === tempSkuId); if (!itemOpt) return; const existing = selectedItems.find((item) => item.variant_id === tempSkuId); if (existing) { toast.error('This variant is already on the order. Change the quantity instead.'); return; } const prodObj = productsList.find((p) => p.product_id === selectedProductId); const prodName = prodObj?.name || itemOpt.product_name || 'Product item'; setSelectedItems([ ...selectedItems, { variant_id: itemOpt.variant_id, product_name: prodName, sku: itemOpt.sku, quantity: Number(tempQty), unit_price: Number(tempPrice), available_stock: itemOpt.available_stock, }, ]); setTempSkuId(''); setTempQty(1); setTempPrice(0); }; const handleCreateOrderSubmit = async (e: React.FormEvent) => { e.preventDefault(); let finalOrderItems = [...selectedItems]; if (finalOrderItems.length === 0) { if (selectedProductId && tempSkuId) { const itemOpt = skuOptions.find((opt) => opt.variant_id === tempSkuId); if (itemOpt) { const prodObj = productsList.find((p) => p.product_id === selectedProductId); finalOrderItems = [ { variant_id: itemOpt.variant_id, product_name: prodObj?.name || itemOpt.product_name || 'Product item', sku: itemOpt.sku, quantity: Number(tempQty || 1), unit_price: Number(tempPrice || itemOpt.price || 0), available_stock: itemOpt.available_stock, }, ]; setSelectedItems(finalOrderItems); } } } if (finalOrderItems.length === 0) { toast.error('Select a product and variant or click "+ Add item"'); return; } setUpdating(true); try { const digitalAmt = calculatedDigitalAmount; const requiresDigitalRzp = (paymentMethod === 'UPI' || paymentMethod === 'CARD' || paymentMethod === 'MIXED_PAYMENT') && digitalAmt > 0; if (requiresDigitalRzp) { const loaded = await loadRazorpayScript(); if (!loaded) { toast.error('Failed to load Razorpay checkout SDK'); setUpdating(false); return; } // 1. Create Razorpay POS Order const rzpOrderRes = await apiFetch('/api/v1/admin/orders/create-pos-razorpay-order', { method: 'POST', body: JSON.stringify({ amount: digitalAmt }), }); // 2. Launch Razorpay Modal const options = { key: rzpOrderRes.key_id, amount: rzpOrderRes.amount_paise, currency: 'INR', name: 'iFixKart Walk-in Counter', description: `POS Store Order (${paymentMethod})`, order_id: rzpOrderRes.razorpay_order_id, prefill: { name: buyerName || 'Walk-in Guest', email: buyerEmail || 'guest@store.local', contact: buyerPhone || '', }, handler: async function (response: any) { try { const res = await apiFetch('/api/v1/admin/orders/create', { method: 'POST', body: JSON.stringify({ customer_name: buyerName || undefined, customer_email: buyerEmail || undefined, customer_phone: buyerPhone || undefined, items: finalOrderItems.map((item) => ({ variant_id: item.variant_id, quantity: item.quantity, unit_price: item.unit_price, })), shipping_address: shippingAddress || undefined, payment_status: 'PAID', payment_method: paymentMethod, cash_amount: paymentMethod === 'MIXED_PAYMENT' ? Number(cashPaidAmount || 0) : 0, digital_amount: digitalAmt, digital_method: paymentMethod === 'MIXED_PAYMENT' ? digitalMethod : paymentMethod, razorpay_order_id: response.razorpay_order_id, razorpay_payment_id: response.razorpay_payment_id, razorpay_signature: response.razorpay_signature, }), }); toast.success('Walk-in store order created with Razorpay payment'); setPosSuccessData(res); fetchOrders(); } catch (err: any) { toast.error(err.message || 'Failed to submit order after digital payment'); } finally { setUpdating(false); } }, modal: { ondismiss: function () { toast.info('Razorpay payment cancelled by cashier/customer'); setUpdating(false); }, }, theme: { color: '#0f172a' }, }; const rzp = new (window as any).Razorpay(options); rzp.open(); return; } // Pure Cash Order const res = await apiFetch('/api/v1/admin/orders/create', { method: 'POST', body: JSON.stringify({ customer_name: buyerName || undefined, customer_email: buyerEmail || undefined, customer_phone: buyerPhone || undefined, items: finalOrderItems.map((item) => ({ variant_id: item.variant_id, quantity: item.quantity, unit_price: item.unit_price, })), shipping_address: shippingAddress || undefined, payment_status: paymentStatus, payment_method: paymentMethod, cash_amount: calculateOrderTotalWithTax, digital_amount: 0, }), }); toast.success('Walk-in store order created'); setPosSuccessData(res); fetchOrders(); } catch (err: any) { toast.error(err.message || 'Failed to create offline store order'); } finally { setUpdating(false); } }; const clearCreateFormFields = () => { setBuyerName(''); setBuyerEmail(''); setBuyerPhone(''); setShippingAddress(''); setSelectedCategoryId(''); setSelectedBrandId(''); setSelectedProductId(''); setTempSkuId(''); setTempQty(1); setTempPrice(0); setSelectedItems([]); setPaymentStatus('PAID'); setPaymentMethod('CASH'); setDigitalMethod('UPI'); setCashPaidAmount(0); setPosSuccessData(null); }; const handleResetCreateOrderForm = () => { if (updating) return; clearCreateFormFields(); setShowCreateOrderModal(false); }; const fetchOrders = async () => { setLoading(true); try { const data = await apiFetch('/api/v1/admin/orders'); setOrders(data); } catch (err: any) { toast.error(err.message || 'Failed to fetch customer orders'); } finally { setLoading(false); } }; useEffect(() => { fetchOrders(); fetchCatalogProductsAndSkus(); }, []); const handleInspectOrder = async (id: string) => { setSelectedOrderId(id); setDetailLoading(true); setDetailData(null); setVerifiedItems([]); setBarcodeInput(''); setShowStatusForm(false); try { const data = await apiFetch(`/api/v1/admin/orders/${id}`); setDetailData(data); } catch (err: any) { toast.error(err.message || 'Failed to load order detailed view'); setSelectedOrderId(null); } finally { setDetailLoading(false); } }; const closeDetails = () => { if (updating) return; setSelectedOrderId(null); setDetailData(null); setShowStatusForm(false); }; const handleOpenStatusForm = () => { if (!detailData) return; setNewStatus(detailData.status); setTrackingNo(detailData.tracking_number || ''); setCourier(detailData.courier_name || ''); setChangeReason(''); setShowStatusForm(true); }; const handleStatusSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!detailData) return; setUpdating(true); try { await apiFetch(`/api/v1/admin/orders/${detailData.order_id}/status`, { method: 'PUT', body: JSON.stringify({ status: newStatus, tracking_number: trackingNo || null, courier_name: courier || null, reason: changeReason || null, }), }); toast.success('Order status saved'); setShowStatusForm(false); handleInspectOrder(detailData.order_id); fetchOrders(); } catch (err: any) { toast.error(err.message || 'Failed to update order status'); } finally { setUpdating(false); } }; const handleBarcodeSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!detailData || !barcodeInput.trim()) return; const matchedItem = detailData.items.find( (item) => item.sku.toLowerCase() === barcodeInput.trim().toLowerCase() ); if (matchedItem) { if (!verifiedItems.includes(matchedItem.sku)) { setVerifiedItems([...verifiedItems, matchedItem.sku]); toast.success(`Verified: ${matchedItem.product_name} (${matchedItem.sku})`); } else { toast.info(`Already verified: ${matchedItem.sku}`); } } else { toast.error(`SKU "${barcodeInput}" is not on this order`); } setBarcodeInput(''); }; const openCount = orders.filter((o) => isOpenStatus(o.status)).length; const shippedCount = orders.filter((o) => isShippedStatus(o.status)).length; const deliveredCount = orders.filter((o) => isDeliveredStatus(o.status)).length; const cancelledCount = orders.filter((o) => isCancelledStatus(o.status)).length; const filteredOrders = useMemo(() => { const q = searchQuery.trim().toLowerCase(); return orders.filter((o) => { const statusMatch = statusFilter === 'all' || (statusFilter === 'open' && isOpenStatus(o.status)) || (statusFilter === 'shipped' && isShippedStatus(o.status)) || (statusFilter === 'delivered' && isDeliveredStatus(o.status)) || (statusFilter === 'cancelled' && isCancelledStatus(o.status)); const searchMatch = !q || o.order_no.toLowerCase().includes(q) || (o.customer_email && o.customer_email.toLowerCase().includes(q)) || (o.customer_name && o.customer_name.toLowerCase().includes(q)) || (o.customer_phone && o.customer_phone.toLowerCase().includes(q)) || o.status.toLowerCase().includes(q); return statusMatch && searchMatch; }); }, [orders, searchQuery, statusFilter]); const pager = useClientPagination(filteredOrders); const dataCardShell = 'crm-radius-section border border-border bg-card shadow-[0_1px_3px_rgba(16,24,40,0.06)] overflow-hidden min-w-0'; const inputClass = 'w-full h-9 px-3 bg-card border border-border crm-radius-control text-[13px] text-foreground focus:outline-none focus:border-primary'; const labelClass = 'block text-[11px] font-semibold text-muted-foreground uppercase mb-1.5'; const primaryButton = 'inline-flex items-center justify-center gap-2 h-9 px-4 crm-radius-control bg-primary hover:bg-primary/90 text-white text-[13px] font-semibold cursor-pointer disabled:opacity-50'; const secondaryButton = 'inline-flex items-center justify-center gap-2 h-9 px-4 crm-radius-control border border-border bg-card text-foreground text-[13px] font-medium hover:bg-muted cursor-pointer disabled:opacity-50'; const renderStatusBadge = (status: string) => ( {(status || 'Draft').replace('_', ' ')} ); const backendUrl = process.env.NEXT_PUBLIC_API_URL || ''; return (

Customer Store Orders

{orders.length}
setSearchQuery(e.target.value)} className="w-full h-9 pl-9 pr-3 crm-radius-control border border-border bg-card text-[13px] text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-primary transition-colors" />
{( [ { id: 'all', label: `All (${orders.length})` }, { id: 'open', label: `Open (${openCount})` }, { id: 'shipped', label: `Shipped (${shippedCount})` }, { id: 'delivered', label: `Delivered (${deliveredCount})` }, { id: 'cancelled', label: `Cancelled (${cancelledCount})` }, ] as { id: StatusFilter; label: string }[] ).map((tab) => ( ))}
{loading ? (
Loading orders...
) : (
{filteredOrders.length === 0 ? ( ) : ( pager.items.map((order) => ( handleInspectOrder(order.order_id)} > )) )}
Order no. Customer Total Date Status Action
{searchQuery || statusFilter !== 'all' ? 'No orders match your search.' : 'No orders found.'}
{order.order_no}

{order.customer_name || 'Walk-in guest'}

{order.customer_phone && order.customer_phone !== 'N/A' ? order.customer_phone : order.customer_email && order.customer_email !== 'N/A' ? order.customer_email : '—'}

₹{order.final_amount.toFixed(2)} {formatOrderDate(order.created_at)} {renderStatusBadge(order.status)}
)}
} > {detailLoading ? (
Loading order...
) : !detailData ? (
Failed to load order details.
) : (

{formatOrderDate(detailData.created_at)}

{renderStatusBadge(detailData.status)} {(detailData.payment_status || 'UNPAID').replace('_', ' ')}
{!showStatusForm && ( )}
{detailData.customer_name || 'Walk-in guest'}
{detailData.customer_email && (

{detailData.customer_email}

)} {detailData.shipping_address_json && (

Shipping address

{renderFormattedAddress(detailData.shipping_address_json)}
)}
Subtotal ₹{detailData.total_amount.toFixed(2)}
{detailData.discount_amount > 0 && (
Discount -₹{detailData.discount_amount.toFixed(2)}
)}
GST ₹{detailData.tax_amount.toFixed(2)}
Shipping ₹{detailData.shipping_cost.toFixed(2)}
Final amount ₹{detailData.final_amount.toFixed(2)}
{showStatusForm && (

Update status

{newStatus === 'SHIPPED' && ( <>
setCourier(e.target.value)} className={inputClass} />
setTrackingNo(e.target.value)} className={inputClass} />
)}