430- 431- // Checkout layout states 432- const [couponInput, setCouponInput] = useState(''); 433- const [appliedCoupon, setAppliedCoupon] = useState(null); 434- const [shippingCharge, setShippingCharge] = useState(60); 435: const [checkoutNotes, setCheckoutNotes] = useState(''); 436- 437- // B2B Checkout state states 438: const [checkoutIsB2B, setCheckoutIsB2B] = useState(false); 439: const [checkoutCompanyName, setCheckoutCompanyName] = useState(''); 440: const [checkoutGstNumber, setCheckoutGstNumber] = useState(''); 441- 442- // Checkout Shipping Form matching UserProfile addresses 443: const [checkoutName, setCheckoutName] = useState(user?.name || ''); 444: const [checkoutPhone, setCheckoutPhone] = useState(user?.addresses[0]?.mobile || ''); 445: const [checkoutLine, setCheckoutLine] = useState(user?.addresses[0]?.addressLine || ''); 446: const [checkoutCity, setCheckoutCity] = useState(user?.addresses[0]?.city || ''); 447: const [checkoutState, setCheckoutState] = useState(user?.addresses[0]?.state || ''); 448: const [checkoutPincode, setCheckoutPincode] = useState(user?.addresses[0]?.pincode || ''); 449- 450- // Initialize and Sync Firebase authentication state 451- useEffect(() => { 452- const unsubscribe = onAuthStateChanged(auth, async (firebaseUser) => { 453- if (firebaseUser) { -- 515- } 516- }); 517- return () => unsubscribe(); 518- }, []); 519- 520: // Set checkout shipping values accordingly when user profile updates 521- useEffect(() => { 522- if (user) { 523- setCheckoutName(user.name || ''); 524- if (user.addresses && user.addresses.length > 0) { 525- setCheckoutPhone(user.addresses[0].mobile || ''); -- 575- }); 576- }; 577- 578- const getCouponDetails = (coupon: Coupon) => { 579- let terms = "Standard consumer discount terms apply. Cannot be used in conjunction with other offers. Expires upon specified date."; 580: let limits = "Single use per checkout order node."; 581- let desc = ""; 582- 583- if (coupon.code === 'WELCOME10') { 584- terms = "Welcome offer for newly registered account nodes. Applicable onto export-grade dehydrated spices, dry onion flakes, and premium powders. Valid for your next purchase transaction above minimum order requirement."; 585- limits = "Limited to 1-time usage per customer account."; 586- desc = "10% percentage discount deducted from the product subtotal value."; 587- } else if (coupon.code === 'EXIM50') { 588- terms = "Flat discount coupon valid for all active client orders. Minimum cart value requirements must be satisfied at purchase to claim of value release. Valid for retail and bulk packages."; 589- limits = "Reusable across multiple sequential purchases."; 590: desc = "Flat ₹50 currency discount directly subtracted from checkouts."; 591- } else if (coupon.code === 'FREESHIP') { 592- terms = "Complementary safe expedited routing across domestic shipping zones. Activated automatically upon subtotal condition with zero-rate priority assignment."; 593- limits = "Can be applied with any active shipping address coordinates."; 594- desc = "Converts shipping fee to completely FREE of charge."; 595- } else { 596- // Dynamic fallback 597- desc = coupon.discountType === 'percentage' 598: ? `${coupon.value}% percentage discount reduction on eligible checkout values.` 599- : `Flat ₹${coupon.value} instant cash deduction value applied on product sum.`; 600- } 601- 602- return { terms, limits, desc }; 603- }; -- 731- setProfileNewAddrCompanyName(''); 732- setProfileNewAddrGstNumber(''); 733- setProfileEditingAddressId(null); 734- setProfileAddAddressOpen(false); 735- 736: alert(profileEditingAddressId ? "šŸ“ Address modified successfully!" : "šŸ“ Address saved successfully! You can now select it directly during checkout."); 737- }; 738- 739- const handleProfileDeleteAddress = (id: string) => { 740- const updated = savedAddresses.filter(addr => addr.id !== id); 741- setSavedAddresses(updated); -- 791- return () => clearTimeout(timer); 792- } 793- }, [showGooglePrompt, settings.googleClientId]); 794- 795- const handleSaveCurrentAddress = () => { 796: if (!checkoutName || !checkoutPhone || !checkoutLine || !checkoutCity || !checkoutPincode) { 797- alert('Please fill out Name, Phone, and Address details before saving.'); 798- return; 799- } 800- const newAddr = { 801- id: 'addr_' + Date.now(), 802: name: checkoutName, 803: phone: checkoutPhone, 804: addressLine: checkoutLine, 805: city: checkoutCity, 806: state: checkoutState, 807: pincode: checkoutPincode 808- }; 809: const updated = [newAddr, ...savedAddresses.filter(a => a.addressLine !== checkoutLine)]; 810- setSavedAddresses(updated); 811- const userKey = user?.id ? 'zinnora_saved_addresses_' + user.id : 'zinnora_saved_addresses_guest'; 812- localStorage.setItem(userKey, JSON.stringify(updated)); 813- alert('Address successfully saved in your quick selection panel! šŸ’¾'); 814- }; -- 1150- const gst = prod?.gstRate || 0; 1151- return sum + ((item.price * item.quantity * gst) / 100); 1152- }, 0) 1153- ); 1154- 1155: const totalHandling = 0; // Handing charges removed from checkout as per user instructions 1156- 1157- const freeThreshold = settings.freeShippingThreshold !== undefined ? settings.freeShippingThreshold : 999; 1158- const globalShipping = settings.globalShippingCharge !== undefined ? settings.globalShippingCharge : 60; 1159- 1160- const totalShipping = subTotal === 0 -- 1847- alert('Validation failed locally.'); 1848- } 1849- } 1850- }; 1851- 1852: // Complete customer order checkout, dispatch to backend API & compile tailored WhatsApp message URL 1853- const handlePlaceOrderCheckout = async () => { 1854: if (!checkoutName || !checkoutPhone || !checkoutLine || !checkoutCity) { 1855- alert('Please fill out Name, Phone number, and accurate Delivery Address details.'); 1856- return; 1857- } 1858- 1859: if (checkoutIsB2B) { 1860: if (!checkoutCompanyName.trim()) { 1861- alert('Please specify your Business / Company Name for B2B Registration.'); 1862- return; 1863- } 1864: if (!checkoutGstNumber.trim() || checkoutGstNumber.trim().length < 15) { 1865- alert('Please specify a valid GST identification number (GSTIN) - 15 digits minimum.'); 1866- return; 1867- } 1868- } 1869- 1870- const orderPayload = { 1871- userId: user?.id || 'usr_anonymous', 1872: customerName: checkoutName, 1873- email: user?.email || 'guest@gmail.com', 1874: mobile: checkoutPhone, 1875- address: { 1876: addressLine: checkoutLine, 1877: city: checkoutCity, 1878: state: checkoutState, 1879: pincode: checkoutPincode 1880- }, 1881- items: cart, 1882- totalAmount: subTotal, 1883- shippingCharge: totalShipping, 1884- gstCharge: totalGst, 1885- handlingCharge: totalHandling, 1886- couponCode: appliedCoupon?.code, 1887- discountAmount: discountAmount, 1888- payableAmount: overallPayableAmount, 1889: notes: checkoutNotes, 1890: isB2B: checkoutIsB2B, 1891: companyName: checkoutCompanyName, 1892: gstNumber: checkoutGstNumber 1893- }; 1894- 1895- let orderRefNumber = 'zinnora-' + Math.floor(10000 + Math.random() * 90000); 1896- let orderCreatedLocally = false; 1897- -- 1918- if (!orderCreatedLocally) { 1919- const fallbackOrder: Order = { 1920- id: 'ord_' + Math.random().toString(36).substring(2, 11), 1921- userId: user?.id || 'usr_anonymous', 1922- orderNumber: orderRefNumber, 1923: customerName: checkoutName, 1924- email: user?.email || 'guest@gmail.com', 1925: mobile: checkoutPhone, 1926- address: { 1927: addressLine: checkoutLine, 1928: city: checkoutCity, 1929: state: checkoutState, 1930: pincode: checkoutPincode 1931- }, 1932- items: cart, 1933- totalAmount: subTotal, 1934- shippingCharge: totalShipping, 1935- gstCharge: totalGst, -- 1940- status: 'pending', 1941- createdAt: new Date().toISOString(), 1942- trackingNumber: '', 1943- trackingLink: '', 1944- courierCompany: '', 1945: notes: checkoutNotes, 1946: isB2B: checkoutIsB2B, 1947: companyName: checkoutCompanyName, 1948: gstNumber: checkoutGstNumber 1949- }; 1950- setOrders(prev => { 1951- const updated = [fallbackOrder, ...prev]; 1952- localStorage.setItem('zinnora_orders', JSON.stringify(updated)); 1953- return updated; -- 1976- // -------------------- WHATSAPP CORRESPONDENCE COMPILER -------------------- 1977- const brandNameParam = settings?.brandName || 'zinnora Global'; 1978- const cleanPhone = (settings?.whatsappNumber || '919274838389').replace(/[^0-9]/g, ''); 1979- 1980- const cartSummaryText = cart.map((it, idx) => `${idx + 1}. *${it.name}* [Size: ${it.size}] x${it.quantity} = ₹${it.price * it.quantity}`).join('\n'); 1981: const orderNotesText = checkoutNotes || 'None'; 1982- const orderDateText = new Date().toISOString().substring(0, 10); 1983: const shippingAddressText = `${checkoutLine}, ${checkoutCity}, ${checkoutState} - ${checkoutPincode}`; 1984- 1985- let compiledMsg = ''; 1986- if (settings?.whatsappOrderTemplate) { 1987- compiledMsg = settings.whatsappOrderTemplate 1988- .replace(/{brandName}/g, brandNameParam) 1989- .replace(/{orderNumber}/g, orderRefNumber) 1990- .replace(/{orderDate}/g, orderDateText) 1991: .replace(/{customerName}/g, checkoutName) 1992: .replace(/{customerPhone}/g, checkoutPhone) 1993- .replace(/{shippingAddress}/g, shippingAddressText) 1994- .replace(/{cartSummary}/g, cartSummaryText) 1995- .replace(/{subTotalAmount}/g, String(subTotal)) 1996- .replace(/{discountAmount}/g, String(discountAmount)) 1997- .replace(/{payableAmount}/g, String(overallPayableAmount)) 1998- .replace(/{orderNotes}/g, orderNotesText); 1999- } else { 2000: compiledMsg = `🌱 *${brandNameParam.toUpperCase()} — ORDER INQUIRY* 🌱\n*Order Reference:* ${orderRefNumber}\n*Date:* ${orderDateText}\n\n*Customer Details:*\n• Name: ${checkoutName}\n• Phone: ${checkoutPhone}\n• Shipping Address: ${shippingAddressText}\n\n*Ordered Powders:*\n${cartSummaryText}\n\nšŸ’ø *Cost Summary:*\n• Subtotal Amount: ₹${subTotal}\n• Applied Promotions: -₹${discountAmount}\n*Total Payable Amount: ₹${overallPayableAmount}*\n\n_Order Notes:_ ${orderNotesText}\n------------------------------------------------\nPlease fulfill our order. Thank you!`; 2001- } 2002- 2003- const encodedText = encodeURIComponent(compiledMsg); 2004- const whatsappApiUrl = `https://wa.me/${cleanPhone}?text=${encodedText}`; 2005- -- 2008- 2009- // Save address if checked 2010- if (saveThisAddress) { 2011- const newAddr = { 2012- id: 'addr_' + Date.now(), 2013: name: checkoutName, 2014: phone: checkoutPhone, 2015: addressLine: checkoutLine, 2016: city: checkoutCity, 2017: state: checkoutState, 2018: pincode: checkoutPincode 2019- }; 2020: const updated = [newAddr, ...savedAddresses.filter(a => a.addressLine !== checkoutLine)]; 2021- setSavedAddresses(updated); 2022- const userKey = user?.id ? 'zinnora_saved_addresses_' + user.id : 'zinnora_saved_addresses_guest'; 2023- localStorage.setItem(userKey, JSON.stringify(updated)); 2024- } 2025- -- 3887- 3888- 3889- ))} 3890- 3891- 3892: {/* checkout customer form */} 3893-
3894- Fulfillment Delivery Coordinates 3895- 3896- {/* Saved Addresses Quick-Fill Selector */} 3897- {savedAddresses.length > 0 && ( -- 3919-
3920- 3921- setCheckoutName(e.target.value)} 3926- placeholder="Your complete name" 3927- className="w-full border rounded-lg p-2 text-xs focus:ring-1 focus:ring-brand-green-500 outline-none" 3928- /> 3929-
-- 3931-
3932- 3933- setCheckoutPhone(e.target.value)} 3938- placeholder="e.g. +91 91102 34455" 3939- className="w-full border rounded-lg p-2 text-xs focus:ring-1 focus:ring-brand-green-500 outline-none" 3940- /> 3941-
-- 3943-
3944- 3945- setCheckoutLine(e.target.value)} 3950- placeholder="Premises name, floor, street details..." 3951- className="w-full border rounded-lg p-2 text-xs focus:ring-1 focus:ring-brand-green-500 outline-none" 3952- /> 3953-
-- 3956-
3957- 3958- setCheckoutCity(e.target.value)} 3963- className="w-full border rounded p-1.5 text-xs focus:ring-1 focus:ring-brand-green-500 outline-none" 3964- /> 3965-
3966-
3967- 3968- setCheckoutState(e.target.value)} 3973- placeholder="Gujarat" 3974- className="w-full border rounded p-1.5 text-xs focus:ring-1 focus:ring-brand-green-500 outline-none" 3975- /> 3976-
3977-
3978- 3979- setCheckoutPincode(e.target.value)} 3984- className="w-full border rounded p-1.5 text-xs font-mono focus:ring-1 focus:ring-brand-green-500 outline-none" 3985- /> 3986-
3987-
-- 3990-
3991-
3992- 4009: 4010: {checkoutIsB2B ? 'B2B ORDER' : 'B2C CUSTOMER'} 4011- 4012-
4013- 4014: {checkoutIsB2B && ( 4015-
4016-
4017- 4018- setCheckoutCompanyName(e.target.value)} 4024- className="w-full border border-slate-200 bg-white rounded-lg p-2.5 text-xs focus:ring-1 focus:ring-brand-green-500 outline-none" 4025- /> 4026-
4027- 4028-
4029- 4030- setCheckoutGstNumber(e.target.value.toUpperCase())} 4036- className="w-full border border-slate-200 bg-white rounded-lg p-2.5 text-xs font-mono font-bold focus:ring-1 focus:ring-brand-green-500 outline-none" 4037- /> 4038-
4039-
-- 4042- 4043-
4044- 4045-