- Add WP Media Library integration for product and variation images - Support images array (URLs) conversion to attachment IDs - Add images array to API responses (Admin & Customer SPA) - Implement drag-and-drop sortable images in Admin product form - Add image gallery thumbnails in Customer SPA product page - Initialize WooCommerce session for guest cart operations - Fix product variations and attributes display in Customer SPA - Add variation image field in Admin SPA Changes: - includes/Api/ProductsController.php: Handle images array, add to responses - includes/Frontend/ShopController.php: Add images array for customer SPA - includes/Frontend/CartController.php: Initialize WC session for guests - admin-spa/src/lib/wp-media.ts: Add openWPMediaGallery function - admin-spa/src/routes/Products/partials/tabs/GeneralTab.tsx: WP Media + sortable images - admin-spa/src/routes/Products/partials/tabs/VariationsTab.tsx: Add variation image field - customer-spa/src/pages/Product/index.tsx: Add gallery thumbnails display
11 KiB
11 KiB
WooNooW Customer SPA Settings
📍 Settings Location
Admin SPA > Settings > Customer SPA
(NOT in wp-admin, but in our React admin interface)
📊 Settings Schema
TypeScript Interface
interface CustomerSPASettings {
// Mode
mode: 'disabled' | 'full' | 'checkout_only';
// Checkout-Only mode settings
checkoutPages?: {
checkout: boolean;
thankyou: boolean;
account: boolean;
cart: boolean;
};
// Layout (for full mode)
layout: 'classic' | 'modern' | 'boutique' | 'launch';
// Branding
branding: {
logo: string; // URL
favicon: string; // URL
siteName: string;
};
// Colors (Design Tokens)
colors: {
primary: string; // #3B82F6
secondary: string; // #8B5CF6
accent: string; // #10B981
background: string; // #FFFFFF
text: string; // #1F2937
};
// Typography
typography: {
preset: 'professional' | 'modern' | 'elegant' | 'tech' | 'custom';
customFonts?: {
heading: string;
body: string;
};
};
// Navigation
menus: {
primary: number; // WP menu ID
footer: number; // WP menu ID
};
// Homepage
homepage: {
sections: Array<{
id: string;
type: 'hero' | 'featured' | 'categories' | 'testimonials' | 'newsletter' | 'custom';
enabled: boolean;
order: number;
config: Record<string, any>;
}>;
};
// Product Page
product: {
layout: 'standard' | 'gallery' | 'minimal';
showRelatedProducts: boolean;
showReviews: boolean;
};
// Checkout
checkout: {
style: 'onepage' | 'multistep';
enableGuestCheckout: boolean;
showTrustBadges: boolean;
showOrderSummary: 'sidebar' | 'inline';
};
}
Default Settings
const DEFAULT_SETTINGS: CustomerSPASettings = {
mode: 'disabled',
checkoutPages: {
checkout: true,
thankyou: true,
account: true,
cart: false,
},
layout: 'modern',
branding: {
logo: '',
favicon: '',
siteName: get_bloginfo('name'),
},
colors: {
primary: '#3B82F6',
secondary: '#8B5CF6',
accent: '#10B981',
background: '#FFFFFF',
text: '#1F2937',
},
typography: {
preset: 'professional',
},
menus: {
primary: 0,
footer: 0,
},
homepage: {
sections: [
{ id: 'hero-1', type: 'hero', enabled: true, order: 0, config: {} },
{ id: 'featured-1', type: 'featured', enabled: true, order: 1, config: {} },
{ id: 'categories-1', type: 'categories', enabled: true, order: 2, config: {} },
],
},
product: {
layout: 'standard',
showRelatedProducts: true,
showReviews: true,
},
checkout: {
style: 'onepage',
enableGuestCheckout: true,
showTrustBadges: true,
showOrderSummary: 'sidebar',
},
};
🔌 REST API Endpoints
Get Settings
GET /wp-json/woonoow/v1/settings/customer-spa
Response:
{
"enabled": true,
"layout": "modern",
"colors": {
"primary": "#3B82F6",
"secondary": "#8B5CF6",
"accent": "#10B981"
},
...
}
Update Settings
POST /wp-json/woonoow/v1/settings/customer-spa
Content-Type: application/json
{
"enabled": true,
"layout": "modern",
"colors": {
"primary": "#FF6B6B"
}
}
Response:
{
"success": true,
"data": {
"enabled": true,
"layout": "modern",
"colors": {
"primary": "#FF6B6B",
"secondary": "#8B5CF6",
"accent": "#10B981"
},
...
}
}
🎨 Customization Options
1. Layout Options (4 Presets)
Classic Layout
- Traditional ecommerce design
- Header with logo + horizontal menu
- Sidebar filters on shop page
- Grid product listing
- Footer with widgets
- Best for: B2B, traditional retail
Modern Layout (Default)
- Minimalist, clean design
- Centered logo
- Top filters (no sidebar)
- Large product cards with hover effects
- Simplified footer
- Best for: Fashion, lifestyle brands
Boutique Layout
- Fashion/luxury focused
- Full-width hero sections
- Masonry grid layout
- Elegant typography
- Minimal UI elements
- Best for: High-end fashion, luxury goods
Launch Layout 🆕 (Single Product Funnel)
- Landing page: User's custom design (Elementor/Divi) - NOT controlled by WooNooW
- WooNooW takes over: From checkout onwards (after CTA click)
- No traditional header/footer on checkout/thank you/account pages
- Streamlined checkout (one-page, minimal fields, no cart)
- Upsell/downsell on thank you page
- Direct product access in My Account
- Best for:
- Digital products (courses, ebooks, software)
- SaaS trials → paid conversion
- Webinar funnels
- High-ticket consulting
- Limited-time offers
- Product launches
Flow: Landing Page (Custom) → [CTA to /checkout] → Checkout (SPA) → Thank You (SPA) → My Account (SPA)
Note: This is essentially Checkout-Only mode with funnel-optimized design.
2. Color Customization
Primary Color:
- Used for: Buttons, links, active states
- Default:
#3B82F6(Blue)
Secondary Color:
- Used for: Badges, accents, secondary buttons
- Default:
#8B5CF6(Purple)
Accent Color:
- Used for: Success states, CTAs, highlights
- Default:
#10B981(Green)
Background & Text:
- Auto-calculated for proper contrast
- Supports light/dark mode
3. Typography Presets
Professional
- Heading: Inter
- Body: Lora
- Use case: Corporate, B2B
Modern
- Heading: Poppins
- Body: Roboto
- Use case: Tech, SaaS
Elegant
- Heading: Playfair Display
- Body: Source Sans Pro
- Use case: Fashion, Luxury
Tech
- Heading: Space Grotesk
- Body: IBM Plex Mono
- Use case: Electronics, Gadgets
Custom
- Upload custom fonts
- Specify font families
4. Homepage Sections
Available section types:
Hero Banner
{
type: 'hero',
config: {
image: string; // Background image URL
heading: string; // Main heading
subheading: string; // Subheading
ctaText: string; // Button text
ctaLink: string; // Button URL
alignment: 'left' | 'center' | 'right';
}
}
Featured Products
{
type: 'featured',
config: {
title: string;
productIds: number[]; // Manual selection
autoSelect: boolean; // Auto-select featured products
limit: number; // Number of products to show
columns: 2 | 3 | 4;
}
}
Category Grid
{
type: 'categories',
config: {
title: string;
categoryIds: number[];
columns: 2 | 3 | 4;
showProductCount: boolean;
}
}
Testimonials
{
type: 'testimonials',
config: {
title: string;
testimonials: Array<{
name: string;
avatar: string;
rating: number;
text: string;
}>;
}
}
Newsletter
{
type: 'newsletter',
config: {
title: string;
description: string;
placeholder: string;
buttonText: string;
mailchimpListId?: string;
}
}
💾 Storage
WordPress Options Table
Settings are stored in wp_options:
// Option name: woonoow_customer_spa_enabled
// Value: boolean (true/false)
// Option name: woonoow_customer_spa_settings
// Value: JSON-encoded settings object
PHP Implementation
// Get settings
$settings = get_option('woonoow_customer_spa_settings', []);
// Update settings
update_option('woonoow_customer_spa_settings', $settings);
// Check if enabled
$enabled = get_option('woonoow_customer_spa_enabled', false);
🔒 Permissions
Who Can Modify Settings?
- Capability required:
manage_woocommerce - Roles: Administrator, Shop Manager
REST API Permission Check
public function update_settings_permission_check() {
return current_user_can('manage_woocommerce');
}
🎯 Settings UI Components
Admin SPA Components
-
Enable/Disable Toggle
- Component:
Switch - Shows warning when enabling
- Component:
-
Layout Selector
- Component:
LayoutPreview - Visual preview of each layout
- Radio button selection
- Component:
-
Color Picker
- Component:
ColorPicker - Supports hex, rgb, hsl
- Live preview
- Component:
-
Typography Selector
- Component:
TypographyPreview - Shows font samples
- Dropdown selection
- Component:
-
Homepage Section Builder
- Component:
SectionBuilder - Drag-and-drop reordering
- Add/remove/configure sections
- Component:
-
Menu Selector
- Component:
MenuDropdown - Fetches WP menus via API
- Dropdown selection
- Component:
📤 Data Flow
Settings Update Flow
1. User changes setting in Admin SPA
↓
2. React state updates (optimistic UI)
↓
3. POST to /wp-json/woonoow/v1/settings/customer-spa
↓
4. PHP validates & saves to wp_options
↓
5. Response confirms save
↓
6. React Query invalidates cache
↓
7. Customer SPA receives new settings on next load
Settings Load Flow (Customer SPA)
1. PHP renders spa-full-page.php
↓
2. wp_head() outputs inline script:
window.woonoowCustomer = {
theme: <?php echo json_encode($settings); ?>
}
↓
3. React app reads window.woonoowCustomer
↓
4. ThemeProvider applies settings
↓
5. CSS variables injected
↓
6. Components render with theme
🧪 Testing
Unit Tests
describe('CustomerSPASettings', () => {
it('should load default settings', () => {
const settings = getDefaultSettings();
expect(settings.enabled).toBe(false);
expect(settings.layout).toBe('modern');
});
it('should validate color format', () => {
expect(isValidColor('#FF6B6B')).toBe(true);
expect(isValidColor('invalid')).toBe(false);
});
it('should merge partial updates', () => {
const current = getDefaultSettings();
const update = { colors: { primary: '#FF0000' } };
const merged = mergeSettings(current, update);
expect(merged.colors.primary).toBe('#FF0000');
expect(merged.colors.secondary).toBe('#8B5CF6'); // Unchanged
});
});
Integration Tests
class CustomerSPASettingsTest extends WP_UnitTestCase {
public function test_save_settings() {
$settings = ['enabled' => true, 'layout' => 'modern'];
update_option('woonoow_customer_spa_settings', $settings);
$saved = get_option('woonoow_customer_spa_settings');
$this->assertEquals('modern', $saved['layout']);
}
public function test_rest_api_requires_permission() {
wp_set_current_user(0); // Not logged in
$request = new WP_REST_Request('POST', '/woonoow/v1/settings/customer-spa');
$response = rest_do_request($request);
$this->assertEquals(401, $response->get_status());
}
}