diff --git a/assets/css/template/shop/_section--product.scss b/assets/css/template/shop/_section--product.scss index 82790ac..972dae8 100644 --- a/assets/css/template/shop/_section--product.scss +++ b/assets/css/template/shop/_section--product.scss @@ -1,12 +1,3 @@ -.section__product, -.store__nav{ - max-width: 1200px; - margin-left: auto; - margin-right: auto; -} - - - .section__product, .store__nav { max-width: 1200px; @@ -14,10 +5,6 @@ margin-right: auto; } - - - - .store__nav { padding-top: calc(var(--spacing) * 1); padding-bottom: calc(var(--spacing) * 0.5); @@ -27,174 +14,35 @@ a { text-decoration: none; + &::before { + content: "← "; + } + &:hover { text-decoration: underline; } } - a::before { - content: "← "; - } -} - - - -.section__product .details { - // margin-bottom: calc(var(--spacing) * 2); - - ul{ - margin-left: 2ch; - li{ - padding-bottom: 0.2em; + @media #{$small} { + a { + padding-top: 0; + font-size: var(--fs-small); } } } +.section__product { + .details { + ul { + margin-left: 2ch; -.product-options__list { - list-style: none; - display: flex; - gap: 2ch; - li { - position: relative; - - input[type="radio"] { - position: fixed; - opacity: 0; - pointer-events: none; - } - - label { - font-family: var(--title); - font-size: var(--fs-normal); - height: 4ch; - width: 4ch; - border-radius: 50%; - border: var(--border); - border-color: transparent; - display: flex; - align-items: center; - justify-content: center; - padding-top: 0px; - cursor: pointer; - } - - input[type="radio"]:checked + label { - border-color: var(--color-txt); - } - - input[type="radio"]:not(:checked) + label:hover { - border-color: var(--grey-600); - background-color: var(--grey-800); - } - } - } - - - - - - - - - -.product-gallery { - position: relative; - aspect-ratio: 4 / 3; - - .swiper-slide { - width: 100%; - - figure { - - aspect-ratio: 4 / 3; - width: 100%; - height: 100%; - - img { - width: 100%; - height: 100%; - object-fit: contain; + padding-bottom: 0.2em; } } } - // Swiper navigation arrows - .swiper-button-prev, - .swiper-button-next { - color: var(--color-txt); - width: 20px; - height: 20px; - - &:after { - font-size: 20px; - font-weight: bold; - } - - &:hover { - opacity: 0.7; - } - } - - // Swiper pagination dots - .swiper-pagination { - position: relative; - margin-top: calc(var(--spacing) * 0.5); - bottom: 0; - - .swiper-pagination-bullet { - width: 8px; - height: 8px; - background: var(--grey-600); - opacity: 0.5; - transition: opacity 0.3s; - - &:hover { - opacity: 0.7; - } - } - - .swiper-pagination-bullet-active { - background: var(--color-txt); - opacity: 1; - } - } -} - - - .hero { - margin-bottom: calc(var(--spacing) * 1); - padding: calc(var(--spacing) * 0.5) 0; - border-top: var(--border-light); - border-bottom: var(--border-light); - - .p__baseline-big { - margin: 0; - text-align: left; - } - } - - .add-to-cart { - margin: 0; - border-bottom: var(--border-light); - padding: calc(var(--spacing) * 0.5) 0; - } - - .product-options { - border-bottom: var(--border-light); - padding: calc(var(--spacing) * 0.25) 0; - } - - - -@media #{$small} { - .store__nav a { - padding-top: 0; - font-size: var(--fs-small); - } - - .section__product { + @media #{$small} { display: flex; flex-direction: column; margin-bottom: 10vh; @@ -207,6 +55,7 @@ margin-top: calc(var(--spacing) * 0.5); order: 1; } + figure { order: 2; margin-bottom: calc(var(--spacing) * 1); @@ -226,25 +75,25 @@ order: 5; } - .product-gallery{ + .product-gallery { width: 100vw; position: relative; - left: calc(var(--padding-body)*-1); + left: calc(var(--padding-body) * -1); .swiper-button-prev, - .swiper-button-next{ display: none; } + .swiper-button-next { + display: none; + } } } -} -@media #{$small-up} { - - - .section__product{ - display: grid; - grid-template-columns: 1fr 1fr; - gap: calc(var(--padding-body)*2); - margin-bottom: calc(var(--spacing)*3); + @media #{$small-up} { + .product-content { + display: grid; + grid-template-columns: 1fr 1fr; + gap: calc(var(--padding-body) * 2); + margin-bottom: calc(var(--spacing) * 3); + } .details { margin-bottom: calc(var(--spacing) * 2); @@ -255,17 +104,140 @@ border-top: var(--border-light); } - .col-left{ + .col-left { min-height: 100%; - padding-bottom: 40px; //dots - + padding-bottom: 40px; display: flex; flex-direction: column; } - - } - - .product-gallery .swiper-slide figure{ - width: calc(100% - 60px); } } + +.product-options { + border-bottom: var(--border-light); + padding: calc(var(--spacing) * 0.25) 0; +} + +.product-options__list { + list-style: none; + display: flex; + gap: 2ch; + + li { + position: relative; + + input[type="radio"] { + position: fixed; + opacity: 0; + pointer-events: none; + + &:checked + label { + border-color: var(--color-txt); + } + + &:not(:checked) + label:hover { + border-color: var(--grey-600); + background-color: var(--grey-800); + } + } + + label { + font-family: var(--title); + font-size: var(--fs-normal); + height: 4ch; + width: 4ch; + border-radius: 50%; + border: var(--border); + border-color: transparent; + display: flex; + align-items: center; + justify-content: center; + padding-top: 0px; + cursor: pointer; + } + } +} + +.product-gallery { + position: relative; + aspect-ratio: 4 / 3; + + .swiper-slide { + width: 100%; + + figure { + aspect-ratio: 4 / 3; + width: 100%; + height: 100%; + + img { + width: 100%; + height: 100%; + object-fit: contain; + } + } + + @media #{$small-up} { + figure { + width: calc(100% - 60px); + } + } + } + + .swiper-button-prev, + .swiper-button-next { + color: var(--color-txt); + width: 20px; + height: 20px; + + &:after { + font-size: 20px; + font-weight: bold; + } + + &:hover { + opacity: 0.7; + } + } + + .swiper-pagination { + position: relative; + margin-top: calc(var(--spacing) * 0.5); + bottom: 0; + + .swiper-pagination-bullet { + width: 8px; + height: 8px; + background: var(--grey-600); + opacity: 0.5; + transition: opacity 0.3s; + + &:hover { + opacity: 0.7; + } + + &.swiper-pagination-bullet-active { + background: var(--color-txt); + opacity: 1; + } + } + } +} + +.hero { + margin-bottom: calc(var(--spacing) * 1); + padding: calc(var(--spacing) * 0.5) 0; + border-top: var(--border-light); + border-bottom: var(--border-light); + + .p__baseline-big { + margin: 0; + text-align: left; + } +} + +.add-to-cart { + margin: 0; + border-bottom: var(--border-light); + padding: calc(var(--spacing) * 0.5) 0; +} diff --git a/assets/js/product-add-to-cart.js b/assets/js/product-add-to-cart.js index 5c5d5d5..51ee83c 100644 --- a/assets/js/product-add-to-cart.js +++ b/assets/js/product-add-to-cart.js @@ -1,109 +1,46 @@ -/** - * Product Add to Cart functionality - * Handles the add to cart button interaction with Shopify - */ (function() { - // Initialize Shopify Cart const cart = new ShopifyCart({ domain: 'nv7cqv-bu.myshopify.com', storefrontAccessToken: 'dec3d35a2554384d149c72927d1cfd1b' }); - // Get product ID from data attribute const addToCartBtn = document.querySelector('[data-shopify-add-to-cart]'); if (!addToCartBtn) { - console.warn('Add to cart button not found'); return; } - const productId = addToCartBtn.dataset.productId; - const variantId = addToCartBtn.dataset.variantId; - const stockDisplay = document.querySelector('[data-product-stock]'); - - // Get translated texts const texts = { add: addToCartBtn.dataset.textAdd || 'Add to cart', adding: addToCartBtn.dataset.textAdding || 'Adding...', added: addToCartBtn.dataset.textAdded || 'Added! ✓', - error: addToCartBtn.dataset.textError || 'Error - Try again', - outOfStock: addToCartBtn.dataset.textOutOfStock || 'Out of stock', - inStock: addToCartBtn.dataset.textInStock || 'In stock' + error: addToCartBtn.dataset.textError || 'Error - Try again' }; - // Load product data to check availability - async function loadProductData() { - try { - const product = await cart.getProduct(productId); - - if (!product) { - console.error('Product not found'); - return; - } - - // Find the specific variant or use the first one - let variant; - if (variantId) { - variant = product.variants.edges.find( - edge => edge.node.id === `gid://shopify/ProductVariant/${variantId}` - )?.node; - } else { - variant = product.variants.edges[0]?.node; - } - - if (!variant) { - console.error('Variant not found'); - return; - } - - // Update button based on availability - if (!variant.availableForSale) { - addToCartBtn.disabled = true; - addToCartBtn.textContent = texts.outOfStock; - addToCartBtn.classList.add('out-of-stock'); - if (stockDisplay) { - stockDisplay.textContent = texts.outOfStock; - stockDisplay.classList.add('out-of-stock'); - } - } else { - // Show in stock - if (stockDisplay) { - stockDisplay.textContent = texts.inStock; - stockDisplay.classList.add('in-stock'); - } - } - - // Store variant ID for later use - addToCartBtn.dataset.variantId = variant.id.replace('gid://shopify/ProductVariant/', ''); - - } catch (error) { - console.error('Error loading product:', error); - } - } - - // Handle add to cart click addToCartBtn.addEventListener('click', async function(e) { e.preventDefault(); - // Disable button during request + const variantId = this.dataset.variantId; + + if (!variantId) { + console.error('No variant ID found'); + return; + } + addToCartBtn.disabled = true; const originalText = addToCartBtn.textContent; addToCartBtn.textContent = texts.adding; try { - const variantId = this.dataset.variantId; const cartResult = await cart.addToCart(variantId, 1); - // Show success feedback addToCartBtn.textContent = texts.added; addToCartBtn.classList.add('success'); - // Dispatch event to open cart drawer document.dispatchEvent(new CustomEvent('cart:updated', { detail: { cart: cartResult } })); - // Reset button after short delay setTimeout(() => { addToCartBtn.disabled = false; addToCartBtn.textContent = originalText; @@ -113,11 +50,9 @@ } catch (error) { console.error('Error adding to cart:', error); - // Show error feedback addToCartBtn.textContent = texts.error; addToCartBtn.classList.add('error'); - // Re-enable button after delay setTimeout(() => { addToCartBtn.disabled = false; addToCartBtn.textContent = originalText; @@ -125,7 +60,4 @@ }, 2000); } }); - - // Load product data on page load - loadProductData(); })(); diff --git a/assets/js/product-loader.js b/assets/js/product-loader.js new file mode 100644 index 0000000..142e4ad --- /dev/null +++ b/assets/js/product-loader.js @@ -0,0 +1,188 @@ +(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); + + 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); + renderVariants(product); + setupAddToCart(product); + renderStock(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 + : 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 renderVariants(product) { + if (product.variants.edges.length <= 1) return; + + const variantsContainer = document.querySelector("[data-product-variants]"); + const variantSelector = document.querySelector("[data-variant-selector]"); + + if (!variantsContainer || !variantSelector) return; + + variantsContainer.style.display = "block"; + + variantSelector.innerHTML = product.variants.edges + .map((edge) => { + const variant = edge.node; + const variantId = variant.id.replace( + "gid://shopify/ProductVariant/", + "" + ); + const price = parseFloat(variant.price.amount).toFixed(2) + "€"; + const availability = variant.availableForSale + ? "" + : " (Rupture de stock)"; + + return ``; + }) + .join(""); + + variantSelector.addEventListener("change", (e) => { + const addToCartBtn = document.querySelector("[data-shopify-add-to-cart]"); + if (addToCartBtn) { + addToCartBtn.dataset.variantId = e.target.value; + } + }); + } + + 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 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 renderStock(product) { + const stockEl = document.querySelector("[data-product-stock]"); + if (!stockEl) return; + + const addToCartBtn = document.querySelector("[data-shopify-add-to-cart]"); + + if (product.availableForSale) { + stockEl.textContent = addToCartBtn?.dataset.textInStock || "En stock"; + stockEl.classList.add("in-stock"); + } else { + stockEl.textContent = + addToCartBtn?.dataset.textOutOfStock || "Rupture de stock"; + stockEl.classList.add("out-of-stock"); + } + } +})(); diff --git a/assets/js/products-list-loader.js b/assets/js/products-list-loader.js new file mode 100644 index 0000000..be32f4a --- /dev/null +++ b/assets/js/products-list-loader.js @@ -0,0 +1,47 @@ +(async function() { + const container = document.querySelector('[data-products-loader]'); + if (!container) return; + + const language = (container.dataset.language || 'FR').toLowerCase(); + const isEnglish = language === 'en'; + + try { + const cart = new ShopifyCart({ + domain: 'nv7cqv-bu.myshopify.com', + storefrontAccessToken: 'dec3d35a2554384d149c72927d1cfd1b' + }); + + const productsData = await cart.getAllProducts(); + const products = productsData.edges; + + const productsHtml = products.map(edge => { + const product = edge.node; + const image = product.images.edges[0]?.node; + const price = parseFloat(product.priceRange.minVariantPrice.amount).toFixed(2); + const slug = product.handle; + const productUrl = isEnglish ? `/en/${slug}` : `/${slug}`; + const productTitle = isEnglish && product.titleEn?.value + ? product.titleEn.value + : product.title; + + return ` +
+
+ ${image ? `${image.altText || productTitle}` : ''} +
+

+ ${productTitle} +

+

${price}€

+ +
+ `; + }).join(''); + + container.innerHTML = productsHtml; + + } catch (error) { + console.error('Error loading products:', error); + container.innerHTML = '

Erreur lors du chargement des produits

'; + } +})(); diff --git a/assets/js/shopify-cart.js b/assets/js/shopify-cart.js index faf88d7..18b1338 100644 --- a/assets/js/shopify-cart.js +++ b/assets/js/shopify-cart.js @@ -73,6 +73,117 @@ class ShopifyCart { return data.product; } + /** + * Get product by handle + * @param {string} handle - Product handle (slug) + */ + async getProductByHandle(handle) { + const query = ` + query getProductByHandle($handle: String!) { + product(handle: $handle) { + id + handle + title + description + descriptionHtml + availableForSale + tags + titleEn: metafield(namespace: "custom", key: "title_en") { + value + } + descriptionEn: metafield(namespace: "custom", key: "description_en") { + value + } + priceRange { + minVariantPrice { + amount + currencyCode + } + } + images(first: 10) { + edges { + node { + id + url + altText + width + height + } + } + } + variants(first: 20) { + edges { + node { + id + title + sku + availableForSale + price { + amount + currencyCode + } + selectedOptions { + name + value + } + } + } + } + } + } + `; + + const data = await this.query(query, { handle }); + return data.product || null; + } + + /** + * Get all products for listing page + * @param {number} first - Number of products to fetch + */ + async getAllProducts(first = 20) { + const query = ` + query getAllProducts($first: Int!) { + products(first: $first, sortKey: TITLE) { + edges { + node { + id + handle + title + description + availableForSale + titleEn: metafield(namespace: "custom", key: "title_en") { + value + } + priceRange { + minVariantPrice { + amount + currencyCode + } + } + images(first: 1) { + edges { + node { + id + url + altText + } + } + } + } + } + pageInfo { + hasNextPage + endCursor + } + } + } + `; + + const data = await this.query(query, { first }); + return data.products; + } + /** * Create a new cart */ diff --git a/content/1_eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat/product.en.txt b/content/1_eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat/product.en.txt new file mode 100644 index 0000000..6bbc828 --- /dev/null +++ b/content/1_eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat/product.en.txt @@ -0,0 +1,5 @@ +Title: Éclairages : 12 entretiens et analyses sur les violences d'État + +---- + +Uuid: gzshayl6xoefrnsz diff --git a/content/1_eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat/product.fr.txt b/content/1_eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat/product.fr.txt new file mode 100644 index 0000000..f26cd4f --- /dev/null +++ b/content/1_eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat/product.fr.txt @@ -0,0 +1,9 @@ +Title: Éclairages : 12 entretiens et analyses sur les violences d’État + +---- + +Shopifyhandle: eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat + +---- + +Uuid: gzshayl6xoefrnsz \ No newline at end of file diff --git a/content/1_tshirt-index-01/product.en.txt b/content/1_tshirt-index-01/product.en.txt deleted file mode 100644 index 7dec4a5..0000000 --- a/content/1_tshirt-index-01/product.en.txt +++ /dev/null @@ -1,33 +0,0 @@ -Title: T-shirt Index 01 - ----- - -Price: 35 - ----- - -Description:

T-shirt de soutien à Index, 100% coton

- ----- - -Details:

Organic cotton t-shirt with screen printing.
Print on the front: "INDEX" logo, 10 cm wide.

Shipping only via Mondial Relay to France, Belgium and Switzerland.

- ----- - -Options: - -- - label: Size - values: XS, S, M, L, XL - ----- - -Snipcartid: tshirt-01 - ----- - -Backgroundcolor: #ffffff - ----- - -Template: product \ No newline at end of file diff --git a/content/1_tshirt-index-01/product.fr.txt b/content/1_tshirt-index-01/product.fr.txt deleted file mode 100644 index 2226ab3..0000000 --- a/content/1_tshirt-index-01/product.fr.txt +++ /dev/null @@ -1,56 +0,0 @@ -Title: T-shirt Index 01 - ----- - -Price: 35 - ----- - -Stock: 10 - ----- - -Description: T-shirt de soutien à Index, 100% coton - ----- - -Details:

T-shirt en coton organique avec impression sérigraphique.
Marquage sur la face avant : logo « INDEX » de 10 cm de large.

Envoi uniquement via Mondial Relay vers la France, la Belgique et la Suisse.

- ----- - -Hasoptions: true - ----- - -Optionlabel: Taille - ----- - -Optionvalues: XS, S, M, L, XL - ----- - -Options: - -- - label: Taille - values: XS, S, M, L, XL -- - label: Couleur - values: Rouge, Vert - ----- - -Snipcartid: tshirt-01 - ----- - -Backgroundcolor: #ffffff - ----- - -Template: product - ----- - -Uuid: udrrfizhayqixfoo \ No newline at end of file diff --git a/content/1_tshirt-index-01/test.png b/content/1_tshirt-index-01/test.png deleted file mode 100644 index f05f4a3..0000000 Binary files a/content/1_tshirt-index-01/test.png and /dev/null differ diff --git a/content/1_tshirt-index-01/test.png.fr.txt b/content/1_tshirt-index-01/test.png.fr.txt deleted file mode 100644 index cd5255a..0000000 --- a/content/1_tshirt-index-01/test.png.fr.txt +++ /dev/null @@ -1,9 +0,0 @@ -Sort: 1 - ----- - -Uuid: elxkhcta8dkjhr60 - ----- - -Template: image \ No newline at end of file diff --git a/content/1_tshirt-index-01/tshirt-01.png b/content/1_tshirt-index-01/tshirt-01.png deleted file mode 100644 index 99899b8..0000000 Binary files a/content/1_tshirt-index-01/tshirt-01.png and /dev/null differ diff --git a/content/1_tshirt-index-01/tshirt-01.png.fr.txt b/content/1_tshirt-index-01/tshirt-01.png.fr.txt deleted file mode 100644 index c9a1f27..0000000 --- a/content/1_tshirt-index-01/tshirt-01.png.fr.txt +++ /dev/null @@ -1,9 +0,0 @@ -Sort: 2 - ----- - -Uuid: deupkqq83jvloz0r - ----- - -Template: image \ No newline at end of file diff --git a/content/2_t-shirt-index/product.en.txt b/content/2_t-shirt-index/product.en.txt new file mode 100644 index 0000000..7aa8f14 --- /dev/null +++ b/content/2_t-shirt-index/product.en.txt @@ -0,0 +1,5 @@ +Title: T-shirt Index + +---- + +Uuid: qq27mjjpethsvnwp diff --git a/content/2_t-shirt-index/product.fr.txt b/content/2_t-shirt-index/product.fr.txt new file mode 100644 index 0000000..395184c --- /dev/null +++ b/content/2_t-shirt-index/product.fr.txt @@ -0,0 +1,9 @@ +Title: T-shirt Index + +---- + +Shopifyhandle: t-shirt-index-01 + +---- + +Uuid: qq27mjjpethsvnwp \ No newline at end of file diff --git a/site/blueprints/pages/product.yml b/site/blueprints/pages/product.yml index 52fde12..978bcde 100644 --- a/site/blueprints/pages/product.yml +++ b/site/blueprints/pages/product.yml @@ -1,134 +1,21 @@ -title: - en: Product - fr: Produit +title: Product icon: cart -tabs: - content: - label: - en: Content - fr: Contenu - columns: - - width: 2/3 - sections: - main: - type: fields - fields: - price: - label: - en: Price (€) - fr: Prix (€) - type: number - min: 0 - step: 0.01 - required: true - translate: false - width: 1/4 - stock: - label: Stock - type: number - min: 0 - default: 0 - help: - en: Edit through french version - fr: Partagé entre les versions FR et EN - translate: false - width: 1/4 - space: - type: gap - width: 2/4 - weight: - label: - en: Weight (g) - fr: Poids (g) - type: number - min: 0 - default: 0 - help: - en: Weight in grams for shipping calculation - fr: Poids en grammes pour le calcul de la livraison - translate: false - width: 1/4 - length: - label: - en: Length (cm) - fr: Longueur (cm) - type: number - min: 0 - default: 0 - help: - en: Package length in centimeters - fr: Longueur du colis en centimètres - translate: false - width: 1/4 - width: - label: - en: Width (cm) - fr: Largeur (cm) - type: number - min: 0 - default: 0 - help: - en: Package width in centimeters - fr: Largeur du colis en centimètres - translate: false - width: 1/4 - height: - label: - en: Height (cm) - fr: Hauteur (cm) - type: number - min: 0 - default: 0 - help: - en: Package height in centimeters - fr: Hauteur du colis en centimètres - translate: false - width: 1/4 - description: - label: Description panier - type: writer - help: Visible dans le panier seulement. - details: - label: - en: Details - fr: Détails - type: writer - hasOptions: - label: - en: Options - fr: Options - type: toggle - default: false - translate: false - width: 1/7 - optionLabel: - label: - en: Option label - fr: Libellé de l'option - type: text - width: 3/7 - when: - hasOptions: true - optionValues: - label: - en: Option values - fr: Valeurs de l'option - type: tags - help: - en: "Comma-separated values (e.g.: XS, S, M, L, XL)" - fr: "Valeurs séparées par des virgules (ex: XS, S, M, L, XL)" - translate: false - when: - hasOptions: true - width: 3/7 +columns: + - width: 1/1 + fields: + info: + type: info + text: + en: "Product data (title, description, images, price) is managed in Shopify Admin. This Kirby page only serves for routing." + fr: "Les données produit (titre, description, images, prix) sont gérées dans Shopify Admin. Cette page Kirby sert uniquement au routing." - - width: 1/3 - sections: - images: - type: files - headline: - en: Product Images - fr: Images du produit - template: image - layout: cards + shopifyHandle: + label: + en: Shopify Handle + fr: Shopify Handle + type: text + help: + en: "Product handle from Shopify (e.g. tshirt-index-01). If empty, uses the page slug." + fr: "Handle du produit Shopify (ex: tshirt-index-01). Si vide, utilise le slug de la page Kirby." + placeholder: tshirt-index-01 diff --git a/site/languages/en.php b/site/languages/en.php index 613b944..19e9aec 100644 --- a/site/languages/en.php +++ b/site/languages/en.php @@ -27,6 +27,9 @@ return [ 'addedToCart' => 'Added! ✓', 'errorAddToCart' => 'Error - Try again', 'closeCart' => 'Close cart', + 'loading' => 'Loading...', + 'productNotFound' => 'Product not found', + 'selectVariant' => 'Select', // Blueprints - Home 'home.title' => 'Home', diff --git a/site/languages/fr.php b/site/languages/fr.php index 63ca317..249bb4d 100644 --- a/site/languages/fr.php +++ b/site/languages/fr.php @@ -27,6 +27,9 @@ return [ 'addedToCart' => 'Ajouté ! ✓', 'errorAddToCart' => 'Erreur - Réessayer', 'closeCart' => 'Fermer le panier', + 'loading' => 'Chargement...', + 'productNotFound' => 'Produit non trouvé', + 'selectVariant' => 'Choisir', // Blueprints - Home 'home.title' => 'Accueil', diff --git a/site/snippets/buy-button.php b/site/snippets/buy-button.php new file mode 100644 index 0000000..7769f2e --- /dev/null +++ b/site/snippets/buy-button.php @@ -0,0 +1,25 @@ +
+
+

+
+ + + + +
diff --git a/site/snippets/footer.php b/site/snippets/footer.php index 3f6ecb9..91cbf60 100644 --- a/site/snippets/footer.php +++ b/site/snippets/footer.php @@ -13,11 +13,16 @@ - - - - - + + + + + + + + + + diff --git a/site/templates/home.php b/site/templates/home.php index 93bfc37..dcb47e5 100644 --- a/site/templates/home.php +++ b/site/templates/home.php @@ -1,35 +1,24 @@ $site->title(), 'template' => 'store']) ?> -
-

- baseline()->or('Bienvenue sur la boutique de soutien à Index') ?> -

+
+

+ baseline()->or('Bienvenue sur la boutique de soutien à Index') ?> +

-
- children()->listed() as $product): ?> -
-
- files()->sortBy('sort', 'asc')->first()): ?> - $cover, - 'alt' => $product->title()->html(), - 'preset' => 'product-card', - 'size' => 25, - 'lazy' => true - ]) ?> - -
-

title()->html() ?>

-

price() ?>€

- -
- -
+
-

- - -

-
+
+

+
+ + + +

+ + +

+
diff --git a/site/templates/product.php b/site/templates/product.php index ecde2c4..46d2487 100644 --- a/site/templates/product.php +++ b/site/templates/product.php @@ -1,60 +1,47 @@ - $page->title(), 'template' => 'shop']) ?> +shopifyHandle()->or($page->slug()); -
- +snippet('header', ['title' => $page->title(), 'template' => 'shop']); +?> -
+
+ + +
+ +
+

+
+ +
-
+ - [ - 'assets/js/product-add-to-cart.js', - 'assets/js/product-gallery.js' -]]) ?> + +
+
+ + ['product']]) ?>