diff --git a/assets/css/admin.css b/assets/css/admin.css
index 480d71a..bfb0475 100644
--- a/assets/css/admin.css
+++ b/assets/css/admin.css
@@ -2103,16 +2103,20 @@
WooCommerce Sync Button
============================================ */
.bnb-sync-rooms-btn {
- display: inline-flex;
- align-items: center;
+ display: inline-flex !important;
+ align-items: center !important;
gap: 6px;
+ vertical-align: middle;
}
.bnb-sync-rooms-btn .dashicons {
font-size: 16px;
width: 16px;
height: 16px;
- line-height: 1;
+ line-height: 16px;
+ display: inline-block;
+ vertical-align: middle;
+ margin-top: -2px;
}
.bnb-sync-rooms-btn .dashicons.bnb-spin {
diff --git a/assets/js/admin.js b/assets/js/admin.js
index bc31c42..213b2b7 100644
--- a/assets/js/admin.js
+++ b/assets/js/admin.js
@@ -1213,7 +1213,7 @@
e.preventDefault();
var $btn = $(this);
- var $status = $btn.siblings('.sync-status');
+ var $status = $btn.parent().find('.sync-status');
$btn.prop('disabled', true);
$btn.find('.dashicons').addClass('bnb-spin');
@@ -1230,11 +1230,19 @@
if (response.success) {
$status.html('' + response.data.message + '');
} else {
- $status.html('' + (response.data.message || 'Error') + '');
+ var errorMsg = response.data && response.data.message ? response.data.message : 'Unknown error';
+ $status.html('' + errorMsg + '');
}
},
- error: function() {
- $status.html('' + (wpBnbAdmin.i18n.error || 'Error occurred') + '');
+ error: function(xhr, status, error) {
+ var errorMsg = wpBnbAdmin.i18n.error || 'Error occurred';
+ if (xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) {
+ errorMsg = xhr.responseJSON.data.message;
+ } else if (error) {
+ errorMsg = error;
+ }
+ $status.html('' + errorMsg + '');
+ console.error('WP-BnB Sync Error:', status, error, xhr.responseText);
},
complete: function() {
$btn.prop('disabled', false);
diff --git a/src/Integration/WooCommerce/Manager.php b/src/Integration/WooCommerce/Manager.php
index e68fc2d..4490c98 100644
--- a/src/Integration/WooCommerce/Manager.php
+++ b/src/Integration/WooCommerce/Manager.php
@@ -130,7 +130,10 @@ final class Manager {
// Declare HPOS compatibility.
add_action( 'before_woocommerce_init', array( self::class, 'declare_hpos_compatibility' ) );
- // Only initialize components if integration is enabled.
+ // Always register admin AJAX handlers (for settings page).
+ add_action( 'woocommerce_loaded', array( ProductSync::class, 'init_admin_ajax' ) );
+
+ // Only initialize full components if integration is enabled.
if ( ! self::is_enabled() ) {
return;
}
diff --git a/src/Integration/WooCommerce/ProductSync.php b/src/Integration/WooCommerce/ProductSync.php
index b58f2cf..0fe43c8 100644
--- a/src/Integration/WooCommerce/ProductSync.php
+++ b/src/Integration/WooCommerce/ProductSync.php
@@ -25,6 +25,13 @@ use Magdev\WpBnb\Taxonomies\RoomType;
*/
final class ProductSync {
+ /**
+ * Track if admin AJAX has been initialized.
+ *
+ * @var bool
+ */
+ private static bool $admin_ajax_initialized = false;
+
/**
* Initialize product synchronization.
*
@@ -40,8 +47,27 @@ final class ProductSync {
// Add linked room info to product edit screen.
add_action( 'woocommerce_product_options_general_product_data', array( self::class, 'add_product_room_info' ) );
+ // Register admin AJAX if not already done.
+ self::init_admin_ajax();
+ }
+
+ /**
+ * Initialize admin AJAX handlers only.
+ *
+ * Called separately from init() to ensure AJAX is available on settings page
+ * even when full integration is not yet enabled.
+ *
+ * @return void
+ */
+ public static function init_admin_ajax(): void {
+ if ( self::$admin_ajax_initialized ) {
+ return;
+ }
+
// AJAX handler for syncing all rooms.
add_action( 'wp_ajax_wp_bnb_sync_all_rooms', array( self::class, 'ajax_sync_all_rooms' ) );
+
+ self::$admin_ajax_initialized = true;
}
/**