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:
dwindown
2026-04-18 16:55:56 +07:00
parent ab69d03f78
commit bd9cdac02e
12 changed files with 570 additions and 275 deletions

View File

@@ -12,6 +12,8 @@ class ReactAdmin {
add_action( 'admin_enqueue_scripts', [$this, 'enqueue_assets'] );
add_filter( 'formipay/admin/data', [$this, 'localize_data'] );
add_action( 'admin_notices', [$this, 'version_notice'] );
add_filter( 'admin_footer_text', [$this, 'footer_toggle'] );
}
@@ -29,16 +31,25 @@ class ReactAdmin {
$build_url = FORMIPAY_URL . 'build';
if ( ! file_exists( $build_dir . '/admin.asset.php' ) ) {
error_log('[Formipay] Build files not found at: ' . $build_dir . '/admin.asset.php');
return; // Build not generated yet
}
$assets_file = require $build_dir . '/admin.asset.php';
$dependencies = $assets_file['dependencies'] ?? [];
// Filter out icon build dependencies - they're bundled, not separate scripts
$original_count = count($dependencies);
$dependencies = array_values(array_filter($dependencies, function($dep) {
return strpos($dep, 'wp-icons/build/') === false;
}));
error_log('[Formipay] Filtered dependencies: ' . $original_count . ' -> ' . count($dependencies));
$version = $assets_file['version'] ?? FORMIPAY_VERSION;
wp_enqueue_style(
'formipay-admin-style',
$build_url . '/style-admin.css',
$build_url . '/admin.css',
[],
$version
);
@@ -58,6 +69,10 @@ class ReactAdmin {
'nonce' => wp_create_nonce( 'formipay-admin' ),
] );
// Debug logging
error_log('[Formipay] Enqueuing React assets on screen: ' . $screen->id);
error_log('[Formipay] Page data: ' . wp_json_encode($data));
wp_localize_script( 'formipay-admin', 'formipayAdmin', $data );
}
@@ -116,6 +131,14 @@ class ReactAdmin {
$data['currencies'] = formipay_global_currency_options();
break;
case 'forms':
case 'coupons':
case 'access':
case 'licenses':
// These pages fetch data via AJAX, no initial data needed
$data = [];
break;
}
return $data;
@@ -128,8 +151,63 @@ class ReactAdmin {
public static function render_mount_point( $page ) {
printf(
'<div id="formipay-admin-root" data-formipay-mount="%s"></div>',
esc_attr( $page )
'<div id="formipay-admin-root" data-formipay-mount="%s">Loading %s...</div>',
esc_attr( $page ),
esc_html( ucfirst( $page ) )
);
}
/**
* Show admin notice about current admin version
*/
public function version_notice() {
$screen = get_current_screen();
// Only show on Formipay admin pages
if ( strpos( $screen->id, 'formipay' ) === false ) {
return;
}
$use_react = isset($_GET['react']) || get_option('formipay_use_react_admin', false);
$version = $use_react ? 'React (Beta)' : 'Classic';
printf(
'<div class="notice notice-info inline">
<p>
<strong>Formipay Admin:</strong> Using %s version.
<a href="%s" class="button button-small" style="margin-left: 10px;">Switch to %s</a>
</p>
</div>',
esc_html( $version ),
esc_url( add_query_arg( 'react', $use_react ? '0' : '1' ) ),
esc_html( $use_react ? 'Classic' : 'React (Beta)' )
);
}
/**
* Add toggle link to admin footer
*/
public function footer_toggle( $text ) {
$screen = get_current_screen();
// Only add toggle on Formipay admin pages
if ( strpos( $screen->id, 'formipay' ) === false ) {
return $text;
}
$use_react = isset($_GET['react']) || get_option('formipay_use_react_admin', false);
$toggle_url = add_query_arg( 'react', $use_react ? '0' : '1' );
$toggle_text = $use_react ? 'Switch to Classic' : 'Try React (Beta)';
return sprintf(
'%s | <a href="%s">%s</a>',
$text,
esc_url( $toggle_url ),
esc_html( $toggle_text )
);
}