175 lines
5.5 KiB
TypeScript

import React, { useEffect, useMemo, useState } from 'react';
import { Text, View, FlatList, Alert } from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { getStyles } from './customers.styles';
import { useTheme } from '../../theme';
import { useAppDispatch, useAppSelector, RootState } from '@store';
import { getCustomers, deleteCustomer, resetCustomersState } from './thunk';
import { CustomerItem } from '@interfaces';
import { SearchInput, Loader, StatCard, CustomerItemCard } from '@components';
import { CustomersStackParamList } from '../../navigation/customersStack';
import { route } from '@utils';
type CustomersScreenNavigationProp = NativeStackNavigationProp<
CustomersStackParamList,
'customersList'
>;
export const CustomersScreen = () => {
const { theme: colors } = useTheme();
const styles = getStyles(colors);
const dispatch = useAppDispatch();
const navigation = useNavigation<CustomersScreenNavigationProp>();
const [searchTerm, setSearchTerm] = useState('');
const [selectedFilter, setSelectedFilter] = useState<'all' | 'active' | 'inactive'>('all');
const { items, loading, error, successMessage, deleteError } = useAppSelector(
(state: RootState) => state.customers,
);
useEffect(() => {
dispatch(getCustomers());
}, [dispatch]);
useEffect(() => {
if (successMessage) {
Alert.alert('Success', successMessage);
dispatch(resetCustomersState());
} else if (deleteError) {
Alert.alert('Error', deleteError);
dispatch(resetCustomersState());
}
}, [successMessage, deleteError, dispatch]);
// Stats
const totalCount = items.length;
const activeCount = items.filter(c => c.active === '1').length;
const inactiveCount = items.filter(c => c.active !== '1').length;
const statCards = [
{ label: 'Total', count: totalCount, color: colors.icon, key: 'all' },
{ label: 'Active', count: activeCount, color: '#10B981', key: 'active' },
{ label: 'Inactive', count: inactiveCount, color: '#EF4444', key: 'inactive' },
] as const;
// Filtered list
const filteredCustomers = useMemo(() => {
return items.filter(c => {
const matchesSearch = searchTerm
? c.company?.toLowerCase().includes(searchTerm.toLowerCase()) ||
c.fullname?.toLowerCase().includes(searchTerm.toLowerCase()) ||
c.email?.toLowerCase().includes(searchTerm.toLowerCase())
: true;
const matchesFilter =
selectedFilter === 'all'
? true
: selectedFilter === 'active'
? c.active === '1'
: c.active !== '1';
return matchesSearch && matchesFilter;
});
}, [items, searchTerm, selectedFilter]);
const handleCustomerPress = (customer: CustomerItem) => {
navigation.navigate(route.customerDetails, {
userid: customer.userid,
customer,
});
};
const handleDeleteCustomer = (customer: CustomerItem) => {
Alert.alert(
'Delete Customer',
`Are you sure you want to delete "${customer.company || customer.fullname || 'this customer'}"? This action cannot be undone.`,
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Delete',
style: 'destructive',
onPress: () => {
dispatch(deleteCustomer({ userid: customer.userid }));
},
},
],
);
};
const renderCustomer = ({ item }: { item: CustomerItem }) => (
<CustomerItemCard
item={item}
onPress={() => handleCustomerPress(item)}
onDelete={() => handleDeleteCustomer(item)}
/>
);
return (
<View style={styles.container}>
{/* Search Header */}
<View style={styles.header}>
<SearchInput
value={searchTerm}
onChangeText={setSearchTerm}
placeholder="Search customers..."
style={styles.searchInput}
/>
</View>
{/* Stat Cards - Grid aligned across the screen width */}
<View style={styles.countsContainer}>
{statCards.map(s => (
<StatCard
key={s.key}
label={s.label}
count={s.count}
color={s.color}
isSelected={selectedFilter === s.key}
style={styles.statCardFlex}
onPress={() =>
setSelectedFilter(prev => (prev === s.key ? 'all' : s.key))
}
/>
))}
</View>
{/* Customer List */}
{loading ? (
<Loader message="Loading customers..." />
) : error ? (
<View style={styles.errorContainer}>
<Text style={styles.errorText}>{error}</Text>
</View>
) : (
<FlatList
data={filteredCustomers}
keyExtractor={item => item.userid}
style={{ flex: 1 }}
contentContainerStyle={styles.listContent}
renderItem={renderCustomer}
ListEmptyComponent={() => (
<View style={styles.emptyContainer}>
<View style={styles.emptyIconWrap}>
<Icon name="business-outline" size={32} color={colors.icon} />
</View>
<Text style={styles.emptyTitle}>No Customers Found</Text>
<Text style={styles.emptyText}>
{searchTerm
? `No customers match "${searchTerm}". Try a different search.`
: 'No customers yet. They will appear here.'}
</Text>
</View>
)}
/>
)}
</View>
);
};