ahs-cti 0.0.2-beta.8 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +425 -294
- package/dist/agentDetailReport-XSBMOUMW.mjs +12 -0
- package/dist/agentDetailReport-XSBMOUMW.mjs.map +1 -0
- package/dist/agentPerformanceReport-UT6QZWYD.mjs +533 -0
- package/dist/agentPerformanceReport-UT6QZWYD.mjs.map +1 -0
- package/dist/auditReport-R67BYA4Z.mjs +15 -0
- package/dist/auditReport-R67BYA4Z.mjs.map +1 -0
- package/dist/callHistory-PHGY224F.mjs +805 -0
- package/dist/callHistory-PHGY224F.mjs.map +1 -0
- package/dist/campaigns-JSMYHHDF.mjs +3049 -0
- package/dist/campaigns-JSMYHHDF.mjs.map +1 -0
- package/dist/caroQualityAuditDashboard-7X44HRZL.mjs +66 -0
- package/dist/caroQualityAuditDashboard-7X44HRZL.mjs.map +1 -0
- package/dist/caroVoiceAI-OZAB7LK4.mjs +21 -0
- package/dist/caroVoiceAI-OZAB7LK4.mjs.map +1 -0
- package/dist/cdrReport-44LN5VUX.mjs +819 -0
- package/dist/cdrReport-44LN5VUX.mjs.map +1 -0
- package/dist/chunk-6ICPXSN6.mjs +61 -0
- package/dist/chunk-6ICPXSN6.mjs.map +1 -0
- package/dist/chunk-CXULBAK6.mjs +493 -0
- package/dist/chunk-CXULBAK6.mjs.map +1 -0
- package/dist/chunk-FVXHNBYV.mjs +82 -0
- package/dist/chunk-FVXHNBYV.mjs.map +1 -0
- package/dist/chunk-G6KDIN5W.mjs +749 -0
- package/dist/chunk-G6KDIN5W.mjs.map +1 -0
- package/dist/chunk-GGACEO3I.mjs +180 -0
- package/dist/chunk-GGACEO3I.mjs.map +1 -0
- package/dist/chunk-HBR2JS4C.mjs +95 -0
- package/dist/chunk-HBR2JS4C.mjs.map +1 -0
- package/dist/chunk-HRM6S6J2.mjs +61 -0
- package/dist/chunk-HRM6S6J2.mjs.map +1 -0
- package/dist/chunk-JOZ4YQMR.mjs +116 -0
- package/dist/chunk-JOZ4YQMR.mjs.map +1 -0
- package/dist/chunk-O2XGWZLT.mjs +1060 -0
- package/dist/chunk-O2XGWZLT.mjs.map +1 -0
- package/dist/chunk-RZZQ42MG.mjs +115 -0
- package/dist/chunk-RZZQ42MG.mjs.map +1 -0
- package/dist/chunk-UZF5Q3GR.mjs +678 -0
- package/dist/chunk-UZF5Q3GR.mjs.map +1 -0
- package/dist/chunk-VQCHBU2Q.mjs +27 -0
- package/dist/chunk-VQCHBU2Q.mjs.map +1 -0
- package/dist/chunk-WWWM33FY.mjs +57 -0
- package/dist/chunk-WWWM33FY.mjs.map +1 -0
- package/dist/index.d.mts +795 -112
- package/dist/index.d.ts +796 -112
- package/dist/index.js +13690 -2103
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2335 -2250
- package/dist/index.mjs.map +1 -1
- package/dist/liveStatus-AHKS4XLW.mjs +1077 -0
- package/dist/liveStatus-AHKS4XLW.mjs.map +1 -0
- package/dist/loginReport-7GBMZP55.mjs +828 -0
- package/dist/loginReport-7GBMZP55.mjs.map +1 -0
- package/dist/managementDashboard-TYON77NW.mjs +529 -0
- package/dist/managementDashboard-TYON77NW.mjs.map +1 -0
- package/dist/qualityAuditDashboard-AGJH5VVN.mjs +66 -0
- package/dist/qualityAuditDashboard-AGJH5VVN.mjs.map +1 -0
- package/package.json +16 -6
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PageHeader
|
|
3
|
+
} from "./chunk-JOZ4YQMR.mjs";
|
|
4
|
+
import {
|
|
5
|
+
AppButton
|
|
6
|
+
} from "./chunk-HBR2JS4C.mjs";
|
|
7
|
+
import {
|
|
8
|
+
SDKProvider
|
|
9
|
+
} from "./chunk-6ICPXSN6.mjs";
|
|
10
|
+
import {
|
|
11
|
+
END_POINT,
|
|
12
|
+
axios_default,
|
|
13
|
+
deepFindArray,
|
|
14
|
+
sdkStateManager
|
|
15
|
+
} from "./chunk-O2XGWZLT.mjs";
|
|
16
|
+
import {
|
|
17
|
+
__spreadProps,
|
|
18
|
+
__spreadValues
|
|
19
|
+
} from "./chunk-VQCHBU2Q.mjs";
|
|
20
|
+
|
|
21
|
+
// call-control-sdk/lib/pages/agentDetailReport/index.tsx
|
|
22
|
+
import { useState as useState2, useEffect, useMemo, useCallback as useCallback2 } from "react";
|
|
23
|
+
import {
|
|
24
|
+
Box,
|
|
25
|
+
Typography,
|
|
26
|
+
Table,
|
|
27
|
+
TableBody,
|
|
28
|
+
TableCell,
|
|
29
|
+
TableContainer,
|
|
30
|
+
TableHead,
|
|
31
|
+
TableRow,
|
|
32
|
+
TablePagination,
|
|
33
|
+
IconButton,
|
|
34
|
+
Chip,
|
|
35
|
+
CircularProgress,
|
|
36
|
+
Alert
|
|
37
|
+
} from "@mui/material";
|
|
38
|
+
import SearchIcon from "@mui/icons-material/Search";
|
|
39
|
+
import RefreshIcon from "@mui/icons-material/Refresh";
|
|
40
|
+
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
|
41
|
+
import FileDownloadIcon from "@mui/icons-material/FileDownload";
|
|
42
|
+
import Tooltip from "@mui/material/Tooltip";
|
|
43
|
+
import {
|
|
44
|
+
BarChart,
|
|
45
|
+
Bar,
|
|
46
|
+
XAxis,
|
|
47
|
+
YAxis,
|
|
48
|
+
CartesianGrid,
|
|
49
|
+
Tooltip as RTooltip,
|
|
50
|
+
PieChart,
|
|
51
|
+
Pie,
|
|
52
|
+
Cell,
|
|
53
|
+
Legend,
|
|
54
|
+
ResponsiveContainer,
|
|
55
|
+
LabelList
|
|
56
|
+
} from "recharts";
|
|
57
|
+
import dayjs from "dayjs";
|
|
58
|
+
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
|
|
59
|
+
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
|
60
|
+
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
|
|
61
|
+
|
|
62
|
+
// call-control-sdk/lib/pages/agentDetailReport/useAgentDetailReport.ts
|
|
63
|
+
import { useCallback, useState } from "react";
|
|
64
|
+
function buildCdrQs(params) {
|
|
65
|
+
const qs = new URLSearchParams();
|
|
66
|
+
if (params.start_date) qs.append("start_date", params.start_date);
|
|
67
|
+
if (params.end_date) qs.append("end_date", params.end_date);
|
|
68
|
+
if (params.agent_id) for (const id of params.agent_id) qs.append("agent_id", String(id));
|
|
69
|
+
if (params.queue_id) for (const id of params.queue_id) qs.append("queue_id", String(id));
|
|
70
|
+
if (params.call_type) qs.append("call_type", params.call_type);
|
|
71
|
+
if (params.status) qs.append("status", params.status);
|
|
72
|
+
if (params.search) qs.append("search", params.search);
|
|
73
|
+
if (params.page) qs.append("page", String(params.page));
|
|
74
|
+
if (params.pageSize) qs.append("pageSize", String(params.pageSize));
|
|
75
|
+
const str = qs.toString();
|
|
76
|
+
return str ? `?${str}` : "";
|
|
77
|
+
}
|
|
78
|
+
async function fetchRecordingBlob(callUuid) {
|
|
79
|
+
const res = await axios_default.get(END_POINT.RECORDING_BY_CALL(callUuid), {
|
|
80
|
+
responseType: "blob"
|
|
81
|
+
});
|
|
82
|
+
return res.data;
|
|
83
|
+
}
|
|
84
|
+
async function getAgentDetailReport(agentId, params) {
|
|
85
|
+
const qs = new URLSearchParams();
|
|
86
|
+
if (params == null ? void 0 : params.start_date) qs.append("start_date", params.start_date);
|
|
87
|
+
if (params == null ? void 0 : params.end_date) qs.append("end_date", params.end_date);
|
|
88
|
+
const qsStr = qs.toString();
|
|
89
|
+
const res = await axios_default.get(
|
|
90
|
+
`${END_POINT.AGENT_DETAIL_REPORT(agentId)}${qsStr ? `?${qsStr}` : ""}`
|
|
91
|
+
);
|
|
92
|
+
return res.data;
|
|
93
|
+
}
|
|
94
|
+
async function getCdrReport(params) {
|
|
95
|
+
const qs = buildCdrQs(params);
|
|
96
|
+
const res = await axios_default.get(`${END_POINT.CDR_REPORT}${qs}`);
|
|
97
|
+
return res.data;
|
|
98
|
+
}
|
|
99
|
+
async function getRecordingByCall(callUuid) {
|
|
100
|
+
return fetchRecordingBlob(callUuid);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// call-control-sdk/lib/pages/agentDetailReport/index.tsx
|
|
104
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
105
|
+
var C = {
|
|
106
|
+
navy: "#0d2a56",
|
|
107
|
+
blue: "#1565c8",
|
|
108
|
+
green: "#0a9a62",
|
|
109
|
+
amber: "#c47c00",
|
|
110
|
+
red: "#cc2a2a",
|
|
111
|
+
purple: "#6b3fbf",
|
|
112
|
+
teal: "#0b7a8f",
|
|
113
|
+
bg: "#fff",
|
|
114
|
+
surface: "#fff",
|
|
115
|
+
s2: "#f7f9fc",
|
|
116
|
+
s3: "#eef2f7",
|
|
117
|
+
b1: "rgba(20,50,100,.07)",
|
|
118
|
+
b2: "rgba(20,50,100,.13)",
|
|
119
|
+
t1: "#0d1e35",
|
|
120
|
+
t2: "#364f6e",
|
|
121
|
+
t3: "#7a93b5",
|
|
122
|
+
t4: "#bccad9",
|
|
123
|
+
blt: "#e8f0fc",
|
|
124
|
+
glt: "#e4f6ef",
|
|
125
|
+
alt: "#fdf3e0",
|
|
126
|
+
rlt: "#fde8e8",
|
|
127
|
+
plt: "#f0eafa",
|
|
128
|
+
tlt: "#e3f4f7"
|
|
129
|
+
};
|
|
130
|
+
var monoSx = { fontFamily: "'JetBrains Mono', monospace", fontSize: 11, fontWeight: 600 };
|
|
131
|
+
var QUEUE_COLORS = [C.blue, C.green, C.purple, C.amber, C.teal, C.red, C.navy];
|
|
132
|
+
function StatCard({ value, label, color }) {
|
|
133
|
+
return /* @__PURE__ */ jsxs(Box, { sx: {
|
|
134
|
+
px: 1.5,
|
|
135
|
+
py: 1.2,
|
|
136
|
+
borderRadius: "10px",
|
|
137
|
+
backgroundColor: "#fafafa",
|
|
138
|
+
border: "1px solid #e0e0e0",
|
|
139
|
+
borderLeftWidth: "3px",
|
|
140
|
+
borderLeftStyle: "solid",
|
|
141
|
+
borderLeftColor: color
|
|
142
|
+
}, children: [
|
|
143
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontFamily: "poppins, Arial, sans-serif", fontSize: "0.72rem", fontWeight: 600, color: "#888", mb: 0.3 }, children: label }),
|
|
144
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontFamily: "poppins, Arial, sans-serif", fontSize: "0.95rem", fontWeight: 700, color: "#1a1a1a" }, children: value })
|
|
145
|
+
] });
|
|
146
|
+
}
|
|
147
|
+
function StatusChip({ status }) {
|
|
148
|
+
const s = status == null ? void 0 : status.toLowerCase();
|
|
149
|
+
const isAns = s === "answered";
|
|
150
|
+
const isAband = s === "abandoned";
|
|
151
|
+
return /* @__PURE__ */ jsx(Chip, { label: status || "\u2014", size: "small", sx: {
|
|
152
|
+
fontSize: 11,
|
|
153
|
+
fontWeight: 700,
|
|
154
|
+
height: 22,
|
|
155
|
+
background: isAns ? C.glt : isAband ? C.alt : C.rlt,
|
|
156
|
+
color: isAns ? C.green : isAband ? C.amber : C.red,
|
|
157
|
+
border: `1px solid ${isAns ? "rgba(10,154,98,.2)" : isAband ? "rgba(196,124,0,.2)" : "rgba(204,42,42,.2)"}`,
|
|
158
|
+
fontFamily: "'JetBrains Mono', monospace"
|
|
159
|
+
} });
|
|
160
|
+
}
|
|
161
|
+
function CallTypeChip({ type }) {
|
|
162
|
+
const isIn = (type == null ? void 0 : type.toLowerCase()) === "inbound";
|
|
163
|
+
return /* @__PURE__ */ jsx(Chip, { label: type || "\u2014", size: "small", sx: {
|
|
164
|
+
fontSize: 11,
|
|
165
|
+
fontWeight: 700,
|
|
166
|
+
height: 22,
|
|
167
|
+
background: isIn ? C.glt : C.blt,
|
|
168
|
+
color: isIn ? C.green : C.blue,
|
|
169
|
+
border: `1px solid ${isIn ? "rgba(10,154,98,.2)" : "rgba(21,101,200,.2)"}`,
|
|
170
|
+
fontFamily: "'JetBrains Mono', monospace"
|
|
171
|
+
} });
|
|
172
|
+
}
|
|
173
|
+
function RecordingCell({ callUuid, recordingPath }) {
|
|
174
|
+
const [audioUrl, setAudioUrl] = useState2(null);
|
|
175
|
+
const [loading, setLoading] = useState2(false);
|
|
176
|
+
const [error, setError] = useState2(false);
|
|
177
|
+
if (!recordingPath) {
|
|
178
|
+
return /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.3 }, children: [
|
|
179
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "No recording available", children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(IconButton, { size: "small", disabled: true, sx: { p: 0.4 }, children: /* @__PURE__ */ jsx(PlayArrowIcon, { sx: { fontSize: 16 } }) }) }) }),
|
|
180
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "No recording available", children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(IconButton, { size: "small", disabled: true, sx: { p: 0.4 }, children: /* @__PURE__ */ jsx(FileDownloadIcon, { sx: { fontSize: 16 } }) }) }) })
|
|
181
|
+
] });
|
|
182
|
+
}
|
|
183
|
+
const handlePlay = async () => {
|
|
184
|
+
if (audioUrl) return;
|
|
185
|
+
try {
|
|
186
|
+
setLoading(true);
|
|
187
|
+
setError(false);
|
|
188
|
+
const blob = await getRecordingByCall(callUuid);
|
|
189
|
+
const url = URL.createObjectURL(blob);
|
|
190
|
+
setAudioUrl(url);
|
|
191
|
+
} catch (e) {
|
|
192
|
+
setError(true);
|
|
193
|
+
} finally {
|
|
194
|
+
setLoading(false);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
const handleDownload = async () => {
|
|
198
|
+
try {
|
|
199
|
+
const blob = audioUrl ? await fetch(audioUrl).then((r) => r.blob()) : await getRecordingByCall(callUuid);
|
|
200
|
+
const url = URL.createObjectURL(blob);
|
|
201
|
+
const a = document.createElement("a");
|
|
202
|
+
a.href = url;
|
|
203
|
+
a.download = `recording_${callUuid}.wav`;
|
|
204
|
+
a.click();
|
|
205
|
+
URL.revokeObjectURL(url);
|
|
206
|
+
} catch (e) {
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
if (audioUrl) {
|
|
210
|
+
return /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 }, children: [
|
|
211
|
+
/* @__PURE__ */ jsx(
|
|
212
|
+
"audio",
|
|
213
|
+
{
|
|
214
|
+
controls: true,
|
|
215
|
+
autoPlay: true,
|
|
216
|
+
src: audioUrl,
|
|
217
|
+
onEnded: () => {
|
|
218
|
+
if (audioUrl.startsWith("blob:")) URL.revokeObjectURL(audioUrl);
|
|
219
|
+
setAudioUrl(null);
|
|
220
|
+
},
|
|
221
|
+
style: { width: 140, height: 30 }
|
|
222
|
+
}
|
|
223
|
+
),
|
|
224
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Download", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: handleDownload, sx: { color: C.blue, p: 0.4 }, children: /* @__PURE__ */ jsx(FileDownloadIcon, { sx: { fontSize: 16 } }) }) })
|
|
225
|
+
] });
|
|
226
|
+
}
|
|
227
|
+
return /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.3 }, children: [
|
|
228
|
+
/* @__PURE__ */ jsx(Tooltip, { title: error ? "Recording unavailable" : "Play", children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: handlePlay, disabled: loading || error, sx: { color: C.green, p: 0.4 }, children: loading ? /* @__PURE__ */ jsx(CircularProgress, { size: 14 }) : /* @__PURE__ */ jsx(PlayArrowIcon, { sx: { fontSize: 16 } }) }) }) }),
|
|
229
|
+
/* @__PURE__ */ jsx(Tooltip, { title: error ? "Recording unavailable" : "Download", children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: handleDownload, disabled: loading || error, sx: { color: C.blue, p: 0.4 }, children: /* @__PURE__ */ jsx(FileDownloadIcon, { sx: { fontSize: 16 } }) }) }) }),
|
|
230
|
+
error && /* @__PURE__ */ jsx(Chip, { label: "No File", size: "small", sx: { fontSize: "0.6rem", height: 18 } })
|
|
231
|
+
] });
|
|
232
|
+
}
|
|
233
|
+
var cdrHeadSx = {
|
|
234
|
+
bgcolor: "#f1f1f1",
|
|
235
|
+
color: "#333",
|
|
236
|
+
fontWeight: 600,
|
|
237
|
+
whiteSpace: "nowrap",
|
|
238
|
+
fontSize: "14px",
|
|
239
|
+
py: 0.7,
|
|
240
|
+
px: 1
|
|
241
|
+
};
|
|
242
|
+
function AgentDetailReportContent({ agentRow: propAgentRow, onBack, from, initialStartDate, initialEndDate, initialQuickRange } = {}) {
|
|
243
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
244
|
+
const agentRow = propAgentRow || null;
|
|
245
|
+
const isFromReportsList = from === "REPORTS_LIST";
|
|
246
|
+
const agentIdFromRow = isFromReportsList ? ((_a = agentRow == null ? void 0 : agentRow.agentDetails) == null ? void 0 : _a.agentId) || "" : sdkStateManager.getState().agentId || ((_b = agentRow == null ? void 0 : agentRow.agentDetails) == null ? void 0 : _b.agentId) || "";
|
|
247
|
+
const [quickRange, setQuickRange] = useState2(initialQuickRange || "today");
|
|
248
|
+
const [startDate, setStartDate] = useState2(initialStartDate != null ? initialStartDate : dayjs());
|
|
249
|
+
const [endDate, setEndDate] = useState2(initialEndDate != null ? initialEndDate : dayjs());
|
|
250
|
+
const syncFilterToUrl = (_sd, _ed, _range) => {
|
|
251
|
+
};
|
|
252
|
+
const [activeTab, setActiveTab] = useState2(0);
|
|
253
|
+
const [detail, setDetail] = useState2(null);
|
|
254
|
+
const [detailLoading, setDetailLoading] = useState2(!isFromReportsList);
|
|
255
|
+
const [detailError, setDetailError] = useState2("");
|
|
256
|
+
const [cdrRecords, setCdrRecords] = useState2([]);
|
|
257
|
+
const [cdrTotal, setCdrTotal] = useState2(0);
|
|
258
|
+
const [cdrPage, setCdrPage] = useState2(0);
|
|
259
|
+
const [cdrPageSize, setCdrPageSize] = useState2(10);
|
|
260
|
+
const [cdrLoading, setCdrLoading] = useState2(false);
|
|
261
|
+
const [cdrError, setCdrError] = useState2("");
|
|
262
|
+
const profile = (detail == null ? void 0 : detail.profile) || null;
|
|
263
|
+
const agentName = (profile == null ? void 0 : profile.fullName) || ((_c = agentRow == null ? void 0 : agentRow.agentDetails) == null ? void 0 : _c.fullName) || "Unknown Agent";
|
|
264
|
+
const showInitialSpinner = !isFromReportsList && detailLoading && !profile && !((_d = agentRow == null ? void 0 : agentRow.agentDetails) == null ? void 0 : _d.fullName);
|
|
265
|
+
const setQuick = (range) => {
|
|
266
|
+
setQuickRange(range);
|
|
267
|
+
const today = dayjs();
|
|
268
|
+
let sd = today;
|
|
269
|
+
const ed = today;
|
|
270
|
+
if (range === "week") sd = today.startOf("week");
|
|
271
|
+
else if (range === "month") sd = today.startOf("month");
|
|
272
|
+
setStartDate(sd);
|
|
273
|
+
setEndDate(ed);
|
|
274
|
+
syncFilterToUrl(sd, ed, range);
|
|
275
|
+
};
|
|
276
|
+
const fetchDetailFn = async (sd, ed) => {
|
|
277
|
+
if (!agentIdFromRow) return;
|
|
278
|
+
try {
|
|
279
|
+
setDetailLoading(true);
|
|
280
|
+
setDetailError("");
|
|
281
|
+
const res = await getAgentDetailReport(agentIdFromRow, {
|
|
282
|
+
start_date: sd.startOf("day").format("YYYY-MM-DDTHH:mm:ss"),
|
|
283
|
+
end_date: ed.isSame(dayjs(), "day") ? dayjs().format("YYYY-MM-DDTHH:mm:ss") : ed.endOf("day").format("YYYY-MM-DDTHH:mm:ss")
|
|
284
|
+
});
|
|
285
|
+
setDetail(res);
|
|
286
|
+
} catch (err) {
|
|
287
|
+
setDetailError((err == null ? void 0 : err.message) || "Failed to load agent detail");
|
|
288
|
+
} finally {
|
|
289
|
+
setDetailLoading(false);
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
const fetchCdrFn = async (sd, ed, page, pageSize) => {
|
|
293
|
+
var _a2, _b2, _c2, _d2, _e2;
|
|
294
|
+
if (!agentIdFromRow) return;
|
|
295
|
+
try {
|
|
296
|
+
setCdrLoading(true);
|
|
297
|
+
setCdrError("");
|
|
298
|
+
const params = {
|
|
299
|
+
start_date: sd.startOf("day").format("YYYY-MM-DDTHH:mm:ss"),
|
|
300
|
+
end_date: ed.isSame(dayjs(), "day") ? dayjs().format("YYYY-MM-DDTHH:mm:ss") : ed.endOf("day").format("YYYY-MM-DDTHH:mm:ss"),
|
|
301
|
+
search: agentIdFromRow,
|
|
302
|
+
page: page + 1,
|
|
303
|
+
pageSize
|
|
304
|
+
};
|
|
305
|
+
const res = await getCdrReport(params);
|
|
306
|
+
setCdrRecords(deepFindArray(res, ["data", "records"]));
|
|
307
|
+
setCdrTotal((_e2 = (_d2 = (_b2 = (_a2 = res == null ? void 0 : res.records) == null ? void 0 : _a2.total) != null ? _b2 : res == null ? void 0 : res.total) != null ? _d2 : (_c2 = res == null ? void 0 : res.data) == null ? void 0 : _c2.total) != null ? _e2 : 0);
|
|
308
|
+
} catch (err) {
|
|
309
|
+
setCdrError((err == null ? void 0 : err.message) || "Failed to load call history");
|
|
310
|
+
} finally {
|
|
311
|
+
setCdrLoading(false);
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
const handleSearch = (sd, ed) => {
|
|
315
|
+
const fromDate = sd != null ? sd : startDate;
|
|
316
|
+
const toDate = ed != null ? ed : endDate;
|
|
317
|
+
if (!fromDate || !toDate) return;
|
|
318
|
+
fetchDetailFn(fromDate, toDate);
|
|
319
|
+
setCdrPage(0);
|
|
320
|
+
if (activeTab === 1) fetchCdrFn(fromDate, toDate, 0, cdrPageSize);
|
|
321
|
+
};
|
|
322
|
+
useEffect(() => {
|
|
323
|
+
if (agentIdFromRow) fetchDetailFn(startDate != null ? startDate : dayjs(), endDate != null ? endDate : dayjs());
|
|
324
|
+
}, [agentIdFromRow]);
|
|
325
|
+
useEffect(() => {
|
|
326
|
+
if (activeTab === 1 && startDate && endDate) {
|
|
327
|
+
fetchCdrFn(startDate, endDate, cdrPage, cdrPageSize);
|
|
328
|
+
}
|
|
329
|
+
}, [activeTab, cdrPage, cdrPageSize]);
|
|
330
|
+
const hourlyCallData = useMemo(() => {
|
|
331
|
+
return ((detail == null ? void 0 : detail.hourlyCalls) || []).map((h) => ({
|
|
332
|
+
hour: String(h.hourOfDay).padStart(2, "0"),
|
|
333
|
+
answered: h.answeredCalls,
|
|
334
|
+
abandoned: h.missedCalls
|
|
335
|
+
}));
|
|
336
|
+
}, [detail]);
|
|
337
|
+
const timeUtilData = useMemo(() => {
|
|
338
|
+
const tu = detail == null ? void 0 : detail.timeUtilisation;
|
|
339
|
+
if (!tu) return [];
|
|
340
|
+
const total = tu.totalLoginSeconds || 1;
|
|
341
|
+
return [
|
|
342
|
+
{ name: "On Call / Handle", value: tu.onCallSeconds, pct: tu.onCallSeconds / total * 100, formatted: tu.onCallFormatted, color: C.blue },
|
|
343
|
+
{ name: "Idle", value: tu.idleSeconds, pct: tu.idleSeconds / total * 100, formatted: tu.idleFormatted, color: "#e8a0a0" },
|
|
344
|
+
{ name: "Break", value: tu.breakSeconds, pct: tu.breakSeconds / total * 100, formatted: tu.breakFormatted, color: C.amber }
|
|
345
|
+
];
|
|
346
|
+
}, [detail]);
|
|
347
|
+
const talkDurationBuckets = useMemo(() => {
|
|
348
|
+
const items = [...(detail == null ? void 0 : detail.talkDistribution) || []].sort((a, b) => a.bucketOrder - b.bucketOrder);
|
|
349
|
+
const total = items.reduce((s, b) => s + (b.callCount || 0), 0);
|
|
350
|
+
return items.map((b) => ({
|
|
351
|
+
bucket: b.bucketLabel,
|
|
352
|
+
count: b.callCount,
|
|
353
|
+
minSeconds: b.minSeconds,
|
|
354
|
+
maxSeconds: b.maxSeconds,
|
|
355
|
+
pct: total > 0 ? b.callCount / total * 100 : 0
|
|
356
|
+
}));
|
|
357
|
+
}, [detail]);
|
|
358
|
+
const talkDurationTotal = useMemo(
|
|
359
|
+
() => talkDurationBuckets.reduce((s, b) => s + (b.count || 0), 0),
|
|
360
|
+
[talkDurationBuckets]
|
|
361
|
+
);
|
|
362
|
+
const queuePieData = useMemo(() => {
|
|
363
|
+
return ((detail == null ? void 0 : detail.callsByQueue) || []).map((q, i) => ({
|
|
364
|
+
name: q.queueName,
|
|
365
|
+
value: q.callCount,
|
|
366
|
+
pctOfTotal: q.pctOfTotal,
|
|
367
|
+
color: QUEUE_COLORS[i % QUEUE_COLORS.length]
|
|
368
|
+
}));
|
|
369
|
+
}, [detail]);
|
|
370
|
+
const queueMetrics = useMemo(() => {
|
|
371
|
+
const items = (detail == null ? void 0 : detail.queueMetrics) || [];
|
|
372
|
+
const grandTotal = items.reduce((sum, q) => sum + q.totalCalls, 0);
|
|
373
|
+
return items.map((q, i) => __spreadProps(__spreadValues({}, q), {
|
|
374
|
+
pctOfTotal: grandTotal > 0 ? q.totalCalls / grandTotal * 100 : 0,
|
|
375
|
+
answerRate: q.totalCalls > 0 ? q.answeredCalls / q.totalCalls * 100 : 0,
|
|
376
|
+
color: QUEUE_COLORS[i % QUEUE_COLORS.length]
|
|
377
|
+
}));
|
|
378
|
+
}, [detail]);
|
|
379
|
+
const hourlyChartTitle = useMemo(() => {
|
|
380
|
+
if (quickRange === "today") return "Hourly Call Volume \u2014 Today";
|
|
381
|
+
if (quickRange === "month") return `Hourly Call Volume \u2014 ${dayjs().format("MMMM YYYY")}`;
|
|
382
|
+
if (quickRange === "week") return "Hourly Call Volume \u2014 This Week";
|
|
383
|
+
if (startDate && endDate) {
|
|
384
|
+
if (startDate.isSame(endDate, "day")) return `Hourly Call Volume \u2014 ${startDate.format("DD MMM YYYY")}`;
|
|
385
|
+
return `Hourly Call Volume \u2014 ${startDate.format("DD MMM YYYY")} to ${endDate.format("DD MMM YYYY")}`;
|
|
386
|
+
}
|
|
387
|
+
return "Hourly Call Volume";
|
|
388
|
+
}, [quickRange, startDate, endDate]);
|
|
389
|
+
const handleBack = useCallback2(() => {
|
|
390
|
+
onBack == null ? void 0 : onBack();
|
|
391
|
+
}, [onBack]);
|
|
392
|
+
if (!agentIdFromRow) {
|
|
393
|
+
return /* @__PURE__ */ jsxs(Box, { sx: { p: 4, textAlign: "center" }, children: [
|
|
394
|
+
/* @__PURE__ */ jsx(Typography, { sx: { color: C.t3, mb: 2 }, children: "No agent selected." }),
|
|
395
|
+
isFromReportsList && /* @__PURE__ */ jsx(AppButton, { onClick: handleBack, children: "Go Back" })
|
|
396
|
+
] });
|
|
397
|
+
}
|
|
398
|
+
if (showInitialSpinner) {
|
|
399
|
+
return /* @__PURE__ */ jsx(Box, { sx: { display: "flex", alignItems: "center", justifyContent: "center", minHeight: 400 }, children: /* @__PURE__ */ jsx(CircularProgress, { size: 36 }) });
|
|
400
|
+
}
|
|
401
|
+
const tabs = ["Overview", "Call History", "Break Details"];
|
|
402
|
+
return /* @__PURE__ */ jsx(LocalizationProvider, { dateAdapter: AdapterDayjs, children: /* @__PURE__ */ jsxs(Box, { sx: { background: C.bg, minHeight: "100vh" }, children: [
|
|
403
|
+
/* @__PURE__ */ jsx(
|
|
404
|
+
PageHeader,
|
|
405
|
+
{
|
|
406
|
+
title: agentName,
|
|
407
|
+
showBack: isFromReportsList,
|
|
408
|
+
onBack: handleBack
|
|
409
|
+
}
|
|
410
|
+
),
|
|
411
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap", pb: 1.5, px: 2 }, children: [
|
|
412
|
+
/* @__PURE__ */ jsx(DatePicker, { value: startDate, onChange: (val) => {
|
|
413
|
+
const d = val;
|
|
414
|
+
setStartDate(d);
|
|
415
|
+
setQuickRange("custom");
|
|
416
|
+
if (d && endDate) syncFilterToUrl(d, endDate, "custom");
|
|
417
|
+
}, slotProps: { textField: { size: "small", sx: { width: 150, "& .MuiOutlinedInput-root": { borderRadius: "7px", fontSize: 12, height: 30 }, "& .MuiInputBase-input": { py: "4px", fontSize: 12 } } } } }),
|
|
418
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontSize: 12, color: C.t3 }, children: "to" }),
|
|
419
|
+
/* @__PURE__ */ jsx(DatePicker, { value: endDate, onChange: (val) => {
|
|
420
|
+
const d = val;
|
|
421
|
+
setEndDate(d);
|
|
422
|
+
setQuickRange("custom");
|
|
423
|
+
if (startDate && d) syncFilterToUrl(startDate, d, "custom");
|
|
424
|
+
}, slotProps: { textField: { size: "small", sx: { width: 150, "& .MuiOutlinedInput-root": { borderRadius: "7px", fontSize: 12, height: 30 }, "& .MuiInputBase-input": { py: "4px", fontSize: 12 } } } } }),
|
|
425
|
+
/* @__PURE__ */ jsx(AppButton, { variant: quickRange === "today" ? "primary" : "outlined", onClick: () => {
|
|
426
|
+
setQuick("today");
|
|
427
|
+
handleSearch(dayjs(), dayjs());
|
|
428
|
+
}, sx: { whiteSpace: "nowrap" }, children: "Today" }),
|
|
429
|
+
/* @__PURE__ */ jsx(AppButton, { variant: quickRange === "week" ? "primary" : "outlined", onClick: () => {
|
|
430
|
+
setQuick("week");
|
|
431
|
+
handleSearch(dayjs().startOf("week"), dayjs());
|
|
432
|
+
}, sx: { whiteSpace: "nowrap" }, children: "This Week" }),
|
|
433
|
+
/* @__PURE__ */ jsx(AppButton, { variant: quickRange === "month" ? "primary" : "outlined", onClick: () => {
|
|
434
|
+
setQuick("month");
|
|
435
|
+
handleSearch(dayjs().startOf("month"), dayjs());
|
|
436
|
+
}, sx: { whiteSpace: "nowrap" }, children: "This Month" })
|
|
437
|
+
] }),
|
|
438
|
+
/* @__PURE__ */ jsxs(Box, { sx: { py: 2, px: 2, position: "relative" }, children: [
|
|
439
|
+
detailLoading && /* @__PURE__ */ jsx(Box, { sx: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, background: "rgba(240,244,248,0.7)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 10 }, children: /* @__PURE__ */ jsx(CircularProgress, {}) }),
|
|
440
|
+
detailError && /* @__PURE__ */ jsx(Alert, { severity: "error", sx: { mb: 2 }, onClose: () => setDetailError(""), children: detailError }),
|
|
441
|
+
/* @__PURE__ */ jsx(Box, { sx: { display: "grid", gridTemplateColumns: { xs: "1fr 1fr", sm: "repeat(3, 1fr)", md: "repeat(5, 1fr)" }, gap: 1.5, mb: 2 }, children: [
|
|
442
|
+
{ v: (_e = profile == null ? void 0 : profile.totalCalls) != null ? _e : 0, l: "Total Calls", c: C.navy },
|
|
443
|
+
{ v: (_f = profile == null ? void 0 : profile.answeredCalls) != null ? _f : 0, l: "Answered", c: C.green },
|
|
444
|
+
{ v: (_g = profile == null ? void 0 : profile.missedCalls) != null ? _g : 0, l: "Missed", c: C.red },
|
|
445
|
+
{ v: (profile == null ? void 0 : profile.avgAhtFormatted) || "00:00:00", l: "Avg AHT", c: C.purple },
|
|
446
|
+
{ v: `${((_h = profile == null ? void 0 : profile.answerRate) != null ? _h : 0).toFixed(1)}%`, l: "Answer Rate", c: C.green },
|
|
447
|
+
{ v: (profile == null ? void 0 : profile.loginDurationFormatted) || "00:00:00", l: "Login Duration", c: C.blue },
|
|
448
|
+
{ v: (profile == null ? void 0 : profile.productionFormatted) || "00:00:00", l: "Production", c: C.blue },
|
|
449
|
+
{ v: (profile == null ? void 0 : profile.handleTimeFormatted) || "00:00:00", l: "Handle Time", c: C.purple },
|
|
450
|
+
{ v: (profile == null ? void 0 : profile.breakTimeFormatted) || "00:00:00", l: "Break Time", c: C.amber },
|
|
451
|
+
{ v: `${((_i = profile == null ? void 0 : profile.adherencePct) != null ? _i : 0).toFixed(1)}%`, l: `Adherence${(profile == null ? void 0 : profile.adherenceGrade) ? ` (${profile.adherenceGrade})` : ""}`, c: C.teal }
|
|
452
|
+
].map((kpi, i) => /* @__PURE__ */ jsxs(Box, { sx: {
|
|
453
|
+
px: 1.5,
|
|
454
|
+
py: 1.2,
|
|
455
|
+
borderRadius: "10px",
|
|
456
|
+
backgroundColor: "#fafafa",
|
|
457
|
+
borderLeft: `3px solid ${kpi.c}`,
|
|
458
|
+
border: "1px solid #e0e0e0",
|
|
459
|
+
borderLeftWidth: "3px",
|
|
460
|
+
borderLeftStyle: "solid",
|
|
461
|
+
borderLeftColor: kpi.c
|
|
462
|
+
}, children: [
|
|
463
|
+
/* @__PURE__ */ jsx(Typography, { sx: {
|
|
464
|
+
fontFamily: "poppins, Arial, sans-serif",
|
|
465
|
+
fontSize: "0.72rem",
|
|
466
|
+
fontWeight: 600,
|
|
467
|
+
color: "#888",
|
|
468
|
+
mb: 0.3
|
|
469
|
+
}, children: kpi.l }),
|
|
470
|
+
/* @__PURE__ */ jsx(Typography, { sx: {
|
|
471
|
+
fontFamily: "poppins, Arial, sans-serif",
|
|
472
|
+
fontSize: "0.95rem",
|
|
473
|
+
fontWeight: 700,
|
|
474
|
+
color: "#1a1a1a"
|
|
475
|
+
}, children: kpi.v })
|
|
476
|
+
] }, i)) }),
|
|
477
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: { xs: "flex-start", sm: "center" }, justifyContent: "space-between", flexDirection: { xs: "column", sm: "row" }, gap: 1, borderBottom: `2px solid ${C.b1}`, mb: 2 }, children: [
|
|
478
|
+
/* @__PURE__ */ jsx(Box, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" }, children: tabs.map((tab, idx) => /* @__PURE__ */ jsx(
|
|
479
|
+
Box,
|
|
480
|
+
{
|
|
481
|
+
onClick: () => setActiveTab(idx),
|
|
482
|
+
sx: {
|
|
483
|
+
px: 2,
|
|
484
|
+
py: 1,
|
|
485
|
+
fontSize: 13,
|
|
486
|
+
fontWeight: 600,
|
|
487
|
+
cursor: "pointer",
|
|
488
|
+
color: activeTab === idx ? C.blue : C.t3,
|
|
489
|
+
borderBottom: `2px solid ${activeTab === idx ? C.blue : "transparent"}`,
|
|
490
|
+
mb: "-2px",
|
|
491
|
+
transition: "all .15s",
|
|
492
|
+
"&:hover": { color: C.t1 }
|
|
493
|
+
},
|
|
494
|
+
children: tab
|
|
495
|
+
},
|
|
496
|
+
tab
|
|
497
|
+
)) }),
|
|
498
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.5, mb: "-2px" }, children: [
|
|
499
|
+
/* @__PURE__ */ jsx(
|
|
500
|
+
AppButton,
|
|
501
|
+
{
|
|
502
|
+
size: "small",
|
|
503
|
+
startIcon: /* @__PURE__ */ jsx(RefreshIcon, {}),
|
|
504
|
+
disabled: detailLoading || cdrLoading,
|
|
505
|
+
onClick: () => handleSearch(),
|
|
506
|
+
children: "Refresh"
|
|
507
|
+
}
|
|
508
|
+
),
|
|
509
|
+
/* @__PURE__ */ jsx(AppButton, { size: "small", startIcon: /* @__PURE__ */ jsx(SearchIcon, {}), onClick: () => handleSearch(), children: "Search" })
|
|
510
|
+
] })
|
|
511
|
+
] }),
|
|
512
|
+
activeTab === 0 && /* @__PURE__ */ jsxs(Box, { children: [
|
|
513
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "grid", gridTemplateColumns: { xs: "1fr", md: "1fr 1fr" }, gap: 2, mb: 2 }, children: [
|
|
514
|
+
/* @__PURE__ */ jsxs(Box, { sx: { border: `1px solid ${C.b1}`, borderRadius: "10px", overflow: "hidden", background: "transparent" }, children: [
|
|
515
|
+
/* @__PURE__ */ jsxs(Box, { sx: { px: 2, py: 1.2, background: C.s2, borderBottom: `1px solid ${C.b1}`, display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
|
|
516
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontSize: 12, fontWeight: 700, letterSpacing: 0.8, textTransform: "uppercase", color: C.t2 }, children: hourlyChartTitle }),
|
|
517
|
+
/* @__PURE__ */ jsx(Chip, { label: agentName, size: "small", sx: { fontSize: 11, fontWeight: 700, background: C.blt, color: C.blue, border: `1px solid rgba(21,101,200,.2)` } })
|
|
518
|
+
] }),
|
|
519
|
+
/* @__PURE__ */ jsx(Box, { sx: { p: 2, height: 260 }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(BarChart, { data: hourlyCallData, barGap: 1, children: [
|
|
520
|
+
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: C.b1 }),
|
|
521
|
+
/* @__PURE__ */ jsx(XAxis, { dataKey: "hour", tick: { fontSize: 11, fill: C.t3 } }),
|
|
522
|
+
/* @__PURE__ */ jsx(YAxis, { tick: { fontSize: 11, fill: C.t3 }, allowDecimals: false }),
|
|
523
|
+
/* @__PURE__ */ jsx(RTooltip, { labelFormatter: (label) => `${String(label).padStart(2, "0")}:00`, contentStyle: { fontSize: 12, borderRadius: 8, border: `1px solid ${C.b2}` } }),
|
|
524
|
+
/* @__PURE__ */ jsx(Legend, { wrapperStyle: { fontSize: 11 } }),
|
|
525
|
+
/* @__PURE__ */ jsx(Bar, { dataKey: "answered", name: "Answered", fill: C.blue, radius: [3, 3, 0, 0] }),
|
|
526
|
+
/* @__PURE__ */ jsx(Bar, { dataKey: "abandoned", name: "Abandoned", fill: C.red, radius: [3, 3, 0, 0] })
|
|
527
|
+
] }) }) })
|
|
528
|
+
] }),
|
|
529
|
+
/* @__PURE__ */ jsxs(Box, { sx: { border: `1px solid ${C.b1}`, borderRadius: "10px", overflow: "hidden", background: "transparent" }, children: [
|
|
530
|
+
/* @__PURE__ */ jsx(Box, { sx: { px: 2, py: 1.2, background: C.s2, borderBottom: `1px solid ${C.b1}` }, children: /* @__PURE__ */ jsx(Typography, { sx: { fontSize: 12, fontWeight: 700, letterSpacing: 0.8, textTransform: "uppercase", color: C.t2 }, children: "Time Utilisation Breakdown" }) }),
|
|
531
|
+
/* @__PURE__ */ jsx(Box, { sx: { p: 2, height: 260 }, children: timeUtilData.length > 0 ? /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(PieChart, { children: [
|
|
532
|
+
/* @__PURE__ */ jsx(Pie, { data: timeUtilData, cx: "50%", cy: "50%", innerRadius: 60, outerRadius: 95, dataKey: "value", nameKey: "name", minAngle: 8, children: timeUtilData.map((entry, idx) => /* @__PURE__ */ jsx(Cell, { fill: entry.color }, idx)) }),
|
|
533
|
+
/* @__PURE__ */ jsx(RTooltip, { formatter: (_val, name, entry) => [`${entry.payload.formatted} (${entry.payload.pct.toFixed(1)}%)`, name], contentStyle: { fontSize: 12, borderRadius: 8, border: `1px solid ${C.b2}` } }),
|
|
534
|
+
/* @__PURE__ */ jsx(Legend, { wrapperStyle: { fontSize: 12 }, formatter: (value) => /* @__PURE__ */ jsx("span", { style: { color: C.t1, fontWeight: 500 }, children: value }) })
|
|
535
|
+
] }) }) : /* @__PURE__ */ jsx(Box, { sx: { display: "flex", alignItems: "center", justifyContent: "center", height: "100%", color: C.t3, fontSize: 13 }, children: "No data" }) })
|
|
536
|
+
] })
|
|
537
|
+
] }),
|
|
538
|
+
/* @__PURE__ */ jsxs(Box, { sx: { border: `1px solid ${C.b1}`, borderRadius: "10px", overflow: "hidden", background: "transparent", mb: 2 }, children: [
|
|
539
|
+
/* @__PURE__ */ jsxs(Box, { sx: { px: 2, py: 1.2, background: C.s2, borderBottom: `1px solid ${C.b1}`, display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
|
|
540
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontSize: 12, fontWeight: 700, letterSpacing: 0.8, textTransform: "uppercase", color: C.t2 }, children: "Talk Duration Distribution" }),
|
|
541
|
+
/* @__PURE__ */ jsx(Chip, { label: `${talkDurationTotal} call${talkDurationTotal === 1 ? "" : "s"}`, size: "small", sx: { fontSize: 11, fontWeight: 700, background: C.glt, color: C.green, border: `1px solid rgba(10,154,98,.2)` } })
|
|
542
|
+
] }),
|
|
543
|
+
/* @__PURE__ */ jsx(Box, { sx: { p: 2, height: 260 }, children: talkDurationBuckets.length > 0 ? /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(BarChart, { data: talkDurationBuckets, margin: { top: 16, right: 16, left: 0, bottom: 20 }, children: [
|
|
544
|
+
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: C.b1 }),
|
|
545
|
+
/* @__PURE__ */ jsx(XAxis, { dataKey: "bucket", tick: { fontSize: 11, fill: C.t3 }, label: { value: "Talking Duration", position: "insideBottom", offset: -5, style: { fontSize: 11, fontWeight: 600, fill: C.t2 } } }),
|
|
546
|
+
/* @__PURE__ */ jsx(YAxis, { tick: { fontSize: 11, fill: C.t3 }, allowDecimals: false, label: { value: "Call Count", angle: -90, position: "insideLeft", offset: 10, style: { fontSize: 11, fontWeight: 600, fill: C.t2 } } }),
|
|
547
|
+
/* @__PURE__ */ jsx(
|
|
548
|
+
RTooltip,
|
|
549
|
+
{
|
|
550
|
+
contentStyle: { fontSize: 12, borderRadius: 8, border: `1px solid ${C.b2}` },
|
|
551
|
+
labelFormatter: (label, payload) => {
|
|
552
|
+
var _a2;
|
|
553
|
+
const p = (_a2 = payload == null ? void 0 : payload[0]) == null ? void 0 : _a2.payload;
|
|
554
|
+
if (!p) return String(label);
|
|
555
|
+
return `${label} (${p.minSeconds}\u2013${p.maxSeconds}s)`;
|
|
556
|
+
},
|
|
557
|
+
formatter: (val, _name, entry) => {
|
|
558
|
+
var _a2;
|
|
559
|
+
const pct = (_a2 = entry == null ? void 0 : entry.payload) == null ? void 0 : _a2.pct;
|
|
560
|
+
return [`${val} call${val === 1 ? "" : "s"}${pct != null ? ` (${pct.toFixed(1)}%)` : ""}`, "Calls"];
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
),
|
|
564
|
+
/* @__PURE__ */ jsx(Bar, { dataKey: "count", name: "Calls", fill: C.teal, radius: [4, 4, 0, 0], children: /* @__PURE__ */ jsx(LabelList, { dataKey: "count", position: "top", style: { fontSize: 11, fontWeight: 700, fill: C.t1 } }) })
|
|
565
|
+
] }) }) : /* @__PURE__ */ jsx(Box, { sx: { display: "flex", alignItems: "center", justifyContent: "center", height: "100%", color: C.t3, fontSize: 13 }, children: "No data" }) })
|
|
566
|
+
] }),
|
|
567
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "grid", gridTemplateColumns: { xs: "1fr", md: "1fr 1fr" }, gap: 2 }, children: [
|
|
568
|
+
/* @__PURE__ */ jsxs(Box, { sx: { border: `1px solid ${C.b1}`, borderRadius: "10px", overflow: "hidden", background: "transparent" }, children: [
|
|
569
|
+
/* @__PURE__ */ jsx(Box, { sx: { px: 2, py: 1.2, background: C.s2, borderBottom: `1px solid ${C.b1}` }, children: /* @__PURE__ */ jsx(Typography, { sx: { fontSize: 12, fontWeight: 700, letterSpacing: 0.8, textTransform: "uppercase", color: C.t2 }, children: "Calls by Queue" }) }),
|
|
570
|
+
/* @__PURE__ */ jsx(Box, { sx: { p: 2, height: 260 }, children: queuePieData.length > 0 ? /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(PieChart, { children: [
|
|
571
|
+
/* @__PURE__ */ jsx(Pie, { data: queuePieData, cx: "50%", cy: "50%", outerRadius: 90, dataKey: "value", nameKey: "name", children: queuePieData.map((entry, idx) => /* @__PURE__ */ jsx(Cell, { fill: entry.color }, idx)) }),
|
|
572
|
+
/* @__PURE__ */ jsx(
|
|
573
|
+
RTooltip,
|
|
574
|
+
{
|
|
575
|
+
contentStyle: { fontSize: 12, borderRadius: 8, border: `1px solid ${C.b2}` },
|
|
576
|
+
formatter: (val, name, entry) => {
|
|
577
|
+
var _a2;
|
|
578
|
+
const pct = (_a2 = entry == null ? void 0 : entry.payload) == null ? void 0 : _a2.pctOfTotal;
|
|
579
|
+
return [`${val} calls${pct != null ? ` (${pct.toFixed(1)}%)` : ""}`, name];
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
),
|
|
583
|
+
/* @__PURE__ */ jsx(Legend, { wrapperStyle: { fontSize: 12 }, formatter: (value) => /* @__PURE__ */ jsx("span", { style: { color: C.t1, fontWeight: 500 }, children: value }) })
|
|
584
|
+
] }) }) : /* @__PURE__ */ jsx(Box, { sx: { display: "flex", alignItems: "center", justifyContent: "center", height: "100%", color: C.t3, fontSize: 13 }, children: "No queue data" }) })
|
|
585
|
+
] }),
|
|
586
|
+
/* @__PURE__ */ jsxs(Box, { sx: { border: `1px solid ${C.b1}`, borderRadius: "10px", overflow: "hidden", background: "transparent" }, children: [
|
|
587
|
+
/* @__PURE__ */ jsx(Box, { sx: { px: 2, py: 1.2, background: C.s2, borderBottom: `1px solid ${C.b1}` }, children: /* @__PURE__ */ jsx(Typography, { sx: { fontSize: 12, fontWeight: 700, letterSpacing: 0.8, textTransform: "uppercase", color: C.t2 }, children: "Queue Performance Metrics" }) }),
|
|
588
|
+
/* @__PURE__ */ jsx(Box, { sx: { p: 2 }, children: queueMetrics.length > 0 ? queueMetrics.map((q) => {
|
|
589
|
+
var _a2, _b2, _c2;
|
|
590
|
+
return /* @__PURE__ */ jsxs(Box, { sx: { mb: 2, "&:last-child": { mb: 0 } }, children: [
|
|
591
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", justifyContent: "space-between", mb: 0.3 }, children: [
|
|
592
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
|
|
593
|
+
/* @__PURE__ */ jsx(Box, { sx: { width: 10, height: 10, borderRadius: "50%", background: q.color, flexShrink: 0 } }),
|
|
594
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontSize: 13, fontWeight: 700, color: C.t1 }, children: q.queueName })
|
|
595
|
+
] }),
|
|
596
|
+
/* @__PURE__ */ jsxs(Typography, { sx: __spreadProps(__spreadValues({}, monoSx), { fontSize: 13, fontWeight: 800, color: q.color }), children: [
|
|
597
|
+
q.totalCalls,
|
|
598
|
+
" calls",
|
|
599
|
+
/* @__PURE__ */ jsxs(Typography, { component: "span", sx: __spreadProps(__spreadValues({}, monoSx), { color: C.t3, ml: 0.5 }), children: [
|
|
600
|
+
"(",
|
|
601
|
+
q.answeredCalls,
|
|
602
|
+
" ans / ",
|
|
603
|
+
q.missedCalls,
|
|
604
|
+
" miss)"
|
|
605
|
+
] })
|
|
606
|
+
] })
|
|
607
|
+
] }),
|
|
608
|
+
/* @__PURE__ */ jsx(Box, { sx: { height: 6, background: C.s3, borderRadius: 3, overflow: "hidden", mb: 0.3 }, children: /* @__PURE__ */ jsx(Box, { sx: { height: "100%", width: `${(_a2 = q.pctOfTotal) != null ? _a2 : 0}%`, background: q.color, borderRadius: 3 } }) }),
|
|
609
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", flexWrap: "wrap", columnGap: 1.5, rowGap: 0.2 }, children: [
|
|
610
|
+
/* @__PURE__ */ jsxs(Typography, { sx: { fontSize: 11, color: C.t3 }, children: [
|
|
611
|
+
((_b2 = q.pctOfTotal) != null ? _b2 : 0).toFixed(0),
|
|
612
|
+
"% of total"
|
|
613
|
+
] }),
|
|
614
|
+
/* @__PURE__ */ jsxs(Typography, { sx: { fontSize: 11, color: C.t3 }, children: [
|
|
615
|
+
"Ans: ",
|
|
616
|
+
((_c2 = q.answerRate) != null ? _c2 : 0).toFixed(0),
|
|
617
|
+
"%"
|
|
618
|
+
] }),
|
|
619
|
+
/* @__PURE__ */ jsxs(Typography, { sx: { fontSize: 11, color: C.t3 }, children: [
|
|
620
|
+
"Talk: ",
|
|
621
|
+
/* @__PURE__ */ jsx("span", { style: __spreadProps(__spreadValues({}, monoSx), { color: C.t2 }), children: q.avgTalkFormatted })
|
|
622
|
+
] }),
|
|
623
|
+
/* @__PURE__ */ jsxs(Typography, { sx: { fontSize: 11, color: C.t3 }, children: [
|
|
624
|
+
"Hold: ",
|
|
625
|
+
/* @__PURE__ */ jsx("span", { style: __spreadProps(__spreadValues({}, monoSx), { color: C.t2 }), children: q.avgHoldFormatted })
|
|
626
|
+
] }),
|
|
627
|
+
/* @__PURE__ */ jsxs(Typography, { sx: { fontSize: 11, color: C.t3 }, children: [
|
|
628
|
+
"Wrap: ",
|
|
629
|
+
/* @__PURE__ */ jsx("span", { style: __spreadProps(__spreadValues({}, monoSx), { color: C.t2 }), children: q.avgWrapupFormatted })
|
|
630
|
+
] }),
|
|
631
|
+
/* @__PURE__ */ jsxs(Typography, { sx: { fontSize: 11, color: C.t3 }, children: [
|
|
632
|
+
"AHT: ",
|
|
633
|
+
/* @__PURE__ */ jsx("span", { style: __spreadProps(__spreadValues({}, monoSx), { color: C.t2 }), children: q.avgAhtFormatted })
|
|
634
|
+
] })
|
|
635
|
+
] })
|
|
636
|
+
] }, q.queueName);
|
|
637
|
+
}) : /* @__PURE__ */ jsx(Box, { sx: { textAlign: "center", py: 4, color: C.t3, fontSize: 13 }, children: "No queue data" }) })
|
|
638
|
+
] })
|
|
639
|
+
] })
|
|
640
|
+
] }),
|
|
641
|
+
activeTab === 1 && /* @__PURE__ */ jsxs(Box, { sx: { borderRadius: "8px", border: "1px solid #e0e7ef", overflow: "hidden", background: "transparent" }, children: [
|
|
642
|
+
/* @__PURE__ */ jsx(Box, { sx: { px: 2, py: 1.2, background: C.s2, borderBottom: `1px solid ${C.b1}`, display: "flex", alignItems: "center", justifyContent: "space-between" }, children: /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
|
|
643
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontSize: 12, fontWeight: 700, letterSpacing: 0.8, textTransform: "uppercase", color: C.t2 }, children: "Call History" }),
|
|
644
|
+
/* @__PURE__ */ jsx(Chip, { label: `${cdrTotal} records`, size: "small", sx: { fontSize: 11, fontWeight: 700, background: C.s2, color: C.t3, border: `1px solid ${C.b2}` } })
|
|
645
|
+
] }) }),
|
|
646
|
+
cdrError && /* @__PURE__ */ jsx(Alert, { severity: "error", sx: { mx: 2, mt: 1 }, onClose: () => setCdrError(""), children: cdrError }),
|
|
647
|
+
/* @__PURE__ */ jsxs(TableContainer, { sx: { position: "relative" }, children: [
|
|
648
|
+
cdrLoading && /* @__PURE__ */ jsx(Box, { sx: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, background: "rgba(255,255,255,0.7)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 5 }, children: /* @__PURE__ */ jsx(CircularProgress, { size: 28 }) }),
|
|
649
|
+
/* @__PURE__ */ jsxs(Table, { size: "small", sx: {
|
|
650
|
+
minWidth: 1400,
|
|
651
|
+
"& .MuiTableCell-root": { fontSize: "0.75rem", borderBottom: "1px solid #eef1f6", py: 0.6, px: 1 }
|
|
652
|
+
}, children: [
|
|
653
|
+
/* @__PURE__ */ jsx(TableHead, { children: /* @__PURE__ */ jsx(TableRow, { children: [
|
|
654
|
+
"Phone Number",
|
|
655
|
+
"Call Start",
|
|
656
|
+
"Call Type",
|
|
657
|
+
"Status",
|
|
658
|
+
"Queue",
|
|
659
|
+
"Extension",
|
|
660
|
+
"Talk Duration",
|
|
661
|
+
"Hold Duration",
|
|
662
|
+
"Ringing Duration",
|
|
663
|
+
"Wrapup Duration",
|
|
664
|
+
"Disposition",
|
|
665
|
+
"Transferred",
|
|
666
|
+
"Recording"
|
|
667
|
+
].map((label) => /* @__PURE__ */ jsx(TableCell, { sx: cdrHeadSx, children: label }, label)) }) }),
|
|
668
|
+
/* @__PURE__ */ jsxs(TableBody, { children: [
|
|
669
|
+
cdrRecords.map((row, idx) => {
|
|
670
|
+
var _a2;
|
|
671
|
+
return /* @__PURE__ */ jsxs(TableRow, { hover: true, sx: {
|
|
672
|
+
backgroundColor: "#fff",
|
|
673
|
+
"&:hover": { backgroundColor: "#f0f7f8" }
|
|
674
|
+
}, children: [
|
|
675
|
+
/* @__PURE__ */ jsx(TableCell, { sx: { fontWeight: 600, color: C.navy, fontSize: "0.75rem" }, children: row.phoneNumber || "" }),
|
|
676
|
+
/* @__PURE__ */ jsx(TableCell, { sx: { fontSize: "0.73rem", color: "#4a5568" }, children: row.callStartTime ? dayjs(row.callStartTime).format("DD MMM YYYY hh:mm A") : "" }),
|
|
677
|
+
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(CallTypeChip, { type: row.callType || "" }) }),
|
|
678
|
+
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(StatusChip, { status: row.callStatus || "" }) }),
|
|
679
|
+
/* @__PURE__ */ jsx(TableCell, { children: row.queueName ? /* @__PURE__ */ jsx(Chip, { label: row.queueName, size: "small", sx: { backgroundColor: "#f0ebfa", color: C.purple, fontWeight: 500, fontSize: "0.67rem", height: 22 } }) : "" }),
|
|
680
|
+
/* @__PURE__ */ jsx(TableCell, { sx: { fontSize: "0.73rem", color: "#4a5568" }, children: row.extension || "" }),
|
|
681
|
+
/* @__PURE__ */ jsx(TableCell, { sx: { fontWeight: 600, color: C.blue, fontSize: "0.75rem", fontVariantNumeric: "tabular-nums" }, children: row.talkDurationFmt || "" }),
|
|
682
|
+
/* @__PURE__ */ jsx(TableCell, { sx: { fontWeight: 500, color: C.amber, fontSize: "0.75rem", fontVariantNumeric: "tabular-nums" }, children: row.holdDurationFmt || "" }),
|
|
683
|
+
/* @__PURE__ */ jsx(TableCell, { sx: { fontWeight: 500, fontSize: "0.75rem", fontVariantNumeric: "tabular-nums" }, children: row.ringingDurationFmt || "" }),
|
|
684
|
+
/* @__PURE__ */ jsx(TableCell, { sx: { fontWeight: 500, color: C.purple, fontSize: "0.75rem", fontVariantNumeric: "tabular-nums" }, children: row.wrapupDurationFmt || "" }),
|
|
685
|
+
/* @__PURE__ */ jsx(TableCell, { sx: { fontSize: "0.73rem", color: "#4a5568" }, children: row.disposition || "" }),
|
|
686
|
+
/* @__PURE__ */ jsx(TableCell, { children: row.wasTransferred ? /* @__PURE__ */ jsx(Chip, { label: "Yes", size: "small", sx: { backgroundColor: "#e6f7ef", color: C.green, fontWeight: 600, fontSize: "0.65rem", height: 20, borderRadius: "6px" } }) : /* @__PURE__ */ jsx(Chip, { label: "No", size: "small", sx: { backgroundColor: "transparent", color: "#6b7b8d", fontWeight: 600, fontSize: "0.65rem", height: 20, borderRadius: "6px" } }) }),
|
|
687
|
+
/* @__PURE__ */ jsx(TableCell, { children: row.callUuid ? /* @__PURE__ */ jsx(RecordingCell, { callUuid: row.callUuid, recordingPath: (_a2 = row.recordingPath) != null ? _a2 : null }) : "" })
|
|
688
|
+
] }, row.callUuid || idx);
|
|
689
|
+
}),
|
|
690
|
+
!cdrLoading && cdrRecords.length === 0 && /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: 13, sx: { textAlign: "center", py: 4, color: C.t3, fontSize: 13 }, children: "No call records found" }) })
|
|
691
|
+
] })
|
|
692
|
+
] })
|
|
693
|
+
] }),
|
|
694
|
+
/* @__PURE__ */ jsx(
|
|
695
|
+
TablePagination,
|
|
696
|
+
{
|
|
697
|
+
component: "div",
|
|
698
|
+
count: cdrTotal,
|
|
699
|
+
page: cdrPage,
|
|
700
|
+
onPageChange: (_, p) => setCdrPage(p),
|
|
701
|
+
rowsPerPage: cdrPageSize,
|
|
702
|
+
onRowsPerPageChange: (e) => {
|
|
703
|
+
setCdrPageSize(parseInt(e.target.value, 10));
|
|
704
|
+
setCdrPage(0);
|
|
705
|
+
},
|
|
706
|
+
rowsPerPageOptions: [10, 15, 25, 50, 100],
|
|
707
|
+
sx: { borderTop: "1px solid #eef1f6", "& .MuiTablePagination-displayedRows": { fontSize: "0.73rem" }, "& .MuiTablePagination-selectLabel": { fontSize: "0.73rem" } }
|
|
708
|
+
}
|
|
709
|
+
)
|
|
710
|
+
] }),
|
|
711
|
+
activeTab === 2 && /* @__PURE__ */ jsxs(Box, { children: [
|
|
712
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "grid", gridTemplateColumns: { xs: "1fr 1fr", md: "repeat(3, 1fr)", lg: "repeat(4, 1fr)" }, gap: { xs: 1.5, lg: 2 }, mb: 2 }, children: [
|
|
713
|
+
/* @__PURE__ */ jsx(StatCard, { value: (profile == null ? void 0 : profile.breakTimeFormatted) || "00:00:00", label: "Total Break Time", color: C.amber }),
|
|
714
|
+
/* @__PURE__ */ jsx(StatCard, { value: (_j = profile == null ? void 0 : profile.breaksTaken) != null ? _j : 0, label: "Breaks Taken", color: C.purple }),
|
|
715
|
+
/* @__PURE__ */ jsx(StatCard, { value: (profile == null ? void 0 : profile.productionFormatted) || "00:00:00", label: "Production Time", color: C.green }),
|
|
716
|
+
/* @__PURE__ */ jsx(StatCard, { value: (profile == null ? void 0 : profile.loginDurationFormatted) || "00:00:00", label: "Login Duration", color: C.teal })
|
|
717
|
+
] }),
|
|
718
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
719
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontSize: "0.72rem", fontWeight: 700, color: "#888", textTransform: "uppercase", letterSpacing: 0.8, mb: 1, fontFamily: "poppins, Arial, sans-serif" }, children: "Attendance Summary" }),
|
|
720
|
+
/* @__PURE__ */ jsx(Box, { sx: { display: "grid", gridTemplateColumns: { xs: "1fr 1fr", md: "repeat(4, 1fr)" }, gap: 1.5 }, children: [
|
|
721
|
+
{ label: "First Login", value: (profile == null ? void 0 : profile.firstLoginTime) ? dayjs(profile.firstLoginTime).format("YYYY-MM-DD HH:mm:ss") : "\u2014", color: C.blue },
|
|
722
|
+
{ label: "Adherence Score", value: `${(_k = profile == null ? void 0 : profile.adherencePct) != null ? _k : 0}%`, color: C.green },
|
|
723
|
+
{ label: "Adherence Grade", value: (profile == null ? void 0 : profile.adherenceGrade) || "\u2014", color: C.purple },
|
|
724
|
+
{ label: "Answer Rate", value: `${((_l = profile == null ? void 0 : profile.answerRate) != null ? _l : 0).toFixed(1)}%`, color: C.teal }
|
|
725
|
+
].map((item) => /* @__PURE__ */ jsxs(Box, { sx: {
|
|
726
|
+
px: 1.5,
|
|
727
|
+
py: 1.2,
|
|
728
|
+
borderRadius: "10px",
|
|
729
|
+
backgroundColor: "#fafafa",
|
|
730
|
+
border: "1px solid #e0e0e0",
|
|
731
|
+
borderLeftWidth: "3px",
|
|
732
|
+
borderLeftStyle: "solid",
|
|
733
|
+
borderLeftColor: item.color
|
|
734
|
+
}, children: [
|
|
735
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontFamily: "poppins, Arial, sans-serif", fontSize: "0.72rem", fontWeight: 600, color: "#888", mb: 0.3 }, children: item.label }),
|
|
736
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontFamily: "poppins, Arial, sans-serif", fontSize: "0.95rem", fontWeight: 700, color: "#1a1a1a" }, children: item.value })
|
|
737
|
+
] }, item.label)) })
|
|
738
|
+
] })
|
|
739
|
+
] })
|
|
740
|
+
] })
|
|
741
|
+
] }) });
|
|
742
|
+
}
|
|
743
|
+
var AgentDetailReport = (props) => /* @__PURE__ */ jsx(SDKProvider, { children: /* @__PURE__ */ jsx(AgentDetailReportContent, __spreadValues({}, props)) });
|
|
744
|
+
var agentDetailReport_default = AgentDetailReport;
|
|
745
|
+
|
|
746
|
+
export {
|
|
747
|
+
agentDetailReport_default
|
|
748
|
+
};
|
|
749
|
+
//# sourceMappingURL=chunk-G6KDIN5W.mjs.map
|