/** * Registration Form Editor Tab * * Field management interface with drag-and-drop reordering */ import { useState, useRef, useEffect } from "react" import { Card, CardContent } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Switch } from "@/components/ui/switch" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { ScrollArea } from "@/components/ui/scroll-area" import { Separator } from "@/components/ui/separator" import { Plus, GripVertical, Pencil, Copy, Trash2, MoreVertical, EyeOff, Lock, Type, Mail, Phone, Link, Hash, AlignLeft, ChevronDown, ListChecks, Circle, CheckSquare, CheckCircle, Calendar, CalendarClock, Upload, Heading, FileText, Minus, X, } from "lucide-react" import { RegistrationField, RegistrationFieldType, registrationFieldTypes, getFieldTypesByCategory, FieldOption, } from "../../config" // Icon mapping for field types const fieldTypeIcons: Record> = { Type, Mail, Lock, Phone, Link, Hash, AlignLeft, ChevronDown, ListChecks, Circle, CheckSquare, CheckCircle, Calendar, CalendarClock, Upload, EyeOff, Heading, FileText, Minus, } interface EditorTabProps { fields: RegistrationField[] onAddField: (type: RegistrationFieldType) => RegistrationField onUpdateField: (id: string, updates: Partial) => void onDeleteField: (id: string) => void onDuplicateField: (id: string) => void onToggleFieldEnabled: (id: string) => void onReorderFields: (fields: RegistrationField[]) => void } export function EditorTab({ fields, onAddField, onUpdateField, onDeleteField, onDuplicateField, onToggleFieldEnabled, onReorderFields, }: EditorTabProps) { const [editingField, setEditingField] = useState(null) const [isAddDialogOpen, setIsAddDialogOpen] = useState(false) const [draggedField, setDraggedField] = useState(null) // Handle drag start const handleDragStart = (e: React.DragEvent, fieldId: string) => { setDraggedField(fieldId) e.dataTransfer.effectAllowed = 'move' } // Handle drag over const handleDragOver = (e: React.DragEvent, fieldId: string) => { e.preventDefault() if (!draggedField || draggedField === fieldId) return const newFields = [...fields] const draggedIndex = newFields.findIndex(f => f.id === draggedField) const targetIndex = newFields.findIndex(f => f.id === fieldId) if (draggedIndex !== -1 && targetIndex !== -1) { const [draggedItem] = newFields.splice(draggedIndex, 1) newFields.splice(targetIndex, 0, draggedItem) onReorderFields(newFields) } } // Handle drag end const handleDragEnd = () => { setDraggedField(null) } // Get icon component for field type const getFieldIcon = (iconName: string) => { const Icon = fieldTypeIcons[iconName] return Icon ? : } // Get field type definition const getFieldTypeDef = (type: RegistrationFieldType) => { return registrationFieldTypes.find(ft => ft.type === type) } return (
{/* Add Field Button */}

Registration Fields

Drag and drop to reorder. Click to edit field properties.

Add New Field Select the type of field you want to add to the registration form.
{/* Basic Fields */}

Basic Fields

{getFieldTypesByCategory('basic').map((fieldType) => ( ))}
{/* Selection Fields */}

Selection Fields

{getFieldTypesByCategory('selection').map((fieldType) => ( ))}
{/* DateTime Fields */}

Date & Time Fields

{getFieldTypesByCategory('datetime').map((fieldType) => ( ))}
{/* Content Fields */}

Content & Layout

{getFieldTypesByCategory('content').map((fieldType) => ( ))}
{/* Special Fields */}

Special Fields

