827 lines
24 KiB
TypeScript
827 lines
24 KiB
TypeScript
/*
|
|
* File: ChangePasswordScreen.tsx
|
|
* Description: Change password screen with comprehensive password validation
|
|
* Design & Developed by Tech4Biz Solutions
|
|
* Copyright (c) Spurrin Innovations. All rights reserved.
|
|
*/
|
|
|
|
import React, { useState } from 'react';
|
|
import {
|
|
View,
|
|
Text,
|
|
StyleSheet,
|
|
ScrollView,
|
|
TextInput,
|
|
TouchableOpacity,
|
|
Alert,
|
|
KeyboardAvoidingView,
|
|
Platform,
|
|
} from 'react-native';
|
|
import Icon from 'react-native-vector-icons/Feather';
|
|
import { theme } from '../../../theme/theme';
|
|
import { SettingsHeader } from '../components/SettingsHeader';
|
|
import { useAppDispatch } from '../../../store/hooks';
|
|
import { changePasswordAsync } from '../../Auth/redux/authActions';
|
|
|
|
/**
|
|
* ChangePasswordScreenProps Interface
|
|
*
|
|
* Purpose: Defines the props required by the ChangePasswordScreen component
|
|
*
|
|
* Props:
|
|
* - navigation: React Navigation object for screen navigation
|
|
*/
|
|
interface ChangePasswordScreenProps {
|
|
navigation: any;
|
|
}
|
|
|
|
/**
|
|
* FormData Interface
|
|
*
|
|
* Purpose: Defines the structure of the password change form data
|
|
*/
|
|
interface FormData {
|
|
currentPassword: string;
|
|
newPassword: string;
|
|
confirmPassword: string;
|
|
}
|
|
|
|
/**
|
|
* FormErrors Interface
|
|
*
|
|
* Purpose: Defines the structure of form validation errors
|
|
*/
|
|
interface FormErrors {
|
|
currentPassword?: string;
|
|
newPassword?: string;
|
|
confirmPassword?: string;
|
|
}
|
|
|
|
/**
|
|
* PasswordStrength Interface
|
|
*
|
|
* Purpose: Defines the structure of password strength information
|
|
*/
|
|
interface PasswordStrength {
|
|
score: number;
|
|
label: string;
|
|
color: string;
|
|
requirements: string[];
|
|
}
|
|
|
|
/**
|
|
* ChangePasswordScreen Component
|
|
*
|
|
* Purpose: Allows users to change their password with comprehensive validation
|
|
* including current password verification, new password strength requirements,
|
|
* and password confirmation
|
|
*
|
|
* Features:
|
|
* 1. Current password verification
|
|
* 2. New password strength validation
|
|
* 3. Password confirmation matching
|
|
* 4. Real-time password strength indicator
|
|
* 5. Comprehensive error handling
|
|
*/
|
|
export const ChangePasswordScreen: React.FC<ChangePasswordScreenProps> = ({
|
|
navigation,
|
|
}) => {
|
|
// ============================================================================
|
|
// REDUX STATE
|
|
// ============================================================================
|
|
|
|
const dispatch = useAppDispatch();
|
|
|
|
// ============================================================================
|
|
// LOCAL STATE
|
|
// ============================================================================
|
|
|
|
const [formData, setFormData] = useState<FormData>({
|
|
currentPassword: '',
|
|
newPassword: '',
|
|
confirmPassword: '',
|
|
});
|
|
|
|
const [errors, setErrors] = useState<FormErrors>({});
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
const [showCurrentPassword, setShowCurrentPassword] = useState(false);
|
|
const [showNewPassword, setShowNewPassword] = useState(false);
|
|
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
|
const [passwordStrength, setPasswordStrength] = useState<PasswordStrength>({
|
|
score: 0,
|
|
label: 'Very Weak',
|
|
color: theme.colors.error,
|
|
requirements: [],
|
|
});
|
|
|
|
// ============================================================================
|
|
// PASSWORD STRENGTH VALIDATION
|
|
// ============================================================================
|
|
|
|
/**
|
|
* checkPasswordStrength Function
|
|
*
|
|
* Purpose: Analyze password strength and return strength information
|
|
*
|
|
* @param password - Password to analyze
|
|
* @returns PasswordStrength object with score, label, color, and requirements
|
|
*/
|
|
const checkPasswordStrength = (password: string): PasswordStrength => {
|
|
const requirements: string[] = [];
|
|
let score = 0;
|
|
|
|
// Length requirement
|
|
if (password.length >= 8) {
|
|
score += 1;
|
|
requirements.push('✓ At least 8 characters');
|
|
} else {
|
|
requirements.push('✗ At least 8 characters');
|
|
}
|
|
|
|
// Uppercase requirement
|
|
if (/[A-Z]/.test(password)) {
|
|
score += 1;
|
|
requirements.push('✓ Contains uppercase letter');
|
|
} else {
|
|
requirements.push('✗ Contains uppercase letter');
|
|
}
|
|
|
|
// Lowercase requirement
|
|
if (/[a-z]/.test(password)) {
|
|
score += 1;
|
|
requirements.push('✓ Contains lowercase letter');
|
|
} else {
|
|
requirements.push('✗ Contains lowercase letter');
|
|
}
|
|
|
|
// Number requirement
|
|
if (/\d/.test(password)) {
|
|
score += 1;
|
|
requirements.push('✓ Contains number');
|
|
} else {
|
|
requirements.push('✗ Contains number');
|
|
}
|
|
|
|
// Special character requirement
|
|
if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
|
|
score += 1;
|
|
requirements.push('✓ Contains special character');
|
|
} else {
|
|
requirements.push('✗ Contains special character');
|
|
}
|
|
|
|
// Determine strength label and color
|
|
let label: string;
|
|
let color: string;
|
|
|
|
if (score <= 1) {
|
|
label = 'Very Weak';
|
|
color = theme.colors.error;
|
|
} else if (score <= 2) {
|
|
label = 'Weak';
|
|
color = theme.colors.warning;
|
|
} else if (score <= 3) {
|
|
label = 'Fair';
|
|
color = theme.colors.warning;
|
|
} else if (score <= 4) {
|
|
label = 'Good';
|
|
color = theme.colors.info;
|
|
} else {
|
|
label = 'Strong';
|
|
color = theme.colors.success;
|
|
}
|
|
|
|
return {
|
|
score,
|
|
label,
|
|
color,
|
|
requirements,
|
|
};
|
|
};
|
|
|
|
// ============================================================================
|
|
// VALIDATION FUNCTIONS
|
|
// ============================================================================
|
|
|
|
/**
|
|
* validateField Function
|
|
*
|
|
* Purpose: Validate individual form fields
|
|
*
|
|
* @param field - Field name to validate
|
|
* @param value - Field value to validate
|
|
* @returns Validation error message or undefined
|
|
*/
|
|
const validateField = (field: keyof FormData, value: string): string | undefined => {
|
|
switch (field) {
|
|
case 'currentPassword':
|
|
if (!value.trim()) {
|
|
return 'Current password is required';
|
|
}
|
|
if (value.trim().length < 6) {
|
|
return 'Current password must be at least 6 characters';
|
|
}
|
|
break;
|
|
|
|
case 'newPassword':
|
|
if (!value.trim()) {
|
|
return 'New password is required';
|
|
}
|
|
if (value.trim().length < 8) {
|
|
return 'New password must be at least 8 characters';
|
|
}
|
|
if (value === formData.currentPassword) {
|
|
return 'New password must be different from current password';
|
|
}
|
|
break;
|
|
|
|
case 'confirmPassword':
|
|
if (!value.trim()) {
|
|
return 'Please confirm your new password';
|
|
}
|
|
if (value !== formData.newPassword) {
|
|
return 'Passwords do not match';
|
|
}
|
|
break;
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
/**
|
|
* validateForm Function
|
|
*
|
|
* Purpose: Validate entire form and return validation errors
|
|
*
|
|
* @returns Object containing validation errors
|
|
*/
|
|
const validateForm = (): FormErrors => {
|
|
const newErrors: FormErrors = {};
|
|
|
|
Object.keys(formData).forEach((field) => {
|
|
const key = field as keyof FormData;
|
|
const error = validateField(key, formData[key]);
|
|
if (error) {
|
|
newErrors[key] = error;
|
|
}
|
|
});
|
|
|
|
return newErrors;
|
|
};
|
|
|
|
// ============================================================================
|
|
// EVENT HANDLERS
|
|
// ============================================================================
|
|
|
|
/**
|
|
* handleInputChange Function
|
|
*
|
|
* Purpose: Handle input field changes and update password strength
|
|
*
|
|
* @param field - Field name that changed
|
|
* @param value - New field value
|
|
*/
|
|
const handleInputChange = (field: keyof FormData, value: string) => {
|
|
setFormData(prev => ({ ...prev, [field]: value }));
|
|
|
|
// Clear field-specific error when user starts typing
|
|
if (errors[field]) {
|
|
setErrors(prev => ({ ...prev, [field]: undefined }));
|
|
}
|
|
|
|
// Update password strength for new password field
|
|
if (field === 'newPassword') {
|
|
const strength = checkPasswordStrength(value);
|
|
setPasswordStrength(strength);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* handleInputBlur Function
|
|
*
|
|
* Purpose: Validate field when user leaves the input
|
|
*
|
|
* @param field - Field name to validate
|
|
*/
|
|
const handleInputBlur = (field: keyof FormData) => {
|
|
const error = validateField(field, formData[field]);
|
|
setErrors(prev => ({ ...prev, [field]: error }));
|
|
};
|
|
|
|
/**
|
|
* handleSubmit Function
|
|
*
|
|
* Purpose: Handle form submission with validation and API call
|
|
*/
|
|
const handleSubmit = async () => {
|
|
// Validate form
|
|
const validationErrors = validateForm();
|
|
if (Object.keys(validationErrors).length > 0) {
|
|
setErrors(validationErrors);
|
|
return;
|
|
}
|
|
|
|
// Check password strength
|
|
if (passwordStrength.score < 3) {
|
|
Alert.alert(
|
|
'Weak Password',
|
|
'Please choose a stronger password that meets the requirements.',
|
|
[{ text: 'OK' }]
|
|
);
|
|
return;
|
|
}
|
|
|
|
setIsSubmitting(true);
|
|
|
|
try {
|
|
// Dispatch password change action
|
|
await dispatch(changePasswordAsync({
|
|
currentPassword: formData.currentPassword,
|
|
newPassword: formData.newPassword,
|
|
})).unwrap();
|
|
|
|
// Navigate back after successful password change
|
|
navigation.goBack();
|
|
} catch (error: any) {
|
|
// Handle error - toast notification is already shown in the thunk
|
|
console.error('Password change error:', error);
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* handleCancel Function
|
|
*
|
|
* Purpose: Handle cancel action
|
|
*/
|
|
const handleCancel = () => {
|
|
navigation.goBack();
|
|
};
|
|
|
|
/**
|
|
* togglePasswordVisibility Function
|
|
*
|
|
* Purpose: Toggle password visibility for specified field
|
|
*
|
|
* @param field - Field to toggle visibility for
|
|
*/
|
|
const togglePasswordVisibility = (field: 'current' | 'new' | 'confirm') => {
|
|
switch (field) {
|
|
case 'current':
|
|
setShowCurrentPassword(!showCurrentPassword);
|
|
break;
|
|
case 'new':
|
|
setShowNewPassword(!showNewPassword);
|
|
break;
|
|
case 'confirm':
|
|
setShowConfirmPassword(!showConfirmPassword);
|
|
break;
|
|
}
|
|
};
|
|
|
|
// ============================================================================
|
|
// MAIN RENDER
|
|
// ============================================================================
|
|
|
|
return (
|
|
<KeyboardAvoidingView
|
|
style={styles.container}
|
|
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
>
|
|
{/* Header with back button */}
|
|
<SettingsHeader
|
|
title="Change Password"
|
|
showBackButton={true}
|
|
onBackPress={handleCancel}
|
|
/>
|
|
|
|
{/* Scrollable form content */}
|
|
<ScrollView
|
|
style={styles.scrollView}
|
|
contentContainerStyle={styles.scrollContent}
|
|
showsVerticalScrollIndicator={false}
|
|
keyboardShouldPersistTaps="handled"
|
|
>
|
|
{/* Password requirements info */}
|
|
<View style={styles.infoSection}>
|
|
<Text style={styles.sectionTitle}>Password Requirements</Text>
|
|
<Text style={styles.infoText}>
|
|
Your new password must meet the following requirements to ensure security:
|
|
</Text>
|
|
<View style={styles.requirementsList}>
|
|
<Text style={styles.requirementItem}>• At least 8 characters long</Text>
|
|
<Text style={styles.requirementItem}>• Contains uppercase and lowercase letters</Text>
|
|
<Text style={styles.requirementItem}>• Contains at least one number</Text>
|
|
<Text style={styles.requirementItem}>• Contains at least one special character</Text>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Password change form */}
|
|
<View style={styles.infoSection}>
|
|
<Text style={styles.sectionTitle}>Change Password</Text>
|
|
|
|
{/* Current Password Input */}
|
|
<View style={styles.inputContainer}>
|
|
<Text style={styles.inputLabel}>Current Password *</Text>
|
|
<View style={styles.passwordInputContainer}>
|
|
<TextInput
|
|
style={[
|
|
styles.textInput,
|
|
styles.passwordInput,
|
|
errors.currentPassword ? styles.inputError : null,
|
|
]}
|
|
value={formData.currentPassword}
|
|
onChangeText={(value) => handleInputChange('currentPassword', value)}
|
|
onBlur={() => handleInputBlur('currentPassword')}
|
|
placeholder="Enter your current password"
|
|
placeholderTextColor={theme.colors.textMuted}
|
|
secureTextEntry={!showCurrentPassword}
|
|
autoCapitalize="none"
|
|
autoCorrect={false}
|
|
/>
|
|
<TouchableOpacity
|
|
style={styles.eyeIcon}
|
|
onPress={() => togglePasswordVisibility('current')}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Icon
|
|
name={showCurrentPassword ? 'eye-off' : 'eye'}
|
|
size={20}
|
|
color={theme.colors.textMuted}
|
|
/>
|
|
</TouchableOpacity>
|
|
</View>
|
|
{errors.currentPassword && (
|
|
<Text style={styles.errorText}>{errors.currentPassword}</Text>
|
|
)}
|
|
</View>
|
|
|
|
{/* New Password Input */}
|
|
<View style={styles.inputContainer}>
|
|
<Text style={styles.inputLabel}>New Password *</Text>
|
|
<View style={styles.passwordInputContainer}>
|
|
<TextInput
|
|
style={[
|
|
styles.textInput,
|
|
styles.passwordInput,
|
|
errors.newPassword ? styles.inputError : null,
|
|
]}
|
|
value={formData.newPassword}
|
|
onChangeText={(value) => handleInputChange('newPassword', value)}
|
|
onBlur={() => handleInputBlur('newPassword')}
|
|
placeholder="Enter your new password"
|
|
placeholderTextColor={theme.colors.textMuted}
|
|
secureTextEntry={!showNewPassword}
|
|
autoCapitalize="none"
|
|
autoCorrect={false}
|
|
/>
|
|
<TouchableOpacity
|
|
style={styles.eyeIcon}
|
|
onPress={() => togglePasswordVisibility('new')}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Icon
|
|
name={showNewPassword ? 'eye-off' : 'eye'}
|
|
size={20}
|
|
color={theme.colors.textMuted}
|
|
/>
|
|
</TouchableOpacity>
|
|
</View>
|
|
{errors.newPassword && (
|
|
<Text style={styles.errorText}>{errors.newPassword}</Text>
|
|
)}
|
|
</View>
|
|
|
|
{/* Password Strength Indicator */}
|
|
{formData.newPassword.length > 0 && (
|
|
<View style={styles.strengthContainer}>
|
|
<Text style={styles.strengthLabel}>Password Strength:</Text>
|
|
<View style={styles.strengthBar}>
|
|
<View
|
|
style={[
|
|
styles.strengthProgress,
|
|
{
|
|
width: `${(passwordStrength.score / 5) * 100}%`,
|
|
backgroundColor: passwordStrength.color,
|
|
}
|
|
]}
|
|
/>
|
|
</View>
|
|
<Text style={[styles.strengthText, { color: passwordStrength.color }]}>
|
|
{passwordStrength.label}
|
|
</Text>
|
|
</View>
|
|
)}
|
|
|
|
{/* Password Requirements Check */}
|
|
{formData.newPassword.length > 0 && (
|
|
<View style={styles.requirementsCheck}>
|
|
{passwordStrength.requirements.map((requirement, index) => (
|
|
<Text key={index} style={styles.requirementCheckItem}>
|
|
{requirement}
|
|
</Text>
|
|
))}
|
|
</View>
|
|
)}
|
|
|
|
{/* Confirm Password Input */}
|
|
<View style={styles.inputContainer}>
|
|
<Text style={styles.inputLabel}>Confirm New Password *</Text>
|
|
<View style={styles.passwordInputContainer}>
|
|
<TextInput
|
|
style={[
|
|
styles.textInput,
|
|
styles.passwordInput,
|
|
errors.confirmPassword ? styles.inputError : null,
|
|
]}
|
|
value={formData.confirmPassword}
|
|
onChangeText={(value) => handleInputChange('confirmPassword', value)}
|
|
onBlur={() => handleInputBlur('confirmPassword')}
|
|
placeholder="Confirm your new password"
|
|
placeholderTextColor={theme.colors.textMuted}
|
|
secureTextEntry={!showConfirmPassword}
|
|
autoCapitalize="none"
|
|
autoCorrect={false}
|
|
/>
|
|
<TouchableOpacity
|
|
style={styles.eyeIcon}
|
|
onPress={() => togglePasswordVisibility('confirm')}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Icon
|
|
name={showConfirmPassword ? 'eye-off' : 'eye'}
|
|
size={20}
|
|
color={theme.colors.textMuted}
|
|
/>
|
|
</TouchableOpacity>
|
|
</View>
|
|
{errors.confirmPassword && (
|
|
<Text style={styles.errorText}>{errors.confirmPassword}</Text>
|
|
)}
|
|
</View>
|
|
</View>
|
|
|
|
{/* Action buttons */}
|
|
<View style={styles.buttonContainer}>
|
|
<TouchableOpacity
|
|
style={[
|
|
styles.submitButton,
|
|
isSubmitting && styles.submitButtonDisabled,
|
|
]}
|
|
onPress={handleSubmit}
|
|
disabled={isSubmitting}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Text style={[
|
|
styles.submitButtonText,
|
|
isSubmitting && styles.submitButtonTextDisabled,
|
|
]}>
|
|
{isSubmitting ? 'Changing Password...' : 'Change Password'}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity
|
|
style={styles.cancelButton}
|
|
onPress={handleCancel}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Text style={styles.cancelButtonText}>Cancel</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
{/* Bottom spacing for tab bar */}
|
|
<View style={styles.bottomSpacing} />
|
|
</ScrollView>
|
|
</KeyboardAvoidingView>
|
|
);
|
|
};
|
|
|
|
// ============================================================================
|
|
// STYLES SECTION
|
|
// ============================================================================
|
|
|
|
const styles = StyleSheet.create({
|
|
// Main container
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: theme.colors.background,
|
|
},
|
|
|
|
// Scroll view styling
|
|
scrollView: {
|
|
flex: 1,
|
|
},
|
|
|
|
// Scroll content styling
|
|
scrollContent: {
|
|
paddingHorizontal: theme.spacing.md,
|
|
},
|
|
|
|
// Bottom spacing for tab bar
|
|
bottomSpacing: {
|
|
height: theme.spacing.xl,
|
|
},
|
|
|
|
// Information sections
|
|
infoSection: {
|
|
backgroundColor: theme.colors.background,
|
|
borderRadius: theme.borderRadius.medium,
|
|
padding: theme.spacing.md,
|
|
marginBottom: theme.spacing.md,
|
|
...theme.shadows.primary,
|
|
},
|
|
|
|
sectionTitle: {
|
|
fontSize: theme.typography.fontSize.displaySmall,
|
|
fontFamily: theme.typography.fontFamily.bold,
|
|
color: theme.colors.textPrimary,
|
|
marginBottom: theme.spacing.md,
|
|
},
|
|
|
|
infoText: {
|
|
fontSize: theme.typography.fontSize.bodyMedium,
|
|
fontFamily: theme.typography.fontFamily.regular,
|
|
color: theme.colors.textSecondary,
|
|
lineHeight: 22,
|
|
marginBottom: theme.spacing.md,
|
|
},
|
|
|
|
// Requirements list styling
|
|
requirementsList: {
|
|
marginTop: theme.spacing.sm,
|
|
},
|
|
|
|
requirementItem: {
|
|
fontSize: theme.typography.fontSize.bodyMedium,
|
|
fontFamily: theme.typography.fontFamily.regular,
|
|
color: theme.colors.textSecondary,
|
|
marginBottom: theme.spacing.xs,
|
|
},
|
|
|
|
// Input container styling
|
|
inputContainer: {
|
|
marginBottom: theme.spacing.sm,
|
|
backgroundColor: theme.colors.background,
|
|
padding: theme.spacing.sm,
|
|
borderRadius: theme.borderRadius.small,
|
|
},
|
|
|
|
inputLabel: {
|
|
fontSize: theme.typography.fontSize.bodyMedium,
|
|
fontFamily: theme.typography.fontFamily.medium,
|
|
color: theme.colors.textPrimary,
|
|
marginBottom: theme.spacing.sm,
|
|
fontWeight: '600',
|
|
},
|
|
|
|
// Password input styling
|
|
passwordInputContainer: {
|
|
position: 'relative',
|
|
},
|
|
|
|
textInput: {
|
|
borderWidth: 1,
|
|
borderColor: theme.colors.border,
|
|
borderRadius: theme.borderRadius.medium,
|
|
paddingHorizontal: theme.spacing.md,
|
|
paddingVertical: theme.spacing.md,
|
|
fontSize: theme.typography.fontSize.bodyMedium,
|
|
fontFamily: theme.typography.fontFamily.regular,
|
|
color: theme.colors.textPrimary,
|
|
backgroundColor: theme.colors.background,
|
|
minHeight: 48,
|
|
},
|
|
|
|
passwordInput: {
|
|
paddingRight: theme.spacing.xl + theme.spacing.md,
|
|
},
|
|
|
|
eyeIcon: {
|
|
position: 'absolute',
|
|
right: theme.spacing.md,
|
|
top: theme.spacing.md,
|
|
padding: theme.spacing.xs,
|
|
},
|
|
|
|
inputError: {
|
|
borderColor: theme.colors.error,
|
|
backgroundColor: theme.colors.error + '10',
|
|
},
|
|
|
|
errorText: {
|
|
fontSize: theme.typography.fontSize.bodySmall,
|
|
fontFamily: theme.typography.fontFamily.regular,
|
|
color: theme.colors.error,
|
|
marginTop: theme.spacing.sm,
|
|
marginLeft: theme.spacing.xs,
|
|
paddingHorizontal: theme.spacing.sm,
|
|
paddingVertical: theme.spacing.xs,
|
|
backgroundColor: theme.colors.error + '10',
|
|
borderRadius: theme.borderRadius.small,
|
|
alignSelf: 'flex-start',
|
|
},
|
|
|
|
// Password strength styling
|
|
strengthContainer: {
|
|
marginBottom: theme.spacing.md,
|
|
},
|
|
|
|
strengthLabel: {
|
|
fontSize: theme.typography.fontSize.bodySmall,
|
|
fontFamily: theme.typography.fontFamily.medium,
|
|
color: theme.colors.textPrimary,
|
|
marginBottom: theme.spacing.xs,
|
|
},
|
|
|
|
strengthBar: {
|
|
height: 4,
|
|
backgroundColor: theme.colors.border,
|
|
borderRadius: 2,
|
|
marginBottom: theme.spacing.xs,
|
|
overflow: 'hidden',
|
|
},
|
|
|
|
strengthProgress: {
|
|
height: '100%',
|
|
borderRadius: 2,
|
|
},
|
|
|
|
strengthText: {
|
|
fontSize: theme.typography.fontSize.bodySmall,
|
|
fontFamily: theme.typography.fontFamily.medium,
|
|
textAlign: 'center',
|
|
},
|
|
|
|
// Requirements check styling
|
|
requirementsCheck: {
|
|
marginBottom: theme.spacing.md,
|
|
padding: theme.spacing.sm,
|
|
backgroundColor: theme.colors.backgroundAlt,
|
|
borderRadius: theme.borderRadius.small,
|
|
},
|
|
|
|
requirementCheckItem: {
|
|
fontSize: theme.typography.fontSize.bodySmall,
|
|
fontFamily: theme.typography.fontFamily.regular,
|
|
color: theme.colors.textSecondary,
|
|
marginBottom: theme.spacing.xs,
|
|
},
|
|
|
|
// Button container
|
|
buttonContainer: {
|
|
marginTop: theme.spacing.lg,
|
|
marginBottom: theme.spacing.md,
|
|
},
|
|
|
|
submitButton: {
|
|
backgroundColor: theme.colors.primary,
|
|
borderRadius: theme.borderRadius.medium,
|
|
paddingVertical: theme.spacing.md,
|
|
paddingHorizontal: theme.spacing.lg,
|
|
alignItems: 'center',
|
|
marginBottom: theme.spacing.md,
|
|
...theme.shadows.primary,
|
|
},
|
|
|
|
submitButtonDisabled: {
|
|
backgroundColor: theme.colors.border,
|
|
shadowColor: 'transparent',
|
|
shadowOffset: { width: 0, height: 0 },
|
|
shadowOpacity: 0,
|
|
shadowRadius: 0,
|
|
elevation: 0,
|
|
},
|
|
|
|
submitButtonText: {
|
|
color: theme.colors.background,
|
|
fontSize: theme.typography.fontSize.bodyLarge,
|
|
fontFamily: theme.typography.fontFamily.bold,
|
|
},
|
|
|
|
submitButtonTextDisabled: {
|
|
color: theme.colors.textMuted,
|
|
},
|
|
|
|
cancelButton: {
|
|
backgroundColor: 'transparent',
|
|
borderWidth: 1,
|
|
borderColor: theme.colors.border,
|
|
borderRadius: theme.borderRadius.medium,
|
|
paddingVertical: theme.spacing.md,
|
|
paddingHorizontal: theme.spacing.lg,
|
|
alignItems: 'center',
|
|
},
|
|
|
|
cancelButtonText: {
|
|
color: theme.colors.textSecondary,
|
|
fontSize: theme.typography.fontSize.bodyLarge,
|
|
fontFamily: theme.typography.fontFamily.medium,
|
|
},
|
|
});
|
|
|
|
/*
|
|
* End of File: ChangePasswordScreen.tsx
|
|
* Design & Developed by Tech4Biz Solutions
|
|
* Copyright (c) Spurrin Innovations. All rights reserved.
|
|
*/
|