update multicurrencies functionality on global level

This commit is contained in:
dwindown
2025-08-25 19:55:38 +07:00
parent 38b6b5cddb
commit ccb2b1aea1
21 changed files with 1240 additions and 476 deletions

View File

@@ -19,33 +19,201 @@
}
});
function formatLabel(fieldId, str, tab) {
if(fieldId == str){
str = str
.split('-') // Split by hyphen
.map(part =>
part
.split('_') // Split by underscore
.map(word => word.charAt(0).toUpperCase() + word.slice(1)) // Capitalize
.join(' ')
)
.join(' → ');
return str;
}
return tab + ' → ' + str;
function formatLabelPath(parts) {
return parts.filter(Boolean).map(function (s) {
return String(s).trim();
}).filter(Boolean).join(' → ');
}
function triggerSwalEmptyRequired(fieldId, firstLabel, tab) {
function triggerSwalEmptyRequired(arg1, firstLabel, tab) {
// Supports both old signature (fieldId, firstLabel, tab)
// and new signature (object { id, tab, group, repeater, label, parent })
var path = '';
var p;
if (typeof arg1 === 'object' && arg1 !== null) {
p = arg1;
// Include parent (repeater parent field label) when present
path = formatLabelPath([p.tab, p.group, p.parent, p.repeater, p.label]);
} else {
// Fallback to old behavior
if (firstLabel) {
path = formatLabelPath([tab, firstLabel]);
}
}
// Wider dialog for repeater breadcrumb to avoid wrapping
var isRepeaterContext = (typeof p !== 'undefined') && (!!p.parent || !!p.repeater);
Swal.fire({
icon: 'warning',
title: 'Empty Required Field',
html: firstLabel ? 'Check: <b>' + formatLabel(fieldId, firstLabel, tab) + '</b>' : '',
html: path ? 'Check: <b>' + path + '</b>' : '',
width: isRepeaterContext ? '50em' : undefined,
customClass: {
confirmButton: 'btn text-bg-primary'
},
didOpen: (popup) => {
if (isRepeaterContext) {
var htmlC = popup.querySelector('.swal2-html-container');
if (htmlC) {
htmlC.style.whiteSpace = 'nowrap';
}
}
}
});
}
// === DeweBox/Nuxy required/visibility helpers (global) ===
function isBoxChildVisible($box, includeHidden) {
if (includeHidden === true) return true; // scan *all* tabs/sections
return $box.is(':visible');
}
function readFieldValue($input) {
const id = $input.attr('id');
const tag = $input.prop('tagName');
const type= ($input.attr('type') || '').toLowerCase();
// TinyMCE
if (tag === 'TEXTAREA' && typeof window.tinymce !== 'undefined') {
const ed = tinymce.get(id);
if (ed) return ed.getContent();
}
// CodeMirror via WP code editor (textarea sibling with .CodeMirror)
if (tag === 'TEXTAREA' && window.wp && wp.codeEditor) {
const cmWrap = $input.next('.CodeMirror');
if (cmWrap.length && cmWrap[0].CodeMirror) {
return cmWrap[0].CodeMirror.getValue();
}
}
if (type === 'checkbox' || type === 'radio') {
const name = $input.attr('name');
return !!$(`[name="${name}"]:checked`).length;
}
return $.trim($input.val() || '');
}
function isRequiredEmpty($input) {
if (!$input.is('[required]')) return false;
const tag = $input.prop('tagName');
const type = ($input.attr('type') || '').toLowerCase();
if (type === 'checkbox' || type === 'radio') {
const name = $input.attr('name');
return !$(`[name="${name}"]:checked`).length;
}
const v = readFieldValue($input);
return v === '' || v === null || typeof v === 'undefined';
}
function collectInvalidFields(includeHidden) {
const invalid = [];
// Ensure TinyMCE mirrors content back to textarea before reading values
if (window.tinymce) tinymce.triggerSave();
$('.wpcfto-box-child').each(function () {
const $boxChild = $(this);
const fieldId = $boxChild.attr('data-field');
const $tab = $boxChild.closest('.wpcfto-tab');
const tabId = $tab.attr('id');
const tabTitle = $(`[data-section=${tabId}]`).text();
if (!isBoxChildVisible($boxChild, includeHidden)) return;
// Try to find nearest group title before this field
const groupLabel = ($boxChild.prevAll('.wpcfto_group_started:first')
.find('.wpcfto-field-aside__label span:first-child').text() || '').trim();
if ($boxChild.hasClass('repeater')) {
// Repeater parent label (the field label for the repeater itself)
const parentLabel = ($boxChild.find('.wpcfto-field-aside__label span:first-child').first().text() || '').trim();
// checker for the parent itself
if($boxChild.find('.wpcfto-repeater-single').length == 0){
invalid.push({
id: fieldId,
tab: tabTitle,
group: groupLabel,
parent: parentLabel
});
}
$boxChild.find('.wpcfto-repeater-single').each(function (idx) {
const $item = $(this);
// Repeater item label heuristics (prefer the Vue-rendered title)
const titleEl = $item.find('.wpcfto_group_title').first();
let repeaterLabel = (titleEl.text() || '').toString().trim();
if (!repeaterLabel) {
repeaterLabel = (
$item.find('[data-repeater-label]').val() ||
$item.find('[name*="[label]"]').val() ||
$item.find('[name*="[title]"]').val() ||
$item.find('[name*="[name]"]').val() ||
$item.find('.wpcfto-repeater-title, .wpcfto-repeater-item-label').first().text() ||
''
).toString().trim();
}
if (!repeaterLabel) repeaterLabel = `Item #${idx+1}`;
$item.find('input, textarea, select').filter('[required]').each(function () {
const $f = $(this);
if (isRequiredEmpty($f)) {
const emptyLabel = $f.closest('.wpcfto_generic_field')
.find('.wpcfto-field-aside__label span:first-child').text() || fieldId;
invalid.push({
id: fieldId,
tab: tabTitle,
group: groupLabel,
parent: parentLabel,
repeater: repeaterLabel,
label: emptyLabel
});
}
});
});
return;
}
$boxChild.find('input, textarea, select').filter('[required]').each(function () {
const $f = $(this);
if (isRequiredEmpty($f)) {
const fieldLabel = $(`[data-field=${fieldId}] label > span:first-child`).text() || fieldId;
invalid.push({
id: fieldId,
tab: tabTitle,
group: groupLabel,
repeater: null,
label: fieldLabel
});
}
});
});
return invalid;
}
function firstInvalidField(includeHidden) {
const inv = collectInvalidFields(includeHidden);
return inv.length ? inv[0] : null;
}
function focusFirstInvalid(invalid) {
if (!invalid) return;
const $targetTab = $(`[data-field=${invalid.id}]`).closest('.wpcfto-tab');
const tabId = $targetTab.attr('id');
if (tabId) {
// try Vue's changeTab if available
if (typeof vm !== 'undefined' && vm.changeTab) {
vm.changeTab(tabId);
} else {
$('#' + tabId).addClass('active').siblings('.wpcfto-tab').removeClass('active');
$(`[data-section="${tabId}"]`).closest('.wpcfto-nav').addClass('active').siblings('.wpcfto-nav').removeClass('active');
}
$('html, body').animate({ scrollTop: $(`[data-field=${invalid.id}]`).offset().top - 120 }, 'fast');
}
}
$('[data-vue]').each(function () {
var $this = $(this);
var data_var = $this.attr('data-vue');
@@ -77,96 +245,56 @@
mounted: function mounted() {
this.getSettings();
this.clearEmptyGroups();
// Intercept #publish (WP Update/Publish) button
// Intercept Classic Editor Publish/Save buttons
const vm = this;
$(document).on('click', '#publish', function(e) {
vm.validateAllFields();
const invalidField = vm.validateAllFields();
if (invalidField) {
e.preventDefault();
let firstInvalidFieldId = invalidField.id;
let firstLabel = invalidField.label;
let tabTitle = invalidField.tab;
if (typeof window.Swal === 'function') {
triggerSwalEmptyRequired(firstInvalidFieldId, firstLabel, tabTitle);
} else {
alert('Please fill all required fields before saving.' + (firstLabel ? '\nFirst missing: ' + firstLabel : ''));
vm.$nextTick(() => {
$(document).off('click.deweboxPublish').on('click.deweboxPublish', '#publish, #save-post', function(e) {
const invalidField = firstInvalidField(true);
if (invalidField) {
e.preventDefault();
if (typeof window.Swal === 'function') {
triggerSwalEmptyRequired(invalidField);
} else {
alert('Please fill all required fields before saving.' + (invalidField.label ? '\nFirst missing: ' + invalidField.label : ''));
}
focusFirstInvalid(invalidField);
return false;
}
return false;
}
});
});
// Gutenberg: lock publish when invalid and show SweetAlert on attempt
if (window.wp && wp.data && wp.data.select('core/editor')) {
const { subscribe, dispatch } = wp.data;
const LOCK_KEY = 'wpcfto-required-lock';
const applyLock = () => {
const invalid = firstInvalidField(true);
if (invalid) {
dispatch('core/editor').lockPostSaving(LOCK_KEY);
} else {
dispatch('core/editor').unlockPostSaving(LOCK_KEY);
}
};
applyLock();
subscribe(applyLock);
$(document).off('click.deweboxGutenberg').on('click.deweboxGutenberg', '.editor-post-publish-button, .editor-post-publish-panel__toggle', function(e){
const invalid = firstInvalidField(true);
if (invalid) {
e.preventDefault();
triggerSwalEmptyRequired(invalid);
focusFirstInvalid(invalid);
return false;
}
});
}
},
methods: {
validateAllFields: function validateAllFields() {
let domInvalidFields = [];
$('.wpcfto-box-child').each(function () {
const boxChild = $(this);
const fieldId = boxChild.attr('data-field');
const fieldTab = boxChild.closest('.wpcfto-tab').attr('id');
const fieldTabTitle = $(`[data-section=${fieldTab}]`).text();
const $inputs = boxChild.find('input, textarea, select');
if(boxChild.hasClass('repeater')){
const repeater_item = boxChild.find('.wpcfto-repeater-single');
repeater_item.each(function(key, field){
const _key = key+1;
const repeater_item_inputs = $(this).find('input, textarea, select');
repeater_item_inputs.each(function () {
const $field = $(this);
const name = $field.attr('name');
// if (!$field.is(':visible')) return;
if (!$field.is('[required]')) return;
let isEmpty = false;
if ($field.is(':checkbox') || $field.is(':radio')) {
if (!$(`[name="${name}"]`).val()) isEmpty = true;
} else {
if (!$.trim($field.val())) isEmpty = true;
}
if (isEmpty) {
const fieldSubTabTitle = fieldTabTitle +'→ Item #'+_key ;
const fieldSubLabel = $field.closest('.wpcfto_generic_field').find('.wpcfto-field-aside__label span:first-child').text()
domInvalidFields.push({
id: fieldId,
tab: fieldSubTabTitle,
label: fieldSubLabel
});
}
});
});
}else{
$inputs.each(function () {
const $field = $(this);
const name = $field.attr('name');
if (!$field.is('[required]')) return;
let isEmpty = false;
if ($field.is(':checkbox') || $field.is(':radio')) {
if (!$(`[name="${name}"]`).val()) isEmpty = true;
} else {
if (!$.trim($field.val())) isEmpty = true;
}
if (isEmpty) {
domInvalidFields.push({
id: fieldId,
tab: fieldTabTitle,
label: $(`[data-field=${fieldId}] label > span:first-child`).text()
});
}
});
}
});
if (domInvalidFields.length > 0) {
console.log(domInvalidFields);
return domInvalidFields[0]; // Return the first invalid field object
}
return null;
const invalid = firstInvalidField(true);
return invalid ? invalid : null;
},
getFieldLabelById(fieldId) {
let label = '';
@@ -256,17 +384,17 @@
});
},
saveSettings: function saveSettings(id) {
const invalidField = this.validateAllFields();
// Ensure editors sync back to DOM before validation/post
if (window.tinymce) tinymce.triggerSave();
const invalidField = firstInvalidField(true);
if (invalidField) {
let firstInvalidFieldId = invalidField.id;
let firstLabel = invalidField.label;
var firstTab = firstInvalidFieldId.split('-');
var _firstTab = firstTab[0];
if (typeof window.Swal === 'function') {
triggerSwalEmptyRequired(firstInvalidFieldId, firstLabel, _firstTab);
triggerSwalEmptyRequired(invalidField);
} else {
alert('Please fill all required fields before saving.' + (firstLabel ? '\nFirst missing: ' + firstLabel : ''));
alert('Please fill all required fields before saving.' + (invalidField.label ? '\nFirst missing: ' + invalidField.label : ''));
}
focusFirstInvalid(invalidField);
return;
}
@@ -346,7 +474,8 @@
});
},
addRepeaterRow() {
if (!this.validateAllFields()) {
const invalid = firstInvalidField(true);
if (invalid) {
if (typeof window.Swal === 'function') {
Toast.fire({
icon: "warning",
@@ -355,9 +484,10 @@
} else {
alert('Please fill all required fields before adding a new row.');
}
focusFirstInvalid(invalid);
return;
}
// Your existing logic to add repeater row
// TODO: your existing logic to actually add a repeater row goes here
}
},
watch: {