From 21d3c0fe8fc096073fc4621e21e87f00d538f18c Mon Sep 17 00:00:00 2001 From: Aaron Jones Date: Mon, 19 Nov 2018 11:19:21 -0600 Subject: [PATCH] Adds The ability to have multiple discount targets on boga discounts. Providing the selection and configuration of these targets via a cart modal --- labels/en-US.json | 1 + scripts/modules/api.js | 2 +- scripts/modules/backbone-mozu-view.js | 11 + .../discount-dialog/models-discount-dialog.js | 125 ++++++ .../discount-dialog/views-discount-dialog.js | 282 ++++++++++++++ scripts/modules/eventbus.js | 2 +- scripts/modules/modal-dialog.js | 4 +- scripts/modules/models-cart.js | 32 +- scripts/modules/models-discount.js | 153 ++++++++ scripts/modules/models-product.js | 10 +- scripts/modules/views-location.js | 94 +++++ scripts/pages/cart.js | 30 +- scripts/pages/location.js | 90 +---- scripts/pages/product.js | 7 +- stylesheets/modules/cart/cart-table.less | 9 + stylesheets/modules/cart/discount-modal.less | 22 ++ stylesheets/storefront.less | 1 + .../modules/cart/cart-table-item.hypr.live | 16 + templates/modules/cart/cart-table.hypr.live | 1 - .../discount-add-product.hypr.live | 183 +++++++++ .../discount-choose-product.hypr.live | 9 + .../discount-modal/discount-modal.hypr.live | 18 + .../discount-product-location.hypr.live | 10 + .../product/product-detail-discount.hypr.live | 10 + .../modules/product/product-detail.hypr.live | 358 +++++++++--------- .../product-discount-listing.hypr.live | 37 ++ .../product-options-discount.hypr.live | 48 +++ templates/pages/cart.hypr | 10 +- 28 files changed, 1281 insertions(+), 294 deletions(-) create mode 100644 scripts/modules/cart/discount-dialog/models-discount-dialog.js create mode 100644 scripts/modules/cart/discount-dialog/views-discount-dialog.js create mode 100644 scripts/modules/models-discount.js create mode 100644 scripts/modules/views-location.js create mode 100644 stylesheets/modules/cart/discount-modal.less create mode 100644 templates/modules/cart/discount-modal/discount-add-product.hypr.live create mode 100644 templates/modules/cart/discount-modal/discount-choose-product.hypr.live create mode 100644 templates/modules/cart/discount-modal/discount-modal.hypr.live create mode 100644 templates/modules/cart/discount-modal/discount-product-location.hypr.live create mode 100644 templates/modules/product/product-detail-discount.hypr.live create mode 100644 templates/modules/product/product-discount-listing.hypr.live create mode 100644 templates/modules/product/product-options-discount.hypr.live diff --git a/labels/en-US.json b/labels/en-US.json index a2036eee5..d03bbe2c2 100644 --- a/labels/en-US.json +++ b/labels/en-US.json @@ -43,6 +43,7 @@ "billingPhone": "Billing Phone", "billingPhoneNumber": "Billing Phone Number", "billingPostalCode": "Billing Zip Code", + "bogaModalHeading": "Your Free BOGA Item", "bundleContents": "Bundle Contents", "bundleExtraPartOf": "Bought as optional extra for {1} bundle", "bundlePartOf": "Bought as part of {1} bundle", diff --git a/scripts/modules/api.js b/scripts/modules/api.js index 08de1d064..b22e7f738 100644 --- a/scripts/modules/api.js +++ b/scripts/modules/api.js @@ -4,7 +4,7 @@ * (tenant, catalog and store IDs, and authorization tickets). */ -define(['sdk', 'jquery', 'hyprlive'], function (Mozu, $, Hypr) { +define(['sdk', 'jquery', 'hyprlive'], function (Mozu, $, Hypr) { var apiConfig = require.mozuData('apicontext'); Mozu.setServiceUrls(apiConfig.urls); var api = Mozu.Store(apiConfig.headers).api(); diff --git a/scripts/modules/backbone-mozu-view.js b/scripts/modules/backbone-mozu-view.js index e41dc3189..40ae55706 100644 --- a/scripts/modules/backbone-mozu-view.js +++ b/scripts/modules/backbone-mozu-view.js @@ -173,6 +173,17 @@ this.$('.mz-drop-zone').each(function() { if (dropzones[this.id]) $(this).replaceWith(dropzones[this.id]); }); + }, + + removeInner: function () { + this._removeInnerElements(); + this.stopListening(); + return this; + }, + + _removeInnerElements: function () { + this.$el.off(); + this.$el.empty(); } /** diff --git a/scripts/modules/cart/discount-dialog/models-discount-dialog.js b/scripts/modules/cart/discount-dialog/models-discount-dialog.js new file mode 100644 index 000000000..9f9bc089c --- /dev/null +++ b/scripts/modules/cart/discount-dialog/models-discount-dialog.js @@ -0,0 +1,125 @@ +define(["backbone", 'underscore', 'hyprlive', 'modules/api', 'modules/models-product', 'modules/models-dialog', 'modules/models-discount' ], function(Backbone, _, Hypr, Api, ProductModels, Dialog, DiscountModel) { + + var modalDialog = Dialog.extend({ + handlesMessages: true, + relations : { + product: ProductModels.Product, + discounts: Backbone.Collection.extend({ + model: DiscountModel + }), + discount: DiscountModel + }, + hasNextDiscount: function() { + return this.get('discounts').find(function (discount) { + return !discount.get('complete') && ((discount.get('autoAdd') || (discount.get('hasOptions') || discount.get('hasMultipleProducts')))); + //return !discount.get('complete'); + }); + }, + loadNextDiscount: function(){ + // var nextDiscount = this.get('discounts').find(function(discount){ + // return !discount.get('complete'); + // }); + // if (nextDiscount) { + // this.setNewDiscount(nextDiscount); + // } + var self = this; + var nextDiscount = this.get('discounts').find(function (discount) { + return !discount.get('complete'); + }); + if (nextDiscount) { + this.setNewDiscount(nextDiscount); + if( this.get('discount').get('hasMultipleProducts') ){ + return this.get('discount').getDiscountDetails().then(function(discount){ + self.get('discount').set('productCodes', discount.includedProductCodes); + }); + } else { + return this.get('discount').getProductDetails().then(function(data){ + if (self.get('discount').get('hasOptions')) { + data = self.get('discount').tagVariationOptions(self.get('discount').get('productCode'), data); + } + self.get('discount').get('products').reset([data]); + }); + } + } + }, + completeDiscount:function(){ + var self = this; + if (self.hasDiscount()) { + var discount = this.get('discounts').findWhere({ discountId: self.get('discount').get('discountId') }); + discount.set('complete', true); + } + }, + addDiscounts: function(discounts){ + this.set('discounts', discounts); + this.setNewDiscount(discounts[0]); + }, + setNewDiscount: function(discount){ + if (!discount.complete) { + this.set('discount', discount); + //this.trigger('newDiscountSet'); + } + }, + initialize: function () { + //this.set('order', new OrderModels.Order({})); + }, + hasDiscount: function(){ + return !(_.isEmpty(this.get('discount'))); + }, + hasMultipleProducts: function () { + return this.get('discount').get('hasMultipleProducts'); + }, + // getProductDetails: function(productCode){ + // var self = this; + // var productCode = productCode || this.get('discount').productCode + + // if( !productCode || productCode === "" ){ + // this.trigger('error', 'No Product Code Found'); + // throw 'No Product Code Found'; + // } + + // var productModel = new ProductModels.Product({productCode: productCode}); + // return productModel.apiGet().then(function(data){ + // self.set('product', data.data); + // return data.data; + // }) + // }, + isProductConfigurable: function(){ + return this.get('discount').get('products').at(0).get('productUsage') === "Configurable"; + }, + productHasOptions: function () { + return this.get('discount').get('hasOptions'); + }, + isDiscountAutoAdd: function(){ + return this.get('discount').get('autoAdd'); + }, + autoAddProduct: function() { + var self = this; + var process = []; + + var bogaProduct = new ProductModels.Product({ productCode: self.get('discount').get('products').at(0).get('productCode')}); + process.push(function () { + return bogaProduct.fetch(); + }); + process.push(function () { + return bogaProduct.apiAddToCart({ autoAddDiscountId: self.get('discount').get('discountId') }).then(function (cartItem) { + return cartItem; + }); + }); + return Api.steps(process); + } + // getDiscount: function(){ + // var temp = new Promise(function (resolve, reject) { + // setTimeout(function () { + // testData = { + // name: "Discount 1", + // productCodes: ['4101', '4104', 'config-1', 'config-1-2'] + // }; + // resolve(testData); + // }, 300); + // }); + // return temp + // } + }); + + return modalDialog; +}); diff --git a/scripts/modules/cart/discount-dialog/views-discount-dialog.js b/scripts/modules/cart/discount-dialog/views-discount-dialog.js new file mode 100644 index 000000000..b3f245036 --- /dev/null +++ b/scripts/modules/cart/discount-dialog/views-discount-dialog.js @@ -0,0 +1,282 @@ +define(['modules/backbone-mozu', 'hyprlive', 'modules/jquery-mozu', 'underscore', 'hyprlivecontext', 'modules/views-modal-dialog', 'modules/api', 'modules/models-product', 'modules/views-location', 'modules/models-location', 'modules/models-discount'], function (Backbone, Hypr, $, _, HyprLiveContext, ModalDialogView, Api, ProductModels, LocationViews, LocationModels, Discount) { + + var ChooseProductStepView = Backbone.MozuView.extend({ + templateName: "modules/cart/discount-modal/discount-choose-product", + autoUpdate: [ + ], + renderOnChange: [ + ], + initialize: function () { + var self = this; + this.model.getDiscountProducts().then(function(discount){ + self.render(); + }); + }, + onProductSelect: function(e){ + var self = this; + var $target = $(e.currentTarget); + var productCode = $target.data("mzProductCode"); + + var productModel = this.model.get('products').findWhere({ 'productCode': productCode + ''}); + + if (productModel) + { + if (self._productStepView) { + self._productStepView.removeInner(); + } + productModel._parent = this; + var addProductStepView = new AddProductStepView({ + el: self.el, + model: productModel + }); + self._productStepView = addProductStepView; + addProductStepView.render(); + } + } + }); + + var ProductLocationView = Backbone.MozuView.extend({ + templateName: "modules/cart/discount-modal/discount-product-location", + render: function(){ + Backbone.MozuView.prototype.render.apply(this, arguments); + var $locationSearch = $('#location-list'), + product = this.model, + productPresent = !!this.model.get('productCode'), + locationsCollection = new LocationModels.LocationCollection(), + ViewClass = productPresent ? LocationViews.LocationsSearchView : LocationViews.LocationsView, + view = new ViewClass({ + model: locationsCollection, + el: $locationSearch + }); + + if (productPresent) view.setProduct(product); + window.lv = view; + } + + }); + + + var AddProductStepView = Backbone.MozuView.extend({ + templateName: "modules/cart/discount-modal/discount-add-product", + additionalEvents: { + "change [data-mz-product-option]": "onOptionChange", + "blur [data-mz-product-option]": "onOptionChange", + "change [data-mz-value='quantity']": "onQuantityChange", + "keyup input[data-mz-value='quantity']": "onQuantityChange" + }, + render: function () { + var me = this; + if (this.oldOptions) { + me.model.get('options').map(function(option){ + var oldOption = _.find(me.oldOptions, function(old){ + return old.attributeFQN === option.get('attributeFQN'); + }); + if (oldOption) { + option.set('values', oldOption.values); + } + }); + } + Backbone.MozuView.prototype.render.apply(this); + this.$('[data-mz-is-datepicker]').each(function (ix, dp) { + $(dp).dateinput().css('color', Hypr.getThemeSetting('textColor')).on('change blur', _.bind(me.onOptionChange, me)); + }); + }, + onOptionChange: function (e) { + return this.configure($(e.currentTarget)); + }, + onQuantityChange: _.debounce(function (e) { + var $qField = $(e.currentTarget), + newQuantity = parseInt($qField.val(), 10); + if (!isNaN(newQuantity)) { + this.model.updateQuantity(newQuantity); + } + }, 500), + configure: function ($optionEl) { + var newValue = $optionEl.val(), + oldValue, + id = $optionEl.data('mz-product-option'), + optionEl = $optionEl[0], + isPicked = (optionEl.type !== "checkbox" && optionEl.type !== "radio") || optionEl.checked, + option = this.model.get('options').findWhere({ 'attributeFQN': id }); + if (option) { + if (option.get('attributeDetail').inputType === "YesNo") { + option.set("value", isPicked); + } else if (isPicked) { + oldValue = option.get('value'); + if (oldValue !== newValue && !(oldValue === undefined && newValue === '')) { + option.set('value', newValue); + this.oldOptions = this.model.get('options').toJSON(); + } + } + } + }, + onBackToProductSelection: function (e) { + var self = this; + if (self.model._parent) { + self.model._parent.render(); + } + }, + addToCart: function (e) { + var self = this; + e.preventDefault(); + try { + var discountModel = self.model.collection.parent.parent; + if (discountModel) { + var cartItem = discountModel.get('selectedCartItem'); + if (cartItem) { + discountModel.parent.removeItem(cartItem).then(function () { + self.model.addToCart(true).then(function () { + discountModel.completeDiscount(); + discountModel.trigger('newDiscountSet'); + }); + }); + return; + } + self.model.addToCart(true).then(function () { + discountModel.completeDiscount(); + discountModel.trigger('newDiscountSet'); + }); + } + } catch(error) {} + }, + addToWishlist: function () { + this.model.addToWishlist(); + }, + checkLocalStores: function (e) { + var me = this; + e.preventDefault(); + this.model.whenReady(function () { + var productLocationView = new ProductLocationView({ + el: $('.mz-product-locations'), + model: me.model + }); + + productLocationView.render(); + }); + + }, + initialize: function () { + // handle preset selects, etc + var me = this; + this.$('[data-mz-product-option]').each(function () { + var $this = $(this), isChecked, wasChecked; + if ($this.val()) { + switch ($this.attr('type')) { + case "checkbox": + case "radio": + isChecked = $this.prop('checked'); + wasChecked = !!$this.attr('checked'); + if ((isChecked && !wasChecked) || (wasChecked && !isChecked)) { + me.configure($this); + } + break; + default: + me.configure($this); + } + } + }); + } + }); + + + var DiscountModalView = ModalDialogView.extend({ + templateName: "modules/cart/discount-modal/discount-modal", + initialize: function () { + var self = this; + this.listenTo(this.model, 'newDiscountSet', function () { + self.render(); + }); + ModalDialogView.prototype.initialize.apply(this, arguments); + }, + handleDialogOpen : function(){ + this.model.trigger('dialogOpen'); + this.bootstrapInstance.show(); + }, + handleDialogCancel: function(){ + var self = this; + + this.model.completeDiscount(); + if (this._productStepView) { + this._productStepView.removeInner(); + } + window.cartView.cartView.model.set('discountId', self.model.get('discount').get('discountId')); + window.cartView.cartView.model.apiRejectSuggestedDiscount(); + this.render(); + + }, + setInit: function (updatingItemId){ + var self = this; + if (this.model.hasNextDiscount()) { + this.model.loadNextDiscount().then(function(){ + if (!self.model.hasMultipleProducts() && self.model.productHasOptions()) { + self.loadAddProductView(); + self.handleDialogOpen(); + } else if (self.model.hasMultipleProducts()) { + self.loadProductSelectionView(); + self.handleDialogOpen(); + } else if (self.model.isDiscountAutoAdd()) { + self.model.autoAddProduct().ensure(function(data){ + self.model.completeDiscount(); + self.render(); + }); + } + }); + } else { + this.model.trigger('dialogCancel'); + window.cartView.cartView.model.fetch().then(function () { + window.cartView.cartView.render(); + }); + this.bootstrapInstance.hide(); + } + }, + modalContentEl: function () { + return this.$el.find('[data-mz-discount-modal-content]'); + }, + loadProductSelectionView: function () { + var self = this; + if (self._chooseProductView) { + self._chooseProductView.removeInner(); + } + var chooseProductStepView = new ChooseProductStepView({ + el: $(self.modalContentEl()), + model: self.model.get('discount') + }); + self._chooseProductView = chooseProductStepView; + }, + loadAddProductView: function () { + var self = this; + if(self._productStepView) { + self._productStepView.removeInner(); + } + var addProductStepView = new AddProductStepView({ + el: $(self.modalContentEl()), + model: self.model.get('discount').get('products').at(0) + }); + self._productStepView = addProductStepView; + addProductStepView.render(); + }, + updateSelectedAutoAddItem : function(cartItemId, discountId) { + var self = this; + Api.action('discounts', 'get', { + discountId: discountId + }).then(function (discount) { + var newDiscount = new Discount({ + discountId: discount.discountId, + autoAdd: true, + hasMultipleProducts: true, + hasOptions: false + }); + self.model.set('discounts', new Backbone.Collection([newDiscount])); + self.model.set('selectedCartItem', cartItemId); + self.handleDialogOpen(); + self.setInit(); + }); + }, + render : function() { + var self = this; + self.setInit(); + } + }); + + return DiscountModalView; +}); diff --git a/scripts/modules/eventbus.js b/scripts/modules/eventbus.js index 2f165b3a0..246ab0c90 100644 --- a/scripts/modules/eventbus.js +++ b/scripts/modules/eventbus.js @@ -1,3 +1,3 @@ define(["underscore", "backbone"], function(_, Backbone){ - return _.extend({}, Backbone.Events); + return _.extend({}, Backbone.Events); }); \ No newline at end of file diff --git a/scripts/modules/modal-dialog.js b/scripts/modules/modal-dialog.js index 4b98fff25..df29c1c0c 100644 --- a/scripts/modules/modal-dialog.js +++ b/scripts/modules/modal-dialog.js @@ -156,12 +156,10 @@ define(['modules/jquery-mozu', 'shim!vendor/bootstrap/js/modal[jquery=jQuery]'], show: function(){ if (me.options.backdrop===null || me.options.backdrop===undefined){ - theElement.modal(); + theElement.modal(); } else { theElement.modal({backdrop: me.options.backdrop}); } - - }, hide: function(){ diff --git a/scripts/modules/models-cart.js b/scripts/modules/models-cart.js index 94d16dd71..a84530f01 100644 --- a/scripts/modules/models-cart.js +++ b/scripts/modules/models-cart.js @@ -1,7 +1,7 @@ define(['underscore', 'modules/backbone-mozu', 'hyprlive', "modules/api", "modules/models-product", - "hyprlivecontext", 'modules/models-location' + "hyprlivecontext", 'modules/models-location', 'modules/cart/discount-dialog/models-discount-dialog' ], function (_, Backbone, Hypr, api, ProductModels, - HyprLiveContext, LocationModels) { + HyprLiveContext, LocationModels, DiscountDialogModels) { var CartItemProduct = ProductModels.Product.extend({ helpers: ['mainImage','directShipSupported', 'inStorePickupSupported'], @@ -21,12 +21,12 @@ define(['underscore', 'modules/backbone-mozu', 'hyprlive', "modules/api", "modul inStorePickupSupported: function(){ return (_.indexOf(this.get('fulfillmentTypesSupported'), "InStorePickup") !== -1) ? true : false; } + }), CartItem = Backbone.MozuModel.extend({ relations: { product: CartItemProduct - }, validation: { quantity: { @@ -50,7 +50,7 @@ define(['underscore', 'modules/backbone-mozu', 'hyprlive', "modules/api", "modul this.apiModel.updateQuantity(this.get("quantity")) .then( function() { - self.collection.parent.checkBOGA(); + //self.collection.parent.checkBOGA(); }, function() { // Quantity update failed, e.g. due to limited quantity or min. quantity not met. Roll back. @@ -67,7 +67,6 @@ define(['underscore', 'modules/backbone-mozu', 'hyprlive', "modules/api", "modul } return; } - }), StoreLocationsCache = Backbone.Collection.extend({ addLocation : function(location){ @@ -91,7 +90,8 @@ define(['underscore', 'modules/backbone-mozu', 'hyprlive', "modules/api", "modul items: Backbone.Collection.extend({ model: CartItem }), - storeLocationsCache : StoreLocationsCache + storeLocationsCache : StoreLocationsCache, + discountModal: DiscountDialogModels }, initialize: function() { var self = this; @@ -106,6 +106,21 @@ define(['underscore', 'modules/backbone-mozu', 'hyprlive', "modules/api", "modul }); } }); + + this.get('discountModal').set('discounts', this.getSuggestedDiscounts()); + }, + getSuggestedDiscounts: function(){ + var self = this; + + var rejectedDiscounts = self.get('rejectedDiscounts') || []; + var suggestedDiscounts = self.get('suggestedDiscounts') || []; + var filteredDiscounts = []; + if (suggestedDiscounts.length) { + filteredDiscounts = _.filter(suggestedDiscounts, function(discount){ + return !_.findWhere(rejectedDiscounts, {discountId: discount.discountId}); + }); + } + return filteredDiscounts; }, checkBOGA: function(){ //Called whenever we would need to add an additional item to the cart @@ -222,7 +237,10 @@ define(['underscore', 'modules/backbone-mozu', 'hyprlive', "modules/api", "modul return d.couponCode && d.couponCode.toLowerCase() === lowerCode; })); me.set('tentativeCoupon', couponExists && couponIsNotApplied ? code : undefined); - me.checkBOGA(); + if (me.getSuggestedDiscounts().length) { + me.get('discountModal').set('discounts', me.getSuggestedDiscounts()); + window.cartView.discountModalView.render(); + } me.isLoading(false); }); }, diff --git a/scripts/modules/models-discount.js b/scripts/modules/models-discount.js new file mode 100644 index 000000000..dc5b6e0e6 --- /dev/null +++ b/scripts/modules/models-discount.js @@ -0,0 +1,153 @@ +define(["backbone", 'underscore', 'hyprlive', 'modules/api', 'modules/models-product', 'modules/models-dialog'], function (Backbone, _, Hypr, api, ProductModels, Dialog) { + + var discountModel = Backbone.MozuModel.extend({ + mozuType: 'discounts', + relations: { + products: Backbone.Collection.extend({ + model: ProductModels.Product + }) + }, + defaults: { + productCode: "", + hasMultipleProducts: false + }, + getProductDetails: function (productCode) { + var self = this; + productCode = productCode || this.get('productCode'); + + if (!productCode || productCode === "") { + this.trigger('error', 'No Product Code Found'); + throw 'No Product Code Found'; + } + + var productModel = new ProductModels.Product({ productCode: productCode }); + return productModel.apiGet({ productCode: productCode, acceptVariantProductCode: true }).then(function (data) { + //self.get('products').reset([data.data]); + return data.data; + }); + }, + getDiscountDetails: function () { + var self = this; + return self.apiGet(); + }, + tagVariationOptions: function (productCode, data) { + var variation = _.find(data.variations, function (v) { + return v.productCode === productCode; + }); + + if (variation) { + _.each(variation.options, function (variationOption) { + _.each(data.options, function (option, optionIdx) { + if (option.attributeFQN === variationOption.attributeFQN) { + var valueIdx = _.findIndex(option.values, function (v) { + return v.value === variationOption.value; + }); + data.options[optionIdx].values[valueIdx].autoAddEnabled = true; + return false; + } + }); + }); + } + data.variationCollection = true; + return data; + }, + getDiscountProducts: function () { + var self = this; + var deferred = api.defer(); + var products = []; + + var hasBaseProduct = function (products, variationCode) { + return _.findIndex(products, function (product) { + return _.find(product.variations, function (variation) { + return variation.productCode === variationCode; + }); + }); + }; + + var getVariationBase = function (productCode) { + return self.getProductDetails(productCode).then(function (data) { + data = self.tagVariationOptions(productCode, data); + products.push(data); + self.get('products').add(data); + return data; + }); + }; + + var removeVaritionsWithBase = function (unFoundProducts) { + _.each(unFoundProducts, function (productCode) { + var foundProductIdx = hasBaseProduct(unFoundProducts, productCode); + if (foundProductIdx) { + unFoundProducts = unFoundProducts.splice(foundProductIdx, 1); + } + }); + return unFoundProducts; + }; + + self.getAllProductDetails().then(function (data) { + + products = data; + self.get('products').add(data); + + var unFoundProducts = _.reject(self.get('productCodes'), function (productCode) { + return _.find(products, function (product) { + return product.productCode === productCode; + }); + }); + + var getVariationBases = function(){ + var baseProductIdx = hasBaseProduct(products, unFoundProducts[unFoundProducts.length - 1]); + if (baseProductIdx === -1 && unFoundProducts.length) { + getVariationBase(unFoundProducts[unFoundProducts.length - 1]).then(function () { + unFoundProducts.splice(unFoundProducts.length-1, 1); + //unFoundProducts = removeVaritionsWithBase(unFoundProducts); + return getVariationBases(); + }); + return; + } else if (baseProductIdx != -1 && unFoundProducts.length) { + self.tagVariationOptions(unFoundProducts[unFoundProducts.length - 1], products[baseProductIdx]); + unFoundProducts.splice(unFoundProducts.length - 1, 1); + return getVariationBases(); + } + return deferred.resolve(products); + }; + getVariationBases(); + return data; + }); + return deferred.promise; + }, + getAllProductDetails: function () { + var self = this; + var filter = ''; + _.forEach(self.get('productCodes'), function (code, idx) { + if (idx === 0) { + filter += 'productCode eq ' + code; + return; + } + filter += ' or productCode eq ' + code; + }); + + return api.get('products', { filter: filter }).then(function (data) { + var mappedProducts = _.map(data, function (product) { + return product.data; + }); + + _.forEach(mappedProducts, function (product, idx) { + if (product.isVariation) { + var foundBaseProduct = _.findWhere(mappedProducts, { productCode: product.baseProductCode }); + if (foundBaseProduct) { + foundBaseProduct.options.add(product.option); + delete mappedProducts[idx]; + return; + } + mappedProducts[idx].productCode = product.baseProductCode; + mappedProducts[idx].productCode.options = [product.option]; + } + }); + + self.get('products').reset(mappedProducts); + return mappedProducts; + }); + } + }); + return discountModel; +}); \ No newline at end of file diff --git a/scripts/modules/models-product.js b/scripts/modules/models-product.js index 8d2b05594..b3f1d7d46 100644 --- a/scripts/modules/models-product.js +++ b/scripts/modules/models-product.js @@ -304,22 +304,20 @@ define(["modules/jquery-mozu", "underscore", "modules/backbone-mozu", "hyprlive" return biscuit; }, []); }, - - - addToCart: function () { + addToCart: function (stopRedirect) { var me = this; - this.whenReady(function () { + return this.whenReady(function () { if (!me.validate()) { var fulfillMethod = me.get('fulfillmentMethod'); if (!fulfillMethod) { fulfillMethod = (me.get('goodsType') === 'Physical') ? Product.Constants.FulfillmentMethods.SHIP : Product.Constants.FulfillmentMethods.DIGITAL; } - me.apiAddToCart({ + return me.apiAddToCart({ options: me.getConfiguredOptions(), fulfillmentMethod: fulfillMethod, quantity: me.get("quantity") }).then(function (item) { - me.trigger('addedtocart', item); + //me.trigger('addedtocart', item, stopRedirect); }); } }); diff --git a/scripts/modules/views-location.js b/scripts/modules/views-location.js new file mode 100644 index 000000000..7b177e992 --- /dev/null +++ b/scripts/modules/views-location.js @@ -0,0 +1,94 @@ +define(['modules/jquery-mozu', 'hyprlive', 'modules/backbone-mozu', 'modules/models-location', 'modules/models-product', + 'hyprlivecontext'], + function ($, Hypr, Backbone, LocationModels, ProductModels, + HyprLiveContext) { + + var positionErrorLabel = Hypr.getLabel('positionError'), + + LocationsView = Backbone.MozuView.extend({ + templateName: 'modules/location/locations', + initialize: function () { + var self = this; + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition(function (pos) { + delete self.positionError; + self.populate(pos); + }, function (err) { + if (err.code !== err.PERMISSION_DENIED) { + self.positionError = positionErrorLabel; + } + self.populate(); + }, { + timeout: 10000 + }); + } else { + this.populate(); + } + }, + populate: function (location) { + var self = this; + var show = function () { + self.render(); + $('.mz-locationsearch-pleasewait').fadeOut(); + self.$el.noFlickerFadeIn(); + }; + if (location) { + this.model.apiGetByLatLong({ location: location }).then(show); + } else { + this.model.apiGet().then(show); + } + }, + getRenderContext: function () { + var c = Backbone.MozuView.prototype.getRenderContext.apply(this, arguments); + c.model.positionError = this.positionError; + return c; + } + }), + + LocationsSearchView = LocationsView.extend({ + templateName: 'modules/location/location-search', + populate: function (location) { + var self = this; + this.model.apiGetForProduct({ + productCode: this.product.get('variationProductCode') || this.product.get('productCode'), + location: location + }).then(function () { + self.render(); + $('.mz-locationsearch-pleasewait').fadeOut(); + self.$el.noFlickerFadeIn(); + }); + }, + addToCartForPickup: function (e) { + var $target = $(e.currentTarget), + loc = $target.data('mzLocation'), + name = $target.data('mzName'); + $target.parent().addClass('is-loading'); + this.product.addToCartForPickup(loc, name, this.product.get('quantity')); + }, + setProduct: function (product) { + var me = this; + me.product = product; + this.listenTo(me.product, 'addedtocart', function () { + $(window).on('beforeunload', function () { + me.$('.is-loading').removeClass('is-loading'); + }); + window.location.href = (HyprLiveContext.locals.pageContext.secureHost || HyprLiveContext.locals.siteContext.siteSubdirectory) + "/cart"; + }); + this.listenTo(me.product, 'error', function () { + this.$('.is-loading').removeClass('is-loading'); + this.render(); + }); + }, + getRenderContext: function () { + var c = Backbone.MozuView.prototype.getRenderContext.apply(this, arguments); + c.model.messages = (this.product.messages) ? this.product.messages.toJSON() : []; + return c; + } + }); + + return { + LocationsView: LocationsView, + LocationsSearchView: LocationsSearchView + }; + } +); diff --git a/scripts/pages/cart.js b/scripts/pages/cart.js index 8f10f682a..dc365b830 100644 --- a/scripts/pages/cart.js +++ b/scripts/pages/cart.js @@ -11,8 +11,10 @@ define(['modules/api', 'modules/xpress-paypal', 'modules/models-location', 'modules/amazonPay', - 'modules/applepay' - ], function (api, Backbone, _, $, CartModels, CartMonitor, HyprLiveContext, Hypr, preserveElement, modalDialog, paypal, LocationModels, AmazonPay, ApplePay) { + 'modules/applepay', + 'modules/cart/discount-dialog/views-discount-dialog', + 'modules/models-discount' +], function (api, Backbone, _, $, CartModels, CartMonitor, HyprLiveContext, Hypr, preserveElement, modalDialog, paypal, LocationModels, AmazonPay, ApplePay, DiscountModalView, Discount) { var ThresholdMessageView = Backbone.MozuView.extend({ templateName: 'modules/cart/cart-discount-threshold-messages' @@ -25,7 +27,6 @@ define(['modules/api', var me = this; - //setup coupon code text box enter. this.listenTo(this.model, 'change:couponCode', this.onEnterCouponCode, this); this.codeEntered = !!this.model.get('couponCode'); this.$el.on('keypress', 'input', function (e) { @@ -88,11 +89,19 @@ define(['modules/api', // on the cart template return false; } - var $removeButton = $(e.currentTarget), - id = $removeButton.data('mz-cart-item'); + var $removeButton = $(e.currentTarget); + var id = $removeButton.data('mz-cart-item'); this.model.removeItem(id); return false; }, + updateAutoAddItem: function(e) { + var self = this; + var $target = $(e.currentTarget); + var discountId = $target.data('mz-discount-id'); + var itemId = $target.data('mz-cart-item'); + + window.cartView.discountModalView.updateSelectedAutoAddItem(itemId, discountId); + }, empty: function() { this.model.apiDel().then(function() { window.location.reload(); @@ -398,10 +407,15 @@ define(['modules/api', el: $('#cart'), model: cartModel, messagesEl: $('[data-mz-message-bar]') + }), + discountModalView: new DiscountModalView({ + el: $("[mz-modal-discount-dialog]"), + model: cartModel.get('discountModal'), + messagesEl: $("[mz-modal-discount-dialog]").find('[data-mz-message-bar]') }) }; - + cartModel.on('ordercreated', function (order) { cartModel.isLoading(true); window.location = (HyprLiveContext.locals.siteContext.siteSubdirectory||'') + '/checkout/' + order.prop('id'); @@ -411,7 +425,7 @@ define(['modules/api', CartMonitor.setCount(cartModel.count()); }); - cartModel.checkBOGA(); + //cartModel.checkBOGA(); window.cartView = cartViews; @@ -419,7 +433,7 @@ define(['modules/api', CartMonitor.setCount(cartModel.count()); _.invoke(cartViews, 'render'); - + //cartViews.discountModalView.handleDialogOpen(); renderVisaCheckout(cartModel); paypal.loadScript(); if (cartModel.count() > 0){ diff --git a/scripts/pages/location.js b/scripts/pages/location.js index 2c2756498..cf198ee3a 100644 --- a/scripts/pages/location.js +++ b/scripts/pages/location.js @@ -1,90 +1,8 @@ -require(['modules/jquery-mozu', 'hyprlive', 'modules/backbone-mozu', 'modules/models-location', 'modules/models-product', - 'hyprlivecontext'], - function($, Hypr, Backbone, LocationModels, ProductModels, - HyprLiveContext) { +require(['modules/jquery-mozu', 'hyprlive', 'modules/backbone-mozu', 'modules/models-location', 'modules/models-product', 'modules/views-location'], + function($, Hypr, Backbone, LocationModels, ProductModels, LocationViews) { - var positionErrorLabel = Hypr.getLabel('positionError'), + var positionErrorLabel = Hypr.getLabel('positionError'); - LocationsView = Backbone.MozuView.extend({ - templateName: 'modules/location/locations', - initialize: function () { - var self = this; - if (navigator.geolocation) { - navigator.geolocation.getCurrentPosition(function (pos) { - delete self.positionError; - self.populate(pos); - }, function (err) { - if (err.code !== err.PERMISSION_DENIED) { - self.positionError = positionErrorLabel; - } - self.populate(); - }, { - timeout: 10000 - }); - } else { - this.populate(); - } - }, - populate: function(location) { - var self = this; - var show = function() { - self.render(); - $('.mz-locationsearch-pleasewait').fadeOut(); - self.$el.noFlickerFadeIn(); - }; - if (location) { - this.model.apiGetByLatLong({ location: location }).then(show); - } else { - this.model.apiGet().then(show); - } - }, - getRenderContext: function () { - var c = Backbone.MozuView.prototype.getRenderContext.apply(this, arguments); - c.model.positionError = this.positionError; - return c; - } - }), - - LocationsSearchView = LocationsView.extend({ - templateName: 'modules/location/location-search', - populate: function (location) { - var self = this; - this.model.apiGetForProduct({ - productCode: this.product.get('variationProductCode') || this.product.get('productCode'), - location: location - }).then(function () { - self.render(); - $('.mz-locationsearch-pleasewait').fadeOut(); - self.$el.noFlickerFadeIn(); - }); - }, - addToCartForPickup: function (e) { - var $target = $(e.currentTarget), - loc = $target.data('mzLocation'), - name = $target.data('mzName'); - $target.parent().addClass('is-loading'); - this.product.addToCartForPickup(loc, name, this.product.get('quantity')); - }, - setProduct: function (product) { - var me = this; - me.product = product; - this.listenTo(me.product, 'addedtocart', function() { - $(window).on('beforeunload', function() { - me.$('.is-loading').removeClass('is-loading'); - }); - window.location.href = (HyprLiveContext.locals.pageContext.secureHost||HyprLiveContext.locals.siteContext.siteSubdirectory) + "/cart"; - }); - this.listenTo(me.product, 'error', function () { - this.$('.is-loading').removeClass('is-loading'); - this.render(); - }); - }, - getRenderContext: function () { - var c = Backbone.MozuView.prototype.getRenderContext.apply(this, arguments); - c.model.messages = (this.product.messages) ? this.product.messages.toJSON() : []; - return c; - } - }); $(document).ready(function() { @@ -92,7 +10,7 @@ require(['modules/jquery-mozu', 'hyprlive', 'modules/backbone-mozu', 'modules/mo product = ProductModels.Product.fromCurrent(), productPresent = !!product.get('productCode'), locationsCollection = new LocationModels.LocationCollection(), - ViewClass = productPresent ? LocationsSearchView : LocationsView, + ViewClass = productPresent ? LocationViews.LocationsSearchView : LocationViews.LocationsView, view = new ViewClass({ model: locationsCollection, el: $locationSearch diff --git a/scripts/pages/product.js b/scripts/pages/product.js index 932f5468a..b2328d9b1 100644 --- a/scripts/pages/product.js +++ b/scripts/pages/product.js @@ -88,11 +88,14 @@ $(document).ready(function () { var product = ProductModels.Product.fromCurrent(); - product.on('addedtocart', function (cartitem) { + product.on('addedtocart', function (cartitem, stopRedirect) { if (cartitem && cartitem.prop('id')) { product.isLoading(true); CartMonitor.addToCount(product.get('quantity')); - window.location.href = (HyprLiveContext.locals.pageContext.secureHost||HyprLiveContext.locals.siteContext.siteSubdirectory) + "/cart"; + if(stopRedirect) { + window.location.href = (HyprLiveContext.locals.pageContext.secureHost || HyprLiveContext.locals.siteContext.siteSubdirectory) + "/cart"; + } + } else { product.trigger("error", { message: Hypr.getLabel('unexpectedError') }); } diff --git a/stylesheets/modules/cart/cart-table.less b/stylesheets/modules/cart/cart-table.less index b21c713af..d9a7df423 100644 --- a/stylesheets/modules/cart/cart-table.less +++ b/stylesheets/modules/cart/cart-table.less @@ -85,6 +85,15 @@ .mz-carttable-thirdpartypayment { margin-bottom:10px; } + + .mz-carttable-item-autoAddDiscount { + padding: 6px 10px; + background: @warningBackground; + .mz-carttable-item-autoAddDiscount-actions { + float: right; + margin-right: 15px; + } + } } .mz-carttable-button-active { diff --git a/stylesheets/modules/cart/discount-modal.less b/stylesheets/modules/cart/discount-modal.less new file mode 100644 index 000000000..42837d2d9 --- /dev/null +++ b/stylesheets/modules/cart/discount-modal.less @@ -0,0 +1,22 @@ +.mz-discount-modal { + + .modal-dialog{ + width: 90%; + } + .modal-close-x { + position: absolute; + right: 20px; + top: 14px; + cursor: pointer; + font-size: 17px; + font-weight: 600; + } + .modal-body { + overflow: hidden; + .mz-productlist-tiled .mz-productlist-item { + width: 30%; + margin: 0 10%; + max-width: 300px; + } + } +} \ No newline at end of file diff --git a/stylesheets/storefront.less b/stylesheets/storefront.less index 5b9afa204..1bb9e7967 100644 --- a/stylesheets/storefront.less +++ b/stylesheets/storefront.less @@ -75,6 +75,7 @@ @import "/stylesheets/modules/product/volume-pricing.less"; // Cart @import "/stylesheets/modules/cart/cart-table.less"; +@import "/stylesheets/modules/cart/discount-modal.less"; // Checkout @import "/stylesheets/modules/checkout/checkout-store-credit.less"; @import "/stylesheets/modules/checkout/checkout-digital-credit.less"; diff --git a/templates/modules/cart/cart-table-item.hypr.live b/templates/modules/cart/cart-table-item.hypr.live index 24cbf5b81..318f47fe6 100644 --- a/templates/modules/cart/cart-table-item.hypr.live +++ b/templates/modules/cart/cart-table-item.hypr.live @@ -62,3 +62,19 @@ {{ labels.remove }} +{% if item.productDiscount %} + + +
+ {{ item.productDiscount.discount.name }} + + + {% if item.productDiscount.discount.hasMultipleTargetProducts %} + Update + {% endif %} + Remove + +
+ + +{% endif %} diff --git a/templates/modules/cart/cart-table.hypr.live b/templates/modules/cart/cart-table.hypr.live index fb2d5ba41..376f2708a 100644 --- a/templates/modules/cart/cart-table.hypr.live +++ b/templates/modules/cart/cart-table.hypr.live @@ -37,7 +37,6 @@
- {{ discount.discount.name }}: -{{ discount.impact|currency }}
diff --git a/templates/modules/cart/discount-modal/discount-add-product.hypr.live b/templates/modules/cart/discount-modal/discount-add-product.hypr.live new file mode 100644 index 000000000..89ef1bf06 --- /dev/null +++ b/templates/modules/cart/discount-modal/discount-add-product.hypr.live @@ -0,0 +1,183 @@ +
+ <- back +

{{ model.content.productName }}

+ +
+ {% include "modules/product/product-images" %} +
+ +
+ {% include "modules/common/message-bar" %} +
+ {% if model.content.productShortDescription and themeSettings.showProductDetailShortDesc %} +
+

{{ labels.shortDesc }}

+ {{ model.content.productShortDescription|safe }} +
+ {% endif %} {% if model.options and model.options.length > 0 %} +
+

{{ labels.options }}

+ {% include "modules/product/product-options-discount" %} +
+ {% endif %} + +
+

{{ labels.price }}

+ {% include "modules/product/price-stack" %} +
+
+
+
{{ labels.productCode }}
+
{{ model.variationProductCode|default(model.productCode) }}
+ {% if themeSettings.showProductDetailMfgPartNumber and model.mfgPartNumber.length == 1 %} +
{{ labels.mfgPartNumber }}
+
{{ model.mfgPartNumber|first }}
+ {% endif %} {% if themeSettings.showProductDetailUPC and model.upc.length == 1 %} +
{{ labels.upc }}
+
{{ model.upc|first }}
+ {% endif %} +
+ +
+
+ + + {% if siteContext.generalSettings.isWishlistCreationEnabled and not user.isAnonymous %} + + {% endif %} + {% if siteContext.supportsInStorePickup %} + + {% endif %} +
+
+ + {% if not model.isPurchasable %} +

+ {{ labels.notPurchasable }}: {% for message in model.purchasableState.messages %} + {{ message.message }} + {% endfor %} +

+ {% endif %} +
+
+ + {% if model.hasVolumePricing %} +
+ {% include "modules/product/volume-pricing" %} +
+ {% endif %} + +
+

{{ labels.fullDesc }}

+ {{ model.content.productFullDescription|safe }} +
+ + {% if model.productUsage == 'Bundle' %} +
+

{{ labels.bundleContents }}

+
+ {% for prod in model.bundledProducts %} +
+ {% if prod.quantity != 1 %}{{ prod.quantity }} x {% endif %} {{ prod.content.productName }} +
+
+ {{ prod.content.productShortDescription|safe }} +
+ {% endfor %} +
+
+ {% endif %} {% if model.properties and themeSettings.showProductDetailProperties %} +
+

{{ labels.properties }}

+
+ {% for property in model.properties %} {% if property.values and not property.isHidden %} +
{{property.attributeDetail.name}}
+ {% for v in property.values %} +
{% if v.stringValue %}{{v.stringValue}}{% else %}{{v.value}}{% endif %}
+ {% endfor %} {% endif %} {% endfor %} +
+
+ {% endif %} {% if model.measurements and themeSettings.showProductDetailMeasurements and not model.bundledProducts %} +
+

{{ labels.measurements }}

+
+
{{ labels.weight }}
+
+ {{ model.measurements.packageWeight.value }} + {{ model.measurements.packageWeight.unit }} +
+ +
{{ labels.length }}
+
+ {{ model.measurements.packageLength.value }} + {{ model.measurements.packageLength.unit }} +
+ +
{{ labels.width }}
+
+ {{ model.measurements.packageWidth.value }} + {{ model.measurements.packageWidth.unit }} +
+ +
{{ labels.height }}
+
+ {{ model.measurements.packageHeight.value }} + {{ model.measurements.packageHeight.unit }} +
+ +
+
+ + + + {% endif %} {% if themeSettings.showProductDetailMeasurements and model.bundledProducts %} +
+

{{ labels.measurements }}

+ + {% for product in model.bundledProducts %} {% if product.measurements %} +
+ {{ product.content.productName }} + x {{ product.quantity }} +
+
+
{{ labels.weight }}
+
+ {{ product.measurements.packageWeight.value }} + {{ product.measurements.packageWeight.unit }} +
+ +
{{ labels.length }}
+
+ {{ product.measurements.packageLength.value }} + {{ product.measurements.packageLength.unit }} +
+ +
{{ labels.width }}
+
+ {{ product.measurements.packageWidth.value }} + {{ product.measurements.packageWidth.unit }} +
+ +
{{ labels.height }}
+
+ {{ product.measurements.packageHeight.value }} + {{ product.measurements.packageHeight.unit }} +
+ +
+ {% endif %} {% endfor %} +
+ {% endif %} +
+
+
\ No newline at end of file diff --git a/templates/modules/cart/discount-modal/discount-choose-product.hypr.live b/templates/modules/cart/discount-modal/discount-choose-product.hypr.live new file mode 100644 index 000000000..0b02ca9ea --- /dev/null +++ b/templates/modules/cart/discount-modal/discount-choose-product.hypr.live @@ -0,0 +1,9 @@ +
+ {% if model.products %} + + {% endif %} +
\ No newline at end of file diff --git a/templates/modules/cart/discount-modal/discount-modal.hypr.live b/templates/modules/cart/discount-modal/discount-modal.hypr.live new file mode 100644 index 000000000..2fd2c3cd7 --- /dev/null +++ b/templates/modules/cart/discount-modal/discount-modal.hypr.live @@ -0,0 +1,18 @@ +{% extends "modules/common/modal-dialog" %} + +{% block modal-header %} + +{% endblock modal-header %} + +{% block modal-body %} + +{% include "modules/common/message-bar" with model=model.messages %} + +
+
+ +{% endblock modal-body %} + +{% block modal-footer %} + +{% endblock modal-footer %} \ No newline at end of file diff --git a/templates/modules/cart/discount-modal/discount-product-location.hypr.live b/templates/modules/cart/discount-modal/discount-product-location.hypr.live new file mode 100644 index 000000000..371e3ad26 --- /dev/null +++ b/templates/modules/cart/discount-modal/discount-product-location.hypr.live @@ -0,0 +1,10 @@ +{% require_script "pages/location" %} + +
+

+ {{ labels.locationWaitProd|string_format(model.content.productName)|safe }} +

+
+ {% include "modules/location/location-search" with model=model.locations %} +
+
\ No newline at end of file diff --git a/templates/modules/product/product-detail-discount.hypr.live b/templates/modules/product/product-detail-discount.hypr.live new file mode 100644 index 000000000..3ddb07e6c --- /dev/null +++ b/templates/modules/product/product-detail-discount.hypr.live @@ -0,0 +1,10 @@ +{% extends "modules/product/product-detail" %} + +{% block in-store-pickup %} + {% if siteContext.supportsInStorePickup %} + + {% endif %} +
+{% endblock in-store-pickup %} \ No newline at end of file diff --git a/templates/modules/product/product-detail.hypr.live b/templates/modules/product/product-detail.hypr.live index d47670499..5e95d615c 100644 --- a/templates/modules/product/product-detail.hypr.live +++ b/templates/modules/product/product-detail.hypr.live @@ -1,179 +1,181 @@ -{% if model.content.productShortDescription and themeSettings.showProductDetailShortDesc %} -
-

{{ labels.shortDesc }}

- {{ model.content.productShortDescription|safe }} -
-{% endif %} - -{% if model.options and model.options.length > 0 %} -
-

{{ labels.options }}

- {% include "modules/product/product-options" %} -
-{% endif %} - -
-

{{ labels.price }}

-{% include "modules/product/price-stack" %} -
-
-
-
{{ labels.productCode }}
-
{{ model.variationProductCode|default(model.productCode) }}
- {% if themeSettings.showProductDetailMfgPartNumber and model.mfgPartNumber.length == 1 %} -
{{ labels.mfgPartNumber }}
-
{{ model.mfgPartNumber|first }}
- {% endif %} - {% if themeSettings.showProductDetailUPC and model.upc.length == 1 %} -
{{ labels.upc }}
-
{{ model.upc|first }}
- {% endif %} -
- -
-
- {{ labels.qty }} - - -
-
- - - {% if siteContext.generalSettings.isWishlistCreationEnabled and not user.isAnonymous %} - - {% endif %} - - {% if siteContext.supportsInStorePickup %} -
- - -
- {% endif %} -
- - {% if not model.isPurchasable %} -

- {{ labels.notPurchasable }}: - {% for message in model.purchasableState.messages %} - {{ message.message }} - {% endfor %} -

- {% endif %} -
-
- -{% if model.hasVolumePricing %} -
- {% include "modules/product/volume-pricing" %} -
-{% endif %} - -
-

{{ labels.fullDesc }}

- {{ model.content.productFullDescription|safe }} -
- -{% if model.productUsage == 'Bundle' %} -
-

{{ labels.bundleContents }}

-
- {% for prod in model.bundledProducts %} -
- {% if prod.quantity != 1 %}{{ prod.quantity }} x {% endif %} - {{ prod.content.productName }} -
-
- {{ prod.content.productShortDescription|safe }} -
- {% endfor %} -
-
-{% endif %} - -{% if model.properties and themeSettings.showProductDetailProperties %} -
-

{{ labels.properties }}

-
- {% for property in model.properties %} - {% if property.values and not property.isHidden %} -
{{property.attributeDetail.name}}
- {% for v in property.values %} -
{% if v.stringValue %}{{v.stringValue}}{% else %}{{v.value}}{% endif %}
- {% endfor %} - {% endif %} - {% endfor %} -
-
-{% endif %} - -{% if model.measurements and themeSettings.showProductDetailMeasurements and not model.bundledProducts %} -
-

{{ labels.measurements }}

-
-
{{ labels.weight }}
-
- {{ model.measurements.packageWeight.value }} {{ model.measurements.packageWeight.unit }} -
- -
{{ labels.length }}
-
- {{ model.measurements.packageLength.value }} {{ model.measurements.packageLength.unit }} -
- -
{{ labels.width }}
-
- {{ model.measurements.packageWidth.value }} {{ model.measurements.packageWidth.unit }} -
- -
{{ labels.height }}
-
- {{ model.measurements.packageHeight.value }} {{ model.measurements.packageHeight.unit }} -
- -
-
- - - -{% endif %} - -{% if themeSettings.showProductDetailMeasurements and model.bundledProducts %} -
-

{{ labels.measurements }}

- - {% for product in model.bundledProducts %} - {% if product.measurements %} -
- {{ product.content.productName }} - x {{ product.quantity }} -
-
-
{{ labels.weight }}
-
- {{ product.measurements.packageWeight.value }} {{ product.measurements.packageWeight.unit }} -
- -
{{ labels.length }}
-
- {{ product.measurements.packageLength.value }} {{ product.measurements.packageLength.unit }} -
- -
{{ labels.width }}
-
- {{ product.measurements.packageWidth.value }} {{ product.measurements.packageWidth.unit }} -
- -
{{ labels.height }}
-
- {{ product.measurements.packageHeight.value }} {{ product.measurements.packageHeight.unit }} -
- -
- {% endif %} - {% endfor %} -
+{% if model.content.productShortDescription and themeSettings.showProductDetailShortDesc %} +
+

{{ labels.shortDesc }}

+ {{ model.content.productShortDescription|safe }} +
+{% endif %} + +{% if model.options and model.options.length > 0 %} +
+

{{ labels.options }}

+ {% include "modules/product/product-options" %} +
+{% endif %} + +
+

{{ labels.price }}

+{% include "modules/product/price-stack" %} +
+
+
+
{{ labels.productCode }}
+
{{ model.variationProductCode|default(model.productCode) }}
+ {% if themeSettings.showProductDetailMfgPartNumber and model.mfgPartNumber.length == 1 %} +
{{ labels.mfgPartNumber }}
+
{{ model.mfgPartNumber|first }}
+ {% endif %} + {% if themeSettings.showProductDetailUPC and model.upc.length == 1 %} +
{{ labels.upc }}
+
{{ model.upc|first }}
+ {% endif %} +
+ +
+
+ {{ labels.qty }} + + +
+
+ + + {% if siteContext.generalSettings.isWishlistCreationEnabled and not user.isAnonymous %} + + {% endif %} + + {% block in-store-pickup %} + {% if siteContext.supportsInStorePickup %} +
+ + +
+ {% endif %} + {% endblock in-store-pickup %} +
+ + {% if not model.isPurchasable %} +

+ {{ labels.notPurchasable }}: + {% for message in model.purchasableState.messages %} + {{ message.message }} + {% endfor %} +

+ {% endif %} +
+
+ +{% if model.hasVolumePricing %} +
+ {% include "modules/product/volume-pricing" %} +
+{% endif %} + +
+

{{ labels.fullDesc }}

+ {{ model.content.productFullDescription|safe }} +
+ +{% if model.productUsage == 'Bundle' %} +
+

{{ labels.bundleContents }}

+
+ {% for prod in model.bundledProducts %} +
+ {% if prod.quantity != 1 %}{{ prod.quantity }} x {% endif %} + {{ prod.content.productName }} +
+
+ {{ prod.content.productShortDescription|safe }} +
+ {% endfor %} +
+
+{% endif %} + +{% if model.properties and themeSettings.showProductDetailProperties %} +
+

{{ labels.properties }}

+
+ {% for property in model.properties %} + {% if property.values and not property.isHidden %} +
{{property.attributeDetail.name}}
+ {% for v in property.values %} +
{% if v.stringValue %}{{v.stringValue}}{% else %}{{v.value}}{% endif %}
+ {% endfor %} + {% endif %} + {% endfor %} +
+
+{% endif %} + +{% if model.measurements and themeSettings.showProductDetailMeasurements and not model.bundledProducts %} +
+

{{ labels.measurements }}

+
+
{{ labels.weight }}
+
+ {{ model.measurements.packageWeight.value }} {{ model.measurements.packageWeight.unit }} +
+ +
{{ labels.length }}
+
+ {{ model.measurements.packageLength.value }} {{ model.measurements.packageLength.unit }} +
+ +
{{ labels.width }}
+
+ {{ model.measurements.packageWidth.value }} {{ model.measurements.packageWidth.unit }} +
+ +
{{ labels.height }}
+
+ {{ model.measurements.packageHeight.value }} {{ model.measurements.packageHeight.unit }} +
+ +
+
+ + + +{% endif %} + +{% if themeSettings.showProductDetailMeasurements and model.bundledProducts %} +
+

{{ labels.measurements }}

+ + {% for product in model.bundledProducts %} + {% if product.measurements %} +
+ {{ product.content.productName }} + x {{ product.quantity }} +
+
+
{{ labels.weight }}
+
+ {{ product.measurements.packageWeight.value }} {{ product.measurements.packageWeight.unit }} +
+ +
{{ labels.length }}
+
+ {{ product.measurements.packageLength.value }} {{ product.measurements.packageLength.unit }} +
+ +
{{ labels.width }}
+
+ {{ product.measurements.packageWidth.value }} {{ product.measurements.packageWidth.unit }} +
+ +
{{ labels.height }}
+
+ {{ product.measurements.packageHeight.value }} {{ product.measurements.packageHeight.unit }} +
+ +
+ {% endif %} + {% endfor %} +
{% endif %} \ No newline at end of file diff --git a/templates/modules/product/product-discount-listing.hypr.live b/templates/modules/product/product-discount-listing.hypr.live new file mode 100644 index 000000000..bee2dcda2 --- /dev/null +++ b/templates/modules/product/product-discount-listing.hypr.live @@ -0,0 +1,37 @@ +
+ +
+ {{model.content.productName}} + {% if model.content.productShortDescription and themeSettings.listProductShortDesc %} +

{{ model.content.productShortDescription|truncatewords(themeSettings.maxProductSummaryWords)|safe }}

+ {% endif %} {% block product-code %} {% if themeSettings.listProductCode %} +
{{model.productCode}}
+ {% endif %} {% endblock product-code %} {% if model.volumePriceRange %} +
+ + {% include "modules/common/volume-price" with model=model.volumePriceRange.lower %} + + + {% include "modules/common/volume-price" with model=model.volumePriceRange.upper %} + +
+ {% else %} {% include "modules/product/price-stack" %} {% endif %} {% block product-extrainfo %} {% if dealOfTheDay %} {% + if dealOfTheDay.savings %} {% if model.price.discount.impact %} +

You save: {{ model.price.discount.impact|currency }}

+ {% endif %} {% endif %} {% if dealOfTheDay.expirationDate %} {% if model.price.discount.discount.expirationDate %} +

Expires: {{ model.price.discount.discount.expirationDate|date("F j, Y") }}

+ {% endif %} {% endif %} {% endif %} {% endblock product-extrainfo %} +
+
\ No newline at end of file diff --git a/templates/modules/product/product-options-discount.hypr.live b/templates/modules/product/product-options-discount.hypr.live new file mode 100644 index 000000000..8518ef730 --- /dev/null +++ b/templates/modules/product/product-options-discount.hypr.live @@ -0,0 +1,48 @@ +
+ {% for option in model.options %} +
+ {% if option.isRequired %} + * + {% endif %} + +
+ {% if option.attributeDetail.inputType == "List" %} + + {% else %} + {% if option.attributeDetail.inputType == "YesNo" %} + + {% endif %} + {% if option.attributeDetail.inputType == "TextBox" %} + {% if option.attributeDetail.dataType == "Number" %} + + {% else %} + + {% endif %} + {% endif %} + {% if option.attributeDetail.inputType == "TextArea" %} + + {% endif %} + {% if option.attributeDetail.inputType == "Date" %} + + {% endif %} + {% if option.values %} + {% for value in option.values %} + {% if forloop.counter0 == 0 and value.deltaPrice > 0 %} + ({{ value.deltaPrice|currency }} {{ labels.more }}) + {% endif %} + {% endfor %} + {% endif %} + {% endif %} +
+
+ {% endfor %} +
\ No newline at end of file diff --git a/templates/pages/cart.hypr b/templates/pages/cart.hypr index 85f176be4..1bb2e668d 100644 --- a/templates/pages/cart.hypr +++ b/templates/pages/cart.hypr @@ -32,7 +32,15 @@ {% include "modules/common/message-bar" with model=model.messages %} {% include "modules/cart/cart-table" %} -
+ + + {% block modal-dialog %} +
+ +
+ {% endblock modal-dialog %} {% endblock body-content %}