3import { useState } from 'react';
4import { useRouter } from 'next/navigation';
6interface OrderLineItem {
31export default function CreatePurchaseOrder() {
32 const router = useRouter();
34 const [supplierId, setSupplierId] = useState('');
35 const [supplierName, setSupplierName] = useState('');
36 const [supplierSearch, setSupplierSearch] = useState('');
37 const [showSupplierDropdown, setShowSupplierDropdown] = useState(false);
38 const [suppliers, setSuppliers] = useState<Supplier[]>([]);
39 const [orderDate, setOrderDate] = useState(new Date().toISOString().split('T')[0]);
40 const [expectedDeliveryDate, setExpectedDeliveryDate] = useState('');
41 const [poNumber, setPoNumber] = useState('');
42 const [notes, setNotes] = useState('');
43 const [freightCost, setFreightCost] = useState('');
44 const [lineItems, setLineItems] = useState<OrderLineItem[]>([]);
47 const [newProductCode, setNewProductCode] = useState('');
48 const [newProductName, setNewProductName] = useState('');
49 const [productCodeSearch, setProductCodeSearch] = useState('');
50 const [productNameSearch, setProductNameSearch] = useState('');
51 const [showProductCodeDropdown, setShowProductCodeDropdown] = useState(false);
52 const [showProductNameDropdown, setShowProductNameDropdown] = useState(false);
53 const [filteredProducts, setFilteredProducts] = useState<Product[]>([]);
54 const [newQuantity, setNewQuantity] = useState('');
55 const [newUnitCost, setNewUnitCost] = useState('');
58 const [showEmailModal, setShowEmailModal] = useState(false);
59 const [sendViaEmail, setSendViaEmail] = useState(false);
60 const [otherEmail, setOtherEmail] = useState('');
61 const [additionalNotes, setAdditionalNotes] = useState('');
62 const [showEmailOptions, setShowEmailOptions] = useState(false);
64 // Mock suppliers - replace with actual API call
65 const mockSuppliers: Supplier[] = [
66 { id: '1001', name: 'ABC Suppliers Ltd', email: 'orders@abcsuppliers.com', phone: '555-0100' },
67 { id: '1002', name: 'Best Wholesale Co', email: 'sales@bestwholesale.com', phone: '555-0200' },
68 { id: '1003', name: 'Central Distributors', email: 'info@centraldist.com', phone: '555-0300' },
69 { id: '1004', name: 'Delta Trading Inc', email: 'orders@deltatrading.com', phone: '555-0400' },
70 { id: '1005', name: 'Elite Products Ltd', email: 'sales@eliteproducts.com', phone: '555-0500' },
71 { id: '1006', name: 'Fresh Foods Suppliers', email: 'orders@freshfoods.com', phone: '555-0600' },
72 { id: '1007', name: 'Global Imports Co', email: 'info@globalimports.com', phone: '555-0700' },
73 { id: '1008', name: 'Home Goods Wholesale', email: 'sales@homegoods.com', phone: '555-0800' },
76 // Mock inventory catalog - replace with actual API call
77 const mockInventory: Product[] = [
78 { code: 'COF-001', name: 'Premium Coffee Beans 1kg', description: 'Arabica blend', lastCost: 25.50 },
79 { code: 'TEA-002', name: 'Organic Tea Selection Box', description: '12 varieties', lastCost: 18.75 },
80 { code: 'POT-003', name: 'Stainless Steel Teapot', description: '1.5L capacity', lastCost: 42.00 },
81 { code: 'GRN-004', name: 'Coffee Grinder Professional', description: 'Commercial grade', lastCost: 156.30 },
82 { code: 'CUP-005', name: 'Paper Cups 100pk', description: '12oz disposable', lastCost: 8.90 },
83 { code: 'MUG-006', name: 'Ceramic Mug Set', description: '6 piece set', lastCost: 34.50 },
84 { code: 'SYR-007', name: 'Flavored Syrup Vanilla', description: '750ml bottle', lastCost: 12.25 },
85 { code: 'SYR-008', name: 'Flavored Syrup Caramel', description: '750ml bottle', lastCost: 12.25 },
86 { code: 'SYR-009', name: 'Flavored Syrup Hazelnut', description: '750ml bottle', lastCost: 12.25 },
87 { code: 'MIL-010', name: 'Milk Frother Electric', description: 'Battery operated', lastCost: 28.90 },
88 { code: 'SPO-011', name: 'Stainless Steel Spoons', description: '12 pack', lastCost: 15.75 },
89 { code: 'NAP-012', name: 'Paper Napkins 500pk', description: 'White premium', lastCost: 6.50 },
92 const handleProductCodeSearch = (value: string) => {
93 setProductCodeSearch(value);
95 const filtered = mockInventory.filter(p =>
96 p.code.toLowerCase().includes(value.toLowerCase())
98 setFilteredProducts(filtered);
99 setShowProductCodeDropdown(true);
100 setShowProductNameDropdown(false);
102 setFilteredProducts(mockInventory);
103 setShowProductCodeDropdown(true);
104 setShowProductNameDropdown(false);
108 const handleProductNameSearch = (value: string) => {
109 setProductNameSearch(value);
111 const filtered = mockInventory.filter(p =>
112 p.name.toLowerCase().includes(value.toLowerCase()) ||
113 (p.description && p.description.toLowerCase().includes(value.toLowerCase()))
115 setFilteredProducts(filtered);
116 setShowProductNameDropdown(true);
117 setShowProductCodeDropdown(false);
119 setFilteredProducts(mockInventory);
120 setShowProductNameDropdown(true);
121 setShowProductCodeDropdown(false);
125 const handleSelectProduct = (product: Product) => {
126 setNewProductCode(product.code);
127 setNewProductName(product.name);
128 setProductCodeSearch(product.code);
129 setProductNameSearch(product.name);
130 if (product.lastCost) {
131 setNewUnitCost(product.lastCost.toString());
133 setShowProductCodeDropdown(false);
134 setShowProductNameDropdown(false);
137 const handleShowAllProducts = (type: 'code' | 'name') => {
138 setFilteredProducts(mockInventory);
139 if (type === 'code') {
140 setShowProductCodeDropdown(true);
141 setShowProductNameDropdown(false);
143 setShowProductNameDropdown(true);
144 setShowProductCodeDropdown(false);
148 const handleClearProduct = () => {
149 setNewProductCode('');
150 setNewProductName('');
151 setProductCodeSearch('');
152 setProductNameSearch('');
154 setFilteredProducts([]);
157 const handleSupplierSearch = (value: string) => {
158 setSupplierSearch(value);
160 const filtered = mockSuppliers.filter(s =>
161 s.name.toLowerCase().includes(value.toLowerCase()) ||
164 setSuppliers(filtered);
165 setShowSupplierDropdown(true);
167 setSuppliers(mockSuppliers);
168 setShowSupplierDropdown(true);
172 const handleShowAllSuppliers = () => {
173 setSuppliers(mockSuppliers);
174 setShowSupplierDropdown(true);
177 const handleSelectSupplier = (supplier: Supplier) => {
178 setSupplierId(supplier.id);
179 setSupplierName(supplier.name);
180 setSupplierSearch(supplier.name);
181 setShowSupplierDropdown(false);
184 const handleClearSupplier = () => {
187 setSupplierSearch('');
191 const calculateExtendedCost = () => {
192 const quantity = parseFloat(newQuantity) || 0;
193 const unitCost = parseFloat(newUnitCost) || 0;
194 return quantity * unitCost;
197 const handleAddLine = () => {
198 if (!newProductCode || !newProductName || !newQuantity || !newUnitCost) {
199 alert('Please fill in all line item fields');
203 const quantity = parseFloat(newQuantity);
204 const unitCost = parseFloat(newUnitCost);
205 const extendedCost = calculateExtendedCost();
206 const lineTotal = extendedCost;
208 const newLine: OrderLineItem = {
209 id: Date.now().toString(),
210 productCode: newProductCode,
211 productName: newProductName,
218 setLineItems([...lineItems, newLine]);
221 setNewProductCode('');
222 setNewProductName('');
223 setProductCodeSearch('');
224 setProductNameSearch('');
229 const handleLineFormKeyDown = (e: React.KeyboardEvent) => {
230 if (e.key === 'Enter') {
236 const handleRemoveLine = (id: string) => {
237 setLineItems(lineItems.filter(line => line.id !== id));
240 const handleUpdateLineQuantity = (id: string, value: string) => {
241 const quantity = parseFloat(value) || 0;
242 setLineItems(lineItems.map(line => {
243 if (line.id === id) {
247 lineTotal: quantity * line.unitCost + line.extendedCost
254 const handleUpdateLineUnitCost = (id: string, value: string) => {
255 const unitCost = parseFloat(value) || 0;
256 setLineItems(lineItems.map(line => {
257 if (line.id === id) {
261 lineTotal: line.quantity * unitCost + line.extendedCost
268 const handleUpdateLineExtendedCost = (id: string) => {
269 setLineItems(lineItems.map(line => {
270 if (line.id === id) {
271 const extendedCost = line.quantity * line.unitCost;
275 lineTotal: extendedCost
282 const calculateTotal = () => {
283 const lineTotal = lineItems.reduce((sum, line) => sum + line.lineTotal, 0);
284 const freight = parseFloat(freightCost) || 0;
285 return lineTotal + freight;
288 const handleSubmit = async () => {
289 if (!supplierId || !supplierName) {
290 alert('Please enter supplier information');
294 if (lineItems.length === 0) {
295 alert('Please add at least one line item');
299 const orderTotal = calculateTotal();
301 if (confirm(`Create purchase order for ${supplierName} with ${lineItems.length} line item(s)?\n\nSubtotal: $${lineItems.reduce((sum, line) => sum + line.lineTotal, 0).toFixed(2)}\nFreight: $${(parseFloat(freightCost) || 0).toFixed(2)}\nTotal: $${orderTotal.toFixed(2)}`)) {
303 // Generate PO number (in production, this would come from Fieldpine)
304 const generatedPO = poNumber || `PO-${Date.now()}`;
306 console.log('Creating purchase order with Fieldpine DATI packet:');
308 request: 'save_data',
309 client: 'everydaypos-web',
311 table: 'PurchaseOrders',
314 f100_s: generatedPO, // PO Number
315 f101_E: supplierId, // Supplier ID
316 f102_s: supplierName, // Supplier Name
317 f103_E: orderDate, // Order Date
318 f104_N: orderTotal, // Total Amount (including freight)
319 f105_E: expectedDeliveryDate || null, // Expected Delivery Date
320 f106_N: parseFloat(freightCost) || 0, // Freight Cost
321 f114_N: 1, // Status: 1 = Draft
322 f132_s: notes, // Notes
323 lines: lineItems.map((line, index) => ({
324 f1100_E: (index + 1).toString(), // Line Number
325 f1101_E: line.productCode, // Product Code
326 f1102_s: line.productName, // Product Description
327 f1103_N: line.quantity, // Quantity Ordered
328 f1105_N: line.unitCost, // Unit Cost
329 f1106_N: line.lineTotal, // Line Total
330 f1107_s: line.productCode, // Product Code (alt field)
331 f1108_N: line.quantity // Quantity Outstanding (initially same as ordered)
336 console.log(JSON.stringify(datiPacket, null, 2));
338 // Simulate API call success
339 await new Promise(resolve => setTimeout(resolve, 500));
341 // Show success message with PO number
342 alert(`✓ Purchase Order Created Successfully!\n\nPO Number: ${generatedPO}\nSupplier: ${supplierName}\nTotal: $${orderTotal.toFixed(2)}\n\nIn production, this would be saved to Fieldpine.`);
344 // Navigate back to purchase orders page
345 router.push('/purchase-orders');
349 const handleSaveDraft = () => {
350 alert('Draft saved! (This would call Fieldpine API with status=0 in production)');
353 const handleSubmitWithEmail = async () => {
355 alert('Please enable email sending');
359 if (!supplierId || !supplierName) {
360 alert('Please enter supplier information');
364 if (lineItems.length === 0) {
365 alert('Please add at least one line item');
369 const orderTotal = calculateTotal();
370 const generatedPO = poNumber || `PO-${Date.now()}`;
372 if (confirm(`Create purchase order and email to supplier?\n\nPO: ${generatedPO}\nSupplier: ${supplierName}\nTotal: $${orderTotal.toFixed(2)}`)) {
374 // 1. Log DATI packet for order creation
375 console.log('Creating purchase order with Fieldpine DATI packet:');
377 request: 'save_data',
378 client: 'everydaypos-web',
380 table: 'PurchaseOrders',
385 f102_s: supplierName,
388 f105_E: expectedDeliveryDate || null,
389 f106_N: parseFloat(freightCost) || 0,
392 lines: lineItems.map((line, index) => ({
393 f1100_E: (index + 1).toString(),
394 f1101_E: line.productCode,
395 f1102_s: line.productName,
396 f1103_N: line.quantity,
397 f1105_N: line.unitCost,
398 f1106_N: line.lineTotal,
399 f1107_s: line.productCode,
400 f1108_N: line.quantity
405 console.log(JSON.stringify(datiPacket, null, 2));
407 // 2. Simulate order creation
408 await new Promise(resolve => setTimeout(resolve, 300));
410 // 3. Prepare and send email
411 const targetEmail = otherEmail.trim() || 'supplier@example.com';
413 let emailBody = `Dear ${supplierName},\n\n`;
414 emailBody += `Please find our purchase order details below:\n\n`;
415 emailBody += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
416 emailBody += `PURCHASE ORDER #${generatedPO}\n`;
417 emailBody += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n`;
419 emailBody += `Order Date: ${orderDate}\n`;
420 emailBody += `Expected Delivery: ${expectedDeliveryDate || 'To be confirmed'}\n\n`;
422 if (additionalNotes) {
423 emailBody += `ADDITIONAL NOTES:\n${additionalNotes}\n\n`;
426 emailBody += `ORDER SUMMARY:\n`;
427 emailBody += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
428 emailBody += `Total Lines: ${lineItems.length}\n`;
429 emailBody += `Subtotal: $${lineItems.reduce((sum, line) => sum + line.lineTotal, 0).toFixed(2)}\n`;
430 if (parseFloat(freightCost)) {
431 emailBody += `Freight: $${parseFloat(freightCost).toFixed(2)}\n`;
433 emailBody += `Total: $${orderTotal.toFixed(2)}\n\n`;
435 emailBody += `LINE ITEMS:\n`;
436 emailBody += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
437 lineItems.forEach((line, index) => {
438 emailBody += `${index + 1}. ${line.productCode} - ${line.productName}\n`;
439 emailBody += ` Quantity: ${line.quantity} @ $${line.unitCost.toFixed(2)} each\n`;
440 emailBody += ` Line Total: $${line.lineTotal.toFixed(2)}\n\n`;
444 emailBody += `\nNOTES:\n${notes}\n\n`;
447 emailBody += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
448 emailBody += `\nThank you for your service.\n\n`;
449 emailBody += `Best regards\n`;
450 emailBody += `\n---\nGenerated by EverydayPOS`;
452 // Log email DATI packet
453 const emailDatiPacket = {
454 request: 'save_data',
455 client: 'everydaypos-web',
457 table: 'PurchaseOrders',
462 subject: `Purchase Order #${generatedPO} from ${supplierName}`,
467 console.log('Email DATI packet:', emailDatiPacket);
469 // Create mailto link
470 const subject = encodeURIComponent(`Purchase Order #${generatedPO} from ${supplierName}`);
471 const mailtoLink = `mailto:${targetEmail}?subject=${subject}&body=${encodeURIComponent(emailBody)}`;
472 window.location.href = mailtoLink;
474 alert(`✓ Purchase Order Created & Email Ready!\n\nPO Number: ${generatedPO}\nSupplier: ${supplierName}\nTotal: $${orderTotal.toFixed(2)}\n\nEmail to: ${targetEmail}`);
477 setShowEmailModal(false);
478 setSendViaEmail(false);
480 setAdditionalNotes('');
481 setShowEmailOptions(false);
483 // Navigate after a delay to allow email client to open
485 router.push('/purchase-orders');
491 <div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 overflow-auto">
492 <div className="container mx-auto px-4 py-8">
494 <div className="mb-6">
495 <div className="flex items-center justify-between mb-2">
496 <h1 className="text-3xl font-bold text-slate-800">Create Purchase Order</h1>
498 onClick={() => router.push('/purchase-orders')}
499 className="px-4 py-2 text-slate-600 hover:text-slate-800"
504 <p className="text-slate-600">Create a new purchase order for your supplier</p>
507 {/* Supplier Information */}
508 <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 mb-6">
509 <h2 className="text-lg font-semibold text-slate-800 mb-4">Purchase Order Details</h2>
510 <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
512 <label className="block text-sm font-medium text-slate-700 mb-2">PO Number</label>
516 onChange={(e) => setPoNumber(e.target.value)}
517 placeholder="Auto-generated if left blank"
518 className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm"
522 <h3 className="text-lg font-semibold text-slate-800 mb-4">Supplier Information</h3>
523 <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
524 <div className="md:col-span-2 relative">
525 <label className="block text-sm font-medium text-slate-700 mb-2">
526 Search Supplier <span className="text-red-500">*</span>
528 <div className="relative">
531 value={supplierSearch}
532 onChange={(e) => handleSupplierSearch(e.target.value)}
533 onFocus={() => handleShowAllSuppliers()}
534 placeholder="Start typing supplier name or ID..."
535 className="w-full px-3 py-2 pr-20 border border-slate-300 rounded-lg text-sm"
537 <div className="absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-2">
540 onClick={handleClearSupplier}
541 className="text-slate-400 hover:text-slate-600"
547 onClick={handleShowAllSuppliers}
548 className="text-slate-400 hover:text-slate-600"
549 title="Show all suppliers"
555 {showSupplierDropdown && suppliers.length > 0 && (
556 <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">
557 {suppliers.map(supplier => (
560 onClick={() => handleSelectSupplier(supplier)}
561 className="px-4 py-3 hover:bg-slate-50 cursor-pointer border-b border-slate-100 last:border-b-0"
563 <div className="font-medium text-slate-800">{supplier.name}</div>
564 <div className="text-sm text-slate-500">ID: {supplier.id}</div>
566 <div className="text-xs text-slate-400 mt-1">{supplier.email}</div>
576 <label className="block text-sm font-medium text-slate-700 mb-2">Supplier ID</label>
581 className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm bg-slate-50"
585 <label className="block text-sm font-medium text-slate-700 mb-2">Supplier Name</label>
590 className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm bg-slate-50"
596 <label className="block text-sm font-medium text-slate-700 mb-2">
597 Order Date <span className="text-red-500">*</span>
602 onChange={(e) => setOrderDate(e.target.value)}
603 className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm"
607 <label className="block text-sm font-medium text-slate-700 mb-2">
608 Expected Delivery Date
612 value={expectedDeliveryDate}
613 onChange={(e) => setExpectedDeliveryDate(e.target.value)}
614 className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm"
620 {/* Freight and Notes */}
621 <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 mb-6">
622 <label className="block text-sm font-medium text-slate-700 mb-2">Notes</label>
625 onChange={(e) => setNotes(e.target.value)}
626 placeholder="Add any notes or special instructions"
628 className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm"
632 {/* Add Line Item Form */}
633 <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 mb-6">
634 <h2 className="text-lg font-semibold text-slate-800 mb-4">Add Line Item</h2>
635 <div className="grid grid-cols-1 md:grid-cols-6 gap-4">
636 <div className="relative">
637 <label className="block text-sm font-medium text-slate-700 mb-2">Product Code</label>
640 value={productCodeSearch}
641 onChange={(e) => handleProductCodeSearch(e.target.value)}
642 onFocus={() => handleShowAllProducts('code')}
643 placeholder="Search code..."
644 className="w-full px-3 py-2 pr-8 border border-slate-300 rounded-lg text-sm"
647 onClick={() => handleShowAllProducts('code')}
648 className="absolute right-2 top-[38px] text-slate-400 hover:text-slate-600"
649 title="Show all products"
653 {showProductCodeDropdown && filteredProducts.length > 0 && (
654 <div className="absolute z-20 w-full mt-1 bg-white border border-slate-300 rounded-lg shadow-lg max-h-60 overflow-y-auto">
655 {filteredProducts.map(product => (
658 onClick={() => handleSelectProduct(product)}
659 className="px-3 py-2 hover:bg-slate-50 cursor-pointer border-b border-slate-100 last:border-b-0"
661 <div className="font-medium text-slate-800 text-sm">{product.code}</div>
662 <div className="text-xs text-slate-500">{product.name}</div>
668 <div className="md:col-span-2 relative">
669 <label className="block text-sm font-medium text-slate-700 mb-2">Product Name</label>
672 value={productNameSearch}
673 onChange={(e) => handleProductNameSearch(e.target.value)}
674 onFocus={() => handleShowAllProducts('name')}
675 placeholder="Search product..."
676 className="w-full px-3 py-2 pr-8 border border-slate-300 rounded-lg text-sm"
679 onClick={() => handleShowAllProducts('name')}
680 className="absolute right-2 top-[38px] text-slate-400 hover:text-slate-600"
681 title="Show all products"
685 {showProductNameDropdown && filteredProducts.length > 0 && (
686 <div className="absolute z-20 w-full mt-1 bg-white border border-slate-300 rounded-lg shadow-lg max-h-60 overflow-y-auto">
687 {filteredProducts.map(product => (
690 onClick={() => handleSelectProduct(product)}
691 className="px-3 py-2 hover:bg-slate-50 cursor-pointer border-b border-slate-100 last:border-b-0"
693 <div className="font-medium text-slate-800 text-sm">{product.name}</div>
694 <div className="text-xs text-slate-500">{product.code} • {product.description}</div>
701 <label className="block text-sm font-medium text-slate-700 mb-2">Quantity</label>
705 onChange={(e) => setNewQuantity(e.target.value)}
706 onKeyDown={handleLineFormKeyDown}
710 className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm"
714 <label className="block text-sm font-medium text-slate-700 mb-2">Unit Cost ex. GST</label>
718 onChange={(e) => setNewUnitCost(e.target.value)}
719 onKeyDown={handleLineFormKeyDown}
723 className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm"
727 <label className="block text-sm font-medium text-slate-700 mb-2">Extended Cost ex. GST</label>
730 value={calculateExtendedCost().toFixed(2)}
732 className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm bg-slate-50 text-slate-700 font-semibold"
736 <div className="mt-4 flex justify-between items-center">
738 onClick={handleAddLine}
739 className="px-6 py-2 bg-[#00946b] text-white rounded-lg hover:opacity-80 font-semibold"
743 {(newProductCode || newProductName) && (
745 onClick={handleClearProduct}
746 className="px-4 py-2 text-slate-600 hover:text-slate-800 text-sm"
754 {/* Line Items Table */}
755 {lineItems.length > 0 && (
756 <div className="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden mb-6">
757 <div className="px-6 py-4 border-b border-slate-200">
758 <h2 className="text-lg font-semibold text-slate-800">Order Lines ({lineItems.length})</h2>
760 <div className="overflow-x-auto">
761 <table className="w-full">
762 <thead className="bg-slate-50 border-b border-slate-200">
764 <th className="px-4 py-3 text-left text-sm font-semibold text-slate-700">Code</th>
765 <th className="px-4 py-3 text-left text-sm font-semibold text-slate-700">Description</th>
766 <th className="px-4 py-3 text-right text-sm font-semibold text-slate-700">Quantity</th>
767 <th className="px-4 py-3 text-right text-sm font-semibold text-slate-700">Unit Cost</th>
768 <th className="px-4 py-3 text-right text-sm font-semibold text-slate-700">Extended Cost</th>
769 <th className="px-4 py-3 text-right text-sm font-semibold text-slate-700">Line Total</th>
770 <th className="px-4 py-3 text-center text-sm font-semibold text-slate-700">Actions</th>
773 <tbody className="divide-y divide-slate-200">
774 {lineItems.map((line) => (
775 <tr key={line.id} className="hover:bg-slate-50">
776 <td className="px-4 py-3 text-sm text-slate-800 font-medium">{line.productCode}</td>
777 <td className="px-4 py-3 text-sm text-slate-700">{line.productName}</td>
778 <td className="px-4 py-3 text-right">
781 value={line.quantity}
782 onChange={(e) => handleUpdateLineQuantity(line.id, e.target.value)}
785 className="w-20 px-2 py-1 border border-slate-300 rounded text-sm text-right"
788 <td className="px-4 py-3 text-right">
791 value={line.unitCost}
792 onChange={(e) => handleUpdateLineUnitCost(line.id, e.target.value)}
795 className="w-24 px-2 py-1 border border-slate-300 rounded text-sm text-right"
798 <td className="px-4 py-3 text-right text-sm text-slate-800 font-semibold">
799 ${line.extendedCost.toFixed(2)}
801 <td className="px-4 py-3 text-sm text-slate-800 font-semibold text-right">
802 ${line.lineTotal.toFixed(2)}
804 <td className="px-4 py-3 text-center">
806 onClick={() => handleRemoveLine(line.id)}
807 className="text-red-600 hover:text-red-800 text-sm font-medium"
815 <tfoot className="bg-slate-50 border-t-2 border-slate-300">
817 <td colSpan={4} className="px-4 py-3 text-right text-sm font-bold text-slate-800">
820 <td className="px-4 py-3 text-right text-lg font-bold text-[#00946b]">
821 ${calculateTotal().toFixed(2)}
831 {/* Action Buttons */}
832 <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
833 <div className="flex items-center justify-between">
834 <div className="flex-1">
835 <div className="flex items-end gap-6 mb-4">
837 <label className="block text-sm font-medium text-slate-700 mb-2">Freight ex. GST</label>
841 onChange={(e) => setFreightCost(e.target.value)}
845 className="w-32 px-3 py-2 border border-slate-300 rounded-lg text-lg font-semibold text-slate-700"
849 <div className="text-sm text-slate-500">Subtotal</div>
850 <div className="text-lg font-semibold text-slate-700">${lineItems.reduce((sum, line) => sum + line.lineTotal, 0).toFixed(2)}</div>
851 <div className="text-sm text-slate-500 mt-3 border-t border-slate-200 pt-2">Order Total ex. GST</div>
852 <div className="text-3xl font-bold text-[#00946b]">${calculateTotal().toFixed(2)}</div>
853 <div className="text-sm text-slate-600 mt-1">{lineItems.length} line item(s)</div>
855 <div className="flex gap-3">
857 onClick={() => router.push('/purchase-orders')}
858 className="px-6 py-3 bg-slate-300 text-slate-700 rounded-lg hover:bg-slate-400 font-semibold"
863 onClick={handleSaveDraft}
864 className="px-6 py-3 bg-slate-600 text-white rounded-lg hover:bg-slate-700 font-semibold"
869 onClick={handleSubmit}
870 disabled={lineItems.length === 0 || !supplierId || !supplierName}
871 className="px-6 py-3 bg-[#00946b] text-white rounded-lg hover:opacity-80 font-semibold disabled:opacity-50 disabled:cursor-not-allowed"
876 onClick={() => setShowEmailModal(true)}
877 disabled={lineItems.length === 0 || !supplierId || !supplierName}
878 className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 font-semibold disabled:opacity-50 disabled:cursor-not-allowed"
887 <div className="fixed inset-0 bg-black/30 z-50 flex items-center justify-center p-4">
888 <div className="bg-white rounded-lg shadow-xl max-w-md w-full">
889 <div className="border-b border-slate-200 p-4 flex items-center justify-between">
890 <h2 className="text-lg font-semibold text-slate-800">Email Options</h2>
893 setShowEmailModal(false);
894 setSendViaEmail(false);
896 setAdditionalNotes('');
897 setShowEmailOptions(false);
899 className="text-slate-400 hover:text-slate-600"
905 <div className="p-6 space-y-4">
906 <div className="border border-slate-200 rounded-lg p-4 bg-slate-50">
907 <label className="flex items-center gap-2 cursor-pointer mb-3">
910 checked={sendViaEmail}
911 onChange={(e) => setSendViaEmail(e.target.checked)}
912 className="w-4 h-4 text-blue-600 rounded"
914 <span className="font-medium text-slate-700">Email to Supplier</span>
919 <div className="text-sm text-slate-600 mb-3">
920 Email: {supplierName}
924 onClick={() => setShowEmailOptions(!showEmailOptions)}
925 className="text-sm text-blue-600 hover:text-blue-700 mb-3"
927 {showEmailOptions ? '▲' : '▼'} Options
930 {showEmailOptions && (
931 <div className="space-y-3 mb-3 border-t border-slate-200 pt-3">
933 <label className="block text-sm font-medium text-slate-700 mb-1">
939 onChange={(e) => setOtherEmail(e.target.value)}
940 placeholder="Enter alternative email"
941 className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm"
946 <label className="block text-sm font-medium text-slate-700 mb-1">
950 value={additionalNotes}
951 onChange={(e) => setAdditionalNotes(e.target.value)}
953 placeholder="Add any extra message"
954 className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm"
963 <div className="flex gap-3">
966 setShowEmailModal(false);
967 setSendViaEmail(false);
969 setAdditionalNotes('');
970 setShowEmailOptions(false);
972 className="flex-1 px-4 py-2 bg-slate-300 text-slate-700 rounded-lg hover:bg-slate-400 font-semibold"
977 onClick={handleSubmitWithEmail}
978 disabled={!sendViaEmail}
979 className="flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 font-semibold disabled:opacity-50 disabled:cursor-not-allowed"