# 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
<?php
/**
 * Plugin Name: My WooNooW Addon
 * Description: Extends WooNooW functionality
 * Version: 1.0.0
 * Requires: WooNooW 1.0.0+
 */

// 1. Register addon
add_filter('woonoow/addon_registry', function($addons) {
    $addons['my-addon'] = [
        'id'           => '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}
          <MyCustomSection data={formData} onChange={setFormData} />
        </>
      );
    });
  }
});
```

### 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}
          <SubdistrictSelector
            value={formData.shipping?.subdistrict_id}
            onChange={(id) => 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}<AddonAFields /></>;
});

// Addon B
addFilter('woonoow_order_form_after_shipping', (content) => {
  return <>{content}<AddonBFields /></>;
});

// Both render!
```

✅ **Type Safety**
```typescript
addFilter<ReactNode, [OrderFormData, SetState<OrderFormData>]>(
  'woonoow_order_form_after_shipping',
  (content, formData, setFormData) => {
    // TypeScript knows the types!
    return <MyComponent />;
  }
);
```

---

## Component Development

### Basic Component

```typescript
// dist/MyPage.tsx
import React from 'react';

export default function MyPage() {
  return (
    <div className="space-y-6">
      <div className="rounded-lg border p-6 bg-card">
        <h2 className="text-xl font-semibold mb-2">My Addon</h2>
        <p className="text-sm opacity-70">Welcome!</p>
      </div>
    </div>
  );
}
```

### 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 (
    <Card className="p-6">
      <h2>{__('My Addon', 'my-addon')}</h2>
      <p>{formatMoney(1234.56)}</p>
      <Button>{__('Click Me', 'my-addon')}</Button>
    </Card>
  );
}
```

### 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';
   <SubdistrictSelector />
   
   // ✅ 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
<?php
/**
 * Plugin Name: WooNooW Reports
 * Description: Custom reports page
 */

add_filter('woonoow/addon_registry', function($addons) {
    $addons['reports'] = [
        'id'      => '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}
          <div className="border rounded-lg p-4 mt-4">
            <h3 className="font-medium mb-3">📍 Shipping Destination</h3>
            <SubdistrictSelector
              value={formData.shipping?.subdistrict_id}
              onChange={(id) => setFormData({
                ...formData,
                shipping: { ...formData.shipping, subdistrict_id: id }
              })}
            />
          </div>
        </>
      );
    });
  }
});
```

### Example 3: Full-Featured Addon (Both Systems)

```php
<?php
/**
 * Plugin Name: WooNooW Subscriptions
 * Description: Subscription management
 */

// Backend: Register addon + routes
add_filter('woonoow/addon_registry', function($addons) {
    $addons['subscriptions'] = [
        'id'         => '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}
          <SubscriptionOptions data={formData} onChange={setFormData} />
        </>
      );
    });

    // Add subscription fields to product form
    addFilter('woonoow_product_form_fields', (content, formData, setFormData) => {
      return (
        <>
          {content}
          <SubscriptionSettings data={formData} onChange={setFormData} />
        </>
      );
    });
  }
});
```

---

## 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.**
