/* * File: PasswordStep.tsx * Description: Password step component for signup flow with comprehensive validation * Design & Developed by Tech4Biz Solutions * Copyright (c) Spurrin Innovations. All rights reserved. */ import React, { useState, useEffect } from 'react'; import { View, Text, TextInput, TouchableOpacity, StyleSheet, KeyboardAvoidingView, Platform, ScrollView, } from 'react-native'; import { theme } from '../../../../theme/theme'; import { PasswordStepProps } from '../../types/signup'; import Icon from 'react-native-vector-icons/Feather'; // ============================================================================ // INTERFACES // ============================================================================ /** * PasswordRule Interface * * Purpose: Defines the structure for password validation rules */ interface PasswordRule { id: string; label: string; validator: (password: string) => boolean; isValid: boolean; } // ============================================================================ // PASSWORD STEP COMPONENT // ============================================================================ /** * PasswordStep Component * * Purpose: Second step of signup flow - password creation with comprehensive validation * * Features: * - Password input with visibility toggle * - Comprehensive password validation rules * - Real-time password strength checking * - Visual feedback for each requirement * - Continue button with loading state * - Back navigation with modern header */ const PasswordStep: React.FC = ({ onContinue, onBack, data, isLoading, }) => { // ============================================================================ // STATE MANAGEMENT // ============================================================================ const [password, setPassword] = useState(data.password || ''); const [confirmPassword, setConfirmPassword] = useState(''); const [passwordError, setPasswordError] = useState(''); const [confirmPasswordError, setConfirmPasswordError] = useState(''); const [isPasswordVisible, setPasswordVisible] = useState(false); const [isConfirmPasswordVisible, setConfirmPasswordVisible] = useState(false); // 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 === confirmPassword && confirmPassword.length > 0, isValid: false, }, ]); // ============================================================================ // EFFECTS // ============================================================================ /** * useEffect for password validation * * Purpose: Update password rules when password or confirm password changes */ useEffect(() => { updatePasswordRules(password); }, [password, confirmPassword]); // ============================================================================ // VALIDATION FUNCTIONS // ============================================================================ /** * Update Password Rules * * Purpose: Update password validation rules based on current password and confirm password * * @param pwd - Current password value */ const updatePasswordRules = (pwd: string) => { setPasswordRules(prevRules => prevRules.map(rule => { if (rule.id === 'match') { return { ...rule, isValid: pwd === confirmPassword && confirmPassword.length > 0, }; } return { ...rule, isValid: rule.validator(pwd), }; }) ); }; /** * Validate Password * * 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); }; /** * Handle Password Change * * Purpose: Update password and clear errors */ const handlePasswordChange = (text: string) => { setPassword(text); setPasswordError(''); // Clear confirm password error if passwords now match if (confirmPassword && text === confirmPassword) { setConfirmPasswordError(''); } // Update password rules to reflect the match status updatePasswordRules(text); }; /** * Handle Confirm Password Change * * Purpose: Update confirm password and validate match */ const handleConfirmPasswordChange = (text: string) => { setConfirmPassword(text); setConfirmPasswordError(''); // Check if passwords match if (text && text !== password) { setConfirmPasswordError('Passwords do not match'); } // Update password rules to reflect the match status updatePasswordRules(password); }; /** * Handle Continue * * Purpose: Validate password and proceed to next step */ const handleContinue = () => { // Clear previous errors setPasswordError(''); setConfirmPasswordError(''); // Validate password if (!password.trim()) { setPasswordError('Password is required'); return; } if (!validatePassword(password.trim())) { setPasswordError('Please meet all password requirements'); return; } // Validate confirm password if (!confirmPassword.trim()) { setConfirmPasswordError('Please confirm your password'); return; } if (password.trim() !== confirmPassword.trim()) { setConfirmPasswordError('Passwords do not match'); return; } // Call parent handler onContinue(password.trim()); }; /** * Render Password Rule * * 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 // ============================================================================ return ( {/* Header */} Create Account Step 2 of 5 {/* Content */} Create a strong password Choose a password that meets all the security requirements below. {/* Password Input */} Password setPasswordVisible(!isPasswordVisible)} style={styles.eyeIcon} disabled={isLoading} > {passwordError ? ( {passwordError} ) : null} {/* Confirm Password Input */} Confirm Password setConfirmPasswordVisible(!isConfirmPasswordVisible)} style={styles.eyeIcon} disabled={isLoading} > {confirmPasswordError ? ( {confirmPasswordError} ) : null} {/* Password Requirements */} Password Requirements: {passwordRules.map(renderPasswordRule)} {/* Continue Button */} {isLoading ? 'Processing...' : 'Continue'} ); }; // ============================================================================ // STYLES // ============================================================================ const styles = StyleSheet.create({ // Main container container: { flex: 1, backgroundColor: theme.colors.background, }, // Scroll view scrollView: { flex: 1, }, // Scroll content scrollContent: { flexGrow: 1, paddingHorizontal: theme.spacing.sm, }, // Header section header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingTop: theme.spacing.xl, paddingBottom: theme.spacing.lg, marginBottom: theme.spacing.lg, }, // Back button backButton: { padding: theme.spacing.sm, borderRadius: theme.borderRadius.medium, backgroundColor: theme.colors.backgroundAlt, }, // Header content headerContent: { flex: 1, alignItems: 'center', }, // Header spacer headerSpacer: { width: 40, }, // Title title: { fontSize: theme.typography.fontSize.displaySmall, fontFamily: theme.typography.fontFamily.bold, color: theme.colors.textPrimary, marginBottom: theme.spacing.xs, }, // Subtitle subtitle: { fontSize: theme.typography.fontSize.bodyMedium, fontFamily: theme.typography.fontFamily.regular, color: theme.colors.textSecondary, }, // Content section content: { flex: 1, justifyContent: 'center', paddingBottom: theme.spacing.xl, }, // Section title sectionTitle: { fontSize: theme.typography.fontSize.displaySmall, fontFamily: theme.typography.fontFamily.bold, color: theme.colors.textPrimary, marginBottom: theme.spacing.sm, }, // Description description: { fontSize: theme.typography.fontSize.bodyMedium, fontFamily: theme.typography.fontFamily.regular, color: theme.colors.textSecondary, marginBottom: theme.spacing.xl, }, // Input container inputContainer: { marginBottom: theme.spacing.xl, }, // Input label inputLabel: { fontSize: theme.typography.fontSize.bodyMedium, fontFamily: theme.typography.fontFamily.medium, color: theme.colors.textPrimary, marginBottom: theme.spacing.sm, }, // Input wrapper inputWrapper: { flexDirection: 'row', alignItems: 'center', borderWidth: 1, borderColor: theme.colors.border, borderRadius: theme.borderRadius.medium, backgroundColor: theme.colors.background, }, // Input field input: { flex: 1, paddingHorizontal: theme.spacing.md, 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, }, // Input wrapper error state inputWrapperError: { borderColor: theme.colors.error, }, // Error text errorText: { fontSize: theme.typography.fontSize.bodySmall, fontFamily: theme.typography.fontFamily.regular, color: theme.colors.error, marginTop: theme.spacing.xs, }, // Requirements container requirementsContainer: { marginBottom: theme.spacing.xl, padding: theme.spacing.md, backgroundColor: theme.colors.backgroundAlt, borderRadius: theme.borderRadius.medium, }, // Requirements title requirementsTitle: { 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, }, // Continue button continueButton: { backgroundColor: theme.colors.primary, borderRadius: theme.borderRadius.medium, paddingVertical: theme.spacing.md, paddingHorizontal: theme.spacing.lg, alignItems: 'center', marginBottom: theme.spacing.lg, ...theme.shadows.primary, }, // Continue button disabled continueButtonDisabled: { backgroundColor: theme.colors.border, opacity: 0.6, }, // Continue button text continueButtonText: { fontSize: theme.typography.fontSize.bodyLarge, fontFamily: theme.typography.fontFamily.bold, color: theme.colors.background, }, // Continue button text disabled continueButtonTextDisabled: { color: theme.colors.textMuted, }, }); export default PasswordStep; /* * End of File: PasswordStep.tsx * Design & Developed by Tech4Biz Solutions * Copyright (c) Spurrin Innovations. All rights reserved. */