mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-04-12 02:38:15 +00:00
feat: build Lead Workspace page with KPIs, source grid, lead cards, and sidebar widgets
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
71
src/components/leads/aging-widget.tsx
Normal file
71
src/components/leads/aging-widget.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import { cx } from '@/utils/cx';
|
||||
import { daysAgoFromNow } from '@/lib/format';
|
||||
import type { Lead } from '@/types/entities';
|
||||
|
||||
interface AgingWidgetProps {
|
||||
leads: Lead[];
|
||||
}
|
||||
|
||||
type AgingBracket = {
|
||||
label: string;
|
||||
color: string;
|
||||
barColor: string;
|
||||
count: number;
|
||||
};
|
||||
|
||||
export const AgingWidget = ({ leads }: AgingWidgetProps) => {
|
||||
const leadsWithAge = leads.filter((l) => l.createdAt !== null);
|
||||
const total = leadsWithAge.length || 1;
|
||||
|
||||
const freshCount = leadsWithAge.filter((l) => daysAgoFromNow(l.createdAt!) < 2).length;
|
||||
const warmCount = leadsWithAge.filter((l) => {
|
||||
const days = daysAgoFromNow(l.createdAt!);
|
||||
return days >= 2 && days <= 5;
|
||||
}).length;
|
||||
const coldCount = leadsWithAge.filter((l) => daysAgoFromNow(l.createdAt!) > 5).length;
|
||||
|
||||
const brackets: AgingBracket[] = [
|
||||
{
|
||||
label: 'Fresh (<2 days)',
|
||||
color: 'text-success-primary',
|
||||
barColor: 'bg-success-solid',
|
||||
count: freshCount,
|
||||
},
|
||||
{
|
||||
label: 'Warm (2-5 days)',
|
||||
color: 'text-warning-primary',
|
||||
barColor: 'bg-warning-solid',
|
||||
count: warmCount,
|
||||
},
|
||||
{
|
||||
label: 'Cold (>5 days)',
|
||||
color: 'text-error-primary',
|
||||
barColor: 'bg-error-solid',
|
||||
count: coldCount,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3 className="mb-3 text-sm font-bold text-primary">Lead Aging</h3>
|
||||
<div className="space-y-3">
|
||||
{brackets.map((bracket) => (
|
||||
<div key={bracket.label}>
|
||||
<div className="mb-1 flex items-center justify-between">
|
||||
<span className={cx('text-xs', bracket.color)}>{bracket.label}</span>
|
||||
<span className={cx('text-sm font-bold', bracket.color)}>
|
||||
{bracket.count}
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-2 overflow-hidden rounded-full bg-tertiary">
|
||||
<div
|
||||
className={cx('h-full rounded-full transition-all', bracket.barColor)}
|
||||
style={{ width: `${(bracket.count / total) * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user