import React, { useState } from "react"; import { Paper, Box, Typography, TextField, Button, Stack, Alert, Select, MenuItem, FormControl, InputLabel, FormHelperText, LinearProgress, CircularProgress, Link, Card, CardContent, CardActions, useMediaQuery, useTheme, IconButton, InputAdornment, } from "@mui/material"; import { Visibility, VisibilityOff } from "@mui/icons-material"; import { ApiClient } from "services/api-client"; import { RegistrationSuccessDialog } from "components/EmailVerificationComponents"; import { useAuth } from "hooks/AuthContext"; import { useNavigate } from "react-router-dom"; // Candidate Registration Form const CandidateRegistrationForm = () => { const { apiClient } = useAuth(); const navigate = useNavigate(); const [formData, setFormData] = useState({ email: "", username: "", password: "", confirmPassword: "", firstName: "", lastName: "", phone: "", }); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down("sm")); const [loading, setLoading] = useState(false); const [errors, setErrors] = useState>({}); const [showSuccess, setShowSuccess] = useState(false); const [registrationResult, setRegistrationResult] = useState(null); // Password visibility states const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); const validateForm = () => { const newErrors: Record = {}; // Email validation const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!formData.email) { newErrors.email = "Email is required"; } else if (!emailRegex.test(formData.email)) { newErrors.email = "Please enter a valid email address"; } // Username validation if (!formData.username) { newErrors.username = "Username is required"; } else if (formData.username.length < 3) { newErrors.username = "Username must be at least 3 characters"; } else if (!/^[a-zA-Z0-9_]+$/.test(formData.username)) { newErrors.username = "Username can only contain letters, numbers, and underscores"; } // Password validation if (!formData.password) { newErrors.password = "Password is required"; } else { const passwordErrors = validatePassword(formData.password); if (passwordErrors.length > 0) { newErrors.password = passwordErrors.join(", "); } } // Confirm password if (formData.password !== formData.confirmPassword) { newErrors.confirmPassword = "Passwords do not match"; } // Name validation if (!formData.firstName.trim()) { newErrors.firstName = "First name is required"; } if (!formData.lastName.trim()) { newErrors.lastName = "Last name is required"; } // Phone validation (optional but must be valid if provided) if ( formData.phone && !/^[+]?[1-9][\d]{0,15}$/.test(formData.phone.replace(/\s/g, "")) ) { newErrors.phone = "Please enter a valid phone number"; } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const validatePassword = (password: string): string[] => { const errors: string[] = []; if (password.length < 8) { errors.push("at least 8 characters"); } if (!/[a-z]/.test(password)) { errors.push("one lowercase letter"); } if (!/[A-Z]/.test(password)) { errors.push("one uppercase letter"); } if (!/\d/.test(password)) { errors.push("one number"); } if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) { errors.push("one special character"); } return errors.length > 0 ? [`Password must contain ${errors.join(", ")}`] : []; }; const handleInputChange = (field: string, value: string) => { setFormData((prev) => ({ ...prev, [field]: value })); // Clear error when user starts typing if (errors[field]) { setErrors((prev) => ({ ...prev, [field]: "" })); } }; const handleSubmit = async () => { if (!validateForm()) { return; } setLoading(true); try { const result = await apiClient.createCandidate({ email: formData.email, username: formData.username, password: formData.password, firstName: formData.firstName, lastName: formData.lastName, phone: formData.phone || undefined, }); // Set pending verification apiClient.setPendingEmailVerification(formData.email); setRegistrationResult(result); setShowSuccess(true); } catch (error: any) { if (error.message.includes("already exists")) { if (error.message.includes("email")) { setErrors({ email: "An account with this email already exists" }); } else if (error.message.includes("username")) { setErrors({ username: "This username is already taken" }); } } else { setErrors({ general: error.message || "Registration failed. Please try again.", }); } } finally { setLoading(false); } }; const getPasswordStrength = (password: string) => { const validations = [ password.length >= 8, /[a-z]/.test(password), /[A-Z]/.test(password), /\d/.test(password), /[!@#$%^&*(),.?":{}|<>]/.test(password), ]; const strength = validations.filter(Boolean).length; if (strength < 2) return { level: "weak", color: "error", value: 20 }; if (strength < 4) return { level: "medium", color: "warning", value: 60 }; return { level: "strong", color: "success", value: 100 }; }; const passwordStrength = formData.password ? getPasswordStrength(formData.password) : null; return ( Join as a Candidate Create your account to start finding your dream job handleInputChange("email", e.target.value)} placeholder="your.email@example.com" error={!!errors.email} helperText={errors.email} required /> handleInputChange("username", e.target.value.toLowerCase()) } placeholder="johndoe123" error={!!errors.username} helperText={errors.username} required /> handleInputChange("firstName", e.target.value)} placeholder="John" error={!!errors.firstName} helperText={errors.firstName} required /> handleInputChange("lastName", e.target.value)} placeholder="Doe" error={!!errors.lastName} helperText={errors.lastName} required /> handleInputChange("phone", e.target.value)} placeholder="+1 (555) 123-4567" error={!!errors.phone} helperText={errors.phone || "Optional"} /> handleInputChange("password", e.target.value)} placeholder="Create a strong password" error={!!errors.password} helperText={errors.password} required InputProps={{ endAdornment: ( setShowPassword(!showPassword)} onMouseDown={(e) => e.preventDefault()} edge="end" > {showPassword ? : } ), }} /> {formData.password && passwordStrength && ( Password strength: {passwordStrength.level} )} handleInputChange("confirmPassword", e.target.value)} placeholder="Confirm your password" error={!!errors.confirmPassword} helperText={errors.confirmPassword} required InputProps={{ endAdornment: ( setShowConfirmPassword(!showConfirmPassword)} onMouseDown={(e) => e.preventDefault()} edge="end" > {showConfirmPassword ? : } ), }} /> {errors.general && {errors.general}} Already have an account?{" "} { e.preventDefault(); navigate("/login"); }} sx={{ fontWeight: 600 }} > Sign in here {showSuccess && registrationResult && ( setShowSuccess(false)} email={registrationResult.email} userType="candidate" /> )} ); }; // Employer Registration Form const EmployerRegistrationForm = () => { const [formData, setFormData] = useState({ email: "", username: "", password: "", confirmPassword: "", companyName: "", industry: "", companySize: "", companyDescription: "", websiteUrl: "", phone: "", }); const [loading, setLoading] = useState(false); const [errors, setErrors] = useState>({}); const [showSuccess, setShowSuccess] = useState(false); const [registrationResult, setRegistrationResult] = useState(null); // Password visibility states const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); const apiClient = new ApiClient(); const industryOptions = [ "Technology", "Healthcare", "Finance", "Education", "Manufacturing", "Retail", "Consulting", "Media", "Non-profit", "Government", "Other", ]; const companySizeOptions = [ "1-10 employees", "11-50 employees", "51-200 employees", "201-500 employees", "501-1000 employees", "1000+ employees", ]; const validateForm = () => { const newErrors: Record = {}; // Email validation const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!formData.email) { newErrors.email = "Email is required"; } else if (!emailRegex.test(formData.email)) { newErrors.email = "Please enter a valid email address"; } // Username validation if (!formData.username) { newErrors.username = "Username is required"; } else if (formData.username.length < 3) { newErrors.username = "Username must be at least 3 characters"; } // Password validation if (!formData.password) { newErrors.password = "Password is required"; } else { const passwordErrors = validatePassword(formData.password); if (passwordErrors.length > 0) { newErrors.password = passwordErrors.join(", "); } } // Confirm password if (formData.password !== formData.confirmPassword) { newErrors.confirmPassword = "Passwords do not match"; } // Company validation if (!formData.companyName.trim()) { newErrors.companyName = "Company name is required"; } if (!formData.industry) { newErrors.industry = "Industry is required"; } if (!formData.companySize) { newErrors.companySize = "Company size is required"; } if (!formData.companyDescription.trim()) { newErrors.companyDescription = "Company description is required"; } else if (formData.companyDescription.length < 50) { newErrors.companyDescription = "Company description must be at least 50 characters"; } // Website URL validation (optional but must be valid if provided) if (formData.websiteUrl) { try { new URL(formData.websiteUrl); } catch { newErrors.websiteUrl = "Please enter a valid website URL"; } } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const validatePassword = (password: string): string[] => { const errors: string[] = []; if (password.length < 8) { errors.push("at least 8 characters"); } if (!/[a-z]/.test(password)) { errors.push("one lowercase letter"); } if (!/[A-Z]/.test(password)) { errors.push("one uppercase letter"); } if (!/\d/.test(password)) { errors.push("one number"); } if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) { errors.push("one special character"); } return errors.length > 0 ? [`Password must contain ${errors.join(", ")}`] : []; }; const handleInputChange = (field: string, value: string) => { setFormData((prev) => ({ ...prev, [field]: value })); // Clear error when user starts typing if (errors[field]) { setErrors((prev) => ({ ...prev, [field]: "" })); } }; const handleSubmit = async () => { if (!validateForm()) { return; } setLoading(true); try { const result = await apiClient.createEmployerWithVerification({ email: formData.email, username: formData.username, password: formData.password, companyName: formData.companyName, industry: formData.industry, companySize: formData.companySize, companyDescription: formData.companyDescription, websiteUrl: formData.websiteUrl || undefined, phone: formData.phone || undefined, }); // Set pending verification apiClient.setPendingEmailVerification(formData.email); setRegistrationResult(result); setShowSuccess(true); } catch (error: any) { if (error.message.includes("already exists")) { if (error.message.includes("email")) { setErrors({ email: "An account with this email already exists" }); } else if (error.message.includes("username")) { setErrors({ username: "This username is already taken" }); } } else { setErrors({ general: error.message || "Registration failed. Please try again.", }); } } finally { setLoading(false); } }; return ( Join as an Employer Create your company account to start hiring top talent {/* Account Information Section */} Account Information handleInputChange("email", e.target.value)} placeholder="company@example.com" error={!!errors.email} helperText={errors.email} required /> handleInputChange("username", e.target.value.toLowerCase()) } placeholder="company123" error={!!errors.username} helperText={errors.username} required /> handleInputChange("password", e.target.value) } placeholder="Create a strong password" error={!!errors.password} helperText={errors.password} required InputProps={{ endAdornment: ( setShowPassword(!showPassword)} onMouseDown={(e) => e.preventDefault()} edge="end" > {showPassword ? : } ), }} /> handleInputChange("confirmPassword", e.target.value) } placeholder="Confirm your password" error={!!errors.confirmPassword} helperText={errors.confirmPassword} required InputProps={{ endAdornment: ( setShowConfirmPassword(!showConfirmPassword) } onMouseDown={(e) => e.preventDefault()} edge="end" > {showConfirmPassword ? ( ) : ( )} ), }} /> {/* Company Information Section */} Company Information handleInputChange("companyName", e.target.value) } placeholder="Your Company Inc." error={!!errors.companyName} helperText={errors.companyName} required /> Industry {errors.industry && ( {errors.industry} )} Company Size {errors.companySize && ( {errors.companySize} )} handleInputChange("companyDescription", e.target.value) } placeholder="Tell us about your company, what you do, your mission, and what makes you unique..." error={!!errors.companyDescription} helperText={ errors.companyDescription || `${formData.companyDescription.length}/50 characters minimum` } required /> handleInputChange("websiteUrl", e.target.value) } placeholder="https://www.yourcompany.com" error={!!errors.websiteUrl} helperText={errors.websiteUrl || "Optional"} /> handleInputChange("phone", e.target.value)} placeholder="+1 (555) 123-4567" error={!!errors.phone} helperText={errors.phone || "Optional"} /> {errors.general && {errors.general}} Already have an account?{" "} Sign in here {showSuccess && registrationResult && ( setShowSuccess(false)} email={registrationResult.email} userType="employer" /> )} ); }; // Registration Type Selector Component export function RegistrationTypeSelector() { return ( Join Backstory Choose how you'd like to get started {/* Candidate Option */} (window.location.href = "/register/candidate")} > 👤 I'm looking for work Create a candidate profile to find your next opportunity {/* Employer Option */} (window.location.href = "/register/employer")} > 🏢 I'm hiring Create a company account to find and hire talent Already have an account?{" "} Sign in here ); } export { CandidateRegistrationForm, EmployerRegistrationForm };