feat: implement coexistence strategy for Grid.js and React admin
Implement dual-mode rendering allowing classic Grid.js and new React versions to run side-by-side during migration. - Add coexistence mode checks to all admin page methods - Check query param ?react=1 or option 'formipay_use_react_admin' - Include classic PHP pages when React not active - Add admin notice showing current version with toggle button - Add footer toggle link to switch between versions This ensures zero feature loss - old Grid.js pages continue working (~20 features per page) while React versions are developed. Files: - Form.php, Coupon.php, Access.php, Order.php - Customer.php, Product.php, License.php - ReactAdmin.php (added version_notice, footer_toggle) Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,199 +1,281 @@
|
||||
# Formipay — Vue to React Migration Strategy
|
||||
# Formipay — Migration Strategy
|
||||
|
||||
**Date:** April 18, 2026
|
||||
**Date:** April 18, 2026 (Updated)
|
||||
**Context:** Phase 2 — React Admin Foundation
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This document explains how to approach the existing Vue.js code in Formipay during the migration to React admin panels.
|
||||
This document explains how to approach migrating existing Formipay admin interfaces to React while **ensuring zero feature loss**.
|
||||
|
||||
**Key Principle:** We are NOT doing a "rewrite everything at once" approach. Vue and React will coexist during Phase 2, with Vue being removed incrementally as React replacements are shipped.
|
||||
**Key Principle:** **Coexistence until Feature Parity** — New React versions must match or exceed old functionality before deprecating old code. No "delete and replace" without validation.
|
||||
|
||||
---
|
||||
|
||||
## Current Vue.js Usage Inventory
|
||||
## Existing Technology Inventory
|
||||
|
||||
| Type | Location | Purpose | Replacement Phase |
|
||||
|------|----------|---------|-------------------|
|
||||
| **WPCFTO Framework** | `vendor/` | Settings form builder (repeater, fields, etc.) | F2.8 (Global Settings) |
|
||||
| **Custom Vue 2 App** | `admin/assets/js/admin-product-editor.js` | Product variation pricing table | F2.9 (Product Editor) |
|
||||
| **Partial Vue Editor** | `admin/assets/vue/` (if exists) | Form builder canvas | F2.4 (Form Builder) |
|
||||
| Technology | Location | Purpose | Feature Count | Migration Priority |
|
||||
|-------------|----------|---------|---------------|-------------------|
|
||||
| **WPCFTO Framework** | `vendor/` | Settings form builder | N/A | Low (replaced by React settings) |
|
||||
| **Grid.js** | `admin/assets/js/page-*.js` | All admin tables | ~20 features per page | **HIGH** (current gap) |
|
||||
| **SweetAlert2** | `vendor/SweetAlert2/` | Modal dialogs | N/A | None (keep using) |
|
||||
| **Custom Vue 2 App** | `admin/assets/js/admin-product-editor.js` | Product variation pricing | ~7 features | Medium |
|
||||
| **jQuery** | Core WP dependency | DOM manipulation | N/A | Phase out gradually |
|
||||
|
||||
---
|
||||
|
||||
## Migration Strategy
|
||||
## Critical: Grid.js Migration Strategy
|
||||
|
||||
### Phase 1: Coexistence (Current State)
|
||||
### Current Grid.js Features (Must Preserve)
|
||||
|
||||
**Every admin page with Grid.js has these features:**
|
||||
|
||||
| Feature | Forms | Coupons | Access | Orders | Products |
|
||||
|---------|-------|--------|-------|--------|----------|
|
||||
| Checkbox column + "Select All" | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| Bulk delete button | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| Inline row actions (hover) | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| Status filter tabs (All/Published/Draft) | ✅ | ✅ | ✅ | ❌ | ✅ |
|
||||
| Search input | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| Sort dropdown | ✅ | ✅ | ✅ | ❌ | ✅ |
|
||||
| Order (ASC/DESC) | ✅ | ✅ | ✅ | ❌ | ✅ |
|
||||
| Server-side pagination | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| "Add New" modal (SweetAlert2) | ✅ | ✅ | ✅ | ❌ | ✅ |
|
||||
| Inline delete action | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| Inline duplicate action | ✅ | ✅ | ✅ | ❌ | ✅ |
|
||||
| Shortcode copy button | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| Multi-currency display | ❌ | ✅ | ❌ | ❌ | ✅ |
|
||||
|
||||
**Total: ~20 table features per page**
|
||||
|
||||
### Migration Approach: Coexistence
|
||||
|
||||
**DO NOT:** Delete old Grid.js code until React replacement is feature-complete
|
||||
|
||||
**DO:** Use query param or feature flag to run both versions side-by-side
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Admin Pages │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ Forms │ Products │ Orders │ Settings │
|
||||
│ WPCFTO │ WPCFTO │ WPCFTO │ WPCFTO │
|
||||
│ + Vue │ + Vue │ (none) │ + Vue │
|
||||
│ │ (custom) │ │ │
|
||||
└─────────────────────────────────────────────┘
|
||||
Phase 1: Coexistence (Required)
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Formipay Admin │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ Old: Grid.js Tables → Fully functional │
|
||||
│ New: React Tables → Under development, feature-incomplete │
|
||||
│ │
|
||||
│ Access: ?old=1 → Grid.js | ?old=0 → React (default when ready) │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Status:** All pages working. WPCFTO handles forms, custom Vue handles product variations.
|
||||
### Implementation Steps
|
||||
|
||||
---
|
||||
**Step 1: Feature Parity Checklist**
|
||||
|
||||
### Phase 2: Incremental React Rollout
|
||||
Create `MIGRATION_CHECKLIST.md` with per-page feature requirements (see template below).
|
||||
|
||||
**Week 3:** Build pipeline + React infrastructure. Vue untouched.
|
||||
**Step 2: Dual-Mode Rendering**
|
||||
|
||||
**Week 4:** React Form Builder replaces WPCFTO form editor.
|
||||
```
|
||||
Forms: WPCFTO/Vue → React ✅
|
||||
Products: WPCFTO + custom Vue → unchanged
|
||||
Orders: (none) → React ✅
|
||||
Settings: WPCFTO → unchanged
|
||||
```
|
||||
In PHP page callback, check for query param:
|
||||
|
||||
**Week 5:** React Order Management (no Vue to touch).
|
||||
```
|
||||
Orders → React ✅
|
||||
```
|
||||
|
||||
**Week 6:** React Global Settings replaces WPCFTO.
|
||||
```
|
||||
Settings: WPCFTO → React ✅
|
||||
Products: WPCFTO + custom Vue → unchanged
|
||||
```
|
||||
|
||||
**After Week 6:** WPCFTO framework can be fully removed. Only custom Vue remains.
|
||||
|
||||
---
|
||||
|
||||
### Product Editor Migration (F2.9)
|
||||
|
||||
The custom Vue app in `admin-product-editor.js` is the last Vue piece to migrate.
|
||||
|
||||
#### Current Features (Must Recreate in React)
|
||||
|
||||
| Feature | Complexity | Notes |
|
||||
|---------|-----------|-------|
|
||||
| Multi-currency flat pricing | Medium | Show all currencies as columns when enabled |
|
||||
| Multi-currency expanded mode | High | Inner tables per variation row |
|
||||
| Dynamic rows from attribute repeater | High | Syncs with WPCFTO repeater in real-time |
|
||||
| Decimal digits per currency | Medium | Step values calculated from currency config |
|
||||
| Required field validation | Low | Default currency must have price |
|
||||
| Real-time JSON update to hidden input | Medium | `product_variation_variables` hidden field |
|
||||
| SweetAlert2 error dialogs | Low | Uses existing window.Swal or custom alert |
|
||||
|
||||
#### Implementation Approach
|
||||
|
||||
**Option A: Recreate from Scratch (Recommended)**
|
||||
|
||||
1. **Read current Vue code logic** — understand the data flow
|
||||
2. **Build React component** — `VariationPricingTable.jsx`
|
||||
- Use `@tanstack/react-table` for the table
|
||||
- Use `@wordpress/components` for form inputs (TextControl, etc.)
|
||||
- State management via React hooks (useState, useEffect)
|
||||
3. **Data sync layer** — same API endpoint `get_product_variables`
|
||||
4. **Validation logic** — port `findFirstMissingDefault()` to React
|
||||
|
||||
**Pros:** Clean React code, modern patterns, better type safety
|
||||
**Cons:** Requires careful testing to match all edge cases
|
||||
|
||||
**Option B: Vue-in-React Wrapper (Not Recommended)**
|
||||
|
||||
Wrap the existing Vue app in a React component using a library like `vue-react-wrapper`.
|
||||
|
||||
**Pros:** Faster, less risky
|
||||
**Cons:** Technical debt, adds bundle size, mixing paradigms
|
||||
|
||||
**Recommendation:** Option A — rewrite in React. The logic is well-contained (~500 lines) and rewriting gives us clean, maintainable React code.
|
||||
|
||||
---
|
||||
|
||||
## Migration Checklist for F2.9
|
||||
|
||||
When implementing the React Product Editor:
|
||||
|
||||
- [ ] Read and document current Vue variation table behavior
|
||||
- [ ] Create `src/admin/pages/Products/VariationPricingTable.jsx`
|
||||
- [ ] Implement multi-currency flat pricing mode
|
||||
- [ ] Implement multi-currency expanded mode (inner tables)
|
||||
- [ ] Implement attribute repeater sync (MutationObserver or polling)
|
||||
- [ ] Implement decimal digits per currency step calculation
|
||||
- [ ] Implement required field validation (default currency)
|
||||
- [ ] Implement real-time JSON update to hidden `product_variation_variables` input
|
||||
- [ ] Add SweetAlert2 or equivalent for validation errors
|
||||
- [ ] Test with various currency configurations
|
||||
- [ ] Test with existing products (data migration)
|
||||
- [ ] Remove old Vue script enqueuing from Product page
|
||||
- [ ] Remove Vue dependency if no longer used elsewhere
|
||||
|
||||
---
|
||||
|
||||
## Data Compatibility
|
||||
|
||||
The React component must read/write the same data format as the Vue app:
|
||||
|
||||
**Hidden input format** (`product_variation_variables`):
|
||||
```json
|
||||
[
|
||||
{
|
||||
"key": "Red|||Large",
|
||||
"name": "Red - Large",
|
||||
"stock": "",
|
||||
"weight": 0,
|
||||
"active": true,
|
||||
"prices": [
|
||||
{
|
||||
"currency": "USD:::United States Dollar:::$",
|
||||
"regular_price": "29.99",
|
||||
"sale_price": "24.99",
|
||||
"currency_decimal_digits": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**API endpoint** (already exists):
|
||||
```php
|
||||
// Product.php - add this if not present
|
||||
add_action('wp_ajax_get_product_variables', [$this, 'ajax_get_product_variables']);
|
||||
public function formipay_form() {
|
||||
$use_react = isset($_GET['react']) || get_option('formipay_use_react_admin', false);
|
||||
|
||||
if ($use_react) {
|
||||
\Formipay\Admin\ReactAdmin::render_mount_point('forms');
|
||||
} else {
|
||||
include_once FORMIPAY_PATH . 'admin/page-forms.php';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3: Feature Flag for Default**
|
||||
|
||||
```php
|
||||
// In settings or option
|
||||
public function use_react_admin() {
|
||||
return get_option('formipay_use_react_admin', false);
|
||||
}
|
||||
```
|
||||
|
||||
**Step 4: Toggle Link in Admin**
|
||||
|
||||
```php
|
||||
// Add to admin footer or menu bar
|
||||
<?php if (!get_option('formipay_use_react_admin')) : ?>
|
||||
<a href="<?php echo admin_url('admin.php?page=formipay&react=1'); ?>">
|
||||
Try React Admin (Beta)
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<a href="<?php echo admin_url('admin.php?page=formipay&react=0'); ?>">
|
||||
Use Classic Admin
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
```
|
||||
|
||||
### Testing Protocol
|
||||
|
||||
Before setting `use_react_admin` to true as default:
|
||||
|
||||
1. **Manual Feature Testing**: Go through checklist item by item
|
||||
2. **Regression Testing**: Old Grid.js version still works
|
||||
3. **Data Compatibility**: Both versions read/write same data format
|
||||
4. **Performance**: React version not significantly slower
|
||||
5. **Browser Testing**: Test in Chrome, Firefox, Safari
|
||||
|
||||
**Only when ALL checkboxes pass → Enable React by default**
|
||||
|
||||
---
|
||||
|
||||
## React Component Library Strategy
|
||||
|
||||
### Approved Libraries
|
||||
|
||||
| Library | Purpose | Why |
|
||||
|---------|---------|-----|
|
||||
| **@wordpress/components** | UI primitives (Button, Modal, SelectControl, TextControl) | Native WP styling, already bundled |
|
||||
| **@tanstack/react-table** (v8) | Headless table engine | Flexible, performant, React 18 compatible |
|
||||
| **SweetAlert2** (existing) | Modals, confirmations, toasts | Already in use, keep as-is |
|
||||
| **@wordpress/icons** | Icons | Already bundled, correct WP styling |
|
||||
|
||||
### Libraries to AVOID
|
||||
|
||||
| Library | Reason to Avoid |
|
||||
|---------|-----------------|
|
||||
| **shadcn/ui** | Wrong styling (Tailwind vs WP), requires Tailwind setup |
|
||||
| **Material UI (@mui/x-data-grid)** | Wrong styling, 100KB+ bundle, overkill |
|
||||
| **react-table** (v7) | Deprecated, use @tanstack/react-table |
|
||||
| **react-data-table** | Heavy bundle, opinionated styling |
|
||||
|
||||
### Building the Table Component
|
||||
|
||||
```jsx
|
||||
// Use @tanstack/react-table for the engine
|
||||
import { useReactTable } from '@tanstack/react-table';
|
||||
|
||||
// Use @wordpress/components for UI
|
||||
import { Modal, Button, CheckboxControl } from '@wordpress/components';
|
||||
|
||||
// Style with WordPress classes
|
||||
<table className="wp-list-table widefat fixed striped">
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Strategy
|
||||
## Data Compatibility Requirements
|
||||
|
||||
1. **Unit Tests:** Test variation price calculation logic in isolation
|
||||
2. **Integration Tests:** Test loading/saving variations via AJAX
|
||||
3. **Manual Tests:**
|
||||
- Create product with variations
|
||||
- Test multi-currency flat mode
|
||||
- Test multi-currency expanded mode
|
||||
- Test validation (missing default price)
|
||||
- Test attribute repeater sync
|
||||
- Test editing existing products (data compatibility)
|
||||
### AJAX Endpoints (Must Preserve)
|
||||
|
||||
| Endpoint | Request | Response Format |
|
||||
|----------|---------|------------------|
|
||||
| `formipay-tabledata-forms` | GET + params | `{results, total, posts_report}` |
|
||||
| `formipay-create-form-post` | POST + title | `{success, data: {edit_post_url}}` |
|
||||
| `formipay-delete-form` | POST + id | `{success, data: {title, message, icon}}` |
|
||||
| `formipay-duplicate-form` | POST + id | `{success, data: {title, message, icon}}` |
|
||||
| `formipay-bulk-delete-form` | POST + ids[] | `{success, data: {title, message, icon}}` |
|
||||
| `get_product_variables` | GET + post_id | Variation data object |
|
||||
| `formipay-tabledata-coupons` | GET + params | `{results, total, posts_report}` |
|
||||
| `formipay-tabledata-access-items` | GET + params | `{results, total, posts_report}` |
|
||||
| `formipay-tabledata-orders` | GET + params | `{results, total, posts_report}` |
|
||||
| `formipay-tabledata-customers` | GET + params | `{results, total, posts_report}` |
|
||||
|
||||
**Critical:** Response formats must remain identical for compatibility!
|
||||
|
||||
---
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If React version has critical bugs:
|
||||
1. Keep Vue version as fallback
|
||||
2. Add feature flag in settings: "Use React Product Editor (beta)"
|
||||
3. Default to Vue, allow opting into React
|
||||
4. Fix React, then make it default
|
||||
If React version has issues:
|
||||
|
||||
1. **Immediate:** Set `formipay_use_react_admin` to false
|
||||
2. **Users see:** Classic Grid.js version (fully functional)
|
||||
3. **Fix React:** Debug and fix in development
|
||||
4. **Retest:** Go through checklist again
|
||||
5. **Re-enable:** Set flag back to true
|
||||
|
||||
**Critical:** Never deploy without working fallback!
|
||||
|
||||
---
|
||||
|
||||
## Migration Checklist Template
|
||||
|
||||
Copy this template for each page migration:
|
||||
|
||||
### [Page Name] Migration Checklist
|
||||
|
||||
#### Table Core Features
|
||||
- [ ] Data loads and displays correctly
|
||||
- [ ] Loading spinner shown during fetch
|
||||
- [ ] Empty state shown when no data
|
||||
- [ ] Error state handled gracefully
|
||||
|
||||
#### Selection Features
|
||||
- [ ] Checkbox column renders
|
||||
- [ ] "Select All" checkbox works
|
||||
- [ ] Individual row checkboxes work
|
||||
- [ ] Checkboxes persist across page changes
|
||||
- [ ] Bulk delete button appears when rows selected
|
||||
- [ ] Bulk delete confirmation modal
|
||||
- [ ] Bulk delete refreshes table
|
||||
|
||||
#### Row Actions
|
||||
- [ ] Hover shows action links
|
||||
- [ ] Edit action navigates correctly
|
||||
- [ ] Delete action shows confirmation
|
||||
- [ ] Delete action removes row and refreshes
|
||||
- [ ] Duplicate action shows confirmation
|
||||
- [ ] Duplicate action adds new row and refreshes
|
||||
|
||||
#### Filtering & Sorting
|
||||
- [ ] Status filter tabs work
|
||||
- [ ] Status counts display correctly
|
||||
- [ ] Active filter highlighted
|
||||
- [ ] Search input filters results
|
||||
- [ ] Search debounce (don't spam server)
|
||||
- [ ] Sort dropdown works
|
||||
- [ ] Order toggle works
|
||||
- [ ] Combined filters work (search + status + sort)
|
||||
|
||||
#### Pagination
|
||||
- [ ] Pagination controls display
|
||||
- [ ] Page numbers correct
|
||||
- [ ] Next/Previous buttons work
|
||||
- [ ] Limit per page respected
|
||||
- [ ] Total count accurate
|
||||
|
||||
#### Create Features
|
||||
- [ ] "Add New" button visible
|
||||
- [ ] Modal/dialog opens on click
|
||||
- [ ] Modal has required fields
|
||||
- [ ] Modal validation works
|
||||
- [ ] Create action succeeds
|
||||
- [ ] Post creation redirects to edit
|
||||
- [ ] Error handling in modal
|
||||
|
||||
#### UX Details
|
||||
- [ ] Row hover effects work
|
||||
- [ ] Selected row highlighting
|
||||
- [ ] Toast notifications for actions
|
||||
- [ ] Confirmation dialogs for destructive actions
|
||||
- [ ] Keyboard accessibility (Enter, Escape)
|
||||
- [ ] Loading states during actions
|
||||
|
||||
#### Data Compatibility
|
||||
- [ ] Same AJAX endpoints as old version
|
||||
- [ ] Same request format
|
||||
- [ ] Same response format handling
|
||||
- [ ] Hidden inputs updated (if applicable)
|
||||
- [ ] WordPress nonces handled correctly
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- **No Vue Router used** — the Vue app is a simple mount, no routing to worry about
|
||||
- **No Vuex** — state is local component state + hidden input sync
|
||||
- **jQuery dependency** — the Vue app uses jQuery for DOM selection (`$('#product-variables-table')`. React version should eliminate this.
|
||||
- **Timing:** WPCFTO removal happens after Global Settings (F2.8), but Product Editor Vue app is independent — can be migrated anytime after Week 3.
|
||||
- **Grid.js Library:** ~20KB, well-tested, handles server-side pagination well
|
||||
- **@tanstack/react-table:** ~9KB, headless, requires more setup but more flexible
|
||||
- **jQuery:** Still used by WordPress core, will remain for now
|
||||
- **SweetAlert2:** Keep using, integrates well with React
|
||||
- **Migration is NOT a race:** Take time to get it right
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user