'use client'; import { ClipboardList } from 'lucide-react'; import * as React from 'react'; import { CreateLeadDialog } from '@/components/leads/create-lead-dialog'; import { LeadDetailDialog } from '@/components/leads/lead-detail-dialog'; import { LeadStatusBadge } from '@/components/leads/lead-status-badge'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Select } from '@/components/ui/select'; import { useLeads, useLeadStats } from '@/lib/hooks/use-leads'; import { LEAD_STATUSES, LEAD_SOURCES, type LeadReadDto, type LeadStatus } from '@/lib/leads-api'; function getSourceLabel(source: string): string { const found = LEAD_SOURCES.find((s) => s.value === source); return found?.label ?? source; } function formatDate(dateStr: string): string { return new Date(dateStr).toLocaleDateString('vi-VN', { day: '2-digit', month: '2-digit', year: 'numeric', }); } export default function LeadsPage() { const [page, setPage] = React.useState(1); const [statusFilter, setStatusFilter] = React.useState(''); const [createOpen, setCreateOpen] = React.useState(false); const [selectedLead, setSelectedLead] = React.useState(null); const [detailOpen, setDetailOpen] = React.useState(false); const searchParams = React.useMemo(() => { const params: { page: number; limit: number; status?: LeadStatus } = { page, limit: 20 }; if (statusFilter) params.status = statusFilter; return params; }, [page, statusFilter]); const { data: result, isLoading: loading } = useLeads(searchParams); const { data: stats, isLoading: statsLoading } = useLeadStats(); const handleSelectLead = (lead: LeadReadDto) => { setSelectedLead(lead); setDetailOpen(true); }; return (
{/* Header */}

Quản lý lead

Theo dõi và chuyển đổi khách hàng tiềm năng

{/* Stats */}
Tổng lead {statsLoading ? '...' : stats?.totalLeads ?? 0} Tỷ lệ chuyển đổi {statsLoading ? '...' : `${(stats?.conversionRate ?? 0).toFixed(1)}%`} Điểm TB {statsLoading ? '...' : stats?.avgScore !== null ? stats?.avgScore?.toFixed(0) : 'N/A'} Lead mới {statsLoading ? '...' : stats?.byStatus?.['NEW'] ?? 0}
{/* Status breakdown */} {stats && !statsLoading && (
{Object.entries(stats.byStatus).map(([status, count]) => { const config = LEAD_STATUSES[status as LeadStatus]; if (!config || count === 0) return null; return ( ); })}
)} {/* Filters */}
{result && ( {result.total} lead )}
{/* Content */} {loading ? (
) : !result || result.data.length === 0 ? (
) : ( <> {/* Mobile card view */}
{result.data.map((lead) => ( handleSelectLead(lead)} >

{lead.name}

{lead.phone}

{getSourceLabel(lead.source)} {lead.score !== null && Điểm: {lead.score}} {formatDate(lead.createdAt)}
))}
{/* Desktop table view */}
{result.data.map((lead) => ( handleSelectLead(lead)} > ))}
Khách hàng Nguồn Điểm Trạng thái Ngày tạo Cập nhật
{lead.name} {lead.phone} {lead.email && ( {lead.email} )}
{getSourceLabel(lead.source)} {lead.score !== null ? (
{lead.score}
) : ( )}
{formatDate(lead.createdAt)} {formatDate(lead.updatedAt)}
)} {/* Pagination */} {result && result.totalPages > 1 && (
Trang {result.page} / {result.totalPages}
)} {/* Dialogs */} { setDetailOpen(open); if (!open) setSelectedLead(null); }} />
); }