From 91a1f33d35ad86c2f5e068ba166b1273c729d1f7 Mon Sep 17 00:00:00 2001 From: saridsa2 Date: Wed, 15 Apr 2026 08:47:57 +0530 Subject: [PATCH] fix: notifications use real data + agent-detail follows new id scheme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. notification-bell: drop the DEMO_ALERTS fallback (Riya Mehta etc.). Empty state ("No active alerts") shows when the live computation returns nothing — which is the truthful state until thresholds are set on Agent records. 2. use-performance-alerts: bucket calls by c.agentId === agent.id when the relation is set; fall back to legacy agentName matching only for un-enriched rows. Fixes conversion% calc going to 0 after backfill. 3. agent-table: Link target uses agent.id (UUID or "legacy:NAME") so the URL is a stable identifier instead of a display string. 4. agent-detail: parse the route param into UUID vs legacy:NAME, filter calls by c.agentId or c.agentName accordingly, and resolve display name via the platform Agents list. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/dashboard/agent-table.tsx | 2 +- src/components/layout/notification-bell.tsx | 38 ++------------------- src/hooks/use-performance-alerts.ts | 6 +++- src/pages/agent-detail.tsx | 27 ++++++++++++--- 4 files changed, 32 insertions(+), 41 deletions(-) diff --git a/src/components/dashboard/agent-table.tsx b/src/components/dashboard/agent-table.tsx index e82fe75..fee70bf 100644 --- a/src/components/dashboard/agent-table.tsx +++ b/src/components/dashboard/agent-table.tsx @@ -95,7 +95,7 @@ export const AgentTable = ({ calls }: AgentTableProps) => { {(agent) => ( - +
{agent.name} diff --git a/src/components/layout/notification-bell.tsx b/src/components/layout/notification-bell.tsx index 2170da0..aaf1201 100644 --- a/src/components/layout/notification-bell.tsx +++ b/src/components/layout/notification-bell.tsx @@ -2,46 +2,14 @@ import { useState, useRef, useEffect } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faBell, faTriangleExclamation, faXmark, faCheck } from '@fortawesome/pro-duotone-svg-icons'; import { Badge } from '@/components/base/badges/badges'; -import { usePerformanceAlerts, type PerformanceAlert } from '@/hooks/use-performance-alerts'; +import { usePerformanceAlerts } from '@/hooks/use-performance-alerts'; import { cx } from '@/utils/cx'; -const DEMO_ALERTS: PerformanceAlert[] = [ - { id: 'demo-1', agent: 'Riya Mehta', type: 'Excessive Idle Time', value: '120m', severity: 'error', dismissed: false }, - { id: 'demo-2', agent: 'Arjun Kapoor', type: 'Excessive Idle Time', value: '180m', severity: 'error', dismissed: false }, - { id: 'demo-3', agent: 'Sneha Iyer', type: 'Excessive Idle Time', value: '250m', severity: 'error', dismissed: false }, - { id: 'demo-4', agent: 'Vikrant Desai', type: 'Excessive Idle Time', value: '300m', severity: 'error', dismissed: false }, - { id: 'demo-5', agent: 'Vikrant Desai', type: 'Low NPS', value: '35', severity: 'warning', dismissed: false }, - { id: 'demo-6', agent: 'Vikrant Desai', type: 'Low Conversion', value: '40%', severity: 'warning', dismissed: false }, - { id: 'demo-7', agent: 'Pooja Rao', type: 'Excessive Idle Time', value: '200m', severity: 'error', dismissed: false }, - { id: 'demo-8', agent: 'Mohammed Rizwan', type: 'Excessive Idle Time', value: '80m', severity: 'error', dismissed: false }, -]; - export const NotificationBell = () => { - const { alerts: liveAlerts, dismiss: liveDismiss, dismissAll: liveDismissAll } = usePerformanceAlerts(); - const [demoAlerts, setDemoAlerts] = useState(DEMO_ALERTS); - const [open, setOpen] = useState(true); + const { alerts, dismiss, dismissAll } = usePerformanceAlerts(); + const [open, setOpen] = useState(false); const panelRef = useRef(null); - // Use live alerts if available, otherwise demo - const alerts = liveAlerts.length > 0 ? liveAlerts : demoAlerts.filter(a => !a.dismissed); - const isDemo = liveAlerts.length === 0; - - const dismiss = (id: string) => { - if (isDemo) { - setDemoAlerts(prev => prev.map(a => a.id === id ? { ...a, dismissed: true } : a)); - } else { - liveDismiss(id); - } - }; - - const dismissAll = () => { - if (isDemo) { - setDemoAlerts(prev => prev.map(a => ({ ...a, dismissed: true }))); - } else { - liveDismissAll(); - } - }; - // Close on outside click useEffect(() => { if (!open) return; diff --git a/src/hooks/use-performance-alerts.ts b/src/hooks/use-performance-alerts.ts index 506db49..e37b904 100644 --- a/src/hooks/use-performance-alerts.ts +++ b/src/hooks/use-performance-alerts.ts @@ -47,7 +47,11 @@ export const usePerformanceAlerts = () => { let idx = 0; for (const agent of teamPerf.agents) { - const agentCalls = calls.filter(c => c.agentName === agent.name || c.agentName === agent.ozonetelAgentId); + const agentCalls = calls.filter((c) => { + if (c.agentId && agent.id && c.agentId === agent.id) return true; + if (!c.agentId && (c.agentName === agent.name || c.agentName === agent.ozonetelAgentId)) return true; + return false; + }); const totalCalls = agentCalls.length; const agentAppts = agentCalls.filter((c: any) => c.disposition === 'APPOINTMENT_BOOKED').length; const convPercent = totalCalls > 0 ? Math.round((agentAppts / totalCalls) * 100) : 0; diff --git a/src/pages/agent-detail.tsx b/src/pages/agent-detail.tsx index 7e62f32..4bb44ab 100644 --- a/src/pages/agent-detail.tsx +++ b/src/pages/agent-detail.tsx @@ -87,20 +87,39 @@ const DirectionIcon = ({ direction, status }: { direction: CallDirection | null; export const AgentDetailPage = () => { const { id } = useParams<{ id: string }>(); - const { calls, leads, loading } = useData(); + const { calls, leads, agents, loading } = useData(); - const agentName = id ? decodeURIComponent(id) : ''; + // Route param is either a platform Agent UUID (new bucketing) or + // "legacy:" for calls that haven't been enriched yet. + // Older bookmarks may still pass the raw display name — handle that too. + const rawId = id ? decodeURIComponent(id) : ''; + const isLegacy = rawId.startsWith('legacy:'); + const agentUuid = !isLegacy ? rawId : null; + const legacyName = isLegacy ? rawId.slice('legacy:'.length) : null; + + // Resolve display name: prefer Agent entity name, else the legacy string. + const agentName = useMemo(() => { + if (agentUuid) { + const a = agents.find((x: any) => x.id === agentUuid); + return a?.name ?? rawId; + } + return legacyName ?? ''; + }, [agentUuid, legacyName, agents, rawId]); const agentCalls = useMemo( () => calls - .filter((c) => c.agentName === agentName) + .filter((c) => { + if (agentUuid) return c.agentId === agentUuid; + if (legacyName) return !c.agentId && c.agentName === legacyName; + return false; + }) .sort((a, b) => { const dateA = a.startedAt ? new Date(a.startedAt).getTime() : 0; const dateB = b.startedAt ? new Date(b.startedAt).getTime() : 0; return dateB - dateA; }), - [calls, agentName], + [calls, agentUuid, legacyName], ); // Build lead name map for enrichment