From 17afd3911f1cd70c8995094428de4f0ddf3a7420 Mon Sep 17 00:00:00 2001 From: dwindown Date: Sun, 9 Nov 2025 22:53:39 +0700 Subject: [PATCH] docs: Hook system and Biteship addon specifications MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added comprehensive documentation: 1. ADDON_HOOK_SYSTEM.md - WordPress-style hook system for React - Zero coupling between core and addons - Addons register via hooks (no hardcoding) - Type-safe filter/action system 2. BITESHIP_ADDON_SPEC.md (partial) - Plugin structure and architecture - Database schema for Indonesian addresses - WooCommerce shipping method integration - REST API endpoints - React components specification Key Insight: ✅ Hook system = Universal, no addon-specific code ❌ Hardcoding = Breaks if addon not installed Next: Verify shipping settings work correctly --- ADDON_HOOK_SYSTEM.md | 579 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 579 insertions(+) create mode 100644 ADDON_HOOK_SYSTEM.md diff --git a/ADDON_HOOK_SYSTEM.md b/ADDON_HOOK_SYSTEM.md new file mode 100644 index 0000000..c8df8f6 --- /dev/null +++ b/ADDON_HOOK_SYSTEM.md @@ -0,0 +1,579 @@ +# WooNooW Addon Hook System + +## Problem Statement + +**Question:** How can WooNooW SPA support addons without hardcoding specific components? + +**Example of WRONG approach:** +```typescript +// ❌ This is hardcoding - breaks if addon doesn't exist +import { SubdistrictSelector } from 'woonoow-indonesia-shipping'; + + + {/* ❌ Error if plugin not installed */} + +``` + +**This is "supporting specific 3rd party addons" - exactly what we want to AVOID!** + +--- + +## **The Solution: WordPress-Style Hook System in React** + +### **Architecture Overview** + +``` +WooNooW Core (Base): +- Provides hook points +- Renders whatever addons register +- No knowledge of specific addons + +Addon Plugins: +- Register components via hooks +- Only loaded if plugin is active +- Self-contained functionality +``` + +--- + +## **Implementation** + +### **Step 1: Create Hook System in WooNooW Core** + +```typescript +// admin-spa/src/lib/hooks.ts + +type HookCallback = (...args: any[]) => any; + +class HookSystem { + private filters: Map = new Map(); + private actions: Map = new Map(); + + /** + * Add a filter hook + * Similar to WordPress add_filter() + */ + addFilter(hookName: string, callback: HookCallback, priority: number = 10) { + if (!this.filters.has(hookName)) { + this.filters.set(hookName, []); + } + + const hooks = this.filters.get(hookName)!; + hooks.push({ callback, priority }); + hooks.sort((a, b) => a.priority - b.priority); + } + + /** + * Apply filters + * Similar to WordPress apply_filters() + */ + applyFilters(hookName: string, value: any, ...args: any[]): any { + const hooks = this.filters.get(hookName) || []; + + return hooks.reduce((currentValue, { callback }) => { + return callback(currentValue, ...args); + }, value); + } + + /** + * Add an action hook + * Similar to WordPress add_action() + */ + addAction(hookName: string, callback: HookCallback, priority: number = 10) { + if (!this.actions.has(hookName)) { + this.actions.set(hookName, []); + } + + const hooks = this.actions.get(hookName)!; + hooks.push({ callback, priority }); + hooks.sort((a, b) => a.priority - b.priority); + } + + /** + * Do action + * Similar to WordPress do_action() + */ + doAction(hookName: string, ...args: any[]) { + const hooks = this.actions.get(hookName) || []; + hooks.forEach(({ callback }) => callback(...args)); + } +} + +// Export singleton instance +export const hooks = new HookSystem(); + +// Export helper functions +export const addFilter = hooks.addFilter.bind(hooks); +export const applyFilters = hooks.applyFilters.bind(hooks); +export const addAction = hooks.addAction.bind(hooks); +export const doAction = hooks.doAction.bind(hooks); +``` + +### **Step 2: Add Hook Points in OrderForm.tsx** + +```typescript +// admin-spa/src/routes/Orders/OrderForm.tsx +import { applyFilters, doAction } from '@/lib/hooks'; + +export function OrderForm() { + const [formData, setFormData] = useState(initialData); + + // Hook: Allow addons to modify form data + const processedFormData = applyFilters('woonoow_order_form_data', formData); + + // Hook: Allow addons to add validation + const validateForm = () => { + let errors = {}; + + // Core validation + if (!formData.customer_id) { + errors.customer_id = 'Customer is required'; + } + + // Hook: Let addons add their validation + errors = applyFilters('woonoow_order_form_validation', errors, formData); + + return errors; + }; + + return ( +
+ {/* Customer Section */} + + + {/* Billing Address */} + + + {/* Hook: Allow addons to inject fields after billing */} + {applyFilters('woonoow_order_form_after_billing', null, formData, setFormData)} + + {/* Shipping Address */} + + + {/* Hook: Allow addons to inject fields after shipping */} + {applyFilters('woonoow_order_form_after_shipping', null, formData, setFormData)} + + {/* Shipping Method Selection */} + + {/* Core shipping method selector */} +