This commit is contained in:
azizziy
2025-08-27 15:44:54 +05:00
20 changed files with 619 additions and 630 deletions

View File

@@ -1,4 +1,4 @@
export const BASE_URL = 'https://api.cpcargo.uz/api/v1'; export const BASE_URL = 'http://141.105.64.233:7723/api/v1';
export const REGISTER = '/mobile/auth/register'; export const REGISTER = '/mobile/auth/register';
export const LOGIN = '/mobile/auth/login'; export const LOGIN = '/mobile/auth/login';

View File

@@ -3,7 +3,7 @@ import axios, { AxiosError } from 'axios';
import { navigate } from 'components/NavigationRef'; import { navigate } from 'components/NavigationRef';
const axiosInstance = axios.create({ const axiosInstance = axios.create({
baseURL: 'https://api.cpcargo.uz/api/v1', baseURL: 'http://141.105.64.233:7723/api/v1',
timeout: 10000, timeout: 10000,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',

49
src/components/Layout.tsx Normal file
View File

@@ -0,0 +1,49 @@
// src/components/Layout.tsx
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import Navbar from './Navbar';
import Navigation from './Navigation';
interface LayoutProps {
children: React.ReactNode;
}
const Layout: React.FC<LayoutProps> = ({ children }) => {
return (
<SafeAreaView style={styles.safeArea}>
<View style={styles.container}>
<Navbar />
<View style={styles.content}>{children}</View>
<Navigation />
</View>
</SafeAreaView>
);
};
export default Layout;
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: '#fff',
},
container: {
flex: 1,
},
header: {
height: 60,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5f5f5',
},
content: {
flex: 1,
},
footer: {
height: 50,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#eee',
},
});

View File

@@ -0,0 +1,50 @@
// src/components/Layout.tsx
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import NavbarBack from './NavbarBack';
import Navigation from './Navigation';
interface LayoutProps {
title: string;
children: React.ReactNode;
}
const LayoutTwo: React.FC<LayoutProps> = ({ title, children }) => {
return (
<SafeAreaView style={styles.safeArea}>
<View style={styles.container}>
<NavbarBack title={title} />
<View style={styles.content}>{children}</View>
<Navigation />
</View>
</SafeAreaView>
);
};
export default LayoutTwo;
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: '#fff',
},
container: {
flex: 1,
},
header: {
height: 60,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5f5f5',
},
content: {
flex: 1,
},
footer: {
height: 50,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#eee',
},
});

View File

