From 96f0482cfbf77d99d334985b31c76b63848b3ac3 Mon Sep 17 00:00:00 2001 From: dwindown Date: Wed, 5 Nov 2025 23:52:57 +0700 Subject: [PATCH] fix: Modal initial values + sticky footer + HTML descriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✅ Issue 1: Modal Not Showing Current Values (FIXED!) Problem: Opening modal showed defaults, not current saved values Root Cause: Backend only sent field.default, not current value Solution: - Backend: Added field.value with current saved value - normalize_field() now includes: value: $current_settings[$key] - Frontend: Use field.value ?? field.default for initial data - GenericGatewayForm initializes with current values Result: ✅ Modal now shows "BNI Virtual Account 2" not "BNI Virtual Account" ✅ Issue 2: Sticky Modal Footer (FIXED!) Problem: Footer scrolls away with long forms Solution: - Restructured modal: header + scrollable body + sticky footer - DialogContent: flex flex-col with overflow on body only - Footer: sticky bottom-0 with border-t - Save button triggers form.requestSubmit() Result: ✅ Cancel, View in WooCommerce, Save always visible ✅ Issue 3: HTML in Descriptions (FIXED!) Problem: TriPay icon shows as raw HTML string Solution: - Changed: {field.description} - To: dangerouslySetInnerHTML={{ __html: field.description }} - Respects vendor creativity (images, formatting, links) Result: ✅ TriPay icon image renders properly 📋 Technical Details: Backend Changes (PaymentGatewaysProvider.php): - get_gateway_settings() passes $current_settings to extractors - normalize_field() adds 'value' => $current_settings[$key] - All fields now have both default and current value Frontend Changes: - GatewayField interface: Added value?: string | boolean - GenericGatewayForm: Initialize with field.value - Modal structure: Header + Body (scroll) + Footer (sticky) - Descriptions: Render as HTML with dangerouslySetInnerHTML Files Modified: - PaymentGatewaysProvider.php: Add current values to fields - Payments.tsx: Restructure modal layout + add value to interface - GenericGatewayForm.tsx: Use field.value + sticky footer + HTML descriptions 🎯 Result: ✅ Modal shows current saved values ✅ Footer always visible (no scrolling) ✅ Vendor HTML/images render properly --- .../settings/GenericGatewayForm.tsx | 81 +++++++++++++------ admin-spa/src/routes/Settings/Payments.tsx | 17 ++-- includes/Compat/PaymentGatewaysProvider.php | 29 ++++--- 3 files changed, 84 insertions(+), 43 deletions(-) diff --git a/admin-spa/src/components/settings/GenericGatewayForm.tsx b/admin-spa/src/components/settings/GenericGatewayForm.tsx index afdca97..9ccba9d 100644 --- a/admin-spa/src/components/settings/GenericGatewayForm.tsx +++ b/admin-spa/src/components/settings/GenericGatewayForm.tsx @@ -21,6 +21,7 @@ interface GatewayField { title: string; description: string; default: string | boolean; + value?: string | boolean; // Current saved value from backend placeholder?: string; required: boolean; options?: Record; @@ -56,6 +57,26 @@ export function GenericGatewayForm({ gateway, onSave, onCancel }: GenericGateway const [isSaving, setIsSaving] = useState(false); const [unsupportedFields, setUnsupportedFields] = useState([]); + // Initialize form data with current gateway values + React.useEffect(() => { + const initialData: Record = {}; + + const categories: Record[] = [ + gateway.settings.basic, + gateway.settings.api, + gateway.settings.advanced, + ]; + + categories.forEach((category) => { + Object.values(category).forEach((field) => { + // Use current value from field (backend sends this now!) + initialData[field.id] = field.value ?? field.default; + }); + }); + + setFormData(initialData); + }, [gateway]); + // Check for unsupported fields React.useEffect(() => { const unsupported: string[] = []; @@ -121,7 +142,10 @@ export function GenericGatewayForm({ gateway, onSave, onCancel }: GenericGateway {field.required && *} {field.description && ( -

{field.description}

+

)} @@ -225,25 +249,26 @@ export function GenericGatewayForm({ gateway, onSave, onCancel }: GenericGateway const useMultiPage = totalFields >= 20; return ( -

- {/* Warning for unsupported fields */} - {unsupportedFields.length > 0 && ( - - - - Some advanced settings are not supported in this interface.{' '} - - Configure in WooCommerce - - - - - )} + <> + + {/* Warning for unsupported fields */} + {unsupportedFields.length > 0 && ( + + + + Some advanced settings are not supported in this interface.{' '} + + Configure in WooCommerce + + + + + )} {useMultiPage ? ( @@ -295,9 +320,10 @@ export function GenericGatewayForm({ gateway, onSave, onCancel }: GenericGateway )} )} + - {/* Actions */} -
+ {/* Sticky Footer */} +
-
- + ); } diff --git a/admin-spa/src/routes/Settings/Payments.tsx b/admin-spa/src/routes/Settings/Payments.tsx index 71c6054..89e48b3 100644 --- a/admin-spa/src/routes/Settings/Payments.tsx +++ b/admin-spa/src/routes/Settings/Payments.tsx @@ -18,6 +18,7 @@ interface GatewayField { title: string; description: string; default: string | boolean; + value: string | boolean; // Current saved value placeholder?: string; required: boolean; options?: Record; @@ -327,15 +328,17 @@ export default function PaymentsPage() { {/* Gateway Settings Modal */} {selectedGateway && ( - - + + {selectedGateway.title} Settings - setIsModalOpen(false)} - /> +
+ setIsModalOpen(false)} + /> +
)} diff --git a/includes/Compat/PaymentGatewaysProvider.php b/includes/Compat/PaymentGatewaysProvider.php index 9ad6f00..01a3e25 100644 --- a/includes/Compat/PaymentGatewaysProvider.php +++ b/includes/Compat/PaymentGatewaysProvider.php @@ -194,11 +194,12 @@ class PaymentGatewaysProvider { */ private static function get_gateway_settings(WC_Payment_Gateway $gateway): array { $form_fields = $gateway->get_form_fields(); + $current_settings = $gateway->settings; // Get current saved values return [ - 'basic' => self::extract_basic_fields($form_fields), - 'api' => self::extract_api_fields($form_fields), - 'advanced' => self::extract_advanced_fields($form_fields), + 'basic' => self::extract_basic_fields($form_fields, $current_settings), + 'api' => self::extract_api_fields($form_fields, $current_settings), + 'advanced' => self::extract_advanced_fields($form_fields, $current_settings), ]; } @@ -208,9 +209,9 @@ class PaymentGatewaysProvider { * @param array $form_fields All form fields * @return array Basic fields */ - private static function extract_basic_fields(array $form_fields): array { + private static function extract_basic_fields(array $form_fields, array $current_settings): array { $basic_keys = ['enabled', 'title', 'description', 'instructions']; - return self::filter_fields($form_fields, $basic_keys); + return self::filter_fields($form_fields, $basic_keys, $current_settings); } /** @@ -219,14 +220,14 @@ class PaymentGatewaysProvider { * @param array $form_fields All form fields * @return array API fields */ - private static function extract_api_fields(array $form_fields): array { + private static function extract_api_fields(array $form_fields, array $current_settings): array { $api_patterns = ['key', 'secret', 'token', 'api', 'client', 'merchant', 'account']; $api_fields = []; foreach ($form_fields as $key => $field) { foreach ($api_patterns as $pattern) { if (stripos($key, $pattern) !== false) { - $api_fields[$key] = self::normalize_field($key, $field); + $api_fields[$key] = self::normalize_field($key, $field, $current_settings); break; } } @@ -241,7 +242,7 @@ class PaymentGatewaysProvider { * @param array $form_fields All form fields * @return array Advanced fields */ - private static function extract_advanced_fields(array $form_fields): array { + private static function extract_advanced_fields(array $form_fields, array $current_settings): array { $basic_keys = ['enabled', 'title', 'description', 'instructions']; $advanced_fields = []; @@ -262,7 +263,7 @@ class PaymentGatewaysProvider { } if (!$is_api) { - $advanced_fields[$key] = self::normalize_field($key, $field); + $advanced_fields[$key] = self::normalize_field($key, $field, $current_settings); } } @@ -276,12 +277,12 @@ class PaymentGatewaysProvider { * @param array $keys Keys to extract * @return array Filtered fields */ - private static function filter_fields(array $form_fields, array $keys): array { + private static function filter_fields(array $form_fields, array $keys, array $current_settings): array { $filtered = []; foreach ($keys as $key) { if (isset($form_fields[$key])) { - $filtered[$key] = self::normalize_field($key, $form_fields[$key]); + $filtered[$key] = self::normalize_field($key, $form_fields[$key], $current_settings); } } @@ -295,13 +296,17 @@ class PaymentGatewaysProvider { * @param array $field Field data * @return array Normalized field */ - private static function normalize_field(string $key, array $field): array { + private static function normalize_field(string $key, array $field, array $current_settings): array { + // Use current value if available, otherwise use default + $current_value = $current_settings[$key] ?? $field['default'] ?? ''; + return [ 'id' => $key, 'type' => $field['type'] ?? 'text', 'title' => $field['title'] ?? '', 'description' => $field['description'] ?? '', 'default' => $field['default'] ?? '', + 'value' => $current_value, // Add current value! 'placeholder' => $field['placeholder'] ?? '', 'required' => !empty($field['required']), 'options' => $field['options'] ?? null,