chordia-ui 3.7.2 → 3.7.4
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/dist/CustomFilterChips.cjs.js +1 -1
- package/dist/CustomFilterChips.cjs.js.map +1 -1
- package/dist/CustomFilterChips.es.js +239 -125
- package/dist/CustomFilterChips.es.js.map +1 -1
- package/dist/DataTable2.cjs.js +2 -0
- package/dist/DataTable2.cjs.js.map +1 -0
- package/dist/DataTable2.es.js +1863 -0
- package/dist/DataTable2.es.js.map +1 -0
- package/dist/components/UpdatedInteractionDetails.cjs.js +2 -2
- package/dist/components/UpdatedInteractionDetails.cjs.js.map +1 -1
- package/dist/components/UpdatedInteractionDetails.es.js +14 -13
- package/dist/components/UpdatedInteractionDetails.es.js.map +1 -1
- package/dist/components/data.cjs.js +1 -1
- package/dist/components/data.cjs.js.map +1 -1
- package/dist/components/data.es.js +157 -153
- package/dist/components/data.es.js.map +1 -1
- package/dist/components/performance.cjs.js +1 -1
- package/dist/components/performance.cjs.js.map +1 -1
- package/dist/components/performance.es.js +1900 -480
- package/dist/components/performance.es.js.map +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +94 -89
- package/dist/index.es.js.map +1 -1
- package/package.json +1 -1
- package/src/components/UpdatedInteractionDetails/UpdatedInteractionDetails.jsx +13 -13
- package/src/components/UpdatedInteractionDetails/UpdatedThreads.jsx +1 -0
- package/src/components/common/CustomFilterChips.jsx +5 -1
- package/src/components/common/Pagination.jsx +152 -39
- package/src/components/data/DataTable2.jsx +2449 -0
- package/src/components/data/DataTableFilters2.jsx +186 -0
- package/src/components/data/index.js +2 -0
- package/src/components/index.js +2 -2
- package/src/components/performance/PerformanceDetailsPage.jsx +940 -0
- package/src/components/performance/PerformancePanel.jsx +423 -297
- package/src/components/performance/SupervisorSelect.jsx +386 -0
- package/src/components/performance/index.js +3 -1
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import React from "react";
|
|
4
4
|
import {
|
|
5
5
|
Download,
|
|
6
|
-
Users,
|
|
7
6
|
ChevronDown,
|
|
8
7
|
Calendar,
|
|
9
8
|
Search,
|
|
@@ -13,8 +12,15 @@ import {
|
|
|
13
12
|
Lightbulb,
|
|
14
13
|
ShieldCheck,
|
|
15
14
|
Phone,
|
|
16
|
-
|
|
15
|
+
Signal,
|
|
16
|
+
FolderKanban,
|
|
17
|
+
Layers,
|
|
18
|
+
Check,
|
|
17
19
|
} from "lucide-react";
|
|
20
|
+
import DataTable2 from "../data/DataTable2";
|
|
21
|
+
import PerformanceDetailsPage from "./PerformanceDetailsPage";
|
|
22
|
+
|
|
23
|
+
const EMPTY_STAT = { value: "", label: "", trend: "up" };
|
|
18
24
|
|
|
19
25
|
const C = {
|
|
20
26
|
ink: "#2E3236",
|
|
@@ -258,219 +264,142 @@ function SearchBar() {
|
|
|
258
264
|
}
|
|
259
265
|
|
|
260
266
|
function CompassRating({ value = 0, max = 5 }) {
|
|
261
|
-
const
|
|
267
|
+
const v = typeof value === "number" ? value : parseFloat(value) || 0;
|
|
268
|
+
const full = Math.floor(v);
|
|
269
|
+
const hasPartial = v % 1 > 0 && full < max;
|
|
262
270
|
return (
|
|
263
271
|
<div style={{ display: "flex", gap: 4 }}>
|
|
264
|
-
{Array.from({ length: max }).map((_, i) =>
|
|
265
|
-
<
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
return (
|
|
308
|
-
<div style={{ width: "100%" }}>
|
|
309
|
-
<div
|
|
310
|
-
style={{
|
|
311
|
-
display: "flex",
|
|
312
|
-
alignItems: "center",
|
|
313
|
-
background: C.iconCircle,
|
|
314
|
-
border: `1px solid ${C.border}`,
|
|
315
|
-
borderTopLeftRadius: 4,
|
|
316
|
-
borderTopRightRadius: 4,
|
|
317
|
-
}}
|
|
318
|
-
>
|
|
319
|
-
{TABLE_COLS.map((col) => (
|
|
320
|
-
<div
|
|
321
|
-
key={col.key}
|
|
272
|
+
{Array.from({ length: max }).map((_, i) => {
|
|
273
|
+
if (i < full) {
|
|
274
|
+
return (
|
|
275
|
+
<span
|
|
276
|
+
key={i}
|
|
277
|
+
style={{
|
|
278
|
+
width: 10,
|
|
279
|
+
height: 10,
|
|
280
|
+
borderRadius: "50%",
|
|
281
|
+
background: C.black,
|
|
282
|
+
display: "inline-block",
|
|
283
|
+
}}
|
|
284
|
+
/>
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
if (i === full && hasPartial) {
|
|
288
|
+
return (
|
|
289
|
+
<span
|
|
290
|
+
key={i}
|
|
291
|
+
style={{
|
|
292
|
+
width: 10,
|
|
293
|
+
height: 10,
|
|
294
|
+
borderRadius: "50%",
|
|
295
|
+
display: "inline-flex",
|
|
296
|
+
alignItems: "center",
|
|
297
|
+
justifyContent: "center",
|
|
298
|
+
}}
|
|
299
|
+
>
|
|
300
|
+
<span
|
|
301
|
+
style={{
|
|
302
|
+
width: 6,
|
|
303
|
+
height: 6,
|
|
304
|
+
borderRadius: "50%",
|
|
305
|
+
background: C.black,
|
|
306
|
+
display: "inline-block",
|
|
307
|
+
}}
|
|
308
|
+
/>
|
|
309
|
+
</span>
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
return (
|
|
313
|
+
<span
|
|
314
|
+
key={i}
|
|
322
315
|
style={{
|
|
323
|
-
width:
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
justifyContent: col.align === "right" ? "flex-end" : "flex-start",
|
|
330
|
-
fontFamily: FONT_DISPLAY,
|
|
331
|
-
fontSize: 14,
|
|
332
|
-
fontWeight: 500,
|
|
333
|
-
color: C.black,
|
|
316
|
+
width: 10,
|
|
317
|
+
height: 10,
|
|
318
|
+
borderRadius: "50%",
|
|
319
|
+
background: C.white,
|
|
320
|
+
border: `1px solid ${C.border}`,
|
|
321
|
+
display: "inline-block",
|
|
334
322
|
boxSizing: "border-box",
|
|
335
323
|
}}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
{col.suffix && (
|
|
340
|
-
<span style={{ fontWeight: 400, color: C.muted, marginLeft: 4 }}>{col.suffix}</span>
|
|
341
|
-
)}
|
|
342
|
-
</span>
|
|
343
|
-
</div>
|
|
344
|
-
))}
|
|
345
|
-
</div>
|
|
346
|
-
{TABLE_ROWS.map((row, i) => (
|
|
347
|
-
<div
|
|
348
|
-
key={i}
|
|
349
|
-
style={{
|
|
350
|
-
display: "flex",
|
|
351
|
-
alignItems: "center",
|
|
352
|
-
borderLeft: `1px solid ${C.border}`,
|
|
353
|
-
borderRight: `1px solid ${C.border}`,
|
|
354
|
-
borderBottom: `1px solid ${C.border}`,
|
|
355
|
-
background: C.white,
|
|
356
|
-
}}
|
|
357
|
-
>
|
|
358
|
-
{TABLE_COLS.map((col) => {
|
|
359
|
-
const value = row[col.key];
|
|
360
|
-
const isCompass = col.key === "compass";
|
|
361
|
-
return (
|
|
362
|
-
<div
|
|
363
|
-
key={col.key}
|
|
364
|
-
style={{
|
|
365
|
-
width: col.width,
|
|
366
|
-
flex: col.flex,
|
|
367
|
-
padding: "0 12px",
|
|
368
|
-
height: 36,
|
|
369
|
-
display: "flex",
|
|
370
|
-
alignItems: "center",
|
|
371
|
-
justifyContent: col.align === "right" ? "flex-end" : "flex-start",
|
|
372
|
-
gap: isCompass ? 12 : 0,
|
|
373
|
-
fontFamily: FONT_BODY,
|
|
374
|
-
fontSize: 14,
|
|
375
|
-
fontWeight: col.bold ? 500 : 400,
|
|
376
|
-
color: col.bold ? C.black : C.ink,
|
|
377
|
-
boxSizing: "border-box",
|
|
378
|
-
}}
|
|
379
|
-
>
|
|
380
|
-
{isCompass ? (
|
|
381
|
-
<>
|
|
382
|
-
<span style={{ width: 32, textAlign: "right", color: C.ink }}>{value}</span>
|
|
383
|
-
<CompassRating value={value} />
|
|
384
|
-
</>
|
|
385
|
-
) : (
|
|
386
|
-
<span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{value}</span>
|
|
387
|
-
)}
|
|
388
|
-
</div>
|
|
389
|
-
);
|
|
390
|
-
})}
|
|
391
|
-
</div>
|
|
392
|
-
))}
|
|
324
|
+
/>
|
|
325
|
+
);
|
|
326
|
+
})}
|
|
393
327
|
</div>
|
|
394
328
|
);
|
|
395
329
|
}
|
|
396
330
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
>
|
|
420
|
-
{children}
|
|
421
|
-
</button>
|
|
422
|
-
);
|
|
331
|
+
export const DEFAULT_AGENT_COLUMNS = [
|
|
332
|
+
{ id: "agent", label: "Agent", width: 180, sortable: true, filterable: true },
|
|
333
|
+
{ id: "supervisor", label: "Supervisor", width: 170, sortable: true, filterable: true },
|
|
334
|
+
{ id: "calls", label: "Calls", width: 100, sortable: true },
|
|
335
|
+
{ id: "baseline", label: "Baseline", width: 110, sortable: true },
|
|
336
|
+
{ id: "lift", label: "Lift", width: 100, sortable: true },
|
|
337
|
+
{ id: "score", label: "Score", width: 100, sortable: true },
|
|
338
|
+
{
|
|
339
|
+
id: "compass",
|
|
340
|
+
label: "Compass Score",
|
|
341
|
+
width: 200,
|
|
342
|
+
sortable: true,
|
|
343
|
+
render: (value) => (
|
|
344
|
+
<span style={{ display: "inline-flex", alignItems: "center", gap: 12 }}>
|
|
345
|
+
<span style={{ minWidth: 32 }}>{value}</span>
|
|
346
|
+
<CompassRating value={typeof value === "number" ? value : 0} />
|
|
347
|
+
</span>
|
|
348
|
+
),
|
|
349
|
+
},
|
|
350
|
+
{ id: "totalDuration", label: "Total Duration", width: 150, sortable: true },
|
|
351
|
+
{ id: "avgDuration", label: "Average Duration", width: 170, sortable: true },
|
|
352
|
+
];
|
|
423
353
|
|
|
354
|
+
|
|
355
|
+
function SectionHeading({ icon: Icon, title, trailing, iconColor = C.ink, bordered = false }) {
|
|
424
356
|
return (
|
|
425
357
|
<div
|
|
426
358
|
style={{
|
|
427
359
|
display: "flex",
|
|
428
|
-
justifyContent: "space-between",
|
|
429
360
|
alignItems: "center",
|
|
430
|
-
|
|
361
|
+
gap: 8,
|
|
362
|
+
width: "100%",
|
|
363
|
+
paddingBottom: bordered ? 12 : 0,
|
|
364
|
+
borderBottom: bordered ? `1px solid ${C.border}` : "none",
|
|
431
365
|
}}
|
|
432
366
|
>
|
|
433
|
-
<div
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
367
|
+
<div
|
|
368
|
+
style={{
|
|
369
|
+
width: 24,
|
|
370
|
+
height: 24,
|
|
371
|
+
display: "flex",
|
|
372
|
+
alignItems: "center",
|
|
373
|
+
justifyContent: "center",
|
|
374
|
+
flexShrink: 0,
|
|
375
|
+
}}
|
|
376
|
+
>
|
|
377
|
+
<Icon size={22} color={iconColor} strokeWidth={1.75} />
|
|
442
378
|
</div>
|
|
443
|
-
<div
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
</PageBtn>
|
|
379
|
+
<div
|
|
380
|
+
style={{
|
|
381
|
+
flex: 1,
|
|
382
|
+
fontFamily: FONT_DISPLAY,
|
|
383
|
+
fontWeight: 700,
|
|
384
|
+
fontSize: 16,
|
|
385
|
+
color: C.ink,
|
|
386
|
+
lineHeight: "1.2",
|
|
387
|
+
textTransform: "uppercase",
|
|
388
|
+
letterSpacing: "0.02em",
|
|
389
|
+
}}
|
|
390
|
+
>
|
|
391
|
+
{title}
|
|
457
392
|
</div>
|
|
393
|
+
{trailing && (
|
|
394
|
+
<div style={{ fontFamily: FONT_BODY, fontSize: 14, color: C.ink }}>{trailing}</div>
|
|
395
|
+
)}
|
|
458
396
|
</div>
|
|
459
397
|
);
|
|
460
398
|
}
|
|
461
399
|
|
|
462
|
-
function
|
|
400
|
+
function SubSectionHeading({ icon: Icon, title }) {
|
|
463
401
|
return (
|
|
464
|
-
<div
|
|
465
|
-
style={{
|
|
466
|
-
display: "flex",
|
|
467
|
-
alignItems: "center",
|
|
468
|
-
gap: 16,
|
|
469
|
-
width: "100%",
|
|
470
|
-
paddingBottom: 12,
|
|
471
|
-
borderBottom: `1px solid ${C.border}`,
|
|
472
|
-
}}
|
|
473
|
-
>
|
|
402
|
+
<div style={{ display: "flex", alignItems: "center", gap: 16, width: "100%" }}>
|
|
474
403
|
<div
|
|
475
404
|
style={{
|
|
476
405
|
width: 34,
|
|
@@ -485,109 +414,130 @@ function SectionHeading({ icon: Icon, title, trailing }) {
|
|
|
485
414
|
>
|
|
486
415
|
<Icon size={20} color={C.ink} strokeWidth={1.75} />
|
|
487
416
|
</div>
|
|
488
|
-
<
|
|
417
|
+
<span
|
|
489
418
|
style={{
|
|
490
|
-
flex: 1,
|
|
491
419
|
fontFamily: FONT_DISPLAY,
|
|
492
|
-
fontWeight:
|
|
420
|
+
fontWeight: 700,
|
|
493
421
|
fontSize: 16,
|
|
494
422
|
color: C.ink,
|
|
495
|
-
lineHeight: "
|
|
423
|
+
lineHeight: "24px",
|
|
496
424
|
}}
|
|
497
425
|
>
|
|
498
426
|
{title}
|
|
499
|
-
</
|
|
500
|
-
{trailing && (
|
|
501
|
-
<div style={{ fontFamily: FONT_BODY, fontSize: 14, color: C.ink }}>{trailing}</div>
|
|
502
|
-
)}
|
|
427
|
+
</span>
|
|
503
428
|
</div>
|
|
504
429
|
);
|
|
505
430
|
}
|
|
506
431
|
|
|
507
|
-
|
|
432
|
+
const DEFAULT_BULLETS = [
|
|
433
|
+
{ text: "Bullet point one", citation: "Q:83", date: "2026-04-14" },
|
|
434
|
+
{ text: "Bullet point one", citation: "Q:83", date: "2026-04-14" },
|
|
435
|
+
{ text: "Bullet point one", citation: "Q:83", date: "2026-04-14" },
|
|
436
|
+
];
|
|
437
|
+
|
|
438
|
+
function AccordionItem({ title, summary, calls, percent, bullets, defaultExpanded = false }) {
|
|
439
|
+
const [expanded, setExpanded] = React.useState(defaultExpanded);
|
|
440
|
+
const rows = bullets && bullets.length > 0 ? bullets : DEFAULT_BULLETS;
|
|
508
441
|
return (
|
|
509
|
-
<
|
|
442
|
+
<button
|
|
443
|
+
type="button"
|
|
444
|
+
onClick={() => setExpanded((v) => !v)}
|
|
445
|
+
aria-expanded={expanded}
|
|
510
446
|
style={{
|
|
511
447
|
borderTop: `1px solid ${C.border}`,
|
|
448
|
+
borderLeft: "none",
|
|
449
|
+
borderRight: "none",
|
|
450
|
+
borderBottom: "none",
|
|
451
|
+
background: "transparent",
|
|
512
452
|
padding: 16,
|
|
513
453
|
display: "flex",
|
|
514
|
-
gap:
|
|
454
|
+
gap: 12,
|
|
515
455
|
alignItems: "flex-start",
|
|
516
456
|
cursor: "pointer",
|
|
457
|
+
width: "100%",
|
|
458
|
+
textAlign: "left",
|
|
459
|
+
fontFamily: FONT_BODY,
|
|
517
460
|
}}
|
|
518
461
|
>
|
|
519
|
-
<div
|
|
520
|
-
|
|
521
|
-
|
|
462
|
+
<div
|
|
463
|
+
style={{
|
|
464
|
+
flex: 1,
|
|
465
|
+
display: "flex",
|
|
466
|
+
flexDirection: "column",
|
|
467
|
+
gap: expanded ? 16 : 12,
|
|
468
|
+
minWidth: 0,
|
|
469
|
+
}}
|
|
470
|
+
>
|
|
471
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
|
472
|
+
<div
|
|
473
|
+
style={{
|
|
474
|
+
fontFamily: FONT_DISPLAY,
|
|
475
|
+
fontWeight: 600,
|
|
476
|
+
fontSize: 14,
|
|
477
|
+
color: C.black,
|
|
478
|
+
lineHeight: "24px",
|
|
479
|
+
}}
|
|
480
|
+
>
|
|
481
|
+
{title}
|
|
482
|
+
</div>
|
|
483
|
+
<div style={{ fontFamily: FONT_BODY, fontSize: 14, color: C.muted, lineHeight: "24px" }}>
|
|
484
|
+
{summary}
|
|
485
|
+
</div>
|
|
522
486
|
</div>
|
|
523
|
-
|
|
487
|
+
{expanded && (
|
|
488
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
|
489
|
+
{rows.map((b, i) => (
|
|
490
|
+
<div
|
|
491
|
+
key={i}
|
|
492
|
+
style={{
|
|
493
|
+
display: "flex",
|
|
494
|
+
alignItems: "center",
|
|
495
|
+
justifyContent: "space-between",
|
|
496
|
+
fontFamily: FONT_BODY,
|
|
497
|
+
fontSize: 14,
|
|
498
|
+
color: C.ink,
|
|
499
|
+
lineHeight: "24px",
|
|
500
|
+
gap: 16,
|
|
501
|
+
}}
|
|
502
|
+
>
|
|
503
|
+
<span>{b.text}</span>
|
|
504
|
+
<span style={{ display: "inline-flex", gap: 4, whiteSpace: "nowrap" }}>
|
|
505
|
+
<span style={{ color: C.ink }}>{b.citation}</span>
|
|
506
|
+
<span style={{ color: C.muted }}>({b.date})</span>
|
|
507
|
+
</span>
|
|
508
|
+
</div>
|
|
509
|
+
))}
|
|
510
|
+
</div>
|
|
511
|
+
)}
|
|
524
512
|
<div style={{ display: "flex", gap: 16 }}>
|
|
525
513
|
<div style={{ display: "flex", alignItems: "center", gap: 4 }}>
|
|
526
|
-
<Phone size={
|
|
527
|
-
<span style={{ fontFamily: FONT_BODY, fontSize: 12, color: C.ink }}>
|
|
514
|
+
<Phone size={14} color={C.ink} strokeWidth={1.75} />
|
|
515
|
+
<span style={{ fontFamily: FONT_BODY, fontSize: 12, color: C.ink, lineHeight: 1.5 }}>
|
|
516
|
+
{calls}
|
|
517
|
+
</span>
|
|
528
518
|
</div>
|
|
529
519
|
<div style={{ display: "flex", alignItems: "center", gap: 4 }}>
|
|
530
|
-
<TrendingUp size={
|
|
531
|
-
<span style={{ fontFamily: FONT_BODY, fontSize: 12, color: C.ink }}>
|
|
520
|
+
<TrendingUp size={14} color={C.ink} strokeWidth={1.75} />
|
|
521
|
+
<span style={{ fontFamily: FONT_BODY, fontSize: 12, color: C.ink, lineHeight: 1.5 }}>
|
|
522
|
+
{percent}
|
|
523
|
+
</span>
|
|
532
524
|
</div>
|
|
533
525
|
</div>
|
|
534
526
|
</div>
|
|
535
|
-
<ChevronDown
|
|
536
|
-
|
|
527
|
+
<ChevronDown
|
|
528
|
+
size={20}
|
|
529
|
+
color={C.ink}
|
|
530
|
+
strokeWidth={2}
|
|
531
|
+
style={{
|
|
532
|
+
flexShrink: 0,
|
|
533
|
+
transform: expanded ? "rotate(180deg)" : "none",
|
|
534
|
+
transition: "transform 150ms ease",
|
|
535
|
+
}}
|
|
536
|
+
/>
|
|
537
|
+
</button>
|
|
537
538
|
);
|
|
538
539
|
}
|
|
539
540
|
|
|
540
|
-
const GUIDANCE_ITEMS = [
|
|
541
|
-
{ title: "Close the loop:", summary: "Restate the plan and confirm what happens next so the customer", calls: "9411 Calls", percent: "95.5% (0.6pp)" },
|
|
542
|
-
{ title: "Frame solutions positively:", summary: "Lead with what you can do rather than what you can’t", calls: "9411 Calls", percent: "95.5% (0.6pp)" },
|
|
543
|
-
{ title: "Show expertise:", summary: "Lead with what you can do rather than what you can’t", calls: "9411 Calls", percent: "95.5% (0.6pp)" },
|
|
544
|
-
{ title: "Demonstrate active listening:", summary: "Lead with what you can do rather than what you can’t", calls: "9411 Calls", percent: "95.5% (0.6pp)" },
|
|
545
|
-
{ title: "Close the call properly:", summary: "Lead with what you can do rather than what you can’t", calls: "9411 Calls", percent: "95.5% (0.6pp)" },
|
|
546
|
-
{ title: "Address confusion:", summary: "Lead with what you can do rather than what you can’t", calls: "9411 Calls", percent: "95.5% (0.6pp)" },
|
|
547
|
-
];
|
|
548
|
-
|
|
549
|
-
const STRENGTH_ITEMS = [
|
|
550
|
-
{ title: "Excellence:", summary: "Used solution focused, Positive framing", calls: "9411 Calls", percent: "15.1% (0.6pp)" },
|
|
551
|
-
{ title: "Excellence:", summary: "Demonstrated strong product knowledge - The no. 1 behavioral predictor", calls: "9411 Calls", percent: "5.7% (0.6pp)" },
|
|
552
|
-
{ title: "Excellence:", summary: "Customer explicitly confirmed their issue was resolved", calls: "9411 Calls", percent: "5.7% (0.6pp)" },
|
|
553
|
-
];
|
|
554
|
-
|
|
555
|
-
const CALL_DRIVERS = [
|
|
556
|
-
{ label: "General Information Request", value: "26.3 %" },
|
|
557
|
-
{ label: "Appointment Scheduling", value: "12.2 %" },
|
|
558
|
-
{ label: "Order Placement", value: "12.3 %" },
|
|
559
|
-
{ label: "Order Status", value: "10.5 %" },
|
|
560
|
-
{ label: "Appointment Follow-up", value: "8.3 %" },
|
|
561
|
-
];
|
|
562
|
-
|
|
563
|
-
const CALL_PARADIGMS = [
|
|
564
|
-
{ label: "Resolutions", value: "43.6 %" },
|
|
565
|
-
{ label: "Informational", value: "32.6 %" },
|
|
566
|
-
{ label: "Conversion", value: "23.7 %" },
|
|
567
|
-
{ label: "Recovery", value: "0.2 %" },
|
|
568
|
-
];
|
|
569
|
-
|
|
570
|
-
const TOP_SIGNALS_LEFT = [
|
|
571
|
-
{ label: "Closure Quality Adequate", value: "99 %" },
|
|
572
|
-
{ label: "Evaluation Review", value: "96.5 %" },
|
|
573
|
-
{ label: "Customer Needs Fully Addressed", value: "59.1 %" },
|
|
574
|
-
{ label: "Call Flow Expectation Set", value: "57.6 %" },
|
|
575
|
-
{ label: "Next Steps Stated", value: "37 %" },
|
|
576
|
-
{ label: "Active Listening Demonstrated", value: "31 %" },
|
|
577
|
-
{ label: "Agent Courtesy Demonstrated", value: "30.5 %" },
|
|
578
|
-
{ label: "Engagement Budget Discussed", value: "29.8 %" },
|
|
579
|
-
];
|
|
580
|
-
|
|
581
|
-
const TOP_SIGNALS_RIGHT = [
|
|
582
|
-
{ label: "Fee or Cost Discussed", value: "26.6 %" },
|
|
583
|
-
{ label: "Objection Handling Effective", value: "27.2 %" },
|
|
584
|
-
{ label: "Opening Expectations Set", value: "26.6 %" },
|
|
585
|
-
{ label: "Empathy or Acknowledgement Expressed", value: "24.2 %" },
|
|
586
|
-
{ label: "Follow up Required", value: "22.4 %" },
|
|
587
|
-
{ label: "Estimate Provided", value: "18.9 %" },
|
|
588
|
-
{ label: "Script Adherence Likely", value: "17.5 %" },
|
|
589
|
-
];
|
|
590
|
-
|
|
591
541
|
function PercentRow({ label, value }) {
|
|
592
542
|
return (
|
|
593
543
|
<div
|
|
@@ -608,7 +558,80 @@ function PercentRow({ label, value }) {
|
|
|
608
558
|
);
|
|
609
559
|
}
|
|
610
560
|
|
|
611
|
-
export default function PerformancePanel({
|
|
561
|
+
export default function PerformancePanel({
|
|
562
|
+
className = "",
|
|
563
|
+
onAgentClick,
|
|
564
|
+
// Header text
|
|
565
|
+
title = "Performance",
|
|
566
|
+
subtitle = "Agent performance metrics, trends and coaching Insights",
|
|
567
|
+
dateRangeLabel = "",
|
|
568
|
+
// View-by dropdown
|
|
569
|
+
viewByOptions = [],
|
|
570
|
+
initialViewBy = "",
|
|
571
|
+
// KPI strip
|
|
572
|
+
volumeStat = EMPTY_STAT,
|
|
573
|
+
compassStat = EMPTY_STAT,
|
|
574
|
+
baselineSubStat = EMPTY_STAT,
|
|
575
|
+
agentLiftSubStat = EMPTY_STAT,
|
|
576
|
+
scoreSubStat = EMPTY_STAT,
|
|
577
|
+
// Agent table
|
|
578
|
+
agentRows = [],
|
|
579
|
+
agentColumns = [],
|
|
580
|
+
initialPageSize = 10,
|
|
581
|
+
// Insight sections
|
|
582
|
+
guidanceItems = [],
|
|
583
|
+
strengthItems = [],
|
|
584
|
+
callDrivers = [],
|
|
585
|
+
callParadigms = [],
|
|
586
|
+
topSignalsLeft = [],
|
|
587
|
+
topSignalsRight = [],
|
|
588
|
+
topSignalsTrailing = "",
|
|
589
|
+
}) {
|
|
590
|
+
const [selectedAgent, setSelectedAgent] = React.useState(null);
|
|
591
|
+
const [viewByOpen, setViewByOpen] = React.useState(false);
|
|
592
|
+
const [viewBy, setViewBy] = React.useState(initialViewBy);
|
|
593
|
+
const viewByTriggerRef = React.useRef(null);
|
|
594
|
+
const viewByPopoverRef = React.useRef(null);
|
|
595
|
+
|
|
596
|
+
React.useEffect(() => {
|
|
597
|
+
if (!viewByOpen) return;
|
|
598
|
+
const handler = (e) => {
|
|
599
|
+
if (
|
|
600
|
+
viewByPopoverRef.current?.contains(e.target) ||
|
|
601
|
+
viewByTriggerRef.current?.contains(e.target)
|
|
602
|
+
) {
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
setViewByOpen(false);
|
|
606
|
+
};
|
|
607
|
+
document.addEventListener("mousedown", handler);
|
|
608
|
+
return () => document.removeEventListener("mousedown", handler);
|
|
609
|
+
}, [viewByOpen]);
|
|
610
|
+
|
|
611
|
+
const viewByLabel =
|
|
612
|
+
viewByOptions.find((o) => o.id === viewBy)?.label || (viewByOptions[0]?.label ?? "");
|
|
613
|
+
|
|
614
|
+
const handleRowClick = (row) => {
|
|
615
|
+
if (onAgentClick) {
|
|
616
|
+
onAgentClick(row);
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
setSelectedAgent(row);
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
if (selectedAgent) {
|
|
623
|
+
return (
|
|
624
|
+
<PerformanceDetailsPage
|
|
625
|
+
className={className}
|
|
626
|
+
agentName={selectedAgent.agent}
|
|
627
|
+
startDate="2026-03-16"
|
|
628
|
+
endDate="2026-04-15"
|
|
629
|
+
totalCalls={`${selectedAgent.calls} Calls`}
|
|
630
|
+
onBack={() => setSelectedAgent(null)}
|
|
631
|
+
/>
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
|
|
612
635
|
return (
|
|
613
636
|
<div
|
|
614
637
|
className={className}
|
|
@@ -638,10 +661,10 @@ export default function PerformancePanel({ className = "" }) {
|
|
|
638
661
|
letterSpacing: "-0.01em",
|
|
639
662
|
}}
|
|
640
663
|
>
|
|
641
|
-
|
|
664
|
+
{title}
|
|
642
665
|
</h1>
|
|
643
666
|
<p style={{ fontFamily: FONT_BODY, fontSize: 14, color: "#1E1E1E", margin: 0, lineHeight: "1.4" }}>
|
|
644
|
-
|
|
667
|
+
{subtitle}
|
|
645
668
|
</p>
|
|
646
669
|
</div>
|
|
647
670
|
<button
|
|
@@ -667,11 +690,116 @@ export default function PerformancePanel({ className = "" }) {
|
|
|
667
690
|
</div>
|
|
668
691
|
|
|
669
692
|
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
|
670
|
-
<
|
|
671
|
-
|
|
672
|
-
|
|
693
|
+
<div style={{ position: "relative" }}>
|
|
694
|
+
<button
|
|
695
|
+
type="button"
|
|
696
|
+
ref={viewByTriggerRef}
|
|
697
|
+
onClick={() => setViewByOpen((v) => !v)}
|
|
698
|
+
aria-haspopup="listbox"
|
|
699
|
+
aria-expanded={viewByOpen}
|
|
700
|
+
style={{
|
|
701
|
+
display: "inline-flex",
|
|
702
|
+
alignItems: "center",
|
|
703
|
+
gap: 8,
|
|
704
|
+
height: 32,
|
|
705
|
+
padding: "0 16px",
|
|
706
|
+
borderRadius: 10,
|
|
707
|
+
border: `1px solid ${C.neutralBorder}`,
|
|
708
|
+
background: C.white,
|
|
709
|
+
cursor: "pointer",
|
|
710
|
+
fontFamily: FONT_BODY,
|
|
711
|
+
fontSize: 14,
|
|
712
|
+
lineHeight: "24px",
|
|
713
|
+
color: "#323232",
|
|
714
|
+
whiteSpace: "nowrap",
|
|
715
|
+
}}
|
|
716
|
+
>
|
|
717
|
+
{viewByLabel}
|
|
718
|
+
<ChevronDown
|
|
719
|
+
size={20}
|
|
720
|
+
strokeWidth={1.75}
|
|
721
|
+
style={{
|
|
722
|
+
transform: viewByOpen ? "rotate(180deg)" : "none",
|
|
723
|
+
transition: "transform 120ms ease",
|
|
724
|
+
}}
|
|
725
|
+
/>
|
|
726
|
+
</button>
|
|
727
|
+
{viewByOpen && (
|
|
728
|
+
<div
|
|
729
|
+
ref={viewByPopoverRef}
|
|
730
|
+
role="listbox"
|
|
731
|
+
style={{
|
|
732
|
+
position: "absolute",
|
|
733
|
+
top: 36,
|
|
734
|
+
left: 0,
|
|
735
|
+
zIndex: 20,
|
|
736
|
+
background: C.white,
|
|
737
|
+
border: `1px solid ${C.border}`,
|
|
738
|
+
borderRadius: 4,
|
|
739
|
+
boxShadow: "-2px 2px 1px rgba(0, 0, 0, 0.25)",
|
|
740
|
+
display: "flex",
|
|
741
|
+
flexDirection: "column",
|
|
742
|
+
minWidth: 144,
|
|
743
|
+
fontFamily: FONT_BODY,
|
|
744
|
+
}}
|
|
745
|
+
>
|
|
746
|
+
<div
|
|
747
|
+
style={{
|
|
748
|
+
padding: 12,
|
|
749
|
+
fontFamily: FONT_DISPLAY,
|
|
750
|
+
fontWeight: 700,
|
|
751
|
+
fontSize: 13,
|
|
752
|
+
color: C.ink,
|
|
753
|
+
lineHeight: 1.2,
|
|
754
|
+
}}
|
|
755
|
+
>
|
|
756
|
+
View by
|
|
757
|
+
</div>
|
|
758
|
+
{viewByOptions.map((opt) => {
|
|
759
|
+
const isSelected = viewBy === opt.id;
|
|
760
|
+
return (
|
|
761
|
+
<button
|
|
762
|
+
key={opt.id}
|
|
763
|
+
type="button"
|
|
764
|
+
role="option"
|
|
765
|
+
aria-selected={isSelected}
|
|
766
|
+
onClick={() => {
|
|
767
|
+
setViewBy(opt.id);
|
|
768
|
+
setViewByOpen(false);
|
|
769
|
+
}}
|
|
770
|
+
style={{
|
|
771
|
+
display: "flex",
|
|
772
|
+
alignItems: "center",
|
|
773
|
+
gap: 8,
|
|
774
|
+
padding: isSelected ? 12 : "12px 12px 12px 40px",
|
|
775
|
+
borderTop: `1px solid ${C.border}`,
|
|
776
|
+
borderLeft: "none",
|
|
777
|
+
borderRight: "none",
|
|
778
|
+
borderBottom: "none",
|
|
779
|
+
background: "transparent",
|
|
780
|
+
cursor: "pointer",
|
|
781
|
+
textAlign: "left",
|
|
782
|
+
fontFamily: FONT_BODY,
|
|
783
|
+
fontSize: 13,
|
|
784
|
+
lineHeight: 1.2,
|
|
785
|
+
color: C.ink,
|
|
786
|
+
width: "100%",
|
|
787
|
+
}}
|
|
788
|
+
>
|
|
789
|
+
{isSelected && (
|
|
790
|
+
<Check size={20} color={C.ink} strokeWidth={1.75} style={{ flexShrink: 0 }} />
|
|
791
|
+
)}
|
|
792
|
+
<span>{opt.label}</span>
|
|
793
|
+
</button>
|
|
794
|
+
);
|
|
795
|
+
})}
|
|
796
|
+
</div>
|
|
797
|
+
)}
|
|
798
|
+
</div>
|
|
673
799
|
<div style={{ display: "flex", gap: 10, alignItems: "center" }}>
|
|
674
|
-
|
|
800
|
+
{dateRangeLabel && (
|
|
801
|
+
<span style={{ fontFamily: FONT_BODY, fontSize: 13, color: C.ink }}>{dateRangeLabel}</span>
|
|
802
|
+
)}
|
|
675
803
|
<RangeChip label="8h" />
|
|
676
804
|
<RangeChip label="1d" />
|
|
677
805
|
<RangeChip label="7d" />
|
|
@@ -682,8 +810,8 @@ export default function PerformancePanel({ className = "" }) {
|
|
|
682
810
|
</div>
|
|
683
811
|
|
|
684
812
|
<div style={{ display: "flex", gap: 24 }}>
|
|
685
|
-
<StatCard value=
|
|
686
|
-
<StatCard value=
|
|
813
|
+
<StatCard value={volumeStat.value} label={volumeStat.label} trend={volumeStat.trend} style={{ width: 264, flexShrink: 0 }} />
|
|
814
|
+
<StatCard value={compassStat.value} label={compassStat.label} trend={compassStat.trend} style={{ width: 264, flexShrink: 0 }} />
|
|
687
815
|
<div
|
|
688
816
|
style={{
|
|
689
817
|
flex: 1,
|
|
@@ -697,30 +825,32 @@ export default function PerformancePanel({ className = "" }) {
|
|
|
697
825
|
boxSizing: "border-box",
|
|
698
826
|
}}
|
|
699
827
|
>
|
|
700
|
-
<StatSubCell value=
|
|
701
|
-
<StatSubCell value=
|
|
702
|
-
<StatSubCell value=
|
|
828
|
+
<StatSubCell value={baselineSubStat.value} label={baselineSubStat.label} trend={baselineSubStat.trend} />
|
|
829
|
+
<StatSubCell value={agentLiftSubStat.value} label={agentLiftSubStat.label} trend={agentLiftSubStat.trend} withDivider />
|
|
830
|
+
<StatSubCell value={scoreSubStat.value} label={scoreSubStat.label} trend={scoreSubStat.trend} withDivider />
|
|
703
831
|
</div>
|
|
704
832
|
</div>
|
|
705
833
|
|
|
706
|
-
<
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
834
|
+
<DataTable2
|
|
835
|
+
data={agentRows}
|
|
836
|
+
columns={agentColumns}
|
|
837
|
+
initialPageSize={initialPageSize}
|
|
838
|
+
onRowClick={handleRowClick}
|
|
839
|
+
/>
|
|
710
840
|
|
|
711
841
|
<div style={{ display: "flex", gap: 24 }}>
|
|
712
842
|
<div style={{ flex: 1, display: "flex", flexDirection: "column", gap: 16 }}>
|
|
713
|
-
<SectionHeading icon={Lightbulb} title="Guidance" />
|
|
714
|
-
<div style={{ display: "flex", flexDirection: "column" }}>
|
|
715
|
-
{
|
|
843
|
+
<SectionHeading icon={Lightbulb} title="Guidance" iconColor="#C98A5A" />
|
|
844
|
+
<div style={{ display: "flex", flexDirection: "column", borderBottom: `1px solid ${C.border}` }}>
|
|
845
|
+
{guidanceItems.map((item, i) => (
|
|
716
846
|
<AccordionItem key={i} {...item} />
|
|
717
847
|
))}
|
|
718
848
|
</div>
|
|
719
849
|
</div>
|
|
720
850
|
<div style={{ flex: 1, display: "flex", flexDirection: "column", gap: 16 }}>
|
|
721
|
-
<SectionHeading icon={ShieldCheck} title="
|
|
722
|
-
<div style={{ display: "flex", flexDirection: "column" }}>
|
|
723
|
-
{
|
|
851
|
+
<SectionHeading icon={ShieldCheck} title="Strengths" iconColor="#C98A5A" />
|
|
852
|
+
<div style={{ display: "flex", flexDirection: "column", borderBottom: `1px solid ${C.border}` }}>
|
|
853
|
+
{strengthItems.map((item, i) => (
|
|
724
854
|
<AccordionItem key={i} {...item} />
|
|
725
855
|
))}
|
|
726
856
|
</div>
|
|
@@ -728,24 +858,20 @@ export default function PerformancePanel({ className = "" }) {
|
|
|
728
858
|
</div>
|
|
729
859
|
|
|
730
860
|
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
|
731
|
-
<SectionHeading icon={Phone} title="Call Mix" />
|
|
861
|
+
<SectionHeading icon={Phone} title="Call Mix" iconColor="#C98A5A" />
|
|
732
862
|
<div style={{ display: "flex", gap: 24 }}>
|
|
733
863
|
<div style={{ flex: 1, display: "flex", flexDirection: "column", gap: 8 }}>
|
|
734
|
-
<
|
|
735
|
-
Drivers
|
|
736
|
-
</div>
|
|
864
|
+
<SubSectionHeading icon={FolderKanban} title="Drivers" />
|
|
737
865
|
<div style={{ display: "flex", flexDirection: "column" }}>
|
|
738
|
-
{
|
|
866
|
+
{callDrivers.map((row, i) => (
|
|
739
867
|
<PercentRow key={i} {...row} />
|
|
740
868
|
))}
|
|
741
869
|
</div>
|
|
742
870
|
</div>
|
|
743
871
|
<div style={{ flex: 1, display: "flex", flexDirection: "column", gap: 8 }}>
|
|
744
|
-
<
|
|
745
|
-
Paradigms
|
|
746
|
-
</div>
|
|
872
|
+
<SubSectionHeading icon={Layers} title="Paradigms" />
|
|
747
873
|
<div style={{ display: "flex", flexDirection: "column" }}>
|
|
748
|
-
{
|
|
874
|
+
{callParadigms.map((row, i) => (
|
|
749
875
|
<PercentRow key={i} {...row} />
|
|
750
876
|
))}
|
|
751
877
|
</div>
|
|
@@ -754,15 +880,15 @@ export default function PerformancePanel({ className = "" }) {
|
|
|
754
880
|
</div>
|
|
755
881
|
|
|
756
882
|
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
|
757
|
-
<SectionHeading icon={
|
|
883
|
+
<SectionHeading icon={Signal} title="Top Signals" iconColor="#C98A5A" trailing={topSignalsTrailing} bordered />
|
|
758
884
|
<div style={{ display: "flex", gap: 24 }}>
|
|
759
885
|
<div style={{ flex: 1, display: "flex", flexDirection: "column" }}>
|
|
760
|
-
{
|
|
886
|
+
{topSignalsLeft.map((row, i) => (
|
|
761
887
|
<PercentRow key={i} {...row} />
|
|
762
888
|
))}
|
|
763
889
|
</div>
|
|
764
890
|
<div style={{ flex: 1, display: "flex", flexDirection: "column" }}>
|
|
765
|
-
{
|
|
891
|
+
{topSignalsRight.map((row, i) => (
|
|
766
892
|
<PercentRow key={i} {...row} />
|
|
767
893
|
))}
|
|
768
894
|
</div>
|