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>
9.7 KiB
Formipay — Migration Strategy
Date: April 18, 2026 (Updated) Context: Phase 2 — React Admin Foundation
Overview
This document explains how to approach migrating existing Formipay admin interfaces to React while ensuring zero feature loss.
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.
Existing Technology Inventory
| 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 |
Critical: Grid.js Migration Strategy
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
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) │
└─────────────────────────────────────────────────────────┘
Implementation Steps
Step 1: Feature Parity Checklist
Create MIGRATION_CHECKLIST.md with per-page feature requirements (see template below).
Step 2: Dual-Mode Rendering
In PHP page callback, check for query param:
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
// In settings or option
public function use_react_admin() {
return get_option('formipay_use_react_admin', false);
}
Step 4: Toggle Link in Admin
// 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:
- Manual Feature Testing: Go through checklist item by item
- Regression Testing: Old Grid.js version still works
- Data Compatibility: Both versions read/write same data format
- Performance: React version not significantly slower
- 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
// 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">
Data Compatibility Requirements
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 issues:
- Immediate: Set
formipay_use_react_adminto false - Users see: Classic Grid.js version (fully functional)
- Fix React: Debug and fix in development
- Retest: Go through checklist again
- 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
- 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
End of Migration Strategy.