diff --git a/addons/website_event_track/static/src/js/website_event_track_proposal_form.js b/addons/website_event_track/static/src/js/website_event_track_proposal_form.js index 808517b3682..f104dafeb24 100644 --- a/addons/website_event_track/static/src/js/website_event_track_proposal_form.js +++ b/addons/website_event_track/static/src/js/website_event_track_proposal_form.js @@ -7,161 +7,160 @@ var publicWidget = require('web.public.widget'); var _t = core._t; publicWidget.registry.websiteEventTrackProposalForm = publicWidget.Widget.extend({ + selector: '.o_website_event_track_proposal_form', + events: { + 'click .o_wetrack_add_contact_information_checkbox': '_onAdvancedContactToggle', + 'input input[name="partner_name"]': '_onPartnerNameInput', + 'click .o_wetrack_proposal_submit_button': '_onProposalFormSubmit', + }, -selector: '.o_website_event_track_proposal_form', -events: { - 'click .o_wetrack_add_contact_information_checkbox': '_onAdvancedContactToggle', - 'input input[name="partner_name"]': '_onPartnerNameInput', - 'click .o_wetrack_proposal_submit_button': '_onProposalFormSubmit', -}, + /** + * @override + */ + init: function () { + this._super(...arguments); + this.useAdvancedContact = false; + }, -/** - * @override - */ -init: function () { - this._super(...arguments); - this.useAdvancedContact = false; -}, + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- -//-------------------------------------------------------------------------- -// Private -//-------------------------------------------------------------------------- + /** + * Evaluate and return validity of form input fields: + * - 1) error 'invalidFormInputs' : Invalid ones are marked as is-invalid and o_wetrack_input_error. + * - 2) error 'noContactMean' : Contact mean fields are marked as is-invalid and contact + * section as o_wetrack_no_contact_mean_error if none of them is filled. + * + * @private + * @returns {Boolean} - True if no error remain, false otherwise + */ + _isFormValid: function () { + var formErrors = []; -/** - * Evaluate and return validity of form input fields: - * - 1) error 'invalidFormInputs' : Invalid ones are marked as is-invalid and o_wetrack_input_error. - * - 2) error 'noContactMean' : Contact mean fields are marked as is-invalid and contact - * section as o_wetrack_no_contact_mean_error if none of them is filled. - * - * @private - * @returns {Boolean} - True if no error remain, false otherwise - */ -_isFormValid: function () { - var formErrors = []; + // 1) Valid Form Inputs + this.$('.form-group').each(function (index, field) { + var $field = $(field); + // Validate current input, if not select2 field. + var inputs = $field.find('.form-control').not('.o_wetrack_select2_tags'); + var invalidInputs = inputs.toArray().filter(function (input) { + return !input.checkValidity(); + }); - // 1) Valid Form Inputs - this.$('.form-group').each(function (index, field) { - var $field = $(field); - // Validate current input, if not select2 field. - var inputs = $field.find('.form-control').not('.o_wetrack_select2_tags'); - var invalidInputs = inputs.toArray().filter(function (input) { - return !input.checkValidity(); + $field.find('.form-control').removeClass('o_wetrack_input_error is-invalid'); + if (invalidInputs.length) { + $field.find('.form-control').addClass('o_wetrack_input_error is-invalid'); + formErrors.push('invalidFormInputs'); + } }); - - $field.find('.form-control').removeClass('o_wetrack_input_error is-invalid'); - if (invalidInputs.length) { - $field.find('.form-control').addClass('o_wetrack_input_error is-invalid'); - formErrors.push('invalidFormInputs'); - } - }); - // 2) Advanced Contact Must Have a Contact Mean - if (this.useAdvancedContact) { - var hasContactMean = this.$('.o_wetrack_contact_phone_input').val() || - this.$('.o_wetrack_contact_email_input').val(); - if (!hasContactMean) { - this.$('.o_wetrack_contact_information').addClass('o_wetrack_no_contact_mean_error'); - this.$('.o_wetrack_contact_mean').addClass('is-invalid'); - formErrors.push('noContactMean'); + // 2) Advanced Contact Must Have a Contact Mean + if (this.useAdvancedContact) { + var hasContactMean = this.$('.o_wetrack_contact_phone_input').val() || + this.$('.o_wetrack_contact_email_input').val(); + if (!hasContactMean) { + this.$('.o_wetrack_contact_information').addClass('o_wetrack_no_contact_mean_error'); + this.$('.o_wetrack_contact_mean').addClass('is-invalid'); + formErrors.push('noContactMean'); + } else { + this.$('.o_wetrack_contact_information').removeClass('o_wetrack_no_contact_mean_error'); + this.$('.o_wetrack_contact_mean:not(".o_wetrack_input_error")').removeClass('is-invalid'); + } + } + + // Form Validity and Error Display + this._updateErrorDisplay(formErrors); + return formErrors.length === 0; + }, + + /** + * If there are still errors in form, display the error section and + * compose the error message accordingly. + * + * @private + * @param {Array} errors - Names of errors still present in form. + */ + _updateErrorDisplay: function (errors) { + + this.$('.o_wetrack_proposal_error_section').toggleClass('d-none', !errors.length); + + var errorMessage = ''; + var $errorElement = this.$('.o_wetrack_proposal_error_message'); + + if (errors.includes('invalidFormInputs')) { errorMessage += _t(' Please fill out the form correctly.'); } + if (errors.includes('noContactMean')) { errorMessage += _t(' Please enter either a contact email address or a contact phone number.'); } + + $errorElement.text(errorMessage).change(); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * Display / Hide Additional Contact Information section when toggling + * the checkbox on the form o_wetrack_add_contact_information_checkbox. + * Also empty the email to prevent hidden email format error. + * + * @private + * @param {Event} ev + */ + _onAdvancedContactToggle: function (ev) { + this.useAdvancedContact = !this.useAdvancedContact; + var $contactName = this.$(".o_wetrack_contact_name_input")[0]; + var $advancedInformation = this.$('.o_wetrack_contact_information'); + + if (this.useAdvancedContact) { + $advancedInformation.removeClass('d-none'); + $contactName.setAttribute("required", "True"); } else { - this.$('.o_wetrack_contact_information').removeClass('o_wetrack_no_contact_mean_error'); - this.$('.o_wetrack_contact_mean:not(".o_wetrack_input_error")').removeClass('is-invalid'); + this.$('.o_wetrack_contact_email_input').val('').change(); + $advancedInformation.addClass('d-none'); + $contactName.removeAttribute("required"); } - } + }, - // Form Validity and Error Display - this._updateErrorDisplay(formErrors); - return formErrors.length === 0; -}, + /** + * Propagates the new input on speaker name to contact name, as long as the latter + * is the start of partner name. Otherwise, do not modify existing contact name. + * + * @private + * @param {Event} ev + */ + _onPartnerNameInput: function (ev) { + var partnerNameText = $(ev.currentTarget).val(); + var contactNameText = this.$(".o_wetrack_contact_name_input").val(); + if (partnerNameText.startsWith(contactNameText)) { + this.$(".o_wetrack_contact_name_input").val(partnerNameText).change(); + } + }, -/** - * If there are still errors in form, display the error section and - * compose the error message accordingly. - * - * @private - * @param {Array} errors - Names of errors still present in form. - */ -_updateErrorDisplay: function(errors) { + /** + * Submits the form if no errors are present in the form after validation. + * + * @private + * @param {Event} ev + */ + _onProposalFormSubmit: function (ev) { + ev.preventDefault(); + ev.stopPropagation(); - this.$('.o_wetrack_proposal_error_section').toggleClass('d-none', !errors.length); + // Prevent further clicking + this.$target.find('.o_wetrack_proposal_submit_button') + .addClass('disabled') + .attr('disabled', 'disabled'); - var errorMessage = ''; - var $errorElement = this.$('.o_wetrack_proposal_error_message'); + // Submission of the form if no errors remain + if (this._isFormValid()) { + this.$el.submit(); + this.$target[0].reset(); + } - if (errors.includes('invalidFormInputs')) { errorMessage += _t(' Please fill out the form correctly.'); } - if (errors.includes('noContactMean')) { errorMessage += _t(' Please enter either a contact email address or a contact phone number.'); } - - $errorElement.text(errorMessage).change(); -}, - -//-------------------------------------------------------------------------- -// Handlers -//-------------------------------------------------------------------------- - -/** - * Display / Hide Additional Contact Information section when toggling - * the checkbox on the form o_wetrack_add_contact_information_checkbox. - * Also empty the email to prevent hidden email format error. - * - * @private - * @param {Event} ev - */ -_onAdvancedContactToggle: function(ev) { - this.useAdvancedContact = !this.useAdvancedContact; - var $contactName = this.$(".o_wetrack_contact_name_input")[0]; - var $advancedInformation = this.$('.o_wetrack_contact_information'); - - if (this.useAdvancedContact) { - $advancedInformation.removeClass('d-none'); - $contactName.setAttribute("required", "True"); - } else { - this.$('.o_wetrack_contact_email_input').val('').change(); - $advancedInformation.addClass('d-none'); - $contactName.removeAttribute("required"); - } -}, - -/** - * Propagates the new input on speaker name to contact name, as long as the latter - * is the start of partner name. Otherwise, do not modify existing contact name. - * - * @private - * @param {Event} ev - */ -_onPartnerNameInput: function(ev) { - var partnerNameText = $(ev.currentTarget).val(); - var contactNameText = this.$(".o_wetrack_contact_name_input").val(); - if (partnerNameText.startsWith(contactNameText)) { - this.$(".o_wetrack_contact_name_input").val(partnerNameText).change(); - } -}, - -/** - * Submits the form if no errors are present in the form after validation. - * - * @private - * @param {Event} ev - */ -_onProposalFormSubmit: function(ev) { - ev.preventDefault(); - ev.stopPropagation(); - - // Prevent further clicking - this.$target.find('.o_wetrack_proposal_submit_button') - .addClass('disabled') - .attr('disabled', 'disabled'); - - // Submission of the form if no errors remain - if (this._isFormValid()) { - this.$el.submit(); - this.$target[0].reset(); - } - - // Restore button - this.$target.find('.o_wetrack_proposal_submit_button') - .removeAttr('disabled') - .removeClass('disabled'); -}, + // Restore button + this.$target.find('.o_wetrack_proposal_submit_button') + .removeAttr('disabled') + .removeClass('disabled'); + }, }); return publicWidget.registry.websiteEventTrackProposalForm; diff --git a/addons/website_event_track/static/src/js/website_event_track_proposal_form_tags.js b/addons/website_event_track/static/src/js/website_event_track_proposal_form_tags.js index fa3dc9267c3..fbfe0d123cb 100644 --- a/addons/website_event_track/static/src/js/website_event_track_proposal_form_tags.js +++ b/addons/website_event_track/static/src/js/website_event_track_proposal_form_tags.js @@ -7,93 +7,92 @@ var publicWidget = require('web.public.widget'); var _t = core._t; publicWidget.registry.websiteEventTrackProposalFormTags = publicWidget.Widget.extend({ + selector: '.o_website_event_track_proposal_form_tags', -selector: '.o_website_event_track_proposal_form_tags', + start: function () { + var self = this; + return this._super.apply(this, arguments).then(function () { + self._bindSelect2Dropdown(); + }); + }, -start: function () { - var self = this; - return this._super.apply(this, arguments).then(function () { - self._bindSelect2Dropdown(); - }); -}, - -/** - * Handler for select2 on tags added to the proposal track form. - * - * @private - */ -_bindSelect2Dropdown: function () { - var self = this; - this.$('.o_wetrack_select2_tags').select2(this._select2Wrapper(_t('Select categories'), - function () { - return self._rpc({ - route: "/event/track_tag/search_read", - params: { - fields: ['name', 'category_id'], - domain: [], - } - }); - }) - ); -}, - -/** - * Wrapper for select2. Load data from server once and store it. - * Tags are sorted in alphabetical order and have format "tag.category.name : tag.name" - * Or "tag.name" if tag does not belong to any category. - * - * @private - * @param {String} tag - Placeholder for element. - * @param {Function} fetchFNC - Fetch data from remote location. Should return a Promise. - * Resolved data should be array of objects with id and name. eg. [{'id': id, 'name': 'text'}, ...] - * @param {String} nameKey - (optional) the name key of the returned record - * ('name' if not provided) - * @returns {Object} select2 wrapper object -*/ -_select2Wrapper: function (tag, fetchFNC, nameKey) { - nameKey = nameKey || 'name'; - - var values = { - placeholder: tag, - allowClear: true, - formatNoMatches: _t('No results found'), - selection_data: false, - fetch_rpc_fnc: fetchFNC, - multiple: 'multiple', - sorter: data => data.sort((a, b) => a.text.localeCompare(b.text)), - - // category_id structure : [id, tag category name] - fill_data: function (query, data) { - var that = this, - tags = {results: []}; - _.each(data, function (obj) { - // select tags matching either category or tag name - if (that.matcher(query.term, obj[nameKey]) || that.matcher(query.term, obj.category_id[1])) { - if (obj.category_id[1]) { - tags.results.push({id: obj.id, text: obj.category_id[1] + " : " + obj[nameKey]}); - } else { - tags.results.push({id: obj.id, text: obj[nameKey]}); + /** + * Handler for select2 on tags added to the proposal track form. + * + * @private + */ + _bindSelect2Dropdown: function () { + var self = this; + this.$('.o_wetrack_select2_tags').select2(this._select2Wrapper(_t('Select categories'), + function () { + return self._rpc({ + route: "/event/track_tag/search_read", + params: { + fields: ['name', 'category_id'], + domain: [], } - } - }); - query.callback(tags); - }, - - query: function (query) { - var that = this; - // fetch data only once and store it - if (!this.selection_data) { - this.fetch_rpc_fnc().then(function (data) { - that.fill_data(query, data); - that.selection_data = data; }); - } else { - this.fill_data(query, this.selection_data); + }) + ); + }, + + /** + * Wrapper for select2. Load data from server once and store it. + * Tags are sorted in alphabetical order and have format "tag.category.name : tag.name" + * Or "tag.name" if tag does not belong to any category. + * + * @private + * @param {String} tag - Placeholder for element. + * @param {Function} fetchFNC - Fetch data from remote location. Should return a Promise. + * Resolved data should be array of objects with id and name. eg. [{'id': id, 'name': 'text'}, ...] + * @param {String} nameKey - (optional) the name key of the returned record + * ('name' if not provided) + * @returns {Object} select2 wrapper object + */ + _select2Wrapper: function (tag, fetchFNC, nameKey) { + nameKey = nameKey || 'name'; + + var values = { + placeholder: tag, + allowClear: true, + formatNoMatches: _t('No results found'), + selection_data: false, + fetch_rpc_fnc: fetchFNC, + multiple: 'multiple', + sorter: data => data.sort((a, b) => a.text.localeCompare(b.text)), + + // category_id structure : [id, tag category name] + fill_data: function (query, data) { + var that = this, + tags = {results: []}; + _.each(data, function (obj) { + // select tags matching either category or tag name + if (that.matcher(query.term, obj[nameKey]) || that.matcher(query.term, obj.category_id[1])) { + if (obj.category_id[1]) { + tags.results.push({id: obj.id, text: obj.category_id[1] + " : " + obj[nameKey]}); + } else { + tags.results.push({id: obj.id, text: obj[nameKey]}); + } + } + }); + query.callback(tags); + }, + + query: function (query) { + var that = this; + // fetch data only once and store it + if (!this.selection_data) { + this.fetch_rpc_fnc().then(function (data) { + that.fill_data(query, data); + that.selection_data = data; + }); + } else { + this.fill_data(query, this.selection_data); + } } - } - }; - return values; -}, + }; + return values; + }, }); return publicWidget.registry.websiteEventTrackProposalFormTags;