/** * Checkout Field Editor - Editor Tab * * Main drag-and-drop field editor interface */ import { useState, useEffect } from "react" import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, useDraggable, useDroppable, DragOverlay, DragEndEvent, DragStartEvent, } from "@dnd-kit/core" import { arrayMove, SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy, } from "@dnd-kit/sortable" import { CSS } from "@dnd-kit/utilities" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Switch } from "@/components/ui/switch" import { Badge } from "@/components/ui/badge" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { ScrollArea } from "@/components/ui/scroll-area" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog" // Accordion import removed - not currently used but may be added for conditional logic UI import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip" import { Textarea } from "@/components/ui/textarea" import { GripVertical, Plus, Trash2, Copy, Eye, EyeOff, Settings, ChevronDown, ChevronUp, Type, Hash, Mail, Phone, Lock, EyeOff as EyeOffIcon, Link, AlignLeft, ListChecks, Circle, CheckSquare, CheckCircle, Upload, Calendar, Clock, CalendarClock, CalendarDays, CalendarRange, CalendarCheck, Timer, Heading, Tag, FileText, Pencil, Zap, Globe, MapPin, } from "lucide-react" import { CheckoutField, CheckoutSection, SectionConfig, FieldType, FieldOption, ConditionalLogic, fieldTypeDefinitions, fieldCategories, widthOptions, validationPatterns } from "../../config" import { usePro } from "@/contexts/pro-context" import { ConditionalLogicEditor } from "../conditional-logic-editor" // Icon mapping const iconMap: Record> = { Type, Hash, Mail, Phone, Lock, EyeOff: EyeOffIcon, Link, AlignLeft, ChevronDown, ListChecks, Circle, CheckSquare, CheckCircle, Upload, Calendar, Clock, CalendarClock, CalendarDays, CalendarRange, CalendarCheck, Timer, Heading, Tag, FileText, Settings, Globe, MapPin } interface EditorTabProps { fields: CheckoutField[] sections: SectionConfig[] addField: (type: FieldType, section: CheckoutSection) => CheckoutField updateField: (id: string, updates: Partial) => void deleteField: (id: string) => void duplicateField: (id: string) => CheckoutField | null toggleFieldEnabled: (id: string) => void reorderFields: (section: CheckoutSection, fieldIds: string[]) => void moveFieldToSection: (fieldId: string, newSection: CheckoutSection) => void updateSection: (id: CheckoutSection, updates: Partial) => void toggleSectionEnabled: (id: CheckoutSection) => void editingFieldFromCL?: CheckoutField | null clearEditingFieldFromCL?: () => void } interface FieldEditorDialogProps { field: CheckoutField | null allFields: CheckoutField[] isOpen: boolean isNewField: boolean onClose: () => void onCancel: () => void onSave: (updates: Partial) => void } // Field Editor Dialog Component function FieldEditorDialog({ field, allFields, isOpen, isNewField, onClose, onCancel, onSave }: FieldEditorDialogProps) { const { isPro } = usePro() const [editedField, setEditedField] = useState>({}) // Reset edited state whenever the dialog opens with a different field useEffect(() => { if (field && isOpen) { setEditedField({}) } }, [field?.id, isOpen]) if (!field) return null const currentField = { ...field, ...editedField } const fieldDef = fieldTypeDefinitions.find(f => f.type === currentField.type) const handleSave = () => { onSave(editedField) onClose() } const updateEditedField = (key: K, value: CheckoutField[K]) => { setEditedField(prev => ({ ...prev, [key]: value })) } const updateValidation = ( key: K, value: CheckoutField['validation'][K] ) => { setEditedField(prev => ({ ...prev, validation: { ...(prev.validation || field.validation), [key]: value } })) } const addOption = () => { const options = currentField.options || [] const newOption: FieldOption = { value: `option_${options.length + 1}`, label: `Option ${options.length + 1}` } updateEditedField('options', [...options, newOption]) } const updateOption = (index: number, updates: Partial) => { const options = [...(currentField.options || [])] options[index] = { ...options[index], ...updates } updateEditedField('options', options) } const removeOption = (index: number) => { const options = (currentField.options || []).filter((_, i) => i !== index) updateEditedField('options', options) } return ( { if (!open) onCancel() }}> Edit Field: {currentField.label} Configure the field properties, validation, and conditional logic. General Validation Options Advanced {isPro && ( Logic )}
updateEditedField('label', e.target.value)} />
updateEditedField('key', e.target.value)} disabled={currentField.isCore} />
{fieldDef?.hasPlaceholder && (
updateEditedField('placeholder', e.target.value)} />
)}