t.toUpperCase())}$(document).ready(function(){if($(window).width()<768&&($(".menu-side").on("click",function(t){t.preventDefault(),$('.menu-side li:not(".active")').slideToggle()}),$('.menu-side li:not(".active")').hide(),$(".menu-side>.active").html('
'),$(".menu-side>.active").addClass("menu-side-mobile"),0===$("#navbar ul li").length&&$("#navbar").hide(),$("#offcanvasClose").hide()),$("#userSetupForm").validator().on("submit",function(t){t.isDefaultPrevented()||(t.preventDefault(),$.ajax({method:"POST",url:"/admin/setup_action",data:{usersName:$("#usersName").val(),userEmail:$("#userEmail").val(),userPassword:$("#userPassword").val()}}).done(function(t){showNotification(t.message,"success",!1,"/admin/login")}).fail(function(t){showNotification(t.responseJSON.message,"danger")}))}),$(document).on("click",".menu-btn",function(t){t.preventDefault(),$("body").addClass("pushy-open-right")}),$("table").each(function(){$(this).addClass("table table-hover")}),$("#productTags").length&&$("#productTags").tokenfield(),$(document).on("click",".dashboard_list",function(t){window.document.location=$(this).attr("href")}).hover(function(){$(this).toggleClass("hover")}),$(document).on("click",".btn-qty-minus",function(t){t.preventDefault();var a=$(t.target).parent().parent().find(".cart-product-quantity");$(a).val(parseInt(a.val())-1),cartUpdate(a)}),$(document).on("click",".btn-qty-add",function(t){t.preventDefault();var a=$(t.target).parent().parent().find(".cart-product-quantity");$(a).val(parseInt(a.val())+1),cartUpdate(a)}),$(document).on("click",".btn-delete-from-cart",function(t){deleteFromCart($(t.target))}),$("#pager").length){var t=$("#pageNum").val(),a=$("#productsPerPage").val(),e=$("#totalProductCount").val(),n=$("#paginateUrl").val(),o=$("#searchTerm").val();""!==o&&(o+="/");var i="/"+n+"/"+o+"{{number}}",r=Math.ceil(e/a);parseInt(e)>parseInt(a)&&($("#pager").bootpag({total:r,page:t,maxVisible:5,href:i,wrapClass:"pagination",prevClass:"page-item previous",nextClass:"page-item next",activeClass:"page-item active"}),$("#pager a").each(function(){$(this).addClass("page-link")}))}if($("#customerLogout").on("click",function(t){$.ajax({method:"POST",url:"/customer/logout",data:{}}).done(function(t){location.reload()})}),$("#customerForgotten").validator().on("submit",function(t){t.isDefaultPrevented()||(t.preventDefault(),$.ajax({method:"POST",url:"/customer/forgotten_action",data:{email:$("#email").val()}}).done(function(t){showNotification(t.message,"success")}).fail(function(t){t.message?showNotification(t.responseJSON.message,"danger"):showNotification(t.responseText,"danger")}))}),$(document).on("click","#createAccountCheckbox",function(t){$("#newCustomerPassword").prop("required",$("#createAccountCheckbox").prop("checked"))}),$("#checkoutInformation").validator().on("click",function(t){if(t.preventDefault(),0===$("#shipping-form").validator("validate").has(".has-error").length){var a="/customer/save";$("#createAccountCheckbox").prop("checked")&&(a="/customer/create"),$.ajax({method:"POST",url:a,data:{email:$("#shipEmail").val(),firstName:$("#shipFirstname").val(),lastName:$("#shipLastname").val(),address1:$("#shipAddr1").val(),address2:$("#shipAddr2").val(),country:$("#shipCountry").val(),state:$("#shipState").val(),postcode:$("#shipPostcode").val(),phone:$("#shipPhoneNumber").val(),password:$("#newCustomerPassword").val(),orderComment:$("#orderComment").val()}}).done(function(){window.location="/checkout/shipping"}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}}),$("#addDiscountCode").on("click",function(t){t.preventDefault(),$.ajax({method:"POST",url:"/checkout/adddiscountcode",data:{discountCode:$("#discountCode").val()}}).done(function(t){showNotification(t.message,"success",!0)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$("#removeDiscountCode").on("click",function(t){t.preventDefault(),$.ajax({method:"POST",url:"/checkout/removediscountcode",data:{}}).done(function(t){showNotification(t.message,"success",!0)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$("#loginForm").on("click",function(t){t.isDefaultPrevented()||(t.preventDefault(),$.ajax({method:"POST",url:"/admin/login_action",data:{email:$("#email").val(),password:$("#password").val()}}).done(function(t){window.location="/admin"}).fail(function(t){showNotification(t.responseJSON.message,"danger")})),t.preventDefault()}),$("#customerLogin").on("click",function(t){t.isDefaultPrevented()||(t.preventDefault(),$.ajax({method:"POST",url:"/customer/login_action",data:{loginEmail:$("#customerLoginEmail").val(),loginPassword:$("#customerLoginPassword").val()}}).done(function(t){var a=t.customer;$("#shipEmail").val(a.email),$("#shipFirstname").val(a.firstName),$("#shipLastname").val(a.lastName),$("#shipAddr1").val(a.address1),$("#shipAddr2").val(a.address2),$("#shipCountry").val(a.country),$("#shipState").val(a.state),$("#shipPostcode").val(a.postcode),$("#shipPhoneNumber").val(a.phone),location.reload()}).fail(function(t){showNotification(t.responseJSON.message,"danger")})),t.preventDefault()}),$(document).on("click",".image-next",function(t){var a=$(".thumbnail-image"),e=0,n=0;$(".thumbnail-image").each(function(){$("#product-title-image").attr("src")===$(this).attr("src")&&(n=e+1===a.length||e+1<0?0:e+1),e++}),$("#product-title-image").attr("src",$(a).eq(n).attr("src"))}),$(document).on("click",".image-prev",function(t){var a=$(".thumbnail-image"),e=0,n=0;$(".thumbnail-image").each(function(){$("#product-title-image").attr("src")===$(this).attr("src")&&(n=e-1===a.length||e-1<0?a.length-1:e-1),e++}),$("#product-title-image").attr("src",$(a).eq(n).attr("src"))}),$(document).on("click",".product-add-to-cart",function(t){var a=getSelectedOptions();parseInt($("#product_quantity").val())<1&&$("#product_quantity").val(1),$.ajax({method:"POST",url:"/product/addtocart",data:{productId:$("#productId").val(),productQuantity:$("#product_quantity").val(),productOptions:JSON.stringify(a),productComment:$("#product_comment").val()}}).done(function(t){showNotification(t.message,"success"),updateCartDiv()}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$("#product_quantity").on("keyup",function(t){checkMaxQuantity(t,$("#product_quantity"))}),$(".cart-product-quantity").on("keyup",function(t){checkMaxQuantity(t,$(".cart-product-quantity"))}),$(".cart-product-quantity").on("focusout",function(t){cartUpdate($(t.target))}),$(document).on("click",".pushy-link",function(t){$("body").removeClass("pushy-open-right")}),$(document).on("click",".add-to-cart",function(t){var a="/product/"+$(this).attr("data-id");$(this).attr("data-link")&&(a="/product/"+$(this).attr("data-link")),"true"===$(this).attr("data-has-options")?window.location=a:$.ajax({method:"POST",url:"/product/addtocart",data:{productId:$(this).attr("data-id")}}).done(function(t){showNotification(t.message,"success"),updateCartDiv()}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(document).on("click","#empty-cart",function(t){$.ajax({method:"POST",url:"/product/emptycart"}).done(function(t){showNotification(t.message,"success",!0),updateCartDiv()})}),$(".qty-btn-minus").on("click",function(){var t=parseInt($("#product_quantity").val())-1;$("#product_quantity").val(t>0?t:1)}),$(".qty-btn-plus").on("click",function(){$("#product_quantity").val(parseInt($("#product_quantity").val())+1)}),$(".thumbnail-image").on("click",function(){$("#product-title-image").attr("src",$(this).attr("src"))}),$(document).on("click","#btn_search_reset",function(t){window.location.replace("/")}),$(document).on("click","#btn_search",function(t){t.preventDefault(),""===$("#frm_search").val().trim()?showNotification("Please enter a search value","danger"):window.location.href="/search/"+$("#frm_search").val()}),""!==$("#input_notify_message").val()){var c=$("#input_notify_message").val(),s=$("#input_notify_messageType").val();$("#input_notify_message").val(""),$("#input_notify_messageType").val(""),showNotification(c,s||"danger",!1)}});
\ No newline at end of file
+function checkMaxQuantity(t,a){if($("#maxQuantity").length){if(46===t.keyCode||8===t.keyCode)return;if(parseInt($(t.target).val())>parseInt($("#maxQuantity").val())){const n=a.val();t.preventDefault(),a.val(n.slice(0,-1)),showNotification(`Exceeds maximum quantity: ${$("#maxQuantity").val()}`,"warning",!1)}}}function deleteFromCart(t){$.ajax({method:"POST",url:"/product/removefromcart",data:{cartId:t.attr("data-cartid")}}).done(function(t){showNotification(t.message,"success"),updateCartDiv()}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}function cartUpdate(t){$(t).val()>0?""!==$(t).val()&&updateCart(t):$(t).val(1)}function updateCart(t){$.ajax({method:"POST",url:"/product/updatecart",data:{cartId:t.attr("data-cartid"),productId:t.attr("data-id"),quantity:t.val()}}).done(function(t){updateCartDiv()}).fail(function(t){showNotification(t.responseJSON.message,"danger",!0)})}function getSelectedOptions(){var t={};return $(".product-opt").each(function(){var a=$(this).val().trim(),n=$(this).attr("data-label"),e=$(this).attr("name"),o=$(this).attr("type");o||(t[e]={label:n,name:e,value:a}),"radio"===o&&(t[e]={label:n,name:e,value:$('input[name="'+e+'"]:checked').val()}),"checkbox"===o&&(t[e]={label:n,name:e,value:$('input[name="'+$(this).attr("name")+'"]').is(":checked")})}),t}function updateCartDiv(){$.ajax({method:"GET",url:"/checkout/cartdata"}).done(function(t){var a=t.cart,n=t.session,e="",o=numeral(n.totalCartAmount).format("0.00"),i=numeral(n.totalCartShipping).format("0.00"),r=`${n.shippingMessage} :
${t.currencySymbol}${i}`;0===n.totalCartShipping&&(r=`
${n.shippingMessage}`);var c=numeral(n.totalCartDiscount).format("0.00"),s="";n.totalCartDiscount>0&&(s=`\n
\n Discount: ${t.currencySymbol}${c}\n
`),a?($("#cart-empty").empty(),Object.keys(a).forEach(function(n){var o=a[n],i=numeral(o.totalItemPrice).format("0.00"),r="",c=1;Object.keys(o.options).forEach(function(t){var a=o.options[t];c===Object.keys(o.options).length?r+=`
${upperFirst(a.name)}: ${a.value}`:r+=`
${upperFirst(a.name)}: ${a.value} / `,c++});var s=`
`;o.productImage&&(s=`
`),e+=`\n
\n
\n
\n
\n ${s}\n
\n
\n
\n
\n
\n
\n \n
\n
\n ${t.currencySymbol}${i}\n
\n
\n
\n
\n
\n
`}),$(".cartBodyWrapper").html(e)):$(".cartBodyWrapper").html(""),$("#cart-count").text(n.totalCartItems);var d=`\n
\n
\n
\n ${r}\n
\n ${s}\n
\n Total:\n ${t.currencySymbol}${o}\n
\n
\n
`;a?($(".cartTotalsWrapper").html(d),$(".cart-buttons").removeClass("d-none")):($(".cartTotalsWrapper").html('\n
\n
t.toUpperCase())}function emptyCart(){$.ajax({method:"POST",url:"/product/emptycart"}).done(function(t){showNotification(t.message,"success",!0),updateCartDiv()})}$(document).ready(function(){if($(window).width()<768&&($(".menu-side").on("click",function(t){t.preventDefault(),$('.menu-side li:not(".active")').slideToggle()}),$('.menu-side li:not(".active")').hide(),$(".menu-side>.active").html(''),$(".menu-side>.active").addClass("menu-side-mobile"),0===$("#navbar ul li").length&&$("#navbar").hide(),$("#offcanvasClose").hide()),$("#userSetupForm").validator().on("submit",function(t){t.isDefaultPrevented()||(t.preventDefault(),$.ajax({method:"POST",url:"/admin/setup_action",data:{usersName:$("#usersName").val(),userEmail:$("#userEmail").val(),userPassword:$("#userPassword").val()}}).done(function(t){showNotification(t.message,"success",!1,"/admin/login")}).fail(function(t){showNotification(t.responseJSON.message,"danger")}))}),$(document).on("click",".menu-btn",function(t){t.preventDefault(),$("body").addClass("pushy-open-right")}),$("table").each(function(){$(this).addClass("table table-hover")}),$("#productTags").length&&$("#productTags").tokenfield(),$(document).on("click",".dashboard_list",function(t){window.document.location=$(this).attr("href")}).hover(function(){$(this).toggleClass("hover")}),$(document).on("click",".btn-qty-minus",function(t){t.preventDefault();var a=$(t.target).parent().parent().find(".cart-product-quantity");$(a).val(parseInt(a.val())-1),cartUpdate(a)}),$(document).on("click",".btn-qty-add",function(t){t.preventDefault();var a=$(t.target).parent().parent().find(".cart-product-quantity");$(a).val(parseInt(a.val())+1),cartUpdate(a)}),$(document).on("click",".btn-delete-from-cart",function(t){deleteFromCart($(t.target))}),$("#pager").length){var t=$("#pageNum").val(),a=$("#productsPerPage").val(),n=$("#totalProductCount").val(),e=$("#paginateUrl").val(),o=$("#searchTerm").val();""!==o&&(o+="/");var i="/"+e+"/"+o+"{{number}}",r=Math.ceil(n/a);parseInt(n)>parseInt(a)&&($("#pager").bootpag({total:r,page:t,maxVisible:5,href:i,wrapClass:"pagination",prevClass:"page-item previous",nextClass:"page-item next",activeClass:"page-item active"}),$("#pager a").each(function(){$(this).addClass("page-link")}))}if($("#customerLogout").on("click",function(t){$.ajax({method:"POST",url:"/customer/logout",data:{}}).done(function(t){location.reload()})}),$("#customerForgotten").validator().on("submit",function(t){t.isDefaultPrevented()||(t.preventDefault(),$.ajax({method:"POST",url:"/customer/forgotten_action",data:{email:$("#email").val()}}).done(function(t){showNotification(t.message,"success")}).fail(function(t){t.message?showNotification(t.responseJSON.message,"danger"):showNotification(t.responseText,"danger")}))}),$(document).on("click","#createAccountCheckbox",function(t){$("#newCustomerPassword").prop("required",$("#createAccountCheckbox").prop("checked"))}),$("#checkoutInformation").validator().on("click",function(t){if(t.preventDefault(),0===$("#shipping-form").validator("validate").has(".has-error").length){var a="/customer/save";$("#createAccountCheckbox").prop("checked")&&(a="/customer/create"),$.ajax({method:"POST",url:a,data:{email:$("#shipEmail").val(),firstName:$("#shipFirstname").val(),lastName:$("#shipLastname").val(),address1:$("#shipAddr1").val(),address2:$("#shipAddr2").val(),country:$("#shipCountry").val(),state:$("#shipState").val(),postcode:$("#shipPostcode").val(),phone:$("#shipPhoneNumber").val(),password:$("#newCustomerPassword").val(),orderComment:$("#orderComment").val()}}).done(function(){window.location="/checkout/shipping"}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}}),$("#addDiscountCode").on("click",function(t){t.preventDefault(),$.ajax({method:"POST",url:"/checkout/adddiscountcode",data:{discountCode:$("#discountCode").val()}}).done(function(t){showNotification(t.message,"success",!0)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$("#removeDiscountCode").on("click",function(t){t.preventDefault(),$.ajax({method:"POST",url:"/checkout/removediscountcode",data:{}}).done(function(t){showNotification(t.message,"success",!0)}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$("#loginForm").on("click",function(t){t.isDefaultPrevented()||(t.preventDefault(),$.ajax({method:"POST",url:"/admin/login_action",data:{email:$("#email").val(),password:$("#password").val()}}).done(function(t){window.location="/admin"}).fail(function(t){showNotification(t.responseJSON.message,"danger")})),t.preventDefault()}),$("#customerLogin").on("click",function(t){t.isDefaultPrevented()||(t.preventDefault(),$.ajax({method:"POST",url:"/customer/login_action",data:{loginEmail:$("#customerLoginEmail").val(),loginPassword:$("#customerLoginPassword").val()}}).done(function(t){var a=t.customer;$("#shipEmail").val(a.email),$("#shipFirstname").val(a.firstName),$("#shipLastname").val(a.lastName),$("#shipAddr1").val(a.address1),$("#shipAddr2").val(a.address2),$("#shipCountry").val(a.country),$("#shipState").val(a.state),$("#shipPostcode").val(a.postcode),$("#shipPhoneNumber").val(a.phone),location.reload()}).fail(function(t){showNotification(t.responseJSON.message,"danger")})),t.preventDefault()}),$(document).on("click",".image-next",function(t){var a=$(".thumbnail-image"),n=0,e=0;$(".thumbnail-image").each(function(){$("#product-title-image").attr("src")===$(this).attr("src")&&(e=n+1===a.length||n+1<0?0:n+1),n++}),$("#product-title-image").attr("src",$(a).eq(e).attr("src"))}),$(document).on("click",".image-prev",function(t){var a=$(".thumbnail-image"),n=0,e=0;$(".thumbnail-image").each(function(){$("#product-title-image").attr("src")===$(this).attr("src")&&(e=n-1===a.length||n-1<0?a.length-1:n-1),n++}),$("#product-title-image").attr("src",$(a).eq(e).attr("src"))}),$(document).on("click",".product-add-to-cart",function(t){var a=getSelectedOptions();parseInt($("#product_quantity").val())<1&&$("#product_quantity").val(1),$.ajax({method:"POST",url:"/product/addtocart",data:{productId:$("#productId").val(),productQuantity:$("#product_quantity").val(),productOptions:JSON.stringify(a),productComment:$("#product_comment").val()}}).done(function(t){showNotification(t.message,"success"),updateCartDiv()}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$("#product_quantity").on("keyup",function(t){checkMaxQuantity(t,$("#product_quantity"))}),$(".cart-product-quantity").on("keyup",function(t){checkMaxQuantity(t,$(".cart-product-quantity"))}),$(".cart-product-quantity").on("focusout",function(t){cartUpdate($(t.target))}),$(document).on("click",".pushy-link",function(t){$("body").removeClass("pushy-open-right")}),$(document).on("click",".add-to-cart",function(t){var a="/product/"+$(this).attr("data-id");$(this).attr("data-link")&&(a="/product/"+$(this).attr("data-link")),"true"===$(this).attr("data-has-options")?window.location=a:$.ajax({method:"POST",url:"/product/addtocart",data:{productId:$(this).attr("data-id")}}).done(function(t){showNotification(t.message,"success"),updateCartDiv()}).fail(function(t){showNotification(t.responseJSON.message,"danger")})}),$(document).on("click","#empty-cart",function(t){$("#confirmModal").modal("show"),$("#buttonConfirm").attr("data-func","emptyCart")}),$(document).on("click","#buttonConfirm",function(t){var a=$(t.target).attr("data-func");window[a](),$("#confirmModal").modal("hide")}),$(".qty-btn-minus").on("click",function(){var t=parseInt($("#product_quantity").val())-1;$("#product_quantity").val(t>0?t:1)}),$(".qty-btn-plus").on("click",function(){$("#product_quantity").val(parseInt($("#product_quantity").val())+1)}),$(".thumbnail-image").on("click",function(){$("#product-title-image").attr("src",$(this).attr("src"))}),$(document).on("click","#btn_search_reset",function(t){window.location.replace("/")}),$(document).on("click","#btn_search",function(t){t.preventDefault(),""===$("#frm_search").val().trim()?showNotification("Please enter a search value","danger"):window.location.href="/search/"+$("#frm_search").val()}),""!==$("#input_notify_message").val()){var c=$("#input_notify_message").val(),s=$("#input_notify_messageType").val();$("#input_notify_message").val(""),$("#input_notify_messageType").val(""),showNotification(c,s||"danger",!1)}});
\ No newline at end of file
diff --git a/public/stylesheets/pushy.css b/public/stylesheets/pushy.css
index 60e4309..7e64904 100644
--- a/public/stylesheets/pushy.css
+++ b/public/stylesheets/pushy.css
@@ -8,7 +8,7 @@
width: 500px;
height: 100%;
top: 0;
- z-index: 9999;
+ z-index: 999;
background: #fff;
overflow: auto;
visibility: hidden;
@@ -80,7 +80,7 @@
right: 0;
bottom: 0;
left: 0;
- z-index: 9998;
+ z-index: 998;
background-color: rgba(0, 0, 0, 0.5);
-webkit-animation: fade 500ms;
animation: fade 500ms;
diff --git a/public/stylesheets/pushy.min.css b/public/stylesheets/pushy.min.css
index ad287e3..af85f46 100644
--- a/public/stylesheets/pushy.min.css
+++ b/public/stylesheets/pushy.min.css
@@ -1,4 +1,4 @@
/*! Pushy - v1.0.0 - 2016-3-1
* Pushy is a responsive off-canvas navigation menu using CSS transforms & transitions.
* https://github.com/christophery/pushy/
-* by Christopher Yee */.pushy{position:fixed;width:500px;height:100%;top:0;z-index:9999;background:#fff;overflow:auto;visibility:hidden;-webkit-overflow-scrolling:touch}.pushy ul:first-child{margin-top:10px}.pushy.pushy-left{left:0}.pushy.pushy-right{right:0}.pushy-left{-webkit-transform:translate3d(-500px,0,0);-ms-transform:translate3d(-500px,0,0);transform:translate3d(-500px,0,0)}.pushy-open-left #container,.pushy-open-left .push{-webkit-transform:translate3d(500px,0,0);-ms-transform:translate3d(500px,0,0);transform:translate3d(500px,0,0)}.pushy-right{-webkit-transform:translate3d(500px,0,0);-ms-transform:translate3d(500px,0,0);transform:translate3d(500px,0,0)}.pushy-open-right #container,.pushy-open-right .push{-webkit-transform:translate3d(-500px,0,0);-ms-transform:translate3d(-500px,0,0);transform:translate3d(-500px,0,0)}.pushy-open-left .pushy,.pushy-open-right .pushy{-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}#container,.push,.pushy{transition:transform .2s cubic-bezier(.16,.68,.43,.99)}.site-overlay{display:none}.pushy-open-left .site-overlay,.pushy-open-right .site-overlay{display:block;position:fixed;top:0;right:0;bottom:0;left:0;z-index:9998;background-color:rgba(0,0,0,.5);-webkit-animation:fade .5s;animation:fade .5s}@keyframes fade{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes fade{0%{opacity:0}100%{opacity:1}}.pushy-submenu ul{padding-left:15px;transition:max-height .2s ease-in-out}.pushy-submenu ul .pushy-link{transition:opacity .2s ease-in-out}.pushy-submenu>a{position:relative}.pushy-submenu>a::after{content:'';display:block;height:11px;width:8px;position:absolute;top:50%;right:15px;background:url(../img/arrow.svg) no-repeat;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);transition:transform .2s}.pushy-submenu-closed ul{max-height:0;overflow:hidden}.pushy-submenu-closed .pushy-link{opacity:0}.pushy-submenu-open ul{max-height:1000px}.pushy-submenu-open .pushy-link{opacity:1}.pushy-submenu-open a::after{-webkit-transform:translateY(-50%) rotate(90deg);-ms-transform:translateY(-50%) rotate(90deg);transform:translateY(-50%) rotate(90deg)}.no-csstransforms3d .pushy-submenu-closed ul{max-height:none;display:none}
\ No newline at end of file
+* by Christopher Yee */.pushy{position:fixed;width:500px;height:100%;top:0;z-index:999;background:#fff;overflow:auto;visibility:hidden;-webkit-overflow-scrolling:touch}.pushy ul:first-child{margin-top:10px}.pushy.pushy-left{left:0}.pushy.pushy-right{right:0}.pushy-left{-webkit-transform:translate3d(-500px,0,0);-ms-transform:translate3d(-500px,0,0);transform:translate3d(-500px,0,0)}.pushy-open-left #container,.pushy-open-left .push{-webkit-transform:translate3d(500px,0,0);-ms-transform:translate3d(500px,0,0);transform:translate3d(500px,0,0)}.pushy-right{-webkit-transform:translate3d(500px,0,0);-ms-transform:translate3d(500px,0,0);transform:translate3d(500px,0,0)}.pushy-open-right #container,.pushy-open-right .push{-webkit-transform:translate3d(-500px,0,0);-ms-transform:translate3d(-500px,0,0);transform:translate3d(-500px,0,0)}.pushy-open-left .pushy,.pushy-open-right .pushy{-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}#container,.push,.pushy{transition:transform .2s cubic-bezier(.16,.68,.43,.99)}.site-overlay{display:none}.pushy-open-left .site-overlay,.pushy-open-right .site-overlay{display:block;position:fixed;top:0;right:0;bottom:0;left:0;z-index:998;background-color:rgba(0,0,0,.5);-webkit-animation:fade .5s;animation:fade .5s}@keyframes fade{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes fade{0%{opacity:0}100%{opacity:1}}.pushy-submenu ul{padding-left:15px;transition:max-height .2s ease-in-out}.pushy-submenu ul .pushy-link{transition:opacity .2s ease-in-out}.pushy-submenu>a{position:relative}.pushy-submenu>a::after{content:'';display:block;height:11px;width:8px;position:absolute;top:50%;right:15px;background:url(../img/arrow.svg) no-repeat;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);transition:transform .2s}.pushy-submenu-closed ul{max-height:0;overflow:hidden}.pushy-submenu-closed .pushy-link{opacity:0}.pushy-submenu-open ul{max-height:1000px}.pushy-submenu-open .pushy-link{opacity:1}.pushy-submenu-open a::after{-webkit-transform:translateY(-50%) rotate(90deg);-ms-transform:translateY(-50%) rotate(90deg);transform:translateY(-50%) rotate(90deg)}.no-csstransforms3d .pushy-submenu-closed ul{max-height:none;display:none}
\ No newline at end of file
diff --git a/routes/index.js b/routes/index.js
index 76ba220..429772e 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -1,6 +1,7 @@
const express = require('express');
const router = express.Router();
const colors = require('colors');
+const hash = require('object-hash');
const moment = require('moment');
const _ = require('lodash');
const {
@@ -362,12 +363,12 @@ router.post('/product/updatecart', async (req, res, next) => {
if(productQuantity === 0){
// quantity equals zero so we remove the item
- delete req.session.cart[cartItem.productId];
+ delete req.session.cart[cartItem.cartId];
res.status(400).json({ message: 'There was an error updating the cart', totalCartItems: Object.keys(req.session.cart).length });
return;
}
- const product = await db.products.findOne({ _id: getId(cartItem.productId) });
+ const product = await db.products.findOne({ _id: getId(req.session.cart[cartItem.cartId].productId) });
if(!product){
res.status(400).json({ message: 'There was an error updating the cart', totalCartItems: Object.keys(req.session.cart).length });
return;
@@ -382,14 +383,14 @@ router.post('/product/updatecart', async (req, res, next) => {
}
const productPrice = parseFloat(product.productPrice).toFixed(2);
- if(!req.session.cart[cartItem.productId]){
+ if(!req.session.cart[cartItem.cartId]){
res.status(400).json({ message: 'There was an error updating the cart', totalCartItems: Object.keys(req.session.cart).length });
return;
}
// Update the cart
- req.session.cart[cartItem.productId].quantity = productQuantity;
- req.session.cart[cartItem.productId].totalItemPrice = productPrice * productQuantity;
+ req.session.cart[cartItem.cartId].quantity = productQuantity;
+ req.session.cart[cartItem.cartId].totalItemPrice = productPrice * productQuantity;
// update total cart amount
await updateTotalCart(req, res);
@@ -410,12 +411,12 @@ router.post('/product/removefromcart', async (req, res, next) => {
const db = req.app.db;
// Check for item in cart
- if(!req.session.cart[req.body.productId]){
+ if(!req.session.cart[req.body.cartId]){
return res.status(400).json({ message: 'Product not found in cart' });
}
// remove item from cart
- delete req.session.cart[req.body.productId];
+ delete req.session.cart[req.body.cartId];
// If not items in cart, empty it
if(Object.keys(req.session.cart).length === 0){
@@ -535,19 +536,25 @@ router.post('/product/addtocart', async (req, res, next) => {
}catch(ex){}
}
+ // Product with options hash
+ const productHash = hash({
+ productId: product._id.toString(),
+ options
+ });
+
// if exists we add to the existing value
let cartQuantity = 0;
- if(req.session.cart[product._id]){
- cartQuantity = parseInt(req.session.cart[product._id].quantity) + productQuantity;
- req.session.cart[product._id].quantity = cartQuantity;
- req.session.cart[product._id].totalItemPrice = productPrice * parseInt(req.session.cart[product._id].quantity);
+ if(req.session.cart[productHash]){
+ cartQuantity = parseInt(req.session.cart[productHash].quantity) + productQuantity;
+ req.session.cart[productHash].quantity = cartQuantity;
+ req.session.cart[productHash].totalItemPrice = productPrice * parseInt(req.session.cart[productHash].quantity);
}else{
// Set the card quantity
cartQuantity = productQuantity;
// new product deets
const productObj = {};
- productObj.productId = req.body.productId;
+ productObj.productId = product._id;
productObj.title = product.productTitle;
productObj.quantity = productQuantity;
productObj.totalItemPrice = productPrice * productQuantity;
@@ -562,7 +569,7 @@ router.post('/product/addtocart', async (req, res, next) => {
}
// merge into the current cart
- req.session.cart[product._id] = productObj;
+ req.session.cart[productHash] = productObj;
}
// Update cart to the DB
@@ -580,7 +587,11 @@ router.post('/product/addtocart', async (req, res, next) => {
req.session.cartSubscription = product.productSubscription;
}
- return res.status(200).json({ message: 'Cart successfully updated', totalCartItems: req.session.totalCartItems });
+ return res.status(200).json({
+ message: 'Cart successfully updated',
+ cartId: productHash,
+ totalCartItems: req.session.totalCartItems
+ });
});
// search products
diff --git a/test/specs/cart.js b/test/specs/cart.js
new file mode 100644
index 0000000..b77126f
--- /dev/null
+++ b/test/specs/cart.js
@@ -0,0 +1,166 @@
+import{ serial as test }from'ava';
+const {
+ runBefore,
+ g
+} = require('../helper');
+
+test.before(async () => {
+ await runBefore();
+});
+
+test('[Success] Add subscripton product to cart', async t => {
+ const res = await g.request
+ .post('/product/addtocart')
+ .send({
+ productId: g.products[7]._id,
+ productQuantity: 1,
+ productOptions: {}
+ })
+ .expect(200);
+ const sessions = await g.db.cart.find({}).toArray();
+ if(!sessions || sessions.length === 0){
+ t.fail();
+ }
+ t.deepEqual(res.body.message, 'Cart successfully updated');
+});
+
+test('[Fail] Add product to cart when subscription already added', async t => {
+ const res = await g.request
+ .post('/product/addtocart')
+ .send({
+ productId: g.products[1]._id,
+ productQuantity: 1,
+ productOptions: JSON.stringify(g.products[1].productOptions)
+ })
+ .expect(400);
+ t.deepEqual(res.body.message, 'Subscription already existing in cart. You cannot add more.');
+});
+
+test('[Fail] Add quantity which exceeds the maxQuantity', async t => {
+ const res = await g.request
+ .post('/product/addtocart')
+ .send({
+ productId: g.products[4]._id,
+ productQuantity: 75,
+ productOptions: {}
+ })
+ .expect(400);
+ t.deepEqual(res.body.message, 'The quantity exceeds the max amount. Please contact us for larger orders.');
+});
+
+test('[Success] Empty cart', async t => {
+ const res = await g.request
+ .post('/product/emptycart')
+ .expect(200);
+ t.deepEqual(res.body.message, 'Cart successfully emptied');
+});
+
+test('[Success] Add product to cart', async t => {
+ const res = await g.request
+ .post('/product/addtocart')
+ .send({
+ productId: g.products[0]._id,
+ productQuantity: 1,
+ productOptions: JSON.stringify(g.products[0].productOptions)
+ })
+ .expect(200);
+ const sessions = await g.db.cart.find({}).toArray();
+ if(!sessions || sessions.length === 0){
+ t.fail();
+ }
+ t.deepEqual(res.body.message, 'Cart successfully updated');
+});
+
+test('[Success] Update cart', async t => {
+ const cart = await g.request
+ .get('/cart/retrieve')
+ .expect(200);
+
+ const cartId = Object.keys(cart.body.cart)[0];
+ const productId = cart.body.cart[cartId].id;
+
+ const res = await g.request
+ .post('/product/updatecart')
+ .send({
+ productId: productId,
+ cartId: cartId,
+ quantity: 10
+ })
+ .expect(200);
+
+ t.deepEqual(res.body.message, 'Cart successfully updated');
+
+ const checkCart = await g.request
+ .get('/cart/retrieve')
+ .expect(200);
+
+ // Check new quantity and total price has been updated
+ t.deepEqual(checkCart.body.cart[cartId].quantity, 10);
+ t.deepEqual(checkCart.body.cart[cartId].totalItemPrice, cart.body.cart[cartId].totalItemPrice * 10);
+});
+
+test('[Fail] Cannot add subscripton when other product in cart', async t => {
+ const res = await g.request
+ .post('/product/addtocart')
+ .send({
+ productId: g.products[7]._id,
+ productQuantity: 1,
+ productOptions: {}
+ })
+ .expect(400);
+ t.deepEqual(res.body.message, 'You cannot combine subscription products with existing in your cart. Empty your cart and try again.');
+});
+
+test('[Fail] Add product to cart with not enough stock', async t => {
+ const res = await g.request
+ .post('/product/addtocart')
+ .send({
+ productId: g.products[0]._id,
+ productQuantity: 20,
+ productOptions: JSON.stringify(g.products[0].productOptions)
+ })
+ .expect(400);
+ t.deepEqual(res.body.message, 'There is insufficient stock of this product.');
+});
+
+test('[Fail] Add incorrect product to cart', async t => {
+ const res = await g.request
+ .post('/product/addtocart')
+ .send({
+ productId: 'fake_product_id',
+ productQuantity: 20,
+ productOptions: JSON.stringify(g.products[0].productOptions)
+ })
+ .expect(400);
+ t.deepEqual(res.body.message, 'Error updating cart. Please try again.');
+});
+
+test('[Success] Remove item previously added to cart', async t => {
+ // Add a second product to cart
+ const cart = await g.request
+ .post('/product/addtocart')
+ .send({
+ productId: g.products[1]._id,
+ productQuantity: 1,
+ productOptions: JSON.stringify(g.products[1].productOptions)
+ })
+ .expect(200);
+
+ const res = await g.request
+ .post('/product/removefromcart')
+ .send({
+ cartId: cart.body.cartId
+ })
+ .expect(200);
+ t.deepEqual(res.body.message, 'Product successfully removed');
+});
+
+test('[Fail] Try remove an item which is not in the cart', async t => {
+ const res = await g.request
+ .post('/product/removefromcart')
+ .send({
+ cartId: 'bogus_product_id'
+ })
+ .expect(400);
+ t.deepEqual(res.body.message, 'Product not found in cart');
+});
diff --git a/test/specs/products.js b/test/specs/products.js
index 43e801d..30be9d4 100644
--- a/test/specs/products.js
+++ b/test/specs/products.js
@@ -19,160 +19,6 @@ test('[Success] Get products JSON', async t => {
}
});
-test('[Success] Add subscripton product to cart', async t => {
- const res = await g.request
- .post('/product/addtocart')
- .send({
- productId: g.products[7]._id,
- productQuantity: 1,
- productOptions: {}
- })
- .expect(200);
- const sessions = await g.db.cart.find({}).toArray();
- if(!sessions || sessions.length === 0){
- t.fail();
- }
- t.deepEqual(res.body.message, 'Cart successfully updated');
-});
-
-test('[Fail] Add product to cart when subscription already added', async t => {
- const res = await g.request
- .post('/product/addtocart')
- .send({
- productId: g.products[1]._id,
- productQuantity: 1,
- productOptions: JSON.stringify(g.products[1].productOptions)
- })
- .expect(400);
- t.deepEqual(res.body.message, 'Subscription already existing in cart. You cannot add more.');
-});
-
-test('[Fail] Add quantity which exceeds the maxQuantity', async t => {
- const res = await g.request
- .post('/product/addtocart')
- .send({
- productId: g.products[4]._id,
- productQuantity: 75,
- productOptions: {}
- })
- .expect(400);
- t.deepEqual(res.body.message, 'The quantity exceeds the max amount. Please contact us for larger orders.');
-});
-
-test('[Success] Empty cart', async t => {
- const res = await g.request
- .post('/product/emptycart')
- .expect(200);
- t.deepEqual(res.body.message, 'Cart successfully emptied');
-});
-
-test('[Success] Add product to cart', async t => {
- const res = await g.request
- .post('/product/addtocart')
- .send({
- productId: g.products[0]._id,
- productQuantity: 1,
- productOptions: JSON.stringify(g.products[0].productOptions)
- })
- .expect(200);
- const sessions = await g.db.cart.find({}).toArray();
- if(!sessions || sessions.length === 0){
- t.fail();
- }
- t.deepEqual(res.body.message, 'Cart successfully updated');
-});
-
-test('[Success] Update cart', async t => {
- const cart = await g.request
- .get('/cart/retrieve')
- .expect(200);
-
- const productId = g.products[0]._id;
-
- const res = await g.request
- .post('/product/updatecart')
- .send({
- productId: productId,
- quantity: 10
- })
- .expect(200);
-
- t.deepEqual(res.body.message, 'Cart successfully updated');
-
- const checkCart = await g.request
- .get('/cart/retrieve')
- .expect(200);
-
- // Check new quantity and total price has been updated
- t.deepEqual(checkCart.body.cart[productId].quantity, 10);
- t.deepEqual(checkCart.body.cart[productId].totalItemPrice, cart.body.cart[productId].totalItemPrice * 10);
-});
-
-test('[Fail] Cannot add subscripton when other product in cart', async t => {
- const res = await g.request
- .post('/product/addtocart')
- .send({
- productId: g.products[7]._id,
- productQuantity: 1,
- productOptions: {}
- })
- .expect(400);
- t.deepEqual(res.body.message, 'You cannot combine subscription products with existing in your cart. Empty your cart and try again.');
-});
-
-test('[Fail] Add product to cart with not enough stock', async t => {
- const res = await g.request
- .post('/product/addtocart')
- .send({
- productId: g.products[0]._id,
- productQuantity: 20,
- productOptions: JSON.stringify(g.products[0].productOptions)
- })
- .expect(400);
- t.deepEqual(res.body.message, 'There is insufficient stock of this product.');
-});
-
-test('[Fail] Add incorrect product to cart', async t => {
- const res = await g.request
- .post('/product/addtocart')
- .send({
- id: 'fake_product_id',
- state: false
- })
- .expect(400);
- t.deepEqual(res.body.message, 'Error updating cart. Please try again.');
-});
-
-test('[Success] Remove item previously added to cart', async t => {
- // Add a second product to cart
- await g.request
- .post('/product/addtocart')
- .send({
- productId: g.products[1]._id,
- productQuantity: 1,
- productOptions: JSON.stringify(g.products[1].productOptions)
- })
- .expect(200);
-
- const res = await g.request
- .post('/product/removefromcart')
- .send({
- productId: g.products[0]._id
- })
- .expect(200);
- t.deepEqual(res.body.message, 'Product successfully removed');
-});
-
-test('[Fail] Try remove an item which is not in the cart', async t => {
- const res = await g.request
- .post('/product/removefromcart')
- .send({
- cartId: 'bogus_product_id'
- })
- .expect(400);
- t.deepEqual(res.body.message, 'Product not found in cart');
-});
-
test('[Success] Search products', async t => {
const res = await g.request
.get('/category/backpack?json=true')
diff --git a/views/layouts/layout.hbs b/views/layouts/layout.hbs
index f20020a..b9a3a1f 100644
--- a/views/layouts/layout.hbs
+++ b/views/layouts/layout.hbs
@@ -208,5 +208,6 @@
{{/if}}
+ {{> partials/confirmModal}}