mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-04-12 02:38:15 +00:00
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:
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user