Files
info-target-mobile/app/(auth)/select-category.tsx
Samandar Turgunboyev a671706fb3 register address
2026-03-26 14:23:58 +05:00

328 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import AuthHeader from '@/components/ui/AuthHeader';
import { auth_api } from '@/screens/auth/login/lib/api';
import { products_api } from '@/screens/home/lib/api';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useMutation, useQuery } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { LinearGradient } from 'expo-linear-gradient';
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
import { ChevronLeft } from 'lucide-react-native';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
ActivityIndicator,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View
} from 'react-native';
import { Toast } from 'toastify-react-native';
interface Category {
id: number;
name: string;
is_leaf: boolean;
}
type DRFError = {
[key: string]: Array<
| string
| {
[key: string]: string[];
}
>;
};
export default function CategorySelectScreen() {
const router = useRouter();
const { t } = useTranslation();
const { phone, stir, person_type, referal, director_full_name, first_name, last_name, district, company_name, address } = useLocalSearchParams<{
phone: string;
stir: string;
person_type: 'band' | 'ytt' | "legal_entity";
referal: string;
director_full_name: string;
first_name: string;
last_name: string;
district: string
address: string;
company_name: string;
}>();
const [selected, setSelected] = useState<number | null>(null);
const [categories, setCategories] = useState<Category[]>([]);
const [history, setHistory] = useState<Category[][]>([]);
/** ROOT categories */
const { isLoading } = useQuery({
queryKey: ['categories-root'],
queryFn: () => products_api.getCategorys(),
select: (res) => {
setCategories(res.data.data);
},
});
/** CHILD categories */
const childMutation = useMutation({
mutationFn: (id: number) => products_api.getCategorys({ parent: id }),
onSuccess: (res) => {
setHistory((prev) => [...prev, categories]);
setCategories(res.data.data);
},
});
/** REGISTER */
const registerMutation = useMutation({
mutationFn: (body: {
phone: string;
stir: string;
person_type: string;
activate_types: number[];
director_full_name: string;
referral: string;
first_name: string;
last_name: string;
district: string;
company_name: string;
address: string;
}) => auth_api.register(body),
onSuccess: async () => {
router.replace('/(auth)/register-confirm');
await AsyncStorage.setItem('phone', phone);
},
onError: (error: AxiosError<DRFError>) => {
const data = error.response?.data as any;
let message = t('Xatolik yuz berdi');
if (data) {
const source = data.data || data; // 🔥 ba'zida data ichida keladi
if (typeof source === 'object') {
const firstKey = Object.keys(source)[0];
const firstValue = source[firstKey];
// ✅ 1⃣ Agar oddiy string array bolsa
if (Array.isArray(firstValue) && typeof firstValue[0] === 'string') {
message = firstValue[0];
}
// ✅ 2⃣ Agar nested object bolsa
else if (Array.isArray(firstValue) && typeof firstValue[0] === 'object') {
const firstErrorObj = firstValue[0];
const innerKey = Object.keys(firstErrorObj)[0];
const innerValue = firstErrorObj[innerKey];
if (Array.isArray(innerValue)) {
message = innerValue[0];
}
}
// ✅ 3⃣ Agar string bolsa
else if (typeof firstValue === 'string') {
message = firstValue;
}
}
}
// ❗ Network error fallback
if (!error.response) {
message = error.message;
}
Toast.error(message);
}
});
const onCategoryPress = (cat: Category) => {
if (cat.is_leaf) {
setSelected(cat.id);
} else {
childMutation.mutate(cat.id);
}
};
const goBack = () => {
if (history.length === 0) return;
const prev = history[history.length - 1];
setCategories(prev);
setHistory((h) => h.slice(0, -1));
setSelected(null);
};
return (
<View style={styles.safeArea}>
<AuthHeader />
<Stack.Screen options={{ title: t('Yonalishni tanlang') }} />
<LinearGradient colors={['#0f172a', '#1e293b']} style={StyleSheet.absoluteFill} />
<ScrollView contentContainerStyle={styles.container}>
{history.length > 0 && (
<TouchableOpacity onPress={goBack} style={styles.backBtn}>
<ChevronLeft size={20} color="#3b82f6" />
<Text style={styles.backText}>{t('Orqaga')}</Text>
</TouchableOpacity>
)}
<Text style={styles.title}>{t("Yo'nalishni tanlang")}</Text>
{isLoading || childMutation.isPending ? (
<ActivityIndicator color="#3b82f6" />
) : (
categories.map((c) => {
const active = selected === c.id;
return (
<TouchableOpacity
key={c.id}
style={[styles.item, active && styles.itemActive]}
onPress={() => onCategoryPress(c)}
>
<Text style={[styles.text, active && styles.textActive]}>{c.name}</Text>
</TouchableOpacity>
);
})
)}
</ScrollView>
<TouchableOpacity
disabled={!selected || registerMutation.isPending}
style={[styles.bottom, (!selected || registerMutation.isPending) && styles.bottomDisabled]}
onPress={() => {
if (!selected) return;
registerMutation.mutate({
activate_types: [selected],
person_type,
phone: `998${phone}`,
stir,
referral: referal,
director_full_name,
first_name,
last_name,
district: district,
address: address,
company_name
});
}}
>
<Text style={styles.bottomText} disabled={registerMutation.isPending}>
{t('Tadiqlash')}
</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
title: {
fontSize: 18,
fontWeight: '700',
color: '#ffffff',
marginBottom: 12,
},
safeArea: {
flex: 1,
backgroundColor: '#0f172a',
},
container: {
paddingHorizontal: 20,
paddingTop: 16,
paddingBottom: 90,
gap: 12,
},
backBtn: {
flexDirection: 'row',
alignItems: 'center',
gap: 6,
marginBottom: 12,
},
backText: {
fontSize: 14,
color: '#3b82f6',
fontWeight: '600',
},
item: {
paddingVertical: 18,
paddingHorizontal: 18,
borderRadius: 16,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor: 'rgba(255,255,255,0.06)',
borderWidth: 1,
borderColor: 'rgba(255,255,255,0.12)',
},
itemActive: {
backgroundColor: 'rgba(59,130,246,0.15)',
borderColor: 'rgba(59,130,246,0.6)',
},
text: {
fontSize: 15,
fontWeight: '600',
color: '#cbd5f5',
},
textActive: {
color: '#ffffff',
fontWeight: '800',
},
arrow: {
fontSize: 18,
color: '#94a3b8',
},
bottom: {
position: 'absolute',
bottom: 20,
left: 16,
right: 16,
height: 54,
borderRadius: 16,
backgroundColor: '#3b82f6',
alignItems: 'center',
justifyContent: 'center',
shadowColor: '#3b82f6',
shadowOffset: { width: 0, height: 8 },
shadowOpacity: 0.35,
shadowRadius: 12,
elevation: 10,
},
bottomDisabled: {
backgroundColor: '#64748b',
},
bottomText: {
color: '#ffffff',
fontWeight: '800',
fontSize: 16,
},
decorCircle1: {
position: 'absolute',
top: -120,
right: -80,
width: 300,
height: 300,
borderRadius: 150,
backgroundColor: 'rgba(59,130,246,0.12)',
},
decorCircle2: {
position: 'absolute',
bottom: -120,
left: -100,
width: 280,
height: 280,
borderRadius: 140,
backgroundColor: 'rgba(16,185,129,0.1)',
},
});