import { View, Text, TouchableOpacity, ScrollView, Alert, ActivityIndicator } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useLocalSearchParams, router } from 'expo-router'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { api } from '../../../services/api'; import { Icon } from '../../../components/Icon'; const STATUS_CONFIG: Record = { NEW: { label: 'New', color: '#0891B2', bg: '#ECFEFF' }, CONTACTED: { label: 'Contacted', color: '#D97706', bg: '#FEF3C7' }, INTERESTED: { label: 'Interested', color: '#7C3AED', bg: '#F5F3FF' }, CONVERTED: { label: 'Converted', color: '#166534', bg: '#DCFCE7' }, LOST: { label: 'Lost', color: '#6B7280', bg: '#F1F5F9' }, }; const STATUS_FLOW = ['NEW', 'CONTACTED', 'INTERESTED', 'CONVERTED', 'LOST']; function InfoRow({ label, value }: { label: string; value?: string | null }) { if (!value) return null; return ( {label} {value} ); } export default function LeadDetailScreen() { const { id } = useLocalSearchParams<{ id: string }>(); const qc = useQueryClient(); const { data: lead, isLoading, refetch } = useQuery({ queryKey: ['lead', id], queryFn: () => api.get(`/api/v1/leads/${id}`).then(r => r.data), staleTime: 0, }); const cfg = STATUS_CONFIG[lead?.status ?? 'NEW'] ?? STATUS_CONFIG.NEW; const isConverted = lead?.status === 'CONVERTED'; const updateStatus = async (status: string) => { try { await api.patch(`/api/v1/leads/${id}`, { status }); await refetch(); qc.invalidateQueries({ queryKey: ['leads'] }); } catch { Alert.alert('Error', 'Could not update status.'); } }; const confirmConvert = () => { Alert.alert( 'Convert to Client?', `This will start the onboarding process for ${lead?.firstName} ${lead?.lastName}. They will be added to Clients and an installation ticket will be created.`, [ { text: 'Cancel', style: 'cancel' }, { text: 'Convert & Onboard', style: 'default', onPress: convertLead }, ] ); }; const convertLead = async () => { try { // Create client from lead const clientRes = await api.post('/api/v1/clients', { firstName: lead.firstName, lastName: lead.lastName !== '—' ? lead.lastName : '', phone: lead.phone, address: lead.address ?? '—', ...(lead.email ? { email: lead.email } : {}), ...(lead.areaId ? { areaId: lead.areaId } : {}), }); const client = clientRes.data; // Create installation ticket const ticketRes = await api.post('/api/v1/tickets', { clientId: client.id, subject: `New Installation — ${lead.firstName} ${lead.lastName !== '—' ? lead.lastName : ''}`.trim(), type: 'INSTALLATION', priority: 'NORMAL', }); const ticket = ticketRes.data; // Mark lead as CONVERTED await api.patch(`/api/v1/leads/${id}`, { status: 'CONVERTED', convertedClientId: client.id, }); qc.invalidateQueries({ queryKey: ['leads'] }); qc.invalidateQueries({ queryKey: ['clients'] }); qc.invalidateQueries({ queryKey: ['tasks'] }); Alert.alert( 'Lead Converted! 🎉', `${lead.firstName} is now a client with an installation ticket created.`, [{ text: 'View Installation Ticket', onPress: () => { router.replace('/(app)/tasks'); setTimeout(() => router.push(`/(app)/tasks/${ticket.id}`), 300); }, }, { text: 'Done', onPress: () => router.back(), }] ); } catch (e: any) { const msg = e?.response?.data?.message ?? 'Could not convert lead.'; Alert.alert('Error', Array.isArray(msg) ? msg.join('\n') : msg); } }; const confirmDelete = () => { Alert.alert( 'Delete Lead?', `Are you sure you want to delete ${lead?.firstName} ${lead?.lastName}? This cannot be undone.`, [ { text: 'Cancel', style: 'cancel' }, { text: 'Delete', style: 'destructive', onPress: deleteLead }, ] ); }; const deleteLead = async () => { try { await api.delete(`/api/v1/leads/${id}`); qc.invalidateQueries({ queryKey: ['leads'] }); router.back(); } catch { Alert.alert('Error', 'Could not delete lead.'); } }; if (isLoading) { return ( ); } return ( {/* Header */} router.back()} hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} style={{ marginRight: 14 }}> {lead?.firstName} {lead?.lastName !== '—' ? lead?.lastName : ''} Lead {cfg.label} {/* Info card */} {/* Status update */} {!isConverted && ( Update Status {STATUS_FLOW.filter(s => s !== 'CONVERTED').map(s => { const c = STATUS_CONFIG[s]; const isActive = lead?.status === s; return ( !isActive && updateStatus(s)} style={{ paddingHorizontal: 14, paddingVertical: 8, borderRadius: 20, borderWidth: 1.5, borderColor: isActive ? c.color : '#E2E8F0', backgroundColor: isActive ? c.bg : '#F8FAFC' }} > {c.label} ); })} )} {/* Action buttons */} {!isConverted && ( 🚀 Convert to Client & Schedule Install )} {isConverted && ( ✓ Already Converted to Client )} 🗑 Delete Lead ); }