plan: Complete implementation plan for Level 1 meta compatibility
**Implementation Plan Created: IMPLEMENTATION_PLAN_META_COMPAT.md** Following all documentation guidelines: - ADDON_BRIDGE_PATTERN.md (3-level strategy) - ADDON_DEVELOPMENT_GUIDE.md (hook system) - ADDON_REACT_INTEGRATION.md (React exposure) - METABOX_COMPAT.md (compatibility requirements) **Key Principles:** 1. ✅ Zero addon dependencies in core 2. ✅ Listen to WP/WooCommerce hooks (NOT WooNooW-specific) 3. ✅ Community does NOTHING extra 4. ❌ Do NOT support specific plugins 5. ❌ Do NOT integrate plugins into core **3 Phases:** Phase 1: Backend API Enhancement (2-3 days) - Add get_order_meta_data() / get_product_meta_data() - Add update_order_meta_data() / update_product_meta_data() - Expose meta in API responses - Add filters: woonoow/order_allowed_private_meta - Add filters: woonoow/order_updatable_meta - Add filters: woonoow/order_api_data - Add actions: woonoow/order_updated Phase 2: Frontend Components (3-4 days) - Create MetaFields.tsx component (generic field renderer) - Create useMetaFields.ts hook (registry access) - Update Orders/Edit.tsx to include meta fields - Update Products/Edit.tsx to include meta fields - Support all field types: text, textarea, number, select, checkbox Phase 3: PHP Registry System (2-3 days) - Create MetaFieldsRegistry.php - Add action: woonoow/register_meta_fields - Auto-register fields to allowed meta lists - Localize to JavaScript (window.WooNooWMetaFields) - Initialize in Plugin.php **Testing Plan:** - WooCommerce Shipment Tracking plugin - Advanced Custom Fields (ACF) - Custom metabox plugins - Meta data save/update - Field registration **Timeline:** 8-12 days (1.5-2 weeks) **Success Criteria:** ✅ Plugins using standard WP/WooCommerce meta work automatically ✅ No special integration needed ✅ Meta fields visible and editable ✅ Zero coupling with specific plugins ✅ Community does NOTHING extra Ready to start implementation!
This commit is contained in:
640
IMPLEMENTATION_PLAN_META_COMPAT.md
Normal file
640
IMPLEMENTATION_PLAN_META_COMPAT.md
Normal file
@@ -0,0 +1,640 @@
|
||||
# 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<string, any>;
|
||||
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<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 htmlFor={field.key}>
|
||||
{field.label}
|
||||
{field.description && (
|
||||
<span className="text-xs text-muted-foreground ml-2">
|
||||
{field.description}
|
||||
</span>
|
||||
)}
|
||||
</Label>
|
||||
|
||||
{field.type === 'text' && (
|
||||
<Input
|
||||
id={field.key}
|
||||
value={meta[field.key] || ''}
|
||||
onChange={(e) => onChange(field.key, e.target.value)}
|
||||
disabled={readOnly}
|
||||
placeholder={field.placeholder}
|
||||
/>
|
||||
)}
|
||||
|
||||
{field.type === 'textarea' && (
|
||||
<Textarea
|
||||
id={field.key}
|
||||
value={meta[field.key] || ''}
|
||||
onChange={(e) => onChange(field.key, e.target.value)}
|
||||
disabled={readOnly}
|
||||
placeholder={field.placeholder}
|
||||
rows={4}
|
||||
/>
|
||||
)}
|
||||
|
||||
{field.type === 'number' && (
|
||||
<Input
|
||||
id={field.key}
|
||||
type="number"
|
||||
value={meta[field.key] || ''}
|
||||
onChange={(e) => onChange(field.key, e.target.value)}
|
||||
disabled={readOnly}
|
||||
placeholder={field.placeholder}
|
||||
/>
|
||||
)}
|
||||
|
||||
{field.type === 'select' && field.options && (
|
||||
<Select
|
||||
value={meta[field.key] || ''}
|
||||
onValueChange={(value) => onChange(field.key, value)}
|
||||
disabled={readOnly}
|
||||
>
|
||||
<SelectTrigger id={field.key}>
|
||||
<SelectValue placeholder={field.placeholder || 'Select...'} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{field.options.map(opt => (
|
||||
<SelectItem key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
|
||||
{field.type === 'checkbox' && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id={field.key}
|
||||
checked={!!meta[field.key]}
|
||||
onCheckedChange={(checked) => onChange(field.key, checked)}
|
||||
disabled={readOnly}
|
||||
/>
|
||||
<label htmlFor={field.key} className="text-sm cursor-pointer">
|
||||
{field.placeholder || 'Enable'}
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 useMetaFields Hook
|
||||
|
||||
**File:** `admin-spa/src/hooks/useMetaFields.ts`
|
||||
|
||||
**Purpose:** Hook to get registered meta fields from global registry
|
||||
|
||||
```tsx
|
||||
interface MetaFieldsRegistry {
|
||||
orders: MetaField[];
|
||||
products: MetaField[];
|
||||
}
|
||||
|
||||
// Global registry exposed by PHP
|
||||
declare global {
|
||||
interface Window {
|
||||
WooNooWMetaFields?: MetaFieldsRegistry;
|
||||
}
|
||||
}
|
||||
|
||||
export function useMetaFields(type: 'orders' | 'products'): MetaField[] {
|
||||
const [fields, setFields] = useState<MetaField[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
// Get fields from global registry (set by PHP)
|
||||
const registry = window.WooNooWMetaFields || { orders: [], products: [] };
|
||||
setFields(registry[type] || []);
|
||||
|
||||
// Listen for dynamic field registration
|
||||
const handleFieldsUpdated = (e: CustomEvent) => {
|
||||
if (e.detail.type === type) {
|
||||
setFields(e.detail.fields);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('woonoow:meta_fields_updated', handleFieldsUpdated as EventListener);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('woonoow:meta_fields_updated', handleFieldsUpdated as EventListener);
|
||||
};
|
||||
}, [type]);
|
||||
|
||||
return fields;
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3 Integration in Order Edit
|
||||
|
||||
**File:** `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 [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 className="space-y-6">
|
||||
{/* Existing order form fields */}
|
||||
<OrderForm data={formData} onChange={setFormData} />
|
||||
|
||||
{/* Custom meta fields (Level 1 compatibility) */}
|
||||
{metaFields.length > 0 && (
|
||||
<MetaFields
|
||||
meta={formData.meta}
|
||||
fields={metaFields}
|
||||
onChange={handleMetaChange}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: PHP Registry System (2-3 days)
|
||||
|
||||
#### 3.1 MetaFieldsRegistry Class
|
||||
|
||||
**File:** `includes/Compat/MetaFieldsRegistry.php`
|
||||
|
||||
**Purpose:** Allow plugins to register meta fields for display in SPA
|
||||
|
||||
```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
|
||||
*
|
||||
* @param string $key Meta key (e.g., '_tracking_number')
|
||||
* @param array $args Field configuration
|
||||
*/
|
||||
public static function register_order_field($key, $args = []) {
|
||||
$defaults = [
|
||||
'key' => $key,
|
||||
'label' => self::format_label($key),
|
||||
'type' => 'text',
|
||||
'section' => 'Additional Fields',
|
||||
'description' => '',
|
||||
'placeholder' => '',
|
||||
];
|
||||
|
||||
self::$order_fields[$key] = array_merge($defaults, $args);
|
||||
|
||||
// Auto-add to allowed meta lists
|
||||
add_filter('woonoow/order_allowed_private_meta', function($allowed) use ($key) {
|
||||
if (!in_array($key, $allowed, true)) {
|
||||
$allowed[] = $key;
|
||||
}
|
||||
return $allowed;
|
||||
});
|
||||
|
||||
add_filter('woonoow/order_updatable_meta', function($allowed) use ($key) {
|
||||
if (!in_array($key, $allowed, true)) {
|
||||
$allowed[] = $key;
|
||||
}
|
||||
return $allowed;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register product meta field
|
||||
*/
|
||||
public static function register_product_field($key, $args = []) {
|
||||
$defaults = [
|
||||
'key' => $key,
|
||||
'label' => self::format_label($key),
|
||||
'type' => 'text',
|
||||
'section' => 'Additional Fields',
|
||||
'description' => '',
|
||||
'placeholder' => '',
|
||||
];
|
||||
|
||||
self::$product_fields[$key] = array_merge($defaults, $args);
|
||||
|
||||
// Auto-add to allowed meta lists
|
||||
add_filter('woonoow/product_allowed_private_meta', function($allowed) use ($key) {
|
||||
if (!in_array($key, $allowed, true)) {
|
||||
$allowed[] = $key;
|
||||
}
|
||||
return $allowed;
|
||||
});
|
||||
|
||||
add_filter('woonoow/product_updatable_meta', function($allowed) use ($key) {
|
||||
if (!in_array($key, $allowed, true)) {
|
||||
$allowed[] = $key;
|
||||
}
|
||||
return $allowed;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Format meta key to human-readable 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Localize fields to JavaScript
|
||||
*/
|
||||
public static function localize_fields() {
|
||||
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));
|
||||
|
||||
wp_localize_script('woonoow-admin', 'WooNooWMetaFields', [
|
||||
'orders' => $order_fields,
|
||||
'products' => $product_fields,
|
||||
]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 Initialize Registry
|
||||
|
||||
**File:** `includes/Core/Plugin.php`
|
||||
|
||||
```php
|
||||
// Add to init() method
|
||||
\WooNooW\Compat\MetaFieldsRegistry::init();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Plan
|
||||
|
||||
### Test Case 1: WooCommerce Shipment Tracking
|
||||
```php
|
||||
// Plugin stores tracking number
|
||||
update_post_meta($order_id, '_tracking_number', '1234567890');
|
||||
|
||||
// Expected: Field visible in WooNooW order edit
|
||||
// Expected: Can edit and save tracking number
|
||||
```
|
||||
|
||||
### Test Case 2: Advanced Custom Fields (ACF)
|
||||
```php
|
||||
// ACF stores custom field
|
||||
update_post_meta($product_id, 'custom_field', 'value');
|
||||
|
||||
// Expected: Field visible in WooNooW product edit
|
||||
// Expected: Can edit and save custom field
|
||||
```
|
||||
|
||||
### Test Case 3: Custom Metabox Plugin
|
||||
```php
|
||||
// Plugin registers field
|
||||
add_action('woonoow/register_meta_fields', function() {
|
||||
\WooNooW\Compat\MetaFieldsRegistry::register_order_field('_custom_field', [
|
||||
'label' => 'Custom Field',
|
||||
'type' => 'text',
|
||||
'section' => 'My Plugin',
|
||||
]);
|
||||
});
|
||||
|
||||
// Expected: Field appears in "My Plugin" section
|
||||
// Expected: Can edit and save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
### Backend (PHP)
|
||||
- [ ] Add `get_order_meta_data()` to OrdersController
|
||||
- [ ] Add `update_order_meta_data()` to OrdersController
|
||||
- [ ] Add `get_product_meta_data()` to ProductsController
|
||||
- [ ] Add `update_product_meta_data()` to ProductsController
|
||||
- [ ] Add filters: `woonoow/order_allowed_private_meta`
|
||||
- [ ] Add filters: `woonoow/order_updatable_meta`
|
||||
- [ ] Add filters: `woonoow/product_allowed_private_meta`
|
||||
- [ ] Add filters: `woonoow/product_updatable_meta`
|
||||
- [ ] Add filters: `woonoow/order_api_data`
|
||||
- [ ] Add filters: `woonoow/product_api_data`
|
||||
- [ ] Add actions: `woonoow/order_updated`
|
||||
- [ ] Add actions: `woonoow/product_updated`
|
||||
- [ ] Create `MetaFieldsRegistry.php`
|
||||
- [ ] Add action: `woonoow/register_meta_fields`
|
||||
- [ ] Initialize registry in Plugin.php
|
||||
|
||||
### Frontend (React/TypeScript)
|
||||
- [ ] 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 Product detail page
|
||||
|
||||
### Testing
|
||||
- [ ] Test with WooCommerce Shipment Tracking
|
||||
- [ ] Test with ACF (Advanced Custom Fields)
|
||||
- [ ] Test with custom metabox plugin
|
||||
- [ ] Test meta data save/update
|
||||
- [ ] Test meta data display in detail view
|
||||
- [ ] Test field registration via `woonoow/register_meta_fields`
|
||||
|
||||
---
|
||||
|
||||
## Timeline
|
||||
|
||||
- **Phase 1 (Backend):** 2-3 days
|
||||
- **Phase 2 (Frontend):** 3-4 days
|
||||
- **Phase 3 (Registry):** 2-3 days
|
||||
- **Testing:** 1-2 days
|
||||
|
||||
**Total:** 8-12 days (1.5-2 weeks)
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
✅ Plugins using standard WP/WooCommerce meta storage work automatically
|
||||
✅ No special integration needed from plugin developers
|
||||
✅ Meta fields visible and editable in WooNooW admin
|
||||
✅ Data saved correctly to WooCommerce database
|
||||
✅ Compatible with popular plugins (Shipment Tracking, ACF, etc.)
|
||||
✅ Follows 3-level compatibility strategy
|
||||
✅ Zero coupling with specific plugins
|
||||
✅ Community does NOTHING extra for Level 1 compatibility
|
||||
Reference in New Issue
Block a user