# WooNooW Addon Development Guide **Version:** 2.0.0 **Last Updated:** November 9, 2025 **Status:** Production Ready --- ## 📋 Table of Contents 1. [Overview](#overview) 2. [Addon Types](#addon-types) 3. [Quick Start](#quick-start) 4. [SPA Route Injection](#spa-route-injection) 5. [Hook System Integration](#hook-system-integration) 6. [Component Development](#component-development) 7. [Best Practices](#best-practices) 8. [Examples](#examples) 9. [Troubleshooting](#troubleshooting) --- ## Overview WooNooW provides **two powerful addon systems**: ### 1. **SPA Route Injection** (Admin UI) - ✅ Register custom SPA routes - ✅ Inject navigation menu items - ✅ Add submenu items to existing sections - ✅ Load React components dynamically - ✅ Full isolation and safety ### 2. **Hook System** (Functional Extension) - ✅ Extend OrderForm, ProductForm, etc. - ✅ Add custom fields and validation - ✅ Inject components at specific points - ✅ Zero coupling with core - ✅ WordPress-style filters and actions **Both systems work together seamlessly!** --- ## Addon Types ### Type A: UI-Only Addon (Route Injection) **Use when:** Adding new pages/sections to admin **Example:** Reports, Analytics, Custom Dashboard ```php // Registers routes + navigation add_filter('woonoow/spa_routes', ...); add_filter('woonoow/nav_tree', ...); ``` ### Type B: Functional Addon (Hook System) **Use when:** Extending existing functionality **Example:** Indonesia Shipping, Custom Fields, Validation ```typescript // Registers hooks addFilter('woonoow_order_form_after_shipping', ...); addAction('woonoow_order_created', ...); ``` ### Type C: Full-Featured Addon (Both Systems) **Use when:** Complex integration needed **Example:** Subscriptions, Bookings, Memberships ```php // Backend: Routes + Hooks add_filter('woonoow/spa_routes', ...); add_filter('woonoow/nav_tree', ...); // Frontend: Hook registration addonLoader.register({ init: () => { addFilter('woonoow_order_form_custom_sections', ...); } }); ``` --- ## Quick Start ### Step 1: Create Plugin File ```php 'my-addon', 'name' => 'My Addon', 'version' => '1.0.0', 'spa_bundle' => plugin_dir_url(__FILE__) . 'dist/addon.js', 'dependencies' => ['woocommerce' => '8.0'], ]; return $addons; }); // 2. Register routes (optional - for UI pages) add_filter('woonoow/spa_routes', function($routes) { $routes[] = [ 'path' => '/my-addon', 'component_url' => plugin_dir_url(__FILE__) . 'dist/MyPage.js', 'capability' => 'manage_woocommerce', 'title' => 'My Addon', ]; return $routes; }); // 3. Add navigation (optional - for UI pages) add_filter('woonoow/nav_tree', function($tree) { $tree[] = [ 'key' => 'my-addon', 'label' => 'My Addon', 'path' => '/my-addon', 'icon' => 'puzzle', ]; return $tree; }); ``` ### Step 2: Create Frontend Integration ```typescript // admin-spa/src/index.ts import { addonLoader, addFilter } from '@woonoow/hooks'; addonLoader.register({ id: 'my-addon', name: 'My Addon', version: '1.0.0', init: () => { // Register hooks here addFilter('woonoow_order_form_custom_sections', (content, formData, setFormData) => { return ( <> {content} ); }); } }); ``` ### Step 3: Build ```bash npm run build ``` **Done!** Your addon is now integrated. --- ## SPA Route Injection ### Register Routes ```php add_filter('woonoow/spa_routes', function($routes) { $base_url = plugin_dir_url(__FILE__) . 'dist/'; $routes[] = [ 'path' => '/subscriptions', 'component_url' => $base_url . 'SubscriptionsList.js', 'capability' => 'manage_woocommerce', 'title' => 'Subscriptions', ]; $routes[] = [ 'path' => '/subscriptions/:id', 'component_url' => $base_url . 'SubscriptionDetail.js', 'capability' => 'manage_woocommerce', 'title' => 'Subscription Detail', ]; return $routes; }); ``` ### Add Navigation ```php // Main menu item add_filter('woonoow/nav_tree', function($tree) { $tree[] = [ 'key' => 'subscriptions', 'label' => __('Subscriptions', 'my-addon'), 'path' => '/subscriptions', 'icon' => 'repeat', 'children' => [ [ 'label' => __('All Subscriptions', 'my-addon'), 'mode' => 'spa', 'path' => '/subscriptions', ], [ 'label' => __('New', 'my-addon'), 'mode' => 'spa', 'path' => '/subscriptions/new', ], ], ]; return $tree; }); // Or inject into existing section add_filter('woonoow/nav_tree/products/children', function($children) { $children[] = [ 'label' => __('Bundles', 'my-addon'), 'mode' => 'spa', 'path' => '/products/bundles', ]; return $children; }); ``` --- ## Hook System Integration ### Available Hooks #### Order Form Hooks ```typescript // Add fields after billing address 'woonoow_order_form_after_billing' // Add fields after shipping address 'woonoow_order_form_after_shipping' // Add custom shipping fields 'woonoow_order_form_shipping_fields' // Add custom sections 'woonoow_order_form_custom_sections' // Add validation rules 'woonoow_order_form_validation' // Modify form data before render 'woonoow_order_form_data' ``` #### Action Hooks ```typescript // Before form submission 'woonoow_order_form_submit' // After order created 'woonoow_order_created' // After order updated 'woonoow_order_updated' ``` ### Hook Registration Example ```typescript import { addonLoader, addFilter, addAction } from '@woonoow/hooks'; addonLoader.register({ id: 'indonesia-shipping', name: 'Indonesia Shipping', version: '1.0.0', init: () => { // Filter: Add subdistrict selector addFilter('woonoow_order_form_after_shipping', (content, formData, setFormData) => { return ( <> {content} setFormData({ ...formData, shipping: { ...formData.shipping, subdistrict_id: id } })} /> ); }); // Filter: Add validation addFilter('woonoow_order_form_validation', (errors, formData) => { if (!formData.shipping?.subdistrict_id) { errors.subdistrict = 'Subdistrict is required'; } return errors; }); // Action: Log when order created addAction('woonoow_order_created', (orderId, orderData) => { console.log('Order created:', orderId); }); } }); ``` ### Hook System Benefits ✅ **Zero Coupling** ```typescript // WooNooW Core has no knowledge of your addon {applyFilters('woonoow_order_form_after_shipping', null, formData, setFormData)} // If addon exists: Returns your component // If addon doesn't exist: Returns null // No import, no error! ``` ✅ **Multiple Addons Can Hook** ```typescript // Addon A addFilter('woonoow_order_form_after_shipping', (content) => { return <>{content}; }); // Addon B addFilter('woonoow_order_form_after_shipping', (content) => { return <>{content}; }); // Both render! ``` ✅ **Type Safety** ```typescript addFilter]>( 'woonoow_order_form_after_shipping', (content, formData, setFormData) => { // TypeScript knows the types! return ; } ); ``` --- ## Component Development ### Basic Component ```typescript // dist/MyPage.tsx import React from 'react'; export default function MyPage() { return (

