let products = []; let cart = []; let currentPage = 1; let itemsPerPage = 0; let filteredProducts = []; // Load Products async function loadProducts() { const response = await fetch('/muestras/get-inventory.php'); const csvText = await response.text(); products = Papa.parse(csvText, { header: true, dynamicTyping: true }).data; filteredProducts = products; createFilters(); calculateItemsPerPage(); displayPage(1); filterProducts(); } // Calculate Items per Page function calculateItemsPerPage() { const containerWidth = document.getElementById('productContainer').offsetWidth; const productWidth = 210; // Product width + margin itemsPerPage = Math.floor(containerWidth / productWidth) * 50; // Fixed 10 rows } // Display Products on Page function displayPage(page) { const container = document.getElementById('productContainer'); container.innerHTML = ''; const start = (page - 1) * itemsPerPage; const slice = filteredProducts.slice(start, start + itemsPerPage); if (slice.length === 0) { container.innerHTML = '

No results found.

'; return; } slice.forEach(product => { const productDiv = document.createElement('div'); productDiv.className = 'product'; productDiv.innerHTML = `

${product['Paint Code']}

${product['Brand']}

${product['Paint Name']}

Agregar al carrito `; container.appendChild(productDiv); }); document.getElementById('prevPageButton').disabled = page === 1; document.getElementById('nextPageButton').disabled = start + itemsPerPage >= filteredProducts.length; currentPage = page; } // Pagination // Next and Previous Page Controls with Scroll to Top function nextPage() { if (currentPage * itemsPerPage < filteredProducts.length) { displayPage(currentPage + 1); scrollToTop(); // Scroll back to the top } } function prevPage() { if (currentPage > 1) { displayPage(currentPage - 1); scrollToTop(); // Scroll back to the top } } // Helper function to scroll back to the top function scrollToTop() { window.scrollTo({ top: 0, // Scroll to the very top of the page behavior: 'smooth' // Smooth scrolling }); } // Quantity Controls function incrementQuantity(code) { updateQuantity(code, 1); } function decrementQuantity(code) { updateQuantity(code, -1); } function updateQuantity(code, change) { const input = document.getElementById(`qty-${code}`); input.value = Math.max(1, parseInt(input.value) + change); } // Cart Management function addToCart(code) { const button = event.currentTarget; // Get the button that triggered the event button.classList.add('loading'); // Add loading class code = String(code); // Ensure the code is always treated as a string const product = products.find(p => String(p['Paint Code']) === code); const quantity = parseInt(document.getElementById(`qty-${code}`).value); const existing = cart.find(item => String(item['Paint Code']) === code); setTimeout(() => { if (existing) existing.quantity += quantity; else cart.push({ ...product, quantity }); updateCart(); button.classList.remove('loading'); // Remove loading spinner }, 1000); // Simulate loading for 1 second } // Update Cart function updateCart() { const cartItems = document.getElementById('cartItems'); const cartTotal = document.getElementById('cartTotal'); const discountDiv = document.getElementById('discountDiv') const itemCountDiv1 = document.getElementById('itemCountDiv1'); const itemCountDiv2 = document.getElementById('itemCountDiv2'); const checkoutButton = document.querySelector('#checkoutForm button[type="submit"]'); cartItems.innerHTML = ''; let total = 0; let itemCount = 0; cart.forEach(item => { total += item['Price'] * item.quantity; itemCount += item.quantity; const li = document.createElement('li'); li.innerHTML = `
${item['Paint Code']} ${item['Paint Name']}
${item['Brand']}
${item.quantity}
`; cartItems.appendChild(li); }); // Apply discount based on the number of items let discount = 0; if (itemCount >= 3 && itemCount <= 7) { discount = total * 0.10; // 10% discount } else if (itemCount >= 8) { discount = total * 0.25; // 25% discount } const finalTotal = total - discount; // Update the cart total display cartTotal.textContent = `${finalTotal.toFixed(2)} HNL`; discountDiv.textContent = `${discount.toFixed(2)} HNL`; // Update the cart total display with a prefix and bold text cartTotal.innerHTML = `Total: ${finalTotal.toFixed(2)} HNL`; // Update the discount display with a prefix and bold text discountDiv.innerHTML = `Descuento Aplicado: ${discount.toFixed(2)} HNL`; // Hide or show cart total and discount divs based on their values cartTotal.style.display = finalTotal === 0 ? 'none' : 'block'; discountDiv.style.display = discount === 0 ? 'none' : 'block'; //Update both item count displays itemCountDiv1.textContent = itemCount; itemCountDiv2.textContent = itemCount; saveCartToCookies(); // Disable the "Pagar" button if the cart is empty checkoutButton.disabled = itemCount === 0; return itemCount; // Return the item count for further use if needed } function removeFromCart(paintCode) { // Ensure type consistency by converting both to strings cart = cart.filter(item => String(item['Paint Code']) !== String(paintCode)); updateCart(); } function incrementCart(paintCode) { const cartItem = cart.find(item => String(item['Paint Code']) === String(paintCode)); if (cartItem) { cartItem.quantity++; updateCart(); } } function decrementCart(paintCode) { const cartItem = cart.find(item => String(item['Paint Code']) === String(paintCode)); if (cartItem && cartItem.quantity > 1) { cartItem.quantity--; updateCart(); } } document.addEventListener('DOMContentLoaded', function() { emailjs.init("ZXdPTp8fqeo6m6_xu"); // Your Public Key }); function decodeScriptUrl(data) { const binaryString = atob(data); const bytes = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } return new TextDecoder('utf-8').decode(bytes); } // Checkout and PDF Invoice async function handleCheckout(event) { event.preventDefault(); const form = this; const formData = new FormData(form); const cartData = JSON.stringify(cart); const itemCountDiv2 = document.getElementById('itemCountDiv2').textContent; const cartTotalElement = document.getElementById('cartTotal'); const discountDivElement = document.getElementById('discountDiv'); const cartTotal = cartTotalElement ? cartTotalElement.textContent : '0 HNL'; const discountDiv = discountDivElement ? discountDivElement.textContent : '0 HNL'; console.log("Cart Total: ", cartTotal); console.log("Discount: ", discountDiv); formData.append('cart', cartData); formData.append('itemCount', itemCountDiv2); // ✅ Generate Order ID once and store it in the DOM const orderID = generateOrderID(); document.getElementById('orderIDField').value = orderID; // Generate invoice, clear cart, and reset form generateInvoice(); generateOrder(); cart = []; updateCart(); event.target.reset(); toggleCart(); Toastify({ text: "¡Gracias por su compra!", duration: 5000, gravity: "top", position: "center" }).showToast(); const paymentProofFile2 = formData.get('paymentProof'); // Convert payment proof to Base64 const base64Fileproof = await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result.split(',')[1]); reader.onerror = reject; reader.readAsDataURL(paymentProofFile2); }); // console.log(base64Fileproof); // Send Email using Google Apps Script const emailParams = { order_id: orderID, from_name: formData.get('name'), email: formData.get('email'), phone: formData.get('phone'), department: formData.get('department'), city: formData.get('city'), address: formData.get('address'), reference: formData.get('reference') || 'No reference provided', cart: cartData, Count: itemCountDiv2, total_price: cartTotal, discount: discountDiv, payment_proof: base64Fileproof }; try { const response = await fetch("https://script.google.com/macros/s/AKfycby28gTSktoFwDIGppVeEg6EWiMvbxQyEUaCSGedzJ0lbAob9tmEmmSbBzQUtAX092uNVA/exec", { method: 'POST', mode: 'no-cors', body: JSON.stringify(emailParams), headers: { 'Content-Type': 'application/json' } }); console.log("Email sent successfully via Google Apps Script!"); } catch (error) { console.error("Failed to send email via Apps Script:", error); } // Dropbox Upload for Payment Proof const paymentProofFile = formData.get('paymentProof'); const reader = new FileReader(); reader.onload = async function (event) { const base64File = event.target.result.split(',')[1]; // Ensure the token is refreshed before uploading await refreshDropboxToken(); const dropboxUploadUrl = 'https://content.dropboxapi.com/2/files/upload'; const accessToken = DROPBOX_CONFIG.accessToken; const fileName = `${formData.get('name') || 'Unknown'}_payment_proof.png`; const parentFolder = '/ColorDemoOrders/pendingorders/'; const orderFolder = `${orderID}`; const folderPath = `${parentFolder}${orderFolder}/${fileName}`; const response = await fetch(dropboxUploadUrl, { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/octet-stream', 'Dropbox-API-Arg': JSON.stringify({ path: folderPath, mode: 'add', autorename: true, mute: false }) }, body: Uint8Array.from(atob(base64File), c => c.charCodeAt(0)) }); const result = await response.json(); if (response.ok) { console.log("File uploaded successfully to Dropbox!"); console.log("Uploaded to:", result.path_display); appendOrderToCSV(orderID, formData, cartTotal, discountDiv, itemCountDiv2); } else { alert("Error uploading to Dropbox!"); console.error(result); } }; reader.readAsDataURL(paymentProofFile); } // Function to generate unique Order ID function generateOrderID() { const now = new Date(); return `CD-${now.getFullYear()}${(now.getMonth() + 1)}${now.getDate()}-${now.getTime()}`; } // Save sales to Dropbox as CSV async function appendOrderToCSV(orderID, formData, cartTotal, discountDiv, itemCountDiv2) { await refreshDropboxToken(); const dropboxUploadUrl = 'https://content.dropboxapi.com/2/files/upload'; const accessToken = DROPBOX_CONFIG.accessToken; const fileName = `/ColorDemoOrders/orders/order_${orderID}.csv`; const csvHeader = 'Order ID,Name,Email,Phone,Department,City,Address,Total Price,Discount,Item Count,Cart Items\n'; // Wrap address in double quotes const address = formData.get('address').replace(/"/g, '""'); // Escape existing quotes const quotedAddress = `"${address}"`; // Wrap address in double quotes // Convert cart items to readable format const formattedCartItems = cart.map(item => { const itemName = item['Paint Name']; const itemCode = item['Paint Code'] || 'N/A'; const itemQuantity = item.quantity; const itemPrice = item['Price']; const itemTotal = itemQuantity * itemPrice; return `${itemName} (${itemCode}) x${itemQuantity} - ${itemTotal} HNL`; }).join('; '); // Join items with a semicolon separator // Create CSV row const csvLine = `${orderID},${formData.get('name')},${formData.get('email')},${formData.get('phone')},${formData.get('department')},${formData.get('city')},${quotedAddress},${cartTotal},${discountDiv},${itemCountDiv2},"${formattedCartItems}"\n`; const csvContent = csvHeader + csvLine; try { console.log("Uploading CSV to Dropbox..."); const response = await fetch(dropboxUploadUrl, { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/octet-stream', 'Dropbox-API-Arg': JSON.stringify({ path: fileName, mode: 'overwrite', autorename: false, mute: false }) }, body: csvContent }); const result = await response.json(); if (result.error) { console.error("Error uploading CSV:", result.error); console.log("Failed to upload CSV to Dropbox."); } else { console.log("CSV uploaded successfully to Dropbox:", result.path_display); } } catch (error) { console.error("Failed to upload CSV to Dropbox:", error); console.log("Error uploading order CSV."); } } // Generate Invoice PDF and Upload to Dropbox function generateInvoice() { const { jsPDF } = window.jspdf; const pdf = new jsPDF(); const orderID = document.getElementById('orderIDField').value; const clientName = document.querySelector('input[name="name"]').value || 'Cliente Desconocido'; const clientEmail = document.querySelector('input[name="email"]').value || 'No Email'; const clientPhone = document.querySelector('input[name="phone"]').value || 'No Phone'; const clientrtn = document.querySelector('input[name="rtn"]').value const currentDate = new Date().toLocaleDateString(); const currentTime = new Date().toLocaleTimeString(); const itemCount = document.getElementById('itemCountDiv1').textContent; const cartTotal = document.getElementById('cartTotal').textContent; const discount = document.getElementById('discountDiv').textContent || '0 HNL'; // Add Image to Header (if available) if (paperImageBase64) { pdf.addImage(paperImageBase64, 'PNG', 12, 15, 30, 30); // (x, y, width, height) } // HEADER (No space between consecutive lines) let yHeader = 20; pdf.setFontSize(18); pdf.text("Color Demo", 105, yHeader, { align: "center" }); pdf.setFontSize(14); pdf.text("Factura de Venta", 105, yHeader + 7, { align: "center" }); pdf.setFontSize(12); yHeader += 13; // Slightly larger space after the title pdf.text("Dirección: Residencial Santa Cruz,", 105, yHeader, { align: "center" }); pdf.text("II Etapa, Bloque 10, Casa 25", 105, yHeader + 5, { align: "center" }); pdf.text("Tel. Pendiente", 105, yHeader + 10, { align: "center" }); pdf.text("email: colordemo89@gmail.com", 105, yHeader + 15, { align: "center" }); // +5 for tight spacing pdf.text("RTN: 08011989128389", 105, yHeader + 20, { align: "center" }); // Combine label and value pdf.text("C.A.I.: F4EB28-BFD5C0-7C4693-E00235-F6DED6-86", 105, yHeader + 25, { align: "center" }); // Space below the header block yHeader += 40; // ORDER DETAILS pdf.text(`Orden: ${orderID}`, 15, yHeader); pdf.text(`Fecha: ${currentDate}`, 160, yHeader); pdf.text(`Hora: ${currentTime}`, 162, yHeader + 5); // === CLIENT INFO (Tighter grouping) === let yClient = yHeader + 15; pdf.setFontSize(14); pdf.text("Información del Cliente", 15, yClient); // Calculate the text width and position the underline const textWidth = pdf.getTextWidth("Información del Cliente:"); const underlineY = yClient + 1; // Slightly below the text pdf.line(15, underlineY, 15 + textWidth, underlineY); // (x1, y1, x2, y2) pdf.setFontSize(12); yClient += 8; // Small gap after section title pdf.text(`Nombre: ${clientName}`, 15, yClient); pdf.text(`Correo: ${clientEmail}`, 15, yClient + 5); // Tight spacing pdf.text(`RTN: ${clientrtn}`, 15, yClient + 10); // Tight spacing pdf.text(`Teléfono: ${clientPhone}`, 15, yClient + 15); // Tight spacing // === TABLE HEADER === let yTable = yClient + 30; pdf.setFontSize(14); pdf.text("Resumen del Pedido", 15, yTable - 3); // Calculate the text width and position the underline const textWidth2 = pdf.getTextWidth("Resumen del Pedido"); const underlineY2 = yTable - 2; // Slightly below the text pdf.line(15, underlineY2, 15 + textWidth2, underlineY2); // (x1, y1, x2, y2) pdf.setFontSize(12); pdf.setFillColor(240, 240, 240); pdf.rect(15, yTable, 180, 10, 'F'); pdf.text("Código", 18, yTable + 7); pdf.text("Color", 42, yTable + 7); pdf.text("Marca", 90, yTable + 7); pdf.text("Cantidad", 143, yTable + 7, { align: "right" }); pdf.text("P. Unitario", 170, yTable + 7, { align: "right" }); pdf.text("Total", 192, yTable + 7, { align: "right" }); yTable += 15; // === CART ITEMS === const pageHeight = pdf.internal.pageSize.height; const marginBottom = 30; cart.forEach(item => { const paintCode = item['Paint Code'] || 'N/A'; if (yTable + 10 > pageHeight - marginBottom) { pdf.addPage(); yTable = 30; pdf.setFillColor(240, 240, 240); pdf.rect(15, yTable, 180, 10, 'F'); pdf.text("Código", 18, yTable + 7); pdf.text("Color", 42, yTable + 7); pdf.text("Marca", 90, yTable + 7); pdf.text("Cantidad", 143, yTable + 7, { align: "right" }); pdf.text("P. Unitario", 170, yTable + 7, { align: "right" }); pdf.text("Total", 192, yTable + 7, { align: "right" }); yTable += 15; } pdf.text(`${paintCode}`, 18, yTable); pdf.text(`${item['Paint Name']}`, 42, yTable); pdf.text(`${item['Brand']}`, 90, yTable); pdf.text(`x${item.quantity}`, 143, yTable, { align: "right" }); pdf.text(`${item['Price']} HNL`, 170, yTable, { align: "right" }); pdf.text(`${item['Price'] * item.quantity} HNL`, 192, yTable, { align: "right" }); yTable += 10; }); // === FOOTER === yTable += 10; pdf.line(15, yTable, 195, yTable); yTable += 10; const rightMargin = 195; const discountWidth = pdf.getTextWidth(`${discount}`); const totalWidth = pdf.getTextWidth(cartTotal); pdf.setFontSize(12); pdf.text(`${discount}`, rightMargin - discountWidth, yTable); pdf.setFont("helvetica", "bold"); pdf.text(cartTotal, rightMargin - totalWidth, yTable + 6); pdf.setTextColor(255, 0, 0); pdf.setFontSize(14); pdf.text("PAGADO", rightMargin - totalWidth, yTable + 13); pdf.setTextColor(0, 0, 0); pdf.setFont("helvetica", "normal"); pdf.text(`Cantidad Total: ${itemCount}`, 15, yTable + 6); // === THANK YOU MESSAGE === yTable += 18; pdf.setFontSize(11); pdf.setTextColor(100); pdf.text("¡Gracias por su compra!", 105, yTable, { align: "center" }); // SAVE PDF LOCALLY const pdfFileName = `${orderID}_recibo.pdf`; const pdfBlob = pdf.output("blob"); // UPLOAD PDF TO DROPBOX uploadInvoiceToDropbox(pdfBlob, pdfFileName, orderID); // Create a URL from the blob const pdfURL = URL.createObjectURL(pdfBlob); } // Generate Order PDF and Upload to Dropbox function generateOrder() { const { jsPDF } = window.jspdf; const pdf = new jsPDF(); const orderID = document.getElementById('orderIDField').value; const clientName = document.querySelector('input[name="name"]').value || 'Cliente Desconocido'; const clientEmail = document.querySelector('input[name="email"]').value || 'No Email'; const clientPhone = document.querySelector('input[name="phone"]').value || 'No Phone'; const clientrtn = document.querySelector('input[name="rtn"]').value const currentDate = new Date().toLocaleDateString(); const currentTime = new Date().toLocaleTimeString(); const itemCount = document.getElementById('itemCountDiv1').textContent; const cartTotal = document.getElementById('cartTotal').textContent; const discount = document.getElementById('discountDiv').textContent || '0 HNL'; // Add Image to Header (if available) if (paperImageBase64) { pdf.addImage(paperImageBase64, 'PNG', 12, 15, 30, 30); // (x, y, width, height) } // HEADER (No space between consecutive lines) let yHeader = 20; pdf.setFontSize(18); pdf.text("Color Demo", 105, yHeader, { align: "center" }); pdf.setFontSize(14); pdf.text("Orden de Compra", 105, yHeader + 7, { align: "center" }); pdf.setFontSize(12); yHeader += 13; // Slightly larger space after the title pdf.text("Cel.: Pendiente", 105, yHeader, { align: "center" }); pdf.text("email: colordemo89@gmail.com", 105, yHeader + 5, { align: "center" }); // +5 for tight spacing // Space below the header block yHeader += 30; // ORDER DETAILS pdf.text(`Orden: ${orderID}`, 15, yHeader); pdf.text(`Fecha: ${currentDate}`, 160, yHeader); pdf.text(`Hora: ${currentTime}`, 162, yHeader + 5); // === CLIENT INFO (Tighter grouping) === let yClient = yHeader + 15; pdf.setFontSize(14); pdf.text("Información del Cliente", 15, yClient); // Calculate the text width and position the underline const textWidth = pdf.getTextWidth("Información del Cliente:"); const underlineY = yClient + 1; // Slightly below the text pdf.line(15, underlineY, 15 + textWidth, underlineY); // (x1, y1, x2, y2) pdf.setFontSize(12); yClient += 8; // Small gap after section title pdf.text(`Nombre: ${clientName}`, 15, yClient); pdf.text(`Correo: ${clientEmail}`, 15, yClient + 5); // Tight spacing pdf.text(`RTN: ${clientrtn}`, 15, yClient + 10); // Tight spacing pdf.text(`Teléfono: ${clientPhone}`, 15, yClient + 15); // Tight spacing // === TABLE HEADER === let yTable = yClient + 30; pdf.setFontSize(14); pdf.text("Resumen del Pedido", 15, yTable - 3); // Calculate the text width and position the underline const textWidth2 = pdf.getTextWidth("Resumen del Pedido"); const underlineY2 = yTable - 2; // Slightly below the text pdf.line(15, underlineY2, 15 + textWidth2, underlineY2); // (x1, y1, x2, y2) pdf.setFontSize(12); pdf.setFillColor(240, 240, 240); pdf.rect(15, yTable, 180, 10, 'F'); pdf.text("Código", 18, yTable + 7); pdf.text("Color", 42, yTable + 7); pdf.text("Marca", 90, yTable + 7); pdf.text("Cantidad", 143, yTable + 7, { align: "right" }); pdf.text("P. Unitario", 170, yTable + 7, { align: "right" }); pdf.text("Total", 192, yTable + 7, { align: "right" }); yTable += 15; // === CART ITEMS === const pageHeight = pdf.internal.pageSize.height; const marginBottom = 30; cart.forEach(item => { const paintCode = item['Paint Code'] || 'N/A'; if (yTable + 10 > pageHeight - marginBottom) { pdf.addPage(); yTable = 30; pdf.setFillColor(240, 240, 240); pdf.rect(15, yTable, 180, 10, 'F'); pdf.text("Código", 18, yTable + 7); pdf.text("Color", 42, yTable + 7); pdf.text("Marca", 90, yTable + 7); pdf.text("Cantidad", 143, yTable + 7, { align: "right" }); pdf.text("P. Unitario", 170, yTable + 7, { align: "right" }); pdf.text("Total", 192, yTable + 7, { align: "right" }); yTable += 15; } pdf.text(`${paintCode}`, 18, yTable); pdf.text(`${item['Paint Name']}`, 42, yTable); pdf.text(`${item['Brand']}`, 90, yTable); pdf.text(`x${item.quantity}`, 143, yTable, { align: "right" }); pdf.text(`${item['Price']} HNL`, 170, yTable, { align: "right" }); pdf.text(`${item['Price'] * item.quantity} HNL`, 192, yTable, { align: "right" }); yTable += 10; }); // === FOOTER === yTable += 10; pdf.line(15, yTable, 195, yTable); yTable += 10; const rightMargin = 195; const discountWidth = pdf.getTextWidth(`${discount}`); const totalWidth = pdf.getTextWidth(cartTotal); pdf.setFontSize(12); pdf.text(`${discount}`, rightMargin - discountWidth, yTable); pdf.setFont("helvetica", "bold"); pdf.text(cartTotal, rightMargin - totalWidth, yTable + 6); pdf.setTextColor(255, 0, 0); pdf.setFontSize(14); pdf.text("VERIFICANDO PAGO", rightMargin -50, yTable + 13); pdf.setTextColor(0, 0, 0); pdf.setFont("helvetica", "normal"); pdf.text(`Cantidad Total: ${itemCount}`, 15, yTable + 6); // === THANK YOU MESSAGE === yTable += 18; pdf.setFontSize(11); pdf.setTextColor(100); pdf.text("¡Gracias! Estamos procesando su orden.", 105, yTable, { align: "center" }); // SAVE PDF LOCALLY const pdfFileName = `${orderID}_orden.pdf`; const pdfBlob = pdf.output("blob"); // UPLOAD PDF TO DROPBOX uploadInvoiceToDropbox(pdfBlob, pdfFileName, orderID); Toastify({ text: "Su orden se está procesando...", duration: 5000, gravity: "middle", position: "center" }).showToast(); // Create a URL from the blob const pdfURL = URL.createObjectURL(pdfBlob); // Open the PDF in a new window/tab window.open(pdfURL, '_blank'); // Optional: Revoke the object URL after some time to free memory setTimeout(() => URL.revokeObjectURL(pdfURL), 10000); } async function bankaccount(event) { event.preventDefault(); // Find the form associated with the clicked button const form = document.getElementById('checkoutForm'); // Use a specific ID for the form if (!form) { console.error("Form element not found."); return; } const formData = new FormData(form); // Validate inputs const from_name = formData.get('name')?.trim(); const email = formData.get('email')?.trim(); const phone = formData.get('phone')?.trim(); if (!from_name || !email || !phone) { Toastify({ text: "Favor llenar los campos de Nombre, correo y teléfono.", duration: 3000, gravity: "middle", position: "center" }).showToast(); } else { Toastify({ text: "Su solicitud fue enviada. Favor revisar su correo en bandeja de entrada o spam (correo no deseado). ¡Gracias!", duration: 5000, gravity: "middle", position: "center" }).showToast(); } const emailParams = { from_name: from_name, email: email, phone: phone, }; try { const response = await fetch("https://script.google.com/macros/s/AKfycbzPqIWRPykp3ycPAoTNpyj3W7Hv_4Fwv3cy-9TWivdEMtmKFGA2S1WNZ7TVmrrG1SZaFA/exec", { method: 'POST', mode: 'no-cors', body: JSON.stringify(emailParams), // Uncomment if needed // headers: { // 'Content-Type': 'application/json' // } }); console.log("Email sent successfully via Google Apps Script!"); } catch (error) { console.error("Failed to send email via Apps Script:", error); } } // Logo on PDF // Load image as Base64 and generate PDF let paperImageBase64 = 'data:image/jpeg;base64,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'; // Convert image to Base64 function loadImage() { const img = new Image(); img.src = 'images/paper.png'; img.crossOrigin = 'Anonymous'; // Prevent CORS issues if served remotely img.onload = function () { const canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); paperImageBase64 = canvas.toDataURL('image/png'); liveGenerateInvoice(); // Generate PDF after loading image }; } // Event Listeners window.onload = () => loadProducts(); window.addEventListener('resize', () => { calculateItemsPerPage(); displayPage(currentPage); }); window.onload = () => { console.log("Triggering CSV test upload..."); appendOrderToCSV('CD-TEST-123', new FormData(), '1500 HNL', '200 HNL', '3'); }; document.getElementById('checkoutForm').addEventListener('submit', handleCheckout); document.getElementById('checkoutForm').addEventListener('accounts', bankaccount); function toggleCart() { const cartSidebar = document.getElementById('cartSidebar'); if (cartSidebar.classList.contains('open')) { cartSidebar.classList.remove('open'); } else { cartSidebar.classList.add('open'); } } // Filter products based on user input function filterProducts() { const query = document.getElementById('searchInput').value.toLowerCase(); filteredProducts = products.filter(product => String(product['Paint Name'] || '').toLowerCase().includes(query) || String(product['Brand'] || '').toLowerCase().includes(query) || String(product['Paint Code'] || '').toLowerCase().includes(query) || String(product['Combinaciones'] || '').toLowerCase().includes(query) || String(product['Disenador'] || '').toLowerCase().includes(query) ); currentPage = 1; // Reset to the first page displayPage(currentPage); // Show filtered results } // Make precio disappear document.addEventListener('DOMContentLoaded', () => { const ribbon = document.querySelector('.precio'); let lastScrollPosition = 0; window.addEventListener('scroll', () => { const currentScrollPosition = window.scrollY; if (currentScrollPosition > lastScrollPosition) { // Scrolling down ribbon.classList.add('hidden'); } else { // Scrolling up ribbon.classList.remove('hidden'); } lastScrollPosition = currentScrollPosition; }); }); // Calculate products top position window.addEventListener('load', () => { const precioHeight = document.querySelector('.precio').offsetHeight; document.querySelector('.products').style.marginTop = `${75 + precioHeight + 20}px`; }); window.addEventListener('load', adjustMargins); window.addEventListener('resize', adjustMargins); function adjustMargins() { const headerHeight = document.querySelector('header').offsetHeight; const calculatedMargin = headerHeight + 20 + 'px'; // Header height + additional spacing document.querySelector('.search-container').style.marginTop = calculatedMargin; document.querySelector('.products-wrapper').style.marginTop = calculatedMargin; } function updateItemCount(count) { const itemCountDiv1 = document.getElementById('itemCountDiv1'); const itemCountDiv2 = document.getElementById('itemCountDiv2'); // Update both elements with the same content itemCountDiv1.innerHTML = count; itemCountDiv2.innerHTML = count; } // Set a cookie with name, value, and expiration (in days) function setCookie(name, value, days) { const date = new Date(); date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); document.cookie = `${name}=${encodeURIComponent(value)};expires=${date.toUTCString()};path=/`; } // Get a cookie by name function getCookie(name) { const cookies = document.cookie.split('; '); for (let cookie of cookies) { const [key, value] = cookie.split('='); if (key === name) return decodeURIComponent(value); } return null; } function saveCartToCookies() { const cartString = JSON.stringify(cart); setCookie('cart', cartString, 7); // Cookie expires in 7 days } function loadCartFromCookies() { const cartString = getCookie('cart'); if (cartString) { cart = JSON.parse(cartString); updateCart(); } } function acceptCookies() { setCookie('cookieConsent', 'true', 365); // Consent valid for 1 year document.getElementById('cookieConsent').classList.add('hidden'); } function checkCookieConsent() { const consent = getCookie('cookieConsent'); if (!consent) { document.getElementById('cookieConsent').classList.remove('hidden'); } } // Create filters function createFilters() { const familySet = new Set(products.map(p => p.Family).filter(Boolean)); const typeSet = new Set(products.map(p => p.Type).filter(Boolean)); const brandSet = new Set(products.map(p => p.Brand).filter(Boolean)); // Extract unique values for Brand const filterContainer = document.createElement('div'); filterContainer.id = 'filterMenu'; filterContainer.style.display = 'none'; // Hidden by default filterContainer.style.position = 'absolute'; filterContainer.style.background = 'white'; filterContainer.style.border = '1px solid #ccc'; filterContainer.style.padding = '10px'; filterContainer.style.zIndex = '1000'; const familyFilter = createFilterSection('Family', [...familySet]); const typeFilter = createFilterSection('Type', [...typeSet]); const brandFilter = createFilterSection('Brand', [...brandSet]); // Add filter section for Brand filterContainer.appendChild(familyFilter); filterContainer.appendChild(typeFilter); filterContainer.appendChild(brandFilter); // Append Brand filter document.body.appendChild(filterContainer); const searchInput = document.getElementById('searchInput'); const adjustFilterPosition = () => { const rect = searchInput.getBoundingClientRect(); filterContainer.style.top = `${rect.bottom + window.scrollY}px`; filterContainer.style.left = `${rect.left + window.scrollX}px`; filterContainer.style.width = `${rect.width}px`; // Match the search box width }; searchInput.addEventListener('focus', () => { adjustFilterPosition(); filterContainer.style.display = 'block'; }); window.addEventListener('resize', adjustFilterPosition); // Adjust on window resize window.addEventListener('scroll', adjustFilterPosition); // Adjust on page scroll document.addEventListener('click', (event) => { if (!filterContainer.contains(event.target) && event.target.id !== 'searchInput') { filterContainer.style.display = 'none'; } }); } function createFilterSection(title, options) { const section = document.createElement('div'); section.style.marginBottom = '10px'; const label = document.createElement('strong'); label.textContent = `${title}:`; section.appendChild(label); options.forEach(option => { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.value = option; checkbox.addEventListener('change', applyFilters); const span = document.createElement('span'); span.textContent = ` ${option}`; const wrapper = document.createElement('div'); wrapper.appendChild(checkbox); wrapper.appendChild(span); section.appendChild(wrapper); }); return section; } function applyFilters() { const selectedFamilies = Array.from(document.querySelectorAll('#filterMenu [type="checkbox"][value]')) .filter(checkbox => checkbox.checked && checkbox.parentElement.parentElement.querySelector('strong').textContent.includes('Family')) .map(checkbox => checkbox.value); const selectedTypes = Array.from(document.querySelectorAll('#filterMenu [type="checkbox"][value]')) .filter(checkbox => checkbox.checked && checkbox.parentElement.parentElement.querySelector('strong').textContent.includes('Type')) .map(checkbox => checkbox.value); const selectedBrands = Array.from(document.querySelectorAll('#filterMenu [type="checkbox"][value]')) .filter(checkbox => checkbox.checked && checkbox.parentElement.parentElement.querySelector('strong').textContent.includes('Brand')) .map(checkbox => checkbox.value); filteredProducts = products.filter(product => (selectedFamilies.length === 0 || selectedFamilies.includes(product.Family)) && (selectedTypes.length === 0 || selectedTypes.includes(product.Type)) && (selectedBrands.length === 0 || selectedBrands.includes(product.Brand)) ); currentPage = 1; displayPage(currentPage); } async function refreshDropboxToken() { const dropboxTokenUrl = "https://api.dropboxapi.com/oauth2/token"; const credentials = `${DROPBOX_CONFIG.clientId}:${DROPBOX_CONFIG.clientSecret}`; const encodedCredentials = btoa(credentials); const requestOptions = { method: 'POST', headers: { 'Authorization': `Basic ${encodedCredentials}`, 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: DROPBOX_CONFIG.refreshToken }) }; const response = await fetch(dropboxTokenUrl, requestOptions); const data = await response.json(); if (data.access_token) { DROPBOX_CONFIG.accessToken = data.access_token; console.log("New Dropbox Access Token Acquired!"); } else { console.error("Failed to refresh Dropbox token:", data); console.log("Failed to refresh Dropbox token. Check console for details."); } } // Upload Invoice async function uploadInvoiceToDropbox(pdfBlob, fileName, orderID) { await refreshDropboxToken(); // Ensure token is refreshed const accessToken = DROPBOX_CONFIG.accessToken; // Paths for both folders const folderPath1 = `/ColorDemoOrders/pendingorders/${orderID}/${fileName}`; const folderPath2 = `/ColorDemoOrders/history/${fileName}`; // New history folder // Upload to primary folder const response1 = await fetch('https://content.dropboxapi.com/2/files/upload', { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/octet-stream', 'Dropbox-API-Arg': JSON.stringify({ path: folderPath1, mode: 'add', autorename: true, mute: false }) }, body: pdfBlob }); const result1 = await response1.json(); if (result1.error) { console.error("Error uploading PDF invoice to main Dropbox folder:", result1.error); console.log("Failed to upload PDF to Dropbox."); } else { console.log("PDF invoice uploaded successfully to main folder:", result1.path_display); } // Upload to history folder const response2 = await fetch('https://content.dropboxapi.com/2/files/upload', { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/octet-stream', 'Dropbox-API-Arg': JSON.stringify({ path: folderPath2, mode: 'add', autorename: true, mute: false }) }, body: pdfBlob }); const result2 = await response2.json(); if (result2.error) { console.error("Error uploading PDF invoice to history Dropbox folder:", result2.error); console.log("Failed to upload PDF to history folder."); } else { console.log("PDF invoice uploaded successfully to history folder:", result2.path_display); } } // Upload Order async function uploadOrderToDropbox(pdfBlob, fileName, orderID) { await refreshDropboxToken(); // Ensure token is refreshed const accessToken = DROPBOX_CONFIG.accessToken; // Paths for both folders const folderPath1 = `/ColorDemoOrders/pendingorders/${orderID}/${fileName}`; const folderPath2 = `/ColorDemoOrders/history/${fileName}`; // New history folder // Upload to primary folder const response1 = await fetch('https://content.dropboxapi.com/2/files/upload', { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/octet-stream', 'Dropbox-API-Arg': JSON.stringify({ path: folderPath1, mode: 'add', autorename: true, mute: false }) }, body: pdfBlob }); const result1 = await response1.json(); if (result1.error) { console.error("Error uploading PDF invoice to main Dropbox folder:", result1.error); console.log("Failed to upload PDF to Dropbox."); } else { console.log("PDF invoice uploaded successfully to main folder:", result1.path_display); } // Upload to history folder const response2 = await fetch('https://content.dropboxapi.com/2/files/upload', { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/octet-stream', 'Dropbox-API-Arg': JSON.stringify({ path: folderPath2, mode: 'add', autorename: true, mute: false }) }, body: pdfBlob }); const result2 = await response2.json(); if (result2.error) { console.error("Error uploading PDF invoice to history Dropbox folder:", result2.error); console.log("Failed to upload PDF to history folder."); } else { console.log("PDF invoice uploaded successfully to history folder:", result2.path_display); } } // Helper function to convert blob to base64 function blobToBase64(blob) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result.split(",")[1]); // Get only the Base64 part reader.onerror = reject; reader.readAsDataURL(blob); }); } // Reset Cart function clearCart() { cart = []; // Empty the cart array updateCart(); // Update the cart display Toastify({ text: "El carrito ha sido vaciado.", duration: 3000, gravity: "top", position: "center" }).showToast(); } window.onload = function() { // Extract the query parameter from the URL const urlParams = new URLSearchParams(window.location.search); const keyword = urlParams.get('filter'); // Get the 'filter' parameter if (keyword) { // Set the search input value to the keyword document.getElementById('searchInput').value = keyword; // Optionally, set the keyword as the placeholder document.getElementById('searchInput').setAttribute("placeholder", keyword); // Call the filter function to filter products based on the keyword filterProducts(); // This will use the value from the search input to filter } // Ensure products are loaded after page load (if needed) loadProducts(); // Call loadProducts to load all products if no filter is applied checkCookieConsent(); loadCartFromCookies(); // history.replaceState(null, '', '/muestras'); }; // Open Quick View Modal function openQuickView(element) { const modal = document.getElementById("quick-view-modal"); const modalSwatch = document.getElementById("modal-swatch"); // Target the new swatch div const modalName = document.getElementById("modal-name"); const modalId = document.getElementById("modal-id"); const modalbrand = document.getElementById("modal-brand"); const modalfamily = document.getElementById("modal-family"); const modalDescription = document.getElementById("modal-description"); // Get the data from the clicked swatch const hexColor = element.style.background; // Extract the swatch color modalSwatch.style.background = hexColor; // Apply it to the modal modalName.textContent = element.dataset.name; modalId.textContent = "Código: " + element.dataset.id; modalbrand.textContent = "Marca: " + element.dataset.brand; modalfamily.textContent = "Familia: " + element.dataset.family; modalDescription.textContent = "Descripción: " + element.dataset.desc; // Show modal modal.style.display = "flex"; } // Close Modal document.querySelector(".close").addEventListener("click", function () { document.getElementById("quick-view-modal").style.display = "none"; }); // Close Quick View Modal and Cart Sidebar when clicking outside window.addEventListener("click", function (event) { const modal = document.getElementById("quick-view-modal"); const cartsideb = document.getElementById("cartSidebar"); // Close Quick View Modal if (event.target === modal) { modal.style.display = "none"; } // Prevent closing the cart when clicking inside it or on control buttons if ( !cartsideb.contains(event.target) && !event.target.closest(".cart-button") && // Prevent closing when clicking cart button !event.target.closest(".quantity") && // Prevent closing when clicking quantity buttons !event.target.closest(".remove-btn") && // Prevent closing when clicking remove button !event.target.closest(".quantity-btn") // Prevent closing when clicking + or - ) { cartsideb.classList.remove("open"); } }); // Manual Checkout document.addEventListener("DOMContentLoaded", function () { const manualCheckoutCheckbox = document.querySelector("input[name='manualcheckout']"); const pagarButton = document.getElementById("pagarButton"); const whatsappButton = document.getElementById("whatsappButton"); const checkoutForm = document.getElementById("checkoutForm"); const cartItems = document.getElementById("cartItems"); const cartTotal = document.getElementById("cartTotal"); const requiredFields = [...checkoutForm.querySelectorAll("input[required], textarea[required]")]; const paymentProofInput = document.querySelector("input[name='paymentProof']"); const paymentProofSection = document.querySelector("h3[name='PmntPr']"); // Select using name attribute // Function to reset the cart function resetCart() { cartItems.innerHTML = ""; // Clear cart items cartTotal.innerText = "Total: 0.00 HNL"; // Reset total price checkConditions(); // Recheck conditions to disable buttons } // Function to toggle payment proof requirement function togglePaymentProof() { if (manualCheckoutCheckbox.checked) { paymentProofInput.required = false; paymentProofInput.disabled = true; paymentProofInput.style.display = "none"; if (paymentProofSection) { paymentProofSection.style.display = "none"; // Hide the entire section } } else { paymentProofInput.required = true; paymentProofInput.disabled = false; paymentProofInput.style.display = "block"; if (paymentProofSection) { paymentProofSection.style.display = "block"; // Show the entire section } } } // Function to check if all required conditions are met function checkConditions() { const isManualCheckout = manualCheckoutCheckbox.checked; const isCartEmpty = cartItems.children.length === 0; // Check required fields and payment proof if not in manual checkout mode const requiredInputs = requiredFields.filter(field => field !== paymentProofInput); const allFieldsFilled = requiredInputs.every(field => field.value.trim() !== ""); // If manual checkout is OFF, payment proof is required const isPaymentProofValid = isManualCheckout || paymentProofInput.files.length > 0; console.log("Cart Empty:", isCartEmpty, "| All Fields Filled:", allFieldsFilled, "| Manual Checkout:", isManualCheckout, "| Payment Proof Valid:", isPaymentProofValid); // Enable or disable both buttons pagarButton.disabled = isCartEmpty || !allFieldsFilled || !isPaymentProofValid; whatsappButton.disabled = isCartEmpty || !allFieldsFilled; if (isManualCheckout) { pagarButton.style.display = "none"; whatsappButton.style.display = "inline-block"; } else { pagarButton.style.display = "inline-block"; whatsappButton.style.display = "none"; } // Force-enable the WhatsApp button if conditions are met if (!whatsappButton.disabled) { whatsappButton.removeAttribute("disabled"); } } // Initialize button state and payment proof on page load checkConditions(); togglePaymentProof(); // Listen for checkbox changes to switch buttons and payment proof state manualCheckoutCheckbox.addEventListener("change", function () { togglePaymentProof(); checkConditions(); }); // Listen for changes in required form fields requiredFields.forEach(field => { field.addEventListener("input", checkConditions); }); // Listen for changes in payment proof upload paymentProofInput.addEventListener("change", checkConditions); // Monitor cart changes dynamically const observer = new MutationObserver(checkConditions); observer.observe(cartItems, { childList: true }); // Handle WhatsApp button click whatsappButton.addEventListener("click", async function (event) { event.preventDefault(); const isCartEmpty = cartItems.children.length === 0; const requiredInputs = requiredFields.filter(field => field !== paymentProofInput); const allFieldsFilled = requiredInputs.every(field => field.value.trim() !== ""); if (isCartEmpty) { Toastify({ text: "Su carrito está vacío. Agregue productos antes de solicitar ayuda.", duration: 3000, gravity: "middle", position: "center" }).showToast(); return; } if (!allFieldsFilled) { Toastify({ text: "Por favor, complete todos los campos requeridos.", duration: 3000, gravity: "middle", position: "center" }).showToast(); return; } const formData = new FormData(checkoutForm); const apiKey = "7215694"; // Your CallMeBot API Key // Get the subscription preference const wantsNewsletter = formData.get("subscribe") ? "Sí" : "No"; // Gather order details // Extract cart items with quantities let cartDetails = cart.map(item => { const itemName = item["Paint Name"] || "Producto Desconocido"; const itemCode = item["Paint Code"] || "N/A"; const quantity = item.quantity || 1; return `📌 ${itemName} (${itemCode}) - x ${quantity}`; }).join("\n"); // Get discount value const discountElement = document.getElementById("discountDiv"); const discount = discountElement ? discountElement.innerText : "0 HNL"; // Gather base order details const baseOrderDetails = ` 👤 Nombre: ${formData.get("name")}\n 📧 Email: ${formData.get("email")}\n 📞 Teléfono: ${formData.get("phone")}\n 📍 Dirección: ${formData.get("address")}\n 🏙️ Ciudad: ${formData.get("city")}, ${formData.get("department")}\n`; // Complete order text const fullOrderText = `¡Hola! Favor procesar mi pedido. 📦\n\n${baseOrderDetails}\n🛍️ Pedido:\n${cartDetails}\n💰 ${document.getElementById("cartTotal").innerText}\n🎉 ${discount}\n📩 Recibir noticias y ofertas: ${wantsNewsletter}\n📝 Favor indicar el método de pago.`; // **Encode the message for the WhatsApp URL** const encodedMessage = encodeURIComponent(fullOrderText); // **Detect device type & screen width** const isMobileOrTablet = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent); const isNarrowScreen = window.innerWidth <= 768; // Mobile screens // **Use WhatsApp Web for Desktop & WhatsApp App for Mobile** const phoneNumber = "50431811871"; // Your WhatsApp Number (without +) const whatsappMobileURL = `whatsapp://send?phone=${phoneNumber}&text=${encodedMessage}`; const whatsappWebURL = `https://web.whatsapp.com/send?phone=${phoneNumber}&text=${encodedMessage}`; function openWhatsApp() { // **Decide if it should open in the same tab or new tab** let openInNewTab = !isNarrowScreen; // Open in new tab for tablets & desktops // **Pre-open a tab only if it's a wide screen** let newTab = openInNewTab ? window.open("", "_blank") : null; // Show message before opening WhatsApp Toastify({ text: "Redirigiendo a WhatsApp. Color Demo nunca te pedirá tu nombre de usuario ni tus contraseñas...", duration: 2000, gravity: "middle", position: "center" }).showToast(); setTimeout(() => { if (!openInNewTab) { // **Mobile: Force immediate navigation to WhatsApp (no confirmation)** setTimeout(() => { window.location.href = whatsappMobileURL; }, 100); } else { newTab.location.href = whatsappWebURL; } // **Reset form after opening WhatsApp** checkoutForm.reset(); manualCheckoutCheckbox.checked = false; checkConditions(); togglePaymentProof(); cart = []; // Empty the cart array updateCart(); // Update the cart display toggleCart(); }, 2000); } // **Trigger WhatsApp Opening** openWhatsApp(); }); });