From e1adf1e525041e36621d01f6676e200b2d4f77b3 Mon Sep 17 00:00:00 2001 From: dwindown Date: Tue, 11 Nov 2025 10:43:03 +0700 Subject: [PATCH] fix: Cookie auth in standalone + dynamic VIP calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## ✅ Issue 1: Cookie Authentication in Standalone Mode **Problem:** - `rest_cookie_invalid_nonce` errors on customer-settings - `Cookie check failed` errors on media uploads - Both endpoints returning 403 in standalone mode **Root Cause:** WordPress REST API requires `credentials: "include"` for cookie-based authentication in cross-origin contexts (standalone mode uses different URL). **Fixed:** 1. **Customer Settings (Customers.tsx)** - Added `credentials: "include"` to both GET and POST requests - Use `WNW_CONFIG.nonce` as primary nonce source - Fallback to `wpApiSettings.nonce` 2. **Media Upload (image-upload.tsx)** - Added `credentials: "include"` to media upload - Prioritize `WNW_CONFIG.nonce` for standalone mode - Changed from `same-origin` to `include` for cross-origin support **Result:** - ✅ Customer settings load and save in standalone mode - ✅ Image/logo uploads work in standalone mode - ✅ SVG uploads work with proper authentication ## ✅ Issue 2: Dynamic VIP Customer Calculation **Problem:** VIP calculation was hardcoded (TODO comment) **Requirement:** Use dynamic settings from Customer Settings page **Fixed (AnalyticsController.php):** 1. **Individual Customer VIP Status** - Call `CustomerSettingsProvider::is_vip_customer()` for each customer - Add `is_vip` field to customer data - Set `segment` to "vip" for VIP customers - Count VIP customers dynamically 2. **Segments Overview** - Replace hardcoded `vip: 0` with actual `$vip_count` - VIP count updates automatically based on settings **How It Works:** - CustomerSettingsProvider reads settings from database - Checks: min_spent, min_orders, timeframe, require_both, exclude_refunded - Calculates VIP status in real-time based on current criteria - Updates immediately when settings change **Result:** - ✅ VIP badge shows correctly on customer list - ✅ VIP count in segments reflects actual qualified customers - ✅ Changes to VIP criteria instantly affect dashboard - ✅ No cache issues - recalculates on each request --- ## Files Modified: - `Customers.tsx` - Add credentials for cookie auth - `image-upload.tsx` - Add credentials for media upload - `AnalyticsController.php` - Dynamic VIP calculation ## Testing: 1. ✅ Customer settings save in standalone mode 2. ✅ Logo upload works in standalone mode 3. ✅ VIP customers show correct badge 4. ✅ Change VIP criteria → dashboard updates 5. ✅ Segments show correct VIP count --- admin-spa/src/components/ui/image-upload.tsx | 7 ++++--- admin-spa/src/routes/Settings/Customers.tsx | 6 ++++-- includes/Api/AnalyticsController.php | 12 ++++++++++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/admin-spa/src/components/ui/image-upload.tsx b/admin-spa/src/components/ui/image-upload.tsx index c5271a2..3ec9bd3 100644 --- a/admin-spa/src/components/ui/image-upload.tsx +++ b/admin-spa/src/components/ui/image-upload.tsx @@ -74,8 +74,9 @@ export function ImageUpload({ const formData = new FormData(); formData.append('file', file); - // Get nonce from REST API settings - const nonce = (window as any).wpApiSettings?.nonce || + // Get nonce from REST API settings (prioritize WNW_CONFIG for standalone mode) + const nonce = (window as any).WNW_CONFIG?.nonce || + (window as any).wpApiSettings?.nonce || (window as any).WooNooW?.nonce || document.querySelector('meta[name="wp-rest-nonce"]')?.getAttribute('content') || ''; @@ -85,7 +86,7 @@ export function ImageUpload({ headers: { 'X-WP-Nonce': nonce, }, - credentials: 'same-origin', + credentials: 'include', // Important for standalone mode body: formData, }); diff --git a/admin-spa/src/routes/Settings/Customers.tsx b/admin-spa/src/routes/Settings/Customers.tsx index 02d8444..aa294a8 100644 --- a/admin-spa/src/routes/Settings/Customers.tsx +++ b/admin-spa/src/routes/Settings/Customers.tsx @@ -40,8 +40,9 @@ export default function CustomersSettings() { const response = await fetch( `${(window as any).WNW_CONFIG?.restUrl || ''}/store/customer-settings`, { + credentials: 'include', headers: { - 'X-WP-Nonce': (window as any).wpApiSettings?.nonce || '', + 'X-WP-Nonce': (window as any).WNW_CONFIG?.nonce || (window as any).wpApiSettings?.nonce || '', }, } ); @@ -65,9 +66,10 @@ export default function CustomersSettings() { `${(window as any).WNW_CONFIG?.restUrl || ''}/store/customer-settings`, { method: 'POST', + credentials: 'include', headers: { 'Content-Type': 'application/json', - 'X-WP-Nonce': (window as any).wpApiSettings?.nonce || '', + 'X-WP-Nonce': (window as any).WNW_CONFIG?.nonce || (window as any).wpApiSettings?.nonce || '', }, body: JSON.stringify(settings), } diff --git a/includes/Api/AnalyticsController.php b/includes/Api/AnalyticsController.php index c3eda50..1e4292c 100644 --- a/includes/Api/AnalyticsController.php +++ b/includes/Api/AnalyticsController.php @@ -814,6 +814,7 @@ class AnalyticsController { $formatted_customers = []; $total_customers = 0; $total_revenue = 0; + $vip_count = 0; $formatted_customers = []; foreach ($top_customers as $customer) { $user = get_user_by('id', $customer->customer_id); @@ -823,6 +824,12 @@ class AnalyticsController { $total_customers++; $total_revenue += $total_spent; + // Check if customer is VIP based on dynamic settings + $is_vip = \WooNooW\Compat\CustomerSettingsProvider::is_vip_customer($customer->customer_id); + if ($is_vip) { + $vip_count++; + } + $formatted_customers[] = [ 'id' => intval($customer->customer_id), 'customer_id' => intval($customer->customer_id), @@ -833,7 +840,8 @@ class AnalyticsController { 'total_spent' => $total_spent, 'avg_order_value' => round($total_spent / $order_count, 2), 'last_order_date' => '', // TODO: Implement - 'segment' => 'returning', // TODO: Calculate + 'segment' => $is_vip ? 'vip' : 'returning', + 'is_vip' => $is_vip, 'days_since_last_order' => 0, // TODO: Calculate ]; } @@ -871,7 +879,7 @@ class AnalyticsController { 'segments' => [ 'new' => intval($new_customers), 'returning' => $returning_customers, - 'vip' => 0, // TODO: Calculate + 'vip' => $vip_count, 'at_risk' => 0, // TODO: Calculate ], 'top_customers' => $formatted_customers,