(async function () { const container = document.querySelector("[data-product-loader]"); if (!container) return; const handle = container.dataset.shopifyHandle; const language = container.dataset.language || "fr"; const isEnglish = language === "en"; const loadingState = container.querySelector(".product-loading"); const contentState = container.querySelector(".product-content"); const errorState = container.querySelector(".product-error"); try { const cart = new ShopifyCart({ domain: "nv7cqv-bu.myshopify.com", storefrontAccessToken: "dec3d35a2554384d149c72927d1cfd1b", }); const product = await cart.getProductByHandle(handle); if (!product) { throw new Error("Product not found"); } renderProduct(product, isEnglish); updateMetaTags(product, isEnglish); loadingState.style.display = "none"; contentState.removeAttribute("style"); setTimeout(() => { if (typeof Swiper !== "undefined" && product.images.edges.length > 0) { new Swiper(".product-gallery", { loop: product.images.edges.length > 1, navigation: { nextEl: ".swiper-button-next", prevEl: ".swiper-button-prev", }, pagination: { el: ".swiper-pagination", clickable: true, }, keyboard: { enabled: true, }, }); } }, 100); } catch (error) { console.error("Error loading product:", error); loadingState.style.display = "none"; errorState.style.display = "block"; } function renderProduct(product, isEnglish) { renderTitle(product, isEnglish); renderPrice(product); renderDetails(product, isEnglish); renderImages(product, isEnglish); renderOptions(product); setupAddToCart(product); } function renderTitle(product, isEnglish) { const titleEl = document.querySelector("[data-product-title]"); if (titleEl) { const title = isEnglish && product.titleEn?.value ? product.titleEn.value : product.title; titleEl.textContent = title; } } function renderPrice(product) { const priceEl = document.querySelector("[data-product-price]"); if (priceEl) { const price = parseFloat(product.priceRange.minVariantPrice.amount); priceEl.textContent = price.toFixed(2) + "€"; } } function renderDetails(product, isEnglish) { const detailsEl = document.querySelector("[data-product-details]"); if (detailsEl) { const description = isEnglish && product.descriptionEn?.value ? product.descriptionEn.value.replaceAll("\n", "
") : product.descriptionHtml || ""; detailsEl.innerHTML = description; } } function renderImages(product, isEnglish) { const imagesContainer = document.querySelector("[data-product-images]"); if (imagesContainer && product.images.edges.length > 0) { const productTitle = isEnglish && product.titleEn?.value ? product.titleEn.value : product.title; imagesContainer.innerHTML = product.images.edges .map((edge) => { const img = edge.node; return `
${img.altText || productTitle}
`; }) .join(""); } } function renderOptions(product) { if (product.variants.edges.length <= 1) return; const firstVariant = product.variants.edges[0].node; if ( !firstVariant.selectedOptions || firstVariant.selectedOptions.length === 0 ) return; const mainOption = firstVariant.selectedOptions[0]; const optionValues = new Set(); product.variants.edges.forEach((edge) => { const variant = edge.node; if (variant.selectedOptions && variant.selectedOptions[0]) { optionValues.add(variant.selectedOptions[0].value); } }); if (optionValues.size <= 1) return; const optionsContainer = document.querySelector("[data-product-options]"); const optionsList = document.querySelector("[data-product-options-list]"); if (!optionsContainer || !optionsList) return; const optionName = mainOption.name; const optionSlug = optionName.toLowerCase().replace(/\s+/g, "-"); optionsList.innerHTML = Array.from(optionValues) .map((value) => { const uniqueId = `${optionSlug}-${value .toLowerCase() .replace(/\s+/g, "-")}`; const variant = product.variants.edges.find( (e) => e.node.selectedOptions && e.node.selectedOptions[0]?.value === value )?.node; const isAvailable = variant?.availableForSale || false; return `
  • `; }) .join(""); optionsContainer.style.display = "block"; const radios = optionsList.querySelectorAll('input[type="radio"]'); const addToCartBtn = document.querySelector("[data-shopify-add-to-cart]"); const buttonText = addToCartBtn?.querySelector("[data-button-text]"); radios.forEach((radio) => { radio.addEventListener("change", function () { const variantId = this.dataset.variantId; if (addToCartBtn) { addToCartBtn.dataset.variantId = variantId; addToCartBtn.removeAttribute("disabled"); } if (buttonText) { buttonText.textContent = addToCartBtn.dataset.defaultText || "Ajouter au panier"; } const allLi = optionsList.querySelectorAll("li"); allLi.forEach((li) => li.classList.remove("is-selected")); this.closest("li").classList.add("is-selected"); }); }); } function setupAddToCart(product) { const addToCartBtn = document.querySelector("[data-shopify-add-to-cart]"); if (!addToCartBtn) return; const productId = product.id.replace("gid://shopify/Product/", ""); addToCartBtn.dataset.productId = productId; const hasMultipleVariants = product.variants.edges.length > 1; const firstVariant = product.variants.edges[0]?.node; const hasOptions = firstVariant?.selectedOptions && firstVariant.selectedOptions.length > 0; const uniqueOptions = new Set(); product.variants.edges.forEach((edge) => { if (edge.node.selectedOptions && edge.node.selectedOptions[0]) { uniqueOptions.add(edge.node.selectedOptions[0].value); } }); const hasMultipleOptions = uniqueOptions.size > 1; if (hasMultipleVariants && hasOptions && hasMultipleOptions) { addToCartBtn.setAttribute("disabled", "disabled"); const buttonText = addToCartBtn.querySelector("[data-button-text]"); if (buttonText) { buttonText.textContent = addToCartBtn.dataset.textChooseOption || "Choisissez une option"; } } else { const firstAvailableVariant = product.variants.edges.find( (e) => e.node.availableForSale ); if (firstAvailableVariant) { const variantId = firstAvailableVariant.node.id.replace( "gid://shopify/ProductVariant/", "" ); addToCartBtn.dataset.variantId = variantId; } } } function updateMetaTags(product, isEnglish) { // Update title and description const title = isEnglish && product.titleEn?.value ? product.titleEn.value : product.title; const description = isEnglish && product.descriptionEn?.value ? product.descriptionEn.value : product.description; // Update Open Graph title const ogTitle = document.getElementById("og-title"); if (ogTitle) { ogTitle.setAttribute("content", title); } // Update Open Graph description const ogDescription = document.getElementById("og-description"); if (ogDescription && description) { const excerpt = description.substring(0, 160); ogDescription.setAttribute("content", excerpt); } // Update Open Graph image const ogImage = document.getElementById("og-image"); if (ogImage && product.images.edges.length > 0) { ogImage.setAttribute("content", product.images.edges[0].node.url); } // Update product price const ogPrice = document.getElementById("og-price"); if (ogPrice) { const price = parseFloat( product.priceRange.minVariantPrice.amount ).toFixed(2); ogPrice.setAttribute("content", price); } // Update availability const ogAvailability = document.getElementById("og-availability"); if (ogAvailability) { const availability = product.availableForSale ? "in stock" : "out of stock"; ogAvailability.setAttribute("content", availability); } // Update page title document.title = `${title} | Index.ngo`; // Update meta description let metaDescription = document.querySelector('meta[name="description"]'); if (metaDescription && description) { const excerpt = description.substring(0, 160); metaDescription.setAttribute("content", excerpt); } } })();