From a119fb1b67def7032b1a47f65555f5f6c0102acb Mon Sep 17 00:00:00 2001 From: saridsa2 Date: Mon, 16 Mar 2026 16:20:27 +0530 Subject: [PATCH] fix: wire all modals and lead activity slideout into Lead Workspace and All Leads pages Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/leads/lead-table.tsx | 3 + src/pages/all-leads.tsx | 97 +++++++++++++++++++++++++++-- src/pages/lead-workspace.tsx | 80 +++++++++++++++++++++--- 3 files changed, 165 insertions(+), 15 deletions(-) diff --git a/src/components/leads/lead-table.tsx b/src/components/leads/lead-table.tsx index b5a07a7..29fa486 100644 --- a/src/components/leads/lead-table.tsx +++ b/src/components/leads/lead-table.tsx @@ -19,6 +19,7 @@ type LeadTableProps = { sortField: string; sortDirection: 'asc' | 'desc'; onSort: (field: string) => void; + onViewActivity?: (lead: Lead) => void; }; type TableRow = { @@ -47,6 +48,7 @@ export const LeadTable = ({ sortField, sortDirection, onSort, + onViewActivity, }: LeadTableProps) => { const [expandedDupId, setExpandedDupId] = useState(null); @@ -298,6 +300,7 @@ export const LeadTable = ({ color="tertiary" iconLeading={DotsVertical} aria-label="Row actions" + onClick={() => onViewActivity?.(lead)} /> diff --git a/src/pages/all-leads.tsx b/src/pages/all-leads.tsx index 40fb1af..17bce3b 100644 --- a/src/pages/all-leads.tsx +++ b/src/pages/all-leads.tsx @@ -8,9 +8,14 @@ import { TopBar } from '@/components/layout/top-bar'; import { LeadTable } from '@/components/leads/lead-table'; import { BulkActionBar } from '@/components/leads/bulk-action-bar'; import { FilterPills } from '@/components/leads/filter-pills'; +import { AssignModal } from '@/components/modals/assign-modal'; +import { WhatsAppSendModal } from '@/components/modals/whatsapp-send-modal'; +import { MarkSpamModal } from '@/components/modals/mark-spam-modal'; +import { LeadActivitySlideout } from '@/components/leads/lead-activity-slideout'; import { useLeads } from '@/hooks/use-leads'; import { useAuth } from '@/providers/auth-provider'; -import type { LeadSource, LeadStatus } from '@/types/entities'; +import { useData } from '@/providers/data-provider'; +import type { Lead, LeadSource, LeadStatus } from '@/types/entities'; type TabKey = 'new' | 'my-leads' | 'all'; @@ -35,12 +40,14 @@ export const AllLeadsPage = () => { const statusFilter: LeadStatus | undefined = tab === 'new' ? 'NEW' : undefined; const myLeadsOnly = tab === 'my-leads'; - const { leads: filteredLeads, total } = useLeads({ + const { leads: filteredLeads, total, updateLead } = useLeads({ source: sourceFilter ?? undefined, status: statusFilter, search: searchQuery || undefined, }); + const { agents, templates, leadActivities } = useData(); + // Client-side sorting const sortedLeads = useMemo(() => { const sorted = [...filteredLeads]; @@ -158,6 +165,29 @@ export const AllLeadsPage = () => { { id: 'all', label: 'All Leads', badge: tab === 'all' ? total : undefined }, ]; + // Bulk action modal state + const [isAssignOpen, setIsAssignOpen] = useState(false); + const [isWhatsAppOpen, setIsWhatsAppOpen] = useState(false); + const [isSpamOpen, setIsSpamOpen] = useState(false); + + const selectedLeadsForAction = useMemo( + () => displayLeads.filter((l) => selectedIds.includes(l.id)), + [displayLeads, selectedIds], + ); + + const handleBulkAssign = () => setIsAssignOpen(true); + const handleBulkWhatsApp = () => setIsWhatsAppOpen(true); + const handleBulkSpam = () => setIsSpamOpen(true); + + // Activity slideout state + const [activityLead, setActivityLead] = useState(null); + const [isActivityOpen, setIsActivityOpen] = useState(false); + + const handleViewActivity = (lead: Lead) => { + setActivityLead(lead); + setIsActivityOpen(true); + }; + return (
@@ -237,9 +267,9 @@ export const AllLeadsPage = () => {
{}} - onWhatsApp={() => {}} - onMarkSpam={() => {}} + onAssign={handleBulkAssign} + onWhatsApp={handleBulkWhatsApp} + onMarkSpam={handleBulkSpam} onDeselect={() => setSelectedIds([])} />
@@ -254,6 +284,7 @@ export const AllLeadsPage = () => { sortField={sortField} sortDirection={sortDirection} onSort={handleSort} + onViewActivity={handleViewActivity} />
@@ -268,6 +299,62 @@ export const AllLeadsPage = () => { )} + + {/* Bulk action modals */} + {selectedLeadsForAction.length > 0 && ( + <> + { + const agentName = agents.find((a) => a.id === agentId)?.name ?? null; + selectedIds.forEach((id) => { + updateLead(id, { assignedAgent: agentName, leadStatus: 'CONTACTED' }); + }); + setIsAssignOpen(false); + setSelectedIds([]); + }} + /> + t.approvalStatus === 'APPROVED')} + onSend={() => { + setIsWhatsAppOpen(false); + setSelectedIds([]); + }} + /> + + )} + + {/* Bulk spam: use first selected lead for the single-lead MarkSpamModal */} + {selectedLeadsForAction.length > 0 && selectedLeadsForAction[0] && ( + { + selectedIds.forEach((id) => { + updateLead(id, { isSpam: true, leadStatus: 'LOST' }); + }); + setIsSpamOpen(false); + setSelectedIds([]); + }} + /> + )} + + {/* Activity slideout */} + {activityLead && ( + + )} ); }; diff --git a/src/pages/lead-workspace.tsx b/src/pages/lead-workspace.tsx index 4852c7b..ee6235d 100644 --- a/src/pages/lead-workspace.tsx +++ b/src/pages/lead-workspace.tsx @@ -8,33 +8,50 @@ import { LeadCard } from '@/components/leads/lead-card'; import { AgingWidget } from '@/components/leads/aging-widget'; import { FollowupWidget } from '@/components/leads/followup-widget'; import { AlertsWidget } from '@/components/leads/alerts-widget'; +import { AssignModal } from '@/components/modals/assign-modal'; +import { WhatsAppSendModal } from '@/components/modals/whatsapp-send-modal'; +import { MarkSpamModal } from '@/components/modals/mark-spam-modal'; +import { MergeModal } from '@/components/modals/merge-modal'; import { useLeads } from '@/hooks/use-leads'; import { useFollowUps } from '@/hooks/use-follow-ups'; -import type { LeadSource } from '@/types/entities'; +import { useData } from '@/providers/data-provider'; +import type { Lead, LeadSource } from '@/types/entities'; export const LeadWorkspacePage = () => { const [sourceFilter, setSourceFilter] = useState(null); - const { leads, total } = useLeads({ source: sourceFilter ?? undefined, status: 'NEW' }); + const { leads, total, updateLead } = useLeads({ source: sourceFilter ?? undefined, status: 'NEW' }); const { leads: allLeads } = useLeads(); const { overdue, upcoming } = useFollowUps(); + const { agents, templates } = useData(); const displayLeads = leads.slice(0, 10); - const handleAssign = () => { - // placeholder + // Modal state + const [isAssignOpen, setIsAssignOpen] = useState(false); + const [isWhatsAppOpen, setIsWhatsAppOpen] = useState(false); + const [isSpamOpen, setIsSpamOpen] = useState(false); + const [isMergeOpen, setIsMergeOpen] = useState(false); + const [targetLead, setTargetLead] = useState(null); + + const handleAssign = (lead: Lead) => { + setTargetLead(lead); + setIsAssignOpen(true); }; - const handleMessage = () => { - // placeholder + const handleMessage = (lead: Lead) => { + setTargetLead(lead); + setIsWhatsAppOpen(true); }; - const handleMarkSpam = () => { - // placeholder + const handleMarkSpam = (lead: Lead) => { + setTargetLead(lead); + setIsSpamOpen(true); }; - const handleMerge = () => { - // placeholder + const handleMerge = (lead: Lead) => { + setTargetLead(lead); + setIsMergeOpen(true); }; const handleLogCall = () => { @@ -97,6 +114,49 @@ export const LeadWorkspacePage = () => { + + {/* Modals */} + {targetLead && ( + <> + { + updateLead(targetLead.id, { + assignedAgent: agents.find((a) => a.id === agentId)?.name ?? null, + leadStatus: 'CONTACTED', + }); + setIsAssignOpen(false); + }} + /> + t.approvalStatus === 'APPROVED')} + onSend={() => setIsWhatsAppOpen(false)} + /> + { + updateLead(targetLead.id, { isSpam: true, leadStatus: 'LOST' }); + setIsSpamOpen(false); + }} + /> + l.id === targetLead.duplicateOfLeadId) ?? targetLead} + onMerge={() => setIsMergeOpen(false)} + onKeepSeparate={() => setIsMergeOpen(false)} + /> + + )} ); };