fix: Team Dashboard PageHeader + Call Recordings agent name enrichment
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

- team-dashboard.tsx: replaced inline header with PageHeader (was the
  actual page being rendered, not team-performance.tsx)
- call-recordings.tsx: added agent relation to GraphQL query, render
  uses enriched agent.name with raw agentName fallback — matches
  Call History page pattern. Search + sort also use enriched name.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-17 06:14:45 +05:30
parent e175735d6c
commit fd7ee4fc1f
3 changed files with 48 additions and 42 deletions

View File

@@ -26,6 +26,7 @@ type RecordingRecord = {
callStatus: string | null;
callerNumber: { primaryPhoneNumber: string } | null;
agentName: string | null;
agent: { id: string; name: string; ozonetelAgentId: string } | null;
startedAt: string | null;
durationSec: number | null;
disposition: string | null;
@@ -35,7 +36,8 @@ type RecordingRecord = {
const QUERY = `{ calls(first: 200, orderBy: [{ startedAt: DescNullsLast }]) { edges { node {
id createdAt direction callStatus callerNumber { primaryPhoneNumber }
agentName startedAt durationSec disposition sla
agentName agent { id name ozonetelAgentId }
startedAt durationSec disposition sla
recording { primaryLinkUrl primaryLinkLabel }
} } } }`;
@@ -109,7 +111,7 @@ export const CallRecordingsPage = () => {
const dirColor: 'blue' | 'brand' = call.direction === 'INBOUND' ? 'blue' : 'brand';
switch (colId) {
case 'agent':
return <span className="text-sm text-primary">{call.agentName || '—'}</span>;
return <span className="text-sm text-primary">{call.agent?.name ?? call.agentName ?? '—'}</span>;
case 'caller':
return phone
? <PhoneActionCell phoneNumber={phone} displayNumber={formatPhone({ number: phone, callingCode: '+91' })} />
@@ -207,7 +209,7 @@ export const CallRecordingsPage = () => {
if (search.trim()) {
const q = search.toLowerCase();
result = result.filter(c =>
(c.agentName ?? '').toLowerCase().includes(q) ||
(c.agent?.name ?? c.agentName ?? '').toLowerCase().includes(q) ||
(c.callerNumber?.primaryPhoneNumber ?? '').includes(q) ||
(c.disposition ?? '').toLowerCase().includes(q),
);
@@ -217,7 +219,7 @@ export const CallRecordingsPage = () => {
const dir = sortDescriptor.direction === 'ascending' ? 1 : -1;
result = [...result].sort((a, b) => {
switch (sortDescriptor.column) {
case 'agent': return (a.agentName ?? '').localeCompare(b.agentName ?? '') * dir;
case 'agent': return (a.agent?.name ?? a.agentName ?? '').localeCompare(b.agent?.name ?? b.agentName ?? '') * dir;
case 'dateTime': {
const ta = a.startedAt ? new Date(a.startedAt).getTime() : 0;
const tb = b.startedAt ? new Date(b.startedAt).getTime() : 0;

View File

@@ -1,6 +1,7 @@
import { useMemo, useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSidebarFlip, faSidebar } from '@fortawesome/pro-duotone-svg-icons';
import { PageHeader } from '@/components/layout/page-header';
import { AiChatPanel } from '@/components/call-desk/ai-chat-panel';
import { DashboardKpi } from '@/components/dashboard/kpi-cards';
import { MissedQueue } from '@/components/dashboard/missed-queue';
@@ -55,13 +56,12 @@ export const TeamDashboardPage = () => {
return (
<div className="flex flex-1 flex-col overflow-hidden">
{/* Header */}
<div className="flex shrink-0 items-center justify-between border-b border-secondary px-6 py-3">
<div className="flex items-center gap-3">
<h1 className="text-lg font-bold text-primary">Team Dashboard</h1>
<span className="text-sm text-tertiary">{dateRangeLabel}</span>
</div>
<div className="flex items-center gap-2">
<PageHeader
title="Team Dashboard"
subtitle={dateRangeLabel}
infoText="Aggregated call metrics, agent performance, and operational alerts."
controls={
<>
<div className="flex rounded-lg border border-secondary overflow-hidden">
{(['today', 'week', 'month'] as const).map((range) => (
<button
@@ -83,8 +83,9 @@ export const TeamDashboardPage = () => {
>
<FontAwesomeIcon icon={aiOpen ? faSidebarFlip : faSidebar} className="size-4" />
</button>
</div>
</div>
</>
}
/>
<div className="flex flex-1 overflow-hidden">
{/* Main content — scrollable column with KPIs pinned at the

View File

@@ -291,25 +291,28 @@ export const TeamPerformancePage = () => {
if (loading) {
return (
<>
<PageHeader title="Team Performance" infoText="Aggregated metrics across all agents." />
<div className="flex flex-1 flex-col overflow-hidden">
<PageHeader title="Team Dashboard" infoText="Aggregated metrics across all agents." />
<div className="flex flex-1 items-center justify-center">
<p className="text-sm text-tertiary">Loading team performance...</p>
</div>
</>
</div>
);
}
return (
<>
<PageHeader title="Team Performance" infoText="Aggregated metrics across all agents." />
<div className="flex flex-1 flex-col overflow-hidden">
<PageHeader
title="Team Dashboard"
infoText="Aggregated metrics across all agents."
controls={<DateFilter value={range} onChange={setRange} />}
/>
<div className="flex flex-1 flex-col overflow-y-auto">
{/* Section 1: Key Metrics */}
<div className="px-6 pt-5">
<div className="flex items-center justify-between mb-4">
<div className="mb-4">
<h3 className="text-sm font-semibold text-secondary">Key Metrics</h3>
<DateFilter value={range} onChange={setRange} />
</div>
<div className="grid grid-cols-5 gap-3">
<KpiCard icon={faUsers} value={activeAgents} label="Active Agents" color="bg-brand-secondary" />
@@ -510,6 +513,6 @@ export const TeamPerformancePage = () => {
</div>
)}
</div>
</>
</div>
);
};