Files
info-target-mobile/screens/create-ads/ui/CreateAdsScreens.tsx
Samandar Turgunboyev ab363ca3b9 bug fixed
2026-03-02 13:22:55 +05:00

406 lines
11 KiB
TypeScript

import { useTheme } from '@/components/ThemeContext';
import { BottomSheetBackdrop, BottomSheetModal, BottomSheetScrollView } from '@gorhom/bottom-sheet';
import { useMutation, useQuery } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { router, useFocusEffect } from 'expo-router';
import React, { useCallback, useRef, useState } from 'react';
import {
Alert,
Image,
KeyboardAvoidingView,
Linking,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import OneClick from '@/assets/images/one_click.png';
import PAYME from '@/assets/images/Payme_NEW.png';
import { useTranslation } from 'react-i18next';
import { price_calculation } from '../lib/api';
import { CreateAdsResponse } from '../lib/types';
import StepFour from './StepFour';
import StepOne from './StepOne';
import StepThree from './StepThree';
import StepTwo from './StepTwo';
type MediaFile = {
uri: string;
type: string;
name: string;
};
interface formDataType {
title: string;
description: string;
phone: string;
media: MediaFile[];
category: any[];
country: string;
region: string;
district: string;
company: any[];
}
const getMimeType = (uri: string) => {
const ext = uri.split('.').pop()?.toLowerCase();
switch (ext) {
case 'jpg':
case 'jpeg':
return 'image/jpeg';
case 'png':
return 'image/png';
case 'webp':
return 'image/webp';
case 'heic':
return 'image/heic';
default:
return 'application/octet-stream';
}
};
export default function CreateAdsScreens() {
const { isDark } = useTheme();
const { t } = useTranslation();
const [paymentType, setPaymentType] = useState<'PAYME' | 'REFFERAL'>('PAYME');
const [ads, setAds] = useState<CreateAdsResponse | null>(null);
const [currentStep, setCurrentStep] = useState(1);
const stepOneRef = useRef<{ validate: () => boolean } | null>(null);
const stepTwoRef = useRef<{ validate: () => boolean } | null>(null);
const stepThreeRef = useRef<{ validate: () => boolean } | null>(null);
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
const renderBackdrop = useCallback(
(props: any) => (
<BottomSheetBackdrop {...props} disappearsOnIndex={-1} appearsOnIndex={0} opacity={0.5} />
),
[]
);
const [formData, setFormData] = useState<formDataType>({
title: '',
description: '',
phone: '',
media: [],
category: [],
country: '',
region: '',
district: 'all',
company: [],
});
useFocusEffect(
useCallback(() => {
return () => {
setFormData({
title: '',
description: '',
phone: '',
media: [],
category: [],
country: '',
region: '',
district: 'all',
company: [],
});
setCurrentStep(1);
};
}, [])
);
const { data } = useQuery({
queryKey: ['price-calculation', formData],
queryFn: () =>
price_calculation.calculation({
country: formData.country,
district: formData.district,
region: formData.region,
types: formData.category.map((c: any) => c.id).join(','),
letters: formData.company.map((c: any) => c.latter).join(','),
}),
enabled: formData.company.length > 0,
});
const updateForm = (key: string, value: any) => setFormData((p) => ({ ...p, [key]: value }));
const { mutate, isPending } = useMutation({
mutationFn: (body: FormData) => price_calculation.ad(body),
onSuccess: (res) => {
setAds(res.data);
setCurrentStep(4);
},
onError: (err: AxiosError) => {
Alert.alert('Xatolik', err.message);
},
});
const handleSubmit = () => {
const form = new FormData();
form.append('title', formData.title);
form.append('description', formData.description);
formData.media.forEach((file, index) => {
form.append(`files[${index}]`, {
uri: file.uri,
type: getMimeType(file.uri),
name: file.uri.split('/').pop(),
} as any);
});
formData.category.forEach((e, index) => {
form.append(`types[${index}]`, e.id);
});
if (data) {
form.append('total_price', data.data.total_price.toString());
}
form.append('phone_number', `998${formData.phone}`);
const letters = formData.company.map((c: any) => c.latter).join(',');
form.append('company_selections[0]continent', 'Asia');
form.append('company_selections[0]country', formData.country);
form.append('company_selections[0]region', formData.region);
form.append('company_selections[0]district', formData.district);
form.append('company_selections[0]letters', letters);
form.append('company_selections[0]total_companies', data?.data.user_count.toString() || '0');
form.append('company_selections[0]ad_price', data?.data.one_person_price.toString() || '0');
form.append('company_selections[0]total_price', data?.data.total_price.toString() || '0');
mutate(form);
};
const handlePresentModalPress = useCallback(() => {
bottomSheetModalRef.current?.present();
}, []);
const { mutate: payment } = useMutation({
mutationFn: (body: { return_url: string; adId: number; paymentType: 'payme' | 'referral' }) =>
price_calculation.payment(body),
onSuccess: async (res, variables) => {
if (variables.paymentType === 'payme') {
await Linking.openURL(res.data.url);
router.push('/(dashboard)/announcements');
} else {
router.push('/(dashboard)/announcements');
}
},
onError: (err: AxiosError) => {
const errMessage = (err.response?.data as { referral_amount: string }).referral_amount
Alert.alert(t('Xatolik yuz berdi'), errMessage || err.message);
},
});
const sendPayment = (type: 'payme' | 'referral') => {
if (ads) {
payment({
adId: ads.data.id,
paymentType: type,
return_url: 'https://infotarget.uz/en/main/dashboard',
});
}
};
return (
<KeyboardAvoidingView
behavior="padding"
style={[styles.safeArea, isDark ? styles.darkBg : styles.lightBg]}
>
<ScrollView contentContainerStyle={[styles.container, { paddingBottom: 90 }]}>
<Image
source={OneClick}
style={{ width: 180, height: 56, marginBottom: 10 }}
resizeMode="contain"
/>
<Text style={[styles.title, isDark ? styles.darkText : styles.lightText]}>
{t("Bir Zumda Jonatish")}
</Text>
{currentStep === 1 && (
<StepOne ref={stepOneRef} formData={formData} updateForm={updateForm} />
)}
{currentStep === 2 && (
<StepTwo ref={stepTwoRef} formData={formData} updateForm={updateForm} />
)}
{currentStep === 3 && (
<StepThree
ref={stepThreeRef}
formData={formData}
updateForm={updateForm}
data={data?.data}
/>
)}
{currentStep === 4 && <StepFour data={ads} setPayment={setPaymentType} />}
<View style={styles.footer}>
{currentStep > 1 && currentStep !== 4 && (
<TouchableOpacity
style={[styles.back, isDark ? styles.darkBack : styles.lightBack]}
onPress={() => setCurrentStep((s) => s - 1)}
>
<Text style={[styles.btnText, isDark ? styles.darkBtnText : styles.lightBtnText]}>
{t('Orqaga')}
</Text>
</TouchableOpacity>
)}
<TouchableOpacity
style={styles.next}
disabled={isPending}
onPress={() => {
let isValid = true;
if (currentStep === 1) isValid = stepOneRef.current?.validate() ?? false;
if (currentStep === 2) isValid = stepTwoRef.current?.validate() ?? false;
if (currentStep === 3) isValid = stepThreeRef.current?.validate() ?? false;
if (!isValid) return;
if (currentStep < 3) setCurrentStep((s) => s + 1);
if (currentStep === 3) handleSubmit();
if (currentStep === 4) handlePresentModalPress();
}}
>
<Text style={styles.btnText}>
{currentStep === 3
? t('Yaratish')
: currentStep === 4
? t("To'lash")
: t('Keyingisi')}
</Text>
</TouchableOpacity>
</View>
</ScrollView>
{/* FOOTER */}
{/* PAYMENT BOTTOM SHEET */}
<BottomSheetModal
ref={bottomSheetModalRef}
index={0}
snapPoints={['70%', '95%']}
backdropComponent={renderBackdrop}
handleIndicatorStyle={{ backgroundColor: '#94a3b8', width: 50 }}
backgroundStyle={{ backgroundColor: isDark ? '#0f172a' : '#ffffff' }}
enablePanDownToClose
>
<BottomSheetScrollView
style={styles.sheetContent}
contentContainerStyle={styles.sheetContentContainer}
>
<View style={{ padding: 20 }}>
<Text style={[styles.sheetTitle, isDark ? styles.darkText : styles.lightText]}>
{t("To'lov turini tanlang")}
</Text>
<TouchableOpacity
style={[
styles.paymentItem,
isDark ? styles.darkPaymentItem : styles.lightPaymentItem,
{ flexDirection: 'row', justifyContent: 'flex-start', alignItems: 'center' },
]}
onPress={() => sendPayment('payme')}
>
<Image source={PAYME} style={{ width: 80, height: 80 }} />
</TouchableOpacity>
<TouchableOpacity
style={[
styles.paymentItem,
isDark ? styles.darkPaymentItem : styles.lightPaymentItem,
]}
onPress={() => sendPayment('referral')}
>
<Text style={[styles.paymentText, isDark ? styles.darkText : styles.lightText]}>
{t('Referal orqali')}
</Text>
</TouchableOpacity>
</View>
</BottomSheetScrollView>
</BottomSheetModal>
</KeyboardAvoidingView>
);
}
const styles = StyleSheet.create({
safeArea: { flex: 1 },
darkBg: {
backgroundColor: '#0f172a',
},
lightBg: {
backgroundColor: '#f8fafc',
},
container: { padding: 20 },
title: { fontSize: 18, fontWeight: '800', marginBottom: 20 },
darkText: {
color: '#f1f5f9',
},
lightText: {
color: '#0f172a',
},
footer: {
marginTop: 10,
gap: 5,
},
back: {
flex: 1,
height: 56,
borderRadius: 16,
justifyContent: 'center',
alignItems: 'center',
},
darkBack: {
backgroundColor: '#1e293b',
},
lightBack: {
backgroundColor: '#ffffff',
borderWidth: 1,
borderColor: '#e2e8f0',
},
sheetContent: { flex: 1 },
sheetContentContainer: { paddingBottom: 40 },
next: {
flex: 2,
height: 56,
backgroundColor: '#3b82f6',
borderRadius: 16,
justifyContent: 'center',
alignItems: 'center',
},
btnText: { color: '#ffffff', fontWeight: '700', fontSize: 16 },
darkBtnText: {
color: '#f1f5f9',
},
lightBtnText: {
color: '#0f172a',
},
sheetTitle: {
fontSize: 18,
fontWeight: '700',
marginBottom: 16,
},
paymentItem: {
height: 56,
borderRadius: 14,
justifyContent: 'center',
paddingHorizontal: 16,
marginBottom: 12,
},
darkPaymentItem: {
backgroundColor: '#1e293b',
},
lightPaymentItem: {
backgroundColor: '#f8fafc',
},
paymentText: {
fontSize: 16,
fontWeight: '600',
},
});