import React, { useState } from 'react'; import { View, Text, TextInput, TouchableOpacity, StyleSheet, ScrollView, KeyboardAvoidingView, Platform, ActivityIndicator, } from 'react-native'; import { useRouter } from 'expo-router'; import { authApi, handleApiError } from '../lib/api'; type Step = 'email' | 'sent' | 'reset'; export default function ForgotPasswordScreen() { const router = useRouter(); const [step, setStep] = useState('email'); const [email, setEmail] = useState(''); const [resetToken, setResetToken] = useState(''); const [newPassword, setNewPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [showPassword, setShowPassword] = useState(false); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [validationErrors, setValidationErrors] = useState<{ email?: string; resetToken?: string; newPassword?: string; confirmPassword?: string; }>({}); // Validate email format const validateEmail = (email: string): boolean => { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); }; // Validate email step const validateEmailStep = (): boolean => { const errors: { email?: string } = {}; if (!email.trim()) { errors.email = 'Email is required'; } else if (!validateEmail(email)) { errors.email = 'Please enter a valid email'; } setValidationErrors(errors); return Object.keys(errors).length === 0; }; // Validate reset step const validateResetStep = (): boolean => { const errors: { resetToken?: string; newPassword?: string; confirmPassword?: string; } = {}; if (!resetToken.trim()) { errors.resetToken = 'Reset code is required'; } if (!newPassword) { errors.newPassword = 'New password is required'; } else if (newPassword.length < 8) { errors.newPassword = 'Password must be at least 8 characters'; } if (!confirmPassword) { errors.confirmPassword = 'Please confirm your password'; } else if (newPassword !== confirmPassword) { errors.confirmPassword = 'Passwords do not match'; } setValidationErrors(errors); return Object.keys(errors).length === 0; }; // Handle send reset email const handleSendReset = async () => { setError(null); if (!validateEmailStep()) { return; } setIsLoading(true); try { await authApi.forgotPassword({ email: email.trim().toLowerCase() }); setStep('sent'); } catch (err) { setError(handleApiError(err)); } finally { setIsLoading(false); } }; // Handle reset password const handleResetPassword = async () => { setError(null); if (!validateResetStep()) { return; } setIsLoading(true); try { await authApi.resetPassword({ token: resetToken.trim(), newPassword, }); // Navigate to login with success message router.replace('/(auth)/login'); } catch (err) { setError(handleApiError(err)); } finally { setIsLoading(false); } }; // Navigate back to login const handleBackToLogin = () => { router.push('/(auth)/login'); }; // Render email step const renderEmailStep = () => ( <> Forgot Password Enter your email address and we'll send you a link to reset your password. Email { setEmail(text); if (validationErrors.email) { setValidationErrors((prev) => ({ ...prev, email: undefined })); } }} keyboardType="email-address" autoCapitalize="none" autoCorrect={false} autoComplete="email" editable={!isLoading} /> {validationErrors.email && ( {validationErrors.email} )} {error && ( {error} )} {isLoading ? ( ) : ( Send Reset Link )} Back to Sign In ); // Render sent confirmation step const renderSentStep = () => ( <> Check Your Email We've sent a password reset link to{'\n'} {email} What's next? 1. Check your email inbox{'\n'} 2. Click the reset link in the email{'\n'} 3. Enter your reset code below setStep('reset')} > I Have My Reset Code {isLoading ? ( ) : ( Resend Email )} Back to Sign In ); // Render reset password step const renderResetStep = () => ( <> Reset Password Enter the code from your email and create a new password. {/* Reset Token Input */} Reset Code { setResetToken(text); if (validationErrors.resetToken) { setValidationErrors((prev) => ({ ...prev, resetToken: undefined })); } }} autoCapitalize="none" autoCorrect={false} editable={!isLoading} /> {validationErrors.resetToken && ( {validationErrors.resetToken} )} {/* New Password Input */} New Password { setNewPassword(text); if (validationErrors.newPassword) { setValidationErrors((prev) => ({ ...prev, newPassword: undefined })); } }} secureTextEntry={!showPassword} autoCapitalize="none" autoCorrect={false} editable={!isLoading} /> setShowPassword(!showPassword)} disabled={isLoading} > {showPassword ? 'Hide' : 'Show'} {validationErrors.newPassword && ( {validationErrors.newPassword} )} {/* Confirm Password Input */} Confirm New Password { setConfirmPassword(text); if (validationErrors.confirmPassword) { setValidationErrors((prev) => ({ ...prev, confirmPassword: undefined, })); } }} secureTextEntry={!showPassword} autoCapitalize="none" autoCorrect={false} editable={!isLoading} /> {validationErrors.confirmPassword && ( {validationErrors.confirmPassword} )} {error && ( {error} )} {isLoading ? ( ) : ( Reset Password )} setStep('sent')} disabled={isLoading} > Go Back ); return ( {step === 'email' && renderEmailStep()} {step === 'sent' && renderSentStep()} {step === 'reset' && renderResetStep()} ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#FFFFFF', }, scrollContent: { flexGrow: 1, paddingHorizontal: 24, paddingTop: 60, paddingBottom: 40, }, header: { marginBottom: 32, alignItems: 'center', }, iconContainer: { width: 64, height: 64, borderRadius: 32, backgroundColor: '#E8F5E9', justifyContent: 'center', alignItems: 'center', marginBottom: 24, }, checkIcon: { fontSize: 32, color: '#34C759', }, title: { fontSize: 28, fontWeight: 'bold', color: '#333333', marginBottom: 12, textAlign: 'center', }, subtitle: { fontSize: 16, color: '#666666', textAlign: 'center', lineHeight: 24, }, emailHighlight: { fontWeight: '600', color: '#007AFF', }, form: { flex: 1, }, inputContainer: { marginBottom: 20, }, label: { fontSize: 14, fontWeight: '600', color: '#333333', marginBottom: 8, }, input: { height: 50, borderWidth: 1, borderColor: '#E0E0E0', borderRadius: 8, paddingHorizontal: 16, fontSize: 16, color: '#333333', backgroundColor: '#FAFAFA', }, inputError: { borderColor: '#FF3B30', }, passwordContainer: { position: 'relative', }, passwordInput: { paddingRight: 60, }, showPasswordButton: { position: 'absolute', right: 16, top: 0, bottom: 0, justifyContent: 'center', }, showPasswordText: { fontSize: 14, color: '#007AFF', fontWeight: '600', }, errorText: { fontSize: 12, color: '#FF3B30', marginTop: 4, }, apiError: { backgroundColor: '#FFF0F0', borderRadius: 8, padding: 12, marginBottom: 16, borderWidth: 1, borderColor: '#FF3B30', }, apiErrorText: { fontSize: 14, color: '#FF3B30', textAlign: 'center', }, instructionsContainer: { backgroundColor: '#F5F5F5', borderRadius: 8, padding: 16, marginBottom: 24, }, instructionsTitle: { fontSize: 14, fontWeight: '600', color: '#333333', marginBottom: 8, }, instructionsText: { fontSize: 14, color: '#666666', lineHeight: 22, }, button: { height: 50, backgroundColor: '#007AFF', borderRadius: 8, justifyContent: 'center', alignItems: 'center', marginBottom: 16, }, buttonDisabled: { backgroundColor: '#B0B0B0', }, buttonText: { fontSize: 16, fontWeight: '600', color: '#FFFFFF', }, resendButton: { height: 50, backgroundColor: '#FFFFFF', borderRadius: 8, borderWidth: 1, borderColor: '#007AFF', justifyContent: 'center', alignItems: 'center', marginBottom: 16, }, resendButtonText: { fontSize: 16, fontWeight: '600', color: '#007AFF', }, backButton: { height: 50, justifyContent: 'center', alignItems: 'center', }, backButtonText: { fontSize: 14, color: '#666666', fontWeight: '600', }, });