feat: add form builder AJAX handlers (F2.11)
- Create FormBuilderAjax class for React form builder - Add formipay_save_form_fields AJAX action - Add formipay_load_form_fields AJAX action - Sanitize field data on save - Update FormBuilder to load fields on mount - Add save status feedback (saving, saved, error) - Register FormBuilderAjax singleton in main plugin file
This commit is contained in:
@@ -50,6 +50,7 @@ spl_autoload_register(function ($class) {
|
||||
|
||||
\Formipay\Init::get_instance();
|
||||
\Formipay\Admin\ReactAdmin::get_instance();
|
||||
\Formipay\Admin\FormBuilderAjax::get_instance();
|
||||
|
||||
register_activation_hook( __FILE__, 'formipay_activate' );
|
||||
function formipay_activate() {
|
||||
|
||||
127
includes/Admin/FormBuilderAjax.php
Normal file
127
includes/Admin/FormBuilderAjax.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
namespace Formipay\Admin;
|
||||
use Formipay\Traits\SingletonTrait;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) exit;
|
||||
|
||||
/**
|
||||
* AJAX handlers for React Form Builder
|
||||
*/
|
||||
class FormBuilderAjax {
|
||||
|
||||
use SingletonTrait;
|
||||
|
||||
protected function __construct() {
|
||||
|
||||
add_action( 'wp_ajax_formipay_save_form_fields', [$this, 'save_form_fields'] );
|
||||
add_action( 'wp_ajax_formipay_load_form_fields', [$this, 'load_form_fields'] );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Save form fields via AJAX
|
||||
*/
|
||||
public function save_form_fields() {
|
||||
|
||||
check_ajax_referer( 'formipay-admin', '_wpnonce' );
|
||||
|
||||
if ( ! current_user_can( 'edit_posts' ) ) {
|
||||
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
|
||||
}
|
||||
|
||||
$post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0;
|
||||
$fields_json = isset( $_POST['fields'] ) ? wp_unslash( $_POST['fields'] ) : '[]';
|
||||
|
||||
if ( $post_id === 0 ) {
|
||||
wp_send_json_error( [ 'message' => 'Invalid post ID' ] );
|
||||
}
|
||||
|
||||
$fields = json_decode( $fields_json, true );
|
||||
|
||||
if ( json_last_error() !== JSON_ERROR_NONE ) {
|
||||
wp_send_json_error( [ 'message' => 'Invalid JSON data' ] );
|
||||
}
|
||||
|
||||
// Sanitize fields
|
||||
$sanitized_fields = [];
|
||||
foreach ( $fields as $field ) {
|
||||
$sanitized_fields[] = $this->sanitize_field( $field );
|
||||
}
|
||||
|
||||
// Update post meta
|
||||
$current_settings = get_post_meta( $post_id, 'formipay_settings', true );
|
||||
$current_settings = is_array( $current_settings ) ? $current_settings : [];
|
||||
|
||||
$current_settings['fields'] = $sanitized_fields;
|
||||
|
||||
update_post_meta( $post_id, 'formipay_settings', $current_settings );
|
||||
|
||||
wp_send_json_success( [
|
||||
'message' => 'Form fields saved successfully',
|
||||
'fields' => $sanitized_fields
|
||||
] );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Load form fields via AJAX
|
||||
*/
|
||||
public function load_form_fields() {
|
||||
|
||||
check_ajax_referer( 'formipay-admin', '_wpnonce' );
|
||||
|
||||
if ( ! current_user_can( 'edit_posts' ) ) {
|
||||
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
|
||||
}
|
||||
|
||||
$post_id = isset( $_GET['post_id'] ) ? intval( $_GET['post_id'] ) : 0;
|
||||
|
||||
if ( $post_id === 0 ) {
|
||||
wp_send_json_error( [ 'message' => 'Invalid post ID' ] );
|
||||
}
|
||||
|
||||
$settings = get_post_meta( $post_id, 'formipay_settings', true );
|
||||
$fields = isset( $settings['fields'] ) ? $settings['fields'] : [];
|
||||
|
||||
wp_send_json_success( [
|
||||
'fields' => $fields
|
||||
] );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize a single field
|
||||
*/
|
||||
private function sanitize_field( $field ) {
|
||||
|
||||
$sanitized = [
|
||||
'field_type' => sanitize_text_field( $field['field_type'] ?? 'text' ),
|
||||
'label' => sanitize_text_field( $field['label'] ?? '' ),
|
||||
'field_id' => sanitize_title( str_replace( ' ', '_', $field['field_id'] ?? '' ) ),
|
||||
'placeholder' => sanitize_text_field( $field['placeholder'] ?? '' ),
|
||||
'default_value' => sanitize_text_field( $field['default_value'] ?? '' ),
|
||||
'description' => sanitize_textarea_field( $field['description'] ?? '' ),
|
||||
'is_required' => (bool) ( $field['is_required'] ?? false ),
|
||||
'option_grid_columns' => absint( $field['option_grid_columns'] ?? 1 ),
|
||||
'field_options' => [],
|
||||
];
|
||||
|
||||
// Sanitize field options
|
||||
if ( isset( $field['field_options'] ) && is_array( $field['field_options'] ) ) {
|
||||
foreach ( $field['field_options'] as $option ) {
|
||||
$sanitized['field_options'][] = [
|
||||
'label' => sanitize_text_field( $option['label'] ?? '' ),
|
||||
'value' => sanitize_text_field( $option['value'] ?? '' ),
|
||||
'amount' => floatval( $option['amount'] ?? 0 ),
|
||||
'weight' => floatval( $option['weight'] ?? 0 ),
|
||||
'quantity' => (bool) ( $option['quantity'] ?? false ),
|
||||
'thumbnail' => absint( $option['thumbnail'] ?? 0 ),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $sanitized;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useState, useCallback } from '@wordpress/element';
|
||||
import { useState, useCallback, useEffect } from '@wordpress/element';
|
||||
import FormCanvas from './FormCanvas';
|
||||
import FieldPalette from './FieldPalette';
|
||||
import FieldSettingsPanel from './FieldSettingsPanel';
|
||||
@@ -13,9 +13,36 @@ import './FormBuilder.css';
|
||||
export default function FormBuilder({ formId, initialData = {} }) {
|
||||
const [fields, setFields] = useState(initialData.fields || []);
|
||||
const [selectedFieldId, setSelectedFieldId] = useState(null);
|
||||
const [saveStatus, setSaveStatus] = useState(null);
|
||||
|
||||
const selectedField = fields.find(f => f.field_id === selectedFieldId) || null;
|
||||
|
||||
// Load existing fields on mount
|
||||
useEffect(() => {
|
||||
if (formId && !initialData.fields) {
|
||||
loadFields();
|
||||
}
|
||||
}, [formId]);
|
||||
|
||||
const loadFields = useCallback(() => {
|
||||
const params = new URLSearchParams({
|
||||
action: 'formipay_load_form_fields',
|
||||
post_id: formId,
|
||||
_wpnonce: window.formipayAdmin?.nonce || '',
|
||||
});
|
||||
|
||||
fetch(`${window.formipayAdmin?.ajaxUrl || '/wp-admin/admin-ajax.php'}?${params}`)
|
||||
.then(response => response.json())
|
||||
.then(result => {
|
||||
if (result.success && result.data.fields) {
|
||||
setFields(result.data.fields);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Load error:', error);
|
||||
});
|
||||
}, [formId]);
|
||||
|
||||
const handleDrop = useCallback((newField) => {
|
||||
setFields([...fields, newField]);
|
||||
setSelectedFieldId(newField.field_id);
|
||||
@@ -42,7 +69,7 @@ export default function FormBuilder({ formId, initialData = {} }) {
|
||||
}, [fields, selectedFieldId]);
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
// Save form fields via AJAX
|
||||
setSaveStatus('saving');
|
||||
const formData = new FormData();
|
||||
formData.append('action', 'formipay_save_form_fields');
|
||||
formData.append('post_id', formId);
|
||||
@@ -57,13 +84,15 @@ export default function FormBuilder({ formId, initialData = {} }) {
|
||||
.then(response => response.json())
|
||||
.then(result => {
|
||||
if (result.success) {
|
||||
// Show success message
|
||||
console.log('Form saved successfully');
|
||||
setSaveStatus('saved');
|
||||
setTimeout(() => setSaveStatus(null), 2000);
|
||||
} else {
|
||||
setSaveStatus('error');
|
||||
console.error('Failed to save form:', result.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
setSaveStatus('error');
|
||||
console.error('Save error:', error);
|
||||
});
|
||||
}, [fields, formId]);
|
||||
@@ -82,10 +111,14 @@ export default function FormBuilder({ formId, initialData = {} }) {
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="button button-primary"
|
||||
className={`button button-primary ${saveStatus === 'saving' ? 'is-busy' : ''}`}
|
||||
onClick={handleSave}
|
||||
disabled={saveStatus === 'saving'}
|
||||
>
|
||||
{ __('Save Form', 'formipay') }
|
||||
{ saveStatus === 'saved' ? __('Saved!', 'formipay') :
|
||||
saveStatus === 'error' ? __('Failed', 'formipay') :
|
||||
saveStatus === 'saving' ? __('Saving...', 'formipay') :
|
||||
__('Save Form', 'formipay') }
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user