import React, { useState, useEffect, useMemo, useCallback } from 'react'; import { useNavigate, Link } from 'react-router-dom'; import { useAuth } from '../context/AuthContext'; import { Button } from '../components/ui/button'; import { Card } from '../components/ui/card'; import { toast } from 'sonner'; import PublicNavbar from '../components/PublicNavbar'; import PublicFooter from '../components/PublicFooter'; import { ArrowRight, ArrowLeft, Loader2 } from 'lucide-react'; import DynamicRegistrationForm, { DynamicStepIndicator, validateStep, evaluateConditionalRules, } from '../components/registration/DynamicRegistrationForm'; import api from '../utils/api'; // Fallback schema for when API is unavailable const FALLBACK_SCHEMA = { version: '1.0', steps: [ { id: 'step_account', title: 'Account Setup', description: 'Create your account credentials.', order: 1, sections: [ { id: 'section_credentials', title: 'Account Credentials', order: 1, fields: [ { id: 'first_name', type: 'text', label: 'First Name', required: true, is_fixed: true, mapping: 'first_name', width: 'half', order: 1 }, { id: 'last_name', type: 'text', label: 'Last Name', required: true, is_fixed: true, mapping: 'last_name', width: 'half', order: 2 }, { id: 'email', type: 'email', label: 'Email Address', required: true, is_fixed: true, mapping: 'email', width: 'full', order: 3 }, { id: 'password', type: 'password', label: 'Password', required: true, is_fixed: true, mapping: 'password', validation: { minLength: 6 }, width: 'half', order: 4 }, { id: 'confirmPassword', type: 'password', label: 'Confirm Password', required: true, is_fixed: true, client_only: true, width: 'half', order: 5, validation: { matchField: 'password' } }, { id: 'accepts_tos', type: 'checkbox', label: 'I accept the Terms of Service and Privacy Policy', required: true, is_fixed: true, mapping: 'accepts_tos', width: 'full', order: 6 }, ], }, ], }, ], conditional_rules: [], fixed_fields: ['email', 'password', 'first_name', 'last_name', 'accepts_tos'], }; const Register = () => { const navigate = useNavigate(); const { register } = useAuth(); const [loading, setLoading] = useState(false); const [schemaLoading, setSchemaLoading] = useState(true); const [schema, setSchema] = useState(null); const [currentStep, setCurrentStep] = useState(1); const [formData, setFormData] = useState({}); const [errors, setErrors] = useState({}); // Fetch registration schema on mount useEffect(() => { const fetchSchema = async () => { try { const response = await api.get('/registration/schema'); setSchema(response.data); } catch (error) { console.error('Failed to load registration schema:', error); toast.error('Failed to load registration form. Using default form.'); setSchema(FALLBACK_SCHEMA); } finally { setSchemaLoading(false); } }; fetchSchema(); }, []); // Get sorted steps const sortedSteps = useMemo(() => { if (!schema?.steps) return []; return [...schema.steps].sort((a, b) => a.order - b.order); }, [schema]); // Get current step data const currentStepData = useMemo(() => { return sortedSteps[currentStep - 1] || null; }, [sortedSteps, currentStep]); // Get hidden fields based on conditional rules const hiddenFields = useMemo(() => { return evaluateConditionalRules(schema, formData); }, [schema, formData]); // Validate current step const validateCurrentStep = useCallback(() => { if (!currentStepData) return { isValid: true, errors: {} }; return validateStep(currentStepData, formData, hiddenFields); }, [currentStepData, formData, hiddenFields]); // Handle next step const handleNext = () => { const { isValid, errors: stepErrors } = validateCurrentStep(); if (!isValid) { setErrors(stepErrors); const firstErrorField = Object.keys(stepErrors)[0]; if (firstErrorField) { toast.error(stepErrors[firstErrorField][0]); } return; } setErrors({}); setCurrentStep((prev) => Math.min(prev + 1, sortedSteps.length)); window.scrollTo({ top: 0, behavior: 'smooth' }); }; // Handle previous step const handleBack = () => { setErrors({}); setCurrentStep((prev) => Math.max(prev - 1, 1)); window.scrollTo({ top: 0, behavior: 'smooth' }); }; // Handle form submission const handleSubmit = async (e) => { e.preventDefault(); // Validate final step const { isValid, errors: stepErrors } = validateCurrentStep(); if (!isValid) { setErrors(stepErrors); const firstErrorField = Object.keys(stepErrors)[0]; if (firstErrorField) { toast.error(stepErrors[firstErrorField][0]); } return; } setLoading(true); try { // Prepare submission data const submitData = { ...formData }; // Remove client-only fields delete submitData.confirmPassword; // Convert date fields to ISO format if (submitData.date_of_birth) { submitData.date_of_birth = new Date(submitData.date_of_birth).toISOString(); } if (submitData.directory_dob) { submitData.directory_dob = new Date(submitData.directory_dob).toISOString(); } // Ensure boolean fields are actually booleans const booleanFields = [ 'partner_is_member', 'partner_plan_to_become_member', 'newsletter_publish_name', 'newsletter_publish_photo', 'newsletter_publish_birthday', 'newsletter_publish_none', 'scholarship_requested', 'show_in_directory', 'accepts_tos', ]; for (const field of booleanFields) { if (field in submitData) { submitData[field] = Boolean(submitData[field]); } } // Ensure array fields are arrays const arrayFields = ['lead_sources', 'volunteer_interests']; for (const field of arrayFields) { if (field in submitData && !Array.isArray(submitData[field])) { submitData[field] = submitData[field] ? [submitData[field]] : []; } } await register(submitData); toast.success('Please check your email for a confirmation email.'); navigate('/login'); } catch (error) { const errorMessage = error.response?.data?.detail; if (typeof errorMessage === 'object' && errorMessage.errors) { // Handle structured validation errors const errorList = errorMessage.errors; toast.error(errorList[0] || 'Registration failed'); } else { toast.error(errorMessage || 'Registration failed. Please try again.'); } } finally { setLoading(false); } }; // Show loading state while fetching schema if (schemaLoading) { return (

Loading registration form...

); } return (
Back to Home

Join Our Community

Fill out the form below to start your membership journey.

{/* Step Indicator */} {sortedSteps.length > 1 && ( )} {/* Dynamic Form Content */} {/* Navigation Buttons */}
{currentStep > 1 ? ( ) : (
)} {currentStep < sortedSteps.length ? ( ) : ( )}

Already have an account?{' '} Login here

); }; export default Register;