228 lines
7.4 KiB
JavaScript
228 lines
7.4 KiB
JavaScript
/** @odoo-module **/
|
|
|
|
import publicWidget from "@web/legacy/js/public/public_widget";
|
|
import { rpc } from "@web/core/network/rpc";
|
|
import { debounce } from "@web/core/utils/timing";
|
|
|
|
publicWidget.registry.websiteSaleAddress = publicWidget.Widget.extend({
|
|
// /shop/address
|
|
selector: '.o_wsale_address_fill',
|
|
events: {
|
|
'change select[name="country_id"]': '_onChangeCountry',
|
|
'click #save_address': '_onSaveAddress',
|
|
"change select[name='state_id']": "_onChangeState",
|
|
},
|
|
|
|
/**
|
|
* @constructor
|
|
*/
|
|
init: function () {
|
|
this._super.apply(this, arguments);
|
|
|
|
this.http = this.bindService('http');
|
|
|
|
this._changeCountry = debounce(this._changeCountry.bind(this), 500);
|
|
this.addressForm = document.querySelector('form.checkout_autoformat');
|
|
this.errorsDiv = document.getElementById('errors');
|
|
this.addressType = this.addressForm['address_type'].value;
|
|
this.countryCode = this.addressForm.dataset.companyCountryCode;
|
|
this.requiredFields = this.addressForm.required_fields.value.split(',');
|
|
},
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
start() {
|
|
const def = this._super(...arguments);
|
|
|
|
this.requiredFields.forEach((fname) => {
|
|
this._markRequired(fname, true);
|
|
})
|
|
this._changeCountry(true);
|
|
|
|
return def;
|
|
},
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Handlers
|
|
//--------------------------------------------------------------------------
|
|
|
|
/**
|
|
* @private
|
|
* @param {Event} ev
|
|
*/
|
|
_onChangeCountry(ev) {
|
|
return this._changeCountry();
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
* @param {Event} ev
|
|
*/
|
|
_onChangeState(ev) {
|
|
return Promise.resolve();
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
async _changeCountry(init=false) {
|
|
const countryId = parseInt(this.addressForm.country_id.value);
|
|
if (!countryId) {
|
|
return;
|
|
}
|
|
|
|
const data = await rpc(
|
|
`/shop/country_info/${parseInt(countryId)}`,
|
|
{address_type: this.addressType},
|
|
);
|
|
|
|
if (data.phone_code !== 0) {
|
|
this.addressForm.phone.placeholder = '+' + data.phone_code;
|
|
} else {
|
|
this.addressForm.phone.placeholder = '';
|
|
}
|
|
|
|
// populate states and display
|
|
var selectStates = this.addressForm.state_id;
|
|
if (!init || selectStates.options.length === 1) {
|
|
// dont reload state at first loading (done in qweb)
|
|
if (data.states.length || data.state_required) {
|
|
// empty existing options, only keep the placeholder.
|
|
selectStates.options.length = 1;
|
|
|
|
// create new options and append them to the select element
|
|
data.states.forEach((state) => {
|
|
let option = new Option(state[1], state[0]);
|
|
// Used by localizations
|
|
option.setAttribute('data-code', state[2]);
|
|
selectStates.appendChild(option);
|
|
});
|
|
this._showInput('state_id');
|
|
} else {
|
|
this._hideInput('state_id');
|
|
}
|
|
}
|
|
|
|
// manage fields order / visibility
|
|
if (data.fields) {
|
|
if (data.zip_before_city) {
|
|
this._getInputDiv('zip').after(this._getInputDiv('city'));
|
|
} else {
|
|
this._getInputDiv('zip').before(this._getInputDiv('city'));
|
|
}
|
|
|
|
var all_fields = ['street', 'zip', 'city'];
|
|
all_fields.forEach((fname) => {
|
|
if (data.fields.includes(fname)) {
|
|
this._showInput(fname);
|
|
} else {
|
|
this._hideInput(fname);
|
|
}
|
|
});
|
|
}
|
|
|
|
const required_fields = this.addressForm.querySelectorAll(':required');
|
|
required_fields.forEach((element) => {
|
|
// remove requirement on previously required fields
|
|
if (
|
|
!data.required_fields.includes(element.name)
|
|
&& !this.requiredFields.includes(element.name)
|
|
) {
|
|
this._markRequired(element.name, false);
|
|
}
|
|
});
|
|
data.required_fields.forEach((fieldName) => {
|
|
this._markRequired(fieldName, true);
|
|
})
|
|
},
|
|
|
|
_getInputDiv(name) {
|
|
return this.addressForm[name].parentElement;
|
|
},
|
|
|
|
_getInputLabel(name) {
|
|
const input = this.addressForm[name];
|
|
return input?.parentElement.querySelector(`label[for='${input.id}']`);
|
|
},
|
|
|
|
_showInput(name) {
|
|
// show parent div, containing label and input
|
|
this.addressForm[name].parentElement.style.display = '';
|
|
},
|
|
|
|
_hideInput(name) {
|
|
// show parent div, containing label and input
|
|
this.addressForm[name].parentElement.style.display = 'none';
|
|
},
|
|
|
|
_markRequired(name, required) {
|
|
const input = this.addressForm[name];
|
|
if (input) {
|
|
input.required = required;
|
|
}
|
|
this._getInputLabel(name)?.classList.toggle('label-optional', !required);
|
|
},
|
|
|
|
/**
|
|
* Disable the button, submit the form and add a spinner while the submission is ongoing
|
|
*
|
|
* @private
|
|
* @param {Event} ev
|
|
*/
|
|
async _onSaveAddress(ev) {
|
|
if (!this.addressForm.reportValidity()) {
|
|
return
|
|
}
|
|
|
|
const submitButton = ev.currentTarget;
|
|
if (!ev.defaultPrevented && !submitButton.disabled) {
|
|
ev.preventDefault();
|
|
|
|
submitButton.disabled = true;
|
|
const spinner = document.createElement('span');
|
|
spinner.classList.add('fa', 'fa-cog', 'fa-spin');
|
|
submitButton.appendChild(spinner);
|
|
|
|
const result = await this.http.post(
|
|
'/shop/address/submit',
|
|
new FormData(this.addressForm),
|
|
)
|
|
if (result.successUrl) {
|
|
window.location = result.successUrl;
|
|
} else {
|
|
// Highlight missing/invalid form values
|
|
document.querySelectorAll('.is-invalid').forEach(element => {
|
|
if (!result.invalid_fields.includes(element.name)) {
|
|
element.classList.remove('is-invalid');
|
|
}
|
|
})
|
|
result.invalid_fields.forEach(
|
|
fieldName => this.addressForm[fieldName].classList.add('is-invalid')
|
|
);
|
|
|
|
// Display the error messages
|
|
// NOTE: setCustomValidity is not used as we would have to reset the error msg on
|
|
// input update, which is not worth catching for the rare cases where the
|
|
// server-side validation will catch validation issues (now that required inputs
|
|
// are also handled client-side)
|
|
const newErrors = result.messages.map(message => {
|
|
const errorHeader = document.createElement('h5');
|
|
errorHeader.classList.add('text-danger');
|
|
errorHeader.appendChild(document.createTextNode(message));
|
|
return errorHeader;
|
|
});
|
|
|
|
this.errorsDiv.replaceChildren(...newErrors);
|
|
|
|
// Re-enable button and remove spinner
|
|
submitButton.disabled = false;
|
|
spinner.remove();
|
|
}
|
|
}
|
|
},
|
|
|
|
});
|
|
|
|
export default publicWidget.registry.websiteSaleAddress;
|