diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/website_sale_wishlist/static | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/website_sale_wishlist/static')
5 files changed, 642 insertions, 0 deletions
diff --git a/addons/website_sale_wishlist/static/description/icon.png b/addons/website_sale_wishlist/static/description/icon.png Binary files differnew file mode 100644 index 00000000..80cf059e --- /dev/null +++ b/addons/website_sale_wishlist/static/description/icon.png diff --git a/addons/website_sale_wishlist/static/description/icon.svg b/addons/website_sale_wishlist/static/description/icon.svg new file mode 100644 index 00000000..9b04ee49 --- /dev/null +++ b/addons/website_sale_wishlist/static/description/icon.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="70" height="70" viewBox="0 0 70 70"><defs><path id="a" d="M4 0h61c4 0 5 1 5 5v60c0 4-1 5-5 5H4c-3 0-4-1-4-5V5c0-4 1-5 4-5z"/><linearGradient id="c" x1="100%" x2="0%" y1="0%" y2="100%"><stop offset="0%" stop-color="#269396"/><stop offset="100%" stop-color="#218689"/></linearGradient><path id="d" d="M50.363 41a1.636 1.636 0 0 1-1.59 1.286L27 43l1 4h19c1 0 1 2 0 2H26l-6-24h-2v1c0 .667-.333 1-1 1s-1-.333-1-1v-2c.066-.667.4-1 1-1h4c.517 0 .85.333 1 1l1 3.281h28.45c1.048 0 1.824.985 1.592 2.019L50.363 41zM45.5 55a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5zm-19 0a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5zm11.375-15c.114 0 .21-.04.289-.118l4.084-3.934c1.001-1.001 1.502-1.985 1.502-2.95 0-.962-.278-1.714-.833-2.256-.555-.542-1.322-.813-2.301-.813-.271 0-.548.047-.83.14-.282.095-.544.221-.786.38a8.39 8.39 0 0 0-.627.45c-.175.14-.34.288-.498.446a6.041 6.041 0 0 0-.498-.446 8.39 8.39 0 0 0-.627-.45 3.357 3.357 0 0 0-.786-.38 2.614 2.614 0 0 0-.83-.14c-.979 0-1.746.27-2.301.813-.555.542-.833 1.294-.833 2.255 0 .293.051.595.154.905.103.31.22.575.35.793.132.219.28.432.447.64.166.207.287.35.364.43.076.078.136.135.18.17l4.091 3.947a.392.392 0 0 0 .289.118z"/><path id="e" d="M50.363 39a1.636 1.636 0 0 1-1.59 1.286L27 41l1 4h19c1 0 1 2 0 2H26l-6-24h-2v1c0 .667-.333 1-1 1s-1-.333-1-1v-2c.066-.667.4-1 1-1h4c.517 0 .85.333 1 1l1 3.281h28.45c1.048 0 1.824.985 1.592 2.019L50.363 39zM45.5 53a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5zm-19 0a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5zm11.375-15c.114 0 .21-.04.289-.118l4.084-3.934c1.001-1.001 1.502-1.985 1.502-2.95 0-.962-.278-1.714-.833-2.256-.555-.542-1.322-.813-2.301-.813-.271 0-.548.047-.83.14-.282.095-.544.221-.786.38a8.39 8.39 0 0 0-.627.45c-.175.14-.34.288-.498.446a6.041 6.041 0 0 0-.498-.446 8.39 8.39 0 0 0-.627-.45 3.357 3.357 0 0 0-.786-.38 2.614 2.614 0 0 0-.83-.14c-.979 0-1.746.27-2.301.813-.555.542-.833 1.294-.833 2.255 0 .293.051.595.154.905.103.31.22.575.35.793.132.219.28.432.447.64.166.207.287.35.364.43.076.078.136.135.18.17l4.091 3.947a.392.392 0 0 0 .289.118z"/></defs><g fill="none" fill-rule="evenodd"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><g mask="url(#b)"><path fill="url(#c)" d="M0 0H70V70H0z"/><path fill="#FFF" fill-opacity=".383" d="M4 1h61c2.667 0 4.333.667 5 2V0H0v3c.667-1.333 2-2 4-2z"/><path fill="#393939" d="M31.59 69H4c-2 0-4-1-4-4V38.29l16.235-16.918L21 21l2 5h29.636L50.23 39.393 45.537 45H47l.511 1.724-2.21 2.392 2.3 2.671L31.59 69z" opacity=".324"/><path fill="#000" fill-opacity=".383" d="M4 69h61c2.667 0 4.333-1 5-3v4H0v-4c.667 2 2 3 4 3z"/><use fill="#000" fill-rule="nonzero" opacity=".3" xlink:href="#d"/><use fill="#FFF" fill-rule="nonzero" xlink:href="#e"/></g></g></svg>
\ No newline at end of file diff --git a/addons/website_sale_wishlist/static/src/js/website_sale_wishlist.js b/addons/website_sale_wishlist/static/src/js/website_sale_wishlist.js new file mode 100644 index 00000000..075dfc92 --- /dev/null +++ b/addons/website_sale_wishlist/static/src/js/website_sale_wishlist.js @@ -0,0 +1,274 @@ +odoo.define('website_sale_wishlist.wishlist', function (require) { +"use strict"; + +var publicWidget = require('web.public.widget'); +var wSaleUtils = require('website_sale.utils'); +var VariantMixin = require('sale.VariantMixin'); + +// VariantMixin events are overridden on purpose here +// to avoid registering them more than once since they are already registered +// in website_sale.js +publicWidget.registry.ProductWishlist = publicWidget.Widget.extend(VariantMixin, { + selector: '.oe_website_sale', + events: { + 'click .o_wsale_my_wish': '_onClickMyWish', + 'click .o_add_wishlist, .o_add_wishlist_dyn': '_onClickAddWish', + 'change input.product_id': '_onChangeVariant', + 'change input.js_product_change': '_onChangeProduct', + 'click .wishlist-section .o_wish_rm': '_onClickWishRemove', + 'click .wishlist-section .o_wish_add': '_onClickWishAdd', + }, + + /** + * @constructor + */ + init: function (parent) { + this._super.apply(this, arguments); + this.wishlistProductIDs = []; + }, + /** + * Gets the current wishlist items. + * In editable mode, do nothing instead. + * + * @override + */ + willStart: function () { + var self = this; + var def = this._super.apply(this, arguments); + + var wishDef = $.get('/shop/wishlist', { + count: 1, + }).then(function (res) { + self.wishlistProductIDs = JSON.parse(res); + }); + + return Promise.all([def, wishDef]); + }, + /** + * Updates the wishlist view (navbar) & the wishlist button (product page). + * In editable mode, do nothing instead. + * + * @override + */ + start: function () { + var def = this._super.apply(this, arguments); + + this._updateWishlistView(); + // trigger change on only one input + if (this.$('input.js_product_change').length) { // manage "List View of variants" + this.$('input.js_product_change:checked').first().trigger('change'); + } else { + this.$('input.product_id').first().trigger('change'); + } + + return def; + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @private + */ + _addNewProducts: function ($el) { + var self = this; + var productID = $el.data('product-product-id'); + if ($el.hasClass('o_add_wishlist_dyn')) { + productID = $el.parent().find('.product_id').val(); + if (!productID) { // case List View Variants + productID = $el.parent().find('input:checked').first().val(); + } + productID = parseInt(productID, 10); + } + var $form = $el.closest('form'); + var templateId = $form.find('.product_template_id').val(); + // when adding from /shop instead of the product page, need another selector + if (!templateId) { + templateId = $el.data('product-template-id'); + } + $el.prop("disabled", true).addClass('disabled'); + var productReady = this.selectOrCreateProduct( + $el.closest('form'), + productID, + templateId, + false + ); + + productReady.then(function (productId) { + productId = parseInt(productId, 10); + + if (productId && !_.contains(self.wishlistProductIDs, productId)) { + return self._rpc({ + route: '/shop/wishlist/add', + params: { + product_id: productId, + }, + }).then(function () { + var $navButton = $('header .o_wsale_my_wish').first(); + self.wishlistProductIDs.push(productId); + self._updateWishlistView(); + wSaleUtils.animateClone($navButton, $el.closest('form'), 25, 40); + }).guardedCatch(function () { + $el.prop("disabled", false).removeClass('disabled'); + }); + } + }).guardedCatch(function () { + $el.prop("disabled", false).removeClass('disabled'); + }); + }, + /** + * @private + */ + _updateWishlistView: function () { + const $wishButton = $('.o_wsale_my_wish'); + if ($wishButton.hasClass('o_wsale_my_wish_hide_empty')) { + $wishButton.toggleClass('d-none', !this.wishlistProductIDs.length); + } + $wishButton.find('.my_wish_quantity').text(this.wishlistProductIDs.length); + }, + /** + * @private + */ + _removeWish: function (e, deferred_redirect) { + var tr = $(e.currentTarget).parents('tr'); + var wish = tr.data('wish-id'); + var product = tr.data('product-id'); + var self = this; + + this._rpc({ + route: '/shop/wishlist/remove/' + wish, + }).then(function () { + $(tr).hide(); + }); + + this.wishlistProductIDs = _.without(this.wishlistProductIDs, product); + if (this.wishlistProductIDs.length === 0) { + if (deferred_redirect) { + deferred_redirect.then(function () { + self._redirectNoWish(); + }); + } + } + this._updateWishlistView(); + }, + /** + * @private + */ + _addOrMoveWish: function (e) { + var $navButton = $('header .o_wsale_my_cart').first(); + var tr = $(e.currentTarget).parents('tr'); + var product = tr.data('product-id'); + $('.o_wsale_my_cart').removeClass('d-none'); + wSaleUtils.animateClone($navButton, tr, 25, 40); + + if ($('#b2b_wish').is(':checked')) { + return this._addToCart(product, tr.find('add_qty').val() || 1); + } else { + var adding_deffered = this._addToCart(product, tr.find('add_qty').val() || 1); + this._removeWish(e, adding_deffered); + return adding_deffered; + } + }, + /** + * @private + */ + _addToCart: function (productID, qty_id) { + return this._rpc({ + route: "/shop/cart/update_json", + params: { + product_id: parseInt(productID, 10), + add_qty: parseInt(qty_id, 10), + display: false, + }, + }).then(function (resp) { + if (resp.warning) { + if (! $('#data_warning').length) { + $('.wishlist-section').prepend('<div class="mt16 alert alert-danger alert-dismissable" role="alert" id="data_warning"></div>'); + } + var cart_alert = $('.wishlist-section').parent().find('#data_warning'); + cart_alert.html('<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> ' + resp.warning); + } + $('.my_cart_quantity').html(resp.cart_quantity || '<i class="fa fa-warning" /> '); + }); + }, + /** + * @private + */ + _redirectNoWish: function () { + window.location.href = '/shop/cart'; + }, + + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + */ + _onClickMyWish: function () { + if (this.wishlistProductIDs.length === 0) { + this._updateWishlistView(); + this._redirectNoWish(); + return; + } + window.location = '/shop/wishlist'; + }, + /** + * @private + * @param {Event} ev + */ + _onClickAddWish: function (ev) { + this._addNewProducts($(ev.currentTarget)); + }, + /** + * @private + * @param {Event} ev + */ + _onChangeVariant: function (ev) { + var $input = $(ev.target); + var $parent = $input.closest('.js_product'); + var $el = $parent.find("[data-action='o_wishlist']"); + if (!_.contains(this.wishlistProductIDs, parseInt($input.val(), 10))) { + $el.prop("disabled", false).removeClass('disabled').removeAttr('disabled'); + } else { + $el.prop("disabled", true).addClass('disabled').attr('disabled', 'disabled'); + } + $el.data('product-product-id', parseInt($input.val(), 10)); + }, + /** + * @private + * @param {Event} ev + */ + _onChangeProduct: function (ev) { + var productID = ev.currentTarget.value; + var $el = $(ev.target).closest('.js_add_cart_variants').find("[data-action='o_wishlist']"); + + if (!_.contains(this.wishlistProductIDs, parseInt(productID, 10))) { + $el.prop("disabled", false).removeClass('disabled').removeAttr('disabled'); + } else { + $el.prop("disabled", true).addClass('disabled').attr('disabled', 'disabled'); + } + $el.data('product-product-id', productID); + }, + /** + * @private + * @param {Event} ev + */ + _onClickWishRemove: function (ev) { + this._removeWish(ev, false); + }, + /** + * @private + * @param {Event} ev + */ + _onClickWishAdd: function (ev) { + var self = this; + this.$('.wishlist-section .o_wish_add').addClass('disabled'); + this._addOrMoveWish(ev).then(function () { + self.$('.wishlist-section .o_wish_add').removeClass('disabled'); + }); + }, +}); +}); diff --git a/addons/website_sale_wishlist/static/src/scss/website_sale_wishlist.scss b/addons/website_sale_wishlist/static/src/scss/website_sale_wishlist.scss new file mode 100644 index 00000000..13a1ec4b --- /dev/null +++ b/addons/website_sale_wishlist/static/src/scss/website_sale_wishlist.scss @@ -0,0 +1,23 @@ +.oe_website_sale { + .td-wish-btn { + width: 140px; + } + + div.css_not_available .o_add_wishlist_dyn { + display: none; + } +} + +// XS size +@include media-breakpoint-down(sm) { + .oe_website_sale { + .td-wish-btn { + width: 100px; + } + } +} + +table.table-comparator .td-img img { + // allows sizing the placeholder image to the "image" size of 100px + max-height: 100px; +}
\ No newline at end of file diff --git a/addons/website_sale_wishlist/static/tests/tours/website_sale_wishlist.js b/addons/website_sale_wishlist/static/tests/tours/website_sale_wishlist.js new file mode 100644 index 00000000..ab919360 --- /dev/null +++ b/addons/website_sale_wishlist/static/tests/tours/website_sale_wishlist.js @@ -0,0 +1,344 @@ +odoo.define('website_sale_wishlist.tour', function (require) { +'use strict'; + +var rpc = require('web.rpc'); +var tour = require("web_tour.tour"); + +tour.register('shop_wishlist', { + test: true, + url: '/shop?search=Customizable Desk', +}, + [ + { + content: "click on add to wishlist", + trigger: '.o_add_wishlist', + }, + { + content: "go to wishlist", + extra_trigger: 'a[href="/shop/wishlist"] .badge:contains(1)', + trigger: 'a[href="/shop/wishlist"]', + }, + { + content: "remove first item in whishlist", + trigger: '.o_wish_rm:first', + }, + { + content: "go back to the store", + trigger: "a[href='/shop']" + }, + { + content: "click on add to wishlist", + trigger: '.o_add_wishlist', + }, + { + content: "check value of wishlist and go to login", + extra_trigger: ".my_wish_quantity:contains(1)", + trigger: 'a[href="/web/login"]', + }, + { + content: "submit login", + trigger: ".oe_login_form", + run: function (){ + $('.oe_login_form input[name="login"]').val("admin"); + $('.oe_login_form input[name="password"]').val("admin"); + $('.oe_login_form input[name="redirect"]').val("/shop?search=Customizable Desk"); + $('.oe_login_form').submit(); + }, + }, + { + content: "check that logged in", + trigger: "li span:contains('Mitchell Admin')", + run: function () {}, + }, + { + content: "click on Customizable Desk (TEST)", + trigger: '.oe_product_cart a:contains("Customizable Desk (TEST)")', + }, + { + content: "check the first variant is already in wishlist", + trigger: '#product_detail .o_add_wishlist_dyn:disabled', + run: function () {}, + }, + { + content: "change variant", + extra_trigger: '#product_detail label:contains(Aluminium) input', + trigger: 'label:contains(Aluminium) input', + }, + { + content: "wait button enable and click on add to wishlist", + extra_trigger: '#product_detail .o_add_wishlist_dyn:not(:disabled)', + trigger: '#product_detail .o_add_wishlist_dyn', + }, + { + content: "check that wishlist contains 2 items and go to wishlist", + extra_trigger: 'a[href="/shop/wishlist"] .badge:contains(2)', + trigger: 'a[href="/shop/wishlist"]', + }, + { + content: "remove Customizable Desk (TEST)", + trigger: 'tr:contains("Customizable Desk (TEST)") .o_wish_rm:first', + }, + { + content: "check that wishlist contains 1 item", + trigger: ".my_wish_quantity:contains(1)", + run: function() {}, + }, + { + content: "check B2B wishlist mode", + trigger: "input#b2b_wish", + }, + { + content: "add item to cart", + trigger: '.o_wish_add:eq(1)', + }, + { + content: "check that cart contains 1 item", + trigger: ".my_cart_quantity:contains(1)", + run: function() {}, + }, + { + content: "check that wishlist contains 1 item", + trigger: ".my_wish_quantity:contains(1)", + run: function() {}, + }, + { + content: "remove B2B wishlist mode", + trigger: "input#b2b_wish", + }, + { + content: "add last item to cart", + trigger: '.o_wish_add:eq(1)', + }, + { + content: "check that user is redirect - wishlist is empty", + trigger: "#wrap #cart_products", + run: function() {}, + }, + { + content: "check that cart contains 2 items", + trigger: ".my_cart_quantity:contains(2)", + run: function() {}, + }, + { + content: "check that wishlist is empty and no more visible", + trigger: ":not(:has(.my_wish_quantity:visible))", + run: function() {}, + }, + // Test dynamic attributes + { + content: "Create a product with dynamic attribute and its values.", + trigger: 'body', + run: function () { + rpc.query({ + model: 'product.attribute', + method: 'create', + args: [{ + 'name': "color", + 'display_type': 'color', + 'create_variant': 'dynamic' + }], + }).then(function (attributeId) { + return rpc.query({ + model: 'product.template', + method: 'create', + args: [{ + 'name': "Bottle", + 'is_published': true, + 'attribute_line_ids': [[0, 0, { + 'attribute_id': attributeId, + 'value_ids': [ + [0, 0, { + 'name': "red", + 'attribute_id': attributeId, + }], + [0, 0, { + 'name': "blue", + 'attribute_id': attributeId, + }], + [0, 0, { + 'name': "black", + 'attribute_id': attributeId, + }], + ] + }]], + }], + }); + }).then(function () { + window.location.href = '/web/session/logout?redirect=/shop?search=Bottle'; + }); + }, + }, + { + content: "Add Bottle to wishlist from /shop", + extra_trigger: '.oe_product_cart:contains("Bottle")', + trigger: '.oe_product_cart:contains("Bottle") .o_add_wishlist', + }, + { + content: "Check that wishlist contains 1 item", + trigger: '.my_wish_quantity:contains(1)', + run: function () {}, + }, + { + content: "Click on product", + extra_trigger: '.oe_product_cart:contains("Bottle") .o_add_wishlist.disabled', + trigger: '.oe_product_cart a:contains("Bottle")', + }, + { + content: "Select Bottle with second variant from /product", + trigger: '.js_variant_change[data-value_name="blue"]', + }, + { + content: "Add product in wishlist", + extra_trigger: '#product_detail .o_add_wishlist_dyn:not(".disabled")', + trigger: '#product_detail .o_add_wishlist_dyn', + }, + { + content: "Select Bottle with third variant from /product", + trigger: '.js_variant_change[data-value_name="black"]', + }, + { + content: "Add product in wishlist", + extra_trigger: '#product_detail .o_add_wishlist_dyn:not(".disabled")', + trigger: '#product_detail .o_add_wishlist_dyn', + }, + { + content: "Check that wishlist contains 3 items and go to wishlist", + trigger: '.my_wish_quantity:contains(3)', + run: function () { + window.location.href = '/shop/wishlist'; + }, + }, + { + content: "Check wishlist contains first variant", + trigger: '#o_comparelist_table tr:contains("red")', + run: function () {}, + }, + { + content: "Check wishlist contains second variant", + trigger: '#o_comparelist_table tr:contains("blue")', + run: function () {}, + }, + { + content: "Check wishlist contains third variant, then go to login", + trigger: '#o_comparelist_table tr:contains("black")', + run: function () { + window.location.href = "/web/login"; + }, + }, + { + content: "Submit login as admin", + trigger: '.oe_login_form', + run: function () { + $('.oe_login_form input[name="login"]').val("admin"); + $('.oe_login_form input[name="password"]').val("admin"); + $('.oe_login_form input[name="redirect"]').val("/"); + $('.oe_login_form').submit(); + }, + }, + // Test one impossible combination while other combinations are possible + { + content: "Archive the first variant", + trigger: '#top_menu:contains("Mitchell Admin")', + run: function () { + rpc.query({ + model: 'product.product', + method: 'search', + args: [[['name', '=', "Bottle"]]], + }) + .then(function (productIds) { + return rpc.query({ + model: 'product.product', + method: 'write', + args: [productIds[0], {active: false}], + }); + }) + .then(function () { + window.location.href = '/web/session/logout?redirect=/shop?search=Bottle'; + }); + }, + }, + { + content: "Check there is wishlist button on product from /shop", + extra_trigger: '.js_sale', + trigger: '.oe_product_cart:contains("Bottle") .o_add_wishlist', + run: function () {}, + }, + { + content: "Click on product", + trigger: '.oe_product_cart a:contains("Bottle")', + }, + { + content: "Select Bottle with first variant (red) from /product", + trigger: '.js_variant_change[data-value_name="red"]', + }, + { + content: "Check there is no wishlist button when selecting impossible variant", + trigger: '#product_detail:not(:has(.o_add_wishlist))', + run: function () {}, + }, + { + content: "Select Bottle with second variant (blue) from /product", + trigger: '.js_variant_change[data-value_name="blue"]', + }, + { + content: "Click on wishlist when selecting a possible variant from /product", + trigger: '#product_detail .o_add_wishlist_dyn:not(.disabled)', + }, + { + content: "Check product added to wishlist and go to login", + trigger: '.my_wish_quantity:contains(1)', + run: function () { + window.location.href = "/web/login"; + }, + }, + { + content: "Submit login", + trigger: '.oe_login_form', + run: function () { + $('.oe_login_form input[name="login"]').val("admin"); + $('.oe_login_form input[name="password"]').val("admin"); + $('.oe_login_form input[name="redirect"]').val("/"); + $('.oe_login_form').submit(); + }, + }, + // test when all combinations are impossible + { + content: "Archive all variants", + trigger: '#top_menu:contains("Mitchell Admin")', + run: function () { + rpc.query({ + model: 'product.product', + method: 'search', + args: [[['name', '=', "Bottle"]]], + }) + .then(function (productIds) { + return rpc.query({ + model: 'product.product', + method: 'write', + args: [productIds, {active: false}], + }); + }) + .then(function () { + window.location.href = '/web/session/logout?redirect=/shop?search=Bottle'; + }); + } + }, + { + content: "Check that there is no wishlist button from /shop", + extra_trigger: '.js_sale', + trigger: '.oe_product_cart:contains("Bottle"):not(:has(.o_add_wishlist))', + run: function () {}, + }, + { + content: "Click on product", + trigger: '.oe_product_cart a:contains("Bottle")', + }, + { + content: "Check that there is no wishlist button from /product", + trigger: '#product_detail:not(:has(.o_add_wishlist_dyn))', + run: function () {}, + }, + ] +); + +}); |
