NeoScan_Radiologist/app/modules/Auth/screens/LoginScreen.tsx

419 lines
12 KiB
TypeScript

/*
* File: LoginScreen.tsx
* Description: Login screen with credential-based authentication
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
TouchableWithoutFeedback,
Keyboard,
TouchableOpacity,
TextInput,
ScrollView,
KeyboardAvoidingView,
Alert,
Image,
} from 'react-native';
import { useAppDispatch, useAppSelector } from '../../../store/hooks';
import { login } from '../redux/authActions';
import { selectAuthLoading } from '../redux/authSelectors';
import { theme } from '../../../theme/theme';
import { validateEmail } from '../../../shared/utils/validators';
import Icon from 'react-native-vector-icons/Feather';
import { AuthNavigationProp } from '../navigation';
/**
* LoginScreenProps Interface
*
* Purpose: Defines the props required by the LoginScreen component
*
* Props:
* - navigation: React Navigation object for screen navigation
*/
interface LoginScreenProps {
navigation: AuthNavigationProp;
}
/**
* LoginScreen Component
*
* Purpose: Main authentication screen for credential-based login
*
* Authentication Flow:
* 1. Email/Password validation
* 2. Redux action dispatch for login
* 3. Loading state management
* 4. Error handling and user feedback
*
* Features:
* - Keyboard-aware layout for better UX
* - Form validation and error handling
* - Loading states during authentication
* - Password visibility toggle
* - Navigation to sign up screen
* - Responsive design for different screen sizes
*/
const LoginScreen: React.FC<LoginScreenProps> = ({ navigation }) => {
// ============================================================================
// STATE MANAGEMENT
// ============================================================================
// Form input states
const [email, setEmail] = useState(''); // User's email address
const [password, setPassword] = useState(''); // User's password
const [isPasswordVisible, setPasswordVisible] = useState(false); // Password visibility toggle
// Redux state
const dispatch = useAppDispatch();
const loading = useAppSelector(selectAuthLoading);
// ============================================================================
// EVENT HANDLERS
// ============================================================================
/**
* handleLogin Function
*
* Purpose: Process credential-based login with email and password
*
* Flow:
* 1. Validate that both email and password are provided
* 2. Validate email format
* 3. Show error alert if validation fails
* 4. Dispatch Redux login action with credentials
*/
const handleLogin = () => {
// Validate required fields
if (!email.trim() || !password.trim()) {
Alert.alert('Validation Error', 'Email and password are required.');
return;
}
// Validate email format
if (!validateEmail(email)) {
Alert.alert('Validation Error', 'Please enter a valid email address.');
return;
}
// Dispatch login action
dispatch(login({ email, password }));
};
/**
* handleSignUp Function
*
* Purpose: Navigate to the SignUpScreen for new user registration
*
* Flow: Navigate to SignUp screen using React Navigation
*/
const handleSignUp = () => {
navigation.navigate('SignUp');
};
/**
* togglePasswordVisibility Function
*
* Purpose: Toggle password field visibility for better UX
*/
const togglePasswordVisibility = () => {
setPasswordVisible(!isPasswordVisible);
};
// ============================================================================
// RENDER SECTION
// ============================================================================
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<KeyboardAvoidingView behavior="padding" style={styles.container}>
<ScrollView showsVerticalScrollIndicator={false}>
<View style={styles.content}>
{/* ========================================================================
* HEADER SECTION - App branding and title
* ======================================================================== */}
<View style={styles.header}>
<Text style={styles.title}>Radiologist</Text>
{/* <Text style={styles.subtitle}>Emergency Department Access</Text> */}
</View>
<View style={styles.imageContainer}>
<Image source={require('../../../assets/images/hospital-logo.png')} style={styles.image} />
</View>
{/* ========================================================================
* LOGIN FORM - Main authentication interface
* ======================================================================== */}
<View style={styles.formContainer}>
{/* Email Input */}
<View style={styles.inputContainer}>
<Icon name="mail" size={20} color={theme.colors.textSecondary} style={styles.inputIcon} />
<TextInput
placeholder="Email"
value={email}
onChangeText={setEmail}
style={styles.inputField}
autoCapitalize="none"
keyboardType="email-address"
placeholderTextColor={theme.colors.textMuted}
editable={!loading}
/>
</View>
{/* Password Input */}
<View style={styles.inputContainer}>
<Icon name="lock" size={20} color={theme.colors.textSecondary} style={styles.inputIcon} />
<TextInput
placeholder="Password"
value={password}
onChangeText={setPassword}
style={styles.inputField}
secureTextEntry={!isPasswordVisible}
placeholderTextColor={theme.colors.textMuted}
editable={!loading}
/>
<TouchableOpacity
onPress={togglePasswordVisibility}
style={styles.eyeIcon}
disabled={loading}
>
<Icon
name={isPasswordVisible ? 'eye-off' : 'eye'}
size={22}
color={theme.colors.textSecondary}
/>
</TouchableOpacity>
</View>
{/* Login Button */}
<TouchableOpacity
style={[styles.button, styles.loginButton, loading && styles.buttonDisabled]}
onPress={handleLogin}
disabled={loading}
>
{loading ? (
<View style={styles.loadingContainer}>
<Text style={styles.buttonText}>Logging in...</Text>
</View>
) : (
<Text style={styles.buttonText}>Login</Text>
)}
</TouchableOpacity>
{/* Divider */}
<View style={styles.divider}>
<View style={styles.dividerLine} />
<Text style={styles.dividerText}>OR</Text>
<View style={styles.dividerLine} />
</View>
{/* Sign Up Button */}
<TouchableOpacity
style={[styles.button, styles.signUpButton]}
onPress={handleSignUp}
disabled={loading}
>
<Text style={styles.signUpButtonText}>Sign Up</Text>
</TouchableOpacity>
</View>
{/* ========================================================================
* FOOTER - Security and information message
* ======================================================================== */}
<View style={styles.footer}>
<Text style={styles.footerText}>
Secure access to patient information and critical alerts
</Text>
</View>
</View>
</ScrollView>
</KeyboardAvoidingView>
</TouchableWithoutFeedback>
);
};
// ============================================================================
// STYLES SECTION
// ============================================================================
const styles = StyleSheet.create({
// Main container
container: {
flex: 1,
backgroundColor: theme.colors.background,
},
// Content wrapper
content: {
flex: 1,
justifyContent: 'center',
padding: theme.spacing.lg,
},
// Header section
header: {
alignItems: 'center',
marginBottom: theme.spacing.xxl,
},
// App title
title: {
fontSize: theme.typography.fontSize.displayMedium,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.primary,
marginBottom: theme.spacing.sm,
textAlign: 'center',
},
// App subtitle
subtitle: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
textAlign: 'center',
},
// Form container
formContainer: {
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 for password visibility
eyeIcon: {
padding: theme.spacing.sm,
},
// Base button styling
button: {
paddingVertical: theme.spacing.md,
paddingHorizontal: theme.spacing.lg,
borderRadius: theme.borderRadius.medium,
alignItems: 'center',
marginBottom: theme.spacing.md,
},
// Login button
loginButton: {
backgroundColor: theme.colors.primary,
...theme.shadows.primary,
},
// Sign up button
signUpButton: {
backgroundColor: theme.colors.background,
borderWidth: 1,
borderColor: theme.colors.primary,
},
// Disabled button
buttonDisabled: {
opacity: 0.6,
},
// Button text
buttonText: {
color: theme.colors.background,
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.medium,
},
// Sign up button text
signUpButtonText: {
color: theme.colors.primary,
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.medium,
},
// Loading container
loadingContainer: {
flexDirection: 'row',
alignItems: 'center',
},
// Divider
divider: {
flexDirection: 'row',
alignItems: 'center',
marginVertical: theme.spacing.lg,
},
// Divider line
dividerLine: {
flex: 1,
height: 1,
backgroundColor: theme.colors.border,
},
// Divider text
dividerText: {
marginHorizontal: theme.spacing.md,
color: theme.colors.textSecondary,
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
},
// Footer
footer: {
alignItems: 'center',
},
// Footer text
footerText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
textAlign: 'center',
},
// Image container
imageContainer: {
alignItems: 'center',
marginBottom: theme.spacing.xl,
},
// Image
image: {
width: '100%',
height: 150,
},
});
export default LoginScreen;
/*
* End of File: LoginScreen.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/