diff --git a/addons/website/models/website.py b/addons/website/models/website.py index f2cb46a9238..4688ac5ee75 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -386,6 +386,16 @@ class Website(models.Model): website = self.get_current_website() website.configurator_done = True + @api.model + def configurator_missing_industry(self, unknown_industry): + self._website_api_rpc( + '/api/website/unknown_industry', + { + 'unknown_industry': unknown_industry, + 'lang': self.env.context.get('lang'), + } + ) + @api.model def configurator_apply(self, **kwargs): def set_colors(selected_palette): diff --git a/addons/website/static/src/client_actions/configurator/configurator.js b/addons/website/static/src/client_actions/configurator/configurator.js index 7d0858f1780..c62521e5aae 100644 --- a/addons/website/static/src/client_actions/configurator/configurator.js +++ b/addons/website/static/src/client_actions/configurator/configurator.js @@ -4,7 +4,7 @@ import concurrency from 'web.concurrency'; import utils from 'web.utils'; import weUtils from 'web_editor.utils'; import {ColorpickerWidget} from 'web.Colorpicker'; -import {_t, _lt} from 'web.core'; +import {_lt} from 'web.core'; import {svgToPNG} from 'website.utils'; import { useService } from "@web/core/utils/hooks"; import { registry } from "@web/core/registry"; @@ -89,6 +89,7 @@ class DescriptionScreen extends Component { this.state = useStore(); this.labelToId = {}; this.autocompleteHasResults = true; + this.orm = useService('orm'); onMounted(() => this.onMounted()); } @@ -101,8 +102,6 @@ class DescriptionScreen extends Component { minLength: 1, source: this._autocompleteSearch.bind(this), select: this._selectIndustry.bind(this), - open: this._customizeNoResultMenuStyle.bind(this), - focus: this._disableKeyboardNav.bind(this), classes: { 'ui-autocomplete': 'custom-ui-autocomplete shadow-lg border-0 o_configurator_show_fast o_configurator_industry_dropdown', }, @@ -114,17 +113,6 @@ class DescriptionScreen extends Component { } } - /** - * Clear the input and its parent label and set the selected industry to undefined. - * - * @private - */ - _clearIndustrySelection() { - this.industrySelection.el.value = ''; - this.industrySelection.el.parentNode.dataset.value = ''; - this.state.selectIndustry(); - } - /** * Set the input's parent label value to automatically adapt input size * and update the selected industry. @@ -134,37 +122,17 @@ class DescriptionScreen extends Component { */ _setSelectedIndustry(label) { this.industrySelection.el.parentNode.dataset.value = label; - const id = this.labelToId[label]; - this.state.selectIndustry(label, id); + if (!this.autocompleteHasResults) { + // If no industry has been found, the first one is chosen by + // default to continue the website configuration. + this.state.selectIndustry(label, 1); + } else { + const id = this.labelToId[label]; + this.state.selectIndustry(label, id); + } this.checkDescriptionCompletion(); } - /** - * Called each time the suggestion menu is opened or updated. If there are no - * results to display the style of the "No result found" message is customized. - * - * @private - */ - _customizeNoResultMenuStyle() { - if (!this.autocompleteHasResults) { - const noResultLinkEl = this.industrySelection.el.parentElement.getElementsByTagName('a')[0]; - noResultLinkEl.classList.add('o_no_result'); - } - } - - /** - * Disables keyboard navigation when there are no results to avoid selecting the - * "No result found" message by pressing the down arrow key. - * - * @private - * @param {Event} ev - */ - _disableKeyboardNav(ev) { - if (!this.autocompleteHasResults) { - ev.preventDefault(); - } - } - /** * Called each time the autocomplete input's value changes. Only industries * having a label or a synonym containing all terms of the input value are @@ -172,7 +140,7 @@ class DescriptionScreen extends Component { * The order received from IAP is kept (expected to be on descending hit * count) unless there are 7 or less matches in which case the results are * sorted alphabetically. - * The result size is limited to 15. + * The result size is limited to 30. * * @param {Object} request object with a single 'term' property which is the * input current value @@ -181,7 +149,7 @@ class DescriptionScreen extends Component { */ _autocompleteSearch(request, response) { const terms = request.term.toLowerCase().split(/[|,\n]+/); - const limit = 15; + const limit = 30; const sortLimit = 7; // `this.state.industries` is already sorted by hit count (from IAP). // That order should be kept after manipulating the recordset. @@ -196,7 +164,7 @@ class DescriptionScreen extends Component { }); if (matches.length > limit) { // Keep matches with the least number of words so that e.g. - // "restaurant" remains available even if there are 15 specific + // "restaurant" remains available even if there are 30 specific // sub-types that have a higher hit count. matches = matches.sort((x, y) => x.wordCount - y.wordCount) .slice(0, limit) @@ -215,14 +183,13 @@ class DescriptionScreen extends Component { this.labelToId[r.label] = r.id; }); } else { - labels = [_t("No result found, broaden your search.")]; + labels = [request.term]; } response(labels); } /** - * Called when a menu option is selected. Update the selected industry or - * clear the input if the option is the "No result found" message. + * Called when a menu option is selected. Update the selected industry. * * @private * @param {Event} ev @@ -230,27 +197,19 @@ class DescriptionScreen extends Component { * the selected option. */ _selectIndustry(ev, ui) { - if (this.autocompleteHasResults) { - this._setSelectedIndustry(ui.item.label); - } else { - this._clearIndustrySelection(); - ev.preventDefault(); - } + this._setSelectedIndustry(ui.item.label); } /** - * Called on industrySelection input blur. Updates the selected industry or - * clears the input if its current value is not a valid industry. + * Called on industrySelection input blur. Updates the selected industry. * * @private * @param {Event} ev */ _blurIndustrySelection(ev) { - if (this.labelToId[ev.target.value] !== undefined) { - this._setSelectedIndustry(ev.target.value); - } else { - this._clearIndustrySelection(); - } + // Check if the input is known by the server. + this.autocompleteHasResults = !!this.labelToId[ev.target.value]; + this._setSelectedIndustry(ev.target.value); } selectWebsiteType(id) { @@ -269,6 +228,13 @@ class DescriptionScreen extends Component { checkDescriptionCompletion() { const {selectedType, selectedPurpose, selectedIndustry} = this.state; if (selectedType && selectedPurpose && selectedIndustry) { + // If the industry name is not known by the server, send it to the + // IAP server. + if (!this.autocompleteHasResults) { + this.orm.call('website', 'configurator_missing_industry', [], { + 'unknown_industry': selectedIndustry.label, + }); + } this.props.navigate(ROUTES.paletteSelectionScreen); } } @@ -451,13 +417,35 @@ class ThemeSelectionScreen extends ApplyConfiguratorScreen { setup() { super.setup(); + this.uiService = useService('ui'); this.orm = useService('orm'); this.state = useStore(); this.themeSVGPreviews = [useRef('ThemePreview1'), useRef('ThemePreview2'), useRef('ThemePreview3')]; + const proms = []; - onMounted(() => { + onMounted(async () => { + // Add a loading effect during the loading of the images inside the + // svgs. + this.uiService.block(); this.state.themes.forEach((theme, idx) => { - $(this.themeSVGPreviews[idx].el).append(theme.svg); + // Transform the text svg into a svg element. + const svgEl = new DOMParser().parseFromString(theme.svg, 'image/svg+xml').documentElement; + for (const imgEl of svgEl.querySelectorAll('image')) { + proms.push(new Promise((resolve, reject) => { + imgEl.addEventListener('load', () => { + resolve(imgEl); + }, {once: true}); + imgEl.addEventListener('error', () => { + reject(imgEl); + }, {once: true}); + })); + } + $(this.themeSVGPreviews[idx].el).append(svgEl); + }); + // When all the images inside the svgs are loaded then remove the + // loading effect. + Promise.allSettled(proms).then(() => { + this.uiService.unblock(); }); }); } diff --git a/addons/website/static/src/client_actions/configurator/configurator.scss b/addons/website/static/src/client_actions/configurator/configurator.scss index 4b73ac8c0c6..0837d35acf5 100644 --- a/addons/website/static/src/client_actions/configurator/configurator.scss +++ b/addons/website/static/src/client_actions/configurator/configurator.scss @@ -171,7 +171,7 @@ } .dropdown-item, .fa-angle-down, .ui-menu-item a, .ui-menu-item a.ui-state-active { - font-size: .65em; + font-size: .5em; font-weight: inherit; } diff --git a/addons/website/static/src/client_actions/configurator/configurator.xml b/addons/website/static/src/client_actions/configurator/configurator.xml index fa34fd78239..8ef806f8d58 100644 --- a/addons/website/static/src/client_actions/configurator/configurator.xml +++ b/addons/website/static/src/client_actions/configurator/configurator.xml @@ -3,7 +3,7 @@
- +
@@ -12,10 +12,10 @@