backstory/frontend/src/components/RegistrationForms.tsx
2025-05-31 19:25:04 -07:00

1308 lines
43 KiB
TypeScript

import React, { useState } from 'react';
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 [loading, setLoading] = useState(false);
const [errors, setErrors] = useState<Record<string, string>>({});
const [showSuccess, setShowSuccess] = useState(false);
const [registrationResult, setRegistrationResult] = useState<any>(null);
const validateForm = () => {
const newErrors: Record<string, string> = {};
// 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.createCandidateWithVerification({
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: '#f44336', width: '20%' };
if (strength < 4) return { level: 'medium', color: '#ff9800', width: '60%' };
return { level: 'strong', color: '#4caf50', width: '100%' };
};
const passwordStrength = formData.password ? getPasswordStrength(formData.password) : null;
return (
<div style={{
maxWidth: '500px',
margin: '0 auto',
padding: '20px',
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
}}>
<div style={{
backgroundColor: 'white',
borderRadius: '12px',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
padding: '40px'
}}>
<div style={{ textAlign: 'center', marginBottom: '30px' }}>
<h1 style={{ margin: '0 0 8px 0', color: '#333' }}>
Join as a Candidate
</h1>
<p style={{ margin: 0, color: '#666' }}>
Create your account to start finding your dream job
</p>
</div>
<div>
{/* Email Field */}
<div style={{ marginBottom: '20px' }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
Email Address *
</label>
<input
type="email"
value={formData.email}
onChange={(e) => handleInputChange('email', e.target.value)}
placeholder="your.email@example.com"
style={{
width: '100%',
padding: '12px',
border: errors.email ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box',
transition: 'border-color 0.3s ease'
}}
onFocus={(e) => e.target.style.borderColor = '#1976d2'}
onBlur={(e) => e.target.style.borderColor = errors.email ? '#f44336' : '#ddd'}
/>
{errors.email && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.email}
</div>
)}
</div>
{/* Username Field */}
<div style={{ marginBottom: '20px' }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
Username *
</label>
<input
type="text"
value={formData.username}
onChange={(e) => handleInputChange('username', e.target.value.toLowerCase())}
placeholder="johndoe123"
style={{
width: '100%',
padding: '12px',
border: errors.username ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box'
}}
/>
{errors.username && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.username}
</div>
)}
</div>
{/* Name Fields */}
<div style={{ display: 'flex', gap: '12px', marginBottom: '20px' }}>
<div style={{ flex: 1 }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
First Name *
</label>
<input
type="text"
value={formData.firstName}
onChange={(e) => handleInputChange('firstName', e.target.value)}
placeholder="John"
style={{
width: '100%',
padding: '12px',
border: errors.firstName ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box'
}}
/>
{errors.firstName && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.firstName}
</div>
)}
</div>
<div style={{ flex: 1 }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
Last Name *
</label>
<input
type="text"
value={formData.lastName}
onChange={(e) => handleInputChange('lastName', e.target.value)}
placeholder="Doe"
style={{
width: '100%',
padding: '12px',
border: errors.lastName ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box'
}}
/>
{errors.lastName && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.lastName}
</div>
)}
</div>
</div>
{/* Phone Field */}
<div style={{ marginBottom: '20px' }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
Phone Number (Optional)
</label>
<input
type="tel"
value={formData.phone}
onChange={(e) => handleInputChange('phone', e.target.value)}
placeholder="+1 (555) 123-4567"
style={{
width: '100%',
padding: '12px',
border: errors.phone ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box'
}}
/>
{errors.phone && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.phone}
</div>
)}
</div>
{/* Password Field */}
<div style={{ marginBottom: '20px' }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
Password *
</label>
<input
type="password"
value={formData.password}
onChange={(e) => handleInputChange('password', e.target.value)}
placeholder="Create a strong password"
style={{
width: '100%',
padding: '12px',
border: errors.password ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box'
}}
/>
{/* Password Strength Indicator */}
{formData.password && passwordStrength && (
<div style={{ marginTop: '8px' }}>
<div style={{
width: '100%',
height: '4px',
backgroundColor: '#f0f0f0',
borderRadius: '2px',
overflow: 'hidden'
}}>
<div style={{
width: passwordStrength.width,
height: '100%',
backgroundColor: passwordStrength.color,
transition: 'all 0.3s ease'
}} />
</div>
<div style={{
fontSize: '12px',
marginTop: '4px',
color: passwordStrength.color,
textTransform: 'capitalize'
}}>
Password strength: {passwordStrength.level}
</div>
</div>
)}
{errors.password && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.password}
</div>
)}
</div>
{/* Confirm Password Field */}
<div style={{ marginBottom: '24px' }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
Confirm Password *
</label>
<input
type="password"
value={formData.confirmPassword}
onChange={(e) => handleInputChange('confirmPassword', e.target.value)}
placeholder="Confirm your password"
style={{
width: '100%',
padding: '12px',
border: errors.confirmPassword ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box'
}}
/>
{errors.confirmPassword && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.confirmPassword}
</div>
)}
</div>
{/* General Error */}
{errors.general && (
<div style={{
padding: '12px 16px',
backgroundColor: '#ffebee',
border: '1px solid #f44336',
borderRadius: '8px',
marginBottom: '20px',
color: '#c62828'
}}>
{errors.general}
</div>
)}
{/* Submit Button */}
<button
onClick={handleSubmit}
disabled={loading}
style={{
width: '100%',
padding: '16px',
backgroundColor: loading ? '#ccc' : '#1976d2',
color: 'white',
border: 'none',
borderRadius: '8px',
fontSize: '16px',
fontWeight: '600',
cursor: loading ? 'not-allowed' : 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
transition: 'background-color 0.3s ease'
}}
>
{loading ? (
<>
<div style={{
width: '20px',
height: '20px',
border: '2px solid transparent',
borderTop: '2px solid white',
borderRadius: '50%',
animation: 'spin 1s linear infinite',
marginRight: '8px'
}} />
Creating Account...
</>
) : (
'Create Account'
)}
</button>
{/* Login Link */}
<div style={{ textAlign: 'center', marginTop: '20px' }}>
<span style={{ color: '#666', fontSize: '14px' }}>
Already have an account?{' '}
<a
onClick={(e) => { e.preventDefault(); navigate('/login'); }}
style={{
color: '#1976d2',
textDecoration: 'none',
fontWeight: '600'
}}
>
Sign in here
</a>
</span>
</div>
</div>
</div>
{/* Success Dialog */}
{showSuccess && registrationResult && (
<RegistrationSuccessDialog
open={showSuccess}
onClose={() => setShowSuccess(false)}
email={registrationResult.email}
userType="candidate"
/>
)}
<style>
{`
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`}
</style>
</div>
);
}
// 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<Record<string, string>>({});
const [showSuccess, setShowSuccess] = useState(false);
const [registrationResult, setRegistrationResult] = useState<any>(null);
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<string, string> = {};
// 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 (
<div style={{
maxWidth: '600px',
margin: '0 auto',
padding: '20px',
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
}}>
<div style={{
backgroundColor: 'white',
borderRadius: '12px',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
padding: '40px'
}}>
<div style={{ textAlign: 'center', marginBottom: '30px' }}>
<h1 style={{ margin: '0 0 8px 0', color: '#333' }}>
Join as an Employer
</h1>
<p style={{ margin: 0, color: '#666' }}>
Create your company account to start hiring top talent
</p>
</div>
<div>
{/* Account Information Section */}
<div style={{
backgroundColor: '#f8f9fa',
padding: '20px',
borderRadius: '8px',
marginBottom: '24px'
}}>
<h3 style={{ margin: '0 0 16px 0', color: '#333' }}>Account Information</h3>
{/* Email and Username */}
<div style={{ display: 'flex', gap: '12px', marginBottom: '20px' }}>
<div style={{ flex: 1 }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
Email Address *
</label>
<input
type="email"
value={formData.email}
onChange={(e) => handleInputChange('email', e.target.value)}
placeholder="company@example.com"
style={{
width: '100%',
padding: '12px',
border: errors.email ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box'
}}
/>
{errors.email && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.email}
</div>
)}
</div>
<div style={{ flex: 1 }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
Username *
</label>
<input
type="text"
value={formData.username}
onChange={(e) => handleInputChange('username', e.target.value.toLowerCase())}
placeholder="company123"
style={{
width: '100%',
padding: '12px',
border: errors.username ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box'
}}
/>
{errors.username && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.username}
</div>
)}
</div>
</div>
{/* Password Fields */}
<div style={{ display: 'flex', gap: '12px' }}>
<div style={{ flex: 1 }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
Password *
</label>
<input
type="password"
value={formData.password}
onChange={(e) => handleInputChange('password', e.target.value)}
placeholder="Create a strong password"
style={{
width: '100%',
padding: '12px',
border: errors.password ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box'
}}
/>
{errors.password && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.password}
</div>
)}
</div>
<div style={{ flex: 1 }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
Confirm Password *
</label>
<input
type="password"
value={formData.confirmPassword}
onChange={(e) => handleInputChange('confirmPassword', e.target.value)}
placeholder="Confirm your password"
style={{
width: '100%',
padding: '12px',
border: errors.confirmPassword ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box'
}}
/>
{errors.confirmPassword && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.confirmPassword}
</div>
)}
</div>
</div>
</div>
{/* Company Information Section */}
<div style={{
backgroundColor: '#f0f7ff',
padding: '20px',
borderRadius: '8px',
marginBottom: '24px'
}}>
<h3 style={{ margin: '0 0 16px 0', color: '#333' }}>Company Information</h3>
{/* Company Name */}
<div style={{ marginBottom: '20px' }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
Company Name *
</label>
<input
type="text"
value={formData.companyName}
onChange={(e) => handleInputChange('companyName', e.target.value)}
placeholder="Your Company Inc."
style={{
width: '100%',
padding: '12px',
border: errors.companyName ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box'
}}
/>
{errors.companyName && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.companyName}
</div>
)}
</div>
{/* Industry and Company Size */}
<div style={{ display: 'flex', gap: '12px', marginBottom: '20px' }}>
<div style={{ flex: 1 }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
Industry *
</label>
<select
value={formData.industry}
onChange={(e) => handleInputChange('industry', e.target.value)}
style={{
width: '100%',
padding: '12px',
border: errors.industry ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box',
backgroundColor: 'white'
}}
>
<option value="">Select Industry</option>
{industryOptions.map(industry => (
<option key={industry} value={industry}>{industry}</option>
))}
</select>
{errors.industry && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.industry}
</div>
)}
</div>
<div style={{ flex: 1 }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
Company Size *
</label>
<select
value={formData.companySize}
onChange={(e) => handleInputChange('companySize', e.target.value)}
style={{
width: '100%',
padding: '12px',
border: errors.companySize ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box',
backgroundColor: 'white'
}}
>
<option value="">Select Size</option>
{companySizeOptions.map(size => (
<option key={size} value={size}>{size}</option>
))}
</select>
{errors.companySize && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.companySize}
</div>
)}
</div>
</div>
{/* Company Description */}
<div style={{ marginBottom: '20px' }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
Company Description * (minimum 50 characters)
</label>
<textarea
value={formData.companyDescription}
onChange={(e) => handleInputChange('companyDescription', e.target.value)}
placeholder="Tell us about your company, what you do, your mission, and what makes you unique..."
rows={4}
style={{
width: '100%',
padding: '12px',
border: errors.companyDescription ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box',
resize: 'vertical',
fontFamily: 'inherit'
}}
/>
<div style={{
fontSize: '12px',
color: '#666',
marginTop: '4px',
textAlign: 'right'
}}>
{formData.companyDescription.length}/50 characters minimum
</div>
{errors.companyDescription && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.companyDescription}
</div>
)}
</div>
{/* Website and Phone */}
<div style={{ display: 'flex', gap: '12px' }}>
<div style={{ flex: 1 }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
Website URL (Optional)
</label>
<input
type="url"
value={formData.websiteUrl}
onChange={(e) => handleInputChange('websiteUrl', e.target.value)}
placeholder="https://www.yourcompany.com"
style={{
width: '100%',
padding: '12px',
border: errors.websiteUrl ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box'
}}
/>
{errors.websiteUrl && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.websiteUrl}
</div>
)}
</div>
<div style={{ flex: 1 }}>
<label style={{
display: 'block',
marginBottom: '6px',
fontWeight: '600',
color: '#333'
}}>
Phone Number (Optional)
</label>
<input
type="tel"
value={formData.phone}
onChange={(e) => handleInputChange('phone', e.target.value)}
placeholder="+1 (555) 123-4567"
style={{
width: '100%',
padding: '12px',
border: errors.phone ? '2px solid #f44336' : '2px solid #ddd',
borderRadius: '8px',
fontSize: '16px',
outline: 'none',
boxSizing: 'border-box'
}}
/>
{errors.phone && (
<div style={{ color: '#f44336', fontSize: '14px', marginTop: '4px' }}>
{errors.phone}
</div>
)}
</div>
</div>
</div>
{/* General Error */}
{errors.general && (
<div style={{
padding: '12px 16px',
backgroundColor: '#ffebee',
border: '1px solid #f44336',
borderRadius: '8px',
marginBottom: '20px',
color: '#c62828'
}}>
{errors.general}
</div>
)}
{/* Submit Button */}
<button
onClick={handleSubmit}
disabled={loading}
style={{
width: '100%',
padding: '16px',
backgroundColor: loading ? '#ccc' : '#1976d2',
color: 'white',
border: 'none',
borderRadius: '8px',
fontSize: '16px',
fontWeight: '600',
cursor: loading ? 'not-allowed' : 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
transition: 'background-color 0.3s ease'
}}
>
{loading ? (
<>
<div style={{
width: '20px',
height: '20px',
border: '2px solid transparent',
borderTop: '2px solid white',
borderRadius: '50%',
animation: 'spin 1s linear infinite',
marginRight: '8px'
}} />
Creating Company Account...
</>
) : (
'Create Company Account'
)}
</button>
{/* Login Link */}
<div style={{ textAlign: 'center', marginTop: '20px' }}>
<span style={{ color: '#666', fontSize: '14px' }}>
Already have an account?{' '}
<a
href="/login"
style={{
color: '#1976d2',
textDecoration: 'none',
fontWeight: '600'
}}
>
Sign in here
</a>
</span>
</div>
</div>
</div>
{/* Success Dialog */}
{showSuccess && registrationResult && (
<RegistrationSuccessDialog
open={showSuccess}
onClose={() => setShowSuccess(false)}
email={registrationResult.email}
userType="employer"
/>
)}
<style>
{`
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`}
</style>
</div>
);
}
// Registration Type Selector Component
export function RegistrationTypeSelector() {
return (
<div style={{
maxWidth: '600px',
margin: '60px auto',
padding: '20px',
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
}}>
<div style={{
textAlign: 'center',
marginBottom: '40px'
}}>
<h1 style={{ margin: '0 0 16px 0', fontSize: '32px', color: '#333' }}>
Join Backstory
</h1>
<p style={{ margin: 0, fontSize: '18px', color: '#666' }}>
Choose how you'd like to get started
</p>
</div>
<div style={{ display: 'flex', gap: '20px' }}>
{/* Candidate Option */}
<div style={{
flex: 1,
padding: '30px',
backgroundColor: 'white',
borderRadius: '12px',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
textAlign: 'center',
cursor: 'pointer',
transition: 'transform 0.3s ease, box-shadow 0.3s ease',
border: '2px solid transparent'
}}
onMouseEnter={(e) => {
e.currentTarget.style.transform = 'translateY(-4px)';
e.currentTarget.style.boxShadow = '0 8px 24px rgba(0, 0, 0, 0.15)';
e.currentTarget.style.borderColor = '#1976d2';
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.1)';
e.currentTarget.style.borderColor = 'transparent';
}}
onClick={() => window.location.href = '/register/candidate'}
>
<div style={{ fontSize: '48px', marginBottom: '16px' }}>👤</div>
<h3 style={{ margin: '0 0 12px 0', color: '#333' }}>I'm looking for work</h3>
<p style={{ margin: '0 0 20px 0', color: '#666', fontSize: '14px' }}>
Create a candidate profile to find your next opportunity
</p>
<div style={{
display: 'inline-block',
padding: '10px 20px',
backgroundColor: '#1976d2',
color: 'white',
borderRadius: '6px',
fontSize: '14px',
fontWeight: '600'
}}>
Join as Candidate
</div>
</div>
{/* Employer Option */}
<div style={{
flex: 1,
padding: '30px',
backgroundColor: 'white',
borderRadius: '12px',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
textAlign: 'center',
cursor: 'pointer',
transition: 'transform 0.3s ease, box-shadow 0.3s ease',
border: '2px solid transparent'
}}
onMouseEnter={(e) => {
e.currentTarget.style.transform = 'translateY(-4px)';
e.currentTarget.style.boxShadow = '0 8px 24px rgba(0, 0, 0, 0.15)';
e.currentTarget.style.borderColor = '#1976d2';
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.1)';
e.currentTarget.style.borderColor = 'transparent';
}}
onClick={() => window.location.href = '/register/employer'}
>
<div style={{ fontSize: '48px', marginBottom: '16px' }}>🏢</div>
<h3 style={{ margin: '0 0 12px 0', color: '#333' }}>I'm hiring</h3>
<p style={{ margin: '0 0 20px 0', color: '#666', fontSize: '14px' }}>
Create a company account to find and hire talent
</p>
<div style={{
display: 'inline-block',
padding: '10px 20px',
backgroundColor: '#1976d2',
color: 'white',
borderRadius: '6px',
fontSize: '14px',
fontWeight: '600'
}}>
Join as Employer
</div>
</div>
</div>
<div style={{ textAlign: 'center', marginTop: '30px' }}>
<span style={{ color: '#666', fontSize: '14px' }}>
Already have an account?{' '}
<a
href="/login"
style={{
color: '#1976d2',
textDecoration: 'none',
fontWeight: '600'
}}
>
Sign in here
</a>
</span>
</div>
</div>
);
}
export { CandidateRegistrationForm, EmployerRegistrationForm };