feat: add React FieldRenderer system for settings and metaboxes

Complete React-based field rendering system that replaces WPCFTO Vue.js
layer while maintaining PHP field configuration compatibility.

Components:
- FieldRenderer: Main renderer with tabs support (metabox) and direct mode (settings)
- FieldTypes: 15+ field types (Text, Number, Select, Radio, Date, etc.)
- RepeaterField: Collapsible repeater with currency label parsing
- DependencyEngine: Show/hide fields based on conditions
- ValidationEngine: Client-side validation with error messages
- SettingsRenderer: Settings page with AJAX save to wp_options

Features:
- Repeater rows collapsed by default with readable currency titles
- Searchable dropdowns using Popover + Command pattern
- Proper label resolution for pre-selected values
- Hidden input sync for WordPress form submission

Also includes:
- FieldConfigBridge: Transform PHP configs to React format
- Updated Settings.php for React-based settings page
- Radio-group UI component
- wp-admin-restore.css for admin panel isolation
This commit is contained in:
dwindown
2026-04-28 16:48:08 +07:00
parent 7a6765a579
commit 622c9f8eb7
206 changed files with 5788 additions and 1612 deletions

View File

@@ -13,6 +13,9 @@ class ReactAdmin {
add_action( 'admin_enqueue_scripts', [$this, 'enqueue_assets'] );
add_filter( 'formipay/admin/data', [$this, 'localize_data'] );
// AJAX endpoint for field configuration
add_action( 'wp_ajax_formipay-get-field-config', [$this, 'ajax_get_field_config'] );
}
public function enqueue_assets() {
@@ -40,18 +43,16 @@ class ReactAdmin {
$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 . '/admin.css',
[],
['wp-admin', 'colors', 'dashicons', 'common', 'forms', 'admin-menu', 'dashboard', 'list-tables', 'edit', 'revisions', 'media', 'themes', 'about', 'nav-menus'],
$version
);
@@ -72,10 +73,6 @@ class ReactAdmin {
'siteUrl' => site_url(),
]);
// 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);
}
@@ -161,4 +158,27 @@ class ReactAdmin {
}
/**
* AJAX handler for getting field configuration
*/
public function ajax_get_field_config() {
check_ajax_referer( 'formipay-admin', '_wpnonce', '', true );
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => __('Unauthorized', 'formipay')]);
}
$post_id = isset($_REQUEST['post_id']) ? intval($_REQUEST['post_id']) : 0;
$post_type = isset($_REQUEST['post_type']) ? sanitize_text_field($_REQUEST['post_type']) : '';
if (!$post_id || !$post_type) {
wp_send_json_error(['message' => __('Invalid request', 'formipay')]);
}
$config = FieldConfigBridge::get_config_for_post($post_id, $post_type);
wp_send_json_success($config);
}
}