Compare commits
3 commits
f3f302513e
...
9b4bd4b731
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b4bd4b731 | ||
|
|
aa873e117f | ||
| 9396ae4e02 |
5 changed files with 166 additions and 63 deletions
|
|
@ -32,6 +32,9 @@
|
|||
|
||||
// Initialize event listeners
|
||||
setupEventListeners();
|
||||
|
||||
// Load initial cart state
|
||||
loadCart();
|
||||
}
|
||||
|
||||
function setupEventListeners() {
|
||||
|
|
@ -60,6 +63,18 @@
|
|||
});
|
||||
}
|
||||
|
||||
async function loadCart() {
|
||||
if (!cartInstance) return;
|
||||
|
||||
try {
|
||||
const cart = await cartInstance.getCart();
|
||||
currentCart = cart;
|
||||
renderCart();
|
||||
} catch (error) {
|
||||
console.error('Error loading cart:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function openDrawer() {
|
||||
drawer.classList.add('is-open');
|
||||
document.body.style.overflow = 'hidden';
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
if (!container) return;
|
||||
|
||||
const handle = container.dataset.shopifyHandle;
|
||||
const language = container.dataset.language || 'fr';
|
||||
const isEnglish = language === 'en';
|
||||
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");
|
||||
|
|
@ -63,7 +63,8 @@
|
|||
function renderTitle(product, isEnglish) {
|
||||
const titleEl = document.querySelector("[data-product-title]");
|
||||
if (titleEl) {
|
||||
const title = isEnglish && product.titleEn?.value
|
||||
const title =
|
||||
isEnglish && product.titleEn?.value
|
||||
? product.titleEn.value
|
||||
: product.title;
|
||||
titleEl.textContent = title;
|
||||
|
|
@ -81,8 +82,9 @@
|
|||
function renderDetails(product, isEnglish) {
|
||||
const detailsEl = document.querySelector("[data-product-details]");
|
||||
if (detailsEl) {
|
||||
const description = isEnglish && product.descriptionEn?.value
|
||||
? product.descriptionEn.value
|
||||
const description =
|
||||
isEnglish && product.descriptionEn?.value
|
||||
? product.descriptionEn.value.replace("\n", "<br><br>")
|
||||
: product.descriptionHtml || "";
|
||||
detailsEl.innerHTML = description;
|
||||
}
|
||||
|
|
@ -92,7 +94,8 @@
|
|||
const imagesContainer = document.querySelector("[data-product-images]");
|
||||
|
||||
if (imagesContainer && product.images.edges.length > 0) {
|
||||
const productTitle = isEnglish && product.titleEn?.value
|
||||
const productTitle =
|
||||
isEnglish && product.titleEn?.value
|
||||
? product.titleEn.value
|
||||
: product.title;
|
||||
|
||||
|
|
@ -117,12 +120,16 @@
|
|||
if (product.variants.edges.length <= 1) return;
|
||||
|
||||
const firstVariant = product.variants.edges[0].node;
|
||||
if (!firstVariant.selectedOptions || firstVariant.selectedOptions.length === 0) return;
|
||||
if (
|
||||
!firstVariant.selectedOptions ||
|
||||
firstVariant.selectedOptions.length === 0
|
||||
)
|
||||
return;
|
||||
|
||||
const mainOption = firstVariant.selectedOptions[0];
|
||||
const optionValues = new Set();
|
||||
|
||||
product.variants.edges.forEach(edge => {
|
||||
product.variants.edges.forEach((edge) => {
|
||||
const variant = edge.node;
|
||||
if (variant.selectedOptions && variant.selectedOptions[0]) {
|
||||
optionValues.add(variant.selectedOptions[0].value);
|
||||
|
|
@ -137,11 +144,15 @@
|
|||
if (!optionsContainer || !optionsList) return;
|
||||
|
||||
const optionName = mainOption.name;
|
||||
const optionSlug = optionName.toLowerCase().replace(/\s+/g, '-');
|
||||
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 =>
|
||||
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;
|
||||
|
|
@ -153,36 +164,42 @@
|
|||
id="${uniqueId}"
|
||||
name="${optionSlug}"
|
||||
value="${value}"
|
||||
data-variant-id="${variant ? variant.id.replace('gid://shopify/ProductVariant/', '') : ''}"
|
||||
${!isAvailable ? 'disabled' : ''}
|
||||
data-variant-id="${
|
||||
variant
|
||||
? variant.id.replace("gid://shopify/ProductVariant/", "")
|
||||
: ""
|
||||
}"
|
||||
${!isAvailable ? "disabled" : ""}
|
||||
/>
|
||||
<label for="${uniqueId}">${value}</label>
|
||||
</li>
|
||||
`;
|
||||
}).join('');
|
||||
})
|
||||
.join("");
|
||||
|
||||
optionsContainer.style.display = 'block';
|
||||
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]');
|
||||
const buttonText = addToCartBtn?.querySelector("[data-button-text]");
|
||||
|
||||
radios.forEach(radio => {
|
||||
radio.addEventListener('change', function() {
|
||||
radios.forEach((radio) => {
|
||||
radio.addEventListener("change", function () {
|
||||
const variantId = this.dataset.variantId;
|
||||
|
||||
if (addToCartBtn) {
|
||||
addToCartBtn.dataset.variantId = variantId;
|
||||
addToCartBtn.removeAttribute('disabled');
|
||||
addToCartBtn.removeAttribute("disabled");
|
||||
}
|
||||
|
||||
if (buttonText) {
|
||||
buttonText.textContent = addToCartBtn.dataset.defaultText || 'Ajouter au panier';
|
||||
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');
|
||||
const allLi = optionsList.querySelectorAll("li");
|
||||
allLi.forEach((li) => li.classList.remove("is-selected"));
|
||||
this.closest("li").classList.add("is-selected");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -196,10 +213,11 @@
|
|||
|
||||
const hasMultipleVariants = product.variants.edges.length > 1;
|
||||
const firstVariant = product.variants.edges[0]?.node;
|
||||
const hasOptions = firstVariant?.selectedOptions && firstVariant.selectedOptions.length > 0;
|
||||
const hasOptions =
|
||||
firstVariant?.selectedOptions && firstVariant.selectedOptions.length > 0;
|
||||
|
||||
const uniqueOptions = new Set();
|
||||
product.variants.edges.forEach(edge => {
|
||||
product.variants.edges.forEach((edge) => {
|
||||
if (edge.node.selectedOptions && edge.node.selectedOptions[0]) {
|
||||
uniqueOptions.add(edge.node.selectedOptions[0].value);
|
||||
}
|
||||
|
|
@ -207,10 +225,11 @@
|
|||
const hasMultipleOptions = uniqueOptions.size > 1;
|
||||
|
||||
if (hasMultipleVariants && hasOptions && hasMultipleOptions) {
|
||||
addToCartBtn.setAttribute('disabled', 'disabled');
|
||||
const buttonText = addToCartBtn.querySelector('[data-button-text]');
|
||||
addToCartBtn.setAttribute("disabled", "disabled");
|
||||
const buttonText = addToCartBtn.querySelector("[data-button-text]");
|
||||
if (buttonText) {
|
||||
buttonText.textContent = addToCartBtn.dataset.textChooseOption || 'Choisissez une option';
|
||||
buttonText.textContent =
|
||||
addToCartBtn.dataset.textChooseOption || "Choisissez une option";
|
||||
}
|
||||
} else {
|
||||
const firstAvailableVariant = product.variants.edges.find(
|
||||
|
|
@ -228,44 +247,50 @@
|
|||
|
||||
function updateMetaTags(product, isEnglish) {
|
||||
// Update title and description
|
||||
const title = isEnglish && product.titleEn?.value
|
||||
const title =
|
||||
isEnglish && product.titleEn?.value
|
||||
? product.titleEn.value
|
||||
: product.title;
|
||||
const description = isEnglish && product.descriptionEn?.value
|
||||
const description =
|
||||
isEnglish && product.descriptionEn?.value
|
||||
? product.descriptionEn.value
|
||||
: product.description;
|
||||
|
||||
// Update Open Graph title
|
||||
const ogTitle = document.getElementById('og-title');
|
||||
const ogTitle = document.getElementById("og-title");
|
||||
if (ogTitle) {
|
||||
ogTitle.setAttribute('content', title);
|
||||
ogTitle.setAttribute("content", title);
|
||||
}
|
||||
|
||||
// Update Open Graph description
|
||||
const ogDescription = document.getElementById('og-description');
|
||||
const ogDescription = document.getElementById("og-description");
|
||||
if (ogDescription && description) {
|
||||
const excerpt = description.substring(0, 160);
|
||||
ogDescription.setAttribute('content', excerpt);
|
||||
ogDescription.setAttribute("content", excerpt);
|
||||
}
|
||||
|
||||
// Update Open Graph image
|
||||
const ogImage = document.getElementById('og-image');
|
||||
const ogImage = document.getElementById("og-image");
|
||||
if (ogImage && product.images.edges.length > 0) {
|
||||
ogImage.setAttribute('content', product.images.edges[0].node.url);
|
||||
ogImage.setAttribute("content", product.images.edges[0].node.url);
|
||||
}
|
||||
|
||||
// Update product price
|
||||
const ogPrice = document.getElementById('og-price');
|
||||
const ogPrice = document.getElementById("og-price");
|
||||
if (ogPrice) {
|
||||
const price = parseFloat(product.priceRange.minVariantPrice.amount).toFixed(2);
|
||||
ogPrice.setAttribute('content', price);
|
||||
const price = parseFloat(
|
||||
product.priceRange.minVariantPrice.amount
|
||||
).toFixed(2);
|
||||
ogPrice.setAttribute("content", price);
|
||||
}
|
||||
|
||||
// Update availability
|
||||
const ogAvailability = document.getElementById('og-availability');
|
||||
const ogAvailability = document.getElementById("og-availability");
|
||||
if (ogAvailability) {
|
||||
const availability = product.availableForSale ? 'in stock' : 'out of stock';
|
||||
ogAvailability.setAttribute('content', availability);
|
||||
const availability = product.availableForSale
|
||||
? "in stock"
|
||||
: "out of stock";
|
||||
ogAvailability.setAttribute("content", availability);
|
||||
}
|
||||
|
||||
// Update page title
|
||||
|
|
@ -275,7 +300,7 @@
|
|||
let metaDescription = document.querySelector('meta[name="description"]');
|
||||
if (metaDescription && description) {
|
||||
const excerpt = description.substring(0, 160);
|
||||
metaDescription.setAttribute('content', excerpt);
|
||||
metaDescription.setAttribute("content", excerpt);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -306,6 +306,58 @@ class ShopifyCart {
|
|||
return cart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get existing cart by ID
|
||||
*/
|
||||
async getCart() {
|
||||
if (!this.cartId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const query = `
|
||||
query getCart($cartId: ID!) {
|
||||
cart(id: $cartId) {
|
||||
id
|
||||
checkoutUrl
|
||||
lines(first: 10) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
quantity
|
||||
merchandise {
|
||||
... on ProductVariant {
|
||||
id
|
||||
title
|
||||
price {
|
||||
amount
|
||||
currencyCode
|
||||
}
|
||||
product {
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
try {
|
||||
const data = await this.query(query, {
|
||||
cartId: this.cartId
|
||||
});
|
||||
|
||||
return data.cart;
|
||||
} catch (error) {
|
||||
// Cart might be expired or invalid
|
||||
console.error('Error fetching cart:', error);
|
||||
this.clearCart();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get checkout URL to redirect user
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ function fetchShopifyProducts(): array
|
|||
$ch = curl_init($endpoint);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Nécessaire pour dev local sur Windows
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'X-Shopify-Storefront-Access-Token: ' . $token
|
||||
|
|
|
|||
|
|
@ -38,6 +38,12 @@
|
|||
const language = container.dataset.language || 'fr';
|
||||
const isEnglish = language === 'en';
|
||||
|
||||
function initStructuredData() {
|
||||
if (typeof ShopifyCart === 'undefined') {
|
||||
setTimeout(initStructuredData, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
const cart = new ShopifyCart({
|
||||
domain: 'nv7cqv-bu.myshopify.com',
|
||||
storefrontAccessToken: 'dec3d35a2554384d149c72927d1cfd1b'
|
||||
|
|
@ -80,5 +86,9 @@
|
|||
schemaScript.textContent = JSON.stringify(schema, null, 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize when ShopifyCart is available
|
||||
initStructuredData();
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue