@edvak99/voice-receptionist-widget 1.1.20 → 1.1.21
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/fesm2022/{edvak99-voice-receptionist-widget-activities-page.component-CLpNIrcb.mjs → edvak99-voice-receptionist-widget-activities-page.component-CpXa1J9-.mjs} +3 -3
- package/fesm2022/{edvak99-voice-receptionist-widget-activities-page.component-CLpNIrcb.mjs.map → edvak99-voice-receptionist-widget-activities-page.component-CpXa1J9-.mjs.map} +1 -1
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-CmImX5FN.mjs +435 -0
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-CmImX5FN.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-audit-page.component-DwHRYqNe.mjs → edvak99-voice-receptionist-widget-audit-page.component-DTAB2WTX.mjs} +2 -2
- package/fesm2022/{edvak99-voice-receptionist-widget-audit-page.component-DwHRYqNe.mjs.map → edvak99-voice-receptionist-widget-audit-page.component-DTAB2WTX.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-call-history-detail-page.component-gdOpQsoM.mjs → edvak99-voice-receptionist-widget-call-history-detail-page.component-BT4clUO-.mjs} +4 -4
- package/fesm2022/{edvak99-voice-receptionist-widget-call-history-detail-page.component-gdOpQsoM.mjs.map → edvak99-voice-receptionist-widget-call-history-detail-page.component-BT4clUO-.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-call-history-page.component-CDOyFwGI.mjs → edvak99-voice-receptionist-widget-call-history-page.component-BACON4uq.mjs} +3 -3
- package/fesm2022/{edvak99-voice-receptionist-widget-call-history-page.component-CDOyFwGI.mjs.map → edvak99-voice-receptionist-widget-call-history-page.component-BACON4uq.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-call-recording-player.component-o0bttL-m.mjs → edvak99-voice-receptionist-widget-call-recording-player.component-4fL-zUWy.mjs} +2 -2
- package/fesm2022/{edvak99-voice-receptionist-widget-call-recording-player.component-o0bttL-m.mjs.map → edvak99-voice-receptionist-widget-call-recording-player.component-4fL-zUWy.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-B3y421d_.mjs → edvak99-voice-receptionist-widget-call-review.service-BhbCmB1S.mjs} +2 -2
- package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-B3y421d_.mjs.map → edvak99-voice-receptionist-widget-call-review.service-BhbCmB1S.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-dashboard-page.component-CWAsroOR.mjs → edvak99-voice-receptionist-widget-dashboard-page.component-CxNYLBbg.mjs} +2 -2
- package/fesm2022/{edvak99-voice-receptionist-widget-dashboard-page.component-CWAsroOR.mjs.map → edvak99-voice-receptionist-widget-dashboard-page.component-CxNYLBbg.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-BEyM4U02.mjs → edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-DDS7VdnD.mjs} +26 -19
- package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-DDS7VdnD.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-live-calls-page.component-BEhbKM1c.mjs → edvak99-voice-receptionist-widget-live-calls-page.component-4LtgT4lS.mjs} +2 -2
- package/fesm2022/{edvak99-voice-receptionist-widget-live-calls-page.component-BEhbKM1c.mjs.map → edvak99-voice-receptionist-widget-live-calls-page.component-4LtgT4lS.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-live-monitor-page.component-DrqhaEEw.mjs → edvak99-voice-receptionist-widget-live-monitor-page.component-B1lKfw-j.mjs} +8 -29
- package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-B1lKfw-j.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-outbound-purposes-page.component-DTqLwtvb.mjs → edvak99-voice-receptionist-widget-outbound-purposes-page.component-BkIHblSu.mjs} +2 -2
- package/fesm2022/{edvak99-voice-receptionist-widget-outbound-purposes-page.component-DTqLwtvb.mjs.map → edvak99-voice-receptionist-widget-outbound-purposes-page.component-BkIHblSu.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-task-detail-drawer.component-lW-CQ71m.mjs → edvak99-voice-receptionist-widget-task-detail-drawer.component-aDTKTV79.mjs} +2 -2
- package/fesm2022/{edvak99-voice-receptionist-widget-task-detail-drawer.component-lW-CQ71m.mjs.map → edvak99-voice-receptionist-widget-task-detail-drawer.component-aDTKTV79.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-tasks-page.component-MBm-mLFO.mjs → edvak99-voice-receptionist-widget-tasks-page.component-Dj1nWx6V.mjs} +3 -3
- package/fesm2022/{edvak99-voice-receptionist-widget-tasks-page.component-MBm-mLFO.mjs.map → edvak99-voice-receptionist-widget-tasks-page.component-Dj1nWx6V.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-wizard-page.component-CgK05X9O.mjs → edvak99-voice-receptionist-widget-wizard-page.component-CQttbEzW.mjs} +2 -2
- package/fesm2022/{edvak99-voice-receptionist-widget-wizard-page.component-CgK05X9O.mjs.map → edvak99-voice-receptionist-widget-wizard-page.component-CQttbEzW.mjs.map} +1 -1
- package/fesm2022/edvak99-voice-receptionist-widget.mjs +1 -1
- package/lib/components/live-transcript-panel/live-transcript-panel.component.d.ts +1 -5
- package/lib/models/ai-receptionist.types.d.ts +2 -0
- package/lib/pages/analytics-page/analytics-page.component.d.ts +17 -8
- package/lib/pages/live-monitor-page/live-monitor-page.component.d.ts +0 -2
- package/lib/services/ai-receptionist-api.service.d.ts +1 -0
- package/package.json +1 -1
- package/styles/embed.scss +2 -1
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-D5ze9OdJ.mjs +0 -416
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-D5ze9OdJ.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-BEyM4U02.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-DrqhaEEw.mjs.map +0 -1
|
@@ -5,9 +5,9 @@ import { ActivatedRoute, RouterLink } from '@angular/router';
|
|
|
5
5
|
import * as i1 from '@angular/forms';
|
|
6
6
|
import { FormsModule } from '@angular/forms';
|
|
7
7
|
import { firstValueFrom } from 'rxjs';
|
|
8
|
-
import { j as resolveToolKey, k as resolveToolDisplayName, D as DashboardSocketService, V as VOICE_RECEPTIONIST_CONFIG, i as isUnknownCaller, I as IconComponent, A as AiReceptionistApiService, T as ToastService, l as toolEventTrackKey, c as callerMetaLine, b as ageFromDob, E as EdAvatarComponent, f as EdButtonComponent, g as EdSkeletonComponent } from './edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-
|
|
8
|
+
import { j as resolveToolKey, k as resolveToolDisplayName, D as DashboardSocketService, V as VOICE_RECEPTIONIST_CONFIG, i as isUnknownCaller, I as IconComponent, A as AiReceptionistApiService, T as ToastService, l as toolEventTrackKey, c as callerMetaLine, b as ageFromDob, E as EdAvatarComponent, f as EdButtonComponent, g as EdSkeletonComponent } from './edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-DDS7VdnD.mjs';
|
|
9
9
|
import { E as EdsElapsedPipe, a as sentimentLabel, s as sentimentIconSrc } from './edvak99-voice-receptionist-widget-eds-elapsed.pipe-Dra7e7xN.mjs';
|
|
10
|
-
import { C as CallRecordingPlayerComponent } from './edvak99-voice-receptionist-widget-call-recording-player.component-
|
|
10
|
+
import { C as CallRecordingPlayerComponent } from './edvak99-voice-receptionist-widget-call-recording-player.component-4fL-zUWy.mjs';
|
|
11
11
|
import { Track, Room, RoomEvent, ConnectionState } from 'livekit-client';
|
|
12
12
|
|
|
13
13
|
function mapChunkToLine(chunk) {
|
|
@@ -291,8 +291,6 @@ class LiveTranscriptPanelComponent {
|
|
|
291
291
|
this.paused = input(false);
|
|
292
292
|
this.humanTakeover = input(false);
|
|
293
293
|
this.showStaffInput = input(false);
|
|
294
|
-
/** Co-Pilot chips + purple send control (Figma live monitor). */
|
|
295
|
-
this.showCopilot = input(false);
|
|
296
294
|
/** Call start ms for the Recording timer pill. */
|
|
297
295
|
this.callStarted = input(null);
|
|
298
296
|
this.staffMessage = input('');
|
|
@@ -315,12 +313,6 @@ class LiveTranscriptPanelComponent {
|
|
|
315
313
|
return 'Unknown Caller';
|
|
316
314
|
return name || 'Unknown Caller';
|
|
317
315
|
});
|
|
318
|
-
this.copilotChips = [
|
|
319
|
-
'Ask for MRN',
|
|
320
|
-
'Create Task: Follow Up',
|
|
321
|
-
'Ask for full Name & DOB',
|
|
322
|
-
'Verify Phone Number',
|
|
323
|
-
];
|
|
324
316
|
this.shouldScroll = false;
|
|
325
317
|
effect(() => {
|
|
326
318
|
this.lines();
|
|
@@ -371,9 +363,6 @@ class LiveTranscriptPanelComponent {
|
|
|
371
363
|
onStaffInput(value) {
|
|
372
364
|
this.staffMessageChange.emit(value);
|
|
373
365
|
}
|
|
374
|
-
onChipClick(chip) {
|
|
375
|
-
this.staffMessageChange.emit(chip);
|
|
376
|
-
}
|
|
377
366
|
onSend() {
|
|
378
367
|
this.sendMessage.emit();
|
|
379
368
|
}
|
|
@@ -381,13 +370,13 @@ class LiveTranscriptPanelComponent {
|
|
|
381
370
|
this.shouldScroll = true;
|
|
382
371
|
}
|
|
383
372
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LiveTranscriptPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
384
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: LiveTranscriptPanelComponent, isStandalone: true, selector: "eds-live-transcript-panel", inputs: { live: { classPropertyName: "live", publicName: "live", isSignal: true, isRequired: false, transformFunction: null }, paused: { classPropertyName: "paused", publicName: "paused", isSignal: true, isRequired: false, transformFunction: null }, humanTakeover: { classPropertyName: "humanTakeover", publicName: "humanTakeover", isSignal: true, isRequired: false, transformFunction: null }, showStaffInput: { classPropertyName: "showStaffInput", publicName: "showStaffInput", isSignal: true, isRequired: false, transformFunction: null }, showCopilot: { classPropertyName: "showCopilot", publicName: "showCopilot", isSignal: true, isRequired: false, transformFunction: null }, callStarted: { classPropertyName: "callStarted", publicName: "callStarted", isSignal: true, isRequired: false, transformFunction: null }, staffMessage: { classPropertyName: "staffMessage", publicName: "staffMessage", isSignal: true, isRequired: false, transformFunction: null }, callerName: { classPropertyName: "callerName", publicName: "callerName", isSignal: true, isRequired: false, transformFunction: null }, callerMatched: { classPropertyName: "callerMatched", publicName: "callerMatched", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { staffMessageChange: "staffMessageChange", sendMessage: "sendMessage" }, host: { classAttribute: "flex h-full min-h-0 flex-1 flex-col max-[900px]:h-auto max-[900px]:max-h-none max-[900px]:min-h-[360px]" }, viewQueries: [{ propertyName: "scrollHost", first: true, predicate: ["scrollHost"], descendants: true }], ngImport: i0, template: "<section\n class=\"flex h-full min-h-0 flex-1 flex-col overflow-hidden rounded-xl bg-white p-4 max-[900px]:h-auto max-[900px]:max-h-none max-[900px]:min-h-[360px]\"\n aria-labelledby=\"live-transcript-title\"\n>\n <header class=\"mb-4 flex shrink-0 items-center justify-between gap-3\">\n <div class=\"flex min-w-0 items-center gap-2\">\n <h4 id=\"live-transcript-title\" class=\"m-0\">Live Transcript</h4>\n @if (live() && !paused()) {\n <span class=\"live-transcript-panel__recording inline-flex h-5 items-center gap-1 rounded px-1 text-red-600\">\n <span class=\"h-1.5 w-1.5 rounded-full bg-red-600\" aria-hidden=\"true\"></span>\n Recording\n </span>\n }\n </div>\n @if (callStarted()) {\n <span class=\"live-transcript-panel__timer inline-flex items-center gap-0.5 rounded-full bg-green-500/10 px-1 py-0.5 text-green-700\">\n <span class=\"h-1.5 w-1.5 rounded-full bg-green-600\" aria-hidden=\"true\"></span>\n <span class=\"font-mono uppercase tracking-wide\">{{ callStarted() | edsElapsed }}</span>\n </span>\n }\n </header>\n\n <div #scrollHost class=\"chat-thread live-transcript-panel__thread\" role=\"log\" aria-live=\"polite\">\n @for (line of lines(); track $index) {\n @if (line.who === 'system') {\n <div class=\"chat-row chat-row--system\">\n <div\n class=\"chat-bubble chat-bubble--system inline-flex items-center gap-2\"\n [class.border-emerald-200]=\"line.flag === 'verify'\"\n [class.bg-emerald-50]=\"line.flag === 'verify'\"\n [class.text-emerald-700]=\"line.flag === 'verify'\"\n [class.border-amber-200]=\"line.flag === 'fail' || line.flag === 'escalate'\"\n [class.bg-amber-50]=\"line.flag === 'fail' || line.flag === 'escalate'\"\n [class.text-amber-700]=\"line.flag === 'fail' || line.flag === 'escalate'\"\n >\n <ed-icon [name]=\"systemIcon(line.flag)\" [size]=\"14\" />\n <span>{{ line.text }}</span>\n </div>\n </div>\n } @else {\n <article\n class=\"chat-row\"\n [class.chat-row--caller]=\"line.who === 'patient'\"\n [class.chat-row--ai]=\"line.who === 'ai'\"\n [class.chat-row--staff]=\"line.who === 'staff'\"\n >\n <div class=\"chat-meta\">\n <span [class.live-transcript-panel__meta--ai]=\"line.who === 'ai'\">{{ roleLabel(line.who) }}</span>\n </div>\n <div class=\"chat-bubble-row\">\n <div\n class=\"chat-avatar\"\n [class.chat-avatar--ai]=\"line.who === 'ai'\"\n [class.live-transcript-panel__avatar--ai]=\"line.who === 'ai'\"\n [class.chat-avatar--staff]=\"line.who === 'staff'\"\n [class.chat-avatar--caller]=\"line.who === 'patient'\"\n >\n <ed-icon [name]=\"speakerIcon(line.who)\" [size]=\"16\" />\n </div>\n <div\n class=\"chat-bubble\"\n [class.chat-bubble--caller]=\"line.who === 'patient'\"\n [class.chat-bubble--ai]=\"line.who === 'ai'\"\n [class.chat-bubble--staff]=\"line.who === 'staff'\"\n >\n <p class=\"chat-content\">{{ line.text }}</p>\n </div>\n </div>\n </article>\n }\n }\n\n @if (patientInterim(); as interim) {\n <article class=\"chat-row chat-row--caller\">\n <div class=\"chat-meta\">\n <span>{{ callerLabel() }}</span>\n </div>\n <div class=\"chat-bubble-row\">\n <div class=\"chat-avatar chat-avatar--caller\">\n <ed-icon name=\"person\" [size]=\"16\" />\n </div>\n <div class=\"chat-bubble chat-bubble--caller chat-bubble--interim live-transcript-panel__typing\">\n <span class=\"live-transcript-panel__dot\" aria-hidden=\"true\"></span>\n <span class=\"live-transcript-panel__dot\" aria-hidden=\"true\"></span>\n <span class=\"live-transcript-panel__dot\" aria-hidden=\"true\"></span>\n <span class=\"sr-only\">{{ interim }}</span>\n </div>\n </div>\n </article>\n }\n\n @if (staffInterim(); as interim) {\n <article class=\"chat-row chat-row--staff\">\n <div class=\"chat-meta\"><span>Staff</span></div>\n <div class=\"chat-bubble-row\">\n <div class=\"chat-avatar chat-avatar--staff\">\n <ed-icon name=\"Robot\" [size]=\"16\" />\n </div>\n <div class=\"chat-bubble chat-bubble--staff chat-bubble--interim\">\n <p class=\"chat-content\">{{ interim }}</p>\n </div>\n </div>\n </article>\n }\n\n @if (aiInterim(); as interim) {\n <article class=\"chat-row chat-row--ai\">\n <div class=\"chat-meta\">\n <span class=\"live-transcript-panel__meta--ai\">AI Receptionist</span>\n </div>\n <div class=\"chat-bubble-row\">\n <div class=\"chat-avatar chat-avatar--ai live-transcript-panel__avatar--ai\">\n <ed-icon name=\"graph_new\" [size]=\"16\" />\n </div>\n <div class=\"chat-bubble chat-bubble--ai chat-bubble--interim\">\n <p class=\"chat-content\">\n {{ interim }}<span class=\"live-transcript-panel__caret\" aria-hidden=\"true\"></span>\n </p>\n </div>\n </div>\n </article>\n }\n\n @if (lines().length === 0 && !patientInterim() && !aiInterim() && !staffInterim()) {\n <p class=\"m-0 px-3 py-8 text-center text-zinc-500\">Waiting for conversation\u2026</p>\n }\n </div>\n\n @if (showCopilot() || showStaffInput()) {\n <footer class=\"mt-4 flex shrink-0 flex-col gap-4\">\n @if (showCopilot()) {\n <div class=\"flex flex-wrap items-center gap-2\">\n <span class=\"inline-flex items-center justify-center rounded-md bg-purple-500/10 p-1 text-purple-700\">\n <ed-icon name=\"sparkle\" [size]=\"12\" />\n </span>\n @for (chip of copilotChips; track chip) {\n <button\n type=\"button\"\n class=\"live-transcript-panel__chip inline-flex h-5 items-center rounded border border-zinc-200 bg-white px-1 text-zinc-700\"\n (click)=\"onChipClick(chip)\"\n >\n {{ chip }}\n </button>\n }\n </div>\n }\n\n <div class=\"flex items-center gap-2 rounded-lg border border-zinc-200 p-2\">\n <input\n type=\"text\"\n class=\"h-8 min-w-0 flex-1 border-0 bg-transparent px-1 text-zinc-900 outline-none placeholder:text-zinc-400\"\n [placeholder]=\"showCopilot() ? 'Tell the AI what it should know or do next\u2026' : 'Type to speak as staff\u2026'\"\n [ngModel]=\"staffMessage()\"\n (ngModelChange)=\"onStaffInput($event)\"\n (keyup.enter)=\"onSend()\"\n autocomplete=\"off\"\n />\n @if (showCopilot()) {\n <button\n type=\"button\"\n class=\"live-transcript-panel__copilot-btn inline-flex h-8 shrink-0 items-center gap-0.5 rounded-md bg-purple-600 px-2 text-white\"\n (click)=\"onSend()\"\n >\n Co-Pilot\n <ed-icon name=\"sparkle\" [size]=\"14\" />\n </button>\n }\n </div>\n </footer>\n }\n</section>\n", styles: ["@charset \"UTF-8\";.chat-thread{display:flex;flex-direction:column;gap:0;flex:1 1 auto;min-height:0;overflow:auto;padding:1rem 1.5rem;background:#fff;scroll-behavior:smooth}.chat-row{display:flex;flex-direction:column;width:100%;margin-bottom:1rem}.chat-row--caller,.chat-row--user,.chat-row--patient{align-items:flex-start}.chat-row--agent,.chat-row--ai,.chat-row--staff{align-items:flex-end}.chat-row--tool,.chat-row--system{align-items:center}.chat-meta{display:flex;align-items:center;gap:.5rem;margin-bottom:.25rem;font-size:.875rem;color:var(--color-text-light, #6b7280)}.chat-row--agent .chat-meta,.chat-row--ai .chat-meta,.chat-row--staff .chat-meta{justify-content:flex-end;padding-right:2.75rem}.chat-bubble-row{display:flex;align-items:flex-start;gap:.5rem;max-width:min(72%,40rem)}.chat-row--agent .chat-bubble-row,.chat-row--ai .chat-bubble-row,.chat-row--staff .chat-bubble-row{flex-direction:row-reverse}.chat-avatar{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border-radius:9999px}.chat-avatar--ai{background:#ad46ff1f;color:#8200db}.chat-avatar--staff{border:1px solid #ddd6fe;background:#f5f3ff;color:#6d28d9}.chat-avatar--caller{background:#e5e7eb;color:#4b5563}.chat-bubble{max-width:100%;padding:.75rem 1rem;font-size:.875rem;line-height:1.5;word-break:break-word;white-space:pre-wrap}.chat-bubble--caller,.chat-bubble--user,.chat-bubble--patient{border:1px solid var(--color-border-light, #e5e7eb);border-radius:0 .5rem .5rem;background:#fff;color:var(--color-text-dark, #111827)}.chat-bubble--agent,.chat-bubble--ai{border:none;border-radius:.5rem;background:#ad46ff1a;color:var(--color-text-dark, #53575c)}.chat-bubble--staff{border:1px solid #ddd6fe;border-radius:.5rem 0 .5rem .5rem;background:#f5f3ff;color:#4c1d95}.chat-bubble--tool,.chat-bubble--system{max-width:min(90%,40rem);padding:.5rem .75rem;border:1px solid var(--color-border-light, #e5e7eb);border-radius:9999px;background:#f9fafb;color:#6b7280;font-size:.75rem;text-align:center}.chat-bubble--interim{opacity:.9;font-style:italic}.chat-content{margin:0}.chat-time{font-size:.75rem;color:#9ca3af}.live-transcript-panel__thread.chat-thread{background:#f7f9fa;border-radius:.75rem;padding:1rem;min-height:0}@media(max-width:900px){.live-transcript-panel__thread.chat-thread{max-height:min(60vh,36rem)}}.live-transcript-panel__avatar--ai{background:#ad46ff1f;color:#8200db}.live-transcript-panel__meta--ai{color:#8200db}.live-transcript-panel__caret{display:inline-block;width:7px;height:14px;margin-left:2px;vertical-align:text-bottom;background:#8200db;animation:eds-caret 1s step-end infinite}.live-transcript-panel__typing{display:inline-flex;align-items:center;gap:4px;min-width:2.5rem;min-height:1.25rem}.live-transcript-panel__chip{cursor:pointer}.live-transcript-panel__chip:hover{background:#f7f9fa}.live-transcript-panel__copilot-btn{cursor:pointer;border:0}.live-transcript-panel__dot{width:6px;height:6px;border-radius:9999px;background:#a2a4a6;animation:eds-typing 1.2s ease-in-out infinite}.live-transcript-panel__dot:nth-child(2){animation-delay:.15s}.live-transcript-panel__dot:nth-child(3){animation-delay:.3s}@keyframes eds-caret{50%{opacity:0}}@keyframes eds-typing{0%,80%,to{opacity:.35;transform:translateY(0)}40%{opacity:1;transform:translateY(-2px)}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IconComponent, selector: "ed-icon", inputs: ["name", "size", "color", "cssClass", "decorative", "ariaHidden", "ariaLabel", "title", "desc", "focusable", "tabIndex"] }, { kind: "pipe", type: EdsElapsedPipe, name: "edsElapsed" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
373
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: LiveTranscriptPanelComponent, isStandalone: true, selector: "eds-live-transcript-panel", inputs: { live: { classPropertyName: "live", publicName: "live", isSignal: true, isRequired: false, transformFunction: null }, paused: { classPropertyName: "paused", publicName: "paused", isSignal: true, isRequired: false, transformFunction: null }, humanTakeover: { classPropertyName: "humanTakeover", publicName: "humanTakeover", isSignal: true, isRequired: false, transformFunction: null }, showStaffInput: { classPropertyName: "showStaffInput", publicName: "showStaffInput", isSignal: true, isRequired: false, transformFunction: null }, callStarted: { classPropertyName: "callStarted", publicName: "callStarted", isSignal: true, isRequired: false, transformFunction: null }, staffMessage: { classPropertyName: "staffMessage", publicName: "staffMessage", isSignal: true, isRequired: false, transformFunction: null }, callerName: { classPropertyName: "callerName", publicName: "callerName", isSignal: true, isRequired: false, transformFunction: null }, callerMatched: { classPropertyName: "callerMatched", publicName: "callerMatched", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { staffMessageChange: "staffMessageChange", sendMessage: "sendMessage" }, host: { classAttribute: "flex h-full min-h-0 flex-1 flex-col overflow-hidden" }, viewQueries: [{ propertyName: "scrollHost", first: true, predicate: ["scrollHost"], descendants: true }], ngImport: i0, template: "<section\n class=\"flex h-full min-h-0 flex-1 flex-col overflow-hidden rounded-xl bg-white p-4\"\n aria-labelledby=\"live-transcript-title\"\n>\n <header class=\"mb-4 flex shrink-0 items-center justify-between gap-3\">\n <div class=\"flex min-w-0 items-center gap-2\">\n <h4 id=\"live-transcript-title\" class=\"m-0\">Live Transcript</h4>\n @if (live() && !paused()) {\n <span class=\"live-transcript-panel__recording inline-flex h-5 items-center gap-1 rounded px-1 text-red-600\">\n <span class=\"h-1.5 w-1.5 rounded-full bg-red-600\" aria-hidden=\"true\"></span>\n Recording\n </span>\n }\n </div>\n @if (callStarted()) {\n <span class=\"live-transcript-panel__timer inline-flex items-center gap-0.5 rounded-full bg-green-500/10 px-1 py-0.5 text-green-700\">\n <span class=\"h-1.5 w-1.5 rounded-full bg-green-600\" aria-hidden=\"true\"></span>\n <span class=\"font-mono uppercase tracking-wide\">{{ callStarted() | edsElapsed }}</span>\n </span>\n }\n </header>\n\n <div #scrollHost class=\"chat-thread live-transcript-panel__thread\" role=\"log\" aria-live=\"polite\">\n @for (line of lines(); track $index) {\n @if (line.who === 'system') {\n <div class=\"chat-row chat-row--system\">\n <div\n class=\"chat-bubble chat-bubble--system inline-flex items-center gap-2\"\n [class.border-emerald-200]=\"line.flag === 'verify'\"\n [class.bg-emerald-50]=\"line.flag === 'verify'\"\n [class.text-emerald-700]=\"line.flag === 'verify'\"\n [class.border-amber-200]=\"line.flag === 'fail' || line.flag === 'escalate'\"\n [class.bg-amber-50]=\"line.flag === 'fail' || line.flag === 'escalate'\"\n [class.text-amber-700]=\"line.flag === 'fail' || line.flag === 'escalate'\"\n >\n <ed-icon [name]=\"systemIcon(line.flag)\" [size]=\"14\" />\n <span>{{ line.text }}</span>\n </div>\n </div>\n } @else {\n <article\n class=\"chat-row\"\n [class.chat-row--caller]=\"line.who === 'patient'\"\n [class.chat-row--ai]=\"line.who === 'ai'\"\n [class.chat-row--staff]=\"line.who === 'staff'\"\n >\n <div class=\"chat-meta\">\n <span [class.live-transcript-panel__meta--ai]=\"line.who === 'ai'\">{{ roleLabel(line.who) }}</span>\n </div>\n <div class=\"chat-bubble-row\">\n <div\n class=\"chat-avatar\"\n [class.chat-avatar--ai]=\"line.who === 'ai'\"\n [class.live-transcript-panel__avatar--ai]=\"line.who === 'ai'\"\n [class.chat-avatar--staff]=\"line.who === 'staff'\"\n [class.chat-avatar--caller]=\"line.who === 'patient'\"\n >\n <ed-icon [name]=\"speakerIcon(line.who)\" [size]=\"16\" />\n </div>\n <div\n class=\"chat-bubble\"\n [class.chat-bubble--caller]=\"line.who === 'patient'\"\n [class.chat-bubble--ai]=\"line.who === 'ai'\"\n [class.chat-bubble--staff]=\"line.who === 'staff'\"\n >\n <p class=\"chat-content\">{{ line.text }}</p>\n </div>\n </div>\n </article>\n }\n }\n\n @if (patientInterim(); as interim) {\n <article class=\"chat-row chat-row--caller\">\n <div class=\"chat-meta\">\n <span>{{ callerLabel() }}</span>\n </div>\n <div class=\"chat-bubble-row\">\n <div class=\"chat-avatar chat-avatar--caller\">\n <ed-icon name=\"person\" [size]=\"16\" />\n </div>\n <div class=\"chat-bubble chat-bubble--caller chat-bubble--interim live-transcript-panel__typing\">\n <span class=\"live-transcript-panel__dot\" aria-hidden=\"true\"></span>\n <span class=\"live-transcript-panel__dot\" aria-hidden=\"true\"></span>\n <span class=\"live-transcript-panel__dot\" aria-hidden=\"true\"></span>\n <span class=\"sr-only\">{{ interim }}</span>\n </div>\n </div>\n </article>\n }\n\n @if (staffInterim(); as interim) {\n <article class=\"chat-row chat-row--staff\">\n <div class=\"chat-meta\"><span>Staff</span></div>\n <div class=\"chat-bubble-row\">\n <div class=\"chat-avatar chat-avatar--staff\">\n <ed-icon name=\"Robot\" [size]=\"16\" />\n </div>\n <div class=\"chat-bubble chat-bubble--staff chat-bubble--interim\">\n <p class=\"chat-content\">{{ interim }}</p>\n </div>\n </div>\n </article>\n }\n\n @if (aiInterim(); as interim) {\n <article class=\"chat-row chat-row--ai\">\n <div class=\"chat-meta\">\n <span class=\"live-transcript-panel__meta--ai\">AI Receptionist</span>\n </div>\n <div class=\"chat-bubble-row\">\n <div class=\"chat-avatar chat-avatar--ai live-transcript-panel__avatar--ai\">\n <ed-icon name=\"graph_new\" [size]=\"16\" />\n </div>\n <div class=\"chat-bubble chat-bubble--ai chat-bubble--interim\">\n <p class=\"chat-content\">\n {{ interim }}<span class=\"live-transcript-panel__caret\" aria-hidden=\"true\"></span>\n </p>\n </div>\n </div>\n </article>\n }\n\n @if (lines().length === 0 && !patientInterim() && !aiInterim() && !staffInterim()) {\n <p class=\"m-0 px-3 py-8 text-center text-zinc-500\">Waiting for conversation\u2026</p>\n }\n </div>\n\n @if (showStaffInput()) {\n <footer class=\"mt-4 flex shrink-0 flex-col gap-4\">\n <div class=\"flex items-center gap-2 rounded-lg border border-zinc-200 p-2\">\n <input\n type=\"text\"\n class=\"h-8 min-w-0 flex-1 border-0 bg-transparent px-1 text-zinc-900 outline-none placeholder:text-zinc-400\"\n placeholder=\"Type to speak as staff\u2026\"\n [ngModel]=\"staffMessage()\"\n (ngModelChange)=\"onStaffInput($event)\"\n (keyup.enter)=\"onSend()\"\n autocomplete=\"off\"\n />\n </div>\n </footer>\n }\n</section>\n", styles: ["@charset \"UTF-8\";.chat-thread{display:flex;flex-direction:column;gap:0;flex:1 1 auto;min-height:0;overflow:auto;padding:1rem 1.5rem;background:#fff;scroll-behavior:smooth}.chat-row{display:flex;flex-direction:column;width:100%;margin-bottom:1rem}.chat-row--caller,.chat-row--user,.chat-row--patient{align-items:flex-start}.chat-row--agent,.chat-row--ai,.chat-row--staff{align-items:flex-end}.chat-row--tool,.chat-row--system{align-items:center}.chat-meta{display:flex;align-items:center;gap:.5rem;margin-bottom:.25rem;font-size:.875rem;color:var(--color-text-light, #6b7280)}.chat-row--agent .chat-meta,.chat-row--ai .chat-meta,.chat-row--staff .chat-meta{justify-content:flex-end;padding-right:2.75rem}.chat-bubble-row{display:flex;align-items:flex-start;gap:.5rem;max-width:min(72%,40rem)}.chat-row--agent .chat-bubble-row,.chat-row--ai .chat-bubble-row,.chat-row--staff .chat-bubble-row{flex-direction:row-reverse}.chat-avatar{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border-radius:9999px}.chat-avatar--ai{background:#ad46ff1f;color:#8200db}.chat-avatar--staff{border:1px solid #ddd6fe;background:#f5f3ff;color:#6d28d9}.chat-avatar--caller{background:#e5e7eb;color:#4b5563}.chat-bubble{max-width:100%;padding:.75rem 1rem;font-size:.875rem;line-height:1.5;word-break:break-word;white-space:pre-wrap}.chat-bubble--caller,.chat-bubble--user,.chat-bubble--patient{border:1px solid var(--color-border-light, #e5e7eb);border-radius:0 .5rem .5rem;background:#fff;color:var(--color-text-dark, #111827)}.chat-bubble--agent,.chat-bubble--ai{border:none;border-radius:.5rem;background:#ad46ff1a;color:var(--color-text-dark, #53575c)}.chat-bubble--staff{border:1px solid #ddd6fe;border-radius:.5rem 0 .5rem .5rem;background:#f5f3ff;color:#4c1d95}.chat-bubble--tool,.chat-bubble--system{max-width:min(90%,40rem);padding:.5rem .75rem;border:1px solid var(--color-border-light, #e5e7eb);border-radius:9999px;background:#f9fafb;color:#6b7280;font-size:.75rem;text-align:center}.chat-bubble--interim{opacity:.9;font-style:italic}.chat-content{margin:0}.chat-time{font-size:.75rem;color:#9ca3af}.live-transcript-panel__thread.chat-thread{flex:1 1 auto;min-height:0;overflow:auto;background:#f7f9fa;border-radius:.75rem;padding:1rem}.live-transcript-panel__avatar--ai{background:#ad46ff1f;color:#8200db}.live-transcript-panel__meta--ai{color:#8200db}.live-transcript-panel__caret{display:inline-block;width:7px;height:14px;margin-left:2px;vertical-align:text-bottom;background:#8200db;animation:eds-caret 1s step-end infinite}.live-transcript-panel__typing{display:inline-flex;align-items:center;gap:4px;min-width:2.5rem;min-height:1.25rem}.live-transcript-panel__dot{width:6px;height:6px;border-radius:9999px;background:#a2a4a6;animation:eds-typing 1.2s ease-in-out infinite}.live-transcript-panel__dot:nth-child(2){animation-delay:.15s}.live-transcript-panel__dot:nth-child(3){animation-delay:.3s}@keyframes eds-caret{50%{opacity:0}}@keyframes eds-typing{0%,80%,to{opacity:.35;transform:translateY(0)}40%{opacity:1;transform:translateY(-2px)}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IconComponent, selector: "ed-icon", inputs: ["name", "size", "color", "cssClass", "decorative", "ariaHidden", "ariaLabel", "title", "desc", "focusable", "tabIndex"] }, { kind: "pipe", type: EdsElapsedPipe, name: "edsElapsed" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
385
374
|
}
|
|
386
375
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LiveTranscriptPanelComponent, decorators: [{
|
|
387
376
|
type: Component,
|
|
388
377
|
args: [{ selector: 'eds-live-transcript-panel', imports: [FormsModule, IconComponent, EdsElapsedPipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
389
|
-
class: 'flex h-full min-h-0 flex-1 flex-col
|
|
390
|
-
}, template: "<section\n class=\"flex h-full min-h-0 flex-1 flex-col overflow-hidden rounded-xl bg-white p-4 max-[900px]:h-auto max-[900px]:max-h-none max-[900px]:min-h-[360px]\"\n aria-labelledby=\"live-transcript-title\"\n>\n <header class=\"mb-4 flex shrink-0 items-center justify-between gap-3\">\n <div class=\"flex min-w-0 items-center gap-2\">\n <h4 id=\"live-transcript-title\" class=\"m-0\">Live Transcript</h4>\n @if (live() && !paused()) {\n <span class=\"live-transcript-panel__recording inline-flex h-5 items-center gap-1 rounded px-1 text-red-600\">\n <span class=\"h-1.5 w-1.5 rounded-full bg-red-600\" aria-hidden=\"true\"></span>\n Recording\n </span>\n }\n </div>\n @if (callStarted()) {\n <span class=\"live-transcript-panel__timer inline-flex items-center gap-0.5 rounded-full bg-green-500/10 px-1 py-0.5 text-green-700\">\n <span class=\"h-1.5 w-1.5 rounded-full bg-green-600\" aria-hidden=\"true\"></span>\n <span class=\"font-mono uppercase tracking-wide\">{{ callStarted() | edsElapsed }}</span>\n </span>\n }\n </header>\n\n <div #scrollHost class=\"chat-thread live-transcript-panel__thread\" role=\"log\" aria-live=\"polite\">\n @for (line of lines(); track $index) {\n @if (line.who === 'system') {\n <div class=\"chat-row chat-row--system\">\n <div\n class=\"chat-bubble chat-bubble--system inline-flex items-center gap-2\"\n [class.border-emerald-200]=\"line.flag === 'verify'\"\n [class.bg-emerald-50]=\"line.flag === 'verify'\"\n [class.text-emerald-700]=\"line.flag === 'verify'\"\n [class.border-amber-200]=\"line.flag === 'fail' || line.flag === 'escalate'\"\n [class.bg-amber-50]=\"line.flag === 'fail' || line.flag === 'escalate'\"\n [class.text-amber-700]=\"line.flag === 'fail' || line.flag === 'escalate'\"\n >\n <ed-icon [name]=\"systemIcon(line.flag)\" [size]=\"14\" />\n <span>{{ line.text }}</span>\n </div>\n </div>\n } @else {\n <article\n class=\"chat-row\"\n [class.chat-row--caller]=\"line.who === 'patient'\"\n [class.chat-row--ai]=\"line.who === 'ai'\"\n [class.chat-row--staff]=\"line.who === 'staff'\"\n >\n <div class=\"chat-meta\">\n <span [class.live-transcript-panel__meta--ai]=\"line.who === 'ai'\">{{ roleLabel(line.who) }}</span>\n </div>\n <div class=\"chat-bubble-row\">\n <div\n class=\"chat-avatar\"\n [class.chat-avatar--ai]=\"line.who === 'ai'\"\n [class.live-transcript-panel__avatar--ai]=\"line.who === 'ai'\"\n [class.chat-avatar--staff]=\"line.who === 'staff'\"\n [class.chat-avatar--caller]=\"line.who === 'patient'\"\n >\n <ed-icon [name]=\"speakerIcon(line.who)\" [size]=\"16\" />\n </div>\n <div\n class=\"chat-bubble\"\n [class.chat-bubble--caller]=\"line.who === 'patient'\"\n [class.chat-bubble--ai]=\"line.who === 'ai'\"\n [class.chat-bubble--staff]=\"line.who === 'staff'\"\n >\n <p class=\"chat-content\">{{ line.text }}</p>\n </div>\n </div>\n </article>\n }\n }\n\n @if (patientInterim(); as interim) {\n <article class=\"chat-row chat-row--caller\">\n <div class=\"chat-meta\">\n <span>{{ callerLabel() }}</span>\n </div>\n <div class=\"chat-bubble-row\">\n <div class=\"chat-avatar chat-avatar--caller\">\n <ed-icon name=\"person\" [size]=\"16\" />\n </div>\n <div class=\"chat-bubble chat-bubble--caller chat-bubble--interim live-transcript-panel__typing\">\n <span class=\"live-transcript-panel__dot\" aria-hidden=\"true\"></span>\n <span class=\"live-transcript-panel__dot\" aria-hidden=\"true\"></span>\n <span class=\"live-transcript-panel__dot\" aria-hidden=\"true\"></span>\n <span class=\"sr-only\">{{ interim }}</span>\n </div>\n </div>\n </article>\n }\n\n @if (staffInterim(); as interim) {\n <article class=\"chat-row chat-row--staff\">\n <div class=\"chat-meta\"><span>Staff</span></div>\n <div class=\"chat-bubble-row\">\n <div class=\"chat-avatar chat-avatar--staff\">\n <ed-icon name=\"Robot\" [size]=\"16\" />\n </div>\n <div class=\"chat-bubble chat-bubble--staff chat-bubble--interim\">\n <p class=\"chat-content\">{{ interim }}</p>\n </div>\n </div>\n </article>\n }\n\n @if (aiInterim(); as interim) {\n <article class=\"chat-row chat-row--ai\">\n <div class=\"chat-meta\">\n <span class=\"live-transcript-panel__meta--ai\">AI Receptionist</span>\n </div>\n <div class=\"chat-bubble-row\">\n <div class=\"chat-avatar chat-avatar--ai live-transcript-panel__avatar--ai\">\n <ed-icon name=\"graph_new\" [size]=\"16\" />\n </div>\n <div class=\"chat-bubble chat-bubble--ai chat-bubble--interim\">\n <p class=\"chat-content\">\n {{ interim }}<span class=\"live-transcript-panel__caret\" aria-hidden=\"true\"></span>\n </p>\n </div>\n </div>\n </article>\n }\n\n @if (lines().length === 0 && !patientInterim() && !aiInterim() && !staffInterim()) {\n <p class=\"m-0 px-3 py-8 text-center text-zinc-500\">Waiting for conversation\u2026</p>\n }\n </div>\n\n @if (showCopilot() || showStaffInput()) {\n <footer class=\"mt-4 flex shrink-0 flex-col gap-4\">\n @if (showCopilot()) {\n <div class=\"flex flex-wrap items-center gap-2\">\n <span class=\"inline-flex items-center justify-center rounded-md bg-purple-500/10 p-1 text-purple-700\">\n <ed-icon name=\"sparkle\" [size]=\"12\" />\n </span>\n @for (chip of copilotChips; track chip) {\n <button\n type=\"button\"\n class=\"live-transcript-panel__chip inline-flex h-5 items-center rounded border border-zinc-200 bg-white px-1 text-zinc-700\"\n (click)=\"onChipClick(chip)\"\n >\n {{ chip }}\n </button>\n }\n </div>\n }\n\n <div class=\"flex items-center gap-2 rounded-lg border border-zinc-200 p-2\">\n <input\n type=\"text\"\n class=\"h-8 min-w-0 flex-1 border-0 bg-transparent px-1 text-zinc-900 outline-none placeholder:text-zinc-400\"\n [placeholder]=\"showCopilot() ? 'Tell the AI what it should know or do next\u2026' : 'Type to speak as staff\u2026'\"\n [ngModel]=\"staffMessage()\"\n (ngModelChange)=\"onStaffInput($event)\"\n (keyup.enter)=\"onSend()\"\n autocomplete=\"off\"\n />\n @if (showCopilot()) {\n <button\n type=\"button\"\n class=\"live-transcript-panel__copilot-btn inline-flex h-8 shrink-0 items-center gap-0.5 rounded-md bg-purple-600 px-2 text-white\"\n (click)=\"onSend()\"\n >\n Co-Pilot\n <ed-icon name=\"sparkle\" [size]=\"14\" />\n </button>\n }\n </div>\n </footer>\n }\n</section>\n", styles: ["@charset \"UTF-8\";.chat-thread{display:flex;flex-direction:column;gap:0;flex:1 1 auto;min-height:0;overflow:auto;padding:1rem 1.5rem;background:#fff;scroll-behavior:smooth}.chat-row{display:flex;flex-direction:column;width:100%;margin-bottom:1rem}.chat-row--caller,.chat-row--user,.chat-row--patient{align-items:flex-start}.chat-row--agent,.chat-row--ai,.chat-row--staff{align-items:flex-end}.chat-row--tool,.chat-row--system{align-items:center}.chat-meta{display:flex;align-items:center;gap:.5rem;margin-bottom:.25rem;font-size:.875rem;color:var(--color-text-light, #6b7280)}.chat-row--agent .chat-meta,.chat-row--ai .chat-meta,.chat-row--staff .chat-meta{justify-content:flex-end;padding-right:2.75rem}.chat-bubble-row{display:flex;align-items:flex-start;gap:.5rem;max-width:min(72%,40rem)}.chat-row--agent .chat-bubble-row,.chat-row--ai .chat-bubble-row,.chat-row--staff .chat-bubble-row{flex-direction:row-reverse}.chat-avatar{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border-radius:9999px}.chat-avatar--ai{background:#ad46ff1f;color:#8200db}.chat-avatar--staff{border:1px solid #ddd6fe;background:#f5f3ff;color:#6d28d9}.chat-avatar--caller{background:#e5e7eb;color:#4b5563}.chat-bubble{max-width:100%;padding:.75rem 1rem;font-size:.875rem;line-height:1.5;word-break:break-word;white-space:pre-wrap}.chat-bubble--caller,.chat-bubble--user,.chat-bubble--patient{border:1px solid var(--color-border-light, #e5e7eb);border-radius:0 .5rem .5rem;background:#fff;color:var(--color-text-dark, #111827)}.chat-bubble--agent,.chat-bubble--ai{border:none;border-radius:.5rem;background:#ad46ff1a;color:var(--color-text-dark, #53575c)}.chat-bubble--staff{border:1px solid #ddd6fe;border-radius:.5rem 0 .5rem .5rem;background:#f5f3ff;color:#4c1d95}.chat-bubble--tool,.chat-bubble--system{max-width:min(90%,40rem);padding:.5rem .75rem;border:1px solid var(--color-border-light, #e5e7eb);border-radius:9999px;background:#f9fafb;color:#6b7280;font-size:.75rem;text-align:center}.chat-bubble--interim{opacity:.9;font-style:italic}.chat-content{margin:0}.chat-time{font-size:.75rem;color:#9ca3af}.live-transcript-panel__thread.chat-thread{background:#f7f9fa;border-radius:.75rem;padding:1rem;min-height:0}@media(max-width:900px){.live-transcript-panel__thread.chat-thread{max-height:min(60vh,36rem)}}.live-transcript-panel__avatar--ai{background:#ad46ff1f;color:#8200db}.live-transcript-panel__meta--ai{color:#8200db}.live-transcript-panel__caret{display:inline-block;width:7px;height:14px;margin-left:2px;vertical-align:text-bottom;background:#8200db;animation:eds-caret 1s step-end infinite}.live-transcript-panel__typing{display:inline-flex;align-items:center;gap:4px;min-width:2.5rem;min-height:1.25rem}.live-transcript-panel__chip{cursor:pointer}.live-transcript-panel__chip:hover{background:#f7f9fa}.live-transcript-panel__copilot-btn{cursor:pointer;border:0}.live-transcript-panel__dot{width:6px;height:6px;border-radius:9999px;background:#a2a4a6;animation:eds-typing 1.2s ease-in-out infinite}.live-transcript-panel__dot:nth-child(2){animation-delay:.15s}.live-transcript-panel__dot:nth-child(3){animation-delay:.3s}@keyframes eds-caret{50%{opacity:0}}@keyframes eds-typing{0%,80%,to{opacity:.35;transform:translateY(0)}40%{opacity:1;transform:translateY(-2px)}}\n"] }]
|
|
378
|
+
class: 'flex h-full min-h-0 flex-1 flex-col overflow-hidden',
|
|
379
|
+
}, template: "<section\n class=\"flex h-full min-h-0 flex-1 flex-col overflow-hidden rounded-xl bg-white p-4\"\n aria-labelledby=\"live-transcript-title\"\n>\n <header class=\"mb-4 flex shrink-0 items-center justify-between gap-3\">\n <div class=\"flex min-w-0 items-center gap-2\">\n <h4 id=\"live-transcript-title\" class=\"m-0\">Live Transcript</h4>\n @if (live() && !paused()) {\n <span class=\"live-transcript-panel__recording inline-flex h-5 items-center gap-1 rounded px-1 text-red-600\">\n <span class=\"h-1.5 w-1.5 rounded-full bg-red-600\" aria-hidden=\"true\"></span>\n Recording\n </span>\n }\n </div>\n @if (callStarted()) {\n <span class=\"live-transcript-panel__timer inline-flex items-center gap-0.5 rounded-full bg-green-500/10 px-1 py-0.5 text-green-700\">\n <span class=\"h-1.5 w-1.5 rounded-full bg-green-600\" aria-hidden=\"true\"></span>\n <span class=\"font-mono uppercase tracking-wide\">{{ callStarted() | edsElapsed }}</span>\n </span>\n }\n </header>\n\n <div #scrollHost class=\"chat-thread live-transcript-panel__thread\" role=\"log\" aria-live=\"polite\">\n @for (line of lines(); track $index) {\n @if (line.who === 'system') {\n <div class=\"chat-row chat-row--system\">\n <div\n class=\"chat-bubble chat-bubble--system inline-flex items-center gap-2\"\n [class.border-emerald-200]=\"line.flag === 'verify'\"\n [class.bg-emerald-50]=\"line.flag === 'verify'\"\n [class.text-emerald-700]=\"line.flag === 'verify'\"\n [class.border-amber-200]=\"line.flag === 'fail' || line.flag === 'escalate'\"\n [class.bg-amber-50]=\"line.flag === 'fail' || line.flag === 'escalate'\"\n [class.text-amber-700]=\"line.flag === 'fail' || line.flag === 'escalate'\"\n >\n <ed-icon [name]=\"systemIcon(line.flag)\" [size]=\"14\" />\n <span>{{ line.text }}</span>\n </div>\n </div>\n } @else {\n <article\n class=\"chat-row\"\n [class.chat-row--caller]=\"line.who === 'patient'\"\n [class.chat-row--ai]=\"line.who === 'ai'\"\n [class.chat-row--staff]=\"line.who === 'staff'\"\n >\n <div class=\"chat-meta\">\n <span [class.live-transcript-panel__meta--ai]=\"line.who === 'ai'\">{{ roleLabel(line.who) }}</span>\n </div>\n <div class=\"chat-bubble-row\">\n <div\n class=\"chat-avatar\"\n [class.chat-avatar--ai]=\"line.who === 'ai'\"\n [class.live-transcript-panel__avatar--ai]=\"line.who === 'ai'\"\n [class.chat-avatar--staff]=\"line.who === 'staff'\"\n [class.chat-avatar--caller]=\"line.who === 'patient'\"\n >\n <ed-icon [name]=\"speakerIcon(line.who)\" [size]=\"16\" />\n </div>\n <div\n class=\"chat-bubble\"\n [class.chat-bubble--caller]=\"line.who === 'patient'\"\n [class.chat-bubble--ai]=\"line.who === 'ai'\"\n [class.chat-bubble--staff]=\"line.who === 'staff'\"\n >\n <p class=\"chat-content\">{{ line.text }}</p>\n </div>\n </div>\n </article>\n }\n }\n\n @if (patientInterim(); as interim) {\n <article class=\"chat-row chat-row--caller\">\n <div class=\"chat-meta\">\n <span>{{ callerLabel() }}</span>\n </div>\n <div class=\"chat-bubble-row\">\n <div class=\"chat-avatar chat-avatar--caller\">\n <ed-icon name=\"person\" [size]=\"16\" />\n </div>\n <div class=\"chat-bubble chat-bubble--caller chat-bubble--interim live-transcript-panel__typing\">\n <span class=\"live-transcript-panel__dot\" aria-hidden=\"true\"></span>\n <span class=\"live-transcript-panel__dot\" aria-hidden=\"true\"></span>\n <span class=\"live-transcript-panel__dot\" aria-hidden=\"true\"></span>\n <span class=\"sr-only\">{{ interim }}</span>\n </div>\n </div>\n </article>\n }\n\n @if (staffInterim(); as interim) {\n <article class=\"chat-row chat-row--staff\">\n <div class=\"chat-meta\"><span>Staff</span></div>\n <div class=\"chat-bubble-row\">\n <div class=\"chat-avatar chat-avatar--staff\">\n <ed-icon name=\"Robot\" [size]=\"16\" />\n </div>\n <div class=\"chat-bubble chat-bubble--staff chat-bubble--interim\">\n <p class=\"chat-content\">{{ interim }}</p>\n </div>\n </div>\n </article>\n }\n\n @if (aiInterim(); as interim) {\n <article class=\"chat-row chat-row--ai\">\n <div class=\"chat-meta\">\n <span class=\"live-transcript-panel__meta--ai\">AI Receptionist</span>\n </div>\n <div class=\"chat-bubble-row\">\n <div class=\"chat-avatar chat-avatar--ai live-transcript-panel__avatar--ai\">\n <ed-icon name=\"graph_new\" [size]=\"16\" />\n </div>\n <div class=\"chat-bubble chat-bubble--ai chat-bubble--interim\">\n <p class=\"chat-content\">\n {{ interim }}<span class=\"live-transcript-panel__caret\" aria-hidden=\"true\"></span>\n </p>\n </div>\n </div>\n </article>\n }\n\n @if (lines().length === 0 && !patientInterim() && !aiInterim() && !staffInterim()) {\n <p class=\"m-0 px-3 py-8 text-center text-zinc-500\">Waiting for conversation\u2026</p>\n }\n </div>\n\n @if (showStaffInput()) {\n <footer class=\"mt-4 flex shrink-0 flex-col gap-4\">\n <div class=\"flex items-center gap-2 rounded-lg border border-zinc-200 p-2\">\n <input\n type=\"text\"\n class=\"h-8 min-w-0 flex-1 border-0 bg-transparent px-1 text-zinc-900 outline-none placeholder:text-zinc-400\"\n placeholder=\"Type to speak as staff\u2026\"\n [ngModel]=\"staffMessage()\"\n (ngModelChange)=\"onStaffInput($event)\"\n (keyup.enter)=\"onSend()\"\n autocomplete=\"off\"\n />\n </div>\n </footer>\n }\n</section>\n", styles: ["@charset \"UTF-8\";.chat-thread{display:flex;flex-direction:column;gap:0;flex:1 1 auto;min-height:0;overflow:auto;padding:1rem 1.5rem;background:#fff;scroll-behavior:smooth}.chat-row{display:flex;flex-direction:column;width:100%;margin-bottom:1rem}.chat-row--caller,.chat-row--user,.chat-row--patient{align-items:flex-start}.chat-row--agent,.chat-row--ai,.chat-row--staff{align-items:flex-end}.chat-row--tool,.chat-row--system{align-items:center}.chat-meta{display:flex;align-items:center;gap:.5rem;margin-bottom:.25rem;font-size:.875rem;color:var(--color-text-light, #6b7280)}.chat-row--agent .chat-meta,.chat-row--ai .chat-meta,.chat-row--staff .chat-meta{justify-content:flex-end;padding-right:2.75rem}.chat-bubble-row{display:flex;align-items:flex-start;gap:.5rem;max-width:min(72%,40rem)}.chat-row--agent .chat-bubble-row,.chat-row--ai .chat-bubble-row,.chat-row--staff .chat-bubble-row{flex-direction:row-reverse}.chat-avatar{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border-radius:9999px}.chat-avatar--ai{background:#ad46ff1f;color:#8200db}.chat-avatar--staff{border:1px solid #ddd6fe;background:#f5f3ff;color:#6d28d9}.chat-avatar--caller{background:#e5e7eb;color:#4b5563}.chat-bubble{max-width:100%;padding:.75rem 1rem;font-size:.875rem;line-height:1.5;word-break:break-word;white-space:pre-wrap}.chat-bubble--caller,.chat-bubble--user,.chat-bubble--patient{border:1px solid var(--color-border-light, #e5e7eb);border-radius:0 .5rem .5rem;background:#fff;color:var(--color-text-dark, #111827)}.chat-bubble--agent,.chat-bubble--ai{border:none;border-radius:.5rem;background:#ad46ff1a;color:var(--color-text-dark, #53575c)}.chat-bubble--staff{border:1px solid #ddd6fe;border-radius:.5rem 0 .5rem .5rem;background:#f5f3ff;color:#4c1d95}.chat-bubble--tool,.chat-bubble--system{max-width:min(90%,40rem);padding:.5rem .75rem;border:1px solid var(--color-border-light, #e5e7eb);border-radius:9999px;background:#f9fafb;color:#6b7280;font-size:.75rem;text-align:center}.chat-bubble--interim{opacity:.9;font-style:italic}.chat-content{margin:0}.chat-time{font-size:.75rem;color:#9ca3af}.live-transcript-panel__thread.chat-thread{flex:1 1 auto;min-height:0;overflow:auto;background:#f7f9fa;border-radius:.75rem;padding:1rem}.live-transcript-panel__avatar--ai{background:#ad46ff1f;color:#8200db}.live-transcript-panel__meta--ai{color:#8200db}.live-transcript-panel__caret{display:inline-block;width:7px;height:14px;margin-left:2px;vertical-align:text-bottom;background:#8200db;animation:eds-caret 1s step-end infinite}.live-transcript-panel__typing{display:inline-flex;align-items:center;gap:4px;min-width:2.5rem;min-height:1.25rem}.live-transcript-panel__dot{width:6px;height:6px;border-radius:9999px;background:#a2a4a6;animation:eds-typing 1.2s ease-in-out infinite}.live-transcript-panel__dot:nth-child(2){animation-delay:.15s}.live-transcript-panel__dot:nth-child(3){animation-delay:.3s}@keyframes eds-caret{50%{opacity:0}}@keyframes eds-typing{0%,80%,to{opacity:.35;transform:translateY(0)}40%{opacity:1;transform:translateY(-2px)}}\n"] }]
|
|
391
380
|
}], ctorParameters: () => [], propDecorators: { scrollHost: [{
|
|
392
381
|
type: ViewChild,
|
|
393
382
|
args: ['scrollHost']
|
|
@@ -720,7 +709,6 @@ class LiveMonitorPageComponent {
|
|
|
720
709
|
this.toast = inject(ToastService);
|
|
721
710
|
this.destroyRef = inject(DestroyRef);
|
|
722
711
|
this.call = signal(null);
|
|
723
|
-
this.staffMessage = signal('');
|
|
724
712
|
this.loading = signal(true);
|
|
725
713
|
this.callId = signal('');
|
|
726
714
|
this.summary = signal(null);
|
|
@@ -953,15 +941,6 @@ class LiveMonitorPageComponent {
|
|
|
953
941
|
});
|
|
954
942
|
});
|
|
955
943
|
}
|
|
956
|
-
sendMessage() {
|
|
957
|
-
const text = this.staffMessage().trim();
|
|
958
|
-
if (!text)
|
|
959
|
-
return;
|
|
960
|
-
this.api.postMessage(this.callId(), text).subscribe(() => {
|
|
961
|
-
this.staffMessage.set('');
|
|
962
|
-
this.refreshMetadata();
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
944
|
enableSound() {
|
|
966
945
|
void this.staffMedia.startAudioPlayback();
|
|
967
946
|
}
|
|
@@ -1114,7 +1093,7 @@ class LiveMonitorPageComponent {
|
|
|
1114
1093
|
});
|
|
1115
1094
|
}
|
|
1116
1095
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LiveMonitorPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1117
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: LiveMonitorPageComponent, isStandalone: true, selector: "eds-live-monitor-page", host: { classAttribute: "block h-full min-h-0" }, ngImport: i0, template: "<div class=\"live-monitor-page box-border flex h-full min-h-0 flex-col overflow-hidden bg-lighter p-base text-zinc-900\">\n @if (loading()) {\n <div class=\"flex min-h-0 flex-1 flex-col gap-4\" aria-busy=\"true\">\n <header class=\"flex shrink-0 items-center justify-between gap-3 rounded-lg bg-white p-3\">\n <div class=\"flex min-w-0 flex-1 items-center gap-4\">\n <ed-skeleton class=\"h-5 w-5 !rounded-sm\"></ed-skeleton>\n <div class=\"flex min-w-0 items-center gap-2\">\n <ed-skeleton class=\"h-9 w-9 !rounded-md\"></ed-skeleton>\n <div class=\"grid min-w-0 gap-2xs\">\n <ed-skeleton class=\"h-4 w-40\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-32\"></ed-skeleton>\n </div>\n </div>\n </div>\n <div class=\"flex shrink-0 items-center gap-3\">\n <ed-skeleton class=\"h-8 w-24 !rounded-md\"></ed-skeleton>\n <ed-skeleton class=\"h-8 w-28 !rounded-md\"></ed-skeleton>\n </div>\n </header>\n <div class=\"grid min-h-0 flex-1 grid-cols-[minmax(0,1fr)_minmax(280px,447px)] items-stretch gap-4 max-[1100px]:grid-cols-1\">\n <div class=\"flex min-h-0 flex-col rounded-xl bg-white p-4\">\n <ed-skeleton class=\"mb-4 h-5 w-36\"></ed-skeleton>\n @for (_ of transcriptSkeletonRows; track _) {\n <div class=\"mb-4 flex flex-col gap-2\">\n <ed-skeleton class=\"h-3 w-24\"></ed-skeleton>\n <ed-skeleton class=\"h-16 w-full max-w-xl !rounded-lg\"></ed-skeleton>\n </div>\n }\n </div>\n <aside class=\"flex min-h-0 flex-col gap-4\">\n <ed-skeleton class=\"h-40 w-full !rounded-lg\"></ed-skeleton>\n <ed-skeleton class=\"min-h-48 w-full flex-1 !rounded-lg\"></ed-skeleton>\n <ed-skeleton class=\"h-48 w-full !rounded-lg\"></ed-skeleton>\n </aside>\n </div>\n </div>\n } @else {\n @if (call(); as c) {\n <header class=\"live-monitor-page__header mb-4 flex shrink-0 flex-wrap items-center justify-between gap-3 rounded-lg bg-white p-3\">\n <div class=\"flex min-w-0 flex-1 items-center gap-4\">\n <a\n routerLink=\"..\"\n class=\"live-monitor-page__back inline-flex h-5 w-5 shrink-0 items-center justify-center text-zinc-600 no-underline\"\n aria-label=\"Back to live calls\"\n >\n <ed-icon name=\"arrow_back\" [size]=\"14\" />\n </a>\n\n <div class=\"live-monitor-page__profile flex min-w-0 items-center gap-2\">\n <ed-avatar\n class=\"shrink-0\"\n shape=\"box\"\n size=\"md\"\n [initials]=\"profileInitials(c)\"\n [tone]=\"isUnknownCaller(c) ? 'gray' : 'warm'\"\n />\n\n <div class=\"min-w-0\">\n <div class=\"flex flex-wrap items-center gap-3\">\n <h4 class=\"m-0\">{{ displayName(c) }}</h4>\n @if (isUnknownCaller(c)) {\n <ed-badge-2 variant=\"primary\" pill uppercase>Unknown</ed-badge-2>\n } @else {\n @if (goalBadge(c); as badge) {\n <ed-badge-2 variant=\"primary\" pill uppercase>{{ badge }}</ed-badge-2>\n }\n }\n </div>\n <p class=\"m-0 mt-0.5 text-zinc-500\">{{ c.phone || profileMeta(c) }}</p>\n </div>\n </div>\n </div>\n\n <div class=\"live-monitor-page__header-actions flex flex-wrap items-center gap-3\">\n @if (!callEnded()) {\n @if (mediaState() === 'connected' && isMediaForThisCall() && !audioPlaybackAllowed()) {\n <ed-button variant=\"secondary\" outline size=\"sm\" (click$)=\"enableSound()\">\n <ed-icon slot=\"prefix\" name=\"mic\" [size]=\"16\" />\n Enable sound\n </ed-button>\n }\n @if (isMediaForThisCall() && (mediaState() === 'connected' || mediaState() === 'connecting')) {\n <span\n class=\"live-monitor-page__media-pill inline-flex h-7 items-center rounded-full border border-zinc-200 bg-zinc-50 px-3 text-zinc-600\"\n [attr.data-mode]=\"mediaMode() || 'idle'\"\n [attr.data-state]=\"mediaState()\"\n >\n @if (mediaState() === 'connecting') {\n Connecting\u2026\n } @else if (mediaMode() === 'takeover') {\n {{ micEnabled() ? 'On call \u00B7 mic on' : 'On call \u00B7 mic off' }}\n } @else {\n Joined\n }\n </span>\n }\n <ed-button\n variant=\"danger\"\n outline\n size=\"sm\"\n [disabled]=\"mediaBusy()\"\n (click$)=\"endCall()\"\n >\n <ed-icon slot=\"prefix\" name=\"call_end\" [size]=\"16\" />\n End Call\n </ed-button>\n @if (isTakeover()) {\n @if (mediaState() === 'connected' && isMediaForThisCall()) {\n <ed-button variant=\"secondary\" outline size=\"sm\" (click$)=\"toggleMic()\">\n <ed-icon slot=\"prefix\" [name]=\"micEnabled() ? 'mic' : 'mic_off'\" [size]=\"16\" />\n {{ micEnabled() ? 'Mute' : 'Unmute' }}\n </ed-button>\n }\n } @else {\n <ed-button\n variant=\"primary\"\n fill\n size=\"sm\"\n [disabled]=\"mediaBusy()\"\n (click$)=\"takeover()\"\n >\n <ed-icon slot=\"prefix\" name=\"phone_call\" [size]=\"16\" />\n Take Over\n </ed-button>\n }\n } @else {\n <span class=\"inline-flex items-center gap-2 font-mono text-zinc-900\">\n <span class=\"h-2 w-2 rounded-full bg-green-500 ring-4 ring-green-500/15\" aria-hidden=\"true\"></span>\n {{ c.started | edsElapsed }}\n </span>\n }\n </div>\n </header>\n\n @if (callEnded()) {\n <div class=\"mb-4 flex items-center gap-2 rounded-lg bg-green-50 px-4 py-3 text-green-700\" role=\"status\">\n <ed-icon name=\"check_circle\" [size]=\"18\" />\n Call ended\n </div>\n }\n\n <div\n class=\"live-monitor-page__grid grid min-h-0 flex-1 grid-cols-[minmax(0,1fr)_minmax(280px,447px)] items-stretch gap-4 max-[1100px]:grid-cols-1 max-[1100px]:overflow-auto\"\n >\n <div class=\"live-monitor-page__main flex h-full min-h-0 flex-col gap-4 max-[1100px]:min-h-[420px]\">\n <eds-live-transcript-panel\n [live]=\"isLive()\"\n [paused]=\"isPaused()\"\n [humanTakeover]=\"isTakeover()\"\n [showStaffInput]=\"false\"\n [showCopilot]=\"isLive() && !isTakeover()\"\n [callStarted]=\"c.started\"\n [staffMessage]=\"staffMessage()\"\n [callerName]=\"c.name\"\n [callerMatched]=\"c.matched\"\n (staffMessageChange)=\"staffMessage.set($event)\"\n (sendMessage)=\"sendMessage()\"\n />\n\n @if (callEnded()) {\n <section class=\"live-monitor-page__post-call flex flex-col gap-4\">\n <div class=\"rounded-lg border border-zinc-200 bg-white p-4 text-zinc-600\">\n <h4 class=\"mb-3 m-0\">Call summary</h4>\n @if (summaryLoading()) {\n <div class=\"grid gap-2xs\" aria-busy=\"true\">\n <ed-skeleton class=\"h-3 w-full\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-5/6\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-3/4\"></ed-skeleton>\n </div>\n } @else if (summary()) {\n <p class=\"m-0\">{{ summary() }}</p>\n } @else {\n <p class=\"m-0 text-zinc-500\">Summary will appear when processing completes.</p>\n }\n </div>\n <eds-call-recording-player [callId]=\"callId()\" />\n </section>\n }\n </div>\n\n <aside class=\"live-monitor-page__sidebar flex h-full min-h-0 flex-col gap-4 overflow-auto\">\n @if (!callEnded()) {\n <section class=\"flex flex-col gap-4 rounded-lg bg-white p-4\" aria-labelledby=\"live-call-state-title\">\n <header class=\"flex items-center gap-3\">\n <span class=\"inline-flex items-center justify-center rounded-md bg-fuchsia-500/10 p-1 text-fuchsia-600\">\n <ed-icon name=\"phone_call\" [size]=\"16\" />\n </span>\n <h4 id=\"live-call-state-title\" class=\"m-0\">Call State</h4>\n </header>\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-center justify-between gap-2 text-zinc-600\">\n <span class=\"live-monitor-page__eyebrow\">Agent confidence</span>\n <span class=\"live-monitor-page__eyebrow\">{{ c.confidence }}%</span>\n </div>\n <div class=\"live-monitor-page__confidence-track\">\n <span\n class=\"live-monitor-page__confidence-fill\"\n [style.width.%]=\"c.confidence\"\n [attr.data-level]=\"confidenceLevel(c.confidence)\"\n ></span>\n </div>\n <div class=\"flex items-center gap-2 text-zinc-600\">\n <ed-icon\n [name]=\"confidenceLevel(c.confidence) === 'low' ? 'trend_down' : 'info'\"\n [size]=\"16\"\n />\n <span>{{ confidenceHint(c.confidence) }}</span>\n </div>\n </div>\n\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"live-monitor-page__eyebrow\">Patient sentiment</span>\n <span class=\"vr-sentiment\" [attr.data-sentiment]=\"c.sentiment\">\n <span class=\"vr-sentiment__icon\" aria-hidden=\"true\">\n <img [src]=\"sentimentIcon(c.sentiment)\" alt=\"\" width=\"16\" height=\"16\" />\n </span>\n <span>{{ sentimentLabel(c.sentiment) }}</span>\n </span>\n </div>\n </section>\n }\n\n <section\n class=\"live-monitor-page__sidebar-block flex min-h-0 flex-1 flex-col gap-4 rounded-lg bg-white p-4\"\n aria-labelledby=\"live-goal-timeline-title\"\n >\n <header class=\"flex items-center gap-3\">\n <span class=\"inline-flex items-center justify-center rounded-md bg-blue-500/10 p-1 text-blue-600\">\n <ed-icon name=\"flag\" [size]=\"16\" />\n </span>\n <h4 id=\"live-goal-timeline-title\" class=\"m-0\">Goal Timeline</h4>\n </header>\n\n <div class=\"live-monitor-page__goal-panel flex min-h-0 flex-1 flex-col rounded-lg bg-zinc-50 p-2\">\n @if (c.goal) {\n <p class=\"live-monitor-page__eyebrow mb-3 m-0 text-zinc-900\">{{ c.goal }}</p>\n } @else {\n <p class=\"live-monitor-page__eyebrow mb-3 m-0 text-zinc-900\">No Goal identified yet</p>\n }\n\n <ul class=\"live-monitor-page__tools m-0 flex min-h-0 flex-1 list-none flex-col gap-4 overflow-auto p-0\">\n @for (tool of liveTools(); track toolTrackKey(tool, $index)) {\n <li class=\"flex flex-col gap-1\" [class]=\"toolStateClass(tool.state)\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <span class=\"text-zinc-900\">{{ tool.name }}</span>\n <ed-badge-2 [attr.variant]=\"toolBadgeTone(tool.state)\" pill uppercase>\n {{ toolBadgeLabel(tool.state) }}\n </ed-badge-2>\n </div>\n @if (tool.result) {\n <p class=\"m-0 text-zinc-500\">{{ tool.result }}</p>\n }\n </li>\n } @empty {\n @for (step of goalPlaceholderSteps; track step.title) {\n <li class=\"flex flex-col gap-1\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <span class=\"text-zinc-900\">{{ step.title }}</span>\n <ed-badge-2 [attr.variant]=\"step.tone\" pill uppercase>{{ step.label }}</ed-badge-2>\n </div>\n <p class=\"m-0 text-zinc-500\">{{ step.detail }}</p>\n </li>\n }\n }\n </ul>\n </div>\n </section>\n\n <section class=\"flex flex-col gap-2.5 rounded-lg bg-white p-4\" aria-labelledby=\"live-patient-context-title\">\n <header class=\"flex items-center justify-between gap-3\">\n <div class=\"flex items-center gap-3\">\n <span class=\"inline-flex items-center justify-center rounded-md bg-green-500/10 p-1 text-green-700\">\n <ed-icon name=\"person\" [size]=\"16\" />\n </span>\n <h4 id=\"live-patient-context-title\" class=\"m-0\">Patient Context</h4>\n </div>\n @if (isUnknownCaller(c) || !c.matched) {\n <ed-badge-2 variant=\"primary\" pill uppercase>Asking details\u2026</ed-badge-2>\n }\n </header>\n\n @if (isUnknownCaller(c) || !c.matched) {\n <div\n class=\"flex flex-1 flex-col justify-center gap-6 rounded-lg border border-dashed border-zinc-200 bg-zinc-50 p-4\"\n aria-busy=\"true\"\n >\n <div class=\"flex flex-col gap-2\">\n <ed-skeleton class=\"h-3 w-full !rounded-full\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-3/4 !rounded-full\"></ed-skeleton>\n </div>\n <div class=\"flex flex-col gap-2\">\n <ed-skeleton class=\"h-3 w-full !rounded-full\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-3/4 !rounded-full\"></ed-skeleton>\n </div>\n </div>\n } @else {\n <div class=\"rounded-lg border border-zinc-200 bg-zinc-50 p-4\">\n <p class=\"m-0 text-zinc-900\">{{ displayName(c) }}</p>\n <p class=\"m-0 mt-1 text-zinc-500\">{{ profileMeta(c) }}</p>\n @if (c.phone) {\n <p class=\"m-0 mt-1 text-zinc-500\">{{ c.phone }}</p>\n }\n </div>\n }\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"flex flex-col items-start gap-3\">\n <a\n routerLink=\"..\"\n class=\"live-monitor-page__back inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-md border border-zinc-200 bg-white text-zinc-600 no-underline\"\n aria-label=\"Back to live calls\"\n >\n <ed-icon name=\"arrow_back\" [size]=\"19\" />\n </a>\n <p class=\"m-0 text-zinc-500\">Call ended or not found.</p>\n </div>\n }\n }\n</div>\n", styles: [".vr-sentiment{display:inline-flex;align-items:center;justify-content:center;gap:4px;height:20px;padding:2px 4px;border-radius:6px;font-size:11px;font-weight:500;line-height:16px;white-space:nowrap}.vr-sentiment__icon{display:block;width:16px;height:16px;flex-shrink:0;overflow:hidden}.vr-sentiment__icon img{display:block;width:100%;height:100%;max-width:none}.vr-sentiment[data-sentiment=calm]{background:#3681f41a;color:#2162f0}.vr-sentiment[data-sentiment=neutral]{background:#f0f2f5;color:#41464c}.vr-sentiment[data-sentiment=urgent]{background:#f26b0d1a;color:#e94e0c}.vr-sentiment[data-sentiment=frustrated]{background:#f037401a;color:#db0c15}.live-monitor-page__eyebrow{font-size:10px;font-weight:600;letter-spacing:1px;line-height:1;text-transform:uppercase}.live-monitor-page__confidence-track{height:8px;overflow:hidden;border-radius:9999px;background:linear-gradient(90deg,#7d34fb1a,#e76b171a)}.live-monitor-page__confidence-fill{display:block;height:100%;border-radius:9999px;background:linear-gradient(90deg,#7d34fb,#e76b17)}.live-monitor-page__tools li{position:relative;padding-left:1.25rem}.live-monitor-page__tools li:before{content:\"\";position:absolute;top:.35rem;left:0;width:.5rem;height:.5rem;border-radius:9999px;background:#a2a4a6}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: EdAvatarComponent, selector: "ed-avatar", inputs: ["initials", "tone", "shape", "size", "gradient"] }, { kind: "component", type: IconComponent, selector: "ed-icon", inputs: ["name", "size", "color", "cssClass", "decorative", "ariaHidden", "ariaLabel", "title", "desc", "focusable", "tabIndex"] }, { kind: "component", type: EdButtonComponent, selector: "ed-button", inputs: ["variant", "size", "disabled", "ghost", "outline", "subtle", "fill", "loading", "block", "elevated", "shape", "type", "href", "routerLink", "rel", "target", "download", "ariaLabel"], outputs: ["click$"] }, { kind: "pipe", type: EdsElapsedPipe, name: "edsElapsed" }, { kind: "component", type: LiveTranscriptPanelComponent, selector: "eds-live-transcript-panel", inputs: ["live", "paused", "humanTakeover", "showStaffInput", "showCopilot", "callStarted", "staffMessage", "callerName", "callerMatched"], outputs: ["staffMessageChange", "sendMessage"] }, { kind: "component", type: CallRecordingPlayerComponent, selector: "eds-call-recording-player", inputs: ["callId"], outputs: ["currentTimeChange", "durationChange"] }, { kind: "component", type: EdSkeletonComponent, selector: "ed-skeleton", inputs: ["class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1096
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: LiveMonitorPageComponent, isStandalone: true, selector: "eds-live-monitor-page", host: { classAttribute: "block h-full min-h-0" }, ngImport: i0, template: "<div class=\"live-monitor-page box-border flex h-full min-h-0 flex-col overflow-hidden bg-lighter p-base text-zinc-900\">\n @if (loading()) {\n <div class=\"flex min-h-0 flex-1 flex-col gap-4\" aria-busy=\"true\">\n <header class=\"flex shrink-0 items-center justify-between gap-3 rounded-lg bg-white p-3\">\n <div class=\"flex min-w-0 flex-1 items-center gap-4\">\n <ed-skeleton class=\"h-5 w-5 !rounded-sm\"></ed-skeleton>\n <div class=\"flex min-w-0 items-center gap-2\">\n <ed-skeleton class=\"h-9 w-9 !rounded-md\"></ed-skeleton>\n <div class=\"grid min-w-0 gap-2xs\">\n <ed-skeleton class=\"h-4 w-40\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-32\"></ed-skeleton>\n </div>\n </div>\n </div>\n <div class=\"flex shrink-0 items-center gap-3\">\n <ed-skeleton class=\"h-8 w-24 !rounded-md\"></ed-skeleton>\n <ed-skeleton class=\"h-8 w-28 !rounded-md\"></ed-skeleton>\n </div>\n </header>\n <div class=\"grid min-h-0 flex-1 grid-cols-[minmax(0,1fr)_minmax(280px,447px)] items-stretch gap-4 max-[1100px]:grid-cols-1\">\n <div class=\"flex min-h-0 flex-col rounded-xl bg-white p-4\">\n <ed-skeleton class=\"mb-4 h-5 w-36\"></ed-skeleton>\n @for (_ of transcriptSkeletonRows; track _) {\n <div class=\"mb-4 flex flex-col gap-2\">\n <ed-skeleton class=\"h-3 w-24\"></ed-skeleton>\n <ed-skeleton class=\"h-16 w-full max-w-xl !rounded-lg\"></ed-skeleton>\n </div>\n }\n </div>\n <aside class=\"flex min-h-0 flex-col gap-4\">\n <ed-skeleton class=\"h-40 w-full !rounded-lg\"></ed-skeleton>\n <ed-skeleton class=\"min-h-48 w-full flex-1 !rounded-lg\"></ed-skeleton>\n <ed-skeleton class=\"h-48 w-full !rounded-lg\"></ed-skeleton>\n </aside>\n </div>\n </div>\n } @else {\n @if (call(); as c) {\n <header class=\"live-monitor-page__header mb-4 flex shrink-0 flex-wrap items-center justify-between gap-3 rounded-lg bg-white p-3\">\n <div class=\"flex min-w-0 flex-1 items-center gap-4\">\n <a\n routerLink=\"..\"\n class=\"live-monitor-page__back inline-flex h-5 w-5 shrink-0 items-center justify-center text-zinc-600 no-underline\"\n aria-label=\"Back to live calls\"\n >\n <ed-icon name=\"arrow_back\" [size]=\"14\" />\n </a>\n\n <div class=\"live-monitor-page__profile flex min-w-0 items-center gap-2\">\n <ed-avatar\n class=\"shrink-0\"\n shape=\"box\"\n size=\"md\"\n [initials]=\"profileInitials(c)\"\n [tone]=\"isUnknownCaller(c) ? 'gray' : 'warm'\"\n />\n\n <div class=\"min-w-0\">\n <div class=\"flex flex-wrap items-center gap-3\">\n <h4 class=\"m-0\">{{ displayName(c) }}</h4>\n @if (isUnknownCaller(c)) {\n <ed-badge-2 variant=\"primary\" pill uppercase>Unknown</ed-badge-2>\n } @else {\n @if (goalBadge(c); as badge) {\n <ed-badge-2 variant=\"primary\" pill uppercase>{{ badge }}</ed-badge-2>\n }\n }\n </div>\n <p class=\"m-0 mt-0.5 text-zinc-500\">{{ c.phone || profileMeta(c) }}</p>\n </div>\n </div>\n </div>\n\n <div class=\"live-monitor-page__header-actions flex flex-wrap items-center gap-3\">\n @if (!callEnded()) {\n @if (mediaState() === 'connected' && isMediaForThisCall() && !audioPlaybackAllowed()) {\n <ed-button variant=\"secondary\" outline size=\"sm\" (click$)=\"enableSound()\">\n <ed-icon slot=\"prefix\" name=\"mic\" [size]=\"16\" />\n Enable sound\n </ed-button>\n }\n @if (isMediaForThisCall() && (mediaState() === 'connected' || mediaState() === 'connecting')) {\n <span\n class=\"live-monitor-page__media-pill inline-flex h-7 items-center rounded-full border border-zinc-200 bg-zinc-50 px-3 text-zinc-600\"\n [attr.data-mode]=\"mediaMode() || 'idle'\"\n [attr.data-state]=\"mediaState()\"\n >\n @if (mediaState() === 'connecting') {\n Connecting\u2026\n } @else if (mediaMode() === 'takeover') {\n {{ micEnabled() ? 'On call \u00B7 mic on' : 'On call \u00B7 mic off' }}\n } @else {\n Joined\n }\n </span>\n }\n <ed-button\n variant=\"danger\"\n outline\n size=\"sm\"\n [disabled]=\"mediaBusy()\"\n (click$)=\"endCall()\"\n >\n <ed-icon slot=\"prefix\" name=\"call_end\" [size]=\"16\" />\n End Call\n </ed-button>\n @if (isTakeover()) {\n @if (mediaState() === 'connected' && isMediaForThisCall()) {\n <ed-button variant=\"secondary\" outline size=\"sm\" (click$)=\"toggleMic()\">\n <ed-icon slot=\"prefix\" [name]=\"micEnabled() ? 'mic' : 'mic_off'\" [size]=\"16\" />\n {{ micEnabled() ? 'Mute' : 'Unmute' }}\n </ed-button>\n }\n } @else {\n <ed-button\n variant=\"primary\"\n fill\n size=\"sm\"\n [disabled]=\"mediaBusy()\"\n (click$)=\"takeover()\"\n >\n <ed-icon slot=\"prefix\" name=\"phone_call\" [size]=\"16\" />\n Take Over\n </ed-button>\n }\n } @else {\n <span class=\"inline-flex items-center gap-2 font-mono text-zinc-900\">\n <span class=\"h-2 w-2 rounded-full bg-green-500 ring-4 ring-green-500/15\" aria-hidden=\"true\"></span>\n {{ c.started | edsElapsed }}\n </span>\n }\n </div>\n </header>\n\n @if (callEnded()) {\n <div class=\"mb-4 flex items-center gap-2 rounded-lg bg-green-50 px-4 py-3 text-green-700\" role=\"status\">\n <ed-icon name=\"check_circle\" [size]=\"18\" />\n Call ended\n </div>\n }\n\n <div\n class=\"live-monitor-page__grid grid min-h-0 flex-1 grid-cols-[minmax(0,1fr)_360px] items-stretch gap-4 max-[1100px]:grid-cols-1 max-[1100px]:overflow-auto\"\n >\n <div class=\"live-monitor-page__main flex h-full min-h-0 flex-col overflow-hidden\">\n <eds-live-transcript-panel\n [live]=\"isLive()\"\n [paused]=\"isPaused()\"\n [humanTakeover]=\"isTakeover()\"\n [callStarted]=\"c.started\"\n [callerName]=\"c.name\"\n [callerMatched]=\"c.matched\"\n />\n\n @if (callEnded()) {\n <section class=\"live-monitor-page__post-call flex flex-col gap-4\">\n <div class=\"rounded-lg border border-zinc-200 bg-white p-4 text-zinc-600\">\n <h4 class=\"mb-3 m-0\">Call summary</h4>\n @if (summaryLoading()) {\n <div class=\"grid gap-2xs\" aria-busy=\"true\">\n <ed-skeleton class=\"h-3 w-full\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-5/6\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-3/4\"></ed-skeleton>\n </div>\n } @else if (summary()) {\n <p class=\"m-0\">{{ summary() }}</p>\n } @else {\n <p class=\"m-0 text-zinc-500\">Summary will appear when processing completes.</p>\n }\n </div>\n <eds-call-recording-player [callId]=\"callId()\" />\n </section>\n }\n </div>\n\n <aside class=\"live-monitor-page__sidebar flex h-full min-h-0 flex-col gap-4 overflow-hidden\">\n @if (!callEnded()) {\n <section class=\"flex shrink-0 flex-col gap-4 rounded-lg bg-white p-4\" aria-labelledby=\"live-call-state-title\">\n <header class=\"flex items-center gap-3\">\n <span class=\"inline-flex items-center justify-center rounded-md bg-fuchsia-500/10 p-1 text-fuchsia-600\">\n <ed-icon name=\"phone_call\" [size]=\"16\" />\n </span>\n <h4 id=\"live-call-state-title\" class=\"m-0\">Call State</h4>\n </header>\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-center justify-between gap-2 text-zinc-600\">\n <span class=\"live-monitor-page__eyebrow\">Agent confidence</span>\n <span class=\"live-monitor-page__eyebrow\">{{ c.confidence }}%</span>\n </div>\n <div class=\"live-monitor-page__confidence-track\">\n <span\n class=\"live-monitor-page__confidence-fill\"\n [style.width.%]=\"c.confidence\"\n [attr.data-level]=\"confidenceLevel(c.confidence)\"\n ></span>\n </div>\n <div class=\"flex items-center gap-2 text-zinc-600\">\n <ed-icon\n [name]=\"confidenceLevel(c.confidence) === 'low' ? 'trend_down' : 'info'\"\n [size]=\"16\"\n />\n <span>{{ confidenceHint(c.confidence) }}</span>\n </div>\n </div>\n\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"live-monitor-page__eyebrow\">Patient sentiment</span>\n <span class=\"vr-sentiment\" [attr.data-sentiment]=\"c.sentiment\">\n <span class=\"vr-sentiment__icon\" aria-hidden=\"true\">\n <img [src]=\"sentimentIcon(c.sentiment)\" alt=\"\" width=\"16\" height=\"16\" />\n </span>\n <span>{{ sentimentLabel(c.sentiment) }}</span>\n </span>\n </div>\n </section>\n }\n\n <section\n class=\"live-monitor-page__sidebar-block flex min-h-[16rem] flex-1 flex-col gap-4 overflow-hidden rounded-lg bg-white p-4\"\n aria-labelledby=\"live-goal-timeline-title\"\n >\n <header class=\"flex items-center gap-3\">\n <span class=\"inline-flex items-center justify-center rounded-md bg-blue-500/10 p-1 text-blue-600\">\n <ed-icon name=\"flag\" [size]=\"16\" />\n </span>\n <h4 id=\"live-goal-timeline-title\" class=\"m-0\">Goal Timeline</h4>\n </header>\n\n <div class=\"live-monitor-page__goal-panel flex min-h-0 flex-1 flex-col rounded-lg bg-zinc-50 p-2\">\n @if (c.goal) {\n <p class=\"live-monitor-page__eyebrow mb-3 m-0 text-zinc-900\">{{ c.goal }}</p>\n } @else {\n <p class=\"live-monitor-page__eyebrow mb-3 m-0 text-zinc-900\">No Goal identified yet</p>\n }\n\n <ul class=\"live-monitor-page__tools m-0 flex min-h-0 flex-1 list-none flex-col gap-4 overflow-auto p-0\">\n @for (tool of liveTools(); track toolTrackKey(tool, $index)) {\n <li class=\"flex flex-col gap-1\" [class]=\"toolStateClass(tool.state)\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <span class=\"text-zinc-900\">{{ tool.name }}</span>\n <ed-badge-2 [attr.variant]=\"toolBadgeTone(tool.state)\" pill uppercase>\n {{ toolBadgeLabel(tool.state) }}\n </ed-badge-2>\n </div>\n @if (tool.result) {\n <p class=\"m-0 text-zinc-500\">{{ tool.result }}</p>\n }\n </li>\n } @empty {\n @for (step of goalPlaceholderSteps; track step.title) {\n <li class=\"flex flex-col gap-1\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <span class=\"text-zinc-900\">{{ step.title }}</span>\n <ed-badge-2 [attr.variant]=\"step.tone\" pill uppercase>{{ step.label }}</ed-badge-2>\n </div>\n <p class=\"m-0 text-zinc-500\">{{ step.detail }}</p>\n </li>\n }\n }\n </ul>\n </div>\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"flex flex-col items-start gap-3\">\n <a\n routerLink=\"..\"\n class=\"live-monitor-page__back inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-md border border-zinc-200 bg-white text-zinc-600 no-underline\"\n aria-label=\"Back to live calls\"\n >\n <ed-icon name=\"arrow_back\" [size]=\"19\" />\n </a>\n <p class=\"m-0 text-zinc-500\">Call ended or not found.</p>\n </div>\n }\n }\n</div>\n", styles: [".vr-sentiment{display:inline-flex;align-items:center;justify-content:center;gap:4px;height:20px;padding:2px 4px;border-radius:6px;font-size:11px;font-weight:500;line-height:16px;white-space:nowrap}.vr-sentiment__icon{display:block;width:16px;height:16px;flex-shrink:0;overflow:hidden}.vr-sentiment__icon img{display:block;width:100%;height:100%;max-width:none}.vr-sentiment[data-sentiment=calm]{background:#3681f41a;color:#2162f0}.vr-sentiment[data-sentiment=neutral]{background:#f0f2f5;color:#41464c}.vr-sentiment[data-sentiment=urgent]{background:#f26b0d1a;color:#e94e0c}.vr-sentiment[data-sentiment=frustrated]{background:#f037401a;color:#db0c15}.live-monitor-page__eyebrow{font-size:10px;font-weight:600;letter-spacing:1px;line-height:1;text-transform:uppercase}.live-monitor-page__confidence-track{height:8px;overflow:hidden;border-radius:9999px;background:linear-gradient(90deg,#7d34fb1a,#e76b171a)}.live-monitor-page__confidence-fill{display:block;height:100%;border-radius:9999px;background:linear-gradient(90deg,#7d34fb,#e76b17)}.live-monitor-page__sidebar-block{min-height:16rem}.live-monitor-page__tools li{position:relative;padding-left:1.25rem}.live-monitor-page__tools li:before{content:\"\";position:absolute;top:.35rem;left:0;width:.5rem;height:.5rem;border-radius:9999px;background:#a2a4a6}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: EdAvatarComponent, selector: "ed-avatar", inputs: ["initials", "tone", "shape", "size", "gradient"] }, { kind: "component", type: IconComponent, selector: "ed-icon", inputs: ["name", "size", "color", "cssClass", "decorative", "ariaHidden", "ariaLabel", "title", "desc", "focusable", "tabIndex"] }, { kind: "component", type: EdButtonComponent, selector: "ed-button", inputs: ["variant", "size", "disabled", "ghost", "outline", "subtle", "fill", "loading", "block", "elevated", "shape", "type", "href", "routerLink", "rel", "target", "download", "ariaLabel"], outputs: ["click$"] }, { kind: "pipe", type: EdsElapsedPipe, name: "edsElapsed" }, { kind: "component", type: LiveTranscriptPanelComponent, selector: "eds-live-transcript-panel", inputs: ["live", "paused", "humanTakeover", "showStaffInput", "callStarted", "staffMessage", "callerName", "callerMatched"], outputs: ["staffMessageChange", "sendMessage"] }, { kind: "component", type: CallRecordingPlayerComponent, selector: "eds-call-recording-player", inputs: ["callId"], outputs: ["currentTimeChange", "durationChange"] }, { kind: "component", type: EdSkeletonComponent, selector: "ed-skeleton", inputs: ["class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1118
1097
|
}
|
|
1119
1098
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LiveMonitorPageComponent, decorators: [{
|
|
1120
1099
|
type: Component,
|
|
@@ -1128,8 +1107,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
1128
1107
|
LiveTranscriptPanelComponent,
|
|
1129
1108
|
CallRecordingPlayerComponent,
|
|
1130
1109
|
EdSkeletonComponent,
|
|
1131
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, schemas: [CUSTOM_ELEMENTS_SCHEMA], host: { class: 'block h-full min-h-0' }, template: "<div class=\"live-monitor-page box-border flex h-full min-h-0 flex-col overflow-hidden bg-lighter p-base text-zinc-900\">\n @if (loading()) {\n <div class=\"flex min-h-0 flex-1 flex-col gap-4\" aria-busy=\"true\">\n <header class=\"flex shrink-0 items-center justify-between gap-3 rounded-lg bg-white p-3\">\n <div class=\"flex min-w-0 flex-1 items-center gap-4\">\n <ed-skeleton class=\"h-5 w-5 !rounded-sm\"></ed-skeleton>\n <div class=\"flex min-w-0 items-center gap-2\">\n <ed-skeleton class=\"h-9 w-9 !rounded-md\"></ed-skeleton>\n <div class=\"grid min-w-0 gap-2xs\">\n <ed-skeleton class=\"h-4 w-40\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-32\"></ed-skeleton>\n </div>\n </div>\n </div>\n <div class=\"flex shrink-0 items-center gap-3\">\n <ed-skeleton class=\"h-8 w-24 !rounded-md\"></ed-skeleton>\n <ed-skeleton class=\"h-8 w-28 !rounded-md\"></ed-skeleton>\n </div>\n </header>\n <div class=\"grid min-h-0 flex-1 grid-cols-[minmax(0,1fr)_minmax(280px,447px)] items-stretch gap-4 max-[1100px]:grid-cols-1\">\n <div class=\"flex min-h-0 flex-col rounded-xl bg-white p-4\">\n <ed-skeleton class=\"mb-4 h-5 w-36\"></ed-skeleton>\n @for (_ of transcriptSkeletonRows; track _) {\n <div class=\"mb-4 flex flex-col gap-2\">\n <ed-skeleton class=\"h-3 w-24\"></ed-skeleton>\n <ed-skeleton class=\"h-16 w-full max-w-xl !rounded-lg\"></ed-skeleton>\n </div>\n }\n </div>\n <aside class=\"flex min-h-0 flex-col gap-4\">\n <ed-skeleton class=\"h-40 w-full !rounded-lg\"></ed-skeleton>\n <ed-skeleton class=\"min-h-48 w-full flex-1 !rounded-lg\"></ed-skeleton>\n <ed-skeleton class=\"h-48 w-full !rounded-lg\"></ed-skeleton>\n </aside>\n </div>\n </div>\n } @else {\n @if (call(); as c) {\n <header class=\"live-monitor-page__header mb-4 flex shrink-0 flex-wrap items-center justify-between gap-3 rounded-lg bg-white p-3\">\n <div class=\"flex min-w-0 flex-1 items-center gap-4\">\n <a\n routerLink=\"..\"\n class=\"live-monitor-page__back inline-flex h-5 w-5 shrink-0 items-center justify-center text-zinc-600 no-underline\"\n aria-label=\"Back to live calls\"\n >\n <ed-icon name=\"arrow_back\" [size]=\"14\" />\n </a>\n\n <div class=\"live-monitor-page__profile flex min-w-0 items-center gap-2\">\n <ed-avatar\n class=\"shrink-0\"\n shape=\"box\"\n size=\"md\"\n [initials]=\"profileInitials(c)\"\n [tone]=\"isUnknownCaller(c) ? 'gray' : 'warm'\"\n />\n\n <div class=\"min-w-0\">\n <div class=\"flex flex-wrap items-center gap-3\">\n <h4 class=\"m-0\">{{ displayName(c) }}</h4>\n @if (isUnknownCaller(c)) {\n <ed-badge-2 variant=\"primary\" pill uppercase>Unknown</ed-badge-2>\n } @else {\n @if (goalBadge(c); as badge) {\n <ed-badge-2 variant=\"primary\" pill uppercase>{{ badge }}</ed-badge-2>\n }\n }\n </div>\n <p class=\"m-0 mt-0.5 text-zinc-500\">{{ c.phone || profileMeta(c) }}</p>\n </div>\n </div>\n </div>\n\n <div class=\"live-monitor-page__header-actions flex flex-wrap items-center gap-3\">\n @if (!callEnded()) {\n @if (mediaState() === 'connected' && isMediaForThisCall() && !audioPlaybackAllowed()) {\n <ed-button variant=\"secondary\" outline size=\"sm\" (click$)=\"enableSound()\">\n <ed-icon slot=\"prefix\" name=\"mic\" [size]=\"16\" />\n Enable sound\n </ed-button>\n }\n @if (isMediaForThisCall() && (mediaState() === 'connected' || mediaState() === 'connecting')) {\n <span\n class=\"live-monitor-page__media-pill inline-flex h-7 items-center rounded-full border border-zinc-200 bg-zinc-50 px-3 text-zinc-600\"\n [attr.data-mode]=\"mediaMode() || 'idle'\"\n [attr.data-state]=\"mediaState()\"\n >\n @if (mediaState() === 'connecting') {\n Connecting\u2026\n } @else if (mediaMode() === 'takeover') {\n {{ micEnabled() ? 'On call \u00B7 mic on' : 'On call \u00B7 mic off' }}\n } @else {\n Joined\n }\n </span>\n }\n <ed-button\n variant=\"danger\"\n outline\n size=\"sm\"\n [disabled]=\"mediaBusy()\"\n (click$)=\"endCall()\"\n >\n <ed-icon slot=\"prefix\" name=\"call_end\" [size]=\"16\" />\n End Call\n </ed-button>\n @if (isTakeover()) {\n @if (mediaState() === 'connected' && isMediaForThisCall()) {\n <ed-button variant=\"secondary\" outline size=\"sm\" (click$)=\"toggleMic()\">\n <ed-icon slot=\"prefix\" [name]=\"micEnabled() ? 'mic' : 'mic_off'\" [size]=\"16\" />\n {{ micEnabled() ? 'Mute' : 'Unmute' }}\n </ed-button>\n }\n } @else {\n <ed-button\n variant=\"primary\"\n fill\n size=\"sm\"\n [disabled]=\"mediaBusy()\"\n (click$)=\"takeover()\"\n >\n <ed-icon slot=\"prefix\" name=\"phone_call\" [size]=\"16\" />\n Take Over\n </ed-button>\n }\n } @else {\n <span class=\"inline-flex items-center gap-2 font-mono text-zinc-900\">\n <span class=\"h-2 w-2 rounded-full bg-green-500 ring-4 ring-green-500/15\" aria-hidden=\"true\"></span>\n {{ c.started | edsElapsed }}\n </span>\n }\n </div>\n </header>\n\n @if (callEnded()) {\n <div class=\"mb-4 flex items-center gap-2 rounded-lg bg-green-50 px-4 py-3 text-green-700\" role=\"status\">\n <ed-icon name=\"check_circle\" [size]=\"18\" />\n Call ended\n </div>\n }\n\n <div\n class=\"live-monitor-page__grid grid min-h-0 flex-1 grid-cols-[minmax(0,1fr)_minmax(280px,447px)] items-stretch gap-4 max-[1100px]:grid-cols-1 max-[1100px]:overflow-auto\"\n >\n <div class=\"live-monitor-page__main flex h-full min-h-0 flex-col gap-4 max-[1100px]:min-h-[420px]\">\n <eds-live-transcript-panel\n [live]=\"isLive()\"\n [paused]=\"isPaused()\"\n [humanTakeover]=\"isTakeover()\"\n [showStaffInput]=\"false\"\n [showCopilot]=\"isLive() && !isTakeover()\"\n [callStarted]=\"c.started\"\n [staffMessage]=\"staffMessage()\"\n [callerName]=\"c.name\"\n [callerMatched]=\"c.matched\"\n (staffMessageChange)=\"staffMessage.set($event)\"\n (sendMessage)=\"sendMessage()\"\n />\n\n @if (callEnded()) {\n <section class=\"live-monitor-page__post-call flex flex-col gap-4\">\n <div class=\"rounded-lg border border-zinc-200 bg-white p-4 text-zinc-600\">\n <h4 class=\"mb-3 m-0\">Call summary</h4>\n @if (summaryLoading()) {\n <div class=\"grid gap-2xs\" aria-busy=\"true\">\n <ed-skeleton class=\"h-3 w-full\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-5/6\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-3/4\"></ed-skeleton>\n </div>\n } @else if (summary()) {\n <p class=\"m-0\">{{ summary() }}</p>\n } @else {\n <p class=\"m-0 text-zinc-500\">Summary will appear when processing completes.</p>\n }\n </div>\n <eds-call-recording-player [callId]=\"callId()\" />\n </section>\n }\n </div>\n\n <aside class=\"live-monitor-page__sidebar flex h-full min-h-0 flex-col gap-4 overflow-auto\">\n @if (!callEnded()) {\n <section class=\"flex flex-col gap-4 rounded-lg bg-white p-4\" aria-labelledby=\"live-call-state-title\">\n <header class=\"flex items-center gap-3\">\n <span class=\"inline-flex items-center justify-center rounded-md bg-fuchsia-500/10 p-1 text-fuchsia-600\">\n <ed-icon name=\"phone_call\" [size]=\"16\" />\n </span>\n <h4 id=\"live-call-state-title\" class=\"m-0\">Call State</h4>\n </header>\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-center justify-between gap-2 text-zinc-600\">\n <span class=\"live-monitor-page__eyebrow\">Agent confidence</span>\n <span class=\"live-monitor-page__eyebrow\">{{ c.confidence }}%</span>\n </div>\n <div class=\"live-monitor-page__confidence-track\">\n <span\n class=\"live-monitor-page__confidence-fill\"\n [style.width.%]=\"c.confidence\"\n [attr.data-level]=\"confidenceLevel(c.confidence)\"\n ></span>\n </div>\n <div class=\"flex items-center gap-2 text-zinc-600\">\n <ed-icon\n [name]=\"confidenceLevel(c.confidence) === 'low' ? 'trend_down' : 'info'\"\n [size]=\"16\"\n />\n <span>{{ confidenceHint(c.confidence) }}</span>\n </div>\n </div>\n\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"live-monitor-page__eyebrow\">Patient sentiment</span>\n <span class=\"vr-sentiment\" [attr.data-sentiment]=\"c.sentiment\">\n <span class=\"vr-sentiment__icon\" aria-hidden=\"true\">\n <img [src]=\"sentimentIcon(c.sentiment)\" alt=\"\" width=\"16\" height=\"16\" />\n </span>\n <span>{{ sentimentLabel(c.sentiment) }}</span>\n </span>\n </div>\n </section>\n }\n\n <section\n class=\"live-monitor-page__sidebar-block flex min-h-0 flex-1 flex-col gap-4 rounded-lg bg-white p-4\"\n aria-labelledby=\"live-goal-timeline-title\"\n >\n <header class=\"flex items-center gap-3\">\n <span class=\"inline-flex items-center justify-center rounded-md bg-blue-500/10 p-1 text-blue-600\">\n <ed-icon name=\"flag\" [size]=\"16\" />\n </span>\n <h4 id=\"live-goal-timeline-title\" class=\"m-0\">Goal Timeline</h4>\n </header>\n\n <div class=\"live-monitor-page__goal-panel flex min-h-0 flex-1 flex-col rounded-lg bg-zinc-50 p-2\">\n @if (c.goal) {\n <p class=\"live-monitor-page__eyebrow mb-3 m-0 text-zinc-900\">{{ c.goal }}</p>\n } @else {\n <p class=\"live-monitor-page__eyebrow mb-3 m-0 text-zinc-900\">No Goal identified yet</p>\n }\n\n <ul class=\"live-monitor-page__tools m-0 flex min-h-0 flex-1 list-none flex-col gap-4 overflow-auto p-0\">\n @for (tool of liveTools(); track toolTrackKey(tool, $index)) {\n <li class=\"flex flex-col gap-1\" [class]=\"toolStateClass(tool.state)\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <span class=\"text-zinc-900\">{{ tool.name }}</span>\n <ed-badge-2 [attr.variant]=\"toolBadgeTone(tool.state)\" pill uppercase>\n {{ toolBadgeLabel(tool.state) }}\n </ed-badge-2>\n </div>\n @if (tool.result) {\n <p class=\"m-0 text-zinc-500\">{{ tool.result }}</p>\n }\n </li>\n } @empty {\n @for (step of goalPlaceholderSteps; track step.title) {\n <li class=\"flex flex-col gap-1\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <span class=\"text-zinc-900\">{{ step.title }}</span>\n <ed-badge-2 [attr.variant]=\"step.tone\" pill uppercase>{{ step.label }}</ed-badge-2>\n </div>\n <p class=\"m-0 text-zinc-500\">{{ step.detail }}</p>\n </li>\n }\n }\n </ul>\n </div>\n </section>\n\n <section class=\"flex flex-col gap-2.5 rounded-lg bg-white p-4\" aria-labelledby=\"live-patient-context-title\">\n <header class=\"flex items-center justify-between gap-3\">\n <div class=\"flex items-center gap-3\">\n <span class=\"inline-flex items-center justify-center rounded-md bg-green-500/10 p-1 text-green-700\">\n <ed-icon name=\"person\" [size]=\"16\" />\n </span>\n <h4 id=\"live-patient-context-title\" class=\"m-0\">Patient Context</h4>\n </div>\n @if (isUnknownCaller(c) || !c.matched) {\n <ed-badge-2 variant=\"primary\" pill uppercase>Asking details\u2026</ed-badge-2>\n }\n </header>\n\n @if (isUnknownCaller(c) || !c.matched) {\n <div\n class=\"flex flex-1 flex-col justify-center gap-6 rounded-lg border border-dashed border-zinc-200 bg-zinc-50 p-4\"\n aria-busy=\"true\"\n >\n <div class=\"flex flex-col gap-2\">\n <ed-skeleton class=\"h-3 w-full !rounded-full\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-3/4 !rounded-full\"></ed-skeleton>\n </div>\n <div class=\"flex flex-col gap-2\">\n <ed-skeleton class=\"h-3 w-full !rounded-full\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-3/4 !rounded-full\"></ed-skeleton>\n </div>\n </div>\n } @else {\n <div class=\"rounded-lg border border-zinc-200 bg-zinc-50 p-4\">\n <p class=\"m-0 text-zinc-900\">{{ displayName(c) }}</p>\n <p class=\"m-0 mt-1 text-zinc-500\">{{ profileMeta(c) }}</p>\n @if (c.phone) {\n <p class=\"m-0 mt-1 text-zinc-500\">{{ c.phone }}</p>\n }\n </div>\n }\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"flex flex-col items-start gap-3\">\n <a\n routerLink=\"..\"\n class=\"live-monitor-page__back inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-md border border-zinc-200 bg-white text-zinc-600 no-underline\"\n aria-label=\"Back to live calls\"\n >\n <ed-icon name=\"arrow_back\" [size]=\"19\" />\n </a>\n <p class=\"m-0 text-zinc-500\">Call ended or not found.</p>\n </div>\n }\n }\n</div>\n", styles: [".vr-sentiment{display:inline-flex;align-items:center;justify-content:center;gap:4px;height:20px;padding:2px 4px;border-radius:6px;font-size:11px;font-weight:500;line-height:16px;white-space:nowrap}.vr-sentiment__icon{display:block;width:16px;height:16px;flex-shrink:0;overflow:hidden}.vr-sentiment__icon img{display:block;width:100%;height:100%;max-width:none}.vr-sentiment[data-sentiment=calm]{background:#3681f41a;color:#2162f0}.vr-sentiment[data-sentiment=neutral]{background:#f0f2f5;color:#41464c}.vr-sentiment[data-sentiment=urgent]{background:#f26b0d1a;color:#e94e0c}.vr-sentiment[data-sentiment=frustrated]{background:#f037401a;color:#db0c15}.live-monitor-page__eyebrow{font-size:10px;font-weight:600;letter-spacing:1px;line-height:1;text-transform:uppercase}.live-monitor-page__confidence-track{height:8px;overflow:hidden;border-radius:9999px;background:linear-gradient(90deg,#7d34fb1a,#e76b171a)}.live-monitor-page__confidence-fill{display:block;height:100%;border-radius:9999px;background:linear-gradient(90deg,#7d34fb,#e76b17)}.live-monitor-page__tools li{position:relative;padding-left:1.25rem}.live-monitor-page__tools li:before{content:\"\";position:absolute;top:.35rem;left:0;width:.5rem;height:.5rem;border-radius:9999px;background:#a2a4a6}\n"] }]
|
|
1110
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, schemas: [CUSTOM_ELEMENTS_SCHEMA], host: { class: 'block h-full min-h-0' }, template: "<div class=\"live-monitor-page box-border flex h-full min-h-0 flex-col overflow-hidden bg-lighter p-base text-zinc-900\">\n @if (loading()) {\n <div class=\"flex min-h-0 flex-1 flex-col gap-4\" aria-busy=\"true\">\n <header class=\"flex shrink-0 items-center justify-between gap-3 rounded-lg bg-white p-3\">\n <div class=\"flex min-w-0 flex-1 items-center gap-4\">\n <ed-skeleton class=\"h-5 w-5 !rounded-sm\"></ed-skeleton>\n <div class=\"flex min-w-0 items-center gap-2\">\n <ed-skeleton class=\"h-9 w-9 !rounded-md\"></ed-skeleton>\n <div class=\"grid min-w-0 gap-2xs\">\n <ed-skeleton class=\"h-4 w-40\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-32\"></ed-skeleton>\n </div>\n </div>\n </div>\n <div class=\"flex shrink-0 items-center gap-3\">\n <ed-skeleton class=\"h-8 w-24 !rounded-md\"></ed-skeleton>\n <ed-skeleton class=\"h-8 w-28 !rounded-md\"></ed-skeleton>\n </div>\n </header>\n <div class=\"grid min-h-0 flex-1 grid-cols-[minmax(0,1fr)_minmax(280px,447px)] items-stretch gap-4 max-[1100px]:grid-cols-1\">\n <div class=\"flex min-h-0 flex-col rounded-xl bg-white p-4\">\n <ed-skeleton class=\"mb-4 h-5 w-36\"></ed-skeleton>\n @for (_ of transcriptSkeletonRows; track _) {\n <div class=\"mb-4 flex flex-col gap-2\">\n <ed-skeleton class=\"h-3 w-24\"></ed-skeleton>\n <ed-skeleton class=\"h-16 w-full max-w-xl !rounded-lg\"></ed-skeleton>\n </div>\n }\n </div>\n <aside class=\"flex min-h-0 flex-col gap-4\">\n <ed-skeleton class=\"h-40 w-full !rounded-lg\"></ed-skeleton>\n <ed-skeleton class=\"min-h-48 w-full flex-1 !rounded-lg\"></ed-skeleton>\n <ed-skeleton class=\"h-48 w-full !rounded-lg\"></ed-skeleton>\n </aside>\n </div>\n </div>\n } @else {\n @if (call(); as c) {\n <header class=\"live-monitor-page__header mb-4 flex shrink-0 flex-wrap items-center justify-between gap-3 rounded-lg bg-white p-3\">\n <div class=\"flex min-w-0 flex-1 items-center gap-4\">\n <a\n routerLink=\"..\"\n class=\"live-monitor-page__back inline-flex h-5 w-5 shrink-0 items-center justify-center text-zinc-600 no-underline\"\n aria-label=\"Back to live calls\"\n >\n <ed-icon name=\"arrow_back\" [size]=\"14\" />\n </a>\n\n <div class=\"live-monitor-page__profile flex min-w-0 items-center gap-2\">\n <ed-avatar\n class=\"shrink-0\"\n shape=\"box\"\n size=\"md\"\n [initials]=\"profileInitials(c)\"\n [tone]=\"isUnknownCaller(c) ? 'gray' : 'warm'\"\n />\n\n <div class=\"min-w-0\">\n <div class=\"flex flex-wrap items-center gap-3\">\n <h4 class=\"m-0\">{{ displayName(c) }}</h4>\n @if (isUnknownCaller(c)) {\n <ed-badge-2 variant=\"primary\" pill uppercase>Unknown</ed-badge-2>\n } @else {\n @if (goalBadge(c); as badge) {\n <ed-badge-2 variant=\"primary\" pill uppercase>{{ badge }}</ed-badge-2>\n }\n }\n </div>\n <p class=\"m-0 mt-0.5 text-zinc-500\">{{ c.phone || profileMeta(c) }}</p>\n </div>\n </div>\n </div>\n\n <div class=\"live-monitor-page__header-actions flex flex-wrap items-center gap-3\">\n @if (!callEnded()) {\n @if (mediaState() === 'connected' && isMediaForThisCall() && !audioPlaybackAllowed()) {\n <ed-button variant=\"secondary\" outline size=\"sm\" (click$)=\"enableSound()\">\n <ed-icon slot=\"prefix\" name=\"mic\" [size]=\"16\" />\n Enable sound\n </ed-button>\n }\n @if (isMediaForThisCall() && (mediaState() === 'connected' || mediaState() === 'connecting')) {\n <span\n class=\"live-monitor-page__media-pill inline-flex h-7 items-center rounded-full border border-zinc-200 bg-zinc-50 px-3 text-zinc-600\"\n [attr.data-mode]=\"mediaMode() || 'idle'\"\n [attr.data-state]=\"mediaState()\"\n >\n @if (mediaState() === 'connecting') {\n Connecting\u2026\n } @else if (mediaMode() === 'takeover') {\n {{ micEnabled() ? 'On call \u00B7 mic on' : 'On call \u00B7 mic off' }}\n } @else {\n Joined\n }\n </span>\n }\n <ed-button\n variant=\"danger\"\n outline\n size=\"sm\"\n [disabled]=\"mediaBusy()\"\n (click$)=\"endCall()\"\n >\n <ed-icon slot=\"prefix\" name=\"call_end\" [size]=\"16\" />\n End Call\n </ed-button>\n @if (isTakeover()) {\n @if (mediaState() === 'connected' && isMediaForThisCall()) {\n <ed-button variant=\"secondary\" outline size=\"sm\" (click$)=\"toggleMic()\">\n <ed-icon slot=\"prefix\" [name]=\"micEnabled() ? 'mic' : 'mic_off'\" [size]=\"16\" />\n {{ micEnabled() ? 'Mute' : 'Unmute' }}\n </ed-button>\n }\n } @else {\n <ed-button\n variant=\"primary\"\n fill\n size=\"sm\"\n [disabled]=\"mediaBusy()\"\n (click$)=\"takeover()\"\n >\n <ed-icon slot=\"prefix\" name=\"phone_call\" [size]=\"16\" />\n Take Over\n </ed-button>\n }\n } @else {\n <span class=\"inline-flex items-center gap-2 font-mono text-zinc-900\">\n <span class=\"h-2 w-2 rounded-full bg-green-500 ring-4 ring-green-500/15\" aria-hidden=\"true\"></span>\n {{ c.started | edsElapsed }}\n </span>\n }\n </div>\n </header>\n\n @if (callEnded()) {\n <div class=\"mb-4 flex items-center gap-2 rounded-lg bg-green-50 px-4 py-3 text-green-700\" role=\"status\">\n <ed-icon name=\"check_circle\" [size]=\"18\" />\n Call ended\n </div>\n }\n\n <div\n class=\"live-monitor-page__grid grid min-h-0 flex-1 grid-cols-[minmax(0,1fr)_360px] items-stretch gap-4 max-[1100px]:grid-cols-1 max-[1100px]:overflow-auto\"\n >\n <div class=\"live-monitor-page__main flex h-full min-h-0 flex-col overflow-hidden\">\n <eds-live-transcript-panel\n [live]=\"isLive()\"\n [paused]=\"isPaused()\"\n [humanTakeover]=\"isTakeover()\"\n [callStarted]=\"c.started\"\n [callerName]=\"c.name\"\n [callerMatched]=\"c.matched\"\n />\n\n @if (callEnded()) {\n <section class=\"live-monitor-page__post-call flex flex-col gap-4\">\n <div class=\"rounded-lg border border-zinc-200 bg-white p-4 text-zinc-600\">\n <h4 class=\"mb-3 m-0\">Call summary</h4>\n @if (summaryLoading()) {\n <div class=\"grid gap-2xs\" aria-busy=\"true\">\n <ed-skeleton class=\"h-3 w-full\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-5/6\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-3/4\"></ed-skeleton>\n </div>\n } @else if (summary()) {\n <p class=\"m-0\">{{ summary() }}</p>\n } @else {\n <p class=\"m-0 text-zinc-500\">Summary will appear when processing completes.</p>\n }\n </div>\n <eds-call-recording-player [callId]=\"callId()\" />\n </section>\n }\n </div>\n\n <aside class=\"live-monitor-page__sidebar flex h-full min-h-0 flex-col gap-4 overflow-hidden\">\n @if (!callEnded()) {\n <section class=\"flex shrink-0 flex-col gap-4 rounded-lg bg-white p-4\" aria-labelledby=\"live-call-state-title\">\n <header class=\"flex items-center gap-3\">\n <span class=\"inline-flex items-center justify-center rounded-md bg-fuchsia-500/10 p-1 text-fuchsia-600\">\n <ed-icon name=\"phone_call\" [size]=\"16\" />\n </span>\n <h4 id=\"live-call-state-title\" class=\"m-0\">Call State</h4>\n </header>\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-center justify-between gap-2 text-zinc-600\">\n <span class=\"live-monitor-page__eyebrow\">Agent confidence</span>\n <span class=\"live-monitor-page__eyebrow\">{{ c.confidence }}%</span>\n </div>\n <div class=\"live-monitor-page__confidence-track\">\n <span\n class=\"live-monitor-page__confidence-fill\"\n [style.width.%]=\"c.confidence\"\n [attr.data-level]=\"confidenceLevel(c.confidence)\"\n ></span>\n </div>\n <div class=\"flex items-center gap-2 text-zinc-600\">\n <ed-icon\n [name]=\"confidenceLevel(c.confidence) === 'low' ? 'trend_down' : 'info'\"\n [size]=\"16\"\n />\n <span>{{ confidenceHint(c.confidence) }}</span>\n </div>\n </div>\n\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"live-monitor-page__eyebrow\">Patient sentiment</span>\n <span class=\"vr-sentiment\" [attr.data-sentiment]=\"c.sentiment\">\n <span class=\"vr-sentiment__icon\" aria-hidden=\"true\">\n <img [src]=\"sentimentIcon(c.sentiment)\" alt=\"\" width=\"16\" height=\"16\" />\n </span>\n <span>{{ sentimentLabel(c.sentiment) }}</span>\n </span>\n </div>\n </section>\n }\n\n <section\n class=\"live-monitor-page__sidebar-block flex min-h-[16rem] flex-1 flex-col gap-4 overflow-hidden rounded-lg bg-white p-4\"\n aria-labelledby=\"live-goal-timeline-title\"\n >\n <header class=\"flex items-center gap-3\">\n <span class=\"inline-flex items-center justify-center rounded-md bg-blue-500/10 p-1 text-blue-600\">\n <ed-icon name=\"flag\" [size]=\"16\" />\n </span>\n <h4 id=\"live-goal-timeline-title\" class=\"m-0\">Goal Timeline</h4>\n </header>\n\n <div class=\"live-monitor-page__goal-panel flex min-h-0 flex-1 flex-col rounded-lg bg-zinc-50 p-2\">\n @if (c.goal) {\n <p class=\"live-monitor-page__eyebrow mb-3 m-0 text-zinc-900\">{{ c.goal }}</p>\n } @else {\n <p class=\"live-monitor-page__eyebrow mb-3 m-0 text-zinc-900\">No Goal identified yet</p>\n }\n\n <ul class=\"live-monitor-page__tools m-0 flex min-h-0 flex-1 list-none flex-col gap-4 overflow-auto p-0\">\n @for (tool of liveTools(); track toolTrackKey(tool, $index)) {\n <li class=\"flex flex-col gap-1\" [class]=\"toolStateClass(tool.state)\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <span class=\"text-zinc-900\">{{ tool.name }}</span>\n <ed-badge-2 [attr.variant]=\"toolBadgeTone(tool.state)\" pill uppercase>\n {{ toolBadgeLabel(tool.state) }}\n </ed-badge-2>\n </div>\n @if (tool.result) {\n <p class=\"m-0 text-zinc-500\">{{ tool.result }}</p>\n }\n </li>\n } @empty {\n @for (step of goalPlaceholderSteps; track step.title) {\n <li class=\"flex flex-col gap-1\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <span class=\"text-zinc-900\">{{ step.title }}</span>\n <ed-badge-2 [attr.variant]=\"step.tone\" pill uppercase>{{ step.label }}</ed-badge-2>\n </div>\n <p class=\"m-0 text-zinc-500\">{{ step.detail }}</p>\n </li>\n }\n }\n </ul>\n </div>\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"flex flex-col items-start gap-3\">\n <a\n routerLink=\"..\"\n class=\"live-monitor-page__back inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-md border border-zinc-200 bg-white text-zinc-600 no-underline\"\n aria-label=\"Back to live calls\"\n >\n <ed-icon name=\"arrow_back\" [size]=\"19\" />\n </a>\n <p class=\"m-0 text-zinc-500\">Call ended or not found.</p>\n </div>\n }\n }\n</div>\n", styles: [".vr-sentiment{display:inline-flex;align-items:center;justify-content:center;gap:4px;height:20px;padding:2px 4px;border-radius:6px;font-size:11px;font-weight:500;line-height:16px;white-space:nowrap}.vr-sentiment__icon{display:block;width:16px;height:16px;flex-shrink:0;overflow:hidden}.vr-sentiment__icon img{display:block;width:100%;height:100%;max-width:none}.vr-sentiment[data-sentiment=calm]{background:#3681f41a;color:#2162f0}.vr-sentiment[data-sentiment=neutral]{background:#f0f2f5;color:#41464c}.vr-sentiment[data-sentiment=urgent]{background:#f26b0d1a;color:#e94e0c}.vr-sentiment[data-sentiment=frustrated]{background:#f037401a;color:#db0c15}.live-monitor-page__eyebrow{font-size:10px;font-weight:600;letter-spacing:1px;line-height:1;text-transform:uppercase}.live-monitor-page__confidence-track{height:8px;overflow:hidden;border-radius:9999px;background:linear-gradient(90deg,#7d34fb1a,#e76b171a)}.live-monitor-page__confidence-fill{display:block;height:100%;border-radius:9999px;background:linear-gradient(90deg,#7d34fb,#e76b17)}.live-monitor-page__sidebar-block{min-height:16rem}.live-monitor-page__tools li{position:relative;padding-left:1.25rem}.live-monitor-page__tools li:before{content:\"\";position:absolute;top:.35rem;left:0;width:.5rem;height:.5rem;border-radius:9999px;background:#a2a4a6}\n"] }]
|
|
1132
1111
|
}], ctorParameters: () => [] });
|
|
1133
1112
|
|
|
1134
1113
|
export { LiveMonitorPageComponent };
|
|
1135
|
-
//# sourceMappingURL=edvak99-voice-receptionist-widget-live-monitor-page.component-
|
|
1114
|
+
//# sourceMappingURL=edvak99-voice-receptionist-widget-live-monitor-page.component-B1lKfw-j.mjs.map
|