Files
helix-engage/src/components/campaigns/source-breakdown.tsx
2026-03-16 15:01:00 +05:30

70 lines
2.6 KiB
TypeScript

import { cx } from '@/utils/cx';
import type { Lead } from '@/types/entities';
interface SourceBreakdownProps {
leads: Lead[];
}
const sourceColors: Record<string, string> = {
FACEBOOK_AD: 'bg-brand-solid',
GOOGLE_AD: 'bg-success-solid',
INSTAGRAM: 'bg-error-solid',
GOOGLE_MY_BUSINESS: 'bg-warning-solid',
WEBSITE: 'bg-fg-brand-primary',
REFERRAL: 'bg-fg-tertiary',
WHATSAPP: 'bg-success-solid',
WALK_IN: 'bg-fg-quaternary',
PHONE: 'bg-fg-secondary',
OTHER: 'bg-fg-disabled',
};
const sourceLabel = (source: string): string =>
source.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
export const SourceBreakdown = ({ leads }: SourceBreakdownProps) => {
const sourceCounts = leads.reduce<Record<string, number>>((acc, lead) => {
const source = lead.leadSource ?? 'OTHER';
acc[source] = (acc[source] ?? 0) + 1;
return acc;
}, {});
const sorted = Object.entries(sourceCounts).sort(([, a], [, b]) => b - a);
const maxCount = sorted.length > 0 ? sorted[0][1] : 1;
if (sorted.length === 0) {
return null;
}
return (
<div className="rounded-xl border border-secondary bg-primary p-4">
<h4 className="mb-3 text-sm font-bold text-primary">Lead Sources</h4>
<div className="space-y-2">
{sorted.map(([source, count]) => {
const widthPercent = (count / maxCount) * 100;
return (
<div key={source} className="flex items-center gap-3">
<span className="w-28 shrink-0 truncate text-xs text-tertiary">
{sourceLabel(source)}
</span>
<div className="flex-1">
<div className="h-4 rounded bg-secondary overflow-hidden">
<div
className={cx(
'h-full rounded transition-all duration-300',
sourceColors[source] ?? 'bg-fg-disabled',
)}
style={{ width: `${Math.max(widthPercent, 4)}%` }}
/>
</div>
</div>
<span className="w-8 shrink-0 text-right text-xs font-bold text-primary">
{count}
</span>
</div>
);
})}
</div>
</div>
);
};