Add/Update/Remove/empty cart without refreshing the page
parent
f96d8335d6
commit
3373a60d8f
6
app.js
6
app.js
|
@ -302,6 +302,12 @@ handlebars = handlebars.create({
|
|||
'/': lvalue / rvalue,
|
||||
'%': lvalue % rvalue
|
||||
}[operator];
|
||||
},
|
||||
showCartButtons: (cart) => {
|
||||
if(!cart){
|
||||
return'd-none';
|
||||
}
|
||||
return'';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-disable prefer-arrow-callback, no-var, no-tabs */
|
||||
/* globals showNotification */
|
||||
/* globals showNotification, numeral */
|
||||
$(document).ready(function (){
|
||||
if($(window).width() < 768){
|
||||
$('.menu-side').on('click', function(e){
|
||||
|
@ -19,13 +19,6 @@ $(document).ready(function (){
|
|||
$('#offcanvasClose').hide();
|
||||
}
|
||||
|
||||
// If cart was open before reload, open it again
|
||||
var isCartOpen = (localStorage.getItem('cartOpen') === 'true');
|
||||
if(isCartOpen === true){
|
||||
localStorage.setItem('cartOpen', false);
|
||||
$('body').addClass('pushy-open-right');
|
||||
}
|
||||
|
||||
$('#userSetupForm').validator().on('submit', function(e){
|
||||
if(!e.isDefaultPrevented()){
|
||||
e.preventDefault();
|
||||
|
@ -304,8 +297,8 @@ $(document).ready(function (){
|
|||
}
|
||||
})
|
||||
.done(function(msg){
|
||||
$('#cart-count').text(msg.totalCartItems);
|
||||
showNotification(msg.message, 'success', true);
|
||||
showNotification(msg.message, 'success');
|
||||
updateCartDiv();
|
||||
})
|
||||
.fail(function(msg){
|
||||
showNotification(msg.responseJSON.message, 'danger');
|
||||
|
@ -335,8 +328,8 @@ $(document).ready(function (){
|
|||
data: { productId: $(this).attr('data-id') }
|
||||
})
|
||||
.done(function(msg){
|
||||
$('#cart-count').text(msg.totalCartItems);
|
||||
showNotification(msg.message, 'success', true);
|
||||
showNotification(msg.message, 'success');
|
||||
updateCartDiv();
|
||||
})
|
||||
.fail(function(msg){
|
||||
showNotification(msg.responseJSON.message, 'danger');
|
||||
|
@ -350,7 +343,7 @@ $(document).ready(function (){
|
|||
url: '/product/emptycart'
|
||||
})
|
||||
.done(function(msg){
|
||||
$('#cart-count').text(msg.totalCartItems);
|
||||
updateCartDiv();
|
||||
showNotification(msg.message, 'success', true);
|
||||
});
|
||||
});
|
||||
|
@ -405,8 +398,8 @@ function deleteFromCart(element){
|
|||
data: { productId: element.attr('data-id') }
|
||||
})
|
||||
.done(function(msg){
|
||||
setCartOpen();
|
||||
showNotification(msg.message, 'success', true);
|
||||
showNotification(msg.message, 'success');
|
||||
updateCartDiv();
|
||||
})
|
||||
.fail(function(msg){
|
||||
showNotification(msg.responseJSON.message, 'danger');
|
||||
|
@ -423,14 +416,6 @@ function cartUpdate(element){
|
|||
}
|
||||
}
|
||||
|
||||
function setCartOpen(){
|
||||
if($('body').hasClass('pushy-open-right') === true){
|
||||
localStorage.setItem('cartOpen', true);
|
||||
}else{
|
||||
localStorage.setItem('cartOpen', false);
|
||||
}
|
||||
}
|
||||
|
||||
function updateCart(element){
|
||||
// update cart on server
|
||||
$.ajax({
|
||||
|
@ -442,8 +427,7 @@ function updateCart(element){
|
|||
}
|
||||
})
|
||||
.done(function(msg){
|
||||
setCartOpen();
|
||||
showNotification(msg.message, 'success', true);
|
||||
updateCartDiv();
|
||||
})
|
||||
.fail(function(msg){
|
||||
showNotification(msg.responseJSON.message, 'danger', true);
|
||||
|
@ -487,3 +471,128 @@ function getSelectedOptions(){
|
|||
});
|
||||
return options;
|
||||
}
|
||||
|
||||
function updateCartDiv(){
|
||||
$.ajax({
|
||||
method: 'GET',
|
||||
url: '/checkout/cartdata'
|
||||
})
|
||||
.done(function(result){
|
||||
// Update the cart div
|
||||
var cart = result.cart;
|
||||
var session = result.session;
|
||||
var productHtml = '';
|
||||
var totalAmount = numeral(session.totalCartAmount).format('0.00');
|
||||
|
||||
// Work out the shipping
|
||||
var shippingTotalAmt = numeral(session.totalCartShipping).format('0.00');
|
||||
var shippingTotal = `<strong id="shipping-amount">${result.currencySymbol}${shippingTotalAmt}</strong>`;
|
||||
if(shippingTotalAmt === 0){
|
||||
shippingTotal = '<strong id="shipping-amount">FREE</strong>';
|
||||
}
|
||||
|
||||
// If the cart has contents
|
||||
if(cart){
|
||||
$('#cart-empty').empty();
|
||||
Object.keys(cart).forEach(function(productId){
|
||||
var item = cart[productId];
|
||||
// Setup the product
|
||||
var productTotalAmount = numeral(item.totalItemPrice).format('0.00');
|
||||
var optionsHtml = '';
|
||||
var optionIndex = 1;
|
||||
Object.keys(item.options).forEach(function(key){
|
||||
var option = item.options[key];
|
||||
if(optionIndex === Object.keys(item.options).length){
|
||||
optionsHtml += `<strong>${upperFirst(option.name)}</strong>: ${option.value}`;
|
||||
}else{
|
||||
optionsHtml += `<strong>${upperFirst(option.name)}</strong>: ${option.value} / `;
|
||||
}
|
||||
optionIndex++;
|
||||
});
|
||||
var productImage = `<img class="img-fluid" src="/uploads/placeholder.png" alt="${item.title} product image"></img>`;
|
||||
if(item.productImage){
|
||||
productImage = `<img class="img-fluid" src="${item.productImage}" alt="${item.title} product image"></img>`;
|
||||
}
|
||||
|
||||
// Setup the product html
|
||||
productHtml += `
|
||||
<div class="d-flex flex-row bottom-pad-15">
|
||||
<div class="col-4 col-md-3">
|
||||
${productImage}
|
||||
</div>
|
||||
<div class="col-12 col-md-7">
|
||||
<div class="row h-200">
|
||||
<div class="col-sm-12 text-left no-pad-left">
|
||||
<h6><a href="/product/${item.link}">${item.title}</a></h6>
|
||||
</div>
|
||||
<div class="col-sm-12 text-left no-pad-left">
|
||||
${optionsHtml}
|
||||
</div>
|
||||
<div class="col-md-8 no-pad-left">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<button class="btn btn-outline-primary btn-qty-minus" type="button">-</button>
|
||||
</div>
|
||||
<input type="number" class="form-control cart-product-quantity text-center" id="${productId}-qty" data-id="${productId}" maxlength="2" value="${item.quantity}">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-primary btn-qty-add" type="button">+</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 text-right">
|
||||
<button class="btn btn-outline-danger btn-delete-from-cart" data-id="${productId}" type="button"><i class="far fa-trash-alt" data-id="${productId}" aria-hidden="true"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-2 align-self-center text-right no-pad-right">
|
||||
<strong class="my-auto">${result.currencySymbol}${productTotalAmount}</strong>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
$('#cartBodyWrapper').html(productHtml);
|
||||
$('#cart-count').text(session.totalCartItems);
|
||||
}else{
|
||||
$('#cartBodyWrapper').html('');
|
||||
}
|
||||
|
||||
// Set the totals section
|
||||
var cartTotalsHtml = `
|
||||
<div class="row">
|
||||
<div class="cart-contents-shipping col-md-12 no-pad-right">
|
||||
<div class="text-right">
|
||||
Shipping: ${shippingTotal}
|
||||
</div>
|
||||
<div class="text-right">
|
||||
Total:
|
||||
<strong id="total-cart-amount">${result.currencySymbol}${totalAmount}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
var cartTotalsEmptyHtml = `
|
||||
<div id="cart-empty" class="row">
|
||||
<div class="cart-contents-shipping col-md-12 no-pad-right">
|
||||
Cart empty
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// Set depending on cart contents
|
||||
if(cart){
|
||||
$('#cartTotalsWrapper').html(cartTotalsHtml);
|
||||
$('#cart-buttons').removeClass('d-none');
|
||||
}else{
|
||||
$('#cartTotalsWrapper').html(cartTotalsEmptyHtml);
|
||||
$('#cart-buttons').addClass('d-none');
|
||||
}
|
||||
})
|
||||
.fail(function(result){
|
||||
showNotification(result.responseJSON.message, 'danger');
|
||||
});
|
||||
}
|
||||
|
||||
function upperFirst(value){
|
||||
return value.replace(/^\w/, (chr) => {
|
||||
return chr.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -131,6 +131,7 @@ router.get('/checkout/shipping', async (req, res, next) => {
|
|||
session: req.session,
|
||||
cartSize: 'part',
|
||||
cartClose: false,
|
||||
cartReadOnly: true,
|
||||
page: 'checkout-shipping',
|
||||
countryList,
|
||||
message: clearSessionValue(req.session, 'message'),
|
||||
|
@ -146,7 +147,7 @@ router.get('/checkout/cart', (req, res) => {
|
|||
res.render(`${config.themeViews}checkout-cart`, {
|
||||
page: req.query.path,
|
||||
cartSize: 'full',
|
||||
config: req.app.config,
|
||||
config,
|
||||
session: req.session,
|
||||
message: clearSessionValue(req.session, 'message'),
|
||||
messageType: clearSessionValue(req.session, 'messageType'),
|
||||
|
@ -156,7 +157,13 @@ router.get('/checkout/cart', (req, res) => {
|
|||
});
|
||||
|
||||
router.get('/checkout/cartdata', (req, res) => {
|
||||
res.status(200).json(req.session.cart);
|
||||
const config = req.app.config;
|
||||
|
||||
res.status(200).json({
|
||||
cart: req.session.cart,
|
||||
session: req.session,
|
||||
currencySymbol: config.currencySymbol || '$'
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/checkout/payment', (req, res) => {
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.20.2/mode/css/css.min.js" integrity="sha256-D5oJ11cOmRhXSYWELwG2U/XYH3YveZJr9taRYLZ2DSM=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.20.2/mode/xml/xml.min.js" integrity="sha256-ERFGS58tayDq5kkyNwd/89iZZ+HglMH7eYXxG1hxTvA=" crossorigin="anonymous"></script>
|
||||
{{/if}}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/numeral.js/2.0.6/numeral.min.js" integrity="sha256-LlHVI5rUauudM5ZcZaD6hHPHKrA7CSefHHnKgq+/AZc=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.7/js/tether.min.js" integrity="sha256-4lietOiwRDBKx1goZZbRiwB06L+/bPYEGDIKZt82bgg=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/1000hz-bootstrap-validator/0.11.9/validator.min.js"></script>
|
||||
<script src="/javascripts/jquery.bootpag.min.js"></script>
|
||||
|
@ -138,12 +139,10 @@
|
|||
<div id="cart" class="col-md-12">
|
||||
{{> (getTheme 'cart')}}
|
||||
<div class="row">
|
||||
{{#if @root.session.cart}}
|
||||
<div class="col-sm-12">
|
||||
<div id="cart-buttons" class="col-sm-12 {{showCartButtons @root.session.cart}}">
|
||||
<button class="btn btn-outline-danger float-left" id="empty-cart" type="button">{{ @root.__ "Empty cart" }}</button>
|
||||
<a href="/checkout/information" class="btn btn-outline-primary float-right">Checkout</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
|
||||
<div class="card top-marg-15 bottom-marg-15">
|
||||
<div class="card-body cart-body">
|
||||
<h5 class="card-title">{{ @root.__ "Cart contents" }}</h5>
|
||||
<div id="cartBodyWrapper">
|
||||
{{#each @root.session.cart}}
|
||||
<div class="d-flex flex-row bottom-pad-15">
|
||||
<div class="col-xs-4 col-md-3">
|
||||
<div class="col-4 col-md-3">
|
||||
{{#if productImage}}
|
||||
<img class="img-fluid" src="{{this.productImage}}" alt="{{this.title}} product image"> {{else}}
|
||||
<img class="img-fluid" src="/uploads/placeholder.png" alt="{{this.title}} product image"> {{/if}}
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-7">
|
||||
<div class="col-12 col-md-7">
|
||||
<div class="row h-200">
|
||||
<div class="col-sm-12 text-left no-pad-left">
|
||||
<h6><a href="/product/{{this.link}}">{{this.title}}</a></h6>
|
||||
|
@ -20,18 +19,17 @@
|
|||
{{#if @last}}
|
||||
<strong>{{#upperFirst this.name}}{{/upperFirst}}</strong>: {{this.value}}
|
||||
{{else}}
|
||||
<strong>{{#upperFirst this.name}}{{/upperFirst}}:</strong> {{this.value}} /
|
||||
<strong>{{#upperFirst this.name}}{{/upperFirst}}:</strong> {{this.value}} /
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{#ifCond cartReadOnly '!=' true}}
|
||||
{{#ifCond @root.cartReadOnly '!=' true}}
|
||||
<div class="col-md-8 no-pad-left">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<button class="btn btn-outline-primary btn-qty-minus" type="button">-</button>
|
||||
</div>
|
||||
<input type="number" class="form-control cart-product-quantity text-center" data-id="{{../this.productId}}" data-index="{{@key}}"
|
||||
maxlength="2" value="{{../this.quantity}}">
|
||||
<input type="number" class="form-control cart-product-quantity text-center" id="{{../this.productId}}-qty" data-id="{{../this.productId}}" maxlength="2" value="{{../this.quantity}}">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-primary btn-qty-add" type="button">+</button>
|
||||
</div>
|
||||
|
@ -43,35 +41,35 @@
|
|||
{{/ifCond}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="align-self-center col-sm-12 col-md-2 text-right no-pad-right">
|
||||
<div class="col-12 col-md-2 align-self-center text-right no-pad-right">
|
||||
<strong class="my-auto">{{currencySymbol @root.config.currencySymbol}}{{formatAmount this.totalItemPrice}}</strong>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
<div id="cartTotalsWrapper" class="container-fluid">
|
||||
{{#if @root.session.cart}}
|
||||
<div class="row">
|
||||
<div class="cart-contents-shipping col-md-12 no-pad-right">
|
||||
{{#ifCond @root.session.shippingCostApplied '===' true}}
|
||||
<div class="text-right">
|
||||
{{ @root.__ "Shipping" }}
|
||||
<strong>{{currencySymbol @root.config.currencySymbol}}{{formatAmount @root.session.totalCartShipping}}</strong>
|
||||
<strong id="shipping-amount">{{currencySymbol @root.config.currencySymbol}}{{formatAmount @root.session.totalCartShipping}}</strong>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="text-right">
|
||||
{{ @root.__ "Shipping" }}
|
||||
<strong>FREE</strong>
|
||||
<strong id="shipping-amount">FREE</strong>
|
||||
</div>
|
||||
{{/ifCond}}
|
||||
<div class="text-right">
|
||||
Total:
|
||||
<strong>{{currencySymbol @root.config.currencySymbol}}{{formatAmount @root.session.totalCartAmount}}</strong>
|
||||
<strong id="total-cart-amount">{{currencySymbol @root.config.currencySymbol}}{{formatAmount @root.session.totalCartAmount}}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="row">
|
||||
<div id="cart-empty" class="row">
|
||||
<div class="cart-contents-shipping col-md-12 no-pad-right">
|
||||
Cart empty
|
||||
</div>
|
||||
|
|
|
@ -3,12 +3,10 @@
|
|||
<div id="cart" class="col-md-12">
|
||||
{{> (getTheme 'cart')}}
|
||||
<div class="row">
|
||||
{{#if @root.session.cart}}
|
||||
<div class="col-sm-12">
|
||||
<div id="cart-buttons" class="col-sm-12 {{showCartButtons @root.session.cart}}">
|
||||
<button class="btn btn-outline-danger float-left" id="empty-cart" type="button">{{ @root.__ "Empty cart" }}</button>
|
||||
<a href="/checkout/information" class="btn btn-outline-danger float-right">Checkout</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
{{> (getTheme 'cart')}}
|
||||
{{#if @root.session.cart}}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div id="cart-buttons" class="col-sm-12 {{showCartButtons @root.session.cart}}">
|
||||
<button class="btn btn-outline-danger float-right" id="empty-cart" type="button">{{ @root.__ "Empty cart" }}</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
|
@ -29,11 +29,9 @@
|
|||
<div id="cart" class="col-md-7 d-none d-sm-block">
|
||||
{{> (getTheme 'cart')}}
|
||||
<div class="row">
|
||||
{{#if @root.session.cart}}
|
||||
<div class="col-sm-12">
|
||||
<div id="cart-buttons" class="col-sm-12 {{showCartButtons @root.session.cart}}">
|
||||
<button class="btn btn-outline-danger float-right" id="empty-cart" type="button">{{ @root.__ "Empty cart" }}</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue