Initial commit

This commit is contained in:
Samandar Turgunboyev
2025-08-26 16:26:59 +05:00
commit fd95422447
318 changed files with 38301 additions and 0 deletions

View File

@@ -0,0 +1,249 @@
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { useMutation } from '@tanstack/react-query';
import packetsApi from 'api/packets';
import React, { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
ActivityIndicator,
Animated,
Dimensions,
Linking,
StyleSheet,
Text,
TouchableOpacity,
TouchableWithoutFeedback,
View,
} from 'react-native';
import Check from 'svg/Check';
import Click from 'svg/Click';
import Payme from 'svg/Payme';
import { RootStackParamList } from 'types/types';
import { PaymentStyle } from '../../payment/ui/style';
const { height } = Dimensions.get('window');
interface ModalCardViewProps {
isVisible: boolean;
setIsVisible: React.Dispatch<React.SetStateAction<boolean>>;
selectedId: 'click' | 'payme' | null;
packId: number;
setSelectedId: React.Dispatch<React.SetStateAction<'click' | 'payme' | null>>;
}
type NavigationProp = NativeStackNavigationProp<
RootStackParamList,
'PaymentMethod'
>;
const ModalCard = ({
isVisible,
setIsVisible,
packId,
selectedId,
setSelectedId,
}: ModalCardViewProps) => {
const slideAnim = useRef(new Animated.Value(height)).current;
const [load, setLoad] = React.useState(false);
const [pay, setPay] = useState<string>('');
const { t } = useTranslation();
const openLink = async (url: string) => {
try {
const supported = await Linking.canOpenURL(url);
if (supported) {
await Linking.openURL(url);
} else {
// Agar app ocholmasa, default brauzerda ochishga urinadi
await Linking.openURL(url);
}
} catch (err) {
console.error('Link xatolik:', err);
// Xato bolsa ham brauzer orqali ochishga urinish
try {
await Linking.openURL(url);
} catch (err2) {
console.error('Brauzer orqali ham ochilmadi:', err2);
}
}
};
const { mutate, isPending } = useMutation({
mutationFn: ({ id, payType }: { id: number; payType: string }) =>
packetsApi.payPackets(id, { payType }),
onSuccess: res => {
setIsVisible(false);
const url = res.data.paymentUrl;
openLink(url);
},
onError: err => {
console.dir(err);
},
});
const openModal = () => {
Animated.timing(slideAnim, {
toValue: 0,
duration: 100,
useNativeDriver: true,
}).start();
};
const closeModal = () => {
Animated.timing(slideAnim, {
toValue: height,
duration: 200,
useNativeDriver: true,
}).start(() => {
setIsVisible(false);
});
};
const handlePayment = () => {
mutate({ id: packId, payType: pay });
};
useEffect(() => {
if (isVisible) openModal();
}, [isVisible]);
if (!isVisible) return null;
return (
<View style={styles.overlay}>
<TouchableWithoutFeedback onPress={closeModal}>
<View style={styles.backdrop} />
</TouchableWithoutFeedback>
<Animated.View
style={[styles.sheet, { transform: [{ translateY: slideAnim }] }]}
>
<View style={styles.sheetContent}>
{/* CLICK */}
<TouchableOpacity
style={[
styles.paymentOption,
{
backgroundColor:
selectedId === 'click' ? '#28A7E81A' : '#FFFFFF',
},
]}
onPress={() => setSelectedId('click')}
>
<View style={PaymentStyle.paymentCard}>
<Click width={80} height={80} />
</View>
<View
style={[
PaymentStyle.select,
{
backgroundColor:
selectedId === 'click' ? '#28A7E8' : '#FFFFFF',
borderColor: selectedId === 'click' ? '#28A7E8' : '#383838',
},
]}
>
{selectedId === 'click' && (
<Check color="#fff" width={20} height={20} />
)}
</View>
</TouchableOpacity>
{/* PAYME */}
<TouchableOpacity
style={[
styles.paymentOption,
{ backgroundColor: '#FFFFFF', marginBottom: 50 },
]}
onPress={() => {
setPay('PAYME'), setSelectedId('payme');
}}
>
<View style={PaymentStyle.paymentCard}>
<Payme width={80} height={80} />
</View>
<View
style={[
PaymentStyle.select,
{
backgroundColor:
selectedId === 'payme' ? '#28A7E8' : '#FFFFFF',
borderColor: selectedId === 'payme' ? '#28A7E8' : '#383838',
},
]}
>
{selectedId === 'payme' && (
<Check color="#fff" width={20} height={20} />
)}
</View>
</TouchableOpacity>
<TouchableOpacity
style={[
PaymentStyle.modalBtn,
{
margin: 'auto',
right: 0,
bottom: -20,
backgroundColor:
load || selectedId === null ? '#88888840' : '#28A7E8',
},
]}
onPress={handlePayment}
disabled={load}
>
{load || isPending ? (
<ActivityIndicator size="small" color="#fff" />
) : (
<Text
style={[
PaymentStyle.btnText,
{
color: load || selectedId === null ? '#000000b9' : '#FFFF',
},
]}
>
{t("To'lash")}
</Text>
)}
</TouchableOpacity>
</View>
</Animated.View>
</View>
);
};
const styles = StyleSheet.create({
overlay: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: 1000,
justifyContent: 'flex-end',
},
backdrop: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(0,0,0,0.4)',
},
sheet: {
width: '100%',
backgroundColor: '#F3FAFF',
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
padding: 20,
paddingBottom: 30,
},
sheetContent: {
gap: 10,
},
paymentOption: {
flexDirection: 'row',
justifyContent: 'space-between',
borderRadius: 10,
paddingLeft: 20,
paddingRight: 20,
alignItems: 'center',
},
});
export default ModalCard;