Files
pwap/mobile/src/components/ChangePasswordModal.tsx
T
pezkuwichain 1f8da810e0 fix: TypeScript errors, shadow deprecations, and build configuration
- Fix shadow style deprecation warnings across components (boxShadow)
- Add type declaration files (codec.d.ts, modules.d.ts)
- Update metro.config.cjs for proper asset extensions
- Update tsconfig.json with module resolution settings
- Fix TypeScript errors in shared/lib files
- Update app icons (optimized PNG files)
2026-01-15 09:37:37 +03:00

347 lines
9.2 KiB
TypeScript

import React, { useState } from 'react';
import {
Modal,
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
Alert,
ActivityIndicator,
} from 'react-native';
import { KurdistanColors } from '../theme/colors';
import { useTheme } from '../contexts/ThemeContext';
import { useAuth } from '../contexts/AuthContext';
interface ChangePasswordModalProps {
visible: boolean;
onClose: () => void;
}
const ChangePasswordModal: React.FC<ChangePasswordModalProps> = ({
visible,
onClose,
}) => {
const { colors } = useTheme();
const { changePassword, resetPassword, user } = useAuth();
const [currentPassword, setCurrentPassword] = useState('');
const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [loading, setLoading] = useState(false);
const handleSubmit = async () => {
// Validation
if (!currentPassword) {
Alert.alert('Error', 'Please enter your current password');
return;
}
if (!newPassword || newPassword.length < 6) {
Alert.alert('Error', 'New password must be at least 6 characters long');
return;
}
if (newPassword !== confirmPassword) {
Alert.alert('Error', 'Passwords do not match');
return;
}
if (currentPassword === newPassword) {
Alert.alert('Error', 'New password must be different from current password');
return;
}
setLoading(true);
// First verify current password by re-authenticating
const { error: verifyError } = await changePassword(newPassword, currentPassword);
setLoading(false);
if (verifyError) {
Alert.alert('Error', verifyError.message || 'Failed to change password');
} else {
Alert.alert('Success', 'Password changed successfully');
setCurrentPassword('');
setNewPassword('');
setConfirmPassword('');
onClose();
}
};
const handleClose = () => {
setCurrentPassword('');
setNewPassword('');
setConfirmPassword('');
onClose();
};
const handleForgotPassword = () => {
if (!user?.email) {
Alert.alert('Error', 'No email address found for this account');
return;
}
Alert.alert(
'Reset Password',
`A password reset link will be sent to ${user.email}`,
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Send Reset Link',
onPress: async () => {
const { error } = await resetPassword(user.email!);
if (error) {
Alert.alert('Error', error.message || 'Failed to send reset email');
} else {
Alert.alert('Success', 'Password reset link sent to your email. Please check your inbox.');
handleClose();
}
},
},
]
);
};
const styles = createStyles(colors);
return (
<Modal
visible={visible}
animationType="fade"
transparent={true}
onRequestClose={handleClose}
>
<View style={styles.overlay}>
<View style={styles.container}>
{/* Header */}
<View style={styles.header}>
<Text style={styles.headerTitle}>Change Password</Text>
<TouchableOpacity onPress={handleClose} style={styles.closeButton}>
<Text style={styles.closeButtonText}></Text>
</TouchableOpacity>
</View>
{/* Content */}
<View style={styles.content}>
<Text style={styles.description}>
To change your password, first enter your current password, then your new password.
</Text>
<View style={styles.inputContainer}>
<Text style={styles.label}>Current Password</Text>
<TextInput
style={styles.input}
value={currentPassword}
onChangeText={setCurrentPassword}
placeholder="Enter current password"
placeholderTextColor={colors.textSecondary}
secureTextEntry
autoCapitalize="none"
editable={!loading}
/>
</View>
<View style={styles.inputContainer}>
<Text style={styles.label}>New Password</Text>
<TextInput
style={styles.input}
value={newPassword}
onChangeText={setNewPassword}
placeholder="Enter new password"
placeholderTextColor={colors.textSecondary}
secureTextEntry
autoCapitalize="none"
editable={!loading}
/>
</View>
<View style={styles.inputContainer}>
<Text style={styles.label}>Confirm Password</Text>
<TextInput
style={styles.input}
value={confirmPassword}
onChangeText={setConfirmPassword}
placeholder="Confirm new password"
placeholderTextColor={colors.textSecondary}
secureTextEntry
autoCapitalize="none"
editable={!loading}
/>
</View>
{newPassword.length > 0 && newPassword.length < 6 && (
<Text style={styles.errorText}>
Password must be at least 6 characters
</Text>
)}
{confirmPassword.length > 0 && newPassword !== confirmPassword && (
<Text style={styles.errorText}>Passwords do not match</Text>
)}
{/* Forgot Password Link */}
<TouchableOpacity
onPress={handleForgotPassword}
style={styles.forgotPasswordButton}
disabled={loading}
>
<Text style={styles.forgotPasswordText}>Forgot Password?</Text>
</TouchableOpacity>
</View>
{/* Footer */}
<View style={styles.footer}>
<TouchableOpacity
style={styles.cancelButton}
onPress={handleClose}
disabled={loading}
>
<Text style={styles.cancelButtonText}>Cancel</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.submitButton,
(loading || !currentPassword || !newPassword || newPassword !== confirmPassword || newPassword.length < 6) &&
styles.submitButtonDisabled,
]}
onPress={handleSubmit}
disabled={loading || !currentPassword || !newPassword || newPassword !== confirmPassword || newPassword.length < 6}
>
{loading ? (
<ActivityIndicator color={KurdistanColors.spi} />
) : (
<Text style={styles.submitButtonText}>Change Password</Text>
)}
</TouchableOpacity>
</View>
</View>
</View>
</Modal>
);
};
const createStyles = (colors: any) => StyleSheet.create({
overlay: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
},
container: {
width: '90%',
maxWidth: 400,
backgroundColor: colors.surface,
borderRadius: 16,
overflow: 'hidden',
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: 20,
borderBottomWidth: 1,
borderBottomColor: colors.border,
},
headerTitle: {
fontSize: 20,
fontWeight: 'bold',
color: colors.text,
},
closeButton: {
width: 32,
height: 32,
borderRadius: 16,
backgroundColor: colors.background,
justifyContent: 'center',
alignItems: 'center',
},
closeButtonText: {
fontSize: 20,
color: colors.textSecondary,
},
content: {
padding: 20,
},
description: {
fontSize: 14,
color: colors.textSecondary,
marginBottom: 20,
lineHeight: 20,
},
inputContainer: {
marginBottom: 16,
},
label: {
fontSize: 14,
fontWeight: '600',
color: colors.text,
marginBottom: 8,
},
input: {
height: 48,
borderWidth: 1,
borderColor: colors.border,
borderRadius: 8,
paddingHorizontal: 16,
fontSize: 16,
color: colors.text,
backgroundColor: colors.background,
},
errorText: {
fontSize: 13,
color: KurdistanColors.sor,
marginTop: -8,
marginBottom: 8,
},
forgotPasswordButton: {
marginTop: 8,
paddingVertical: 8,
},
forgotPasswordText: {
fontSize: 14,
color: KurdistanColors.kesk,
fontWeight: '600',
textAlign: 'center',
},
footer: {
flexDirection: 'row',
padding: 20,
borderTopWidth: 1,
borderTopColor: colors.border,
gap: 12,
},
cancelButton: {
flex: 1,
paddingVertical: 14,
borderRadius: 8,
borderWidth: 1,
borderColor: colors.border,
alignItems: 'center',
},
cancelButtonText: {
fontSize: 16,
fontWeight: '600',
color: colors.text,
},
submitButton: {
flex: 1,
paddingVertical: 14,
borderRadius: 8,
backgroundColor: KurdistanColors.kesk,
alignItems: 'center',
justifyContent: 'center',
},
submitButtonDisabled: {
opacity: 0.5,
},
submitButtonText: {
fontSize: 16,
fontWeight: '600',
color: KurdistanColors.spi,
},
});
export default ChangePasswordModal;