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.
Files changed (36) hide show
  1. package/dist/CustomFilterChips.cjs.js +1 -1
  2. package/dist/CustomFilterChips.cjs.js.map +1 -1
  3. package/dist/CustomFilterChips.es.js +239 -125
  4. package/dist/CustomFilterChips.es.js.map +1 -1
  5. package/dist/DataTable2.cjs.js +2 -0
  6. package/dist/DataTable2.cjs.js.map +1 -0
  7. package/dist/DataTable2.es.js +1863 -0
  8. package/dist/DataTable2.es.js.map +1 -0
  9. package/dist/components/UpdatedInteractionDetails.cjs.js +2 -2
  10. package/dist/components/UpdatedInteractionDetails.cjs.js.map +1 -1
  11. package/dist/components/UpdatedInteractionDetails.es.js +14 -13
  12. package/dist/components/UpdatedInteractionDetails.es.js.map +1 -1
  13. package/dist/components/data.cjs.js +1 -1
  14. package/dist/components/data.cjs.js.map +1 -1
  15. package/dist/components/data.es.js +157 -153
  16. package/dist/components/data.es.js.map +1 -1
  17. package/dist/components/performance.cjs.js +1 -1
  18. package/dist/components/performance.cjs.js.map +1 -1
  19. package/dist/components/performance.es.js +1900 -480
  20. package/dist/components/performance.es.js.map +1 -1
  21. package/dist/index.cjs.js +1 -1
  22. package/dist/index.es.js +94 -89
  23. package/dist/index.es.js.map +1 -1
  24. package/package.json +1 -1
  25. package/src/components/UpdatedInteractionDetails/UpdatedInteractionDetails.jsx +13 -13
  26. package/src/components/UpdatedInteractionDetails/UpdatedThreads.jsx +1 -0
  27. package/src/components/common/CustomFilterChips.jsx +5 -1
  28. package/src/components/common/Pagination.jsx +152 -39
  29. package/src/components/data/DataTable2.jsx +2449 -0
  30. package/src/components/data/DataTableFilters2.jsx +186 -0
  31. package/src/components/data/index.js +2 -0
  32. package/src/components/index.js +2 -2
  33. package/src/components/performance/PerformanceDetailsPage.jsx +940 -0
  34. package/src/components/performance/PerformancePanel.jsx +423 -297
  35. package/src/components/performance/SupervisorSelect.jsx +386 -0
  36. 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
