Last active
December 17, 2024 10:40
-
-
Save gijsbotje/cb631ff6e59095d142f875c0eab7a6e3 to your computer and use it in GitHub Desktop.
Afosto 2.0 storefront scripts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- twig/layouts/storefrontScripts.twig --> | |
<!-- Styling voor de error meldingen d.m.v. toastify-js --> | |
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css"> | |
<script type="module"> | |
import 'https://esm.sh/preact/debug'; | |
import { h, render, Fragment } from 'https://esm.sh/preact'; | |
import { useEffect, useState, useRef } from 'https://esm.sh/preact/hooks'; | |
import { html } from 'https://esm.sh/htm/preact'; | |
import Toastify from 'https://esm.sh/toastify-js'; | |
import { createStorefrontClient } from 'https://esm.sh/@afosto/storefront@3'; | |
import { getDomain } from 'https://esm.sh/tldts'; | |
const getErrorMessage = error => { | |
const errorResponse = error?.response || {}; | |
const errorResponseData = errorResponse?.data || {}; | |
const errorResponseError = errorResponseData?.error || {}; | |
const gqlResponseErrors = errorResponse?.errors || []; | |
const [firstGqlError] = gqlResponseErrors || []; | |
const gqlErrorExtensions = firstGqlError?.extensions || {}; | |
const pointers = errorResponseError?.details?.pointers || firstGqlError?.extensions?.pointers; | |
const [firstPointer] = pointers || []; | |
return ( | |
firstPointer?.message || | |
errorResponseError?.message || | |
errorResponseData?.message || | |
firstGqlError?.message | |
); | |
}; | |
const formatPrice = (value) => { | |
const intl = Intl.NumberFormat(`${document.documentElement.lang}-${document.documentElement.lang.toUpperCase()}`, { | |
style: 'currency', | |
currency: document.body.dataset.afCurrencyIso, | |
}); | |
const decimal = intl.formatToParts(value / 100).find(part => part.type === 'decimal').value; | |
return intl.format(value / 100).replace(`${decimal}00`, `${decimal}-`); | |
}; | |
const CouponForm = ({ coupons }) => { | |
const [value, setValue] = useState(''); | |
const [errorMessage, setErrorMessage] = useState(null); | |
const [isSubmitting, setIsSubmitting] = useState(false); | |
const handleAddCouponToCart = async e => { | |
e.preventDefault(); | |
try { | |
setIsSubmitting(true); | |
const updatedCart = await window.Storefront.addCouponToCart(value); | |
if (!!updatedCart) { | |
setValue(''); | |
$('body').trigger('cart:updated', updatedCart); | |
} else { | |
alert('something went wrong'); | |
} | |
} catch (error) { | |
$("#add-response-modal .modal-header .modal-title").html($("#add-response-modal").data("error-title")); | |
$("#add-response-modal .modal-body").addClass('text-center'); | |
$("#add-response-modal .modal-body").html('<span class="fa-stack fa-3x text-warning"><i class="far fa-circle fa-stack-2x"></i><i class="fas fa-exclamation fa-stack-1x"></i></span><p class="lead mt-15">' + getErrorMessage(error) + '</p>'); | |
$("#add-response-modal").modal('show'); | |
} finally { | |
setIsSubmitting(false); | |
} | |
}; | |
const handleRemoveCouponToCart = async code => { | |
try { | |
setIsSubmitting(true); | |
const updatedCart = await window.Storefront.removeCouponFromCart(code); | |
$('body').trigger('cart:updated', updatedCart); | |
} catch (error) { | |
$("#add-response-modal .modal-header .modal-title").html($("#add-response-modal").data("error-title")); | |
$("#add-response-modal .modal-body").addClass('text-center'); | |
$("#add-response-modal .modal-body").html('<span class="fa-stack fa-3x text-warning"><i class="far fa-circle fa-stack-2x"></i><i class="fas fa-exclamation fa-stack-1x"></i></span><p class="lead mt-15">' + getErrorMessage(error) + '</p>'); | |
$("#add-response-modal").modal('show'); | |
} finally { | |
setIsSubmitting(false); | |
} | |
}; | |
const handleChange = e => { | |
setValue(e.target.value); | |
}; | |
const handleInput = () => { | |
setErrorMessage(null); | |
}; | |
return html` | |
<form onSubmit="${handleAddCouponToCart}"> | |
<div class=""> | |
${(coupons || []).map(({ code }, index) => html` | |
<div class="panel panel-default d-flex justify-content-between align-items-center"> | |
<div class="py-4 px-12"> | |
${code || null} | |
</div> | |
<button class="btn btn-danger btn-sm" type="button" onClick="${() => handleRemoveCouponToCart(code)}"> | |
<i class="fal fa-times" /> | |
</button> | |
</div> | |
`)} | |
</div> | |
<div class="input-group"> | |
<input class="form-control" placeholder="Coupon code toevoegen" onChange="${handleChange}" onInput="${handleInput}" value="${value}" /> | |
<span class="input-group-btn"> | |
<button type="submit" class="btn btn-primary" disabled="${isSubmitting}"> | |
<i class="fal fa-angle-right" /> | |
</button> | |
</span> | |
</div> | |
${errorMessage && errorMessage} | |
</form> | |
`; | |
}; | |
const ShareCartDialog = () => { | |
const id = window.Storefront.getCartTokenFromStorage(); | |
const link = `${window.location.origin}/cart?hash=${id}`; | |
useEffect(() => { | |
if (id) { | |
$('[data-toggle="tooltip"]').tooltip(); | |
$('.copy-to-clipboard').tooltip({ | |
container: '.input-group-btn', | |
trigger: 'click' | |
}).on('mouseout', function(){ | |
$(this).tooltip('hide'); | |
}); | |
new Clipboard('.copy-to-clipboard'); | |
// new Clipboard('#copy-field'); | |
$('input[data-clipboard-text]').on('click, focus', function() { | |
$(this).select(); | |
}); | |
} | |
}, [id]); | |
return html` | |
<div class="modal fade" tabindex="-1" role="dialog" id="share-cart-dialog"> | |
<div class="modal-dialog" role="document"> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> | |
<h4 class="modal-title">{{'Winkelwagen delen'|t}}</h4> | |
</div> | |
<div class="modal-body text-center"> | |
<div class="row"> | |
<div class="col-md-4 col-xs-3"> | |
<a class="text-black" href="https://www.facebook.com/sharer/sharer.php?u=${link}&t=" rel="noopener noreferrer" target="_blank" title="{{"Share on Facebook"|t}}" > | |
<i class="fab fa-3x fa-facebook" /> | |
</a> | |
</div> | |
<div class="col-md-4 visible-xs visible-sm col-xs-3"> | |
<a class="text-black" href="whatsapp://send?text=${link}"> | |
<i class="fab fa-3x fa-whatsapp" /> | |
</a> | |
</div> | |
<div class="col-md-4 col-xs-3"> | |
<a class="text-black" href="mailto:?subject=&body=${link}" rel="noopener noreferrer" target="_self" title="{{"Send email"|t}}"> | |
<i class="fas fa-3x fa-envelope" /> | |
</a> | |
</div> | |
</div> | |
</div> | |
<div class="modal-footer text-left"> | |
<p class="text-center"> | |
{{' Kopieer deze link om op je website te plaatsen of om te gebruiken in een bericht'|t}} | |
</p> | |
<div class="form-group"> | |
<div class="input-group"> | |
<input class="form-control" value="${link}" id="copy-cart-url-field" data-clipboard-text="${link}" /> | |
<div class="input-group-btn"> | |
<button | |
type="button" | |
class="copy-to-clipboard btn btn-secondary" | |
data-clipboard-target="#copy-cart-url-field" | |
data-toggle="tooltip" | |
data-placement="bottom" | |
title="{{'Gekopieerd!'|t}}" | |
> | |
{{'Kopieer'|t}} | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
`; | |
}; | |
const CopyCartLink = ({ id }) => { | |
const handleShowShareDialog = () => { | |
$('#share-cart-dialog').modal('show'); | |
}; | |
return html` | |
<button class="btn btn-link btn-block" data-toggle="modal" data-target="#share-cart-dialog">Winkelwagen delen</button> | |
`; | |
}; | |
const CartPageSummary = ({ cart, checkoutUrl, isLoading }) => { | |
const { subtotal, total, totalExcludingVat, vat, adjustments, fees, coupons, id } = cart || {}; | |
const { shipping: shippingFees, payment: paymentFees } = fees || {}; | |
return html` | |
<div class="cost-summary px-15"> | |
<div class="cost-summary-inner"> | |
<strong class="h4"> | |
{{'Checkout'|t}} | |
</strong> | |
<div class="d-flex justify-content-between align-items-center"> | |
<span class="h6 my-5 text-muted"> | |
{{'Subtotaal'|t}} | |
</span> | |
<span class="h5 my-5 text-uppercase${isLoading ? 'text-muted' : ''}"> | |
${isLoading ? '-' : formatPrice(subtotal || 0)} | |
</span> | |
</div> | |
${(adjustments || []).map(({ description, amount, isDiscount, isPercentage, outcome }, index) => html` | |
<div class="d-flex justify-content-between align-items-center${index > 0 ? ' mt-20' : ''}"> | |
<span class="h6 my-5 text-muted"> | |
${description}${isPercentage ? ` (${amount}%)` : ''} | |
</span> | |
<span class="h5 my-5"> | |
${formatPrice((outcome.amount || 0) * (isDiscount ? -1 : 1) )} | |
</span> | |
</div> | |
`)} | |
${(shippingFees || []).map(({ description, total }, index) => html` | |
<div class="d-flex justify-content-between align-items-center${index > 0 ? ' mt-20' : ''}"> | |
<span class="h6 my-5 text-muted"> | |
${description} | |
</span> | |
<span class="h5 my-5"> | |
${formatPrice(total)} | |
</span> | |
</div> | |
`)} | |
${(paymentFees || []).map(({ description, total }, index) => html` | |
<div class="d-flex justify-content-between align-items-center${index > 0 ? ' mt-20' : ''}"> | |
<span class="h6 my-5 text-muted"> | |
${description} | |
</span> | |
<span class="h5 my-5"> | |
${formatPrice(total)} | |
</span> | |
</div> | |
`)} | |
<div class="d-flex justify-content-between align-items-center"> | |
<span class="h6 my-5 text-muted"> | |
{{'Totaal excl. BTW'|t}} | |
</span> | |
<span class="h5 my-5${isLoading ? 'text-muted' : ''}"> | |
${isLoading ? '-' : formatPrice(totalExcludingVat || 0)} | |
</span> | |
</div> | |
${(vat || []).map(({ rate, amount }, index) => html` | |
<div class="d-flex justify-content-between align-items-center${index > 0 ? ' mt-20' : ''}"> | |
<span class="h6 my-5 text-muted"> | |
BTW ${rate || 0}% | |
</span> | |
<span class="h5 my-5"> | |
${formatPrice(amount || 0)} | |
</span> | |
</div> | |
`)} | |
<hr /> | |
<div class="d-flex justify-content-between align-items-center mb-32"> | |
<span class="h6 my-0 text-muted"> | |
{{'Totaal'|t}} | |
</span> | |
<span class="h4 my-0${isLoading ? 'text-muted' : ''}"> | |
${isLoading ? '-' : formatPrice(total || 0)} | |
</span> | |
</div> | |
<${CouponForm} coupons="${coupons}" /> | |
// <${PayPalButton} options="${paypalOptions}" /> | |
<a class="btn btn-success btn-block mt-16" href="${checkoutUrl}" title="{{'Verder met bestellen'|t}}"> | |
{{'Verder met bestellen'|t}} | |
</a> | |
<${CopyCartLink} id="${id}" /> | |
</div> | |
</div> | |
`; | |
}; | |
const ItemsGrid = ({ items, onRemove, onChangeQuantity, isLoading }) => { | |
const increaseQuantity = item => onChangeQuantity(item, item.quantity + 1); | |
const decreaseQuantity = item => onChangeQuantity(item, item.quantity - 1); | |
return html` | |
<div class="cart-grid px-15 d-flex flex-column"> | |
<div class="cart-grid-header"> | |
<div class="cart-grid-header-action"></div> | |
<div class="cart-grid-header-information"> | |
<span> | |
{{'Informatie'|t}} | |
</span> | |
</div> | |
<div class="cart-grid-header-single-price"> | |
<span> | |
{{'Stuksprijs'|t}} | |
</span> | |
</div> | |
<div class="cart-grid-header-quantity"> | |
<span> | |
{{'Aantal'|t}} | |
</span> | |
</div> | |
<div class="cart-grid-header-subtotal"> | |
<span> | |
{{'Subtotaal'|t}} | |
</span> | |
</div> | |
</div> | |
${!isLoading ? items.map(item => { | |
const { adjustments, label, sku, total, subtotal, quantity, ids, details, image } = item || {}; | |
const unitPrice = details[0] && details[0].pricing && details[0].pricing.amount || 0; | |
return html` | |
<div class="cart-grid-item-container" key="${sku}"> | |
<div class="cart-grid-item"> | |
<div class="cart-grid-item-action hidden-xs hidden-sm"> | |
<button | |
type="button" | |
class="btn btn-link text-danger btn-block btn-icon p-10" | |
onClick="${() => onRemove(item)}" | |
> | |
<i class="fa fa-times"></i> | |
</button> | |
</div> | |
<div class="cart-grid-item-information"> | |
<div class="cart-grid-item-information-image"> | |
<a href="{{cartItem.url}}" title="${label || sku}"> | |
<div class="lazyload-wrapper lazyload-square"> | |
<img loading="lazy" src="${image}" alt="${'{{'Afbeelding van [name]'|t}}'.replace('[name]', label || sku) }" class="lazyload-cover" /> | |
</div> | |
</a> | |
</div> | |
<div class="cart-grid-item-information-inner"> | |
<strong class="h5 cart-grid-item-product-name"> | |
${label || sku} | |
</strong> | |
<div class="visible-xs visible-sm"> | |
<div class="d-flex"> | |
<div class="flex-100 d-flex align-items-center"> | |
<label class="h6 text-muted d-block mr-10"> | |
{{'Aantal'|t}} | |
</label> | |
<div class="quantity-counter"> | |
<button | |
type="button" | |
class="btn" | |
onClick="${() => decreaseQuantity(item)}" | |
> | |
<i class="fa fa-minus"></i> | |
</button> | |
<div class="mx-10"> | |
${quantity} | |
</div> | |
<button | |
type="button" | |
class="btn" | |
onClick="${() => increaseQuantity(item)}" | |
> | |
<i class="fa fa-plus"></i> | |
</button> | |
</div> | |
</div> | |
</div> | |
<hr class="light my-10"/> | |
<div class="d-flex align-items-end flex-wrap"> | |
<div> | |
${adjustments.length > 0 && html` | |
<div class="d-flex justify-content-between align-items-center"> | |
<span class="h6 my-0 text-muted mr-10"> | |
{{'Subtotaal'|t}} | |
</span> | |
<span class="h6 my-0"> | |
${formatPrice(subtotal)} | |
</span> | |
</div> | |
${adjustments.map((adjustment, idx) => html` | |
<div class=""> | |
<span class="h6 my-0 text-muted mr-10"> | |
${adjustment.description} ${adjustment.isPercentage ? `(${adjustment.amount}%)` : ''} | |
</span> | |
<span class="h6 my-0"> | |
${formatPrice(adjustment.outcome.amount * (adjustment.isDiscount ? -1 : 1))} | |
</span> | |
</div> | |
`)} | |
`} | |
<div class="d-flex justify-content-between align-items-center my-8"> | |
<span class="h6 my-0 text-muted mr-10"> | |
{{'Totaal'|t}} | |
</span> | |
<span class="h5 my-0"> | |
${formatPrice(total)} | |
</span> | |
</div> | |
</div> | |
<button | |
type="button" | |
class="btn btn-link btn-sm text-danger btn-icon p-5 ml-auto" | |
onClick="${() => onRemove(item)}" | |
> | |
<i class="fa fa-times"></i> | |
<span> | |
{{'Verwijderen'|t}} | |
</span> | |
</button> | |
</div> | |
</div> | |
</div> | |
<div class="cart-grid-item-single-price hidden-xs hidden-sm text-right"> | |
<div class="h5"> | |
${formatPrice(unitPrice)} | |
</div> | |
</div> | |
<div class="cart-grid-item-quantity hidden-xs hidden-sm"> | |
<div class="quantity-counter"> | |
<button | |
type="button" | |
class="btn" | |
onClick="${() => decreaseQuantity(item)}" | |
> | |
<i class="fa fa-minus fa-sm"></i> | |
</button> | |
<div class="mx-10"> | |
${quantity} | |
</div> | |
<button | |
type="button" | |
class="btn" | |
onClick="${() => increaseQuantity(item)}" | |
> | |
<i class="fa fa-plus fa-sm"></i> | |
</button> | |
</div> | |
</div> | |
<div class="cart-grid-item-subtotal hidden-xs hidden-sm text-right"> | |
<span class="h5 ${adjustments.length > 0 ? 'text-through text-muted' : ''}"> | |
${formatPrice(subtotal)} | |
</span> | |
</div> | |
</div> | |
</div> | |
${(adjustments || []).length > 0 && html` | |
<div class="cart-grid-item-adjustments hidden-xs hidden-sm"> | |
${adjustments.map((adjustment, idx) => html` | |
<div class="cart-grid-item-adjustments-description"> | |
${adjustment.description} ${adjustment.isPercentage ? `(${adjustment.amount}%)` : ''} | |
</div> | |
<div class="cart-grid-item-adjustments-outcome"> | |
${formatPrice(adjustment.outcome.amount * (adjustment.isDiscount ? -1 : 1))} | |
</div> | |
<div class="cart-grid-item-adjustments-new-total"> | |
${idx + 1 === adjustments.length ? html` | |
<span class="h5 my-0"> | |
${formatPrice(total)} | |
</span> | |
` : ''} | |
</div> | |
`)} | |
</div> | |
`} | |
</div> | |
`; | |
}) : ''} | |
</div> | |
`; | |
}; | |
const ProductCard = ({ product }) => { | |
return html` | |
<div class="col-xs-6 col-sm-4 col-md-4 col-lg-3 mb-30"> | |
<article class="product-grid-item h-pr-100"> | |
<div class="product-image"> | |
<a href="${product.url}" title="${product.name}"> | |
<div class="lazyload-wrapper lazyload-square"> | |
<img data-src="${product.image_default.thumbs[400]}" alt="Foto van ${product.name}" title="${product.name}" class="lazyload"/> | |
</div> | |
<div class="product-name"> | |
<strong> | |
${product.name} | |
</strong> | |
</div> | |
</a> | |
</div> | |
<div class="product-info"> | |
<div class="product-info-description hidden-xs list-visible"> | |
<a data-af-href="${product.url}" class="product-name list-visible" title="${product.name}"> | |
<strong>${product.name}</strong> | |
</a> | |
</div> | |
<div class="product-action"> | |
<div class="product-action-text"> | |
<strong class="product-price"> | |
${product.has_discount && html` | |
<small> | |
<s> | |
${formatPrice(product.original_price * 100)} | |
</s> | |
</small> | |
`} | |
<span>${formatPrice(product.price * 100)}</span> | |
</strong> | |
<div class="product-action-buttons"> | |
${!product.available && html` | |
<button type="button" disabled class="btn btn-primary btn-sm" aria-label="{{'Toevoegen aan winkelwagen'|t}}"> | |
<i class="fa fa-shopping-cart fa-lg"> </i> | |
<i class="fa fa-plus fa-lg"> </i> | |
</button> | |
`} | |
${product.available && product.price > 0 && html` | |
${product.has_options ? html` | |
<div | |
class="btn btn-primary btn-sm quick-view-link" | |
data-qv-product="${product.url}" | |
data-qv-ajax-element="#quick-view-modal .modal-content" | |
data-qv-ajax-input="#quick-view-content > *" | |
data-product-url="${product.url}" | |
data-skip-drawer | |
> | |
<i class="fa fa-shopping-cart fa-lg mr-12"> </i> | |
<i class="fa fa-plus fa-lg"> </i> | |
</div> | |
` : html` | |
<form | |
action="${product.cart_url}" | |
method="POST" | |
id="product-form-${product.id}" | |
data-cart-url="${location.origin}/cart" | |
class="grid-product-form" | |
data-product-url="${product.url}" | |
data-skip-drawer | |
data-form-input="[{'product_id' : '${product.id}', 'quantity' : '1', 'price' : '${product.price}'}]" | |
> | |
<input type="hidden" value="${product.price}" name="price"/> | |
<input type="hidden" value="${product.id}" name="product_id"/> | |
<input type="hidden" value="${product.sku}" name="sku"/> | |
<input type="hidden" value="1" name="quantity"/> | |
<button type="submit" class="btn btn-primary btn-sm" aria-label="{{'Toevoegen aan winkelwagen'|t}}"> | |
<i class="fa fa-shopping-cart fa-lg mr-12"></i> <i class="fa fa-plus fa-lg"> </i> | |
</button> | |
</form> | |
`} | |
`} | |
${product.product_wishlist_url && html` | |
<button | |
type="button" | |
class="btn btn-sm btn-primary-inverse wishlist-link" | |
data-toggle="modal" | |
data-target="#wishlist-modal" | |
data-wl-product="${product.product_wishlist_url}" | |
data-wl-ajax-element="#wishlist-modal .modal-body" | |
data-wl-ajax-input="#wishlist-content > *" | |
aria-label="{{'Toevoegen aan wishlist'|t}}" | |
> | |
<i class="fa fa-heart"></i> | |
</button> | |
`} | |
</div> | |
</div> | |
</div> | |
</div> | |
</article> | |
</div> | |
`; | |
}; | |
const CrossSellProducts = ({ cartItems }) => { | |
const [crossSellItems, setCrossSellItems] = useState([]); | |
const [isLoading, setIsLoading] = useState(true); | |
useEffect(() => { | |
const fetchCrossSellItems = async (skus) => { | |
try { | |
const url = new URL(`${window.location.origin}/cart/cross`); | |
url.searchParams.set('sku', skus.join(',')); | |
const response = await fetch(url.toString(), { | |
headers: { | |
Accept: 'application/json' | |
} | |
}); | |
const data = await response.json(); | |
setCrossSellItems(data || []); | |
} catch (error) { | |
console.log(error); | |
} | |
}; | |
if (cartItems.length > 0) { | |
fetchCrossSellItems(cartItems.map(({ sku }) => sku)); | |
} | |
}, [cartItems]); | |
if (crossSellItems.length === 0) { | |
return null; | |
} | |
return html` | |
<hr /> | |
<h3> | |
{{'Anderen bestelden ook'|t}} | |
</h3> | |
<div class="product-grid grid"> | |
${crossSellItems.map(item => { | |
return html` | |
<${ProductCard} key="${item.id}" product="${item || {}}" /> | |
`; | |
})} | |
</div> | |
`; | |
}; | |
const CartPage = () => { | |
const [cart, setCart] = useState({}); | |
const [isLoadingCart, setIsLoadingCart] = useState(true); | |
const [error, setError] = useState(null); | |
const checkoutUrl = cart && cart.checkout && cart.checkout.url || ''; | |
const handleRemoveItems = async item => { | |
const updatedCart = await window.Storefront.removeCartItems(item.ids); | |
$('body').trigger('cart:updated', updatedCart); | |
$(document).trigger('af.cart.remove', [[{ | |
"price": ((item.details?.at(0)?.pricing?.amount || 0) / 100).toFixed(2), | |
"product_id": item.sku, | |
"sku": item.sku, | |
"quantity": item.quantity, | |
}]]); | |
}; | |
const handleChangeQuantity = async (item, quantity) => { | |
try { | |
if (!quantity || quantity === item.quantity) { | |
return; | |
} | |
let cartResponse; | |
if (quantity > item.quantity) { | |
cartResponse = await window.Storefront.addCartItems([ | |
{ | |
sku: item.sku, | |
quantity: quantity - item.quantity, | |
}, | |
]); | |
$(document).trigger('af.cart.add', [[{ | |
"price": ((item.details?.at(0)?.pricing?.amount || 0) / 100).toFixed(2), | |
"product_id": item.sku, | |
"sku": item.sku, | |
"quantity": quantity - item.quantity, | |
}]]); | |
} else { | |
const difference = item.quantity - quantity; | |
const ids = [...(item.ids || [])].reverse().slice(0, difference); | |
cartResponse = await window.Storefront.removeCartItems(ids); | |
$(document).trigger('af.cart.remove', [[{ | |
"price": ((item.details?.at(0)?.pricing?.amount || 0) / 100).toFixed(2), | |
"product_id": item.sku, | |
"sku": item.sku, | |
"quantity": difference, | |
}]]); | |
} | |
$('body').trigger('cart:updated', cartResponse); | |
} catch(error) { | |
$("#add-response-modal .modal-header .modal-title").html($("#add-response-modal").data("error-title")); | |
$("#add-response-modal .modal-body").addClass('text-center'); | |
$("#add-response-modal .modal-body").html('<span class="fa-stack fa-3x text-warning"><i class="far fa-circle fa-stack-2x"></i><i class="fas fa-exclamation fa-stack-1x"></i></span><p class="lead mt-15">' + getErrorMessage(error) + '</p>'); | |
$("#add-response-modal").modal('show'); | |
} | |
}; | |
useEffect(() => { | |
const updateCart = (event, val) => { | |
setCart(val); | |
if (isLoadingCart) { | |
setIsLoadingCart(false); | |
} | |
}; | |
$('body').on('cart:updated', updateCart); | |
return () => { | |
$('body').off('cart:updated', updateCart); | |
} | |
}, []); | |
return html` | |
<section id="contentcart" class="container mb-20 cart-version-flex${isLoadingCart ? ' loading-ajax' : ''}"> | |
<div class="mb-15"> | |
<div class="cart-header d-flex align-items-center justify-content-between"> | |
<h1 class="my-0 cart-title"> | |
{{'Winkelwagen'|t}} | |
</h1> | |
${(cart && cart.items && cart.items.length > 0) && !isLoadingCart && html` | |
<a class="btn btn-success visible-sm visible-xs" href="${checkoutUrl}"> | |
{{'Afrekenen'|t}} | |
</a>`} | |
</div> | |
<div class="clearfix"></div> | |
</div> | |
${error && html` | |
<div class="alert alert-warning">${error}</div> | |
`} | |
${(cart && cart.items && cart.items.length > 0) || isLoadingCart ? html` | |
<div class="d-flex flex-wrap flex-md-nowrap mx-n15"> | |
<${ItemsGrid} isLoading="${isLoadingCart}" items="${cart.items || []}" onRemove="${handleRemoveItems}" onChangeQuantity="${handleChangeQuantity}" /> | |
<${CartPageSummary} isLoading="${isLoadingCart}" cart="${cart}" checkoutUrl="${checkoutUrl}" /> | |
</div> | |
<${CrossSellProducts} cartItems="${cart?.items || []}" /> | |
` : html` | |
<h3 class="text-center text-warning"> | |
{{'Uw winkelwagen is leeg.'|t}} | |
<br /> | |
<br /> | |
<a class="center btn btn-primary" href="{{home_url}}">{{'Bekijk onze producten'|t}}</a> | |
</h3> | |
`} | |
</section> | |
`; | |
}; | |
const CartDrawerItem = ({ item, onRemove, onChangeQuantity }) => { | |
const { label, sku, image, ids, quantity, subtotal, total, adjustments } = item; | |
const increaseQuantity = item => onChangeQuantity(item, item.quantity + 1); | |
const decreaseQuantity = item => onChangeQuantity(item, item.quantity - 1); | |
return html` | |
<div class="cart-overview-item"> | |
<div class="cart-overview-item-inner d-flex gap-8"> | |
<div class="cart-overview-item-delete align-self-center"> | |
<a class="text-primary" onClick="${() => onRemove(item)}"> | |
<i class="fa fa-times text-danger fa-sm"></i> | |
</a> | |
</div> | |
<div class="cart-overview-item-image px-5"> | |
<div class="d-block icon-75"> | |
<div class="lazyload-wrapper lazyload-square"> | |
<img class="lazyload lazyload-cover" data-src="${image}" alt="${'{{'Afbeelding van [name]'|t}}'.replace('[name]', label || sku) }"/> | |
</div> | |
</div> | |
</div> | |
<div class="cart-overview-item-group d-flex flex-column justify-content-between flex-fill"> | |
<div class="cart-overview-item-name"> | |
${label || sku} | |
</div> | |
${adjustments.length > 0 && html` | |
<div class="d-flex justify-content-between text-muted mt-8"> | |
<small> | |
{{'Subtotaal'|t}} | |
</small> | |
<small> | |
${formatPrice(subtotal)} | |
</small> | |
</div> | |
${adjustments.map(adjustment => html` | |
<div class="d-flex justify-content-between text-muted"> | |
<small> | |
${adjustment.description}${adjustment.isPercentage ? ` (${adjustment.amount}%)` : ''} | |
</small> | |
<small> | |
${formatPrice(adjustment.outcome.amount * (adjustment.isDiscount ? -1 : 1))} | |
</small> | |
</div> | |
`)} | |
`} | |
<div class="d-flex justify-content-between${adjustments.length > 0 ? ' mt-12' : ''}"> | |
<div class="cart-overview-item-quantity align-self-end"> | |
<div class="quantity-counter"> | |
<button | |
type="button" | |
class="btn" | |
aria-label="{{'aantal min 1'|t}}" | |
onClick="${() => decreaseQuantity(item)}" | |
> | |
<i class="fa fa-minus fa-sm"></i> | |
</button> | |
<div class="mx-10"> | |
${quantity} | |
</div> | |
<button | |
type="button" | |
class="btn" | |
aria-label="{{'aantal plus 1'|t}}" | |
onClick="${() => increaseQuantity(item)}" | |
> | |
<i class="fa fa-plus fa-sm"></i> | |
</button> | |
</div> | |
</div> | |
<div class="cart-overview-item-price text-right"> | |
<strong> | |
${formatPrice(total)} | |
</strong> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
`; | |
}; | |
const CartDrawer = () => { | |
const [cart, setCart] = useState(null); | |
const [isLoadingCart, setIsLoadingCart] = useState(false); | |
const drawerRef = useRef(null); | |
const handleRemoveItems = async item => { | |
const updatedCart = await window.Storefront.removeCartItems(item.ids); | |
setCart(updatedCart); | |
$('body').trigger('cart:updated', updatedCart); | |
$(document).trigger('af.cart.remove', [[{ | |
"price": ((item.details?.at(0)?.pricing?.amount || 0) / 100).toFixed(2), | |
"product_id": item.sku, | |
"sku": item.sku, | |
"quantity": item.quantity, | |
}]]); | |
}; | |
const handleChangeQuantity = async (item, quantity) => { | |
try { | |
if (!quantity || quantity === item.quantity) { | |
return; | |
} | |
let cartResponse; | |
if (quantity > item.quantity) { | |
cartResponse = await window.Storefront.addCartItems([ | |
{ | |
sku: item.sku, | |
quantity: quantity - item.quantity, | |
}, | |
]); | |
$(document).trigger('af.cart.add', [[{ | |
"price": ((item.details?.at(0)?.pricing?.amount || 0) / 100).toFixed(2), | |
"product_id": item.sku, | |
"sku": item.sku, | |
"quantity": quantity - item.quantity, | |
}]]); | |
} else { | |
const difference = item.quantity - quantity; | |
const ids = [...(item.ids || [])].reverse().slice(0, difference); | |
cartResponse = await window.Storefront.removeCartItems(ids); | |
$(document).trigger('af.cart.remove', [[{ | |
"price": ((item.details?.at(0)?.pricing?.amount || 0) / 100).toFixed(2), | |
"product_id": item.sku, | |
"sku": item.sku, | |
"quantity": difference, | |
}]]); | |
} | |
setCart(cartResponse); | |
$('body').trigger('cart:updated', cartResponse); | |
} catch(error) { | |
$("#add-response-modal .modal-header .modal-title").html($("#add-response-modal").data("error-title")); | |
$("#add-response-modal .modal-body").addClass('text-center'); | |
$("#add-response-modal .modal-body").html('<span class="fa-stack fa-3x text-warning"><i class="far fa-circle fa-stack-2x"></i><i class="fas fa-exclamation fa-stack-1x"></i></span><p class="lead mt-15">' + getErrorMessage(error) + '</p>'); | |
$("#add-response-modal").modal('show'); | |
} | |
}; | |
const getCartData = async () => { | |
setIsLoadingCart(true); | |
let tokenIsValid = true; | |
const { search = '', pathname } = window?.location || {}; | |
const params = new URLSearchParams(search); | |
const cartToken = params.get('hash'); | |
const isCartPage = pathname === '/cart'; | |
const intent = isCartPage ? 'VIEW_CART' : null; | |
const cartResponse = await window.Storefront.getCart(cartToken, intent).catch(() => { | |
if (cartToken) { | |
tokenIsValid = false; | |
return window.Storefront.getCart(null, intent); | |
} | |
return Promise.reject(new Error('Cart not found')); | |
}); | |
if (cartResponse && tokenIsValid && cartToken) { | |
await window.Storefront.storeCartTokenInStorage(cartToken); | |
} | |
// window.Storefront.getCart().then(response => { | |
setIsLoadingCart(false); | |
$('body').trigger('cart:updated', cartResponse); | |
// }); | |
}; | |
useEffect(() => { | |
getCartData(); | |
}, []); | |
useEffect(() => { | |
const updateCart = (event, val) => { | |
if(!!val) { | |
setCart(val); | |
} | |
if (isLoadingCart) { | |
setIsLoadingCart(false); | |
} | |
}; | |
$('body').on('cart:updated', updateCart); | |
return () => { | |
$('body').off('cart:updated', updateCart); | |
} | |
}, []); | |
return html` | |
<div class="drawer drawer-right" id="cart-drawer" ref="${drawerRef}"> | |
<div class="drawer-header"> | |
<div class="h4 m-0 fw-700"> | |
{{"Winkelwagen"|t}} | |
</div> | |
<button class="btn btn-link m-4 p-0 icon-40 mr-n12" type="button" data-dismiss="drawer" aria-label="{{'Sluit winkelwagen'|t}}"> | |
<i class="fa fa-times icon-size-20 text-black"></i> | |
</button> | |
</div> | |
<div class="drawer-body configurator-drawer-inner p-0"> | |
${((cart && cart.items) || []).length === 0 ? html` | |
<div class="text-center"> | |
<i class="fa fa-cart-plus text-primary fa-4x mt-40 mb-40"></i> | |
<span class="h4 mb-12">{{"Uw winkelwagen is leeg"|t}}</span> | |
<p class=" mb-40">{{"Bekijk het aanbod op onze website en voeg producten toe aan je winkelwagen."|t}}</p> | |
<button type="button" class="btn btn-primary" data-dismiss="drawer"> | |
{{'Verder winkelen'|t}} | |
</button> | |
</div> | |
` : html` | |
<div class="cart-preview-items py-8"> | |
${((cart && cart.items) || []).map(item => html` | |
<${CartDrawerItem} | |
key="${item.sku}" | |
item="${item}" | |
onRemove="${handleRemoveItems}" | |
onChangeQuantity="${handleChangeQuantity}" | |
/> | |
`)} | |
</div> | |
`} | |
</div> | |
${((cart && cart.items) || []).length ? html` | |
<div class="drawer-footer drawer-footer-sticky pb-32 px-32"> | |
<hr class="mb-20" /> | |
${(cart.adjustments || []).map(({ description, amount, isDiscount, isPercentage, outcome }, index) => html` | |
<div class="d-flex justify-content-between my-5"> | |
<small> | |
${description}${isPercentage ? ` (${amount}%)` : ''} | |
</small> | |
<small> | |
${formatPrice((outcome.amount || 0) * (isDiscount ? -1 : 1) )} | |
</small> | |
</div> | |
`)} | |
${(cart.fees.shipping || []).map(({ description, total }, index) => html` | |
<div class="d-flex justify-content-between my-5"> | |
<small> | |
${description} | |
</small> | |
<small> | |
${formatPrice(total)} | |
</small> | |
</div> | |
`)} | |
${(cart.fees.payment || []).map(({ description, total }, index) => html` | |
<div class="d-flex justify-content-between my-5"> | |
<small> | |
${description} | |
</small> | |
<small> | |
${formatPrice(total)} | |
</small> | |
</div> | |
`)} | |
<div class="d-flex justify-content-between py-20"> | |
<span> | |
{{'Totaal'|t}} | |
</span> | |
<span> | |
${formatPrice(cart.total)} | |
</span> | |
</div> | |
<hr class="mt-0 mb-32" /> | |
<a class="btn btn-primary btn-block" href="{{cart_url}}"> | |
{{'Afrekenen'|t}} | |
</a> | |
</div> | |
` : ''} | |
</div> | |
`; | |
}; | |
const AccountDrawer = () => { | |
const [channel, setChannel] = useState(null); | |
const accountLink = channel?.links?.find(({ type }) => type === 'MY_ACCOUNT')?.value; | |
const [account, setAccount] = useState(window.Storefront.getUser()); | |
const getChannelData = async () => { | |
const channelResponse = await window.Storefront.getChannel(); | |
setChannel(channelResponse); | |
}; | |
const handleSignOut = () => { | |
window.Storefront.signOut(); | |
setAccount(window.Storefront.getUser()); | |
location.reload(); | |
}; | |
useEffect(() => { | |
getChannelData(); | |
}, []); | |
return html` | |
<div class="drawer drawer-right" id="account-drawer"> | |
<div class="drawer-header"> | |
<div class="h4 m-0 fw-700"> | |
{{"Account"|t}} | |
</div> | |
<button class="btn btn-link m-4 p-0 icon-40 mr-n12" type="button" data-dismiss="drawer" aria-label="{{'Sluit winkelwagen'|t}}"> | |
<i class="fa fa-times icon-size-20 text-black"></i> | |
</button> | |
</div> | |
<div class="drawer-body configurator-drawer-inner px-0 py-16 mx-0"> | |
<ul class="nav nav-pills nav-stacked"> | |
<li> | |
<a href="${accountLink}/account/orders" class="text-black"> | |
<i class="fa-solid fa-receipt fa-fw mr-12"></i> {{"Mijn bestellingen"|t}} | |
</a> | |
</li> | |
<li> | |
<a href="${accountLink}/account/details" class="text-black"> | |
<i class="fa-solid fa-address-card fa-fw mr-12"></i> {{"Mijn gegevens"|t}} | |
</a> | |
</li> | |
<li> | |
<a href="${accountLink}/account/addresses" class="text-black"> | |
<i class="fa-solid fa-location-dot fa-fw mr-12"></i> {{"Mijn adressen"|t}} | |
</a> | |
</li> | |
<li> | |
<a href="${accountLink}/account/preferences" class="text-black"> | |
<i class="fa-solid fa-sliders fa-fw mr-12"></i> {{"Mijn voorkeuren"|t}} | |
</a> | |
</li> | |
<li role="separator"> | |
<hr class="my-8" /> | |
</li> | |
<li> | |
<a onClick="${handleSignOut}" class="text-black"> | |
<i class="fa-solid fa-right-from-bracket fa-fw mr-12"></i> {{"Uitloggen"|t}} | |
</a> | |
</li> | |
</ul> | |
</div> | |
</div> | |
`; | |
}; | |
const AccountMenu = () => { | |
const [channel, setChannel] = useState(null); | |
const accountLink = channel?.links?.find(({ type }) => type === 'MY_ACCOUNT')?.value; | |
const [account, setAccount] = useState(window.Storefront.getUser()); | |
const getChannelData = async () => { | |
const channelResponse = await window.Storefront.getChannel(); | |
setChannel(channelResponse); | |
}; | |
const handleSignOut = () => { | |
window.Storefront.signOut(); | |
setAccount(window.Storefront.getUser()); | |
location.reload(); | |
}; | |
useEffect(() => { | |
getChannelData(); | |
}, []); | |
$('[data-toggle="tooltip"]').tooltip(); | |
if (account) { | |
return html` | |
<a data-toggle="drawer" data-target="#account-drawer"> | |
<i class="fa fa-user-check"></i> | |
<br /> | |
<span class="hidden-xs account-link-label">${account.givenName}</span> | |
</a> | |
`; | |
} | |
return html` | |
<a href="${accountLink}" data-toggle="tooltip" data-placement="bottom" title="Inloggen"> | |
<i class="fa fa-user-times"></i> | |
<br /> | |
<span class="hidden-xs account-link-label">{{"Inloggen"|t}}</span> | |
</a> | |
`; | |
}; | |
const StockUpdateSubscribeDialog = () => { | |
const [product, setProduct] = useState(null); | |
const [isSubmitting, setIsSubmitting] = useState(false); | |
const [isSubmitted, setIsSubmitted] = useState(false); | |
const [submittedData, setSubmittedData] = useState(null); | |
const { name, sku } = product || {}; | |
const handleSubmit = async (event) => { | |
event.preventDefault(); | |
$('#stock-update-email').parsley().removeError('serverError'); | |
try { | |
const formIsValid = $('#stock-update-email').parsley().isValid(); | |
if (!formIsValid) { | |
$('#stock-update-email').parsley().validate(); | |
return; | |
} | |
setIsSubmitting(true); | |
const form = event.target; | |
const formData = new FormData(form); | |
const formValues = {}; | |
formData.forEach((value, key) => { | |
formValues[key] = value; | |
}); | |
const response = await window.Storefront.createStockUpdateSubscription(formValues); | |
setSubmittedData(formValues); | |
setIsSubmitted(true); | |
} catch (e) { | |
$('#stock-update-email').parsley().addError('serverError', {message: getErrorMessage(e) }); | |
} | |
}; | |
useEffect(() => { | |
$('#stock-update-email').parsley({ | |
successClass: "form-val-success", | |
errorClass: "form-val-error", | |
errorsContainer: function(el) { | |
return el.$element.parents('.form-group').find('.field-error-container'); | |
}, | |
classHandler: function(el) { | |
return el.$element.closest(".validation-container"); | |
}, | |
errorsWrapper: "<ul class='list-unstyled text-danger'></ul>", | |
}); | |
$('body').on('click', '[data-toggle="stock-update"]', function() { | |
$('#stock-update-subscribe-dialog').modal('show'); | |
setProduct({ | |
name: $(this).data('name'), | |
sku: $(this).data('sku'), | |
}); | |
}); | |
$('#stock-update-subscribe-dialog').on('hidden.bs.modal', () => { | |
$('#stock-update-form').trigger('reset'); | |
setIsSubmitted(false); | |
setProduct(null); | |
setSubmittedData(null); | |
setIsSubmitting(false); | |
$('#stock-update-email').parsley({ | |
successClass: "form-val-success", | |
errorClass: "form-val-error", | |
errorsContainer: function(el) { | |
return el.$element.parents('.form-group').find('.field-error-container'); | |
}, | |
classHandler: function(el) { | |
return el.$element.closest(".validation-container"); | |
}, | |
errorsWrapper: "<ul class='list-unstyled text-danger'></ul>", | |
}); | |
}); | |
}, []); | |
return html` | |
<form id="stock-update-form" novalidate="" onSubmit="${handleSubmit}"> | |
<div class="modal fade" tabindex="-1" role="dialog" id="stock-update-subscribe-dialog"> | |
<div class="modal-dialog" role="document"> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |
<span aria-hidden="true">×</span> | |
</button> | |
<div class="h4 modal-title"> | |
${!isSubmitted && html` | |
{{'Blijf op de hoogte'|t}} | |
`} | |
${isSubmitted && html` | |
<i class="fa fa-check-circle text-success mr-12" aria-hidden="true" /> | |
{{'We houden je op de hoogte'|t}} | |
`} | |
</div> | |
</div> | |
<div class="modal-body"> | |
${!isSubmitted && html` | |
<p> | |
Helaas is <strong>${name}</strong> op dit moment uitverkocht. | |
Vul je e-mailadres in en ontvang een bericht zodra het weer op voorraad is. | |
</p> | |
<div class="form-group"> | |
<label for="stock-update-email">E-mailadres</label> | |
<input type="hidden" name="sku" value="${sku}" /> | |
<div class="validation-container"> | |
<input type="email" class="form-control" name="email" id="stock-update-email" required="" /> | |
</div> | |
<div class="field-error-container"></div> | |
</div> | |
`} | |
${isSubmitted && html` | |
<p> | |
Wanneer <strong>${name}</strong> leverbaar is, ontvang je een e-mailbericht op | |
<strong>${submittedData.email}</strong>. | |
</p> | |
`} | |
</div> | |
${!isSubmitted && html` | |
<div class="modal-footer" id="stock-update-dialog-footer-form"> | |
<button type="submit" class="btn btn-info" disabled="${isSubmitting}"> | |
{{'Houd mij op de hoogte'|t}} | |
</button> | |
</div> | |
`} | |
</div> | |
</div> | |
</div> | |
</form> | |
`; | |
}; | |
const StockUpdateActionDialog = () => { | |
const { action, token } = Object.fromEntries(new URLSearchParams(location.search)); | |
const [isLoading, setIsLoading] = useState(action !== 'unsubscribe_stock_updates'); | |
const [isSuccessful, setIsSuccessful] = useState(false); | |
const [errorMessage, setErrorMessage] = useState(null); | |
const handleApprove = async () => { | |
try { | |
const response = await window.Storefront.approveStockUpdateSubscription(token); | |
console.log(response); | |
setIsSuccessful(response.isSuccessful); | |
} catch (error) { | |
setErrorMessage(getErrorMessage(error)); | |
} finally { | |
setIsLoading(false); | |
} | |
}; | |
const handleUnsubscribe = async () => { | |
try { | |
const response = await window.Storefront.removeStockUpdateSubscription(token); | |
console.log(response); | |
setIsSuccessful(response.isSuccessful); | |
} catch (error) { | |
setErrorMessage(getErrorMessage(error)); | |
} finally { | |
setIsLoading(false); | |
} | |
}; | |
useEffect(() => { | |
$('#stock-update-dialog').modal('show'); | |
$('#stock-update-dialog').on('shown.bs.modal', function() { | |
switch(action) { | |
case 'approve_stock_updates': | |
handleApprove(); | |
break; | |
case 'unsubscribe_stock_updates': | |
break; | |
default: | |
setIsLoading(false); | |
setErrorMessage(`Unknown action ${action} provided`); | |
} | |
}); | |
}, []); | |
return html` | |
<div class="modal fade" tabindex="-1" role="dialog" id="stock-update-dialog"> | |
<div class="modal-dialog" role="document"> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |
<span aria-hidden="true">×</span> | |
</button> | |
<h4 class="modal-title"> | |
${isLoading && !errorMessage && action === 'approve_stock_updates' && '{{'Aanmelden...'|t}}'} | |
${!isLoading && !errorMessage && action === 'approve_stock_updates' && html` | |
<i class="fa fa-fw fa-check-circle text-success" /> {{'E-mailadres bevestigd'|t}} | |
`} | |
${action === 'unsubscribe_stock_updates' && !errorMessage && html` | |
${!isLoading && !isSuccessful && !errorMessage && html` | |
{{'Weet je zeker dat je wilt uitschrijven?'|t}} | |
`} | |
${isLoading && !errorMessage && '{{'Uitschrijven...'|t}}'} | |
${!isLoading && isSuccessful && !errorMessage && html` | |
<i class="fa fa-fw fa-check-circle text-success" />{{'Uitgeschreven'|t}} | |
`} | |
`} | |
${!!errorMessage && html` | |
<i class="fa fa-fw fa-times-circle text-danger" /> {{'Er is iets mis gegaan'|t}} | |
`} | |
</h4> | |
</div> | |
<div class="modal-body"> | |
${isLoading && html` | |
<div class="d-flex justify-content-center py-24"> | |
<i class="fa fa-circle-notch fa-spin fa-2x" /> | |
</div> | |
`} | |
${!isLoading && !errorMessage && action === 'approve_stock_updates' && html` | |
<p> | |
Je e-mailadres is bevestigd. Wanneer we jouw producten weer op voorraad hebben sturen we een mail. | |
</p> | |
`} | |
${!isLoading && !errorMessage && !isSuccessful && action === 'unsubscribe_stock_updates' && html` | |
<p> | |
Wanneer je je uitschrijft voor voorraad updates krijg geen e-mails meer met voorraad updates. | |
</p> | |
`} | |
${!isLoading && !errorMessage && isSuccessful && action === 'unsubscribe_stock_updates' && html` | |
<p> | |
Je bent uitgeschreven van voorraad updates. Je ontvangt kun geen e-mails meer over voorraad updates. | |
</p> | |
`} | |
${!!errorMessage && html` | |
<p>${errorMessage}</p> | |
`} | |
</div> | |
${!isLoading && !errorMessage && !isSuccessful && action === 'unsubscribe_stock_updates' && html` | |
<div class="modal-footer"> | |
<button | |
type="button" | |
class="btn btn-danger" | |
disable="${isLoading}" | |
onClick="${handleUnsubscribe}" | |
> | |
{{'Uitschrijven'|t}} | |
</button> | |
</div> | |
`} | |
</div> | |
</div> | |
</div> | |
`; | |
}; | |
const setupStorefront = () => { | |
const client = createStorefrontClient({ | |
domain: getDomain(window.location.host), | |
storefrontToken: '{{pluginData.orm.storefront}}', | |
graphQLClientOptions: { | |
headers: { | |
"Accept-Language": document.documentElement.lang, | |
} | |
} | |
}); | |
if (window.visitor_id) { | |
client.setSessionID(window.visitor_id); | |
} | |
const renderCartDrawer = () => { | |
if (!document.getElementById('cart-drawer-container')) { | |
const container = document.createElement('div'); | |
container.id = 'cart-drawer-container'; | |
document.body.appendChild(container); | |
} | |
return render(html`<${CartDrawer} />`, document.getElementById('cart-drawer-container')); | |
}; | |
const renderShareCartDialog = () => { | |
if (!document.getElementById('share-cart-dialog-container')) { | |
const container = document.createElement('div'); | |
container.id = 'share-cart-dialog-container'; | |
document.body.appendChild(container); | |
} | |
return render(html`<${ShareCartDialog} />`, document.getElementById('share-cart-dialog-container')); | |
}; | |
const renderCartPage = () => { | |
if (document.getElementById('cart-container')) { | |
return render(html`<${CartPage} />`, document.getElementById('cart-container')); | |
} | |
console.warn('[Afosto Storefront] No element found with id "cart-container" to mount the CartPage on. Add the id to an element or check your storefrontScripts.twig file.') | |
}; | |
const renderAccountDisplay = () => { | |
if (document.getElementById('account-display')) { | |
return render(html`<${AccountMenu} />`, document.getElementById('account-display')); | |
} | |
console.warn('[Afosto Storefront] No element found with id "account-display" to mount the AccountMenu on. Add the id to an element or check your storefrontScripts.twig file.') | |
}; | |
const renderAccountDrawer = () => { | |
if (!document.getElementById('account-drawer-container')) { | |
const container = document.createElement('div'); | |
container.id = 'account-drawer-container'; | |
document.body.appendChild(container); | |
} | |
return render(html`<${AccountDrawer} />`, document.getElementById('account-drawer-container')); | |
}; | |
const renderStockUpdateSubscribeDialog = () => { | |
if (!document.getElementById('stock-update-subscribe-dialog-wrapper')) { | |
const container = document.createElement('div'); | |
container.id = 'stock-update-subscribe-dialog-wrapper'; | |
document.body.appendChild(container); | |
} | |
return render(html`<${StockUpdateSubscribeDialog} />`, document.getElementById('stock-update-subscribe-dialog-wrapper')); | |
}; | |
const renderStockUpdateActionDialog = () => { | |
if (!document.getElementById('stock-update-action-dialog-wrapper')) { | |
const container = document.createElement('div'); | |
container.id = 'stock-update-action-dialog-wrapper'; | |
document.body.appendChild(container); | |
} | |
return render(html`<${StockUpdateActionDialog} />`, document.getElementById('stock-update-action-dialog-wrapper')); | |
}; | |
return { | |
...client, | |
client, | |
renderCartDrawer, | |
renderShareCartDialog, | |
renderCartPage, | |
renderAccountDisplay, | |
renderAccountDrawer, | |
renderStockUpdateSubscribeDialog, | |
renderStockUpdateActionDialog, | |
}; | |
}; | |
const afostoStorefront = setupStorefront(); | |
window.Storefront = afostoStorefront; | |
const initializeCart = async () => { | |
afostoStorefront.renderCartDrawer(); | |
afostoStorefront.renderAccountDisplay(); | |
afostoStorefront.renderAccountDrawer(); | |
if ("{{ type }}" === 'cart') { | |
afostoStorefront.renderCartPage(); | |
afostoStorefront.renderShareCartDialog(); | |
} | |
if ("{{ type }}" === 'product') { | |
afostoStorefront.renderStockUpdateSubscribeDialog(); | |
} | |
if (!!location.search.match(/approve_stock_updates|unsubscribe_stock_updates/g)) { | |
afostoStorefront.renderStockUpdateActionDialog(); | |
} | |
}; | |
document.addEventListener("readystatechange", (event) => { | |
if (document.readyState === 'complete') { | |
initializeCart().catch(() => { | |
// Do nothing. | |
}) | |
} | |
}); | |
</script> | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment