diff --git a/ADDON_MODULE_DESIGN_DECISIONS.md b/ADDON_MODULE_DESIGN_DECISIONS.md new file mode 100644 index 0000000..f246cc7 --- /dev/null +++ b/ADDON_MODULE_DESIGN_DECISIONS.md @@ -0,0 +1,616 @@ +# Addon-Module Integration: Design Decisions + +**Date**: December 26, 2025 +**Status**: 🎯 Decision Document + +--- + +## 1. Dynamic Categories (RECOMMENDED) + +### ❌ Problem with Static Categories +```php +// BAD: Empty categories if no modules use them +public static function get_categories() { + return [ + 'shipping' => 'Shipping & Fulfillment', // Empty if no shipping modules! + 'payments' => 'Payments & Checkout', // Empty if no payment modules! + ]; +} +``` + +### βœ… Solution: Dynamic Category Generation + +```php +class ModuleRegistry { + + /** + * Get categories dynamically from registered modules + */ + public static function get_categories() { + $all_modules = self::get_all_modules(); + $categories = []; + + // Extract unique categories from modules + foreach ($all_modules as $module) { + $cat = $module['category'] ?? 'other'; + if (!isset($categories[$cat])) { + $categories[$cat] = self::get_category_label($cat); + } + } + + // Sort by predefined order (if exists), then alphabetically + $order = ['marketing', 'customers', 'products', 'shipping', 'payments', 'analytics', 'other']; + uksort($categories, function($a, $b) use ($order) { + $pos_a = array_search($a, $order); + $pos_b = array_search($b, $order); + if ($pos_a === false) $pos_a = 999; + if ($pos_b === false) $pos_b = 999; + return $pos_a - $pos_b; + }); + + return $categories; + } + + /** + * Get human-readable label for category + */ + private static function get_category_label($category) { + $labels = [ + 'marketing' => __('Marketing & Sales', 'woonoow'), + 'customers' => __('Customer Experience', 'woonoow'), + 'products' => __('Products & Inventory', 'woonoow'), + 'shipping' => __('Shipping & Fulfillment', 'woonoow'), + 'payments' => __('Payments & Checkout', 'woonoow'), + 'analytics' => __('Analytics & Reports', 'woonoow'), + 'other' => __('Other Extensions', 'woonoow'), + ]; + + return $labels[$category] ?? ucfirst($category); + } + + /** + * Group modules by category + */ + public static function get_grouped_modules() { + $all_modules = self::get_all_modules(); + $grouped = []; + + foreach ($all_modules as $module) { + $cat = $module['category'] ?? 'other'; + if (!isset($grouped[$cat])) { + $grouped[$cat] = []; + } + $grouped[$cat][] = $module; + } + + return $grouped; + } +} +``` + +### Benefits +- βœ… No empty categories +- βœ… Addons can define custom categories +- βœ… Single registration point (module only) +- βœ… Auto-sorted by predefined order + +--- + +## 2. Module Settings URL Pattern (RECOMMENDED) + +### ❌ Problem with Custom URLs +```php +'settings_url' => '/settings/shipping/biteship', // Conflict risk! +'settings_url' => '/marketing/newsletter', // Inconsistent! +``` + +### βœ… Solution: Convention-Based Pattern + +#### Option A: Standardized Pattern (RECOMMENDED) +```php +// Module registration - NO settings_url needed! +$addons['biteship-shipping'] = [ + 'id' => 'biteship-shipping', + 'name' => 'Biteship Shipping', + 'has_settings' => true, // Just a flag! +]; + +// Auto-generated URL pattern: +// /settings/modules/{module_id} +// Example: /settings/modules/biteship-shipping +``` + +#### Backend: Auto Route Registration +```php +class ModuleRegistry { + + /** + * Register module settings routes automatically + */ + public static function register_settings_routes() { + $modules = self::get_all_modules(); + + foreach ($modules as $module) { + if (empty($module['has_settings'])) continue; + + // Auto-register route: /settings/modules/{module_id} + add_filter('woonoow/spa_routes', function($routes) use ($module) { + $routes[] = [ + 'path' => "/settings/modules/{$module['id']}", + 'component_url' => $module['settings_component'] ?? null, + 'title' => sprintf(__('%s Settings', 'woonoow'), $module['label']), + ]; + return $routes; + }); + } + } +} +``` + +#### Frontend: Automatic Navigation +```tsx +// Modules.tsx - Gear icon auto-links +{module.has_settings && module.enabled && ( + +)} +``` + +### Benefits +- βœ… No URL conflicts (enforced pattern) +- βœ… Consistent navigation +- βœ… Simpler addon registration +- βœ… Auto-generated breadcrumbs + +--- + +## 3. Form Builder vs Custom HTML (HYBRID APPROACH) + +### βœ… Recommended: Provide Both Options + +#### Option A: Schema-Based Form Builder (For Simple Settings) +```php +// Addon defines settings schema +add_filter('woonoow/module_settings_schema', function($schemas) { + $schemas['biteship-shipping'] = [ + 'api_key' => [ + 'type' => 'text', + 'label' => 'API Key', + 'description' => 'Your Biteship API key', + 'required' => true, + ], + 'enable_tracking' => [ + 'type' => 'toggle', + 'label' => 'Enable Tracking', + 'default' => true, + ], + 'default_courier' => [ + 'type' => 'select', + 'label' => 'Default Courier', + 'options' => [ + 'jne' => 'JNE', + 'jnt' => 'J&T Express', + 'sicepat' => 'SiCepat', + ], + ], + ]; + return $schemas; +}); +``` + +**Auto-rendered form** - No React needed! + +#### Option B: Custom React Component (For Complex Settings) +```php +// Addon provides custom React component +add_filter('woonoow/addon_registry', function($addons) { + $addons['biteship-shipping'] = [ + 'id' => 'biteship-shipping', + 'has_settings' => true, + 'settings_component' => plugin_dir_url(__FILE__) . 'dist/Settings.js', + ]; + return $addons; +}); +``` + +**Full control** - Custom React UI + +### Implementation + +```php +class ModuleSettingsRenderer { + + /** + * Render settings page + */ + public static function render($module_id) { + $module = ModuleRegistry::get_module($module_id); + + // Option 1: Has custom component + if (!empty($module['settings_component'])) { + return self::render_custom_component($module); + } + + // Option 2: Has schema - auto-generate form + $schema = apply_filters('woonoow/module_settings_schema', []); + if (isset($schema[$module_id])) { + return self::render_schema_form($module_id, $schema[$module_id]); + } + + // Option 3: No settings + return ['error' => 'No settings available']; + } +} +``` + +### Benefits +- βœ… Simple addons use schema (no React needed) +- βœ… Complex addons use custom components +- βœ… Consistent data persistence for both +- βœ… Gradual complexity curve + +--- + +## 4. Settings Data Persistence (STANDARDIZED) + +### βœ… Recommended: Unified Settings API + +#### Backend: Automatic Persistence +```php +class ModuleSettingsController extends WP_REST_Controller { + + /** + * GET /woonoow/v1/modules/{module_id}/settings + */ + public function get_settings($request) { + $module_id = $request['module_id']; + $settings = get_option("woonoow_module_{$module_id}_settings", []); + + // Apply defaults from schema + $schema = apply_filters('woonoow/module_settings_schema', []); + if (isset($schema[$module_id])) { + $settings = wp_parse_args($settings, self::get_defaults($schema[$module_id])); + } + + return rest_ensure_response($settings); + } + + /** + * POST /woonoow/v1/modules/{module_id}/settings + */ + public function update_settings($request) { + $module_id = $request['module_id']; + $new_settings = $request->get_json_params(); + + // Validate against schema + $schema = apply_filters('woonoow/module_settings_schema', []); + if (isset($schema[$module_id])) { + $validated = self::validate_settings($new_settings, $schema[$module_id]); + if (is_wp_error($validated)) { + return $validated; + } + $new_settings = $validated; + } + + // Save + update_option("woonoow_module_{$module_id}_settings", $new_settings); + + // Allow addons to react + do_action("woonoow/module_settings_updated/{$module_id}", $new_settings); + + return rest_ensure_response(['success' => true]); + } +} +``` + +#### Frontend: Unified Hook +```tsx +// useModuleSettings.ts +export function useModuleSettings(moduleId: string) { + const queryClient = useQueryClient(); + + const { data: settings, isLoading } = useQuery({ + queryKey: ['module-settings', moduleId], + queryFn: async () => { + const response = await api.get(`/modules/${moduleId}/settings`); + return response; + }, + }); + + const updateSettings = useMutation({ + mutationFn: async (newSettings: any) => { + return api.post(`/modules/${moduleId}/settings`, newSettings); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['module-settings', moduleId] }); + toast.success('Settings saved'); + }, + }); + + return { settings, isLoading, updateSettings }; +} +``` + +#### Addon Usage +```tsx +// Custom settings component +export default function BiteshipSettings() { + const { settings, updateSettings } = useModuleSettings('biteship-shipping'); + + return ( + + + updateSettings.mutate({ api_key: e.target.value })} + /> + + + ); +} +``` + +### Benefits +- βœ… Consistent storage pattern: `woonoow_module_{id}_settings` +- βœ… Automatic validation (if schema provided) +- βœ… React hook for easy access +- βœ… Action hooks for addon logic + +--- + +## 5. React Extension Pattern (DOCUMENTED) + +### βœ… Solution: Window API + Build Externals + +#### WooNooW Core Exposes React +```typescript +// admin-spa/src/main.tsx +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { useQuery, useMutation } from '@tanstack/react-query'; + +// Expose for addons +window.WooNooW = { + React, + ReactDOM, + hooks: { + useQuery, + useMutation, + useModuleSettings, // Our custom hook! + }, + components: { + SettingsLayout, + SettingsCard, + Button, + Input, + Select, + Switch, + // ... all shadcn components + }, + utils: { + api, + toast, + }, +}; +``` + +#### Addon Development +```typescript +// addon/src/Settings.tsx +const { React, hooks, components, utils } = window.WooNooW; +const { useModuleSettings } = hooks; +const { SettingsLayout, SettingsCard, Input, Button } = components; +const { toast } = utils; + +export default function BiteshipSettings() { + const { settings, updateSettings } = useModuleSettings('biteship-shipping'); + const [apiKey, setApiKey] = React.useState(settings?.api_key || ''); + + const handleSave = () => { + updateSettings.mutate({ api_key: apiKey }); + }; + + return React.createElement(SettingsLayout, { title: 'Biteship Settings' }, + React.createElement(SettingsCard, null, + React.createElement(Input, { + label: 'API Key', + value: apiKey, + onChange: (e) => setApiKey(e.target.value), + }), + React.createElement(Button, { onClick: handleSave }, 'Save') + ) + ); +} +``` + +#### With JSX (Build Required) +```tsx +// addon/src/Settings.tsx +const { React, hooks, components } = window.WooNooW; +const { useModuleSettings } = hooks; +const { SettingsLayout, SettingsCard, Input, Button } = components; + +export default function BiteshipSettings() { + const { settings, updateSettings } = useModuleSettings('biteship-shipping'); + + return ( + + + updateSettings.mutate({ api_key: e.target.value })} + /> + + + ); +} +``` + +```javascript +// vite.config.js +export default { + build: { + lib: { + entry: 'src/Settings.tsx', + formats: ['es'], + }, + rollupOptions: { + external: ['react', 'react-dom'], + output: { + globals: { + react: 'window.WooNooW.React', + 'react-dom': 'window.WooNooW.ReactDOM', + }, + }, + }, + }, +}; +``` + +### Benefits +- βœ… Addons don't bundle React (use ours) +- βœ… Access to all WooNooW components +- βœ… Consistent UI automatically +- βœ… Type safety with TypeScript + +--- + +## 6. Newsletter as Addon Example (RECOMMENDED) + +### βœ… Yes, Refactor Newsletter as Built-in Addon + +#### Why This is Valuable + +1. **Dogfooding** - We use our own addon system +2. **Example** - Best reference for addon developers +3. **Consistency** - Newsletter follows same pattern as external addons +4. **Testing** - Proves the system works + +#### Proposed Structure + +``` +includes/ + Modules/ + Newsletter/ + NewsletterModule.php # Module registration + NewsletterController.php # API endpoints (moved from Api/) + NewsletterSettings.php # Settings schema + +admin-spa/src/modules/ + Newsletter/ + Settings.tsx # Settings page + Subscribers.tsx # Subscribers page + index.ts # Module exports +``` + +#### Registration Pattern +```php +// includes/Modules/Newsletter/NewsletterModule.php +class NewsletterModule { + + public static function register() { + // Register as module + add_filter('woonoow/builtin_modules', function($modules) { + $modules['newsletter'] = [ + 'id' => 'newsletter', + 'label' => __('Newsletter', 'woonoow'), + 'description' => __('Email newsletter subscriptions', 'woonoow'), + 'category' => 'marketing', + 'icon' => 'mail', + 'default_enabled' => true, + 'has_settings' => true, + 'settings_component' => self::get_settings_url(), + ]; + return $modules; + }); + + // Register routes (only if enabled) + if (ModuleRegistry::is_enabled('newsletter')) { + self::register_routes(); + } + } + + private static function register_routes() { + // Settings route + add_filter('woonoow/spa_routes', function($routes) { + $routes[] = [ + 'path' => '/settings/modules/newsletter', + 'component_url' => plugins_url('admin-spa/dist/modules/Newsletter/Settings.js', WOONOOW_FILE), + ]; + return $routes; + }); + + // Subscribers route + add_filter('woonoow/spa_routes', function($routes) { + $routes[] = [ + 'path' => '/marketing/newsletter', + 'component_url' => plugins_url('admin-spa/dist/modules/Newsletter/Subscribers.js', WOONOOW_FILE), + ]; + return $routes; + }); + } +} +``` + +### Benefits +- βœ… Newsletter becomes reference implementation +- βœ… Proves addon system works for complex modules +- βœ… Shows best practices +- βœ… Easier to maintain (follows pattern) + +--- + +## Summary of Decisions + +| # | Question | Decision | Rationale | +|---|----------|----------|-----------| +| 1 | Categories | **Dynamic from modules** | No empty categories, single registration | +| 2 | Settings URL | **Pattern: `/settings/modules/{id}`** | No conflicts, consistent, auto-generated | +| 3 | Form Builder | **Hybrid: Schema + Custom** | Simple for basic, flexible for complex | +| 4 | Data Persistence | **Unified API + Hook** | Consistent storage, easy access | +| 5 | React Extension | **Window API + Externals** | No bundling, access to components | +| 6 | Newsletter Refactor | **Yes, as example** | Dogfooding, reference implementation | + +--- + +## Implementation Order + +### Phase 1: Foundation +1. βœ… Dynamic category generation +2. βœ… Standardized settings URL pattern +3. βœ… Module settings API endpoints +4. βœ… `useModuleSettings` hook + +### Phase 2: Form System +1. βœ… Schema-based form renderer +2. βœ… Custom component loader +3. βœ… Settings validation + +### Phase 3: UI Enhancement +1. βœ… Search input on Modules page +2. βœ… Category filter pills +3. βœ… Gear icon with auto-routing + +### Phase 4: Example +1. βœ… Refactor Newsletter as built-in addon +2. βœ… Document pattern +3. βœ… Create external addon example (Biteship) + +--- + +## Next Steps + +**Ready to implement?** We have clear decisions on all 6 questions. Should we: + +1. Start with Phase 1 (Foundation)? +2. Create the schema-based form system first? +3. Refactor Newsletter as proof-of-concept? + +**Your call!** All design decisions are documented and justified. diff --git a/ADDON_MODULE_INTEGRATION.md b/ADDON_MODULE_INTEGRATION.md new file mode 100644 index 0000000..c6d1664 --- /dev/null +++ b/ADDON_MODULE_INTEGRATION.md @@ -0,0 +1,476 @@ +# Addon-Module Integration Strategy + +**Date**: December 26, 2025 +**Status**: 🎯 Proposal + +--- + +## Vision + +**Module Registry as the Single Source of Truth for all extensions** - both built-in modules and external addons. + +--- + +## Current State Analysis + +### What We Have + +#### 1. **Module System** (Just Built) +- `ModuleRegistry.php` - Manages built-in modules +- Enable/disable functionality +- Module metadata (label, description, features, icon) +- Categories (Marketing, Customers, Products) +- Settings page UI with toggles + +#### 2. **Addon System** (Existing) +- `AddonRegistry.php` - Manages external addons +- SPA route injection +- Hook system integration +- Navigation tree injection +- React component loading + +### The Opportunity + +**These two systems should be unified!** An addon is just an external module. + +--- + +## Proposed Integration + +### Concept: Unified Extension Registry + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Module Registry (Single Source) β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ Built-in Modules External Addons β”‚ +β”‚ β”œβ”€ Newsletter β”œβ”€ Biteship Shipping β”‚ +β”‚ β”œβ”€ Wishlist β”œβ”€ Subscriptions β”‚ +β”‚ β”œβ”€ Affiliate β”œβ”€ Bookings β”‚ +β”‚ β”œβ”€ Subscription └─ Custom Reports β”‚ +β”‚ └─ Licensing β”‚ +β”‚ β”‚ +β”‚ All share same interface: β”‚ +β”‚ β€’ Enable/disable toggle β”‚ +β”‚ β€’ Settings page (optional) β”‚ +β”‚ β€’ Icon & metadata β”‚ +β”‚ β€’ Feature list β”‚ +β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +--- + +## Implementation Plan + +### Phase 1: Extend Module Registry for Addons + +#### Backend: ModuleRegistry.php Enhancement + +```php +class ModuleRegistry { + + /** + * Get all modules (built-in + addons) + */ + public static function get_all_modules() { + $builtin = self::get_builtin_modules(); + $addons = self::get_addon_modules(); + + return array_merge($builtin, $addons); + } + + /** + * Get addon modules from AddonRegistry + */ + private static function get_addon_modules() { + $addons = apply_filters('woonoow/addon_registry', []); + $modules = []; + + foreach ($addons as $addon_id => $addon) { + $modules[$addon_id] = [ + 'id' => $addon_id, + 'label' => $addon['name'], + 'description' => $addon['description'] ?? '', + 'category' => $addon['category'] ?? 'addons', + 'icon' => $addon['icon'] ?? 'puzzle', + 'default_enabled' => false, + 'features' => $addon['features'] ?? [], + 'is_addon' => true, + 'version' => $addon['version'] ?? '1.0.0', + 'author' => $addon['author'] ?? '', + 'settings_url' => $addon['settings_url'] ?? '', // NEW! + ]; + } + + return $modules; + } +} +``` + +#### Addon Registration Enhancement + +```php +// Addon developers register with enhanced metadata +add_filter('woonoow/addon_registry', function($addons) { + $addons['biteship-shipping'] = [ + 'id' => 'biteship-shipping', + 'name' => 'Biteship Shipping', + 'description' => 'Indonesia shipping with Biteship API', + 'version' => '1.0.0', + 'author' => 'WooNooW Team', + 'category' => 'shipping', // NEW! + 'icon' => 'truck', // NEW! + 'features' => [ // NEW! + 'Real-time shipping rates', + 'Multiple couriers', + 'Tracking integration', + ], + 'settings_url' => '/settings/shipping/biteship', // NEW! + 'spa_bundle' => plugin_dir_url(__FILE__) . 'dist/addon.js', + ]; + return $addons; +}); +``` + +--- + +### Phase 2: Module Settings Page with Gear Icon + +#### UI Enhancement: Modules.tsx + +```tsx +{modules.map((module) => ( +
+ {/* Icon */} +
+ {getIcon(module.icon)} +
+ + {/* Content */} +
+
+

{module.label}

+ {module.enabled && Active} + {module.is_addon && Addon} +
+

{module.description}

+ + {/* Features */} + +
+ + {/* Actions */} +
+ {/* Settings Gear Icon - Only if module has settings */} + {module.settings_url && module.enabled && ( + + )} + + {/* Enable/Disable Toggle */} + toggleModule.mutate({ moduleId: module.id, enabled })} + /> +
+
+))} +``` + +--- + +### Phase 3: Dynamic Categories + +#### Support for Addon Categories + +```php +// ModuleRegistry.php +public static function get_categories() { + return [ + 'marketing' => __('Marketing & Sales', 'woonoow'), + 'customers' => __('Customer Experience', 'woonoow'), + 'products' => __('Products & Inventory', 'woonoow'), + 'shipping' => __('Shipping & Fulfillment', 'woonoow'), // NEW! + 'payments' => __('Payments & Checkout', 'woonoow'), // NEW! + 'analytics' => __('Analytics & Reports', 'woonoow'), // NEW! + 'addons' => __('Other Extensions', 'woonoow'), // Fallback + ]; +} +``` + +#### Frontend: Dynamic Category Rendering + +```tsx +// Modules.tsx +const { data: modulesData } = useQuery({ + queryKey: ['modules'], + queryFn: async () => { + const response = await api.get('/modules'); + return response as ModulesData; + }, +}); + +// Get unique categories from modules +const categories = Object.keys(modulesData?.grouped || {}); + +return ( + + {categories.map((category) => { + const modules = modulesData.grouped[category] || []; + if (modules.length === 0) return null; + + return ( + + {/* Module cards */} + + ); + })} + +); +``` + +--- + +## Benefits + +### 1. **Unified Management** +- βœ… One place to see all extensions (built-in + addons) +- βœ… Consistent enable/disable interface +- βœ… Unified metadata (icon, description, features) + +### 2. **Better UX** +- βœ… Users don't need to distinguish between "modules" and "addons" +- βœ… Settings gear icon for quick access to module configuration +- βœ… Clear visual indication of what's enabled + +### 3. **Developer Experience** +- βœ… Addon developers use familiar pattern +- βœ… Automatic integration with module system +- βœ… No extra work to appear in Modules page + +### 4. **Extensibility** +- βœ… Dynamic categories support any addon type +- βœ… Settings URL allows deep linking to config +- βœ… Version and author info for better management + +--- + +## Example: Biteship Addon Integration + +### Addon Registration (PHP) + +```php + 'biteship-shipping', + 'name' => 'Biteship Shipping', + 'description' => 'Real-time shipping rates from Indonesian couriers', + 'version' => '1.0.0', + 'author' => 'WooNooW Team', + 'category' => 'shipping', + 'icon' => 'truck', + 'features' => [ + 'JNE, J&T, SiCepat, and more', + 'Real-time rate calculation', + 'Shipment tracking', + 'Automatic label printing', + ], + 'settings_url' => '/settings/shipping/biteship', + 'spa_bundle' => plugin_dir_url(__FILE__) . 'dist/addon.js', + ]; + return $addons; +}); + +// Register settings route +add_filter('woonoow/spa_routes', function($routes) { + $routes[] = [ + 'path' => '/settings/shipping/biteship', + 'component_url' => plugin_dir_url(__FILE__) . 'dist/Settings.js', + 'title' => 'Biteship Settings', + ]; + return $routes; +}); +``` + +### Result in Modules Page + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Shipping & Fulfillment β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ 🚚 Biteship Shipping [βš™οΈ] [Toggle] β”‚ +β”‚ Real-time shipping rates from Indonesian... β”‚ +β”‚ β€’ JNE, J&T, SiCepat, and more β”‚ +β”‚ β€’ Real-time rate calculation β”‚ +β”‚ β€’ Shipment tracking β”‚ +β”‚ β€’ Automatic label printing β”‚ +β”‚ β”‚ +β”‚ Version: 1.0.0 | By: WooNooW Team | [Addon] β”‚ +β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +Clicking βš™οΈ navigates to `/settings/shipping/biteship` + +--- + +## Migration Path + +### Step 1: Enhance ModuleRegistry (Backward Compatible) +- Add `get_addon_modules()` method +- Merge built-in + addon modules +- No breaking changes + +### Step 2: Update Modules UI +- Add gear icon for settings +- Add "Addon" badge +- Support dynamic categories + +### Step 3: Document for Addon Developers +- Update ADDON_DEVELOPMENT_GUIDE.md +- Add examples with new metadata +- Show settings page pattern + +### Step 4: Update Existing Addons (Optional) +- Addons work without changes +- Enhanced metadata is optional +- Settings URL is optional + +--- + +## API Changes + +### New Module Properties + +```typescript +interface Module { + id: string; + label: string; + description: string; + category: string; + icon: string; + default_enabled: boolean; + features: string[]; + enabled: boolean; + + // NEW for addons + is_addon?: boolean; + version?: string; + author?: string; + settings_url?: string; // Route to settings page +} +``` + +### New API Endpoint (Optional) + +```php +// GET /woonoow/v1/modules/:module_id/settings +// Returns module-specific settings schema +``` + +--- + +## Settings Page Pattern + +### Option 1: Dedicated Route (Recommended) + +```php +// Addon registers its own settings route +add_filter('woonoow/spa_routes', function($routes) { + $routes[] = [ + 'path' => '/settings/my-addon', + 'component_url' => plugin_dir_url(__FILE__) . 'dist/Settings.js', + ]; + return $routes; +}); +``` + +### Option 2: Modal/Drawer (Alternative) + +```tsx +// Modules page opens modal with addon settings + + + + + +``` + +--- + +## Backward Compatibility + +### Existing Addons Continue to Work +- βœ… No breaking changes +- βœ… Enhanced metadata is optional +- βœ… Addons without metadata still function +- βœ… Gradual migration path + +### Existing Modules Unaffected +- βœ… Built-in modules work as before +- βœ… No changes to existing module logic +- βœ… Only UI enhancement + +--- + +## Summary + +### What This Achieves + +1. **Newsletter Footer Integration** βœ… + - Newsletter form respects module status + - Hidden from footer builder when disabled + +2. **Addon-Module Unification** 🎯 + - Addons appear in Module Registry + - Same enable/disable interface + - Settings gear icon for configuration + +3. **Better Developer Experience** 🎯 + - Consistent registration pattern + - Automatic UI integration + - Optional settings page routing + +4. **Better User Experience** 🎯 + - One place to manage all extensions + - Clear visual hierarchy + - Quick access to settings + +### Next Steps + +1. βœ… Newsletter footer integration (DONE) +2. 🎯 Enhance ModuleRegistry for addon support +3. 🎯 Add settings URL support to Modules UI +4. 🎯 Update documentation +5. 🎯 Create example addon with settings + +--- + +**This creates a truly unified extension system where built-in modules and external addons are first-class citizens with the same management interface.** diff --git a/APPEARANCE_MENU_RESTRUCTURE.md b/APPEARANCE_MENU_RESTRUCTURE.md deleted file mode 100644 index f469e02..0000000 --- a/APPEARANCE_MENU_RESTRUCTURE.md +++ /dev/null @@ -1,212 +0,0 @@ -# Appearance Menu Restructure βœ… - -**Date:** November 27, 2025 -**Status:** IN PROGRESS - ---- - -## 🎯 GOALS - -1. βœ… Add Appearance menu to both Sidebar and TopNav -2. βœ… Fix path conflict (was `/settings/customer-spa`, now `/appearance`) -3. βœ… Move CustomerSPA.tsx to Appearance folder -4. βœ… Create page-specific submenus structure -5. ⏳ Create placeholder pages for each submenu -6. ⏳ Update App.tsx routes - ---- - -## πŸ“ NEW FOLDER STRUCTURE - -``` -admin-spa/src/routes/ -β”œβ”€β”€ Appearance/ ← NEW FOLDER -β”‚ β”œβ”€β”€ index.tsx ← Redirects to /appearance/themes -β”‚ β”œβ”€β”€ Themes.tsx ← Moved from Settings/CustomerSPA.tsx -β”‚ β”œβ”€β”€ Shop.tsx ← Shop page appearance -β”‚ β”œβ”€β”€ Product.tsx ← Product page appearance -β”‚ β”œβ”€β”€ Cart.tsx ← Cart page appearance -β”‚ β”œβ”€β”€ Checkout.tsx ← Checkout page appearance -β”‚ β”œβ”€β”€ ThankYou.tsx ← Thank you page appearance -β”‚ └── Account.tsx ← My Account/Customer Portal appearance -└── Settings/ - β”œβ”€β”€ Store.tsx - β”œβ”€β”€ Payments.tsx - β”œβ”€β”€ Shipping.tsx - β”œβ”€β”€ Tax.tsx - β”œβ”€β”€ Customers.tsx - β”œβ”€β”€ Notifications.tsx - └── Developer.tsx -``` - ---- - -## πŸ—ΊοΈ NAVIGATION STRUCTURE - -### **Appearance Menu** -- **Path:** `/appearance` -- **Icon:** `palette` -- **Submenus:** - 1. **Themes** β†’ `/appearance/themes` (Main SPA activation & layout selection) - 2. **Shop** β†’ `/appearance/shop` (Shop page customization) - 3. **Product** β†’ `/appearance/product` (Product page customization) - 4. **Cart** β†’ `/appearance/cart` (Cart page customization) - 5. **Checkout** β†’ `/appearance/checkout` (Checkout page customization) - 6. **Thank You** β†’ `/appearance/thankyou` (Order confirmation page) - 7. **My Account** β†’ `/appearance/account` (Customer portal customization) - ---- - -## βœ… CHANGES MADE - -### **1. Backend - NavigationRegistry.php** -```php -[ - 'key' => 'appearance', - 'label' => __('Appearance', 'woonoow'), - 'path' => '/appearance', // Changed from /settings/customer-spa - 'icon' => 'palette', - 'children' => [ - ['label' => __('Themes', 'woonoow'), 'mode' => 'spa', 'path' => '/appearance/themes'], - ['label' => __('Shop', 'woonoow'), 'mode' => 'spa', 'path' => '/appearance/shop'], - ['label' => __('Product', 'woonoow'), 'mode' => 'spa', 'path' => '/appearance/product'], - ['label' => __('Cart', 'woonoow'), 'mode' => 'spa', 'path' => '/appearance/cart'], - ['label' => __('Checkout', 'woonoow'), 'mode' => 'spa', 'path' => '/appearance/checkout'], - ['label' => __('Thank You', 'woonoow'), 'mode' => 'spa', 'path' => '/appearance/thankyou'], - ['label' => __('My Account', 'woonoow'), 'mode' => 'spa', 'path' => '/appearance/account'], - ], -], -``` - -**Version bumped:** `1.0.3` - -### **2. Frontend - App.tsx** - -**Added Palette icon:** -```tsx -import { ..., Palette, ... } from 'lucide-react'; -``` - -**Updated Sidebar to use dynamic navigation:** -```tsx -function Sidebar() { - const iconMap: Record = { - 'layout-dashboard': LayoutDashboard, - 'receipt-text': ReceiptText, - 'package': Package, - 'tag': Tag, - 'users': Users, - 'palette': Palette, // ← NEW - 'settings': SettingsIcon, - }; - - const navTree = (window as any).WNW_NAV_TREE || []; - - return ( - - ); -} -``` - -**Updated TopNav to use dynamic navigation:** -```tsx -function TopNav({ fullscreen = false }: { fullscreen?: boolean }) { - // Same icon mapping and navTree logic as Sidebar - const navTree = (window as any).WNW_NAV_TREE || []; - - return ( -
- {navTree.map((item: any) => { - const IconComponent = iconMap[item.icon] || Package; - return ; - })} -
- ); -} -``` - -### **3. File Moves** -- βœ… Created `/admin-spa/src/routes/Appearance/` folder -- βœ… Moved `Settings/CustomerSPA.tsx` β†’ `Appearance/Themes.tsx` -- βœ… Created `Appearance/index.tsx` (redirects to themes) -- βœ… Created `Appearance/Shop.tsx` (placeholder) - ---- - -## ⏳ TODO - -### **Create Remaining Placeholder Pages:** -1. `Appearance/Product.tsx` -2. `Appearance/Cart.tsx` -3. `Appearance/Checkout.tsx` -4. `Appearance/ThankYou.tsx` -5. `Appearance/Account.tsx` - -### **Update App.tsx Routes:** -```tsx -// Add imports -import AppearanceIndex from '@/routes/Appearance'; -import AppearanceThemes from '@/routes/Appearance/Themes'; -import AppearanceShop from '@/routes/Appearance/Shop'; -import AppearanceProduct from '@/routes/Appearance/Product'; -import AppearanceCart from '@/routes/Appearance/Cart'; -import AppearanceCheckout from '@/routes/Appearance/Checkout'; -import AppearanceThankYou from '@/routes/Appearance/ThankYou'; -import AppearanceAccount from '@/routes/Appearance/Account'; - -// Add routes -} /> -} /> -} /> -} /> -} /> -} /> -} /> -} /> -``` - -### **Remove Old Route:** -```tsx -// DELETE THIS: -} /> -``` - ---- - -## 🎨 DESIGN PHILOSOPHY - -Each Appearance submenu will allow customization of: - -1. **Themes** - Overall SPA activation, layout selection (Classic/Modern/Boutique/Launch) -2. **Shop** - Product grid, filters, sorting, categories display -3. **Product** - Image gallery, description layout, reviews, related products -4. **Cart** - Cart table, coupon input, shipping calculator -5. **Checkout** - Form fields, payment methods, order summary -6. **Thank You** - Order confirmation message, next steps, upsells -7. **My Account** - Dashboard, orders, addresses, downloads - ---- - -## πŸ” VERIFICATION - -After completing TODO: - -1. βœ… Appearance shows in Sidebar (both fullscreen and normal) -2. βœ… Appearance shows in TopNav -3. βœ… Clicking Appearance goes to `/appearance` β†’ redirects to `/appearance/themes` -4. βœ… Settings menu is NOT active when on Appearance -5. βœ… All 7 submenus are accessible -6. βœ… No 404 errors - ---- - -**Last Updated:** November 27, 2025 -**Version:** 1.0.3 -**Status:** Awaiting route updates in App.tsx diff --git a/BITESHIP_ADDON_SPEC.md b/BITESHIP_ADDON_SPEC.md deleted file mode 100644 index 057f3d2..0000000 --- a/BITESHIP_ADDON_SPEC.md +++ /dev/null @@ -1,260 +0,0 @@ -# WooNooW Indonesia Shipping (Biteship Integration) - -## Plugin Specification - -**Plugin Name:** WooNooW Indonesia Shipping -**Description:** Simple Indonesian shipping integration using Biteship Rate API -**Version:** 1.0.0 -**Requires:** WooNooW 1.0.0+, WooCommerce 8.0+ -**License:** GPL v2 or later - ---- - -## Overview - -A lightweight shipping plugin that integrates Biteship's Rate API with WooNooW SPA, providing: -- βœ… Indonesian address fields (Province, City, District, Subdistrict) -- βœ… Real-time shipping rate calculation -- βœ… Multiple courier support (JNE, SiCepat, J&T, AnterAja, etc.) -- βœ… Works in both frontend checkout AND admin order form -- βœ… No subscription required (uses free Biteship Rate API) - ---- - -## Features Roadmap - -### Phase 1: Core Functionality -- [ ] WooCommerce Shipping Method integration -- [ ] Biteship Rate API integration -- [ ] Indonesian address database (Province β†’ Subdistrict) -- [ ] Frontend checkout integration -- [ ] Admin settings page - -### Phase 2: SPA Integration -- [ ] REST API endpoints for address data -- [ ] REST API for rate calculation -- [ ] React components (SubdistrictSelector, CourierSelector) -- [ ] Hook integration with WooNooW OrderForm -- [ ] Admin order form support - -### Phase 3: Advanced Features -- [ ] Rate caching (reduce API calls) -- [ ] Custom rate markup -- [ ] Free shipping threshold -- [ ] Multi-origin support -- [ ] Shipping label generation (optional, requires paid Biteship plan) - ---- - -## Plugin Structure - -``` -woonoow-indonesia-shipping/ -β”œβ”€β”€ woonoow-indonesia-shipping.php # Main plugin file -β”œβ”€β”€ includes/ -β”‚ β”œβ”€β”€ class-shipping-method.php # WooCommerce shipping method -β”‚ β”œβ”€β”€ class-biteship-api.php # Biteship API client -β”‚ β”œβ”€β”€ class-address-database.php # Indonesian address data -β”‚ β”œβ”€β”€ class-addon-integration.php # WooNooW addon integration -β”‚ └── Api/ -β”‚ └── AddressController.php # REST API endpoints -β”œβ”€β”€ admin/ -β”‚ β”œβ”€β”€ class-settings.php # Admin settings page -β”‚ └── views/ -β”‚ └── settings-page.php # Settings UI -β”œβ”€β”€ admin-spa/ -β”‚ β”œβ”€β”€ src/ -β”‚ β”‚ β”œβ”€β”€ components/ -β”‚ β”‚ β”‚ β”œβ”€β”€ SubdistrictSelector.tsx # Address selector -β”‚ β”‚ β”‚ └── CourierSelector.tsx # Courier selection -β”‚ β”‚ β”œβ”€β”€ hooks/ -β”‚ β”‚ β”‚ β”œβ”€β”€ useAddressData.ts # Fetch address data -β”‚ β”‚ β”‚ └── useRateCalculation.ts # Calculate rates -β”‚ β”‚ └── index.ts # Addon registration -β”‚ β”œβ”€β”€ package.json -β”‚ └── vite.config.ts -β”œβ”€β”€ data/ -β”‚ └── indonesia-areas.sql # Address database dump -└── README.md -``` - ---- - -## Database Schema - -```sql -CREATE TABLE `wp_woonoow_indonesia_areas` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `biteship_area_id` varchar(50) NOT NULL, - `name` varchar(255) NOT NULL, - `type` enum('province','city','district','subdistrict') NOT NULL, - `parent_id` bigint(20) DEFAULT NULL, - `postal_code` varchar(10) DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `biteship_area_id` (`biteship_area_id`), - KEY `parent_id` (`parent_id`), - KEY `type` (`type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -``` - ---- - -## WooCommerce Shipping Method - -```php -id = 'woonoow_indonesia_shipping'; - $this->instance_id = absint($instance_id); - $this->method_title = __('Indonesia Shipping', 'woonoow-indonesia-shipping'); - $this->supports = array('shipping-zones', 'instance-settings'); - $this->init(); - } - - public function init_form_fields() { - $this->instance_form_fields = array( - 'api_key' => array( - 'title' => 'Biteship API Key', - 'type' => 'text' - ), - 'origin_subdistrict_id' => array( - 'title' => 'Origin Subdistrict', - 'type' => 'select', - 'options' => $this->get_subdistrict_options() - ), - 'couriers' => array( - 'title' => 'Available Couriers', - 'type' => 'multiselect', - 'options' => array( - 'jne' => 'JNE', - 'sicepat' => 'SiCepat', - 'jnt' => 'J&T Express' - ) - ) - ); - } - - public function calculate_shipping($package = array()) { - $origin = $this->get_option('origin_subdistrict_id'); - $destination = $package['destination']['subdistrict_id'] ?? null; - - if (!$origin || !$destination) return; - - $api = new WooNooW_Biteship_API($this->get_option('api_key')); - $rates = $api->get_rates($origin, $destination, $package); - - foreach ($rates as $rate) { - $this->add_rate(array( - 'id' => $this->id . ':' . $rate['courier_code'], - 'label' => $rate['courier_name'] . ' - ' . $rate['service_name'], - 'cost' => $rate['price'] - )); - } - } -} -``` - ---- - -## REST API Endpoints - -```php - 'GET', - 'callback' => 'get_provinces' -)); - -register_rest_route('woonoow/v1', '/indonesia-shipping/calculate-rates', array( - 'methods' => 'POST', - 'callback' => 'calculate_rates' -)); -``` - ---- - -## React Components - -```typescript -// admin-spa/src/components/SubdistrictSelector.tsx - -export function SubdistrictSelector({ value, onChange }) { - const [provinceId, setProvinceId] = useState(''); - const [cityId, setCityId] = useState(''); - - const { data: provinces } = useQuery({ - queryKey: ['provinces'], - queryFn: () => api.get('/indonesia-shipping/provinces') - }); - - return ( -
- - -) : ( - -)} -``` - ---- - -### **3. Icon-Based Actions** -**Shopify Pattern:** -- Icons for cart, account, search -- Less visual clutter -- Better mobile experience - -**Our Implementation:** -```tsx - -``` - ---- - -### **4. Spacing & Height** -**Shopify Pattern:** -- Generous padding (py-4 to py-6) -- Taller header (h-20 vs h-16) -- Better breathing room - -**Our Implementation:** -```tsx -
{/* was h-16 */} -
-``` - ---- - -### **5. Mobile Menu** -**Shopify Pattern:** -- Full-screen or slide-out menu -- Includes search -- Easy to close (X icon) - -**Our Implementation:** -```tsx -{mobileMenuOpen && ( -
- -
-)} -``` - ---- - -### **6. Social Media Integration** -**Shopify Pattern:** -- Social icons in footer -- Circular design -- Hover effects - -**Our Implementation:** -```tsx - - - -``` - ---- - -### **7. Payment Trust Badges** -**Shopify Pattern:** -- Payment method logos -- "We Accept" label -- Professional presentation - -**Our Implementation:** -```tsx -
- We Accept -
-
- VISA -
- {/* More payment methods */} -
-
-``` - ---- - -### **8. Newsletter Signup** -**Shopify Pattern:** -- Styled input with button -- Clear call-to-action -- Privacy notice - -**Our Implementation:** -```tsx -
- - -
-

