diff --git a/src/admin/components/formBuilder/FieldPalette.css b/src/admin/components/formBuilder/FieldPalette.css new file mode 100644 index 000000000..defb23427 --- /dev/null +++ b/src/admin/components/formBuilder/FieldPalette.css @@ -0,0 +1,69 @@ +.formipay-field-palette { + padding: 16px; + background: #fff; + border-right: 1px solid #e0e0e0; + overflow-y: auto; + max-height: calc(100vh - 100px); +} + +.formipay-field-palette h3 { + margin: 0 0 16px; + font-size: 14px; + font-weight: 600; + color: #1e1e1e; +} + +.formipay-palette-category { + margin-bottom: 20px; +} + +.formipay-palette-category h4 { + margin: 0 0 8px; + font-size: 12px; + font-weight: 600; + text-transform: uppercase; + color: #646970; +} + +.formipay-palette-items { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 8px; +} + +.formipay-palette-item { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 12px; + background: #f0f0f1; + border: 1px solid #dcdcde; + border-radius: 4px; + cursor: grab; + transition: all 0.2s; + user-select: none; +} + +.formipay-palette-item:hover { + background: #fff; + border-color: #2271b1; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.formipay-palette-item:active { + cursor: grabbing; +} + +.formipay-palette-item svg { + width: 18px; + height: 18px; + fill: #1e1e1e; +} + +.formipay-palette-item span { + font-size: 12px; + color: #1e1e1e; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/src/admin/components/formBuilder/FieldPalette.js b/src/admin/components/formBuilder/FieldPalette.js new file mode 100644 index 000000000..c8fad8f4a --- /dev/null +++ b/src/admin/components/formBuilder/FieldPalette.js @@ -0,0 +1,50 @@ +/** + * Field Palette - Drag-and-drop source for form fields + */ + +import { __ } from '@wordpress/i18n'; +import { Icon } from '@wordpress/icons'; +import { FIELD_CATEGORIES, getFieldTypesByCategory } from '../../config/fieldTypes'; +import './FieldPalette.css'; + +export default function FieldPalette({ onDragStart }) { + const fieldTypesByCategory = getFieldTypesByCategory(); + const categories = Object.values(FIELD_CATEGORIES).sort((a, b) => a.order - b.order); + + const handleDragStart = (event, fieldType) => { + event.dataTransfer.effectAllowed = 'copy'; + event.dataTransfer.setData('formipay-field-type', fieldType); + onDragStart?.(fieldType); + }; + + return ( +
+

{ __('Add Field', 'formipay') }

+ + {categories.map((category) => { + const fields = fieldTypesByCategory[category.label] || []; + if (fields.length === 0) return null; + + return ( +
+

{ category.label }

+
+ {fields.map((field) => ( +
handleDragStart(e, field.type)} + title={field.label} + > + + { field.label } +
+ ))} +
+
+ ); + })} +
+ ); +} diff --git a/src/admin/components/formBuilder/FieldSettingsPanel.css b/src/admin/components/formBuilder/FieldSettingsPanel.css new file mode 100644 index 000000000..5be79be93 --- /dev/null +++ b/src/admin/components/formBuilder/FieldSettingsPanel.css @@ -0,0 +1,48 @@ +.formipay-field-settings-panel { + width: 320px; + background: #fff; + border-left: 1px solid #e0e0e0; + display: flex; + flex-direction: column; + max-height: calc(100vh - 100px); +} + +.formipay-settings-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px; + border-bottom: 1px solid #e0e0e0; +} + +.formipay-settings-header h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + display: flex; + align-items: center; + gap: 8px; +} + +.formipay-settings-header .field-type-badge { + padding: 2px 6px; + font-size: 10px; + font-weight: 600; + text-transform: uppercase; + background: #e0e0e0; + border-radius: 2px; +} + +.formipay-settings-content { + flex: 1; + padding: 16px; + overflow-y: auto; +} + +.formipay-settings-content .components-base-control { + margin-bottom: 16px; +} + +.formipay-settings-content .components-base-control:last-child { + margin-bottom: 0; +} diff --git a/src/admin/components/formBuilder/FieldSettingsPanel.js b/src/admin/components/formBuilder/FieldSettingsPanel.js new file mode 100644 index 000000000..85b5b35ef --- /dev/null +++ b/src/admin/components/formBuilder/FieldSettingsPanel.js @@ -0,0 +1,129 @@ +/** + * Field Settings Panel - Edit field properties + */ + +import { __ } from '@wordpress/i18n'; +import { TextControl, CheckboxControl, SelectControl, TextareaControl } from '@wordpress/components'; +import { FIELD_TYPES } from '../../config/fieldTypes'; +import FormFieldOptions from './FormFieldOptions'; +import './FieldSettingsPanel.css'; + +export default function FieldSettingsPanel({ + field, + open, + onClose, + onUpdate, +}) { + if (!open || !field) { + return null; + } + + const fieldConfig = FIELD_TYPES[field.field_type] || FIELD_TYPES.text; + const hasOptions = ['select', 'checkbox', 'radio'].includes(field.field_type); + const hasPlaceholder = [ + 'text', 'url', 'email', 'tel', 'number', 'date', 'datetime', 'color', + 'select', 'checkbox', 'radio', 'hidden', 'textarea', 'country_list' + ].includes(field.field_type); + const hasDefaultValue = [ + 'text', 'url', 'email', 'tel', 'number', 'date', 'datetime', 'color', + 'select', 'checkbox', 'radio', 'hidden', 'textarea' + ].includes(field.field_type); + const hasDescription = !['hidden'].includes(field.field_type); + const isRequired = !['divider', 'page_break', 'hidden'].includes(field.field_type); + const hasGridColumns = ['radio', 'checkbox'].includes(field.field_type); + + const handleChange = (key, value) => { + onUpdate?.({ ...field, [key]: value }); + }; + + return ( +
+
+

+ { fieldConfig.label } + { __('Field Settings', 'formipay') } +

+ +
+ +
+ handleChange('label', value)} + help={ __('The display label for this field', 'formipay') } + /> + + handleChange('field_id', value)} + help={ __('Unique identifier for this field (used in form data)', 'formipay') } + /> + + { hasPlaceholder && ( + handleChange('placeholder', value)} + /> + ) } + + { hasDefaultValue && ( + handleChange('default_value', value)} + /> + ) } + + { hasDescription && ( + handleChange('description', value)} + help={ __('Optional help text displayed below the field', 'formipay') } + rows={3} + /> + ) } + + { hasOptions && ( + handleChange('field_options', value)} + fieldType={field.field_type} + /> + ) } + + { hasGridColumns && ( + handleChange('option_grid_columns', parseInt(value))} + /> + ) } + + { isRequired && ( + handleChange('is_required', value)} + help={ __('User must fill this field before submitting', 'formipay') } + /> + ) } +
+
+ ); +} diff --git a/src/admin/components/formBuilder/FormBuilder.css b/src/admin/components/formBuilder/FormBuilder.css new file mode 100644 index 000000000..66e94b785 --- /dev/null +++ b/src/admin/components/formBuilder/FormBuilder.css @@ -0,0 +1,31 @@ +.formipay-form-builder { + display: flex; + flex-direction: column; + height: calc(100vh - 32px); +} + +.formipay-builder-toolbar { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 20px; + background: #fff; + border-bottom: 1px solid #e0e0e0; +} + +.formipay-builder-toolbar h2 { + margin: 0; + font-size: 18px; + font-weight: 600; +} + +.formipay-builder-actions { + display: flex; + gap: 8px; +} + +.formipay-builder-content { + flex: 1; + display: flex; + overflow: hidden; +} diff --git a/src/admin/components/formBuilder/FormBuilder.js b/src/admin/components/formBuilder/FormBuilder.js new file mode 100644 index 000000000..2394c9ab5 --- /dev/null +++ b/src/admin/components/formBuilder/FormBuilder.js @@ -0,0 +1,114 @@ +/** + * Form Builder - Main container component + */ + +import { __ } from '@wordpress/i18n'; +import { useState, useCallback } from '@wordpress/element'; +import FormCanvas from './FormCanvas'; +import FieldPalette from './FieldPalette'; +import FieldSettingsPanel from './FieldSettingsPanel'; +import FormPreview from './FormPreview'; +import './FormBuilder.css'; + +export default function FormBuilder({ formId, initialData = {} }) { + const [fields, setFields] = useState(initialData.fields || []); + const [selectedFieldId, setSelectedFieldId] = useState(null); + + const selectedField = fields.find(f => f.field_id === selectedFieldId) || null; + + const handleDrop = useCallback((newField) => { + setFields([...fields, newField]); + setSelectedFieldId(newField.field_id); + }, [fields]); + + const handleSelectField = useCallback((fieldId) => { + setSelectedFieldId(fieldId); + }, []); + + const handleUpdateField = useCallback((fieldId, updates) => { + setFields(fields.map(f => f.field_id === fieldId ? { ...f, ...updates } : f)); + }, [fields]); + + const handleMoveField = useCallback((newFields) => { + setFields(newFields); + }, []); + + const handleDeleteField = useCallback((fieldId) => { + const newFields = fields.filter(f => f.field_id !== fieldId); + setFields(newFields); + if (selectedFieldId === fieldId) { + setSelectedFieldId(null); + } + }, [fields, selectedFieldId]); + + const handleSave = useCallback(() => { + // Save form fields via AJAX + const formData = new FormData(); + formData.append('action', 'formipay_save_form_fields'); + formData.append('post_id', formId); + formData.append('fields', JSON.stringify(fields)); + formData.append('_wpnonce', window.formipayAdmin?.nonce || ''); + + fetch(window.formipayAdmin?.ajaxUrl || '/wp-admin/admin-ajax.php', { + method: 'POST', + credentials: 'same-origin', + body: formData, + }) + .then(response => response.json()) + .then(result => { + if (result.success) { + // Show success message + console.log('Form saved successfully'); + } else { + console.error('Failed to save form:', result.message); + } + }) + .catch(error => { + console.error('Save error:', error); + }); + }, [fields, formId]); + + return ( +
+
+

{ __('Form Builder', 'formipay') }

+
+ + +
+
+ +
+ {}} /> + + + setSelectedFieldId(null)} + onUpdate={handleUpdateField} + /> +
+
+ ); +} diff --git a/src/admin/components/formBuilder/FormCanvas.css b/src/admin/components/formBuilder/FormCanvas.css new file mode 100644 index 000000000..77777a9f9 --- /dev/null +++ b/src/admin/components/formBuilder/FormCanvas.css @@ -0,0 +1,62 @@ +.formipay-form-canvas { + flex: 1; + display: flex; + flex-direction: column; + background: #f6f7f7; + min-width: 0; +} + +.formipay-canvas-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px 20px; + background: #fff; + border-bottom: 1px solid #e0e0e0; +} + +.formipay-canvas-header h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: #1e1e1e; +} + +.field-count { + font-size: 12px; + color: #646970; +} + +.formipay-canvas-area { + flex: 1; + padding: 20px; + overflow-y: auto; +} + +.formipay-canvas-area.is-empty { + display: flex; + align-items: center; + justify-content: center; +} + +.formipay-empty-state { + text-align: center; + color: #646970; +} + +.formipay-empty-state svg { + display: block; + margin: 0 auto 16px; + fill: #c3c4c7; +} + +.formipay-empty-state p { + margin: 0; + font-size: 14px; +} + +.formipay-fields-list { + display: flex; + flex-direction: column; + gap: 12px; +} diff --git a/src/admin/components/formBuilder/FormCanvas.js b/src/admin/components/formBuilder/FormCanvas.js new file mode 100644 index 000000000..3d4612d0f --- /dev/null +++ b/src/admin/components/formBuilder/FormCanvas.js @@ -0,0 +1,103 @@ +/** + * Form Canvas - Drop target for form fields + */ + +import { __ } from '@wordpress/i18n'; +import { Icon, plus } from '@wordpress/icons'; +import { DEFAULT_FIELD_CONFIG, generateFieldId } from '../../config/fieldTypes'; +import FormField from './FormField'; +import './FormCanvas.css'; + +export default function FormCanvas({ + fields = [], + selectedFieldId, + onDrop, + onSelectField, + onUpdateField, + onMoveField, + onDeleteField, +}) { + const handleDragOver = (event) => { + event.preventDefault(); + event.dataTransfer.dropEffect = 'copy'; + }; + + const handleDrop = (event) => { + event.preventDefault(); + const fieldType = event.dataTransfer.getData('formipay-field-type'); + if (!fieldType) return; + + const newField = { + ...DEFAULT_FIELD_CONFIG, + field_type: fieldType, + field_id: generateFieldId(fieldType.replace('_', ' ')), + label: fieldType.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase()), + }; + + onDrop?.(newField); + }; + + const handleReorder = (dragIndex, dropIndex) => { + const newFields = [...fields]; + const [draggedField] = newFields.splice(dragIndex, 1); + newFields.splice(dropIndex, 0, draggedField); + onMoveField?.(newFields); + }; + + return ( +
+
+

{ __('Form Fields', 'formipay') }

+ + { fields.length } { __('fields', 'formipay') } + +
+ +
+ {fields.length === 0 ? ( +
+ +

+ { __('Drag fields from the palette to build your form', 'formipay') } +

+
+ ) : ( +
+ {fields.map((field, index) => ( + onSelectField?.(field.field_id)} + onUpdate={(updates) => onUpdateField?.(field.field_id, updates)} + onMoveUp={index > 0 ? () => handleReorder(index, index - 1) : null} + onMoveDown={index < fields.length - 1 ? () => handleReorder(index, index + 1) : null} + onDelete={() => onDeleteField?.(field.field_id)} + onDragStart={(e) => { + e.dataTransfer.effectAllowed = 'move'; + e.dataTransfer.setData('formipay-field-index', index.toString()); + }} + onDragOver={(e) => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + }} + onDrop={(e) => { + e.preventDefault(); + const fromIndex = parseInt(e.dataTransfer.getData('formipay-field-index')); + if (!isNaN(fromIndex) && fromIndex !== index) { + handleReorder(fromIndex, index); + } + }} + /> + ))} +
+ )} +
+
+ ); +} diff --git a/src/admin/components/formBuilder/FormField.css b/src/admin/components/formBuilder/FormField.css new file mode 100644 index 000000000..6bf3b26c0 --- /dev/null +++ b/src/admin/components/formBuilder/FormField.css @@ -0,0 +1,148 @@ +.formipay-field-item { + background: #fff; + border: 1px solid #dcdcde; + border-radius: 4px; + cursor: pointer; + transition: all 0.2s; + user-select: none; +} + +.formipay-field-item:hover { + border-color: #a7aaad; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); +} + +.formipay-field-item.is-selected { + border-color: #2271b1; + box-shadow: 0 0 0 2px rgba(34, 113, 177, 0.2); +} + +.formipay-field-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 14px; + background: #f6f7f7; + border-bottom: 1px solid #e0e0e0; + border-radius: 4px 4px 0 0; +} + +.formipay-field-info { + display: flex; + align-items: center; + gap: 10px; +} + +.field-type-badge { + display: inline-block; + padding: 2px 8px; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + background: #e0e0e0; + color: #1e1e1e; + border-radius: 2px; +} + +.field-id { + font-size: 11px; + color: #646970; + font-family: monospace; +} + +.formipay-field-actions { + display: flex; + gap: 4px; +} + +.formipay-field-actions .button-icon { + display: flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + padding: 0; + border: none; + background: transparent; + cursor: pointer; + border-radius: 2px; + transition: background 0.2s; +} + +.formipay-field-actions .button-icon:hover:not(:disabled) { + background: #e0e0e0; +} + +.formipay-field-actions .button-icon:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.formipay-field-actions .button-icon svg { + fill: #1e1e1e; +} + +.formipay-field-actions .button-danger:hover svg { + fill: #d63638; +} + +.formipay-field-content { + padding: 14px; +} + +.field-label-row { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 8px; +} + +.field-label-row strong { + font-size: 14px; + color: #1e1e1e; +} + +.field-label-row em { + color: #a7aaad; + font-style: italic; +} + +.required-badge { + display: inline-block; + padding: 2px 6px; + font-size: 10px; + font-weight: 600; + color: #d63638; + background: #f6f7f7; + border-radius: 2px; +} + +.field-description { + margin: 0 0 8px; + font-size: 12px; + color: #646970; + line-height: 1.4; +} + +.field-options-preview { + padding: 8px; + margin-bottom: 8px; + background: #f6f7f7; + border-radius: 2px; +} + +.field-options-preview small { + font-size: 11px; + color: #646970; +} + +.field-meta { + display: flex; + flex-wrap: wrap; + gap: 12px; +} + +.field-meta .meta-item { + font-size: 11px; + color: #646970; +} diff --git a/src/admin/components/formBuilder/FormField.js b/src/admin/components/formBuilder/FormField.js new file mode 100644 index 000000000..aa675bdaf --- /dev/null +++ b/src/admin/components/formBuilder/FormField.js @@ -0,0 +1,113 @@ +/** + * Form Field - Individual field component in canvas + */ + +import { __ } from '@wordpress/i18n'; +import { Icon, chevronUp, chevronDown, trash } from '@wordpress/icons'; +import { FIELD_TYPES } from '../../config/fieldTypes'; +import './FormField.css'; + +export default function FormField({ + field, + index, + isSelected, + onSelect, + onUpdate, + onMoveUp, + onMoveDown, + onDelete, + onDragStart, + onDragOver, + onDrop, +}) { + const fieldConfig = FIELD_TYPES[field.field_type] || FIELD_TYPES.text; + const hasOptions = ['select', 'checkbox', 'radio'].includes(field.field_type); + + return ( +
+
+
+ + { fieldConfig.label } + + + #{field.field_id || index} + +
+
+ + + +
+
+ +
+
+ { field.label || Untitled } + { field.is_required && ( + + { __('Required', 'formipay') } + + )} +
+ + { field.description && ( +

+ { field.description } +

+ )} + + { hasOptions && field.field_options && field.field_options.length > 0 && ( +
+ + { field.field_options.length } { __('options', 'formipay') } + +
+ )} + +
+ { field.placeholder && ( + + Placeholder: "{ field.placeholder }" + + )} + { field.default_value && ( + + Default: "{ field.default_value }" + + )} +
+
+
+ ); +} diff --git a/src/admin/components/formBuilder/FormFieldOptions.css b/src/admin/components/formBuilder/FormFieldOptions.css new file mode 100644 index 000000000..dbce102f4 --- /dev/null +++ b/src/admin/components/formBuilder/FormFieldOptions.css @@ -0,0 +1,65 @@ +.formipay-field-options { + margin-top: 16px; + padding: 16px; + background: #f6f7f7; + border: 1px solid #e0e0e0; + border-radius: 4px; +} + +.formipay-options-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 12px; +} + +.formipay-options-header label { + font-size: 13px; + font-weight: 600; + color: #1e1e1e; +} + +.formipay-options-list { + display: flex; + flex-direction: column; + gap: 12px; +} + +.formipay-option-item { + display: flex; + gap: 8px; + align-items: flex-start; +} + +.formipay-option-fields { + flex: 1; + display: flex; + flex-direction: column; + gap: 8px; +} + +.formipay-option-fields .components-base-control { + margin: 0; +} + +.formipay-option-fields .components-base-control__label { + font-size: 11px; +} + +.formipay-option-actions { + padding-top: 20px; +} + +.formipay-no-options { + padding: 20px; + text-align: center; + background: #fff; + border: 1px dashed #dcdcde; + border-radius: 2px; +} + +.formipay-no-options p { + margin: 0; + font-size: 12px; + color: #646970; +} diff --git a/src/admin/components/formBuilder/FormFieldOptions.js b/src/admin/components/formBuilder/FormFieldOptions.js new file mode 100644 index 000000000..a3f5737b5 --- /dev/null +++ b/src/admin/components/formBuilder/FormFieldOptions.js @@ -0,0 +1,105 @@ +/** + * Form Field Options - Manage select/radio/checkbox options + */ + +import { __ } from '@wordpress/i18n'; +import { TextControl, Button } from '@wordpress/components'; +import { Icon as WPIcon, plus } from '@wordpress/icons'; + +export default function FormFieldOptions({ options = [], onChange, fieldType }) { + const handleAddOption = () => { + const newOption = { + label: '', + value: '', + amount: '', + weight: '', + quantity: false, + }; + onChange([...options, newOption]); + }; + + const handleUpdateOption = (index, updates) => { + const newOptions = [...options]; + newOptions[index] = { ...newOptions[index], ...updates }; + onChange(newOptions); + }; + + const handleDeleteOption = (index) => { + const newOptions = options.filter((_, i) => i !== index); + onChange(newOptions); + }; + + const isMultiSelect = fieldType === 'checkbox'; + + return ( +
+
+ + +
+ +
+ {options.map((option, index) => ( +
+
+ handleUpdateOption(index, { label: value })} + placeholder={__('Option label', 'formipay')} + /> + + handleUpdateOption(index, { value: value })} + placeholder={__('Optional custom value', 'formipay')} + /> + + handleUpdateOption(index, { amount: value })} + placeholder={__('0', 'formipay')} + step="0.01" + /> +
+ +
+
+
+ ))} + + {options.length === 0 && ( +
+

+ { __('No options added yet. Click "Add Option" to create one.', 'formipay') } +

+
+ )} +
+
+ ); +} diff --git a/src/admin/components/formBuilder/FormFieldPreview.css b/src/admin/components/formBuilder/FormFieldPreview.css new file mode 100644 index 000000000..cb02f48df --- /dev/null +++ b/src/admin/components/formBuilder/FormFieldPreview.css @@ -0,0 +1,87 @@ +.formipay-preview-form { + display: flex; + flex-direction: column; + gap: 16px; +} + +.formipay-field-preview { + display: flex; + flex-direction: column; + gap: 6px; +} + +.formipay-field-label { + display: block; + font-size: 13px; + font-weight: 500; + color: #1e1e1e; +} + +.formipay-field-label .required { + color: #d63638; + margin-left: 2px; +} + +.formipay-input, +.formipay-textarea, +.formipay-select { + width: 100%; + padding: 8px 12px; + font-size: 13px; + color: #1e1e1e; + background: #fff; + border: 1px solid #8c8f94; + border-radius: 2px; +} + +.formipay-input:focus, +.formipay-textarea:focus, +.formipay-select:focus { + outline: none; + border-color: #2271b1; + box-shadow: 0 0 0 1px #2271b1; +} + +.formipay-textarea { + resize: vertical; + min-height: 80px; +} + +.formipay-radio-group, +.formipay-checkbox-group { + display: flex; + flex-direction: column; + gap: 8px; +} + +.formipay-radio-label, +.formipay-checkbox-label { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + color: #1e1e1e; + cursor: not-allowed; +} + +.formipay-field-description { + margin: 0; + font-size: 12px; + color: #646970; +} + +.formipay-divider { + border: none; + border-top: 1px solid #c3c4c7; + margin: 8px 0; +} + +.formipay-page-break { + padding: 12px; + text-align: center; + background: #f6f7f7; + border: 1px dashed #c3c4c7; + border-radius: 2px; + font-size: 12px; + color: #646970; +} diff --git a/src/admin/components/formBuilder/FormFieldPreview.js b/src/admin/components/formBuilder/FormFieldPreview.js new file mode 100644 index 000000000..3fe32a184 --- /dev/null +++ b/src/admin/components/formBuilder/FormFieldPreview.js @@ -0,0 +1,142 @@ +/** + * Form Field Preview - Preview individual form fields + */ + +import { __ } from '@wordpress/i18n'; +import './FormFieldPreview.css'; + +export default function FormFieldPreview({ field }) { + const fieldType = field.field_type || 'text'; + const label = field.label || ''; + const placeholder = field.placeholder || ''; + const defaultValue = field.default_value || ''; + const description = field.description || ''; + const isRequired = field.is_required || false; + const fieldId = field.field_id || `field_${Math.random().toString(36).slice(2, 11)}`; + + const renderFieldInput = () => { + const commonProps = { + id: fieldId, + name: fieldId, + placeholder, + defaultValue, + required: isRequired, + disabled: true, + }; + + switch (fieldType) { + case 'text': + case 'url': + case 'email': + case 'tel': + case 'number': + case 'date': + case 'datetime': + case 'color': + return ( + + ); + + case 'textarea': + return ( +