/* * File: ResetPasswordScreen.tsx * Description: Password reset screen for onboarding flow * Design & Developed by Tech4Biz Solutions * Copyright (c) Spurrin Innovations. All rights reserved. */ import React, { useState, useEffect } from 'react'; import { View, Text, StyleSheet, TextInput, TouchableOpacity, ScrollView, KeyboardAvoidingView, Platform, Alert, } from 'react-native'; import { useAppDispatch, useAppSelector } from '../../../store/hooks'; import { updateOnboarded, logout } from '../redux/authSlice'; import { authAPI } from '../services/authAPI'; import { showError, showSuccess } from '../../../shared/utils/toast'; import { theme } from '../../../theme/theme'; import Icon from 'react-native-vector-icons/Feather'; import { AuthNavigationProp } from '../navigation/navigationTypes'; /** * ResetPasswordScreenProps Interface * * Purpose: Defines the props required by the ResetPasswordScreen component * * Props: * - navigation: React Navigation object for screen navigation (optional when used in root stack) */ interface ResetPasswordScreenProps { navigation?: AuthNavigationProp; } /** * PasswordRule Interface * * Purpose: Defines the structure for password validation rules */ interface PasswordRule { id: string; label: string; validator: (password: string) => boolean; isValid: boolean; } /** * ResetPasswordScreen Component * * Purpose: Password reset screen for users who need to set their initial password * * Features: * 1. Password and confirm password input fields * 2. Real-time password validation with visual feedback * 3. Password visibility toggles * 4. Password strength requirements display * 5. Integration with Redux for onboarding status * 6. API integration for password change * * Password Requirements: * - At least 8 characters * - One uppercase letter * - One lowercase letter * - One number * - One special character * - Passwords must match */ export const ResetPasswordScreen: React.FC = ({ navigation, }) => { // ============================================================================ // STATE MANAGEMENT // ============================================================================ // Form input states const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [isPasswordVisible, setPasswordVisible] = useState(false); const [isConfirmPasswordVisible, setConfirmPasswordVisible] = useState(false); const [loading, setLoading] = useState(false); // Redux state const dispatch = useAppDispatch(); const user = useAppSelector((state) => state.auth.user); // Password validation rules const [passwordRules, setPasswordRules] = useState([ { id: 'length', label: 'At least 8 characters', validator: (pwd: string) => pwd.length >= 8, isValid: false, }, { id: 'uppercase', label: 'One uppercase letter', validator: (pwd: string) => /[A-Z]/.test(pwd), isValid: false, }, { id: 'lowercase', label: 'One lowercase letter', validator: (pwd: string) => /[a-z]/.test(pwd), isValid: false, }, { id: 'number', label: 'One number', validator: (pwd: string) => /\d/.test(pwd), isValid: false, }, { id: 'special', label: 'One special character', validator: (pwd: string) => /[!@#$%^&*(),.?":{}|<>]/.test(pwd), isValid: false, }, { id: 'match', label: 'Passwords match', validator: (pwd: string) => pwd.length > 0 && confirmPassword.length > 0 && pwd === confirmPassword, isValid: false, }, ]); // ============================================================================ // EFFECTS // ============================================================================ /** * useEffect for password validation * * Purpose: Update password rules when password or confirm password changes */ useEffect(() => { updatePasswordRules(password); }, [password, confirmPassword]); // ============================================================================ // HELPER FUNCTIONS // ============================================================================ /** * validatePassword Function * * Purpose: Check if all password requirements are met * * @param pwd - Password to validate * @returns boolean indicating if password meets all requirements */ const validatePassword = (pwd: string): boolean => { return passwordRules.every(rule => rule.isValid); }; /** * updatePasswordRules Function * * Purpose: Update password validation rules based on current password * * @param pwd - Current password value */ const updatePasswordRules = (pwd: string) => { setPasswordRules(prevRules => prevRules.map(rule => { if (rule.id === 'match') { return { ...rule, isValid: pwd.length > 0 && confirmPassword.length > 0 && pwd === confirmPassword, }; } return { ...rule, isValid: rule.validator(pwd), }; }) ); }; // ============================================================================ // EVENT HANDLERS // ============================================================================ /** * handlePasswordChange Function * * Purpose: Handle password input changes * * @param pwd - New password value */ const handlePasswordChange = (pwd: string) => { setPassword(pwd); }; /** * handleConfirmPasswordChange Function * * Purpose: Handle confirm password input changes * * @param pwd - New confirm password value */ const handleConfirmPasswordChange = (pwd: string) => { setConfirmPassword(pwd); }; /** * handleReset Function * * Purpose: Handle password reset submission * * Flow: * 1. Validate required fields * 2. Validate password requirements * 3. Check password match * 4. Call API to change password * 5. Update onboarding status on success */ const handleReset = async () => { // Validate required fields if (!password.trim() || !confirmPassword.trim()) { Alert.alert('Validation Error', 'Both password fields are required.'); return; } // Validate password requirements if (!validatePassword(password)) { Alert.alert('Validation Error', 'Please meet all password requirements.'); return; } // Check password match if (password !== confirmPassword) { Alert.alert('Validation Error', 'Passwords do not match.'); return; } setLoading(true); try { // Call API to change password const response: any = await authAPI.changepassword({ password, token: user?.access_token, }); console.log('reset response', response); if (response.data && response.data.message) { if (response.data.success) { showSuccess(response.data.message); // Update onboarding status dispatch(updateOnboarded(true)); // Navigate to main app // The app will automatically navigate to MainTabNavigator due to Redux state change } else { showError(response.data.message); } } else { showError('Error while changing password'); } } catch (error: any) { console.error('Password reset error:', error); showError('Failed to reset password. Please try again.'); } finally { setLoading(false); } }; /** * handleBack Function * * Purpose: Handle back navigation - logs out user since they can't go back to login */ const handleBack = () => { Alert.alert( 'Sign Out', 'Are you sure you want to sign out? You will need to log in again.', [ { text: 'Cancel', style: 'cancel', }, { text: 'Sign Out', style: 'destructive', onPress: () => { // Dispatch logout action dispatch(logout()); }, }, ] ); }; /** * renderPasswordRule Function * * Purpose: Render individual password validation rule * * @param rule - Password rule to render * @returns JSX element for the rule */ const renderPasswordRule = (rule: PasswordRule) => ( {rule.isValid && ( )} {rule.label} ); // ============================================================================ // RENDER SECTION // ============================================================================ return ( {/* Header */} Set Your Password {/* Icon */} {/* Title and Subtitle */} Set your password Create a strong password to complete your account setup {/* Password Input */} setPasswordVisible(!isPasswordVisible)} style={styles.eyeIcon} disabled={loading} > {/* Confirm Password Input */} setConfirmPasswordVisible(!isConfirmPasswordVisible)} style={styles.eyeIcon} disabled={loading} > {/* Password Rules Section */} Password Requirements: {passwordRules.map(renderPasswordRule)} {/* Reset Button */} {loading ? ( Setting Password... ) : ( Set Password )} ); }; // ============================================================================ // STYLES SECTION // ============================================================================ const styles = StyleSheet.create({ // Main container container: { flex: 1, backgroundColor: theme.colors.background, }, // Scroll view scrollView: { flex: 1, }, // Scroll content scrollContent: { flexGrow: 1, padding: theme.spacing.lg, }, // Header section header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: theme.spacing.xl, }, // Back button backButton: { padding: theme.spacing.sm, borderRadius: theme.borderRadius.medium, backgroundColor: theme.colors.backgroundAlt, }, // Header title headerTitle: { fontSize: theme.typography.fontSize.displaySmall, fontFamily: theme.typography.fontFamily.bold, color: theme.colors.textPrimary, }, // Header spacer headerSpacer: { width: 40, }, // Icon container iconContainer: { alignItems: 'center', marginBottom: theme.spacing.xl, }, // Title title: { fontSize: theme.typography.fontSize.displayMedium, fontFamily: theme.typography.fontFamily.bold, color: theme.colors.textPrimary, textAlign: 'center', marginBottom: theme.spacing.sm, }, // Subtitle subtitle: { fontSize: theme.typography.fontSize.bodyLarge, fontFamily: theme.typography.fontFamily.regular, color: theme.colors.textSecondary, textAlign: 'center', marginBottom: theme.spacing.xl, }, // Input container inputContainer: { flexDirection: 'row', alignItems: 'center', backgroundColor: theme.colors.backgroundAlt, borderWidth: 1, borderColor: theme.colors.border, borderRadius: theme.borderRadius.medium, marginBottom: theme.spacing.md, paddingHorizontal: theme.spacing.md, paddingVertical: 2, }, // Input icon inputIcon: { marginRight: theme.spacing.sm, }, // Input field inputField: { flex: 1, paddingVertical: theme.spacing.md, fontSize: theme.typography.fontSize.bodyLarge, fontFamily: theme.typography.fontFamily.regular, color: theme.colors.textPrimary, }, // Eye icon eyeIcon: { padding: theme.spacing.sm, }, // Rules container rulesContainer: { marginTop: theme.spacing.sm, marginBottom: theme.spacing.xl, paddingHorizontal: theme.spacing.sm, }, // Rules title rulesTitle: { fontSize: theme.typography.fontSize.bodyMedium, fontFamily: theme.typography.fontFamily.medium, color: theme.colors.textPrimary, marginBottom: theme.spacing.sm, }, // Rules grid rulesGrid: { gap: theme.spacing.xs, }, // Rule container ruleContainer: { flexDirection: 'row', alignItems: 'center', marginBottom: theme.spacing.xs, }, // Checkbox checkbox: { width: 18, height: 18, borderRadius: 4, borderWidth: 2, borderColor: theme.colors.border, backgroundColor: theme.colors.backgroundAlt, marginRight: theme.spacing.sm, alignItems: 'center', justifyContent: 'center', }, // Checkbox checked checkboxChecked: { backgroundColor: theme.colors.primary, borderColor: theme.colors.primary, }, // Rule text ruleText: { fontSize: theme.typography.fontSize.bodySmall, fontFamily: theme.typography.fontFamily.regular, color: theme.colors.textSecondary, flex: 1, }, // Rule text valid ruleTextValid: { color: theme.colors.success, }, // Base button button: { paddingVertical: theme.spacing.md, paddingHorizontal: theme.spacing.lg, borderRadius: theme.borderRadius.medium, alignItems: 'center', marginBottom: theme.spacing.md, }, // Reset button resetButton: { backgroundColor: theme.colors.primary, ...theme.shadows.primary, }, // Disabled button buttonDisabled: { opacity: 0.6, }, // Button text buttonText: { color: theme.colors.background, fontSize: theme.typography.fontSize.bodyLarge, fontFamily: theme.typography.fontFamily.medium, }, // Loading container loadingContainer: { flexDirection: 'row', alignItems: 'center', }, }); export default ResetPasswordScreen; /* * End of File: ResetPasswordScreen.tsx * Design & Developed by Tech4Biz Solutions * Copyright (c) Spurrin Innovations. All rights reserved. */