ahs-cti 1.0.1-beta.2 → 1.0.1-beta.21

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.
@@ -0,0 +1,965 @@
1
+ import {
2
+ SDKPermissionGuard
3
+ } from "./chunk-I4CJTHZR.mjs";
4
+ import {
5
+ SDKButton
6
+ } from "./chunk-FVNDPJUU.mjs";
7
+ import {
8
+ SDKPageHeader
9
+ } from "./chunk-RZZQ42MG.mjs";
10
+ import {
11
+ END_POINT,
12
+ SDKProvider,
13
+ SDK_PERMISSIONS,
14
+ __spreadProps,
15
+ __spreadValues,
16
+ axios_default
17
+ } from "./chunk-YREOEULC.mjs";
18
+
19
+ // call-control-sdk/lib/pages/cdrReport/index.tsx
20
+ import { memo, useCallback as useCallback2, useEffect, useMemo, useRef, useState as useState2 } from "react";
21
+ import {
22
+ Alert,
23
+ Box,
24
+ Checkbox,
25
+ Chip,
26
+ CircularProgress,
27
+ FormControl,
28
+ IconButton,
29
+ InputAdornment,
30
+ InputLabel,
31
+ MenuItem,
32
+ Paper,
33
+ Select,
34
+ Stack,
35
+ Table,
36
+ TableBody,
37
+ TableCell,
38
+ TableContainer,
39
+ TableHead,
40
+ TablePagination,
41
+ TableRow,
42
+ TableSortLabel,
43
+ TextField,
44
+ Tooltip,
45
+ Typography
46
+ } from "@mui/material";
47
+ import CloseIcon from "@mui/icons-material/Close";
48
+ import DownloadIcon from "@mui/icons-material/Download";
49
+ import FileDownloadIcon from "@mui/icons-material/FileDownload";
50
+ import PlayArrowIcon from "@mui/icons-material/PlayArrow";
51
+ import SearchIcon from "@mui/icons-material/Search";
52
+ import dayjs from "dayjs";
53
+ import isoWeek from "dayjs/plugin/isoWeek";
54
+ import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
55
+ import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
56
+ import { DateTimePicker } from "@mui/x-date-pickers/DateTimePicker";
57
+
58
+ // call-control-sdk/lib/pages/cdrReport/useCdrReport.ts
59
+ import { useCallback, useState } from "react";
60
+ function buildCdrQuery(params) {
61
+ const qs = new URLSearchParams();
62
+ if (params.start_date) qs.append("start_date", params.start_date);
63
+ if (params.end_date) qs.append("end_date", params.end_date);
64
+ if (params.agent_id) {
65
+ for (const id of params.agent_id) qs.append("agent_id", String(id));
66
+ }
67
+ if (params.queue_id) {
68
+ for (const id of params.queue_id) qs.append("queue_id", String(id));
69
+ }
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.page_size) qs.append("page_size", String(params.page_size));
75
+ const str = qs.toString();
76
+ return str ? `?${str}` : "";
77
+ }
78
+ function useCdrReport() {
79
+ const [records, setRecords] = useState([]);
80
+ const [totalRecords, setTotalRecords] = useState(0);
81
+ const [loading, setLoading] = useState(false);
82
+ const [error, setError] = useState("");
83
+ const fetchRecords = useCallback(async (params) => {
84
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
85
+ setLoading(true);
86
+ setError("");
87
+ try {
88
+ const qs = buildCdrQuery(params);
89
+ const res = await axios_default.get(`${END_POINT.CDR_REPORT_RECORDS}${qs}`);
90
+ setRecords((_b = (_a = res.data) == null ? void 0 : _a.data) != null ? _b : []);
91
+ setTotalRecords((_d = (_c = res.data) == null ? void 0 : _c.total) != null ? _d : 0);
92
+ } catch (err) {
93
+ setError(
94
+ (_k = (_j = (_i = (_f = (_e = err == null ? void 0 : err.response) == null ? void 0 : _e.data) == null ? void 0 : _f.message) != null ? _i : (_h = (_g = err == null ? void 0 : err.response) == null ? void 0 : _g.data) == null ? void 0 : _h.detail) != null ? _j : err == null ? void 0 : err.message) != null ? _k : "Failed to fetch CDR report"
95
+ );
96
+ setRecords([]);
97
+ setTotalRecords(0);
98
+ } finally {
99
+ setLoading(false);
100
+ }
101
+ }, []);
102
+ const exportExcel = useCallback(async (params) => {
103
+ var _a;
104
+ const qs = buildCdrQuery(params);
105
+ const res = await axios_default.get(`${END_POINT.CDR_REPORT_EXPORT_EXCEL}${qs}`, {
106
+ responseType: "blob"
107
+ });
108
+ const blob = res.data;
109
+ const url = window.URL.createObjectURL(blob);
110
+ const a = document.createElement("a");
111
+ a.href = url;
112
+ a.download = `cdr-report-${(_a = params.start_date) != null ? _a : "export"}.xlsx`;
113
+ document.body.appendChild(a);
114
+ a.click();
115
+ a.remove();
116
+ window.URL.revokeObjectURL(url);
117
+ }, []);
118
+ const fetchRecording = useCallback(async (callUuid) => {
119
+ const res = await axios_default.get(END_POINT.RECORDING_BY_CALL(callUuid), {
120
+ responseType: "blob"
121
+ });
122
+ return res.data;
123
+ }, []);
124
+ return {
125
+ records,
126
+ totalRecords,
127
+ loading,
128
+ error,
129
+ setError,
130
+ fetchRecords,
131
+ exportExcel,
132
+ fetchRecording
133
+ };
134
+ }
135
+ function useCdrFilters() {
136
+ const [agents, setAgents] = useState([]);
137
+ const [queues, setQueues] = useState([]);
138
+ const [loading, setLoading] = useState(false);
139
+ const fetchAgents = useCallback(async () => {
140
+ var _a;
141
+ try {
142
+ const res = await axios_default.get(
143
+ END_POINT.USERS_LIST,
144
+ { params: { role: "AGENT" } }
145
+ );
146
+ const raw = Array.isArray(res.data) ? res.data : Array.isArray((_a = res.data) == null ? void 0 : _a.data) ? res.data.data : [];
147
+ const mapped = raw.filter((u) => {
148
+ const roleName = typeof u.role === "object" && u.role !== null ? u.role.name : u.role;
149
+ return roleName === "AGENT";
150
+ }).map((u) => ({
151
+ id: u.id,
152
+ userId: u.userId,
153
+ fullName: [u.firstName, u.lastName].filter(Boolean).join(" ") || u.userId
154
+ }));
155
+ setAgents(mapped);
156
+ } catch (e) {
157
+ setAgents([]);
158
+ }
159
+ }, []);
160
+ const fetchQueues = useCallback(async () => {
161
+ var _a;
162
+ try {
163
+ const res = await axios_default.get(
164
+ END_POINT.MASTER_QUEUES
165
+ );
166
+ const raw = Array.isArray(res.data) ? res.data : Array.isArray((_a = res.data) == null ? void 0 : _a.data) ? res.data.data : [];
167
+ setQueues(raw.map((q) => ({ id: q.id, name: q.name })));
168
+ } catch (e) {
169
+ setQueues([]);
170
+ }
171
+ }, []);
172
+ const fetchAll = useCallback(async () => {
173
+ setLoading(true);
174
+ try {
175
+ await Promise.all([fetchAgents(), fetchQueues()]);
176
+ } finally {
177
+ setLoading(false);
178
+ }
179
+ }, [fetchAgents, fetchQueues]);
180
+ return { agents, queues, loading, fetchAgents, fetchQueues, fetchAll };
181
+ }
182
+
183
+ // call-control-sdk/lib/pages/cdrReport/types.ts
184
+ var DEFAULT_PAGE_SIZE = 15;
185
+ var ROWS_PER_PAGE_OPTIONS = [5, 10, 20, 25, 50];
186
+
187
+ // call-control-sdk/lib/pages/cdrReport/styles.ts
188
+ var FONT = "'Inter', 'Segoe UI', 'Roboto', -apple-system, BlinkMacSystemFont, sans-serif";
189
+ var COLORS = {
190
+ navy: "#0d2a56",
191
+ blue: "#1565c8",
192
+ green: "#0a9a62",
193
+ amber: "#c47c00",
194
+ red: "#cc2a2a",
195
+ purple: "#6b3fbf",
196
+ bg: "transparent",
197
+ surface: "transparent"
198
+ };
199
+ var pageContainerSx = {
200
+ height: "100%",
201
+ display: "flex",
202
+ flexDirection: "column",
203
+ fontFamily: FONT
204
+ };
205
+ var contentSx = {
206
+ flex: 1,
207
+ overflow: "auto",
208
+ py: 0.8,
209
+ px: 0
210
+ };
211
+ var FILTER_HEIGHT = 32;
212
+ var filterBarSx = {
213
+ py: 0.6,
214
+ px: 1,
215
+ display: "flex",
216
+ alignItems: "center",
217
+ gap: 1,
218
+ flexWrap: "wrap",
219
+ rowGap: 1
220
+ };
221
+ var filterBarBoxSx = {
222
+ mb: 0.8
223
+ };
224
+ var datePickerSx = {
225
+ width: 170,
226
+ "& .MuiOutlinedInput-root": { borderRadius: "6px", height: FILTER_HEIGHT, px: 0.5 },
227
+ "& .MuiInputBase-input": { py: 0, px: "4px", fontSize: "0.72rem" },
228
+ "& .MuiInputAdornment-root": { ml: 0 },
229
+ "& .MuiInputAdornment-root .MuiSvgIcon-root": { fontSize: "0.8rem" },
230
+ "& .MuiOutlinedInput-notchedOutline": { borderColor: "#c0c8d4" },
231
+ "& .MuiPickersSectionList-root": { py: "4px", px: 0, fontSize: 12 }
232
+ };
233
+ var dateSeparatorSx = {
234
+ fontSize: "0.72rem",
235
+ color: "#7a8599"
236
+ };
237
+ var quickBtnSx = {
238
+ height: FILTER_HEIGHT,
239
+ whiteSpace: "nowrap"
240
+ };
241
+ var multiSelectFormSx = {
242
+ minWidth: 130
243
+ };
244
+ var multiSelectSx = {
245
+ fontSize: "0.76rem",
246
+ height: FILTER_HEIGHT,
247
+ borderRadius: "6px"
248
+ };
249
+ var multiSelectMenuPropsSx = {
250
+ maxHeight: 280,
251
+ "& .MuiMenuItem-root": { fontSize: "0.76rem", minHeight: 32, py: 0.3 }
252
+ };
253
+ var multiSelectLabelSx = {
254
+ fontSize: "0.76rem"
255
+ };
256
+ var searchBarSx = {
257
+ width: 250,
258
+ "& .MuiOutlinedInput-root": {
259
+ borderRadius: "50px",
260
+ height: 36,
261
+ backgroundColor: "#f5f7f8",
262
+ "& fieldset": { borderColor: "#dde3e8" },
263
+ "&:hover fieldset": { borderColor: "#1A5F6C" },
264
+ "&.Mui-focused fieldset": { borderColor: "#1A5F6C", borderWidth: "1.5px" }
265
+ },
266
+ "& .MuiInputBase-input": {
267
+ fontSize: "0.85rem",
268
+ "&::placeholder": { color: "#94a3b8", opacity: 1 }
269
+ }
270
+ };
271
+ var tablePaperSx = {
272
+ borderRadius: "8px",
273
+ border: "1px solid #e0e7ef",
274
+ overflow: "hidden",
275
+ mb: 2
276
+ };
277
+ var tableSx = {
278
+ minWidth: 1400,
279
+ fontFamily: FONT,
280
+ "& .MuiTableCell-root": {
281
+ fontFamily: FONT,
282
+ fontSize: "0.75rem",
283
+ borderBottom: "1px solid #eef1f6",
284
+ py: 0.6,
285
+ px: 1,
286
+ whiteSpace: "nowrap"
287
+ }
288
+ };
289
+ var headCellSx = {
290
+ fontFamily: "poppins, Arial, sans-serif",
291
+ bgcolor: "#f1f1f1",
292
+ color: "#333",
293
+ fontWeight: 600,
294
+ whiteSpace: "nowrap",
295
+ fontSize: "14px"
296
+ };
297
+ var sortLabelSx = {
298
+ "&.MuiTableSortLabel-root": { color: "#333" },
299
+ "&.MuiTableSortLabel-root:hover": { color: "#555" },
300
+ "&.Mui-active": { color: "#333" },
301
+ "& .MuiTableSortLabel-icon": { color: "#333 !important" }
302
+ };
303
+ var tableRowSx = {
304
+ backgroundColor: "#fff",
305
+ "&:hover": { backgroundColor: "#f0f7f8" }
306
+ };
307
+ var cellNumberSx = {
308
+ fontWeight: 600,
309
+ color: COLORS.navy,
310
+ fontSize: "0.75rem"
311
+ };
312
+ var cellDateSx = {
313
+ fontSize: "0.73rem",
314
+ color: "#4a5568"
315
+ };
316
+ var cellTalkSx = {
317
+ fontWeight: 600,
318
+ color: COLORS.blue,
319
+ fontSize: "0.75rem",
320
+ fontVariantNumeric: "tabular-nums"
321
+ };
322
+ var cellHoldSx = {
323
+ fontWeight: 500,
324
+ color: COLORS.amber,
325
+ fontSize: "0.75rem",
326
+ fontVariantNumeric: "tabular-nums"
327
+ };
328
+ var cellDurationSx = {
329
+ fontWeight: 500,
330
+ fontSize: "0.75rem",
331
+ fontVariantNumeric: "tabular-nums"
332
+ };
333
+ var callTypePillSx = (value) => {
334
+ const v = (value || "").toLowerCase();
335
+ let color = "#6b7b8d";
336
+ let bg = "transparent";
337
+ if (v.includes("inbound")) {
338
+ color = COLORS.green;
339
+ bg = "#e6f7ef";
340
+ } else if (v.includes("outbound")) {
341
+ color = COLORS.blue;
342
+ bg = "#e3eefa";
343
+ } else if (v.includes("internal")) {
344
+ color = COLORS.purple;
345
+ bg = "#f0e8ff";
346
+ }
347
+ return {
348
+ fontFamily: FONT,
349
+ bgcolor: bg,
350
+ color,
351
+ fontWeight: 600,
352
+ fontSize: "0.65rem",
353
+ height: 20,
354
+ borderRadius: "6px"
355
+ };
356
+ };
357
+ var statusPillSx = (value) => {
358
+ const v = (value || "").toLowerCase();
359
+ let color = "#6b7b8d";
360
+ let bg = "transparent";
361
+ if (v.includes("answered")) {
362
+ color = COLORS.green;
363
+ bg = "#e6f7ef";
364
+ } else if (v.includes("abandoned") || v.includes("missed")) {
365
+ color = COLORS.red;
366
+ bg = "#fce8e8";
367
+ }
368
+ return {
369
+ fontFamily: FONT,
370
+ bgcolor: bg,
371
+ color,
372
+ fontWeight: 600,
373
+ fontSize: "0.65rem",
374
+ height: 20,
375
+ borderRadius: "6px"
376
+ };
377
+ };
378
+ var queueChipSx = {
379
+ fontFamily: FONT,
380
+ backgroundColor: "#f0ebfa",
381
+ color: COLORS.purple,
382
+ fontWeight: 500,
383
+ fontSize: "0.67rem",
384
+ height: 22
385
+ };
386
+ var transferYesSx = {
387
+ fontFamily: FONT,
388
+ backgroundColor: "#e6f7ef",
389
+ color: COLORS.green,
390
+ fontWeight: 600,
391
+ fontSize: "0.65rem",
392
+ height: 20,
393
+ borderRadius: "6px"
394
+ };
395
+ var transferNoSx = {
396
+ fontFamily: FONT,
397
+ backgroundColor: "transparent",
398
+ color: "#6b7b8d",
399
+ fontWeight: 600,
400
+ fontSize: "0.65rem",
401
+ height: 20,
402
+ borderRadius: "6px"
403
+ };
404
+ var paginationSx = {
405
+ borderTop: "1px solid #eef1f6",
406
+ fontFamily: FONT,
407
+ "& .MuiTablePagination-displayedRows": { fontFamily: FONT, fontSize: "0.73rem" },
408
+ "& .MuiTablePagination-selectLabel": { fontFamily: FONT, fontSize: "0.73rem" }
409
+ };
410
+ var alertSx = {
411
+ mt: 1,
412
+ fontFamily: FONT,
413
+ fontSize: "0.78rem"
414
+ };
415
+ var loadingBoxSx = {
416
+ textAlign: "center",
417
+ py: 2.5
418
+ };
419
+
420
+ // call-control-sdk/lib/pages/cdrReport/index.tsx
421
+ import { jsx, jsxs } from "react/jsx-runtime";
422
+ dayjs.extend(isoWeek);
423
+ var AGENT_SELECT_ALL_ID = -999;
424
+ var comparator = (a, b, key) => {
425
+ const av = a[key];
426
+ const bv = b[key];
427
+ if (typeof av === "number" && typeof bv === "number") return av - bv;
428
+ if (typeof av === "boolean" && typeof bv === "boolean") return (av ? 1 : 0) - (bv ? 1 : 0);
429
+ return String(av != null ? av : "").localeCompare(String(bv != null ? bv : ""));
430
+ };
431
+ var RecordingCell = memo(function RecordingCell2({
432
+ callUuid,
433
+ recordingPath,
434
+ fetchRecording
435
+ }) {
436
+ const [audioUrl, setAudioUrl] = useState2(null);
437
+ const [loading, setLoading] = useState2(false);
438
+ const [error, setError] = useState2(false);
439
+ const unavailable = recordingPath === null;
440
+ const handlePlay = async () => {
441
+ if (audioUrl) return;
442
+ try {
443
+ setLoading(true);
444
+ setError(false);
445
+ const blob = await fetchRecording(callUuid);
446
+ setAudioUrl(URL.createObjectURL(blob));
447
+ } catch (e) {
448
+ setError(true);
449
+ } finally {
450
+ setLoading(false);
451
+ }
452
+ };
453
+ const handleDownload = async () => {
454
+ try {
455
+ const blob = audioUrl ? await fetch(audioUrl).then((r) => r.blob()) : await fetchRecording(callUuid);
456
+ const url = URL.createObjectURL(blob);
457
+ const a = document.createElement("a");
458
+ a.href = url;
459
+ a.download = `recording_${callUuid}.wav`;
460
+ a.click();
461
+ URL.revokeObjectURL(url);
462
+ } catch (e) {
463
+ }
464
+ };
465
+ if (audioUrl) {
466
+ return /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 }, children: [
467
+ /* @__PURE__ */ jsx(
468
+ "audio",
469
+ {
470
+ controls: true,
471
+ autoPlay: true,
472
+ src: audioUrl,
473
+ onEnded: () => {
474
+ if (audioUrl.startsWith("blob:")) URL.revokeObjectURL(audioUrl);
475
+ setAudioUrl(null);
476
+ },
477
+ style: { width: 180, height: 30 }
478
+ }
479
+ ),
480
+ /* @__PURE__ */ jsx(Tooltip, { title: "Download", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: handleDownload, sx: { color: COLORS.blue, p: 0.4 }, children: /* @__PURE__ */ jsx(FileDownloadIcon, { sx: { fontSize: 16 } }) }) })
481
+ ] });
482
+ }
483
+ const disabled = loading || error || unavailable;
484
+ const playTitle = unavailable || error ? "Recording Unavailable" : "Play";
485
+ const dlTitle = unavailable || error ? "Recording Unavailable" : "Download";
486
+ return /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.3 }, children: [
487
+ /* @__PURE__ */ jsx(Tooltip, { title: playTitle, children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
488
+ IconButton,
489
+ {
490
+ size: "small",
491
+ onClick: handlePlay,
492
+ disabled,
493
+ sx: { color: unavailable ? "#b0b8c8" : COLORS.green, p: 0.4 },
494
+ children: loading ? /* @__PURE__ */ jsx(CircularProgress, { size: 14 }) : /* @__PURE__ */ jsx(PlayArrowIcon, { sx: { fontSize: 16 } })
495
+ }
496
+ ) }) }),
497
+ /* @__PURE__ */ jsx(Tooltip, { title: dlTitle, children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
498
+ IconButton,
499
+ {
500
+ size: "small",
501
+ onClick: handleDownload,
502
+ disabled,
503
+ sx: { color: unavailable ? "#b0b8c8" : COLORS.blue, p: 0.4 },
504
+ children: /* @__PURE__ */ jsx(FileDownloadIcon, { sx: { fontSize: 16 } })
505
+ }
506
+ ) }) })
507
+ ] });
508
+ });
509
+ var FilterBar = memo(function FilterBar2({
510
+ startDate,
511
+ endDate,
512
+ onStartChange,
513
+ onEndChange,
514
+ quickRange,
515
+ onQuickRangeChange,
516
+ agents,
517
+ selectedAgentIds,
518
+ onAgentsChange,
519
+ queues,
520
+ selectedQueueIds,
521
+ onQueuesChange
522
+ }) {
523
+ const setQuick = (range) => {
524
+ onQuickRangeChange(range);
525
+ const today = dayjs();
526
+ let sd = today.startOf("day");
527
+ const ed = today;
528
+ if (range === "week") sd = today.startOf("week").startOf("day");
529
+ else if (range === "month") sd = today.startOf("month").startOf("day");
530
+ onStartChange(sd);
531
+ onEndChange(ed);
532
+ };
533
+ const now = dayjs();
534
+ const clampFuture = (v) => {
535
+ if (!v) return v;
536
+ const current = dayjs();
537
+ return v.isAfter(current) ? current : v;
538
+ };
539
+ const handleStartChange = (v) => {
540
+ const clamped = clampFuture(v);
541
+ onStartChange(clamped);
542
+ onQuickRangeChange("");
543
+ if (clamped && endDate && endDate.isBefore(clamped)) {
544
+ onEndChange(clamped);
545
+ }
546
+ };
547
+ const handleEndChange = (v) => {
548
+ let clamped = clampFuture(v);
549
+ if (clamped && startDate && clamped.isBefore(startDate)) {
550
+ clamped = startDate;
551
+ }
552
+ onEndChange(clamped);
553
+ onQuickRangeChange("");
554
+ };
555
+ const startMax = endDate && endDate.isBefore(now) ? endDate : now;
556
+ const allAgentsSelected = agents.length > 0 && selectedAgentIds.length === agents.length;
557
+ const allQueuesSelected = queues.length > 0 && selectedQueueIds.length === queues.length;
558
+ return /* @__PURE__ */ jsxs(Box, { sx: filterBarSx, children: [
559
+ /* @__PURE__ */ jsx(
560
+ DateTimePicker,
561
+ {
562
+ value: startDate,
563
+ onChange: (v) => handleStartChange(v),
564
+ disableFuture: true,
565
+ maxDateTime: startMax,
566
+ format: "DD-MM-YYYY hh:mm A",
567
+ slotProps: { textField: { size: "small", sx: datePickerSx } }
568
+ }
569
+ ),
570
+ /* @__PURE__ */ jsx(Typography, { sx: dateSeparatorSx, children: "to" }),
571
+ /* @__PURE__ */ jsx(
572
+ DateTimePicker,
573
+ __spreadProps(__spreadValues({
574
+ value: endDate,
575
+ onChange: (v) => handleEndChange(v),
576
+ disableFuture: true,
577
+ maxDateTime: now
578
+ }, startDate ? { minDateTime: startDate } : {}), {
579
+ format: "DD-MM-YYYY hh:mm A",
580
+ slotProps: { textField: { size: "small", sx: datePickerSx } }
581
+ })
582
+ ),
583
+ /* @__PURE__ */ jsx(
584
+ SDKButton,
585
+ {
586
+ variant: quickRange === "today" ? "primary" : "outlined",
587
+ onClick: () => setQuick("today"),
588
+ sx: quickBtnSx,
589
+ children: "Today"
590
+ }
591
+ ),
592
+ /* @__PURE__ */ jsx(
593
+ SDKButton,
594
+ {
595
+ variant: quickRange === "week" ? "primary" : "outlined",
596
+ onClick: () => setQuick("week"),
597
+ sx: quickBtnSx,
598
+ children: "This Week"
599
+ }
600
+ ),
601
+ /* @__PURE__ */ jsx(
602
+ SDKButton,
603
+ {
604
+ variant: quickRange === "month" ? "primary" : "outlined",
605
+ onClick: () => setQuick("month"),
606
+ sx: quickBtnSx,
607
+ children: "This Month"
608
+ }
609
+ ),
610
+ /* @__PURE__ */ jsxs(FormControl, { size: "small", sx: multiSelectFormSx, children: [
611
+ /* @__PURE__ */ jsx(InputLabel, { sx: multiSelectLabelSx, children: "Agents" }),
612
+ /* @__PURE__ */ jsxs(
613
+ Select,
614
+ {
615
+ multiple: true,
616
+ value: selectedAgentIds,
617
+ label: "Agents",
618
+ onChange: (e) => {
619
+ const ids = e.target.value;
620
+ if (ids.includes(AGENT_SELECT_ALL_ID)) {
621
+ onAgentsChange(allAgentsSelected ? [] : agents.map((a) => a.id));
622
+ } else {
623
+ onAgentsChange(ids);
624
+ }
625
+ },
626
+ renderValue: (selected) => {
627
+ const sel = selected;
628
+ if (sel.length === 0 || sel.length === agents.length) return "All Agents";
629
+ return `${sel.length} Agent(s)`;
630
+ },
631
+ sx: multiSelectSx,
632
+ MenuProps: { PaperProps: { sx: multiSelectMenuPropsSx } },
633
+ children: [
634
+ /* @__PURE__ */ jsxs(MenuItem, { value: AGENT_SELECT_ALL_ID, children: [
635
+ /* @__PURE__ */ jsx(
636
+ Checkbox,
637
+ {
638
+ checked: allAgentsSelected,
639
+ indeterminate: selectedAgentIds.length > 0 && !allAgentsSelected,
640
+ size: "small"
641
+ }
642
+ ),
643
+ "Select All"
644
+ ] }),
645
+ agents.map((a) => /* @__PURE__ */ jsxs(MenuItem, { value: a.id, children: [
646
+ /* @__PURE__ */ jsx(Checkbox, { checked: selectedAgentIds.includes(a.id), size: "small" }),
647
+ /* @__PURE__ */ jsx(
648
+ Typography,
649
+ {
650
+ noWrap: true,
651
+ sx: { fontSize: "0.76rem", maxWidth: 180 },
652
+ title: a.fullName || a.userId,
653
+ children: a.fullName || a.userId
654
+ }
655
+ )
656
+ ] }, a.id))
657
+ ]
658
+ }
659
+ )
660
+ ] }),
661
+ /* @__PURE__ */ jsxs(FormControl, { size: "small", sx: multiSelectFormSx, children: [
662
+ /* @__PURE__ */ jsx(InputLabel, { sx: multiSelectLabelSx, children: "All Queues" }),
663
+ /* @__PURE__ */ jsxs(
664
+ Select,
665
+ {
666
+ multiple: true,
667
+ value: selectedQueueIds,
668
+ label: "All Queues",
669
+ sx: multiSelectSx,
670
+ MenuProps: { PaperProps: { sx: multiSelectMenuPropsSx } },
671
+ onChange: (e) => {
672
+ const ids = e.target.value;
673
+ if (ids.includes(-1)) {
674
+ onQueuesChange(allQueuesSelected ? [] : queues.map((q) => q.id));
675
+ } else {
676
+ onQueuesChange(ids);
677
+ }
678
+ },
679
+ renderValue: (selected) => {
680
+ const sel = selected;
681
+ if (sel.length === 0) return "All Queues";
682
+ if (sel.length === queues.length) return "All Queues";
683
+ return `${sel.length} Queue(s)`;
684
+ },
685
+ children: [
686
+ /* @__PURE__ */ jsxs(MenuItem, { value: -1, children: [
687
+ /* @__PURE__ */ jsx(
688
+ Checkbox,
689
+ {
690
+ checked: selectedQueueIds.length === queues.length && queues.length > 0,
691
+ size: "small"
692
+ }
693
+ ),
694
+ "Select All"
695
+ ] }),
696
+ queues.map((q) => /* @__PURE__ */ jsxs(MenuItem, { value: q.id, children: [
697
+ /* @__PURE__ */ jsx(Checkbox, { checked: selectedQueueIds.includes(q.id), size: "small" }),
698
+ q.name
699
+ ] }, q.id))
700
+ ]
701
+ }
702
+ )
703
+ ] })
704
+ ] });
705
+ });
706
+ var SearchBarLocal = memo(function SearchBarLocal2({ value, onChange }) {
707
+ return /* @__PURE__ */ jsx(
708
+ TextField,
709
+ {
710
+ size: "small",
711
+ placeholder: "Search phone, agent...",
712
+ value,
713
+ onChange: (e) => onChange(e.target.value),
714
+ slotProps: {
715
+ input: {
716
+ startAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "start", children: /* @__PURE__ */ jsx(SearchIcon, { sx: { color: "#1A5F6C", fontSize: 18 } }) }),
717
+ endAdornment: value ? /* @__PURE__ */ jsx(InputAdornment, { position: "end", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => onChange(""), edge: "end", children: /* @__PURE__ */ jsx(CloseIcon, { sx: { fontSize: 16, color: "#94a3b8" } }) }) }) : void 0
718
+ }
719
+ },
720
+ sx: searchBarSx
721
+ }
722
+ );
723
+ });
724
+ var CdrReportContent = memo(function CdrReportContent2() {
725
+ const [startDate, setStartDate] = useState2(dayjs().startOf("day"));
726
+ const [endDate, setEndDate] = useState2(dayjs());
727
+ const [quickRange, setQuickRange] = useState2("today");
728
+ const [searchText, setSearchText] = useState2("");
729
+ const [selectedAgentIds, setSelectedAgentIds] = useState2([]);
730
+ const [selectedQueueIds, setSelectedQueueIds] = useState2([]);
731
+ const [page, setPage] = useState2(0);
732
+ const [rowsPerPage, setRowsPerPage] = useState2(DEFAULT_PAGE_SIZE);
733
+ const [sortKey, setSortKey] = useState2("callStartTime");
734
+ const [sortDir, setSortDir] = useState2("desc");
735
+ const [debouncedSearch, setDebouncedSearch] = useState2("");
736
+ const debounceRef = useRef(void 0);
737
+ useEffect(() => {
738
+ debounceRef.current = setTimeout(() => setDebouncedSearch(searchText.trim()), 400);
739
+ return () => {
740
+ if (debounceRef.current) clearTimeout(debounceRef.current);
741
+ };
742
+ }, [searchText]);
743
+ const {
744
+ records,
745
+ totalRecords,
746
+ loading,
747
+ error,
748
+ setError,
749
+ fetchRecords,
750
+ exportExcel,
751
+ fetchRecording
752
+ } = useCdrReport();
753
+ const { agents, queues, fetchAll: fetchFilters } = useCdrFilters();
754
+ useEffect(() => {
755
+ fetchFilters();
756
+ }, [fetchFilters]);
757
+ const buildParams = useCallback2((pageNum) => {
758
+ const sd = startDate != null ? startDate : dayjs();
759
+ const ed = endDate != null ? endDate : dayjs();
760
+ return {
761
+ start_date: sd.format("YYYY-MM-DDTHH:mm:ss"),
762
+ end_date: ed.format("YYYY-MM-DDTHH:mm:ss"),
763
+ agent_id: selectedAgentIds.length > 0 ? selectedAgentIds : void 0,
764
+ queue_id: selectedQueueIds.length > 0 ? selectedQueueIds : void 0,
765
+ search: debouncedSearch || void 0,
766
+ page: pageNum + 1,
767
+ page_size: rowsPerPage
768
+ };
769
+ }, [startDate, endDate, selectedAgentIds, selectedQueueIds, debouncedSearch, rowsPerPage]);
770
+ useEffect(() => {
771
+ setPage(0);
772
+ fetchRecords(buildParams(0));
773
+ }, [buildParams, fetchRecords]);
774
+ const handlePageChange = useCallback2(
775
+ (_e, newPage) => {
776
+ setPage(newPage);
777
+ fetchRecords(buildParams(newPage));
778
+ },
779
+ [buildParams, fetchRecords]
780
+ );
781
+ const handleRowsPerPageChange = useCallback2((e) => {
782
+ setRowsPerPage(parseInt(e.target.value, 10));
783
+ setPage(0);
784
+ }, []);
785
+ const handleSort = useCallback2((key) => {
786
+ setSortKey((prevKey) => {
787
+ if (prevKey === key) {
788
+ setSortDir((d) => d === "asc" ? "desc" : "asc");
789
+ return prevKey;
790
+ }
791
+ setSortDir("asc");
792
+ return key;
793
+ });
794
+ }, []);
795
+ const sortedRecords = useMemo(() => {
796
+ const rows = [...records];
797
+ rows.sort((a, b) => {
798
+ const cmp = comparator(a, b, sortKey);
799
+ return sortDir === "asc" ? cmp : -cmp;
800
+ });
801
+ return rows;
802
+ }, [records, sortKey, sortDir]);
803
+ const handleExportExcel = useCallback2(async () => {
804
+ const sd = startDate != null ? startDate : dayjs();
805
+ const ed = endDate != null ? endDate : dayjs();
806
+ try {
807
+ await exportExcel({
808
+ start_date: sd.format("YYYY-MM-DDTHH:mm:ss"),
809
+ end_date: ed.format("YYYY-MM-DDTHH:mm:ss"),
810
+ agent_id: selectedAgentIds.length > 0 ? selectedAgentIds : void 0,
811
+ queue_id: selectedQueueIds.length > 0 ? selectedQueueIds : void 0,
812
+ search: debouncedSearch || void 0
813
+ });
814
+ } catch (err) {
815
+ setError((err == null ? void 0 : err.message) || "Failed to export Excel");
816
+ }
817
+ }, [startDate, endDate, selectedAgentIds, selectedQueueIds, debouncedSearch, exportExcel, setError]);
818
+ const columns = [
819
+ { id: "phoneNumber", label: "Phone Number" },
820
+ { id: "callStartTime", label: "Call Start" },
821
+ { id: "callType", label: "Call Type" },
822
+ { id: "callStatus", label: "Status" },
823
+ { id: "agentName", label: "User Name" },
824
+ { id: "queueName", label: "Queues" },
825
+ { id: "extension", label: "Extension" },
826
+ { id: "talkDurationFmt", label: "Talk Duration" },
827
+ { id: "holdDurationFmt", label: "Hold Duration" },
828
+ { id: "ringingDurationFmt", label: "Ringing Duration" },
829
+ { id: "queueDurationFmt", label: "Queue Duration" },
830
+ { id: "wrapupDurationFmt", label: "Wrapup Duration" },
831
+ { id: "disposition", label: "Disposition" },
832
+ { id: "remarks", label: "Remarks" },
833
+ { id: "wasTransferred", label: "Transferred" }
834
+ ];
835
+ return /* @__PURE__ */ jsx(LocalizationProvider, { dateAdapter: AdapterDayjs, children: /* @__PURE__ */ jsxs(Box, { sx: pageContainerSx, children: [
836
+ /* @__PURE__ */ jsx(
837
+ SDKPageHeader,
838
+ {
839
+ title: "CDR Report",
840
+ actions: /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [
841
+ /* @__PURE__ */ jsx(SearchBarLocal, { value: searchText, onChange: setSearchText }),
842
+ /* @__PURE__ */ jsx(
843
+ SDKPermissionGuard,
844
+ {
845
+ permissions: [
846
+ SDK_PERMISSIONS.REPORTS_CDR_EXPORT,
847
+ SDK_PERMISSIONS.REPORTS_CDR_MANAGE
848
+ ],
849
+ showFallback: false,
850
+ children: /* @__PURE__ */ jsx(
851
+ SDKButton,
852
+ {
853
+ startIcon: /* @__PURE__ */ jsx(DownloadIcon, { sx: { fontSize: "0.95rem !important" } }),
854
+ disabled: !records.length,
855
+ onClick: handleExportExcel,
856
+ sx: { height: 36 },
857
+ children: "Export Excel"
858
+ }
859
+ )
860
+ }
861
+ )
862
+ ] })
863
+ }
864
+ ),
865
+ /* @__PURE__ */ jsxs(Box, { sx: contentSx, children: [
866
+ /* @__PURE__ */ jsxs(Box, { sx: filterBarBoxSx, children: [
867
+ /* @__PURE__ */ jsx(
868
+ FilterBar,
869
+ {
870
+ startDate,
871
+ endDate,
872
+ onStartChange: setStartDate,
873
+ onEndChange: setEndDate,
874
+ quickRange,
875
+ onQuickRangeChange: setQuickRange,
876
+ agents,
877
+ selectedAgentIds,
878
+ onAgentsChange: setSelectedAgentIds,
879
+ queues,
880
+ selectedQueueIds,
881
+ onQueuesChange: setSelectedQueueIds
882
+ }
883
+ ),
884
+ error && /* @__PURE__ */ jsx(Alert, { severity: "error", sx: alertSx, children: error })
885
+ ] }),
886
+ loading && /* @__PURE__ */ jsx(Box, { sx: loadingBoxSx, children: /* @__PURE__ */ jsx(CircularProgress, { sx: { color: COLORS.blue }, size: 28 }) }),
887
+ /* @__PURE__ */ jsxs(Paper, { elevation: 0, sx: tablePaperSx, children: [
888
+ /* @__PURE__ */ jsx(TableContainer, { children: /* @__PURE__ */ jsxs(Table, { size: "small", sx: tableSx, children: [
889
+ /* @__PURE__ */ jsx(TableHead, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
890
+ columns.map((col) => /* @__PURE__ */ jsx(TableCell, { sx: headCellSx, children: /* @__PURE__ */ jsx(
891
+ TableSortLabel,
892
+ {
893
+ active: sortKey === col.id,
894
+ direction: sortKey === col.id ? sortDir : "asc",
895
+ onClick: () => handleSort(col.id),
896
+ sx: sortLabelSx,
897
+ children: col.label
898
+ }
899
+ ) }, col.id)),
900
+ /* @__PURE__ */ jsx(TableCell, { sx: __spreadProps(__spreadValues({}, headCellSx), { minWidth: 220 }), children: "Recording" })
901
+ ] }) }),
902
+ /* @__PURE__ */ jsx(TableBody, { children: !loading && sortedRecords.length === 0 ? /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(
903
+ TableCell,
904
+ {
905
+ colSpan: columns.length + 1,
906
+ align: "center",
907
+ sx: { py: 4, color: "#999", fontFamily: FONT, fontSize: "0.85rem" },
908
+ children: "No CDR records for the selected filters."
909
+ }
910
+ ) }) : sortedRecords.map((row, idx) => /* @__PURE__ */ jsxs(TableRow, { hover: true, sx: tableRowSx, children: [
911
+ /* @__PURE__ */ jsx(TableCell, { sx: cellNumberSx, children: row.phoneNumber || "" }),
912
+ /* @__PURE__ */ jsx(TableCell, { sx: cellDateSx, children: row.callStartTime ? dayjs(row.callStartTime).format("DD MMM YYYY hh:mm A") : "" }),
913
+ /* @__PURE__ */ jsx(TableCell, { children: row.callType ? /* @__PURE__ */ jsx(Chip, { label: row.callType, size: "small", sx: callTypePillSx(row.callType) }) : null }),
914
+ /* @__PURE__ */ jsx(TableCell, { children: row.callStatus ? /* @__PURE__ */ jsx(Chip, { label: row.callStatus, size: "small", sx: statusPillSx(row.callStatus) }) : null }),
915
+ /* @__PURE__ */ jsx(TableCell, { sx: cellDateSx, children: row.agentName || "" }),
916
+ /* @__PURE__ */ jsx(TableCell, { children: row.queueName ? /* @__PURE__ */ jsx(Chip, { label: row.queueName, size: "small", sx: queueChipSx }) : null }),
917
+ /* @__PURE__ */ jsx(TableCell, { sx: cellDateSx, children: row.extension || "" }),
918
+ /* @__PURE__ */ jsx(TableCell, { sx: cellTalkSx, children: row.talkDurationFmt || "" }),
919
+ /* @__PURE__ */ jsx(TableCell, { sx: cellHoldSx, children: row.holdDurationFmt || "" }),
920
+ /* @__PURE__ */ jsx(TableCell, { sx: cellDurationSx, children: row.ringingDurationFmt || "" }),
921
+ /* @__PURE__ */ jsx(TableCell, { sx: cellDurationSx, children: row.queueDurationFmt || "" }),
922
+ /* @__PURE__ */ jsx(TableCell, { sx: cellDurationSx, children: row.wrapupDurationFmt || "" }),
923
+ /* @__PURE__ */ jsx(TableCell, { sx: cellDateSx, children: row.disposition || "" }),
924
+ /* @__PURE__ */ jsx(TableCell, { sx: cellDateSx, children: row.remarks ? /* @__PURE__ */ jsx(Tooltip, { title: row.remarks, children: /* @__PURE__ */ jsx("span", { style: { display: "inline-block", maxWidth: 180, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", verticalAlign: "bottom" }, children: row.remarks }) }) : "" }),
925
+ /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(
926
+ Chip,
927
+ {
928
+ label: row.wasTransferred ? "Yes" : "No",
929
+ size: "small",
930
+ sx: row.wasTransferred ? transferYesSx : transferNoSx
931
+ }
932
+ ) }),
933
+ /* @__PURE__ */ jsx(TableCell, { children: row.callUuid ? /* @__PURE__ */ jsx(
934
+ RecordingCell,
935
+ {
936
+ callUuid: row.callUuid,
937
+ recordingPath: row.recordingPath,
938
+ fetchRecording
939
+ }
940
+ ) : null })
941
+ ] }, row.callUuid || idx)) })
942
+ ] }) }),
943
+ /* @__PURE__ */ jsx(
944
+ TablePagination,
945
+ {
946
+ component: "div",
947
+ count: totalRecords,
948
+ page,
949
+ onPageChange: handlePageChange,
950
+ rowsPerPage,
951
+ rowsPerPageOptions: [...ROWS_PER_PAGE_OPTIONS],
952
+ onRowsPerPageChange: handleRowsPerPageChange,
953
+ sx: paginationSx
954
+ }
955
+ )
956
+ ] })
957
+ ] })
958
+ ] }) });
959
+ });
960
+ var CdrReport = () => /* @__PURE__ */ jsx(SDKProvider, { children: /* @__PURE__ */ jsx(CdrReportContent, {}) });
961
+ var cdrReport_default = CdrReport;
962
+ export {
963
+ cdrReport_default as default
964
+ };
965
+ //# sourceMappingURL=cdrReport-6OBRKAYS.mjs.map