Files
Caregiver/src/screens/LoginScreen.tsx
T

233 lines
6.7 KiB
TypeScript

import React, { useState } from 'react';
import { View, StyleSheet, KeyboardAvoidingView, Platform, ScrollView, TouchableOpacity } from 'react-native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { RootStackParamList } from '../navigation/AppNavigator';
import { Typography } from '../components/ui/Typography';
import { Input } from '../components/ui/Input';
import { OtpInput } from '../components/ui/OtpInput';
import { Button } from '../components/ui/Button';
import { authService } from '../services/authService';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { HeartHandshake } from 'lucide-react-native';
type LoginScreenNavigationProp = NativeStackNavigationProp<RootStackParamList, 'Login'>;
interface LoginScreenProps {
navigation: LoginScreenNavigationProp;
}
export const LoginScreen: React.FC<LoginScreenProps> = ({ navigation }) => {
const [phone, setPhone] = useState('+91');
const [otp, setOtp] = useState('');
const [step, setStep] = useState<'PHONE' | 'OTP'>('PHONE');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
const handleLogin = async () => {
if (step === 'PHONE') {
if (!phone || phone.length < 10) {
setError('Please enter a valid phone number');
return;
}
setIsLoading(true);
setError('');
try {
const response = await authService.login(phone);
console.log('OTP sent:', response);
setStep('OTP');
} catch (err: any) {
console.error('Login failed:', err);
setError('Failed to send OTP. Please check your number and try again.');
} finally {
setIsLoading(false);
}
} else {
if (!otp || otp.length < 4) {
setError('Please enter a valid OTP');
return;
}
setIsLoading(true);
setError('');
try {
const response = await authService.verifyOtp(phone, otp);
console.log('OTP verified successfully:', response);
// Save the token
if (response.data && response.data.accessToken) {
await AsyncStorage.setItem('token', response.data.accessToken);
} else if (response.token) {
await AsyncStorage.setItem('token', response.token);
}
navigation.replace('MainTabs');
} catch (err: any) {
console.error('OTP verification failed:', err);
setError('Invalid OTP. Please try again.');
} finally {
setIsLoading(false);
}
}
};
return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
>
<ScrollView contentContainerStyle={styles.scrollContent} keyboardShouldPersistTaps="handled">
<View style={styles.formContainer}>
<View style={styles.card}>
<View style={styles.logoContainer}>
<View style={styles.iconWrapper}>
<HeartHandshake color="#4F46E5" size={40} />
</View>
</View>
<View style={styles.header}>
<Typography variant="h1" weight="bold" style={styles.title}>
Caregiver Portal
</Typography>
<Typography variant="body" style={styles.subtitle}>
Sign in to view your schedule, manage patient care, and stay connected.
</Typography>
</View>
<View style={styles.form}>
{error ? (
<Typography variant="caption" style={styles.errorText}>
{error}
</Typography>
) : null}
{step === 'PHONE' ? (
<Input
label="Phone Number"
labelStyle={{ fontWeight: 'bold', fontSize: 16 }}
placeholder="+917540032060"
keyboardType="phone-pad"
autoCapitalize="none"
value={phone}
onChangeText={setPhone}
/>
) : (
<View style={{ marginBottom: 16 }}>
<Typography variant="body" weight="bold" style={{ marginBottom: 12, fontSize: 16, color: '#111827' }}>
Enter OTP
</Typography>
<OtpInput
length={6}
value={otp}
onChange={setOtp}
error={!!error}
/>
</View>
)}
<Button
title={step === 'PHONE' ? "Get OTP" : "Verify & Log In"}
onPress={handleLogin}
loading={isLoading}
style={styles.loginButton}
/>
</View>
</View>
<View style={styles.footer}>
<Typography variant="body" style={styles.footerText}>
Don't have an account?{' '}
</Typography>
<TouchableOpacity>
<Typography variant="body" weight="bold" style={styles.signupText}>
Sign Up
</Typography>
</TouchableOpacity>
</View>
</View>
</ScrollView>
</KeyboardAvoidingView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#EEF2FF', // Beautiful light indigo background
},
scrollContent: {
flexGrow: 1,
paddingHorizontal: 24,
justifyContent: 'center',
},
formContainer: {
width: '100%',
maxWidth: 480,
alignSelf: 'center',
paddingVertical: 40,
},
card: {
backgroundColor: '#FFFFFF',
borderRadius: 32,
padding: 32,
shadowColor: '#4F46E5',
shadowOffset: { width: 0, height: 12 },
shadowOpacity: 0.1,
shadowRadius: 32,
elevation: 8,
borderWidth: 1,
borderColor: '#E0E7FF',
marginBottom: 32,
},
logoContainer: {
alignItems: 'center',
marginBottom: 24,
},
iconWrapper: {
width: 80,
height: 80,
borderRadius: 40,
backgroundColor: '#EEF2FF',
justifyContent: 'center',
alignItems: 'center',
borderWidth: 2,
borderColor: '#C7D2FE',
},
header: {
marginBottom: 32,
alignItems: 'center',
},
title: {
marginBottom: 8,
color: '#111827',
textAlign: 'center',
},
subtitle: {
color: '#6B7280',
lineHeight: 24,
textAlign: 'center',
},
form: {
marginBottom: 8,
},
errorText: {
color: '#DC2626', // Red
marginBottom: 16,
textAlign: 'center',
},
loginButton: {
width: '100%',
backgroundColor: '#1E3A8A', // Navy blue
},
footer: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
marginBottom: 40,
},
footerText: {
color: '#6B7280',
},
signupText: {
color: '#1E3A8A', // Navy blue
},
});