Files
2meet-data-optimizer/admin/assets/wpdo-settings.js
T
wpdev 86ac84e07c fix(2meet-data-optimizer): show why an admin bridge-mode change was refused
- The settings form and the AJAX section save both went through
  TMDO_Mode_Manager::set() and dropped its WP_Error: an unsafe jump or a
  Promotion Guard refusal still said "saved" while the mode stayed put
- TMDO_Admin::apply_bridge_modes() returns the refusals; the full form keeps
  them in a per-user transient and shows them after the redirect, the AJAX
  save returns them as the error message (the settings JS shows it)
- Verify: AdminBridgeModeSaveTest (5 new); unit 709, integration 606, phpcs 0, stan OK
- PR: PR-135 (manual F-2)
2026-09-28 18:14:35 +08:00

107 lines
2.7 KiB
JavaScript

/**
* WP Data Optimizer — Settings per-section instant save.
*
* Each .wpdo-settings-section card has a "儲存此區塊" button that posts only
* that section's fields via AJAX, updating options without a full page reload.
*
* @since 3.0.2
*/
/* global wpdoSettings */
( function () {
'use strict';
if ( typeof wpdoSettings === 'undefined' ) {
return;
}
const { ajaxUrl, nonce, i18n } = wpdoSettings;
/**
* Collect form fields within a section into a FormData object.
* Unchecked checkboxes are intentionally omitted (server does isset() ? '1' : '0').
*
* @param {HTMLElement} section
* @returns {FormData}
*/
function collectFields( section ) {
const fd = new FormData();
section.querySelectorAll( 'input, select, textarea' ).forEach( ( el ) => {
if ( ! el.name ) {
return;
}
if ( el.type === 'checkbox' ) {
if ( el.checked ) {
fd.append( el.name, el.value );
}
} else if ( el.type === 'radio' ) {
if ( el.checked ) {
fd.append( el.name, el.value );
}
} else {
fd.append( el.name, el.value );
}
} );
return fd;
}
/**
* Show a transient status message, then fade it out after 2.5s.
*
* @param {HTMLElement} statusEl
* @param {string} message
* @param {boolean} isError
*/
function showStatus( statusEl, message, isError ) {
statusEl.textContent = message;
statusEl.style.color = isError ? 'var(--color-error, #b03d3d)' : 'var(--color-success, #5a8c5a)';
statusEl.classList.add( 'visible' );
clearTimeout( statusEl._wpdo_timer );
statusEl._wpdo_timer = setTimeout( () => {
statusEl.classList.remove( 'visible' );
}, 2500 );
}
document.addEventListener( 'click', function ( e ) {
const btn = e.target.closest( '.wpdo-section-save' );
if ( ! btn ) {
return;
}
const section = btn.closest( '.wpdo-settings-section' );
const statusEl = btn.nextElementSibling;
const sectionKey = section ? section.dataset.section : '';
if ( ! sectionKey ) {
return;
}
const originalLabel = btn.textContent;
btn.disabled = true;
btn.textContent = i18n.saving;
const fd = collectFields( section );
fd.append( 'action', 'wpdo_save_settings_section' );
fd.append( '_ajax_nonce', nonce );
fd.append( 'section', sectionKey );
fetch( ajaxUrl, {
method : 'POST',
credentials : 'same-origin',
body : fd,
} )
.then( ( r ) => r.json() )
.then( ( data ) => {
if ( data.success ) {
showStatus( statusEl, i18n.saved, false );
} else {
showStatus( statusEl, ( data.data && data.data.message ) || i18n.error, true );
}
} )
.catch( () => {
showStatus( statusEl, i18n.error, true );
} )
.finally( () => {
btn.disabled = false;
btn.textContent = originalLabel;
} );
} );
} )();