Files
info-target-mobile/screens/auth/login/ui/LoginForm.tsx
Samandar Turgunboyev 124798419b fitst commit
2026-01-28 18:26:50 +05:00

243 lines
5.7 KiB
TypeScript

import { formatPhone, normalizeDigits } from '@/constants/formatPhone';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Check } from 'lucide-react-native';
import { useCallback, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
ActivityIndicator,
Animated,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
import PhonePrefix from './PhonePrefix';
import { UseLoginForm } from './UseLoginForm';
export default function LoginForm() {
const [focused, setFocused] = useState(false);
const scaleAnim = useRef(new Animated.Value(1)).current;
const { phone, setPhone, submit, loading, error } = UseLoginForm();
console.log(error);
const { t } = useTranslation();
const handleChange = useCallback(
(text: string) => {
setPhone(normalizeDigits(text));
},
[setPhone]
);
const handlePressIn = () => {
Animated.spring(scaleAnim, {
toValue: 0.96,
friction: 7,
useNativeDriver: true,
}).start();
};
const handlePressOut = () => {
Animated.spring(scaleAnim, {
toValue: 1,
friction: 7,
useNativeDriver: true,
}).start();
};
const isComplete = phone.length === 9;
const hasError = !!error;
return (
<View style={styles.form}>
<Text style={styles.label}>{t('Telefon raqami')}</Text>
<View
style={[
styles.inputContainer,
focused && styles.inputFocused,
hasError && styles.inputError,
isComplete && styles.inputComplete,
]}
>
<PhonePrefix focused={focused} />
<TextInput
value={formatPhone(phone)}
onChangeText={handleChange}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
keyboardType="phone-pad"
placeholder="90 123 45 67"
placeholderTextColor="#94a3b8"
style={styles.input}
maxLength={12}
/>
{isComplete && (
<View style={styles.iconCheck}>
<Check size={18} color="#10b981" strokeWidth={3} />
</View>
)}
</View>
{phone.length > 0 && (
<View style={styles.progressContainer}>
<View style={styles.progressBar}>
<View
style={[
styles.progressFill,
{ width: `${Math.min((phone.length / 9) * 100, 100)}%` },
]}
/>
</View>
<Text style={styles.progressText}>{phone.length}/9</Text>
</View>
)}
{error && (
<View style={styles.errorContainer}>
<Text style={styles.errorText}>{error}</Text>
</View>
)}
<Animated.View style={{ transform: [{ scale: scaleAnim }] }}>
<TouchableOpacity
activeOpacity={0.9}
disabled={loading || !isComplete}
onPress={async () => {
const fullPhone = `998${phone}`;
await AsyncStorage.setItem('phone', fullPhone);
await AsyncStorage.setItem('userType', 'legal_entity');
submit();
}}
onPressIn={handlePressIn}
onPressOut={handlePressOut}
style={[styles.button, (loading || !isComplete) && styles.buttonDisabled]}
>
{loading ? (
<ActivityIndicator color="#ffffff" size="small" />
) : (
<Text style={styles.buttonText}>{t('Tasdiqlash kodini yuborish')}</Text>
)}
</TouchableOpacity>
</Animated.View>
</View>
);
}
const styles = StyleSheet.create({
form: {
gap: 16,
},
label: {
fontSize: 14,
fontWeight: '600',
color: '#475569',
marginBottom: 4,
letterSpacing: 0.2,
},
inputContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#ffffff',
borderRadius: 14,
borderWidth: 2,
borderColor: '#e2e8f0',
paddingHorizontal: 16,
height: 56,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.05,
shadowRadius: 8,
elevation: 2,
},
inputFocused: {
borderColor: '#3b82f6',
shadowColor: '#3b82f6',
shadowOpacity: 0.15,
shadowRadius: 12,
elevation: 4,
},
inputError: {
borderColor: '#ef4444',
backgroundColor: '#fef2f2',
},
inputComplete: {
borderColor: '#10b981',
backgroundColor: '#f0fdf4',
},
input: {
flex: 1,
fontSize: 16,
fontWeight: '500',
color: '#0f172a',
letterSpacing: 0.5,
},
iconCheck: {
width: 28,
height: 28,
borderRadius: 14,
backgroundColor: '#d1fae5',
alignItems: 'center',
justifyContent: 'center',
},
progressContainer: {
flexDirection: 'row',
alignItems: 'center',
gap: 10,
},
progressBar: {
flex: 1,
height: 3,
backgroundColor: '#e2e8f0',
borderRadius: 2,
overflow: 'hidden',
},
progressFill: {
height: '100%',
backgroundColor: '#3b82f6',
borderRadius: 2,
},
progressText: {
fontSize: 12,
color: '#64748b',
fontWeight: '600',
},
errorContainer: {
marginTop: -8,
},
errorText: {
color: '#ef4444',
fontSize: 13,
fontWeight: '500',
},
button: {
height: 56,
backgroundColor: '#3b82f6',
borderRadius: 14,
alignItems: 'center',
justifyContent: 'center',
shadowColor: '#3b82f6',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 12,
elevation: 6,
},
buttonDisabled: {
opacity: 0.5,
shadowOpacity: 0.1,
},
buttonText: {
color: '#ffffff',
fontSize: 16,
fontWeight: '700',
letterSpacing: 0.5,
},
scrollContent: {
flexGrow: 1,
paddingHorizontal: 24,
paddingBottom: 40,
},
});