From be36d6aa3cd2d39816db745387c365ce077c1f44 Mon Sep 17 00:00:00 2001 From: George Birikorang Date: Wed, 10 Sep 2025 04:49:06 -0700 Subject: [PATCH] feat: enhance product comparison functionality with Add To Quote integration and initialization checks --- scripts/main.js | 205 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 193 insertions(+), 12 deletions(-) diff --git a/scripts/main.js b/scripts/main.js index 1860190..6b2a10d 100644 --- a/scripts/main.js +++ b/scripts/main.js @@ -1054,6 +1054,13 @@ function initSite() { function initProductComparison() { console.log("=== INITIALIZING PRODUCT COMPARISON ==="); + // Prevent running twice (it's invoked in multiple places) + if (window.__cmpInitialized) { + console.log("[Comparison] Already initialized, skipping"); + return; + } + window.__cmpInitialized = true; + // Get product IDs from URL parameters const urlParams = new URLSearchParams(window.location.search); const product1Id = urlParams.get("product1"); @@ -1061,6 +1068,10 @@ function initProductComparison() { console.log("URL Parameters:", { product1Id, product2Id }); + // Keep references to the two products for event handlers + let cmpProduct1 = null; + let cmpProduct2 = null; + // Fetch product data fetch("data/products.json") .then((response) => { @@ -1080,12 +1091,14 @@ function initProductComparison() { // Update product cards if (product1) { console.log("Updating product card 1 with:", product1.name); + cmpProduct1 = product1; updateProductCard(1, product1); updateComparisonTable(product1, 1); } if (product2) { console.log("Updating product card 2 with:", product2.name); + cmpProduct2 = product2; updateProductCard(2, product2); updateComparisonTable(product2, 2); } @@ -1095,12 +1108,113 @@ function initProductComparison() { // Update View More link with current comparison state updateViewMoreLink(product1Id, product2Id); + + // Wire Add To Quote buttons (first -> product1, second -> product2) + wireComparisonAddToQuote(cmpProduct1, cmpProduct2); }) .catch((error) => { console.error("Error loading products:", error); }); } +// Bind the two Add To Quote buttons on the comparison page +function wireComparisonAddToQuote(prod1, prod2) { + try { + const addButtons = Array.from(document.querySelectorAll("button")).filter( + (b) => (b.textContent || "").trim() === "Add To Quote" + ); + + if (addButtons.length === 0) { + console.log("[Comparison] No Add To Quote buttons found"); + return; + } + + // Ensure deterministic order: the first encountered is for product 1 + const btn1 = addButtons[0] || null; + const btn2 = addButtons[1] || null; + + function toQuotePayload(p) { + if (!p) return null; + return { + id: Number(p.id), + name: p.name || "Product", + image: p.image || "", + color: + (p.colors && + p.colors[0] && + (p.colors[0].name || p.colors[0].value)) || + "Default", + size: (p.sizes && p.sizes[0]) || "Standard", + quantity: 1, + }; + } + + if (btn1) { + btn1.type = btn1.getAttribute("type") || "button"; + btn1.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + const payload = toQuotePayload(prod1); + if (!payload) return; + payload.quantity = 1; // force 1 for comparison adds + if (typeof addToQuote === "function") { + addToQuote(payload); + } else { + addToQuoteFallback(payload); + } + }); + } + + if (btn2) { + btn2.type = btn2.getAttribute("type") || "button"; + btn2.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + const payload = toQuotePayload(prod2); + if (!payload) return; + payload.quantity = 1; // force 1 for comparison adds + if (typeof addToQuote === "function") { + addToQuote(payload); + } else { + addToQuoteFallback(payload); + } + }); + } + + // Delegated fallback (in case DOM changes after load) + document.addEventListener("click", (e) => { + const t = e.target.closest && e.target.closest("button"); + if (!t) return; + const label = (t.textContent || "").trim(); + if (label !== "Add To Quote") return; + // Determine which button index this is relative to current NodeList + const currentButtons = Array.from( + document.querySelectorAll("button") + ).filter((b) => (b.textContent || "").trim() === "Add To Quote"); + const idx = currentButtons.indexOf(t); + if (idx === 0) { + const payload = toQuotePayload(prod1); + if (payload) { + payload.quantity = 1; + (typeof addToQuote === "function" ? addToQuote : addToQuoteFallback)( + payload + ); + } + } else if (idx === 1) { + const payload = toQuotePayload(prod2); + if (payload) { + payload.quantity = 1; + (typeof addToQuote === "function" ? addToQuote : addToQuoteFallback)( + payload + ); + } + } + }); + } catch (err) { + console.error("[Comparison] Failed to wire Add To Quote buttons:", err); + } +} + function updateProductCard(slotNumber, product) { console.log(`=== UPDATING PRODUCT CARD ${slotNumber} ===`); console.log("Product data:", product); @@ -1537,20 +1651,59 @@ function initQuoteBadge() { function initAddToQuote() { const addToQuoteBtn = document.getElementById("add-to-quote-btn"); if (addToQuoteBtn) { - addToQuoteBtn.addEventListener("click", function () { - // Get product data from the page + try { + // Ensure button is not treated as a submit in case inside a form + if (!addToQuoteBtn.getAttribute("type")) { + addToQuoteBtn.setAttribute("type", "button"); + } + + addToQuoteBtn.addEventListener("click", function (e) { + e.preventDefault(); + e.stopPropagation(); + console.log("[AddToQuote] Direct click captured"); + + const productData = getProductDataFromPage(); + console.log("[AddToQuote] productData:", productData); + if (productData) { + if (typeof addToQuote === "function") { + addToQuote(productData); + } else { + addToQuoteFallback(productData); + } + } else { + console.warn( + "[AddToQuote] No product data found. Check URL id and DOM." + ); + } + }); + } catch (err) { + console.error("[AddToQuote] Failed to bind direct listener:", err); + } + } + + // Delegated fallback in case the button is replaced dynamically + document.addEventListener("click", function (e) { + const btn = e.target.closest && e.target.closest("#add-to-quote-btn"); + if (!btn) return; + e.preventDefault(); + e.stopPropagation(); + console.log("[AddToQuote] Delegated click captured"); + try { const productData = getProductDataFromPage(); + console.log("[AddToQuote][delegated] productData:", productData); if (productData) { - // Add to quote using the global function if (typeof addToQuote === "function") { addToQuote(productData); } else { - // Fallback: directly use localStorage addToQuoteFallback(productData); } + } else { + console.warn("[AddToQuote][delegated] No product data found."); } - }); - } + } catch (err) { + console.error("[AddToQuote][delegated] Error handling click:", err); + } + }); } // Get product data from the current page @@ -1569,12 +1722,40 @@ function getProductDataFromPage() { const selectedSize = getSelectedSize(); const selectedQuantity = getSelectedQuantity(); - // Get product name and image (you might need to adjust these selectors) - const productName = document.querySelector("h1")?.textContent || "Product"; - const productImage = - document.querySelector(".w-\\[500px\\].h-\\[500px\\] img")?.src || - document.querySelector(".w-[500px].h-[500px] img")?.src || - ""; + // Get product name and a best-effort product image + const productName = + document.querySelector("h1")?.textContent?.trim() || "Product"; + let productImage = ""; + + // Helper to guard against invalid selectors (e.g., unescaped brackets) + function qsSafe(selector) { + try { + return document.querySelector(selector); + } catch (e) { + return null; + } + } + + // Try a series of selectors safely + const imageCandidates = [ + ".w-\\[500px\\].h-\\[500px\\] img", + ".w-[500px].h-[500px] img", + ".bg-floral-white img", + "section img[alt]", + ]; + + for (const sel of imageCandidates) { + const el = qsSafe(sel); + if (el && el.src) { + productImage = el.src; + break; + } + } + + if (!productImage) { + const anyImg = qsSafe("img[alt]") || qsSafe("section img") || qsSafe("img"); + productImage = anyImg?.src || ""; + } return { id: parseInt(productId),