This commit is contained in:
Samandar Turgunboyev
2025-10-02 17:05:40 +05:00
parent 38badbe3dd
commit 9aac17072f
27 changed files with 1636 additions and 1380 deletions

View File

@@ -5,7 +5,6 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { toastConfig } from 'components/CustomAlertModal';
import { navigationRef } from 'components/NavigationRef';
import SplashScreen from 'components/SplashScreen';
import i18n from 'i18n/i18n';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { I18nextProvider } from 'react-i18next';
@@ -18,6 +17,7 @@ import {
View,
} from 'react-native';
import Toast from 'react-native-toast-message';
import SplashScreen from './src/components/SplashScreen';
// Screens
import Login from 'screens/auth/login/ui';

View File

@@ -85,8 +85,8 @@ android {
applicationId "uz.felix.cpost"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 4
versionName "0.4"
versionCode 5
versionName "0.5"
multiDexEnabled true
vectorDrawables.useSupportLibrary = true

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -1 +1 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@drawable/assets_bootsplash_logo_512,@drawable/assets_bootsplash_ru,@drawable/assets_bootsplash_uz,@drawable/assets_bootsplash_avia,@drawable/assets_bootsplash_auto,@drawable/assets_bootsplash_logo,@drawable/assets_bootsplash_passportsample,@drawable/assets_bootsplash_local,@drawable/assets_bootsplash_step_1,@drawable/assets_bootsplash_step_2,@drawable/assets_bootsplash_step_3,@drawable/node_modules_reactnavigation_elements_lib_module_assets_searchicon,@drawable/node_modules_reactnavigation_elements_lib_module_assets_backicon,@drawable/node_modules_reactnavigation_elements_lib_module_assets_backiconmask,@drawable/node_modules_reactnavigation_elements_lib_module_assets_clearicon,@drawable/node_modules_reactnavigation_elements_lib_module_assets_closeicon" />
<resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@drawable/assets_bootsplash_logo,@drawable/assets_bootsplash_logo_512,@drawable/assets_bootsplash_ru,@drawable/assets_bootsplash_uz,@drawable/assets_bootsplash_passportsample,@drawable/assets_bootsplash_telegram,@drawable/assets_bootsplash_local,@drawable/assets_bootsplash_avia,@drawable/assets_bootsplash_auto,@drawable/assets_bootsplash_shablon,@drawable/assets_bootsplash_step_1,@drawable/assets_bootsplash_step_2,@drawable/assets_bootsplash_step_3,@drawable/node_modules_reactnavigation_elements_lib_module_assets_searchicon,@drawable/node_modules_reactnavigation_elements_lib_module_assets_backicon,@drawable/node_modules_reactnavigation_elements_lib_module_assets_backiconmask,@drawable/node_modules_reactnavigation_elements_lib_module_assets_clearicon,@drawable/node_modules_reactnavigation_elements_lib_module_assets_closeicon" />

View File

@@ -29,7 +29,7 @@ export interface resendPayload {
export interface loginPayload {
phoneNumber: string;
passportSerial: string;
branchId: number;
// branchId: number;
fcmToken: string;
deviceId: string;
deviceType: string;

View File

@@ -1,68 +1,86 @@
"use client"
'use client';
import React, { useEffect, useState, useCallback, useMemo } from "react"
import { type LayoutChangeEvent, View } from "react-native"
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { type LayoutChangeEvent, View } from 'react-native';
import Animated, {
useSharedValue,
useAnimatedStyle,
withRepeat,
withTiming,
cancelAnimation,
Easing,
useAnimatedStyle,
useSharedValue,
withRepeat,
withSequence,
withDelay,
} from "react-native-reanimated"
import Auto from "svg/Auto"
import Avia from "svg/Avia"
withTiming,
} from 'react-native-reanimated';
import Auto from 'svg/Auto';
import Avia from 'svg/Avia';
type Props = {
type: "auto" | "avia"
}
type: 'AUTO' | 'AVIA';
};
const AnimatedIcon = ({ type }: Props) => {
const translateX = useSharedValue(0)
const translateY = useSharedValue(0)
const rotateY = useSharedValue(0)
const direction = useSharedValue(1)
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
const rotateY = useSharedValue(0);
const direction = useSharedValue(1);
const [containerWidth, setContainerWidth] = useState(0)
const iconSize = 40
const [containerWidth, setContainerWidth] = useState(0);
const iconSize = 40;
const onLayout = useCallback((event: LayoutChangeEvent) => {
const { width } = event.nativeEvent.layout
setContainerWidth(width)
const { width } = event.nativeEvent.layout;
setContainerWidth(width);
}, []);
const animationConfig = useMemo(() => ({
const animationConfig = useMemo(
() => ({
duration: 4000,
easing: Easing.linear,
rotationDuration: 300,
arcHeight: -30,
}), []);
}),
[],
);
const createXAnimation = useCallback((maxX: number) => {
const createXAnimation = useCallback(
(maxX: number) => {
return withRepeat(
withSequence(
withTiming(maxX, {
withTiming(
maxX,
{
duration: animationConfig.duration,
easing: animationConfig.easing
}, () => {
easing: animationConfig.easing,
},
() => {
direction.value = -1;
rotateY.value = withTiming(180, { duration: animationConfig.rotationDuration });
}),
withTiming(0, {
duration: animationConfig.duration,
easing: animationConfig.easing
}, () => {
direction.value = 1;
rotateY.value = withTiming(0, { duration: animationConfig.rotationDuration });
})
rotateY.value = withTiming(180, {
duration: animationConfig.rotationDuration,
});
},
),
-1
withTiming(
0,
{
duration: animationConfig.duration,
easing: animationConfig.easing,
},
() => {
direction.value = 1;
rotateY.value = withTiming(0, {
duration: animationConfig.rotationDuration,
});
},
),
),
-1,
);
},
[animationConfig, direction, rotateY],
);
}, [animationConfig, direction, rotateY]);
const createYAnimation = useCallback(() => {
if (type === "avia") {
if (type === 'AVIA') {
return withRepeat(
withSequence(
withTiming(animationConfig.arcHeight, {
@@ -80,9 +98,9 @@ const AnimatedIcon = ({ type }: Props) => {
withTiming(0, {
duration: animationConfig.duration / 2,
easing: Easing.in(Easing.quad),
})
}),
),
-1
-1,
);
}
return withTiming(0, { duration: 100 });
@@ -93,9 +111,20 @@ const AnimatedIcon = ({ type }: Props) => {
const maxX = containerWidth - iconSize;
// eski animatsiyani toxtat
cancelAnimation(translateX);
cancelAnimation(translateY);
// qiymatlarni 0 ga reset qil
translateX.value = 0;
translateY.value = 0;
rotateY.value = 0;
direction.value = 1;
// keyin yangisini qayta boshlash
translateX.value = createXAnimation(maxX);
translateY.value = createYAnimation();
}, [containerWidth, type, createXAnimation, createYAnimation, translateX, translateY]);
}, [containerWidth, type]); // type = activeTab dan keladigan qiymat
const animatedStyle = useAnimatedStyle(() => {
return {
@@ -104,34 +133,43 @@ const AnimatedIcon = ({ type }: Props) => {
{ translateY: translateY.value },
{ rotateY: `${rotateY.value}deg` },
],
}
};
});
const containerStyle = useMemo(() => ({
const containerStyle = useMemo(
() => ({
height: 100,
justifyContent: "center" as const,
backgroundColor: "transparent" as const,
position: "relative" as const,
}), []);
justifyContent: 'center' as const,
backgroundColor: 'transparent' as const,
position: 'relative' as const,
}),
[],
);
const trackStyle = useMemo(() => ({
const trackStyle = useMemo(
() => ({
height: 2,
backgroundColor: "#28A7E850",
position: "absolute" as const,
backgroundColor: '#28A7E850',
position: 'absolute' as const,
top: 25,
left: 0,
right: 0,
}), []);
}),
[],
);
const iconContainerStyle = useMemo(() => ({
position: "absolute" as const,
const iconContainerStyle = useMemo(
() => ({
position: 'absolute' as const,
top: 0,
height: iconSize,
width: iconSize,
}), [iconSize]);
}),
[iconSize],
);
const renderIcon = useMemo(() => {
if (type === "auto") {
if (type === 'AUTO') {
return <Auto color="#28A7E8" width={iconSize} height={iconSize} />;
}
return <Avia color="#28A7E8" width={iconSize} height={iconSize} />;
@@ -144,7 +182,7 @@ const AnimatedIcon = ({ type }: Props) => {
{renderIcon}
</Animated.View>
</View>
)
}
);
};
export default AnimatedIcon;

View File

@@ -1,9 +1,18 @@
import React, { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Alert, Image, StyleSheet, TouchableOpacity, View } from 'react-native';
import {
ActionSheetIOS,
Alert,
Image,
Platform,
StyleSheet,
TouchableOpacity,
View,
} from 'react-native';
import {
Asset,
ImagePickerResponse,
launchCamera,
launchImageLibrary,
MediaType,
} from 'react-native-image-picker';
@@ -20,9 +29,6 @@ export interface FileData {
export interface SingleFileDropProps {
title: string;
onFileSelected?: (file: FileData) => void;
/**
* Ruxsat berilgan MIME tipi (masalan: "image/png" yoki "image/jpeg")
*/
type?: string;
}
@@ -44,7 +50,7 @@ const SingleFileDrop: React.FC<SingleFileDropProps> = ({
const handleImagePickerResponse = useCallback(
(response: ImagePickerResponse) => {
if (response.didCancel) return; // foydalanuvchi bekor qilsa
if (response.didCancel) return;
if (response.errorCode) {
Alert.alert('Xato', response.errorMessage || 'Rasmni yuklashda xato');
return;
@@ -53,7 +59,6 @@ const SingleFileDrop: React.FC<SingleFileDropProps> = ({
const asset: Asset | undefined = response.assets?.[0];
if (!asset || !asset.uri || !asset.type || !asset.base64) return;
// faqat belgilangan tipdagi fayllarni qabul qilish
if (!asset.type.startsWith('image/')) {
Alert.alert('Xato', 'Faqat rasm fayllarni yuklashingiz mumkin');
return;
@@ -77,6 +82,32 @@ const SingleFileDrop: React.FC<SingleFileDropProps> = ({
launchImageLibrary(imagePickerOptions, handleImagePickerResponse);
}, [imagePickerOptions, handleImagePickerResponse]);
const openCamera = useCallback((): void => {
launchCamera(imagePickerOptions, handleImagePickerResponse);
}, [imagePickerOptions, handleImagePickerResponse]);
const openPicker = useCallback(() => {
if (Platform.OS === 'ios') {
ActionSheetIOS.showActionSheetWithOptions(
{
options: ['Bekor qilish', 'Kamera', 'Galereya'],
cancelButtonIndex: 0,
},
buttonIndex => {
if (buttonIndex === 1) openCamera();
else if (buttonIndex === 2) openGallery();
},
);
} else {
// Android uchun oddiy alert-style tanlov
Alert.alert(t('Fayl tanlash'), t('Qaysi usulda yuklamoqchisiz?'), [
{ text: t('Bekor qilish'), style: 'cancel' },
{ text: t('Kamera'), onPress: openCamera },
{ text: t('Galereya'), onPress: openGallery },
]);
}
}, [openCamera, openGallery]);
const UploadIcon = useMemo(
() => () =>
(
@@ -122,7 +153,7 @@ const SingleFileDrop: React.FC<SingleFileDropProps> = ({
}, [selectedImage, title, UploadIcon]);
return (
<TouchableOpacity style={styles.dropSection} onPress={openGallery}>
<TouchableOpacity style={styles.dropSection} onPress={openPicker}>
{renderContent}
</TouchableOpacity>
);

View File

@@ -230,5 +230,20 @@
"Xatolik yuz berdi": "Произошла ошибка",
"Passport qo'shishda xatolik yuz berdi": "Ошибка добавления паспорта",
"Kodsiz tovarlar": "Товары без кода",
"Hech qanday ma'lumot topilmadi": "Нет данных"
"Hech qanday ma'lumot topilmadi": "Нет данных",
"Foydalanish shartlari va qoidalari": "Условия и правила использования",
"Kamera": "Камера",
"Galereya": "Галерея",
"Fayl tanlash": "Выбрать файл",
"Qaysi usulda yuklamoqchisiz?": "Каким способом вы хотите загрузить?",
"Umumiy qoidalar": "Общие положения",
"Biz sizdan Xitoy xalq Respublikasi Davlatidan sotib olingan tovarlarni olib kelishda quyidagi tovarlarni jonatmaslikka rozilik so'raymiz.": "Мы просим Вашего согласия не отправлять следующие товары при ввозе товаров, приобретенных в Китайской Народной Республике.",
"Taqiqlangan tovarlar": "Запрещённые товары",
"Taqiqlangan tovarlar matni": "В соответствии с Указом Президента Республики Узбекистан от 25 декабря 1998 года No УП-2160 запрещается ввоз в Республику Узбекистан: брошюр, фотографий, видео, аудиопродукции, агитационных материалов и печатных изданий, направленных на нанесение ущерба государству и обществу; пропаганды нарушений порядка, территориальной целостности, политической независимости и государственного суверенитета, войны, терроризма, насилия, национальной идентичности и религиозной ненависти, расизма и его многообразия (антисемитизм, фашизм), а также порнографических материалов.",
"Tovarlar": "Запрещается ввоз: взрывчатых, ядовитых, легковоспламеняющихся, радиоактивных веществ, продуктов питания, лекарственных средств, медицинских изделий и оборудования, полиграфического оборудования, мобильных телефонов, зубной пасты (порошка), рационов, USB-флешек, различных флеш-карт и других аналогичных товаров.",
"Agar sizda savollar bolsa, quyidagi aloqa manzillaridan foydalanishingiz mumkin:": "Если у вас есть вопросы, вы можете воспользоваться следующими контактами:",
"Aloqa uchun": "Для связи",
"Email": "Электронная почта",
"Telegram": "Телеграм",
"Roziman": "Согласен"
}

View File

@@ -42,13 +42,13 @@
"foydalanuvchi_majburiyatlari": "2. Foydalanuvchi majburiyatlari",
"foydalanuvchi_majburiyatlari_text": "• To'g'ri va aniq ma'lumotlar taqdim etish\n• Boshqa foydalanuvchilarning huquqlarini hurmat qilish\n• Tizimdan noto'g'ri maqsadlarda foydalanmaslik\n• Xavfsizlik qoidalariga rioya qilish",
"maxfiylik_siyosati": "3. Maxfiylik siyosati",
"maxfiylik_siyosati_text": "Sizning shaxsiy ma'lumotlaringiz maxfiylik siyosatimizga muvofiq himoyalanadi. Biz sizning ma'lumotlaringizni uchinchi shaxslarga bermaydi va xavfsiz saqlashni ta'minlaymiz.",
"maxfiylik_siyosati_text": "Sizning shaxsiy ma'lumotlaringiz maxfiylik siyosatimizga muvofiq himoyalanadi. Biz sizning ma'lumotlaringizni uchinchi shaxslarga bermaymiz va xavfsiz saqlashni ta'minlaymiz.",
"javobgarlik": "4. Javobgarlik",
"javobgarlik_text": "Kompaniya ilovadan foydalanish natijasida yuzaga kelishi mumkin bo'lgan zararlar uchun javobgar emas. Foydalanuvchi o'z harakatlari uchun to'liq javobgarlikni o'z zimmasiga oladi.",
"shartlarni_ozgartirish": "5. Shartlarni o'zgartirish",
"shartlarni_ozgartirish_text": "Kompaniya ushbu shartlarni istalgan vaqtda o'zgartirish huquqini o'zida saqlab qoladi. O'zgarishlar ilovada e'lon qilinadi va kuchga kirish sanasi ko'rsatiladi.",
"aloqa": "6. Aloqa",
"aloqa_text": "Savollar yoki takliflar bo'lsa, biz bilan quyidagi manzil orqali bog'laning:\nEmail: support@company.uz\nTelefon: +998 71 123 45 67",
"aloqa_text": "Savollar yoki takliflar bo'lsa, biz bilan quyidagi manzil orqali bog'laning:",
"oxirgi_yangilanish": "Oxirgi yangilanish:",
"roziman": "Roziman",
"Shaxsiy maʼlumotlar": "Shaxsiy maʼlumotlar",
@@ -111,7 +111,7 @@
"Filiallarimiz ro'yhati ilovada mavjud": "Filiallarimiz ro'yhati ilovada mavjud",
"Agar siz yashayotgan hududda bizning filialimiz mavjud bo'lmasa, o'zingizga eng yaqin bo'lgan filialni tanlab, ro'yhatdan o'tishingiz mumkin.": "Agar siz yashayotgan hududda bizning filialimiz mavjud bo'lmasa, o'zingizga eng yaqin bo'lgan filialni tanlab, ro'yhatdan o'tishingiz mumkin.",
"ya'ni, qancha gramm mahsulot olsangiz, shuncha og'irligi (gramm) uchun to'lov qilasiz.": "ya'ni, qancha gramm mahsulot olsangiz, shuncha og'irligi (gramm) uchun to'lov qilasiz.",
"(Bu kargo narxini osishiga olib keladi. Seriyali buyularni avto kargo orqali olib kelish arzonga tushadi)": "(Bu kargo narxini osishiga olib keladi. Seriyali buyularni avto kargo orqali olib kelish arzonga tushadi)",
"(Bu kargo narxini osishiga olib keladi. Seriyali buyularni avto kargo orqali olib kelish arzonga tushadi)": "(Bu kargo narxini osishiga olib keladi. Seriyali buyumlarni avto kargo orqali olib kelish arzonga tushadi)",
"Avto kargoda bir mahsulotdan seriyali ravishda, istalgan katta miqdorda xarid qilish mumkin. Doimiy ravishda kop yuk oluvchilar uchun maxsus chegirmali narxlarimiz mavjud.": "Avto kargoda bir mahsulotdan seriyali ravishda, istalgan katta miqdorda xarid qilish mumkin. Doimiy ravishda kop yuk oluvchilar uchun maxsus chegirmali narxlarimiz mavjud.",
"dan boshlab": "dan boshlab",
@@ -179,8 +179,8 @@
"Yopish": "Yopish",
"Passportlarim": "Passportlarim",
"Hali pasport qo'shilmagan": "Hali pasport qo'shilmagan",
"Yangi pasport qo'shish uchun tugmani bosing": "Yangi pasport qo'shish uchun tugmani bosing",
"Yangi pasport qo'shish": "Yangi pasport qo'shish",
"Yangi pasport qo'shish uchun tugmani bosing": "Yangi passport qo'shish uchun tugmani bosing",
"Yangi pasport qo'shish": "Yangi passport qo'shish",
"Passport malumotlarim": "Passport malumotlarim",
"Tez ID": "Tez ID",
"Toliq ismi": "Toliq ismi",
@@ -231,5 +231,20 @@
"Yuborish": "Yuborish",
"Passport qo'shishda xatolik yuz berdi": "Passport qo'shishda xatolik yuz berdi",
"Xatolik yuz berdi": "Xatolik yuz berdi",
"Kodsiz tovarlar": "Kodsiz tovarlar"
"Kodsiz tovarlar": "Kodsiz tovarlar",
"Kamera": "Kamera",
"Galereya": "Galereya",
"Fayl tanlash": "Fayl tanlash",
"Qaysi usulda yuklamoqchisiz?": "Qaysi usulda yuklamoqchisiz?",
"Foydalanish shartlari va qoidalari": "Foydalanish shartlari va qoidalari",
"Umumiy qoidalar": "Umumiy qoidalar",
"Biz sizdan Xitoy xalq Respublikasi Davlatidan sotib olingan tovarlarni olib kelishda quyidagi tovarlarni jonatmaslikka rozilik so'raymiz.": "Biz sizdan Xitoy xalq Respublikasi Davlatidan sotib olingan tovarlarni olib kelishda quyidagi tovarlarni jonatmaslikka rozilik so'raymiz.",
"Taqiqlangan tovarlar": "Taqiqlangan tovarlar",
"Taqiqlangan tovarlar matni": " Ozbekiston Respublikasi Prezidentining 1998-yil 25-dekabrdagi UP-2160-son Farmoniga asosan Ozbekiston Respublikasiga olib kirish qudagilar taqiqlanadi: davlat va jamiyatga putur yetkazishga qaratilgan risolalar, fotosuratlar, videolar, audiomahsulotlar, tashviqot materiallari va bosma nashrlar; tartib, hududiy yaxlitlik, siyosiy mustaqillik va davlat suverenitetini buzish, urush, terrorizm, zoravonlik, milliy oziga xoslik va diniy nafrat, irqchilik va uning xilma-xilligini (antisemitizm, fashizm), shuningdek, pornografik materiallarga targib qilish.",
"Tovarlar": "Yonuvchan, tez yonuvchi, portlovchi, zaharli, radioaktiv moddalar, oziq-ovqat mahsulotlari, dori-darmonlar, tibbiy buyumlar, tibbiy asbob-uskunalar, poligrafiya uskunalari, uyali telefonlar, tish pastasi (chang), ratsionlar, USB flesh-disk, turli xil flesh-kartalar va boshqa shunga oxshash mahsulotlarni olib kirish taqiqlanadi.",
"Agar sizda savollar bolsa, quyidagi aloqa manzillaridan foydalanishingiz mumkin:": "Agar sizda savollar bolsa, quyidagi aloqa manzillaridan foydalanishingiz mumkin:",
"Aloqa uchun": "Aloqa uchun",
"Email": "Email",
"Telegram": "Telegram",
"Roziman": "Roziman"
}

View File

@@ -5,7 +5,6 @@ export const loginSchema = z.object({
phone: z.string().min(12, 'Xato raqam kiritildi'),
passportSeriya: z.string().length(2, '2 ta harf kerak'),
passportNumber: z.string().length(7, '7 ta raqam kerak'),
branchId: z.number().min(1, 'Filialni tanlang'),
});
export type LoginFormType = z.infer<typeof loginSchema>;

View File

@@ -1,10 +1,9 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { useMutation, useQuery } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import { authApi } from 'api/auth';
import { loginPayload } from 'api/auth/type';
import { Branch, branchApi } from 'api/branch';
import AppText from 'components/AppText';
import formatPhone from 'helpers/formatPhone';
import React, { useCallback, useMemo, useRef, useState } from 'react';
@@ -24,9 +23,7 @@ import { SafeAreaView } from 'react-native-safe-area-context';
import Logo from 'screens/../../assets/bootsplash/logo_512.png';
import { LoginFormType, loginSchema } from 'screens/auth/login/lib/form';
import LanguageSelector from 'screens/auth/select-language/SelectLang';
import ArrowDown from 'svg/ArrowDown';
import ArrowLeft from 'svg/ArrowLeft';
import ArrowUp from 'svg/ArrowUp';
import { useUserStore } from '../lib/userstore';
import { Loginstyle } from './styled';
@@ -46,10 +43,10 @@ const Login = () => {
const { setUser, setExpireTime } = useUserStore(state => state);
const [error, setError] = useState<string>();
const [rawPhone, setRawPhone] = useState('+998');
const { data: branchList } = useQuery({
queryKey: ['branchList'],
queryFn: branchApi.branchList,
});
// const { data: branchList } = useQuery({
// queryKey: ['branchList'],
// queryFn: branchApi.branchList,
// });
// const [firebaseToken, setFirebseToken] = useState<{
// fcmToken: string;
// deviceId: string;
@@ -109,7 +106,6 @@ const Login = () => {
const onSubmit = (data: LoginFormType) => {
mutate({
branchId: data.branchId,
phoneNumber: data.phone,
passportSerial: `${data.passportSeriya.toUpperCase()}${
data.passportNumber
@@ -164,7 +160,7 @@ const Login = () => {
style={Loginstyle.container}
behavior={keyboardBehavior}
>
<ScrollView style={{ flex: 1 }}>
<ScrollView style={{ flex: 1 }} keyboardShouldPersistTaps="handled">
<View style={Loginstyle.scrollContainer}>
<View style={Loginstyle.loginContainer}>
<AppText style={Loginstyle.title}>
@@ -251,7 +247,7 @@ const Login = () => {
)}
</View>
<Controller
{/* <Controller
control={control}
name="branchId"
render={({ field: { value } }) => (
@@ -314,7 +310,7 @@ const Login = () => {
)}
</View>
)}
/>
/> */}
<TouchableOpacity
onPress={handleSubmit(onSubmit)}

View File

@@ -1,8 +1,6 @@
'use client';
import { zodResolver } from '@hookform/resolvers/zod';
// import { getApp } from '@react-native-firebase/app';
// import { getMessaging, getToken } from '@react-native-firebase/messaging';
import {
type RouteProp,
useNavigation,
@@ -15,15 +13,15 @@ import { registerPayload } from 'api/auth/type';
import { Branch, branchApi } from 'api/branch';
import AppText from 'components/AppText';
import formatPhone from 'helpers/formatPhone';
import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import {
ActivityIndicator,
Animated,
ImageBackground,
Keyboard,
KeyboardAvoidingView,
Platform,
ScrollView,
TextInput,
TouchableOpacity,
@@ -66,37 +64,6 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
queryFn: branchApi.branchList,
});
// const [firebaseToken, setFirebseToken] = useState<{
// fcmToken: string;
// deviceId: string;
// deviceName: string;
// deviceType: string;
// } | null>();
// const app = getApp();
// const messaging = getMessaging(app);
// const getDeviceData = async () => {
// try {
// const fcmToken = await getToken(messaging);
// return {
// fcmToken,
// deviceId: await DeviceInfo.getUniqueId(),
// deviceName: await DeviceInfo.getDeviceName(),
// deviceType: await DeviceInfo.getDeviceType(),
// };
// } catch (e) {
// console.log('Xato:', e);
// return null;
// }
// };
// useEffect(() => {
// getDeviceData().then(data => {
// setFirebseToken(data);
// });
// }, []);
const { mutate, isPending } = useMutation({
mutationFn: (payload: registerPayload) => authApi.register(payload),
onSuccess: res => {
@@ -114,12 +81,12 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
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: {
@@ -130,6 +97,12 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
},
});
// 🔑 Input ref'lar
const firstNameRef = useRef<TextInput>(null);
const lastNameRef = useRef<TextInput>(null);
const phoneRef = useRef<TextInput>(null);
const addressRef = useRef<TextInput>(null);
const onSubmit = (data: FirstStepFormType) => {
setUser({
firstName: data.firstName,
@@ -195,7 +168,7 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
imageStyle={{
opacity: 0.1,
height: '100%',
width: '100%',
width: '90%',
transform: [{ scale: 1 }],
}}
>
@@ -207,12 +180,13 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
<LanguageSelector />
</View>
<KeyboardAvoidingView
style={RegisterStyle.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={{ flex: 1 }}
behavior="padding"
keyboardVerticalOffset={50}
>
<ScrollView
showsVerticalScrollIndicator={false}
style={RegisterStyle.content}
contentContainerStyle={{ flexGrow: 1 }}
keyboardShouldPersistTaps="handled"
>
<View style={RegisterStyle.scrollContainer}>
<View style={RegisterStyle.loginContainer}>
@@ -220,18 +194,22 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
{t("Ro'yxatdan o'tish")}
</AppText>
{/* Ism */}
<Controller
control={control}
name="firstName"
render={({ field: { onChange, value } }) => (
<View>
<AppText style={RegisterStyle.label}>{t('Ism')} </AppText>
<AppText style={RegisterStyle.label}>{t('Ism')}</AppText>
<TextInput
ref={firstNameRef}
style={RegisterStyle.input}
placeholder={t('Ismingiz')}
onChangeText={onChange}
value={value}
placeholderTextColor={'#D8DADC'}
returnKeyType="next"
onSubmitEditing={() => lastNameRef.current?.focus()}
/>
{errors.firstName && (
<AppText style={RegisterStyle.errorText}>
@@ -241,20 +219,25 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
</View>
)}
/>
{/* Familiya */}
<Controller
control={control}
name="lastName"
render={({ field: { onChange, value } }) => (
<View>
<AppText style={RegisterStyle.label}>
{t('Familiya')}{' '}
{t('Familiya')}
</AppText>
<TextInput
ref={lastNameRef}
style={RegisterStyle.input}
placeholder={t('Familiyangiz')}
placeholderTextColor={'#D8DADC'}
onChangeText={onChange}
value={value}
returnKeyType="next"
onSubmitEditing={() => phoneRef.current?.focus()}
/>
{errors.lastName && (
<AppText style={RegisterStyle.errorText}>
@@ -264,6 +247,8 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
</View>
)}
/>
{/* Telefon raqami */}
<Controller
control={control}
name="phoneNumber"
@@ -275,6 +260,7 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
{t('Telefon raqami')}
</AppText>
<TextInput
ref={phoneRef}
keyboardType="numeric"
placeholder="+998 __ ___-__-__"
value={formatted}
@@ -289,6 +275,10 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
style={RegisterStyle.input}
placeholderTextColor="#D8DADC"
maxLength={17}
returnKeyType="next"
onSubmitEditing={
() => setFilialDropdownVisible(true) // ❗ Branch select ochiladi
}
/>
{errors.phoneNumber && (
<AppText style={RegisterStyle.errorText}>
@@ -299,20 +289,23 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
);
}}
/>
{/* Filial (dropdown) */}
<Controller
control={control}
name="branchId"
render={({ field: { value } }) => (
<View style={{ position: 'relative' }}>
<AppText style={RegisterStyle.label}>
{t('Filial')}{' '}
{t('Filial')}
</AppText>
<View style={RegisterStyle.input}>
<TouchableOpacity
style={RegisterStyle.selector}
onPress={() =>
setFilialDropdownVisible(prev => !prev)
}
onPress={() => {
setFilialDropdownVisible(prev => !prev);
Keyboard.dismiss();
}}
>
<AppText
style={
@@ -344,6 +337,11 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
onPress={() => {
setValue('branchId', item.id);
setFilialDropdownVisible(false);
// keyingi inputga focus
setTimeout(
() => addressRef.current?.focus(),
200,
);
}}
>
<AppText
@@ -364,6 +362,8 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
</View>
)}
/>
{/* Manzil */}
<Controller
control={control}
name="address"
@@ -373,6 +373,7 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
{t('Manzilingizni kiriting')}
</AppText>
<TextInput
ref={addressRef}
style={RegisterStyle.input}
placeholder={t(
"Toshkent Shahri, Mirzo Ulug'bek tumani...",
@@ -380,15 +381,21 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
placeholderTextColor={'#D8DADC'}
onChangeText={onChange}
value={value}
returnKeyType="done"
onSubmitEditing={
() => setRecommendedDropdownVisible(true) // ❗ recommend select ochiladi
}
/>
{errors.lastName && (
{errors.address && (
<AppText style={RegisterStyle.errorText}>
{t(errors.lastName.message || '')}
{t(errors.address.message || '')}
</AppText>
)}
</View>
)}
/>
{/* Recommend (dropdown) */}
<Controller
control={control}
name="recommend"
@@ -458,6 +465,8 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
</View>
)}
/>
{/* Terms */}
<View style={RegisterStyle.termsContainer}>
<TouchableOpacity
style={RegisterStyle.checkboxContainer}
@@ -503,7 +512,6 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
</View>
</TouchableOpacity>
</View>
<TouchableOpacity
onPress={handleSubmit(onSubmit)}
style={[

View File

@@ -21,8 +21,8 @@ import {
Dimensions,
Image,
ImageBackground,
Keyboard,
KeyboardAvoidingView,
Platform,
ScrollView,
TextInput,
TouchableOpacity,
@@ -55,8 +55,11 @@ const SecondStep = () => {
const passportNumberRef = useRef<TextInput>(null);
const [checkboxAnimation] = useState(new Animated.Value(1));
const [inputValue, setInputValue] = useState('');
const [error, setError] = useState<string>('');
const { firstName, lastName } = useUserStore(state => state);
const passportSeriyaRef = useRef<TextInput>(null);
const jshshirRef = useRef<TextInput>(null);
const birthDateRef = useRef<TextInput>(null);
const navigation =
useNavigation<NativeStackNavigationProp<RootStackParamList, 'Login'>>();
const route = useRoute<RouteProp<RootStackParamList, 'Register'>>();
@@ -76,8 +79,8 @@ const SecondStep = () => {
onSuccess: res => {
navigation.navigate('Home');
},
onError: err => {
console.dir(err);
onError: (err: any) => {
setError(err.response.data);
},
});
@@ -145,10 +148,14 @@ const SecondStep = () => {
</View>
<KeyboardAvoidingView
style={RegisterStyle.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={{ flex: 1 }}
behavior="padding"
keyboardVerticalOffset={50}
>
<ScrollView
contentContainerStyle={{ flexGrow: 1 }}
keyboardShouldPersistTaps="handled"
>
<ScrollView style={RegisterStyle.content}>
<View style={RegisterStyle.scrollContainer}>
<View style={RegisterStyle.loginContainer}>
<AppText style={RegisterStyle.title}>
@@ -170,6 +177,7 @@ const SecondStep = () => {
name="passportSeriya"
render={({ field: { onChange, value } }) => (
<TextInput
ref={passportSeriyaRef}
style={[
RegisterStyle.input,
RegisterStyle.seriyaInput,
@@ -178,12 +186,16 @@ const SecondStep = () => {
maxLength={2}
autoCapitalize="characters"
value={value}
returnKeyType="next"
onChangeText={text => {
onChange(text);
if (text.length === 2)
passportNumberRef.current?.focus();
}}
placeholderTextColor="#D8DADC"
onSubmitEditing={() =>
passportNumberRef.current?.focus()
}
/>
)}
/>
@@ -201,10 +213,12 @@ const SecondStep = () => {
maxLength={7}
keyboardType="numeric"
value={value}
returnKeyType="next"
onChangeText={text => {
const onlyNumbers = text.replace(/[^0-9]/g, '');
onChange(onlyNumbers);
}}
onSubmitEditing={() => jshshirRef.current?.focus()}
placeholderTextColor="#D8DADC"
/>
)}
@@ -228,14 +242,17 @@ const SecondStep = () => {
</AppText>
<TextInput
style={RegisterStyle.input}
ref={jshshirRef}
placeholder="12345678901234"
placeholderTextColor="#D8DADC"
keyboardType="numeric"
maxLength={14}
returnKeyType="next"
value={value}
onChangeText={text =>
onChange(text.replace(/[^0-9]/g, ''))
}
onSubmitEditing={() => birthDateRef.current?.focus()}
/>
{errors.jshshir && (
<AppText style={RegisterStyle.errorText}>
@@ -267,10 +284,12 @@ const SecondStep = () => {
RegisterStyle.input,
{ flex: 1, borderWidth: 0 },
]}
ref={birthDateRef}
placeholder="dd/mm/yyyy"
placeholderTextColor="#D8DADC"
keyboardType="numeric"
value={value}
returnKeyType="done"
onChangeText={text => {
let cleaned = text
.replace(/[^\d]/g, '')
@@ -326,6 +345,7 @@ const SecondStep = () => {
setValue('birthDate', formatted);
}}
onSubmitEditing={handleSubmit(onSubmit)}
/>
<TouchableOpacity
onPress={() => setDatePickerVisibility(true)}
@@ -377,7 +397,10 @@ const SecondStep = () => {
>
<SingleFileDrop
title={t('Old tomon')}
onFileSelected={setFrontImage}
onFileSelected={file => {
setFrontImage(file);
Keyboard.dismiss();
}}
/>
</View>
<View
@@ -388,11 +411,18 @@ const SecondStep = () => {
>
<SingleFileDrop
title={t('Orqa tomon')}
onFileSelected={setBackImage}
onFileSelected={file => {
setBackImage(file);
Keyboard.dismiss();
}}
/>
</View>
</View>
{error && (
<AppText style={[RegisterStyle.errorText, { fontSize: 14 }]}>
{error}
</AppText>
)}
{/* BUTTON */}
<TouchableOpacity
onPress={handleSubmit(onSubmit)}

View File

@@ -3,7 +3,14 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import AppText from 'components/AppText';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native';
import {
Linking,
ScrollView,
StyleSheet,
TouchableOpacity,
View,
} from 'react-native';
import AppLink from 'react-native-app-link';
import { SafeAreaView } from 'react-native-safe-area-context';
import ArrowLeft from 'svg/ArrowLeft';
import { RootStackParamList } from 'types/types';
@@ -16,11 +23,21 @@ type TermsScreenNavigationProp = NativeStackNavigationProp<
const TermsAndConditions = () => {
const navigation = useNavigation<TermsScreenNavigationProp>();
const { t } = useTranslation();
const handleAgree = () => {
navigation.goBack();
};
const openTelegram = React.useCallback(async () => {
try {
await AppLink.maybeOpenURL('tg://resolve?domain=cpost_admin', {
appName: 'Telegram',
appStoreId: 686449807,
appStoreLocale: 'us',
playStoreId: 'org.telegram.messenger',
});
} catch (err) {
Linking.openURL('https://t.me/cpost_admin');
}
}, []);
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
@@ -35,16 +52,24 @@ const TermsAndConditions = () => {
<ScrollView style={styles.content} showsVerticalScrollIndicator={false}>
<AppText style={styles.title}>
{t('foydalanish_shartlari_va_qoidalari')}
{t('Foydalanish shartlari va qoidalari')}
</AppText>
<AppText style={styles.sectionTitle}>{t('umumiy_qoidalar')}</AppText>
<AppText style={styles.text}>{t('umumiy_qoidalar_text')}</AppText>
<AppText style={styles.text}>
{t(
"Biz sizdan Xitoy xalq Respublikasi Davlatidan sotib olingan tovarlarni olib kelishda quyidagi tovarlarni jonatmaslikka rozilik so'raymiz.",
)}
</AppText>
<AppText style={styles.sectionTitle}>1. {t('Umumiy qoidalar')}</AppText>
<AppText style={styles.text}>{t('Taqiqlangan tovarlar matni')}</AppText>
<AppText style={styles.text}>{t('Tovarlar')}</AppText>
<AppText style={styles.sectionTitle}>
{t('foydalanuvchi_majburiyatlari')}
</AppText>
<AppText style={styles.text}>
<AppText style={[styles.text, { textAlign: 'left' }]}>
{t('foydalanuvchi_majburiyatlari_text')}
</AppText>
@@ -63,17 +88,30 @@ const TermsAndConditions = () => {
<AppText style={styles.sectionTitle}>{t('aloqa')}</AppText>
<AppText style={styles.text}>{t('aloqa_text')}</AppText>
<TouchableOpacity
onPress={() => Linking.openURL('mailto:info@cpost.uz')}
>
<AppText style={styles.text}>{t('Email')}: info@cpost.uz</AppText>
</TouchableOpacity>
<TouchableOpacity onPress={() => Linking.openURL('tel:+998951264477')}>
<AppText style={styles.text}>
{t('Telefon')}: +998 (95) 126 44 77
</AppText>
</TouchableOpacity>
<TouchableOpacity onPress={openTelegram}>
<AppText style={styles.text}>{t('Telegram')}: @cpost_admin</AppText>
</TouchableOpacity>
<View style={styles.footer}>
<AppText style={styles.footerText}>
{t('oxirgi_yangilanish')} {new Date().toLocaleDateString('uz-UZ')}
{t('oxirgi_yangilanish')} 01/09/2025
</AppText>
</View>
</ScrollView>
<View style={styles.bottomContainer}>
<TouchableOpacity style={styles.agreeButton} onPress={handleAgree}>
<AppText style={styles.agreeButtonText}>{t('roziman')}</AppText>
<AppText style={styles.agreeButtonText}>{t('Roziman')}</AppText>
</TouchableOpacity>
</View>
</SafeAreaView>

View File

@@ -41,10 +41,16 @@ const SelectAuth = () => {
styles.logoImage,
{
width: 180,
height: 180,
height: 150,
marginLeft: 35,
},
]}
/>
<AppText
style={[styles.logoText, { fontSize: 52, fontWeight: '900' }]}
>
CPOST
</AppText>
{/* <AppText style={[styles.logoText, { fontSize: 32 }]}>CPOST</AppText> */}
</View>

View File

@@ -6,7 +6,14 @@ import AppText from 'components/AppText';
import LayoutTwo from 'components/LayoutTwo';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native';
import {
Linking,
ScrollView,
StyleSheet,
TouchableOpacity,
View,
} from 'react-native';
import AppLink from 'react-native-app-link';
import Tabs from '../../home/ui/Tabs';
interface CargoPricesProps {}
@@ -25,6 +32,19 @@ const CargoPrices = (props: CargoPricesProps) => {
refetch();
}, [activeTab]);
const openTelegram = React.useCallback(async () => {
try {
await AppLink.maybeOpenURL('tg://resolve?domain=cpost_admin', {
appName: 'Telegram',
appStoreId: 686449807,
appStoreLocale: 'us',
playStoreId: 'org.telegram.messenger',
});
} catch (err) {
Linking.openURL('https://t.me/cpost_admin');
}
}, []);
return (
<LayoutTwo title={t('Kargo narxlari')}>
<ScrollView style={{ flex: 1 }}>
@@ -105,7 +125,10 @@ const CargoPrices = (props: CargoPricesProps) => {
</AppText>
</View>
<AppText style={[styles.desc, { color: '#000000' }]}>
{t('Batafsil')}: @CPcargo_admin
{t('Batafsil')}:{' '}
<AppText style={{ color: '#28A7E8' }} onPress={openTelegram}>
@cpost_admin
</AppText>
</AppText>
</View>
<View style={[styles.card]}>
@@ -193,7 +216,10 @@ const CargoPrices = (props: CargoPricesProps) => {
</AppText>
</View>
<AppText style={[styles.desc, { color: '#000000' }]}>
{t('Batafsil')}: @CPcargo_admin
{t('Batafsil')}:{' '}
<AppText style={{ color: '#28A7E8' }} onPress={openTelegram}>
@cpost_admin
</AppText>
</AppText>
</View>
<View style={[styles.card]}>

View File

@@ -75,7 +75,11 @@ const Home = () => {
removeClippedSubviews={true}
keyboardShouldPersistTaps="handled"
>
<PartyCarousel autoData={autoData} aviaData={aviaData} />
<PartyCarousel
autoData={autoData}
aviaData={aviaData}
activeTab={activeTab}
/>
<Tabs setActiveTab={setActiveTab} activeTab={activeTab} />
{activeTabContent}
<Pages />

View File

@@ -1,6 +1,6 @@
import AnimatedIcon from 'components/AnimatedIcon';
import AppText from 'components/AppText';
import React, { useMemo, useRef, useState } from 'react';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
FlatList,
@@ -10,6 +10,11 @@ import {
useWindowDimensions,
View,
} from 'react-native';
import Animated, {
useAnimatedStyle,
useSharedValue,
withTiming,
} from 'react-native-reanimated';
import Auto from 'svg/Auto';
import Avia from 'svg/Avia';
import BoxCreate from 'svg/BoxCreate';
@@ -18,16 +23,35 @@ import { HomeStyle } from './styled';
const PartyCarousel = ({
aviaData,
activeTab,
autoData,
}: {
autoData: any;
aviaData: any;
activeTab: 'AUTO' | 'AVIA';
}) => {
const { width: screenWidth } = useWindowDimensions();
const cardWidth = screenWidth * 0.95;
const styles = useMemo(() => HomeStyle(), []);
const { t } = useTranslation();
// animatsiya
const opacity = useSharedValue(0);
const translateY = useSharedValue(20);
useEffect(() => {
opacity.value = 1;
translateY.value = 0;
opacity.value = withTiming(1, { duration: 500 });
translateY.value = withTiming(0, { duration: 500 });
}, [activeTab]);
const animatedStyle = useAnimatedStyle(() => ({
opacity: opacity.value,
transform: [{ translateY: translateY.value }],
}));
// status config
const statusConfig: any = {
COLLECTING: {
backgroundColor: '#28A7E8',
@@ -56,7 +80,7 @@ const PartyCarousel = ({
},
};
// calendarList tayyorlash
// calendarList faqat activeTab uchun
const calendarList = useMemo(() => {
const data: any[] = [];
const weekdays = [
@@ -89,10 +113,14 @@ const PartyCarousel = ({
});
};
if (activeTab === 'AUTO') {
prepareList(autoData, 'auto');
} else if (activeTab === 'AVIA') {
prepareList(aviaData, 'avia');
}
return data;
}, [autoData, aviaData]);
}, [autoData, aviaData, activeTab]);
const flatListRef = useRef<FlatList>(null);
const today = useMemo(() => {
@@ -102,6 +130,7 @@ const PartyCarousel = ({
}, []);
const [selectedItem, setSelectedItem] = useState<any>(null);
const [modalVisible, setModalVisible] = useState(false);
const renderItem = ({ item, index }: { item: any; index: number }) => {
const isLast = index === calendarList.length - 1;
return (
@@ -171,7 +200,7 @@ const PartyCarousel = ({
{item.cargo.toUpperCase()}
</AppText>
<View style={styles.animatedIconWrapper}>
<AnimatedIcon type={item.cargo} />
<AnimatedIcon type={activeTab} />
</View>
</View>
</>
@@ -181,9 +210,8 @@ const PartyCarousel = ({
};
return (
<>
<Animated.View style={animatedStyle}>
<FlatList
ref={flatListRef}
data={calendarList}
keyExtractor={(_, index) => index.toString()}
renderItem={renderItem}
@@ -233,7 +261,7 @@ const PartyCarousel = ({
</View>
</View>
</Modal>
</>
</Animated.View>
);
};

View File

@@ -12,7 +12,6 @@ import {
ActivityIndicator,
Dimensions,
KeyboardAvoidingView,
Platform,
ScrollView,
TextInput,
TouchableOpacity,
@@ -42,6 +41,17 @@ const CreatePassword = () => {
const [error, setError] = useState(false);
const [frontImage, setFrontImage] = useState<FileData | null>(null);
const [backImage, setBackImage] = useState<FileData | null>(null);
// firstName: '',
// lastName: '',
// birthDate: '',
// passportSeriya: '',
// passportNumber: '',
// jshshir: '',
const firstNameRef = useRef<TextInput>(null);
const lastNameRef = useRef<TextInput>(null);
const birthDateRef = useRef<TextInput>(null);
const passportSeriyaRef = useRef<TextInput>(null);
const jshshirRef = useRef<TextInput>(null);
const { mutate, isPending } = useMutation({
mutationFn: (payload: AddPassportPayload) => {
@@ -105,12 +115,14 @@ const CreatePassword = () => {
});
};
return (
<LayoutTwo title="Yangi pasport qo'shish">
<LayoutTwo title={t("Yangi pasport qo'shish")}>
<KeyboardAvoidingView
style={PassportStyle.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
behavior={'padding'}
keyboardVerticalOffset={30}
>
<ScrollView
keyboardShouldPersistTaps="handled"
showsVerticalScrollIndicator={false}
style={PassportStyle.content}
>
@@ -123,6 +135,9 @@ const CreatePassword = () => {
<View>
<AppText style={PassportStyle.label}>{t('Ism')}</AppText>
<TextInput
ref={firstNameRef}
returnKeyType="next"
onSubmitEditing={() => lastNameRef.current?.focus()}
style={PassportStyle.input}
placeholder={t('Ismingiz')}
onChangeText={onChange}
@@ -146,6 +161,9 @@ const CreatePassword = () => {
{t('Familiya')}
</AppText>
<TextInput
ref={lastNameRef}
returnKeyType="next"
onSubmitEditing={() => passportSeriyaRef.current?.focus()}
style={PassportStyle.input}
placeholder={t('Familiyangiz')}
placeholderTextColor={'#D8DADC'}
@@ -170,6 +188,11 @@ const CreatePassword = () => {
name="passportSeriya"
render={({ field: { onChange, value } }) => (
<TextInput
ref={passportSeriyaRef}
returnKeyType="next"
onSubmitEditing={() =>
passportNumberRef.current?.focus()
}
style={[PassportStyle.input, PassportStyle.seriyaInput]}
placeholder="AA"
maxLength={2}
@@ -191,6 +214,8 @@ const CreatePassword = () => {
render={({ field: { onChange, value } }) => (
<TextInput
ref={passportNumberRef}
returnKeyType="next"
onSubmitEditing={() => jshshirRef.current?.focus()}
style={[PassportStyle.input, PassportStyle.raqamInput]}
placeholder="1234567"
maxLength={7}
@@ -220,6 +245,9 @@ const CreatePassword = () => {
render={({ field: { onChange, value } }) => (
<TextInput
style={PassportStyle.input}
ref={jshshirRef}
returnKeyType="next"
onSubmitEditing={() => birthDateRef.current?.focus()}
placeholder="12345678901234"
placeholderTextColor={'#D8DADC'}
keyboardType="numeric"
@@ -250,6 +278,8 @@ const CreatePassword = () => {
<View style={[PassportStyle.inputContainer]}>
<TextInput
ref={birthDateRef}
returnKeyType="done"
style={[
PassportStyle.input,
{ flex: 1, borderWidth: 0, padding: 0 },

View File

@@ -51,15 +51,14 @@ const ProfilePages = (props: componentNameProps) => {
const openTelegram = React.useCallback(async () => {
try {
await AppLink.maybeOpenURL('tg://resolve?domain=cpostuz', {
await AppLink.maybeOpenURL('tg://resolve?domain=cpost_admin', {
appName: 'Telegram',
appStoreId: 686449807,
appStoreLocale: 'us',
playStoreId: 'org.telegram.messenger',
});
} catch (err) {
// Agar ilovani ham, storeni ham ochib bolmasa, fallback URL
Linking.openURL('https://t.me/cpostuz');
Linking.openURL('https://t.me/cpost_admin');
}
}, []);

View File

@@ -75,7 +75,7 @@ const TabsAutoWarehouses = () => {
};
const { data } = useQuery({
queryKey: ['warhouses'],
queryKey: ['warhouses_auto'],
queryFn: () => warhouses_api.getWarhouses({ cargoType: 'AUTO' }),
});

View File

@@ -75,7 +75,7 @@ const TabsAviaWarehouses = () => {
};
const { data } = useQuery({
queryKey: ['warhouses'],
queryKey: ['warhouses_avia'],
queryFn: () => warhouses_api.getWarhouses({ cargoType: 'AVIA' }),
});
@@ -108,7 +108,7 @@ const TabsAviaWarehouses = () => {
<View style={[styles.card, { marginRight: isLast ? 0 : 10 }]}>
<View style={styles.titleCard}>
<Kitay width={24 * scale} height={24 * scale} />
<AppText style={styles.title}>China (Auto)</AppText>
<AppText style={styles.title}>China (AVIA)</AppText>
</View>
<View style={styles.infoId}>
<View style={{ gap: 4 * scale, width: '90%' }}>

View File

@@ -100,15 +100,14 @@ Cargo Idsi: ${getMe?.aviaCargoId}
);
const openTelegram = React.useCallback(async () => {
try {
await AppLink.maybeOpenURL('tg://resolve?domain=cpostuz', {
await AppLink.maybeOpenURL('tg://resolve?domain=cpost_admin', {
appName: 'Telegram',
appStoreId: 686449807,
appStoreLocale: 'us',
playStoreId: 'org.telegram.messenger',
});
} catch (err) {
// Agar ilovani ham, storeni ham ochib bolmasa, fallback URL
Linking.openURL('https://t.me/cpostuz');
Linking.openURL('https://t.me/cpost_admin');
}
}, []);
return (

View File

@@ -40,11 +40,14 @@ const SelectLangPage = ({
styles.logoImage,
{
width: 180,
height: 180,
height: 150,
marginLeft: 35,
},
]}
/>
<AppText style={[styles.logoText, { fontSize: 24 }]}>
<AppText
style={[styles.logoText, { fontSize: 42, fontWeight: '700' }]}
>
CPOST
</AppText>
</View>
@@ -59,10 +62,19 @@ const SelectLangPage = ({
<View style={styles.btnContainer}>
<TouchableOpacity
onPress={() => onSelectLang('uz')}
style={styles.button}
style={[
styles.button,
{
backgroundColor: 'none',
borderWidth: 1,
borderColor: '#28A7E8',
},
]}
>
<Image source={UZ} style={styles.flag} />
<AppText style={styles.btnText}>O'zbek tili</AppText>
<AppText style={[styles.btnText, { color: '#28A7E8' }]}>
O'zbek tili
</AppText>
</TouchableOpacity>
<TouchableOpacity
@@ -134,7 +146,7 @@ const styles = StyleSheet.create({
},
flag: {
width: 30,
height: 30,
height: 20,
resizeMode: 'cover',
},
});

View File

@@ -6,7 +6,7 @@
"*": ["./src/*"],
"@screens": ["./src/screens/*"],
"screens": ["./src/screens"],
"components": ["./src/components"],
"components": ["./src/components/*"],
"assets": ["./src/assets"],
"api": ["./src/api"],
"helpers": ["./src/helpers"],