- Add ModuleRegistry for managing built-in modules (newsletter, wishlist, affiliate, subscription, licensing) - Add ModulesController REST API for module enable/disable - Create Modules settings page with category grouping and toggle controls - Integrate module checks across admin-spa and customer-spa - Add useModules hook for both SPAs to check module status - Hide newsletter from footer builder when module disabled - Hide wishlist features when module disabled (product cards, account menu, wishlist page) - Protect wishlist API endpoints with module checks - Auto-update navigation tree when modules toggled - Clean up obsolete documentation files - Add comprehensive documentation: - MODULE_SYSTEM_IMPLEMENTATION.md - MODULE_INTEGRATION_SUMMARY.md - ADDON_MODULE_INTEGRATION.md (proposal) - ADDON_MODULE_DESIGN_DECISIONS.md (design doc) - FEATURE_ROADMAP.md - SHIPPING_INTEGRATION.md Module system provides: - Centralized enable/disable for all features - Automatic navigation updates - Frontend/backend integration - Foundation for addon-module unification
477 lines
14 KiB
Markdown
477 lines
14 KiB
Markdown
# 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) => (
|
|
<div className="flex items-start gap-4 p-4 border rounded-lg">
|
|
{/* Icon */}
|
|
<div className={`p-3 rounded-lg ${module.enabled ? 'bg-primary/10' : 'bg-muted'}`}>
|
|
{getIcon(module.icon)}
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className="flex-1">
|
|
<div className="flex items-center gap-2 mb-1">
|
|
<h3 className="font-medium">{module.label}</h3>
|
|
{module.enabled && <Badge>Active</Badge>}
|
|
{module.is_addon && <Badge variant="outline">Addon</Badge>}
|
|
</div>
|
|
<p className="text-sm text-muted-foreground mb-2">{module.description}</p>
|
|
|
|
{/* Features */}
|
|
<ul className="space-y-1">
|
|
{module.features.map((feature, i) => (
|
|
<li key={i} className="text-xs text-muted-foreground">
|
|
• {feature}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
|
|
{/* Actions */}
|
|
<div className="flex items-center gap-2">
|
|
{/* Settings Gear Icon - Only if module has settings */}
|
|
{module.settings_url && module.enabled && (
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={() => navigate(module.settings_url)}
|
|
title="Module Settings"
|
|
>
|
|
<Settings className="h-4 w-4" />
|
|
</Button>
|
|
)}
|
|
|
|
{/* Enable/Disable Toggle */}
|
|
<Switch
|
|
checked={module.enabled}
|
|
onCheckedChange={(enabled) => toggleModule.mutate({ moduleId: module.id, enabled })}
|
|
/>
|
|
</div>
|
|
</div>
|
|
))}
|
|
```
|
|
|
|
---
|
|
|
|
### 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 (
|
|
<SettingsLayout title="Module Management">
|
|
{categories.map((category) => {
|
|
const modules = modulesData.grouped[category] || [];
|
|
if (modules.length === 0) return null;
|
|
|
|
return (
|
|
<SettingsCard
|
|
key={category}
|
|
title={getCategoryLabel(category)}
|
|
description={`Manage ${category} modules`}
|
|
>
|
|
{/* Module cards */}
|
|
</SettingsCard>
|
|
);
|
|
})}
|
|
</SettingsLayout>
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 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
|
|
<?php
|
|
/**
|
|
* Plugin Name: WooNooW Biteship Shipping
|
|
* Description: Indonesia shipping with Biteship API
|
|
* Version: 1.0.0
|
|
* Author: WooNooW Team
|
|
*/
|
|
|
|
add_filter('woonoow/addon_registry', function($addons) {
|
|
$addons['biteship-shipping'] = [
|
|
'id' => '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
|
|
<Dialog open={settingsOpen} onOpenChange={setSettingsOpen}>
|
|
<DialogContent>
|
|
<AddonSettings moduleId={selectedModule} />
|
|
</DialogContent>
|
|
</Dialog>
|
|
```
|
|
|
|
---
|
|
|
|
## 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.**
|