Initial commit
This commit is contained in:
468
src/screens/auth/registeration/ui/FirstStep.tsx
Normal file
468
src/screens/auth/registeration/ui/FirstStep.tsx
Normal file
@@ -0,0 +1,468 @@
|
||||
'use client';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import {
|
||||
type RouteProp,
|
||||
useNavigation,
|
||||
useRoute,
|
||||
} from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { authApi } from 'api/auth';
|
||||
import { registerPayload } from 'api/auth/type';
|
||||
import { Branch, branchApi } from 'api/branch';
|
||||
import formatPhone from 'helpers/formatPhone';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Animated,
|
||||
ImageBackground,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
ScrollView,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import AntDesign from 'react-native-vector-icons/AntDesign';
|
||||
import SimpleLineIcons from 'react-native-vector-icons/SimpleLineIcons';
|
||||
import Logo from 'screens/../../assets/bootsplash/logo_512.png';
|
||||
import {
|
||||
FirstStepFormType,
|
||||
FirstStepSchema,
|
||||
} from 'screens/auth/registeration/lib/form';
|
||||
import LanguageSelector from 'screens/auth/select-language/SelectLang';
|
||||
import { RootStackParamList } from 'types/types';
|
||||
import { useUserStore } from '../lib/userstore';
|
||||
import { RegisterStyle } from './styled';
|
||||
|
||||
type LoginScreenNavigationProp = NativeStackNavigationProp<
|
||||
RootStackParamList,
|
||||
'Login'
|
||||
>;
|
||||
|
||||
const recommended = [
|
||||
{ label: 'Tanishim orqali', value: 'FRIEND' },
|
||||
{ label: 'Telegram orqali', value: 'TELEGRAM' },
|
||||
{ label: 'Instagram orqali', value: 'INSTAGRAM' },
|
||||
{ label: 'Facebook orqali', value: 'FACEBOOK' },
|
||||
];
|
||||
|
||||
const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const [filialDropdownVisible, setFilialDropdownVisible] = useState(false);
|
||||
const [error, setError] = useState<string>();
|
||||
const { setUser } = useUserStore(state => state);
|
||||
const { data: branchList } = useQuery({
|
||||
queryKey: ['branchList'],
|
||||
queryFn: branchApi.branchList,
|
||||
});
|
||||
|
||||
const { mutate, isPending } = useMutation({
|
||||
mutationFn: (payload: registerPayload) => authApi.register(payload),
|
||||
onSuccess: res => {
|
||||
onNext();
|
||||
},
|
||||
onError: err => {
|
||||
console.dir(err);
|
||||
|
||||
setError('Xatolik yuz berdi');
|
||||
},
|
||||
});
|
||||
|
||||
const [recommendedDropdownVisible, setRecommendedDropdownVisible] =
|
||||
useState(false);
|
||||
const [termsAccepted, setTermsAccepted] = useState(false);
|
||||
const [checkboxAnimation] = useState(new Animated.Value(0));
|
||||
const navigation = useNavigation<LoginScreenNavigationProp>();
|
||||
const [rawPhone, setRawPhone] = useState('+998');
|
||||
const route = useRoute<RouteProp<RootStackParamList, 'Register'>>();
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
getValues,
|
||||
} = useForm<FirstStepFormType>({
|
||||
resolver: zodResolver(FirstStepSchema),
|
||||
defaultValues: {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
recommend: '',
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (data: FirstStepFormType) => {
|
||||
setUser({
|
||||
firstName: data.firstName,
|
||||
lastName: data.lastName,
|
||||
phoneNumber: data.phoneNumber,
|
||||
});
|
||||
mutate(data);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (route.params?.termsAccepted) {
|
||||
setTermsAccepted(true);
|
||||
Animated.spring(checkboxAnimation, {
|
||||
toValue: 1,
|
||||
useNativeDriver: false,
|
||||
tension: 100,
|
||||
friction: 8,
|
||||
}).start();
|
||||
}
|
||||
}, [route.params]);
|
||||
|
||||
const navigateToTerms = () => {
|
||||
navigation.navigate('TermsAndConditions');
|
||||
setTermsAccepted(true);
|
||||
Animated.spring(checkboxAnimation, {
|
||||
toValue: 1,
|
||||
useNativeDriver: false,
|
||||
tension: 100,
|
||||
friction: 8,
|
||||
}).start();
|
||||
};
|
||||
|
||||
const toggleCheckbox = () => {
|
||||
if (!termsAccepted) {
|
||||
navigateToTerms();
|
||||
} else {
|
||||
setTermsAccepted(false);
|
||||
Animated.spring(checkboxAnimation, {
|
||||
toValue: 0,
|
||||
useNativeDriver: false,
|
||||
tension: 100,
|
||||
friction: 8,
|
||||
}).start();
|
||||
}
|
||||
};
|
||||
return (
|
||||
<ImageBackground
|
||||
source={Logo}
|
||||
style={RegisterStyle.background}
|
||||
resizeMode="contain"
|
||||
imageStyle={{
|
||||
opacity: 0.3,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
transform: [{ scale: 1.5 }],
|
||||
}}
|
||||
>
|
||||
<SafeAreaView style={{ flex: 1 }}>
|
||||
<View style={RegisterStyle.langContainer}>
|
||||
<TouchableOpacity onPress={() => navigation.navigate('select-auth')}>
|
||||
<SimpleLineIcons name="arrow-left" color="#000" size={20} />
|
||||
</TouchableOpacity>
|
||||
<LanguageSelector />
|
||||
</View>
|
||||
<KeyboardAvoidingView
|
||||
style={RegisterStyle.container}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
>
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
style={RegisterStyle.content}
|
||||
>
|
||||
<View style={RegisterStyle.scrollContainer}>
|
||||
<View style={RegisterStyle.loginContainer}>
|
||||
<Text style={RegisterStyle.title}>
|
||||
{t("Ro'yxatdan o'tish")}
|
||||
</Text>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="firstName"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<View>
|
||||
<Text style={RegisterStyle.label}>{t('Ism')}</Text>
|
||||
<TextInput
|
||||
style={RegisterStyle.input}
|
||||
placeholder={t('Ismingiz')}
|
||||
onChangeText={onChange}
|
||||
value={value}
|
||||
placeholderTextColor={'#D8DADC'}
|
||||
/>
|
||||
{errors.firstName && (
|
||||
<Text style={RegisterStyle.errorText}>
|
||||
{t(errors.firstName.message || '')}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="lastName"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<View>
|
||||
<Text style={RegisterStyle.label}>{t('Familiya')}</Text>
|
||||
<TextInput
|
||||
style={RegisterStyle.input}
|
||||
placeholder={t('Familiyangiz')}
|
||||
placeholderTextColor={'#D8DADC'}
|
||||
onChangeText={onChange}
|
||||
value={value}
|
||||
/>
|
||||
{errors.lastName && (
|
||||
<Text style={RegisterStyle.errorText}>
|
||||
{t(errors.lastName.message || '')}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="phoneNumber"
|
||||
render={({ field: { onChange } }) => {
|
||||
const formatted = formatPhone(rawPhone);
|
||||
return (
|
||||
<View>
|
||||
<Text style={RegisterStyle.label}>
|
||||
{t('Telefon raqami')}
|
||||
</Text>
|
||||
<TextInput
|
||||
keyboardType="numeric"
|
||||
placeholder="+998 __ ___-__-__"
|
||||
value={formatted}
|
||||
onChangeText={text => {
|
||||
const digits = text.replace(/\D/g, '').slice(0, 12);
|
||||
const full = digits.startsWith('998')
|
||||
? digits
|
||||
: `998${digits}`;
|
||||
setRawPhone(full);
|
||||
onChange(full);
|
||||
}}
|
||||
style={RegisterStyle.input}
|
||||
placeholderTextColor="#D8DADC"
|
||||
maxLength={17}
|
||||
/>
|
||||
{errors.phoneNumber && (
|
||||
<Text style={RegisterStyle.errorText}>
|
||||
{t(errors.phoneNumber.message || '')}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="branchId"
|
||||
render={({ field: { value } }) => (
|
||||
<View style={{ position: 'relative' }}>
|
||||
<Text style={RegisterStyle.label}>{t('Filial')}</Text>
|
||||
<View style={RegisterStyle.input}>
|
||||
<TouchableOpacity
|
||||
style={RegisterStyle.selector}
|
||||
onPress={() =>
|
||||
setFilialDropdownVisible(prev => !prev)
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
value
|
||||
? { color: '#000' }
|
||||
: RegisterStyle.selectedText
|
||||
}
|
||||
>
|
||||
{branchList?.find(e => e.id === value)?.name ||
|
||||
t('Filialni tanlang...')}
|
||||
</Text>
|
||||
<SimpleLineIcons
|
||||
name={
|
||||
filialDropdownVisible ? 'arrow-up' : 'arrow-down'
|
||||
}
|
||||
color="#000"
|
||||
size={14}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{filialDropdownVisible && (
|
||||
<View
|
||||
style={[RegisterStyle.dropdown, { maxHeight: 200 }]}
|
||||
>
|
||||
<ScrollView nestedScrollEnabled>
|
||||
{branchList &&
|
||||
branchList.map((item: Branch) => (
|
||||
<TouchableOpacity
|
||||
key={item.id}
|
||||
style={RegisterStyle.dropdownItem}
|
||||
onPress={() => {
|
||||
setValue('branchId', item.id);
|
||||
setFilialDropdownVisible(false);
|
||||
}}
|
||||
>
|
||||
<Text style={RegisterStyle.dropdownItemText}>
|
||||
{item.name}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
)}
|
||||
{errors.branchId && (
|
||||
<Text style={RegisterStyle.errorText}>
|
||||
{t(errors.branchId.message || '')}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="recommend"
|
||||
render={({ field: { value } }) => (
|
||||
<View style={{ position: 'relative' }}>
|
||||
<Text style={RegisterStyle.label}>
|
||||
{t('Bizni qaerdan topdingiz?')}
|
||||
</Text>
|
||||
<View style={RegisterStyle.input}>
|
||||
<TouchableOpacity
|
||||
style={RegisterStyle.selector}
|
||||
onPress={() =>
|
||||
setRecommendedDropdownVisible(prev => !prev)
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
value
|
||||
? { color: '#000' }
|
||||
: RegisterStyle.selectedText
|
||||
}
|
||||
>
|
||||
{t(
|
||||
recommended.find(e => e.value === value)?.label ||
|
||||
'Bizni kim tavsiya qildi...',
|
||||
)}
|
||||
</Text>
|
||||
<SimpleLineIcons
|
||||
name={
|
||||
recommendedDropdownVisible
|
||||
? 'arrow-up'
|
||||
: 'arrow-down'
|
||||
}
|
||||
color="#000"
|
||||
size={14}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{recommendedDropdownVisible && (
|
||||
<View
|
||||
style={[RegisterStyle.dropdown, { maxHeight: 200 }]}
|
||||
>
|
||||
<ScrollView nestedScrollEnabled>
|
||||
{recommended.map((item, index) => (
|
||||
<TouchableOpacity
|
||||
key={index}
|
||||
style={RegisterStyle.dropdownItem}
|
||||
onPress={() => {
|
||||
setValue('recommend', item.value);
|
||||
setRecommendedDropdownVisible(false);
|
||||
}}
|
||||
>
|
||||
<Text style={RegisterStyle.dropdownItemText}>
|
||||
{t(item.label)}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
)}
|
||||
{errors.recommend && (
|
||||
<Text style={RegisterStyle.errorText}>
|
||||
{t(errors.recommend.message || '')}
|
||||
</Text>
|
||||
)}
|
||||
{error && (
|
||||
<Text style={[RegisterStyle.errorText]}>
|
||||
{t(error)}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
<View style={RegisterStyle.termsContainer}>
|
||||
<TouchableOpacity
|
||||
style={RegisterStyle.checkboxContainer}
|
||||
onPress={toggleCheckbox}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Animated.View
|
||||
style={[
|
||||
RegisterStyle.checkbox,
|
||||
termsAccepted && RegisterStyle.checkboxChecked,
|
||||
{
|
||||
transform: [
|
||||
{
|
||||
scale: checkboxAnimation.interpolate({
|
||||
inputRange: [0, 0.5, 1],
|
||||
outputRange: [1, 1.1, 1],
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
>
|
||||
{termsAccepted && (
|
||||
<Animated.View
|
||||
style={{
|
||||
opacity: checkboxAnimation,
|
||||
transform: [
|
||||
{
|
||||
scale: checkboxAnimation,
|
||||
},
|
||||
],
|
||||
}}
|
||||
>
|
||||
<AntDesign name="check" color="#fff" size={20} />
|
||||
</Animated.View>
|
||||
)}
|
||||
</Animated.View>
|
||||
<View style={RegisterStyle.termsTextContainer}>
|
||||
<Text style={RegisterStyle.termsText}>
|
||||
<Text>{t('Foydalanish shartlari')}</Text>
|
||||
<Text> {t('bilan tanishib chiqdim!')}</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
onPress={handleSubmit(onSubmit)}
|
||||
style={[
|
||||
RegisterStyle.button,
|
||||
(!termsAccepted || isPending) &&
|
||||
RegisterStyle.buttonDisabled,
|
||||
]}
|
||||
disabled={!termsAccepted || isPending}
|
||||
>
|
||||
{isPending ? (
|
||||
<ActivityIndicator color="#fff" />
|
||||
) : (
|
||||
<Text
|
||||
style={[
|
||||
RegisterStyle.btnText,
|
||||
(!termsAccepted || isPending) &&
|
||||
RegisterStyle.buttonTextDisabled,
|
||||
]}
|
||||
>
|
||||
{t('Davom etish')}
|
||||
</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
</SafeAreaView>
|
||||
</ImageBackground>
|
||||
);
|
||||
};
|
||||
|
||||
export default FirstStep;
|
||||
Reference in New Issue
Block a user