import React, { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { motion, AnimatePresence } from 'framer-motion'; import { useTheme } from '../context/ThemeContext'; import { useMockStore } from '../data/mockStore'; import { usePermissions } from '../hooks/usePermissions'; import PermissionGate from '../components/PermissionGate'; import { Plus, Search, MapPin, Phone, Zap, FileText, Clock, User, ChevronRight, Filter, Lock, DoorOpen, MessageSquare, } from 'lucide-react'; // --------------------------------------------------------------------------- // Urgency badge // --------------------------------------------------------------------------- const URGENCY_CONFIG = { standard: { label: 'Standard', bg: 'bg-zinc-200 dark:bg-zinc-700', text: 'text-zinc-600 dark:text-zinc-300' }, high: { label: 'High', bg: 'bg-amber-100 dark:bg-amber-500/20', text: 'text-amber-700 dark:text-amber-400' }, emergency: { label: 'Emergency', bg: 'bg-red-100 dark:bg-red-500/20', text: 'text-red-700 dark:text-red-400' }, }; const STATUS_CONFIG = { New: { bg: 'bg-blue-100 dark:bg-blue-500/20', text: 'text-blue-700 dark:text-blue-400' }, Contacted: { bg: 'bg-purple-100 dark:bg-purple-500/20', text: 'text-purple-700 dark:text-purple-400' }, Appointed: { bg: 'bg-emerald-100 dark:bg-emerald-500/20', text: 'text-emerald-700 dark:text-emerald-400' }, Closed: { bg: 'bg-zinc-100 dark:bg-zinc-700', text: 'text-zinc-500 dark:text-zinc-400' }, }; function UrgencyBadge({ urgency }) { const cfg = URGENCY_CONFIG[urgency] ?? URGENCY_CONFIG.standard; return ( {cfg.label} ); } function StatusBadge({ status }) { const cfg = STATUS_CONFIG[status] ?? STATUS_CONFIG.New; return ( {status} ); } // --------------------------------------------------------------------------- // Relative time formatter // --------------------------------------------------------------------------- function timeAgo(isoString) { const diff = Math.floor((Date.now() - new Date(isoString)) / 1000); if (diff < 60) return 'just now'; if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`; return `${Math.floor(diff / 86400)}d ago`; } // --------------------------------------------------------------------------- // Lead Card // --------------------------------------------------------------------------- function LeadCard({ lead, index }) { const { theme } = useTheme(); const isDark = theme === 'dark'; const primaryPhone = lead.phones?.find(p => p.isPrimary) ?? lead.phones?.[0]; return ( {/* Avatar */}
{(lead.firstName?.[0] ?? '?')}{(lead.lastName?.[0] ?? '')}
{/* Main info */}
{lead.firstName} {lead.lastName} {lead.isQuickCapture && ( Quick )}
{(lead.address || lead.city) && ( {[lead.address, lead.city, lead.state].filter(Boolean).join(', ')} )} {primaryPhone?.number && ( {primaryPhone.number} )} {lead.leadSource && ( {lead.leadSource} )} {lead.canvasserName && ( {lead.canvasserName} )} {lead.referralNote && ( {lead.referralNote} )}
{/* Right side */}
{timeAgo(lead.createdAt)} {lead.createdByName && ( {lead.createdByName.split(' ')[0]} )}
); } // --------------------------------------------------------------------------- // Empty state // --------------------------------------------------------------------------- function EmptyState({ isDark, onNewLead }) { return (

No Leads Yet

Capture your first lead at the door or from the office.

); } // --------------------------------------------------------------------------- // Main Page // --------------------------------------------------------------------------- export default function LeadsListPage() { const { theme } = useTheme(); const { leads } = useMockStore(); const { can } = usePermissions(); const navigate = useNavigate(); const isDark = theme === 'dark'; const [search, setSearch] = useState(''); const filtered = leads.filter(lead => { if (!search.trim()) return true; const q = search.toLowerCase(); return ( `${lead.firstName} ${lead.lastName}`.toLowerCase().includes(q) || lead.address?.toLowerCase().includes(q) || lead.city?.toLowerCase().includes(q) || lead.leadSource?.toLowerCase().includes(q) || lead.status?.toLowerCase().includes(q) ); }); return (
{/* --- Header --- */}

Leads

{leads.length} total lead{leads.length !== 1 ? 's' : ''}

{can('leads', 'log') ? ( ) : (
New Lead
)}
{/* --- Search bar --- */} {leads.length > 0 && (
setSearch(e.target.value)} placeholder="Search by name, address, source…" className="flex-1 py-3 pl-3 pr-4 text-sm outline-none bg-transparent text-zinc-800 dark:text-white placeholder-zinc-400 dark:placeholder-zinc-500" />
)} {/* --- List --- */} {leads.length === 0 ? ( navigate('/emp/fa/leads/new')} /> ) : filtered.length === 0 ? (
No leads match "{search}"
) : (
{filtered.map((lead, i) => ( ))}
)}
); }