/** * @license * SPDX-License-Identifier: Apache-2.0 */ import React, { useState, useMemo } from 'react'; import { motion, AnimatePresence } from 'motion/react'; import { FileText, User, Phone, Car, Search, Plus, Trash2, Check, CreditCard, Printer, CheckCircle, CloudLightning, Smartphone, TrendingDown, Briefcase, Share2, Download, Receipt, FileDown, RefreshCw } from 'lucide-react'; import { TireProduct, Invoice, StaffUser, SystemSettings, InvoiceLineItem } from '../types'; interface InvoiceGeneratorProps { products: TireProduct[]; invoices: Invoice[]; currentStaff: StaffUser; settings: SystemSettings; isOnline: boolean; onAddInvoice: (invoice: Invoice) => void; selectedInvoiceForView: Invoice | null; setSelectedInvoiceForView: (invoice: Invoice | null) => void; } export default function InvoiceGenerator({ products, invoices, currentStaff, settings, isOnline, onAddInvoice, selectedInvoiceForView, setSelectedInvoiceForView }: InvoiceGeneratorProps) { // Wizard steps: 'DRAFT' | 'GATEWAY' | 'COMPLETED' const [generationStep, setGenerationStep] = useState<'DRAFT' | 'GATEWAY' | 'COMPLETED'>('DRAFT'); // Invoice Draft State const [customerName, setCustomerName] = useState(''); const [customerPhone, setCustomerPhone] = useState(''); const [customerVehicle, setCustomerVehicle] = useState(''); const [notes, setNotes] = useState(''); const [cart, setCart] = useState[]>([]); const [discount, setDiscount] = useState(0); const [paymentMethod, setPaymentMethod] = useState<'CASH' | 'CARD' | 'BANK_TRANSFER' | 'MOBILE_WALLET'>('CASH'); // Product search in generator const [prodSearch, setProdSearch] = useState(''); const [selectedCategory, setSelectedCategory] = useState('All'); // Checkout gateway simulation state const [activeGateway, setActiveGateway] = useState<'Stripe' | 'PayPal' | 'EasyPaisa' | 'JazzCash' | 'Bank'>('Stripe'); const [gatewayProcessing, setGatewayProcessing] = useState(false); const [gatewayAuthId, setGatewayAuthId] = useState(''); const [gatewayCompleted, setGatewayCompleted] = useState(false); const [ccNumber, setCcNumber] = useState('4242 4242 4242 4242'); const [ccExpiry, setCcExpiry] = useState('12/28'); const [ccCvc, setCcCvc] = useState('321'); // Invoice final outcome const [generatedInvoice, setGeneratedInvoice] = useState(null); // Filter products for invoice item selection const filteredProducts = useMemo(() => { return products.filter(p => { const matchSearch = p.brand.toLowerCase().includes(prodSearch.toLowerCase()) || p.model.toLowerCase().includes(prodSearch.toLowerCase()) || p.size.toLowerCase().includes(prodSearch.toLowerCase()) || p.sku.toLowerCase().includes(prodSearch.toLowerCase()); const matchCategory = selectedCategory === 'All' || p.category === selectedCategory; return matchSearch && matchCategory; }); }, [products, prodSearch, selectedCategory]); // Calculations const subtotal = useMemo(() => { return cart.reduce((sum, item) => sum + (item.quantity * item.unitPrice), 0); }, [cart]); const taxAmount = useMemo(() => { return Math.round((subtotal * settings.taxRate) / 100); }, [subtotal, settings.taxRate]); const total = useMemo(() => { const calculated = subtotal + taxAmount - discount; return calculated < 0 ? 0 : calculated; }, [subtotal, taxAmount, discount]); // Cart operations const addToCart = (product: TireProduct) => { if (product.stock <= 0) { alert(`Cannot add ${product.brand} ${product.model}. Insufficient stock in warehouse.`); return; } const existingItem = cart.find(item => item.productId === product.id); if (existingItem) { if (existingItem.quantity >= product.stock) { alert(`Stock limit reached! Only ${product.stock} tires are currently available.`); return; } setCart(cart.map(item => item.productId === product.id ? { ...item, quantity: item.quantity + 1 } : item )); } else { setCart([...cart, { id: 'li_' + Math.random().toString(36).substr(2, 9), productId: product.id, brand: product.brand, model: product.model, size: product.size, quantity: 1, unitPrice: product.price }]); } }; const updateCartQty = (productId: string, qty: number, stockLimit: number) => { if (qty <= 0) { setCart(cart.filter(item => item.productId !== productId)); return; } if (qty > stockLimit) { alert(`Insufficient stock! Only ${stockLimit} items available in warehouse.`); return; } setCart(cart.map(item => item.productId === productId ? { ...item, quantity: qty } : item )); }; const removeFromCart = (productId: string) => { setCart(cart.filter(item => item.productId !== productId)); }; // Payment triggers const handlePaymentSubmit = () => { if (cart.length === 0) { alert("Cart empty! Build custom tire elements first."); return; } if (!customerName.trim()) { alert("Customer Name is required to bind transaction record."); return; } // Cash checkout needs no gateway simulation if (paymentMethod === 'CASH') { finalizeInvoice(null); } else { // Map gateway based on method if (paymentMethod === 'CARD') { setActiveGateway('Stripe'); } else if (paymentMethod === 'MOBILE_WALLET') { setActiveGateway('EasyPaisa'); } else { setActiveGateway('Bank'); } setGenerationStep('GATEWAY'); setGatewayCompleted(false); setGatewayAuthId('TXN-' + Math.floor(100000 + Math.random() * 900000)); } }; // Secure final commit const finalizeInvoice = (gatewayData: Invoice['gatewayInfo'] | null) => { const invNumber = `HBT-${new Date().getFullYear()}-${1000 + invoices.length + 1}`; // Convert cart to full InvoiceLineItems const finalItems: InvoiceLineItem[] = cart.map(item => ({ ...item, totalPrice: item.quantity * item.unitPrice })); // Local Format custom display date const date = new Date(); const formattedDate = `${String(date.getDate()).padStart(2, '0')}-${String(date.getMonth() + 1).padStart(2, '0')}-${date.getFullYear()} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}`; const newInvoice: Invoice = { id: 'inv_' + Math.random().toString(36).substr(2, 9), invoiceNumber: invNumber, dateTime: formattedDate, customerName, customerPhone, customerVehicle, items: finalItems, subtotal, taxRate: settings.taxRate, taxAmount, discount, total, currencySymbol: settings.currencySymbol, currencyCode: settings.currencyCode, paymentMethod, paymentStatus: 'PAID', amountPaid: total, cashierName: currentStaff.name, notes: notes.trim() ? notes : undefined, gatewayInfo: gatewayData || undefined, isOfflineCreated: !isOnline // Tracks offline generation tags }; onAddInvoice(newInvoice); setGeneratedInvoice(newInvoice); setGenerationStep('COMPLETED'); // Wipe drafts setCustomerName(''); setCustomerPhone(''); setCustomerVehicle(''); setNotes(''); setCart([]); setDiscount(0); }; // Execute Gateway authorization animation const handleAuthorizeGateway = () => { setGatewayProcessing(true); setTimeout(() => { setGatewayProcessing(false); setGatewayCompleted(true); setTimeout(() => { // Complete checkout with authentic keys finalizeInvoice({ gatewayName: activeGateway, transactionId: gatewayAuthId, authTime: new Date().toISOString() }); }, 1000); }, 1800); }; // Format HTML share links const copyShareText = (inv: Invoice) => { const textDetails = `⚔ *Invoice ${inv.invoiceNumber} — Haider Brother Traders* ⚔\nVehicle: ${inv.customerVehicle || 'N/A'}\nCustomer: ${inv.customerName}\nTotal amount settled: *${inv.currencySymbol} ${inv.total.toLocaleString()}*\nPayment: ${inv.paymentMethod} (PAID)\nšŸ’¼ Shop Phone: ${settings.shopPhone}\nThank you for choosing Haider Brother Traders!`; navigator.clipboard.writeText(textDetails); alert("Invoice share prompt successfully copied to clipboard (optimized for WhatsApp/Social)!"); }; // Export invoice structure to readable offline-friendly raw layout HTML file const exportOfflineHTMLReceipt = (inv: Invoice) => { const htmlSnippet = ` Invoice ${inv.invoiceNumber}
${settings.shopName}
${settings.shopAddress}
Phone: ${settings.shopPhone}

INVOICE

Inv No: ${inv.invoiceNumber}
Date: ${inv.dateTime}
Cashier: ${inv.cashierName}
Customer Information:
Name: ${inv.customerName}
Phone: ${inv.customerPhone || 'N/A'}
Vehicle: ${inv.customerVehicle || 'N/A'}
${inv.items.map(item => ` `).join('')}
Item / Size Qty Unit Price Total
${item.brand} ${item.model} (${item.size}) ${item.quantity} ${inv.currencySymbol}${item.unitPrice.toLocaleString()} ${inv.currencySymbol}${item.totalPrice.toLocaleString()}
Subtotal: ${inv.currencySymbol}${inv.subtotal.toLocaleString()}
${settings.taxName} (${inv.taxRate}%): ${inv.currencySymbol}${inv.taxAmount.toLocaleString()}
Discounts: -${inv.currencySymbol}${inv.discount.toLocaleString()}
Total Settled: ${inv.currencySymbol}${inv.total.toLocaleString()}

* Invoice generated under security ledger vault. Offline tag: ${inv.isOfflineCreated ? 'True' : 'False'}

Thank you for choosing Haider Brother Traders!
`; const blob = new Blob([htmlSnippet], { type: 'text/html' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = `Invoice_${inv.invoiceNumber}.html`; link.click(); }; const handleManualPrint = () => { window.print(); }; return (
{/* Offline Status Warning Indicator */} {!isOnline && (
Offline Mode Activated — Internet connection offline. Drafted invoices are instantly committed and backed up locally to cache memory and will sync when system restarts or connects.
AIR-GAPPED COCKPIT
)} {/* Main split wizard screen */} {generationStep === 'DRAFT' && (
{/* LEFT PANEL: Bill drafting & Customer Onboarding */}

Customer & Vehicle Details

Provide client and vehicle details for invoice audit accuracy

{/* Patient Fields */}
setCustomerName(e.target.value)} className="w-full text-xs border border-slate-200 outline-none focus:border-slate-800 bg-white rounded-lg pl-9 pr-4 py-2 font-medium transition duration-150" />
setCustomerPhone(e.target.value)} className="w-full text-xs border border-slate-200 outline-none focus:border-slate-800 bg-white rounded-lg pl-9 pr-4 py-2 font-medium transition duration-150" />
setCustomerVehicle(e.target.value)} className="w-full text-xs border border-slate-200 outline-none focus:border-slate-800 bg-white rounded-lg pl-9 pr-4 py-2 font-medium transition duration-150" />
{/* PRODUCT SEARCH AND SELECTION PANEL */}

Tire Stock Matcher

Search and map tires directly onto live checkout ledger

{/* Categories */}
{['All', 'Passenger', 'SUV', 'Commercial', 'Light Truck', 'Truck/Bus'].map(cat => ( ))}
{/* Fast Search input */}
setProdSearch(e.target.value)} className="w-full text-xs border border-slate-200 outline-none focus:border-slate-800 bg-white rounded-lg pl-10 pr-4 py-2 font-sans transition duration-150" />
{/* Searched Items Grid */}
{filteredProducts.length === 0 ? (

No tires matching database entries.

) : ( filteredProducts.map(p => { const isLowStock = p.stock <= p.minStock; const isOutOfStock = p.stock === 0; return (
{isLowStock && (
{isOutOfStock ? 'OUT OF STOCK' : 'LOW STOCK'}
)}
{p.category}

{p.brand} {p.model}

{p.size}

{p.description || "High structural performance tire."}

MSRP Retail Price

{settings.currencySymbol} {p.price.toLocaleString()}

Qty in stock: {p.stock}
); }) )}
{/* RIGHT PANEL: Live Cart Summary & Checkout triggers */}

Ledger Checkout Cart

{cart.reduce((sum, item) => sum + item.quantity, 0)} Pcs
{/* Items layout */}
{cart.length === 0 ? (

Ledger is empty. Select products from matching list to queue invoice items.

) : ( cart.map((item) => { const matchedProd = products.find(p => p.id === item.productId); const maxStock = matchedProd ? matchedProd.stock : 100; return (
{item.brand} {item.model}

{item.size}

{/* Quantity buttons */}
{item.quantity}
{settings.currencySymbol} {(item.quantity * item.unitPrice).toLocaleString()}
); }) )}
{/* Totals Section */}
Subtotal {settings.currencySymbol} {subtotal.toLocaleString()}
{settings.taxName} ({settings.taxRate}%) {settings.currencySymbol} {taxAmount.toLocaleString()}
{/* Discount field */}
Special Discount ({settings.currencySymbol}) setDiscount(Math.max(0, parseInt(e.target.value) || 0))} className="w-20 border border-slate-200 focus:border-slate-800 text-right text-xs outline-none bg-white hover:bg-slate-50 focus:bg-white rounded-md p-1 font-mono font-bold" />
{/* Final calculated total size */}
Total Settled Amount {settings.currencySymbol} {total.toLocaleString()}
{/* Payment strategy */}
{/* Comments block */}