chordia-ui 3.4.2 → 3.4.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/Timeline.cjs.js +6 -1
- package/dist/Timeline.cjs.js.map +1 -1
- package/dist/Timeline.es.js +597 -129
- package/dist/Timeline.es.js.map +1 -1
- package/dist/UpdatedInteractionRecording.cjs.js +1 -1
- package/dist/UpdatedInteractionRecording.cjs.js.map +1 -1
- package/dist/UpdatedInteractionRecording.es.js +233 -241
- package/dist/UpdatedInteractionRecording.es.js.map +1 -1
- package/dist/components/UpdatedInteractionDetails.cjs.js +2 -2
- package/dist/components/UpdatedInteractionDetails.cjs.js.map +1 -1
- package/dist/components/UpdatedInteractionDetails.es.js +451 -360
- package/dist/components/UpdatedInteractionDetails.es.js.map +1 -1
- package/dist/components/media.cjs.js +1 -1
- package/dist/components/media.cjs.js.map +1 -1
- package/dist/components/media.es.js +8 -9
- package/dist/components/media.es.js.map +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs2.js +1 -1
- package/dist/index.cjs2.js.map +1 -1
- package/dist/index.es.js +52 -53
- package/dist/index.es.js.map +1 -1
- package/dist/index.es2.js +596 -587
- package/dist/index.es2.js.map +1 -1
- package/dist/pages/interactionDetails.cjs.js +2 -2
- package/dist/pages/interactionDetails.cjs.js.map +1 -1
- package/dist/pages/interactionDetails.es.js +16 -17
- package/dist/pages/interactionDetails.es.js.map +1 -1
- package/package.json +1 -1
- package/src/components/UpdatedInteractionDetails/UpdatedCompassScore.jsx +54 -2
- package/src/components/UpdatedInteractionDetails/UpdatedInteractionDetails.jsx +58 -14
- package/src/components/UpdatedInteractionDetails/UpdatedInteractionRecording.jsx +132 -126
- package/src/components/UpdatedInteractionDetails/UpdatedInteractionSignals.jsx +14 -6
- package/src/components/login/LoginPage.jsx +18 -3
- package/dist/TranscriptCard.cjs.js +0 -7
- package/dist/TranscriptCard.cjs.js.map +0 -1
- package/dist/TranscriptCard.es.js +0 -474
- package/dist/TranscriptCard.es.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
2
|
import { Info } from 'lucide-react';
|
|
3
3
|
|
|
4
4
|
/*
|
|
@@ -97,6 +97,56 @@ const ScoreBar = ({ value = 0, maxValue = 5 }) => (
|
|
|
97
97
|
</div>
|
|
98
98
|
);
|
|
99
99
|
|
|
100
|
+
const LEGEND_TOOLTIPS = {
|
|
101
|
+
'CSAT': 'Predicted Customer Satisfaction',
|
|
102
|
+
'Resolution': 'Predicted Resolution: was the issue fully resolved?',
|
|
103
|
+
'Process Adherence': 'Process Adherence: Did the agent follow procedures?',
|
|
104
|
+
'Communication': 'Predicted Communication Quality: Clarity, Empathy, Professionalism',
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const InfoTooltip = ({ text, children }) => {
|
|
108
|
+
const [show, setShow] = useState(false);
|
|
109
|
+
return (
|
|
110
|
+
<div
|
|
111
|
+
style={{ position: 'relative', display: 'inline-flex' }}
|
|
112
|
+
onMouseEnter={() => setShow(true)}
|
|
113
|
+
onMouseLeave={() => setShow(false)}
|
|
114
|
+
>
|
|
115
|
+
{children}
|
|
116
|
+
{show && (
|
|
117
|
+
<div style={{
|
|
118
|
+
position: 'absolute',
|
|
119
|
+
bottom: '100%',
|
|
120
|
+
left: '50%',
|
|
121
|
+
transform: 'translateX(-50%)',
|
|
122
|
+
marginBottom: 6,
|
|
123
|
+
display: 'flex',
|
|
124
|
+
width: 160,
|
|
125
|
+
padding: 10,
|
|
126
|
+
alignItems: 'center',
|
|
127
|
+
gap: 10,
|
|
128
|
+
borderRadius: 4,
|
|
129
|
+
border: '1px solid var(--Grey-absent, #D9D9D9)',
|
|
130
|
+
background: 'var(--Grey-Strong, #2E3236)',
|
|
131
|
+
zIndex: 20,
|
|
132
|
+
pointerEvents: 'none',
|
|
133
|
+
}}>
|
|
134
|
+
<span style={{
|
|
135
|
+
width: 140,
|
|
136
|
+
flexShrink: 0,
|
|
137
|
+
color: '#FFF',
|
|
138
|
+
fontSize: 12,
|
|
139
|
+
fontWeight: 400,
|
|
140
|
+
lineHeight: 'normal',
|
|
141
|
+
}}>
|
|
142
|
+
{text}
|
|
143
|
+
</span>
|
|
144
|
+
</div>
|
|
145
|
+
)}
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
|
|
100
150
|
const LegendItem = ({ label, value = 0, maxValue = 5 }) => (
|
|
101
151
|
<div style={{
|
|
102
152
|
display: 'flex',
|
|
@@ -119,7 +169,9 @@ const LegendItem = ({ label, value = 0, maxValue = 5 }) => (
|
|
|
119
169
|
}}>
|
|
120
170
|
{label}
|
|
121
171
|
</span>
|
|
122
|
-
<
|
|
172
|
+
<InfoTooltip text={LEGEND_TOOLTIPS[label] || label}>
|
|
173
|
+
<Info size={14} color={COLORS.infoIcon} strokeWidth={1} style={{ cursor: 'pointer' }} />
|
|
174
|
+
</InfoTooltip>
|
|
123
175
|
</div>
|
|
124
176
|
<ScoreBar value={value} maxValue={maxValue} />
|
|
125
177
|
</div>
|
|
@@ -43,6 +43,17 @@ const UpdatedInteractionDetails = ({
|
|
|
43
43
|
turnObservations,
|
|
44
44
|
highlightedTurns,
|
|
45
45
|
onTurnPlayPause,
|
|
46
|
+
// Signal evidence playback — host app can provide to play audio segments from signals
|
|
47
|
+
onPlayEvidence,
|
|
48
|
+
onHighlightTurns,
|
|
49
|
+
// Compass score props
|
|
50
|
+
compassScore,
|
|
51
|
+
predictedCsat,
|
|
52
|
+
// Customer session dropdown
|
|
53
|
+
customerSessions: customerSessionsList,
|
|
54
|
+
customerSessionCount,
|
|
55
|
+
onSessionClick,
|
|
56
|
+
onViewAllSessions,
|
|
46
57
|
// Footer navigation
|
|
47
58
|
prevSessionTitle,
|
|
48
59
|
prevSessionDesc,
|
|
@@ -55,6 +66,8 @@ const UpdatedInteractionDetails = ({
|
|
|
55
66
|
const [expandedSignals, setExpandedSignals] = useState(new Set());
|
|
56
67
|
const [showSessionDropdown, setShowSessionDropdown] = useState(false);
|
|
57
68
|
const [hoveredSessionIdx, setHoveredSessionIdx] = useState(null);
|
|
69
|
+
const [internalPlaying, setInternalPlaying] = useState(false);
|
|
70
|
+
const [internalCurrentTime, setInternalCurrentTime] = useState(0);
|
|
58
71
|
const recordingRef = useRef(null);
|
|
59
72
|
|
|
60
73
|
// Section refs for scroll-to-section tab behavior
|
|
@@ -72,23 +85,42 @@ const UpdatedInteractionDetails = ({
|
|
|
72
85
|
}
|
|
73
86
|
};
|
|
74
87
|
|
|
75
|
-
const sessionHistory = [
|
|
76
|
-
{ title: 'Tire Repair Appointment', date: '11/12/2025, 2:30:00 PM' },
|
|
77
|
-
{ title: 'Quote Confirmation', date: '10/1/2025, 9:00:00 AM' },
|
|
78
|
-
{ title: 'Automative Repair Enquiry', date: '09/15/2025, 11:45:00 AM' },
|
|
79
|
-
{ title: 'Vehicle Inspection Reminder', date: '07/20/2025, 1:00:00 PM' },
|
|
80
|
-
{ title: 'Oil Change Service', date: '05/5/2025, 10:00:00 AM' },
|
|
81
|
-
];
|
|
88
|
+
const sessionHistory = customerSessionsList || [];
|
|
82
89
|
|
|
83
90
|
/* Called from Signals "Show in transcript" icon — seeks recording to that time */
|
|
84
91
|
const handleShowInTranscript = (startMs) => {
|
|
85
92
|
const timeSec = startMs / 1000;
|
|
86
|
-
// Call the recording component's seek via ref
|
|
87
93
|
if (recordingRef.current?.seekTo) {
|
|
88
94
|
recordingRef.current.seekTo(timeSec);
|
|
89
95
|
}
|
|
90
96
|
};
|
|
91
97
|
|
|
98
|
+
/* Called from Signals evidence play button — seeks + plays audio segment */
|
|
99
|
+
const handlePlayEvidence = (ev) => {
|
|
100
|
+
if (onPlayEvidence) {
|
|
101
|
+
onPlayEvidence(ev);
|
|
102
|
+
} else {
|
|
103
|
+
// Default: seek to evidence start time via recording ref
|
|
104
|
+
const startMs = ev.start_ms ?? ev.startMs;
|
|
105
|
+
if (startMs != null && recordingRef.current?.seekTo) {
|
|
106
|
+
recordingRef.current.seekTo(startMs / 1000);
|
|
107
|
+
setInternalPlaying(true);
|
|
108
|
+
setInternalCurrentTime(startMs / 1000);
|
|
109
|
+
// Stop playing after evidence duration
|
|
110
|
+
const endMs = ev.end_ms ?? ev.endMs ?? (startMs + 5000);
|
|
111
|
+
const durationMs = endMs - startMs;
|
|
112
|
+
setTimeout(() => setInternalPlaying(false), durationMs);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/* Called to highlight transcript turns related to evidence */
|
|
118
|
+
const handleHighlightTurns = (turnIds) => {
|
|
119
|
+
if (onHighlightTurns) {
|
|
120
|
+
onHighlightTurns(turnIds);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
92
124
|
const toggleSignal = (key) => {
|
|
93
125
|
setExpandedSignals((prev) => {
|
|
94
126
|
const next = new Set(prev);
|
|
@@ -162,7 +194,7 @@ const UpdatedInteractionDetails = ({
|
|
|
162
194
|
|
|
163
195
|
const agentName = externalAgentName || 'Agent';
|
|
164
196
|
const customerName = externalCustomerName || 'Customer';
|
|
165
|
-
const
|
|
197
|
+
const sessionCount = customerSessionCount ?? sessionHistory.length;
|
|
166
198
|
const dateStr = meta.evaluated_dt ? new Date(meta.evaluated_dt).toLocaleString() : '3/29/2026, 8:30:00 AM';
|
|
167
199
|
const direction = demoCallPurpose.interaction_direction === 'inbound' ? 'Inbound' : 'Outbound';
|
|
168
200
|
|
|
@@ -315,7 +347,7 @@ const UpdatedInteractionDetails = ({
|
|
|
315
347
|
>
|
|
316
348
|
<CircleUser size={16} color="var(--Grey-Muted, #808183)" strokeWidth={1.5} />
|
|
317
349
|
<span style={{ fontSize: 13, fontWeight: 400, color: 'var(--Grey-Strong, #2E3236)', lineHeight: 1.2 }}>
|
|
318
|
-
{customerName} ({
|
|
350
|
+
{customerName} ({sessionCount} Sessions)
|
|
319
351
|
</span>
|
|
320
352
|
{showSessionDropdown
|
|
321
353
|
? <ChevronUp size={16} color="var(--Grey-Muted, #808183)" strokeWidth={1.5} />
|
|
@@ -358,7 +390,7 @@ const UpdatedInteractionDetails = ({
|
|
|
358
390
|
color: 'var(--Grey-Muted, #808183)',
|
|
359
391
|
lineHeight: 1,
|
|
360
392
|
}}>
|
|
361
|
-
{
|
|
393
|
+
{sessionCount} Sessions in past
|
|
362
394
|
</span>
|
|
363
395
|
</div>
|
|
364
396
|
</div>
|
|
@@ -366,7 +398,8 @@ const UpdatedInteractionDetails = ({
|
|
|
366
398
|
{/* Session list — Figma: 264px, pad 12 16, space-between, center */}
|
|
367
399
|
{sessionHistory.map((session, i) => (
|
|
368
400
|
<div
|
|
369
|
-
key={i}
|
|
401
|
+
key={session.id || i}
|
|
402
|
+
onClick={() => { if (onSessionClick) onSessionClick(session); setShowSessionDropdown(false); }}
|
|
370
403
|
onMouseEnter={() => setHoveredSessionIdx(i)}
|
|
371
404
|
onMouseLeave={() => setHoveredSessionIdx(null)}
|
|
372
405
|
style={{
|
|
@@ -413,7 +446,9 @@ const UpdatedInteractionDetails = ({
|
|
|
413
446
|
display: 'flex',
|
|
414
447
|
justifyContent: 'center',
|
|
415
448
|
}}>
|
|
416
|
-
<button
|
|
449
|
+
<button
|
|
450
|
+
onClick={() => { if (onViewAllSessions) onViewAllSessions(); setShowSessionDropdown(false); }}
|
|
451
|
+
style={{
|
|
417
452
|
display: 'flex',
|
|
418
453
|
height: 32,
|
|
419
454
|
padding: '16px 16px 16px 12px',
|
|
@@ -453,7 +488,12 @@ const UpdatedInteractionDetails = ({
|
|
|
453
488
|
}}>
|
|
454
489
|
{/* Left — Compass Score */}
|
|
455
490
|
<div style={{ flex: 1, minWidth: 0 }}>
|
|
456
|
-
<UpdatedCompassScore
|
|
491
|
+
<UpdatedCompassScore
|
|
492
|
+
score={compassScore ?? 0}
|
|
493
|
+
maxScore={100}
|
|
494
|
+
predictedScore={predictedCsat ?? 0}
|
|
495
|
+
predictedLabel="Predicted Objective"
|
|
496
|
+
/>
|
|
457
497
|
</div>
|
|
458
498
|
|
|
459
499
|
{/* Right — Context grid + Agent Lift Analysis stacked */}
|
|
@@ -526,7 +566,11 @@ const UpdatedInteractionDetails = ({
|
|
|
526
566
|
signals={demoSignals}
|
|
527
567
|
expandedSignals={expandedSignals}
|
|
528
568
|
toggleSignal={toggleSignal}
|
|
569
|
+
playEvidence={handlePlayEvidence}
|
|
570
|
+
highlightTurns={handleHighlightTurns}
|
|
529
571
|
onShowInTranscript={handleShowInTranscript}
|
|
572
|
+
timelinePlaying={timelinePlaying || internalPlaying}
|
|
573
|
+
currentTimeSeconds={currentTimeSeconds ?? internalCurrentTime}
|
|
530
574
|
/>
|
|
531
575
|
</div>
|
|
532
576
|
<div style={{ flex: 1, minWidth: 0 }}>
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
AudioLines, PlayCircle, PauseCircle,
|
|
6
6
|
FileSignal,
|
|
7
7
|
} from 'lucide-react';
|
|
8
|
-
import Timeline from '../media/Timeline.jsx';
|
|
9
8
|
|
|
10
9
|
function fmtTime(seconds) {
|
|
11
10
|
const m = Math.floor(seconds / 60);
|
|
@@ -222,7 +221,13 @@ const UpdatedInteractionRecording = forwardRef(function UpdatedInteractionRecord
|
|
|
222
221
|
useImperativeHandle(ref, () => ({
|
|
223
222
|
seekTo: (timeSec) => {
|
|
224
223
|
handleSeek(timeSec);
|
|
225
|
-
if (
|
|
224
|
+
if (parentManaged) {
|
|
225
|
+
// Parent manages audio — seek was already called via handleSeek → externalOnSeek
|
|
226
|
+
// Now trigger play if not already playing
|
|
227
|
+
if (!externalPlaying && externalOnTogglePlay) {
|
|
228
|
+
externalOnTogglePlay();
|
|
229
|
+
}
|
|
230
|
+
} else if (selfManaged) {
|
|
226
231
|
const audio = internalAudioRef.current;
|
|
227
232
|
if (audio) {
|
|
228
233
|
audio.play().then(() => {
|
|
@@ -465,91 +470,86 @@ const UpdatedInteractionRecording = forwardRef(function UpdatedInteractionRecord
|
|
|
465
470
|
</div>
|
|
466
471
|
</div>
|
|
467
472
|
|
|
468
|
-
{/* ── Row 2: Progress bar ── */}
|
|
469
|
-
{
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
<
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
473
|
+
{/* ── Row 2: Progress bar (V4 style for all modes) ── */}
|
|
474
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
|
475
|
+
{/* Time label + scrubber bar — same layout as speaker rows: 60px label + 8px gap + flex bar */}
|
|
476
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
477
|
+
<span style={{
|
|
478
|
+
fontSize: 13, fontWeight: 500,
|
|
479
|
+
color: 'var(--Grey-Strong, #2E3236)',
|
|
480
|
+
fontFamily: 'var(--font-sans)',
|
|
481
|
+
lineHeight: 1.2,
|
|
482
|
+
minWidth: 60,
|
|
483
|
+
}}>
|
|
484
|
+
{fmtTime(effectiveDuration)}
|
|
485
|
+
</span>
|
|
486
|
+
<div
|
|
487
|
+
onClick={(e) => {
|
|
488
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
489
|
+
const clickX = e.clientX - rect.left;
|
|
490
|
+
const pct = Math.max(0, Math.min(1, clickX / rect.width));
|
|
491
|
+
handleSeek(pct * effectiveDuration);
|
|
492
|
+
}}
|
|
493
|
+
style={{
|
|
494
|
+
flex: 1, height: 16,
|
|
495
|
+
position: 'relative',
|
|
496
|
+
display: 'flex', alignItems: 'center',
|
|
497
|
+
cursor: 'pointer',
|
|
498
|
+
}}
|
|
499
|
+
>
|
|
500
|
+
{/* Background track */}
|
|
501
|
+
<div style={{
|
|
502
|
+
position: 'absolute', left: 0, right: 0,
|
|
503
|
+
height: 4, borderRadius: 2,
|
|
504
|
+
background: 'var(--rail-surface-2, #E3E1D7)',
|
|
505
|
+
pointerEvents: 'none',
|
|
506
|
+
}} />
|
|
507
|
+
{/* Played portion */}
|
|
508
|
+
<div style={{
|
|
509
|
+
position: 'absolute', left: 0, top: '50%',
|
|
510
|
+
transform: 'translateY(-50%)',
|
|
511
|
+
width: `${progress}%`,
|
|
512
|
+
height: 4, borderRadius: 2,
|
|
513
|
+
background: 'var(--Green-Primary, #00925F)',
|
|
514
|
+
pointerEvents: 'none',
|
|
515
|
+
}} />
|
|
516
|
+
{/* Scrubber handle */}
|
|
517
|
+
<svg
|
|
518
|
+
width="16" height="16" viewBox="0 0 16 16" fill="none"
|
|
519
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
505
520
|
style={{
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
521
|
+
position: 'absolute',
|
|
522
|
+
left: `${progress}%`,
|
|
523
|
+
top: '50%',
|
|
524
|
+
transform: 'translate(-50%, -50%)',
|
|
525
|
+
pointerEvents: 'none',
|
|
510
526
|
}}
|
|
511
527
|
>
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
{/* Played portion — Figma: #00925F, stroke w=4 */}
|
|
520
|
-
<div style={{
|
|
521
|
-
position: 'absolute', left: 0, top: '50%',
|
|
522
|
-
transform: 'translateY(-50%)',
|
|
523
|
-
width: `${progress}%`,
|
|
524
|
-
height: 4, borderRadius: 2,
|
|
525
|
-
background: 'var(--Green-Primary, #00925F)',
|
|
526
|
-
pointerEvents: 'none',
|
|
527
|
-
}} />
|
|
528
|
-
{/* Scrubber handle — Figma ellipse */}
|
|
529
|
-
<svg
|
|
530
|
-
width="16" height="16" viewBox="0 0 16 16" fill="none"
|
|
531
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
532
|
-
style={{
|
|
533
|
-
position: 'absolute',
|
|
534
|
-
left: `${progress}%`,
|
|
535
|
-
top: '50%',
|
|
536
|
-
transform: 'translate(-50%, -50%)',
|
|
537
|
-
pointerEvents: 'none',
|
|
538
|
-
}}
|
|
539
|
-
>
|
|
540
|
-
<path
|
|
541
|
-
d="M7.875 1C11.6572 1 14.75 4.1191 14.75 8C14.75 11.8809 11.6572 15 7.875 15C4.09284 15 1 11.8809 1 8C1 4.1191 4.09284 1 7.875 1Z"
|
|
542
|
-
fill="#FFF"
|
|
543
|
-
stroke="#00925F"
|
|
544
|
-
strokeWidth="2"
|
|
545
|
-
/>
|
|
546
|
-
</svg>
|
|
547
|
-
</div>
|
|
528
|
+
<path
|
|
529
|
+
d="M7.875 1C11.6572 1 14.75 4.1191 14.75 8C14.75 11.8809 11.6572 15 7.875 15C4.09284 15 1 11.8809 1 8C1 4.1191 4.09284 1 7.875 1Z"
|
|
530
|
+
fill="#FFF"
|
|
531
|
+
stroke="#00925F"
|
|
532
|
+
strokeWidth="2"
|
|
533
|
+
/>
|
|
534
|
+
</svg>
|
|
548
535
|
</div>
|
|
536
|
+
</div>
|
|
549
537
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
538
|
+
{/* ── Speaker timeline bars + playback indicator ── */}
|
|
539
|
+
<div style={{ position: 'relative', paddingBottom: 30, cursor: 'pointer' }}>
|
|
540
|
+
{[agentName, customerName].map((speaker, rowIdx) => {
|
|
541
|
+
const isAgent = rowIdx === 0;
|
|
542
|
+
// Build segments from real timelineSegments or fall back to demo
|
|
543
|
+
const speakerSegments = timelineSegments?.length
|
|
544
|
+
? timelineSegments
|
|
545
|
+
.filter(s => isAgent ? s.actor === agentName : s.actor === customerName)
|
|
546
|
+
.map(s => ({
|
|
547
|
+
start: effectiveDuration > 0 ? s.startTime / effectiveDuration : 0,
|
|
548
|
+
end: effectiveDuration > 0 ? s.endTime / effectiveDuration : 0,
|
|
549
|
+
}))
|
|
550
|
+
: (DEMO_SEGMENTS[isAgent ? 'Agent' : 'Customer'] || []);
|
|
551
|
+
|
|
552
|
+
return (
|
|
553
553
|
<div key={speaker} style={{
|
|
554
554
|
display: 'flex', alignItems: 'center', gap: 8,
|
|
555
555
|
height: 20,
|
|
@@ -576,7 +576,7 @@ const UpdatedInteractionRecording = forwardRef(function UpdatedInteractionRecord
|
|
|
576
576
|
cursor: 'pointer',
|
|
577
577
|
}}
|
|
578
578
|
>
|
|
579
|
-
{/* Thin baseline
|
|
579
|
+
{/* Thin baseline */}
|
|
580
580
|
<div style={{
|
|
581
581
|
position: 'absolute', left: 0, right: 0,
|
|
582
582
|
top: '50%', transform: 'translateY(-50%)',
|
|
@@ -584,68 +584,74 @@ const UpdatedInteractionRecording = forwardRef(function UpdatedInteractionRecord
|
|
|
584
584
|
background: 'var(--rail-surface-2, #E3E1D7)',
|
|
585
585
|
}} />
|
|
586
586
|
{/* Speech segments */}
|
|
587
|
-
{
|
|
587
|
+
{speakerSegments.map((seg, i) => (
|
|
588
588
|
<div key={i} style={{
|
|
589
589
|
position: 'absolute',
|
|
590
590
|
left: `${seg.start * 100}%`,
|
|
591
591
|
width: `${(seg.end - seg.start) * 100}%`,
|
|
592
592
|
top: 0, bottom: 0, borderRadius: 3,
|
|
593
|
-
background:
|
|
593
|
+
background: isAgent
|
|
594
594
|
? 'var(--Grey-Strong, #2E3236)'
|
|
595
595
|
: 'var(--Grey-Muted, #808183)',
|
|
596
596
|
}} />
|
|
597
597
|
))}
|
|
598
598
|
</div>
|
|
599
599
|
</div>
|
|
600
|
-
)
|
|
601
|
-
|
|
602
|
-
|
|
600
|
+
);
|
|
601
|
+
})}
|
|
602
|
+
|
|
603
|
+
{/* ── Playback position: dotted line + time tooltip ── */}
|
|
604
|
+
<div style={{
|
|
605
|
+
position: 'absolute',
|
|
606
|
+
left: 68, /* 60 label + 8 gap */
|
|
607
|
+
right: 0,
|
|
608
|
+
top: 0,
|
|
609
|
+
bottom: 0,
|
|
610
|
+
pointerEvents: 'none',
|
|
611
|
+
}}>
|
|
612
|
+
{/* Dotted vertical line */}
|
|
603
613
|
<div style={{
|
|
604
614
|
position: 'absolute',
|
|
605
|
-
left:
|
|
606
|
-
right: 0,
|
|
615
|
+
left: `${progress}%`,
|
|
607
616
|
top: 0,
|
|
617
|
+
height: 44,
|
|
618
|
+
transform: 'translateX(-50%)',
|
|
619
|
+
borderLeft: '1.5px dashed var(--Grey-Muted, #808183)',
|
|
620
|
+
opacity: 0.5,
|
|
621
|
+
}} />
|
|
622
|
+
|
|
623
|
+
{/* Time tooltip */}
|
|
624
|
+
<div style={{
|
|
625
|
+
position: 'absolute',
|
|
626
|
+
left: `${progress}%`,
|
|
608
627
|
bottom: 0,
|
|
609
|
-
|
|
628
|
+
transform: 'translateX(-50%)',
|
|
629
|
+
background: 'var(--Grey-Strong, #2E3236)',
|
|
630
|
+
color: 'var(--Grey-White, #FFF)',
|
|
631
|
+
fontSize: 14, fontWeight: 600,
|
|
632
|
+
lineHeight: 1.2,
|
|
633
|
+
padding: '4px 6px',
|
|
634
|
+
borderRadius: 4,
|
|
635
|
+
whiteSpace: 'nowrap',
|
|
610
636
|
}}>
|
|
611
|
-
{
|
|
612
|
-
<div style={{
|
|
613
|
-
position: 'absolute',
|
|
614
|
-
left: `${progress}%`,
|
|
615
|
-
top: 0,
|
|
616
|
-
height: 44, /* spans both rows */
|
|
617
|
-
transform: 'translateX(-50%)',
|
|
618
|
-
borderLeft: '1.5px dashed var(--Grey-Muted, #808183)',
|
|
619
|
-
opacity: 0.5,
|
|
620
|
-
}} />
|
|
621
|
-
|
|
622
|
-
{/* Time tooltip — Figma: Frame 30, bg=#2E3236, pad 4 6, r=4, 14px/600/#FFF */}
|
|
623
|
-
<div style={{
|
|
624
|
-
position: 'absolute',
|
|
625
|
-
left: `${progress}%`,
|
|
626
|
-
bottom: 0,
|
|
627
|
-
transform: 'translateX(-50%)',
|
|
628
|
-
background: 'var(--Grey-Strong, #2E3236)',
|
|
629
|
-
color: 'var(--Grey-White, #FFF)',
|
|
630
|
-
fontSize: 14, fontWeight: 600,
|
|
631
|
-
lineHeight: 1.2,
|
|
632
|
-
padding: '4px 6px',
|
|
633
|
-
borderRadius: 4,
|
|
634
|
-
whiteSpace: 'nowrap',
|
|
635
|
-
}}>
|
|
636
|
-
{fmtTime(displayTime)}
|
|
637
|
-
</div>
|
|
637
|
+
{fmtTime(displayTime)}
|
|
638
638
|
</div>
|
|
639
639
|
</div>
|
|
640
|
-
|
|
641
|
-
{/* Hidden audio element for self-managed mode */}
|
|
642
|
-
{selfManaged && (
|
|
643
|
-
<audio ref={internalAudioRef} preload="auto" style={{ display: 'none' }}>
|
|
644
|
-
<source src={audioUrl} type="audio/mpeg" />
|
|
645
|
-
</audio>
|
|
646
|
-
)}
|
|
647
640
|
</div>
|
|
648
|
-
|
|
641
|
+
|
|
642
|
+
{/* Hidden audio element — only for self-managed mode */}
|
|
643
|
+
{selfManaged && (
|
|
644
|
+
<audio ref={internalAudioRef} preload="auto" style={{ display: 'none' }}>
|
|
645
|
+
<source src={audioUrl} type="audio/mpeg" />
|
|
646
|
+
</audio>
|
|
647
|
+
)}
|
|
648
|
+
{/* Hidden audio element — for parent-managed mode */}
|
|
649
|
+
{parentManaged && audioUrl && (
|
|
650
|
+
<audio ref={externalAudioRef} preload="auto" style={{ display: 'none' }}>
|
|
651
|
+
<source src={audioUrl} type="audio/mpeg" />
|
|
652
|
+
</audio>
|
|
653
|
+
)}
|
|
654
|
+
</div>
|
|
649
655
|
</div>
|
|
650
656
|
|
|
651
657
|
{/* ════════════════════════════════════════
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
|
-
import { ChevronDown, ChevronUp, PlayCircle, FileSignal, ScrollText } from 'lucide-react';
|
|
2
|
+
import { ChevronDown, ChevronUp, PlayCircle, PauseCircle, FileSignal, ScrollText } from 'lucide-react';
|
|
3
3
|
|
|
4
4
|
/* Hover circle wrapper for icons */
|
|
5
5
|
const HoverIcon = ({ children, size = 28, onClick, title }) => {
|
|
@@ -285,11 +285,19 @@ const UpdatedInteractionSignals = ({
|
|
|
285
285
|
}}
|
|
286
286
|
>
|
|
287
287
|
<HoverIcon size={28}>
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
288
|
+
{isEvPlaying(ev) ? (
|
|
289
|
+
<PauseCircle
|
|
290
|
+
size={17}
|
|
291
|
+
color="var(--Grey-Muted, #808183)"
|
|
292
|
+
strokeWidth={1.5}
|
|
293
|
+
/>
|
|
294
|
+
) : (
|
|
295
|
+
<PlayCircle
|
|
296
|
+
size={17}
|
|
297
|
+
color="var(--Grey-Muted, #808183)"
|
|
298
|
+
strokeWidth={1}
|
|
299
|
+
/>
|
|
300
|
+
)}
|
|
293
301
|
</HoverIcon>
|
|
294
302
|
<span style={{
|
|
295
303
|
fontSize: 13,
|
|
@@ -830,10 +830,11 @@ export default function LoginPage({
|
|
|
830
830
|
|
|
831
831
|
{/* ── RIGHT — Form Panel ── */}
|
|
832
832
|
<div style={{
|
|
833
|
-
flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column',
|
|
834
|
-
alignItems: 'center', padding: view === 'waitlist' ? '20px 48px' : '32px 72px', position: 'relative',
|
|
833
|
+
flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column',
|
|
834
|
+
alignItems: 'center', padding: view === 'waitlist' ? '20px 48px' : '32px 72px 40px', position: 'relative',
|
|
835
835
|
background: 'white',
|
|
836
836
|
overflowY: 'auto', minHeight: 0,
|
|
837
|
+
justifyContent: 'center',
|
|
837
838
|
}}>
|
|
838
839
|
<div style={{
|
|
839
840
|
display: 'flex',
|
|
@@ -1414,12 +1415,26 @@ export default function LoginPage({
|
|
|
1414
1415
|
}}
|
|
1415
1416
|
/>
|
|
1416
1417
|
{/* <NavRow text="Not a member yet?" linkText="Sign Up" onClick={() => { setFirstName(''); setLastName(''); setSignupEmail(''); setSignupPassword(''); setSignupConfirm(''); setSignupError(''); setView('signup'); onSignUp?.(); }} /> */}
|
|
1417
|
-
<TermsFooter onTerms={onTerms} onPrivacyPolicy={onPrivacyPolicy} paddingTop={16} />
|
|
1418
1418
|
</div>
|
|
1419
1419
|
</div>
|
|
1420
1420
|
)}
|
|
1421
1421
|
|
|
1422
1422
|
</div>
|
|
1423
|
+
{/* Terms footer pinned to bottom — only for sign-in view (other views have inline footer) */}
|
|
1424
|
+
{view === 'signin' && (
|
|
1425
|
+
<div style={{
|
|
1426
|
+
position: 'absolute',
|
|
1427
|
+
bottom: 40,
|
|
1428
|
+
left: 0,
|
|
1429
|
+
right: 0,
|
|
1430
|
+
display: 'flex',
|
|
1431
|
+
justifyContent: 'center',
|
|
1432
|
+
}}>
|
|
1433
|
+
<div style={{ width: '100%', maxWidth: 360 }}>
|
|
1434
|
+
<TermsFooter onTerms={onTerms} onPrivacyPolicy={onPrivacyPolicy} paddingTop={0} />
|
|
1435
|
+
</div>
|
|
1436
|
+
</div>
|
|
1437
|
+
)}
|
|
1423
1438
|
</div>
|
|
1424
1439
|
</div>
|
|
1425
1440
|
);
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
"use strict";const r=require("react/jsx-runtime"),l=require("react"),T=require("lucide-react"),w={customer:{label:"Customer",railColor:"var(--rail-discovery)",cardBg:"var(--card-customer)",cardBgHighlight:"rgba(94, 136, 176, 0.12)",borderColor:"var(--border-subtle)"},agent:{label:"Agent",railColor:"var(--rail-outcome)",cardBg:"var(--card-agent)",cardBgHighlight:"rgba(107, 124, 147, 0.10)",borderColor:"var(--border-subtle)"},assistant:{label:"AI Assistant",railColor:"var(--rail-purple)",cardBg:"var(--card-assistant)",cardBgHighlight:"rgba(155, 122, 168, 0.10)",borderColor:"var(--border-subtle)"},system:{label:"System",railColor:"var(--text-faint)",cardBg:"transparent",cardBgHighlight:"transparent",borderColor:"transparent"}},E="conversation-turn-keyframes";function L(){if(typeof document>"u"||document.getElementById(E))return;const i=document.createElement("style");i.id=E,i.textContent=`
|
|
2
|
-
@keyframes turn-playing-pulse {
|
|
3
|
-
0%, 100% { opacity: 0.4; }
|
|
4
|
-
50% { opacity: 0.9; }
|
|
5
|
-
}
|
|
6
|
-
`,document.head.appendChild(i)}function A({role:i="agent",text:s,actorLabel:c,actorRailColor:h,actionKicker:x,toolBadges:f,streaming:d=!1,meta:v,timeRange:b,isHighlighted:m=!1,highlightRailColor:t,observations:n,onObservationClick:y,children:k}){l.useEffect(()=>{L()},[]);const p=w[i]||w.agent,j=c||p.label,C=h||p.railColor,B=m||d;return i==="system"?r.jsx("div",{style:{textAlign:"center",padding:"6px 0",flexShrink:0,fontSize:"var(--text-sm, 11px)",color:"var(--text-faint, rgba(30,33,37,0.36))",fontWeight:500,letterSpacing:"0.02em"},children:s}):r.jsxs("div",{style:{position:"relative",padding:"10px 14px 10px 18px",borderRadius:10,overflow:"hidden",flexShrink:0,background:B?p.cardBgHighlight:p.cardBg,border:`1px solid ${d?C:p.borderColor}`,boxShadow:d?`0 0 8px ${p.cardBgHighlight}`:"none",transition:"background 0.2s, border-color 0.2s, box-shadow 0.2s"},children:[r.jsx("div",{style:{position:"absolute",left:0,top:0,bottom:0,width:"var(--rail-width-thin, 4px)",backgroundColor:C,borderRadius:"10px 0 0 10px"}}),r.jsxs("div",{style:{display:"flex",alignItems:"flex-start",justifyContent:"space-between",gap:8,marginBottom:4},children:[r.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:2},children:[x?r.jsx("div",{style:{fontSize:"var(--text-xs-plus, 10.5px)",fontWeight:650,textTransform:"uppercase",letterSpacing:"var(--tracking-label, 0.16em)",color:C},children:x}):null,r.jsx("div",{style:{fontSize:"var(--text-xs-plus, 10.5px)",fontWeight:650,textTransform:"uppercase",letterSpacing:"var(--tracking-label, 0.16em)",color:"var(--text-faint, rgba(30,33,37,0.36))"},children:j})]}),f&&f.length>0?r.jsx("div",{style:{display:"flex",flexWrap:"wrap",gap:4,flexShrink:0,justifyContent:"flex-end"},children:f.map((e,a)=>{const o=e.pending,g=!o&&e.success===!1,u=o?"var(--rail-discovery, #5E88B0)":g?"var(--rail-compliance, #C98A5A)":"var(--text-xfaint, rgba(30,33,37,0.28))";return r.jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:5,fontSize:"var(--text-xs, 10px)",padding:"2px 8px 2px 6px",borderRadius:999,background:"var(--paper, rgba(255,255,255,0.78))",border:"1px solid var(--border-subtle, rgba(52,58,64,0.08))",color:"var(--text-faint, rgba(30,33,37,0.36))",fontWeight:500,fontFamily:"var(--font-fira-code, var(--font-mono, monospace))",letterSpacing:"0.01em",lineHeight:1.4,whiteSpace:"nowrap"},children:[r.jsx("span",{style:{width:5,height:5,borderRadius:"50%",backgroundColor:u,flexShrink:0}}),e.name]},a)})}):null]}),r.jsxs("div",{style:{fontSize:"var(--text-md, 13px)",lineHeight:"var(--leading-normal, 1.5)",color:d?"var(--text-strong, rgba(30,33,37,0.92))":"var(--text-base, rgba(30,33,37,0.78))",fontWeight:d?550:400},children:[s,d?r.jsx("span",{style:{display:"inline-block",width:6,height:14,background:C,marginLeft:2,borderRadius:1,animation:"cursorBlink 0.8s ease-in-out infinite",verticalAlign:"text-bottom"}}):null]}),b||k||n&&n.length>0?r.jsxs("div",{style:{marginTop:6,display:"flex",alignItems:"center",gap:6},children:[k,b?r.jsx("span",{style:{fontSize:"var(--text-sm, 11px)",padding:"2px 8px",borderRadius:999,border:"1px solid var(--border, rgba(52,58,64,0.12))",background:"var(--timestamp-bg, rgba(255,255,255,0.70))",color:"var(--text-muted, rgba(30,33,37,0.56))",fontFamily:"var(--font-mono, monospace)"},children:b}):null,n&&n.length>0?r.jsx("div",{style:{display:"flex",flexWrap:"wrap",gap:4,marginLeft:"auto"},children:n.map((e,a)=>r.jsxs("span",{onClick:e.onClick||(y?()=>y(e):void 0),style:{display:"inline-flex",alignItems:"center",gap:4,fontSize:"var(--text-xs, 10px)",padding:"2px 8px",borderRadius:999,background:`color-mix(in srgb, ${e.color||"var(--state-present)"} 12%, transparent)`,border:`1px solid color-mix(in srgb, ${e.color||"var(--state-present)"} 25%, transparent)`,color:e.color||"var(--state-present)",fontWeight:550,lineHeight:1.4,whiteSpace:"nowrap",cursor:e.onClick||y?"pointer":"default",transition:"background 0.15s"},title:e.reason||e.label,children:[r.jsx("span",{style:{width:4,height:4,borderRadius:"50%",backgroundColor:e.color||"var(--state-present)",flexShrink:0,opacity:.7}}),e.label]},a))}):null]}):null,v?r.jsx("div",{style:{marginTop:6,fontSize:"var(--text-xs-plus, 10.5px)",color:"var(--text-faint, rgba(30,33,37,0.36))",fontFamily:"var(--font-mono, monospace)"},children:v}):null,B&&t?r.jsx("div",{style:{position:"absolute",right:8,top:8,bottom:8,width:6,borderRadius:4,backgroundColor:t,opacity:.7,animation:"turn-playing-pulse 1.5s ease-in-out infinite"}}):null]})}const P=i=>{const[s,c]=i.split("–").map(h=>{const[x,f]=h.split(":").map(Number);return x*60+f});return{start:s,end:c}},W=i=>({customer:"customer",agent:"agent",third_party:"agent",system:"system",assistant:"assistant"})[i]||"agent";function z({turns:i,audioUrl:s,activeTurnIndex:c=-1,autoScrollActiveTurn:h=!1,isExternalPlaying:x=!1,onTurnPlayPause:f}){const[d,v]=l.useState(null),[b,m]=l.useState(!1),t=l.useRef(null),n=l.useRef(null),y=l.useRef([]),k=l.useRef(null),p=l.useRef(-1),j=typeof f=="function",C=x!==void 0;l.useEffect(()=>{if(s)return t.current=new Audio(s),t.current.preload="auto",()=>{t.current&&(t.current.pause(),t.current=null)}},[s]),l.useEffect(()=>()=>{t.current&&(t.current.pause(),n.current&&t.current.removeEventListener("timeupdate",n.current))},[]),l.useEffect(()=>{if(!h||c<0)return;const e=k.current,a=y.current[c];if(!a||!e)return;const o=e.getBoundingClientRect(),g=a.getBoundingClientRect(),u=12,R=g.top>=o.top+u&&g.bottom<=o.bottom-8;if(!(p.current===c)||!R){const H=g.top-o.top+e.scrollTop;e.scrollTop=Math.max(H-u,0)}p.current=c},[c,h]);const B=(e,a)=>{if(j){f(i[a],a);return}if(d===a&&b)t.current&&(t.current.pause(),n.current&&(t.current.removeEventListener("timeupdate",n.current),n.current=null)),m(!1);else{const o=d;if(v(a),s&&t.current){const{start:g,end:u}=P(e);o!==null&&o!==a&&(t.current.pause(),n.current&&(t.current.removeEventListener("timeupdate",n.current),n.current=null));const R=()=>{if(t.current){if(t.current.currentTime=g,u){const S=()=>{t.current&&t.current.currentTime>=u&&(t.current.pause(),m(!1),v(null),n.current&&(t.current.removeEventListener("timeupdate",n.current),n.current=null))};n.current=S,t.current.addEventListener("timeupdate",S)}t.current.play().then(()=>m(!0)).catch(S=>{S&&S.name==="AbortError"||(m(!1),v(null))})}};t.current.readyState>=2?R():(t.current.addEventListener("loadedmetadata",R,{once:!0}),t.current.load())}else m(!0)}};return r.jsxs("div",{ref:k,className:"custom-thin-scrollbar-library",style:{maxHeight:560,overflowY:"auto",scrollBehavior:"smooth",border:"1px solid var(--border, rgba(52,58,64,0.12))",borderRadius:"12px 0px 0px 12px",background:"var(--paper-elevated, rgba(255,255,255,0.82))",padding:16},children:[r.jsx("div",{style:{fontSize:"var(--text-xs-plus, 10.5px)",fontWeight:650,textTransform:"uppercase",letterSpacing:"var(--tracking-label, 0.16em)",color:"var(--text-faint, rgba(30,33,37,0.36))",marginBottom:12},children:"Transcript"}),r.jsx("div",{style:{display:"flex",flexDirection:"column",gap:8},children:i.map((e,a)=>{const o=j||C?!!x&&c===a:d===a&&b,g=!!e.isHighlighted||o,u=W(e.actorType);return r.jsx("div",{ref:R=>{y.current[a]=R},children:r.jsx(A,{role:u,text:e.text,actorLabel:e.actor,actorRailColor:e.actorColor,timeRange:e.timeRange,isHighlighted:g,highlightRailColor:o?e.highlightColor||(u==="agent"?"var(--rail-outcome)":"var(--rail-discovery)"):e.highlightColor,toolBadges:e.toolBadges,observations:e.observations,onObservationClick:e.onObservationClick,children:e.timeRange?r.jsx("button",{onClick:()=>B(e.timeRange,a),style:{width:24,height:24,borderRadius:"50%",border:"1px solid var(--border, rgba(52,58,64,0.12))",background:o?"var(--rail-discovery, #5E88B0)":"var(--paper, rgba(255,255,255,0.78))",display:"inline-flex",alignItems:"center",justifyContent:"center",cursor:"pointer",padding:0,transition:"all 0.15s"},"aria-label":o?`Pause segment ${e.timeRange}`:`Play segment ${e.timeRange}`,children:o?r.jsx(T.Pause,{style:{width:12,height:12,color:"var(--paper, #fff)",fill:"var(--paper, #fff)"},strokeWidth:0}):r.jsx(T.Play,{style:{width:12,height:12,color:"var(--text-muted, rgba(30,33,37,0.56))",fill:"var(--text-muted, rgba(30,33,37,0.56))",marginLeft:1},strokeWidth:0})}):null})},a)})})]})}exports.ConversationTurn=A;exports.TranscriptCard=z;
|
|
7
|
-
//# sourceMappingURL=TranscriptCard.cjs.js.map
|