diff --git a/METABOX_COMPAT.md b/METABOX_COMPAT.md index cf86561..ebbed0e 100644 --- a/METABOX_COMPAT.md +++ b/METABOX_COMPAT.md @@ -584,12 +584,227 @@ add_filter('woonoow/order_updatable_meta', function($allowed) { 4. Forces users to switch back to classic admin for custom fields **Timeline:** -- Phase 1 (API): 2-3 days -- Phase 2 (Frontend): 3-4 days -- Phase 3 (Integration): 2-3 days -- **Total: ~1-2 weeks** +- Phase 1 (API): 2-3 days ✅ COMPLETE +- Phase 2 (Frontend): 3-4 days ✅ COMPLETE +- Phase 3 (Integration): 2-3 days ✅ COMPLETE +- **Total: ~1-2 weeks** ✅ COMPLETE -**Blocking:** -- Coupons CRUD (can proceed) -- Customers CRUD (can proceed) -- **Production readiness** (BLOCKED until this is done) +**Status:** ✅ **IMPLEMENTED AND READY** + +--- + +## Complete Example: Plugin Integration + +### Example 1: WooCommerce Shipment Tracking + +**Plugin stores data (standard WooCommerce way):** +```php +// Plugin code (no changes needed) +update_post_meta($order_id, '_tracking_number', '1234567890'); +update_post_meta($order_id, '_tracking_provider', 'JNE'); +``` + +**Plugin registers fields for WooNooW (optional, for better UX):** +```php +// In plugin's main file or init hook +add_action('woonoow/register_meta_fields', function() { + // Register tracking number field + \WooNooW\Compat\MetaFieldsRegistry::register_order_field('_tracking_number', [ + 'label' => __('Tracking Number', 'your-plugin'), + 'type' => 'text', + 'section' => 'Shipment Tracking', + 'description' => 'Enter the shipment tracking number', + 'placeholder' => 'e.g., 1234567890', + ]); + + // Register tracking provider field + \WooNooW\Compat\MetaFieldsRegistry::register_order_field('_tracking_provider', [ + 'label' => __('Tracking Provider', 'your-plugin'), + 'type' => 'select', + 'section' => 'Shipment Tracking', + 'options' => [ + ['value' => 'jne', 'label' => 'JNE'], + ['value' => 'jnt', 'label' => 'J&T Express'], + ['value' => 'sicepat', 'label' => 'SiCepat'], + ['value' => 'anteraja', 'label' => 'AnterAja'], + ], + ]); +}); +``` + +**Result:** +- ✅ Fields automatically exposed in API +- ✅ Fields displayed in WooNooW order edit page +- ✅ Fields editable by admin +- ✅ Data saved to WooCommerce database +- ✅ Compatible with classic admin +- ✅ **Zero migration needed** + +### Example 2: Advanced Custom Fields (ACF) + +**ACF stores data (standard way):** +```php +// ACF automatically stores to post meta +update_field('custom_field', 'value', $product_id); +// Stored as: update_post_meta($product_id, 'custom_field', 'value'); +``` + +**Register for WooNooW (optional):** +```php +add_action('woonoow/register_meta_fields', function() { + \WooNooW\Compat\MetaFieldsRegistry::register_product_field('custom_field', [ + 'label' => __('Custom Field', 'your-plugin'), + 'type' => 'textarea', + 'section' => 'Custom Fields', + ]); +}); +``` + +**Result:** +- ✅ ACF data visible in WooNooW +- ✅ Editable in WooNooW admin +- ✅ Synced with ACF +- ✅ Works with both admins + +### Example 3: Custom Plugin (No Registration) + +**Plugin stores data:** +```php +// Plugin stores public meta (no underscore) +update_post_meta($order_id, 'custom_note', 'Some note'); +``` + +**Result:** +- ✅ **Automatically exposed** (public meta) +- ✅ Displayed in API response +- ✅ No registration needed +- ✅ Works immediately + +--- + +## API Response Examples + +### Order with Meta Fields + +**Request:** +``` +GET /wp-json/woonoow/v1/orders/123 +``` + +**Response:** +```json +{ + "id": 123, + "status": "processing", + "billing": {...}, + "shipping": {...}, + "items": [...], + "meta": { + "_tracking_number": "1234567890", + "_tracking_provider": "jne", + "custom_note": "Some note" + } +} +``` + +### Product with Meta Fields + +**Request:** +``` +GET /wp-json/woonoow/v1/products/456 +``` + +**Response:** +```json +{ + "id": 456, + "name": "Product Name", + "price": 100000, + "meta": { + "custom_field": "Custom value", + "another_field": "Another value" + } +} +``` + +--- + +## Field Types Reference + +### Text Field +```php +MetaFieldsRegistry::register_order_field('_field_name', [ + 'label' => 'Field Label', + 'type' => 'text', + 'placeholder' => 'Enter value...', +]); +``` + +### Textarea Field +```php +MetaFieldsRegistry::register_order_field('_field_name', [ + 'label' => 'Field Label', + 'type' => 'textarea', + 'placeholder' => 'Enter description...', +]); +``` + +### Number Field +```php +MetaFieldsRegistry::register_order_field('_field_name', [ + 'label' => 'Field Label', + 'type' => 'number', + 'placeholder' => '0', +]); +``` + +### Select Field +```php +MetaFieldsRegistry::register_order_field('_field_name', [ + 'label' => 'Field Label', + 'type' => 'select', + 'options' => [ + ['value' => 'option1', 'label' => 'Option 1'], + ['value' => 'option2', 'label' => 'Option 2'], + ], +]); +``` + +### Date Field +```php +MetaFieldsRegistry::register_order_field('_field_name', [ + 'label' => 'Field Label', + 'type' => 'date', +]); +``` + +### Checkbox Field +```php +MetaFieldsRegistry::register_order_field('_field_name', [ + 'label' => 'Field Label', + 'type' => 'checkbox', + 'placeholder' => 'Enable this option', +]); +``` + +--- + +## Summary + +**For Plugin Developers:** +1. ✅ Continue using standard WP/WooCommerce meta storage +2. ✅ Optionally register fields for better UX +3. ✅ No special integration needed +4. ✅ Works with both classic and WooNooW admin + +**For WooNooW Core:** +1. ✅ Zero addon dependencies +2. ✅ Provides mechanism, not integration +3. ✅ Plugins register themselves +4. ✅ Clean separation of concerns + +**Result:** +✅ **Level 1 compatibility fully implemented** +✅ **Plugins work automatically** +✅ **No migration needed** +✅ **Production ready** diff --git a/includes/Compat/MetaFieldsRegistry.php b/includes/Compat/MetaFieldsRegistry.php new file mode 100644 index 0000000..cbb5ff9 --- /dev/null +++ b/includes/Compat/MetaFieldsRegistry.php @@ -0,0 +1,212 @@ + __('Tracking Number'), + * 'type' => 'text', + * 'section' => 'Shipment Tracking', + * ]); + * }); + */ + do_action('woonoow/register_meta_fields'); + } + + /** + * Register an order meta field + * + * @param string $key Meta key (e.g., '_tracking_number') + * @param array $args Field configuration + * - label: string - Field label (default: formatted key) + * - type: string - Field type: text, textarea, number, select, date, checkbox (default: text) + * - section: string - Section name for grouping (default: 'Additional Fields') + * - description: string - Help text (default: '') + * - placeholder: string - Placeholder text (default: '') + * - options: array - For select type: [{value: 'x', label: 'X'}] (default: []) + */ + public static function register_order_field($key, $args = []) { + $defaults = [ + 'key' => $key, + 'label' => self::format_label($key), + 'type' => 'text', + 'section' => 'Additional Fields', + 'description' => '', + 'placeholder' => '', + 'options' => [], + ]; + + self::$order_fields[$key] = array_merge($defaults, $args); + + // Auto-add to allowed meta lists (Level 1 compatibility) + self::add_to_allowed_meta('order', $key); + } + + /** + * Register a product meta field + * + * @param string $key Meta key (e.g., '_custom_field') + * @param array $args Field configuration (same as register_order_field) + */ + public static function register_product_field($key, $args = []) { + $defaults = [ + 'key' => $key, + 'label' => self::format_label($key), + 'type' => 'text', + 'section' => 'Additional Fields', + 'description' => '', + 'placeholder' => '', + 'options' => [], + ]; + + self::$product_fields[$key] = array_merge($defaults, $args); + + // Auto-add to allowed meta lists (Level 1 compatibility) + self::add_to_allowed_meta('product', $key); + } + + /** + * Format meta key to human-readable label + * + * @param string $key Meta key + * @return string Formatted label + */ + private static function format_label($key) { + // Remove leading underscore + $label = ltrim($key, '_'); + + // Replace underscores with spaces + $label = str_replace('_', ' ', $label); + + // Capitalize words + $label = ucwords($label); + + return $label; + } + + /** + * Add meta key to allowed lists automatically + * + * @param string $type 'order' or 'product' + * @param string $key Meta key + */ + private static function add_to_allowed_meta($type, $key) { + if ($type === 'order') { + // Add to allowed private meta (for reading) + add_filter('woonoow/order_allowed_private_meta', function($allowed) use ($key) { + if (!in_array($key, $allowed, true)) { + $allowed[] = $key; + } + return $allowed; + }); + + // Add to updatable meta (for writing) + add_filter('woonoow/order_updatable_meta', function($allowed) use ($key) { + if (!in_array($key, $allowed, true)) { + $allowed[] = $key; + } + return $allowed; + }); + } elseif ($type === 'product') { + // Add to allowed private meta (for reading) + add_filter('woonoow/product_allowed_private_meta', function($allowed) use ($key) { + if (!in_array($key, $allowed, true)) { + $allowed[] = $key; + } + return $allowed; + }); + + // Add to updatable meta (for writing) + add_filter('woonoow/product_updatable_meta', function($allowed) use ($key) { + if (!in_array($key, $allowed, true)) { + $allowed[] = $key; + } + return $allowed; + }); + } + } + + /** + * Localize fields to JavaScript + * Exposes registered fields to frontend via window.WooNooWMetaFields + */ + public static function localize_fields() { + // Only on admin pages + if (!is_admin()) { + return; + } + + // Allow plugins to modify fields before localizing + $order_fields = apply_filters('woonoow/meta_fields_orders', array_values(self::$order_fields)); + $product_fields = apply_filters('woonoow/meta_fields_products', array_values(self::$product_fields)); + + // Localize to JavaScript + wp_localize_script('woonoow-admin', 'WooNooWMetaFields', [ + 'orders' => $order_fields, + 'products' => $product_fields, + ]); + } + + /** + * Get registered order fields (for testing/debugging) + * + * @return array + */ + public static function get_order_fields() { + return self::$order_fields; + } + + /** + * Get registered product fields (for testing/debugging) + * + * @return array + */ + public static function get_product_fields() { + return self::$product_fields; + } +} diff --git a/includes/Core/Bootstrap.php b/includes/Core/Bootstrap.php index 4d9f7fd..6aae964 100644 --- a/includes/Core/Bootstrap.php +++ b/includes/Core/Bootstrap.php @@ -12,6 +12,7 @@ use WooNooW\Compat\RouteRegistry; use WooNooW\Compat\NavigationRegistry; use WooNooW\Compat\PaymentChannels; use WooNooW\Compat\SettingsProvider; +use WooNooW\Compat\MetaFieldsRegistry; use WooNooW\Admin\Rest\MenuController; use WooNooW\Admin\Rest\SettingsController; use WooNooW\Api\Routes; @@ -45,6 +46,9 @@ class Bootstrap { NavigationRegistry::init(); PaymentChannels::init(); + // Level 1 compatibility: Meta fields registry + MetaFieldsRegistry::init(); + MenuProvider::init(); MenuController::init(); SettingsProvider::init();