@edvak99/voice-receptionist-widget 1.0.0 → 1.1.0
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/README.md +56 -43
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-CoVjr5Y3.mjs +113 -0
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-CoVjr5Y3.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-audit-page.component-ou9oxnJp.mjs +35 -0
- package/fesm2022/edvak99-voice-receptionist-widget-audit-page.component-ou9oxnJp.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-BV7KOhd9.mjs +316 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-BV7KOhd9.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-CWWma-6r.mjs +138 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-CWWma-6r.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-recording-player.component-B2IuwsVH.mjs +110 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-recording-player.component-B2IuwsVH.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-review.service-D-GutOar.mjs +29 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-review.service-D-GutOar.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-review.util-DQLIw7xb.mjs +70 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-review.util-DQLIw7xb.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-B93giErR.mjs +248 -0
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-B93giErR.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-PCnIWeOA.mjs +27 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-PCnIWeOA.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-elapsed.pipe-BrMNsxB5.mjs +136 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-elapsed.pipe-BrMNsxB5.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-DClMfxeY.mjs +2666 -0
- package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-DClMfxeY.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-knowledge-hub.page-BNsM-o3z.mjs +575 -0
- package/fesm2022/edvak99-voice-receptionist-widget-knowledge-hub.page-BNsM-o3z.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-list.service-C2UZJ5wF.mjs +120 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-list.service-C2UZJ5wF.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-DQTVJjsP.mjs +115 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-DQTVJjsP.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-C3iTtVdn.mjs +574 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-C3iTtVdn.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purpose.util-BQECIuJu.mjs +54 -0
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purpose.util-BQECIuJu.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-C10N-x5g.mjs +265 -0
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-C10N-x5g.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-BBE_H2jb.mjs +509 -0
- package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-BBE_H2jb.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-task.util-DdtI0CR3.mjs +52 -0
- package/fesm2022/edvak99-voice-receptionist-widget-task.util-DdtI0CR3.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-COnYcaMT.mjs +237 -0
- package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-COnYcaMT.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-Box7X-Cq.mjs +45 -0
- package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-Box7X-Cq.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget.mjs +1 -41
- package/fesm2022/edvak99-voice-receptionist-widget.mjs.map +1 -1
- package/lib/api-paths.d.ts +11 -0
- package/lib/components/call-conversation-panel/call-conversation-panel.component.d.ts +45 -0
- package/lib/components/call-recording-player/call-recording-player.component.d.ts +29 -0
- package/lib/components/call-tasks-panel/call-tasks-panel.component.d.ts +19 -0
- package/lib/components/live-transcript-panel/live-transcript-panel.component.d.ts +36 -0
- package/lib/components/outbound-dial-modal/outbound-dial-modal.component.d.ts +37 -0
- package/lib/components/outbound-purpose-form-modal/outbound-purpose-form-modal.component.d.ts +38 -0
- package/lib/components/task-detail-drawer/task-detail-drawer.component.d.ts +20 -0
- package/lib/config.d.ts +23 -0
- package/lib/extract-api-rows.d.ts +2 -0
- package/lib/knowledge/components/faq-empty-state/faq-empty-state.component.d.ts +22 -0
- package/lib/knowledge/components/faq-form/faq-form.component.d.ts +27 -0
- package/lib/knowledge/components/faq-search-tester/faq-search-tester.component.d.ts +17 -0
- package/lib/knowledge/mocks/practice-faq.seed.d.ts +2 -0
- package/lib/knowledge/models/practice-faq.model.d.ts +47 -0
- package/lib/knowledge/pages/knowledge-hub.page.d.ts +47 -0
- package/lib/knowledge/services/practice-faq-api.mock.d.ts +16 -0
- package/lib/knowledge/services/practice-faq-api.service.d.ts +22 -0
- package/lib/knowledge/utils/faq.util.d.ts +3 -0
- package/lib/layout/eds-app-rail.component.d.ts +6 -0
- package/lib/layout/eds-app-shell.component.d.ts +5 -0
- package/lib/layout/eds-module-nav.component.d.ts +24 -0
- package/lib/layout/eds-topbar.component.d.ts +29 -0
- package/lib/mocks/api-log.seed.d.ts +5 -0
- package/lib/mocks/outbound-purpose.seed.d.ts +3 -0
- package/lib/mocks/seed-data.d.ts +38 -0
- package/lib/models/ai-receptionist.types.d.ts +257 -0
- package/lib/models/api-dto.types.d.ts +215 -0
- package/lib/models/api-log.types.d.ts +53 -0
- package/lib/models/live-transcript.types.d.ts +26 -0
- package/lib/models/outbound-purpose.types.d.ts +50 -0
- package/lib/pages/analytics-page/analytics-page.component.d.ts +44 -0
- package/lib/pages/audit-page/audit-page.component.d.ts +14 -0
- package/lib/pages/call-history-detail-page/call-history-detail-page.component.d.ts +25 -0
- package/lib/pages/call-history-page/call-history-page.component.d.ts +42 -0
- package/lib/pages/dashboard-page/dashboard-page.component.d.ts +60 -0
- package/lib/pages/live-calls-page/live-calls-page.component.d.ts +37 -0
- package/lib/pages/live-monitor-page/live-monitor-page.component.d.ts +43 -0
- package/lib/pages/outbound-purposes-page/outbound-purposes-page.component.d.ts +24 -0
- package/lib/pages/settings-page/settings-fields.config.d.ts +35 -0
- package/lib/pages/settings-page/settings-page.component.d.ts +50 -0
- package/lib/pages/settings-page/tts-voice-presets.d.ts +7 -0
- package/lib/pages/tasks-page/tasks-page.component.d.ts +35 -0
- package/lib/pages/wizard-page/wizard-page.component.d.ts +17 -0
- package/lib/pipes/eds-elapsed.pipe.d.ts +7 -0
- package/lib/provide-voice-receptionist.d.ts +6 -0
- package/lib/routes.d.ts +8 -0
- package/lib/services/agent-online.service.d.ts +29 -0
- package/lib/services/ai-receptionist-api.mappers.d.ts +45 -0
- package/lib/services/ai-receptionist-api.mock.d.ts +150 -0
- package/lib/services/ai-receptionist-api.service.d.ts +156 -0
- package/lib/services/call-review.service.d.ts +12 -0
- package/lib/services/dashboard-socket.service.d.ts +26 -0
- package/lib/services/live-calls-list.service.d.ts +29 -0
- package/lib/services/live-transcript.service.d.ts +39 -0
- package/lib/shared/copy-button.component.d.ts +14 -0
- package/lib/shared/modal.component.d.ts +20 -0
- package/lib/shared/toast-container.component.d.ts +7 -0
- package/lib/shared/toast.service.d.ts +16 -0
- package/lib/ui/eds-avatar.component.d.ts +8 -0
- package/lib/ui/eds-badge.component.d.ts +7 -0
- package/lib/ui/eds-button.component.d.ts +9 -0
- package/lib/ui/eds-icon.component.d.ts +10 -0
- package/lib/ui/eds-pulse-dot.component.d.ts +5 -0
- package/lib/ui/eds-waveform.component.d.ts +8 -0
- package/lib/utils/call-outcome.util.d.ts +8 -0
- package/lib/utils/call-review.util.d.ts +8 -0
- package/lib/utils/outbound-purpose.util.d.ts +14 -0
- package/lib/utils/task.util.d.ts +10 -0
- package/lib/utils/tool-labels.util.d.ts +14 -0
- package/lib/utils/transcript-timing.util.d.ts +17 -0
- package/lib/utils/welcome-message.util.d.ts +5 -0
- package/package.json +12 -5
- package/public-api.d.ts +6 -2
- package/styles/_index.scss +55 -0
- package/styles/chat-ui.scss +133 -0
- package/styles/eds.scss +328 -0
- package/lib/voice-receptionist-widget.component.d.ts +0 -5
- package/lib/voice-receptionist-widget.service.d.ts +0 -6
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, signal, Injectable, input, ChangeDetectionStrategy, Component, output, effect, ViewChild, DestroyRef } from '@angular/core';
|
|
3
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
4
|
+
import { ActivatedRoute, RouterLink } from '@angular/router';
|
|
5
|
+
import * as i1 from '@angular/forms';
|
|
6
|
+
import { FormsModule } from '@angular/forms';
|
|
7
|
+
import { r as resolveToolKey, d as resolveToolDisplayName, V as VOICE_RECEPTIONIST_CONFIG, b as EdsIconComponent, A as AiReceptionistApiService, t as toolEventTrackKey } from './edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-DClMfxeY.mjs';
|
|
8
|
+
import { D as DashboardSocketService, E as EdsElapsedPipe } from './edvak99-voice-receptionist-widget-eds-elapsed.pipe-BrMNsxB5.mjs';
|
|
9
|
+
import { C as CallRecordingPlayerComponent } from './edvak99-voice-receptionist-widget-call-recording-player.component-B2IuwsVH.mjs';
|
|
10
|
+
import { E as EdsAvatarComponent } from './edvak99-voice-receptionist-widget-eds-avatar.component-PCnIWeOA.mjs';
|
|
11
|
+
|
|
12
|
+
function mapChunkToLine(chunk) {
|
|
13
|
+
const at = Date.parse(chunk.timestamp);
|
|
14
|
+
return {
|
|
15
|
+
who: chunk.role === 'assistant' ? 'ai' : 'patient',
|
|
16
|
+
text: chunk.text,
|
|
17
|
+
at: Number.isFinite(at) ? at : Date.now(),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function formatEventTime(iso) {
|
|
21
|
+
return new Date(iso).toLocaleTimeString('en-US', {
|
|
22
|
+
hour: 'numeric',
|
|
23
|
+
minute: '2-digit',
|
|
24
|
+
second: '2-digit',
|
|
25
|
+
hour12: false,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function mapCallEventToTool(event) {
|
|
29
|
+
const p = event.payload;
|
|
30
|
+
const state = p['state'];
|
|
31
|
+
const rawName = String(p['name'] ?? event.eventType.replace(/^tool_/, ''));
|
|
32
|
+
const tech = String(p['tech'] ?? event.eventType);
|
|
33
|
+
const key = resolveToolKey(p['key'] != null ? String(p['key']) : undefined, rawName, tech);
|
|
34
|
+
return {
|
|
35
|
+
t: formatEventTime(event.occurredAt),
|
|
36
|
+
key,
|
|
37
|
+
name: resolveToolDisplayName(key, rawName),
|
|
38
|
+
tech,
|
|
39
|
+
result: String(p['result'] ?? ''),
|
|
40
|
+
state: state === 'ok' || state === 'warn' || state === 'error' || state === 'active'
|
|
41
|
+
? state
|
|
42
|
+
: 'ok',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function mapLifecycleEventToTool(event) {
|
|
46
|
+
const p = event.payload;
|
|
47
|
+
return {
|
|
48
|
+
t: formatEventTime(event.occurredAt),
|
|
49
|
+
name: event.eventType.replace(/_/g, ' '),
|
|
50
|
+
tech: event.eventType,
|
|
51
|
+
result: String(p['detail'] ?? p['message'] ?? p['result'] ?? ''),
|
|
52
|
+
state: 'ok',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function isDuplicateTranscriptLine(lines, line) {
|
|
56
|
+
return lines.some((l) => l.who === line.who && l.text.trim() === line.text.trim());
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Live call stream over shared Socket.io (`/dashboard` namespace).
|
|
61
|
+
* REST seeds transcript/tools once; socket delivers `new_chunk` + `call_event`.
|
|
62
|
+
*/
|
|
63
|
+
class LiveTranscriptService {
|
|
64
|
+
constructor() {
|
|
65
|
+
this.socket = inject(DashboardSocketService);
|
|
66
|
+
this.config = inject(VOICE_RECEPTIONIST_CONFIG);
|
|
67
|
+
this.lines = signal([]);
|
|
68
|
+
this.patientInterim = signal(null);
|
|
69
|
+
this.aiInterim = signal(null);
|
|
70
|
+
this.tools = signal([]);
|
|
71
|
+
this.connected = this.socket.connected;
|
|
72
|
+
this.error = signal(null);
|
|
73
|
+
this.callEnded = signal(false);
|
|
74
|
+
this.summaryRequested = signal(false);
|
|
75
|
+
this.activeCallId = null;
|
|
76
|
+
this.watching = false;
|
|
77
|
+
this.mockTimer = null;
|
|
78
|
+
this.mockIndex = 0;
|
|
79
|
+
this.chunkHandler = (chunk) => this.handleChunk(chunk);
|
|
80
|
+
this.eventHandler = (event) => this.handleCallEvent(event);
|
|
81
|
+
this.onSocketConnect = () => {
|
|
82
|
+
if (this.activeCallId) {
|
|
83
|
+
this.socket.emit('watch_call', { callId: this.activeCallId });
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
seed(transcript, toolEvents) {
|
|
88
|
+
this.lines.set(transcript.map((line) => ({ ...line })));
|
|
89
|
+
this.tools.set(toolEvents.map((tool) => ({ ...tool })));
|
|
90
|
+
}
|
|
91
|
+
watch(callId, live = true) {
|
|
92
|
+
const id = callId.trim();
|
|
93
|
+
if (!id || !live)
|
|
94
|
+
return;
|
|
95
|
+
const switchingCall = this.activeCallId !== id;
|
|
96
|
+
if (switchingCall) {
|
|
97
|
+
this.unsubscribeSocketEvents();
|
|
98
|
+
this.clearInterim();
|
|
99
|
+
this.callEnded.set(false);
|
|
100
|
+
this.summaryRequested.set(false);
|
|
101
|
+
this.activeCallId = id;
|
|
102
|
+
}
|
|
103
|
+
else if (this.watching) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (this.config.useMockApi) {
|
|
107
|
+
this.watching = true;
|
|
108
|
+
this.startMockStream(id);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (!this.watching) {
|
|
112
|
+
this.socket.acquire();
|
|
113
|
+
this.socket.on('new_chunk', this.chunkHandler);
|
|
114
|
+
this.socket.on('call_event', this.eventHandler);
|
|
115
|
+
this.socket.onConnect(this.onSocketConnect);
|
|
116
|
+
this.watching = true;
|
|
117
|
+
}
|
|
118
|
+
if (this.socket.connected()) {
|
|
119
|
+
this.socket.emit('watch_call', { callId: id });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
stop() {
|
|
123
|
+
this.activeCallId = null;
|
|
124
|
+
this.unsubscribeSocketEvents();
|
|
125
|
+
this.resetStreamState();
|
|
126
|
+
}
|
|
127
|
+
markCompleted() {
|
|
128
|
+
this.callEnded.set(true);
|
|
129
|
+
this.unsubscribeSocketEvents();
|
|
130
|
+
this.clearInterim();
|
|
131
|
+
}
|
|
132
|
+
unsubscribeSocketEvents() {
|
|
133
|
+
this.stopMockStream();
|
|
134
|
+
if (!this.watching)
|
|
135
|
+
return;
|
|
136
|
+
this.socket.off('new_chunk', this.chunkHandler);
|
|
137
|
+
this.socket.off('call_event', this.eventHandler);
|
|
138
|
+
this.socket.offConnect(this.onSocketConnect);
|
|
139
|
+
this.socket.release();
|
|
140
|
+
this.watching = false;
|
|
141
|
+
}
|
|
142
|
+
handleChunk(chunk) {
|
|
143
|
+
if (!chunk?.text?.trim())
|
|
144
|
+
return;
|
|
145
|
+
if (chunk.role === 'user') {
|
|
146
|
+
if (chunk.isFinal) {
|
|
147
|
+
this.patientInterim.set(null);
|
|
148
|
+
this.appendLineIfNew(mapChunkToLine(chunk));
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
this.patientInterim.set(chunk.text);
|
|
152
|
+
}
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (chunk.isFinal) {
|
|
156
|
+
this.aiInterim.set(null);
|
|
157
|
+
this.appendLineIfNew(mapChunkToLine(chunk));
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
this.aiInterim.set(chunk.text);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
handleCallEvent(event) {
|
|
164
|
+
if (!event?.eventType)
|
|
165
|
+
return;
|
|
166
|
+
if (event.eventType.startsWith('tool_')) {
|
|
167
|
+
this.tools.update((prev) => [...prev, mapCallEventToTool(event)]);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (event.eventType === 'call_completed') {
|
|
171
|
+
this.markCompleted();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (event.eventType === 'summary_generated') {
|
|
175
|
+
this.summaryRequested.set(true);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
this.tools.update((prev) => [...prev, mapLifecycleEventToTool(event)]);
|
|
179
|
+
}
|
|
180
|
+
appendLineIfNew(line) {
|
|
181
|
+
this.lines.update((prev) => {
|
|
182
|
+
if (isDuplicateTranscriptLine(prev, line))
|
|
183
|
+
return prev;
|
|
184
|
+
return [...prev, line];
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
startMockStream(callId) {
|
|
188
|
+
this.stopMockStream();
|
|
189
|
+
this.mockIndex = 0;
|
|
190
|
+
const mockChunks = [
|
|
191
|
+
{
|
|
192
|
+
role: 'user',
|
|
193
|
+
text: 'Hi, I need to schedule an appointment.',
|
|
194
|
+
isFinal: false,
|
|
195
|
+
timestamp: new Date().toISOString(),
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
role: 'user',
|
|
199
|
+
text: 'Hi, I need to schedule an appointment.',
|
|
200
|
+
isFinal: true,
|
|
201
|
+
timestamp: new Date().toISOString(),
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
role: 'assistant',
|
|
205
|
+
text: 'Of course — I can help with that. Which provider would you prefer?',
|
|
206
|
+
isFinal: true,
|
|
207
|
+
timestamp: new Date().toISOString(),
|
|
208
|
+
},
|
|
209
|
+
];
|
|
210
|
+
const mockEvents = [
|
|
211
|
+
{
|
|
212
|
+
eventType: 'tool_check_doctor_availability',
|
|
213
|
+
occurredAt: new Date().toISOString(),
|
|
214
|
+
payload: {
|
|
215
|
+
key: 'check_doctor_availability',
|
|
216
|
+
name: 'Checked Doctor Availability',
|
|
217
|
+
tech: 'tool_check_doctor_availability',
|
|
218
|
+
result: 'Found 3 available time slots',
|
|
219
|
+
state: 'ok',
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
];
|
|
223
|
+
this.mockTimer = setInterval(() => {
|
|
224
|
+
if (this.activeCallId !== callId) {
|
|
225
|
+
this.stopMockStream();
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (this.mockIndex < mockChunks.length) {
|
|
229
|
+
this.handleChunk(mockChunks[this.mockIndex]);
|
|
230
|
+
this.mockIndex++;
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (this.mockIndex === mockChunks.length) {
|
|
234
|
+
this.handleCallEvent(mockEvents[0]);
|
|
235
|
+
this.mockIndex++;
|
|
236
|
+
}
|
|
237
|
+
}, 1500);
|
|
238
|
+
}
|
|
239
|
+
stopMockStream() {
|
|
240
|
+
if (this.mockTimer) {
|
|
241
|
+
clearInterval(this.mockTimer);
|
|
242
|
+
this.mockTimer = null;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
clearInterim() {
|
|
246
|
+
this.patientInterim.set(null);
|
|
247
|
+
this.aiInterim.set(null);
|
|
248
|
+
}
|
|
249
|
+
resetStreamState() {
|
|
250
|
+
this.lines.set([]);
|
|
251
|
+
this.tools.set([]);
|
|
252
|
+
this.clearInterim();
|
|
253
|
+
this.error.set(null);
|
|
254
|
+
this.callEnded.set(false);
|
|
255
|
+
this.summaryRequested.set(false);
|
|
256
|
+
}
|
|
257
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LiveTranscriptService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
258
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LiveTranscriptService, providedIn: 'root' }); }
|
|
259
|
+
}
|
|
260
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LiveTranscriptService, decorators: [{
|
|
261
|
+
type: Injectable,
|
|
262
|
+
args: [{ providedIn: 'root' }]
|
|
263
|
+
}] });
|
|
264
|
+
|
|
265
|
+
class EdsWaveformComponent {
|
|
266
|
+
constructor() {
|
|
267
|
+
this.active = input(true);
|
|
268
|
+
this.tone = input('brand');
|
|
269
|
+
this.bars = Array.from({ length: 28 }, (_, i) => i);
|
|
270
|
+
}
|
|
271
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EdsWaveformComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
272
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: EdsWaveformComponent, isStandalone: true, selector: "eds-waveform", inputs: { active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
273
|
+
<div
|
|
274
|
+
class="eds-waveform"
|
|
275
|
+
[class.eds-waveform--inactive]="!active()"
|
|
276
|
+
[attr.data-tone]="tone()"
|
|
277
|
+
aria-hidden="true"
|
|
278
|
+
>
|
|
279
|
+
@for (i of bars; track i) {
|
|
280
|
+
<span class="eds-waveform__bar" [style.animation-delay.ms]="i * 70"></span>
|
|
281
|
+
}
|
|
282
|
+
</div>
|
|
283
|
+
`, isInline: true, styles: [".eds-waveform{display:flex;align-items:flex-end;gap:3px;height:20px}.eds-waveform__bar{width:3px;height:100%;border-radius:3px;transform-origin:bottom;animation:eds-wave 1.1s ease-in-out infinite}.eds-waveform[data-tone=brand] .eds-waveform__bar{background:var(--brand-400, #5aa3f6)}.eds-waveform[data-tone=violet] .eds-waveform__bar{background:var(--violet-400, #a684ff)}.eds-waveform--inactive .eds-waveform__bar{height:4px;opacity:.3;animation:none;align-self:flex-end}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
284
|
+
}
|
|
285
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EdsWaveformComponent, decorators: [{
|
|
286
|
+
type: Component,
|
|
287
|
+
args: [{ selector: 'eds-waveform', standalone: true, template: `
|
|
288
|
+
<div
|
|
289
|
+
class="eds-waveform"
|
|
290
|
+
[class.eds-waveform--inactive]="!active()"
|
|
291
|
+
[attr.data-tone]="tone()"
|
|
292
|
+
aria-hidden="true"
|
|
293
|
+
>
|
|
294
|
+
@for (i of bars; track i) {
|
|
295
|
+
<span class="eds-waveform__bar" [style.animation-delay.ms]="i * 70"></span>
|
|
296
|
+
}
|
|
297
|
+
</div>
|
|
298
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".eds-waveform{display:flex;align-items:flex-end;gap:3px;height:20px}.eds-waveform__bar{width:3px;height:100%;border-radius:3px;transform-origin:bottom;animation:eds-wave 1.1s ease-in-out infinite}.eds-waveform[data-tone=brand] .eds-waveform__bar{background:var(--brand-400, #5aa3f6)}.eds-waveform[data-tone=violet] .eds-waveform__bar{background:var(--violet-400, #a684ff)}.eds-waveform--inactive .eds-waveform__bar{height:4px;opacity:.3;animation:none;align-self:flex-end}\n"] }]
|
|
299
|
+
}] });
|
|
300
|
+
|
|
301
|
+
class LiveTranscriptPanelComponent {
|
|
302
|
+
constructor() {
|
|
303
|
+
this.live = input(true);
|
|
304
|
+
this.paused = input(false);
|
|
305
|
+
this.humanTakeover = input(false);
|
|
306
|
+
this.showStaffInput = input(false);
|
|
307
|
+
this.staffMessage = input('');
|
|
308
|
+
this.staffMessageChange = output();
|
|
309
|
+
this.sendMessage = output();
|
|
310
|
+
this.stream = inject(LiveTranscriptService);
|
|
311
|
+
this.lines = this.stream.lines;
|
|
312
|
+
this.patientInterim = this.stream.patientInterim;
|
|
313
|
+
this.aiInterim = this.stream.aiInterim;
|
|
314
|
+
this.connected = this.stream.connected;
|
|
315
|
+
this.error = this.stream.error;
|
|
316
|
+
this.shouldScroll = false;
|
|
317
|
+
effect(() => {
|
|
318
|
+
this.lines();
|
|
319
|
+
this.patientInterim();
|
|
320
|
+
this.aiInterim();
|
|
321
|
+
this.requestScroll();
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
ngAfterViewChecked() {
|
|
325
|
+
if (this.shouldScroll && this.scrollHost) {
|
|
326
|
+
const el = this.scrollHost.nativeElement;
|
|
327
|
+
el.scrollTop = el.scrollHeight;
|
|
328
|
+
this.shouldScroll = false;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
waveformActive() {
|
|
332
|
+
return this.live() && this.connected() && !this.paused();
|
|
333
|
+
}
|
|
334
|
+
waveformTone() {
|
|
335
|
+
return this.humanTakeover() ? 'violet' : 'brand';
|
|
336
|
+
}
|
|
337
|
+
recLabel() {
|
|
338
|
+
if (this.paused())
|
|
339
|
+
return 'PAUSED';
|
|
340
|
+
if (this.live() && this.connected())
|
|
341
|
+
return 'REC';
|
|
342
|
+
if (this.error())
|
|
343
|
+
return 'ERROR';
|
|
344
|
+
return this.live() ? 'CONNECT' : 'LOG';
|
|
345
|
+
}
|
|
346
|
+
roleLabel(who) {
|
|
347
|
+
switch (who) {
|
|
348
|
+
case 'ai':
|
|
349
|
+
return 'Ava · AI';
|
|
350
|
+
case 'patient':
|
|
351
|
+
return 'Patient';
|
|
352
|
+
case 'staff':
|
|
353
|
+
return 'Staff';
|
|
354
|
+
default:
|
|
355
|
+
return 'System';
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
speakerIcon(who) {
|
|
359
|
+
switch (who) {
|
|
360
|
+
case 'ai':
|
|
361
|
+
return 'graphic_eq';
|
|
362
|
+
case 'staff':
|
|
363
|
+
return 'support_agent';
|
|
364
|
+
case 'patient':
|
|
365
|
+
return 'person';
|
|
366
|
+
default:
|
|
367
|
+
return 'info';
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
systemIcon(flag) {
|
|
371
|
+
if (flag === 'verify')
|
|
372
|
+
return 'verified_user';
|
|
373
|
+
if (flag === 'fail' || flag === 'escalate')
|
|
374
|
+
return 'bolt';
|
|
375
|
+
return 'info';
|
|
376
|
+
}
|
|
377
|
+
systemClass(flag) {
|
|
378
|
+
if (flag === 'verify')
|
|
379
|
+
return 'live-transcript-panel__system--verify';
|
|
380
|
+
if (flag === 'fail' || flag === 'escalate')
|
|
381
|
+
return 'live-transcript-panel__system--warn';
|
|
382
|
+
return 'live-transcript-panel__system--info';
|
|
383
|
+
}
|
|
384
|
+
msgClass(who) {
|
|
385
|
+
return `live-transcript-panel__msg--${who}`;
|
|
386
|
+
}
|
|
387
|
+
avatarClass(who) {
|
|
388
|
+
return `live-transcript-panel__avatar--${who}`;
|
|
389
|
+
}
|
|
390
|
+
onStaffInput(value) {
|
|
391
|
+
this.staffMessageChange.emit(value);
|
|
392
|
+
}
|
|
393
|
+
onSend() {
|
|
394
|
+
this.sendMessage.emit();
|
|
395
|
+
}
|
|
396
|
+
requestScroll() {
|
|
397
|
+
this.shouldScroll = true;
|
|
398
|
+
}
|
|
399
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LiveTranscriptPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
400
|
+
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 }, staffMessage: { classPropertyName: "staffMessage", publicName: "staffMessage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { staffMessageChange: "staffMessageChange", sendMessage: "sendMessage" }, viewQueries: [{ propertyName: "scrollHost", first: true, predicate: ["scrollHost"], descendants: true }], ngImport: i0, template: "<section class=\"live-transcript-panel\" aria-labelledby=\"live-transcript-title\">\n <header class=\"live-transcript-panel__header\">\n <h2 id=\"live-transcript-title\" class=\"live-transcript-panel__title\">Live transcript</h2>\n <div class=\"live-transcript-panel__rec\">\n <eds-waveform [active]=\"waveformActive()\" [tone]=\"waveformTone()\" />\n <span class=\"live-transcript-panel__rec-label\">{{ recLabel() }}</span>\n </div>\n </header>\n\n <div #scrollHost class=\"live-transcript-panel__scroll\" role=\"log\" aria-live=\"polite\">\n @for (line of lines(); track $index) {\n @if (line.who === 'system') {\n <div class=\"live-transcript-panel__system eds-animate-fade-up\" [class]=\"systemClass(line.flag)\">\n <eds-icon [name]=\"systemIcon(line.flag)\" [size]=\"14\" />\n <span>{{ line.text }}</span>\n </div>\n } @else {\n <article class=\"live-transcript-panel__msg eds-animate-fade-up\" [class]=\"msgClass(line.who)\">\n <div class=\"live-transcript-panel__avatar\" [class]=\"avatarClass(line.who)\">\n <eds-icon [name]=\"speakerIcon(line.who)\" [size]=\"16\" />\n </div>\n <div class=\"live-transcript-panel__content\">\n <span class=\"live-transcript-panel__label\">{{ roleLabel(line.who) }}</span>\n <div class=\"live-transcript-panel__bubble\">\n <p>{{ line.text }}</p>\n </div>\n </div>\n </article>\n }\n }\n\n @if (patientInterim(); as interim) {\n <article class=\"live-transcript-panel__msg live-transcript-panel__msg--patient eds-animate-fade-up\">\n <div class=\"live-transcript-panel__avatar live-transcript-panel__avatar--patient\">\n <eds-icon name=\"person\" [size]=\"16\" />\n </div>\n <div class=\"live-transcript-panel__content\">\n <span class=\"live-transcript-panel__label\">Patient \u00B7 listening</span>\n <div class=\"live-transcript-panel__bubble live-transcript-panel__bubble--interim\">\n <p>{{ interim }}</p>\n </div>\n </div>\n </article>\n }\n\n @if (aiInterim(); as interim) {\n <article class=\"live-transcript-panel__msg live-transcript-panel__msg--ai eds-animate-fade-up\">\n <div class=\"live-transcript-panel__avatar live-transcript-panel__avatar--ai\">\n <eds-icon name=\"graphic_eq\" [size]=\"16\" />\n </div>\n <div class=\"live-transcript-panel__content\">\n <span class=\"live-transcript-panel__label\">Ava \u00B7 AI</span>\n <div class=\"live-transcript-panel__bubble live-transcript-panel__bubble--interim\">\n <p>{{ interim }}<span class=\"live-transcript-panel__caret\" aria-hidden=\"true\"></span></p>\n </div>\n </div>\n </article>\n }\n\n @if (lines().length === 0 && !patientInterim() && !aiInterim()) {\n <p class=\"live-transcript-panel__empty\">Waiting for conversation\u2026</p>\n }\n </div>\n\n @if (showStaffInput()) {\n <footer class=\"live-transcript-panel__footer\">\n <input\n type=\"text\"\n class=\"live-transcript-panel__input\"\n placeholder=\"Type to speak as staff\u2026\"\n [ngModel]=\"staffMessage()\"\n (ngModelChange)=\"onStaffInput($event)\"\n (keyup.enter)=\"onSend()\"\n />\n <button type=\"button\" class=\"live-transcript-panel__send\" (click)=\"onSend()\">\n <eds-icon name=\"send\" [size]=\"18\" />\n Send\n </button>\n </footer>\n }\n</section>\n", styles: [".live-transcript-panel{display:flex;flex-direction:column;min-height:0;height:calc(100vh - 234px);min-height:460px;background:var(--slate-900);border:1px solid var(--slate-800);border-radius:var(--radius-lg)}.live-transcript-panel__header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 18px;border-bottom:1px solid var(--slate-800);flex-shrink:0}.live-transcript-panel__title{margin:0;font:600 11px/1 var(--font-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--slate-400)}.live-transcript-panel__rec{display:flex;align-items:center;gap:10px}.live-transcript-panel__rec-label{font:600 11px/1 var(--font-mono);letter-spacing:.06em;color:var(--slate-500)}.live-transcript-panel__scroll{flex:1;overflow:auto;padding:18px;display:flex;flex-direction:column;gap:14px}.live-transcript-panel__empty{margin:0;text-align:center;padding:32px 12px;font:400 13px/1.4 var(--font-body);color:var(--slate-500)}.live-transcript-panel__system{align-self:center;display:inline-flex;align-items:center;gap:6px;padding:6px 12px;border-radius:var(--radius-pill);border:1px solid var(--slate-800);font:500 12px/1 var(--font-mono);max-width:90%;text-align:center}.live-transcript-panel__system--verify{color:var(--green-400);background:#0abf521a;border-color:#0abf524d}.live-transcript-panel__system--warn{color:var(--amber-300);background:#fe9a001a;border-color:#fe9a004d}.live-transcript-panel__system--info{color:var(--slate-300);background:var(--slate-900);border-color:var(--slate-800)}.live-transcript-panel__msg{display:flex;gap:10px;max-width:560px}.live-transcript-panel__msg--ai,.live-transcript-panel__msg--staff{margin-left:auto;flex-direction:row-reverse}.live-transcript-panel__msg--ai .live-transcript-panel__content,.live-transcript-panel__msg--staff .live-transcript-panel__content{align-items:flex-end}.live-transcript-panel__msg--ai .live-transcript-panel__bubble,.live-transcript-panel__msg--staff .live-transcript-panel__bubble{border-radius:12px 4px 12px 12px}.live-transcript-panel__msg--typing .live-transcript-panel__bubble{padding:12px 16px}.live-transcript-panel__avatar{width:30px;height:30px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff}.live-transcript-panel__avatar--ai{background:linear-gradient(135deg,var(--brand-500),var(--violet-500))}.live-transcript-panel__avatar--staff{background:#8e51ff2e;color:var(--violet-400);border:1px solid rgba(142,81,255,.35)}.live-transcript-panel__avatar--patient{background:var(--slate-800);color:var(--slate-400)}.live-transcript-panel__content{display:flex;flex-direction:column;gap:4px;min-width:0}.live-transcript-panel__label{font:600 11px/1 var(--font-mono);letter-spacing:.04em;color:var(--slate-500)}.live-transcript-panel__msg--ai .live-transcript-panel__label{color:var(--brand-400)}.live-transcript-panel__msg--staff .live-transcript-panel__label{color:var(--purple-300)}.live-transcript-panel__bubble{padding:9px 13px;border-radius:4px 12px 12px;background:#3681f41f;border:1px solid rgba(54,129,244,.28)}.live-transcript-panel__bubble p{margin:0;font:400 13px/19px var(--font-sans);color:var(--gray-100)}.live-transcript-panel__bubble--interim{opacity:.85;font-style:italic}.live-transcript-panel__msg--patient .live-transcript-panel__bubble{background:var(--slate-900);border-color:var(--slate-800)}.live-transcript-panel__msg--staff .live-transcript-panel__bubble{background:#8e51ff24;border-color:#8e51ff4d}.live-transcript-panel__msg--staff .live-transcript-panel__bubble p{color:var(--purple-300)}.live-transcript-panel__caret{display:inline-block;width:7px;height:14px;margin-left:2px;vertical-align:text-bottom;background:var(--brand-400);animation:eds-caret 1s step-end infinite}.live-transcript-panel__dots{display:inline-flex;align-items:center;gap:5px;height:14px}.live-transcript-panel__dots span{width:6px;height:6px;border-radius:50%;background:var(--brand-400);animation:eds-blink 1.4s ease-in-out infinite}.live-transcript-panel__dots span:nth-child(2){animation-delay:.2s}.live-transcript-panel__dots span:nth-child(3){animation-delay:.4s}.live-transcript-panel__footer{display:flex;gap:8px;padding:12px;border-top:1px solid var(--slate-800);flex-shrink:0}.live-transcript-panel__input{flex:1;height:38px;padding:0 14px;border-radius:var(--radius-md);border:1px solid var(--slate-700);background:var(--slate-950);color:var(--gray-100);font:400 13px/1 var(--font-sans)}.live-transcript-panel__input::placeholder{color:var(--slate-500)}.live-transcript-panel__input:focus{outline:none;border-color:var(--violet-400)}.live-transcript-panel__send{display:inline-flex;align-items:center;gap:6px;height:38px;padding:0 14px;border:none;border-radius:var(--radius-md);background:#8e51ff38;color:var(--purple-300);font:600 13px/1 var(--font-sans);cursor:pointer}.live-transcript-panel__send:hover{background:var(--violet-500);color:#fff}@media(max-width:900px){.live-transcript-panel{height:auto;min-height:360px;max-height:60vh}}\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: EdsIconComponent, selector: "eds-icon", inputs: ["name", "size", "fill", "weight"] }, { kind: "component", type: EdsWaveformComponent, selector: "eds-waveform", inputs: ["active", "tone"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
401
|
+
}
|
|
402
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LiveTranscriptPanelComponent, decorators: [{
|
|
403
|
+
type: Component,
|
|
404
|
+
args: [{ selector: 'eds-live-transcript-panel', imports: [FormsModule, EdsIconComponent, EdsWaveformComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"live-transcript-panel\" aria-labelledby=\"live-transcript-title\">\n <header class=\"live-transcript-panel__header\">\n <h2 id=\"live-transcript-title\" class=\"live-transcript-panel__title\">Live transcript</h2>\n <div class=\"live-transcript-panel__rec\">\n <eds-waveform [active]=\"waveformActive()\" [tone]=\"waveformTone()\" />\n <span class=\"live-transcript-panel__rec-label\">{{ recLabel() }}</span>\n </div>\n </header>\n\n <div #scrollHost class=\"live-transcript-panel__scroll\" role=\"log\" aria-live=\"polite\">\n @for (line of lines(); track $index) {\n @if (line.who === 'system') {\n <div class=\"live-transcript-panel__system eds-animate-fade-up\" [class]=\"systemClass(line.flag)\">\n <eds-icon [name]=\"systemIcon(line.flag)\" [size]=\"14\" />\n <span>{{ line.text }}</span>\n </div>\n } @else {\n <article class=\"live-transcript-panel__msg eds-animate-fade-up\" [class]=\"msgClass(line.who)\">\n <div class=\"live-transcript-panel__avatar\" [class]=\"avatarClass(line.who)\">\n <eds-icon [name]=\"speakerIcon(line.who)\" [size]=\"16\" />\n </div>\n <div class=\"live-transcript-panel__content\">\n <span class=\"live-transcript-panel__label\">{{ roleLabel(line.who) }}</span>\n <div class=\"live-transcript-panel__bubble\">\n <p>{{ line.text }}</p>\n </div>\n </div>\n </article>\n }\n }\n\n @if (patientInterim(); as interim) {\n <article class=\"live-transcript-panel__msg live-transcript-panel__msg--patient eds-animate-fade-up\">\n <div class=\"live-transcript-panel__avatar live-transcript-panel__avatar--patient\">\n <eds-icon name=\"person\" [size]=\"16\" />\n </div>\n <div class=\"live-transcript-panel__content\">\n <span class=\"live-transcript-panel__label\">Patient \u00B7 listening</span>\n <div class=\"live-transcript-panel__bubble live-transcript-panel__bubble--interim\">\n <p>{{ interim }}</p>\n </div>\n </div>\n </article>\n }\n\n @if (aiInterim(); as interim) {\n <article class=\"live-transcript-panel__msg live-transcript-panel__msg--ai eds-animate-fade-up\">\n <div class=\"live-transcript-panel__avatar live-transcript-panel__avatar--ai\">\n <eds-icon name=\"graphic_eq\" [size]=\"16\" />\n </div>\n <div class=\"live-transcript-panel__content\">\n <span class=\"live-transcript-panel__label\">Ava \u00B7 AI</span>\n <div class=\"live-transcript-panel__bubble live-transcript-panel__bubble--interim\">\n <p>{{ interim }}<span class=\"live-transcript-panel__caret\" aria-hidden=\"true\"></span></p>\n </div>\n </div>\n </article>\n }\n\n @if (lines().length === 0 && !patientInterim() && !aiInterim()) {\n <p class=\"live-transcript-panel__empty\">Waiting for conversation\u2026</p>\n }\n </div>\n\n @if (showStaffInput()) {\n <footer class=\"live-transcript-panel__footer\">\n <input\n type=\"text\"\n class=\"live-transcript-panel__input\"\n placeholder=\"Type to speak as staff\u2026\"\n [ngModel]=\"staffMessage()\"\n (ngModelChange)=\"onStaffInput($event)\"\n (keyup.enter)=\"onSend()\"\n />\n <button type=\"button\" class=\"live-transcript-panel__send\" (click)=\"onSend()\">\n <eds-icon name=\"send\" [size]=\"18\" />\n Send\n </button>\n </footer>\n }\n</section>\n", styles: [".live-transcript-panel{display:flex;flex-direction:column;min-height:0;height:calc(100vh - 234px);min-height:460px;background:var(--slate-900);border:1px solid var(--slate-800);border-radius:var(--radius-lg)}.live-transcript-panel__header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 18px;border-bottom:1px solid var(--slate-800);flex-shrink:0}.live-transcript-panel__title{margin:0;font:600 11px/1 var(--font-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--slate-400)}.live-transcript-panel__rec{display:flex;align-items:center;gap:10px}.live-transcript-panel__rec-label{font:600 11px/1 var(--font-mono);letter-spacing:.06em;color:var(--slate-500)}.live-transcript-panel__scroll{flex:1;overflow:auto;padding:18px;display:flex;flex-direction:column;gap:14px}.live-transcript-panel__empty{margin:0;text-align:center;padding:32px 12px;font:400 13px/1.4 var(--font-body);color:var(--slate-500)}.live-transcript-panel__system{align-self:center;display:inline-flex;align-items:center;gap:6px;padding:6px 12px;border-radius:var(--radius-pill);border:1px solid var(--slate-800);font:500 12px/1 var(--font-mono);max-width:90%;text-align:center}.live-transcript-panel__system--verify{color:var(--green-400);background:#0abf521a;border-color:#0abf524d}.live-transcript-panel__system--warn{color:var(--amber-300);background:#fe9a001a;border-color:#fe9a004d}.live-transcript-panel__system--info{color:var(--slate-300);background:var(--slate-900);border-color:var(--slate-800)}.live-transcript-panel__msg{display:flex;gap:10px;max-width:560px}.live-transcript-panel__msg--ai,.live-transcript-panel__msg--staff{margin-left:auto;flex-direction:row-reverse}.live-transcript-panel__msg--ai .live-transcript-panel__content,.live-transcript-panel__msg--staff .live-transcript-panel__content{align-items:flex-end}.live-transcript-panel__msg--ai .live-transcript-panel__bubble,.live-transcript-panel__msg--staff .live-transcript-panel__bubble{border-radius:12px 4px 12px 12px}.live-transcript-panel__msg--typing .live-transcript-panel__bubble{padding:12px 16px}.live-transcript-panel__avatar{width:30px;height:30px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff}.live-transcript-panel__avatar--ai{background:linear-gradient(135deg,var(--brand-500),var(--violet-500))}.live-transcript-panel__avatar--staff{background:#8e51ff2e;color:var(--violet-400);border:1px solid rgba(142,81,255,.35)}.live-transcript-panel__avatar--patient{background:var(--slate-800);color:var(--slate-400)}.live-transcript-panel__content{display:flex;flex-direction:column;gap:4px;min-width:0}.live-transcript-panel__label{font:600 11px/1 var(--font-mono);letter-spacing:.04em;color:var(--slate-500)}.live-transcript-panel__msg--ai .live-transcript-panel__label{color:var(--brand-400)}.live-transcript-panel__msg--staff .live-transcript-panel__label{color:var(--purple-300)}.live-transcript-panel__bubble{padding:9px 13px;border-radius:4px 12px 12px;background:#3681f41f;border:1px solid rgba(54,129,244,.28)}.live-transcript-panel__bubble p{margin:0;font:400 13px/19px var(--font-sans);color:var(--gray-100)}.live-transcript-panel__bubble--interim{opacity:.85;font-style:italic}.live-transcript-panel__msg--patient .live-transcript-panel__bubble{background:var(--slate-900);border-color:var(--slate-800)}.live-transcript-panel__msg--staff .live-transcript-panel__bubble{background:#8e51ff24;border-color:#8e51ff4d}.live-transcript-panel__msg--staff .live-transcript-panel__bubble p{color:var(--purple-300)}.live-transcript-panel__caret{display:inline-block;width:7px;height:14px;margin-left:2px;vertical-align:text-bottom;background:var(--brand-400);animation:eds-caret 1s step-end infinite}.live-transcript-panel__dots{display:inline-flex;align-items:center;gap:5px;height:14px}.live-transcript-panel__dots span{width:6px;height:6px;border-radius:50%;background:var(--brand-400);animation:eds-blink 1.4s ease-in-out infinite}.live-transcript-panel__dots span:nth-child(2){animation-delay:.2s}.live-transcript-panel__dots span:nth-child(3){animation-delay:.4s}.live-transcript-panel__footer{display:flex;gap:8px;padding:12px;border-top:1px solid var(--slate-800);flex-shrink:0}.live-transcript-panel__input{flex:1;height:38px;padding:0 14px;border-radius:var(--radius-md);border:1px solid var(--slate-700);background:var(--slate-950);color:var(--gray-100);font:400 13px/1 var(--font-sans)}.live-transcript-panel__input::placeholder{color:var(--slate-500)}.live-transcript-panel__input:focus{outline:none;border-color:var(--violet-400)}.live-transcript-panel__send{display:inline-flex;align-items:center;gap:6px;height:38px;padding:0 14px;border:none;border-radius:var(--radius-md);background:#8e51ff38;color:var(--purple-300);font:600 13px/1 var(--font-sans);cursor:pointer}.live-transcript-panel__send:hover{background:var(--violet-500);color:#fff}@media(max-width:900px){.live-transcript-panel{height:auto;min-height:360px;max-height:60vh}}\n"] }]
|
|
405
|
+
}], ctorParameters: () => [], propDecorators: { scrollHost: [{
|
|
406
|
+
type: ViewChild,
|
|
407
|
+
args: ['scrollHost']
|
|
408
|
+
}] } });
|
|
409
|
+
|
|
410
|
+
class LiveMonitorPageComponent {
|
|
411
|
+
constructor() {
|
|
412
|
+
this.api = inject(AiReceptionistApiService);
|
|
413
|
+
this.route = inject(ActivatedRoute);
|
|
414
|
+
this.stream = inject(LiveTranscriptService);
|
|
415
|
+
this.destroyRef = inject(DestroyRef);
|
|
416
|
+
this.call = signal(null);
|
|
417
|
+
this.staffMessage = signal('');
|
|
418
|
+
this.loading = signal(true);
|
|
419
|
+
this.callId = signal('');
|
|
420
|
+
this.summary = signal(null);
|
|
421
|
+
this.summaryLoading = signal(false);
|
|
422
|
+
this.liveTools = this.stream.tools;
|
|
423
|
+
this.callEnded = this.stream.callEnded;
|
|
424
|
+
this.summaryLoaded = false;
|
|
425
|
+
this.toolTrackKey = toolEventTrackKey;
|
|
426
|
+
effect(() => {
|
|
427
|
+
if (this.stream.callEnded()) {
|
|
428
|
+
this.loadCallMetadataOnce();
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
effect(() => {
|
|
432
|
+
if (this.stream.summaryRequested() && !this.summaryLoaded) {
|
|
433
|
+
this.loadPostCallSummary();
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
ngOnInit() {
|
|
438
|
+
this.route.paramMap.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => {
|
|
439
|
+
const id = params.get('id') ?? '';
|
|
440
|
+
this.callId.set(id);
|
|
441
|
+
this.loading.set(true);
|
|
442
|
+
this.summary.set(null);
|
|
443
|
+
this.summaryLoaded = false;
|
|
444
|
+
this.stream.stop();
|
|
445
|
+
this.loadCallMetadata();
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
ngOnDestroy() {
|
|
449
|
+
this.stream.stop();
|
|
450
|
+
}
|
|
451
|
+
isTakeover() {
|
|
452
|
+
const c = this.call();
|
|
453
|
+
return c?.status === 'takeover' || !!c?.assigned;
|
|
454
|
+
}
|
|
455
|
+
isPaused() {
|
|
456
|
+
return !!this.call()?.aiPaused;
|
|
457
|
+
}
|
|
458
|
+
isLive() {
|
|
459
|
+
return !!this.call() && !this.callEnded();
|
|
460
|
+
}
|
|
461
|
+
confidenceLevel(confidence) {
|
|
462
|
+
if (confidence >= 70)
|
|
463
|
+
return 'high';
|
|
464
|
+
if (confidence < 50)
|
|
465
|
+
return 'low';
|
|
466
|
+
return 'mid';
|
|
467
|
+
}
|
|
468
|
+
sentimentPercent(sentiment) {
|
|
469
|
+
const map = {
|
|
470
|
+
calm: 85,
|
|
471
|
+
neutral: 60,
|
|
472
|
+
frustrated: 35,
|
|
473
|
+
urgent: 15,
|
|
474
|
+
};
|
|
475
|
+
return map[sentiment];
|
|
476
|
+
}
|
|
477
|
+
toolStateClass(state) {
|
|
478
|
+
return `live-monitor-page__tool--${state}`;
|
|
479
|
+
}
|
|
480
|
+
takeover() {
|
|
481
|
+
this.api.postTakeover(this.callId()).subscribe(() => this.refreshMetadata());
|
|
482
|
+
}
|
|
483
|
+
resumeAi() {
|
|
484
|
+
this.api.postResumeAi(this.callId()).subscribe(() => this.refreshMetadata());
|
|
485
|
+
}
|
|
486
|
+
transfer() {
|
|
487
|
+
this.api.postTransfer(this.callId()).subscribe(() => this.refreshMetadata());
|
|
488
|
+
}
|
|
489
|
+
endCall() {
|
|
490
|
+
const id = this.callId();
|
|
491
|
+
this.api.postEnd(id).subscribe(() => {
|
|
492
|
+
this.stream.markCompleted();
|
|
493
|
+
this.loadCallMetadataOnce();
|
|
494
|
+
this.loadPostCallSummary();
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
sendMessage() {
|
|
498
|
+
const text = this.staffMessage().trim();
|
|
499
|
+
if (!text)
|
|
500
|
+
return;
|
|
501
|
+
this.api.postMessage(this.callId(), text).subscribe(() => {
|
|
502
|
+
this.staffMessage.set('');
|
|
503
|
+
this.refreshMetadata();
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
/** Initial load — seeds transcript/tools from REST, then starts socket. */
|
|
507
|
+
loadCallMetadata() {
|
|
508
|
+
const id = this.callId();
|
|
509
|
+
if (!id)
|
|
510
|
+
return;
|
|
511
|
+
this.api.getLiveCall(id).subscribe(({ data }) => {
|
|
512
|
+
this.call.set(data);
|
|
513
|
+
this.loading.set(false);
|
|
514
|
+
if (data) {
|
|
515
|
+
this.stream.seed(data.transcript, data.tools);
|
|
516
|
+
if (!this.callEnded()) {
|
|
517
|
+
this.stream.watch(id, true);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
/** Metadata-only refresh after staff actions — does not overwrite live transcript/tools. */
|
|
523
|
+
refreshMetadata() {
|
|
524
|
+
const id = this.callId();
|
|
525
|
+
if (!id)
|
|
526
|
+
return;
|
|
527
|
+
this.api.getLiveCall(id).subscribe(({ data }) => {
|
|
528
|
+
if (data)
|
|
529
|
+
this.call.set(data);
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
/** One-time metadata refresh after call ends (no socket). */
|
|
533
|
+
loadCallMetadataOnce() {
|
|
534
|
+
const id = this.callId();
|
|
535
|
+
if (!id)
|
|
536
|
+
return;
|
|
537
|
+
this.api.getLiveCall(id).subscribe(({ data }) => {
|
|
538
|
+
if (data) {
|
|
539
|
+
this.call.set(data);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
loadPostCallSummary() {
|
|
544
|
+
const id = this.callId();
|
|
545
|
+
if (!id || this.summaryLoaded)
|
|
546
|
+
return;
|
|
547
|
+
this.summaryLoading.set(true);
|
|
548
|
+
this.api.getCallSummary(id).subscribe(({ data }) => {
|
|
549
|
+
this.summaryLoading.set(false);
|
|
550
|
+
this.summaryLoaded = true;
|
|
551
|
+
if (!data)
|
|
552
|
+
return;
|
|
553
|
+
this.summary.set(data.summary);
|
|
554
|
+
this.stream.seed(data.transcript, data.tools);
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LiveMonitorPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
558
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: LiveMonitorPageComponent, isStandalone: true, selector: "eds-live-monitor-page", ngImport: i0, template: "<div class=\"eds-page eds-page--dark live-monitor-page\">\n @if (loading()) {\n <p class=\"live-monitor-page__loading\">Loading call\u2026</p>\n } @else {\n @if (call(); as c) {\n <header class=\"live-monitor-page__header\">\n <a routerLink=\"../live\" class=\"live-monitor-page__back\" aria-label=\"Back to live calls\">\n <eds-icon name=\"arrow_back\" [size]=\"19\" />\n </a>\n\n <div class=\"live-monitor-page__header-main\">\n <eds-avatar\n [initials]=\"c.initials\"\n tone=\"brand\"\n [gradient]=\"c.matched\"\n [class.live-monitor-page__avatar--ring]=\"c.matched\"\n />\n\n <div class=\"live-monitor-page__caller\">\n <div class=\"live-monitor-page__name-row\">\n <h1 class=\"live-monitor-page__name\">{{ c.name }}</h1>\n <span class=\"live-monitor-page__patient-tag\" [attr.data-kind]=\"c.matched ? 'registered' : 'new'\">\n <eds-icon [name]=\"c.matched ? 'person_check' : 'person_add'\" [size]=\"14\" />\n {{ c.matched ? 'Registered' : 'New' }}\n </span>\n </div>\n <div class=\"live-monitor-page__meta-row\">\n <eds-icon [name]=\"c.direction === 'inbound' ? 'call_received' : 'call_made'\" [size]=\"14\" />\n <span>{{ c.phone }}</span>\n <span class=\"live-monitor-page__sep\">\u00B7</span>\n <span>{{ c.reason }}</span>\n </div>\n </div>\n\n <div class=\"live-monitor-page__header-right\">\n <span class=\"live-monitor-page__duration\">\n <span class=\"live-monitor-page__duration-dot\" aria-hidden=\"true\"></span>\n {{ c.started | edsElapsed }}\n </span>\n </div>\n </div>\n </header>\n\n @if (callEnded()) {\n <div class=\"live-monitor-page__completed-banner\" role=\"status\">\n <eds-icon name=\"check_circle\" [size]=\"18\" />\n Call ended\n </div>\n }\n\n <div class=\"live-monitor-page__grid\">\n <div class=\"live-monitor-page__main\">\n <eds-live-transcript-panel\n [live]=\"isLive()\"\n [paused]=\"isPaused()\"\n [humanTakeover]=\"isTakeover()\"\n [showStaffInput]=\"isTakeover() && !callEnded()\"\n [staffMessage]=\"staffMessage()\"\n (staffMessageChange)=\"staffMessage.set($event)\"\n (sendMessage)=\"sendMessage()\"\n />\n\n @if (callEnded()) {\n <section class=\"live-monitor-page__post-call\">\n <div class=\"live-monitor-page__summary\">\n <h2 class=\"live-monitor-page__section-title\">Call summary</h2>\n @if (summaryLoading()) {\n <p class=\"live-monitor-page__muted\">Generating summary\u2026</p>\n } @else if (summary()) {\n <p>{{ summary() }}</p>\n } @else {\n <p class=\"live-monitor-page__muted\">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\">\n @if (!callEnded()) {\n <div class=\"live-monitor-page__orb-wrap\" [attr.data-mode]=\"c.status === 'transferring' ? 'transfer' : 'ai'\">\n <span class=\"live-monitor-page__ring live-monitor-page__ring--1\"></span>\n <span class=\"live-monitor-page__ring live-monitor-page__ring--2\"></span>\n <span class=\"live-monitor-page__orb\">\n <eds-icon name=\"smart_toy\" [size]=\"28\" />\n </span>\n <span class=\"live-monitor-page__confidence\" [attr.data-level]=\"confidenceLevel(c.confidence)\">\n AI confidence {{ c.confidence }}%\n </span>\n </div>\n\n <section class=\"live-monitor-page__sidebar-block\">\n <h3 class=\"live-monitor-page__section-title\">Patient sentiment</h3>\n <div class=\"live-monitor-page__sentiment-bar\">\n <span\n class=\"live-monitor-page__sentiment-fill\"\n [style.width.%]=\"sentimentPercent(c.sentiment)\"\n [attr.data-sentiment]=\"c.sentiment\"\n ></span>\n </div>\n <span class=\"live-monitor-page__sentiment-label\">{{ c.sentiment }}</span>\n </section>\n }\n\n <section class=\"live-monitor-page__sidebar-block\">\n <h3 class=\"live-monitor-page__section-title\">Tools used</h3>\n <ul class=\"live-monitor-page__tools\">\n @for (tool of liveTools(); track toolTrackKey(tool, $index)) {\n <li [class]=\"toolStateClass(tool.state)\">\n <span class=\"live-monitor-page__tool-time\">{{ tool.t }}</span>\n <div class=\"live-monitor-page__tool-body\">\n <span class=\"live-monitor-page__tool-name\">{{ tool.name }}</span>\n <span class=\"live-monitor-page__tool-result\">{{ tool.result }}</span>\n </div>\n @if (tool.state === 'active') {\n <eds-icon name=\"sync\" [size]=\"16\" class=\"live-monitor-page__tool-spin\" />\n }\n </li>\n } @empty {\n <li class=\"live-monitor-page__tools-empty\">No tool activity yet.</li>\n }\n </ul>\n </section>\n\n <section class=\"live-monitor-page__sidebar-block\">\n <h3 class=\"live-monitor-page__section-title\">Goal</h3>\n <p class=\"live-monitor-page__goal\">{{ c.goal }}</p>\n </section>\n </aside>\n </div>\n\n @if (!callEnded()) {\n <footer class=\"live-monitor-page__control-bar\">\n <div class=\"live-monitor-page__control-group\">\n @if (isTakeover()) {\n <button type=\"button\" class=\"live-monitor-page__ctrl live-monitor-page__ctrl--violet\" (click)=\"resumeAi()\">\n <eds-icon name=\"smart_toy\" [size]=\"18\" /> Resume AI\n </button>\n } @else {\n <button type=\"button\" class=\"live-monitor-page__ctrl live-monitor-page__ctrl--primary\" (click)=\"takeover()\">\n <eds-icon name=\"pan_tool_alt\" [size]=\"18\" /> Take over\n </button>\n }\n <button type=\"button\" class=\"live-monitor-page__ctrl live-monitor-page__ctrl--neutral\" disabled title=\"Coming soon\">\n <eds-icon name=\"hearing\" [size]=\"18\" /> Listen\n </button>\n <button type=\"button\" class=\"live-monitor-page__ctrl live-monitor-page__ctrl--neutral\" (click)=\"transfer()\">\n <eds-icon name=\"phone_forwarded\" [size]=\"18\" /> Transfer\n </button>\n </div>\n\n <span class=\"live-monitor-page__divider\" aria-hidden=\"true\"></span>\n\n <div class=\"live-monitor-page__control-group\">\n <button type=\"button\" class=\"live-monitor-page__ctrl live-monitor-page__ctrl--neutral\" disabled title=\"Coming soon\">\n <eds-icon name=\"person_search\" [size]=\"18\" /> Open chart\n </button>\n <button type=\"button\" class=\"live-monitor-page__ctrl live-monitor-page__ctrl--neutral\" disabled title=\"Coming soon\">\n <eds-icon name=\"add_task\" [size]=\"18\" /> Create task\n </button>\n <button type=\"button\" class=\"live-monitor-page__ctrl live-monitor-page__ctrl--neutral\" disabled title=\"Coming soon\">\n <eds-icon name=\"edit_note\" [size]=\"18\" /> Add note\n </button>\n </div>\n\n <button type=\"button\" class=\"live-monitor-page__ctrl live-monitor-page__ctrl--danger\" (click)=\"endCall()\">\n <eds-icon name=\"call_end\" [size]=\"18\" /> End call\n </button>\n </footer>\n }\n } @else {\n <p class=\"live-monitor-page__loading\">Call ended or not found.</p>\n }\n }\n</div>\n", styles: [".live-monitor-page{display:flex;flex-direction:column;min-height:calc(100vh - var(--eds-topbar-height));padding:var(--space-5);color:var(--gray-200)}.live-monitor-page__loading{font:400 13px/1.4 var(--font-body);color:var(--slate-400)}.live-monitor-page__header{display:flex;align-items:flex-start;gap:12px;margin-bottom:var(--space-4)}.live-monitor-page__back{width:36px;height:36px;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--radius-md);background:var(--slate-900);border:1px solid var(--slate-800);color:var(--gray-300);text-decoration:none;flex-shrink:0}.live-monitor-page__back:hover{background:var(--slate-800)}.live-monitor-page__header-main{flex:1;display:flex;align-items:center;gap:14px;min-width:0;flex-wrap:wrap}.live-monitor-page__avatar--ring{box-shadow:0 0 0 2px var(--brand-500)}.live-monitor-page__caller{flex:1;min-width:200px}.live-monitor-page__name-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.live-monitor-page__name{margin:0;font:700 18px/22px var(--font-sans);color:var(--gray-100)}.live-monitor-page__patient-tag{display:inline-flex;align-items:center;gap:4px;padding:4px 10px;border-radius:var(--radius-pill);font:500 11px/1 var(--font-mono);border:1px solid transparent}.live-monitor-page__patient-tag[data-kind=registered]{color:var(--green-400);background:#0abf5224;border-color:#0abf5247}.live-monitor-page__patient-tag[data-kind=new]{color:var(--brand-300);background:#3681f424;border-color:#3681f44d}.live-monitor-page__meta-row{display:flex;align-items:center;gap:6px;margin-top:4px;font:500 12px/1 var(--font-mono);color:var(--slate-400);flex-wrap:wrap}.live-monitor-page__sep{color:var(--slate-600)}.live-monitor-page__header-right{display:flex;flex-direction:column;align-items:flex-end;gap:4px;margin-left:auto}.live-monitor-page__duration{display:inline-flex;align-items:center;gap:8px;font:700 22px/1 var(--font-mono);color:var(--gray-100)}.live-monitor-page__duration-dot{width:9px;height:9px;border-radius:50%;background:var(--green-400);box-shadow:0 0 0 3px #0abf5224}.live-monitor-page__completed-banner{display:flex;align-items:center;gap:8px;padding:10px 14px;margin-bottom:var(--space-4);border-radius:var(--radius-lg);background:#0abf521f;color:var(--green-400);font:400 13px/1.4 var(--font-body)}.live-monitor-page__grid{display:grid;grid-template-columns:1fr 340px;gap:var(--space-4);flex:1;min-height:0}.live-monitor-page__main{display:flex;flex-direction:column;gap:var(--space-4);min-height:0}.live-monitor-page__main-tabs{display:flex;gap:4px;padding:4px;border-radius:var(--radius-md);background:var(--slate-900);border:1px solid var(--slate-800);width:fit-content}.live-monitor-page__main-tab{border:none;background:transparent;color:var(--slate-400);font:600 12px/1 var(--font-body);padding:8px 14px;border-radius:var(--radius-sm);cursor:pointer}.live-monitor-page__main-tab--active{background:var(--slate-800);color:var(--gray-100)}.live-monitor-page__api-logs{flex:1;min-height:280px;padding:16px;background:var(--slate-900);border:1px solid var(--slate-800)}.live-monitor-page__post-call{display:flex;flex-direction:column;gap:var(--space-4)}.live-monitor-page__summary{padding:16px;border-radius:var(--radius-lg);background:var(--slate-900);border:1px solid var(--slate-800);font:400 13px/1.5 var(--font-body);color:var(--slate-300)}.live-monitor-page__muted{color:var(--slate-500);margin:0}.live-monitor-page__sidebar{display:flex;flex-direction:column;gap:var(--space-4);min-height:0}.live-monitor-page__sidebar-block{padding:16px;border-radius:var(--radius-lg);background:var(--slate-900);border:1px solid var(--slate-800)}.live-monitor-page__section-title{margin:0 0 12px;font:600 11px/1 var(--font-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--slate-400)}.live-monitor-page__orb-wrap{display:flex;flex-direction:column;align-items:center;gap:10px;padding:20px 16px;border-radius:var(--radius-lg);background:var(--slate-900);border:1px solid var(--slate-800);position:relative}.live-monitor-page__orb{width:64px;height:64px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff;background:linear-gradient(135deg,var(--brand-500),var(--violet-500));animation:eds-breathe 3s ease-in-out infinite;position:relative;z-index:1}.live-monitor-page__ring{position:absolute;width:64px;height:64px;top:20px;border-radius:50%;border:2px solid var(--brand-400);animation:eds-ring 2s ease-out infinite}.live-monitor-page__ring--2{animation-delay:1s}.live-monitor-page__orb-wrap[data-mode=transfer] .live-monitor-page__orb{background:linear-gradient(135deg,var(--amber-400),var(--violet-500))}.live-monitor-page__confidence{font:500 12px/1 var(--font-mono);color:var(--slate-400)}.live-monitor-page__confidence[data-level=high]{color:var(--green-400)}.live-monitor-page__confidence[data-level=low]{color:var(--amber-400)}.live-monitor-page__sentiment-bar{height:6px;background:var(--slate-800);border-radius:3px;overflow:hidden;margin-bottom:8px}.live-monitor-page__sentiment-fill{display:block;height:100%;border-radius:3px;background:var(--green-400);transition:width .3s ease}.live-monitor-page__sentiment-fill[data-sentiment=frustrated],.live-monitor-page__sentiment-fill[data-sentiment=urgent]{background:var(--red-400)}.live-monitor-page__sentiment-fill[data-sentiment=neutral]{background:var(--amber-400)}.live-monitor-page__sentiment-label{font:500 12px/1 var(--font-mono);color:var(--slate-400);text-transform:capitalize}.live-monitor-page__tools{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px;max-height:280px;overflow:auto}.live-monitor-page__tools li{display:grid;grid-template-columns:auto 1fr auto;gap:10px;align-items:start;padding:10px;border-radius:var(--radius-md);background:var(--slate-950);border-left:3px solid var(--slate-700)}.live-monitor-page__tool-time{font:500 11px/1 var(--font-mono);color:var(--slate-500);white-space:nowrap}.live-monitor-page__tool-body{display:flex;flex-direction:column;gap:2px;min-width:0}.live-monitor-page__tool-name{font:600 13px/1.2 var(--font-sans);color:var(--gray-100)}.live-monitor-page__tool-result{font:400 12px/1.4 var(--font-body);color:var(--slate-400)}.live-monitor-page__tool-tech{font:500 10px/1 var(--font-mono);color:var(--slate-500)}.live-monitor-page__tool-spin{animation:eds-spin 1s linear infinite;color:var(--brand-400)}.live-monitor-page__tool--ok{border-left-color:var(--green-400)}.live-monitor-page__tool--warn{border-left-color:var(--amber-400)}.live-monitor-page__tool--error{border-left-color:var(--red-400)}.live-monitor-page__tool--active{border-left-color:var(--brand-400)}.live-monitor-page__tools-empty{border-left:none!important;background:transparent!important;padding:8px 0!important;font:400 13px/1.4 var(--font-body);color:var(--slate-500)}.live-monitor-page__goal{margin:0;font:400 13px/1.5 var(--font-body);color:var(--slate-300)}.live-monitor-page__control-bar{position:sticky;bottom:0;display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:var(--space-4);padding:12px 16px;border-radius:var(--radius-lg);background:var(--slate-900);border:1px solid var(--slate-800);box-shadow:0 -8px 24px #03071780}.live-monitor-page__control-group{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.live-monitor-page__divider{width:1px;height:24px;background:var(--slate-800);flex-shrink:0}.live-monitor-page__ctrl{display:inline-flex;align-items:center;gap:6px;height:38px;padding:0 14px;border:none;border-radius:var(--radius-md);font:600 13px/1 var(--font-sans);cursor:pointer;white-space:nowrap}.live-monitor-page__ctrl:disabled{opacity:.45;cursor:not-allowed}.live-monitor-page__ctrl--neutral{background:var(--slate-800);color:var(--gray-200)}.live-monitor-page__ctrl--neutral:hover:not(:disabled){background:var(--slate-700)}.live-monitor-page__ctrl--primary{background:var(--brand-600);color:#fff}.live-monitor-page__ctrl--primary:hover:not(:disabled){background:var(--brand-500)}.live-monitor-page__ctrl--violet{background:#8e51ff2e;color:var(--purple-300)}.live-monitor-page__ctrl--violet:hover:not(:disabled){background:var(--violet-500);color:#fff}.live-monitor-page__ctrl--danger{margin-left:auto;background:var(--red-500);color:#fff}.live-monitor-page__ctrl--danger:hover:not(:disabled){background:var(--red-400)}@media(max-width:960px){.live-monitor-page__grid{grid-template-columns:1fr}.live-monitor-page__header-right{align-items:flex-start;width:100%;margin-left:0}.live-monitor-page__ctrl--danger{margin-left:0;width:100%;justify-content:center}}\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: EdsAvatarComponent, selector: "eds-avatar", inputs: ["initials", "tone", "gradient"] }, { kind: "component", type: EdsIconComponent, selector: "eds-icon", inputs: ["name", "size", "fill", "weight"] }, { kind: "pipe", type: EdsElapsedPipe, name: "edsElapsed" }, { kind: "component", type: LiveTranscriptPanelComponent, selector: "eds-live-transcript-panel", inputs: ["live", "paused", "humanTakeover", "showStaffInput", "staffMessage"], outputs: ["staffMessageChange", "sendMessage"] }, { kind: "component", type: CallRecordingPlayerComponent, selector: "eds-call-recording-player", inputs: ["callId"], outputs: ["currentTimeChange", "durationChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
559
|
+
}
|
|
560
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LiveMonitorPageComponent, decorators: [{
|
|
561
|
+
type: Component,
|
|
562
|
+
args: [{ selector: 'eds-live-monitor-page', imports: [
|
|
563
|
+
RouterLink,
|
|
564
|
+
FormsModule,
|
|
565
|
+
EdsAvatarComponent,
|
|
566
|
+
EdsIconComponent,
|
|
567
|
+
EdsElapsedPipe,
|
|
568
|
+
LiveTranscriptPanelComponent,
|
|
569
|
+
CallRecordingPlayerComponent,
|
|
570
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"eds-page eds-page--dark live-monitor-page\">\n @if (loading()) {\n <p class=\"live-monitor-page__loading\">Loading call\u2026</p>\n } @else {\n @if (call(); as c) {\n <header class=\"live-monitor-page__header\">\n <a routerLink=\"../live\" class=\"live-monitor-page__back\" aria-label=\"Back to live calls\">\n <eds-icon name=\"arrow_back\" [size]=\"19\" />\n </a>\n\n <div class=\"live-monitor-page__header-main\">\n <eds-avatar\n [initials]=\"c.initials\"\n tone=\"brand\"\n [gradient]=\"c.matched\"\n [class.live-monitor-page__avatar--ring]=\"c.matched\"\n />\n\n <div class=\"live-monitor-page__caller\">\n <div class=\"live-monitor-page__name-row\">\n <h1 class=\"live-monitor-page__name\">{{ c.name }}</h1>\n <span class=\"live-monitor-page__patient-tag\" [attr.data-kind]=\"c.matched ? 'registered' : 'new'\">\n <eds-icon [name]=\"c.matched ? 'person_check' : 'person_add'\" [size]=\"14\" />\n {{ c.matched ? 'Registered' : 'New' }}\n </span>\n </div>\n <div class=\"live-monitor-page__meta-row\">\n <eds-icon [name]=\"c.direction === 'inbound' ? 'call_received' : 'call_made'\" [size]=\"14\" />\n <span>{{ c.phone }}</span>\n <span class=\"live-monitor-page__sep\">\u00B7</span>\n <span>{{ c.reason }}</span>\n </div>\n </div>\n\n <div class=\"live-monitor-page__header-right\">\n <span class=\"live-monitor-page__duration\">\n <span class=\"live-monitor-page__duration-dot\" aria-hidden=\"true\"></span>\n {{ c.started | edsElapsed }}\n </span>\n </div>\n </div>\n </header>\n\n @if (callEnded()) {\n <div class=\"live-monitor-page__completed-banner\" role=\"status\">\n <eds-icon name=\"check_circle\" [size]=\"18\" />\n Call ended\n </div>\n }\n\n <div class=\"live-monitor-page__grid\">\n <div class=\"live-monitor-page__main\">\n <eds-live-transcript-panel\n [live]=\"isLive()\"\n [paused]=\"isPaused()\"\n [humanTakeover]=\"isTakeover()\"\n [showStaffInput]=\"isTakeover() && !callEnded()\"\n [staffMessage]=\"staffMessage()\"\n (staffMessageChange)=\"staffMessage.set($event)\"\n (sendMessage)=\"sendMessage()\"\n />\n\n @if (callEnded()) {\n <section class=\"live-monitor-page__post-call\">\n <div class=\"live-monitor-page__summary\">\n <h2 class=\"live-monitor-page__section-title\">Call summary</h2>\n @if (summaryLoading()) {\n <p class=\"live-monitor-page__muted\">Generating summary\u2026</p>\n } @else if (summary()) {\n <p>{{ summary() }}</p>\n } @else {\n <p class=\"live-monitor-page__muted\">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\">\n @if (!callEnded()) {\n <div class=\"live-monitor-page__orb-wrap\" [attr.data-mode]=\"c.status === 'transferring' ? 'transfer' : 'ai'\">\n <span class=\"live-monitor-page__ring live-monitor-page__ring--1\"></span>\n <span class=\"live-monitor-page__ring live-monitor-page__ring--2\"></span>\n <span class=\"live-monitor-page__orb\">\n <eds-icon name=\"smart_toy\" [size]=\"28\" />\n </span>\n <span class=\"live-monitor-page__confidence\" [attr.data-level]=\"confidenceLevel(c.confidence)\">\n AI confidence {{ c.confidence }}%\n </span>\n </div>\n\n <section class=\"live-monitor-page__sidebar-block\">\n <h3 class=\"live-monitor-page__section-title\">Patient sentiment</h3>\n <div class=\"live-monitor-page__sentiment-bar\">\n <span\n class=\"live-monitor-page__sentiment-fill\"\n [style.width.%]=\"sentimentPercent(c.sentiment)\"\n [attr.data-sentiment]=\"c.sentiment\"\n ></span>\n </div>\n <span class=\"live-monitor-page__sentiment-label\">{{ c.sentiment }}</span>\n </section>\n }\n\n <section class=\"live-monitor-page__sidebar-block\">\n <h3 class=\"live-monitor-page__section-title\">Tools used</h3>\n <ul class=\"live-monitor-page__tools\">\n @for (tool of liveTools(); track toolTrackKey(tool, $index)) {\n <li [class]=\"toolStateClass(tool.state)\">\n <span class=\"live-monitor-page__tool-time\">{{ tool.t }}</span>\n <div class=\"live-monitor-page__tool-body\">\n <span class=\"live-monitor-page__tool-name\">{{ tool.name }}</span>\n <span class=\"live-monitor-page__tool-result\">{{ tool.result }}</span>\n </div>\n @if (tool.state === 'active') {\n <eds-icon name=\"sync\" [size]=\"16\" class=\"live-monitor-page__tool-spin\" />\n }\n </li>\n } @empty {\n <li class=\"live-monitor-page__tools-empty\">No tool activity yet.</li>\n }\n </ul>\n </section>\n\n <section class=\"live-monitor-page__sidebar-block\">\n <h3 class=\"live-monitor-page__section-title\">Goal</h3>\n <p class=\"live-monitor-page__goal\">{{ c.goal }}</p>\n </section>\n </aside>\n </div>\n\n @if (!callEnded()) {\n <footer class=\"live-monitor-page__control-bar\">\n <div class=\"live-monitor-page__control-group\">\n @if (isTakeover()) {\n <button type=\"button\" class=\"live-monitor-page__ctrl live-monitor-page__ctrl--violet\" (click)=\"resumeAi()\">\n <eds-icon name=\"smart_toy\" [size]=\"18\" /> Resume AI\n </button>\n } @else {\n <button type=\"button\" class=\"live-monitor-page__ctrl live-monitor-page__ctrl--primary\" (click)=\"takeover()\">\n <eds-icon name=\"pan_tool_alt\" [size]=\"18\" /> Take over\n </button>\n }\n <button type=\"button\" class=\"live-monitor-page__ctrl live-monitor-page__ctrl--neutral\" disabled title=\"Coming soon\">\n <eds-icon name=\"hearing\" [size]=\"18\" /> Listen\n </button>\n <button type=\"button\" class=\"live-monitor-page__ctrl live-monitor-page__ctrl--neutral\" (click)=\"transfer()\">\n <eds-icon name=\"phone_forwarded\" [size]=\"18\" /> Transfer\n </button>\n </div>\n\n <span class=\"live-monitor-page__divider\" aria-hidden=\"true\"></span>\n\n <div class=\"live-monitor-page__control-group\">\n <button type=\"button\" class=\"live-monitor-page__ctrl live-monitor-page__ctrl--neutral\" disabled title=\"Coming soon\">\n <eds-icon name=\"person_search\" [size]=\"18\" /> Open chart\n </button>\n <button type=\"button\" class=\"live-monitor-page__ctrl live-monitor-page__ctrl--neutral\" disabled title=\"Coming soon\">\n <eds-icon name=\"add_task\" [size]=\"18\" /> Create task\n </button>\n <button type=\"button\" class=\"live-monitor-page__ctrl live-monitor-page__ctrl--neutral\" disabled title=\"Coming soon\">\n <eds-icon name=\"edit_note\" [size]=\"18\" /> Add note\n </button>\n </div>\n\n <button type=\"button\" class=\"live-monitor-page__ctrl live-monitor-page__ctrl--danger\" (click)=\"endCall()\">\n <eds-icon name=\"call_end\" [size]=\"18\" /> End call\n </button>\n </footer>\n }\n } @else {\n <p class=\"live-monitor-page__loading\">Call ended or not found.</p>\n }\n }\n</div>\n", styles: [".live-monitor-page{display:flex;flex-direction:column;min-height:calc(100vh - var(--eds-topbar-height));padding:var(--space-5);color:var(--gray-200)}.live-monitor-page__loading{font:400 13px/1.4 var(--font-body);color:var(--slate-400)}.live-monitor-page__header{display:flex;align-items:flex-start;gap:12px;margin-bottom:var(--space-4)}.live-monitor-page__back{width:36px;height:36px;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--radius-md);background:var(--slate-900);border:1px solid var(--slate-800);color:var(--gray-300);text-decoration:none;flex-shrink:0}.live-monitor-page__back:hover{background:var(--slate-800)}.live-monitor-page__header-main{flex:1;display:flex;align-items:center;gap:14px;min-width:0;flex-wrap:wrap}.live-monitor-page__avatar--ring{box-shadow:0 0 0 2px var(--brand-500)}.live-monitor-page__caller{flex:1;min-width:200px}.live-monitor-page__name-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.live-monitor-page__name{margin:0;font:700 18px/22px var(--font-sans);color:var(--gray-100)}.live-monitor-page__patient-tag{display:inline-flex;align-items:center;gap:4px;padding:4px 10px;border-radius:var(--radius-pill);font:500 11px/1 var(--font-mono);border:1px solid transparent}.live-monitor-page__patient-tag[data-kind=registered]{color:var(--green-400);background:#0abf5224;border-color:#0abf5247}.live-monitor-page__patient-tag[data-kind=new]{color:var(--brand-300);background:#3681f424;border-color:#3681f44d}.live-monitor-page__meta-row{display:flex;align-items:center;gap:6px;margin-top:4px;font:500 12px/1 var(--font-mono);color:var(--slate-400);flex-wrap:wrap}.live-monitor-page__sep{color:var(--slate-600)}.live-monitor-page__header-right{display:flex;flex-direction:column;align-items:flex-end;gap:4px;margin-left:auto}.live-monitor-page__duration{display:inline-flex;align-items:center;gap:8px;font:700 22px/1 var(--font-mono);color:var(--gray-100)}.live-monitor-page__duration-dot{width:9px;height:9px;border-radius:50%;background:var(--green-400);box-shadow:0 0 0 3px #0abf5224}.live-monitor-page__completed-banner{display:flex;align-items:center;gap:8px;padding:10px 14px;margin-bottom:var(--space-4);border-radius:var(--radius-lg);background:#0abf521f;color:var(--green-400);font:400 13px/1.4 var(--font-body)}.live-monitor-page__grid{display:grid;grid-template-columns:1fr 340px;gap:var(--space-4);flex:1;min-height:0}.live-monitor-page__main{display:flex;flex-direction:column;gap:var(--space-4);min-height:0}.live-monitor-page__main-tabs{display:flex;gap:4px;padding:4px;border-radius:var(--radius-md);background:var(--slate-900);border:1px solid var(--slate-800);width:fit-content}.live-monitor-page__main-tab{border:none;background:transparent;color:var(--slate-400);font:600 12px/1 var(--font-body);padding:8px 14px;border-radius:var(--radius-sm);cursor:pointer}.live-monitor-page__main-tab--active{background:var(--slate-800);color:var(--gray-100)}.live-monitor-page__api-logs{flex:1;min-height:280px;padding:16px;background:var(--slate-900);border:1px solid var(--slate-800)}.live-monitor-page__post-call{display:flex;flex-direction:column;gap:var(--space-4)}.live-monitor-page__summary{padding:16px;border-radius:var(--radius-lg);background:var(--slate-900);border:1px solid var(--slate-800);font:400 13px/1.5 var(--font-body);color:var(--slate-300)}.live-monitor-page__muted{color:var(--slate-500);margin:0}.live-monitor-page__sidebar{display:flex;flex-direction:column;gap:var(--space-4);min-height:0}.live-monitor-page__sidebar-block{padding:16px;border-radius:var(--radius-lg);background:var(--slate-900);border:1px solid var(--slate-800)}.live-monitor-page__section-title{margin:0 0 12px;font:600 11px/1 var(--font-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--slate-400)}.live-monitor-page__orb-wrap{display:flex;flex-direction:column;align-items:center;gap:10px;padding:20px 16px;border-radius:var(--radius-lg);background:var(--slate-900);border:1px solid var(--slate-800);position:relative}.live-monitor-page__orb{width:64px;height:64px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff;background:linear-gradient(135deg,var(--brand-500),var(--violet-500));animation:eds-breathe 3s ease-in-out infinite;position:relative;z-index:1}.live-monitor-page__ring{position:absolute;width:64px;height:64px;top:20px;border-radius:50%;border:2px solid var(--brand-400);animation:eds-ring 2s ease-out infinite}.live-monitor-page__ring--2{animation-delay:1s}.live-monitor-page__orb-wrap[data-mode=transfer] .live-monitor-page__orb{background:linear-gradient(135deg,var(--amber-400),var(--violet-500))}.live-monitor-page__confidence{font:500 12px/1 var(--font-mono);color:var(--slate-400)}.live-monitor-page__confidence[data-level=high]{color:var(--green-400)}.live-monitor-page__confidence[data-level=low]{color:var(--amber-400)}.live-monitor-page__sentiment-bar{height:6px;background:var(--slate-800);border-radius:3px;overflow:hidden;margin-bottom:8px}.live-monitor-page__sentiment-fill{display:block;height:100%;border-radius:3px;background:var(--green-400);transition:width .3s ease}.live-monitor-page__sentiment-fill[data-sentiment=frustrated],.live-monitor-page__sentiment-fill[data-sentiment=urgent]{background:var(--red-400)}.live-monitor-page__sentiment-fill[data-sentiment=neutral]{background:var(--amber-400)}.live-monitor-page__sentiment-label{font:500 12px/1 var(--font-mono);color:var(--slate-400);text-transform:capitalize}.live-monitor-page__tools{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px;max-height:280px;overflow:auto}.live-monitor-page__tools li{display:grid;grid-template-columns:auto 1fr auto;gap:10px;align-items:start;padding:10px;border-radius:var(--radius-md);background:var(--slate-950);border-left:3px solid var(--slate-700)}.live-monitor-page__tool-time{font:500 11px/1 var(--font-mono);color:var(--slate-500);white-space:nowrap}.live-monitor-page__tool-body{display:flex;flex-direction:column;gap:2px;min-width:0}.live-monitor-page__tool-name{font:600 13px/1.2 var(--font-sans);color:var(--gray-100)}.live-monitor-page__tool-result{font:400 12px/1.4 var(--font-body);color:var(--slate-400)}.live-monitor-page__tool-tech{font:500 10px/1 var(--font-mono);color:var(--slate-500)}.live-monitor-page__tool-spin{animation:eds-spin 1s linear infinite;color:var(--brand-400)}.live-monitor-page__tool--ok{border-left-color:var(--green-400)}.live-monitor-page__tool--warn{border-left-color:var(--amber-400)}.live-monitor-page__tool--error{border-left-color:var(--red-400)}.live-monitor-page__tool--active{border-left-color:var(--brand-400)}.live-monitor-page__tools-empty{border-left:none!important;background:transparent!important;padding:8px 0!important;font:400 13px/1.4 var(--font-body);color:var(--slate-500)}.live-monitor-page__goal{margin:0;font:400 13px/1.5 var(--font-body);color:var(--slate-300)}.live-monitor-page__control-bar{position:sticky;bottom:0;display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:var(--space-4);padding:12px 16px;border-radius:var(--radius-lg);background:var(--slate-900);border:1px solid var(--slate-800);box-shadow:0 -8px 24px #03071780}.live-monitor-page__control-group{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.live-monitor-page__divider{width:1px;height:24px;background:var(--slate-800);flex-shrink:0}.live-monitor-page__ctrl{display:inline-flex;align-items:center;gap:6px;height:38px;padding:0 14px;border:none;border-radius:var(--radius-md);font:600 13px/1 var(--font-sans);cursor:pointer;white-space:nowrap}.live-monitor-page__ctrl:disabled{opacity:.45;cursor:not-allowed}.live-monitor-page__ctrl--neutral{background:var(--slate-800);color:var(--gray-200)}.live-monitor-page__ctrl--neutral:hover:not(:disabled){background:var(--slate-700)}.live-monitor-page__ctrl--primary{background:var(--brand-600);color:#fff}.live-monitor-page__ctrl--primary:hover:not(:disabled){background:var(--brand-500)}.live-monitor-page__ctrl--violet{background:#8e51ff2e;color:var(--purple-300)}.live-monitor-page__ctrl--violet:hover:not(:disabled){background:var(--violet-500);color:#fff}.live-monitor-page__ctrl--danger{margin-left:auto;background:var(--red-500);color:#fff}.live-monitor-page__ctrl--danger:hover:not(:disabled){background:var(--red-400)}@media(max-width:960px){.live-monitor-page__grid{grid-template-columns:1fr}.live-monitor-page__header-right{align-items:flex-start;width:100%;margin-left:0}.live-monitor-page__ctrl--danger{margin-left:0;width:100%;justify-content:center}}\n"] }]
|
|
571
|
+
}], ctorParameters: () => [] });
|
|
572
|
+
|
|
573
|
+
export { LiveMonitorPageComponent };
|
|
574
|
+
//# sourceMappingURL=edvak99-voice-receptionist-widget-live-monitor-page.component-C3iTtVdn.mjs.map
|