docs: Critical metabox & custom fields compatibility gap identified
**Issue: Third-Party Plugin Compatibility** Current Status: ❌ NOT IMPLEMENTED Priority: 🔴 CRITICAL - Blocks production readiness **Problem:** Our SPA admin does NOT expose: - Custom meta fields from third-party plugins - WordPress metaboxes (add_meta_box) - WooCommerce custom fields - ACF/CMB2/Pods fields - Plugin-injected data (e.g., Tracking Number) **Example Use Case:** Plugin: WooCommerce Shipment Tracking - Adds 'Tracking Number' metabox to order edit page - Stores: _tracking_number meta field - Current: ❌ NOT visible in WooNooW admin - Expected: ✅ Should be visible and editable **Impact:** 1. Breaks compatibility with popular plugins 2. Users cannot see/edit custom fields 3. Data exists but not accessible in SPA 4. Forces users back to classic admin 5. BLOCKS production readiness **Solution Architecture:** Phase 1: API Layer (2-3 days) - Expose meta_data in OrdersController::show() - Expose meta_data in ProductsController::get_product() - Add filters: woonoow/order_api_data, woonoow/product_api_data - Add filters: woonoow/order_allowed_private_meta - Add actions: woonoow/order_updated, woonoow/product_updated Phase 2: Frontend Components (3-4 days) - Create MetaFields.tsx component - Create useMetaFields.ts hook - Update Orders/Edit.tsx to include meta fields - Update Products/Edit.tsx to include meta fields - Add meta fields to detail pages Phase 3: Plugin Integration (2-3 days) - Create MetaFieldsRegistry.php - Add woonoow/register_meta_fields action - Localize fields to JavaScript - Create example: ShipmentTracking.php integration - Document integration pattern **Documentation Created:** - METABOX_COMPAT.md - Complete implementation guide - Includes code examples for all phases - Includes third-party integration guide - Includes testing checklist **Updated:** - PROJECT_SOP.md - Added metabox compat reference - Marked as CRITICAL requirement - Noted as blocking production readiness **Timeline:** Total: 1-2 weeks implementation **Blocking:** - ✅ Coupons CRUD (can proceed) - ✅ Customers CRUD (can proceed) - ❌ Production readiness (BLOCKED) **Next Steps:** 1. Review METABOX_COMPAT.md 2. Prioritize implementation 3. Start with Phase 1 (API layer) 4. Test with popular plugins (Shipment Tracking, ACF)
This commit is contained in:
552
METABOX_COMPAT.md
Normal file
552
METABOX_COMPAT.md
Normal file
@@ -0,0 +1,552 @@
|
|||||||
|
# WooNooW Metabox & Custom Fields Compatibility
|
||||||
|
|
||||||
|
## Current Status: ❌ NOT IMPLEMENTED
|
||||||
|
|
||||||
|
**Critical Gap:** Our SPA admin does NOT currently expose custom meta fields, metaboxes, or third-party plugin data injected via WordPress/WooCommerce hooks.
|
||||||
|
|
||||||
|
### Example Use Case:
|
||||||
|
```
|
||||||
|
Plugin: WooCommerce Shipment Tracking
|
||||||
|
- Adds "Tracking Number" metabox to order edit page
|
||||||
|
- Uses: add_meta_box('wc_shipment_tracking', ...)
|
||||||
|
- Stores: update_post_meta($order_id, '_tracking_number', $value)
|
||||||
|
|
||||||
|
Current Behavior: ❌ Field NOT visible in WooNooW admin
|
||||||
|
Expected Behavior: ✅ Field should be visible and editable
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem Analysis
|
||||||
|
|
||||||
|
### 1. Orders API (`OrdersController.php`)
|
||||||
|
|
||||||
|
**Current Implementation:**
|
||||||
|
```php
|
||||||
|
public static function show(WP_REST_Request $req) {
|
||||||
|
$order = wc_get_order($id);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'id' => $order->get_id(),
|
||||||
|
'status' => $order->get_status(),
|
||||||
|
'billing' => [...],
|
||||||
|
'shipping' => [...],
|
||||||
|
'items' => [...],
|
||||||
|
// ... hardcoded fields only
|
||||||
|
];
|
||||||
|
|
||||||
|
return new WP_REST_Response($data, 200);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Missing:**
|
||||||
|
- ❌ No `get_meta_data()` exposure
|
||||||
|
- ❌ No `apply_filters('woonoow/order_data', $data, $order)`
|
||||||
|
- ❌ No metabox hook listening
|
||||||
|
- ❌ No custom field groups
|
||||||
|
|
||||||
|
### 2. Products API (`ProductsController.php`)
|
||||||
|
|
||||||
|
**Current Implementation:**
|
||||||
|
```php
|
||||||
|
public static function get_product(WP_REST_Request $request) {
|
||||||
|
$product = wc_get_product($id);
|
||||||
|
|
||||||
|
return new WP_REST_Response([
|
||||||
|
'id' => $product->get_id(),
|
||||||
|
'name' => $product->get_name(),
|
||||||
|
// ... hardcoded fields only
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Missing:**
|
||||||
|
- ❌ No custom product meta exposure
|
||||||
|
- ❌ No `apply_filters('woonoow/product_data', $data, $product)`
|
||||||
|
- ❌ No ACF/CMB2/Pods integration
|
||||||
|
- ❌ No custom tabs/panels
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Solution Architecture
|
||||||
|
|
||||||
|
### Phase 1: Meta Data Exposure (API Layer)
|
||||||
|
|
||||||
|
#### 1.1 Orders API Enhancement
|
||||||
|
|
||||||
|
**Add to `OrdersController::show()`:**
|
||||||
|
```php
|
||||||
|
public static function show(WP_REST_Request $req) {
|
||||||
|
$order = wc_get_order($id);
|
||||||
|
|
||||||
|
// ... existing data ...
|
||||||
|
|
||||||
|
// Expose all meta data
|
||||||
|
$meta_data = [];
|
||||||
|
foreach ($order->get_meta_data() as $meta) {
|
||||||
|
$key = $meta->key;
|
||||||
|
|
||||||
|
// Skip internal/private meta (starts with _)
|
||||||
|
// unless explicitly allowed
|
||||||
|
if (strpos($key, '_') === 0) {
|
||||||
|
$allowed_private = apply_filters('woonoow/order_allowed_private_meta', [
|
||||||
|
'_tracking_number',
|
||||||
|
'_tracking_provider',
|
||||||
|
'_shipment_tracking_items',
|
||||||
|
'_wc_shipment_tracking_items',
|
||||||
|
// Add more as needed
|
||||||
|
], $order);
|
||||||
|
|
||||||
|
if (!in_array($key, $allowed_private, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$meta_data[$key] = $meta->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['meta'] = $meta_data;
|
||||||
|
|
||||||
|
// Allow plugins to add/modify data
|
||||||
|
$data = apply_filters('woonoow/order_api_data', $data, $order, $req);
|
||||||
|
|
||||||
|
return new WP_REST_Response($data, 200);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Add to `OrdersController::update()`:**
|
||||||
|
```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
|
||||||
|
if (isset($data['meta']) && is_array($data['meta'])) {
|
||||||
|
foreach ($data['meta'] as $key => $value) {
|
||||||
|
// Validate meta key is allowed
|
||||||
|
$allowed = apply_filters('woonoow/order_updatable_meta', [
|
||||||
|
'_tracking_number',
|
||||||
|
'_tracking_provider',
|
||||||
|
// Add more as needed
|
||||||
|
], $order);
|
||||||
|
|
||||||
|
if (in_array($key, $allowed, true)) {
|
||||||
|
$order->update_meta_data($key, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$order->save();
|
||||||
|
|
||||||
|
// Allow plugins to perform additional updates
|
||||||
|
do_action('woonoow/order_updated', $order, $data, $req);
|
||||||
|
|
||||||
|
return new WP_REST_Response(['success' => true], 200);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 1.2 Products API Enhancement
|
||||||
|
|
||||||
|
**Add to `ProductsController::get_product()`:**
|
||||||
|
```php
|
||||||
|
public static function get_product(WP_REST_Request $request) {
|
||||||
|
$product = wc_get_product($id);
|
||||||
|
|
||||||
|
// ... existing data ...
|
||||||
|
|
||||||
|
// Expose all meta data
|
||||||
|
$meta_data = [];
|
||||||
|
foreach ($product->get_meta_data() as $meta) {
|
||||||
|
$key = $meta->key;
|
||||||
|
|
||||||
|
// Skip internal meta unless allowed
|
||||||
|
if (strpos($key, '_') === 0) {
|
||||||
|
$allowed_private = apply_filters('woonoow/product_allowed_private_meta', [
|
||||||
|
'_custom_field_example',
|
||||||
|
// Add more as needed
|
||||||
|
], $product);
|
||||||
|
|
||||||
|
if (!in_array($key, $allowed_private, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$meta_data[$key] = $meta->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['meta'] = $meta_data;
|
||||||
|
|
||||||
|
// Allow plugins to add/modify data
|
||||||
|
$data = apply_filters('woonoow/product_api_data', $data, $product, $request);
|
||||||
|
|
||||||
|
return new WP_REST_Response($data, 200);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 2: Frontend Rendering (React Components)
|
||||||
|
|
||||||
|
#### 2.1 Dynamic Meta Fields Component
|
||||||
|
|
||||||
|
**Create: `admin-spa/src/components/MetaFields.tsx`**
|
||||||
|
```tsx
|
||||||
|
interface MetaField {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
type: 'text' | 'textarea' | 'number' | 'select' | 'date';
|
||||||
|
options?: Array<{value: string; label: string}>;
|
||||||
|
section?: string; // Group fields into sections
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MetaFieldsProps {
|
||||||
|
meta: Record<string, any>;
|
||||||
|
fields: MetaField[];
|
||||||
|
onChange: (key: string, value: any) => void;
|
||||||
|
readOnly?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MetaFields({ meta, fields, onChange, readOnly }: MetaFieldsProps) {
|
||||||
|
// Group fields by section
|
||||||
|
const sections = fields.reduce((acc, field) => {
|
||||||
|
const section = field.section || 'Other';
|
||||||
|
if (!acc[section]) acc[section] = [];
|
||||||
|
acc[section].push(field);
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<string, MetaField[]>);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-6">
|
||||||
|
{Object.entries(sections).map(([section, sectionFields]) => (
|
||||||
|
<Card key={section}>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>{section}</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
{sectionFields.map(field => (
|
||||||
|
<div key={field.key}>
|
||||||
|
<Label>{field.label}</Label>
|
||||||
|
{field.type === 'text' && (
|
||||||
|
<Input
|
||||||
|
value={meta[field.key] || ''}
|
||||||
|
onChange={(e) => onChange(field.key, e.target.value)}
|
||||||
|
disabled={readOnly}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{field.type === 'textarea' && (
|
||||||
|
<Textarea
|
||||||
|
value={meta[field.key] || ''}
|
||||||
|
onChange={(e) => onChange(field.key, e.target.value)}
|
||||||
|
disabled={readOnly}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{/* Add more field types as needed */}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.2 Hook System for Field Registration
|
||||||
|
|
||||||
|
**Create: `admin-spa/src/hooks/useMetaFields.ts`**
|
||||||
|
```tsx
|
||||||
|
interface MetaFieldsRegistry {
|
||||||
|
orders: MetaField[];
|
||||||
|
products: MetaField[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global registry (can be extended by plugins via window object)
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
WooNooWMetaFields?: MetaFieldsRegistry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useMetaFields(type: 'orders' | 'products'): MetaField[] {
|
||||||
|
const [fields, setFields] = useState<MetaField[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Get fields from global registry
|
||||||
|
const registry = window.WooNooWMetaFields || { orders: [], products: [] };
|
||||||
|
setFields(registry[type] || []);
|
||||||
|
}, [type]);
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.3 Integration in Order Edit Form
|
||||||
|
|
||||||
|
**Update: `admin-spa/src/routes/Orders/Edit.tsx`**
|
||||||
|
```tsx
|
||||||
|
import { MetaFields } from '@/components/MetaFields';
|
||||||
|
import { useMetaFields } from '@/hooks/useMetaFields';
|
||||||
|
|
||||||
|
export default function OrderEdit() {
|
||||||
|
const { id } = useParams();
|
||||||
|
const metaFields = useMetaFields('orders');
|
||||||
|
|
||||||
|
const orderQ = useQuery({
|
||||||
|
queryKey: ['order', id],
|
||||||
|
queryFn: () => api.get(`/orders/${id}`),
|
||||||
|
});
|
||||||
|
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
// ... existing fields ...
|
||||||
|
meta: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (orderQ.data) {
|
||||||
|
setFormData(prev => ({
|
||||||
|
...prev,
|
||||||
|
meta: orderQ.data.meta || {},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, [orderQ.data]);
|
||||||
|
|
||||||
|
const handleMetaChange = (key: string, value: any) => {
|
||||||
|
setFormData(prev => ({
|
||||||
|
...prev,
|
||||||
|
meta: {
|
||||||
|
...prev.meta,
|
||||||
|
[key]: value,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* Existing order form fields */}
|
||||||
|
|
||||||
|
{/* Custom meta fields */}
|
||||||
|
{metaFields.length > 0 && (
|
||||||
|
<MetaFields
|
||||||
|
meta={formData.meta}
|
||||||
|
fields={metaFields}
|
||||||
|
onChange={handleMetaChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 3: Plugin Integration Layer
|
||||||
|
|
||||||
|
#### 3.1 PHP Hook for Field Registration
|
||||||
|
|
||||||
|
**Create: `includes/Compat/MetaFieldsRegistry.php`**
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
namespace WooNooW\Compat;
|
||||||
|
|
||||||
|
class MetaFieldsRegistry {
|
||||||
|
|
||||||
|
private static $order_fields = [];
|
||||||
|
private static $product_fields = [];
|
||||||
|
|
||||||
|
public static function init() {
|
||||||
|
add_action('admin_enqueue_scripts', [__CLASS__, 'localize_fields']);
|
||||||
|
|
||||||
|
// Allow plugins to register fields
|
||||||
|
do_action('woonoow/register_meta_fields');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register order meta field
|
||||||
|
*/
|
||||||
|
public static function register_order_field($key, $args = []) {
|
||||||
|
$defaults = [
|
||||||
|
'key' => $key,
|
||||||
|
'label' => ucfirst(str_replace('_', ' ', $key)),
|
||||||
|
'type' => 'text',
|
||||||
|
'section' => 'Other',
|
||||||
|
];
|
||||||
|
|
||||||
|
self::$order_fields[$key] = array_merge($defaults, $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register product meta field
|
||||||
|
*/
|
||||||
|
public static function register_product_field($key, $args = []) {
|
||||||
|
$defaults = [
|
||||||
|
'key' => $key,
|
||||||
|
'label' => ucfirst(str_replace('_', ' ', $key)),
|
||||||
|
'type' => 'text',
|
||||||
|
'section' => 'Other',
|
||||||
|
];
|
||||||
|
|
||||||
|
self::$product_fields[$key] = array_merge($defaults, $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localize fields to JavaScript
|
||||||
|
*/
|
||||||
|
public static function localize_fields() {
|
||||||
|
if (!is_admin()) return;
|
||||||
|
|
||||||
|
wp_localize_script('woonoow-admin', 'WooNooWMetaFields', [
|
||||||
|
'orders' => array_values(self::$order_fields),
|
||||||
|
'products' => array_values(self::$product_fields),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3.2 Example: Shipment Tracking Integration
|
||||||
|
|
||||||
|
**Create: `includes/Compat/Integrations/ShipmentTracking.php`**
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
namespace WooNooW\Compat\Integrations;
|
||||||
|
|
||||||
|
use WooNooW\Compat\MetaFieldsRegistry;
|
||||||
|
|
||||||
|
class ShipmentTracking {
|
||||||
|
|
||||||
|
public static function init() {
|
||||||
|
// Only load if WC Shipment Tracking is active
|
||||||
|
if (!class_exists('WC_Shipment_Tracking')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_action('woonoow/register_meta_fields', [__CLASS__, 'register_fields']);
|
||||||
|
add_filter('woonoow/order_allowed_private_meta', [__CLASS__, 'allow_meta']);
|
||||||
|
add_filter('woonoow/order_updatable_meta', [__CLASS__, 'allow_meta']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function register_fields() {
|
||||||
|
MetaFieldsRegistry::register_order_field('_tracking_number', [
|
||||||
|
'label' => __('Tracking Number', 'woonoow'),
|
||||||
|
'type' => 'text',
|
||||||
|
'section' => 'Shipment Tracking',
|
||||||
|
]);
|
||||||
|
|
||||||
|
MetaFieldsRegistry::register_order_field('_tracking_provider', [
|
||||||
|
'label' => __('Tracking Provider', 'woonoow'),
|
||||||
|
'type' => 'select',
|
||||||
|
'section' => 'Shipment Tracking',
|
||||||
|
'options' => [
|
||||||
|
['value' => 'jne', 'label' => 'JNE'],
|
||||||
|
['value' => 'jnt', 'label' => 'J&T'],
|
||||||
|
['value' => 'sicepat', 'label' => 'SiCepat'],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function allow_meta($allowed) {
|
||||||
|
$allowed[] = '_tracking_number';
|
||||||
|
$allowed[] = '_tracking_provider';
|
||||||
|
$allowed[] = '_shipment_tracking_items';
|
||||||
|
return $allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Checklist
|
||||||
|
|
||||||
|
### Phase 1: API Layer ✅
|
||||||
|
- [ ] Add meta data exposure to `OrdersController::show()`
|
||||||
|
- [ ] Add meta data update to `OrdersController::update()`
|
||||||
|
- [ ] Add meta data exposure to `ProductsController::get_product()`
|
||||||
|
- [ ] Add meta data update to `ProductsController::update_product()`
|
||||||
|
- [ ] Add filters: `woonoow/order_api_data`, `woonoow/product_api_data`
|
||||||
|
- [ ] Add filters: `woonoow/order_allowed_private_meta`, `woonoow/order_updatable_meta`
|
||||||
|
- [ ] Add actions: `woonoow/order_updated`, `woonoow/product_updated`
|
||||||
|
|
||||||
|
### Phase 2: Frontend Components ✅
|
||||||
|
- [ ] Create `MetaFields.tsx` component
|
||||||
|
- [ ] Create `useMetaFields.ts` hook
|
||||||
|
- [ ] Update `Orders/Edit.tsx` to include meta fields
|
||||||
|
- [ ] Update `Orders/View.tsx` to display meta fields (read-only)
|
||||||
|
- [ ] Update `Products/Edit.tsx` to include meta fields
|
||||||
|
- [ ] Add meta fields to Order/Product detail pages
|
||||||
|
|
||||||
|
### Phase 3: Plugin Integration ✅
|
||||||
|
- [ ] Create `MetaFieldsRegistry.php`
|
||||||
|
- [ ] Add `woonoow/register_meta_fields` action
|
||||||
|
- [ ] Localize fields to JavaScript
|
||||||
|
- [ ] Create example integration: `ShipmentTracking.php`
|
||||||
|
- [ ] Document integration pattern for third-party devs
|
||||||
|
|
||||||
|
### Phase 4: Testing ✅
|
||||||
|
- [ ] Test with WooCommerce Shipment Tracking plugin
|
||||||
|
- [ ] Test with ACF (Advanced Custom Fields)
|
||||||
|
- [ ] Test with CMB2 (Custom Metaboxes 2)
|
||||||
|
- [ ] Test with custom metabox plugins
|
||||||
|
- [ ] Test meta data save/update
|
||||||
|
- [ ] Test meta data display in detail view
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Third-Party Plugin Integration Guide
|
||||||
|
|
||||||
|
### For Plugin Developers:
|
||||||
|
|
||||||
|
**Example: Adding custom fields to WooNooW admin**
|
||||||
|
|
||||||
|
```php
|
||||||
|
// In your plugin file
|
||||||
|
add_action('woonoow/register_meta_fields', function() {
|
||||||
|
// Register order field
|
||||||
|
WooNooW\Compat\MetaFieldsRegistry::register_order_field('_my_custom_field', [
|
||||||
|
'label' => __('My Custom Field', 'my-plugin'),
|
||||||
|
'type' => 'text',
|
||||||
|
'section' => 'My Plugin',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Register product field
|
||||||
|
WooNooW\Compat\MetaFieldsRegistry::register_product_field('_my_product_field', [
|
||||||
|
'label' => __('My Product Field', 'my-plugin'),
|
||||||
|
'type' => 'textarea',
|
||||||
|
'section' => 'My Plugin',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Allow meta to be read/written
|
||||||
|
add_filter('woonoow/order_allowed_private_meta', function($allowed) {
|
||||||
|
$allowed[] = '_my_custom_field';
|
||||||
|
return $allowed;
|
||||||
|
});
|
||||||
|
|
||||||
|
add_filter('woonoow/order_updatable_meta', function($allowed) {
|
||||||
|
$allowed[] = '_my_custom_field';
|
||||||
|
return $allowed;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Priority
|
||||||
|
|
||||||
|
**Status:** 🔴 **CRITICAL - MUST IMPLEMENT**
|
||||||
|
|
||||||
|
**Why:**
|
||||||
|
1. Breaks compatibility with popular plugins (Shipment Tracking, ACF, etc.)
|
||||||
|
2. Users cannot see/edit custom fields added by other plugins
|
||||||
|
3. Data exists in database but not accessible in SPA admin
|
||||||
|
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**
|
||||||
|
|
||||||
|
**Blocking:**
|
||||||
|
- Coupons CRUD (can proceed)
|
||||||
|
- Customers CRUD (can proceed)
|
||||||
|
- **Production readiness** (BLOCKED until this is done)
|
||||||
@@ -33,6 +33,12 @@ WooNooW modernizes WooCommerce **without migration**, delivering a Hybrid + SPA
|
|||||||
- Prevents route conflicts between modules
|
- Prevents route conflicts between modules
|
||||||
- Documents ownership and naming conventions
|
- Documents ownership and naming conventions
|
||||||
|
|
||||||
|
**Metabox & Custom Fields compatibility:**
|
||||||
|
- `METABOX_COMPAT.md` - 🔴 **CRITICAL** compatibility requirement
|
||||||
|
- Documents how to expose WordPress/WooCommerce metaboxes in SPA
|
||||||
|
- **Currently NOT implemented** - blocks production readiness
|
||||||
|
- Required for third-party plugin compatibility (Shipment Tracking, ACF, etc.)
|
||||||
|
|
||||||
**Documentation Rules:**
|
**Documentation Rules:**
|
||||||
1. ✅ Update `PROGRESS_NOTE.md` after completing any major feature
|
1. ✅ Update `PROGRESS_NOTE.md` after completing any major feature
|
||||||
2. ✅ Add test cases to `TESTING_CHECKLIST.md` before implementation
|
2. ✅ Add test cases to `TESTING_CHECKLIST.md` before implementation
|
||||||
|
|||||||
Reference in New Issue
Block a user