Files
helix-engage/src/components/admin/lead-funnel.tsx

84 lines
3.3 KiB
TypeScript

import { useMemo } from 'react';
import { cx } from '@/utils/cx';
import type { Lead } from '@/types/entities';
interface LeadFunnelProps {
leads: Lead[];
}
type FunnelStage = {
label: string;
count: number;
color: string;
};
export const LeadFunnel = ({ leads }: LeadFunnelProps) => {
const stages = useMemo((): FunnelStage[] => {
const total = leads.length;
const contacted = leads.filter((lead) =>
lead.leadStatus === 'CONTACTED' ||
lead.leadStatus === 'QUALIFIED' ||
lead.leadStatus === 'NURTURING' ||
lead.leadStatus === 'APPOINTMENT_SET' ||
lead.leadStatus === 'CONVERTED',
).length;
const appointmentSet = leads.filter((lead) =>
lead.leadStatus === 'APPOINTMENT_SET' ||
lead.leadStatus === 'CONVERTED',
).length;
const converted = leads.filter((lead) =>
lead.leadStatus === 'CONVERTED',
).length;
return [
{ label: 'Generated', count: total, color: 'bg-brand-600' },
{ label: 'Contacted', count: contacted, color: 'bg-brand-500' },
{ label: 'Appointment Set', count: appointmentSet, color: 'bg-brand-400' },
{ label: 'Converted', count: converted, color: 'bg-success-500' },
];
}, [leads]);
const maxCount = stages[0]?.count || 1;
return (
<div className="rounded-2xl border border-secondary bg-primary p-5">
<h3 className="text-sm font-bold text-primary">Lead Funnel &middot; This Week</h3>
<div className="mt-4 space-y-3">
{stages.map((stage, index) => {
const widthPercent = maxCount > 0 ? (stage.count / maxCount) * 100 : 0;
const previousCount = index > 0 ? stages[index - 1].count : null;
const conversionRate =
previousCount !== null && previousCount > 0
? ((stage.count / previousCount) * 100).toFixed(0)
: null;
return (
<div key={stage.label} className="space-y-1">
<div className="flex items-center justify-between">
<span className="text-xs font-medium text-secondary">{stage.label}</span>
<div className="flex items-center gap-2">
<span className="text-sm font-bold text-primary">{stage.count}</span>
{conversionRate !== null && (
<span className="text-xs text-tertiary">({conversionRate}%)</span>
)}
</div>
</div>
<div className="h-6 w-full overflow-hidden rounded-md bg-secondary">
<div
className={cx('h-full rounded-md transition-all', stage.color)}
style={{ width: `${Math.max(widthPercent, 2)}%` }}
/>
</div>
</div>
);
})}
</div>
</div>
);
};