Files
info-tager-mobile/components/ui/IndustrySelection.tsx
Samandar Turgunboyev 124798419b fitst commit
2026-01-28 18:26:50 +05:00

237 lines
6.3 KiB
TypeScript

import { products_api } from '@/screens/home/lib/api';
import { useMutation, useQuery } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { ChevronLeft, ChevronRight } from 'lucide-react-native';
import React, { Dispatch, SetStateAction, useState } from 'react';
import {
ActivityIndicator,
FlatList,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import { useTheme } from '../ThemeContext';
interface Category {
id: number;
name: string;
code: string;
external_id: string | null;
level: number;
is_leaf: boolean;
icon_name: string | null;
}
interface CategoryResponse {
data: {
data: Category[];
};
}
interface Props {
selectedCategories: Category[];
setSelectedCategories: Dispatch<SetStateAction<Category[]>>;
}
export default function CategorySelection({ selectedCategories, setSelectedCategories }: Props) {
const [currentCategories, setCurrentCategories] = useState<Category[]>([]);
const [history, setHistory] = useState<{ parentId: number | null; categories: Category[] }[]>([]);
const [currentParentId, setCurrentParentId] = useState<number | null>(null);
const { isDark } = useTheme();
const theme = {
cardBg: isDark ? '#1e293b' : '#f8fafc',
cardBorder: isDark ? '#334155' : '#e2e8f0',
text: isDark ? '#cbd5e1' : '#334155',
textSelected: '#ffffff',
primary: '#2563eb',
primaryBg: '#3b82f6',
error: '#ef4444',
backButtonBg: isDark ? '#1e293b' : '#e2e8f0',
chevronColor: isDark ? '#94a3b8' : '#64748b',
shadow: isDark ? '#000' : '#94a3b8',
};
const {
data: rootData,
isLoading: rootLoading,
error: rootError,
} = useQuery<CategoryResponse>({
queryKey: ['categories'],
queryFn: async () => products_api.getCategorys(),
select(data) {
setCurrentCategories(data.data.data);
setCurrentParentId(null);
return data;
},
});
const { mutate, isPending: mutatePending } = useMutation({
mutationFn: (id: number) => products_api.getCategorys({ parent: id }),
onSuccess: (response: CategoryResponse, id) => {
const childCategories = response.data.data;
setHistory((prev) => [...prev, { parentId: currentParentId, categories: currentCategories }]);
setCurrentCategories(childCategories);
setCurrentParentId(id);
},
onError: (err: AxiosError) => {
console.error('Child yuklashda xato:', err);
},
});
const toggleCategory = (category: Category) => {
if (category.is_leaf) {
setSelectedCategories((prev) => {
const exists = prev.find((c) => c.id === category.id);
if (exists) return prev.filter((c) => c.id !== category.id);
return [...prev, category];
});
} else {
mutate(category.id);
}
};
const goBack = () => {
if (history.length > 0) {
const previous = history[history.length - 1];
setCurrentCategories(previous.categories);
setCurrentParentId(previous.parentId);
setHistory((prev) => prev.slice(0, -1));
}
};
const isLoading = rootLoading || mutatePending;
const error = rootError;
const renderCategory = ({ item: category }: { item: Category }) => {
const isSelected = selectedCategories.some((c) => c.id === category.id);
return (
<TouchableOpacity
style={[
styles.chip,
{
backgroundColor: isSelected ? theme.primaryBg : theme.cardBg,
borderColor: isSelected ? theme.primaryBg : theme.cardBorder,
shadowColor: isSelected ? theme.primaryBg : theme.shadow,
},
isSelected && styles.chipSelected,
]}
onPress={() => toggleCategory(category)}
>
<Text
style={[
styles.chipText,
{ color: isSelected ? theme.textSelected : theme.text },
isSelected && styles.chipTextSelected,
]}
>
{category.name}
</Text>
{!category.is_leaf && (
<ChevronRight size={20} color={isSelected ? theme.textSelected : theme.chevronColor} />
)}
</TouchableOpacity>
);
};
if (isLoading && currentCategories.length === 0) {
return (
<View style={[styles.centerContainer]}>
<ActivityIndicator size="small" color={theme.primary} />
</View>
);
}
if (error && currentCategories.length === 0) {
return (
<View style={[styles.centerContainer]}>
<Text style={{ color: theme.error }}>Ma'lumot yuklashda xatolik yuz berdi</Text>
</View>
);
}
if (currentCategories.length === 0) {
return (
<View style={[styles.centerContainer]}>
<Text style={{ color: theme.text }}>Kategoriyalar topilmadi</Text>
</View>
);
}
return (
<View style={[styles.container]}>
<View style={styles.header}>
{history.length > 0 && (
<TouchableOpacity
onPress={goBack}
style={[styles.backButton, { backgroundColor: theme.backButtonBg }]}
>
<ChevronLeft color={theme.primary} size={20} />
</TouchableOpacity>
)}
</View>
<FlatList
data={currentCategories}
renderItem={renderCategory}
scrollEnabled={false}
keyExtractor={(item) => item.id.toString()}
showsVerticalScrollIndicator={true}
ItemSeparatorComponent={() => <View style={{ height: 10 }} />}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginBottom: 20,
},
centerContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
header: {
flexDirection: 'row',
alignItems: 'center',
gap: 12,
marginBottom: 12,
paddingHorizontal: 12,
},
backButton: {
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 8,
},
chip: {
paddingHorizontal: 16,
paddingVertical: 12,
borderRadius: 20,
borderWidth: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 2,
elevation: 1,
},
chipSelected: {
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3,
shadowRadius: 4,
elevation: 3,
},
chipText: {
fontSize: 14,
fontWeight: '500',
flex: 1,
},
chipTextSelected: {
fontWeight: '600',
},
});