My Addon

Welcome!

); } ``` ### Access WooNooW APIs ```typescript // Access REST API const api = (window as any).WNW_API; const response = await fetch(`${api.root}my-addon/endpoint`, { headers: { 'X-WP-Nonce': api.nonce }, }); // Access store data const store = (window as any).WNW_STORE; console.log('Currency:', store.currency); // Access site info const wnw = (window as any).wnw; console.log('Site Title:', wnw.siteTitle); ``` ### Use WooNooW Components ```typescript import { __ } from '@/lib/i18n'; import { formatMoney } from '@/lib/currency'; import { Button } from '@/components/ui/button'; import { Card } from '@/components/ui/card'; export default function MyPage() { return (

{__('My Addon', 'my-addon')}

{formatMoney(1234.56)}

); } ``` ### Build Configuration ```javascript // vite.config.js import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()], build: { lib: { entry: 'src/index.ts', name: 'MyAddon', fileName: 'addon', formats: ['es'], }, rollupOptions: { external: ['react', 'react-dom'], output: { globals: { react: 'React', 'react-dom': 'ReactDOM', }, }, }, }, }); ``` --- ## Best Practices ### ✅ DO: 1. **Use Hook System for Functional Extensions** ```typescript // ✅ Good - No hardcoding addFilter('woonoow_order_form_after_shipping', ...); ``` 2. **Use Route Injection for New Pages** ```php // ✅ Good - Separate UI add_filter('woonoow/spa_routes', ...); ``` 3. **Declare Dependencies** ```php 'dependencies' => ['woocommerce' => '8.0'] ``` 4. **Check Capabilities** ```php 'capability' => 'manage_woocommerce' ``` 5. **Internationalize Strings** ```php 'label' => __('My Addon', 'my-addon') ``` 6. **Handle Errors Gracefully** ```typescript try { await api.post(...); } catch (error) { toast.error('Failed to save'); } ``` ### ❌ DON'T: 1. **Don't Hardcode Addon Components in Core** ```typescript // ❌ Bad - Breaks if addon not installed import { SubdistrictSelector } from 'addon'; // ✅ Good - Use hooks {applyFilters('woonoow_order_form_after_shipping', null)} ``` 2. **Don't Skip Capability Checks** ```php // ❌ Bad 'capability' => '' // ✅ Good 'capability' => 'manage_woocommerce' ``` 3. **Don't Modify Core Navigation** ```php // ❌ Bad unset($tree[0]); // ✅ Good $tree[] = ['key' => 'my-addon', ...]; ``` --- ## Examples ### Example 1: Simple UI Addon (Route Injection Only) ```php 'reports', 'name' => 'Reports', 'version' => '1.0.0', ]; return $addons; }); add_filter('woonoow/spa_routes', function($routes) { $routes[] = [ 'path' => '/reports', 'component_url' => plugin_dir_url(__FILE__) . 'dist/Reports.js', 'title' => 'Reports', ]; return $routes; }); add_filter('woonoow/nav_tree', function($tree) { $tree[] = [ 'key' => 'reports', 'label' => 'Reports', 'path' => '/reports', 'icon' => 'bar-chart', ]; return $tree; }); ``` ### Example 2: Functional Addon (Hook System Only) ```typescript // Indonesia Shipping - No UI pages, just extends OrderForm import { addonLoader, addFilter } from '@woonoow/hooks'; import { SubdistrictSelector } from './components/SubdistrictSelector'; addonLoader.register({ id: 'indonesia-shipping', name: 'Indonesia Shipping', version: '1.0.0', init: () => { addFilter('woonoow_order_form_after_shipping', (content, formData, setFormData) => { return ( <> {content}

