native_convex_CRM/app/features/login/login.screen.tsx
2026-07-22 13:33:19 +05:30

285 lines
9.3 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import {
Text,
View,
TouchableOpacity,
KeyboardAvoidingView,
Platform,
ScrollView,
ImageBackground,
ActivityIndicator,
StatusBar,
} from 'react-native';
import { useNavigation } from '@react-navigation/native';
import Icon from 'react-native-vector-icons/Ionicons';
import { getStyles } from './login.styles';
import { useTheme } from '@theme';
import { useAppDispatch, useAppSelector, RootState, login } from '@store';
import { LoginRequest } from '@interfaces';
import { NotificationService } from '@services';
import { backGroundImage } from '@utils';
import { FormInput } from '@components';
import config from 'react-native-config';
import AsyncStorage from '@react-native-async-storage/async-storage';
export const LoginScreen = () => {
const dispatch = useAppDispatch();
const navigation = useNavigation<any>();
const { theme: colors, isDark } = useTheme();
const styles = getStyles(colors, isDark);
const [tenancy, setTenancy] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [showPassword, setShowPassword] = useState(false);
const [focusedField, setFocusedField] = useState<string | null>(null);
const [localError, setLocalError] = useState('');
const { loginLoading, loginError, loginSuccess, token, user_data } =
useAppSelector((state: RootState) => state.auth);
const handleLogin = async () => {
setLocalError('');
if (!tenancy.trim()) {
setLocalError('Tenancy name is required');
return;
}
if (!email.trim()) {
setLocalError('Email address is required');
return;
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email.trim())) {
setLocalError('Please enter a valid email address');
return;
}
if (!password.trim()) {
setLocalError('Password is required');
return;
}
const deviceToken = await NotificationService.getFCMToken();
console.log('deviceToken-', deviceToken)
const payload: LoginRequest = {
email,
password,
device_token: deviceToken ?? '',
};
console.log('payload-', payload)
console.log('tenancy-', config.BASE_URL)
if (tenancy == config.BASE_URL) {
try {
await AsyncStorage.setItem('base_url', tenancy);
await AsyncStorage.setItem('email', email);
await AsyncStorage.setItem('password', password);
} catch (e) {
console.error('Failed to save login credentials to AsyncStorage', e);
}
await dispatch(login(payload));
} else {
setLocalError('Invalid tenancy name');
}
};
useEffect(() => {
const loadCredentials = async () => {
try {
const savedBaseUrl = await AsyncStorage.getItem('base_url');
const savedEmail = await AsyncStorage.getItem('email');
const savedPassword = await AsyncStorage.getItem('password');
if (savedBaseUrl) setTenancy(savedBaseUrl);
if (savedEmail) setEmail(savedEmail);
if (savedPassword) setPassword(savedPassword);
} catch (e) {
console.error('Failed to load login credentials from AsyncStorage', e);
}
};
loadCredentials();
}, []);
useEffect(() => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (
tenancy.trim() &&
email.trim() &&
emailRegex.test(email.trim()) &&
password.trim()
) {
setLocalError('');
}
}, [tenancy, email, password]);
useEffect(() => {
if (loginSuccess && token && user_data) {
navigation.reset({
index: 0,
routes: [{ name: 'DrawerStack' }],
});
}
}, [loginSuccess, token, user_data, navigation]);
return (
<ImageBackground
source={backGroundImage}
style={styles.backgroundImage}
resizeMode="cover"
>
<StatusBar
barStyle={isDark ? 'light-content' : 'dark-content'}
backgroundColor="transparent"
translucent
/>
<View style={styles.overlay}>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
style={styles.container}
>
<ScrollView
contentContainerStyle={styles.scrollContainer}
keyboardShouldPersistTaps="handled"
showsVerticalScrollIndicator={false}
bounces={false}
>
{/* Top Area Spacer displaying background illustration */}
<View style={styles.topSpacer} />
{/* Bottom Sheet Form Card */}
<View style={styles.bottomSheetCard}>
<View style={styles.sheetHandle} />
<View style={styles.cardHeader}>
<Text style={styles.welcomeTitle}>Sign In</Text>
<Text style={styles.welcomeSubtitle}>
Enter your credentials to continue
</Text>
</View>
{localError || loginError ? (
<View style={styles.errorBanner}>
<Icon name="alert-circle-outline" size={20} color="#EF4444" />
<Text style={styles.errorText}>{localError || loginError}</Text>
</View>
) : null}
{/* Tenancy Field */}
<FormInput
label="Tenancy Name"
value={tenancy}
onChangeText={setTenancy}
placeholder="company-name"
autoCapitalize="none"
autoCorrect={false}
containerStyle={styles.fieldContainer}
inputContainerStyle={[
styles.inputWrapper,
focusedField === 'tenancy' && styles.inputWrapperFocused,
]}
onFocus={() => setFocusedField('tenancy')}
onBlur={() => setFocusedField(null)}
leftIcon={
<Icon
name="business-outline"
size={20}
color={
focusedField === 'tenancy'
? colors.icon
: colors.textSecondary
}
/>
}
/>
{/* Email Field */}
<FormInput
label="Email Address"
value={email}
onChangeText={setEmail}
placeholder="name@company.com"
keyboardType="email-address"
autoCapitalize="none"
autoCorrect={false}
containerStyle={styles.fieldContainer}
inputContainerStyle={[
styles.inputWrapper,
focusedField === 'email' && styles.inputWrapperFocused,
]}
onFocus={() => setFocusedField('email')}
onBlur={() => setFocusedField(null)}
leftIcon={
<Icon
name="mail-outline"
size={20}
color={
focusedField === 'email'
? colors.icon
: colors.textSecondary
}
/>
}
/>
{/* Password Field */}
<FormInput
label="Password"
value={password}
onChangeText={setPassword}
placeholder="••••••••"
secureTextEntry={!showPassword}
autoCapitalize="none"
autoCorrect={false}
containerStyle={styles.fieldContainer}
inputContainerStyle={[
styles.inputWrapper,
focusedField === 'password' && styles.inputWrapperFocused,
]}
onFocus={() => setFocusedField('password')}
onBlur={() => setFocusedField(null)}
leftIcon={
<Icon
name="lock-closed-outline"
size={20}
color={
focusedField === 'password'
? colors.icon
: colors.textSecondary
}
/>
}
rightIcon={
<TouchableOpacity
onPress={() => setShowPassword(!showPassword)}
style={styles.eyeIconButton}
activeOpacity={0.7}
>
<Icon
name={showPassword ? 'eye-off-outline' : 'eye-outline'}
size={20}
color={colors.textMuted}
/>
</TouchableOpacity>
}
/>
{/* Sign In Button */}
<TouchableOpacity
style={[styles.button, loginLoading && styles.buttonDisabled]}
activeOpacity={0.85}
onPress={handleLogin}
disabled={loginLoading}
>
{loginLoading ? (
<ActivityIndicator size="small" color="#FFFFFF" />
) : (
<Text style={styles.buttonText}>Sign In</Text>
)}
</TouchableOpacity>
</View>
</ScrollView>
</KeyboardAvoidingView>
</View>
</ImageBackground>
);
};