feat: polish all pages — tooltips, sticky headers, roles, search, AI prompts

Dashboard KPI:
- Fix 1534m → 25h 34m (formatMinutes helper)
- Add info icon tooltips on all KPI and metric cards
- Pass role="admin" to AI panel for manager-specific prompts

Settings:
- Add search + pagination to employee table
- Infer roles from email convention (platform roles API returns null via API key)

AI Assistant:
- Role-specific quick prompts: manager sees "Agent performance", "Missed risks"
- Agent sees "Doctor availability", "Treatment packages"

Sticky headers:
- Add overflow-hidden to campaigns and all-leads pages

Misc:
- Fix free-brands-svg-icons → pro-duotone in integrations
- Remove Follow-ups from CC agent sidebar
- Remove global search from TopBar

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 16:23:10 +05:30
parent c776782af6
commit bb004744f4
6 changed files with 104 additions and 23 deletions

View File

@@ -19,16 +19,25 @@ type CallerContext = {
interface AiChatPanelProps {
callerContext?: CallerContext;
role?: 'cc-agent' | 'admin' | 'executive';
}
const QUICK_ASK_BUTTONS = [
const QUICK_ASK_AGENT = [
{ label: 'Doctor availability', template: 'What are the visiting hours for all doctors?' },
{ label: 'Clinic timings', template: 'What are the clinic locations and timings?' },
{ label: 'Patient history', template: 'Can you summarize this patient\'s history?' },
{ label: 'Treatment packages', template: 'What treatment packages are available?' },
];
export const AiChatPanel = ({ callerContext }: AiChatPanelProps) => {
const QUICK_ASK_MANAGER = [
{ label: 'Agent performance', template: 'Which agents have the highest appointment conversion rates this week?' },
{ label: 'Missed call risks', template: 'Which missed calls have been waiting the longest without a callback?' },
{ label: 'Pending leads', template: 'How many leads are still pending first contact?' },
{ label: 'Weekly summary', template: 'Give me a summary of this week\'s team performance — total calls, conversions, missed calls.' },
];
export const AiChatPanel = ({ callerContext, role = 'cc-agent' }: AiChatPanelProps) => {
const quickButtons = role === 'admin' ? QUICK_ASK_MANAGER : QUICK_ASK_AGENT;
const [messages, setMessages] = useState<ChatMessage[]>([]);
const [input, setInput] = useState('');
const [isLoading, setIsLoading] = useState(false);
@@ -112,7 +121,7 @@ export const AiChatPanel = ({ callerContext }: AiChatPanelProps) => {
{/* Quick ask buttons */}
{messages.length === 0 && (
<div className="mb-3 flex flex-wrap gap-1.5">
{QUICK_ASK_BUTTONS.map((btn) => (
{quickButtons.map((btn) => (
<button
key={btn.label}
onClick={() => handleQuickAsk(btn.template)}

View File

@@ -6,6 +6,7 @@ import {
faPhoneMissed,
} from '@fortawesome/pro-duotone-svg-icons';
import type { IconDefinition } from '@fortawesome/fontawesome-svg-core';
import { faCircleInfo } from '@fortawesome/pro-duotone-svg-icons';
import type { Call, Lead } from '@/types/entities';
type KpiCardProps = {
@@ -15,15 +16,19 @@ type KpiCardProps = {
iconColor: string;
iconBg: string;
subtitle?: string;
tooltip?: string;
};
const KpiCard = ({ label, value, icon, iconColor, iconBg, subtitle }: KpiCardProps) => (
const KpiCard = ({ label, value, icon, iconColor, iconBg, subtitle, tooltip }: KpiCardProps) => (
<div className="flex flex-1 items-center gap-4 rounded-xl border border-secondary bg-primary p-4 shadow-xs">
<div className={`flex size-10 shrink-0 items-center justify-center rounded-full ${iconBg}`}>
<FontAwesomeIcon icon={icon} className={`size-4 ${iconColor}`} />
</div>
<div className="flex flex-col">
<span className="text-xs font-medium text-tertiary">{label}</span>
<div className="flex flex-col flex-1">
<div className="flex items-center gap-1">
<span className="text-xs font-medium text-tertiary">{label}</span>
{tooltip && <FontAwesomeIcon icon={faCircleInfo} className="size-3 text-fg-quaternary" title={tooltip} />}
</div>
<span className="text-lg font-bold text-primary">{value}</span>
{subtitle && <span className="text-[10px] text-tertiary">{subtitle}</span>}
</div>
@@ -34,11 +39,15 @@ type MetricCardProps = {
label: string;
value: string;
description: string;
tooltip?: string;
};
const MetricCard = ({ label, value, description }: MetricCardProps) => (
const MetricCard = ({ label, value, description, tooltip }: MetricCardProps) => (
<div className="flex flex-1 flex-col gap-0.5 rounded-xl border border-secondary bg-primary p-4 shadow-xs">
<span className="text-xs font-medium text-tertiary">{label}</span>
<div className="flex items-center gap-1">
<span className="text-xs font-medium text-tertiary">{label}</span>
{tooltip && <FontAwesomeIcon icon={faCircleInfo} className="size-3 text-fg-quaternary" title={tooltip} />}
</div>
<span className="text-md font-bold text-primary">{value}</span>
<span className="text-[10px] text-tertiary">{description}</span>
</div>
@@ -49,6 +58,14 @@ const formatPercent = (value: number): string => {
return `${Math.round(value)}%`;
};
const formatMinutes = (minutes: number | null): string => {
if (minutes === null) return '—';
if (minutes < 60) return `${minutes}m`;
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
};
interface DashboardKpiProps {
calls: Call[];
leads: Lead[];
@@ -83,17 +100,18 @@ export const DashboardKpi = ({ calls, leads }: DashboardKpiProps) => {
return (
<div className="space-y-4">
<div className="grid grid-cols-2 gap-3 xl:grid-cols-4">
<KpiCard label="Total Calls" value={totalCalls} icon={faPhone} iconColor="text-fg-brand-primary" iconBg="bg-brand-secondary" />
<KpiCard label="Inbound" value={inboundCalls} icon={faPhoneArrowDownLeft} iconColor="text-fg-success-primary" iconBg="bg-success-secondary" />
<KpiCard label="Outbound" value={outboundCalls} icon={faPhoneArrowUpRight} iconColor="text-fg-brand-primary" iconBg="bg-brand-secondary" />
<KpiCard label="Total Calls" value={totalCalls} icon={faPhone} iconColor="text-fg-brand-primary" iconBg="bg-brand-secondary" tooltip="Total inbound + outbound calls in the selected period" />
<KpiCard label="Inbound" value={inboundCalls} icon={faPhoneArrowDownLeft} iconColor="text-fg-success-primary" iconBg="bg-success-secondary" tooltip="Calls received from patients/leads" />
<KpiCard label="Outbound" value={outboundCalls} icon={faPhoneArrowUpRight} iconColor="text-fg-brand-primary" iconBg="bg-brand-secondary" tooltip="Calls made by agents to patients/leads" />
<KpiCard label="Missed" value={missedCalls} icon={faPhoneMissed} iconColor="text-fg-error-primary" iconBg="bg-error-secondary"
subtitle={totalCalls > 0 ? `${formatPercent((missedCalls / totalCalls) * 100)} of total` : undefined} />
subtitle={totalCalls > 0 ? `${formatPercent((missedCalls / totalCalls) * 100)} of total` : undefined}
tooltip="Inbound calls that were not answered by any agent" />
</div>
<div className="grid grid-cols-2 gap-3 xl:grid-cols-4">
<MetricCard label="Avg Response" value={avgResponseTime !== null ? `${avgResponseTime}m` : '—'} description="Lead creation to first contact" />
<MetricCard label="Missed Callback" value={missedCallbackTime !== null ? `${missedCallbackTime}m` : '—'} description="Avg wait for missed callbacks" />
<MetricCard label="Call → Appt" value={formatPercent(callToAppt)} description="Calls resulting in bookings" />
<MetricCard label="Lead → Appt" value={formatPercent(leadToAppt)} description="Leads converted to appointments" />
<MetricCard label="Avg Response" value={formatMinutes(avgResponseTime)} description="Lead creation to first contact" tooltip="Average time between a lead being created and an agent making first contact" />
<MetricCard label="Missed Callback" value={formatMinutes(missedCallbackTime)} description="Avg wait for missed callbacks" tooltip="Average time missed calls have been waiting for a callback" />
<MetricCard label="Call → Appt" value={formatPercent(callToAppt)} description="Calls resulting in bookings" tooltip="Percentage of calls where the outcome was an appointment booking" />
<MetricCard label="Lead → Appt" value={formatPercent(leadToAppt)} description="Leads converted to appointments" tooltip="Percentage of leads that reached APPOINTMENT_SET or CONVERTED status" />
</div>
</div>
);