# Implementation Plan: Level 1 Meta Compatibility ## Objective Make WooNooW listen to ALL standard WordPress/WooCommerce hooks for custom meta fields automatically. ## Principles (From Documentation Review) ### From ADDON_BRIDGE_PATTERN.md: 1. ✅ WooNooW Core = Zero addon dependencies 2. ✅ We listen to WP/WooCommerce hooks (NOT WooNooW-specific) 3. ✅ Community does NOTHING extra 4. ❌ We do NOT support specific plugins 5. ❌ We do NOT integrate plugins into core ### From ADDON_DEVELOPMENT_GUIDE.md: 1. ✅ Hook system for functional extensions 2. ✅ Zero coupling with core 3. ✅ WordPress-style filters and actions ### From ADDON_REACT_INTEGRATION.md: 1. ✅ Expose React runtime on window 2. ✅ Support vanilla JS/jQuery addons 3. ✅ No build process required for simple addons --- ## Implementation Strategy ### Phase 1: Backend API Enhancement (2-3 days) #### 1.1 OrdersController - Expose Meta Data **File:** `includes/Api/OrdersController.php` **Changes:** ```php public static function show(WP_REST_Request $req) { $order = wc_get_order($id); // ... existing data ... // Expose meta data (Level 1 compatibility) $meta_data = self::get_order_meta_data($order); $data['meta'] = $meta_data; // Allow plugins to modify response $data = apply_filters('woonoow/order_api_data', $data, $order, $req); return new WP_REST_Response($data, 200); } /** * Get order meta data for API exposure * Filters out internal meta unless explicitly allowed */ private static function get_order_meta_data($order) { $meta_data = []; foreach ($order->get_meta_data() as $meta) { $key = $meta->key; $value = $meta->value; // Skip internal WooCommerce meta (starts with _wc_) if (strpos($key, '_wc_') === 0) { continue; } // Public meta (no underscore) - always expose if (strpos($key, '_') !== 0) { $meta_data[$key] = $value; continue; } // Private meta (starts with _) - check if allowed $allowed_private = apply_filters('woonoow/order_allowed_private_meta', [ // Common shipping tracking fields '_tracking_number', '_tracking_provider', '_tracking_url', '_shipment_tracking_items', '_wc_shipment_tracking_items', // Allow plugins to add their meta ], $order); if (in_array($key, $allowed_private, true)) { $meta_data[$key] = $value; } } return $meta_data; } ``` **Update Method:** ```php public static function update(WP_REST_Request $req) { $order = wc_get_order($id); $data = $req->get_json_params(); // ... existing update logic ... // Update custom meta fields (Level 1 compatibility) if (isset($data['meta']) && is_array($data['meta'])) { self::update_order_meta_data($order, $data['meta']); } $order->save(); // Allow plugins to perform additional updates do_action('woonoow/order_updated', $order, $data, $req); return new WP_REST_Response(['success' => true], 200); } /** * Update order meta data from API */ private static function update_order_meta_data($order, $meta_updates) { // Get allowed updatable meta keys $allowed = apply_filters('woonoow/order_updatable_meta', [ '_tracking_number', '_tracking_provider', '_tracking_url', // Allow plugins to add their meta ], $order); foreach ($meta_updates as $key => $value) { // Public meta (no underscore) - always allow if (strpos($key, '_') !== 0) { $order->update_meta_data($key, $value); continue; } // Private meta - check if allowed if (in_array($key, $allowed, true)) { $order->update_meta_data($key, $value); } } } ``` #### 1.2 ProductsController - Expose Meta Data **File:** `includes/Api/ProductsController.php` **Changes:** (Same pattern as OrdersController) ```php public static function get_product(WP_REST_Request $request) { $product = wc_get_product($id); // ... existing data ... // Expose meta data (Level 1 compatibility) $meta_data = self::get_product_meta_data($product); $data['meta'] = $meta_data; // Allow plugins to modify response $data = apply_filters('woonoow/product_api_data', $data, $product, $request); return new WP_REST_Response($data, 200); } private static function get_product_meta_data($product) { // Same logic as orders } public static function update_product(WP_REST_Request $request) { // ... existing logic ... if (isset($data['meta']) && is_array($data['meta'])) { self::update_product_meta_data($product, $data['meta']); } do_action('woonoow/product_updated', $product, $data, $request); } ``` --- ### Phase 2: Frontend Components (3-4 days) #### 2.1 MetaFields Component **File:** `admin-spa/src/components/MetaFields.tsx` **Purpose:** Generic component to display/edit meta fields ```tsx interface MetaField { key: string; label: string; type: 'text' | 'textarea' | 'number' | 'select' | 'date' | 'checkbox'; options?: Array<{value: string; label: string}>; section?: string; description?: string; placeholder?: string; } interface MetaFieldsProps { meta: Record; fields: MetaField[]; onChange: (key: string, value: any) => void; readOnly?: boolean; } export function MetaFields({ meta, fields, onChange, readOnly }: MetaFieldsProps) { if (fields.length === 0) return null; // Group fields by section const sections = fields.reduce((acc, field) => { const section = field.section || 'Additional Fields'; if (!acc[section]) acc[section] = []; acc[section].push(field); return acc; }, {} as Record); return (
{Object.entries(sections).map(([section, sectionFields]) => ( {section} {sectionFields.map(field => (
{field.type === 'text' && ( onChange(field.key, e.target.value)} disabled={readOnly} placeholder={field.placeholder} /> )} {field.type === 'textarea' && (