233 lines
6.7 KiB
TypeScript
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
|
|
},
|
|
});
|