You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add native HTML5 <audio controls> to each row in the Person panel Calls tab so managers can listen to a Zadarma recording without leaving the contact page — analogous to embedded audio in Notion or in Twenty's own rich-text / Notes blocks.
Click a row → the row expands and reveals a compact player plus an explicit [Open record →] button for navigation to the canonical /object/callLog/:id detail page.
Why
Every modern CRM lets you preview the call inline. Today the user has to click the row, wait for the detail page to load, and only then see the recording link — three extra interactions for a 10-second listen.
Transcript is great for skimming but tone / pace / interruption patterns only come through audio. Sales managers explicitly asked for "play in panel".
callLog.recording (LINKS, primaryLinkUrl) is already populated by the enrichment job on the current feat/sync-recording-transcript-enrichment branch — the data is there, only the UI is missing.
Add expandedCallId: string | null state to renderCalls.
Replace row onClick={navigate(...)} with onClick={() => toggleExpand(c.id)}.
When expandedCallId === c.id, render an inline panel containing:
<audio controls preload="none" src={c.recording?.primaryLinkUrl}> if URL present.
An [Open record →] button preserving the previous navigation behaviour.
For recording == null: an inline "No recording" tag, no expand affordance.
On <audio>error event → swap player for a [Refresh recording] button that POSTs the new logic-function below; on success swap src; on second failure render fallback text "Recording no longer available — transcript on detail page".
Backend (new logic-function):
src/logic-functions/refresh-zadarma-recording.logic-function.ts — POST /s/zadarma/refresh-recording, isAuthRequired: true, body { callLogId }.
Reuse src/modules/zadarma/utils/fetch-call-recording.ts (new on the current branch) to resolve a fresh signed URL via Zadarma /v1/pbx/record/request.
Persist updated recording.primaryLinkUrl via updateOneCallLog so the next render is fresh.
Return { url } (or { error: 'NOT_AVAILABLE' } when Zadarma 404 → recording purged by retention).
Manual smoke: probe one production recording URL with curl -I to confirm Accept-Ranges: bytes (seek-bar functional). If absent — ship anyway, document limitation in README.md § Calls panel.
Out of scope
Transcript inline preview in the expand panel — transcript is fully visible on the detail page and rendering it twice creates UI churn.
Bulk re-fetch cron for stale URLs — analysis showed background refresh wastes Zadarma quota on calls nobody plays. Reactive lazy refresh on user click is sufficient because transcript is the durable artefact; audio is a "nice to have replay". Revisit only if ops feedback shows pattern of stale-on-first-click.
Custom waveform / scrubber UI — native browser controls are good enough and zero-maintenance.
Risks / open questions
CORS / Range — native <audio> cross-origin playback works without CORS headers, but the seek bar requires Accept-Ranges: bytes from Zadarma's CDN. If Zadarma omits it, playback is linear-only. Probe before merge; document if degraded.
Retention — Zadarma free plan stores ~200 MB shared (FIFO purge as new recordings come in); paid plans store more for longer. The lazy refresh handles "URL expired" cleanly but cannot resurrect a purged recording. Final fallback text covers that case.
Click semantics change — current row click navigates; this PR makes click toggle expand. Mitigation: explicit [Open record →] button preserves the navigation path. Heads-up worth a line in CHANGELOG.
Effort
Frontend: ~150 LOC (state + render + handlers)
Backend: ~50 LOC (logic-function + persist call) + 1 unit test
Roughly half a day end-to-end including manual probe.
Concept
Add native HTML5
<audio controls>to each row in the Person panel Calls tab so managers can listen to a Zadarma recording without leaving the contact page — analogous to embedded audio in Notion or in Twenty's own rich-text / Notes blocks.Click a row → the row expands and reveals a compact player plus an explicit
[Open record →]button for navigation to the canonical/object/callLog/:iddetail page.Why
<audio>infrontComponent(PR Allow video, audio and iFrame in front components twentyhq/twenty#18200, shipped in v2.2.0). No SDK upgrade needed; currentengines.twenty >=2.2.0floor covers it.callLog.recording(LINKS,primaryLinkUrl) is already populated by the enrichment job on the currentfeat/sync-recording-transcript-enrichmentbranch — the data is there, only the UI is missing.Scope
UI (
src/front-components/zadarma-person-panel.front-component.tsx):expandedCallId: string | nullstate torenderCalls.onClick={navigate(...)}withonClick={() => toggleExpand(c.id)}.expandedCallId === c.id, render an inline panel containing:<audio controls preload="none" src={c.recording?.primaryLinkUrl}>if URL present.[Open record →]button preserving the previous navigation behaviour.recording == null: an inline "No recording" tag, no expand affordance.<audio>errorevent → swap player for a[Refresh recording]button that POSTs the new logic-function below; on success swapsrc; on second failure render fallback text "Recording no longer available — transcript on detail page".Backend (new logic-function):
src/logic-functions/refresh-zadarma-recording.logic-function.ts—POST /s/zadarma/refresh-recording,isAuthRequired: true, body{ callLogId }.src/modules/zadarma/utils/fetch-call-recording.ts(new on the current branch) to resolve a fresh signed URL via Zadarma/v1/pbx/record/request.recording.primaryLinkUrlviaupdateOneCallLogso the next render is fresh.{ url }(or{ error: 'NOT_AVAILABLE' }when Zadarma 404 → recording purged by retention).Tests:
refresh-zadarma-recording.test.ts— mocksfetchCallRecording, asserts persistence call shape and JSON response.curl -Ito confirmAccept-Ranges: bytes(seek-bar functional). If absent — ship anyway, document limitation inREADME.md§ Calls panel.Out of scope
Risks / open questions
<audio>cross-origin playback works without CORS headers, but the seek bar requiresAccept-Ranges: bytesfrom Zadarma's CDN. If Zadarma omits it, playback is linear-only. Probe before merge; document if degraded.[Open record →]button preserves the navigation path. Heads-up worth a line in CHANGELOG.Effort