From 54c6d36cfbea31fe60b888bbb903b0c6f22216b3 Mon Sep 17 00:00:00 2001 From: William Braeckman Date: Thu, 23 Jun 2022 12:38:57 +0000 Subject: [PATCH] [IMP] website_sale: add alternative product image displays This commit adds a new display type for a product page, namely the grid mode. The web designer will now be able to choose between carousel (previous mode) and grid. The web designer may also choose how much space the images take on the page between 0%, 50% 66% and 100%. TaskId-2833507 Part-of: odoo/odoo#92447 --- addons/website_sale/__manifest__.py | 1 + addons/website_sale/controllers/main.py | 21 ++-- addons/website_sale/controllers/variant.py | 12 ++- addons/website_sale/models/website.py | 48 ++++++++++ .../img/snippet_options/image-width-100.svg | 3 + .../img/snippet_options/image-width-50.svg | 4 + .../img/snippet_options/image-width-66.svg | 4 + .../img/snippet_options/image-width-none.svg | 3 + .../static/src/js/website_sale.editor.js | 96 ++++++++++++++++++- .../static/src/js/website_sale_utils.js | 5 +- .../website_sale/views/snippets/snippets.xml | 20 ++++ addons/website_sale/views/templates.xml | 75 +++++++++------ addons/website_sale/views/website_views.xml | 20 ++++ 13 files changed, 268 insertions(+), 44 deletions(-) create mode 100644 addons/website_sale/static/src/img/snippet_options/image-width-100.svg create mode 100644 addons/website_sale/static/src/img/snippet_options/image-width-50.svg create mode 100644 addons/website_sale/static/src/img/snippet_options/image-width-66.svg create mode 100644 addons/website_sale/static/src/img/snippet_options/image-width-none.svg create mode 100644 addons/website_sale/views/website_views.xml diff --git a/addons/website_sale/__manifest__.py b/addons/website_sale/__manifest__.py index 6b365ad7eed..1945d050a60 100644 --- a/addons/website_sale/__manifest__.py +++ b/addons/website_sale/__manifest__.py @@ -31,6 +31,7 @@ 'views/website_sale_visitor_views.xml', 'views/base_unit_view.xml', 'views/product_product_add.xml', + 'views/website_views.xml', ], 'demo': [ 'data/demo.xml', diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index abbcc36c919..f99613599cc 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -1207,12 +1207,21 @@ class WebsiteSale(http.Controller): raise NotFound() current_website = request.env['website'].get_current_website() - if "ppg" in options: - current_website.shop_ppg = options["ppg"] or 1 - if "ppr" in options: - current_website.shop_ppr = options["ppr"] - if "default_sort" in options: - current_website.shop_default_sort = options["default_sort"] + # Restrict options we can write to. + writable_fields = { + 'shop_ppg', 'shop_ppr', 'shop_default_sort', + 'product_page_image_layout', 'product_page_image_width', + 'product_page_grid_columns', 'product_page_image_spacing' + } + # Default ppg to 1. + if 'ppg' in options and not options['ppg']: + options['ppg'] = 1 + if 'product_page_grid_columns' in options: + options['product_page_grid_columns'] = int(options['product_page_grid_columns']) + + write_vals = {k: v for k, v in options.items() if k in writable_fields} + if write_vals: + current_website.write(write_vals) def order_lines_2_google_api(self, order_lines): """ Transforms a list of order lines into a dict for google analytics """ diff --git a/addons/website_sale/controllers/variant.py b/addons/website_sale/controllers/variant.py index 3618a22e1d1..692b8363903 100644 --- a/addons/website_sale/controllers/variant.py +++ b/addons/website_sale/controllers/variant.py @@ -18,11 +18,13 @@ class WebsiteSaleVariantController(VariantController): if request.website.google_analytics_key: combination['product_tracking_info'] = request.env['product.template'].get_google_analytics_data(combination) - carousel_view = request.env['ir.ui.view']._render_template('website_sale.shop_product_carousel', values={ - 'product': request.env['product.template'].browse(combination['product_template_id']), - 'product_variant': request.env['product.product'].browse(combination['product_id']), - }) - combination['carousel'] = carousel_view + if request.website.product_page_image_width != 'none': + carousel_view = request.env['ir.ui.view']._render_template('website_sale.shop_product_images', values={ + 'product': request.env['product.template'].browse(combination['product_template_id']), + 'product_variant': request.env['product.product'].browse(combination['product_id']), + 'website': request.env['website'].get_current_website(), + }) + combination['carousel'] = carousel_view return combination @http.route(auth="public") diff --git a/addons/website_sale/models/website.py b/addons/website_sale/models/website.py index dee0a601cd8..eb62f6023ee 100644 --- a/addons/website_sale/models/website.py +++ b/addons/website_sale/models/website.py @@ -80,6 +80,27 @@ class Website(models.Model): ], default='optional') + product_page_image_layout = fields.Selection([ + ('carousel', 'Carousel'), + ('grid', 'Grid'), + ], default='carousel', required=True, + ) + product_page_grid_columns = fields.Integer(default=2) + product_page_image_width = fields.Selection([ + ('none', 'Hidden'), + ('50_pc', '50 %'), + ('66_pc', '66 %'), + ('100_pc', '100 %'), + ], default='50_pc', required=True, + ) + product_page_image_spacing = fields.Selection([ + ('none', 'None'), + ('small', 'Small'), + ('medium', 'Medium'), + ('big', 'Big'), + ], default='small', required=True, + ) + @api.depends('all_pricelist_ids') def _compute_pricelist_ids(self): for website in self: @@ -462,6 +483,33 @@ class Website(models.Model): result.append(self.env['product.template']._search_get_detail(self, order, options)) return result + def _get_product_page_proportions(self): + """ + Returns the number of columns (css) that both the images and the product details should take. + """ + self.ensure_one() + + return { + 'none': (0, 12), + '50_pc': (6, 6), + '66_pc': (8, 4), + '100_pc': (12, 12), + }.get(self.product_page_image_width) + + def _get_product_page_grid_image_classes(self): + spacing_map = { + 'none': 'p-0', + 'small': 'p-2', + 'medium': 'p-3', + 'big': 'p-4', + } + columns_map = { + 1: 'col-12', + 2: 'col-6', + 3: 'col-4', + } + return spacing_map.get(self.product_page_image_spacing) + ' ' +\ + columns_map.get(self.product_page_grid_columns) class WebsiteSaleExtraField(models.Model): _name = 'website.sale.extra.field' diff --git a/addons/website_sale/static/src/img/snippet_options/image-width-100.svg b/addons/website_sale/static/src/img/snippet_options/image-width-100.svg new file mode 100644 index 00000000000..9dd87e5bdc2 --- /dev/null +++ b/addons/website_sale/static/src/img/snippet_options/image-width-100.svg @@ -0,0 +1,3 @@ + + + diff --git a/addons/website_sale/static/src/img/snippet_options/image-width-50.svg b/addons/website_sale/static/src/img/snippet_options/image-width-50.svg new file mode 100644 index 00000000000..02c063399d9 --- /dev/null +++ b/addons/website_sale/static/src/img/snippet_options/image-width-50.svg @@ -0,0 +1,4 @@ + + + + diff --git a/addons/website_sale/static/src/img/snippet_options/image-width-66.svg b/addons/website_sale/static/src/img/snippet_options/image-width-66.svg new file mode 100644 index 00000000000..cbded7aea88 --- /dev/null +++ b/addons/website_sale/static/src/img/snippet_options/image-width-66.svg @@ -0,0 +1,4 @@ + + + + diff --git a/addons/website_sale/static/src/img/snippet_options/image-width-none.svg b/addons/website_sale/static/src/img/snippet_options/image-width-none.svg new file mode 100644 index 00000000000..a64c740dca2 --- /dev/null +++ b/addons/website_sale/static/src/img/snippet_options/image-width-none.svg @@ -0,0 +1,3 @@ + + + diff --git a/addons/website_sale/static/src/js/website_sale.editor.js b/addons/website_sale/static/src/js/website_sale.editor.js index 74ca08e79cd..e9af1eef78b 100644 --- a/addons/website_sale/static/src/js/website_sale.editor.js +++ b/addons/website_sale/static/src/js/website_sale.editor.js @@ -234,7 +234,7 @@ options.registry.WebsiteSaleGridLayout = options.Class.extend({ return this._rpc({ route: '/shop/config/website', params: { - 'ppg': ppg, + 'shop_ppg': ppg, }, }); }, @@ -246,7 +246,7 @@ options.registry.WebsiteSaleGridLayout = options.Class.extend({ this._rpc({ route: '/shop/config/website', params: { - 'ppr': this.ppr, + 'shop_ppr': this.ppr, }, }); }, @@ -258,7 +258,7 @@ options.registry.WebsiteSaleGridLayout = options.Class.extend({ this._rpc({ route: '/shop/config/website', params: { - 'default_sort': this.default_sort, + 'shop_default_sort': this.default_sort, }, }); }, @@ -643,6 +643,96 @@ options.registry.WebsiteSaleProductsItem = options.Class.extend({ } }); +options.registry.WebsiteSaleProductPage = options.Class.extend({ + + _updateWebsiteConfig(params) { + this._rpc({ + route: '/shop/config/website', + params, + }).then(() => this.trigger_up('request_save', {reload: true, optionSelector: this.data.selector})); + }, + + /** + * @override + */ + setImageWidth(previewMode, widgetValue, params) { + this._updateWebsiteConfig({ + product_page_image_width: widgetValue, + }); + }, + + /** + * @override + */ + setImageLayout(previewMode, widgetValue, params) { + this._updateWebsiteConfig({ + product_page_image_layout: widgetValue, + }); + }, + + /** + * @override + */ + async _computeWidgetState(methodName, params) { + switch (methodName) { + case 'setImageWidth': + return this.$target.data('image_width'); + case 'setImageLayout': + return this.$target.data('image_layout'); + } + return this._super(...arguments); + } +}); + +options.registry.WebsiteSaleProductPageGrid = options.Class.extend({ + /** + * @override + */ + setSpacing(previewMode, widgetValue, params) { + const spacing = { + 0: 'none', + 1: 'small', + 2: 'medium', + 3: 'big', + }[widgetValue]; + this._rpc({ + route: '/shop/config/website', + params: { + 'product_page_image_spacing': spacing, + }, + }).then(() => this.trigger_up('request_save', {reload: true, optionSelector: this.data.selector})); + this.$target.data('image_spacing', spacing); + }, + + setColumns(previewMode, widgetValue, params) { + this._rpc({ + route: '/shop/config/website', + params: { + 'product_page_grid_columns': widgetValue, + }, + }).then(() => this.trigger_up('request_save', {reload: true, optionSelector: this.data.selector})); + this.$target.data('grid_columns', widgetValue); + }, + + /** + * @override + */ + async _computeWidgetState(methodName, params) { + switch (methodName) { + case 'setSpacing': + return { + 'none': 0, + 'small': 1, + 'medium': 2, + 'big': 3, + }[this.$target.data('image_spacing')]; + case 'setColumns': + return this.$target.data('grid_columns'); + } + return this._super(methodName, params); + } +}); + options.registry.WebsiteSaleProductAttribute = options.Class.extend({ /** * @override diff --git a/addons/website_sale/static/src/js/website_sale_utils.js b/addons/website_sale/static/src/js/website_sale_utils.js index af2e96aff98..ba5cdc842c7 100644 --- a/addons/website_sale/static/src/js/website_sale_utils.js +++ b/addons/website_sale/static/src/js/website_sale_utils.js @@ -36,7 +36,10 @@ const cartHandlerMixin = { }).then(async data => { sessionStorage.setItem('website_sale_cart_quantity', data.cart_quantity); if (data.cart_quantity && (data.cart_quantity !== parseInt($(".my_cart_quantity").text()))) { - await animateClone($('header .o_wsale_my_cart').first(), this.$itemImgContainer, 25, 40); + // No animation if the product's page images are hidden + if ($('div[data-image_width]').data('image_width') !== 'none') { + await animateClone($('header .o_wsale_my_cart').first(), this.$itemImgContainer, 25, 40); + } updateCartNavBar(data); } }); diff --git a/addons/website_sale/views/snippets/snippets.xml b/addons/website_sale/views/snippets/snippets.xml index bfc265951d8..483e0a9bb95 100644 --- a/addons/website_sale/views/snippets/snippets.xml +++ b/addons/website_sale/views/snippets/snippets.xml @@ -185,6 +185,18 @@ data-reload="/"/> +
+ + + + + + + + Carousel + Grid + +
None @@ -196,6 +208,14 @@
+
+ + + 1 + 2 + 3 + +
Radio diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml index 383eead9fc0..02c331b18f2 100644 --- a/addons/website_sale/views/templates.xml +++ b/addons/website_sale/views/templates.xml @@ -624,11 +624,14 @@
-
-
- +
+ +
+
-
+

Product Name