/** * Registration Form Editor Hook * * Custom hook for managing registration form fields and settings */ import { useState, useEffect, useCallback, useRef } from 'react' import { RegistrationFormSettings, RegistrationField, defaultSettings, defaultFields, createNewField, sortFieldsByPriority, RegistrationFieldType } from '../config' interface UseRegistrationFormReturn { // State settings: RegistrationFormSettings fields: RegistrationField[] isLoading: boolean isSaving: boolean error: string | null hasChanges: boolean // Settings actions updateSettings: (updates: Partial) => void // Field actions addField: (type: RegistrationFieldType) => RegistrationField updateField: (id: string, updates: Partial) => void deleteField: (id: string) => void duplicateField: (id: string) => void toggleFieldEnabled: (id: string) => void reorderFields: (fields: RegistrationField[]) => void // API actions saveSettings: () => Promise resetToDefaults: () => Promise } export function useRegistrationForm(): UseRegistrationFormReturn { // State const [settings, setSettings] = useState(defaultSettings) const [fields, setFields] = useState(defaultFields) const [isLoading, setIsLoading] = useState(true) const [isSaving, setIsSaving] = useState(false) const [error, setError] = useState(null) const [hasChanges, setHasChanges] = useState(false) // Refs for tracking initial state const initialSettingsRef = useRef(defaultSettings) const initialFieldsRef = useRef(defaultFields) // Get API configuration const apiUrl = window.swiftCommerceData?.apiUrl || '/wp-json/swift-commerce/v1' const restNonce = window.swiftCommerceData?.restNonce || '' // Load settings on mount useEffect(() => { loadSettings() }, []) // Track changes useEffect(() => { const settingsChanged = JSON.stringify(settings) !== JSON.stringify(initialSettingsRef.current) const fieldsChanged = JSON.stringify(fields) !== JSON.stringify(initialFieldsRef.current) setHasChanges(settingsChanged || fieldsChanged) }, [settings, fields]) /** * Load settings from API */ const loadSettings = useCallback(async () => { setIsLoading(true) setError(null) try { const response = await fetch(`${apiUrl}/registration-form/settings`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': restNonce, }, credentials: 'same-origin', }) if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } const data = await response.json() if (data.success) { const loadedSettings = { ...defaultSettings, ...data.settings } // Normalize fields to ensure they have the validation object const normalizeField = (field: Partial): RegistrationField => ({ ...field, id: field.id || `field_${Date.now()}`, key: field.key || '', type: field.type || 'text', label: field.label || '', priority: field.priority || 0, enabled: field.enabled ?? true, isCore: field.isCore ?? false, width: field.width || 'full', showOn: field.showOn || ['wordpress', 'woocommerce'], validation: { required: field.validation?.required ?? (field as any).required ?? false, minLength: field.validation?.minLength, maxLength: field.validation?.maxLength, min: field.validation?.min, max: field.validation?.max, pattern: field.validation?.pattern, patternMessage: field.validation?.patternMessage, }, } as RegistrationField) const loadedFields = data.fields && data.fields.length > 0 ? sortFieldsByPriority(data.fields.map(normalizeField)) : defaultFields setSettings(loadedSettings) setFields(loadedFields) initialSettingsRef.current = loadedSettings initialFieldsRef.current = loadedFields } else { throw new Error(data.message || 'Failed to load settings') } } catch (err) { console.error('Failed to load registration form settings:', err) setError(err instanceof Error ? err.message : 'Failed to load settings') // Use defaults on error setSettings(defaultSettings) setFields(defaultFields) } finally { setIsLoading(false) } }, [apiUrl, restNonce]) /** * Update settings */ const updateSettings = useCallback((updates: Partial) => { setSettings(prev => ({ ...prev, ...updates })) }, []) /** * Add a new field */ const addField = useCallback((type: RegistrationFieldType): RegistrationField => { const maxPriority = fields.reduce((max, f) => Math.max(max, f.priority), 0) const newField = createNewField(type, maxPriority + 10) setFields(prev => sortFieldsByPriority([...prev, newField])) return newField }, [fields]) /** * Update a field */ const updateField = useCallback((id: string, updates: Partial) => { setFields(prev => { const updated = prev.map(field => field.id === id ? { ...field, ...updates } : field ) return sortFieldsByPriority(updated) }) }, []) /** * Delete a field */ const deleteField = useCallback((id: string) => { setFields(prev => prev.filter(field => field.id !== id)) }, []) /** * Duplicate a field */ const duplicateField = useCallback((id: string) => { const fieldToDuplicate = fields.find(f => f.id === id) if (!fieldToDuplicate) return const timestamp = Date.now() const newField: RegistrationField = { ...fieldToDuplicate, id: `field_${timestamp}`, key: `${fieldToDuplicate.key}_copy`, label: `${fieldToDuplicate.label} (Copy)`, isCore: false, priority: fieldToDuplicate.priority + 1, } setFields(prev => sortFieldsByPriority([...prev, newField])) }, [fields]) /** * Toggle field enabled state */ const toggleFieldEnabled = useCallback((id: string) => { setFields(prev => prev.map(field => field.id === id ? { ...field, enabled: !field.enabled } : field )) }, []) /** * Reorder fields (from drag and drop) */ const reorderFields = useCallback((newFields: RegistrationField[]) => { // Update priorities based on new order const updatedFields = newFields.map((field, index) => ({ ...field, priority: (index + 1) * 10, })) setFields(updatedFields) }, []) /** * Save all settings and fields */ const saveSettings = useCallback(async (): Promise => { setIsSaving(true) setError(null) try { // Save settings const settingsResponse = await fetch(`${apiUrl}/registration-form/settings`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': restNonce, }, credentials: 'same-origin', body: JSON.stringify(settings), }) if (!settingsResponse.ok) { throw new Error(`HTTP error! status: ${settingsResponse.status}`) } // Save fields const fieldsResponse = await fetch(`${apiUrl}/registration-form/fields`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': restNonce, }, credentials: 'same-origin', body: JSON.stringify(fields), }) if (!fieldsResponse.ok) { throw new Error(`HTTP error! status: ${fieldsResponse.status}`) } // Update initial refs to current state initialSettingsRef.current = settings initialFieldsRef.current = fields setHasChanges(false) return true } catch (err) { console.error('Failed to save registration form settings:', err) setError(err instanceof Error ? err.message : 'Failed to save settings') return false } finally { setIsSaving(false) } }, [apiUrl, restNonce, settings, fields]) /** * Reset to defaults */ const resetToDefaults = useCallback(async (): Promise => { setIsSaving(true) setError(null) try { const response = await fetch(`${apiUrl}/registration-form/reset`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': restNonce, }, credentials: 'same-origin', }) if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } const data = await response.json() if (data.success) { setSettings(data.settings) setFields(sortFieldsByPriority(data.fields)) initialSettingsRef.current = data.settings initialFieldsRef.current = data.fields setHasChanges(false) } return true } catch (err) { console.error('Failed to reset registration form settings:', err) setError(err instanceof Error ? err.message : 'Failed to reset settings') return false } finally { setIsSaving(false) } }, [apiUrl, restNonce]) return { settings, fields, isLoading, isSaving, error, hasChanges, updateSettings, addField, updateField, deleteField, duplicateField, toggleFieldEnabled, reorderFields, saveSettings, resetToDefaults, } }