3import { useState } from 'react';
4import { useRouter } from 'next/navigation';
11interface SaleLineItem {
17 supplierCode?: string;
18 supplierCost?: number;
22interface CustomerDetails {
38 customer: CustomerDetails;
39 lines: SaleLineItem[];
42export default function DropShippingOrder() {
43 const router = useRouter();
45 const [supplierSearch, setSupplierSearch] = useState('');
46 const [supplierId, setSupplierId] = useState('');
47 const [supplierName, setSupplierName] = useState('');
48 const [showSupplierDropdown, setShowSupplierDropdown] = useState(false);
49 const [suppliers, setSuppliers] = useState<Supplier[]>([]);
51 const [saleSearch, setSaleSearch] = useState('');
52 const [selectedSale, setSelectedSale] = useState<Sale | null>(null);
53 const [sales, setSales] = useState<Sale[]>([]);
54 const [showSaleList, setShowSaleList] = useState(false);
55 const [searching, setSearching] = useState(false);
58 const mockSuppliers: Supplier[] = [
59 { id: '1001', name: 'ABC Suppliers Ltd' },
60 { id: '1002', name: 'Best Wholesale Co' },
61 { id: '1003', name: 'Central Distributors' },
62 { id: '1004', name: 'Delta Trading Inc' },
66 const mockSales: Sale[] = [
72 physkey: 'KEP3OJYYYBG4MZTDOOS7Q3YAAAEQAAAB',
75 address1: '123 Main Street',
78 address4: 'Australia',
86 description: 'Premium Coffee Beans 1kg',
94 description: 'Coffee Grinder',
106 physkey: 'KEP3OFQYYBG4MZTDCOPHYIYAAARQAAAB',
109 address1: '456 Oak Ave',
110 address2: 'Melbourne',
111 address3: 'VIC 3000',
112 address4: 'Australia',
120 description: 'Organic Tea Box',
132 physkey: 'KEP3OAYYYBG4MZTDMOCRBRYAAABAAAAB',
135 address1: '789 Elm Street',
136 address2: 'Brisbane',
137 address3: 'QLD 4000',
138 address4: 'Australia',
146 description: 'Premium Coffee Beans 1kg',
154 description: 'Coffee Filters',
163 const handleSupplierSearch = (value: string) => {
164 setSupplierSearch(value);
166 const filtered = mockSuppliers.filter(s =>
167 s.name.toLowerCase().includes(value.toLowerCase()) ||
170 setSuppliers(filtered);
172 setSuppliers(mockSuppliers);
174 setShowSupplierDropdown(true);
177 const handleSupplierFocus = () => {
178 setShowSupplierDropdown(true);
179 if (suppliers.length === 0) {
180 setSuppliers(mockSuppliers);
184 const handleSelectSupplier = (supplier: Supplier) => {
185 setSupplierId(supplier.id);
186 setSupplierName(supplier.name);
187 setSupplierSearch(supplier.name);
188 setShowSupplierDropdown(false);
191 const handleSearchSales = (value: string) => {
192 setSaleSearch(value);
197 const filtered = mockSales.filter(s =>
198 s.saleId.includes(value) ||
199 s.customer.name.toLowerCase().includes(value.toLowerCase())
202 setShowSaleList(true);
207 setShowSaleList(false);
211 const handleSaleFocus = () => {
212 if (saleSearch.trim() === '') {
213 // Show recent sales when focused with empty input
215 setShowSaleList(true);
219 const handleSelectSale = (sale: Sale) => {
220 // Fetch supplier product codes for each item
221 const updatedLines = sale.lines.map(line => ({
223 supplierCode: `SUPP-${line.pluCode}`,
224 supplierCost: line.total / line.quantity * 0.85 // Mock supplier cost
231 setShowSaleList(false);
234 const handleToggleLineItem = (lineId: string) => {
238 lines: selectedSale.lines.map(line =>
239 line.id === lineId ? { ...line, selected: !line.selected } : line
245 const handleCreateOrder = () => {
247 alert('Please select a supplier');
252 alert('Please select a sale');
256 const selectedItems = selectedSale.lines.filter(l => l.selected);
257 if (selectedItems.length === 0) {
258 alert('Please select at least one item');
262 const orderTotal = selectedItems.reduce((sum, line) => sum + line.total, 0);
264 if (confirm(`Create drop shipping order?\n\nSupplier: ${supplierName}\nCustomer: ${selectedSale.customer.name}\nItems: ${selectedItems.length}\nTotal: $${orderTotal.toFixed(2)}`)) {
268 request: 'save_data',
269 client: 'everydaypos-web',
271 table: 'PurchaseOrders',
272 operation: 'insert_dropship',
275 f102_s: supplierName,
276 f103_E: new Date().toISOString().split('T')[0],
278 f114_N: 2, // Pre-auth
279 f132_s: 'This is a Drop Shipping Order. Please ship directly to the customers address shown',
280 f200_E: selectedSale.physkey, // Link to sale
282 contact: selectedSale.customer.name,
284 selectedSale.customer.address1,
285 selectedSale.customer.address2,
286 selectedSale.customer.address3,
287 selectedSale.customer.address4,
288 selectedSale.customer.address5,
289 selectedSale.customer.address6
290 ].filter(a => a && a.trim()).join('|')
292 lines: selectedItems.map((line, index) => ({
293 f1100_E: (index + 1).toString(),
294 f1101_E: line.pluCode,
295 f1102_s: line.description,
296 f1103_N: line.quantity,
297 f1105_N: line.supplierCost || 0,
299 f1107_s: line.supplierCode || line.pluCode,
300 f1108_N: line.quantity
306 console.log('Drop Shipping Order DATI packet:', datiPacket);
308 alert(`✓ Drop Shipping Order Created!\n\nSupplier: ${supplierName}\nCustomer: ${selectedSale.customer.name}\nOrder Total: $${orderTotal.toFixed(2)}\n\nThe supplier will ship directly to the customer.`);
310 router.push('/purchase-orders');
315 <div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 overflow-auto">
316 <div className="container mx-auto px-4 py-8">
318 <div className="mb-6">
319 <div className="flex items-center justify-between mb-2">
320 <h1 className="text-3xl font-bold text-slate-800">Create Drop Shipping Order</h1>
322 onClick={() => router.push('/purchase-orders')}
323 className="px-4 py-2 text-slate-600 hover:text-slate-800"
328 <p className="text-slate-600">Create a purchase order that ships directly to your customer</p>
331 {/* Supplier Selection */}
332 <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 mb-6">
333 <h2 className="text-lg font-semibold text-slate-800 mb-4">Select Supplier</h2>
334 <div className="relative">
337 value={supplierSearch}
338 onChange={(e) => handleSupplierSearch(e.target.value)}
339 onFocus={handleSupplierFocus}
340 placeholder="Search supplier name or ID..."
341 className="w-full px-4 py-2 pr-10 border border-slate-300 rounded-lg text-sm"
344 onClick={() => setShowSupplierDropdown(true)}
345 className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600"
350 {showSupplierDropdown && suppliers.length > 0 && (
351 <div className="absolute z-10 w-full mt-1 bg-white border border-slate-300 rounded-lg shadow-lg max-h-60 overflow-y-auto">
352 {suppliers.map(supplier => (
355 onClick={() => handleSelectSupplier(supplier)}
356 className="px-4 py-3 hover:bg-slate-50 cursor-pointer border-b border-slate-100 last:border-b-0"
358 <div className="font-medium text-slate-800">{supplier.name}</div>
359 <div className="text-sm text-slate-500">ID: {supplier.id}</div>
367 <div className="mt-4 p-3 bg-green-50 border border-green-200 rounded-lg">
368 <div className="text-sm text-green-700">✓ Selected: {supplierName}</div>
373 {/* Sale Selection */}
374 <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 mb-6">
375 <h2 className="text-lg font-semibold text-slate-800 mb-4">Find Sale/Customer Order</h2>
376 <div className="flex gap-2 mb-4">
380 onChange={(e) => handleSearchSales(e.target.value)}
381 onFocus={handleSaleFocus}
382 placeholder="Search by sale ID or customer name..."
383 className="flex-1 px-4 py-2 border border-slate-300 rounded-lg text-sm"
385 {searching && <div className="text-slate-500 py-2">Searching...</div>}
388 {showSaleList && sales.length > 0 && (
389 <div className="border border-slate-200 rounded-lg overflow-hidden">
390 <table className="w-full text-sm">
391 <thead className="bg-slate-50 border-b border-slate-200">
393 <th className="px-4 py-3 text-left font-semibold">Sale ID</th>
394 <th className="px-4 py-3 text-left font-semibold">Date</th>
395 <th className="px-4 py-3 text-left font-semibold">Time</th>
396 <th className="px-4 py-3 text-left font-semibold">Customer</th>
397 <th className="px-4 py-3 text-right font-semibold">Total</th>
400 <tbody className="divide-y divide-slate-200">
404 onClick={() => handleSelectSale(sale)}
405 className="hover:bg-slate-50 cursor-pointer"
407 <td className="px-4 py-3 font-medium text-blue-600">{sale.saleId}</td>
408 <td className="px-4 py-3">{sale.date}</td>
409 <td className="px-4 py-3">{sale.time}</td>
410 <td className="px-4 py-3">{sale.customer.name}</td>
411 <td className="px-4 py-3 text-right">${sale.total.toFixed(2)}</td>
419 {sales.length === 0 && saleSearch && (
420 <div className="p-4 text-center text-slate-500">No sales found</div>
424 {/* Selected Sale Details */}
427 {/* Customer Details */}
428 <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 mb-6">
429 <h2 className="text-lg font-semibold text-slate-800 mb-4">Customer Details</h2>
430 <div className="grid grid-cols-2 gap-4">
432 <div className="text-sm text-slate-500 mb-1">Customer Name</div>
433 <div className="font-semibold text-slate-800">{selectedSale.customer.name}</div>
436 <div className="text-sm text-slate-500 mb-1">Sale ID</div>
437 <div className="font-semibold text-slate-800">{selectedSale.saleId}</div>
440 <div className="mt-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
441 <div className="text-sm text-slate-700">
442 <strong>Shipping Address:</strong>
443 <div className="mt-2 whitespace-pre-wrap font-mono text-xs">
444 {selectedSale.customer.name}
445 {selectedSale.customer.address1 && `\n${selectedSale.customer.address1}`}
446 {selectedSale.customer.address2 && `\n${selectedSale.customer.address2}`}
447 {selectedSale.customer.address3 && `\n${selectedSale.customer.address3}`}
448 {selectedSale.customer.address4 && `\n${selectedSale.customer.address4}`}
454 {/* Line Items Selection */}
455 <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 mb-6">
456 <h2 className="text-lg font-semibold text-slate-800 mb-4">Select Items to Order</h2>
457 <div className="overflow-x-auto">
458 <table className="w-full text-sm">
459 <thead className="bg-slate-50 border-b border-slate-200">
461 <th className="px-4 py-3 text-center w-10">
464 checked={selectedSale.lines.every(l => l.selected)}
468 lines: selectedSale.lines.map(l => ({
470 selected: e.target.checked
476 <th className="px-4 py-3 text-left font-semibold">Your PLU</th>
477 <th className="px-4 py-3 text-left font-semibold">Description</th>
478 <th className="px-4 py-3 text-right font-semibold">Qty</th>
479 <th className="px-4 py-3 text-right font-semibold">Your Total</th>
480 <th className="px-4 py-3 text-left font-semibold">Supplier PLU</th>
481 <th className="px-4 py-3 text-right font-semibold">Supplier Cost</th>
484 <tbody className="divide-y divide-slate-200">
485 {selectedSale.lines.map(line => (
486 <tr key={line.id} className="hover:bg-slate-50">
487 <td className="px-4 py-3 text-center">
490 checked={line.selected}
491 onChange={() => handleToggleLineItem(line.id)}
494 <td className="px-4 py-3 font-mono text-slate-800">{line.pluCode}</td>
495 <td className="px-4 py-3">{line.description}</td>
496 <td className="px-4 py-3 text-right">{line.quantity}</td>
497 <td className="px-4 py-3 text-right font-semibold">${line.total.toFixed(2)}</td>
498 <td className="px-4 py-3 font-mono text-blue-600">{line.supplierCode}</td>
499 <td className="px-4 py-3 text-right">${(line.supplierCost || 0).toFixed(2)}</td>
508 <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
509 <div className="flex items-center justify-between">
511 <div className="text-sm text-slate-500">Selected Items</div>
512 <div className="text-2xl font-bold text-[#00946b]">
513 {selectedSale.lines.filter(l => l.selected).length} item(s)
515 <div className="text-sm text-slate-600 mt-1">
516 Total: ${selectedSale.lines.filter(l => l.selected).reduce((sum, l) => sum + l.total, 0).toFixed(2)}
519 <div className="flex gap-3">
521 onClick={() => router.push('/purchase-orders')}
522 className="px-6 py-3 bg-slate-300 text-slate-700 rounded-lg hover:bg-slate-400 font-semibold"
527 onClick={handleCreateOrder}
528 disabled={!supplierId || selectedSale.lines.filter(l => l.selected).length === 0}
529 className="px-6 py-3 bg-[#00946b] text-white rounded-lg hover:opacity-80 font-semibold disabled:opacity-50 disabled:cursor-not-allowed"
531 Create Drop Ship Order