chordia-ui 3.4.4 → 3.4.6
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/UpdatedInteractionRecording.cjs.js +1 -1
- package/dist/UpdatedInteractionRecording.cjs.js.map +1 -1
- package/dist/UpdatedInteractionRecording.es.js +100 -87
- 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 +425 -380
- package/dist/components/UpdatedInteractionDetails.es.js.map +1 -1
- package/package.json +1 -1
- package/src/components/UpdatedInteractionDetails/UpdatedCompassScore.jsx +35 -10
- package/src/components/UpdatedInteractionDetails/UpdatedInteractionContext.jsx +1 -1
- package/src/components/UpdatedInteractionDetails/UpdatedInteractionDetails.jsx +34 -15
- package/src/components/UpdatedInteractionDetails/UpdatedInteractionRecording.jsx +13 -0
- package/src/components/UpdatedInteractionDetails/UpdatedInteractionSignals.jsx +24 -21
package/package.json
CHANGED
|
@@ -60,7 +60,7 @@ const GaugeMeter = ({ score = 4, maxScore = 10 }) => {
|
|
|
60
60
|
const gap = 2; // degrees gap between segments
|
|
61
61
|
|
|
62
62
|
return (
|
|
63
|
-
<svg width=
|
|
63
|
+
<svg width="100%" viewBox={`0 0 ${svgW} ${svgH}`} fill="none" style={{ maxWidth: svgW }}>
|
|
64
64
|
{/* Segments from left (180°) to right (0°) */}
|
|
65
65
|
{Array.from({ length: totalSeg }, (_, i) => {
|
|
66
66
|
const fromA = 180 - i * segDeg - (i > 0 ? gap / 2 : 0);
|
|
@@ -201,6 +201,7 @@ const UpdatedCompassScore = ({
|
|
|
201
201
|
border: `1px solid ${COLORS.absent}`,
|
|
202
202
|
background: COLORS.white,
|
|
203
203
|
alignSelf: 'stretch',
|
|
204
|
+
flex: 1,
|
|
204
205
|
gap: 24,
|
|
205
206
|
}}>
|
|
206
207
|
{/* Section Title — left-aligned, vertical, gap: 8 */}
|
|
@@ -234,24 +235,48 @@ const UpdatedCompassScore = ({
|
|
|
234
235
|
display: 'flex',
|
|
235
236
|
alignItems: 'center',
|
|
236
237
|
justifyContent: 'center',
|
|
237
|
-
gap:
|
|
238
|
+
gap: 24,
|
|
238
239
|
flex: 1,
|
|
240
|
+
overflow: 'hidden',
|
|
239
241
|
}}>
|
|
240
242
|
{/* Left: Gauge + Pin + Score number */}
|
|
241
243
|
<div style={{
|
|
242
|
-
|
|
243
|
-
|
|
244
|
+
position: 'relative',
|
|
245
|
+
maxWidth: 243,
|
|
246
|
+
minWidth: 160,
|
|
247
|
+
flex: '0 1 243px',
|
|
244
248
|
flexDirection: 'column',
|
|
245
249
|
alignItems: 'center',
|
|
246
|
-
|
|
250
|
+
display: 'flex',
|
|
247
251
|
}}>
|
|
248
252
|
<GaugeMeter score={score} maxScore={maxScore} />
|
|
249
253
|
|
|
250
|
-
{/* Compass pin
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
254
|
+
{/* Compass pin — centered below gauge, needle rotates to point at score */}
|
|
255
|
+
{(() => {
|
|
256
|
+
// Pin sits at gauge center; needle rotates from 180° (score=0) to 0° (score=max)
|
|
257
|
+
const pct = maxScore > 0 ? Math.min(score / maxScore, 1) : 0;
|
|
258
|
+
// SVG pin's default needle points to top-right (~45°)
|
|
259
|
+
// We need: score=0 → needle points left (180°), score=max → needle points right (0°)
|
|
260
|
+
// Target angle on gauge: 180 - pct*180
|
|
261
|
+
// Pin default orientation: needle at ~45° from top → offset is 45
|
|
262
|
+
// Rotation needed: -(targetAngle - 45) to align needle with gauge position
|
|
263
|
+
const targetAngle = 180 - pct * 180;
|
|
264
|
+
const rotateDeg = -(targetAngle - 45);
|
|
265
|
+
return (
|
|
266
|
+
<svg
|
|
267
|
+
width="34" height="35" viewBox="0 0 34 35" fill="none"
|
|
268
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
269
|
+
style={{
|
|
270
|
+
marginTop: -24,
|
|
271
|
+
transform: `rotate(${rotateDeg}deg)`,
|
|
272
|
+
transformOrigin: 'center center',
|
|
273
|
+
}}
|
|
274
|
+
>
|
|
275
|
+
<path d="M16 25.2169C19.958 25.2169 23.1667 22.0083 23.1667 18.0503C23.1667 14.0922 19.958 10.8836 16 10.8836C12.042 10.8836 8.83334 14.0922 8.83334 18.0503C8.83334 22.0083 12.042 25.2169 16 25.2169Z" fill="var(--rail-orange, #C98A5A)" />
|
|
276
|
+
<path fillRule="evenodd" clipRule="evenodd" d="M30.4791 11.2328L33.4351 0L21.6888 3.09113C19.9212 2.41855 18.0036 2.05025 16 2.05025C7.16344 2.05025 0 9.2137 0 18.0503C0 26.8868 7.16344 34.0503 16 34.0503C24.8366 34.0503 32 26.8868 32 18.0503C32 15.6119 31.4546 13.301 30.4791 11.2328ZM16 30.5503C22.9036 30.5503 28.5 24.9538 28.5 18.0503C28.5 11.1467 22.9036 5.55025 16 5.55025C9.09644 5.55025 3.5 11.1467 3.5 18.0503C3.5 24.9538 9.09644 30.5503 16 30.5503Z" fill="var(--rail-orange, #C98A5A)" />
|
|
277
|
+
</svg>
|
|
278
|
+
);
|
|
279
|
+
})()}
|
|
255
280
|
|
|
256
281
|
{/* Score number below gauge */}
|
|
257
282
|
<div style={{
|
|
@@ -71,7 +71,7 @@ const UpdatedInteractionContext = ({
|
|
|
71
71
|
messages != null && { label: 'Messages', value: messages },
|
|
72
72
|
paradigm != null && { label: 'Paradigm', value: paradigm },
|
|
73
73
|
...dimensions.filter((_, i) => i % 2 === 1).map((d) => ({ label: d.label || d.key, value: d.value })),
|
|
74
|
-
{ label: 'More Details', isLink: true },
|
|
74
|
+
// { label: 'More Details', isLink: true },
|
|
75
75
|
].filter(Boolean);
|
|
76
76
|
|
|
77
77
|
const renderRow = (item) => {
|
|
@@ -46,9 +46,16 @@ const UpdatedInteractionDetails = ({
|
|
|
46
46
|
// Signal evidence playback — host app can provide to play audio segments from signals
|
|
47
47
|
onPlayEvidence,
|
|
48
48
|
onHighlightTurns,
|
|
49
|
+
// Context props — pass from host app to override block-derived defaults
|
|
50
|
+
callPurpose: externalCallPurpose,
|
|
51
|
+
classification: externalClassification,
|
|
52
|
+
outcomeQuality: externalOutcomeQuality,
|
|
49
53
|
// Compass score props
|
|
50
|
-
compassScore,
|
|
51
|
-
|
|
54
|
+
compassScore, // gauge meter value (e.g. outcomeLift.p_full) — drives meter fill, range 1-10
|
|
55
|
+
compassMaxScore = 10,
|
|
56
|
+
predictedCsat, // predicted objective number (e.g. compass_score) — shown as "03" top-right
|
|
57
|
+
predictedLabel = 'Predicted Objective',
|
|
58
|
+
compassLegends,
|
|
52
59
|
// Customer session dropdown
|
|
53
60
|
customerSessions: customerSessionsList,
|
|
54
61
|
customerSessionCount,
|
|
@@ -57,8 +64,10 @@ const UpdatedInteractionDetails = ({
|
|
|
57
64
|
// Footer navigation
|
|
58
65
|
prevSessionTitle,
|
|
59
66
|
prevSessionDesc,
|
|
67
|
+
prevSessionDisabled = false,
|
|
60
68
|
nextSessionTitle,
|
|
61
69
|
nextSessionDesc,
|
|
70
|
+
nextSessionDisabled = false,
|
|
62
71
|
onPreviousSession,
|
|
63
72
|
onNextSession,
|
|
64
73
|
}) => {
|
|
@@ -199,7 +208,9 @@ const UpdatedInteractionDetails = ({
|
|
|
199
208
|
const direction = demoCallPurpose.interaction_direction === 'inbound' ? 'Inbound' : 'Outbound';
|
|
200
209
|
|
|
201
210
|
return (
|
|
202
|
-
<div style={{ display: 'flex', flexDirection: 'column', width: '100%', background: 'var(--Grey-White, #FFF)' }}>
|
|
211
|
+
<div style={{ display: 'flex', flexDirection: 'column', width: '100%', height: '100%', background: 'var(--Grey-White, #FFF)' }}>
|
|
212
|
+
{/* Sticky Header + Tabs */}
|
|
213
|
+
<div style={{ position: 'sticky', top: 0, zIndex: 20, background: 'var(--Grey-White, #FFF)' }}>
|
|
203
214
|
{/* Header */}
|
|
204
215
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '24px 24px 0' }}>
|
|
205
216
|
<button
|
|
@@ -267,6 +278,7 @@ const UpdatedInteractionDetails = ({
|
|
|
267
278
|
</button>
|
|
268
279
|
))}
|
|
269
280
|
</div>
|
|
281
|
+
</div>{/* end sticky header + tabs */}
|
|
270
282
|
|
|
271
283
|
{/* All sections rendered — tabs scroll to them */}
|
|
272
284
|
<div style={{ padding: 24, flex: 1, overflowY: 'auto' }}>
|
|
@@ -396,7 +408,7 @@ const UpdatedInteractionDetails = ({
|
|
|
396
408
|
</div>
|
|
397
409
|
|
|
398
410
|
{/* Session list — Figma: 264px, pad 12 16, space-between, center */}
|
|
399
|
-
{sessionHistory.map((session, i) => (
|
|
411
|
+
{sessionHistory.slice(0, 5).map((session, i) => (
|
|
400
412
|
<div
|
|
401
413
|
key={session.id || i}
|
|
402
414
|
onClick={() => { if (onSessionClick) onSessionClick(session); setShowSessionDropdown(false); }}
|
|
@@ -482,17 +494,18 @@ const UpdatedInteractionDetails = ({
|
|
|
482
494
|
*/}
|
|
483
495
|
<div style={{
|
|
484
496
|
display: 'flex',
|
|
485
|
-
alignItems: '
|
|
497
|
+
alignItems: 'stretch',
|
|
486
498
|
gap: 24,
|
|
487
499
|
alignSelf: 'stretch',
|
|
488
500
|
}}>
|
|
489
501
|
{/* Left — Compass Score */}
|
|
490
|
-
<div style={{ flex: 1, minWidth: 0 }}>
|
|
502
|
+
<div style={{ flex: 1, minWidth: 0, display: 'flex' }}>
|
|
491
503
|
<UpdatedCompassScore
|
|
492
|
-
score={compassScore
|
|
504
|
+
score={compassScore != null ? Math.round(compassScore * 100) : 0}
|
|
493
505
|
maxScore={100}
|
|
494
506
|
predictedScore={predictedCsat ?? 0}
|
|
495
|
-
predictedLabel=
|
|
507
|
+
predictedLabel={predictedLabel}
|
|
508
|
+
legends={compassLegends}
|
|
496
509
|
/>
|
|
497
510
|
</div>
|
|
498
511
|
|
|
@@ -508,9 +521,9 @@ const UpdatedInteractionDetails = ({
|
|
|
508
521
|
}}>
|
|
509
522
|
<UpdatedInteractionContext
|
|
510
523
|
meta={demoMeta}
|
|
511
|
-
callPurpose={demoCallPurpose}
|
|
512
|
-
classification={demoClassification}
|
|
513
|
-
outcomeQuality=
|
|
524
|
+
callPurpose={externalCallPurpose || demoCallPurpose}
|
|
525
|
+
classification={externalClassification || demoClassification}
|
|
526
|
+
outcomeQuality={externalOutcomeQuality || 'Neutral'}
|
|
514
527
|
/>
|
|
515
528
|
<UpdatedInteractionScores
|
|
516
529
|
outcomeLift={demoOutcomeLift}
|
|
@@ -618,7 +631,8 @@ const UpdatedInteractionDetails = ({
|
|
|
618
631
|
}}>
|
|
619
632
|
{/* Previous Session */}
|
|
620
633
|
<button
|
|
621
|
-
onClick={onPreviousSession}
|
|
634
|
+
onClick={prevSessionDisabled ? undefined : onPreviousSession}
|
|
635
|
+
disabled={prevSessionDisabled}
|
|
622
636
|
style={{
|
|
623
637
|
display: 'flex',
|
|
624
638
|
alignItems: 'center',
|
|
@@ -627,7 +641,9 @@ const UpdatedInteractionDetails = ({
|
|
|
627
641
|
flex: 1,
|
|
628
642
|
background: 'var(--Grey-White, #FFF)',
|
|
629
643
|
border: '1px solid var(--Grey-absent, #D9D9D9)',
|
|
630
|
-
cursor: 'pointer',
|
|
644
|
+
cursor: prevSessionDisabled ? 'default' : 'pointer',
|
|
645
|
+
opacity: prevSessionDisabled ? 0.4 : 1,
|
|
646
|
+
pointerEvents: prevSessionDisabled ? 'none' : 'auto',
|
|
631
647
|
}}
|
|
632
648
|
>
|
|
633
649
|
<ArrowLeft size={24} color="var(--Grey-Muted, #808183)" strokeWidth={2} />
|
|
@@ -654,7 +670,8 @@ const UpdatedInteractionDetails = ({
|
|
|
654
670
|
|
|
655
671
|
{/* Next Session */}
|
|
656
672
|
<button
|
|
657
|
-
onClick={onNextSession}
|
|
673
|
+
onClick={nextSessionDisabled ? undefined : onNextSession}
|
|
674
|
+
disabled={nextSessionDisabled}
|
|
658
675
|
style={{
|
|
659
676
|
display: 'flex',
|
|
660
677
|
alignItems: 'center',
|
|
@@ -664,7 +681,9 @@ const UpdatedInteractionDetails = ({
|
|
|
664
681
|
flex: 1,
|
|
665
682
|
background: 'var(--Grey-White, #FFF)',
|
|
666
683
|
border: '1px solid var(--Grey-absent, #D9D9D9)',
|
|
667
|
-
cursor: 'pointer',
|
|
684
|
+
cursor: nextSessionDisabled ? 'default' : 'pointer',
|
|
685
|
+
opacity: nextSessionDisabled ? 0.4 : 1,
|
|
686
|
+
pointerEvents: nextSessionDisabled ? 'none' : 'auto',
|
|
668
687
|
}}
|
|
669
688
|
>
|
|
670
689
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
|
@@ -311,6 +311,19 @@ const UpdatedInteractionRecording = forwardRef(function UpdatedInteractionRecord
|
|
|
311
311
|
}))
|
|
312
312
|
: null;
|
|
313
313
|
|
|
314
|
+
// Auto-scroll transcript to active card during playback
|
|
315
|
+
useEffect(() => {
|
|
316
|
+
if (!activePlaying) return;
|
|
317
|
+
const activeIdx = turns ? activeTurnIndex : activeDemoIdx;
|
|
318
|
+
if (activeIdx == null || activeIdx < 0) return;
|
|
319
|
+
const container = scrollRef.current;
|
|
320
|
+
if (!container) return;
|
|
321
|
+
const cards = container.children;
|
|
322
|
+
if (cards?.[activeIdx]) {
|
|
323
|
+
cards[activeIdx].scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
324
|
+
}
|
|
325
|
+
}, [activeTurnIndex, activeDemoIdx, activePlaying, turns]);
|
|
326
|
+
|
|
314
327
|
// Computed display values
|
|
315
328
|
const effectiveTime = isDemo ? (demoSeekTime ?? 0) : activeCurrentTime;
|
|
316
329
|
const effectiveDuration = activeDuration || 156;
|
|
@@ -266,13 +266,14 @@ const UpdatedInteractionSignals = ({
|
|
|
266
266
|
</div>
|
|
267
267
|
|
|
268
268
|
{/* Evidence quotes — Frame 39 */}
|
|
269
|
-
{obs.evidence?.map((ev, evIdx) =>
|
|
270
|
-
ev.
|
|
269
|
+
{obs.evidence?.map((ev, evIdx) => {
|
|
270
|
+
const hasTimestamps = ev.start_ms != null && ev.end_ms != null;
|
|
271
|
+
return ev.text && (
|
|
271
272
|
<div
|
|
272
273
|
key={evIdx}
|
|
273
274
|
onClick={(e) => {
|
|
274
275
|
e.stopPropagation();
|
|
275
|
-
playEvidence(ev);
|
|
276
|
+
if (hasTimestamps) playEvidence(ev);
|
|
276
277
|
}}
|
|
277
278
|
onMouseEnter={() => highlightTurns(ev.turn_ids ?? [])}
|
|
278
279
|
onMouseLeave={() => highlightTurns([])}
|
|
@@ -281,24 +282,26 @@ const UpdatedInteractionSignals = ({
|
|
|
281
282
|
alignItems: 'center',
|
|
282
283
|
gap: 8,
|
|
283
284
|
padding: '8px 0',
|
|
284
|
-
cursor: 'pointer',
|
|
285
|
+
cursor: hasTimestamps ? 'pointer' : 'default',
|
|
285
286
|
}}
|
|
286
287
|
>
|
|
287
|
-
|
|
288
|
-
{
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
288
|
+
{hasTimestamps && (
|
|
289
|
+
<HoverIcon size={28}>
|
|
290
|
+
{isEvPlaying(ev) ? (
|
|
291
|
+
<PauseCircle
|
|
292
|
+
size={17}
|
|
293
|
+
color="var(--Grey-Muted, #808183)"
|
|
294
|
+
strokeWidth={1.5}
|
|
295
|
+
/>
|
|
296
|
+
) : (
|
|
297
|
+
<PlayCircle
|
|
298
|
+
size={17}
|
|
299
|
+
color="var(--Grey-Muted, #808183)"
|
|
300
|
+
strokeWidth={1}
|
|
301
|
+
/>
|
|
302
|
+
)}
|
|
303
|
+
</HoverIcon>
|
|
304
|
+
)}
|
|
302
305
|
<span style={{
|
|
303
306
|
fontSize: 13,
|
|
304
307
|
fontWeight: 400,
|
|
@@ -308,8 +311,8 @@ const UpdatedInteractionSignals = ({
|
|
|
308
311
|
“{ev.text}”
|
|
309
312
|
</span>
|
|
310
313
|
</div>
|
|
311
|
-
)
|
|
312
|
-
)
|
|
314
|
+
);
|
|
315
|
+
})}
|
|
313
316
|
</div>
|
|
314
317
|
))}
|
|
315
318
|
</div>
|