From e0d51159f6123d4ed2a23243e3927a8026ae0ede Mon Sep 17 00:00:00 2001 From: Yashas Date: Wed, 9 Sep 2026 16:19:26 +0530 Subject: [PATCH] console: play the call recording alongside the transcript MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The voice agent uploads a dual-channel WAV of each call to blob storage; until now the console showed only the transcript's conclusions, not the audio. Add a player wherever the call is read: - THE CALL panel on the lead gets an inline "Call recording" player. - The transcript dialog gets the player above the turns, so a reviewer can listen while reading — the words and the audio are the same evidence. Both read row.call_recording_url (the platform's own field for this, surfaced on the lead by backend 107) and use preload="none", so a multi-MB WAV is only fetched when someone actually presses play. The player simply does not render when a lead has no recording. Co-Authored-By: Claude Opus 4.8 --- src/components/CallTranscript.jsx | 12 +++++++++++- src/components/Conversation.css | 10 ++++++++++ src/screens/Lead.jsx | 13 ++++++++++++- src/screens/screens.css | 18 ++++++++++++++++++ 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/components/CallTranscript.jsx b/src/components/CallTranscript.jsx index 14634fe..832ac3a 100644 --- a/src/components/CallTranscript.jsx +++ b/src/components/CallTranscript.jsx @@ -42,7 +42,7 @@ function turnsFrom(text) { return out.filter((t) => t.text) } -export default function CallTranscript({ text, name, onClose }) { +export default function CallTranscript({ text, audio, name, onClose }) { const ref = useRef(null) const restore = useRef(null) const turns = turnsFrom(text) @@ -82,6 +82,16 @@ export default function CallTranscript({ text, name, onClose }) { + {audio ? ( +
+ {/* Listen along with the transcript. preload=none so opening the + dialog does not pull a multi-MB WAV until the user hits play. */} + +
+ ) : null} +
{turns.length ? turns.map((t, i) => (
diff --git a/src/components/Conversation.css b/src/components/Conversation.css index d77f78c..37ac8de 100644 --- a/src/components/Conversation.css +++ b/src/components/Conversation.css @@ -76,3 +76,13 @@ background: var(--zk-tint); border-radius: var(--r-md); } + +/* The call recording, played from inside the transcript dialog. */ +.conv__audio { + padding: 12px 20px 4px; + border-bottom: 1px solid var(--zk-line-soft); +} +.conv__audio audio { + width: 100%; + height: 38px; +} diff --git a/src/screens/Lead.jsx b/src/screens/Lead.jsx index e6119a7..4fa6e3e 100644 --- a/src/screens/Lead.jsx +++ b/src/screens/Lead.jsx @@ -838,6 +838,17 @@ blocked ? ( Read what was actually said ) : null} + {title === 'The call' && row.call_recording_url ? ( + /* The recording itself, to play alongside the transcript. + preload=none keeps the multi-MB WAV off the wire until + a person actually presses play. */ +
+ Call recording + +
+ ) : null} ))}
@@ -864,7 +875,7 @@ blocked ? ( setOpenAgent(null)} /> ) : null} {showCall ? ( - setShowCall(false)} /> + setShowCall(false)} /> ) : null} {showFile ? ( diff --git a/src/screens/screens.css b/src/screens/screens.css index 3523b94..09f5885 100644 --- a/src/screens/screens.css +++ b/src/screens/screens.css @@ -1727,6 +1727,24 @@ /* An action that belongs to one card, not to the panel. Quiet until hovered: the notes above it are the answer most of the time, and the transcript is the follow-up question. */ +.glance__rec { + margin-top: 12px; + display: flex; + flex-direction: column; + gap: 6px; +} +.glance__rec-l { + font-size: var(--fs-3xs); + font-weight: 500; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--zk-grey); +} +.glance__rec audio { + width: 100%; + height: 36px; +} + .glance__act { display: inline-flex; align-items: center;