Implement custom Shopify cart with drawer UI
Replace Shopify Buy Button iframe with custom implementation using Storefront API 2026-01. Create interactive cart drawer with full product management capabilities (add, remove, update quantities) and seamless checkout flow. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
c08662caf8
commit
28501fec7c
13 changed files with 1158 additions and 81 deletions
0
assets/css/components/_shopify-buy-button.scss
Normal file
0
assets/css/components/_shopify-buy-button.scss
Normal file
233
assets/css/components/_shopify-cart-drawer.scss
Normal file
233
assets/css/components/_shopify-cart-drawer.scss
Normal file
|
|
@ -0,0 +1,233 @@
|
||||||
|
/* Cart Drawer Styles */
|
||||||
|
.cart-drawer {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 9999;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer.is-open {
|
||||||
|
pointer-events: auto;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__panel {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 420px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
transform: translateX(100%);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer.is-open .cart-drawer__panel {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__close {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__close:hover {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__empty {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem 1rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__empty.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__items {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__items.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__image {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__details {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__title {
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__variant {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: #666;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__price {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__quantity {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__qty-btn {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__qty-btn:hover:not(:disabled) {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__qty-btn:disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__qty-value {
|
||||||
|
min-width: 30px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__remove {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #ff3333;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
text-decoration: underline;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__remove:hover {
|
||||||
|
color: #cc0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__footer {
|
||||||
|
border-top: 1px solid #e0e0e0;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__checkout-btn {
|
||||||
|
width: 100%;
|
||||||
|
font-family: "Open Sans", sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #000000;
|
||||||
|
background-color: #00ff00;
|
||||||
|
border: none;
|
||||||
|
border-radius: 40px;
|
||||||
|
padding: 14px 34px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__checkout-btn:hover:not(:disabled) {
|
||||||
|
background-color: #00e600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__checkout-btn:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile responsiveness */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.cart-drawer__panel {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading state */
|
||||||
|
.cart-drawer__content.is-loading {
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
@ -1258,8 +1258,238 @@ body.is-fullscreen {
|
||||||
margin-top: calc(var(--spacing) * 4);
|
margin-top: calc(var(--spacing) * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.snipcart-modal__container {
|
/* Cart Drawer Styles */
|
||||||
z-index: 1000;
|
.cart-drawer {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 9999;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer.is-open {
|
||||||
|
pointer-events: auto;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__panel {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 420px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
transform: translateX(100%);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer.is-open .cart-drawer__panel {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__close {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__close:hover {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__empty {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem 1rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__empty.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__items {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__items.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__image {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
-o-object-fit: cover;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__details {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__title {
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__variant {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: #666;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__price {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__quantity {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__qty-btn {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__qty-btn:hover:not(:disabled) {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__qty-btn:disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__qty-value {
|
||||||
|
min-width: 30px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__remove {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #ff3333;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
text-decoration: underline;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item__remove:hover {
|
||||||
|
color: #cc0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__footer {
|
||||||
|
border-top: 1px solid #e0e0e0;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__checkout-btn {
|
||||||
|
width: 100%;
|
||||||
|
font-family: "Open Sans", sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #000000;
|
||||||
|
background-color: #00ff00;
|
||||||
|
border: none;
|
||||||
|
border-radius: 40px;
|
||||||
|
padding: 14px 34px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__checkout-btn:hover:not(:disabled) {
|
||||||
|
background-color: #00e600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-drawer__checkout-btn:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile responsiveness */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.cart-drawer__panel {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Loading state */
|
||||||
|
.cart-drawer__content.is-loading {
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-template=subscription-newsletter] main {
|
[data-template=subscription-newsletter] main {
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -22,6 +22,7 @@
|
||||||
@import "template/shop/layout";
|
@import "template/shop/layout";
|
||||||
@import "template/shop/section--product";
|
@import "template/shop/section--product";
|
||||||
@import "template/shop/thanks";
|
@import "template/shop/thanks";
|
||||||
// @import "template/shop/snipcart"; // Snipcart désactivé - voir assets/snipcart-archive/README.md
|
@import "components/shopify-buy-button.scss";
|
||||||
|
@import "components/shopify-cart-drawer.scss";
|
||||||
|
|
||||||
@import "template/subscription-newsletter/layout";
|
@import "template/subscription-newsletter/layout";
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
.snipcart-modal__container {
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
214
assets/js/cart-drawer.js
Normal file
214
assets/js/cart-drawer.js
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
/**
|
||||||
|
* Cart Drawer Component
|
||||||
|
* Manages the cart sidebar with add/remove/update functionality
|
||||||
|
*/
|
||||||
|
(function() {
|
||||||
|
const drawer = document.getElementById('cart-drawer');
|
||||||
|
const emptyState = document.querySelector('[data-cart-empty]');
|
||||||
|
const itemsContainer = document.querySelector('[data-cart-items]');
|
||||||
|
const checkoutBtn = document.querySelector('[data-cart-checkout]');
|
||||||
|
const closeButtons = document.querySelectorAll('[data-cart-close]');
|
||||||
|
|
||||||
|
let currentCart = null;
|
||||||
|
let cartInstance = null;
|
||||||
|
|
||||||
|
// Wait for ShopifyCart to be available
|
||||||
|
function initCartDrawer() {
|
||||||
|
if (typeof ShopifyCart === 'undefined') {
|
||||||
|
setTimeout(initCartDrawer, 100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cartInstance = new ShopifyCart({
|
||||||
|
domain: 'nv7cqv-bu.myshopify.com',
|
||||||
|
storefrontAccessToken: 'dec3d35a2554384d149c72927d1cfd1b'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize event listeners
|
||||||
|
setupEventListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupEventListeners() {
|
||||||
|
// Close drawer
|
||||||
|
closeButtons.forEach(btn => {
|
||||||
|
btn.addEventListener('click', closeDrawer);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Checkout button
|
||||||
|
checkoutBtn.addEventListener('click', () => {
|
||||||
|
if (currentCart?.checkoutUrl) {
|
||||||
|
window.location.href = currentCart.checkoutUrl;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for custom cart update events
|
||||||
|
document.addEventListener('cart:updated', (e) => {
|
||||||
|
currentCart = e.detail.cart;
|
||||||
|
renderCart();
|
||||||
|
openDrawer();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openDrawer() {
|
||||||
|
drawer.classList.add('is-open');
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDrawer() {
|
||||||
|
drawer.classList.remove('is-open');
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCart() {
|
||||||
|
if (!currentCart || !currentCart.lines || currentCart.lines.edges.length === 0) {
|
||||||
|
emptyState.classList.remove('hidden');
|
||||||
|
itemsContainer.classList.add('hidden');
|
||||||
|
checkoutBtn.disabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emptyState.classList.add('hidden');
|
||||||
|
itemsContainer.classList.remove('hidden');
|
||||||
|
checkoutBtn.disabled = false;
|
||||||
|
|
||||||
|
// Render cart items
|
||||||
|
itemsContainer.innerHTML = currentCart.lines.edges.map(edge => {
|
||||||
|
const item = edge.node;
|
||||||
|
const merchandise = item.merchandise;
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="cart-item" data-line-id="${item.id}">
|
||||||
|
<div class="cart-item__details">
|
||||||
|
<h4 class="cart-item__title">${merchandise.product.title}</h4>
|
||||||
|
${merchandise.title !== 'Default Title' ? `<p class="cart-item__variant">${merchandise.title}</p>` : ''}
|
||||||
|
<p class="cart-item__price">${merchandise.price.amount} ${merchandise.price.currencyCode}</p>
|
||||||
|
|
||||||
|
<div class="cart-item__quantity">
|
||||||
|
<button class="cart-item__qty-btn" data-action="decrease" data-line-id="${item.id}">−</button>
|
||||||
|
<span class="cart-item__qty-value">${item.quantity}</span>
|
||||||
|
<button class="cart-item__qty-btn" data-action="increase" data-line-id="${item.id}">+</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="cart-item__remove" data-action="remove" data-line-id="${item.id}">
|
||||||
|
Retirer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
// Attach event listeners to quantity buttons
|
||||||
|
attachQuantityListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
function attachQuantityListeners() {
|
||||||
|
const buttons = itemsContainer.querySelectorAll('[data-action]');
|
||||||
|
|
||||||
|
buttons.forEach(btn => {
|
||||||
|
btn.addEventListener('click', async (e) => {
|
||||||
|
const action = e.target.dataset.action;
|
||||||
|
const lineId = e.target.dataset.lineId;
|
||||||
|
|
||||||
|
await handleQuantityChange(action, lineId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleQuantityChange(action, lineId) {
|
||||||
|
if (!cartInstance || !currentCart) return;
|
||||||
|
|
||||||
|
// Find the line item
|
||||||
|
const line = currentCart.lines.edges.find(edge => edge.node.id === lineId);
|
||||||
|
if (!line) return;
|
||||||
|
|
||||||
|
const currentQty = line.node.quantity;
|
||||||
|
let newQty = currentQty;
|
||||||
|
|
||||||
|
if (action === 'increase') {
|
||||||
|
newQty = currentQty + 1;
|
||||||
|
} else if (action === 'decrease') {
|
||||||
|
newQty = Math.max(0, currentQty - 1);
|
||||||
|
} else if (action === 'remove') {
|
||||||
|
newQty = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update cart via API
|
||||||
|
try {
|
||||||
|
itemsContainer.classList.add('is-loading');
|
||||||
|
|
||||||
|
const query = `
|
||||||
|
mutation cartLinesUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
|
||||||
|
cartLinesUpdate(cartId: $cartId, lines: $lines) {
|
||||||
|
cart {
|
||||||
|
id
|
||||||
|
checkoutUrl
|
||||||
|
lines(first: 10) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
merchandise {
|
||||||
|
... on ProductVariant {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
price {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
product {
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
userErrors {
|
||||||
|
field
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const data = await cartInstance.query(query, {
|
||||||
|
cartId: currentCart.id,
|
||||||
|
lines: [{
|
||||||
|
id: lineId,
|
||||||
|
quantity: newQty
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.cartLinesUpdate.userErrors.length > 0) {
|
||||||
|
throw new Error(data.cartLinesUpdate.userErrors[0].message);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentCart = data.cartLinesUpdate.cart;
|
||||||
|
renderCart();
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating cart:', error);
|
||||||
|
alert('Erreur lors de la mise à jour du panier');
|
||||||
|
} finally {
|
||||||
|
itemsContainer.classList.remove('is-loading');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public API
|
||||||
|
window.CartDrawer = {
|
||||||
|
open: openDrawer,
|
||||||
|
close: closeDrawer,
|
||||||
|
updateCart: (cart) => {
|
||||||
|
currentCart = cart;
|
||||||
|
renderCart();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize when DOM is ready
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', initCartDrawer);
|
||||||
|
} else {
|
||||||
|
initCartDrawer();
|
||||||
|
}
|
||||||
|
})();
|
||||||
121
assets/js/product-add-to-cart.js
Normal file
121
assets/js/product-add-to-cart.js
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
/**
|
||||||
|
* 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]');
|
||||||
|
|
||||||
|
// 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 = 'Rupture de stock';
|
||||||
|
addToCartBtn.classList.add('out-of-stock');
|
||||||
|
if (stockDisplay) {
|
||||||
|
stockDisplay.textContent = 'Rupture de stock';
|
||||||
|
stockDisplay.classList.add('out-of-stock');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Show in stock
|
||||||
|
if (stockDisplay) {
|
||||||
|
stockDisplay.textContent = 'En stock';
|
||||||
|
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
|
||||||
|
addToCartBtn.disabled = true;
|
||||||
|
const originalText = addToCartBtn.textContent;
|
||||||
|
addToCartBtn.textContent = 'Ajout en cours...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const variantId = this.dataset.variantId;
|
||||||
|
const cartResult = await cart.addToCart(variantId, 1);
|
||||||
|
|
||||||
|
// Show success feedback
|
||||||
|
addToCartBtn.textContent = 'Ajouté ! ✓';
|
||||||
|
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;
|
||||||
|
addToCartBtn.classList.remove('success');
|
||||||
|
}, 1500);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error adding to cart:', error);
|
||||||
|
|
||||||
|
// Show error feedback
|
||||||
|
addToCartBtn.textContent = 'Erreur - Réessayer';
|
||||||
|
addToCartBtn.classList.add('error');
|
||||||
|
|
||||||
|
// Re-enable button after delay
|
||||||
|
setTimeout(() => {
|
||||||
|
addToCartBtn.disabled = false;
|
||||||
|
addToCartBtn.textContent = originalText;
|
||||||
|
addToCartBtn.classList.remove('error');
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load product data on page load
|
||||||
|
loadProductData();
|
||||||
|
})();
|
||||||
232
assets/js/shopify-cart.js
Normal file
232
assets/js/shopify-cart.js
Normal file
|
|
@ -0,0 +1,232 @@
|
||||||
|
/**
|
||||||
|
* Shopify Storefront API Client
|
||||||
|
* Custom implementation using Cart API (2026-01)
|
||||||
|
*/
|
||||||
|
class ShopifyCart {
|
||||||
|
constructor(config) {
|
||||||
|
this.domain = config.domain;
|
||||||
|
this.storefrontAccessToken = config.storefrontAccessToken;
|
||||||
|
this.apiVersion = '2026-01';
|
||||||
|
this.endpoint = `https://${this.domain}/api/${this.apiVersion}/graphql.json`;
|
||||||
|
this.cartId = null;
|
||||||
|
this.cartItems = [];
|
||||||
|
|
||||||
|
// Load existing cart from localStorage
|
||||||
|
this.loadCart();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make GraphQL request to Shopify Storefront API
|
||||||
|
*/
|
||||||
|
async query(query, variables = {}) {
|
||||||
|
const response = await fetch(this.endpoint, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Shopify-Storefront-Access-Token': this.storefrontAccessToken,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ query, variables }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.errors) {
|
||||||
|
console.error('Shopify API Error:', result.errors);
|
||||||
|
throw new Error(result.errors[0].message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get product information by ID
|
||||||
|
*/
|
||||||
|
async getProduct(productId) {
|
||||||
|
const query = `
|
||||||
|
query getProduct($id: ID!) {
|
||||||
|
product(id: $id) {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
description
|
||||||
|
availableForSale
|
||||||
|
variants(first: 10) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
price {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
availableForSale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const data = await this.query(query, {
|
||||||
|
id: `gid://shopify/Product/${productId}`
|
||||||
|
});
|
||||||
|
|
||||||
|
return data.product;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new cart
|
||||||
|
*/
|
||||||
|
async createCart(lines = []) {
|
||||||
|
const query = `
|
||||||
|
mutation cartCreate($input: CartInput!) {
|
||||||
|
cartCreate(input: $input) {
|
||||||
|
cart {
|
||||||
|
id
|
||||||
|
checkoutUrl
|
||||||
|
lines(first: 10) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
merchandise {
|
||||||
|
... on ProductVariant {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
price {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
product {
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
userErrors {
|
||||||
|
field
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const data = await this.query(query, {
|
||||||
|
input: { lines }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.cartCreate.userErrors.length > 0) {
|
||||||
|
throw new Error(data.cartCreate.userErrors[0].message);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cartId = data.cartCreate.cart.id;
|
||||||
|
this.saveCart();
|
||||||
|
|
||||||
|
return data.cartCreate.cart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add item to cart
|
||||||
|
*/
|
||||||
|
async addToCart(variantId, quantity = 1) {
|
||||||
|
const lines = [{
|
||||||
|
merchandiseId: `gid://shopify/ProductVariant/${variantId}`,
|
||||||
|
quantity: quantity
|
||||||
|
}];
|
||||||
|
|
||||||
|
let cart;
|
||||||
|
|
||||||
|
if (this.cartId) {
|
||||||
|
// Add to existing cart
|
||||||
|
const query = `
|
||||||
|
mutation cartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
|
||||||
|
cartLinesAdd(cartId: $cartId, lines: $lines) {
|
||||||
|
cart {
|
||||||
|
id
|
||||||
|
checkoutUrl
|
||||||
|
lines(first: 10) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
merchandise {
|
||||||
|
... on ProductVariant {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
price {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
product {
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
userErrors {
|
||||||
|
field
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const data = await this.query(query, {
|
||||||
|
cartId: this.cartId,
|
||||||
|
lines
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.cartLinesAdd.userErrors.length > 0) {
|
||||||
|
throw new Error(data.cartLinesAdd.userErrors[0].message);
|
||||||
|
}
|
||||||
|
|
||||||
|
cart = data.cartLinesAdd.cart;
|
||||||
|
} else {
|
||||||
|
// Create new cart
|
||||||
|
cart = await this.createCart(lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cartItems = cart.lines.edges;
|
||||||
|
return cart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get checkout URL to redirect user
|
||||||
|
*/
|
||||||
|
getCheckoutUrl(cart) {
|
||||||
|
return cart?.checkoutUrl || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save cart ID to localStorage
|
||||||
|
*/
|
||||||
|
saveCart() {
|
||||||
|
if (this.cartId) {
|
||||||
|
localStorage.setItem('shopify_cart_id', this.cartId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load cart ID from localStorage
|
||||||
|
*/
|
||||||
|
loadCart() {
|
||||||
|
this.cartId = localStorage.getItem('shopify_cart_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear cart
|
||||||
|
*/
|
||||||
|
clearCart() {
|
||||||
|
this.cartId = null;
|
||||||
|
this.cartItems = [];
|
||||||
|
localStorage.removeItem('shopify_cart_id');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export for use in other scripts
|
||||||
|
window.ShopifyCart = ShopifyCart;
|
||||||
85
site/snippets/buy-button--t-shirt.php
Normal file
85
site/snippets/buy-button--t-shirt.php
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
<div class="product-purchase">
|
||||||
|
<div class="product-stock-info">
|
||||||
|
<p data-product-stock class="stock-status"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn-add-to-cart"
|
||||||
|
data-shopify-add-to-cart
|
||||||
|
data-product-id="15689076179317"
|
||||||
|
data-variant-id=""
|
||||||
|
>
|
||||||
|
Ajouter au panier
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.product-purchase {
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-stock-info {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-status {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-status.in-stock {
|
||||||
|
color: #00cc00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-status.low-stock {
|
||||||
|
color: #ff9900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-status.out-of-stock {
|
||||||
|
color: #ff3333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add-to-cart {
|
||||||
|
font-family: "Open Sans", sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #000000;
|
||||||
|
background-color: #00ff00;
|
||||||
|
border: none;
|
||||||
|
border-radius: 40px;
|
||||||
|
padding: 12px 34px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add-to-cart:hover:not(:disabled) {
|
||||||
|
background-color: #00e600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add-to-cart:focus {
|
||||||
|
outline: 2px solid #00e600;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add-to-cart:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add-to-cart.success {
|
||||||
|
background-color: #00cc00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add-to-cart.error {
|
||||||
|
background-color: #ff3333;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add-to-cart.out-of-stock {
|
||||||
|
background-color: #cccccc;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,3 +1,33 @@
|
||||||
|
<!-- Cart Drawer -->
|
||||||
|
<div id="cart-drawer" class="cart-drawer">
|
||||||
|
<div class="cart-drawer__overlay" data-cart-close></div>
|
||||||
|
<div class="cart-drawer__panel">
|
||||||
|
<div class="cart-drawer__header">
|
||||||
|
<h3>Panier</h3>
|
||||||
|
<button class="cart-drawer__close" data-cart-close aria-label="Fermer le panier">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||||
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cart-drawer__content">
|
||||||
|
<div class="cart-drawer__empty" data-cart-empty>
|
||||||
|
<p>Votre panier est vide</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cart-drawer__items" data-cart-items></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cart-drawer__footer">
|
||||||
|
<button class="cart-drawer__checkout-btn" data-cart-checkout>
|
||||||
|
Passer commande
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<footer id="site-footer">
|
<footer id="site-footer">
|
||||||
<div class="site-footer__container">
|
<div class="site-footer__container">
|
||||||
<div class="footer__mentions">
|
<div class="footer__mentions">
|
||||||
|
|
@ -10,6 +40,8 @@
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="<?= url('assets/js/onload.js') ?>"></script>
|
<script src="<?= url('assets/js/onload.js') ?>"></script>
|
||||||
|
<script src="<?= url('assets/js/shopify-cart.js') ?>"></script>
|
||||||
|
<script src="<?= url('assets/js/cart-drawer.js') ?>"></script>
|
||||||
<?php if(isset($scripts) && is_array($scripts)): ?>
|
<?php if(isset($scripts) && is_array($scripts)): ?>
|
||||||
<?php foreach($scripts as $script): ?>
|
<?php foreach($scripts as $script): ?>
|
||||||
<script src="<?= url($script) ?>"></script>
|
<script src="<?= url($script) ?>"></script>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="<?= url('assets/fonts/stylesheet.css') ?>?version-cache-prevent<?= rand(0, 1000)?>" />
|
<link rel="stylesheet" type="text/css" href="<?= url('assets/fonts/stylesheet.css') ?>?version-cache-prevent<?= rand(0, 1000)?>" />
|
||||||
<link rel="stylesheet" type="text/css" href="<?= url('assets/css/style.css') ?>" />
|
<link rel="stylesheet" type="text/css" href="<?= url('assets/css/style.css') ?>" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="<?= url('assets/css/cart-drawer.css') ?>" />
|
||||||
</head>
|
</head>
|
||||||
<body data-template="<?= $page->template() ?>">
|
<body data-template="<?= $page->template() ?>">
|
||||||
<header id="site-header">
|
<header id="site-header">
|
||||||
|
|
|
||||||
|
|
@ -18,75 +18,7 @@
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if($page->hasOptions()->toBool() && $page->optionValues()->isNotEmpty()): ?>
|
<?php snippet('buy-button--t-shirt') ?>
|
||||||
<div class="product-options">
|
|
||||||
<ul class="product-options__list">
|
|
||||||
<?php
|
|
||||||
$values = $page->optionValues()->split(',');
|
|
||||||
$optionSlug = $page->optionLabel()->slug();
|
|
||||||
foreach($values as $index => $value):
|
|
||||||
$value = trim($value);
|
|
||||||
$uniqueId = $optionSlug . '-' . Str::slug(strtolower($value));
|
|
||||||
?>
|
|
||||||
<li>
|
|
||||||
<input type="radio" id="<?= $uniqueId ?>" name="<?= $optionSlug ?>" value="<?= $value ?>" />
|
|
||||||
<label for="<?= $uniqueId ?>"><?= $value ?></label>
|
|
||||||
</li>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
|
||||||
|
|
||||||
<div class="add-to-cart">
|
|
||||||
<!-- Snipcart désactivé - voir assets/snipcart-archive/README.md pour restauration -->
|
|
||||||
<button
|
|
||||||
class="btn__default"
|
|
||||||
disabled
|
|
||||||
<?php
|
|
||||||
/*
|
|
||||||
// SNIPCART - Décommenter pour réactiver
|
|
||||||
class="btn__default snipcart-add-item"
|
|
||||||
data-item-id="<?= $page->slug() ?>"
|
|
||||||
data-item-price="<?= $page->price() ?>"
|
|
||||||
data-item-description="<?= $page->description()->excerpt(100) ?>"
|
|
||||||
data-item-image="<?= $page->images()->first() ? $page->images()->first()->url() : '' ?>"
|
|
||||||
data-item-name="<?= $page->title()->html() ?>"
|
|
||||||
data-item-shippable="true"
|
|
||||||
data-item-weight="<?= $page->weight()->or(0) ?>"
|
|
||||||
data-item-length="<?= $page->length()->or(0) ?>"
|
|
||||||
data-item-width="<?= $page->width()->or(0) ?>"
|
|
||||||
data-item-height="<?= $page->height()->or(0) ?>"
|
|
||||||
*/
|
|
||||||
?>
|
|
||||||
<?php
|
|
||||||
/*
|
|
||||||
// SNIPCART OPTIONS - Décommenter pour réactiver
|
|
||||||
if($page->hasOptions()->toBool() && $page->optionValues()->isNotEmpty()):
|
|
||||||
$values = $page->optionValues()->split(',');
|
|
||||||
$trimmedValues = array_map('trim', $values);
|
|
||||||
$snipcartOptions = implode('|', $trimmedValues);
|
|
||||||
?>
|
|
||||||
data-item-custom1-name="<?= $page->optionLabel()->html() ?>"
|
|
||||||
data-item-custom1-options="<?= $snipcartOptions ?>"
|
|
||||||
data-item-custom1-required="true"
|
|
||||||
disabled
|
|
||||||
<?php endif; */
|
|
||||||
?>
|
|
||||||
>
|
|
||||||
<span class="icon">
|
|
||||||
<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="m14.523 18.787s4.501-4.505 6.255-6.26c.146-.146.219-.338.219-.53s-.073-.383-.219-.530c-1.753-1.754-6.255-6.258-6.255-6.258-.144-.145-.334-.217-.524-.217-.193 0-.385.074-.532.221-.293.292-.295.766-.004 1.056l4.978 4.978h-14.692c-.414 0-.75.336-.75.75s.336.75.75.75h14.692l-4.979 4.979c-.289.289-.286.762.006 1.054.148.148.341.222.533.222.19 0 .378-.072.522-.215z" fill-rule="nonzero" />
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<div class="txt" data-default-text="<?= t('addToCart', 'Ajouter au panier') ?>">
|
|
||||||
<?php if($page->hasOptions()->toBool() && $page->optionValues()->isNotEmpty()): ?>
|
|
||||||
<?= t('chooseOption', 'Choisissez une option') ?>
|
|
||||||
<?php else: ?>
|
|
||||||
<?= t('addToCart', 'Ajouter au panier') ?>
|
|
||||||
<?php endif ?>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="product-gallery swiper">
|
<div class="product-gallery swiper">
|
||||||
|
|
@ -122,8 +54,7 @@
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<?php
|
<?php snippet('footer', ['scripts' => [
|
||||||
// Snipcart désactivé - voir assets/snipcart-archive/README.md
|
'assets/js/product-add-to-cart.js',
|
||||||
// Pour réactiver: ajouter 'assets/js/product-size.js', 'assets/js/snipcart.js' dans le tableau
|
'assets/js/product-gallery.js'
|
||||||
snippet('footer', ['scripts' => ['assets/js/product-gallery.js']])
|
]]) ?>
|
||||||
?>
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue