added login modal
This commit is contained in:
1
App.tsx
1
App.tsx
@@ -163,6 +163,7 @@ export default function App() {
|
|||||||
const [seen, token, lang] = await Promise.all([
|
const [seen, token, lang] = await Promise.all([
|
||||||
AsyncStorage.getItem('hasSeenOnboarding'),
|
AsyncStorage.getItem('hasSeenOnboarding'),
|
||||||
AsyncStorage.getItem('token'),
|
AsyncStorage.getItem('token'),
|
||||||
|
|
||||||
AsyncStorage.getItem('language'),
|
AsyncStorage.getItem('language'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -85,8 +85,8 @@ android {
|
|||||||
applicationId "uz.felix.cpost"
|
applicationId "uz.felix.cpost"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 5
|
versionCode 6
|
||||||
versionName "0.5"
|
versionName "0.6"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
114
src/components/ErrorNotification.tsx
Normal file
114
src/components/ErrorNotification.tsx
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import {
|
||||||
|
Animated,
|
||||||
|
Modal,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
TouchableOpacity,
|
||||||
|
View,
|
||||||
|
} from 'react-native';
|
||||||
|
import CloseIcon from 'svg/Close';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
visible: boolean;
|
||||||
|
error: string;
|
||||||
|
setVisible: (val: boolean) => void;
|
||||||
|
duration?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ErrorNotification: React.FC<Props> = ({
|
||||||
|
visible,
|
||||||
|
error,
|
||||||
|
setVisible,
|
||||||
|
duration = 3000,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const fadeAnim = useRef(new Animated.Value(0)).current;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible) {
|
||||||
|
// fade in
|
||||||
|
Animated.timing(fadeAnim, {
|
||||||
|
toValue: 1,
|
||||||
|
duration: 300,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
// auto-hide after duration
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
Animated.timing(fadeAnim, {
|
||||||
|
toValue: 0,
|
||||||
|
duration: 300,
|
||||||
|
useNativeDriver: true,
|
||||||
|
});
|
||||||
|
}, duration);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}
|
||||||
|
}, [visible]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal transparent visible={visible} animationType="none">
|
||||||
|
<View style={styles.overlay}>
|
||||||
|
<Animated.View style={[styles.container, { opacity: fadeAnim }]}>
|
||||||
|
<Text style={styles.title}>{t('Xatolik yuz berdi')}</Text>
|
||||||
|
<Text style={styles.message}>{t(error)}</Text>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.closeBtn}
|
||||||
|
onPress={() =>
|
||||||
|
Animated.timing(fadeAnim, {
|
||||||
|
toValue: 0,
|
||||||
|
duration: 200,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}).start(() => setVisible(false))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<CloseIcon color="#fff" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</Animated.View>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
overlay: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.4)',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
width: '80%',
|
||||||
|
backgroundColor: 'white',
|
||||||
|
padding: 20,
|
||||||
|
borderRadius: 12,
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOpacity: 0.25,
|
||||||
|
shadowRadius: 6,
|
||||||
|
shadowOffset: { width: 0, height: 3 },
|
||||||
|
elevation: 5,
|
||||||
|
position: 'relative',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: 'red',
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#333',
|
||||||
|
},
|
||||||
|
closeBtn: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 10,
|
||||||
|
right: 10,
|
||||||
|
backgroundColor: '#ff4d4f',
|
||||||
|
padding: 5,
|
||||||
|
borderRadius: 20,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ErrorNotification;
|
||||||
@@ -245,5 +245,8 @@
|
|||||||
"Aloqa uchun": "Для связи",
|
"Aloqa uchun": "Для связи",
|
||||||
"Email": "Электронная почта",
|
"Email": "Электронная почта",
|
||||||
"Telegram": "Телеграм",
|
"Telegram": "Телеграм",
|
||||||
"Roziman": "Согласен"
|
"Roziman": "Согласен",
|
||||||
|
"To'lov holati": "Статус оплаты",
|
||||||
|
"Bekor qilingan": "Отменено",
|
||||||
|
"Ma'lumotlarni to'liq kiriting": "Введите полные данные"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -246,5 +246,8 @@
|
|||||||
"Aloqa uchun": "Aloqa uchun",
|
"Aloqa uchun": "Aloqa uchun",
|
||||||
"Email": "Email",
|
"Email": "Email",
|
||||||
"Telegram": "Telegram",
|
"Telegram": "Telegram",
|
||||||
"Roziman": "Roziman"
|
"Roziman": "Roziman",
|
||||||
|
"To'lov holati": "To'lov holati",
|
||||||
|
"Bekor qilingan": "Bekor qilingan",
|
||||||
|
"Ma'lumotlarni to'liq kiriting": "Ma'lumotlarni to'liq kiriting"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useMutation } from '@tanstack/react-query';
|
|||||||
import { authApi } from 'api/auth';
|
import { authApi } from 'api/auth';
|
||||||
import { otpPayload, resendPayload } from 'api/auth/type';
|
import { otpPayload, resendPayload } from 'api/auth/type';
|
||||||
import AppText from 'components/AppText';
|
import AppText from 'components/AppText';
|
||||||
|
import ErrorNotification from 'components/ErrorNotification';
|
||||||
import formatPhone from 'helpers/formatPhone';
|
import formatPhone from 'helpers/formatPhone';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -47,6 +48,8 @@ const Confirm = () => {
|
|||||||
const [canResend, setCanResend] = useState(false);
|
const [canResend, setCanResend] = useState(false);
|
||||||
const inputRefs = useRef<Array<TextInput | null>>([]);
|
const inputRefs = useRef<Array<TextInput | null>>([]);
|
||||||
const { phoneNumber } = useUserStore(state => state);
|
const { phoneNumber } = useUserStore(state => state);
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [error, setError] = useState<string>('Xatolik yuz berdi');
|
||||||
|
|
||||||
// const app = getApp();
|
// const app = getApp();
|
||||||
// const messaging = getMessaging(app);
|
// const messaging = getMessaging(app);
|
||||||
@@ -76,11 +79,11 @@ const Confirm = () => {
|
|||||||
onSuccess: async res => {
|
onSuccess: async res => {
|
||||||
await AsyncStorage.setItem('token', res.data.accessToken);
|
await AsyncStorage.setItem('token', res.data.accessToken);
|
||||||
navigation.navigate('Home');
|
navigation.navigate('Home');
|
||||||
setErrorConfirm(null);
|
setVisible(false);
|
||||||
console.log(res);
|
|
||||||
},
|
},
|
||||||
onError: (err: any) => {
|
onError: (err: any) => {
|
||||||
setErrorConfirm(err?.response?.data?.message || 'Xato yuz berdi');
|
setError(err?.response?.data?.message || 'Xatolik yuz berdi');
|
||||||
|
setVisible(true);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -91,10 +94,11 @@ const Confirm = () => {
|
|||||||
setCanResend(false);
|
setCanResend(false);
|
||||||
setCode(new Array(OTP_LENGTH).fill(''));
|
setCode(new Array(OTP_LENGTH).fill(''));
|
||||||
inputRefs.current[0]?.focus();
|
inputRefs.current[0]?.focus();
|
||||||
setErrorConfirm(null);
|
setVisible(false);
|
||||||
},
|
},
|
||||||
onError: (err: any) => {
|
onError: (err: any) => {
|
||||||
setErrorConfirm(err?.response?.data?.message || 'Xato yuz berdi');
|
setError(err?.response?.data?.message || 'Xatolik yuz berdi');
|
||||||
|
setVisible(true);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -165,6 +169,11 @@ const Confirm = () => {
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
</View>
|
</View>
|
||||||
|
<ErrorNotification
|
||||||
|
setVisible={setVisible}
|
||||||
|
error={error}
|
||||||
|
visible={visible}
|
||||||
|
/>
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
style={styles.container}
|
style={styles.container}
|
||||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useMutation } from '@tanstack/react-query';
|
|||||||
import { authApi } from 'api/auth';
|
import { authApi } from 'api/auth';
|
||||||
import { loginPayload } from 'api/auth/type';
|
import { loginPayload } from 'api/auth/type';
|
||||||
import AppText from 'components/AppText';
|
import AppText from 'components/AppText';
|
||||||
|
import ErrorNotification from 'components/ErrorNotification';
|
||||||
import formatPhone from 'helpers/formatPhone';
|
import formatPhone from 'helpers/formatPhone';
|
||||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
@@ -38,11 +39,11 @@ const Filial = [
|
|||||||
const Login = () => {
|
const Login = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const passportNumberRef = useRef<TextInput>(null);
|
const passportNumberRef = useRef<TextInput>(null);
|
||||||
const [filialDropdownVisible, setFilialDropdownVisible] = useState(false);
|
|
||||||
const navigation = useNavigation<NativeStackNavigationProp<any>>();
|
const navigation = useNavigation<NativeStackNavigationProp<any>>();
|
||||||
const { setUser, setExpireTime } = useUserStore(state => state);
|
const { setUser, setExpireTime } = useUserStore(state => state);
|
||||||
const [error, setError] = useState<string>();
|
const [error, setError] = useState<string>('Xatolik yuz berdi');
|
||||||
const [rawPhone, setRawPhone] = useState('+998');
|
const [rawPhone, setRawPhone] = useState('+998');
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
// const { data: branchList } = useQuery({
|
// const { data: branchList } = useQuery({
|
||||||
// queryKey: ['branchList'],
|
// queryKey: ['branchList'],
|
||||||
// queryFn: branchApi.branchList,
|
// queryFn: branchApi.branchList,
|
||||||
@@ -84,9 +85,14 @@ const Login = () => {
|
|||||||
navigation.navigate('Login-Confirm');
|
navigation.navigate('Login-Confirm');
|
||||||
setExpireTime(res.data.expireTime);
|
setExpireTime(res.data.expireTime);
|
||||||
},
|
},
|
||||||
onError: err => {
|
onError: (err: any) => {
|
||||||
setError('Xatolik yuz berdi');
|
setVisible(true);
|
||||||
console.dir(err);
|
|
||||||
|
setError(
|
||||||
|
err?.response?.data?.message ||
|
||||||
|
err?.response?.message ||
|
||||||
|
'Xatolik yuz berdi',
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -105,6 +111,18 @@ const Login = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = (data: LoginFormType) => {
|
const onSubmit = (data: LoginFormType) => {
|
||||||
|
// validation xatolarini tekshirish
|
||||||
|
if (errors.phone || errors.passportSeriya || errors.passportNumber) {
|
||||||
|
const firstError =
|
||||||
|
errors.phone?.message ||
|
||||||
|
errors.passportSeriya?.message ||
|
||||||
|
errors.passportNumber?.message ||
|
||||||
|
'Xatolik yuz berdi';
|
||||||
|
setError(firstError);
|
||||||
|
setVisible(true);
|
||||||
|
return; // navigation ishlamasligi uchun return
|
||||||
|
}
|
||||||
|
|
||||||
mutate({
|
mutate({
|
||||||
phoneNumber: data.phone,
|
phoneNumber: data.phone,
|
||||||
passportSerial: `${data.passportSeriya.toUpperCase()}${
|
passportSerial: `${data.passportSeriya.toUpperCase()}${
|
||||||
@@ -115,10 +133,18 @@ const Login = () => {
|
|||||||
deviceType: '',
|
deviceType: '',
|
||||||
deviceName: '',
|
deviceName: '',
|
||||||
});
|
});
|
||||||
// navigation.navigate('Login-Confirm');
|
|
||||||
setUser({
|
setUser({ phoneNumber: data.phone });
|
||||||
phoneNumber: data.phone,
|
};
|
||||||
});
|
|
||||||
|
const onErrorSubmit = (errors: any) => {
|
||||||
|
const firstError =
|
||||||
|
errors.phone?.message ||
|
||||||
|
errors.passportSeriya?.message ||
|
||||||
|
errors.passportNumber?.message ||
|
||||||
|
'Xatolik yuz berdi';
|
||||||
|
setError(firstError);
|
||||||
|
setVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBackNavigation = useCallback(() => {
|
const handleBackNavigation = useCallback(() => {
|
||||||
@@ -156,6 +182,15 @@ const Login = () => {
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{/* error modal start*/}
|
||||||
|
<ErrorNotification
|
||||||
|
setVisible={setVisible}
|
||||||
|
error={error}
|
||||||
|
visible={visible}
|
||||||
|
/>
|
||||||
|
{/* error modal end*/}
|
||||||
|
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
style={Loginstyle.container}
|
style={Loginstyle.container}
|
||||||
behavior={keyboardBehavior}
|
behavior={keyboardBehavior}
|
||||||
@@ -185,11 +220,6 @@ const Login = () => {
|
|||||||
placeholderTextColor="#D8DADC"
|
placeholderTextColor="#D8DADC"
|
||||||
maxLength={19} // +998 90 123-45-67 bo'lishi uchun
|
maxLength={19} // +998 90 123-45-67 bo'lishi uchun
|
||||||
/>
|
/>
|
||||||
{errors.phone && (
|
|
||||||
<AppText style={Loginstyle.errorText}>
|
|
||||||
{t(errors.phone.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
@@ -239,12 +269,6 @@ const Login = () => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
{(errors.passportSeriya || errors.passportNumber) && (
|
|
||||||
<AppText style={Loginstyle.errorText}>
|
|
||||||
{t(errors.passportSeriya?.message || '') ||
|
|
||||||
t(errors.passportNumber?.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* <Controller
|
{/* <Controller
|
||||||
@@ -313,7 +337,7 @@ const Login = () => {
|
|||||||
/> */}
|
/> */}
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={handleSubmit(onSubmit)}
|
onPress={handleSubmit(onSubmit, onErrorSubmit)}
|
||||||
style={Loginstyle.button}
|
style={Loginstyle.button}
|
||||||
>
|
>
|
||||||
{isPending ? (
|
{isPending ? (
|
||||||
@@ -324,6 +348,7 @@ const Login = () => {
|
|||||||
</AppText>
|
</AppText>
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ export const Loginstyle = StyleSheet.create({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
},
|
},
|
||||||
|
errorModal: {
|
||||||
|
width: '80%',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
padding: 20,
|
||||||
|
},
|
||||||
background: {
|
background: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ export const FirstStepSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const SecondStepSchema = z.object({
|
export const SecondStepSchema = z.object({
|
||||||
passportSeriya: z.string().length(2, '2 ta harf kerak'),
|
passportSeriya: z.string().length(2, { message: '2 ta harf kerak' }),
|
||||||
birthDate: z.string().min(8, 'Majburiy maydon'),
|
birthDate: z.string().min(8, { message: 'Majburiy maydon' }),
|
||||||
passportNumber: z.string().length(7, '7 ta raqam kerak'),
|
passportNumber: z.string().length(7, { message: '7 ta raqam kerak' }),
|
||||||
jshshir: z.string().length(14, '14 ta raqam kerak'),
|
jshshir: z.string().min(14, { message: '14 ta raqam kerak' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type FirstStepFormType = z.infer<typeof FirstStepSchema>;
|
export type FirstStepFormType = z.infer<typeof FirstStepSchema>;
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ import { useMutation } from '@tanstack/react-query';
|
|||||||
import { authApi } from 'api/auth';
|
import { authApi } from 'api/auth';
|
||||||
import { otpPayload, resendPayload } from 'api/auth/type';
|
import { otpPayload, resendPayload } from 'api/auth/type';
|
||||||
import AppText from 'components/AppText';
|
import AppText from 'components/AppText';
|
||||||
|
import ErrorNotification from 'components/ErrorNotification';
|
||||||
import formatPhone from 'helpers/formatPhone';
|
import formatPhone from 'helpers/formatPhone';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
@@ -22,7 +23,6 @@ import {
|
|||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import Logo from 'screens/../../assets/bootsplash/logo_512.png';
|
import Logo from 'screens/../../assets/bootsplash/logo_512.png';
|
||||||
import { useModalStore } from 'screens/auth/registeration/lib/modalStore';
|
|
||||||
import LanguageSelector from 'screens/auth/select-language/SelectLang';
|
import LanguageSelector from 'screens/auth/select-language/SelectLang';
|
||||||
import ArrowLeft from 'svg/ArrowLeft';
|
import ArrowLeft from 'svg/ArrowLeft';
|
||||||
import { RootStackParamList } from 'types/types';
|
import { RootStackParamList } from 'types/types';
|
||||||
@@ -49,6 +49,8 @@ const Confirm = ({
|
|||||||
const [errorConfirm, setErrorConfirm] = useState<string | null>(null);
|
const [errorConfirm, setErrorConfirm] = useState<string | null>(null);
|
||||||
const inputRefs = useRef<Array<TextInput | null>>([]);
|
const inputRefs = useRef<Array<TextInput | null>>([]);
|
||||||
const { phoneNumber } = useUserStore(state => state);
|
const { phoneNumber } = useUserStore(state => state);
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [error, setError] = useState<string>('Xatolik yuz berdi');
|
||||||
// const [firebaseToken, setFirebseToken] = useState<{
|
// const [firebaseToken, setFirebseToken] = useState<{
|
||||||
// fcmToken: string;
|
// fcmToken: string;
|
||||||
// deviceId: string;
|
// deviceId: string;
|
||||||
@@ -86,8 +88,8 @@ const Confirm = ({
|
|||||||
setErrorConfirm(null);
|
setErrorConfirm(null);
|
||||||
},
|
},
|
||||||
onError: (err: any) => {
|
onError: (err: any) => {
|
||||||
console.dir(err);
|
setError(err?.response?.data?.message || 'Xatolik yuz berdi');
|
||||||
setErrorConfirm(err?.response.data.message);
|
setVisible(true);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -101,26 +103,11 @@ const Confirm = ({
|
|||||||
setErrorConfirm(null);
|
setErrorConfirm(null);
|
||||||
},
|
},
|
||||||
onError: (err: any) => {
|
onError: (err: any) => {
|
||||||
setErrorConfirm(err?.response.data.message);
|
setError(err?.response?.data?.message || 'Xatolik yuz berdi');
|
||||||
|
setVisible(true);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const openModal = useModalStore(state => state.openModal);
|
|
||||||
useEffect(() => {
|
|
||||||
let interval: NodeJS.Timeout | null = null;
|
|
||||||
if (timer > 0) {
|
|
||||||
interval = setInterval(() => {
|
|
||||||
setTimer(prevTimer => prevTimer - 1);
|
|
||||||
}, 1000);
|
|
||||||
} else {
|
|
||||||
setCanResend(true);
|
|
||||||
if (interval) clearInterval(interval);
|
|
||||||
}
|
|
||||||
return () => {
|
|
||||||
if (interval) clearInterval(interval);
|
|
||||||
};
|
|
||||||
}, [timer]);
|
|
||||||
|
|
||||||
const handleCodeChange = (text: string, index: number) => {
|
const handleCodeChange = (text: string, index: number) => {
|
||||||
const newCode = [...code];
|
const newCode = [...code];
|
||||||
newCode[index] = text;
|
newCode[index] = text;
|
||||||
@@ -179,6 +166,11 @@ const Confirm = ({
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
</View>
|
</View>
|
||||||
|
<ErrorNotification
|
||||||
|
setVisible={setVisible}
|
||||||
|
error={error}
|
||||||
|
visible={visible}
|
||||||
|
/>
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
style={styles.container}
|
style={styles.container}
|
||||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { authApi } from 'api/auth';
|
|||||||
import { registerPayload } from 'api/auth/type';
|
import { registerPayload } from 'api/auth/type';
|
||||||
import { Branch, branchApi } from 'api/branch';
|
import { Branch, branchApi } from 'api/branch';
|
||||||
import AppText from 'components/AppText';
|
import AppText from 'components/AppText';
|
||||||
|
import ErrorNotification from 'components/ErrorNotification';
|
||||||
import formatPhone from 'helpers/formatPhone';
|
import formatPhone from 'helpers/formatPhone';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
@@ -57,7 +58,8 @@ const recommended = [
|
|||||||
const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [filialDropdownVisible, setFilialDropdownVisible] = useState(false);
|
const [filialDropdownVisible, setFilialDropdownVisible] = useState(false);
|
||||||
const [error, setError] = useState<string>();
|
const [error, setError] = useState<string>('Xatolik yuz berdi');
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
const { setUser } = useUserStore(state => state);
|
const { setUser } = useUserStore(state => state);
|
||||||
const { data: branchList } = useQuery({
|
const { data: branchList } = useQuery({
|
||||||
queryKey: ['branchList'],
|
queryKey: ['branchList'],
|
||||||
@@ -69,8 +71,14 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
onSuccess: res => {
|
onSuccess: res => {
|
||||||
onNext();
|
onNext();
|
||||||
},
|
},
|
||||||
onError: err => {
|
onError: (err: any) => {
|
||||||
setError('Xatolik yuz berdi');
|
setVisible(true);
|
||||||
|
|
||||||
|
setError(
|
||||||
|
err?.response?.data?.message ||
|
||||||
|
err?.response?.message ||
|
||||||
|
'Xatolik yuz berdi',
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -104,6 +112,27 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
const addressRef = useRef<TextInput>(null);
|
const addressRef = useRef<TextInput>(null);
|
||||||
|
|
||||||
const onSubmit = (data: FirstStepFormType) => {
|
const onSubmit = (data: FirstStepFormType) => {
|
||||||
|
if (
|
||||||
|
errors.address ||
|
||||||
|
errors.branchId ||
|
||||||
|
errors.firstName ||
|
||||||
|
errors.lastName ||
|
||||||
|
errors.phoneNumber ||
|
||||||
|
errors.recommend
|
||||||
|
) {
|
||||||
|
const firstError =
|
||||||
|
errors.address?.message ||
|
||||||
|
errors.branchId?.message ||
|
||||||
|
errors.firstName?.message ||
|
||||||
|
errors.lastName?.message ||
|
||||||
|
errors.phoneNumber?.message ||
|
||||||
|
errors.recommend?.message ||
|
||||||
|
'Xatolik yuz berdi';
|
||||||
|
setError(firstError);
|
||||||
|
setVisible(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setUser({
|
setUser({
|
||||||
firstName: data.firstName,
|
firstName: data.firstName,
|
||||||
lastName: data.lastName,
|
lastName: data.lastName,
|
||||||
@@ -123,6 +152,20 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onErrorSubmit = (errors: any) => {
|
||||||
|
const firstError =
|
||||||
|
errors.address?.message ||
|
||||||
|
errors.branchId?.message ||
|
||||||
|
errors.firstName?.message ||
|
||||||
|
errors.lastName?.message ||
|
||||||
|
errors.phoneNumber?.message ||
|
||||||
|
errors.recommend?.message;
|
||||||
|
setError(
|
||||||
|
firstError ? "Ma'lumotlarni to'liq kiriting" : 'Xatolik yuz berdi',
|
||||||
|
);
|
||||||
|
setVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (route.params?.termsAccepted) {
|
if (route.params?.termsAccepted) {
|
||||||
setTermsAccepted(true);
|
setTermsAccepted(true);
|
||||||
@@ -194,6 +237,11 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
{t("Ro'yxatdan o'tish")}
|
{t("Ro'yxatdan o'tish")}
|
||||||
</AppText>
|
</AppText>
|
||||||
|
|
||||||
|
<ErrorNotification
|
||||||
|
setVisible={setVisible}
|
||||||
|
error={error}
|
||||||
|
visible={visible}
|
||||||
|
/>
|
||||||
{/* Ism */}
|
{/* Ism */}
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -211,11 +259,6 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
returnKeyType="next"
|
returnKeyType="next"
|
||||||
onSubmitEditing={() => lastNameRef.current?.focus()}
|
onSubmitEditing={() => lastNameRef.current?.focus()}
|
||||||
/>
|
/>
|
||||||
{errors.firstName && (
|
|
||||||
<AppText style={RegisterStyle.errorText}>
|
|
||||||
{t(errors.firstName.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -239,11 +282,6 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
returnKeyType="next"
|
returnKeyType="next"
|
||||||
onSubmitEditing={() => phoneRef.current?.focus()}
|
onSubmitEditing={() => phoneRef.current?.focus()}
|
||||||
/>
|
/>
|
||||||
{errors.lastName && (
|
|
||||||
<AppText style={RegisterStyle.errorText}>
|
|
||||||
{t(errors.lastName.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -280,11 +318,6 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
() => setFilialDropdownVisible(true) // ❗ Branch select ochiladi
|
() => setFilialDropdownVisible(true) // ❗ Branch select ochiladi
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{errors.phoneNumber && (
|
|
||||||
<AppText style={RegisterStyle.errorText}>
|
|
||||||
{t(errors.phoneNumber.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
@@ -354,11 +387,6 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{errors.branchId && (
|
|
||||||
<AppText style={RegisterStyle.errorText}>
|
|
||||||
{t(errors.branchId.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -386,11 +414,6 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
() => setRecommendedDropdownVisible(true) // ❗ recommend select ochiladi
|
() => setRecommendedDropdownVisible(true) // ❗ recommend select ochiladi
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{errors.address && (
|
|
||||||
<AppText style={RegisterStyle.errorText}>
|
|
||||||
{t(errors.address.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -452,16 +475,6 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{errors.recommend && (
|
|
||||||
<AppText style={RegisterStyle.errorText}>
|
|
||||||
{t(errors.recommend.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
{error && (
|
|
||||||
<AppText style={[RegisterStyle.errorText]}>
|
|
||||||
{t(error)}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -513,7 +526,7 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={handleSubmit(onSubmit)}
|
onPress={handleSubmit(onSubmit, onErrorSubmit)}
|
||||||
style={[
|
style={[
|
||||||
RegisterStyle.button,
|
RegisterStyle.button,
|
||||||
(!termsAccepted || isPending) &&
|
(!termsAccepted || isPending) &&
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { useMutation } from '@tanstack/react-query';
|
|||||||
import passportApi, { sendPassportPayload } from 'api/passport';
|
import passportApi, { sendPassportPayload } from 'api/passport';
|
||||||
import AppText from 'components/AppText';
|
import AppText from 'components/AppText';
|
||||||
import DatePickerInput from 'components/DatePicker';
|
import DatePickerInput from 'components/DatePicker';
|
||||||
|
import ErrorNotification from 'components/ErrorNotification';
|
||||||
import SingleFileDrop from 'components/FileDrop';
|
import SingleFileDrop from 'components/FileDrop';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
@@ -55,7 +56,6 @@ const SecondStep = () => {
|
|||||||
const passportNumberRef = useRef<TextInput>(null);
|
const passportNumberRef = useRef<TextInput>(null);
|
||||||
const [checkboxAnimation] = useState(new Animated.Value(1));
|
const [checkboxAnimation] = useState(new Animated.Value(1));
|
||||||
const [inputValue, setInputValue] = useState('');
|
const [inputValue, setInputValue] = useState('');
|
||||||
const [error, setError] = useState<string>('');
|
|
||||||
const { firstName, lastName } = useUserStore(state => state);
|
const { firstName, lastName } = useUserStore(state => state);
|
||||||
const passportSeriyaRef = useRef<TextInput>(null);
|
const passportSeriyaRef = useRef<TextInput>(null);
|
||||||
const jshshirRef = useRef<TextInput>(null);
|
const jshshirRef = useRef<TextInput>(null);
|
||||||
@@ -65,6 +65,8 @@ const SecondStep = () => {
|
|||||||
const route = useRoute<RouteProp<RootStackParamList, 'Register'>>();
|
const route = useRoute<RouteProp<RootStackParamList, 'Register'>>();
|
||||||
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
|
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
|
||||||
const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
|
const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [error, setError] = useState<string>('Xatolik yuz berdi');
|
||||||
|
|
||||||
const formatDate = (date: Date) => {
|
const formatDate = (date: Date) => {
|
||||||
const day = date.getDate().toString().padStart(2, '0');
|
const day = date.getDate().toString().padStart(2, '0');
|
||||||
@@ -80,7 +82,10 @@ const SecondStep = () => {
|
|||||||
navigation.navigate('Home');
|
navigation.navigate('Home');
|
||||||
},
|
},
|
||||||
onError: (err: any) => {
|
onError: (err: any) => {
|
||||||
setError(err.response.data);
|
console.dir(err);
|
||||||
|
|
||||||
|
setError(err?.response?.data || 'Xatolik yuz berdi');
|
||||||
|
setVisible(true);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -112,6 +117,17 @@ const SecondStep = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = async (data: SecondStepFormType) => {
|
const onSubmit = async (data: SecondStepFormType) => {
|
||||||
|
if (
|
||||||
|
errors.birthDate?.message ||
|
||||||
|
errors.jshshir?.message ||
|
||||||
|
errors.passportNumber?.message ||
|
||||||
|
errors.passportSeriya?.message
|
||||||
|
) {
|
||||||
|
setError("Ma'lumotlarni to'liq kiriting");
|
||||||
|
setVisible(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const [d, m, y] = data.birthDate.split('/');
|
const [d, m, y] = data.birthDate.split('/');
|
||||||
const isoBirthDate = `${y}-${m.padStart(2, '0')}-${d.padStart(2, '0')}`;
|
const isoBirthDate = `${y}-${m.padStart(2, '0')}-${d.padStart(2, '0')}`;
|
||||||
|
|
||||||
@@ -127,6 +143,11 @@ const SecondStep = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onErrorSubmit = (errors: any) => {
|
||||||
|
setError("Ma'lumotlarni to'liq kiriting");
|
||||||
|
setVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ImageBackground
|
<ImageBackground
|
||||||
source={Logo}
|
source={Logo}
|
||||||
@@ -147,6 +168,12 @@ const SecondStep = () => {
|
|||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
<ErrorNotification
|
||||||
|
setVisible={setVisible}
|
||||||
|
error={error}
|
||||||
|
visible={visible}
|
||||||
|
/>
|
||||||
|
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
style={{ flex: 1 }}
|
style={{ flex: 1 }}
|
||||||
behavior="padding"
|
behavior="padding"
|
||||||
@@ -224,12 +251,6 @@ const SecondStep = () => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
{(errors.passportSeriya || errors.passportNumber) && (
|
|
||||||
<AppText style={RegisterStyle.errorText}>
|
|
||||||
{t(errors.passportSeriya?.message || '') ||
|
|
||||||
t(errors.passportNumber?.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
{/* JSHSHIR */}
|
{/* JSHSHIR */}
|
||||||
<Controller
|
<Controller
|
||||||
@@ -254,11 +275,6 @@ const SecondStep = () => {
|
|||||||
}
|
}
|
||||||
onSubmitEditing={() => birthDateRef.current?.focus()}
|
onSubmitEditing={() => birthDateRef.current?.focus()}
|
||||||
/>
|
/>
|
||||||
{errors.jshshir && (
|
|
||||||
<AppText style={RegisterStyle.errorText}>
|
|
||||||
{t(errors.jshshir.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -354,12 +370,6 @@ const SecondStep = () => {
|
|||||||
<Calendar color="#D8DADC" width={24} height={24} />
|
<Calendar color="#D8DADC" width={24} height={24} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{errors.birthDate && (
|
|
||||||
<AppText style={RegisterStyle.errorText}>
|
|
||||||
{t(errors.birthDate?.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -418,14 +428,9 @@ const SecondStep = () => {
|
|||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{error && (
|
|
||||||
<AppText style={[RegisterStyle.errorText, { fontSize: 14 }]}>
|
|
||||||
{error}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
{/* BUTTON */}
|
{/* BUTTON */}
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={handleSubmit(onSubmit)}
|
onPress={handleSubmit(onSubmit, onErrorSubmit)}
|
||||||
style={[
|
style={[
|
||||||
RegisterStyle.button,
|
RegisterStyle.button,
|
||||||
!termsAccepted && RegisterStyle.buttonDisabled,
|
!termsAccepted && RegisterStyle.buttonDisabled,
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||||
import AppText from 'components/AppText';
|
import AppText from 'components/AppText';
|
||||||
|
import { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
Image,
|
Image,
|
||||||
|
Linking,
|
||||||
|
Modal,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
useWindowDimensions,
|
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
import SendIntentAndroid from 'react-native-send-intent';
|
||||||
import Logo from 'screens/../../assets/bootsplash/logo_512.png';
|
import Logo from 'screens/../../assets/bootsplash/logo_512.png';
|
||||||
import InfoIcon from 'svg/Info';
|
import InfoIcon from 'svg/Info';
|
||||||
import { RootStackParamList } from 'types/types';
|
import { RootStackParamList } from 'types/types';
|
||||||
@@ -23,7 +26,18 @@ type LoginScreenNavigationProp = NativeStackNavigationProp<
|
|||||||
const SelectAuth = () => {
|
const SelectAuth = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigation = useNavigation<LoginScreenNavigationProp>();
|
const navigation = useNavigation<LoginScreenNavigationProp>();
|
||||||
const { width } = useWindowDimensions();
|
const [modalVisible, setModalVisible] = useState(false);
|
||||||
|
const [newRegisterModalVisible, setNewRegisterModalVisible] = useState(false);
|
||||||
|
|
||||||
|
const openTelegram = async () => {
|
||||||
|
const telegramUri = 'tg://resolve?domain=cpostuz';
|
||||||
|
try {
|
||||||
|
const success = await SendIntentAndroid.openAppWithUri(telegramUri);
|
||||||
|
if (!success) Linking.openURL('https://t.me/cpostuz');
|
||||||
|
} catch (error) {
|
||||||
|
Linking.openURL('https://t.me/cpostuz');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={{ flex: 1 }}>
|
<SafeAreaView style={{ flex: 1 }}>
|
||||||
@@ -60,6 +74,78 @@ const SelectAuth = () => {
|
|||||||
|
|
||||||
<View style={styles.btnContainer}>
|
<View style={styles.btnContainer}>
|
||||||
<View style={{ gap: 6 }}>
|
<View style={{ gap: 6 }}>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => setModalVisible(true)}
|
||||||
|
style={{
|
||||||
|
gap: 4,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<InfoIcon color="#000" height={18} width={18} />
|
||||||
|
<AppText style={styles.helperText}>
|
||||||
|
{t("Botdan ro'yxatdan o’tganmisiz")}?
|
||||||
|
</AppText>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<Modal
|
||||||
|
animationType="slide"
|
||||||
|
transparent={true}
|
||||||
|
visible={modalVisible}
|
||||||
|
onRequestClose={() => setModalVisible(false)}
|
||||||
|
>
|
||||||
|
<View style={styles.modalOverlay}>
|
||||||
|
<View style={styles.modalContainer}>
|
||||||
|
<ScrollView>
|
||||||
|
<AppText style={styles.modalTitle}>
|
||||||
|
{t('Yordam')}
|
||||||
|
</AppText>
|
||||||
|
<AppText style={styles.modalText}>
|
||||||
|
{t('Agar siz oldin ')}
|
||||||
|
|
||||||
|
<AppText
|
||||||
|
style={{ color: '#28A7E8' }}
|
||||||
|
onPress={openTelegram}
|
||||||
|
>
|
||||||
|
@cpcargo_bot
|
||||||
|
</AppText>
|
||||||
|
{t(
|
||||||
|
' orqali ro’yxatdan o’tgan bo’lsangiz, tizimga kirish uchun botda ishlatilgan telefon raqamingiz va passport seriya raqamingizni kiriting.',
|
||||||
|
)}
|
||||||
|
</AppText>
|
||||||
|
<AppText style={styles.modalText}>
|
||||||
|
{t('Masalan')}:
|
||||||
|
</AppText>
|
||||||
|
<AppText style={styles.modalText}>
|
||||||
|
{t('Telefon: +998901234567')}
|
||||||
|
</AppText>
|
||||||
|
<AppText style={styles.modalText}>
|
||||||
|
{t('Passport seriya: AA1234567')}
|
||||||
|
</AppText>
|
||||||
|
<AppText style={styles.modalText}>
|
||||||
|
{t(
|
||||||
|
'Shu ma’lumotlarni kiritganingizdan so’ng siz tizimga kirishingiz mumkin.',
|
||||||
|
)}
|
||||||
|
</AppText>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.closeButton}
|
||||||
|
onPress={() => setModalVisible(false)}
|
||||||
|
>
|
||||||
|
<AppText
|
||||||
|
style={{
|
||||||
|
color: '#fff',
|
||||||
|
textAlign: 'center',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Yopish')}
|
||||||
|
</AppText>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => navigation.navigate('Login')}
|
onPress={() => navigation.navigate('Login')}
|
||||||
style={[
|
style={[
|
||||||
@@ -75,30 +161,10 @@ const SelectAuth = () => {
|
|||||||
{t('Tizimga kirish')}
|
{t('Tizimga kirish')}
|
||||||
</AppText>
|
</AppText>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
gap: 4,
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<InfoIcon color="#000" height={18} width={18} />
|
|
||||||
<AppText style={styles.helperText}>
|
|
||||||
{t("Botdan ro'yxatdan o’tganmisiz")}?
|
|
||||||
</AppText>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
<View style={{ gap: 6 }}>
|
<View style={{ gap: 6 }}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => navigation.navigate('Register')}
|
onPress={() => setNewRegisterModalVisible(true)}
|
||||||
style={styles.button}
|
|
||||||
>
|
|
||||||
<AppText style={styles.btnText}>
|
|
||||||
{t('Ro’yxatdan o’tish')}
|
|
||||||
</AppText>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<View
|
|
||||||
style={{
|
style={{
|
||||||
gap: 4,
|
gap: 4,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
@@ -110,8 +176,70 @@ const SelectAuth = () => {
|
|||||||
<AppText style={styles.helperText}>
|
<AppText style={styles.helperText}>
|
||||||
{t("Yangi ro’yxatdan o'tmoqchimisiz")}?
|
{t("Yangi ro’yxatdan o'tmoqchimisiz")}?
|
||||||
</AppText>
|
</AppText>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => navigation.navigate('Register')}
|
||||||
|
style={styles.button}
|
||||||
|
>
|
||||||
|
<AppText style={styles.btnText}>
|
||||||
|
{t('Ro’yxatdan o’tish')}
|
||||||
|
</AppText>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
<Modal
|
||||||
|
animationType="slide"
|
||||||
|
transparent={true}
|
||||||
|
visible={newRegisterModalVisible}
|
||||||
|
onRequestClose={() => setNewRegisterModalVisible(false)}
|
||||||
|
>
|
||||||
|
<View style={styles.modalOverlay}>
|
||||||
|
<View style={styles.modalContainer}>
|
||||||
|
<ScrollView>
|
||||||
|
<AppText style={styles.modalTitle}>{t('Yordam')}</AppText>
|
||||||
|
|
||||||
|
<AppText style={styles.modalText}>
|
||||||
|
{t(
|
||||||
|
'Agar siz yangi foydalanuvchi bo’lsangiz, tizimga kirishdan oldin ro’yxatdan o’tishingiz kerak. Ro’yxatdan o’tish jarayonida sizdan quyidagi ma’lumotlar so’raladi:',
|
||||||
|
)}
|
||||||
|
</AppText>
|
||||||
|
|
||||||
|
<AppText style={styles.modalText}>
|
||||||
|
1. {t('Telefon raqamingiz')}
|
||||||
|
</AppText>
|
||||||
|
<AppText style={styles.modalText}>
|
||||||
|
2. {t('Passport seriya va raqamingiz')}
|
||||||
|
</AppText>
|
||||||
|
<AppText style={styles.modalText}>
|
||||||
|
3. {t('Passport oldi rasmi')}
|
||||||
|
</AppText>
|
||||||
|
<AppText style={styles.modalText}>
|
||||||
|
4. {t('Passport orqa rasmi')}
|
||||||
|
</AppText>
|
||||||
|
|
||||||
|
<AppText style={styles.modalText}>
|
||||||
|
{t(
|
||||||
|
'Shu ma’lumotlarni kiritganingizdan so’ng siz tizimga kirishingiz mumkin.',
|
||||||
|
)}
|
||||||
|
</AppText>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.closeButton}
|
||||||
|
onPress={() => setNewRegisterModalVisible(false)}
|
||||||
|
>
|
||||||
|
<AppText
|
||||||
|
style={{
|
||||||
|
color: '#fff',
|
||||||
|
textAlign: 'center',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Yopish')}
|
||||||
|
</AppText>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
</Modal>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
@@ -173,4 +301,34 @@ const styles = StyleSheet.create({
|
|||||||
btnContainer: {
|
btnContainer: {
|
||||||
gap: 16,
|
gap: 16,
|
||||||
},
|
},
|
||||||
|
modalOverlay: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.5)',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
modalContainer: {
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderRadius: 12,
|
||||||
|
padding: 20,
|
||||||
|
width: '85%',
|
||||||
|
maxHeight: '70%',
|
||||||
|
},
|
||||||
|
modalTitle: {
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
modalText: {
|
||||||
|
fontSize: 14,
|
||||||
|
marginBottom: 2,
|
||||||
|
lineHeight: 20,
|
||||||
|
color: '#000',
|
||||||
|
},
|
||||||
|
closeButton: {
|
||||||
|
backgroundColor: '#28A7E8',
|
||||||
|
paddingVertical: 12,
|
||||||
|
borderRadius: 8,
|
||||||
|
marginTop: 10,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,9 +26,15 @@ interface ModalSuccessViewProps {
|
|||||||
visible: boolean;
|
visible: boolean;
|
||||||
error: boolean;
|
error: boolean;
|
||||||
setVisible: React.Dispatch<React.SetStateAction<boolean>>;
|
setVisible: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
errorText?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CreateModal = ({ visible, setVisible, error }: ModalSuccessViewProps) => {
|
const CreateModal = ({
|
||||||
|
visible,
|
||||||
|
setVisible,
|
||||||
|
error,
|
||||||
|
errorText,
|
||||||
|
}: ModalSuccessViewProps) => {
|
||||||
const navigation = useNavigation<NavigationProp>();
|
const navigation = useNavigation<NavigationProp>();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [successMet, setSuccessMet] = useState(false);
|
const [successMet, setSuccessMet] = useState(false);
|
||||||
@@ -103,6 +109,7 @@ const CreateModal = ({ visible, setVisible, error }: ModalSuccessViewProps) => {
|
|||||||
{successMet ? (
|
{successMet ? (
|
||||||
<View style={styles.content}>
|
<View style={styles.content}>
|
||||||
{error ? (
|
{error ? (
|
||||||
|
<>
|
||||||
<LottieView
|
<LottieView
|
||||||
source={Warning}
|
source={Warning}
|
||||||
loop
|
loop
|
||||||
@@ -110,6 +117,10 @@ const CreateModal = ({ visible, setVisible, error }: ModalSuccessViewProps) => {
|
|||||||
resizeMode="cover"
|
resizeMode="cover"
|
||||||
style={{ width: 100 * scale, height: 100 * scale }}
|
style={{ width: 100 * scale, height: 100 * scale }}
|
||||||
/>
|
/>
|
||||||
|
<AppText style={styles.status}>
|
||||||
|
{t(errorText!) || t('Xatolik yuz berdi')}
|
||||||
|
</AppText>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<LottieView
|
<LottieView
|
||||||
source={ProgressBar}
|
source={ProgressBar}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useMutation } from '@tanstack/react-query';
|
|||||||
import passportApi, { AddPassportPayload } from 'api/passport';
|
import passportApi, { AddPassportPayload } from 'api/passport';
|
||||||
import AppText from 'components/AppText';
|
import AppText from 'components/AppText';
|
||||||
import DatePickerInput from 'components/DatePicker';
|
import DatePickerInput from 'components/DatePicker';
|
||||||
|
import ErrorNotification from 'components/ErrorNotification';
|
||||||
import SingleFileDrop from 'components/FileDrop';
|
import SingleFileDrop from 'components/FileDrop';
|
||||||
import LayoutTwo from 'components/LayoutTwo';
|
import LayoutTwo from 'components/LayoutTwo';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
@@ -38,9 +39,12 @@ const CreatePassword = () => {
|
|||||||
const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
|
const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
|
||||||
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
|
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
|
||||||
const [success, setSuccess] = React.useState(false);
|
const [success, setSuccess] = React.useState(false);
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState<string>('Xatolik yuz berdi');
|
||||||
const [frontImage, setFrontImage] = useState<FileData | null>(null);
|
const [frontImage, setFrontImage] = useState<FileData | null>(null);
|
||||||
const [backImage, setBackImage] = useState<FileData | null>(null);
|
const [backImage, setBackImage] = useState<FileData | null>(null);
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [errorVisible, setErrorVisible] = useState(false);
|
||||||
|
const [errorText, setErrorText] = useState('');
|
||||||
// firstName: '',
|
// firstName: '',
|
||||||
// lastName: '',
|
// lastName: '',
|
||||||
// birthDate: '',
|
// birthDate: '',
|
||||||
@@ -58,13 +62,15 @@ const CreatePassword = () => {
|
|||||||
const res = passportApi.addPassport(payload);
|
const res = passportApi.addPassport(payload);
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
onSuccess: res => {
|
onSuccess: () => {
|
||||||
setSuccess(true);
|
setSuccess(true);
|
||||||
},
|
},
|
||||||
onError: err => {
|
onError: (err: any) => {
|
||||||
console.dir(err);
|
console.dir(err);
|
||||||
|
setErrorVisible(true);
|
||||||
setError(true);
|
setErrorText(
|
||||||
|
err?.response?.data || err?.response?.message || 'Xatolik yuz berdi',
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -100,6 +106,28 @@ const CreatePassword = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onSubmit = (data: CreatePassSchemaType) => {
|
const onSubmit = (data: CreatePassSchemaType) => {
|
||||||
|
if (
|
||||||
|
errors.birthDate ||
|
||||||
|
errors.firstName ||
|
||||||
|
errors.jshshir ||
|
||||||
|
errors.lastName ||
|
||||||
|
errors.passportNumber ||
|
||||||
|
errors.passportSeriya
|
||||||
|
) {
|
||||||
|
const firstError =
|
||||||
|
errors.birthDate?.message ||
|
||||||
|
errors.passportSeriya?.message ||
|
||||||
|
errors.passportNumber?.message ||
|
||||||
|
errors.firstName?.message ||
|
||||||
|
errors.lastName?.message ||
|
||||||
|
errors.jshshir?.message ||
|
||||||
|
'Xatolik yuz berdi';
|
||||||
|
setError(
|
||||||
|
firstError ? "Ma'lumotlarni to'liq kiriting" : 'Xatolik yuz berdi',
|
||||||
|
);
|
||||||
|
setVisible(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const [d, m, y] = data.birthDate.split('/');
|
const [d, m, y] = data.birthDate.split('/');
|
||||||
const isoBirthDate = `${y}-${m.padStart(2, '0')}-${d.padStart(2, '0')}`;
|
const isoBirthDate = `${y}-${m.padStart(2, '0')}-${d.padStart(2, '0')}`;
|
||||||
|
|
||||||
@@ -114,6 +142,22 @@ const CreatePassword = () => {
|
|||||||
passportBackImage: backImage ? `${backImage.base64}` : '',
|
passportBackImage: backImage ? `${backImage.base64}` : '',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onErrorSubmit = (errors: any) => {
|
||||||
|
const firstError =
|
||||||
|
errors.birthDate?.message ||
|
||||||
|
errors.passportSeriya?.message ||
|
||||||
|
errors.passportNumber?.message ||
|
||||||
|
errors.firstName?.message ||
|
||||||
|
errors.lastName?.message ||
|
||||||
|
errors.jshshir?.message ||
|
||||||
|
'Xatolik yuz berdi';
|
||||||
|
setError(
|
||||||
|
firstError ? "Ma'lumotlarni to'liq kiriting" : 'Xatolik yuz berdi',
|
||||||
|
);
|
||||||
|
setVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutTwo title={t("Yangi pasport qo'shish")}>
|
<LayoutTwo title={t("Yangi pasport qo'shish")}>
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
@@ -127,6 +171,11 @@ const CreatePassword = () => {
|
|||||||
style={PassportStyle.content}
|
style={PassportStyle.content}
|
||||||
>
|
>
|
||||||
<View style={PassportStyle.scrollContainer}>
|
<View style={PassportStyle.scrollContainer}>
|
||||||
|
<ErrorNotification
|
||||||
|
setVisible={setVisible}
|
||||||
|
error={error}
|
||||||
|
visible={visible}
|
||||||
|
/>
|
||||||
<View style={PassportStyle.loginContainer}>
|
<View style={PassportStyle.loginContainer}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -144,11 +193,6 @@ const CreatePassword = () => {
|
|||||||
value={value}
|
value={value}
|
||||||
placeholderTextColor={'#D8DADC'}
|
placeholderTextColor={'#D8DADC'}
|
||||||
/>
|
/>
|
||||||
{errors.firstName && (
|
|
||||||
<AppText style={PassportStyle.errorText}>
|
|
||||||
{t(errors.firstName.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -170,11 +214,6 @@ const CreatePassword = () => {
|
|||||||
onChangeText={onChange}
|
onChangeText={onChange}
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
{errors.lastName && (
|
|
||||||
<AppText style={PassportStyle.errorText}>
|
|
||||||
{t(errors.lastName.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -230,12 +269,6 @@ const CreatePassword = () => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
{(errors.passportSeriya || errors.passportNumber) && (
|
|
||||||
<AppText style={PassportStyle.errorText}>
|
|
||||||
{t(errors.passportSeriya?.message || '') ||
|
|
||||||
t(errors.passportNumber?.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
<View>
|
<View>
|
||||||
<AppText style={PassportStyle.label}>{t('JSHSHIR')}</AppText>
|
<AppText style={PassportStyle.label}>{t('JSHSHIR')}</AppText>
|
||||||
@@ -260,11 +293,6 @@ const CreatePassword = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{errors.jshshir && (
|
|
||||||
<AppText style={PassportStyle.errorText}>
|
|
||||||
{t(errors.jshshir.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
<View>
|
<View>
|
||||||
<Controller
|
<Controller
|
||||||
@@ -345,12 +373,6 @@ const CreatePassword = () => {
|
|||||||
<Calendar color="#D8DADC" width={25} height={25} />
|
<Calendar color="#D8DADC" width={25} height={25} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{errors.birthDate && (
|
|
||||||
<AppText style={PassportStyle.errorText}>
|
|
||||||
{t(errors.birthDate?.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -405,7 +427,7 @@ const CreatePassword = () => {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={handleSubmit(onSubmit)}
|
onPress={handleSubmit(onSubmit, onErrorSubmit)}
|
||||||
style={PassportStyle.button}
|
style={PassportStyle.button}
|
||||||
>
|
>
|
||||||
{isPending ? (
|
{isPending ? (
|
||||||
@@ -421,7 +443,12 @@ const CreatePassword = () => {
|
|||||||
</ScrollView>
|
</ScrollView>
|
||||||
</KeyboardAvoidingView>
|
</KeyboardAvoidingView>
|
||||||
{success && (
|
{success && (
|
||||||
<CreateModal visible={success} setVisible={setSuccess} error={error} />
|
<CreateModal
|
||||||
|
visible={success}
|
||||||
|
setVisible={setSuccess}
|
||||||
|
error={errorVisible}
|
||||||
|
errorText={errorText}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</LayoutTwo>
|
</LayoutTwo>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -54,9 +54,9 @@ const MyPassport = ({ getMe, myPassport }: Props) => {
|
|||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'column',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
alignItems: 'flex-start',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppText style={styles.title}>
|
<AppText style={styles.title}>
|
||||||
@@ -227,7 +227,7 @@ const styles = StyleSheet.create({
|
|||||||
gap: 5,
|
gap: 5,
|
||||||
},
|
},
|
||||||
statusBadge: {
|
statusBadge: {
|
||||||
alignSelf: 'center',
|
alignSelf: 'flex-end',
|
||||||
marginTop: 5,
|
marginTop: 5,
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
paddingHorizontal: 14,
|
paddingHorizontal: 14,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Layout from 'components/Layout';
|
import Layout from 'components/Layout';
|
||||||
import LoadingScreen from 'components/LoadingScreen';
|
import LoadingScreen from 'components/LoadingScreen';
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { RefreshControl, ScrollView, StyleSheet } from 'react-native';
|
import { RefreshControl, ScrollView } from 'react-native';
|
||||||
import ProfileHeader from './ProfileHeader';
|
import ProfileHeader from './ProfileHeader';
|
||||||
import ProfilePages from './ProfilePages';
|
import ProfilePages from './ProfilePages';
|
||||||
|
|
||||||
@@ -61,29 +61,4 @@ const Profile = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
},
|
|
||||||
addBtn: {
|
|
||||||
backgroundColor: '#28A7E8',
|
|
||||||
padding: 10,
|
|
||||||
marginBottom: 10,
|
|
||||||
textAlign: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
width: '95%',
|
|
||||||
margin: 'auto',
|
|
||||||
borderRadius: 8,
|
|
||||||
flexDirection: 'row',
|
|
||||||
gap: 10,
|
|
||||||
position: 'static',
|
|
||||||
},
|
|
||||||
btnText: {
|
|
||||||
color: '#FFFFFF',
|
|
||||||
fontSize: 18,
|
|
||||||
fontFamily: 'GolosText-Bold',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Profile;
|
export default Profile;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import AppText from 'components/AppText';
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
Dimensions,
|
|
||||||
Image,
|
Image,
|
||||||
Linking,
|
Linking,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
@@ -20,9 +19,6 @@ import Plus from 'svg/Plus';
|
|||||||
import Telegram from 'svg/Telegram';
|
import Telegram from 'svg/Telegram';
|
||||||
import Trash from 'svg/Trash';
|
import Trash from 'svg/Trash';
|
||||||
|
|
||||||
const { width } = Dimensions.get('window');
|
|
||||||
const isSmallScreen = width < 360;
|
|
||||||
|
|
||||||
const ProfileHeader = ({ userName = 'Samandar' }: { userName?: string }) => {
|
const ProfileHeader = ({ userName = 'Samandar' }: { userName?: string }) => {
|
||||||
const [imageError, setImageError] = useState(true);
|
const [imageError, setImageError] = useState(true);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -48,7 +44,6 @@ const ProfileHeader = ({ userName = 'Samandar' }: { userName?: string }) => {
|
|||||||
queryKey: ['getMe'],
|
queryKey: ['getMe'],
|
||||||
queryFn: authApi.getMe,
|
queryFn: authApi.getMe,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [isModalVisible, setModalVisible] = useState(false);
|
const [isModalVisible, setModalVisible] = useState(false);
|
||||||
|
|
||||||
const openGallery = async () => {
|
const openGallery = async () => {
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ import {
|
|||||||
import CloseIcon from 'svg/Close';
|
import CloseIcon from 'svg/Close';
|
||||||
import FilterIcon from 'svg/Filter';
|
import FilterIcon from 'svg/Filter';
|
||||||
|
|
||||||
const transportTypes = ['AUTO', 'AVIA'] as const;
|
const transportTypes = ['AVIA', 'AUTO'] as const;
|
||||||
type TransportType = (typeof transportTypes)[number];
|
type TransportType = (typeof transportTypes)[number];
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
transportTypes: TransportType;
|
transportTypes: TransportType;
|
||||||
setTransportTypes: (val: TransportType) => void;
|
setTransportTypes: (val: TransportType) => void;
|
||||||
reys: string;
|
reys: string;
|
||||||
data: PacketsData;
|
data: PacketsData | undefined;
|
||||||
setReys: (val: string) => void;
|
setReys: (val: string) => void;
|
||||||
setSelectedData: (val: any) => void;
|
setSelectedData: (val: any) => void;
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@ const Filter = ({
|
|||||||
|
|
||||||
const styles = makeStyles();
|
const styles = makeStyles();
|
||||||
const newOrders = React.useMemo(
|
const newOrders = React.useMemo(
|
||||||
() => data.data.filter(item => item.paymentStatus === 'NEW'),
|
() => data?.data?.filter(item => item.paymentStatus === 'NEW') || [],
|
||||||
[data],
|
[data],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ const Filter = ({
|
|||||||
selectedType === type && styles.activeTypeText,
|
selectedType === type && styles.activeTypeText,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{type === 'AUTO' ? t('Avto') : t('Avia')}
|
{type === 'AVIA' ? t('Avia') : t('Auto')}
|
||||||
</AppText>
|
</AppText>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
|
|||||||
143
src/screens/status/ui/PaymentFilter.tsx
Normal file
143
src/screens/status/ui/PaymentFilter.tsx
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
import AppText from 'components/AppText';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { FlatList, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||||
|
import CloseIcon from 'svg/Close';
|
||||||
|
import FilterIcon from 'svg/Filter';
|
||||||
|
|
||||||
|
const paymentStatuses = [
|
||||||
|
{ label: "To'lanmagan", value: 'NEW' },
|
||||||
|
{ label: 'Kutilmoqda', value: 'PENDING' },
|
||||||
|
{ label: "To'langan", value: 'PAYED' },
|
||||||
|
{ label: 'Bekor qilingan', value: 'CANCELLED' },
|
||||||
|
];
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
paymentStatus: string;
|
||||||
|
setPaymentStatus: (val: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PaymentFilter = ({ paymentStatus, setPaymentStatus }: Props) => {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<TouchableOpacity style={styles.card} onPress={() => setOpen(p => !p)}>
|
||||||
|
<FilterIcon color="#000000" width={18} height={18} />
|
||||||
|
<AppText style={styles.text}>{t("To'lov")}</AppText>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
{open && (
|
||||||
|
<View style={styles.dropdown}>
|
||||||
|
<View style={styles.dropdownHeader}>
|
||||||
|
<AppText style={styles.sectionTitle}>{t("To'lov holati")}</AppText>
|
||||||
|
<TouchableOpacity onPress={() => setOpen(false)}>
|
||||||
|
<CloseIcon />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<FlatList
|
||||||
|
data={paymentStatuses}
|
||||||
|
keyExtractor={item => item.value}
|
||||||
|
scrollEnabled={false}
|
||||||
|
contentContainerStyle={{ gap: 8 }}
|
||||||
|
renderItem={({ item }) => (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[
|
||||||
|
styles.statusButton,
|
||||||
|
paymentStatus === item.value && styles.activeStatus,
|
||||||
|
]}
|
||||||
|
onPress={() => {
|
||||||
|
setPaymentStatus(item.value);
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AppText
|
||||||
|
style={[
|
||||||
|
styles.statusText,
|
||||||
|
paymentStatus === item.value && styles.activeStatusText,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{t(item.label)}
|
||||||
|
</AppText>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
borderRadius: 8,
|
||||||
|
alignItems: 'flex-end',
|
||||||
|
position: 'relative',
|
||||||
|
zIndex: 10,
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
height: 40,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
backgroundColor: '#D8DADC',
|
||||||
|
borderRadius: 8,
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: 4,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
color: '#000000',
|
||||||
|
fontWeight: '500',
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
dropdown: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 50,
|
||||||
|
right: 0,
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderRadius: 8,
|
||||||
|
paddingVertical: 8,
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOffset: { width: 0, height: 2 },
|
||||||
|
shadowOpacity: 0.2,
|
||||||
|
shadowRadius: 4,
|
||||||
|
elevation: 5,
|
||||||
|
zIndex: 10,
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
|
dropdownHeader: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
sectionTitle: {
|
||||||
|
fontFamily: 'GolosText-Bold',
|
||||||
|
marginBottom: 6,
|
||||||
|
color: '#333',
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
statusButton: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#F3FAFF',
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
paddingVertical: 6,
|
||||||
|
borderRadius: 6,
|
||||||
|
},
|
||||||
|
activeStatus: {
|
||||||
|
backgroundColor: '#28A7E8',
|
||||||
|
},
|
||||||
|
statusText: {
|
||||||
|
color: '#28A7E8',
|
||||||
|
fontWeight: '500',
|
||||||
|
fontSize: 14,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
activeStatusText: {
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PaymentFilter;
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import packetsApi from 'api/packets';
|
import packetsApi from 'api/packets';
|
||||||
import Layout from 'components/Layout';
|
import Layout from 'components/Layout';
|
||||||
@@ -19,11 +20,13 @@ import { DataInfo } from '../lib/data';
|
|||||||
import Filter from './Filter';
|
import Filter from './Filter';
|
||||||
import Order from './Order';
|
import Order from './Order';
|
||||||
import OrderDetailModal from './OrderDetailModal';
|
import OrderDetailModal from './OrderDetailModal';
|
||||||
|
import PaymentFilter from './PaymentFilter';
|
||||||
import Tabs from './Tabs';
|
import Tabs from './Tabs';
|
||||||
|
|
||||||
const Status = () => {
|
const Status = () => {
|
||||||
const { width: screenWidth } = useWindowDimensions();
|
const { width: screenWidth } = useWindowDimensions();
|
||||||
const scale = screenWidth < 360 ? 0.85 : 1;
|
const scale = screenWidth < 360 ? 0.85 : 1;
|
||||||
|
const [paymentStatus, setPaymentStatus] = useState('NEW');
|
||||||
|
|
||||||
const [filter, setFilter] = useState<
|
const [filter, setFilter] = useState<
|
||||||
| 'COLLECTING'
|
| 'COLLECTING'
|
||||||
@@ -38,7 +41,7 @@ const Status = () => {
|
|||||||
const [transportTypes, setTransportTypes] = useState<
|
const [transportTypes, setTransportTypes] = useState<
|
||||||
// 'all'|
|
// 'all'|
|
||||||
'AUTO' | 'AVIA'
|
'AUTO' | 'AVIA'
|
||||||
>('AUTO');
|
>('AVIA');
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const {
|
const {
|
||||||
data: statusData,
|
data: statusData,
|
||||||
@@ -46,15 +49,21 @@ const Status = () => {
|
|||||||
isLoading,
|
isLoading,
|
||||||
isFetching,
|
isFetching,
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
queryKey: ['status', filter, transportTypes, page],
|
queryKey: ['status', filter, transportTypes, page, paymentStatus],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
packetsApi.getPacketsStatus(filter, {
|
packetsApi.getPacketsStatus(filter, {
|
||||||
page,
|
page,
|
||||||
size: 10,
|
size: 10,
|
||||||
cargoType: transportTypes,
|
cargoType: transportTypes,
|
||||||
|
sort: 'id',
|
||||||
|
paymentStatus: paymentStatus,
|
||||||
|
direction: 'DESC',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log(AsyncStorage.getItem('token'));
|
||||||
|
console.log('statusData', statusData);
|
||||||
|
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
const [modalVisible, setModalVisible] = useState(false);
|
||||||
const scaleAnim = React.useRef(new Animated.Value(0.8)).current;
|
const scaleAnim = React.useRef(new Animated.Value(0.8)).current;
|
||||||
const opacityAnim = React.useRef(new Animated.Value(0)).current;
|
const opacityAnim = React.useRef(new Animated.Value(0)).current;
|
||||||
@@ -144,20 +153,30 @@ const Status = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statusData?.data.length === 0) {
|
if (statusData?.data.length === 0 || statusData === undefined) {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Tabs filter={filter} setFilter={setFilter} />
|
<Tabs filter={filter} setFilter={setFilter} />
|
||||||
<View style={styles.controls}>
|
<View style={styles.controls}>
|
||||||
<View style={{ position: 'relative' }}>
|
<View
|
||||||
|
style={{
|
||||||
|
position: 'relative',
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Filter
|
<Filter
|
||||||
transportTypes={transportTypes}
|
transportTypes={transportTypes}
|
||||||
setTransportTypes={setTransportTypes}
|
setTransportTypes={setTransportTypes}
|
||||||
reys={reys}
|
reys={reys}
|
||||||
setReys={setReys}
|
setReys={setReys}
|
||||||
data={statusData!}
|
data={statusData}
|
||||||
setSelectedData={setSelectedData}
|
setSelectedData={setSelectedData}
|
||||||
/>
|
/>
|
||||||
|
<PaymentFilter
|
||||||
|
paymentStatus={paymentStatus}
|
||||||
|
setPaymentStatus={setPaymentStatus}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<NoResult message={t("Hech qanday ma'lumot topilmadi")} />
|
<NoResult message={t("Hech qanday ma'lumot topilmadi")} />
|
||||||
@@ -182,7 +201,13 @@ const Status = () => {
|
|||||||
/>
|
/>
|
||||||
<View style={styles.searchIcon}>{searchIcon}</View>
|
<View style={styles.searchIcon}>{searchIcon}</View>
|
||||||
</View> */}
|
</View> */}
|
||||||
<View style={{ position: 'relative' }}>
|
<View
|
||||||
|
style={{
|
||||||
|
position: 'relative',
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Filter
|
<Filter
|
||||||
transportTypes={transportTypes}
|
transportTypes={transportTypes}
|
||||||
setTransportTypes={setTransportTypes}
|
setTransportTypes={setTransportTypes}
|
||||||
@@ -191,6 +216,10 @@ const Status = () => {
|
|||||||
data={statusData!}
|
data={statusData!}
|
||||||
setSelectedData={setSelectedData}
|
setSelectedData={setSelectedData}
|
||||||
/>
|
/>
|
||||||
|
<PaymentFilter
|
||||||
|
paymentStatus={paymentStatus}
|
||||||
|
setPaymentStatus={setPaymentStatus}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Order
|
<Order
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||||
|
import { authApi } from 'api/auth';
|
||||||
import packetsApi from 'api/packets';
|
import packetsApi from 'api/packets';
|
||||||
import AppText from 'components/AppText';
|
import AppText from 'components/AppText';
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
@@ -52,6 +53,12 @@ const ModalPay = ({
|
|||||||
const { bottom } = useSafeAreaInsets();
|
const { bottom } = useSafeAreaInsets();
|
||||||
const [load, setLoad] = React.useState(false);
|
const [load, setLoad] = React.useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { data: getMe } = useQuery({
|
||||||
|
queryKey: ['getMe'],
|
||||||
|
queryFn: authApi.getMe,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(getMe, 'getMe');
|
||||||
|
|
||||||
const { mutate, isPending } = useMutation({
|
const { mutate, isPending } = useMutation({
|
||||||
mutationFn: ({ id, payType }: { id: number; payType: string }) =>
|
mutationFn: ({ id, payType }: { id: number; payType: string }) =>
|
||||||
@@ -137,11 +144,13 @@ const ModalPay = ({
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
{getMe && !getMe.aviaCargoId.includes('CP') && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
styles.option,
|
styles.option,
|
||||||
{
|
{
|
||||||
backgroundColor: selectedId === 'card' ? '#28A7E81A' : '#fff',
|
backgroundColor:
|
||||||
|
selectedId === 'card' ? '#28A7E81A' : '#fff',
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
onPress={() => setSelectedId('card')}
|
onPress={() => setSelectedId('card')}
|
||||||
@@ -167,7 +176,8 @@ const ModalPay = ({
|
|||||||
{
|
{
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
selectedId === 'card' ? '#28A7E8' : '#FFFFFF',
|
selectedId === 'card' ? '#28A7E8' : '#FFFFFF',
|
||||||
borderColor: selectedId === 'card' ? '#28A7E8' : '#383838',
|
borderColor:
|
||||||
|
selectedId === 'card' ? '#28A7E8' : '#383838',
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
@@ -176,6 +186,7 @@ const ModalPay = ({
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
{paymentType !== 'CASH' && (
|
{paymentType !== 'CASH' && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
|
|||||||
Reference in New Issue
Block a user