146 lines
4.7 KiB
TypeScript
146 lines
4.7 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import {
|
|
Text,
|
|
View,
|
|
FlatList,
|
|
ScrollView,
|
|
TouchableOpacity,
|
|
ActivityIndicator,
|
|
} from 'react-native';
|
|
import { useNavigation } from '@react-navigation/native';
|
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
|
import Icon from 'react-native-vector-icons/Ionicons';
|
|
import { getStyles } from './leads.styles';
|
|
import { useTheme } from '@theme';
|
|
import { useAppDispatch, useAppSelector, RootState } from '@store';
|
|
import { getLeads, getLeadCountList } from './thunk';
|
|
import { LeadItem } from '@interfaces';
|
|
import { LeadItemCard, SearchInput, Loader, StatCard } from '@components';
|
|
import { LeadsStackParamList } from '../../navigation/leadsStack';
|
|
import { route } from '@utils';
|
|
|
|
type LeadsScreenNavigationProp = NativeStackNavigationProp<
|
|
LeadsStackParamList,
|
|
'leads'
|
|
>;
|
|
|
|
export const LeadsScreen = () => {
|
|
const dispatch = useAppDispatch();
|
|
const navigation = useNavigation<LeadsScreenNavigationProp>();
|
|
const { theme: colors } = useTheme();
|
|
const styles = getStyles(colors);
|
|
const [searchTerm, setSearchTerm] = useState('');
|
|
const [selectedStatusId, setSelectedStatusId] = useState<string | null>(null);
|
|
|
|
const { items, loading, error, counts, countsLoading } = useAppSelector(
|
|
(state: RootState) => state.leads,
|
|
);
|
|
const user_data = useAppSelector((state: RootState) => state.auth.user_data);
|
|
|
|
useEffect(() => {
|
|
if (user_data?.staffid) {
|
|
dispatch(getLeads({ leadId: null, staffid: user_data.staffid }));
|
|
dispatch(getLeadCountList(user_data.staffid));
|
|
}
|
|
}, [dispatch, user_data]);
|
|
|
|
const filteredLeads = items.filter(lead => {
|
|
const matchesSearch = searchTerm
|
|
? lead.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
lead.company.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
lead.email.toLowerCase().includes(searchTerm.toLowerCase())
|
|
: true;
|
|
|
|
const matchesStatus =
|
|
!selectedStatusId || selectedStatusId === '0'
|
|
? true
|
|
: lead.status === selectedStatusId;
|
|
|
|
return matchesSearch && matchesStatus;
|
|
});
|
|
|
|
const handleStatusPress = (id: string) => {
|
|
setSelectedStatusId(prev => (prev === id ? null : id));
|
|
};
|
|
|
|
const handleLeadPress = (lead: LeadItem) => {
|
|
navigation.navigate(route.leadDetails, { lead });
|
|
};
|
|
|
|
const renderLead = ({ item }: { item: LeadItem }) => (
|
|
<LeadItemCard item={item} onPress={() => handleLeadPress(item)} />
|
|
);
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
{/* Search + Filter Header (Fixed at top) */}
|
|
<View style={styles.header}>
|
|
<SearchInput
|
|
value={searchTerm}
|
|
onChangeText={setSearchTerm}
|
|
placeholder="Search leads, companies, emails..."
|
|
style={styles.searchInput}
|
|
/>
|
|
<TouchableOpacity style={styles.filterButton}>
|
|
<Icon name="filter-outline" size={20} color={colors.text} />
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
{/* Status count cards */}
|
|
{countsLoading ? (
|
|
<View style={styles.countsLoading}>
|
|
<ActivityIndicator size="small" color={colors.icon} />
|
|
</View>
|
|
) : counts.length > 0 ? (
|
|
<ScrollView
|
|
horizontal
|
|
showsHorizontalScrollIndicator={false}
|
|
style={{ flexGrow: 0 }}
|
|
contentContainerStyle={styles.countsScroll}
|
|
>
|
|
{counts.map(item => (
|
|
<StatCard
|
|
key={item.id}
|
|
label={item.name}
|
|
count={item.count}
|
|
color={item.color || colors.icon}
|
|
isSelected={selectedStatusId === item.id}
|
|
onPress={() => handleStatusPress(item.id)}
|
|
/>
|
|
))}
|
|
</ScrollView>
|
|
) : null}
|
|
|
|
{/* Lead list */}
|
|
{loading ? (
|
|
<Loader message="Loading leads..." />
|
|
) : error ? (
|
|
<View style={styles.errorContainer}>
|
|
<Text style={styles.errorText}>{error}</Text>
|
|
</View>
|
|
) : (
|
|
<FlatList
|
|
data={filteredLeads}
|
|
keyExtractor={item => item.id}
|
|
style={{ flex: 1 }}
|
|
contentContainerStyle={styles.listContent}
|
|
renderItem={renderLead}
|
|
ListEmptyComponent={() => (
|
|
<View style={styles.emptyContainer}>
|
|
<View style={styles.emptyIconWrap}>
|
|
<Icon name="people-outline" size={32} color={colors.icon} />
|
|
</View>
|
|
<Text style={styles.emptyTitle}>No Leads Found</Text>
|
|
<Text style={styles.emptyText}>
|
|
{searchTerm
|
|
? `No leads match "${searchTerm}". Try a different search.`
|
|
: 'You have no leads yet. New leads will appear here.'}
|
|
</Text>
|
|
</View>
|
|
)}
|
|
/>
|
|
)}
|
|
</View>
|
|
);
|
|
};
|