Files
info-target-mobile/components/ui/CompanyList.tsx
Samandar Turgunboyev d747c72c8d complated
2026-02-17 10:46:57 +05:00

324 lines
10 KiB
TypeScript

import { BASE_URL } from '@/api/URLs';
import { useTheme } from '@/components/ThemeContext';
import { products_api } from '@/screens/home/lib/api';
import { CompanyResponse } from '@/screens/home/lib/types';
import { BottomSheetBackdrop, BottomSheetModal, BottomSheetScrollView } from '@gorhom/bottom-sheet';
import { useInfiniteQuery } from '@tanstack/react-query';
import { Building2, ChevronRight, MapPin, Package } from 'lucide-react-native';
import React, { useCallback, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
ActivityIndicator,
Dimensions,
FlatList,
Image,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
const { width: SCREEN_WIDTH } = Dimensions.get('window');
export default function CompanyList({ query }: { query: string }) {
const { isDark } = useTheme();
const { t } = useTranslation();
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
const [selectedCompany, setSelectedCompany] = useState<CompanyResponse | null>(null);
const PAGE_SIZE = 10;
const { data, isLoading, isError, fetchNextPage, hasNextPage, isFetchingNextPage } =
useInfiniteQuery({
queryKey: ['company-list', query],
queryFn: async ({ pageParam = 1 }) => {
const response = await products_api.getCompany({
page: pageParam,
page_size: PAGE_SIZE,
search: query,
});
return response.data.data;
},
getNextPageParam: (lastPage) =>
lastPage.current_page < lastPage.total_pages ? lastPage.current_page + 1 : undefined,
initialPageParam: 1,
});
const allCompanies = data?.pages
.flatMap((page) => page.results)
.filter((company) => company.company_name);
const handlePresentModal = useCallback((company: CompanyResponse) => {
setSelectedCompany(company);
bottomSheetModalRef.current?.present();
}, []);
const renderBackdrop = useCallback(
(props: any) => (
<BottomSheetBackdrop {...props} disappearsOnIndex={-1} appearsOnIndex={0} opacity={0.5} />
),
[]
);
if (isLoading) {
return (
<View style={styles.center}>
<ActivityIndicator size="large" color="#3b82f6" />
</View>
);
}
if (isError) {
return (
<View style={styles.center}>
<Text style={{ color: '#f87171' }}>{t('Xatolik yuz berdi')}</Text>
</View>
);
}
return (
<>
<FlatList
data={allCompanies}
keyExtractor={(item) => item.id.toString()}
contentContainerStyle={{ paddingBottom: 70 }}
renderItem={({ item }) => (
<TouchableOpacity
style={[styles.card, isDark ? styles.darkCard : styles.lightCard]}
activeOpacity={0.8}
onPress={() => handlePresentModal(item)}
>
<View style={styles.cardHeader}>
<Building2 size={22} color="#3b82f6" />
<Text style={[styles.cardTitle, isDark ? styles.darkText : styles.lightText]}>
{item.company_name}
</Text>
<ChevronRight size={20} color={isDark ? '#64748b' : '#94a3b8'} />
</View>
{item.country_name && (
<View style={styles.cardLocation}>
<MapPin size={16} color={isDark ? '#64748b' : '#94a3b8'} />
<Text
style={[
styles.cardLocationText,
isDark ? styles.darkSubText : styles.lightSubText,
]}
>
{item.country_name}, {item.region_name}
</Text>
</View>
)}
</TouchableOpacity>
)}
onEndReached={() => hasNextPage && !isFetchingNextPage && fetchNextPage()}
onEndReachedThreshold={0.4}
ListFooterComponent={
isFetchingNextPage ? <ActivityIndicator color="#3b82f6" style={{ margin: 20 }} /> : null
}
showsVerticalScrollIndicator={false}
/>
<BottomSheetModal
ref={bottomSheetModalRef}
index={0}
snapPoints={['60%', '90%']}
backdropComponent={renderBackdrop}
handleIndicatorStyle={{ backgroundColor: '#94a3b8', width: 50 }}
backgroundStyle={{
backgroundColor: isDark ? '#0f172a' : '#ffffff',
borderRadius: 24,
}}
enablePanDownToClose
>
<BottomSheetScrollView contentContainerStyle={styles.sheetContent}>
{selectedCompany && (
<>
<View style={styles.sheetHeader}>
<Building2 size={28} color="#3b82f6" />
<Text style={[styles.sheetTitle, isDark ? styles.darkText : styles.lightText]}>
{selectedCompany.company_name}
</Text>
<View style={styles.sheetLocation}>
<MapPin size={16} color={isDark ? '#64748b' : '#94a3b8'} />
<Text
style={[
styles.sheetLocationText,
isDark ? styles.darkSubText : styles.lightSubText,
]}
>
{selectedCompany.country_name}, {selectedCompany.region_name},{' '}
{selectedCompany.district_name}
</Text>
</View>
</View>
<View style={[styles.divider, isDark ? styles.darkDivider : styles.lightDivider]} />
{selectedCompany.product_service_company &&
selectedCompany.product_service_company.length > 0 && (
<>
<Text
style={[
styles.sectionLabel,
isDark ? styles.darkSubText : styles.lightSubText,
]}
>
{t('Mahsulotlar')} ({selectedCompany.product_service_company.length})
</Text>
{selectedCompany.product_service_company.map((product) => (
<View
key={product.id}
style={[
styles.productCard,
isDark ? styles.darkProductCard : styles.lightProductCard,
]}
>
<View
style={[
styles.productImage,
isDark ? styles.darkProductImage : styles.lightProductImage,
]}
>
{product.files?.[0]?.file ? (
<Image
source={{ uri: BASE_URL + product.files[0].file }}
style={styles.productImg}
/>
) : (
<Package size={28} color={isDark ? '#64748b' : '#94a3b8'} />
)}
</View>
<View style={styles.productInfo}>
<Text
style={[
styles.productTitle,
isDark ? styles.darkText : styles.lightText,
]}
>
{product.title}
</Text>
<Text
style={[
styles.productDesc,
isDark ? styles.darkSubText : styles.lightSubText,
]}
>
{product.description || 'Tavsif mavjud emas.'}
</Text>
</View>
</View>
))}
</>
)}
</>
)}
</BottomSheetScrollView>
</BottomSheetModal>
</>
);
}
const CARD_WIDTH = SCREEN_WIDTH - 32;
const styles = StyleSheet.create({
center: { flex: 1, justifyContent: 'center', alignItems: 'center' },
// --- Company Card ---
card: {
width: CARD_WIDTH - 4,
marginLeft: 2,
borderRadius: 16,
padding: 16,
marginBottom: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.2,
shadowRadius: 8,
elevation: 2,
},
darkCard: {
backgroundColor: '#1e293b',
},
lightCard: {
backgroundColor: '#ffffff',
},
cardHeader: { flexDirection: 'row', alignItems: 'center', gap: 10, marginBottom: 6 },
cardTitle: { fontSize: 17, fontWeight: '700', flex: 1 },
darkText: {
color: '#f1f5f9',
},
lightText: {
color: '#0f172a',
},
cardLocation: { flexDirection: 'row', alignItems: 'center', gap: 6 },
cardLocationText: { fontSize: 13 },
darkSubText: {
color: '#64748b',
},
lightSubText: {
color: '#94a3b8',
},
// --- Bottom Sheet ---
sheetContent: { padding: 20, paddingBottom: 40 },
sheetHeader: { alignItems: 'center', marginBottom: 20 },
sheetTitle: {
fontSize: 22,
fontWeight: '800',
marginTop: 8,
textAlign: 'center',
},
sheetLocation: { flexDirection: 'row', alignItems: 'center', gap: 6, marginTop: 6 },
sheetLocationText: { fontSize: 14 },
divider: { height: 1, marginBottom: 16 },
darkDivider: {
backgroundColor: '#334155',
},
lightDivider: {
backgroundColor: '#e2e8f0',
},
sectionLabel: {
fontSize: 14,
fontWeight: '700',
textTransform: 'uppercase',
marginBottom: 12,
},
// --- Product Card ---
productCard: {
flexDirection: 'row',
borderRadius: 12,
padding: 16,
marginBottom: 12,
gap: 16,
alignItems: 'flex-start',
},
darkProductCard: {
backgroundColor: '#1e293b',
},
lightProductCard: {
backgroundColor: '#f8fafc',
},
productImage: {
width: 60,
height: 60,
borderRadius: 8,
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
borderWidth: 1,
},
darkProductImage: {
backgroundColor: '#0f172a',
borderColor: '#334155',
},
lightProductImage: {
backgroundColor: '#ffffff',
borderColor: '#e2e8f0',
},
productImg: { width: '100%', height: '100%', borderRadius: 8 },
productInfo: { flex: 1 },
productTitle: { fontSize: 15, fontWeight: '700' },
productDesc: { fontSize: 13, marginTop: 2 },
});