fitst commit

This commit is contained in:
Samandar Turgunboyev
2026-01-28 18:26:50 +05:00
parent 166a55b1e9
commit 124798419b
196 changed files with 26627 additions and 421 deletions

View File

@@ -0,0 +1,263 @@
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,
Alert,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
interface Category {
id: number;
name: string;
is_leaf: boolean;
}
export default function CategorySelectScreen() {
const router = useRouter();
const { t } = useTranslation();
const { phone, stir, person_type } = useLocalSearchParams<{
phone: string;
stir: string;
person_type: 'band' | 'ytt';
}>();
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[];
}) => auth_api.register(body),
onSuccess: async () => {
router.replace('/(auth)/register-confirm');
await AsyncStorage.setItem('phone', phone);
},
onError: (err: AxiosError) => {
const errMessage = (err.response?.data as { data: { stir: string[] } }).data.stir[0];
const errMessageDetail = (err.response?.data as { data: { detail: string } }).data.detail;
const errrAlert = errMessage ? errMessage : errMessageDetail;
Alert.alert(t('Xatolik yuz berdi'), errMessage || errrAlert || t('erroXatolik yuz berdi'));
},
});
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 (
<SafeAreaView 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,
});
}}
>
<Text style={styles.bottomText} disabled={registerMutation.isPending}>
{t('Tadiqlash')}
</Text>
</TouchableOpacity>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
title: {
fontSize: 18,
fontWeight: '700',
color: '#ffffff',
marginBottom: 12,
},
safeArea: {
flex: 1,
backgroundColor: '#0f172a',
},
container: {
paddingHorizontal: 20,
paddingTop: 16,
paddingBottom: 70,
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)',
},
});