{getFieldTypesByCategory('special').map((fieldType) => ( ))}
{/* Fields List */}
{fields.map((field) => { const fieldTypeDef = getFieldTypeDef(field.type) return (
handleDragStart(e, field.id)} onDragOver={(e) => handleDragOver(e, field.id)} onDragEnd={handleDragEnd} className={`flex items-center gap-3 p-3 hover:bg-muted/50 transition-colors ${ draggedField === field.id ? 'opacity-50' : '' } ${!field.enabled ? 'opacity-60' : ''}`} > {/* Drag Handle */}
{/* Field Icon */}
{fieldTypeDef && getFieldIcon(fieldTypeDef.icon)}
{/* Field Info */}
{field.label} {field.isCore && ( Core )} {field.validation.required && ( Required )}
{fieldTypeDef?.label} {field.key} {field.width}
{/* Context Badges */}
{field.showOn.includes('wordpress') && ( WP )} {field.showOn.includes('woocommerce') && ( WC )}
{/* Enable/Disable Toggle */} onToggleFieldEnabled(field.id)} disabled={field.isCore && ['user_login', 'user_email'].includes(field.key)} /> {/* Actions Menu */} Actions setEditingField(field)}> Edit onDuplicateField(field.id)}> Duplicate {!field.isCore && ( <> onDeleteField(field.id)} > Delete )}
) })}
{/* Edit Field Dialog */} {editingField && ( { onUpdateField(editingField.id, updates) setEditingField(null) }} onClose={() => setEditingField(null)} /> )}
) } // Field Edit Dialog Component interface FieldEditDialogProps { field: RegistrationField onSave: (updates: Partial) => void onClose: () => void } function FieldEditDialog({ field, onSave, onClose }: FieldEditDialogProps) { const [editedField, setEditedField] = useState({ ...field }) const fieldTypeDef = registrationFieldTypes.find(ft => ft.type === field.type) const optionsEndRef = useRef(null) const prevOptionsCountRef = useRef((field.options || []).length) // Auto-scroll when a new option is added useEffect(() => { const currentCount = (editedField.options || []).length if (currentCount > prevOptionsCountRef.current) { optionsEndRef.current?.scrollIntoView({ behavior: 'smooth', block: 'nearest' }) } prevOptionsCountRef.current = currentCount }, [editedField.options?.length]) const handleSave = () => { onSave(editedField) } const updateOption = (index: number, updates: Partial) => { const newOptions = [...(editedField.options || [])] newOptions[index] = { ...newOptions[index], ...updates } setEditedField({ ...editedField, options: newOptions }) } const addOption = () => { const newOptions = [...(editedField.options || [])] newOptions.push({ value: `option_${newOptions.length + 1}`, label: `Option ${newOptions.length + 1}` }) setEditedField({ ...editedField, options: newOptions }) } const removeOption = (index: number) => { const newOptions = [...(editedField.options || [])] newOptions.splice(index, 1) setEditedField({ ...editedField, options: newOptions }) } return ( Edit Field Configure the properties for this {fieldTypeDef?.label} field.
{/* Basic Properties */}
setEditedField({ ...editedField, label: e.target.value })} />
setEditedField({ ...editedField, key: e.target.value })} disabled={editedField.isCore} />
{fieldTypeDef?.hasPlaceholder && (
setEditedField({ ...editedField, placeholder: e.target.value })} />
)}
setEditedField({ ...editedField, description: e.target.value })} />
{fieldTypeDef?.hasDefaultValue && (
setEditedField({ ...editedField, defaultValue: e.target.value })} />
)}
{/* Layout */}

Layout

setEditedField({ ...editedField, cssClass: e.target.value })} />
{/* Validation */} {fieldTypeDef?.hasValidation && ( <>

Validation

setEditedField({ ...editedField, validation: { ...editedField.validation, required: checked } }) } />
{['text', 'textarea', 'password'].includes(editedField.type) && (
setEditedField({ ...editedField, validation: { ...editedField.validation, minLength: e.target.value ? parseInt(e.target.value) : undefined } }) } />
setEditedField({ ...editedField, validation: { ...editedField.validation, maxLength: e.target.value ? parseInt(e.target.value) : undefined } }) } />
)}
)} {/* Show On */}

Display Context

{ const showOn = checked ? [...editedField.showOn, 'wordpress'] : editedField.showOn.filter(c => c !== 'wordpress') setEditedField({ ...editedField, showOn: showOn as ('wordpress' | 'woocommerce')[] }) }} />
{ const showOn = checked ? [...editedField.showOn, 'woocommerce'] : editedField.showOn.filter(c => c !== 'woocommerce') setEditedField({ ...editedField, showOn: showOn as ('wordpress' | 'woocommerce')[] }) }} />
{/* Options (for select, radio, checkbox_group) */} {fieldTypeDef?.hasOptions && ( <>

Options

{(editedField.options || []).map((option, index) => (
updateOption(index, { value: e.target.value })} className="flex-1" /> updateOption(index, { label: e.target.value })} className="flex-1" />
))}
)}
) }