Phase 2: Schema-Based Form System
- Add ModuleSettingsController with GET/POST/schema endpoints
- Create SchemaField component supporting 8 field types (text, textarea, email, url, number, toggle, checkbox, select)
- Create SchemaForm component for automatic form generation from schema
- Add ModuleSettings page with dynamic routing (/settings/modules/:moduleId)
- Add useModuleSettings React hook for settings management
- Implement NewsletterSettings as example with 8 configurable fields
- Add has_settings flag to module registry
- Settings stored as woonoow_module_{module_id}_settings
Phase 3: Advanced Features
- Create windowAPI.ts exposing React, hooks, components, icons, utils to addons via window.WooNooW
- Add DynamicComponentLoader for loading external React components
- Create TypeScript definitions (woonoow-addon.d.ts) for addon developers
- Initialize Window API in App.tsx on mount
- Enable custom React components for addon settings pages
Phase 4: Production Polish & Example
- Create complete Biteship addon example demonstrating both approaches:
* Schema-based settings (no build required)
* Custom React component (with build)
- Add comprehensive README with installation and testing guide
- Include package.json with esbuild configuration
- Demonstrate window.WooNooW API usage in custom component
Bug Fixes:
- Fix footer newsletter form visibility (remove redundant module check)
- Fix footer contact_data and social_links not saving (parameter name mismatch: snake_case vs camelCase)
- Fix useModules hook returning undefined (remove .data wrapper, add fallback)
- Add optional chaining to footer settings rendering
- Fix TypeScript errors in woonoow-addon.d.ts (use any for external types)
Files Added (15):
- includes/Api/ModuleSettingsController.php
- includes/Modules/NewsletterSettings.php
- admin-spa/src/components/forms/SchemaField.tsx
- admin-spa/src/components/forms/SchemaForm.tsx
- admin-spa/src/routes/Settings/ModuleSettings.tsx
- admin-spa/src/hooks/useModuleSettings.ts
- admin-spa/src/lib/windowAPI.ts
- admin-spa/src/components/DynamicComponentLoader.tsx
- types/woonoow-addon.d.ts
- examples/biteship-addon/biteship-addon.php
- examples/biteship-addon/src/Settings.jsx
- examples/biteship-addon/package.json
- examples/biteship-addon/README.md
- PHASE_2_3_4_SUMMARY.md
Files Modified (11):
- admin-spa/src/App.tsx
- admin-spa/src/hooks/useModules.ts
- admin-spa/src/routes/Appearance/Footer.tsx
- admin-spa/src/routes/Settings/Modules.tsx
- customer-spa/src/hooks/useModules.ts
- customer-spa/src/layouts/BaseLayout.tsx
- customer-spa/src/components/NewsletterForm.tsx
- includes/Api/Routes.php
- includes/Api/ModulesController.php
- includes/Core/ModuleRegistry.php
- woonoow.php
API Endpoints Added:
- GET /woonoow/v1/modules/{module_id}/settings
- POST /woonoow/v1/modules/{module_id}/settings
- GET /woonoow/v1/modules/{module_id}/schema
For Addon Developers:
- Schema-based: Define settings via woonoow/module_settings_schema filter
- Custom React: Build component using window.WooNooW API, externalize react/react-dom
- Both approaches use same storage and retrieval methods
- TypeScript definitions provided for type safety
- Complete working example (Biteship) included
162 lines
4.4 KiB
TypeScript
162 lines
4.4 KiB
TypeScript
/**
|
|
* WooNooW Addon TypeScript Definitions
|
|
*
|
|
* Type definitions for addon developers using the WooNooW API
|
|
*
|
|
* @package WooNooW
|
|
* @version 1.0.0
|
|
*/
|
|
|
|
import type { ComponentType } from 'react';
|
|
|
|
declare global {
|
|
interface Window {
|
|
/**
|
|
* WooNooW API exposed to addon developers
|
|
*/
|
|
WooNooW: {
|
|
React: typeof import('react');
|
|
ReactDOM: typeof import('react-dom/client');
|
|
|
|
hooks: {
|
|
useQuery: any;
|
|
useMutation: any;
|
|
useQueryClient: any;
|
|
useModules: () => {
|
|
isEnabled: (moduleId: string) => boolean;
|
|
modules: string[];
|
|
};
|
|
useModuleSettings: (moduleId: string) => {
|
|
settings: Record<string, any>;
|
|
isLoading: boolean;
|
|
updateSettings: {
|
|
mutate: (settings: Record<string, any>) => void;
|
|
isPending: boolean;
|
|
};
|
|
saveSetting: (key: string, value: any) => void;
|
|
};
|
|
};
|
|
|
|
components: {
|
|
Button: ComponentType<any>;
|
|
Input: ComponentType<any>;
|
|
Label: ComponentType<any>;
|
|
Textarea: ComponentType<any>;
|
|
Switch: ComponentType<any>;
|
|
Select: ComponentType<any>;
|
|
SelectContent: ComponentType<any>;
|
|
SelectItem: ComponentType<any>;
|
|
SelectTrigger: ComponentType<any>;
|
|
SelectValue: ComponentType<any>;
|
|
Checkbox: ComponentType<any>;
|
|
Badge: ComponentType<any>;
|
|
Card: ComponentType<any>;
|
|
CardContent: ComponentType<any>;
|
|
CardDescription: ComponentType<any>;
|
|
CardFooter: ComponentType<any>;
|
|
CardHeader: ComponentType<any>;
|
|
CardTitle: ComponentType<any>;
|
|
SettingsLayout: ComponentType<{
|
|
title: string;
|
|
description?: string;
|
|
isLoading?: boolean;
|
|
children: React.ReactNode;
|
|
}>;
|
|
SettingsCard: ComponentType<{
|
|
title: string;
|
|
description?: string;
|
|
children: React.ReactNode;
|
|
}>;
|
|
SettingsSection: ComponentType<any>;
|
|
SchemaForm: ComponentType<{
|
|
schema: FormSchema;
|
|
initialValues?: Record<string, any>;
|
|
onSubmit: (values: Record<string, any>) => void | Promise<void>;
|
|
isSubmitting?: boolean;
|
|
submitLabel?: string;
|
|
errors?: Record<string, string>;
|
|
}>;
|
|
SchemaField: ComponentType<any>;
|
|
};
|
|
|
|
icons: {
|
|
Settings: ComponentType<any>;
|
|
Save: ComponentType<any>;
|
|
Trash2: ComponentType<any>;
|
|
Edit: ComponentType<any>;
|
|
Plus: ComponentType<any>;
|
|
X: ComponentType<any>;
|
|
Check: ComponentType<any>;
|
|
AlertCircle: ComponentType<any>;
|
|
Info: ComponentType<any>;
|
|
Loader2: ComponentType<any>;
|
|
ChevronDown: ComponentType<any>;
|
|
ChevronUp: ComponentType<any>;
|
|
ChevronLeft: ComponentType<any>;
|
|
ChevronRight: ComponentType<any>;
|
|
};
|
|
|
|
utils: {
|
|
api: {
|
|
get: (endpoint: string) => Promise<any>;
|
|
post: (endpoint: string, data?: any) => Promise<any>;
|
|
put: (endpoint: string, data?: any) => Promise<any>;
|
|
delete: (endpoint: string) => Promise<any>;
|
|
};
|
|
toast: {
|
|
success: (message: string) => void;
|
|
error: (message: string) => void;
|
|
info: (message: string) => void;
|
|
warning: (message: string) => void;
|
|
};
|
|
__: (text: string, domain?: string) => string;
|
|
};
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Form Schema Types
|
|
*/
|
|
export type FieldType = 'text' | 'textarea' | 'email' | 'url' | 'number' | 'toggle' | 'checkbox' | 'select';
|
|
|
|
export interface FieldSchema {
|
|
type: FieldType;
|
|
label: string;
|
|
description?: string;
|
|
placeholder?: string;
|
|
required?: boolean;
|
|
default?: any;
|
|
options?: Record<string, string>;
|
|
min?: number;
|
|
max?: number;
|
|
}
|
|
|
|
export type FormSchema = Record<string, FieldSchema>;
|
|
|
|
/**
|
|
* Module Registration
|
|
*/
|
|
export interface ModuleRegistration {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
version: string;
|
|
author: string;
|
|
category: 'marketing' | 'customers' | 'products' | 'shipping' | 'payments' | 'analytics' | 'other';
|
|
icon: string;
|
|
features: string[];
|
|
has_settings?: boolean;
|
|
settings_component?: string;
|
|
spa_bundle?: string;
|
|
}
|
|
|
|
/**
|
|
* Settings Schema Registration
|
|
*/
|
|
export interface SettingsSchemaRegistration {
|
|
[moduleId: string]: FormSchema;
|
|
}
|
|
|
|
export {};
|