lead-to-policy/src/components/insurance/LeadRow.tsx
Bhanu Prakash Sai Potteri 1a622b1267 feat: worklist screens + modal-driven activity forms
- state-scoped lead worklists (list → row action → form modal) replace per-activity screens
- shared Modal / ActivityActions / registry; bespoke bodies + generic ActivityForm
- screen RBAC (canSeeScreen + RequireScreen), client-side pagination, lead filters
- fixes: document scroll, PickerShell z-index above modal, friendly RBAC message, Lead360 compact INR, segments-card alignment
2026-06-24 17:04:26 +05:30

86 lines
2.9 KiB
TypeScript

import type { ReactNode } from 'react';
import { cn } from '../../lib/cn';
import type { Lead, Tone } from '../../types';
import { Avatar } from '../core/Avatar';
import { SegmentBadge } from './SegmentBadge';
import { ChannelBadge } from './ChannelBadge';
export interface LeadRowProps {
lead: Lead;
onClick?: () => void;
className?: string;
/** When set, replaces the "last action" cell (e.g. a worklist action button).
* Clicks inside it don't trigger the row's onClick. */
action?: ReactNode;
}
/** Shared grid template for the leads table — keep header + rows in sync. */
export const LEAD_GRID = 'grid-cols-[2fr_64px_96px_120px_1.4fr_2fr]';
const DOT: Record<Tone, string> = {
neutral: 'bg-muted',
success: 'bg-emerald-600',
warning: 'bg-amber-600',
accent: 'bg-sunrise-600',
info: 'bg-sky-600',
};
function scoreColor(score: number): string {
if (score >= 75) return 'var(--sunrise-600)';
if (score >= 50) return 'var(--amber-600)';
return 'var(--slate-500)';
}
/** Leads-list table row — name, score, segment, channel, owner, last AI action
* (or a custom `action` cell). */
export function LeadRow({ lead, onClick, className, action }: LeadRowProps) {
return (
<div
onClick={onClick}
className={cn(
'grid items-center gap-3 px-[18px] py-3.5 bg-card border-b border-border-subtle font-sans transition-colors duration-150',
LEAD_GRID,
onClick && 'cursor-pointer hover:bg-slate-50',
className,
)}
>
{/* name */}
<div className="flex items-center gap-2.5 min-w-0">
<Avatar name={lead.name} size={36} />
<div className="min-w-0">
<div className="text-base font-semibold text-strong truncate">{lead.name}</div>
<div className="text-xs text-faint">{lead.city}</div>
</div>
</div>
{/* score */}
<div className="font-numeric font-extrabold text-2xl tracking-[-0.02em] nums" style={{ color: scoreColor(lead.score) }}>
{lead.score}
</div>
{/* segment */}
<div>
<SegmentBadge segment={lead.segment} size="sm" />
</div>
{/* channel */}
<div>
<ChannelBadge channel={lead.channel} size="sm" />
</div>
{/* owner */}
<div className="flex items-center gap-2 min-w-0">
<Avatar name={lead.owner} ai={lead.ownerAi} size={24} />
<span className="text-sm text-body truncate">{lead.owner}</span>
</div>
{/* last AI action — or a custom action cell */}
{action !== undefined ? (
<div className="flex justify-end" onClick={(e) => e.stopPropagation()}>
{action}
</div>
) : (
<div className="flex items-center gap-[7px] min-w-0">
<span className={cn('w-1.5 h-1.5 rounded-full shrink-0', DOT[lead.lastTone])} />
<span className="text-sm text-muted truncate">{lead.lastAction}</span>
</div>
)}
</div>
);
}