Initial commit
This commit is contained in:
116
src/screens/wallet/payment/ui/Payment.tsx
Normal file
116
src/screens/wallet/payment/ui/Payment.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { PacketsData } from 'api/packets';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Text, TouchableOpacity, View } from 'react-native';
|
||||
import { PaymentStyle } from './style';
|
||||
|
||||
type WalletStackParamList = {
|
||||
PaymentMethod: { packets: PacketsData };
|
||||
PaymentQrCode: { packets: PacketsData };
|
||||
};
|
||||
|
||||
type LoginScreenNavigationProp =
|
||||
NativeStackNavigationProp<WalletStackParamList>;
|
||||
|
||||
interface Props {
|
||||
packets: PacketsData;
|
||||
}
|
||||
|
||||
const Payment = ({ packets }: Props) => {
|
||||
const navigation = useNavigation<LoginScreenNavigationProp>();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handlePaymentPress = useCallback(
|
||||
(item: any) => {
|
||||
const isPaid = item.paymentStatus === 'paid';
|
||||
navigation.navigate(isPaid ? 'PaymentQrCode' : 'PaymentMethod', {
|
||||
packets: item, // tanlangan itemni to‘liq yuboramiz
|
||||
});
|
||||
},
|
||||
[navigation],
|
||||
);
|
||||
|
||||
const cardContainerStyle = useMemo(
|
||||
() => ({
|
||||
flexDirection: 'row' as const,
|
||||
gap: 10,
|
||||
justifyContent: 'center' as const,
|
||||
alignItems: 'center' as const,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const badgeStyle = useMemo(
|
||||
() => [PaymentStyle.badge, { backgroundColor: '#D32F2F' }],
|
||||
[],
|
||||
);
|
||||
|
||||
const renderPaymentCard = useCallback(
|
||||
(item: any) => {
|
||||
const isPaid = item.paymentStatus === 'paid';
|
||||
const cardStyle = [
|
||||
PaymentStyle.card,
|
||||
{ borderColor: isPaid ? '#4CAF50' : '#D32F2F', borderWidth: 1.5 },
|
||||
];
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
onPress={() => handlePaymentPress(item)}
|
||||
key={item.id}
|
||||
>
|
||||
<View style={cardContainerStyle}>
|
||||
<View style={cardStyle}>
|
||||
<View style={PaymentStyle.cardHeader}>
|
||||
<Text style={PaymentStyle.title}>{item.packetName}</Text>
|
||||
{isPaid ? (
|
||||
<Text style={PaymentStyle.badge}>{t("To'langan")}</Text>
|
||||
) : (
|
||||
<Text style={badgeStyle}>{t("To'lanmagan")}</Text>
|
||||
)}
|
||||
</View>
|
||||
<View style={PaymentStyle.row}>
|
||||
<Text style={PaymentStyle.infoTitle}>{t('Reys raqami')}</Text>
|
||||
<Text
|
||||
style={[
|
||||
PaymentStyle.text,
|
||||
{ width: '60%', textAlign: 'right' },
|
||||
]}
|
||||
>
|
||||
{item.packetName}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={PaymentStyle.row}>
|
||||
<Text style={PaymentStyle.infoTitle}>
|
||||
{t("Mahsulotlar og'irligi")}
|
||||
</Text>
|
||||
<Text style={PaymentStyle.text}>{item.weight}</Text>
|
||||
</View>
|
||||
<View style={PaymentStyle.row}>
|
||||
<Text style={PaymentStyle.infoTitle}>
|
||||
{t('Mahsulotlar soni')}
|
||||
</Text>
|
||||
<Text style={PaymentStyle.text}>{item.items.length}</Text>
|
||||
</View>
|
||||
<View style={PaymentStyle.row}>
|
||||
<Text style={PaymentStyle.infoTitle}>{t('Umumiy narxi')}</Text>
|
||||
<Text style={PaymentStyle.text}>{item.totalPrice}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
},
|
||||
[handlePaymentPress, cardContainerStyle, badgeStyle, t],
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={PaymentStyle.container}>
|
||||
{packets?.data.map(renderPaymentCard)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default Payment;
|
||||
268
src/screens/wallet/payment/ui/Wallet.tsx
Normal file
268
src/screens/wallet/payment/ui/Wallet.tsx
Normal file
@@ -0,0 +1,268 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import packetsApi from 'api/packets';
|
||||
import LoadingScreen from 'components/LoadingScreen';
|
||||
import Navbar from 'components/Navbar';
|
||||
import Navigation from 'components/Navigation';
|
||||
import NoResult from 'components/NoResult';
|
||||
import Pagination from 'components/Pagination';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
RefreshControl,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import Payment from './Payment';
|
||||
|
||||
const Wallet = () => {
|
||||
const { t } = useTranslation();
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [page, setPage] = useState(0);
|
||||
const [pageAvia, setPageAvia] = useState(0);
|
||||
const [selectedType, setSelectedType] = useState<'AVIA' | 'AUTO'>('AVIA');
|
||||
const {
|
||||
data: packets,
|
||||
isFetching,
|
||||
isLoading,
|
||||
isError,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
queryKey: ['packets', page],
|
||||
queryFn: () =>
|
||||
packetsApi.getPackets({
|
||||
page,
|
||||
size: 10,
|
||||
cargoType: 'AUTO',
|
||||
direction: 'ASC',
|
||||
sort: 'id',
|
||||
}),
|
||||
});
|
||||
|
||||
const {
|
||||
data: packetsAvia,
|
||||
isFetching: isFetchingAvia,
|
||||
isLoading: isLoadingAvia,
|
||||
isError: isErrorAvia,
|
||||
refetch: refetchAvia,
|
||||
} = useQuery({
|
||||
queryKey: ['packetsAvia', pageAvia],
|
||||
queryFn: () =>
|
||||
packetsApi.getPackets({
|
||||
page: pageAvia,
|
||||
size: 10,
|
||||
cargoType: 'AVIA',
|
||||
direction: 'ASC',
|
||||
sort: 'id',
|
||||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const loadInitialData = async () => {
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
refetch();
|
||||
refetchAvia();
|
||||
} catch (error) {
|
||||
console.error('Wallet loading error:', error);
|
||||
refetch();
|
||||
refetchAvia();
|
||||
}
|
||||
};
|
||||
|
||||
loadInitialData();
|
||||
}, []);
|
||||
|
||||
const onRefresh = useCallback(async () => {
|
||||
try {
|
||||
setRefreshing(true);
|
||||
await refetch();
|
||||
} catch (error) {
|
||||
console.error('Refresh error:', error);
|
||||
} finally {
|
||||
setRefreshing(false);
|
||||
}
|
||||
}, [refetch]);
|
||||
|
||||
const refreshControl = useMemo(
|
||||
() => <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />,
|
||||
[refreshing, onRefresh],
|
||||
);
|
||||
|
||||
if (isLoading || isFetching || isLoadingAvia || isFetchingAvia) {
|
||||
return (
|
||||
<SafeAreaView style={{ flex: 1 }}>
|
||||
<View style={styles.container}>
|
||||
<Navbar />
|
||||
<LoadingScreen />
|
||||
<Navigation />
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError || isErrorAvia) {
|
||||
return (
|
||||
<SafeAreaView style={{ flex: 1 }}>
|
||||
<View style={styles.container}>
|
||||
<Navbar />
|
||||
<NoResult message="Xatolik yuz berdi" />
|
||||
<Navigation />
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
(packets && !(packets?.data.length > 0)) ||
|
||||
(packetsAvia && !(packetsAvia?.data.length > 0))
|
||||
) {
|
||||
return (
|
||||
<SafeAreaView style={{ flex: 1 }}>
|
||||
<Navbar />
|
||||
<View style={styles.container}>
|
||||
<View style={styles.header}>
|
||||
<Text style={styles.title}>{t("To'lov")}</Text>
|
||||
</View>
|
||||
<NoResult />
|
||||
</View>
|
||||
<Navigation />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ flex: 1 }}>
|
||||
<View style={styles.container}>
|
||||
<Navbar />
|
||||
<ScrollView
|
||||
refreshControl={refreshControl}
|
||||
style={{ flex: 1 }}
|
||||
contentContainerStyle={{ flexGrow: 1 }}
|
||||
removeClippedSubviews={true}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
<View style={{ flex: 1, justifyContent: 'space-between' }}>
|
||||
<View>
|
||||
<View style={styles.header}>
|
||||
<Text style={styles.title}>{t("To'lov")}</Text>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||
<TouchableOpacity
|
||||
style={{
|
||||
padding: 5,
|
||||
backgroundColor:
|
||||
selectedType === 'AVIA' ? '#28A7E8' : '#F3FAFF',
|
||||
borderTopLeftRadius: 8,
|
||||
borderBottomLeftRadius: 8,
|
||||
}}
|
||||
onPress={() => setSelectedType('AVIA')}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: selectedType === 'AVIA' ? '#fff' : '#28A7E8',
|
||||
fontSize: 14,
|
||||
}}
|
||||
>
|
||||
AVIA
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={{ width: 1 }} />
|
||||
<TouchableOpacity
|
||||
style={{
|
||||
padding: 5,
|
||||
backgroundColor:
|
||||
selectedType === 'AUTO' ? '#28A7E8' : '#F3FAFF',
|
||||
borderTopRightRadius: 8,
|
||||
borderBottomRightRadius: 8,
|
||||
}}
|
||||
onPress={() => setSelectedType('AUTO')}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: selectedType === 'AUTO' ? '#fff' : '#28A7E8',
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
}}
|
||||
>
|
||||
AUTO
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
<Payment
|
||||
packets={selectedType === 'AUTO' ? packets! : packetsAvia!}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
width: '95%',
|
||||
alignSelf: 'center',
|
||||
paddingRight: 20,
|
||||
paddingVertical: 10,
|
||||
}}
|
||||
>
|
||||
<Pagination
|
||||
page={selectedType === 'AUTO' ? page : pageAvia}
|
||||
totalPages={
|
||||
selectedType === 'AUTO'
|
||||
? packets?.totalPages ?? 1
|
||||
: packetsAvia?.totalPages ?? 1
|
||||
}
|
||||
setPage={selectedType === 'AUTO' ? setPage : setPageAvia}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
<Navigation />
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
header: {
|
||||
paddingHorizontal: 20,
|
||||
paddingTop: 5,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
title: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: '#333',
|
||||
},
|
||||
cards: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around',
|
||||
paddingHorizontal: 20,
|
||||
paddingBottom: 10,
|
||||
},
|
||||
card: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: 100,
|
||||
height: 100,
|
||||
backgroundColor: '#f0f0f0',
|
||||
borderRadius: 10,
|
||||
padding: 10,
|
||||
},
|
||||
cardText: {
|
||||
marginTop: 5,
|
||||
fontSize: 12,
|
||||
color: '#555',
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export default Wallet;
|
||||
268
src/screens/wallet/payment/ui/style.ts
Normal file
268
src/screens/wallet/payment/ui/style.ts
Normal file
@@ -0,0 +1,268 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
export const PaymentStyle = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
width: '95%',
|
||||
margin: 'auto',
|
||||
marginTop: 5,
|
||||
gap: 10,
|
||||
},
|
||||
card: {
|
||||
padding: 20,
|
||||
width: '95%',
|
||||
backgroundColor: '#FFFFFF',
|
||||
borderRadius: 8,
|
||||
gap: 10,
|
||||
},
|
||||
cardHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
title: {
|
||||
width: '60%',
|
||||
fontSize: 18,
|
||||
fontWeight: '600',
|
||||
},
|
||||
badge: {
|
||||
backgroundColor: '#4CAF50',
|
||||
color: '#fff',
|
||||
fontSize: 12,
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 2,
|
||||
borderRadius: 5,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
infoTitle: {
|
||||
fontWeight: '500',
|
||||
fontSize: 14,
|
||||
color: '#979797',
|
||||
},
|
||||
text: {
|
||||
color: '#28A7E8',
|
||||
fontWeight: '500',
|
||||
fontSize: 16,
|
||||
},
|
||||
select: {
|
||||
backgroundColor: '#FFFFFF',
|
||||
height: 25,
|
||||
width: 25,
|
||||
borderWidth: 1,
|
||||
borderRadius: 4,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
button: {
|
||||
position: 'absolute',
|
||||
bottom: 70,
|
||||
left: 20,
|
||||
right: 20,
|
||||
height: 56,
|
||||
borderRadius: 8,
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#28A7E8',
|
||||
zIndex: 10,
|
||||
},
|
||||
modalBtn: {
|
||||
position: 'absolute',
|
||||
height: 56,
|
||||
width: '100%',
|
||||
borderRadius: 8,
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#28A7E8',
|
||||
bottom: 10,
|
||||
right: 20,
|
||||
},
|
||||
btnText: {
|
||||
color: '#fff',
|
||||
fontSize: 20,
|
||||
textAlign: 'center',
|
||||
},
|
||||
containerMethod: {
|
||||
width: '95%',
|
||||
alignSelf: 'center',
|
||||
marginTop: 20,
|
||||
},
|
||||
cardMethod: {
|
||||
backgroundColor: '#FFFFFF',
|
||||
borderRadius: 8,
|
||||
},
|
||||
planeContainer: {
|
||||
alignItems: 'center',
|
||||
marginBottom: 20,
|
||||
},
|
||||
planeIcon: {
|
||||
position: 'absolute',
|
||||
top: 10,
|
||||
left: 85,
|
||||
},
|
||||
infoCard: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
paddingLeft: 20,
|
||||
paddingRight: 20,
|
||||
marginBottom: 20,
|
||||
},
|
||||
info: {
|
||||
marginBottom: 15,
|
||||
gap: 5,
|
||||
},
|
||||
titleMethod: {
|
||||
color: '#1D1D1D',
|
||||
fontSize: 18,
|
||||
fontWeight: '500',
|
||||
},
|
||||
textMethod: {
|
||||
color: '#555555',
|
||||
fontSize: 16,
|
||||
fontWeight: '400',
|
||||
},
|
||||
receiptCard: {
|
||||
borderRadius: 10,
|
||||
padding: 16,
|
||||
marginBottom: 15,
|
||||
gap: 8,
|
||||
},
|
||||
|
||||
rowBetween: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
|
||||
rowRight: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
|
||||
itemName: {
|
||||
fontSize: 17,
|
||||
fontWeight: '600',
|
||||
color: '#333',
|
||||
},
|
||||
|
||||
weight: {
|
||||
fontSize: 16,
|
||||
color: '#777',
|
||||
},
|
||||
|
||||
track: {
|
||||
fontSize: 15,
|
||||
color: '#666',
|
||||
},
|
||||
|
||||
price: {
|
||||
fontSize: 15,
|
||||
fontWeight: '500',
|
||||
color: '#444',
|
||||
},
|
||||
|
||||
total: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
color: '#1D1D1D',
|
||||
},
|
||||
|
||||
paymentCard: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 6,
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
},
|
||||
modalOverlay: {
|
||||
flex: 1,
|
||||
backgroundColor: 'rgba(0,0,0,0.4)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
modalContent: {
|
||||
width: '90%',
|
||||
maxHeight: '80%',
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: 10,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 4,
|
||||
elevation: 5,
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
padding: 20,
|
||||
},
|
||||
content: {
|
||||
padding: 10,
|
||||
margin: 'auto',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
marginBottom: 20,
|
||||
},
|
||||
circle: {
|
||||
backgroundColor: '#28A7E8',
|
||||
width: 60,
|
||||
height: 60,
|
||||
borderRadius: 120,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
titleModal: {
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
},
|
||||
closeBtn: {
|
||||
padding: 5,
|
||||
width: 30,
|
||||
height: 30,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#0000000D',
|
||||
borderRadius: 50,
|
||||
shadowColor: '#FFFFFF40',
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 6,
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
},
|
||||
divider: {
|
||||
width: '100%',
|
||||
height: 1,
|
||||
backgroundColor: '#E0E0E0',
|
||||
marginBottom: 10,
|
||||
marginHorizontal: 0,
|
||||
},
|
||||
status: {
|
||||
color: '#00000099',
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
},
|
||||
label: {
|
||||
fontSize: 12,
|
||||
fontWeight: '500',
|
||||
color: '#28A7E8',
|
||||
marginBottom: 5,
|
||||
},
|
||||
errorText: {
|
||||
color: 'red',
|
||||
fontSize: 12,
|
||||
},
|
||||
input: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#D8DADC',
|
||||
borderRadius: 12,
|
||||
padding: 15,
|
||||
fontSize: 16,
|
||||
fontWeight: '400',
|
||||
backgroundColor: '#FFFFFF',
|
||||
color: '#000000',
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user