📍 Shipping Destination

setFormData({ ...formData, shipping: { ...formData.shipping, subdistrict_id: id } })} />
); }); } }); ``` ### Example 3: Full-Featured Addon (Both Systems) ```php 'subscriptions', 'name' => 'Subscriptions', 'version' => '1.0.0', 'spa_bundle' => plugin_dir_url(__FILE__) . 'dist/addon.js', ]; return $addons; }); add_filter('woonoow/spa_routes', function($routes) { $routes[] = [ 'path' => '/subscriptions', 'component_url' => plugin_dir_url(__FILE__) . 'dist/SubscriptionsList.js', ]; return $routes; }); add_filter('woonoow/nav_tree', function($tree) { $tree[] = [ 'key' => 'subscriptions', 'label' => 'Subscriptions', 'path' => '/subscriptions', 'icon' => 'repeat', ]; return $tree; }); ``` ```typescript // Frontend: Hook integration import { addonLoader, addFilter } from '@woonoow/hooks'; addonLoader.register({ id: 'subscriptions', name: 'Subscriptions', version: '1.0.0', init: () => { // Add subscription fields to order form addFilter('woonoow_order_form_custom_sections', (content, formData, setFormData) => { return ( <> {content} ); }); // Add subscription fields to product form addFilter('woonoow_product_form_fields', (content, formData, setFormData) => { return ( <> {content} ); }); } }); ``` --- ## Troubleshooting ### Addon Not Appearing? - Check dependencies are met - Verify capability requirements - Check browser console for errors - Flush caches: `?flush_wnw_cache=1` ### Route Not Loading? - Verify `component_url` is correct - Check file exists and is accessible - Look for JS errors in console - Ensure component exports `default` ### Hook Not Firing? - Check hook name is correct - Verify addon is registered - Check `window.WNW_ADDONS` in console - Ensure `init()` function runs ### Component Not Rendering? - Check for React errors in console - Verify component returns valid JSX - Check props are passed correctly - Test component in isolation --- ## Support & Resources **Documentation:** - `ADDON_INJECTION_GUIDE.md` - SPA route injection (legacy) - `ADDON_HOOK_SYSTEM.md` - Hook system details (legacy) - `BITESHIP_ADDON_SPEC.md` - Indonesia shipping example - `SHIPPING_ADDON_RESEARCH.md` - Shipping integration patterns **Code References:** - `includes/Compat/AddonRegistry.php` - Addon registration - `includes/Compat/RouteRegistry.php` - Route management - `includes/Compat/NavigationRegistry.php` - Navigation building - `admin-spa/src/lib/hooks.ts` - Hook system implementation - `admin-spa/src/App.tsx` - Dynamic route loading --- **End of Guide** **Version:** 2.0.0 **Last Updated:** November 9, 2025 **Status:** ✅ Production Ready **This is the single source of truth for WooNooW addon development.**