Compare commits
10 Commits
f41451c6b8
...
22dee44306
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22dee44306 | ||
|
|
51e2d778be | ||
|
|
fcd704b258 | ||
|
|
c426b729b9 | ||
|
|
87bd8cdea6 | ||
|
|
684d09e6b5 | ||
|
|
9aac17072f | ||
|
|
38badbe3dd | ||
|
|
b5f6006ae0 | ||
|
|
34fb3e357a |
242
App.tsx
242
App.tsx
@@ -5,7 +5,6 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
|||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
import { toastConfig } from 'components/CustomAlertModal';
|
import { toastConfig } from 'components/CustomAlertModal';
|
||||||
import { navigationRef } from 'components/NavigationRef';
|
import { navigationRef } from 'components/NavigationRef';
|
||||||
import SplashScreen from 'components/SplashScreen';
|
|
||||||
import i18n from 'i18n/i18n';
|
import i18n from 'i18n/i18n';
|
||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { I18nextProvider } from 'react-i18next';
|
import { I18nextProvider } from 'react-i18next';
|
||||||
@@ -13,25 +12,14 @@ import {
|
|||||||
Animated,
|
Animated,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
LogBox,
|
LogBox,
|
||||||
PermissionsAndroid,
|
|
||||||
Platform,
|
|
||||||
StatusBar,
|
StatusBar,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
View,
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import Toast from 'react-native-toast-message';
|
import Toast from 'react-native-toast-message';
|
||||||
|
import SplashScreen from './src/components/SplashScreen';
|
||||||
|
|
||||||
// Screens
|
// Screens
|
||||||
import notifee, { AndroidImportance } from '@notifee/react-native';
|
|
||||||
import { getApp } from '@react-native-firebase/app';
|
|
||||||
import {
|
|
||||||
getInitialNotification,
|
|
||||||
getMessaging,
|
|
||||||
getToken,
|
|
||||||
onMessage,
|
|
||||||
onNotificationOpenedApp,
|
|
||||||
} from '@react-native-firebase/messaging';
|
|
||||||
import DeviceInfo from 'react-native-device-info';
|
|
||||||
import Login from 'screens/auth/login/ui';
|
import Login from 'screens/auth/login/ui';
|
||||||
import Confirm from 'screens/auth/login/ui/Confirm';
|
import Confirm from 'screens/auth/login/ui/Confirm';
|
||||||
import Register from 'screens/auth/registeration/ui';
|
import Register from 'screens/auth/registeration/ui';
|
||||||
@@ -46,7 +34,6 @@ import RestrictedProduct from 'screens/home/restrictedProduct/ui/RestrictedProdu
|
|||||||
import CreatePassword from 'screens/passport/createPassport/ui/CreatePassword';
|
import CreatePassword from 'screens/passport/createPassport/ui/CreatePassword';
|
||||||
import Passport from 'screens/passport/myPassport/ui/Passport';
|
import Passport from 'screens/passport/myPassport/ui/Passport';
|
||||||
import Profile from 'screens/profile/myProfile/ui/Profile';
|
import Profile from 'screens/profile/myProfile/ui/Profile';
|
||||||
import Notifications from 'screens/profile/notifications/ui/Notifications';
|
|
||||||
import AddedLock from 'screens/profile/settings/ui/AddedLock';
|
import AddedLock from 'screens/profile/settings/ui/AddedLock';
|
||||||
import Settings from 'screens/profile/settings/ui/Settings';
|
import Settings from 'screens/profile/settings/ui/Settings';
|
||||||
import SettingsLock from 'screens/profile/settings/ui/SettingsLock';
|
import SettingsLock from 'screens/profile/settings/ui/SettingsLock';
|
||||||
@@ -68,107 +55,107 @@ const Stack = createNativeStackNavigator();
|
|||||||
const screenWidth = Dimensions.get('window').width;
|
const screenWidth = Dimensions.get('window').width;
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
const saveNotification = async (remoteMessage: any) => {
|
// const saveNotification = async (remoteMessage: any) => {
|
||||||
try {
|
// try {
|
||||||
const stored = await AsyncStorage.getItem('notifications');
|
// const stored = await AsyncStorage.getItem('notifications');
|
||||||
const notifications = stored ? JSON.parse(stored) : [];
|
// const notifications = stored ? JSON.parse(stored) : [];
|
||||||
|
|
||||||
const newNotification = {
|
// const newNotification = {
|
||||||
id: Date.now(),
|
// id: Date.now(),
|
||||||
title:
|
// title:
|
||||||
remoteMessage.notification?.title ||
|
// remoteMessage.notification?.title ||
|
||||||
remoteMessage.data?.title ||
|
// remoteMessage.data?.title ||
|
||||||
'Yangi bildirishnoma',
|
// 'Yangi bildirishnoma',
|
||||||
message:
|
// message:
|
||||||
remoteMessage.notification?.body ||
|
// remoteMessage.notification?.body ||
|
||||||
remoteMessage.data?.body ||
|
// remoteMessage.data?.body ||
|
||||||
'Matn yo‘q',
|
// 'Matn yo‘q',
|
||||||
sentTime: remoteMessage.sentTime || Date.now(),
|
// sentTime: remoteMessage.sentTime || Date.now(),
|
||||||
};
|
// };
|
||||||
|
|
||||||
await AsyncStorage.setItem(
|
// await AsyncStorage.setItem(
|
||||||
'notifications',
|
// 'notifications',
|
||||||
JSON.stringify([newNotification, ...notifications]),
|
// JSON.stringify([newNotification, ...notifications]),
|
||||||
);
|
// );
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
console.error('Notification saqlashda xato:', e);
|
// console.error('Notification saqlashda xato:', e);
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
async function onDisplayNotification(remoteMessage: any) {
|
// async function onDisplayNotification(remoteMessage: any) {
|
||||||
const channelId = await notifee.createChannel({
|
// const channelId = await notifee.createChannel({
|
||||||
id: 'default',
|
// id: 'default',
|
||||||
name: 'Umumiy bildirishnomalar',
|
// name: 'Umumiy bildirishnomalar',
|
||||||
sound: 'default',
|
// sound: 'default',
|
||||||
importance: AndroidImportance.HIGH,
|
// importance: AndroidImportance.HIGH,
|
||||||
});
|
// });
|
||||||
|
|
||||||
await notifee.displayNotification({
|
// await notifee.displayNotification({
|
||||||
title:
|
// title:
|
||||||
remoteMessage.notification?.title ||
|
// remoteMessage.notification?.title ||
|
||||||
remoteMessage.data?.title ||
|
// remoteMessage.data?.title ||
|
||||||
'Yangi xabar',
|
// 'Yangi xabar',
|
||||||
body:
|
// body:
|
||||||
remoteMessage.notification?.body ||
|
// remoteMessage.notification?.body ||
|
||||||
remoteMessage.data?.body ||
|
// remoteMessage.data?.body ||
|
||||||
'Matn yo‘q',
|
// 'Matn yo‘q',
|
||||||
android: {
|
// android: {
|
||||||
channelId,
|
// channelId,
|
||||||
largeIcon: 'ic_launcher_foreground',
|
// largeIcon: 'ic_launcher_foreground',
|
||||||
sound: 'default',
|
// sound: 'default',
|
||||||
pressAction: {
|
// pressAction: {
|
||||||
id: 'default',
|
// id: 'default',
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
async function requestNotificationPermission() {
|
// async function requestNotificationPermission() {
|
||||||
if (Platform.OS === 'android' && Platform.Version >= 33) {
|
// if (Platform.OS === 'android' && Platform.Version >= 33) {
|
||||||
const granted = await PermissionsAndroid.request(
|
// const granted = await PermissionsAndroid.request(
|
||||||
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
|
// PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
|
||||||
);
|
// );
|
||||||
console.log('POST_NOTIFICATIONS permission:', granted);
|
// console.log('POST_NOTIFICATIONS permission:', granted);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [initialRoute, setInitialRoute] = useState<string | null>(null);
|
const [initialRoute, setInitialRoute] = useState<string | null>(null);
|
||||||
const slideAnim = useRef(new Animated.Value(0)).current;
|
const slideAnim = useRef(new Animated.Value(0)).current;
|
||||||
const [isSplashVisible, setIsSplashVisible] = useState(true);
|
const [isSplashVisible, setIsSplashVisible] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
requestNotificationPermission();
|
// requestNotificationPermission();
|
||||||
|
|
||||||
const messagingInstance = getMessaging();
|
// const messagingInstance = getMessaging();
|
||||||
|
|
||||||
const unsubscribe = onMessage(messagingInstance, async remoteMessage => {
|
// const unsubscribe = onMessage(messagingInstance, async remoteMessage => {
|
||||||
console.log('Foreground message:', remoteMessage);
|
// console.log('Foreground message:', remoteMessage);
|
||||||
await saveNotification(remoteMessage);
|
// await saveNotification(remoteMessage);
|
||||||
await onDisplayNotification(remoteMessage);
|
// await onDisplayNotification(remoteMessage);
|
||||||
});
|
// });
|
||||||
|
|
||||||
const unsubscribeOpened = onNotificationOpenedApp(
|
// const unsubscribeOpened = onNotificationOpenedApp(
|
||||||
messagingInstance,
|
// messagingInstance,
|
||||||
remoteMessage => {
|
// remoteMessage => {
|
||||||
console.log('Backgrounddan ochildi:', remoteMessage);
|
// console.log('Backgrounddan ochildi:', remoteMessage);
|
||||||
saveNotification(remoteMessage);
|
// saveNotification(remoteMessage);
|
||||||
},
|
// },
|
||||||
);
|
// );
|
||||||
|
|
||||||
(async () => {
|
// (async () => {
|
||||||
const remoteMessage = await getInitialNotification(messagingInstance);
|
// const remoteMessage = await getInitialNotification(messagingInstance);
|
||||||
if (remoteMessage) {
|
// if (remoteMessage) {
|
||||||
console.log('Killeddan ochildi:', remoteMessage);
|
// console.log('Killeddan ochildi:', remoteMessage);
|
||||||
saveNotification(remoteMessage);
|
// saveNotification(remoteMessage);
|
||||||
}
|
// }
|
||||||
})();
|
// })();
|
||||||
|
|
||||||
return () => {
|
// return () => {
|
||||||
unsubscribe();
|
// unsubscribe();
|
||||||
unsubscribeOpened();
|
// unsubscribeOpened();
|
||||||
};
|
// };
|
||||||
}, []);
|
// }, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initializeApp = async () => {
|
const initializeApp = async () => {
|
||||||
@@ -176,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'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -214,34 +202,34 @@ export default function App() {
|
|||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
const [firebaseToken, setFirebseToken] = useState<{
|
// const [firebaseToken, setFirebseToken] = useState<{
|
||||||
fcmToken: string;
|
// fcmToken: string;
|
||||||
deviceId: string;
|
// deviceId: string;
|
||||||
deviceName: string;
|
// deviceName: string;
|
||||||
} | null>();
|
// } | null>();
|
||||||
const app = getApp();
|
// const app = getApp();
|
||||||
const messaging = getMessaging(app);
|
// const messaging = getMessaging(app);
|
||||||
|
|
||||||
const getDeviceData = async () => {
|
// const getDeviceData = async () => {
|
||||||
try {
|
// try {
|
||||||
const fcmToken = await getToken(messaging);
|
// const fcmToken = await getToken(messaging);
|
||||||
return {
|
// return {
|
||||||
fcmToken,
|
// fcmToken,
|
||||||
deviceId: await DeviceInfo.getUniqueId(),
|
// deviceId: await DeviceInfo.getUniqueId(),
|
||||||
deviceName: await DeviceInfo.getDeviceName(),
|
// deviceName: await DeviceInfo.getDeviceName(),
|
||||||
};
|
// };
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
console.log('Xato:', e);
|
// console.log('Xato:', e);
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
console.log(firebaseToken);
|
// console.log(firebaseToken);
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
getDeviceData().then(data => {
|
// getDeviceData().then(data => {
|
||||||
setFirebseToken(data);
|
// setFirebseToken(data);
|
||||||
});
|
// });
|
||||||
}, []);
|
// }, []);
|
||||||
|
|
||||||
if (!initialRoute) return null;
|
if (!initialRoute) return null;
|
||||||
|
|
||||||
@@ -285,9 +273,9 @@ export default function App() {
|
|||||||
<Stack.Screen name="PaymentQrCode" component={PaymentQrCode} />
|
<Stack.Screen name="PaymentQrCode" component={PaymentQrCode} />
|
||||||
<Stack.Screen name="Profile" component={Profile} />
|
<Stack.Screen name="Profile" component={Profile} />
|
||||||
<Stack.Screen name="Settings" component={Settings} />
|
<Stack.Screen name="Settings" component={Settings} />
|
||||||
{Platform.OS === 'android' && (
|
{/* {Platform.OS === 'android' && (
|
||||||
<Stack.Screen name="Notifications" component={Notifications} />
|
<Stack.Screen name="Notifications" component={Notifications} />
|
||||||
)}
|
)} */}
|
||||||
<Stack.Screen name="Warehouses" component={Warehouses} />
|
<Stack.Screen name="Warehouses" component={Warehouses} />
|
||||||
<Stack.Screen name="Support" component={Support} />
|
<Stack.Screen name="Support" component={Support} />
|
||||||
<Stack.Screen name="ListBranches" component={ListBranches} />
|
<Stack.Screen name="ListBranches" component={ListBranches} />
|
||||||
|
|||||||
@@ -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 4
|
versionCode 8
|
||||||
versionName "0.4"
|
versionName "0.8"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -15,7 +15,7 @@ class MainActivity : ReactActivity() {
|
|||||||
override fun getMainComponentName(): String = "first"
|
override fun getMainComponentName(): String = "first"
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: android.os.Bundle?) {
|
override fun onCreate(savedInstanceState: android.os.Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import com.facebook.react.ReactNativeHost
|
|||||||
import com.facebook.react.ReactPackage
|
import com.facebook.react.ReactPackage
|
||||||
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
|
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
|
||||||
import com.facebook.react.defaults.DefaultReactNativeHost
|
import com.facebook.react.defaults.DefaultReactNativeHost
|
||||||
|
import com.swmansion.rnscreens.RNScreensPackage
|
||||||
|
|
||||||
class MainApplication : Application(), ReactApplication {
|
class MainApplication : Application(), ReactApplication {
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 99 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
@@ -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" />
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: ['module:@react-native/babel-preset'],
|
presets: ['module:@react-native/babel-preset'],
|
||||||
plugins: [
|
plugins: [
|
||||||
'react-native-reanimated/plugin',
|
// 'react-native-reanimated/plugin',
|
||||||
|
'react-native-worklets/plugin',
|
||||||
'@babel/plugin-transform-export-namespace-from',
|
'@babel/plugin-transform-export-namespace-from',
|
||||||
[
|
[
|
||||||
'module-resolver',
|
'module-resolver',
|
||||||
|
|||||||
65
index.js
65
index.js
@@ -1,41 +1,46 @@
|
|||||||
/**
|
/**
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
import notifee, { AndroidImportance } from '@notifee/react-native';
|
// import notifee, { AndroidImportance } from '@notifee/react-native';
|
||||||
import messaging from '@react-native-firebase/messaging';
|
// import messaging from '@react-native-firebase/messaging';
|
||||||
import { AppRegistry } from 'react-native';
|
import { AppRegistry } from 'react-native';
|
||||||
|
import { enableScreens } from 'react-native-screens';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import { name as appName } from './app.json';
|
import { name as appName } from './app.json';
|
||||||
|
|
||||||
// 📌 Background/Killed xabarlarni ushlash
|
|
||||||
messaging().setBackgroundMessageHandler(async remoteMessage => {
|
|
||||||
console.log('Background message:', remoteMessage);
|
|
||||||
|
|
||||||
const channelId = await notifee.createChannel({
|
enableScreens({
|
||||||
id: 'default',
|
freezeOnBlur: true, // background screen componenti freeze bo'ladi
|
||||||
name: 'Umumiy bildirishnomalar',
|
|
||||||
sound: 'default',
|
|
||||||
importance: AndroidImportance.HIGH,
|
|
||||||
});
|
|
||||||
|
|
||||||
await notifee.displayNotification({
|
|
||||||
title:
|
|
||||||
remoteMessage.notification?.title ||
|
|
||||||
remoteMessage.data?.title ||
|
|
||||||
'Yangi xabar',
|
|
||||||
body:
|
|
||||||
remoteMessage.notification?.body ||
|
|
||||||
remoteMessage.data?.body ||
|
|
||||||
'Matn yo‘q',
|
|
||||||
android: {
|
|
||||||
channelId,
|
|
||||||
largeIcon: 'ic_launcher_foreground',
|
|
||||||
sound: 'default',
|
|
||||||
pressAction: {
|
|
||||||
id: 'default',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// messaging().setBackgroundMessageHandler(async remoteMessage => {
|
||||||
|
// console.log('Background message:', remoteMessage);
|
||||||
|
|
||||||
|
// const channelId = await notifee.createChannel({
|
||||||
|
// id: 'default',
|
||||||
|
// name: 'Umumiy bildirishnomalar',
|
||||||
|
// sound: 'default',
|
||||||
|
// importance: AndroidImportance.HIGH,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// await notifee.displayNotification({
|
||||||
|
// title:
|
||||||
|
// remoteMessage.notification?.title ||
|
||||||
|
// remoteMessage.data?.title ||
|
||||||
|
// 'Yangi xabar',
|
||||||
|
// body:
|
||||||
|
// remoteMessage.notification?.body ||
|
||||||
|
// remoteMessage.data?.body ||
|
||||||
|
// 'Matn yo‘q',
|
||||||
|
// android: {
|
||||||
|
// channelId,
|
||||||
|
// largeIcon: 'ic_launcher_foreground',
|
||||||
|
// sound: 'default',
|
||||||
|
// pressAction: {
|
||||||
|
// id: 'default',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
AppRegistry.registerComponent(appName, () => App);
|
AppRegistry.registerComponent(appName, () => App);
|
||||||
|
|||||||
2
ios/first/Base.lproj/InfoPlist.strings
Normal file
2
ios/first/Base.lproj/InfoPlist.strings
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
"NSCameraUsageDescription" = "Camera access is needed to take your passport or profile photo.";
|
||||||
|
"NSPhotoLibraryUsageDescription" = "Photo library access is needed to upload your passport or profile photo.";
|
||||||
@@ -38,11 +38,9 @@
|
|||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>Kamera orqali rasm olish uchun ruxsat kerak</string>
|
<string>$(NSCameraUsageDescription)</string>
|
||||||
<key>NSLocationWhenInUseUsageDescription</key>
|
|
||||||
<string>Biz sizning joylashuvingizni aniqlash uchun ruxsat so‘raymiz</string>
|
|
||||||
<key>NSPhotoLibraryUsageDescription</key>
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
<string>Rasm kutubxonasidan ruxsat kerak</string>
|
<string>$(NSPhotoLibraryUsageDescription)</string>
|
||||||
<key>RCTNewArchEnabled</key>
|
<key>RCTNewArchEnabled</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
|
|||||||
2
ios/first/ru.lproj/InfoPlist.strings
Normal file
2
ios/first/ru.lproj/InfoPlist.strings
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
"NSCameraUsageDescription" = "Для съемки фото паспорта или аватара необходимо разрешение камеры";
|
||||||
|
"NSPhotoLibraryUsageDescription" = "Для загрузки фото паспорта или аватара необходимо разрешение доступа к фотогалерее";
|
||||||
2
ios/first/uz.lproj/InfoPlist.strings
Normal file
2
ios/first/uz.lproj/InfoPlist.strings
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
"NSCameraUsageDescription" = "Passport yoki profil rasmini olish uchun kamera ruxsati kerak";
|
||||||
|
"NSPhotoLibraryUsageDescription" = "Passport yoki profil rasmini yuklash uchun rasm kutubxonasidan foydalanish uchun ruxsat kerak";
|
||||||
1458
package-lock.json
generated
1458
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -18,12 +18,11 @@
|
|||||||
"@react-native-async-storage/async-storage": "^2.2.0",
|
"@react-native-async-storage/async-storage": "^2.2.0",
|
||||||
"@react-native-clipboard/clipboard": "^1.16.3",
|
"@react-native-clipboard/clipboard": "^1.16.3",
|
||||||
"@react-native-community/datetimepicker": "^8.4.2",
|
"@react-native-community/datetimepicker": "^8.4.2",
|
||||||
"@react-native-firebase/app": "^23.2.0",
|
|
||||||
"@react-native-firebase/messaging": "^23.2.0",
|
|
||||||
"@react-navigation/native": "^7.1.17",
|
"@react-navigation/native": "^7.1.17",
|
||||||
"@react-navigation/native-stack": "^7.3.25",
|
"@react-navigation/native-stack": "^7.3.25",
|
||||||
"@tanstack/react-query": "^5.84.2",
|
"@tanstack/react-query": "^5.84.2",
|
||||||
"axios": "^1.11.0",
|
"axios": "^1.11.0",
|
||||||
|
"events": "^3.3.0",
|
||||||
"i18next": "^25.3.2",
|
"i18next": "^25.3.2",
|
||||||
"lottie-react-native": "^7.3.0",
|
"lottie-react-native": "^7.3.0",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
@@ -33,7 +32,7 @@
|
|||||||
"react-native-app-link": "^1.0.1",
|
"react-native-app-link": "^1.0.1",
|
||||||
"react-native-asset": "^2.1.1",
|
"react-native-asset": "^2.1.1",
|
||||||
"react-native-biometrics": "^3.0.1",
|
"react-native-biometrics": "^3.0.1",
|
||||||
"react-native-config": "^1.5.6",
|
"react-native-config": "^1.5.0",
|
||||||
"react-native-confirmation-code-field": "^8.0.1",
|
"react-native-confirmation-code-field": "^8.0.1",
|
||||||
"react-native-device-info": "^14.0.4",
|
"react-native-device-info": "^14.0.4",
|
||||||
"react-native-dotenv": "^3.4.11",
|
"react-native-dotenv": "^3.4.11",
|
||||||
@@ -43,7 +42,9 @@
|
|||||||
"react-native-linear-gradient": "^2.8.3",
|
"react-native-linear-gradient": "^2.8.3",
|
||||||
"react-native-localize": "^3.5.1",
|
"react-native-localize": "^3.5.1",
|
||||||
"react-native-mask-input": "^1.2.3",
|
"react-native-mask-input": "^1.2.3",
|
||||||
|
"react-native-mmkv": "^2.2.4",
|
||||||
"react-native-modal": "^14.0.0-rc.1",
|
"react-native-modal": "^14.0.0-rc.1",
|
||||||
|
"react-native-nitro-modules": "^0.31.10",
|
||||||
"react-native-push-notification": "^8.1.1",
|
"react-native-push-notification": "^8.1.1",
|
||||||
"react-native-reanimated": "^4.0.2",
|
"react-native-reanimated": "^4.0.2",
|
||||||
"react-native-safe-area-context": "^5.6.0",
|
"react-native-safe-area-context": "^5.6.0",
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export interface resendPayload {
|
|||||||
export interface loginPayload {
|
export interface loginPayload {
|
||||||
phoneNumber: string;
|
phoneNumber: string;
|
||||||
passportSerial: string;
|
passportSerial: string;
|
||||||
branchId: number;
|
// branchId: number;
|
||||||
fcmToken: string;
|
fcmToken: string;
|
||||||
deviceId: string;
|
deviceId: string;
|
||||||
deviceType: string;
|
deviceType: string;
|
||||||
|
|||||||
@@ -1,68 +1,86 @@
|
|||||||
"use client"
|
'use client';
|
||||||
|
|
||||||
import React, { useEffect, useState, useCallback, useMemo } from "react"
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { type LayoutChangeEvent, View } from "react-native"
|
import { type LayoutChangeEvent, View } from 'react-native';
|
||||||
import Animated, {
|
import Animated, {
|
||||||
useSharedValue,
|
cancelAnimation,
|
||||||
useAnimatedStyle,
|
|
||||||
withRepeat,
|
|
||||||
withTiming,
|
|
||||||
Easing,
|
Easing,
|
||||||
|
useAnimatedStyle,
|
||||||
|
useSharedValue,
|
||||||
|
withRepeat,
|
||||||
withSequence,
|
withSequence,
|
||||||
withDelay,
|
withTiming,
|
||||||
} from "react-native-reanimated"
|
} from 'react-native-reanimated';
|
||||||
import Auto from "svg/Auto"
|
import Auto from 'svg/Auto';
|
||||||
import Avia from "svg/Avia"
|
import Avia from 'svg/Avia';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
type: "auto" | "avia"
|
type: 'AUTO' | 'AVIA';
|
||||||
}
|
};
|
||||||
|
|
||||||
const AnimatedIcon = ({ type }: Props) => {
|
const AnimatedIcon = ({ type }: Props) => {
|
||||||
const translateX = useSharedValue(0)
|
const translateX = useSharedValue(0);
|
||||||
const translateY = useSharedValue(0)
|
const translateY = useSharedValue(0);
|
||||||
const rotateY = useSharedValue(0)
|
const rotateY = useSharedValue(0);
|
||||||
const direction = useSharedValue(1)
|
const direction = useSharedValue(1);
|
||||||
|
|
||||||
const [containerWidth, setContainerWidth] = useState(0)
|
const [containerWidth, setContainerWidth] = useState(0);
|
||||||
const iconSize = 40
|
const iconSize = 40;
|
||||||
|
|
||||||
const onLayout = useCallback((event: LayoutChangeEvent) => {
|
const onLayout = useCallback((event: LayoutChangeEvent) => {
|
||||||
const { width } = event.nativeEvent.layout
|
const { width } = event.nativeEvent.layout;
|
||||||
setContainerWidth(width)
|
setContainerWidth(width);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const animationConfig = useMemo(() => ({
|
const animationConfig = useMemo(
|
||||||
|
() => ({
|
||||||
duration: 4000,
|
duration: 4000,
|
||||||
easing: Easing.linear,
|
easing: Easing.linear,
|
||||||
rotationDuration: 300,
|
rotationDuration: 300,
|
||||||
arcHeight: -30,
|
arcHeight: -30,
|
||||||
}), []);
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const createXAnimation = useCallback((maxX: number) => {
|
const createXAnimation = useCallback(
|
||||||
|
(maxX: number) => {
|
||||||
return withRepeat(
|
return withRepeat(
|
||||||
withSequence(
|
withSequence(
|
||||||
withTiming(maxX, {
|
withTiming(
|
||||||
|
maxX,
|
||||||
|
{
|
||||||
duration: animationConfig.duration,
|
duration: animationConfig.duration,
|
||||||
easing: animationConfig.easing
|
easing: animationConfig.easing,
|
||||||
}, () => {
|
},
|
||||||
|
() => {
|
||||||
direction.value = -1;
|
direction.value = -1;
|
||||||
rotateY.value = withTiming(180, { duration: animationConfig.rotationDuration });
|
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 });
|
|
||||||
})
|
|
||||||
),
|
),
|
||||||
-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(() => {
|
const createYAnimation = useCallback(() => {
|
||||||
if (type === "avia") {
|
if (type === 'AVIA') {
|
||||||
return withRepeat(
|
return withRepeat(
|
||||||
withSequence(
|
withSequence(
|
||||||
withTiming(animationConfig.arcHeight, {
|
withTiming(animationConfig.arcHeight, {
|
||||||
@@ -80,9 +98,9 @@ const AnimatedIcon = ({ type }: Props) => {
|
|||||||
withTiming(0, {
|
withTiming(0, {
|
||||||
duration: animationConfig.duration / 2,
|
duration: animationConfig.duration / 2,
|
||||||
easing: Easing.in(Easing.quad),
|
easing: Easing.in(Easing.quad),
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
-1
|
-1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return withTiming(0, { duration: 100 });
|
return withTiming(0, { duration: 100 });
|
||||||
@@ -93,9 +111,20 @@ const AnimatedIcon = ({ type }: Props) => {
|
|||||||
|
|
||||||
const maxX = containerWidth - iconSize;
|
const maxX = containerWidth - iconSize;
|
||||||
|
|
||||||
|
// eski animatsiyani to‘xtat
|
||||||
|
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);
|
translateX.value = createXAnimation(maxX);
|
||||||
translateY.value = createYAnimation();
|
translateY.value = createYAnimation();
|
||||||
}, [containerWidth, type, createXAnimation, createYAnimation, translateX, translateY]);
|
}, [containerWidth, type]); // type = activeTab dan keladigan qiymat
|
||||||
|
|
||||||
const animatedStyle = useAnimatedStyle(() => {
|
const animatedStyle = useAnimatedStyle(() => {
|
||||||
return {
|
return {
|
||||||
@@ -104,34 +133,43 @@ const AnimatedIcon = ({ type }: Props) => {
|
|||||||
{ translateY: translateY.value },
|
{ translateY: translateY.value },
|
||||||
{ rotateY: `${rotateY.value}deg` },
|
{ rotateY: `${rotateY.value}deg` },
|
||||||
],
|
],
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const containerStyle = useMemo(() => ({
|
const containerStyle = useMemo(
|
||||||
|
() => ({
|
||||||
height: 100,
|
height: 100,
|
||||||
justifyContent: "center" as const,
|
justifyContent: 'center' as const,
|
||||||
backgroundColor: "transparent" as const,
|
backgroundColor: 'transparent' as const,
|
||||||
position: "relative" as const,
|
position: 'relative' as const,
|
||||||
}), []);
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const trackStyle = useMemo(() => ({
|
const trackStyle = useMemo(
|
||||||
|
() => ({
|
||||||
height: 2,
|
height: 2,
|
||||||
backgroundColor: "#28A7E850",
|
backgroundColor: '#28A7E850',
|
||||||
position: "absolute" as const,
|
position: 'absolute' as const,
|
||||||
top: 25,
|
top: 25,
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
}), []);
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const iconContainerStyle = useMemo(() => ({
|
const iconContainerStyle = useMemo(
|
||||||
position: "absolute" as const,
|
() => ({
|
||||||
|
position: 'absolute' as const,
|
||||||
top: 0,
|
top: 0,
|
||||||
height: iconSize,
|
height: iconSize,
|
||||||
width: iconSize,
|
width: iconSize,
|
||||||
}), [iconSize]);
|
}),
|
||||||
|
[iconSize],
|
||||||
|
);
|
||||||
|
|
||||||
const renderIcon = useMemo(() => {
|
const renderIcon = useMemo(() => {
|
||||||
if (type === "auto") {
|
if (type === 'AUTO') {
|
||||||
return <Auto color="#28A7E8" width={iconSize} height={iconSize} />;
|
return <Auto color="#28A7E8" width={iconSize} height={iconSize} />;
|
||||||
}
|
}
|
||||||
return <Avia color="#28A7E8" width={iconSize} height={iconSize} />;
|
return <Avia color="#28A7E8" width={iconSize} height={iconSize} />;
|
||||||
@@ -144,7 +182,7 @@ const AnimatedIcon = ({ type }: Props) => {
|
|||||||
{renderIcon}
|
{renderIcon}
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
</View>
|
</View>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default AnimatedIcon;
|
export default AnimatedIcon;
|
||||||
|
|||||||
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;
|
||||||
@@ -1,9 +1,18 @@
|
|||||||
import React, { useCallback, useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
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 {
|
import {
|
||||||
Asset,
|
Asset,
|
||||||
ImagePickerResponse,
|
ImagePickerResponse,
|
||||||
|
launchCamera,
|
||||||
launchImageLibrary,
|
launchImageLibrary,
|
||||||
MediaType,
|
MediaType,
|
||||||
} from 'react-native-image-picker';
|
} from 'react-native-image-picker';
|
||||||
@@ -20,9 +29,6 @@ export interface FileData {
|
|||||||
export interface SingleFileDropProps {
|
export interface SingleFileDropProps {
|
||||||
title: string;
|
title: string;
|
||||||
onFileSelected?: (file: FileData) => void;
|
onFileSelected?: (file: FileData) => void;
|
||||||
/**
|
|
||||||
* Ruxsat berilgan MIME tipi (masalan: "image/png" yoki "image/jpeg")
|
|
||||||
*/
|
|
||||||
type?: string;
|
type?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +50,7 @@ const SingleFileDrop: React.FC<SingleFileDropProps> = ({
|
|||||||
|
|
||||||
const handleImagePickerResponse = useCallback(
|
const handleImagePickerResponse = useCallback(
|
||||||
(response: ImagePickerResponse) => {
|
(response: ImagePickerResponse) => {
|
||||||
if (response.didCancel) return; // foydalanuvchi bekor qilsa
|
if (response.didCancel) return;
|
||||||
if (response.errorCode) {
|
if (response.errorCode) {
|
||||||
Alert.alert('Xato', response.errorMessage || 'Rasmni yuklashda xato');
|
Alert.alert('Xato', response.errorMessage || 'Rasmni yuklashda xato');
|
||||||
return;
|
return;
|
||||||
@@ -53,7 +59,6 @@ const SingleFileDrop: React.FC<SingleFileDropProps> = ({
|
|||||||
const asset: Asset | undefined = response.assets?.[0];
|
const asset: Asset | undefined = response.assets?.[0];
|
||||||
if (!asset || !asset.uri || !asset.type || !asset.base64) return;
|
if (!asset || !asset.uri || !asset.type || !asset.base64) return;
|
||||||
|
|
||||||
// faqat belgilangan tipdagi fayllarni qabul qilish
|
|
||||||
if (!asset.type.startsWith('image/')) {
|
if (!asset.type.startsWith('image/')) {
|
||||||
Alert.alert('Xato', 'Faqat rasm fayllarni yuklashingiz mumkin');
|
Alert.alert('Xato', 'Faqat rasm fayllarni yuklashingiz mumkin');
|
||||||
return;
|
return;
|
||||||
@@ -77,6 +82,32 @@ const SingleFileDrop: React.FC<SingleFileDropProps> = ({
|
|||||||
launchImageLibrary(imagePickerOptions, handleImagePickerResponse);
|
launchImageLibrary(imagePickerOptions, handleImagePickerResponse);
|
||||||
}, [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(
|
const UploadIcon = useMemo(
|
||||||
() => () =>
|
() => () =>
|
||||||
(
|
(
|
||||||
@@ -122,7 +153,7 @@ const SingleFileDrop: React.FC<SingleFileDropProps> = ({
|
|||||||
}, [selectedImage, title, UploadIcon]);
|
}, [selectedImage, title, UploadIcon]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity style={styles.dropSection} onPress={openGallery}>
|
<TouchableOpacity style={styles.dropSection} onPress={openPicker}>
|
||||||
{renderContent}
|
{renderContent}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
Dimensions,
|
Dimensions,
|
||||||
Image,
|
Image,
|
||||||
Linking,
|
Linking,
|
||||||
Platform,
|
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
@@ -13,7 +12,6 @@ import {
|
|||||||
import AppLink from 'react-native-app-link';
|
import AppLink from 'react-native-app-link';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import Logo from 'screens/../../assets/bootsplash/logo.png';
|
import Logo from 'screens/../../assets/bootsplash/logo.png';
|
||||||
import Bell from 'svg/Bell';
|
|
||||||
import Instagram from 'svg/Instagram';
|
import Instagram from 'svg/Instagram';
|
||||||
import Telegram from 'svg/Telegram';
|
import Telegram from 'svg/Telegram';
|
||||||
import AppText from './AppText';
|
import AppText from './AppText';
|
||||||
@@ -97,14 +95,13 @@ const Navbar = () => {
|
|||||||
size={iconSizes.facebook}
|
size={iconSizes.facebook}
|
||||||
/>
|
/>
|
||||||
</TouchableOpacity> */}
|
</TouchableOpacity> */}
|
||||||
{Platform.OS === 'android' && (
|
{/* {Platform.OS === 'android' && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => navigation.navigate('Notifications')}
|
onPress={() => navigation.navigate('Notifications')}
|
||||||
>
|
>
|
||||||
<Bell color="#fff" width={24} height={24} />
|
<Bell color="#fff" width={24} height={24} />
|
||||||
{/* <View style={styles.bellDot} /> */}
|
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)} */}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|||||||
0
src/helpers/event.ts
Normal file
0
src/helpers/event.ts
Normal file
8
src/helpers/formatPrice.ts
Normal file
8
src/helpers/formatPrice.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export const formatPrice = (amount: number | string) => {
|
||||||
|
const numericAmount = Number(amount) || 0;
|
||||||
|
const formatted = numericAmount
|
||||||
|
.toString()
|
||||||
|
.replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
|
||||||
|
|
||||||
|
return formatted;
|
||||||
|
};
|
||||||
@@ -230,5 +230,26 @@
|
|||||||
"Xatolik yuz berdi": "Произошла ошибка",
|
"Xatolik yuz berdi": "Произошла ошибка",
|
||||||
"Passport qo'shishda xatolik yuz berdi": "Ошибка добавления паспорта",
|
"Passport qo'shishda xatolik yuz berdi": "Ошибка добавления паспорта",
|
||||||
"Kodsiz tovarlar": "Товары без кода",
|
"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 jo‘natmaslikka rozilik so'raymiz.": "Мы просим Вашего согласия не отправлять следующие товары при ввозе товаров, приобретенных в Китайской Народной Республике.",
|
||||||
|
"Taqiqlangan tovarlar": "Запрещённые товары",
|
||||||
|
"Taqiqlangan tovarlar matni": "В соответствии с Указом Президента Республики Узбекистан от 25 декабря 1998 года No УП-2160 запрещается ввоз в Республику Узбекистан: брошюр, фотографий, видео, аудиопродукции, агитационных материалов и печатных изданий, направленных на нанесение ущерба государству и обществу; пропаганды нарушений порядка, территориальной целостности, политической независимости и государственного суверенитета, войны, терроризма, насилия, национальной идентичности и религиозной ненависти, расизма и его многообразия (антисемитизм, фашизм), а также порнографических материалов.",
|
||||||
|
"Tovarlar": "Запрещается ввоз: взрывчатых, ядовитых, легковоспламеняющихся, радиоактивных веществ, продуктов питания, лекарственных средств, медицинских изделий и оборудования, полиграфического оборудования, мобильных телефонов, зубной пасты (порошка), рационов, USB-флешек, различных флеш-карт и других аналогичных товаров.",
|
||||||
|
"Agar sizda savollar bo‘lsa, quyidagi aloqa manzillaridan foydalanishingiz mumkin:": "Если у вас есть вопросы, вы можете воспользоваться следующими контактами:",
|
||||||
|
"Aloqa uchun": "Для связи",
|
||||||
|
"Email": "Электронная почта",
|
||||||
|
"Telegram": "Телеграм",
|
||||||
|
"Roziman": "Согласен",
|
||||||
|
"To'lov kutilmoqda": "Ожидание оплаты",
|
||||||
|
"Reys": "Рейс",
|
||||||
|
"To'lov holati": "Статус оплаты",
|
||||||
|
"Bekor qilingan": "Отменено",
|
||||||
|
"Mahsulot nomi": "Название товара",
|
||||||
|
"Ma'lumotlarni to'liq kiriting": "Введите полные данные"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
"Familiya": "Familiya",
|
"Familiya": "Familiya",
|
||||||
"Familiyangiz": "Familiyangiz",
|
"Familiyangiz": "Familiyangiz",
|
||||||
"Bizni qaerdan topdingiz?": "Bizni qaerdan topdingiz?",
|
"Bizni qaerdan topdingiz?": "Bizni qaerdan topdingiz?",
|
||||||
|
"Reys": "Reys",
|
||||||
|
"narxi": "narxi",
|
||||||
"Bizni kim tavsiya qildi...": "Bizni kim tavsiya qildi...",
|
"Bizni kim tavsiya qildi...": "Bizni kim tavsiya qildi...",
|
||||||
"Foydalanish shartlari": "Foydalanish shartlari",
|
"Foydalanish shartlari": "Foydalanish shartlari",
|
||||||
"bilan tanishib chiqdim!": "bilan tanishib chiqdim!",
|
"bilan tanishib chiqdim!": "bilan tanishib chiqdim!",
|
||||||
@@ -42,13 +44,13 @@
|
|||||||
"foydalanuvchi_majburiyatlari": "2. Foydalanuvchi majburiyatlari",
|
"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",
|
"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": "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": "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.",
|
"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": "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.",
|
"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": "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:",
|
"oxirgi_yangilanish": "Oxirgi yangilanish:",
|
||||||
"roziman": "Roziman",
|
"roziman": "Roziman",
|
||||||
"Shaxsiy maʼlumotlar": "Shaxsiy maʼlumotlar",
|
"Shaxsiy maʼlumotlar": "Shaxsiy maʼlumotlar",
|
||||||
@@ -111,7 +113,7 @@
|
|||||||
"Filiallarimiz ro'yhati ilovada mavjud": "Filiallarimiz ro'yhati ilovada mavjud",
|
"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.",
|
"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.",
|
"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 o’sishiga olib keladi. Seriyali buyularni avto kargo orqali olib kelish arzonga tushadi)": "(Bu kargo narxini o’sishiga olib keladi. Seriyali buyularni avto kargo orqali olib kelish arzonga tushadi)",
|
"(Bu kargo narxini o’sishiga olib keladi. Seriyali buyularni avto kargo orqali olib kelish arzonga tushadi)": "(Bu kargo narxini o’sishiga 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 ko‘p yuk oluvchilar uchun maxsus chegirmali narxlarimiz mavjud.": "Avto kargoda bir mahsulotdan seriyali ravishda, istalgan katta miqdorda xarid qilish mumkin. Doimiy ravishda ko‘p yuk oluvchilar uchun maxsus chegirmali narxlarimiz mavjud.",
|
"Avto kargoda bir mahsulotdan seriyali ravishda, istalgan katta miqdorda xarid qilish mumkin. Doimiy ravishda ko‘p yuk oluvchilar uchun maxsus chegirmali narxlarimiz mavjud.": "Avto kargoda bir mahsulotdan seriyali ravishda, istalgan katta miqdorda xarid qilish mumkin. Doimiy ravishda ko‘p yuk oluvchilar uchun maxsus chegirmali narxlarimiz mavjud.",
|
||||||
"dan boshlab": "dan boshlab",
|
"dan boshlab": "dan boshlab",
|
||||||
|
|
||||||
@@ -156,6 +158,7 @@
|
|||||||
"Mahsulotlar og’irligi": "Mahsulotlar og’irligi",
|
"Mahsulotlar og’irligi": "Mahsulotlar og’irligi",
|
||||||
"Faol": "Faol",
|
"Faol": "Faol",
|
||||||
"Kutilmoqda": "Kutilmoqda",
|
"Kutilmoqda": "Kutilmoqda",
|
||||||
|
"To'lov kutilmoqda": "To'lov kutilmoqda",
|
||||||
"Faol emas": "Faol emas",
|
"Faol emas": "Faol emas",
|
||||||
"Xatolik yuz berdi!": "Xatolik yuz berdi!",
|
"Xatolik yuz berdi!": "Xatolik yuz berdi!",
|
||||||
"Akkaunt faol emas!": "Akkaunt faol emas!",
|
"Akkaunt faol emas!": "Akkaunt faol emas!",
|
||||||
@@ -177,10 +180,11 @@
|
|||||||
"so‘m": "so'm",
|
"so‘m": "so'm",
|
||||||
"Umumiy narx": "Umumiy narx",
|
"Umumiy narx": "Umumiy narx",
|
||||||
"Yopish": "Yopish",
|
"Yopish": "Yopish",
|
||||||
|
"Mahsulot nomi": "Mahsulot nomi",
|
||||||
"Passportlarim": "Passportlarim",
|
"Passportlarim": "Passportlarim",
|
||||||
"Hali pasport qo'shilmagan": "Hali pasport qo'shilmagan",
|
"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 uchun tugmani bosing": "Yangi passport qo'shish uchun tugmani bosing",
|
||||||
"Yangi pasport qo'shish": "Yangi pasport qo'shish",
|
"Yangi pasport qo'shish": "Yangi passport qo'shish",
|
||||||
"Passport ma’lumotlarim": "Passport ma’lumotlarim",
|
"Passport ma’lumotlarim": "Passport ma’lumotlarim",
|
||||||
"Tez ID": "Tez ID",
|
"Tez ID": "Tez ID",
|
||||||
"To’liq ismi": "To’liq ismi",
|
"To’liq ismi": "To’liq ismi",
|
||||||
@@ -231,5 +235,23 @@
|
|||||||
"Yuborish": "Yuborish",
|
"Yuborish": "Yuborish",
|
||||||
"Passport qo'shishda xatolik yuz berdi": "Passport qo'shishda xatolik yuz berdi",
|
"Passport qo'shishda xatolik yuz berdi": "Passport qo'shishda xatolik yuz berdi",
|
||||||
"Xatolik yuz berdi": "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 jo‘natmaslikka rozilik so'raymiz.": "Biz sizdan Xitoy xalq Respublikasi Davlatidan sotib olingan tovarlarni olib kelishda quyidagi tovarlarni jo‘natmaslikka rozilik so'raymiz.",
|
||||||
|
"Taqiqlangan tovarlar": "Taqiqlangan tovarlar",
|
||||||
|
"Taqiqlangan tovarlar matni": " O‘zbekiston Respublikasi Prezidentining 1998-yil 25-dekabrdagi UP-2160-son Farmoniga asosan O‘zbekiston 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, zo‘ravonlik, milliy o‘ziga xoslik va diniy nafrat, irqchilik va uning xilma-xilligini (antisemitizm, fashizm), shuningdek, pornografik materiallarga targ‘ib 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 o‘xshash mahsulotlarni olib kirish taqiqlanadi.",
|
||||||
|
"Agar sizda savollar bo‘lsa, quyidagi aloqa manzillaridan foydalanishingiz mumkin:": "Agar sizda savollar bo‘lsa, quyidagi aloqa manzillaridan foydalanishingiz mumkin:",
|
||||||
|
"Aloqa uchun": "Aloqa uchun",
|
||||||
|
"Email": "Email",
|
||||||
|
"Telegram": "Telegram",
|
||||||
|
"Roziman": "Roziman",
|
||||||
|
"To'lov holati": "To'lov holati",
|
||||||
|
"Bekor qilingan": "Bekor qilingan",
|
||||||
|
"Ma'lumotlarni to'liq kiriting": "Ma'lumotlarni to'liq kiriting"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export const loginSchema = z.object({
|
|||||||
phone: z.string().min(12, 'Xato raqam kiritildi'),
|
phone: z.string().min(12, 'Xato raqam kiritildi'),
|
||||||
passportSeriya: z.string().length(2, '2 ta harf kerak'),
|
passportSeriya: z.string().length(2, '2 ta harf kerak'),
|
||||||
passportNumber: z.string().length(7, '7 ta raqam kerak'),
|
passportNumber: z.string().length(7, '7 ta raqam kerak'),
|
||||||
branchId: z.number().min(1, 'Filialni tanlang'),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type LoginFormType = z.infer<typeof loginSchema>;
|
export type LoginFormType = z.infer<typeof loginSchema>;
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
import { getApp } from '@react-native-firebase/app';
|
|
||||||
import { getMessaging, getToken } from '@react-native-firebase/messaging';
|
|
||||||
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 { useMutation } from '@tanstack/react-query';
|
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';
|
||||||
@@ -20,7 +19,6 @@ import {
|
|||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import DeviceInfo from 'react-native-device-info';
|
|
||||||
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 LanguageSelector from 'screens/auth/select-language/SelectLang';
|
import LanguageSelector from 'screens/auth/select-language/SelectLang';
|
||||||
@@ -39,51 +37,56 @@ const OTP_LENGTH = 4;
|
|||||||
const Confirm = () => {
|
const Confirm = () => {
|
||||||
const navigation = useNavigation<VerificationCodeScreenNavigationProp>();
|
const navigation = useNavigation<VerificationCodeScreenNavigationProp>();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [firebaseToken, setFirebseToken] = useState<{
|
// const [firebaseToken, setFirebseToken] = useState<{
|
||||||
fcmToken: string;
|
// fcmToken: string;
|
||||||
deviceId: string;
|
// deviceId: string;
|
||||||
deviceName: string;
|
// deviceName: string;
|
||||||
} | null>();
|
// } | null>();
|
||||||
const [code, setCode] = useState<string[]>(new Array(OTP_LENGTH).fill(''));
|
const [code, setCode] = useState<string[]>(new Array(OTP_LENGTH).fill(''));
|
||||||
const [timer, setTimer] = useState(60);
|
const [timer, setTimer] = useState(60);
|
||||||
const [errorConfirm, setErrorConfirm] = useState<string | null>(null);
|
const [errorConfirm, setErrorConfirm] = useState<string | null>(null);
|
||||||
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);
|
||||||
|
|
||||||
const getDeviceData = async () => {
|
// const getDeviceData = async () => {
|
||||||
try {
|
// try {
|
||||||
const fcmToken = await getToken(messaging);
|
// const fcmToken = await getToken(messaging);
|
||||||
return {
|
// return {
|
||||||
fcmToken,
|
// fcmToken,
|
||||||
deviceId: await DeviceInfo.getUniqueId(),
|
// deviceId: await DeviceInfo.getUniqueId(),
|
||||||
deviceName: await DeviceInfo.getDeviceName(),
|
// deviceName: await DeviceInfo.getDeviceName(),
|
||||||
};
|
// };
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
console.log('Xato:', e);
|
// console.log('Xato:', e);
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
getDeviceData().then(data => {
|
// getDeviceData().then(data => {
|
||||||
setFirebseToken(data);
|
// setFirebseToken(data);
|
||||||
});
|
// });
|
||||||
}, []);
|
// }, []);
|
||||||
|
|
||||||
const { mutate, isPending } = useMutation({
|
const { mutate, isPending } = useMutation({
|
||||||
mutationFn: (payload: otpPayload) => authApi.verifyOtp(payload),
|
mutationFn: (payload: otpPayload) => authApi.verifyOtp(payload),
|
||||||
onSuccess: async res => {
|
onSuccess: async res => {
|
||||||
await AsyncStorage.setItem('token', res.data.accessToken);
|
await AsyncStorage.setItem('token', res.data.accessToken);
|
||||||
navigation.navigate('Home');
|
navigation.reset({
|
||||||
setErrorConfirm(null);
|
index: 0,
|
||||||
console.log(res);
|
routes: [{ name: 'Home' }], // login sahifasiga qaytarish
|
||||||
|
});
|
||||||
|
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);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -94,10 +97,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);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -139,16 +143,14 @@ const Confirm = () => {
|
|||||||
|
|
||||||
const handleVerifyCode = () => {
|
const handleVerifyCode = () => {
|
||||||
const enteredCode = code.join('');
|
const enteredCode = code.join('');
|
||||||
if (firebaseToken) {
|
|
||||||
mutate({
|
mutate({
|
||||||
phoneNumber,
|
phoneNumber,
|
||||||
otp: enteredCode,
|
otp: enteredCode,
|
||||||
otpType: 'LOGIN',
|
otpType: 'LOGIN',
|
||||||
deviceId: firebaseToken.deviceId,
|
deviceId: '',
|
||||||
deviceName: firebaseToken.deviceName,
|
deviceName: '',
|
||||||
fcmToken: firebaseToken.fcmToken,
|
fcmToken: '',
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -170,6 +172,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'}
|
||||||
|
|||||||
@@ -1,21 +1,13 @@
|
|||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { getApp } from '@react-native-firebase/app';
|
|
||||||
import { getMessaging, getToken } from '@react-native-firebase/messaging';
|
|
||||||
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 { useMutation, useQuery } from '@tanstack/react-query';
|
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 { 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 React, {
|
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
@@ -28,14 +20,11 @@ import {
|
|||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import DeviceInfo from 'react-native-device-info';
|
|
||||||
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 { LoginFormType, loginSchema } from 'screens/auth/login/lib/form';
|
import { LoginFormType, loginSchema } from 'screens/auth/login/lib/form';
|
||||||
import LanguageSelector from 'screens/auth/select-language/SelectLang';
|
import LanguageSelector from 'screens/auth/select-language/SelectLang';
|
||||||
import ArrowDown from 'svg/ArrowDown';
|
|
||||||
import ArrowLeft from 'svg/ArrowLeft';
|
import ArrowLeft from 'svg/ArrowLeft';
|
||||||
import ArrowUp from 'svg/ArrowUp';
|
|
||||||
import { useUserStore } from '../lib/userstore';
|
import { useUserStore } from '../lib/userstore';
|
||||||
import { Loginstyle } from './styled';
|
import { Loginstyle } from './styled';
|
||||||
|
|
||||||
@@ -50,45 +39,45 @@ 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 { data: branchList } = useQuery({
|
const [visible, setVisible] = useState(false);
|
||||||
queryKey: ['branchList'],
|
// const { data: branchList } = useQuery({
|
||||||
queryFn: branchApi.branchList,
|
// queryKey: ['branchList'],
|
||||||
});
|
// queryFn: branchApi.branchList,
|
||||||
const [firebaseToken, setFirebseToken] = useState<{
|
// });
|
||||||
fcmToken: string;
|
// const [firebaseToken, setFirebseToken] = useState<{
|
||||||
deviceId: string;
|
// fcmToken: string;
|
||||||
deviceName: string;
|
// deviceId: string;
|
||||||
deviceType: string;
|
// deviceName: string;
|
||||||
} | null>();
|
// deviceType: string;
|
||||||
|
// } | null>();
|
||||||
|
|
||||||
const app = getApp();
|
// const app = getApp();
|
||||||
const messaging = getMessaging(app);
|
// const messaging = getMessaging(app);
|
||||||
|
|
||||||
const getDeviceData = async () => {
|
// const getDeviceData = async () => {
|
||||||
try {
|
// try {
|
||||||
const fcmToken = await getToken(messaging);
|
// const fcmToken = await getToken(messaging);
|
||||||
return {
|
// return {
|
||||||
fcmToken,
|
// fcmToken,
|
||||||
deviceId: await DeviceInfo.getUniqueId(),
|
// deviceId: await DeviceInfo.getUniqueId(),
|
||||||
deviceName: await DeviceInfo.getDeviceName(),
|
// deviceName: await DeviceInfo.getDeviceName(),
|
||||||
deviceType: await DeviceInfo.getDeviceType(),
|
// deviceType: await DeviceInfo.getDeviceType(),
|
||||||
};
|
// };
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
console.log('Xato:', e);
|
// console.log('Xato:', e);
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
getDeviceData().then(data => {
|
// getDeviceData().then(data => {
|
||||||
setFirebseToken(data);
|
// setFirebseToken(data);
|
||||||
});
|
// });
|
||||||
}, []);
|
// }, []);
|
||||||
|
|
||||||
const { mutate, isPending } = useMutation({
|
const { mutate, isPending } = useMutation({
|
||||||
mutationFn: (payload: loginPayload) => authApi.login(payload),
|
mutationFn: (payload: loginPayload) => authApi.login(payload),
|
||||||
@@ -96,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',
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -117,21 +111,40 @@ 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({
|
||||||
branchId: data.branchId,
|
|
||||||
phoneNumber: data.phone,
|
phoneNumber: data.phone,
|
||||||
passportSerial: `${data.passportSeriya.toUpperCase()}${
|
passportSerial: `${data.passportSeriya.toUpperCase()}${
|
||||||
data.passportNumber
|
data.passportNumber
|
||||||
}`,
|
}`,
|
||||||
fcmToken: firebaseToken?.fcmToken || '',
|
fcmToken: '',
|
||||||
deviceId: firebaseToken?.deviceId || '',
|
deviceId: '',
|
||||||
deviceType: firebaseToken?.deviceType || '',
|
deviceType: '',
|
||||||
deviceName: firebaseToken?.deviceName || '',
|
deviceName: '',
|
||||||
});
|
|
||||||
// navigation.navigate('Login-Confirm');
|
|
||||||
setUser({
|
|
||||||
phoneNumber: data.phone,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setUser({ 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(() => {
|
||||||
@@ -169,11 +182,20 @@ 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}
|
||||||
>
|
>
|
||||||
<ScrollView style={{ flex: 1 }}>
|
<ScrollView style={{ flex: 1 }} keyboardShouldPersistTaps="handled">
|
||||||
<View style={Loginstyle.scrollContainer}>
|
<View style={Loginstyle.scrollContainer}>
|
||||||
<View style={Loginstyle.loginContainer}>
|
<View style={Loginstyle.loginContainer}>
|
||||||
<AppText style={Loginstyle.title}>
|
<AppText style={Loginstyle.title}>
|
||||||
@@ -198,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>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
@@ -252,15 +269,9 @@ 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
|
||||||
control={control}
|
control={control}
|
||||||
name="branchId"
|
name="branchId"
|
||||||
render={({ field: { value } }) => (
|
render={({ field: { value } }) => (
|
||||||
@@ -323,10 +334,10 @@ const Login = () => {
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/> */}
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={handleSubmit(onSubmit)}
|
onPress={handleSubmit(onSubmit, onErrorSubmit)}
|
||||||
style={Loginstyle.button}
|
style={Loginstyle.button}
|
||||||
>
|
>
|
||||||
{isPending ? (
|
{isPending ? (
|
||||||
@@ -337,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>;
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
import { getApp } from '@react-native-firebase/app';
|
// import { getApp } from '@react-native-firebase/app';
|
||||||
import { getMessaging, getToken } from '@react-native-firebase/messaging';
|
// import { getMessaging, getToken } from '@react-native-firebase/messaging';
|
||||||
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 { useMutation } from '@tanstack/react-query';
|
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,
|
||||||
@@ -20,10 +21,8 @@ import {
|
|||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import DeviceInfo from 'react-native-device-info';
|
|
||||||
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';
|
||||||
@@ -50,34 +49,37 @@ 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 [firebaseToken, setFirebseToken] = useState<{
|
const [visible, setVisible] = useState(false);
|
||||||
fcmToken: string;
|
const [error, setError] = useState<string>('Xatolik yuz berdi');
|
||||||
deviceId: string;
|
// const [firebaseToken, setFirebseToken] = useState<{
|
||||||
deviceName: string;
|
// fcmToken: string;
|
||||||
} | null>();
|
// deviceId: string;
|
||||||
|
// deviceName: string;
|
||||||
|
// } | null>();
|
||||||
|
|
||||||
const app = getApp();
|
// const app = getApp();
|
||||||
const messaging = getMessaging(app);
|
// const messaging = getMessaging(app);
|
||||||
|
|
||||||
const getDeviceData = async () => {
|
// const getDeviceData = async () => {
|
||||||
try {
|
// try {
|
||||||
const fcmToken = await getToken(messaging);
|
// const fcmToken = await getToken(messaging);
|
||||||
return {
|
// return {
|
||||||
fcmToken,
|
// fcmToken,
|
||||||
deviceId: await DeviceInfo.getUniqueId(),
|
// deviceId: await DeviceInfo.getUniqueId(),
|
||||||
deviceName: await DeviceInfo.getDeviceName(),
|
// deviceName: await DeviceInfo.getDeviceName(),
|
||||||
};
|
// };
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
console.log('Xato:', e);
|
// console.log('Xato:', e);
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// getDeviceData().then(data => {
|
||||||
|
// setFirebseToken(data);
|
||||||
|
// });
|
||||||
|
// }, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getDeviceData().then(data => {
|
|
||||||
setFirebseToken(data);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
const { mutate, isPending } = useMutation({
|
const { mutate, isPending } = useMutation({
|
||||||
mutationFn: (payload: otpPayload) => authApi.verifyOtp(payload),
|
mutationFn: (payload: otpPayload) => authApi.verifyOtp(payload),
|
||||||
onSuccess: async res => {
|
onSuccess: async res => {
|
||||||
@@ -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;
|
||||||
@@ -149,16 +136,15 @@ const Confirm = ({
|
|||||||
|
|
||||||
const handleVerifyCode = () => {
|
const handleVerifyCode = () => {
|
||||||
const enteredCode = code.join('');
|
const enteredCode = code.join('');
|
||||||
if (firebaseToken) {
|
|
||||||
mutate({
|
mutate({
|
||||||
phoneNumber,
|
phoneNumber,
|
||||||
otp: enteredCode,
|
otp: enteredCode,
|
||||||
otpType: 'REGISTRATION',
|
otpType: 'REGISTRATION',
|
||||||
deviceId: firebaseToken.deviceId,
|
deviceId: '',
|
||||||
deviceName: firebaseToken.deviceName,
|
deviceName: '',
|
||||||
fcmToken: firebaseToken.fcmToken,
|
fcmToken: '',
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -180,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'}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { getApp } from '@react-native-firebase/app';
|
|
||||||
import { getMessaging, getToken } from '@react-native-firebase/messaging';
|
|
||||||
import {
|
import {
|
||||||
type RouteProp,
|
type RouteProp,
|
||||||
useNavigation,
|
useNavigation,
|
||||||
@@ -14,22 +12,22 @@ 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, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Animated,
|
Animated,
|
||||||
ImageBackground,
|
ImageBackground,
|
||||||
|
Keyboard,
|
||||||
KeyboardAvoidingView,
|
KeyboardAvoidingView,
|
||||||
Platform,
|
|
||||||
ScrollView,
|
ScrollView,
|
||||||
TextInput,
|
TextInput,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import DeviceInfo from 'react-native-device-info';
|
|
||||||
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 {
|
import {
|
||||||
@@ -60,51 +58,27 @@ 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'],
|
||||||
queryFn: branchApi.branchList,
|
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({
|
const { mutate, isPending } = useMutation({
|
||||||
mutationFn: (payload: registerPayload) => authApi.register(payload),
|
mutationFn: (payload: registerPayload) => authApi.register(payload),
|
||||||
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',
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -115,12 +89,12 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
const navigation = useNavigation<LoginScreenNavigationProp>();
|
const navigation = useNavigation<LoginScreenNavigationProp>();
|
||||||
const [rawPhone, setRawPhone] = useState('+998');
|
const [rawPhone, setRawPhone] = useState('+998');
|
||||||
const route = useRoute<RouteProp<RootStackParamList, 'Register'>>();
|
const route = useRoute<RouteProp<RootStackParamList, 'Register'>>();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
setValue,
|
setValue,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
getValues,
|
|
||||||
} = useForm<FirstStepFormType>({
|
} = useForm<FirstStepFormType>({
|
||||||
resolver: zodResolver(FirstStepSchema),
|
resolver: zodResolver(FirstStepSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
@@ -131,7 +105,34 @@ 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) => {
|
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,
|
||||||
@@ -144,13 +145,27 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
recommend: data.recommend,
|
recommend: data.recommend,
|
||||||
branchId: data.branchId,
|
branchId: data.branchId,
|
||||||
address: data.address,
|
address: data.address,
|
||||||
fcmToken: firebaseToken?.fcmToken || '',
|
fcmToken: '',
|
||||||
deviceId: firebaseToken?.deviceId || '',
|
deviceId: '',
|
||||||
deviceType: firebaseToken?.deviceType || '',
|
deviceType: '',
|
||||||
deviceName: firebaseToken?.deviceId || '',
|
deviceName: '',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
@@ -196,7 +211,7 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
imageStyle={{
|
imageStyle={{
|
||||||
opacity: 0.1,
|
opacity: 0.1,
|
||||||
height: '100%',
|
height: '100%',
|
||||||
width: '100%',
|
width: '90%',
|
||||||
transform: [{ scale: 1 }],
|
transform: [{ scale: 1 }],
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -208,12 +223,13 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
</View>
|
</View>
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
style={RegisterStyle.container}
|
style={{ flex: 1 }}
|
||||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
behavior="padding"
|
||||||
|
keyboardVerticalOffset={50}
|
||||||
>
|
>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
showsVerticalScrollIndicator={false}
|
contentContainerStyle={{ flexGrow: 1 }}
|
||||||
style={RegisterStyle.content}
|
keyboardShouldPersistTaps="handled"
|
||||||
>
|
>
|
||||||
<View style={RegisterStyle.scrollContainer}>
|
<View style={RegisterStyle.scrollContainer}>
|
||||||
<View style={RegisterStyle.loginContainer}>
|
<View style={RegisterStyle.loginContainer}>
|
||||||
@@ -221,50 +237,56 @@ 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 */}
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="firstName"
|
name="firstName"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<View>
|
<View>
|
||||||
<AppText style={RegisterStyle.label}>{t('Ism')} </AppText>
|
<AppText style={RegisterStyle.label}>{t('Ism')}</AppText>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
ref={firstNameRef}
|
||||||
style={RegisterStyle.input}
|
style={RegisterStyle.input}
|
||||||
placeholder={t('Ismingiz')}
|
placeholder={t('Ismingiz')}
|
||||||
onChangeText={onChange}
|
onChangeText={onChange}
|
||||||
value={value}
|
value={value}
|
||||||
placeholderTextColor={'#D8DADC'}
|
placeholderTextColor={'#D8DADC'}
|
||||||
|
returnKeyType="next"
|
||||||
|
onSubmitEditing={() => lastNameRef.current?.focus()}
|
||||||
/>
|
/>
|
||||||
{errors.firstName && (
|
|
||||||
<AppText style={RegisterStyle.errorText}>
|
|
||||||
{t(errors.firstName.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Familiya */}
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="lastName"
|
name="lastName"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<View>
|
<View>
|
||||||
<AppText style={RegisterStyle.label}>
|
<AppText style={RegisterStyle.label}>
|
||||||
{t('Familiya')}{' '}
|
{t('Familiya')}
|
||||||
</AppText>
|
</AppText>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
ref={lastNameRef}
|
||||||
style={RegisterStyle.input}
|
style={RegisterStyle.input}
|
||||||
placeholder={t('Familiyangiz')}
|
placeholder={t('Familiyangiz')}
|
||||||
placeholderTextColor={'#D8DADC'}
|
placeholderTextColor={'#D8DADC'}
|
||||||
onChangeText={onChange}
|
onChangeText={onChange}
|
||||||
value={value}
|
value={value}
|
||||||
|
returnKeyType="next"
|
||||||
|
onSubmitEditing={() => phoneRef.current?.focus()}
|
||||||
/>
|
/>
|
||||||
{errors.lastName && (
|
|
||||||
<AppText style={RegisterStyle.errorText}>
|
|
||||||
{t(errors.lastName.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Telefon raqami */}
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="phoneNumber"
|
name="phoneNumber"
|
||||||
@@ -276,6 +298,7 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
{t('Telefon raqami')}
|
{t('Telefon raqami')}
|
||||||
</AppText>
|
</AppText>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
ref={phoneRef}
|
||||||
keyboardType="numeric"
|
keyboardType="numeric"
|
||||||
placeholder="+998 __ ___-__-__"
|
placeholder="+998 __ ___-__-__"
|
||||||
value={formatted}
|
value={formatted}
|
||||||
@@ -290,30 +313,32 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
style={RegisterStyle.input}
|
style={RegisterStyle.input}
|
||||||
placeholderTextColor="#D8DADC"
|
placeholderTextColor="#D8DADC"
|
||||||
maxLength={17}
|
maxLength={17}
|
||||||
|
returnKeyType="next"
|
||||||
|
onSubmitEditing={
|
||||||
|
() => setFilialDropdownVisible(true) // ❗ Branch select ochiladi
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
{errors.phoneNumber && (
|
|
||||||
<AppText style={RegisterStyle.errorText}>
|
|
||||||
{t(errors.phoneNumber.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Filial (dropdown) */}
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="branchId"
|
name="branchId"
|
||||||
render={({ field: { value } }) => (
|
render={({ field: { value } }) => (
|
||||||
<View style={{ position: 'relative' }}>
|
<View style={{ position: 'relative' }}>
|
||||||
<AppText style={RegisterStyle.label}>
|
<AppText style={RegisterStyle.label}>
|
||||||
{t('Filial')}{' '}
|
{t('Filial')}
|
||||||
</AppText>
|
</AppText>
|
||||||
<View style={RegisterStyle.input}>
|
<View style={RegisterStyle.input}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={RegisterStyle.selector}
|
style={RegisterStyle.selector}
|
||||||
onPress={() =>
|
onPress={() => {
|
||||||
setFilialDropdownVisible(prev => !prev)
|
setFilialDropdownVisible(prev => !prev);
|
||||||
}
|
Keyboard.dismiss();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<AppText
|
<AppText
|
||||||
style={
|
style={
|
||||||
@@ -345,6 +370,11 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
onPress={() => {
|
onPress={() => {
|
||||||
setValue('branchId', item.id);
|
setValue('branchId', item.id);
|
||||||
setFilialDropdownVisible(false);
|
setFilialDropdownVisible(false);
|
||||||
|
// keyingi inputga focus
|
||||||
|
setTimeout(
|
||||||
|
() => addressRef.current?.focus(),
|
||||||
|
200,
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppText
|
<AppText
|
||||||
@@ -357,14 +387,11 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{errors.branchId && (
|
|
||||||
<AppText style={RegisterStyle.errorText}>
|
|
||||||
{t(errors.branchId.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Manzil */}
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="address"
|
name="address"
|
||||||
@@ -374,6 +401,7 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
{t('Manzilingizni kiriting')}
|
{t('Manzilingizni kiriting')}
|
||||||
</AppText>
|
</AppText>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
ref={addressRef}
|
||||||
style={RegisterStyle.input}
|
style={RegisterStyle.input}
|
||||||
placeholder={t(
|
placeholder={t(
|
||||||
"Toshkent Shahri, Mirzo Ulug'bek tumani...",
|
"Toshkent Shahri, Mirzo Ulug'bek tumani...",
|
||||||
@@ -381,15 +409,16 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
placeholderTextColor={'#D8DADC'}
|
placeholderTextColor={'#D8DADC'}
|
||||||
onChangeText={onChange}
|
onChangeText={onChange}
|
||||||
value={value}
|
value={value}
|
||||||
|
returnKeyType="done"
|
||||||
|
onSubmitEditing={
|
||||||
|
() => setRecommendedDropdownVisible(true) // ❗ recommend select ochiladi
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
{errors.lastName && (
|
|
||||||
<AppText style={RegisterStyle.errorText}>
|
|
||||||
{t(errors.lastName.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Recommend (dropdown) */}
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="recommend"
|
name="recommend"
|
||||||
@@ -446,19 +475,11 @@ 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>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Terms */}
|
||||||
<View style={RegisterStyle.termsContainer}>
|
<View style={RegisterStyle.termsContainer}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={RegisterStyle.checkboxContainer}
|
style={RegisterStyle.checkboxContainer}
|
||||||
@@ -504,9 +525,8 @@ const FirstStep = ({ onNext }: { onNext: () => void }) => {
|
|||||||
</View>
|
</View>
|
||||||
</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';
|
||||||
@@ -21,8 +22,8 @@ import {
|
|||||||
Dimensions,
|
Dimensions,
|
||||||
Image,
|
Image,
|
||||||
ImageBackground,
|
ImageBackground,
|
||||||
|
Keyboard,
|
||||||
KeyboardAvoidingView,
|
KeyboardAvoidingView,
|
||||||
Platform,
|
|
||||||
ScrollView,
|
ScrollView,
|
||||||
TextInput,
|
TextInput,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
@@ -56,12 +57,16 @@ const SecondStep = () => {
|
|||||||
const [checkboxAnimation] = useState(new Animated.Value(1));
|
const [checkboxAnimation] = useState(new Animated.Value(1));
|
||||||
const [inputValue, setInputValue] = useState('');
|
const [inputValue, setInputValue] = useState('');
|
||||||
const { firstName, lastName } = useUserStore(state => state);
|
const { firstName, lastName } = useUserStore(state => state);
|
||||||
|
const passportSeriyaRef = useRef<TextInput>(null);
|
||||||
|
const jshshirRef = useRef<TextInput>(null);
|
||||||
|
const birthDateRef = useRef<TextInput>(null);
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<NativeStackNavigationProp<RootStackParamList, 'Login'>>();
|
useNavigation<NativeStackNavigationProp<RootStackParamList, 'Login'>>();
|
||||||
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');
|
||||||
@@ -74,10 +79,14 @@ const SecondStep = () => {
|
|||||||
mutationFn: (payload: sendPassportPayload) =>
|
mutationFn: (payload: sendPassportPayload) =>
|
||||||
passportApi.sendPassport(payload),
|
passportApi.sendPassport(payload),
|
||||||
onSuccess: res => {
|
onSuccess: res => {
|
||||||
navigation.navigate('Home');
|
navigation.reset({
|
||||||
|
index: 0,
|
||||||
|
routes: [{ name: 'Home' }],
|
||||||
|
});
|
||||||
},
|
},
|
||||||
onError: err => {
|
onError: (err: any) => {
|
||||||
console.dir(err);
|
setError(err?.response?.data || 'Xatolik yuz berdi');
|
||||||
|
setVisible(true);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -109,6 +118,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')}`;
|
||||||
|
|
||||||
@@ -124,6 +144,11 @@ const SecondStep = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onErrorSubmit = (errors: any) => {
|
||||||
|
setError("Ma'lumotlarni to'liq kiriting");
|
||||||
|
setVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ImageBackground
|
<ImageBackground
|
||||||
source={Logo}
|
source={Logo}
|
||||||
@@ -144,11 +169,21 @@ const SecondStep = () => {
|
|||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
<ErrorNotification
|
||||||
|
setVisible={setVisible}
|
||||||
|
error={error}
|
||||||
|
visible={visible}
|
||||||
|
/>
|
||||||
|
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
style={RegisterStyle.container}
|
style={{ flex: 1 }}
|
||||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
behavior="padding"
|
||||||
|
keyboardVerticalOffset={50}
|
||||||
|
>
|
||||||
|
<ScrollView
|
||||||
|
contentContainerStyle={{ flexGrow: 1 }}
|
||||||
|
keyboardShouldPersistTaps="handled"
|
||||||
>
|
>
|
||||||
<ScrollView style={RegisterStyle.content}>
|
|
||||||
<View style={RegisterStyle.scrollContainer}>
|
<View style={RegisterStyle.scrollContainer}>
|
||||||
<View style={RegisterStyle.loginContainer}>
|
<View style={RegisterStyle.loginContainer}>
|
||||||
<AppText style={RegisterStyle.title}>
|
<AppText style={RegisterStyle.title}>
|
||||||
@@ -170,6 +205,7 @@ const SecondStep = () => {
|
|||||||
name="passportSeriya"
|
name="passportSeriya"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<TextInput
|
<TextInput
|
||||||
|
ref={passportSeriyaRef}
|
||||||
style={[
|
style={[
|
||||||
RegisterStyle.input,
|
RegisterStyle.input,
|
||||||
RegisterStyle.seriyaInput,
|
RegisterStyle.seriyaInput,
|
||||||
@@ -178,12 +214,16 @@ const SecondStep = () => {
|
|||||||
maxLength={2}
|
maxLength={2}
|
||||||
autoCapitalize="characters"
|
autoCapitalize="characters"
|
||||||
value={value}
|
value={value}
|
||||||
|
returnKeyType="next"
|
||||||
onChangeText={text => {
|
onChangeText={text => {
|
||||||
onChange(text);
|
onChange(text);
|
||||||
if (text.length === 2)
|
if (text.length === 2)
|
||||||
passportNumberRef.current?.focus();
|
passportNumberRef.current?.focus();
|
||||||
}}
|
}}
|
||||||
placeholderTextColor="#D8DADC"
|
placeholderTextColor="#D8DADC"
|
||||||
|
onSubmitEditing={() =>
|
||||||
|
passportNumberRef.current?.focus()
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -201,21 +241,17 @@ const SecondStep = () => {
|
|||||||
maxLength={7}
|
maxLength={7}
|
||||||
keyboardType="numeric"
|
keyboardType="numeric"
|
||||||
value={value}
|
value={value}
|
||||||
|
returnKeyType="next"
|
||||||
onChangeText={text => {
|
onChangeText={text => {
|
||||||
const onlyNumbers = text.replace(/[^0-9]/g, '');
|
const onlyNumbers = text.replace(/[^0-9]/g, '');
|
||||||
onChange(onlyNumbers);
|
onChange(onlyNumbers);
|
||||||
}}
|
}}
|
||||||
|
onSubmitEditing={() => jshshirRef.current?.focus()}
|
||||||
placeholderTextColor="#D8DADC"
|
placeholderTextColor="#D8DADC"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</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
|
||||||
@@ -228,20 +264,18 @@ const SecondStep = () => {
|
|||||||
</AppText>
|
</AppText>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={RegisterStyle.input}
|
style={RegisterStyle.input}
|
||||||
|
ref={jshshirRef}
|
||||||
placeholder="12345678901234"
|
placeholder="12345678901234"
|
||||||
placeholderTextColor="#D8DADC"
|
placeholderTextColor="#D8DADC"
|
||||||
keyboardType="numeric"
|
keyboardType="numeric"
|
||||||
maxLength={14}
|
maxLength={14}
|
||||||
|
returnKeyType="next"
|
||||||
value={value}
|
value={value}
|
||||||
onChangeText={text =>
|
onChangeText={text =>
|
||||||
onChange(text.replace(/[^0-9]/g, ''))
|
onChange(text.replace(/[^0-9]/g, ''))
|
||||||
}
|
}
|
||||||
|
onSubmitEditing={() => birthDateRef.current?.focus()}
|
||||||
/>
|
/>
|
||||||
{errors.jshshir && (
|
|
||||||
<AppText style={RegisterStyle.errorText}>
|
|
||||||
{t(errors.jshshir.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -267,10 +301,12 @@ const SecondStep = () => {
|
|||||||
RegisterStyle.input,
|
RegisterStyle.input,
|
||||||
{ flex: 1, borderWidth: 0 },
|
{ flex: 1, borderWidth: 0 },
|
||||||
]}
|
]}
|
||||||
|
ref={birthDateRef}
|
||||||
placeholder="dd/mm/yyyy"
|
placeholder="dd/mm/yyyy"
|
||||||
placeholderTextColor="#D8DADC"
|
placeholderTextColor="#D8DADC"
|
||||||
keyboardType="numeric"
|
keyboardType="numeric"
|
||||||
value={value}
|
value={value}
|
||||||
|
returnKeyType="done"
|
||||||
onChangeText={text => {
|
onChangeText={text => {
|
||||||
let cleaned = text
|
let cleaned = text
|
||||||
.replace(/[^\d]/g, '')
|
.replace(/[^\d]/g, '')
|
||||||
@@ -326,6 +362,7 @@ const SecondStep = () => {
|
|||||||
|
|
||||||
setValue('birthDate', formatted);
|
setValue('birthDate', formatted);
|
||||||
}}
|
}}
|
||||||
|
onSubmitEditing={handleSubmit(onSubmit)}
|
||||||
/>
|
/>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => setDatePickerVisibility(true)}
|
onPress={() => setDatePickerVisibility(true)}
|
||||||
@@ -334,12 +371,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>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -377,7 +408,10 @@ const SecondStep = () => {
|
|||||||
>
|
>
|
||||||
<SingleFileDrop
|
<SingleFileDrop
|
||||||
title={t('Old tomon')}
|
title={t('Old tomon')}
|
||||||
onFileSelected={setFrontImage}
|
onFileSelected={file => {
|
||||||
|
setFrontImage(file);
|
||||||
|
Keyboard.dismiss();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View
|
||||||
@@ -388,14 +422,16 @@ const SecondStep = () => {
|
|||||||
>
|
>
|
||||||
<SingleFileDrop
|
<SingleFileDrop
|
||||||
title={t('Orqa tomon')}
|
title={t('Orqa tomon')}
|
||||||
onFileSelected={setBackImage}
|
onFileSelected={file => {
|
||||||
|
setBackImage(file);
|
||||||
|
Keyboard.dismiss();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* BUTTON */}
|
{/* BUTTON */}
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={handleSubmit(onSubmit)}
|
onPress={handleSubmit(onSubmit, onErrorSubmit)}
|
||||||
style={[
|
style={[
|
||||||
RegisterStyle.button,
|
RegisterStyle.button,
|
||||||
!termsAccepted && RegisterStyle.buttonDisabled,
|
!termsAccepted && RegisterStyle.buttonDisabled,
|
||||||
|
|||||||
@@ -3,7 +3,14 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
|||||||
import AppText from 'components/AppText';
|
import AppText from 'components/AppText';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
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 { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import ArrowLeft from 'svg/ArrowLeft';
|
import ArrowLeft from 'svg/ArrowLeft';
|
||||||
import { RootStackParamList } from 'types/types';
|
import { RootStackParamList } from 'types/types';
|
||||||
@@ -16,11 +23,21 @@ type TermsScreenNavigationProp = NativeStackNavigationProp<
|
|||||||
const TermsAndConditions = () => {
|
const TermsAndConditions = () => {
|
||||||
const navigation = useNavigation<TermsScreenNavigationProp>();
|
const navigation = useNavigation<TermsScreenNavigationProp>();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleAgree = () => {
|
const handleAgree = () => {
|
||||||
navigation.goBack();
|
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 (
|
return (
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={styles.container}>
|
||||||
<View style={styles.header}>
|
<View style={styles.header}>
|
||||||
@@ -35,16 +52,24 @@ const TermsAndConditions = () => {
|
|||||||
|
|
||||||
<ScrollView style={styles.content} showsVerticalScrollIndicator={false}>
|
<ScrollView style={styles.content} showsVerticalScrollIndicator={false}>
|
||||||
<AppText style={styles.title}>
|
<AppText style={styles.title}>
|
||||||
{t('foydalanish_shartlari_va_qoidalari')}
|
{t('Foydalanish shartlari va qoidalari')}
|
||||||
</AppText>
|
</AppText>
|
||||||
|
|
||||||
<AppText style={styles.sectionTitle}>{t('umumiy_qoidalar')}</AppText>
|
<AppText style={styles.text}>
|
||||||
<AppText style={styles.text}>{t('umumiy_qoidalar_text')}</AppText>
|
{t(
|
||||||
|
"Biz sizdan Xitoy xalq Respublikasi Davlatidan sotib olingan tovarlarni olib kelishda quyidagi tovarlarni jo‘natmaslikka 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}>
|
<AppText style={styles.sectionTitle}>
|
||||||
{t('foydalanuvchi_majburiyatlari')}
|
{t('foydalanuvchi_majburiyatlari')}
|
||||||
</AppText>
|
</AppText>
|
||||||
<AppText style={styles.text}>
|
<AppText style={[styles.text, { textAlign: 'left' }]}>
|
||||||
{t('foydalanuvchi_majburiyatlari_text')}
|
{t('foydalanuvchi_majburiyatlari_text')}
|
||||||
</AppText>
|
</AppText>
|
||||||
|
|
||||||
@@ -63,17 +88,30 @@ const TermsAndConditions = () => {
|
|||||||
|
|
||||||
<AppText style={styles.sectionTitle}>{t('aloqa')}</AppText>
|
<AppText style={styles.sectionTitle}>{t('aloqa')}</AppText>
|
||||||
<AppText style={styles.text}>{t('aloqa_text')}</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}>
|
<View style={styles.footer}>
|
||||||
<AppText style={styles.footerText}>
|
<AppText style={styles.footerText}>
|
||||||
{t('oxirgi_yangilanish')} {new Date().toLocaleDateString('uz-UZ')}
|
{t('oxirgi_yangilanish')} 01/09/2025
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
<View style={styles.bottomContainer}>
|
<View style={styles.bottomContainer}>
|
||||||
<TouchableOpacity style={styles.agreeButton} onPress={handleAgree}>
|
<TouchableOpacity style={styles.agreeButton} onPress={handleAgree}>
|
||||||
<AppText style={styles.agreeButtonText}>{t('roziman')}</AppText>
|
<AppText style={styles.agreeButtonText}>{t('Roziman')}</AppText>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
|
|||||||
@@ -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 }}>
|
||||||
@@ -41,10 +55,16 @@ const SelectAuth = () => {
|
|||||||
styles.logoImage,
|
styles.logoImage,
|
||||||
{
|
{
|
||||||
width: 180,
|
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> */}
|
{/* <AppText style={[styles.logoText, { fontSize: 32 }]}>CPOST</AppText> */}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -54,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={[
|
||||||
@@ -69,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',
|
||||||
@@ -104,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>
|
||||||
@@ -167,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,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,14 @@ import AppText from 'components/AppText';
|
|||||||
import LayoutTwo from 'components/LayoutTwo';
|
import LayoutTwo from 'components/LayoutTwo';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
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';
|
import Tabs from '../../home/ui/Tabs';
|
||||||
|
|
||||||
interface CargoPricesProps {}
|
interface CargoPricesProps {}
|
||||||
@@ -25,6 +32,19 @@ const CargoPrices = (props: CargoPricesProps) => {
|
|||||||
refetch();
|
refetch();
|
||||||
}, [activeTab]);
|
}, [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 (
|
return (
|
||||||
<LayoutTwo title={t('Kargo narxlari')}>
|
<LayoutTwo title={t('Kargo narxlari')}>
|
||||||
<ScrollView style={{ flex: 1 }}>
|
<ScrollView style={{ flex: 1 }}>
|
||||||
@@ -34,7 +54,7 @@ const CargoPrices = (props: CargoPricesProps) => {
|
|||||||
<View style={{ marginTop: 10, gap: 10, marginBottom: 20 }}>
|
<View style={{ marginTop: 10, gap: 10, marginBottom: 20 }}>
|
||||||
{data &&
|
{data &&
|
||||||
data.map(ref => (
|
data.map(ref => (
|
||||||
<View style={styles.cardWhite}>
|
<View style={styles.cardWhite} key={ref.id}>
|
||||||
<View style={styles.priceCard}>
|
<View style={styles.priceCard}>
|
||||||
<AppText style={styles.titleBlack}>{ref.title}</AppText>
|
<AppText style={styles.titleBlack}>{ref.title}</AppText>
|
||||||
<AppText style={[styles.titleBlack, { fontSize: 16 }]}>
|
<AppText style={[styles.titleBlack, { fontSize: 16 }]}>
|
||||||
@@ -105,7 +125,10 @@ const CargoPrices = (props: CargoPricesProps) => {
|
|||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
<AppText style={[styles.desc, { color: '#000000' }]}>
|
<AppText style={[styles.desc, { color: '#000000' }]}>
|
||||||
{t('Batafsil')}: @CPcargo_admin
|
{t('Batafsil')}:{' '}
|
||||||
|
<AppText style={{ color: '#28A7E8' }} onPress={openTelegram}>
|
||||||
|
@cpost_admin
|
||||||
|
</AppText>
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
<View style={[styles.card]}>
|
<View style={[styles.card]}>
|
||||||
@@ -129,7 +152,7 @@ const CargoPrices = (props: CargoPricesProps) => {
|
|||||||
<View style={{ marginTop: 20, gap: 10, marginBottom: 20 }}>
|
<View style={{ marginTop: 20, gap: 10, marginBottom: 20 }}>
|
||||||
{data &&
|
{data &&
|
||||||
data.map(ref => (
|
data.map(ref => (
|
||||||
<View style={styles.cardWhite}>
|
<View style={styles.cardWhite} key={ref.id}>
|
||||||
<View style={styles.priceCard}>
|
<View style={styles.priceCard}>
|
||||||
<AppText style={styles.titleBlack}>{ref.title}</AppText>
|
<AppText style={styles.titleBlack}>{ref.title}</AppText>
|
||||||
<AppText style={[styles.titleBlack, { fontSize: 16 }]}>
|
<AppText style={[styles.titleBlack, { fontSize: 16 }]}>
|
||||||
@@ -193,7 +216,10 @@ const CargoPrices = (props: CargoPricesProps) => {
|
|||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
<AppText style={[styles.desc, { color: '#000000' }]}>
|
<AppText style={[styles.desc, { color: '#000000' }]}>
|
||||||
{t('Batafsil')}: @CPcargo_admin
|
{t('Batafsil')}:{' '}
|
||||||
|
<AppText style={{ color: '#28A7E8' }} onPress={openTelegram}>
|
||||||
|
@cpost_admin
|
||||||
|
</AppText>
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
<View style={[styles.card]}>
|
<View style={[styles.card]}>
|
||||||
|
|||||||
@@ -75,7 +75,11 @@ const Home = () => {
|
|||||||
removeClippedSubviews={true}
|
removeClippedSubviews={true}
|
||||||
keyboardShouldPersistTaps="handled"
|
keyboardShouldPersistTaps="handled"
|
||||||
>
|
>
|
||||||
<PartyCarousel autoData={autoData} aviaData={aviaData} />
|
<PartyCarousel
|
||||||
|
autoData={autoData}
|
||||||
|
aviaData={aviaData}
|
||||||
|
activeTab={activeTab}
|
||||||
|
/>
|
||||||
<Tabs setActiveTab={setActiveTab} activeTab={activeTab} />
|
<Tabs setActiveTab={setActiveTab} activeTab={activeTab} />
|
||||||
{activeTabContent}
|
{activeTabContent}
|
||||||
<Pages />
|
<Pages />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import AnimatedIcon from 'components/AnimatedIcon';
|
import AnimatedIcon from 'components/AnimatedIcon';
|
||||||
import AppText from 'components/AppText';
|
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 { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
FlatList,
|
FlatList,
|
||||||
@@ -10,6 +10,11 @@ import {
|
|||||||
useWindowDimensions,
|
useWindowDimensions,
|
||||||
View,
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import Animated, {
|
||||||
|
useAnimatedStyle,
|
||||||
|
useSharedValue,
|
||||||
|
withTiming,
|
||||||
|
} from 'react-native-reanimated';
|
||||||
import Auto from 'svg/Auto';
|
import Auto from 'svg/Auto';
|
||||||
import Avia from 'svg/Avia';
|
import Avia from 'svg/Avia';
|
||||||
import BoxCreate from 'svg/BoxCreate';
|
import BoxCreate from 'svg/BoxCreate';
|
||||||
@@ -18,16 +23,35 @@ import { HomeStyle } from './styled';
|
|||||||
|
|
||||||
const PartyCarousel = ({
|
const PartyCarousel = ({
|
||||||
aviaData,
|
aviaData,
|
||||||
|
activeTab,
|
||||||
autoData,
|
autoData,
|
||||||
}: {
|
}: {
|
||||||
autoData: any;
|
autoData: any;
|
||||||
aviaData: any;
|
aviaData: any;
|
||||||
|
activeTab: 'AUTO' | 'AVIA';
|
||||||
}) => {
|
}) => {
|
||||||
const { width: screenWidth } = useWindowDimensions();
|
const { width: screenWidth } = useWindowDimensions();
|
||||||
const cardWidth = screenWidth * 0.95;
|
const cardWidth = screenWidth * 0.95;
|
||||||
const styles = useMemo(() => HomeStyle(), []);
|
const styles = useMemo(() => HomeStyle(), []);
|
||||||
const { t } = useTranslation();
|
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 = {
|
const statusConfig: any = {
|
||||||
COLLECTING: {
|
COLLECTING: {
|
||||||
backgroundColor: '#28A7E8',
|
backgroundColor: '#28A7E8',
|
||||||
@@ -56,7 +80,7 @@ const PartyCarousel = ({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// calendarList tayyorlash
|
// calendarList faqat activeTab uchun
|
||||||
const calendarList = useMemo(() => {
|
const calendarList = useMemo(() => {
|
||||||
const data: any[] = [];
|
const data: any[] = [];
|
||||||
const weekdays = [
|
const weekdays = [
|
||||||
@@ -89,10 +113,14 @@ const PartyCarousel = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (activeTab === 'AUTO') {
|
||||||
prepareList(autoData, 'auto');
|
prepareList(autoData, 'auto');
|
||||||
|
} else if (activeTab === 'AVIA') {
|
||||||
prepareList(aviaData, 'avia');
|
prepareList(aviaData, 'avia');
|
||||||
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}, [autoData, aviaData]);
|
}, [autoData, aviaData, activeTab]);
|
||||||
|
|
||||||
const flatListRef = useRef<FlatList>(null);
|
const flatListRef = useRef<FlatList>(null);
|
||||||
const today = useMemo(() => {
|
const today = useMemo(() => {
|
||||||
@@ -102,6 +130,7 @@ const PartyCarousel = ({
|
|||||||
}, []);
|
}, []);
|
||||||
const [selectedItem, setSelectedItem] = useState<any>(null);
|
const [selectedItem, setSelectedItem] = useState<any>(null);
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
const [modalVisible, setModalVisible] = useState(false);
|
||||||
|
|
||||||
const renderItem = ({ item, index }: { item: any; index: number }) => {
|
const renderItem = ({ item, index }: { item: any; index: number }) => {
|
||||||
const isLast = index === calendarList.length - 1;
|
const isLast = index === calendarList.length - 1;
|
||||||
return (
|
return (
|
||||||
@@ -171,7 +200,7 @@ const PartyCarousel = ({
|
|||||||
{item.cargo.toUpperCase()}
|
{item.cargo.toUpperCase()}
|
||||||
</AppText>
|
</AppText>
|
||||||
<View style={styles.animatedIconWrapper}>
|
<View style={styles.animatedIconWrapper}>
|
||||||
<AnimatedIcon type={item.cargo} />
|
<AnimatedIcon type={activeTab} />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
@@ -181,9 +210,8 @@ const PartyCarousel = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Animated.View style={animatedStyle}>
|
||||||
<FlatList
|
<FlatList
|
||||||
ref={flatListRef}
|
|
||||||
data={calendarList}
|
data={calendarList}
|
||||||
keyExtractor={(_, index) => index.toString()}
|
keyExtractor={(_, index) => index.toString()}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
@@ -233,7 +261,7 @@ const PartyCarousel = ({
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</Animated.View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
@@ -12,7 +13,6 @@ import {
|
|||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
KeyboardAvoidingView,
|
KeyboardAvoidingView,
|
||||||
Platform,
|
|
||||||
ScrollView,
|
ScrollView,
|
||||||
TextInput,
|
TextInput,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
@@ -39,22 +39,38 @@ 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: '',
|
||||||
|
// 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({
|
const { mutate, isPending } = useMutation({
|
||||||
mutationFn: (payload: AddPassportPayload) => {
|
mutationFn: (payload: AddPassportPayload) => {
|
||||||
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',
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -90,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')}`;
|
||||||
|
|
||||||
@@ -104,17 +142,40 @@ 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="Yangi pasport qo'shish">
|
<LayoutTwo title={t("Yangi pasport qo'shish")}>
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
style={PassportStyle.container}
|
style={PassportStyle.container}
|
||||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
behavior={'padding'}
|
||||||
|
keyboardVerticalOffset={30}
|
||||||
>
|
>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
|
keyboardShouldPersistTaps="handled"
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
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}
|
||||||
@@ -123,17 +184,15 @@ const CreatePassword = () => {
|
|||||||
<View>
|
<View>
|
||||||
<AppText style={PassportStyle.label}>{t('Ism')}</AppText>
|
<AppText style={PassportStyle.label}>{t('Ism')}</AppText>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
ref={firstNameRef}
|
||||||
|
returnKeyType="next"
|
||||||
|
onSubmitEditing={() => lastNameRef.current?.focus()}
|
||||||
style={PassportStyle.input}
|
style={PassportStyle.input}
|
||||||
placeholder={t('Ismingiz')}
|
placeholder={t('Ismingiz')}
|
||||||
onChangeText={onChange}
|
onChangeText={onChange}
|
||||||
value={value}
|
value={value}
|
||||||
placeholderTextColor={'#D8DADC'}
|
placeholderTextColor={'#D8DADC'}
|
||||||
/>
|
/>
|
||||||
{errors.firstName && (
|
|
||||||
<AppText style={PassportStyle.errorText}>
|
|
||||||
{t(errors.firstName.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -146,17 +205,15 @@ const CreatePassword = () => {
|
|||||||
{t('Familiya')}
|
{t('Familiya')}
|
||||||
</AppText>
|
</AppText>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
ref={lastNameRef}
|
||||||
|
returnKeyType="next"
|
||||||
|
onSubmitEditing={() => passportSeriyaRef.current?.focus()}
|
||||||
style={PassportStyle.input}
|
style={PassportStyle.input}
|
||||||
placeholder={t('Familiyangiz')}
|
placeholder={t('Familiyangiz')}
|
||||||
placeholderTextColor={'#D8DADC'}
|
placeholderTextColor={'#D8DADC'}
|
||||||
onChangeText={onChange}
|
onChangeText={onChange}
|
||||||
value={value}
|
value={value}
|
||||||
/>
|
/>
|
||||||
{errors.lastName && (
|
|
||||||
<AppText style={PassportStyle.errorText}>
|
|
||||||
{t(errors.lastName.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -170,6 +227,11 @@ const CreatePassword = () => {
|
|||||||
name="passportSeriya"
|
name="passportSeriya"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<TextInput
|
<TextInput
|
||||||
|
ref={passportSeriyaRef}
|
||||||
|
returnKeyType="next"
|
||||||
|
onSubmitEditing={() =>
|
||||||
|
passportNumberRef.current?.focus()
|
||||||
|
}
|
||||||
style={[PassportStyle.input, PassportStyle.seriyaInput]}
|
style={[PassportStyle.input, PassportStyle.seriyaInput]}
|
||||||
placeholder="AA"
|
placeholder="AA"
|
||||||
maxLength={2}
|
maxLength={2}
|
||||||
@@ -191,6 +253,8 @@ const CreatePassword = () => {
|
|||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<TextInput
|
<TextInput
|
||||||
ref={passportNumberRef}
|
ref={passportNumberRef}
|
||||||
|
returnKeyType="next"
|
||||||
|
onSubmitEditing={() => jshshirRef.current?.focus()}
|
||||||
style={[PassportStyle.input, PassportStyle.raqamInput]}
|
style={[PassportStyle.input, PassportStyle.raqamInput]}
|
||||||
placeholder="1234567"
|
placeholder="1234567"
|
||||||
maxLength={7}
|
maxLength={7}
|
||||||
@@ -205,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>
|
||||||
@@ -220,6 +278,9 @@ const CreatePassword = () => {
|
|||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<TextInput
|
<TextInput
|
||||||
style={PassportStyle.input}
|
style={PassportStyle.input}
|
||||||
|
ref={jshshirRef}
|
||||||
|
returnKeyType="next"
|
||||||
|
onSubmitEditing={() => birthDateRef.current?.focus()}
|
||||||
placeholder="12345678901234"
|
placeholder="12345678901234"
|
||||||
placeholderTextColor={'#D8DADC'}
|
placeholderTextColor={'#D8DADC'}
|
||||||
keyboardType="numeric"
|
keyboardType="numeric"
|
||||||
@@ -232,11 +293,6 @@ const CreatePassword = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{errors.jshshir && (
|
|
||||||
<AppText style={PassportStyle.errorText}>
|
|
||||||
{t(errors.jshshir.message || '')}
|
|
||||||
</AppText>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
<View>
|
<View>
|
||||||
<Controller
|
<Controller
|
||||||
@@ -250,6 +306,8 @@ const CreatePassword = () => {
|
|||||||
|
|
||||||
<View style={[PassportStyle.inputContainer]}>
|
<View style={[PassportStyle.inputContainer]}>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
ref={birthDateRef}
|
||||||
|
returnKeyType="done"
|
||||||
style={[
|
style={[
|
||||||
PassportStyle.input,
|
PassportStyle.input,
|
||||||
{ flex: 1, borderWidth: 0, padding: 0 },
|
{ flex: 1, borderWidth: 0, padding: 0 },
|
||||||
@@ -315,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>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -375,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 ? (
|
||||||
@@ -391,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 () => {
|
||||||
|
|||||||
@@ -7,14 +7,12 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
Linking,
|
Linking,
|
||||||
Platform,
|
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import AppLink from 'react-native-app-link';
|
import AppLink from 'react-native-app-link';
|
||||||
import ArrowRightUnderline from 'svg/ArrowRightUnderline';
|
import ArrowRightUnderline from 'svg/ArrowRightUnderline';
|
||||||
import Bell from 'svg/Bell';
|
|
||||||
import Location from 'svg/Location';
|
import Location from 'svg/Location';
|
||||||
import Logout from 'svg/LogOut';
|
import Logout from 'svg/LogOut';
|
||||||
import Setting from 'svg/Setting';
|
import Setting from 'svg/Setting';
|
||||||
@@ -53,15 +51,14 @@ const ProfilePages = (props: componentNameProps) => {
|
|||||||
|
|
||||||
const openTelegram = React.useCallback(async () => {
|
const openTelegram = React.useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
await AppLink.maybeOpenURL('tg://resolve?domain=cpostuz', {
|
await AppLink.maybeOpenURL('tg://resolve?domain=cpost_admin', {
|
||||||
appName: 'Telegram',
|
appName: 'Telegram',
|
||||||
appStoreId: 686449807,
|
appStoreId: 686449807,
|
||||||
appStoreLocale: 'us',
|
appStoreLocale: 'us',
|
||||||
playStoreId: 'org.telegram.messenger',
|
playStoreId: 'org.telegram.messenger',
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Agar ilovani ham, store’ni ham ochib bo‘lmasa, fallback URL
|
Linking.openURL('https://t.me/cpost_admin');
|
||||||
Linking.openURL('https://t.me/cpostuz');
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -80,7 +77,7 @@ const ProfilePages = (props: componentNameProps) => {
|
|||||||
</View>
|
</View>
|
||||||
<ArrowRightUnderline color="#373737" width={24} height={24} />
|
<ArrowRightUnderline color="#373737" width={24} height={24} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
{Platform.OS === 'android' && (
|
{/* {Platform.OS === 'android' && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
styles.card,
|
styles.card,
|
||||||
@@ -94,7 +91,7 @@ const ProfilePages = (props: componentNameProps) => {
|
|||||||
</View>
|
</View>
|
||||||
<ArrowRightUnderline color="#373737" width={24} height={24} />
|
<ArrowRightUnderline color="#373737" width={24} height={24} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)} */}
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
styles.card,
|
styles.card,
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ const TabsAutoWarehouses = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { data } = useQuery({
|
const { data } = useQuery({
|
||||||
queryKey: ['warhouses'],
|
queryKey: ['warhouses_auto'],
|
||||||
queryFn: () => warhouses_api.getWarhouses({ cargoType: 'AUTO' }),
|
queryFn: () => warhouses_api.getWarhouses({ cargoType: 'AUTO' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ const TabsAviaWarehouses = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { data } = useQuery({
|
const { data } = useQuery({
|
||||||
queryKey: ['warhouses'],
|
queryKey: ['warhouses_avia'],
|
||||||
queryFn: () => warhouses_api.getWarhouses({ cargoType: 'AVIA' }),
|
queryFn: () => warhouses_api.getWarhouses({ cargoType: 'AVIA' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ const TabsAviaWarehouses = () => {
|
|||||||
<View style={[styles.card, { marginRight: isLast ? 0 : 10 }]}>
|
<View style={[styles.card, { marginRight: isLast ? 0 : 10 }]}>
|
||||||
<View style={styles.titleCard}>
|
<View style={styles.titleCard}>
|
||||||
<Kitay width={24 * scale} height={24 * scale} />
|
<Kitay width={24 * scale} height={24 * scale} />
|
||||||
<AppText style={styles.title}>China (Auto)</AppText>
|
<AppText style={styles.title}>China (AVIA)</AppText>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.infoId}>
|
<View style={styles.infoId}>
|
||||||
<View style={{ gap: 4 * scale, width: '90%' }}>
|
<View style={{ gap: 4 * scale, width: '90%' }}>
|
||||||
|
|||||||
@@ -100,15 +100,14 @@ Cargo Idsi: ${getMe?.aviaCargoId}
|
|||||||
);
|
);
|
||||||
const openTelegram = React.useCallback(async () => {
|
const openTelegram = React.useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
await AppLink.maybeOpenURL('tg://resolve?domain=cpostuz', {
|
await AppLink.maybeOpenURL('tg://resolve?domain=cpost_admin', {
|
||||||
appName: 'Telegram',
|
appName: 'Telegram',
|
||||||
appStoreId: 686449807,
|
appStoreId: 686449807,
|
||||||
appStoreLocale: 'us',
|
appStoreLocale: 'us',
|
||||||
playStoreId: 'org.telegram.messenger',
|
playStoreId: 'org.telegram.messenger',
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Agar ilovani ham, store’ni ham ochib bo‘lmasa, fallback URL
|
Linking.openURL('https://t.me/cpost_admin');
|
||||||
Linking.openURL('https://t.me/cpostuz');
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { PacketsData } from 'api/packets';
|
import { PacketsData } from 'api/packets';
|
||||||
import AppText from 'components/AppText';
|
import AppText from 'components/AppText';
|
||||||
|
import { formatPrice } from 'helpers/formatPrice';
|
||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FlatList, StyleSheet, TouchableOpacity, View } from 'react-native';
|
import { FlatList, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||||
import Auto from 'svg/Auto';
|
|
||||||
import Avia from 'svg/Avia';
|
import Avia from 'svg/Avia';
|
||||||
import BagIcon from 'svg/BagIcon';
|
import BagIcon from 'svg/BagIcon';
|
||||||
import BoxIcon from 'svg/Box';
|
import BoxIcon from 'svg/Box';
|
||||||
import Store from 'svg/Store';
|
import Store from 'svg/Store';
|
||||||
import SuccessIcon from 'svg/SuccessIcon';
|
|
||||||
import TrunkIcon from 'svg/TrunkIcon';
|
import TrunkIcon from 'svg/TrunkIcon';
|
||||||
|
import Wallet from 'svg/Wallet';
|
||||||
import { DataInfo } from '../lib/data';
|
import { DataInfo } from '../lib/data';
|
||||||
|
|
||||||
const statusColorMap: Record<string, string> = {
|
const statusColorMap: Record<string, string> = {
|
||||||
@@ -30,13 +30,14 @@ const statuses = [
|
|||||||
'DELIVERED',
|
'DELIVERED',
|
||||||
'PAID',
|
'PAID',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
type FilterType = (typeof statuses)[number];
|
type FilterType = (typeof statuses)[number];
|
||||||
|
|
||||||
const tabList: { label: string; value: FilterType }[] = [
|
const tabList: { label: string; value: FilterType }[] = [
|
||||||
{ label: "Yig'ilmoqda", value: 'COLLECTING' },
|
{ label: "Yig'ilmoqda", value: 'COLLECTING' },
|
||||||
{ label: "Yo'lda", value: 'ON_THE_WAY' },
|
{ label: "Yo'lda", value: 'ON_THE_WAY' },
|
||||||
{ label: 'Bojxonada', value: 'IN_CUSTOMS' },
|
{ label: 'Bojxonada', value: 'IN_CUSTOMS' },
|
||||||
{ label: 'Toshkent omboriga yetib keldi', value: 'IN_WAREHOUSE' },
|
{ label: 'Omborga yetib keldi', value: 'IN_WAREHOUSE' },
|
||||||
{ label: 'Topshirish punktida', value: 'DELIVERED' },
|
{ label: 'Topshirish punktida', value: 'DELIVERED' },
|
||||||
{ label: 'Qabul qilingan', value: 'PAID' },
|
{ label: 'Qabul qilingan', value: 'PAID' },
|
||||||
];
|
];
|
||||||
@@ -51,20 +52,18 @@ const Order = ({ data, openModal, selectedData }: Props) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const styles = useMemo(() => makeStyles(), []);
|
const styles = useMemo(() => makeStyles(), []);
|
||||||
|
|
||||||
const createIcons = useCallback(
|
/** 🔥 SUCCESS ICON faqat paymentStatus ga qarab o‘zgaradi */
|
||||||
(cargo: string) => [
|
const createIcons = useCallback((cargo: string, paymentStatus?: string) => {
|
||||||
|
const isPaid = paymentStatus === 'PAYED';
|
||||||
|
|
||||||
|
return [
|
||||||
{
|
{
|
||||||
status: 'COLLECTING',
|
status: 'COLLECTING',
|
||||||
icon: <BoxIcon width={24} height={24} color="" />,
|
icon: <BoxIcon width={24} height={24} color="" />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
status: 'ON_THE_WAY',
|
status: 'ON_THE_WAY',
|
||||||
icon:
|
icon: <Avia width={24} height={24} color="" />,
|
||||||
cargo === 'avia' ? (
|
|
||||||
<Avia width={24} height={24} color="" />
|
|
||||||
) : (
|
|
||||||
<Auto width={24} height={24} color="" view="-4" />
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
status: 'IN_CUSTOMS',
|
status: 'IN_CUSTOMS',
|
||||||
@@ -79,29 +78,33 @@ const Order = ({ data, openModal, selectedData }: Props) => {
|
|||||||
icon: <TrunkIcon width={24} height={24} color="" />,
|
icon: <TrunkIcon width={24} height={24} color="" />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
status: 'PAID',
|
status: 'PENDING',
|
||||||
icon: <SuccessIcon width={24} height={24} color="" />,
|
icon: (
|
||||||
|
<Wallet
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
color={isPaid ? '#28A7E8' : '#C0C0C0'}
|
||||||
|
/>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
],
|
];
|
||||||
[],
|
}, []);
|
||||||
);
|
|
||||||
|
|
||||||
const handleItemPress = useCallback(
|
const handleItemPress = useCallback(
|
||||||
(item: DataInfo) => {
|
(item: DataInfo) => openModal(item),
|
||||||
openModal(item);
|
|
||||||
},
|
|
||||||
[openModal],
|
[openModal],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderOrderItem = useCallback(
|
const renderOrderItem = useCallback(
|
||||||
({ item, index }: { item: DataInfo; index: number }) => {
|
({ item }: { item: DataInfo }) => {
|
||||||
const currentStatusIndex = statuses.indexOf(
|
const currentStatusIndex = statuses.indexOf(
|
||||||
item.deliveryStatus as FilterType,
|
item.deliveryStatus as FilterType,
|
||||||
);
|
);
|
||||||
const icons = createIcons(item.deliveryStatus);
|
|
||||||
|
const icons = createIcons(item.deliveryStatus, item.paymentStatus);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity key={index} onPress={() => handleItemPress(item)}>
|
<TouchableOpacity onPress={() => handleItemPress(item)}>
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
<FlatList
|
<FlatList
|
||||||
data={icons}
|
data={icons}
|
||||||
@@ -109,14 +112,42 @@ const Order = ({ data, openModal, selectedData }: Props) => {
|
|||||||
numColumns={6}
|
numColumns={6}
|
||||||
contentContainerStyle={styles.statusCard}
|
contentContainerStyle={styles.statusCard}
|
||||||
renderItem={({ item: iconItem, index: i }) => {
|
renderItem={({ item: iconItem, index: i }) => {
|
||||||
const iconColor = i <= currentStatusIndex ? '#28A7E8' : '#000';
|
let iconColor = '#000';
|
||||||
const viewColor =
|
let bgColor = '#0000001A';
|
||||||
i <= currentStatusIndex ? '#28A7E81A' : '#0000001A';
|
|
||||||
|
if (
|
||||||
|
iconItem.status === 'DELIVERED' &&
|
||||||
|
item.deliveryStatus === 'DELIVERED'
|
||||||
|
) {
|
||||||
|
iconColor = '#28A7E8';
|
||||||
|
bgColor = '#28A7E81A';
|
||||||
|
} else if (iconItem.status === 'PENDING') {
|
||||||
|
if (item.paymentStatus === 'PAYED') {
|
||||||
|
iconColor = '#28A7E8'; // blue
|
||||||
|
bgColor = '#28A7E81A';
|
||||||
|
} else if (item.paymentStatus === 'PENDING') {
|
||||||
|
iconColor = '#FFA500'; // orange
|
||||||
|
bgColor = '#FFA5001A';
|
||||||
|
} else {
|
||||||
|
iconColor = '#C0C0C0'; // gray
|
||||||
|
bgColor = '#C0C0C01A';
|
||||||
|
}
|
||||||
|
} else if (i <= currentStatusIndex) {
|
||||||
|
iconColor = '#28A7E8'; // blue
|
||||||
|
bgColor = '#28A7E81A';
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.iconWrapper}>
|
<View style={styles.iconWrapper}>
|
||||||
<View
|
<View
|
||||||
style={[styles.circle, { backgroundColor: viewColor }]}
|
style={[
|
||||||
|
styles.circle,
|
||||||
|
{
|
||||||
|
backgroundColor: bgColor,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
{React.cloneElement(iconItem.icon, { color: iconColor })}
|
{React.cloneElement(iconItem.icon, { color: iconColor })}
|
||||||
</View>
|
</View>
|
||||||
@@ -133,29 +164,62 @@ const Order = ({ data, openModal, selectedData }: Props) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Status Label */}
|
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.statusLabelWrapper,
|
|
||||||
{
|
{
|
||||||
backgroundColor: `${statusColorMap[item.deliveryStatus]}1A`,
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
width: '100%',
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<AppText
|
<AppText
|
||||||
style={[
|
style={[
|
||||||
styles.statusText,
|
styles.statusText,
|
||||||
{ color: statusColorMap[item.deliveryStatus] },
|
styles.statusLabelWrapper,
|
||||||
|
{
|
||||||
|
color: statusColorMap[item.deliveryStatus],
|
||||||
|
backgroundColor: `${statusColorMap[item.deliveryStatus]}1A`,
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{t(
|
{t(
|
||||||
tabList.find(tab => tab.value === item.deliveryStatus)
|
tabList.find(el => el.value === item.deliveryStatus)?.label ||
|
||||||
?.label || '',
|
'',
|
||||||
)}
|
)}
|
||||||
</AppText>
|
</AppText>
|
||||||
|
<AppText
|
||||||
|
style={[
|
||||||
|
styles.statusText,
|
||||||
|
styles.statusLabelWrapper,
|
||||||
|
{
|
||||||
|
color:
|
||||||
|
item.paymentStatus === 'NEW'
|
||||||
|
? '#D32F2F'
|
||||||
|
: item.paymentStatus === 'PENDING'
|
||||||
|
? '#FFA500'
|
||||||
|
: item.paymentStatus === 'PAYED'
|
||||||
|
? '#32CD32'
|
||||||
|
: '#D32F2F',
|
||||||
|
backgroundColor:
|
||||||
|
item.paymentStatus === 'NEW'
|
||||||
|
? '#D32F2F1A'
|
||||||
|
: item.paymentStatus === 'PENDING'
|
||||||
|
? '#FFA5001A'
|
||||||
|
: item.paymentStatus === 'PAYED'
|
||||||
|
? '#32CD321A'
|
||||||
|
: '#D32F2F',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{item.paymentStatus === 'NEW'
|
||||||
|
? t("To'lanmagan")
|
||||||
|
: item.paymentStatus === 'PENDING'
|
||||||
|
? t("To'lov kutilmoqda")
|
||||||
|
: item.paymentStatus === 'PAYED' && t("To'langan")}
|
||||||
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Info */}
|
|
||||||
<View style={styles.infoCard}>
|
<View style={styles.infoCard}>
|
||||||
<AppText style={styles.infoTitle}>{t('Reys raqami')}</AppText>
|
<AppText style={styles.infoTitle}>{t('Reys raqami')}</AppText>
|
||||||
<AppText
|
<AppText
|
||||||
@@ -164,16 +228,70 @@ const Order = ({ data, openModal, selectedData }: Props) => {
|
|||||||
{item.packetName}
|
{item.packetName}
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{item.items.map(e => (
|
||||||
|
<View
|
||||||
|
key={e.trekId}
|
||||||
|
style={{
|
||||||
|
...styles.infoCard,
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: 4,
|
||||||
|
marginTop: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AppText style={styles.infoTitle}>
|
||||||
|
{t('Mahsulot nomi')}
|
||||||
|
</AppText>
|
||||||
|
<AppText style={styles.infoText}>{e.name}</AppText>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AppText style={styles.infoTitle}>{t('Trek ID')}</AppText>
|
||||||
|
<AppText style={styles.infoText}>{e.trekId}</AppText>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AppText style={styles.infoTitle}>{t('Og’irligi')}</AppText>
|
||||||
|
<AppText style={styles.infoText}>{e.weight} kg</AppText>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
|
||||||
<View style={styles.infoCard}>
|
<View style={styles.infoCard}>
|
||||||
<AppText style={styles.infoTitle}>
|
<AppText style={styles.infoTitle}>
|
||||||
{t('Mahsulotlar og’irligi')}
|
{t('Mahsulotlar og’irligi')}
|
||||||
</AppText>
|
</AppText>
|
||||||
<AppText style={styles.infoText}>{item.weight}</AppText>
|
<AppText style={styles.infoText}>{item.weight} kg</AppText>
|
||||||
</View>
|
</View>
|
||||||
|
{(item.deliveryStatus === 'DELIVERED' ||
|
||||||
|
item.paymentStatus === 'PAID') && (
|
||||||
<View style={styles.infoCard}>
|
<View style={styles.infoCard}>
|
||||||
<AppText style={styles.infoTitle}>{t('Umumiy narxi')}</AppText>
|
<AppText style={styles.infoTitle}>{t('Umumiy narxi')}</AppText>
|
||||||
<AppText style={styles.infoText}>{item.totalPrice}</AppText>
|
<AppText style={styles.infoText}>
|
||||||
|
{formatPrice(item.totalPrice)} {t('so‘m')}
|
||||||
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
@@ -207,49 +325,37 @@ const Order = ({ data, openModal, selectedData }: Props) => {
|
|||||||
|
|
||||||
const makeStyles = () =>
|
const makeStyles = () =>
|
||||||
StyleSheet.create({
|
StyleSheet.create({
|
||||||
container: {
|
container: { width: '95%', margin: 'auto', borderRadius: 10 },
|
||||||
width: '95%',
|
|
||||||
margin: 'auto',
|
|
||||||
borderRadius: 10,
|
|
||||||
},
|
|
||||||
count: {
|
count: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
|
||||||
marginTop: 10,
|
marginTop: 10,
|
||||||
},
|
},
|
||||||
title: {
|
title: { fontSize: 16, fontWeight: '500', color: '#333' },
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: '500',
|
|
||||||
color: '#333',
|
|
||||||
},
|
|
||||||
card: {
|
card: {
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
margin: 1,
|
|
||||||
marginTop: 8,
|
marginTop: 8,
|
||||||
padding: 15,
|
padding: 15,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
// iOS
|
|
||||||
shadowColor: '#000',
|
shadowColor: '#000',
|
||||||
shadowOffset: { width: 0, height: 2 },
|
shadowOffset: { width: 0, height: 2 },
|
||||||
shadowOpacity: 0.2,
|
shadowOpacity: 0.2,
|
||||||
shadowRadius: 2,
|
shadowRadius: 2,
|
||||||
// Android
|
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
},
|
},
|
||||||
statusCard: {
|
statusCard: { marginBottom: 10 },
|
||||||
marginBottom: 10,
|
|
||||||
},
|
|
||||||
circle: {
|
circle: {
|
||||||
padding: 8,
|
padding: 8,
|
||||||
borderRadius: 50,
|
borderRadius: 50,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
},
|
},
|
||||||
divider: {
|
divider: { width: 20, borderBottomWidth: 2, borderStyle: 'dashed' },
|
||||||
width: 20,
|
iconWrapper: {
|
||||||
borderBottomWidth: 2,
|
flexDirection: 'row',
|
||||||
borderStyle: 'dashed',
|
alignItems: 'center',
|
||||||
|
flex: 1,
|
||||||
|
marginBottom: 10,
|
||||||
},
|
},
|
||||||
statusLabelWrapper: {
|
statusLabelWrapper: {
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
@@ -258,31 +364,14 @@ const makeStyles = () =>
|
|||||||
paddingHorizontal: 10,
|
paddingHorizontal: 10,
|
||||||
marginBottom: 12,
|
marginBottom: 12,
|
||||||
},
|
},
|
||||||
statusText: {
|
statusText: { fontFamily: 'GolosText-Bold', fontSize: 16 },
|
||||||
fontFamily: 'GolosText-Bold',
|
|
||||||
fontSize: 16,
|
|
||||||
},
|
|
||||||
infoCard: {
|
infoCard: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
marginBottom: 6,
|
marginBottom: 6,
|
||||||
},
|
},
|
||||||
iconWrapper: {
|
infoTitle: { fontSize: 16, color: '#979797', fontWeight: '500' },
|
||||||
flexDirection: 'row',
|
infoText: { fontSize: 14, color: '#28A7E8', fontWeight: '500' },
|
||||||
alignItems: 'center',
|
|
||||||
flex: 1,
|
|
||||||
marginBottom: 10,
|
|
||||||
},
|
|
||||||
infoTitle: {
|
|
||||||
fontSize: 16,
|
|
||||||
color: '#979797',
|
|
||||||
fontWeight: '500',
|
|
||||||
},
|
|
||||||
infoText: {
|
|
||||||
fontSize: 14,
|
|
||||||
color: '#28A7E8',
|
|
||||||
fontWeight: '500',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Order;
|
export default Order;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import AppText from 'components/AppText';
|
import AppText from 'components/AppText';
|
||||||
|
import { formatPrice } from 'helpers/formatPrice';
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
@@ -22,14 +23,6 @@ const OrderDetailModal = ({ visible, setVisible, selectedOrder }: Props) => {
|
|||||||
const translateY = useRef(new Animated.Value(50)).current;
|
const translateY = useRef(new Animated.Value(50)).current;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const parsePrice = (priceStr: string) => {
|
|
||||||
return Number(priceStr.replace(/[^\d]/g, ''));
|
|
||||||
};
|
|
||||||
|
|
||||||
const parseWeight = (weightStr: string) => {
|
|
||||||
return Number(weightStr.replace(/[^\d.]/g, ''));
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
Animated.timing(opacity, {
|
Animated.timing(opacity, {
|
||||||
toValue: 0,
|
toValue: 0,
|
||||||
@@ -82,10 +75,6 @@ const OrderDetailModal = ({ visible, setVisible, selectedOrder }: Props) => {
|
|||||||
style={{ maxHeight: 250 }}
|
style={{ maxHeight: 250 }}
|
||||||
>
|
>
|
||||||
{selectedOrder.items.map((product, index) => {
|
{selectedOrder.items.map((product, index) => {
|
||||||
const totalPrice = product.totalPrice;
|
|
||||||
const weight = product.weight;
|
|
||||||
const pricePerKg = Math.ceil(weight ? totalPrice / weight : 0);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View key={product.trekId + index} style={styles.productItem}>
|
<View key={product.trekId + index} style={styles.productItem}>
|
||||||
<View style={styles.row}>
|
<View style={styles.row}>
|
||||||
@@ -100,13 +89,12 @@ const OrderDetailModal = ({ visible, setVisible, selectedOrder }: Props) => {
|
|||||||
{t('Og’irligi')}: {product.weight}
|
{t('Og’irligi')}: {product.weight}
|
||||||
</AppText>
|
</AppText>
|
||||||
<AppText style={styles.detail}>
|
<AppText style={styles.detail}>
|
||||||
{t('Narxi')}: 1kg * {pricePerKg.toLocaleString('uz-UZ')}{' '}
|
{t('Narxi')}: {product.price}$
|
||||||
{t("so'm")}
|
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.rowRight}>
|
<View style={styles.rowRight}>
|
||||||
<AppText style={styles.total}>
|
<AppText style={styles.total}>
|
||||||
{t('Umumiy narxi')}: {product.totalPrice} {t('so‘m')}
|
{t('Umumiy narxi')}: {product.totalPrice}$
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -117,7 +105,7 @@ const OrderDetailModal = ({ visible, setVisible, selectedOrder }: Props) => {
|
|||||||
<View style={styles.totalRow}>
|
<View style={styles.totalRow}>
|
||||||
<AppText style={styles.totalLabel}>{t('Umumiy narx')}:</AppText>
|
<AppText style={styles.totalLabel}>{t('Umumiy narx')}:</AppText>
|
||||||
<AppText style={styles.totalValue}>
|
<AppText style={styles.totalValue}>
|
||||||
{selectedOrder.totalPrice}
|
{formatPrice(selectedOrder.totalPrice)} {t('so‘m')}
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|||||||
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;
|
||||||
@@ -14,16 +14,15 @@ import {
|
|||||||
View,
|
View,
|
||||||
useWindowDimensions,
|
useWindowDimensions,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import Serach from 'svg/Serach';
|
|
||||||
import { DataInfo } from '../lib/data';
|
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 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 +37,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,12 +45,14 @@ const Status = () => {
|
|||||||
isLoading,
|
isLoading,
|
||||||
isFetching,
|
isFetching,
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
queryKey: ['status', filter, transportTypes, page],
|
queryKey: ['status', transportTypes, page, paymentStatus],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
packetsApi.getPacketsStatus(filter, {
|
packetsApi.getPackets({
|
||||||
page,
|
page,
|
||||||
size: 10,
|
size: 10,
|
||||||
cargoType: transportTypes,
|
cargoType: transportTypes,
|
||||||
|
sort: 'id',
|
||||||
|
direction: 'DESC',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -131,11 +132,6 @@ const Status = () => {
|
|||||||
[refreshing, isFetching, onRefresh],
|
[refreshing, isFetching, onRefresh],
|
||||||
);
|
);
|
||||||
|
|
||||||
const searchIcon = useMemo(
|
|
||||||
() => <Serach color="#D8DADC" width={20 * scale} height={20 * scale} />,
|
|
||||||
[scale],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isLoading || isFetching) {
|
if (isLoading || isFetching) {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
@@ -144,20 +140,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")} />
|
||||||
@@ -172,7 +178,7 @@ const Status = () => {
|
|||||||
refreshControl={refreshControl}
|
refreshControl={refreshControl}
|
||||||
removeClippedSubviews={true}
|
removeClippedSubviews={true}
|
||||||
>
|
>
|
||||||
<Tabs filter={filter} setFilter={setFilter} />
|
{/* <Tabs filter={filter} setFilter={setFilter} /> */}
|
||||||
<View style={styles.controls}>
|
<View style={styles.controls}>
|
||||||
{/* <View style={styles.searchContainer}>
|
{/* <View style={styles.searchContainer}>
|
||||||
<TextInput
|
<TextInput
|
||||||
@@ -182,7 +188,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 +203,10 @@ const Status = () => {
|
|||||||
data={statusData!}
|
data={statusData!}
|
||||||
setSelectedData={setSelectedData}
|
setSelectedData={setSelectedData}
|
||||||
/>
|
/>
|
||||||
|
{/* <PaymentFilter
|
||||||
|
paymentStatus={paymentStatus}
|
||||||
|
setPaymentStatus={setPaymentStatus}
|
||||||
|
/> */}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Order
|
<Order
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { PacketsData } from 'api/packets';
|
|||||||
import AppText from 'components/AppText';
|
import AppText from 'components/AppText';
|
||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { TouchableOpacity, View } from 'react-native';
|
import { StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||||
import { PaymentStyle } from './style';
|
import { PaymentStyle } from './style';
|
||||||
|
|
||||||
type WalletStackParamList = {
|
type WalletStackParamList = {
|
||||||
@@ -22,6 +22,7 @@ interface Props {
|
|||||||
const Payment = ({ packets }: Props) => {
|
const Payment = ({ packets }: Props) => {
|
||||||
const navigation = useNavigation<LoginScreenNavigationProp>();
|
const navigation = useNavigation<LoginScreenNavigationProp>();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const styles = useMemo(() => makeStyles(), []);
|
||||||
|
|
||||||
const handlePaymentPress = useCallback(
|
const handlePaymentPress = useCallback(
|
||||||
(item: any) => {
|
(item: any) => {
|
||||||
@@ -52,10 +53,17 @@ const Payment = ({ packets }: Props) => {
|
|||||||
const renderPaymentCard = useCallback(
|
const renderPaymentCard = useCallback(
|
||||||
(item: any) => {
|
(item: any) => {
|
||||||
const isPaid =
|
const isPaid =
|
||||||
item.paymentStatus === 'PAYED' || item.paymentStatus === 'PENDING';
|
item.paymentStatus === 'PAYED' || item.paymentStatus === 'PAID';
|
||||||
const cardStyle = [
|
const cardStyle = [
|
||||||
PaymentStyle.card,
|
PaymentStyle.card,
|
||||||
{ borderColor: isPaid ? '#4CAF50' : '#D32F2F', borderWidth: 1.5 },
|
{
|
||||||
|
borderColor: isPaid
|
||||||
|
? '#4CAF50'
|
||||||
|
: item.paymentStatus === 'PENDING'
|
||||||
|
? '#FFA500'
|
||||||
|
: '#D32F2F',
|
||||||
|
borderWidth: 1.5,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -70,6 +78,20 @@ const Payment = ({ packets }: Props) => {
|
|||||||
<AppText style={PaymentStyle.title}>{item.packetName}</AppText>
|
<AppText style={PaymentStyle.title}>{item.packetName}</AppText>
|
||||||
{isPaid ? (
|
{isPaid ? (
|
||||||
<AppText style={PaymentStyle.badge}>{t("To'langan")}</AppText>
|
<AppText style={PaymentStyle.badge}>{t("To'langan")}</AppText>
|
||||||
|
) : item.paymentStatus === 'PENDING' ? (
|
||||||
|
<AppText
|
||||||
|
style={{
|
||||||
|
backgroundColor: '#FFA500',
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 12,
|
||||||
|
paddingHorizontal: 8,
|
||||||
|
paddingVertical: 2,
|
||||||
|
borderRadius: 5,
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Kutilmoqda')}
|
||||||
|
</AppText>
|
||||||
) : (
|
) : (
|
||||||
<AppText style={badgeStyle}>{t("To'lanmagan")}</AppText>
|
<AppText style={badgeStyle}>{t("To'lanmagan")}</AppText>
|
||||||
)}
|
)}
|
||||||
@@ -87,7 +109,53 @@ const Payment = ({ packets }: Props) => {
|
|||||||
{item.packetName}
|
{item.packetName}
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
<View style={PaymentStyle.row}>
|
{item.items.map((e: any) => (
|
||||||
|
<View
|
||||||
|
key={e.trekId}
|
||||||
|
style={{
|
||||||
|
...styles.infoCard,
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AppText style={styles.infoTitle}>
|
||||||
|
{t('Mahsulot nomi')}
|
||||||
|
</AppText>
|
||||||
|
<AppText style={styles.infoText}>{e.name}</AppText>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AppText style={styles.infoTitle}>{t('Trek ID')}</AppText>
|
||||||
|
<AppText style={styles.infoText}>{e.trekId}</AppText>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AppText style={styles.infoTitle}>{t('Og’irligi')}</AppText>
|
||||||
|
<AppText style={styles.infoText}>{e.weight} kg</AppText>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
{/* <View style={PaymentStyle.row}>
|
||||||
<AppText style={PaymentStyle.infoTitle}>
|
<AppText style={PaymentStyle.infoTitle}>
|
||||||
{t("Mahsulotlar og'irligi")}
|
{t("Mahsulotlar og'irligi")}
|
||||||
</AppText>
|
</AppText>
|
||||||
@@ -104,7 +172,7 @@ const Payment = ({ packets }: Props) => {
|
|||||||
{t('Umumiy narxi')}
|
{t('Umumiy narxi')}
|
||||||
</AppText>
|
</AppText>
|
||||||
<AppText style={PaymentStyle.text}>{item.totalPrice}</AppText>
|
<AppText style={PaymentStyle.text}>{item.totalPrice}</AppText>
|
||||||
</View>
|
</View> */}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
@@ -120,4 +188,59 @@ const Payment = ({ packets }: Props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const makeStyles = () =>
|
||||||
|
StyleSheet.create({
|
||||||
|
container: { width: '95%', margin: 'auto', borderRadius: 10 },
|
||||||
|
count: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginTop: 10,
|
||||||
|
},
|
||||||
|
title: { fontSize: 16, fontWeight: '500', color: '#333' },
|
||||||
|
|
||||||
|
card: {
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
marginTop: 8,
|
||||||
|
padding: 15,
|
||||||
|
borderRadius: 10,
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOffset: { width: 0, height: 2 },
|
||||||
|
shadowOpacity: 0.2,
|
||||||
|
shadowRadius: 2,
|
||||||
|
elevation: 2,
|
||||||
|
},
|
||||||
|
|
||||||
|
statusCard: { marginBottom: 10 },
|
||||||
|
circle: {
|
||||||
|
padding: 8,
|
||||||
|
borderRadius: 50,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
divider: { width: 20, borderBottomWidth: 2, borderStyle: 'dashed' },
|
||||||
|
iconWrapper: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
flex: 1,
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
|
||||||
|
statusLabelWrapper: {
|
||||||
|
borderRadius: 8,
|
||||||
|
alignSelf: 'flex-start',
|
||||||
|
paddingVertical: 6,
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
statusText: { fontFamily: 'GolosText-Bold', fontSize: 16 },
|
||||||
|
|
||||||
|
infoCard: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginBottom: 6,
|
||||||
|
},
|
||||||
|
infoTitle: { fontSize: 16, color: '#979797', fontWeight: '500' },
|
||||||
|
infoText: { fontSize: 14, color: '#28A7E8', fontWeight: '500' },
|
||||||
|
});
|
||||||
|
|
||||||
export default Payment;
|
export default Payment;
|
||||||
|
|||||||
@@ -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,10 @@ 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,
|
||||||
|
});
|
||||||
|
|
||||||
const { mutate, isPending } = useMutation({
|
const { mutate, isPending } = useMutation({
|
||||||
mutationFn: ({ id, payType }: { id: number; payType: string }) =>
|
mutationFn: ({ id, payType }: { id: number; payType: string }) =>
|
||||||
@@ -137,11 +142,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 +174,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 +184,7 @@ const ModalPay = ({
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
{paymentType !== 'CASH' && (
|
{paymentType !== 'CASH' && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import exchanges_api from 'api/exchanges';
|
import exchanges_api from 'api/exchanges';
|
||||||
import AppText from 'components/AppText';
|
import AppText from 'components/AppText';
|
||||||
|
import { formatPrice } from 'helpers/formatPrice';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Dimensions, ScrollView, View } from 'react-native';
|
import { Dimensions, ScrollView, StyleSheet, View } from 'react-native';
|
||||||
import Svg, { Circle, Path } from 'react-native-svg';
|
import Svg, { Circle, Path } from 'react-native-svg';
|
||||||
import Plane from 'svg/Plane';
|
import Plane from 'svg/Plane';
|
||||||
import { PaymentStyle } from '../../payment/ui/style';
|
import { PaymentStyle } from '../../payment/ui/style';
|
||||||
@@ -17,6 +18,7 @@ const PaymentProduct = ({ packet }: PaymentProductProps) => {
|
|||||||
const screenWidth = Dimensions.get('window').width;
|
const screenWidth = Dimensions.get('window').width;
|
||||||
const isSmallScreen = screenWidth < 380;
|
const isSmallScreen = screenWidth < 380;
|
||||||
const svgWidth = screenWidth * 0.8;
|
const svgWidth = screenWidth * 0.8;
|
||||||
|
const svgWidthProduct = screenWidth * 1;
|
||||||
|
|
||||||
const { data } = useQuery({
|
const { data } = useQuery({
|
||||||
queryKey: ['exchanges'],
|
queryKey: ['exchanges'],
|
||||||
@@ -80,21 +82,23 @@ const PaymentProduct = ({ packet }: PaymentProductProps) => {
|
|||||||
: { flexBasis: '48%', alignItems: 'flex-end' },
|
: { flexBasis: '48%', alignItems: 'flex-end' },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<AppText style={PaymentStyle.titleMethod}>Cargo</AppText>
|
<AppText style={PaymentStyle.titleMethod}>{t('Reys')}</AppText>
|
||||||
<AppText style={PaymentStyle.textMethod}>12.267 UZS/ kg</AppText>
|
<AppText style={[PaymentStyle.textMethod, { textAlign: 'left' }]}>
|
||||||
|
{packet.packetName}
|
||||||
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* <View
|
{/* <View
|
||||||
style={[
|
style={[
|
||||||
PaymentStyle.info,
|
PaymentStyle.info,
|
||||||
isSmallScreen
|
isSmallScreen
|
||||||
? { flexBasis: '48%', alignItems: 'flex-start' }
|
? { flexBasis: '48%', alignItems: 'flex-end' }
|
||||||
: { flexBasis: '48%', alignItems: 'flex-start' },
|
: { flexBasis: '48%', alignItems: 'flex-end' },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<AppText style={PaymentStyle.titleMethod}>{t('Yetkazish vaqti')}</AppText>
|
<AppText style={PaymentStyle.titleMethod}>Cargo</AppText>
|
||||||
<AppText style={PaymentStyle.textMethod}>08.25.2025</AppText>
|
<AppText style={PaymentStyle.textMethod}>12.267 UZS/ kg</AppText>
|
||||||
</View> */}
|
</View>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
@@ -104,11 +108,11 @@ const PaymentProduct = ({ packet }: PaymentProductProps) => {
|
|||||||
: { flexBasis: '48%', alignItems: 'flex-start' },
|
: { flexBasis: '48%', alignItems: 'flex-start' },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<AppText style={PaymentStyle.titleMethod}>Reys</AppText>
|
<AppText style={PaymentStyle.titleMethod}>
|
||||||
<AppText style={[PaymentStyle.textMethod, { textAlign: 'left' }]}>
|
{t('Yetkazish vaqti')}
|
||||||
{packet.packetName}
|
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
<AppText style={PaymentStyle.textMethod}>08.25.2025</AppText>
|
||||||
|
</View> */}
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
@@ -149,36 +153,40 @@ const PaymentProduct = ({ packet }: PaymentProductProps) => {
|
|||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
{packet.items.map((item: any, index: number) => {
|
{packet.items.map((item: any, index: number) => {
|
||||||
const price = Number(item.price);
|
|
||||||
const weight = Number(item.weight);
|
|
||||||
const total = price * weight;
|
|
||||||
|
|
||||||
// formatlash: 0 decimal, so‘m bilan
|
|
||||||
const formattedPrice = price.toFixed(0);
|
|
||||||
const formattedTotal = total.toFixed(0);
|
|
||||||
return (
|
return (
|
||||||
<View key={index} style={{ marginBottom: 15 }}>
|
<View key={index} style={{ marginBottom: 15 }}>
|
||||||
<View style={PaymentStyle.receiptCard}>
|
<View style={PaymentStyle.receiptCard}>
|
||||||
<View style={PaymentStyle.rowBetween}>
|
<View style={PaymentStyle.rowBetween}>
|
||||||
|
<View style={{ width: '100%' }}>
|
||||||
<AppText style={PaymentStyle.itemName}>{item.name}</AppText>
|
<AppText style={PaymentStyle.itemName}>{item.name}</AppText>
|
||||||
<AppText style={PaymentStyle.track}>
|
<AppText style={PaymentStyle.track}>
|
||||||
{t('Trek ID')}: {item.trackId}
|
{t('Trek ID')}: {item.trekId}
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
|
</View>
|
||||||
<View style={PaymentStyle.rowBetween}>
|
<View style={PaymentStyle.rowBetween}>
|
||||||
<AppText style={PaymentStyle.weight}>
|
<AppText style={PaymentStyle.weight}>
|
||||||
{t('Og’irligi')}: {weight} kg
|
{t('Og’irligi')}: {item.weight} kg
|
||||||
</AppText>
|
</AppText>
|
||||||
<AppText style={PaymentStyle.price}>
|
<AppText style={PaymentStyle.price}>
|
||||||
1kg * {formattedPrice} {t('so‘m')}
|
{t('Narxi')}: {item.price}$
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
<View style={PaymentStyle.rowRight}>
|
<View style={PaymentStyle.rowRight}>
|
||||||
<AppText style={PaymentStyle.total}>
|
<AppText style={PaymentStyle.total}>
|
||||||
{t('Umumiy narxi')}: {formattedTotal} {t('so‘m')}
|
{t('Umumiy narxi')}: {item.totalPrice} $
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
<Svg height="1" width={svgWidthProduct}>
|
||||||
|
<Path
|
||||||
|
d={`M 0 0 Q ${svgWidthProduct} 0 ${svgWidthProduct} 0`}
|
||||||
|
stroke="#8C8A93"
|
||||||
|
strokeWidth="2"
|
||||||
|
fill="none"
|
||||||
|
strokeDasharray="9"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -205,7 +213,7 @@ const PaymentProduct = ({ packet }: PaymentProductProps) => {
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<AppText style={PaymentStyle.titleMethod}>
|
<AppText style={PaymentStyle.titleMethod}>
|
||||||
{packet.totalPrice}
|
{formatPrice(packet.totalPrice)} {t('so‘m')}
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -214,4 +222,15 @@ const PaymentProduct = ({ packet }: PaymentProductProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
cornerStyle: {
|
||||||
|
position: 'absolute' as const,
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
borderTopWidth: 6,
|
||||||
|
borderLeftWidth: 6,
|
||||||
|
borderColor: '#007AFF',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export default PaymentProduct;
|
export default PaymentProduct;
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { RouteProp, useRoute } from '@react-navigation/native';
|
import { RouteProp, useRoute } from '@react-navigation/native';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { authApi } from 'api/auth';
|
||||||
import exchanges_api from 'api/exchanges';
|
import exchanges_api from 'api/exchanges';
|
||||||
import AppText from 'components/AppText';
|
import AppText from 'components/AppText';
|
||||||
import LayoutTwo from 'components/LayoutTwo';
|
import LayoutTwo from 'components/LayoutTwo';
|
||||||
|
import { formatPrice } from 'helpers/formatPrice';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
@@ -42,6 +44,10 @@ const PaymentQrCode = () => {
|
|||||||
const [cardModal, setCardModal] = React.useState(false);
|
const [cardModal, setCardModal] = React.useState(false);
|
||||||
const [payModal, setPayModal] = React.useState(false);
|
const [payModal, setPayModal] = React.useState(false);
|
||||||
const [success, setSuccess] = React.useState(false);
|
const [success, setSuccess] = React.useState(false);
|
||||||
|
const { data: getMe } = useQuery({
|
||||||
|
queryKey: ['getMe'],
|
||||||
|
queryFn: authApi.getMe,
|
||||||
|
});
|
||||||
|
|
||||||
const screenWidth = Dimensions.get('window').width;
|
const screenWidth = Dimensions.get('window').width;
|
||||||
const isSmallScreen = screenWidth < 380;
|
const isSmallScreen = screenWidth < 380;
|
||||||
@@ -102,7 +108,7 @@ const PaymentQrCode = () => {
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View
|
{/* <View
|
||||||
style={[
|
style={[
|
||||||
PaymentStyle.info,
|
PaymentStyle.info,
|
||||||
isSmallScreen
|
isSmallScreen
|
||||||
@@ -112,7 +118,7 @@ const PaymentQrCode = () => {
|
|||||||
>
|
>
|
||||||
<AppText style={PaymentStyle.titleMethod}>Cargo</AppText>
|
<AppText style={PaymentStyle.titleMethod}>Cargo</AppText>
|
||||||
<AppText style={PaymentStyle.textMethod}>12.267 UZS/ kg</AppText>
|
<AppText style={PaymentStyle.textMethod}>12.267 UZS/ kg</AppText>
|
||||||
</View>
|
</View> */}
|
||||||
|
|
||||||
{/* <View
|
{/* <View
|
||||||
style={[
|
style={[
|
||||||
@@ -132,11 +138,11 @@ const PaymentQrCode = () => {
|
|||||||
style={[
|
style={[
|
||||||
PaymentStyle.info,
|
PaymentStyle.info,
|
||||||
isSmallScreen
|
isSmallScreen
|
||||||
? { flexBasis: '48%', alignItems: 'flex-start' }
|
? { flexBasis: '48%', alignItems: 'flex-end' }
|
||||||
: { flexBasis: '48%', alignItems: 'flex-start' },
|
: { flexBasis: '48%', alignItems: 'flex-end' },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<AppText style={PaymentStyle.titleMethod}>Reys</AppText>
|
<AppText style={PaymentStyle.titleMethod}>{t('Reys')}</AppText>
|
||||||
<AppText style={[PaymentStyle.textMethod, { textAlign: 'left' }]}>
|
<AppText style={[PaymentStyle.textMethod, { textAlign: 'left' }]}>
|
||||||
{packets.packetName}
|
{packets.packetName}
|
||||||
</AppText>
|
</AppText>
|
||||||
@@ -180,12 +186,15 @@ const PaymentQrCode = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
{packets.items.map((item: any, index: number) => (
|
{packets.items.map((item: any, index: number) => {
|
||||||
|
return (
|
||||||
<View key={index} style={{ marginBottom: 15 }}>
|
<View key={index} style={{ marginBottom: 15 }}>
|
||||||
<View style={PaymentStyle.receiptCard}>
|
<View style={PaymentStyle.receiptCard}>
|
||||||
<View style={PaymentStyle.rowBetween}>
|
<View style={PaymentStyle.rowBetween}>
|
||||||
<View style={{ width: '100%' }}>
|
<View style={{ width: '100%' }}>
|
||||||
<AppText style={PaymentStyle.itemName}>{item.name}</AppText>
|
<AppText style={PaymentStyle.itemName}>
|
||||||
|
{item.name}
|
||||||
|
</AppText>
|
||||||
<AppText style={PaymentStyle.track}>
|
<AppText style={PaymentStyle.track}>
|
||||||
{t('Trek ID')}: {item.trekId}
|
{t('Trek ID')}: {item.trekId}
|
||||||
</AppText>
|
</AppText>
|
||||||
@@ -193,15 +202,15 @@ const PaymentQrCode = () => {
|
|||||||
</View>
|
</View>
|
||||||
<View style={PaymentStyle.rowBetween}>
|
<View style={PaymentStyle.rowBetween}>
|
||||||
<AppText style={PaymentStyle.weight}>
|
<AppText style={PaymentStyle.weight}>
|
||||||
{t('Og’irligi')}: {item.weight}
|
{t('Og’irligi')}: {item.weight} kg
|
||||||
</AppText>
|
</AppText>
|
||||||
<AppText style={PaymentStyle.price}>
|
<AppText style={PaymentStyle.price}>
|
||||||
1kg * {item.price}
|
{t('Narxi')}: {item.price}$
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
<View style={PaymentStyle.rowRight}>
|
<View style={PaymentStyle.rowRight}>
|
||||||
<AppText style={PaymentStyle.total}>
|
<AppText style={PaymentStyle.total}>
|
||||||
{t('Umumiy narxi')}: {item.totalPrice} {t('so‘m')}
|
{t('Umumiy narxi')}: {item.totalPrice} $
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -215,7 +224,8 @@ const PaymentQrCode = () => {
|
|||||||
/>
|
/>
|
||||||
</Svg>
|
</Svg>
|
||||||
</View>
|
</View>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
<View style={PaymentStyle.infoCard}>
|
<View style={PaymentStyle.infoCard}>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
@@ -239,7 +249,7 @@ const PaymentQrCode = () => {
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<AppText style={PaymentStyle.titleMethod}>
|
<AppText style={PaymentStyle.titleMethod}>
|
||||||
{packets.totalPrice}
|
{formatPrice(packets.totalPrice)} {t('so‘m')}
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View
|
||||||
@@ -339,6 +349,8 @@ const PaymentQrCode = () => {
|
|||||||
setPayModal={setPayModal}
|
setPayModal={setPayModal}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{getMe && !getMe.aviaCargoId.includes('CP') && (
|
||||||
|
<>
|
||||||
{packets.paymentStatus !== 'PAYED' && (
|
{packets.paymentStatus !== 'PAYED' && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[PaymentStyle.button, { bottom: bottom + 80 }]}
|
style={[PaymentStyle.button, { bottom: bottom + 80 }]}
|
||||||
@@ -347,6 +359,8 @@ const PaymentQrCode = () => {
|
|||||||
<AppText style={PaymentStyle.btnText}>{t("To'lash")}</AppText>
|
<AppText style={PaymentStyle.btnText}>{t("To'lash")}</AppText>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</LayoutTwo>
|
</LayoutTwo>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,11 +40,14 @@ const SelectLangPage = ({
|
|||||||
styles.logoImage,
|
styles.logoImage,
|
||||||
{
|
{
|
||||||
width: 180,
|
width: 180,
|
||||||
height: 180,
|
height: 150,
|
||||||
|
marginLeft: 35,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<AppText style={[styles.logoText, { fontSize: 24 }]}>
|
<AppText
|
||||||
|
style={[styles.logoText, { fontSize: 42, fontWeight: '700' }]}
|
||||||
|
>
|
||||||
CPOST
|
CPOST
|
||||||
</AppText>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
@@ -59,10 +62,19 @@ const SelectLangPage = ({
|
|||||||
<View style={styles.btnContainer}>
|
<View style={styles.btnContainer}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => onSelectLang('uz')}
|
onPress={() => onSelectLang('uz')}
|
||||||
style={styles.button}
|
style={[
|
||||||
|
styles.button,
|
||||||
|
{
|
||||||
|
backgroundColor: 'none',
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#28A7E8',
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Image source={UZ} style={styles.flag} />
|
<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>
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
@@ -134,7 +146,7 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
flag: {
|
flag: {
|
||||||
width: 30,
|
width: 30,
|
||||||
height: 30,
|
height: 20,
|
||||||
resizeMode: 'cover',
|
resizeMode: 'cover',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,24 +1,19 @@
|
|||||||
import * as React from "react"
|
import * as React from 'react';
|
||||||
import Svg, { Path } from "react-native-svg"
|
import Svg, { Path } from 'react-native-svg';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
color: string,
|
color: string;
|
||||||
width?: number,
|
width?: number;
|
||||||
height?: number,
|
height?: number;
|
||||||
view?: string
|
view?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Auto = ({ color, height = 24, width = 24, view = "0" }: Props) => (
|
const Auto = ({ color, height = 24, width = 24, view = '0' }: Props) => (
|
||||||
<Svg
|
<Svg width={width} height={height} fill="none" viewBox={`0 ${view} 24 24`}>
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
fill="none"
|
|
||||||
viewBox={`0 ${view} 24 24`}
|
|
||||||
>
|
|
||||||
<Path
|
<Path
|
||||||
fill={color}
|
fill={color}
|
||||||
d="M7.965 13a3.5 3.5 0 0 1-6.93 0H0V1a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2h3l3 4.056V13h-2.035a3.501 3.501 0 0 1-6.93 0h-5.07ZM14 2H2v8.05a3.5 3.5 0 0 1 5.663.95h5.674c.168-.353.393-.674.663-.95V2Zm2 6h4v-.285L17.992 5H16v3Zm.5 6a1.5 1.5 0 1 0 0-3.001 1.5 1.5 0 0 0 0 3.001ZM6 12.5a1.5 1.5 0 1 0-3 0 1.5 1.5 0 0 0 3 0Z"
|
d="M7.965 13a3.5 3.5 0 0 1-6.93 0H0V1a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2h3l3 4.056V13h-2.035a3.501 3.501 0 0 1-6.93 0h-5.07ZM14 2H2v8.05a3.5 3.5 0 0 1 5.663.95h5.674c.168-.353.393-.674.663-.95V2Zm2 6h4v-.285L17.992 5H16v3Zm.5 6a1.5 1.5 0 1 0 0-3.001 1.5 1.5 0 0 0 0 3.001ZM6 12.5a1.5 1.5 0 1 0-3 0 1.5 1.5 0 0 0 3 0Z"
|
||||||
/>
|
/>
|
||||||
</Svg>
|
</Svg>
|
||||||
)
|
);
|
||||||
export default Auto
|
export default Auto;
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { initializeApp } from '@react-native-firebase/app';
|
// import { initializeApp } from '@react-native-firebase/app';
|
||||||
import { getMessaging } from '@react-native-firebase/messaging';
|
// import { getMessaging } from '@react-native-firebase/messaging';
|
||||||
|
|
||||||
const firebaseConfig = {
|
// const firebaseConfig = {
|
||||||
apiKey: 'AIzaSyBEwWi1TuZBNj2hkFGGIaWZNNDCoiC__lE',
|
// apiKey: 'AIzaSyBEwWi1TuZBNj2hkFGGIaWZNNDCoiC__lE',
|
||||||
authDomain: 'cpcargo-aee14.firebaseapp.com',
|
// authDomain: 'cpcargo-aee14.firebaseapp.com',
|
||||||
projectId: 'cpcargo-aee14',
|
// projectId: 'cpcargo-aee14',
|
||||||
storageBucket: 'cpcargo-aee14.firebasestorage.app',
|
// storageBucket: 'cpcargo-aee14.firebasestorage.app',
|
||||||
messagingSenderId: '1030089382290',
|
// messagingSenderId: '1030089382290',
|
||||||
appId: '1:1030089382290:android:668f0669ad4ac3f74dc94b',
|
// appId: '1:1030089382290:android:668f0669ad4ac3f74dc94b',
|
||||||
};
|
// };
|
||||||
|
|
||||||
export const firebaseApp = initializeApp(firebaseConfig);
|
// export const firebaseApp = initializeApp(firebaseConfig);
|
||||||
export const messaging = getMessaging(await firebaseApp);
|
// export const messaging = getMessaging(await firebaseApp);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import { setLanguage } from 'helpers/event';
|
||||||
import i18n from 'i18n/i18n';
|
import i18n from 'i18n/i18n';
|
||||||
|
|
||||||
export const changeLanguage = async (lang: string) => {
|
export const changeLanguage = async (lang: string) => {
|
||||||
try {
|
try {
|
||||||
await AsyncStorage.setItem('language', lang);
|
setLanguage(lang);
|
||||||
await i18n.changeLanguage(lang);
|
await i18n.changeLanguage(lang);
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
// firebase.js
|
// firebase.js
|
||||||
import { initializeApp } from '@react-native-firebase/app';
|
// import { initializeApp } from '@react-native-firebase/app';
|
||||||
|
|
||||||
const firebaseConfig = {
|
// const firebaseConfig = {
|
||||||
apiKey: 'AIzaSyBnFzHK6XAjxzcQAsg0hFbeRcon8ZMDvVw', // api_key → current_key
|
// apiKey: 'AIzaSyBnFzHK6XAjxzcQAsg0hFbeRcon8ZMDvVw',
|
||||||
authDomain: 'cpcargo-77d93.firebaseapp.com', // Firebase web SDK uchun qo‘shimcha, yo‘q bo‘lsa qoldirish mumkin
|
// authDomain: 'cpcargo-77d93.firebaseapp.com',
|
||||||
projectId: 'cpcargo-77d93', // project_info → project_id
|
// projectId: 'cpcargo-77d93',
|
||||||
storageBucket: 'cpcargo-77d93.firebasestorage.app', // project_info → storage_bucket
|
// storageBucket: 'cpcargo-77d93.firebasestorage.app',
|
||||||
messagingSenderId: '628048576398', // project_info → project_number
|
// messagingSenderId: '628048576398',
|
||||||
appId: '1:628048576398:android:f93293c00f463267a92edf', // client_info → mobilesdk_app_id
|
// appId: '1:628048576398:android:f93293c00f463267a92edf',
|
||||||
};
|
// };
|
||||||
|
|
||||||
// Firebase ilovasini initialize qilish
|
// const app = initializeApp(firebaseConfig);
|
||||||
const app = initializeApp(firebaseConfig);
|
|
||||||
|
|
||||||
export default app;
|
// export default app;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"*": ["./src/*"],
|
"*": ["./src/*"],
|
||||||
"@screens": ["./src/screens/*"],
|
"@screens": ["./src/screens/*"],
|
||||||
"screens": ["./src/screens"],
|
"screens": ["./src/screens"],
|
||||||
"components": ["./src/components"],
|
"components": ["./src/components/*"],
|
||||||
"assets": ["./src/assets"],
|
"assets": ["./src/assets"],
|
||||||
"api": ["./src/api"],
|
"api": ["./src/api"],
|
||||||
"helpers": ["./src/helpers"],
|
"helpers": ["./src/helpers"],
|
||||||
|
|||||||
Reference in New Issue
Block a user