- Activity,
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 filled = Math.round(value);
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
- <span
266
- key={i}
267
- style={{
268
- width: 10,
269
- height: 10,
270
- borderRadius: "50%",
271
- background: i < filled ? C.black : C.white,
272
- border: i < filled ? "none" : `1px solid ${C.border}`,
273
- display: "inline-block",
274
- }}
275
- />
276
- ))}
277
- </div>
278
- );
279
- }
280
-
281
- const TABLE_COLS = [
282
- { key: "agent", label: "Agent", width: 170, align: "left", bold: true },
283
- { key: "supervisor", label: "Supervisor", width: 170, align: "left" },
284
- { key: "calls", label: "Calls", width: 80, align: "right" },
285
- { key: "baseline", label: "Baseline", width: 80, align: "right" },
286
- { key: "lift", label: "Lift", width: 80, align: "right" },
287
- { key: "score", label: "Score", width: 80, align: "right" },
288
- { key: "compass", label: "Compass Score", suffix: "(Out of 5)", width: 170, align: "left" },
289
- { key: "totalDuration", label: "Total Duration", flex: 1, align: "right" },
290
- { key: "avgDuration", label: "Average Duration", flex: 1, align: "right" },
291
- ];
292
-
293
- const TABLE_ROWS = [
294
- { agent: "Alex Johnson", supervisor: "Jordan Smith", calls: "1423", baseline: "97.2%", lift: "-5.7pp", score: "78.3%", compass: 3, totalDuration: "20h 25m", avgDuration: "52s" },
295
- { agent: "Ricardo Odom", supervisor: "Jordan Smith", calls: "1423", baseline: "97.2%", lift: "-5.7pp", score: "78.3%", compass: 3.25, totalDuration: "20h 25m", avgDuration: "52s" },
296
- { agent: "Latoya Tillman", supervisor: "Jordan Smith", calls: "1423", baseline: "97.2%", lift: "-5.7pp", score: "78.3%", compass: 4, totalDuration: "20h 25m", avgDuration: "52s" },
297
- { agent: "Javier Lawmon", supervisor: "Jordan Smith", calls: "1423", baseline: "97.2%", lift: "-5.7pp", score: "78.3%", compass: 4, totalDuration: "20h 25m", avgDuration: "52s" },
298
- { agent: "Naomi Wilkenmare", supervisor: "Jordan Smith", calls: "1423", baseline: "97.2%", lift: "-5.7pp", score: "78.3%", compass: 4, totalDuration: "20h 25m", avgDuration: "52s" },
299
- { agent: "Latoya Tillman", supervisor: "Jordan Smith", calls: "1423", baseline: "97.2%", lift: "-5.7pp", score: "78.3%", compass: 3.75, totalDuration: "20h 25m", avgDuration: "52s" },
300
- { agent: "Latoya Tillman", supervisor: "Jordan Smith", calls: "1423", baseline: "97.2%", lift: "-5.7pp", score: "78.3%", compass: 4, totalDuration: "20h 25m", avgDuration: "52s" },
301
- { agent: "Latoya Tillman", supervisor: "Jordan Smith", calls: "1423", baseline: "97.2%", lift: "-5.7pp", score: "78.3%", compass: 4, totalDuration: "20h 25m", avgDuration: "52s" },
302
- { agent: "Latoya Tillman", supervisor: "Jordan Smith", calls: "1423", baseline: "97.2%", lift: "-5.7pp", score: "78.3%", compass: 4, totalDuration: "20h 25m", avgDuration: "52s" },
303
- { agent: "Latoya Tillman", supervisor: "Jordan Smith", calls: "1423", baseline: "97.2%", lift: "-5.7pp", score: "78.3%", compass: 4, totalDuration: "20h 25m", avgDuration: "52s" },
304
- ];
305
-
306
- function PerfTable() {
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: col.width,
324
- flex: col.flex,
325
- padding: "0 12px",
326
- height: 36,
327
- display: "flex",
328
- alignItems: "center",
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
- <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
338
- {col.label}
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
- function PaginationBar() {
398
- const PageBtn = ({ children, active, disabled }) => (
399
- <button
400
- type="button"
401
- disabled={disabled}
402
- style={{
403
- height: 24,
404
- minWidth: 22,
405
- padding: "0 8px",
406
- borderRadius: 10,
407
- border: active ? "none" : `1px solid ${C.borderSubtle}`,
408
- background: active ? C.black : C.white,
409
- color: active ? C.white : C.black,
410
- opacity: disabled ? 0.5 : 1,
411
- fontFamily: FONT_BODY,
412
- fontSize: 12,
413
- display: "inline-flex",
414
- alignItems: "center",
415
- justifyContent: "center",
416
- gap: 4,
417
- cursor: disabled ? "default" : "pointer",
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
- padding: "12px 0",
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 style={{ display: "flex", alignItems: "center", gap: 16 }}>
434
- <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
435
- <span style={{ fontFamily: FONT_BODY, fontSize: 14, color: "#323232" }}>Result per page</span>
436
- <PageBtn>
437
- 10
438
- <ChevronDown size={12} strokeWidth={2} />
439
- </PageBtn>
440
- </div>
441
- <span style={{ fontFamily: FONT_BODY, fontSize: 14, color: "#323232" }}>1-10 of 50</span>
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 style={{ display: "flex", alignItems: "center", gap: 6 }}>
444
- <PageBtn disabled>
445
- <ChevronLeft size={12} strokeWidth={2} />
446
- Back
447
- </PageBtn>
448
- <PageBtn active>1</PageBtn>
449
- <PageBtn>2</PageBtn>
450
- <PageBtn>3</PageBtn>
451
- <PageBtn>4</PageBtn>
452
- <PageBtn>5</PageBtn>
453
- <PageBtn>
454
- Next
455
- <ChevronRight size={12} strokeWidth={2} />
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 SectionHeading({ icon: Icon, title, trailing }) {
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
- <div
417
+ <span
489
418
  style={{
490
- flex: 1,
491
419
  fontFamily: FONT_DISPLAY,
492
- fontWeight: 600,
420
+ fontWeight: 700,
493
421
  fontSize: 16,
494
422
  color: C.ink,
495
- lineHeight: "1.2",
423
+ lineHeight: "24px",
496
424
  }}
497
425
  >
498
426
  {title}
499
- </div>
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
- function AccordionItem({ title, summary, calls, percent }) {
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
- <div
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: 16,
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 style={{ flex: 1, display: "flex", flexDirection: "column", gap: 12, minWidth: 0 }}>
520
- <div style={{ fontFamily: FONT_BODY, fontWeight: 500, fontSize: 14, color: C.black, lineHeight: "24px" }}>
521
- {title}
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
- <div style={{ fontFamily: FONT_BODY, fontSize: 14, color: C.muted, lineHeight: "24px" }}>{summary}</div>
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={12} color={C.ink} strokeWidth={1.75} />
527
- <span style={{ fontFamily: FONT_BODY, fontSize: 12, color: C.ink }}>{calls}</span>
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={12} color={C.ink} strokeWidth={1.75} />
531
- <span style={{ fontFamily: FONT_BODY, fontSize: 12, color: C.ink }}>{percent}</span>
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 size={18} color={C.ink} strokeWidth={2} />
536
- </div>
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({ className = "" }) {
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
- Performance
664
+ {title}
642
665
  </h1>
643
666
  <p style={{ fontFamily: FONT_BODY, fontSize: 14, color: "#1E1E1E", margin: 0, lineHeight: "1.4" }}>
644
- Agent performance metrics, trends and coaching Insights
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
- <FilterButton icon={Users} trailing={ChevronDown}>
671
- 32 Supervisors
672
- </FilterButton>
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
- <span style={{ fontFamily: FONT_BODY, fontSize: 13, color: C.ink }}>2026-03-16 to 2026-04-15</span>
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="10956" label="Volume" trend="up" style={{ width: 264, flexShrink: 0 }} />
686
- <StatCard value="4.0" label="Compass Score (0.4)" trend="down" style={{ width: 264, flexShrink: 0 }} />
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="97.3%" label="Baseline (2.0)" trend="up" />
701
- <StatSubCell value="-6.3pp" label="Agent Lift (6.0)" trend="down" withDivider />
702
- <StatSubCell value="76.9%" label="Score (6.0)" trend="up" withDivider />
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
- <div>
707
- <PerfTable />
708
- <PaginationBar />
709
- </div>
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
- {GUIDANCE_ITEMS.map((item, i) => (
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="Consistent Strengths" />
722
- <div style={{ display: "flex", flexDirection: "column" }}>
723
- {STRENGTH_ITEMS.map((item, i) => (
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
- <div style={{ padding: "8px 0", fontFamily: FONT_DISPLAY, fontWeight: 600, fontSize: 15, color: C.ink, lineHeight: "24px" }}>
735
- Drivers
736
- </div>
864
+ <SubSectionHeading icon={FolderKanban} title="Drivers" />
737
865
  <div style={{ display: "flex", flexDirection: "column" }}>
738
- {CALL_DRIVERS.map((row, i) => (
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
- <div style={{ padding: "8px 0", fontFamily: FONT_DISPLAY, fontWeight: 600, fontSize: 15, color: C.ink, lineHeight: "24px" }}>
745
- Paradigms
746
- </div>
872
+ <SubSectionHeading icon={Layers} title="Paradigms" />
747
873
  <div style={{ display: "flex", flexDirection: "column" }}>
748
- {CALL_PARADIGMS.map((row, i) => (
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={Activity} title="Top Signals" trailing="15 Signals Detected" />
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
- {TOP_SIGNALS_LEFT.map((row, i) => (
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
- {TOP_SIGNALS_RIGHT.map((row, i) => (
891
+ {topSignalsRight.map((row, i) => (
766
892
  <PercentRow key={i} {...row} />
767
893
  ))}
768
894
  </div>