diff --git a/frontend/static/script/js/core/record-workspace.js b/frontend/static/script/js/core/record-workspace.js index 926b4eca1..d56d3764b 100644 --- a/frontend/static/script/js/core/record-workspace.js +++ b/frontend/static/script/js/core/record-workspace.js @@ -74,7 +74,10 @@ }; nav.appendChild(button); form.appendChild(panel); return panel; }); + var activeIndex = 0; + var restoreTimer; function show(index) { + activeIndex = index; panels.forEach(function (p, i) { p.hidden = i !== index; nav.children[i].setAttribute('aria-selected', String(i === index)); }); } children.forEach(function (child) { @@ -85,6 +88,8 @@ var actions = form.querySelector('.record-actions'); if (actions) form.appendChild(actions); function validateAll() { + clearTimeout(restoreTimer); + restoreTimer = setTimeout(function () { show(activeIndex); }, 0); panels.forEach(function (p) { p.hidden = false; }); form.querySelectorAll('details.record-details').forEach(function (p) { p.open = true; }); } @@ -93,6 +98,8 @@ form.addEventListener('keydown', function (event) { if (event.key === 'Enter' && event.target.tagName !== 'TEXTAREA' && event.target.tagName !== 'BUTTON') validateAll(); }, true); form.addEventListener('submit', validateAll, true); function reveal(input) { + var disclosure = input.closest('details'); + if (disclosure) disclosure.open = true; var index = panels.findIndex(function (p) { return p.contains(input); }); if (index >= 0) show(index); } diff --git a/frontend/static/script/js/modules/js/branches.js b/frontend/static/script/js/modules/js/branches.js index cfbb5a24b..a184d5431 100644 --- a/frontend/static/script/js/modules/js/branches.js +++ b/frontend/static/script/js/modules/js/branches.js @@ -87,32 +87,38 @@ PosnicPro.branches = { PosnicPro.branches.loadFullEdit(id); }, loadFullEdit: function (id) { + var request = (this.editRequest || 0) + 1; this.editRequest = request; + var save = $('#setting_add button[type=submit]').prop('disabled', true); + $('#setting_add').validate().resetForm(); + PosnicPro.settings.stateRequest = (PosnicPro.settings.stateRequest || 0) + 1; PosnicPro.branchSignature.mount('#branch-signature-settings', { branchId: id }); // Option lists first (idempotent - they repopulate by id wherever the // controls live), then this branch's values over them. - PosnicPro.settings.loadSelectSettingCountry(); - PosnicPro.settings.loadSelectSettingCurrency(); - PosnicPro.settings.timeZone(); - PosnicPro.get({ url: 'branches/getBranchDetails', data: { id: id } }, function (response) { - if (response.type !== 'success') { PosnicPro.alert(response.type, response.message); return; } - var d = response.data; - PosnicPro.record_id = id; - $('#edit_branch_target,#be_logo_target').val(id); - $('#be_page_branch_name').text(d.branch_name || ''); - $('#store_name').val(d.branch_name); - $('#store_email').val(d.store_email); - PosnicPro.settings.store_telephone.setNumber(d.store_telephone || ''); - $('#store_alternativephone').val(d.store_alternativephone); - $('#store_address').val(d.store_address); - $('#printing_address').val(d.printing_address); - $('#city').val(d.city); - $('#pincode').val(d.pincode); - $('#website').val(d.website); - $('#branch_gstin_number').val(d.branch_gstin_number); - // Selects fill after their (async) option lists land. - setTimeout(function () { + $.when(PosnicPro.settings.loadSelectSettingCountry(), + PosnicPro.settings.loadSelectSettingCurrency(), PosnicPro.settings.timeZone()).done(function () { + if (PosnicPro.branches.editRequest !== request) return; + PosnicPro.get({ url: 'branches/getBranchDetails', data: { id: id } }, function (response) { + if (PosnicPro.branches.editRequest !== request) return; + if (response.type !== 'success') { PosnicPro.alert(response.type, response.message); return; } + var d = response.data; + PosnicPro.record_id = id; + $('#edit_branch_target,#be_logo_target').val(id); + $('#be_page_branch_name').text(d.branch_name || ''); + $('#store_name').val(d.branch_name); + $('#store_email').val(d.store_email); + $('#branch_upi_id').val(d.branch_upi_id || ''); + $('#branch_upi_name').val(d.branch_upi_name || ''); + PosnicPro.settings.store_telephone.setNumber(d.store_telephone || ''); + $('#store_alternativephone').val(d.store_alternativephone); + $('#store_address').val(d.store_address); + $('#printing_address').val(d.printing_address); + $('#city').val(d.city); + $('#pincode').val(d.pincode); + $('#website').val(d.website); + $('#branch_gstin_number').val(d.branch_gstin_number); + // All reference lists are ready before selecting saved values. $('#setting_country').val(d.country).trigger('change.select2'); - PosnicPro.settings.loadSelectSettingState(d.country_id, d.state); + PosnicPro.settings.loadSelectSettingState(d.country_id, d.state || ''); PosnicPro.settings.applyTaxProfile(d.country); $('#currency_setting').val(d.currency_text).trigger('change.select2'); var cv = (d.currency_value && d.currency_value[0]) || { currency_text: 'INR', currency_sign: '₹' }; @@ -126,18 +132,19 @@ PosnicPro.branches = { $('#storedate').val(d.client_dateformat).trigger('change.select2'); $('#serverdate').val(d.server_dateformat); $('#dateText').val(d.dateformat_text); - }, 600); - var image_path = (d.logo && d.logo !== 'store.png') ? d.logo : 'static/images/default/store.png'; - $('#previewing').attr('src', image_path); - $('#setting_logo_value').val(d.logo || 'store.png'); - $('#be_register_rows').empty(); - $.each(d.register || [], function (i, r) { - var name = (r && (r.register_name || r.name)) || (typeof r === 'string' ? r : ''); - if (name) { PosnicPro.branches.beAddRegisterRow(name); } + var image_path = (d.logo && d.logo !== 'store.png') ? d.logo : 'static/images/default/store.png'; + $('#previewing').attr('src', image_path); + $('#setting_logo_value').val(d.logo || 'store.png'); + $('#be_register_rows').empty(); + save.prop('disabled', false); + $.each(d.register || [], function (i, r) { + var name = (r && (r.register_name || r.name)) || (typeof r === 'string' ? r : ''); + if (name) { PosnicPro.branches.beAddRegisterRow(name); } + }); + }, function (xhr) { + var response = jQuery.parseJSON(xhr.responseText); + PosnicPro.alert(response.type, response.message); }); - }, function (xhr) { - var response = jQuery.parseJSON(xhr.responseText); - PosnicPro.alert(response.type, response.message); }); }, beAddRegisterRow: function (name) { diff --git a/frontend/static/script/js/modules/js/settings.js b/frontend/static/script/js/modules/js/settings.js index 72585ea62..3c0e0c6ee 100644 --- a/frontend/static/script/js/modules/js/settings.js +++ b/frontend/static/script/js/modules/js/settings.js @@ -1,6 +1,7 @@ PosnicPro.settings = { /* which reference lists have been fetched this session */ _refLoaded: {}, + _refPending: {}, /* * ORDERS PER TABLE: one number, typed through two controls. @@ -571,7 +572,7 @@ PosnicPro.settings = { }, /*store details Add/Update function*/ generalSetting: function (value) { - if ($('#store_name').val() !== '' && $('#store_telephone').val() !== '' && PosnicPro.validateEmail($('#store_email').val()) && $('#setting_country').val() !== '' && $('#setting_state').val() !== '' && $('#currency').val() !== '' && $('#time_zone').val() !== '' && $('#storedate').val() !== '' && $('#store_address').val() !== '' && $('#printing_address').val() !== '') { + if ($('#setting_add').valid()) { var loader = $(".loader-view-generalsetting"); $("
").appendTo(loader); var currency = $('#currency_type').val(); @@ -603,6 +604,7 @@ PosnicPro.settings = { data: JSON.stringify(Object.assign(formData, timezoneValue, countryId, registers)) }; PosnicPro.put(params, function (response) { + loader.find(".loadingSpinner:first").remove(); if (response.type === 'success' && !editingCurrent) { loader.find(".loadingSpinner:first").remove(); PosnicPro.alert(response.type, response.message); @@ -711,6 +713,7 @@ PosnicPro.settings = { } PosnicPro.alert(response.type, response.message); }, function (xhr) { + loader.find(".loadingSpinner:first").remove(); var response = jQuery.parseJSON(xhr.responseText); PosnicPro.alert(response.type, response.message); }); @@ -1545,6 +1548,8 @@ if ($wrapper.length) { }); }, resetEditButton: function (id) { + var target = $('#edit_branch_target').val(); + if (target) { PosnicPro.branches.loadFullEdit(target); return; } var branch_id = PosnicPro.local.get('branch_id_set'); PosnicPro.settings.viewSettings(branch_id); }, @@ -2842,8 +2847,10 @@ loadSelectSettingCountry: function (force) { racing inside the in-flight window, both passing a guard that asked whether options existed yet - they did not, so both fetched and both appended, forever. */ - if (!force && PosnicPro.settings._refLoaded.country) { return; } + if (!force && PosnicPro.settings._refLoaded.country) { return PosnicPro.settings._refPending.country.promise(); } PosnicPro.settings._refLoaded.country = true; + var ready = $.Deferred(); + PosnicPro.settings._refPending.country = ready; var countrySelect = $('.setCountry'); var params = { url: 'setting/getJSONCountry', @@ -2857,23 +2864,32 @@ loadSelectSettingCountry: function (force) { countrySelect.append(option).select2(); }); countrySelect.val(PosnicPro.local.get("country_setting")).trigger('change.select2'); + ready.resolve(); }, function (xhr) { /* a failed fetch must not lock the list out for the session */ PosnicPro.settings._refLoaded.country = false; + ready.reject(); var response = jQuery.parseJSON(xhr.responseText); PosnicPro.alert(response.type, response.message); }); + return ready.promise(); }, loadSelectSettingState: function (id, selectedState) { var request = (this.stateRequest || 0) + 1; this.stateRequest = request; if (selectedState === undefined) selectedState = $('#setting_country').val() === PosnicPro.local.get('country_setting') ? PosnicPro.local.get('state_setting') : ''; var stateSelect = $('#setting_state'); + // Display the saved state immediately, even while the country list is loading. + stateSelect.empty().append(new Option('', '')); + if (selectedState) stateSelect.append(new Option(selectedState, selectedState)); + stateSelect.val(selectedState || '').trigger('change.select2'); + var initialState = stateSelect.val(); var params = { url: 'setting/getJSONState', data: { id: id } }; PosnicPro.get(params, function (response) { if (PosnicPro.settings.stateRequest !== request) return; + if (stateSelect.val() !== initialState) selectedState = stateSelect.val(); stateSelect.empty().append(new Option('', '')); (response.data.stateJsonArray || []).forEach(function (state) { stateSelect.append(new Option(state, state)); }); var match = Array.from(stateSelect[0].options).find(function (option) { return option.value.trim().toLowerCase() === String(selectedState || '').trim().toLowerCase(); }); @@ -2894,8 +2910,10 @@ loadSelectSettingCountry: function (force) { * options across four navigation cycles). Pass true to force a reload. */ loadSelectSettingCurrency: function (force) { - if (!force && PosnicPro.settings._refLoaded.currency) { return; } + if (!force && PosnicPro.settings._refLoaded.currency) { return PosnicPro.settings._refPending.currency.promise(); } PosnicPro.settings._refLoaded.currency = true; + var ready = $.Deferred(); + PosnicPro.settings._refPending.currency = ready; var currencySelect = $('#currency_setting'); var params = { url: 'setting/getJSONCurrency' @@ -2908,12 +2926,7 @@ loadSelectSettingCurrency: function (force) { currencySelect.append(option).trigger('change'); }); currencySelect.val(PosnicPro.local.get("currency_setting")).trigger('change.select2'); - currencySelect.trigger({ - type: 'select2:select', - params: { - data: response - } - }).on('select2:select', function (e) { + currencySelect.off('select2:select.settingCurrency').on('select2:select.settingCurrency', function (e) { let data = e.params.data; $('#currencyText').val(data.element.attributes['data-currency-symbol'].value); $('#currencyTextname').val(data.element.attributes['data-currency-text'].value); @@ -2923,10 +2936,14 @@ loadSelectSettingCurrency: function (force) { " '; $('#currency_type').append(currencyDataOPtion); }); + ready.resolve(); }, function (xhr) { + PosnicPro.settings._refLoaded.currency = false; + ready.reject(); var response = jQuery.parseJSON(xhr.responseText); PosnicPro.alert(response.type, response.message); }); + return ready.promise(); }, /* * Reference data loads ONCE. Countries, currencies and timezones do not @@ -2937,8 +2954,10 @@ loadSelectSettingCurrency: function (force) { * options across four navigation cycles). Pass true to force a reload. */ timeZone: function (force) { - if (!force && PosnicPro.settings._refLoaded.timezone) { return; } + if (!force && PosnicPro.settings._refLoaded.timezone) { return PosnicPro.settings._refPending.timezone.promise(); } PosnicPro.settings._refLoaded.timezone = true; + var ready = $.Deferred(); + PosnicPro.settings._refPending.timezone = ready; var timezoneSelect = $('#time_zone'); var params = { url: 'setting/getJSONTimeZone' @@ -2952,10 +2971,14 @@ loadSelectSettingCurrency: function (force) { }); let timezone = PosnicPro.timeZone(); timezoneSelect.val(timezone).trigger('change.select2'); + ready.resolve(); }, function (xhr) { + PosnicPro.settings._refLoaded.timezone = false; + ready.reject(); var response = jQuery.parseJSON(xhr.responseText); PosnicPro.alert(response.type, response.message); }); + return ready.promise(); }, paymentKey: function () { var loader = $(".loader-qrsetting"); @@ -4307,6 +4330,13 @@ $("#textlocal_setting").submit(function (event) { } }); $("#setting_add").validate({ + // Include inactive tabs and Select2 source selects in validation. + ignore: 'input[type="hidden"], :disabled', + errorPlacement: function (error, element) { + var select = element.next('.select2'); + var phone = element.closest('.iti'); + error.insertAfter(select.length ? select : (phone.length ? phone : element)); + }, highlight: function (element, errorClass) { $(element).css("border-color", "#f9616d"); }, @@ -4314,6 +4344,12 @@ $("#setting_add").validate({ $(element).css("border-color", "#eae8e8"); }, rules: { + setting_country: { required: true }, + setting_state: { required: true }, + currency_setting: { required: true }, + currency_type: { required: true }, + time_zone: { required: true }, + storedate: { required: true }, store_name: { required: true, minlength: 3, diff --git a/frontend/static/script/js/modules/js/users.js b/frontend/static/script/js/modules/js/users.js index e23cc6f0b..6c39b65ec 100644 --- a/frontend/static/script/js/modules/js/users.js +++ b/frontend/static/script/js/modules/js/users.js @@ -401,7 +401,8 @@ PosnicPro.users = { $(selectBranch).each(function (index) { var branch_id = selectBranch[index].id; var branch_name = selectBranch[index].text; - var branch_image = selectBranch[index].element.attributes['data-logo'].value; + var option = selectBranch[index].element; + var branch_image = (option && option.getAttribute('data-logo')) || 'store.png'; branchData.push({branch_id: branch_id, branch_name: branch_name, branch_image: branch_image}); }); var branchType = { @@ -1870,7 +1871,13 @@ PosnicPro.users = { }, clickNormalForm: function () { - $(".user_add").validate({ + var validator = $("#user_form_upload").validate({ + // Tabs and Select2 hide real required controls. Only omit the API account fields. + ignore: 'input[type="hidden"], :disabled, #app_name, #users_api', + errorPlacement: function (error, element) { + var select = element.next('.select2'); + error.insertAfter(select.length ? select : element); + }, errorClass: 'error error_users', highlight: function (element, errorClass) { $(element).css("border-color", "#f9616d"); @@ -1943,7 +1950,7 @@ PosnicPro.users = { required: "Type the password again", minlength: "Password must be at least 5 characters", maxlength: "Password should not be more than 20 characters", - equalTo: "Not match retype Password" + equalTo: "Passwords do not match. Enter the same password in both fields." }, email: { required: "Enter the email address", @@ -1961,15 +1968,29 @@ PosnicPro.users = { jQuery.validator.addMethod("lettersonly", function (value, element) { return this.optional(element) || /^[a-z\s]+$/i.test(value); }, "Use letters, numbers and spaces only"); - if ($('.user_add').valid()) { // checks form for validity - var checked = $("#user_form_upload .onoffswitch-checkbox[data-id]:checked, #user_form_upload .pos-action-box:checked").length; - if (checked > 0) { - $("#checkbox_error").text("").removeClass('error'); - PosnicPro.users.userForm(); - } else { - PosnicPro.users.showPermissionError(); + var valid = $('#user_form_upload').valid(); + var checked = $("#user_form_upload .onoffswitch-checkbox[data-id]:checked, #user_form_upload .pos-action-box:checked").length; + $('#user_save_errors').remove(); + if (!checked) PosnicPro.users.showPermissionError(); + else $('#checkbox_error').text('').removeClass('error').hide(); + if (!valid || !checked) { + var summary = $(''); + (validator.errorList || []).forEach(function (item) { + $('