summaryrefslogtreecommitdiff
path: root/addons/website_sale/static/src/snippets
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/website_sale/static/src/snippets
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/website_sale/static/src/snippets')
-rw-r--r--addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.js58
-rw-r--r--addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.xml8
-rw-r--r--addons/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js109
-rw-r--r--addons/website_sale/static/src/snippets/s_products_searchbar/000.js136
4 files changed, 311 insertions, 0 deletions
diff --git a/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.js b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.js
new file mode 100644
index 00000000..2bd13284
--- /dev/null
+++ b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.js
@@ -0,0 +1,58 @@
+odoo.define('website_sale.s_dynamic_snippet_products', function (require) {
+'use strict';
+
+const config = require('web.config');
+const core = require('web.core');
+const publicWidget = require('web.public.widget');
+const DynamicSnippetCarousel = require('website.s_dynamic_snippet_carousel');
+
+const DynamicSnippetProducts = DynamicSnippetCarousel.extend({
+ selector: '.s_dynamic_snippet_products',
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Method to be overridden in child components if additional configuration elements
+ * are required in order to fetch data.
+ * @override
+ * @private
+ */
+ _isConfigComplete: function () {
+ return this._super.apply(this, arguments) && this.$el.get(0).dataset.productCategoryId !== undefined;
+ },
+ /**
+ *
+ * @override
+ * @private
+ */
+ _mustMessageWarningBeHidden: function() {
+ const isInitialDrop = this.$el.get(0).dataset.templateKey === undefined;
+ // This snippet has default values obtained after the initial start and render after drop.
+ // Because of this there is an initial refresh happening right after.
+ // We want to avoid showing the incomplete config message before this refresh.
+ // Since the refreshed call will always happen with a defined templateKey,
+ // if it is not set yet, we know it is the drop call and we can avoid showing the message.
+ return isInitialDrop || this._super.apply(this, arguments);
+ },
+ /**
+ * Method to be overridden in child components in order to provide a search
+ * domain if needed.
+ * @override
+ * @private
+ */
+ _getSearchDomain: function () {
+ const searchDomain = this._super.apply(this, arguments);
+ const productCategoryId = parseInt(this.$el.get(0).dataset.productCategoryId);
+ if (productCategoryId >= 0) {
+ searchDomain.push(['public_categ_ids', 'child_of', productCategoryId]);
+ }
+ return searchDomain;
+ },
+
+});
+publicWidget.registry.dynamic_snippet_products = DynamicSnippetProducts;
+
+return DynamicSnippetProducts;
+});
diff --git a/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.xml b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.xml
new file mode 100644
index 00000000..f45bf3c5
--- /dev/null
+++ b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<templates xml:space="preserve">
+ <t t-name="website_sale.s_dynamic_snippet.products" inherit_id="website.s_dynamic_snippet.carousel">
+ <xpath expr="//div" position="attributes">
+ <attribute name="data-filter-id" add=""/>
+ </xpath>
+ </t>
+</templates>
diff --git a/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js
new file mode 100644
index 00000000..3dabb014
--- /dev/null
+++ b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js
@@ -0,0 +1,109 @@
+odoo.define('website_sale.s_dynamic_snippet_products_options', function (require) {
+'use strict';
+
+const options = require('web_editor.snippets.options');
+const s_dynamic_snippet_carousel_options = require('website.s_dynamic_snippet_carousel_options');
+
+const dynamicSnippetProductsOptions = s_dynamic_snippet_carousel_options.extend({
+
+ /**
+ *
+ * @override
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+ this.productCategories = {};
+ },
+ /**
+ *
+ * @override
+ */
+ onBuilt: function () {
+ this._super.apply(this, arguments);
+ this._rpc({
+ route: '/website_sale/snippet/options_filters'
+ }).then((data) => {
+ if (data.length) {
+ this.$target.get(0).dataset.filterId = data[0].id;
+ this.$target.get(0).dataset.numberOfRecords = this.dynamicFilters[data[0].id].limit;
+ this._refreshPublicWidgets();
+ // Refresh is needed because default values are obtained after start()
+ }
+ });
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ *
+ * @override
+ * @private
+ */
+ _computeWidgetVisibility: function (widgetName, params) {
+ if (widgetName === 'filter_opt') {
+ return false;
+ }
+ return this._super.apply(this, arguments);
+ },
+ /**
+ * Fetches product categories.
+ * @private
+ * @returns {Promise}
+ */
+ _fetchProductCategories: function () {
+ return this._rpc({
+ model: 'product.public.category',
+ method: 'search_read',
+ kwargs: {
+ domain: [],
+ fields: ['id', 'name'],
+ }
+ });
+ },
+ /**
+ *
+ * @override
+ * @private
+ */
+ _renderCustomXML: async function (uiFragment) {
+ await this._super.apply(this, arguments);
+ await this._renderProductCategorySelector(uiFragment);
+ },
+ /**
+ * Renders the product categories option selector content into the provided uiFragment.
+ * @private
+ * @param {HTMLElement} uiFragment
+ */
+ _renderProductCategorySelector: async function (uiFragment) {
+ const productCategories = await this._fetchProductCategories();
+ for (let index in productCategories) {
+ this.productCategories[productCategories[index].id] = productCategories[index];
+ }
+ const productCategoriesSelectorEl = uiFragment.querySelector('[data-name="product_category_opt"]');
+ return this._renderSelectUserValueWidgetButtons(productCategoriesSelectorEl, this.productCategories);
+ },
+ /**
+ * Sets default options values.
+ * @override
+ * @private
+ */
+ _setOptionsDefaultValues: function () {
+ this._super.apply(this, arguments);
+ const templateKeys = this.$el.find("we-select[data-attribute-name='templateKey'] we-selection-items we-button");
+ if (templateKeys.length > 0) {
+ this._setOptionValue('templateKey', templateKeys.attr('data-select-data-attribute'));
+ }
+ const productCategories = this.$el.find("we-select[data-attribute-name='productCategoryId'] we-selection-items we-button");
+ if (productCategories.length > 0) {
+ this._setOptionValue('productCategoryId', productCategories.attr('data-select-data-attribute'));
+ }
+ },
+
+});
+
+options.registry.dynamic_snippet_products = dynamicSnippetProductsOptions;
+
+return dynamicSnippetProductsOptions;
+});
diff --git a/addons/website_sale/static/src/snippets/s_products_searchbar/000.js b/addons/website_sale/static/src/snippets/s_products_searchbar/000.js
new file mode 100644
index 00000000..2aaafca2
--- /dev/null
+++ b/addons/website_sale/static/src/snippets/s_products_searchbar/000.js
@@ -0,0 +1,136 @@
+odoo.define('website_sale.s_products_searchbar', function (require) {
+'use strict';
+
+const concurrency = require('web.concurrency');
+const publicWidget = require('web.public.widget');
+
+const { qweb } = require('web.core');
+
+/**
+ * @todo maybe the custom autocomplete logic could be extract to be reusable
+ */
+publicWidget.registry.productsSearchBar = publicWidget.Widget.extend({
+ selector: '.o_wsale_products_searchbar_form',
+ xmlDependencies: ['/website_sale/static/src/xml/website_sale_utils.xml'],
+ events: {
+ 'input .search-query': '_onInput',
+ 'focusout': '_onFocusOut',
+ 'keydown .search-query': '_onKeydown',
+ },
+ autocompleteMinWidth: 300,
+
+ /**
+ * @constructor
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+
+ this._dp = new concurrency.DropPrevious();
+
+ this._onInput = _.debounce(this._onInput, 400);
+ this._onFocusOut = _.debounce(this._onFocusOut, 100);
+ },
+ /**
+ * @override
+ */
+ start: function () {
+ this.$input = this.$('.search-query');
+
+ this.order = this.$('.o_wsale_search_order_by').val();
+ this.limit = parseInt(this.$input.data('limit'));
+ this.displayDescription = !!this.$input.data('displayDescription');
+ this.displayPrice = !!this.$input.data('displayPrice');
+ this.displayImage = !!this.$input.data('displayImage');
+
+ if (this.limit) {
+ this.$input.attr('autocomplete', 'off');
+ }
+
+ return this._super.apply(this, arguments);
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ */
+ _fetch: function () {
+ return this._rpc({
+ route: '/shop/products/autocomplete',
+ params: {
+ 'term': this.$input.val(),
+ 'options': {
+ 'order': this.order,
+ 'limit': this.limit,
+ 'display_description': this.displayDescription,
+ 'display_price': this.displayPrice,
+ 'max_nb_chars': Math.round(Math.max(this.autocompleteMinWidth, parseInt(this.$el.width())) * 0.22),
+ },
+ },
+ });
+ },
+ /**
+ * @private
+ */
+ _render: function (res) {
+ var $prevMenu = this.$menu;
+ this.$el.toggleClass('dropdown show', !!res);
+ if (res) {
+ var products = res['products'];
+ this.$menu = $(qweb.render('website_sale.productsSearchBar.autocomplete', {
+ products: products,
+ hasMoreProducts: products.length < res['products_count'],
+ currency: res['currency'],
+ widget: this,
+ }));
+ this.$menu.css('min-width', this.autocompleteMinWidth);
+ this.$el.append(this.$menu);
+ }
+ if ($prevMenu) {
+ $prevMenu.remove();
+ }
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ */
+ _onInput: function () {
+ if (!this.limit) {
+ return;
+ }
+ this._dp.add(this._fetch()).then(this._render.bind(this));
+ },
+ /**
+ * @private
+ */
+ _onFocusOut: function () {
+ if (!this.$el.has(document.activeElement).length) {
+ this._render();
+ }
+ },
+ /**
+ * @private
+ */
+ _onKeydown: function (ev) {
+ switch (ev.which) {
+ case $.ui.keyCode.ESCAPE:
+ this._render();
+ break;
+ case $.ui.keyCode.UP:
+ case $.ui.keyCode.DOWN:
+ ev.preventDefault();
+ if (this.$menu) {
+ let $element = ev.which === $.ui.keyCode.UP ? this.$menu.children().last() : this.$menu.children().first();
+ $element.focus();
+ }
+ break;
+ }
+ },
+});
+});