Files
helix-engage/src/pages/lead-workspace.tsx
2026-03-17 08:59:13 +05:30

176 lines
7.6 KiB
TypeScript

import { useState } from 'react';
import { AnimatePresence, motion } from 'motion/react';
import { Button } from '@/components/base/buttons/button';
import { TopBar } from '@/components/layout/top-bar';
import { KpiCards } from '@/components/leads/kpi-cards';
import { SourceGrid } from '@/components/leads/source-grid';
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 { useData } from '@/providers/data-provider';
import type { Lead, LeadSource } from '@/types/entities';
export const LeadWorkspacePage = () => {
const [sourceFilter, setSourceFilter] = useState<LeadSource | null>(null);
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);
// 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<Lead | null>(null);
const handleAssign = (lead: Lead) => {
setTargetLead(lead);
setIsAssignOpen(true);
};
const handleMessage = (lead: Lead) => {
setTargetLead(lead);
setIsWhatsAppOpen(true);
};
const handleMarkSpam = (lead: Lead) => {
setTargetLead(lead);
setIsSpamOpen(true);
};
const handleMerge = (lead: Lead) => {
setTargetLead(lead);
setIsMergeOpen(true);
};
const handleLogCall = () => {
// placeholder
};
const handleUpdateStatus = () => {
// placeholder
};
return (
<div className="flex flex-1 flex-col">
<TopBar title="Lead Workspace" subtitle="Global Hospital · Last 24 hours" />
<div className="flex flex-1 overflow-hidden">
{/* Main content */}
<div className="flex-1 space-y-6 overflow-y-auto p-7">
<KpiCards leads={allLeads} />
<div>
<div className="mb-3 flex items-center justify-between">
<h2 className="font-display text-md font-bold text-primary">Lead Sources</h2>
<span className="text-xs text-quaternary">Click to filter</span>
</div>
<SourceGrid leads={allLeads} onSourceFilter={setSourceFilter} activeSource={sourceFilter} />
</div>
<div>
<div className="mb-3.5 flex items-center justify-between">
<h2 className="font-display text-md font-bold text-primary">New Leads</h2>
<Button
href={sourceFilter ? `/leads?source=${sourceFilter}` : '/leads'}
color="link-color"
size="sm"
>
View All {total} Leads
</Button>
</div>
<div className="space-y-2">
<AnimatePresence>
{displayLeads.map((lead) => (
<motion.div
key={lead.id}
initial={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20, height: 0, marginBottom: 0 }}
transition={{ duration: 0.3 }}
>
<LeadCard
lead={lead}
onAssign={handleAssign}
onMessage={handleMessage}
onMarkSpam={handleMarkSpam}
onMerge={handleMerge}
onLogCall={handleLogCall}
onUpdateStatus={handleUpdateStatus}
/>
</motion.div>
))}
</AnimatePresence>
{displayLeads.length === 0 && (
<p className="py-8 text-center text-sm text-tertiary">
No leads match the current filters.
</p>
)}
</div>
</div>
</div>
{/* Right sidebar */}
<aside className="hidden w-80 space-y-5 overflow-y-auto border-l border-secondary bg-primary p-5 xl:block">
<AgingWidget leads={allLeads} />
<FollowupWidget overdue={overdue} upcoming={upcoming} />
<AlertsWidget leads={allLeads} />
</aside>
</div>
{/* Modals */}
{targetLead && (
<>
<AssignModal
isOpen={isAssignOpen}
onOpenChange={setIsAssignOpen}
selectedLeads={[targetLead]}
agents={agents}
onAssign={(agentId) => {
updateLead(targetLead.id, {
assignedAgent: agents.find((a) => a.id === agentId)?.name ?? null,
leadStatus: 'CONTACTED',
});
setIsAssignOpen(false);
}}
/>
<WhatsAppSendModal
isOpen={isWhatsAppOpen}
onOpenChange={setIsWhatsAppOpen}
selectedLeads={[targetLead]}
templates={templates.filter((t) => t.approvalStatus === 'APPROVED')}
onSend={() => setIsWhatsAppOpen(false)}
/>
<MarkSpamModal
isOpen={isSpamOpen}
onOpenChange={setIsSpamOpen}
lead={targetLead}
onConfirm={() => {
updateLead(targetLead.id, { isSpam: true, leadStatus: 'LOST' });
setIsSpamOpen(false);
}}
/>
<MergeModal
isOpen={isMergeOpen}
onOpenChange={setIsMergeOpen}
primaryLead={targetLead}
duplicateLead={allLeads.find((l) => l.id === targetLead.duplicateOfLeadId) ?? targetLead}
onMerge={() => setIsMergeOpen(false)}
onKeepSeparate={() => setIsMergeOpen(false)}
/>
</>
)}
</div>
);
};