- By subscribing, you agree to our Privacy Policy. -

-``` - ---- - -## 🎨 HEADER IMPROVEMENTS - -### **1. Logo Enhancement** -- βœ… Icon + text combination -- βœ… Serif font for elegance -- βœ… Hover effect -- βœ… Better visual weight - -### **2. Navigation** -- βœ… Clear hierarchy -- βœ… Better spacing (gap-8) -- βœ… Hover states -- βœ… Mobile-responsive - -### **3. Search Functionality** -- βœ… Expandable search bar -- βœ… Auto-focus on open -- βœ… Close button (X) -- βœ… Mobile search in menu - -### **4. Cart Display** -- βœ… Icon with badge -- βœ… Item count visible -- βœ… "Cart (0)" text on desktop -- βœ… Better hover state - -### **5. Mobile Menu** -- βœ… Slide-in animation -- βœ… Full navigation -- βœ… Search included -- βœ… Close button - -### **6. Sticky Behavior** -- βœ… Stays at top on scroll -- βœ… Shadow for depth -- βœ… Backdrop blur effect -- βœ… Z-index management - ---- - -## 🎨 FOOTER IMPROVEMENTS - -### **1. Brand Section** -- βœ… Logo + description -- βœ… Social media icons -- βœ… 2-column span -- βœ… Better visual weight - -### **2. Link Organization** -- βœ… 5-column layout -- βœ… Clear categories -- βœ… More links per section -- βœ… Better hierarchy - -### **3. Newsletter** -- βœ… Styled input field -- βœ… Icon button -- βœ… Privacy notice -- βœ… Professional appearance - -### **4. Payment Badges** -- βœ… "We Accept" label -- βœ… Card logos -- βœ… Clean presentation -- βœ… Trust indicators - -### **5. Legal Links** -- βœ… Privacy Policy -- βœ… Terms of Service -- βœ… Sitemap -- βœ… Bullet separators - -### **6. Multi-tier Structure** -- βœ… Main content (py-12) -- βœ… Payment section (py-6) -- βœ… Copyright (py-6) -- βœ… Clear separation - ---- - -## πŸ“Š TECHNICAL IMPLEMENTATION - -### **Header State Management:** -```tsx -const [mobileMenuOpen, setMobileMenuOpen] = useState(false); -const [searchOpen, setSearchOpen] = useState(false); -``` - -### **Responsive Breakpoints:** -- Mobile: < 768px (full mobile menu) -- Tablet: 768px - 1024px (partial features) -- Desktop: > 1024px (full navigation) - -### **Animation Classes:** -```tsx -className="animate-in fade-in slide-in-from-right-5" -className="animate-in slide-in-from-top-5" -``` - -### **Color Palette:** -- Primary: Gray-900 (#111827) -- Background: White (#FFFFFF) -- Muted: Gray-50 (#F9FAFB) -- Text: Gray-600, Gray-700, Gray-900 -- Borders: Gray-200 - ---- - -## βœ… FEATURE CHECKLIST - -### **Header:** -- [x] Logo icon + text -- [x] Serif typography -- [x] Search functionality -- [x] Icon-based actions -- [x] Cart badge -- [x] Mobile menu -- [x] Sticky behavior -- [x] Hover states -- [x] Responsive design - -### **Footer:** -- [x] Brand description -- [x] Social media icons -- [x] 5-column layout -- [x] Newsletter signup -- [x] Payment badges -- [x] Legal links -- [x] Multi-tier structure -- [x] Responsive design - ---- - -## 🎯 BEFORE/AFTER METRICS - -### **Header:** -**Visual Quality:** -- Before: 5/10 (functional but generic) -- After: 9/10 (professional, polished) - -**Features:** -- Before: 3 features (logo, nav, cart) -- After: 8 features (logo, nav, search, cart, account, mobile menu, sticky, animations) - ---- - -### **Footer:** -**Visual Quality:** -- Before: 4/10 (basic, minimal) -- After: 9/10 (rich, professional) - -**Content Sections:** -- Before: 4 sections -- After: 8 sections (brand, shop, service, newsletter, social, payment, legal, copyright) - ---- - -## πŸš€ EXPECTED IMPACT - -### **User Experience:** -- βœ… Easier navigation -- βœ… Better search access -- βœ… More trust indicators -- βœ… Professional appearance -- βœ… Mobile-friendly - -### **Brand Perception:** -- βœ… More credible -- βœ… More professional -- βœ… More trustworthy -- βœ… Better first impression - -### **Conversion Rate:** -- βœ… Easier product discovery (search) -- βœ… Better mobile experience -- βœ… More trust signals -- βœ… Expected lift: +10-15% - ---- - -## πŸ“± RESPONSIVE BEHAVIOR - -### **Header:** -**Mobile (< 768px):** -- Logo icon only -- Hamburger menu -- Search in menu - -**Tablet (768px - 1024px):** -- Logo icon + text -- Some navigation -- Search icon - -**Desktop (> 1024px):** -- Full logo -- Full navigation -- Expandable search -- Cart with text - ---- - -### **Footer:** -**Mobile (< 768px):** -- 1 column stack -- All sections visible -- Centered content - -**Tablet (768px - 1024px):** -- 2 columns -- Better spacing - -**Desktop (> 1024px):** -- 5 columns -- Full layout -- Optimal spacing - ---- - -## πŸŽ‰ CONCLUSION - -**The header and footer have been completely transformed from basic, functional elements into professional, conversion-optimized components that match Shopify quality standards.** - -### **Key Achievements:** - -**Header:** -- βœ… Professional logo with icon -- βœ… Expandable search functionality -- βœ… Icon-based actions -- βœ… Full mobile menu -- βœ… Better spacing and typography - -**Footer:** -- βœ… Rich content with 5 columns -- βœ… Social media integration -- βœ… Payment trust badges -- βœ… Styled newsletter signup -- βœ… Multi-tier structure - -### **Overall Impact:** -- Visual Quality: 4.5/10 β†’ 9/10 -- Feature Richness: Basic β†’ Comprehensive -- Brand Perception: Generic β†’ Professional -- User Experience: Functional β†’ Excellent - ---- - -**Status:** βœ… PRODUCTION READY - -**Files Modified:** -1. `customer-spa/src/components/Layout/Header.tsx` -2. `customer-spa/src/components/Layout/Footer.tsx` - -**No Breaking Changes:** -- All existing functionality preserved -- Enhanced with new features -- Backward compatible - ---- - -**Last Updated:** November 26, 2025 -**Version:** 2.0.0 -**Status:** Ready for Deployment βœ… diff --git a/IMPLEMENTATION_PLAN_META_COMPAT.md b/IMPLEMENTATION_PLAN_META_COMPAT.md deleted file mode 100644 index 8ade2ac..0000000 --- a/IMPLEMENTATION_PLAN_META_COMPAT.md +++ /dev/null @@ -1,640 +0,0 @@ -# Implementation Plan: Level 1 Meta Compatibility - -## Objective -Make WooNooW listen to ALL standard WordPress/WooCommerce hooks for custom meta fields automatically. - -## Principles (From Documentation Review) - -### From ADDON_BRIDGE_PATTERN.md: -1. βœ… WooNooW Core = Zero addon dependencies -2. βœ… We listen to WP/WooCommerce hooks (NOT WooNooW-specific) -3. βœ… Community does NOTHING extra -4. ❌ We do NOT support specific plugins -5. ❌ We do NOT integrate plugins into core - -### From ADDON_DEVELOPMENT_GUIDE.md: -1. βœ… Hook system for functional extensions -2. βœ… Zero coupling with core -3. βœ… WordPress-style filters and actions - -### From ADDON_REACT_INTEGRATION.md: -1. βœ… Expose React runtime on window -2. βœ… Support vanilla JS/jQuery addons -3. βœ… No build process required for simple addons - ---- - -## Implementation Strategy - -### Phase 1: Backend API Enhancement (2-3 days) - -#### 1.1 OrdersController - Expose Meta Data - -**File:** `includes/Api/OrdersController.php` - -**Changes:** -```php -public static function show(WP_REST_Request $req) { - $order = wc_get_order($id); - - // ... existing data ... - - // Expose meta data (Level 1 compatibility) - $meta_data = self::get_order_meta_data($order); - $data['meta'] = $meta_data; - - // Allow plugins to modify response - $data = apply_filters('woonoow/order_api_data', $data, $order, $req); - - return new WP_REST_Response($data, 200); -} - -/** - * Get order meta data for API exposure - * Filters out internal meta unless explicitly allowed - */ -private static function get_order_meta_data($order) { - $meta_data = []; - - foreach ($order->get_meta_data() as $meta) { - $key = $meta->key; - $value = $meta->value; - - // Skip internal WooCommerce meta (starts with _wc_) - if (strpos($key, '_wc_') === 0) { - continue; - } - - // Public meta (no underscore) - always expose - if (strpos($key, '_') !== 0) { - $meta_data[$key] = $value; - continue; - } - - // Private meta (starts with _) - check if allowed - $allowed_private = apply_filters('woonoow/order_allowed_private_meta', [ - // Common shipping tracking fields - '_tracking_number', - '_tracking_provider', - '_tracking_url', - '_shipment_tracking_items', - '_wc_shipment_tracking_items', - - // Allow plugins to add their meta - ], $order); - - if (in_array($key, $allowed_private, true)) { - $meta_data[$key] = $value; - } - } - - return $meta_data; -} -``` - -**Update Method:** -```php -public static function update(WP_REST_Request $req) { - $order = wc_get_order($id); - $data = $req->get_json_params(); - - // ... existing update logic ... - - // Update custom meta fields (Level 1 compatibility) - if (isset($data['meta']) && is_array($data['meta'])) { - self::update_order_meta_data($order, $data['meta']); - } - - $order->save(); - - // Allow plugins to perform additional updates - do_action('woonoow/order_updated', $order, $data, $req); - - return new WP_REST_Response(['success' => true], 200); -} - -/** - * Update order meta data from API - */ -private static function update_order_meta_data($order, $meta_updates) { - // Get allowed updatable meta keys - $allowed = apply_filters('woonoow/order_updatable_meta', [ - '_tracking_number', - '_tracking_provider', - '_tracking_url', - // Allow plugins to add their meta - ], $order); - - foreach ($meta_updates as $key => $value) { - // Public meta (no underscore) - always allow - if (strpos($key, '_') !== 0) { - $order->update_meta_data($key, $value); - continue; - } - - // Private meta - check if allowed - if (in_array($key, $allowed, true)) { - $order->update_meta_data($key, $value); - } - } -} -``` - -#### 1.2 ProductsController - Expose Meta Data - -**File:** `includes/Api/ProductsController.php` - -**Changes:** (Same pattern as OrdersController) -```php -public static function get_product(WP_REST_Request $request) { - $product = wc_get_product($id); - - // ... existing data ... - - // Expose meta data (Level 1 compatibility) - $meta_data = self::get_product_meta_data($product); - $data['meta'] = $meta_data; - - // Allow plugins to modify response - $data = apply_filters('woonoow/product_api_data', $data, $product, $request); - - return new WP_REST_Response($data, 200); -} - -private static function get_product_meta_data($product) { - // Same logic as orders -} - -public static function update_product(WP_REST_Request $request) { - // ... existing logic ... - - if (isset($data['meta']) && is_array($data['meta'])) { - self::update_product_meta_data($product, $data['meta']); - } - - do_action('woonoow/product_updated', $product, $data, $request); -} -``` - ---- - -### Phase 2: Frontend Components (3-4 days) - -#### 2.1 MetaFields Component - -**File:** `admin-spa/src/components/MetaFields.tsx` - -**Purpose:** Generic component to display/edit meta fields - -```tsx -interface MetaField { - key: string; - label: string; - type: 'text' | 'textarea' | 'number' | 'select' | 'date' | 'checkbox'; - options?: Array<{value: string; label: string}>; - section?: string; - description?: string; - placeholder?: string; -} - -interface MetaFieldsProps { - meta: Record; - fields: MetaField[]; - onChange: (key: string, value: any) => void; - readOnly?: boolean; -} - -export function MetaFields({ meta, fields, onChange, readOnly }: MetaFieldsProps) { - if (fields.length === 0) return null; - - // Group fields by section - const sections = fields.reduce((acc, field) => { - const section = field.section || 'Additional Fields'; - if (!acc[section]) acc[section] = []; - acc[section].push(field); - return acc; - }, {} as Record); - - return ( -
- {Object.entries(sections).map(([section, sectionFields]) => ( - - - {section} - - - {sectionFields.map(field => ( -
- - - {field.type === 'text' && ( - onChange(field.key, e.target.value)} - disabled={readOnly} - placeholder={field.placeholder} - /> - )} - - {field.type === 'textarea' && ( -