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

204 lines
6.2 KiB
TypeScript

import { useTheme } from '@/components/ThemeContext';
import { formatNumber } from '@/constants/formatPhone';
import { useInfiniteQuery } from '@tanstack/react-query';
import { useRouter } from 'expo-router';
import { ArrowLeft, Plus, User } from 'lucide-react-native';
import { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
ActivityIndicator,
FlatList,
Pressable,
RefreshControl,
StyleSheet,
Text,
View,
} from 'react-native';
import { user_api } from '../lib/api';
import { ExployeesDataResponse } from '../lib/type';
const PAGE_SIZE = 20;
export function EmployeesTab() {
const router = useRouter();
const [refreshing, setRefreshing] = useState(false);
const { isDark } = useTheme();
const { t } = useTranslation();
const theme = {
background: isDark ? '#0f172a' : '#f8fafc',
cardBg: isDark ? '#1e293b' : '#ffffff',
text: isDark ? '#f8fafc' : '#0f172a',
textSecondary: isDark ? '#94a3b8' : '#64748b',
iconBg: isDark ? '#1e40af15' : '#dbeafe',
primary: '#3b82f6',
emptyIcon: isDark ? '#334155' : '#cbd5e1',
};
const { data, isLoading, isError, fetchNextPage, hasNextPage, isFetchingNextPage, refetch } =
useInfiniteQuery({
queryKey: ['employees_list'],
queryFn: async ({ pageParam = 1 }) => {
const response = await user_api.employess({
page: pageParam,
page_size: PAGE_SIZE,
});
return response.data.data;
},
getNextPageParam: (lastPage) =>
lastPage && lastPage.current_page && lastPage.total_pages
? lastPage.current_page < lastPage.total_pages
? lastPage.current_page + 1
: undefined
: undefined,
initialPageParam: 1,
});
const allEmployees = data?.pages.flatMap((p) => p.results) ?? [];
const renderItem = useCallback(
({ item }: { item: ExployeesDataResponse }) => (
<View style={[styles.card, { backgroundColor: theme.cardBg }]}>
<View style={[styles.iconContainer, { backgroundColor: theme.iconBg }]}>
<User size={24} color={theme.primary} />
</View>
<View style={styles.infoContainer}>
<Text style={[styles.name, { color: theme.text }]}>
{item.first_name} {item.last_name}
</Text>
<Text style={[styles.phone, { color: theme.textSecondary }]}>
{formatNumber(item.phone)}
</Text>
</View>
</View>
),
[theme]
);
const onRefresh = async () => {
setRefreshing(true);
await refetch();
setRefreshing(false);
};
if (isLoading) {
return (
<View style={[styles.container, { backgroundColor: theme.background }]}>
<View style={styles.topHeader}>
<Pressable onPress={() => router.push('/profile')}>
<ArrowLeft color={theme.text} />
</Pressable>
<Text style={[styles.headerTitle, { color: theme.text }]}>{t('Xodimlar')}</Text>
<Pressable onPress={() => router.push('/profile/employees/add')}>
<Plus color={theme.primary} />
</Pressable>
</View>
<ActivityIndicator size={'large'} />
</View>
);
}
if (isError) {
return (
<View style={[styles.container, { backgroundColor: theme.background }]}>
<Text style={{ color: 'red', marginTop: 50, textAlign: 'center' }}>
{t('Xatolik yuz berdi')}
</Text>
</View>
);
}
return (
<View style={[styles.container, { backgroundColor: theme.background }]}>
<View style={styles.topHeader}>
<Pressable onPress={() => router.push('/profile')}>
<ArrowLeft color={theme.text} />
</Pressable>
<Text style={[styles.headerTitle, { color: theme.text }]}>{t('Xodimlar')}</Text>
<Pressable onPress={() => router.push('/profile/employees/add')}>
<Plus color={theme.primary} />
</Pressable>
</View>
<FlatList
data={allEmployees}
keyExtractor={(item) => item.phone}
renderItem={renderItem}
contentContainerStyle={[styles.list, { flexGrow: 1 }]}
onEndReached={() => {
if (hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
}}
onEndReachedThreshold={0.5}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor={theme.primary} />
}
ListFooterComponent={isFetchingNextPage ? <ActivityIndicator size={'large'} /> : null}
ListEmptyComponent={
<View style={styles.emptyContainer}>
<User size={64} color={theme.emptyIcon} />
<Text style={[styles.emptyText, { color: theme.textSecondary }]}>
{t("Hozircha xodimlar yo'q")}
</Text>
<Pressable
style={[styles.emptyButton, { backgroundColor: theme.primary }]}
onPress={() => router.push('/profile/employees/add')}
>
<Plus size={20} color="#fff" />
<Text style={styles.emptyButtonText}>{t("Xodim qo'shish")}</Text>
</Pressable>
</View>
}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
addButton: { padding: 8 },
list: { padding: 16, gap: 12, paddingBottom: 30 },
card: {
flexDirection: 'row',
alignItems: 'center',
borderRadius: 16,
padding: 16,
gap: 12,
},
iconContainer: {
width: 56,
height: 56,
borderRadius: 28,
alignItems: 'center',
justifyContent: 'center',
},
infoContainer: { flex: 1, gap: 4 },
name: { fontSize: 17, fontWeight: '700' },
phone: { fontSize: 15, fontWeight: '500' },
emptyContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 60,
},
emptyText: { fontSize: 17, fontWeight: '600' },
emptyButton: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 12,
marginTop: 8,
},
emptyButtonText: { fontSize: 16, fontWeight: '600', color: '#fff' },
topHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
padding: 16,
alignItems: 'center',
},
headerTitle: { fontSize: 18, fontWeight: '700' },
});