'use client'; import React, { useState } from 'react'; import { Paper, TextInput, PasswordInput, Button, Stack, Text, Divider, Group, Anchor, Alert, Title, Container, Progress, Box, List, } from '@mantine/core'; import { useForm } from '@mantine/form'; import { IconAlertCircle, IconBrandGoogle, IconBrandGithub, IconCheck, IconX } from '@tabler/icons-react'; import { SignUpProps, SignUpFormData, PasswordRequirements } from './SignUp.types'; import { useAuth } from '../../hooks/useAuth'; import { useRouter } from 'next/navigation'; export function SignUp({ redirectUrl = '/dashboard', afterSignUpUrl, providers = ['email', 'google', 'github'], appearance, onSuccess, onError, initialValues = {}, unsafeMetadata, showDivider = true, signInUrl = '/sign-in', requireDisplayName = true, passwordRequirements = { minLength: 8, requireUppercase: true, requireNumbers: true, requireSymbols: true, }, customValidation, className, style, }: SignUpProps) { const router = useRouter(); const { signUp, signInWithGoogle, signInWithGithub } = useAuth(); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [passwordStrength, setPasswordStrength] = useState(0); const form = useForm({ initialValues: { email: initialValues.email || '', password: '', confirmPassword: '', firstName: initialValues.firstName || '', lastName: initialValues.lastName || '', displayName: '', }, validate: { email: (value) => { if (!value) return 'Email is required'; if (!/^\S+@\S+$/.test(value)) return 'Invalid email'; if (customValidation?.email) return customValidation.email(value); return null; }, password: (value) => { if (!value) return 'Password is required'; if (customValidation?.password) return customValidation.password(value); const errors = []; if (passwordRequirements.minLength && value.length < passwordRequirements.minLength) { errors.push(`at least ${passwordRequirements.minLength} characters`); } if (passwordRequirements.requireUppercase && !/[A-Z]/.test(value)) { errors.push('one uppercase letter'); } if (passwordRequirements.requireNumbers && !/[0-9]/.test(value)) { errors.push('one number'); } if (passwordRequirements.requireSymbols && !/[^A-Za-z0-9]/.test(value)) { errors.push('one special character'); } return errors.length > 0 ? `Password must contain ${errors.join(', ')}` : null; }, confirmPassword: (value, values) => value !== values.password ? 'Passwords do not match' : null, displayName: (value) => { if (requireDisplayName && !value) return 'Display name is required'; if (customValidation?.displayName) return customValidation.displayName(value); return null; }, }, }); const calculatePasswordStrength = (password: string) => { let strength = 0; if (password.length >= (passwordRequirements.minLength || 8)) strength += 25; if (passwordRequirements.requireUppercase && /[A-Z]/.test(password)) strength += 25; if (passwordRequirements.requireNumbers && /[0-9]/.test(password)) strength += 25; if (passwordRequirements.requireSymbols && /[^A-Za-z0-9]/.test(password)) strength += 25; return strength; }; const handlePasswordChange = (value: string) => { form.setFieldValue('password', value); setPasswordStrength(calculatePasswordStrength(value)); }; const handleSubmit = async (values: SignUpFormData) => { setLoading(true); setError(null); try { const displayName = values.displayName || `${values.firstName} ${values.lastName}`.trim(); const user = await signUp(values.email, values.password, displayName); if (onSuccess) { onSuccess(user); } const redirectTo = afterSignUpUrl || redirectUrl; router.push(redirectTo); } catch (err: any) { const errorMessage = err.message || 'Failed to sign up'; setError(errorMessage); if (onError) { onError(err); } } finally { setLoading(false); } }; const handleSocialSignIn = async (provider: 'google' | 'github') => { setLoading(true); setError(null); try { let user; if (provider === 'google') { user = await signInWithGoogle(); } else { user = await signInWithGithub(); } if (onSuccess) { onSuccess(user); } const redirectTo = afterSignUpUrl || redirectUrl; router.push(redirectTo); } catch (err: any) { const errorMessage = err.message || `Failed to sign up with ${provider}`; setError(errorMessage); if (onError) { onError(err); } } finally { setLoading(false); } }; const socialButtons = providers.includes('google') || providers.includes('github') ? ( {providers.includes('google') && ( )} {providers.includes('github') && ( )} ) : null; const showSocialTop = appearance?.layout?.socialButtonsPlacement === 'top'; const getStrengthColor = () => { if (passwordStrength <= 25) return 'red'; if (passwordStrength <= 50) return 'orange'; if (passwordStrength <= 75) return 'yellow'; return 'green'; }; return ( Create your account Sign up to get started {error && ( } color="red" mb="md"> {error} )} {showSocialTop && socialButtons} {showSocialTop && showDivider && providers.includes('email') && ( )} {providers.includes('email') && (
{requireDisplayName && ( )} handlePasswordChange(e.currentTarget.value)} error={form.errors.password} disabled={loading} /> {form.values.password && ( Password strength: {passwordStrength}% )}
)} {!showSocialTop && showDivider && socialButtons && providers.includes('email') && ( )} {!showSocialTop && socialButtons} Already have an account?{' '} Sign in
); }