239 lines
8.5 KiB
TypeScript
239 lines
8.5 KiB
TypeScript
import React, { useEffect } from 'react';
|
|
import { View, Text, ScrollView, TouchableOpacity, Alert } from 'react-native';
|
|
import Icon from 'react-native-vector-icons/Ionicons';
|
|
import { RouteProp, useRoute, useNavigation } from '@react-navigation/native';
|
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
|
import { useTheme } from '@theme';
|
|
import { useAppDispatch, useAppSelector, RootState } from '@store';
|
|
import { getLeadDetails } from './thunk';
|
|
import { updateLeadStatus } from '../leads/thunk';
|
|
import { getStyles } from './leadDetails.styles';
|
|
import { LeadsStackParamList } from '../../navigation/leadsStack';
|
|
import { Loader, LeadDetailHeader, ActionCard } from '@components';
|
|
import { handleEmailPress, handlePhonePress, handleWebsitePress, formatDate, route as routes } from '@utils';
|
|
|
|
type LeadDetailsRouteProp = RouteProp<LeadsStackParamList, 'leadDetails'>;
|
|
type LeadDetailsNavigationProp = NativeStackNavigationProp<LeadsStackParamList, 'leadDetails'>;
|
|
|
|
export const LeadDetailsScreen = () => {
|
|
const route = useRoute<LeadDetailsRouteProp>();
|
|
const navigation = useNavigation<LeadDetailsNavigationProp>();
|
|
const dispatch = useAppDispatch();
|
|
const { theme: colors } = useTheme();
|
|
const styles = getStyles(colors);
|
|
|
|
const leadId = route.params?.lead?.id;
|
|
const { item: lead, loading, error } = useAppSelector((state: RootState) => state.leadDetails);
|
|
const user_data = useAppSelector((state: RootState) => state.auth.user_data);
|
|
const { items: statusItems } = useAppSelector((state: RootState) => state.statusList);
|
|
|
|
const handleStatusChange = (newStatusId: string) => {
|
|
if (!lead) return;
|
|
const matchedStatus = statusItems.find(s => s.id === newStatusId);
|
|
const newStatusName = matchedStatus ? matchedStatus.name : newStatusId;
|
|
const newColor = matchedStatus ? matchedStatus.color : colors.icon;
|
|
|
|
dispatch(
|
|
updateLeadStatus({
|
|
lead: lead as any,
|
|
newStatusId,
|
|
newStatusName,
|
|
newColor,
|
|
}),
|
|
)
|
|
.unwrap()
|
|
.then(res => {
|
|
Alert.alert('Success', res.message || 'Status updated successfully');
|
|
})
|
|
.catch(err => {
|
|
Alert.alert('Error', err || 'Failed to update status');
|
|
});
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (leadId && user_data?.staffid) {
|
|
dispatch(getLeadDetails({ leadId, staffid: user_data.staffid }));
|
|
}
|
|
}, [dispatch, leadId, user_data]);
|
|
|
|
if (loading) return <Loader message="Loading lead details..." />;
|
|
|
|
if (error) {
|
|
return (
|
|
<View style={styles.container}>
|
|
<View style={styles.errorContainer}>
|
|
<Text style={styles.errorText}>{error}</Text>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
if (!lead) {
|
|
return (
|
|
<View style={styles.container}>
|
|
<View style={styles.errorContainer}>
|
|
<Text style={styles.infoValue}>No lead data available</Text>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const addressString = [lead.address, lead.city, lead.state, lead.country]
|
|
.filter(Boolean)
|
|
.join(', ');
|
|
|
|
return (
|
|
<ScrollView style={styles.container} contentContainerStyle={styles.scrollContent}>
|
|
<LeadDetailHeader
|
|
name={lead.name}
|
|
company={lead.company}
|
|
statusId={lead.status}
|
|
statusName={lead.status_name || lead.status}
|
|
statusColor={lead.color}
|
|
onStatusChange={handleStatusChange}
|
|
/>
|
|
|
|
<View style={styles.statsRow}>
|
|
<View style={styles.statCard}>
|
|
<Text style={styles.statLabel}>Lead Value</Text>
|
|
<Text style={styles.statValue}>
|
|
{lead.lead_value ? `$${lead.lead_value}` : 'N/A'}
|
|
</Text>
|
|
</View>
|
|
<View style={styles.statCard}>
|
|
<Text style={styles.statLabel}>Source</Text>
|
|
<Text style={styles.statValue} numberOfLines={1}>
|
|
{lead.source_name || lead.source || 'Direct'}
|
|
</Text>
|
|
</View>
|
|
<View style={styles.statCard}>
|
|
<Text style={styles.statLabel}>Created</Text>
|
|
<Text style={styles.statValue} numberOfLines={1}>
|
|
{formatDate(lead.dateadded) || 'N/A'}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
|
|
<Text style={styles.sectionTitle}>Lead Information</Text>
|
|
<View style={styles.infoCard}>
|
|
{lead.email ? (
|
|
<>
|
|
<TouchableOpacity
|
|
style={styles.infoRow}
|
|
onPress={() => handleEmailPress(lead.email)}
|
|
activeOpacity={0.7}>
|
|
<View style={styles.infoIconWrap}>
|
|
<Icon name="mail-outline" size={15} color={colors.icon} />
|
|
</View>
|
|
<View style={styles.infoMeta}>
|
|
<Text style={styles.infoLabel}>Email</Text>
|
|
<Text style={styles.infoValue}>{lead.email}</Text>
|
|
</View>
|
|
<View style={styles.actionIconButton}>
|
|
<Icon name="chevron-forward" size={14} color={colors.textMuted} />
|
|
</View>
|
|
</TouchableOpacity>
|
|
<View style={styles.divider} />
|
|
</>
|
|
) : null}
|
|
|
|
{lead.phonenumber ? (
|
|
<>
|
|
<TouchableOpacity
|
|
style={styles.infoRow}
|
|
onPress={() => handlePhonePress(lead.phonenumber)}
|
|
activeOpacity={0.7}>
|
|
<View style={styles.infoIconWrap}>
|
|
<Icon name="call-outline" size={15} color={colors.icon} />
|
|
</View>
|
|
<View style={styles.infoMeta}>
|
|
<Text style={styles.infoLabel}>Phone</Text>
|
|
<Text style={styles.infoValue}>{lead.phonenumber}</Text>
|
|
</View>
|
|
<View style={styles.actionIconButton}>
|
|
<Icon name="chevron-forward" size={14} color={colors.textMuted} />
|
|
</View>
|
|
</TouchableOpacity>
|
|
<View style={styles.divider} />
|
|
</>
|
|
) : null}
|
|
|
|
{lead.website ? (
|
|
<>
|
|
<TouchableOpacity
|
|
style={styles.infoRow}
|
|
onPress={() => handleWebsitePress(lead.website)}
|
|
activeOpacity={0.7}>
|
|
<View style={styles.infoIconWrap}>
|
|
<Icon name="globe-outline" size={15} color={colors.icon} />
|
|
</View>
|
|
<View style={styles.infoMeta}>
|
|
<Text style={styles.infoLabel}>Website</Text>
|
|
<Text style={styles.infoValue}>{lead.website}</Text>
|
|
</View>
|
|
<View style={styles.actionIconButton}>
|
|
<Icon name="chevron-forward" size={14} color={colors.textMuted} />
|
|
</View>
|
|
</TouchableOpacity>
|
|
<View style={styles.divider} />
|
|
</>
|
|
) : null}
|
|
|
|
{addressString ? (
|
|
<>
|
|
<View style={styles.infoRow}>
|
|
<View style={styles.infoIconWrap}>
|
|
<Icon name="location-outline" size={15} color={colors.icon} />
|
|
</View>
|
|
<View style={styles.infoMeta}>
|
|
<Text style={styles.infoLabel}>Address</Text>
|
|
<Text style={styles.infoValue}>{addressString}</Text>
|
|
</View>
|
|
</View>
|
|
<View style={styles.divider} />
|
|
</>
|
|
) : null}
|
|
|
|
{lead.assigned ? (
|
|
<View style={styles.infoRow}>
|
|
<View style={styles.infoIconWrap}>
|
|
<Icon name="person-outline" size={15} color={colors.icon} />
|
|
</View>
|
|
<View style={styles.infoMeta}>
|
|
<Text style={styles.infoLabel}>Assigned Staff</Text>
|
|
<Text style={styles.infoValue}>{lead.assigned}</Text>
|
|
</View>
|
|
</View>
|
|
) : null}
|
|
</View>
|
|
|
|
<Text style={styles.sectionTitle}>Activity & Management</Text>
|
|
<View style={styles.cardsContainer}>
|
|
<ActionCard
|
|
title="Proposals"
|
|
icon="document-text-outline"
|
|
count={0}
|
|
onAddPress={() => navigation.navigate(routes.leadProposals, { leadId: lead.id })}
|
|
onViewAllPress={() => navigation.navigate(routes.leadProposals, { leadId: lead.id })}
|
|
/>
|
|
|
|
<ActionCard
|
|
title="Tasks"
|
|
icon="checkbox-outline"
|
|
count={0}
|
|
onAddPress={() => navigation.navigate(routes.leadTasks, { leadId: lead.id })}
|
|
onViewAllPress={() => navigation.navigate(routes.leadTasks, { leadId: lead.id })}
|
|
/>
|
|
|
|
<ActionCard
|
|
title="Notes"
|
|
icon="create-outline"
|
|
count={0}
|
|
onAddPress={() => navigation.navigate(routes.leadNotes, { leadId: lead.id })}
|
|
onViewAllPress={() => navigation.navigate(routes.leadNotes, { leadId: lead.id })}
|
|
/>
|
|
</View>
|
|
</ScrollView>
|
|
);
|
|
};
|