@@ -2,6 +2,7 @@ import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { branchApi } from 'api/branch'; import { branchApi } from 'api/branch';
import LayoutTwo from 'components/LayoutTwo';
import NavbarBack from 'components/NavbarBack'; import NavbarBack from 'components/NavbarBack';
import NoResult from 'components/NoResult'; import NoResult from 'components/NoResult';
import * as React from 'react'; import * as React from 'react';
@@ -38,8 +39,7 @@ const Branches = (props: BranchesProps) => {
} }
return ( return (
<SafeAreaView style={{ flex: 1 }}> <LayoutTwo title={t('Filiallar royxati')}>
<NavbarBack title={t('Filiallar royxati')} />
<View style={styles.scrollWrapper}> <View style={styles.scrollWrapper}>
<ScrollView contentContainerStyle={styles.scrollContainer}> <ScrollView contentContainerStyle={styles.scrollContainer}>
{data && {data &&
@@ -60,7 +60,7 @@ const Branches = (props: BranchesProps) => {
))} ))}
</ScrollView> </ScrollView>
</View> </View>
</SafeAreaView> </LayoutTwo>
); );
}; };
@@ -76,20 +76,28 @@ const styles = StyleSheet.create({
card: { card: {
backgroundColor: '#FFFFFF', backgroundColor: '#FFFFFF',
borderRadius: 8, borderRadius: 8,
padding: 16, padding: 4,
marginBottom: 12, marginBottom: 12,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between', justifyContent: 'space-between',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 1,
}, },
title: { title: {
fontSize: 18, fontSize: 18,
paddingHorizontal: 5,
fontWeight: '600', fontWeight: '600',
color: '#000', color: '#000',
marginBottom: 6, marginBottom: 6,
}, },
subtitle: { subtitle: {
fontSize: 16, fontSize: 16,
paddingHorizontal: 5,
fontWeight: '500', fontWeight: '500',
color: '#000000B2', color: '#000000B2',
}, },

View File

@@ -3,13 +3,11 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { Branch, branchApi } from 'api/branch'; import { Branch, branchApi } from 'api/branch';
import BottomModal from 'components/BottomModal'; import BottomModal from 'components/BottomModal';
import LayoutTwo from 'components/LayoutTwo';
import LoadingScreen from 'components/LoadingScreen'; import LoadingScreen from 'components/LoadingScreen';
import NavbarBack from 'components/NavbarBack';
import Navigation from 'components/Navigation';
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import WebView from 'react-native-webview'; import WebView from 'react-native-webview';
import Minus from 'svg/Minus'; import Minus from 'svg/Minus';
import Plus from 'svg/Plus'; import Plus from 'svg/Plus';
@@ -123,8 +121,7 @@ const ListBranches = () => {
if (!data) return <LoadingScreen />; if (!data) return <LoadingScreen />;
return ( return (
<SafeAreaView style={styles.container}> <LayoutTwo title={t('Filiallar royxati')}>
<NavbarBack title={t('Filiallar royxati')} />
{!webViewReady && ( {!webViewReady && (
<View style={{ width: '100%', height: '100%', margin: 'auto' }}> <View style={{ width: '100%', height: '100%', margin: 'auto' }}>
<LoadingScreen /> <LoadingScreen />
@@ -195,8 +192,7 @@ const ListBranches = () => {
branch={selectedBranch} branch={selectedBranch}
/> />
</View> </View>
<Navigation /> </LayoutTwo>
</SafeAreaView>
); );
}; };

View File

@@ -1,7 +1,6 @@
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 NavbarBack from 'components/NavbarBack'; import LayoutTwo from 'components/LayoutTwo';
import Navigation from 'components/Navigation';
import * as React from 'react'; import * as React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
@@ -11,7 +10,6 @@ import {
TouchableOpacity, TouchableOpacity,
View, View,
} from 'react-native'; } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import Tabs from '../../home/ui/Tabs'; import Tabs from '../../home/ui/Tabs';
interface CargoPricesProps {} interface CargoPricesProps {}
@@ -21,13 +19,12 @@ const CargoPrices = (props: CargoPricesProps) => {
const [activeTab, setActiveTab] = React.useState<'avia' | 'auto'>('avia'); const [activeTab, setActiveTab] = React.useState<'avia' | 'auto'>('avia');
const navigation = useNavigation<NativeStackNavigationProp<any>>(); const navigation = useNavigation<NativeStackNavigationProp<any>>();
return ( return (
<SafeAreaView style={{ flex: 1 }}> <LayoutTwo title={t('Kargo narxlari')}>
<NavbarBack title={t('Kargo narxlari')} />
<ScrollView style={{ flex: 1 }}> <ScrollView style={{ flex: 1 }}>
<View style={styles.container}> <View style={styles.container}>
<Tabs activeTab={activeTab} setActiveTab={setActiveTab} /> <Tabs activeTab={activeTab} setActiveTab={setActiveTab} />
{activeTab === 'avia' && ( {activeTab === 'avia' && (
<View style={{ marginTop: 20, gap: 10, marginBottom: 20 }}> <View style={{ marginTop: 10, gap: 10, marginBottom: 20 }}>
<View style={styles.cardWhite}> <View style={styles.cardWhite}>
<View style={styles.priceCard}> <View style={styles.priceCard}>
<Text style={styles.titleBlack}> <Text style={styles.titleBlack}>
@@ -231,8 +228,7 @@ const CargoPrices = (props: CargoPricesProps) => {
)} )}
</View> </View>
</ScrollView> </ScrollView>
<Navigation /> </LayoutTwo>
</SafeAreaView>
); );
}; };
@@ -240,7 +236,7 @@ export default CargoPrices;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
marginTop: 20, marginTop: 10,
}, },
card: { card: {
width: '95%', width: '95%',
@@ -254,6 +250,14 @@ const styles = StyleSheet.create({
margin: 'auto', margin: 'auto',
padding: 10, padding: 10,
borderRadius: 8, borderRadius: 8,
paddingVertical: 15,
paddingHorizontal: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 1,
}, },
titleBlack: { titleBlack: {
fontSize: 16, fontSize: 16,

View File

@@ -1,20 +1,12 @@
'use client'; 'use client';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import calendarAPi from 'api/calendar'; import calendarAPi from 'api/calendar';
import Layout from 'components/Layout';
import LoadingScreen from 'components/LoadingScreen'; import LoadingScreen from 'components/LoadingScreen';
import Navbar from 'components/Navbar';
import Navigation from 'components/Navigation';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { import { RefreshControl, ScrollView, useWindowDimensions } from 'react-native';
RefreshControl,
ScrollView,
useWindowDimensions,
View,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import Pages from './Pages'; import Pages from './Pages';
import PartyCarousel from './PartyCarousel'; import PartyCarousel from './PartyCarousel';
import { HomeStyle } from './styled';
import Tabs from './Tabs'; import Tabs from './Tabs';
import TabsAuto from './TabsAuto'; import TabsAuto from './TabsAuto';
import TabsAvia from './TabsAvia'; import TabsAvia from './TabsAvia';
@@ -24,7 +16,6 @@ const Home = () => {
const [refreshing, setRefreshing] = useState(false); const [refreshing, setRefreshing] = useState(false);
const { width: screenWidth } = useWindowDimensions(); const { width: screenWidth } = useWindowDimensions();
const scale = screenWidth < 360 ? 0.85 : 1; const scale = screenWidth < 360 ? 0.85 : 1;
const styles = useMemo(() => HomeStyle(scale), [scale]);
const { const {
data: autoData, data: autoData,
@@ -71,33 +62,25 @@ const Home = () => {
if (autoLoad || aviaLoad || fetchAuto || fetchAvia) { if (autoLoad || aviaLoad || fetchAuto || fetchAvia) {
return ( return (
<SafeAreaView style={{ flex: 1 }}> <Layout>
<View style={styles.container}> <LoadingScreen />
<Navbar /> </Layout>
<LoadingScreen />
<Navigation />
</View>
</SafeAreaView>
); );
} }
return ( return (
<SafeAreaView style={{ flex: 1 }}> <Layout>
<View style={styles.container}> <ScrollView
<Navbar /> refreshControl={refreshControl}
<ScrollView removeClippedSubviews={true}
refreshControl={refreshControl} keyboardShouldPersistTaps="handled"
removeClippedSubviews={true} >
keyboardShouldPersistTaps="handled" <PartyCarousel autoData={autoData} aviaData={aviaData} />
> <Tabs setActiveTab={setActiveTab} activeTab={activeTab} />
<PartyCarousel autoData={autoData} aviaData={aviaData} /> {activeTabContent}
<Tabs setActiveTab={setActiveTab} activeTab={activeTab} /> <Pages />
{activeTabContent} </ScrollView>
<Pages /> </Layout>
</ScrollView>
<Navigation />
</View>
</SafeAreaView>
); );
}; };

View File

@@ -2,24 +2,16 @@ import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
StyleSheet,
Text,
TouchableOpacity,
View,
useWindowDimensions,
} from 'react-native';
import ArrowRightUnderline from 'svg/ArrowRightUnderline'; import ArrowRightUnderline from 'svg/ArrowRightUnderline';
import Usd from 'svg/Dollar'; import Usd from 'svg/Dollar';
import InfoIcon from 'svg/Info'; import InfoIcon from 'svg/Info';
import Store from 'svg/Store'; import Store from 'svg/Store';
const Pages = () => { const Pages = () => {
const { width: screenWidth } = useWindowDimensions();
const navigation = useNavigation<NativeStackNavigationProp<any>>(); const navigation = useNavigation<NativeStackNavigationProp<any>>();
const scale = screenWidth < 360 ? 0.85 : 1;
const { t } = useTranslation(); const { t } = useTranslation();
const styles = makeStyles(scale); const styles = makeStyles();
return ( return (
<View style={styles.container}> <View style={styles.container}>
@@ -28,19 +20,10 @@ const Pages = () => {
style={styles.card} style={styles.card}
> >
<View style={styles.text}> <View style={styles.text}>
<Usd <Usd color="#28A7E8" width={28} height={28} colorCircle="#28A7E8" />
color="#28A7E8"
width={28 * scale}
height={28 * scale}
colorCircle="#28A7E8"
/>
<Text style={styles.title}>{t('Kargo narxlari')}</Text> <Text style={styles.title}>{t('Kargo narxlari')}</Text>
</View> </View>
<ArrowRightUnderline <ArrowRightUnderline color="#000000" width={24} height={24} />
color="#000000"
width={24 * scale}
height={24 * scale}
/>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity <TouchableOpacity
@@ -48,14 +31,10 @@ const Pages = () => {
style={styles.card} style={styles.card}
> >
<View style={styles.text}> <View style={styles.text}>
<InfoIcon color="#28A7E8" width={28 * scale} height={28 * scale} /> <InfoIcon color="#28A7E8" width={28} height={28} />
<Text style={styles.title}>{t('Taqiqlangan buyumlar')}</Text> <Text style={styles.title}>{t('Taqiqlangan buyumlar')}</Text>
</View> </View>
<ArrowRightUnderline <ArrowRightUnderline color="#000000" width={24} height={24} />
color="#000000"
width={24 * scale}
height={24 * scale}
/>
</TouchableOpacity> </TouchableOpacity>
{/* <View style={styles.card}> {/* <View style={styles.card}>
@@ -80,14 +59,10 @@ const Pages = () => {
onPress={() => navigation.navigate('ListBranches')} onPress={() => navigation.navigate('ListBranches')}
> >
<View style={styles.text}> <View style={styles.text}>
<Store color="#28A7E8" width={28 * scale} height={28 * scale} /> <Store color="#28A7E8" width={28} height={28} />
<Text style={styles.title}>{t('Filiallar royxati')}</Text> <Text style={styles.title}>{t('Filiallar royxati')}</Text>
</View> </View>
<ArrowRightUnderline <ArrowRightUnderline color="#000000" width={24} height={24} />
color="#000000"
width={24 * scale}
height={24 * scale}
/>
</TouchableOpacity> </TouchableOpacity>
{/* <TouchableOpacity {/* <TouchableOpacity
@@ -108,33 +83,39 @@ const Pages = () => {
); );
}; };
const makeStyles = (scale: number) => const makeStyles = () =>
StyleSheet.create({ StyleSheet.create({
container: { container: {
width: '100%', width: '95%',
marginLeft: 'auto', marginLeft: 'auto',
marginRight: 'auto', marginRight: 'auto',
marginTop: 10 * scale, borderRadius: 12,
borderRadius: 12 * scale, marginBottom: 10,
padding: 12 * scale, gap: 10,
gap: 10 * scale,
}, },
card: { card: {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
backgroundColor: '#FFFFFF',
shadowColor: '#F2FAFF',
padding: 15 * scale,
borderRadius: 8 * scale,
alignItems: 'center', alignItems: 'center',
backgroundColor: '#FFFFFF',
paddingVertical: 15,
paddingHorizontal: 12,
borderRadius: 10,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 1,
}, },
text: { text: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
gap: 10 * scale, gap: 10,
}, },
title: { title: {
fontSize: 16 * scale, fontSize: 16,
fontWeight: '600', fontWeight: '600',
}, },
}); });

View File

@@ -24,9 +24,8 @@ const PartyCarousel = ({
aviaData: any; aviaData: any;
}) => { }) => {
const { width: screenWidth } = useWindowDimensions(); const { width: screenWidth } = useWindowDimensions();
const scale = screenWidth < 360 ? 0.85 : 1;
const cardWidth = screenWidth * 0.95; const cardWidth = screenWidth * 0.95;
const styles = useMemo(() => HomeStyle(scale), [scale]); const styles = useMemo(() => HomeStyle(), []);
const { t } = useTranslation(); const { t } = useTranslation();
const statusConfig: any = { const statusConfig: any = {
@@ -57,21 +56,21 @@ const PartyCarousel = ({
}, },
}; };
// calendarList tayyorlash
const calendarList = useMemo(() => { const calendarList = useMemo(() => {
const data: any[] = []; const data: any[] = [];
const weekdays = [
{ key: 'sunday', label: 'Ya' },
{ key: 'monday', label: 'Du' },
{ key: 'tuesday', label: 'Se' },
{ key: 'wednesday', label: 'Cho' },
{ key: 'thursday', label: 'Pa' },
{ key: 'friday', label: 'Ju' },
{ key: 'saturday', label: 'Sha' },
];
const prepareList = (calendar: any, type: 'auto' | 'avia') => { const prepareList = (calendar: any, type: 'auto' | 'avia') => {
if (!calendar) return; if (!calendar) return;
const weekdays = [
{ key: 'sunday', label: 'Ya' },
{ key: 'monday', label: 'Du' },
{ key: 'tuesday', label: 'Se' },
{ key: 'wednesday', label: 'Cho' },
{ key: 'thursday', label: 'Pa' },
{ key: 'friday', label: 'Ju' },
{ key: 'saturday', label: 'Sha' },
];
data.push({ data.push({
cargo: type, cargo: type,
party: calendar.cargoType, party: calendar.cargoType,
@@ -79,7 +78,6 @@ const PartyCarousel = ({
start: weekdays.map((day, index) => { start: weekdays.map((day, index) => {
const start = new Date(calendar.weekStartDate); const start = new Date(calendar.weekStartDate);
start.setDate(start.getDate() + index); start.setDate(start.getDate() + index);
const status = const status =
calendar[day.key as keyof typeof calendar] || 'DEFAULT'; calendar[day.key as keyof typeof calendar] || 'DEFAULT';
return { return {
@@ -93,7 +91,6 @@ const PartyCarousel = ({
prepareList(autoData, 'auto'); prepareList(autoData, 'auto');
prepareList(aviaData, 'avia'); prepareList(aviaData, 'avia');
return data; return data;
}, [autoData, aviaData]); }, [autoData, aviaData]);
@@ -103,14 +100,10 @@ const PartyCarousel = ({
d.setHours(0, 0, 0, 0); d.setHours(0, 0, 0, 0);
return d; return d;
}, []); }, []);
// --- MODAL STATE ---
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 (
<Pressable <Pressable
onPress={() => { onPress={() => {
@@ -119,67 +112,64 @@ const PartyCarousel = ({
}} }}
style={[ style={[
styles.autoContainer, styles.autoContainer,
{ width: cardWidth, marginRight: isLast ? 0 : 10 }, {
width: cardWidth,
marginRight: isLast ? 0 : 10,
},
]} ]}
> >
{item.isAni && ( {item.isAni && (
<> <>
<View style={styles.cardBody}> <FlatList
{Array.isArray(item.start) && data={item.start}
item.start.map((day: any, idx: number) => { numColumns={7}
const dateObj = new Date(day.date); keyExtractor={(_, idx) => idx.toString()}
const isToday = renderItem={({ item: day }) => {
dateObj.toDateString() === today.toDateString(); const dateObj = new Date(day.date);
const config = const isToday = dateObj.toDateString() === today.toDateString();
statusConfig[day.status] || statusConfig.DEFAULT; const config = statusConfig[day.status] || statusConfig.DEFAULT;
let backgroundColor = config.backgroundColor; let backgroundColor = config.backgroundColor;
let textColor = config.textColor; let textColor = config.textColor;
if (isToday) { if (isToday) {
backgroundColor = '#22b1f88f'; backgroundColor = '#22b1f88f';
textColor = '#fff'; textColor = '#fff';
} }
if (day.weekday === 'Ju') { if (day.weekday === 'Ju') {
backgroundColor = '#4CAF50'; backgroundColor = '#4CAF50';
textColor = '#fff'; textColor = '#fff';
} }
return ( return (
<View <View
key={idx} style={[
style={[ styles.date,
styles.date, {
{ backgroundColor,
backgroundColor, marginRight: 5,
alignItems: 'center', },
justifyContent: 'center', ]}
height: 70, >
}, <Text style={[styles.dateLabel, { color: textColor }]}>
]} {day.date.slice(-2)}
> </Text>
<Text style={[styles.dateLabel, { color: textColor }]}> <Text style={[styles.dateLabel, { color: textColor }]}>
{day.date.slice(-2)} {t(day.weekday)}
</Text> </Text>
<Text style={[styles.dateLabel, { color: textColor }]}>
{t(day.weekday)}
</Text>
</View>
);
})}
</View>
<View style={styles.divider} />
<View style={styles.autoCard}>
<View style={styles.row}>
<View style={styles.rowFull}>
<Text style={styles.reysTitle}>
{item.cargo.toUpperCase()}
</Text>
<View style={styles.animatedIconWrapper}>
<AnimatedIcon type={item.cargo} />
</View> </View>
</View> );
}}
showsHorizontalScrollIndicator={false}
/>
<View style={styles.divider} />
<View style={styles.rowFull}>
<Text style={styles.reysTitle}>{item.cargo.toUpperCase()}</Text>
<View style={styles.animatedIconWrapper}>
<AnimatedIcon type={item.cargo} />
</View> </View>
</View> </View>
</> </>
@@ -200,9 +190,9 @@ const PartyCarousel = ({
decelerationRate="fast" decelerationRate="fast"
contentContainerStyle={{ contentContainerStyle={{
paddingHorizontal: (screenWidth - cardWidth) / 2, paddingHorizontal: (screenWidth - cardWidth) / 2,
padding: 10,
}} }}
pagingEnabled pagingEnabled
style={{ marginBottom: 20 }}
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
getItemLayout={(_, index) => ({ getItemLayout={(_, index) => ({
length: cardWidth, length: cardWidth,
@@ -210,8 +200,6 @@ const PartyCarousel = ({
index, index,
})} })}
/> />
{/* MODAL */}
<Modal <Modal
transparent transparent
visible={modalVisible} visible={modalVisible}
@@ -223,15 +211,12 @@ const PartyCarousel = ({
<Text style={modalStyles.modalTitle}> <Text style={modalStyles.modalTitle}>
{t('Yetkazish tafsilotlari')} {t('Yetkazish tafsilotlari')}
</Text> </Text>
{selectedItem && ( {selectedItem &&
<> selectedItem.start.map((day: any, idx: number) => (
{selectedItem.start.map((day: any, idx: number) => ( <Text key={idx} style={{ textAlign: 'left', width: '100%' }}>
<Text key={idx} style={{ textAlign: 'left', width: '100%' }}> {day.date} - {t(day.weekday)} - {t(day.status)}
{day.date} - {t(day.weekday)} - {t(day.status)} </Text>
</Text> ))}
))}
</>
)}
<Pressable <Pressable
onPress={() => setModalVisible(false)} onPress={() => setModalVisible(false)}
style={modalStyles.closeButton} style={modalStyles.closeButton}

View File

@@ -1,11 +1,11 @@
import { Dispatch, SetStateAction, useCallback, useMemo } from 'react'; import { Dispatch, SetStateAction, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
FlatList,
Image, Image,
Text, Text,
TouchableOpacity, TouchableOpacity,
useWindowDimensions, useWindowDimensions,
View,
} from 'react-native'; } from 'react-native';
import LinearGradient from 'react-native-linear-gradient'; import LinearGradient from 'react-native-linear-gradient';
import AviaLogo from 'screens/../../assets/bootsplash/Avia.png'; import AviaLogo from 'screens/../../assets/bootsplash/Avia.png';
@@ -18,22 +18,14 @@ interface Props {
} }
const Tabs = ({ activeTab, setActiveTab }: Props) => { const Tabs = ({ activeTab, setActiveTab }: Props) => {
const { width: screenWidth } = useWindowDimensions();
const { t } = useTranslation(); const { t } = useTranslation();
const scale = useMemo(() => (screenWidth < 360 ? 0.85 : 1), [screenWidth]); const { width: screenWidth } = useWindowDimensions();
const cardWidth = screenWidth * 0.95;
const styles = useMemo(() => HomeStyle(scale), [scale]); const styles = useMemo(() => HomeStyle(), []);
const handleTabPress = useCallback(
(type: 'avia' | 'auto') => {
setActiveTab(type);
},
[setActiveTab],
);
const gradientStyle = useMemo( const gradientStyle = useMemo(
() => ({ () => ({
width: '100%' as const, flex: 1,
borderRadius: 8, borderRadius: 8,
flexDirection: 'row' as const, flexDirection: 'row' as const,
alignItems: 'center' as const, alignItems: 'center' as const,
@@ -44,48 +36,75 @@ const Tabs = ({ activeTab, setActiveTab }: Props) => {
[], [],
); );
const renderTabButton = useCallback( // Tabs data
(type: 'avia' | 'auto', label: string, logo: any) => { const tabsData = useMemo(
const isActive = activeTab === type; () => [
{
type: 'avia' as const,
label: t('Avia orqali yetkazish'),
logo: AviaLogo,
},
{
type: 'auto' as const,
label: t('Avto orqali yetkazish'),
logo: AutoLogo,
},
],
[t],
);
const renderItem = useCallback(
({ item }: { item: (typeof tabsData)[number] }) => {
const isActive = activeTab === item.type;
const gradientColors = isActive const gradientColors = isActive
? ['#28A7E8', '#28A7E8'] ? ['#28A7E8', '#28A7E8']
: ['#28a8e82d', '#28A7E8']; : ['#28a8e82d', '#28A7E8'];
const textStyle = [
styles.tabsText,
{ color: isActive ? '#fff' : '#000' },
];
return ( return (
<TouchableOpacity <TouchableOpacity
onPress={() => handleTabPress(type)} onPress={() => setActiveTab(item.type)}
style={styles.tabs} style={{ flex: 1 }}
key={type}
> >
<LinearGradient <LinearGradient
colors={gradientColors} colors={gradientColors}
start={{ x: 0.5, y: 1 }} start={{ x: 0, y: 0 }}
end={{ x: 1.5, y: 1 }} end={{ x: 1, y: 0 }}
style={gradientStyle} style={gradientStyle}
> >
<Image source={logo} style={styles.tabsLogo} /> <Image source={item.logo} style={styles.tabsLogo} />
<Text style={textStyle}>{label}</Text> <Text
style={[styles.tabsText, { color: isActive ? '#fff' : '#000' }]}
>
{item.label}
</Text>
</LinearGradient> </LinearGradient>
</TouchableOpacity> </TouchableOpacity>
); );
}, },
[activeTab, styles, handleTabPress, gradientStyle], [activeTab, styles, gradientStyle, setActiveTab],
); );
const tabButtons = useMemo( return (
() => [ <FlatList
renderTabButton('avia', t('Avia orqali yetkazish'), AviaLogo), data={tabsData}
renderTabButton('auto', t('Avto orqali yetkazish'), AutoLogo), numColumns={2}
], keyExtractor={(_, idx) => idx.toString()}
[renderTabButton], renderItem={renderItem}
showsHorizontalScrollIndicator={false}
scrollEnabled={false}
columnWrapperStyle={{
gap: 8,
}}
contentContainerStyle={{
paddingHorizontal: (screenWidth - cardWidth) / 2,
}}
getItemLayout={(_, index) => ({
length: cardWidth,
offset: cardWidth * index,
index,
})}
/>
); );
return <View style={styles.tabsContainer}>{tabButtons}</View>;
}; };
export default Tabs; export default Tabs;

View File

@@ -84,7 +84,7 @@ const TabsAuto = () => {
decelerationRate="fast" decelerationRate="fast"
contentContainerStyle={{ contentContainerStyle={{
paddingHorizontal: (screenWidth - cardWidth) / 2, paddingHorizontal: (screenWidth - cardWidth) / 2,
marginTop: 10, padding: 10,
}} }}
renderItem={({ item, index }) => { renderItem={({ item, index }) => {
const isLast = index === addressList.length - 1; const isLast = index === addressList.length - 1;

View File

@@ -85,7 +85,7 @@ const TabsAvia = () => {
decelerationRate="fast" decelerationRate="fast"
contentContainerStyle={{ contentContainerStyle={{
paddingHorizontal: (screenWidth - cardWidth) / 2, paddingHorizontal: (screenWidth - cardWidth) / 2,
marginTop: 20, padding: 10,
}} }}
renderItem={({ item, index }) => { renderItem={({ item, index }) => {
const isLast = index === addressList.length - 1; const isLast = index === addressList.length - 1;
@@ -125,7 +125,7 @@ const makeStyles = (scale: number, cardWidth: number, screenWidth: number) =>
width: '95%', width: '95%',
backgroundColor: '#28a8e82c', backgroundColor: '#28a8e82c',
margin: 'auto', margin: 'auto',
marginTop: 20, // marginTop: 20,
borderRadius: 12, borderRadius: 12,
padding: 12, padding: 12,
gap: 10, gap: 10,

View File

@@ -1,46 +1,42 @@
import { StyleSheet } from 'react-native'; import { StyleSheet } from 'react-native';
export const HomeStyle = (scale: number) => export const HomeStyle = () =>
StyleSheet.create({ StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
}, },
header: { header: {
backgroundColor: '#28A7E8', backgroundColor: '#28A7E8',
height: 80 * scale, height: 80,
paddingHorizontal: 10 * scale, paddingHorizontal: 10,
paddingVertical: 10 * scale, paddingVertical: 10,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between', justifyContent: 'space-between',
}, },
title: { title: {
color: '#fff', color: '#fff',
fontSize: 20 * scale, fontSize: 20,
}, },
logo: { logo: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
gap: 5 * scale, gap: 5,
}, },
links: { links: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
gap: 15 * scale, gap: 15,
}, },
autoContainer: { autoContainer: {
width: '90%', borderRadius: 8,
height: 140 * scale, padding: 8,
borderRadius: 8 * scale,
padding: 10 * scale,
backgroundColor: '#FFFFFF', backgroundColor: '#FFFFFF',
marginTop: 10, gap: 8,
gap: 8 * scale,
shadowColor: '#000', shadowColor: '#000',
shadowOffset: { width: 0, height: 1 * scale }, shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1, shadowOpacity: 0.1,
shadowRadius: 2 * scale, shadowRadius: 2,
margin: 'auto',
elevation: 2, elevation: 2,
}, },
autoCard: { autoCard: {
@@ -48,33 +44,34 @@ export const HomeStyle = (scale: number) =>
justifyContent: 'space-between', justifyContent: 'space-between',
}, },
reysTitle: { reysTitle: {
fontSize: 20 * scale, fontSize: 20,
color: '#28A7E8', color: '#28A7E8',
fontWeight: '600', fontWeight: '600',
}, },
text: { text: {
fontWeight: '500', fontWeight: '500',
color: 'red', color: 'red',
fontSize: 14 * scale, fontSize: 14,
}, },
box: { box: {
backgroundColor: '#28A7E81A', backgroundColor: '#28A7E81A',
padding: 10 * scale, padding: 10,
width: 'auto', width: 'auto',
alignSelf: 'flex-start', alignSelf: 'flex-start',
borderRadius: 8 * scale, borderRadius: 8,
}, },
date: { date: {
backgroundColor: '#28A7E8', backgroundColor: '#28A7E8',
padding: 6 * scale,
width: '13%', width: '13%',
height: 50,
textAlign: 'center', textAlign: 'center',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
borderRadius: 8 * scale, borderRadius: 8,
padding: 0,
}, },
dateLabel: { dateLabel: {
fontSize: 14 * scale, fontSize: 14,
fontWeight: '500', fontWeight: '500',
color: '#FFFFFF', color: '#FFFFFF',
}, },
@@ -84,23 +81,23 @@ export const HomeStyle = (scale: number) =>
justifyContent: 'space-between', justifyContent: 'space-between',
}, },
logoIcon: { logoIcon: {
width: 50 * scale, width: 50,
height: 50 * scale, height: 50,
resizeMode: 'contain', resizeMode: 'contain',
}, },
bellWrapper: { bellWrapper: {
position: 'relative', position: 'relative',
}, },
bellDot: { bellDot: {
width: 10 * scale, width: 10,
height: 10 * scale, height: 10,
position: 'absolute', position: 'absolute',
backgroundColor: 'red', backgroundColor: 'red',
right: 2 * scale, right: 2,
borderRadius: 100, borderRadius: 100,
}, },
divider: { divider: {
height: 2 * scale, height: 2,
width: '100%', width: '100%',
backgroundColor: '#28A7E81F', backgroundColor: '#28A7E81F',
}, },
@@ -111,23 +108,23 @@ export const HomeStyle = (scale: number) =>
infoBlock: { infoBlock: {
alignItems: 'center', alignItems: 'center',
flexDirection: 'row', flexDirection: 'row',
gap: 6 * scale, gap: 6,
}, },
iconBox: { iconBox: {
backgroundColor: '#28A7E81A', backgroundColor: '#28A7E81A',
padding: 8 * scale, padding: 8,
borderRadius: 8 * scale, borderRadius: 8,
}, },
infoTextBlock: { infoTextBlock: {
flexDirection: 'column', flexDirection: 'column',
}, },
infoTitle: { infoTitle: {
fontWeight: '500', fontWeight: '500',
fontSize: 18 * scale, fontSize: 18,
}, },
infoSubtext: { infoSubtext: {
fontWeight: '500', fontWeight: '500',
fontSize: 14 * scale, fontSize: 14,
color: '#00000066', color: '#00000066',
}, },
subtextRight: { subtextRight: {
@@ -136,38 +133,35 @@ export const HomeStyle = (scale: number) =>
highlightBox: { highlightBox: {
backgroundColor: '#69ec6d9c', backgroundColor: '#69ec6d9c',
alignSelf: 'flex-start', alignSelf: 'flex-start',
padding: 4 * scale, padding: 4,
borderRadius: 8 * scale, borderRadius: 8,
}, },
highlightText: { highlightText: {
fontSize: 16 * scale, fontSize: 16,
}, },
animatedIconWrapper: { animatedIconWrapper: {
width: '80%', width: '80%',
height: 40,
}, },
row: { row: {
flexDirection: 'row', // flexDirection: 'row',
gap: 10 * scale, // gap: 10,
width: '100%', // width: '100%',
}, },
rowFull: { rowFull: {
flexDirection: 'row', flexDirection: 'row',
gap: 2 * scale, gap: 2,
width: '100%',
}, },
tabs: { tabs: {
width: '50%', borderRadius: 8,
borderRadius: 8 * scale,
justifyContent: 'space-between',
gap: 10 * scale,
},
tabsContainer: {
marginLeft: 'auto',
paddingLeft: 8 * scale,
paddingRight: 18 * scale,
flexDirection: 'row',
gap: 8 * scale,
}, },
// tabsContainer: {
// marginLeft: 'auto',
// paddingLeft: 8,
// paddingRight: 18,
// flexDirection: 'row',
// gap: 8,
// },
tabsLogo: { tabsLogo: {
width: '40%', width: '40%',
height: '50%', height: '50%',
@@ -176,7 +170,7 @@ export const HomeStyle = (scale: number) =>
tabsText: { tabsText: {
width: '60%', width: '60%',
color: '#FFFFFF', color: '#FFFFFF',
fontSize: 16 * scale, fontSize: 14,
fontWeight: '600', fontWeight: '600',
}, },
}); });

View File

@@ -1,9 +1,7 @@
import NavbarBack from 'components/NavbarBack'; import LayoutTwo from 'components/LayoutTwo';
import Navigation from 'components/Navigation';
import * as React from 'react'; import * as React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ScrollView, StyleSheet, Text, View } from 'react-native'; import { ScrollView, StyleSheet, Text, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import Battery from 'svg/Battery'; import Battery from 'svg/Battery';
import Blade from 'svg/Blade'; import Blade from 'svg/Blade';
import Book from 'svg/Book'; import Book from 'svg/Book';
@@ -22,13 +20,12 @@ const RestrictedProduct = (props: RestrictedProductProps) => {
const [activeTab, setActiveTab] = React.useState<'avia' | 'auto'>('avia'); const [activeTab, setActiveTab] = React.useState<'avia' | 'auto'>('avia');
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<SafeAreaView style={{ flex: 1 }}> <LayoutTwo title={t('Taqiqlangan buyumlar')}>
<NavbarBack title={t('Taqiqlangan buyumlar')} />
<ScrollView style={{ flex: 1 }}> <ScrollView style={{ flex: 1 }}>
<View style={styles.container}> <View style={styles.container}>
<Tabs activeTab={activeTab} setActiveTab={setActiveTab} /> <Tabs activeTab={activeTab} setActiveTab={setActiveTab} />
{activeTab === 'avia' && ( {activeTab === 'avia' && (
<View style={{ marginTop: 20, gap: 10, marginBottom: 20 }}> <View style={{ marginTop: 10, gap: 10, marginBottom: 20 }}>
<Text <Text
style={{ style={{
width: '95%', width: '95%',
@@ -179,7 +176,7 @@ const RestrictedProduct = (props: RestrictedProductProps) => {
</View> </View>
)} )}
{activeTab === 'auto' && ( {activeTab === 'auto' && (
<View style={{ marginTop: 20, gap: 10, marginBottom: 20 }}> <View style={{ marginTop: 10, gap: 10, marginBottom: 20 }}>
<View style={styles.cardWhite}> <View style={styles.cardWhite}>
<View style={styles.priceCard}> <View style={styles.priceCard}>
<Text style={styles.titleBlack}> <Text style={styles.titleBlack}>
@@ -273,8 +270,7 @@ const RestrictedProduct = (props: RestrictedProductProps) => {
)} )}
</View> </View>
</ScrollView> </ScrollView>
<Navigation /> </LayoutTwo>
</SafeAreaView>
); );
}; };
@@ -282,7 +278,7 @@ export default RestrictedProduct;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
marginTop: 20, marginTop: 10,
}, },
card: { card: {
width: '95%', width: '95%',
@@ -294,8 +290,15 @@ const styles = StyleSheet.create({
width: '95%', width: '95%',
gap: 5, gap: 5,
margin: 'auto', margin: 'auto',
padding: 10,
borderRadius: 8, borderRadius: 8,
paddingVertical: 15,
paddingHorizontal: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 1,
}, },
titleBlack: { titleBlack: {
fontSize: 16, fontSize: 16,

View File

@@ -16,7 +16,6 @@ import {
Text, Text,
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 PassportIcon from 'svg/Passport'; import PassportIcon from 'svg/Passport';
@@ -26,8 +25,6 @@ import MyPassport from './MyPassport';
const Passport = () => { const Passport = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const navigation = useNavigation<NativeStackNavigationProp<any>>(); const navigation = useNavigation<NativeStackNavigationProp<any>>();
const { width: screenWidth } = useWindowDimensions();
const scale = screenWidth < 360 ? 0.85 : 1;
const [refreshing, setRefreshing] = useState(false); const [refreshing, setRefreshing] = useState(false);
const { const {
@@ -105,11 +102,7 @@ const Passport = () => {
{myPassport && myPassport.length === 0 ? ( {myPassport && myPassport.length === 0 ? (
<View style={styles.content}> <View style={styles.content}>
<View style={styles.emptyState}> <View style={styles.emptyState}>
<PassportIcon <PassportIcon color="#ccc" width={80} height={80} />
color="#ccc"
width={80 * scale}
height={80 * scale}
/>
<Text style={styles.emptyText}> <Text style={styles.emptyText}>
{t("Hali pasport qo'shilmagan")} {t("Hali pasport qo'shilmagan")}
</Text> </Text>
@@ -127,7 +120,7 @@ const Passport = () => {
onPress={handleNavigateToCreatePassword} onPress={handleNavigateToCreatePassword}
activeOpacity={0.7} activeOpacity={0.7}
> >
<Plus color="#fff" width={24 * scale} height={24 * scale} /> <Plus color="#fff" width={24} height={24} />
<Text style={styles.addButtonText}> <Text style={styles.addButtonText}>
{t("Yangi pasport qo'shish")} {t("Yangi pasport qo'shish")}
</Text> </Text>

View File

@@ -2,6 +2,7 @@ import { PacketsData } from 'api/packets';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
FlatList,
StyleSheet, StyleSheet,
Text, Text,
TouchableOpacity, TouchableOpacity,
@@ -11,11 +12,7 @@ import {
import CloseIcon from 'svg/Close'; import CloseIcon from 'svg/Close';
import FilterIcon from 'svg/Filter'; import FilterIcon from 'svg/Filter';
const transportTypes = [ const transportTypes = ['AUTO', 'AVIA'] as const;
// 'all',
'AUTO',
'AVIA',
] as const;
type TransportType = (typeof transportTypes)[number]; type TransportType = (typeof transportTypes)[number];
interface Props { interface Props {
@@ -38,9 +35,8 @@ const Filter = ({
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
const { width: screenWidth } = useWindowDimensions(); const { width: screenWidth } = useWindowDimensions();
const { t } = useTranslation(); const { t } = useTranslation();
const scale = screenWidth < 360 ? 0.85 : 1;
const styles = makeStyles(scale); 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],
@@ -48,32 +44,35 @@ const Filter = ({
return ( return (
<View style={styles.container}> <View style={styles.container}>
{/* Tugma */}
<TouchableOpacity <TouchableOpacity
style={styles.card} style={styles.card}
onPress={() => setOpen(prev => !prev)} onPress={() => setOpen(prev => !prev)}
> >
<FilterIcon color="#000000" width={18 * scale} height={18 * scale} /> <FilterIcon color="#000000" width={18} height={18} />
<Text style={styles.text}>{t('Filter')}</Text> <Text style={styles.text}>{t('Filter')}</Text>
</TouchableOpacity> </TouchableOpacity>
{open && ( {open && (
<View style={styles.dropdown}> <View style={styles.dropdown}>
<View {/* Header */}
style={{ <View style={styles.dropdownHeader}>
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 10,
}}
>
<Text style={styles.sectionTitle}>{t('Transport')}</Text> <Text style={styles.sectionTitle}>{t('Transport')}</Text>
<TouchableOpacity onPress={() => setOpen(false)}> <TouchableOpacity onPress={() => setOpen(false)}>
<CloseIcon /> <CloseIcon />
</TouchableOpacity> </TouchableOpacity>
</View> </View>
<View style={styles.typeList}>
{transportTypes.map(type => ( {/* Transport Types -> FlatList */}
<FlatList
data={transportTypes}
keyExtractor={item => item}
numColumns={2}
scrollEnabled={false}
columnWrapperStyle={{ gap: 8 }}
contentContainerStyle={{ gap: 8, marginBottom: 12 }}
renderItem={({ item: type }) => (
<TouchableOpacity <TouchableOpacity
key={type}
style={[ style={[
styles.typeButton, styles.typeButton,
selectedType === type && styles.activeType, selectedType === type && styles.activeType,
@@ -89,48 +88,28 @@ const Filter = ({
selectedType === type && styles.activeTypeText, selectedType === type && styles.activeTypeText,
]} ]}
> >
{ {type === 'AUTO' ? t('Avto') : t('Avia')}
// type === 'all'
// ? t('Barchasi')
// :
type === 'AUTO' ? t('Avto') : t('Avia')
}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
))} )}
</View> />
<Text style={styles.sectionTitle}>{t('Reys raqami')}</Text> <Text style={styles.sectionTitle}>{t('Reys raqami')}</Text>
<View style={styles.flightList}> <FlatList
<TouchableOpacity data={[{ id: 'all', packetName: 'all' }, ...newOrders]}
style={[ keyExtractor={item => item.id?.toString() || 'all'}
styles.flightButton, numColumns={1}
selectedFlight === 'all' && styles.activeFlight, scrollEnabled={false}
]} contentContainerStyle={{ gap: 8 }} // faqat shu kifoya
onPress={() => { renderItem={({ item }) => (
setReys('all');
setSelectedData(null);
setOpen(false);
}}
>
<Text
style={[
styles.flightText,
selectedFlight === 'all' && styles.activeFlightText,
]}
>
{t('Barchasi')}
</Text>
</TouchableOpacity>
{newOrders.map(item => (
<TouchableOpacity <TouchableOpacity
key={item.id}
style={[ style={[
styles.flightButton, styles.flightButton,
selectedFlight === item.packetName && styles.activeFlight, selectedFlight === item.packetName && styles.activeFlight,
]} ]}
onPress={() => { onPress={() => {
setReys(item.packetName); setReys(item.packetName);
setSelectedData(item); setSelectedData(item.id === 'all' ? null : item);
setOpen(false); setOpen(false);
}} }}
> >
@@ -141,75 +120,73 @@ const Filter = ({
styles.activeFlightText, styles.activeFlightText,
]} ]}
> >
{item.packetName} {item.packetName === 'all' ? t('Barchasi') : item.packetName}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
))} )}
</View> />
</View> </View>
)} )}
</View> </View>
); );
}; };
const makeStyles = (scale: number) =>
const makeStyles = () =>
StyleSheet.create({ StyleSheet.create({
container: { container: {
height: 'auto', borderRadius: 8,
borderRadius: 8 * scale,
alignItems: 'flex-end', alignItems: 'flex-end',
justifyContent: 'flex-start',
position: 'relative', position: 'relative',
zIndex: 10, zIndex: 10,
}, },
card: { card: {
paddingHorizontal: 12 * scale, paddingHorizontal: 12,
height: 40 * scale, height: 40,
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
backgroundColor: '#D8DADC', backgroundColor: '#D8DADC',
borderRadius: 8 * scale, borderRadius: 8,
flexDirection: 'row', flexDirection: 'row',
gap: 4 * scale, gap: 4,
}, },
text: { text: {
color: '#000000', color: '#000000',
fontWeight: '500', fontWeight: '500',
fontSize: 14 * scale, fontSize: 14,
}, },
dropdown: { dropdown: {
position: 'absolute', position: 'absolute',
top: 50 * scale, top: 50,
right: 0, right: 0,
backgroundColor: '#fff', backgroundColor: '#fff',
borderRadius: 8 * scale, borderRadius: 8,
paddingVertical: 8 * scale, paddingVertical: 8,
paddingHorizontal: 10 * scale, paddingHorizontal: 10,
shadowColor: '#000', shadowColor: '#000',
shadowOffset: { width: 0, height: 2 }, shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2, shadowOpacity: 0.2,
shadowRadius: 4, shadowRadius: 4,
elevation: 5, elevation: 5,
zIndex: 10, zIndex: 10,
minWidth: 200 * scale, minWidth: 200,
},
dropdownHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 10,
}, },
sectionTitle: { sectionTitle: {
fontWeight: '600', fontWeight: '600',
marginBottom: 6 * scale, marginBottom: 6,
color: '#333', color: '#333',
fontSize: 16 * scale, fontSize: 16,
},
typeList: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 8 * scale,
marginBottom: 12 * scale,
}, },
typeButton: { typeButton: {
flex: 1,
backgroundColor: '#F3FAFF', backgroundColor: '#F3FAFF',
paddingHorizontal: 5 * scale, paddingHorizontal: 5,
paddingVertical: 6 * scale, paddingVertical: 6,
borderRadius: 6 * scale, borderRadius: 6,
}, },
activeType: { activeType: {
backgroundColor: '#28A7E8', backgroundColor: '#28A7E8',
@@ -217,22 +194,18 @@ const makeStyles = (scale: number) =>
typeText: { typeText: {
color: '#28A7E8', color: '#28A7E8',
fontWeight: '500', fontWeight: '500',
fontSize: 14 * scale, fontSize: 14,
textAlign: 'center',
}, },
activeTypeText: { activeTypeText: {
color: '#fff', color: '#fff',
}, },
flightList: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 8 * scale,
marginBottom: 12 * scale,
},
flightButton: { flightButton: {
flex: 1,
backgroundColor: '#F3FAFF', backgroundColor: '#F3FAFF',
paddingHorizontal: 10 * scale, paddingHorizontal: 10,
paddingVertical: 6 * scale, paddingVertical: 6,
borderRadius: 6 * scale, borderRadius: 6,
}, },
activeFlight: { activeFlight: {
backgroundColor: '#28A7E8', backgroundColor: '#28A7E8',
@@ -240,7 +213,8 @@ const makeStyles = (scale: number) =>
flightText: { flightText: {
color: '#28A7E8', color: '#28A7E8',
fontWeight: '500', fontWeight: '500',
fontSize: 14 * scale, fontSize: 14,
textAlign: 'center',
}, },
activeFlightText: { activeFlightText: {
color: '#fff', color: '#fff',

View File

@@ -2,11 +2,11 @@ import { PacketsData } from 'api/packets';
import React, { useCallback, useMemo } from 'react'; import React, { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
FlatList,
StyleSheet, StyleSheet,
Text, Text,
TouchableOpacity, TouchableOpacity,
View, View,
useWindowDimensions,
} from 'react-native'; } from 'react-native';
import Auto from 'svg/Auto'; import Auto from 'svg/Auto';
import Avia from 'svg/Avia'; import Avia from 'svg/Avia';
@@ -53,44 +53,39 @@ interface Props {
} }
const Order = ({ data, openModal, selectedData }: Props) => { const Order = ({ data, openModal, selectedData }: Props) => {
const { width: screenWidth } = useWindowDimensions();
const scale = useMemo(() => (screenWidth < 360 ? 0.85 : 1), [screenWidth]);
const { t } = useTranslation(); const { t } = useTranslation();
const styles = useMemo( const styles = useMemo(() => makeStyles(), []);
() => makeStyles(scale, screenWidth),
[scale, screenWidth],
);
const createIcons = useCallback( const createIcons = useCallback(
(scale: number, cargo: string) => [ (cargo: string) => [
{ {
status: 'COLLECTING', status: 'COLLECTING',
icon: <BoxIcon width={20 * scale} height={20 * scale} color="" />, icon: <BoxIcon width={24} height={24} color="" />,
}, },
{ {
status: 'ON_THE_WAY', status: 'ON_THE_WAY',
icon: icon:
cargo === 'avia' ? ( cargo === 'avia' ? (
<Avia width={20 * scale} height={20 * scale} color="" /> <Avia width={24} height={24} color="" />
) : ( ) : (
<Auto width={20 * scale} height={20 * scale} color="" view="-4" /> <Auto width={24} height={24} color="" view="-4" />
), ),
}, },
{ {
status: 'IN_CUSTOMS', status: 'IN_CUSTOMS',
icon: <BagIcon width={20 * scale} height={20 * scale} color="" />, icon: <BagIcon width={24} height={24} color="" />,
}, },
{ {
status: 'IN_WAREHOUSE', status: 'IN_WAREHOUSE',
icon: <Store width={20 * scale} height={20 * scale} color="" />, icon: <Store width={24} height={24} color="" />,
}, },
{ {
status: 'DELIVERED', status: 'DELIVERED',
icon: <TrunkIcon width={20 * scale} height={20 * scale} color="" />, icon: <TrunkIcon width={24} height={24} color="" />,
}, },
{ {
status: 'PAID', status: 'PAID',
icon: <SuccessIcon width={20 * scale} height={20 * scale} color="" />, icon: <SuccessIcon width={24} height={24} color="" />,
}, },
], ],
[], [],
@@ -104,31 +99,31 @@ const Order = ({ data, openModal, selectedData }: Props) => {
); );
const renderOrderItem = useCallback( const renderOrderItem = useCallback(
(ItemLayout: DataInfo, index: number) => { ({ item, index }: { item: DataInfo; index: number }) => {
const currentStatusIndex = statuses.indexOf( const currentStatusIndex = statuses.indexOf(
ItemLayout.deliveryStatus as FilterType, item.deliveryStatus as FilterType,
); );
const icons = createIcons(item.deliveryStatus);
const icons = createIcons(scale, ItemLayout.deliveryStatus);
return ( return (
<TouchableOpacity <TouchableOpacity key={index} onPress={() => handleItemPress(item)}>
key={index}
onPress={() => handleItemPress(ItemLayout)}
>
<View style={styles.card}> <View style={styles.card}>
<View style={styles.statusCard}> <FlatList
{icons.map((item, i) => { data={icons}
keyExtractor={(iconItem, i) => iconItem.status + i}
numColumns={6}
contentContainerStyle={styles.statusCard}
renderItem={({ item: iconItem, index: i }) => {
const iconColor = i <= currentStatusIndex ? '#28A7E8' : '#000'; const iconColor = i <= currentStatusIndex ? '#28A7E8' : '#000';
const viewColor = const viewColor =
i <= currentStatusIndex ? '#28A7E81A' : '#0000001A'; i <= currentStatusIndex ? '#28A7E81A' : '#0000001A';
return ( return (
<React.Fragment key={item.status}> <View style={styles.iconWrapper}>
<View <View
style={[styles.circle, { backgroundColor: viewColor }]} style={[styles.circle, { backgroundColor: viewColor }]}
> >
{React.cloneElement(item.icon, { color: iconColor })} {React.cloneElement(iconItem.icon, { color: iconColor })}
</View> </View>
{i !== icons.length - 1 && ( {i !== icons.length - 1 && (
<View <View
@@ -138,186 +133,159 @@ const Order = ({ data, openModal, selectedData }: Props) => {
]} ]}
/> />
)} )}
</React.Fragment> </View>
); );
})} }}
</View> />
{/* Status Label */}
<View <View
style={[ style={[
styles.statusLabelWrapper, styles.statusLabelWrapper,
{ {
backgroundColor: `${ backgroundColor: `${statusColorMap[item.deliveryStatus]}1A`,
statusColorMap[ItemLayout.deliveryStatus]
}1A`,
}, },
]} ]}
> >
<Text <Text
style={[ style={[
styles.statusText, styles.statusText,
{ color: statusColorMap[ItemLayout.deliveryStatus] }, { color: statusColorMap[item.deliveryStatus] },
]} ]}
> >
{t( {t(
tabList.find(item => item.value === ItemLayout.deliveryStatus) tabList.find(tab => tab.value === item.deliveryStatus)
?.label || '', ?.label || '',
)} )}
</Text> </Text>
</View> </View>
{/* Info */}
<View style={styles.infoCard}> <View style={styles.infoCard}>
<Text style={styles.infoTitle}>{t('Reys raqami')}</Text> <Text style={styles.infoTitle}>{t('Reys raqami')}</Text>
<Text <Text
style={[styles.infoText, { width: '50%', textAlign: 'right' }]} style={[styles.infoText, { width: '50%', textAlign: 'right' }]}
> >
{ItemLayout.packetName} {item.packetName}
</Text> </Text>
</View> </View>
<View style={styles.infoCard}> <View style={styles.infoCard}>
<Text style={styles.infoTitle}>{t('Mahsulotlar ogirligi')}</Text> <Text style={styles.infoTitle}>{t('Mahsulotlar ogirligi')}</Text>
<Text style={styles.infoText}>{ItemLayout.weight}</Text> <Text style={styles.infoText}>{item.weight}</Text>
</View> </View>
<View style={styles.infoCard}> <View style={styles.infoCard}>
<Text style={styles.infoTitle}>{t('Umumiy narxi')}</Text> <Text style={styles.infoTitle}>{t('Umumiy narxi')}</Text>
<Text style={styles.infoText}>{ItemLayout.totalPrice}</Text> <Text style={styles.infoText}>{item.totalPrice}</Text>
</View> </View>
</View> </View>
</TouchableOpacity> </TouchableOpacity>
); );
}, },
[scale, createIcons, handleItemPress], [createIcons, handleItemPress],
); );
const orderItems = useMemo(() => { const ordersData = useMemo(
if (selectedData) { () => (selectedData ? [selectedData] : data.data),
return [renderOrderItem(selectedData, 0)]; [data, selectedData],
}
return data.data.map(renderOrderItem);
}, [data, renderOrderItem, selectedData]);
const countSection = useMemo(
() => (
<View style={styles.count}>
<Text style={styles.title}>{t('Buyurtmalar soni')}</Text>
{selectedData ? (
<Text style={styles.title}>1</Text>
) : (
<Text style={styles.title}>{data.data.length}</Text>
)}
</View>
),
[styles.count, styles.title, data.data.length, selectedData],
); );
return ( return (
<View style={styles.container}> <View style={styles.container}>
{countSection} <View style={styles.count}>
{orderItems} <Text style={styles.title}>{t('Buyurtmalar soni')}</Text>
<Text style={styles.title}>
{selectedData ? '1' : data.data.length}
</Text>
</View>
<FlatList
data={ordersData}
scrollEnabled={false}
renderItem={renderOrderItem}
keyExtractor={(item, index) => item.id?.toString() || index.toString()}
/>
</View> </View>
); );
}; };
const makeStyles = (scale: number, screenWidth: number) => const makeStyles = () =>
StyleSheet.create({ StyleSheet.create({
container: { container: {
width: screenWidth * 0.95, width: '95%',
marginTop: 10, margin: 'auto',
alignSelf: 'center', borderRadius: 10,
borderRadius: 8 * scale,
padding: 8 * scale,
backgroundColor: '#F5F5F5',
}, },
count: { count: {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center', alignItems: 'center',
marginTop: 10,
}, },
title: { title: {
fontSize: 16 * scale, fontSize: 16,
fontWeight: '500', fontWeight: '500',
color: '#333', color: '#333',
}, },
card: { card: {
backgroundColor: '#FFFFFF', backgroundColor: '#fff',
marginTop: 12 * scale, margin: 1,
padding: 15 * scale, marginTop: 8,
borderRadius: 10 * scale, padding: 15,
gap: 5 * scale, borderRadius: 10,
flexDirection: 'column', // iOS
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 2,
// Android
elevation: 2,
}, },
statusCard: { statusCard: {
flexDirection: 'row', marginBottom: 10,
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 10 * scale,
}, },
circle: { circle: {
padding: 8 * scale, padding: 8,
borderRadius: 50, borderRadius: 50,
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
}, },
divider: { divider: {
width: 20 * scale, width: 20,
borderBottomWidth: 2 * scale, borderBottomWidth: 2,
borderStyle: 'dashed', borderStyle: 'dashed',
}, },
statusLabelWrapper: { statusLabelWrapper: {
borderRadius: 8 * scale, borderRadius: 8,
alignSelf: 'flex-start', alignSelf: 'flex-start',
paddingVertical: 6 * scale, paddingVertical: 6,
paddingHorizontal: 10 * scale, paddingHorizontal: 10,
marginBottom: 12 * scale, marginBottom: 12,
}, },
statusText: { statusText: {
fontWeight: '600', fontWeight: '600',
fontSize: 16 * scale, fontSize: 16,
}, },
infoCard: { infoCard: {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
marginBottom: 6 * scale, marginBottom: 6,
},
iconWrapper: {
flexDirection: 'row',
alignItems: 'center',
flex: 1,
marginBottom: 10,
}, },
infoTitle: { infoTitle: {
fontSize: 16 * scale, fontSize: 16,
color: '#979797', color: '#979797',
fontWeight: '500', fontWeight: '500',
}, },
infoText: { infoText: {
fontSize: 14 * scale, fontSize: 14,
color: '#28A7E8', color: '#28A7E8',
fontWeight: '500', fontWeight: '500',
}, },
modalOverlay: {
flex: 1,
backgroundColor: 'rgba(0,0,0,0.4)',
justifyContent: 'center',
alignItems: 'center',
zIndex: 20,
},
modalContent: {
width: '90%',
maxHeight: '80%',
backgroundColor: '#fff',
borderRadius: 10,
padding: 20,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3,
shadowRadius: 4,
elevation: 5,
},
modalTitle: {
fontSize: 20 * scale,
fontWeight: '600',
marginBottom: 10,
},
closeButton: {
marginTop: 20,
backgroundColor: '#28A7E8',
paddingVertical: 10,
borderRadius: 8,
alignItems: 'center',
},
}); });
export default Order; export default Order;

View File

@@ -1,8 +1,7 @@
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import packetsApi from 'api/packets'; import packetsApi from 'api/packets';
import Layout from 'components/Layout';
import LoadingScreen from 'components/LoadingScreen'; import LoadingScreen from 'components/LoadingScreen';
import Navbar from 'components/Navbar';
import Navigation from 'components/Navigation';
import NoResult from 'components/NoResult'; import NoResult from 'components/NoResult';
import Pagination from 'components/Pagination'; import Pagination from 'components/Pagination';
import React, { useCallback, useEffect, useMemo, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react';
@@ -15,7 +14,6 @@ import {
View, View,
useWindowDimensions, useWindowDimensions,
} from 'react-native'; } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import Serach from 'svg/Serach'; import Serach from 'svg/Serach';
import { DataInfo } from '../lib/data'; import { DataInfo } from '../lib/data';
import Filter from './Filter'; import Filter from './Filter';
@@ -140,61 +138,43 @@ const Status = () => {
if (isLoading || isFetching) { if (isLoading || isFetching) {
return ( return (
<SafeAreaView style={{ flex: 1 }}> <Layout>
<View style={styles.container}> <LoadingScreen message={loadingMessage} progress={progress} />
<Navbar /> </Layout>
<LoadingScreen message={loadingMessage} progress={progress} />
<Navigation />
</View>
</SafeAreaView>
); );
} }
if (statusData?.data.length === 0) { if (statusData?.data.length === 0) {
return ( return (
<SafeAreaView style={{ flex: 1 }}> <Layout>
<View style={styles.container}> <Tabs filter={filter} setFilter={setFilter} />
<Navbar /> <View style={styles.controls}>
<Tabs filter={filter} setFilter={setFilter} /> <View style={{ position: 'relative' }}>
<View style={styles.controls}> <Filter
{/* <View style={styles.searchContainer}> transportTypes={transportTypes}
<TextInput setTransportTypes={setTransportTypes}
placeholder={t('ID orqali izlash')} reys={reys}
placeholderTextColor="#D8DADC" setReys={setReys}
style={styles.search} data={statusData!}
/> setSelectedData={setSelectedData}
<View style={styles.searchIcon}>{searchIcon}</View> />
</View> */}
<View style={{ position: 'relative' }}>
<Filter
transportTypes={transportTypes}
setTransportTypes={setTransportTypes}
reys={reys}
setReys={setReys}
data={statusData!}
setSelectedData={setSelectedData}
/>
</View>
</View> </View>
<NoResult message={t("Hech qanday ma'lumot topilmadi")} />
<Navigation />
</View> </View>
</SafeAreaView> <NoResult message={t("Hech qanday ma'lumot topilmadi")} />
</Layout>
); );
} }
return ( return (
<SafeAreaView style={{ flex: 1 }}> <Layout>
<View style={styles.container}> <ScrollView
<Navbar /> keyboardShouldPersistTaps="handled"
<ScrollView refreshControl={refreshControl}
keyboardShouldPersistTaps="handled" removeClippedSubviews={true}
refreshControl={refreshControl} >
removeClippedSubviews={true} <Tabs filter={filter} setFilter={setFilter} />
> <View style={styles.controls}>
<Tabs filter={filter} setFilter={setFilter} /> {/* <View style={styles.searchContainer}>
<View style={styles.controls}>
{/* <View style={styles.searchContainer}>
<TextInput <TextInput
placeholder={t('ID orqali izlash')} placeholder={t('ID orqali izlash')}
placeholderTextColor="#D8DADC" placeholderTextColor="#D8DADC"
@@ -202,47 +182,45 @@ const Status = () => {
/> />
<View style={styles.searchIcon}>{searchIcon}</View> <View style={styles.searchIcon}>{searchIcon}</View>
</View> */} </View> */}
<View style={{ position: 'relative' }}> <View style={{ position: 'relative' }}>
<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}
/> />
</View>
</View> </View>
<Order
data={statusData!}
openModal={openModal}
selectedData={selectedData}
/>
</ScrollView>
<OrderDetailModal
visible={modalVisible}
setVisible={setModalVisible}
selectedOrder={selectedOrder}
/>
<View
style={{
flexDirection: 'row',
justifyContent: 'flex-end',
width: '95%',
alignSelf: 'center',
paddingRight: 20,
paddingVertical: 10,
}}
>
<Pagination
page={page}
totalPages={statusData?.totalPages ?? 1}
setPage={setPage}
/>
</View> </View>
<Navigation /> <Order
data={statusData!}
openModal={openModal}
selectedData={selectedData}
/>
</ScrollView>
<OrderDetailModal
visible={modalVisible}
setVisible={setModalVisible}
selectedOrder={selectedOrder}
/>
<View
style={{
flexDirection: 'row',
justifyContent: 'flex-end',
width: '95%',
alignSelf: 'center',
paddingRight: 20,
paddingVertical: 10,
}}
>
<Pagination
page={page}
totalPages={statusData?.totalPages ?? 1}
setPage={setPage}
/>
</View> </View>
</SafeAreaView> </Layout>
); );
}; };

View File

@@ -1,9 +1,9 @@
import React, { Dispatch, SetStateAction, useRef, useState } from 'react'; import React, { Dispatch, SetStateAction, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
FlatList,
NativeScrollEvent, NativeScrollEvent,
NativeSyntheticEvent, NativeSyntheticEvent,
ScrollView,
StyleSheet, StyleSheet,
Text, Text,
TouchableOpacity, TouchableOpacity,
@@ -32,31 +32,29 @@ const tabList: { label: string; value: FilterType }[] = [
{ label: 'Bojxonada', value: 'IN_CUSTOMS' }, { label: 'Bojxonada', value: 'IN_CUSTOMS' },
{ label: 'Toshkent omboriga yetib keldi', value: 'IN_WAREHOUSE' }, { label: 'Toshkent omboriga yetib keldi', value: 'IN_WAREHOUSE' },
{ label: 'Topshirish punktiga yuborildi', value: 'DELIVERED' }, { label: 'Topshirish punktiga yuborildi', value: 'DELIVERED' },
// { label: 'Qabul qilingan', value: 'DELIVERED' },
]; ];
const Tabs = ({ filter, setFilter }: Props) => { const Tabs = ({ filter, setFilter }: Props) => {
const { width: screenWidth } = useWindowDimensions(); const { width: screenWidth } = useWindowDimensions();
const scale = screenWidth < 360 ? 0.85 : 1; const cardWidth = screenWidth * 0.95;
const styles = makeStyles(scale); const styles = makeStyles();
const scrollRef = useRef<ScrollView>(null); const flatListRef = useRef<FlatList>(null);
const [scrollX, setScrollX] = useState(0); const [scrollX, setScrollX] = useState(0);
const { t } = useTranslation(); const { t } = useTranslation();
const scrollStep = 120; const scrollStep = 120;
const handleScrollLeft = () => { const handleScrollLeft = () => {
if (scrollRef.current) { flatListRef.current?.scrollToOffset({
scrollRef.current.scrollTo({ offset: Math.max(0, scrollX - scrollStep),
x: Math.max(0, scrollX - scrollStep), animated: true,
animated: true, });
});
}
}; };
const handleScrollRight = () => { const handleScrollRight = () => {
if (scrollRef.current) { flatListRef.current?.scrollToOffset({
scrollRef.current.scrollTo({ x: scrollX + scrollStep, animated: true }); offset: scrollX + scrollStep,
} animated: true,
});
}; };
const onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => { const onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
@@ -69,27 +67,27 @@ const Tabs = ({ filter, setFilter }: Props) => {
<ArrowLeft color="#28A7E8" width={20} height={20} /> <ArrowLeft color="#28A7E8" width={20} height={20} />
</TouchableOpacity> </TouchableOpacity>
<ScrollView <FlatList
ref={flatListRef}
horizontal horizontal
onScroll={onScroll} data={tabList}
ref={scrollRef} keyExtractor={item => item.value}
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.scrollContent} onScroll={onScroll}
> renderItem={({ item }) => (
{tabList.map(tab => (
<TouchableOpacity <TouchableOpacity
key={tab.value} style={[styles.card, filter === item.value && styles.activeCard]}
style={[styles.card, filter === tab.value && styles.activeCard]} onPress={() => setFilter(item.value)}
onPress={() => setFilter(tab.value)}
> >
<Text <Text
style={[styles.text, filter === tab.value && styles.activeText]} style={[styles.text, filter === item.value && styles.activeText]}
> >
{t(tab.label)} {t(item.label)}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
))} )}
</ScrollView> contentContainerStyle={styles.scrollContent}
/>
<TouchableOpacity style={styles.navButton} onPress={handleScrollRight}> <TouchableOpacity style={styles.navButton} onPress={handleScrollRight}>
<ArrowRightUnderline color="#28A7E8" width={20} height={20} /> <ArrowRightUnderline color="#28A7E8" width={20} height={20} />
@@ -98,32 +96,38 @@ const Tabs = ({ filter, setFilter }: Props) => {
); );
}; };
const makeStyles = (scale: number) => const makeStyles = () =>
StyleSheet.create({ StyleSheet.create({
container: { container: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
width: '95%', width: '95%',
height: 50 * scale, height: 50,
backgroundColor: '#FFFFFF', backgroundColor: '#FFFFFF',
marginTop: 20 * scale, marginTop: 10,
alignSelf: 'center', alignSelf: 'center',
borderRadius: 8 * scale, borderRadius: 8,
paddingHorizontal: 4 * scale, paddingHorizontal: 4,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 1,
}, },
scrollContent: { scrollContent: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingHorizontal: 4 * scale, paddingHorizontal: 4,
}, },
card: { card: {
paddingHorizontal: 12 * scale, paddingHorizontal: 12,
paddingVertical: 8 * scale, paddingVertical: 8,
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
backgroundColor: '#F3FAFF', backgroundColor: '#F3FAFF',
marginRight: 8 * scale, marginRight: 8,
borderRadius: 8 * scale, borderRadius: 8,
}, },
activeCard: { activeCard: {
backgroundColor: '#28A7E8', backgroundColor: '#28A7E8',
@@ -131,13 +135,13 @@ const makeStyles = (scale: number) =>
text: { text: {
color: '#28A7E8', color: '#28A7E8',
fontWeight: '500', fontWeight: '500',
fontSize: 14 * scale, fontSize: 14,
}, },
activeText: { activeText: {
color: '#fff', color: '#fff',
}, },
navButton: { navButton: {
padding: 6 * scale, padding: 6,
}, },
}); });