feat: enhance product comparison functionality with Add To Quote integration and initialization checks
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
George Birikorang 2025-09-10 04:49:06 -07:00
parent 6c817cbd20
commit be36d6aa3c

View file

@ -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),