@edvak99/voice-receptionist-widget 1.1.3 → 1.1.4

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.
Files changed (126) hide show
  1. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-DT9UKvQO.mjs +307 -0
  2. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-DT9UKvQO.mjs.map +1 -0
  3. package/fesm2022/{edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-Dn0GrZ6_.mjs → edvak99-voice-receptionist-widget-ai-receptionist-api.service-48cifVPF.mjs} +1275 -561
  4. package/fesm2022/edvak99-voice-receptionist-widget-ai-receptionist-api.service-48cifVPF.mjs.map +1 -0
  5. package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-C5inEHJw.mjs +278 -0
  6. package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-C5inEHJw.mjs.map +1 -0
  7. package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-sYhM20CE.mjs +418 -0
  8. package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-sYhM20CE.mjs.map +1 -0
  9. package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-DpwajEOE.mjs +194 -0
  10. package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-DpwajEOE.mjs.map +1 -0
  11. package/fesm2022/{edvak99-voice-receptionist-widget-call-recording-player.component-B8_qp7Fe.mjs → edvak99-voice-receptionist-widget-call-recording-player.component-CgkRcFOl.mjs} +3 -2
  12. package/fesm2022/{edvak99-voice-receptionist-widget-call-recording-player.component-B8_qp7Fe.mjs.map → edvak99-voice-receptionist-widget-call-recording-player.component-CgkRcFOl.mjs.map} +1 -1
  13. package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-Cj2XTeK4.mjs → edvak99-voice-receptionist-widget-call-review.service-CIMcjGKX.mjs} +2 -2
  14. package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-Cj2XTeK4.mjs.map → edvak99-voice-receptionist-widget-call-review.service-CIMcjGKX.mjs.map} +1 -1
  15. package/fesm2022/edvak99-voice-receptionist-widget-call-review.util-Deh3jPgo.mjs +21 -0
  16. package/fesm2022/edvak99-voice-receptionist-widget-call-review.util-Deh3jPgo.mjs.map +1 -0
  17. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-CxbO0eDS.mjs +440 -0
  18. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-CxbO0eDS.mjs.map +1 -0
  19. package/fesm2022/{edvak99-voice-receptionist-widget-eds-elapsed.pipe-BgLsnq6A.mjs → edvak99-voice-receptionist-widget-dashboard-socket.service-BX91r74O.mjs} +5 -21
  20. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-socket.service-BX91r74O.mjs.map +1 -0
  21. package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-CWRiJGfv.mjs +38 -0
  22. package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-CWRiJGfv.mjs.map +1 -0
  23. package/fesm2022/edvak99-voice-receptionist-widget-eds-button.component-Bg5T5BN8.mjs +27 -0
  24. package/fesm2022/edvak99-voice-receptionist-widget-eds-button.component-Bg5T5BN8.mjs.map +1 -0
  25. package/fesm2022/edvak99-voice-receptionist-widget-eds-elapsed.pipe-BFCiJOT3.mjs +22 -0
  26. package/fesm2022/edvak99-voice-receptionist-widget-eds-elapsed.pipe-BFCiJOT3.mjs.map +1 -0
  27. package/fesm2022/{edvak99-voice-receptionist-widget-live-calls-list.service-0kGWO6sh.mjs → edvak99-voice-receptionist-widget-eds-pulse-dot.component-BPY6xN7C.mjs} +15 -5
  28. package/fesm2022/edvak99-voice-receptionist-widget-eds-pulse-dot.component-BPY6xN7C.mjs.map +1 -0
  29. package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-BsBQIS_f.mjs +203 -0
  30. package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-BsBQIS_f.mjs.map +1 -0
  31. package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-CaSDMxpV.mjs +81 -0
  32. package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-CaSDMxpV.mjs.map +1 -0
  33. package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-Cy3ND-c-.mjs +1070 -0
  34. package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-Cy3ND-c-.mjs.map +1 -0
  35. package/fesm2022/edvak99-voice-receptionist-widget-modal.component-COfi7biN.mjs +48 -0
  36. package/fesm2022/edvak99-voice-receptionist-widget-modal.component-COfi7biN.mjs.map +1 -0
  37. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purpose.util-Bdh4QfFe.mjs +127 -0
  38. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purpose.util-Bdh4QfFe.mjs.map +1 -0
  39. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-fHVzpiVk.mjs +268 -0
  40. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-fHVzpiVk.mjs.map +1 -0
  41. package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-Bok64X2q.mjs +968 -0
  42. package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-Bok64X2q.mjs.map +1 -0
  43. package/fesm2022/{edvak99-voice-receptionist-widget-tasks-page.component-DXxjMVUv.mjs → edvak99-voice-receptionist-widget-tasks-page.component-Bb9lPcYi.mjs} +56 -5
  44. package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-Bb9lPcYi.mjs.map +1 -0
  45. package/fesm2022/{edvak99-voice-receptionist-widget-wizard-page.component-C1Z6yC0q.mjs → edvak99-voice-receptionist-widget-wizard-page.component-BZVlWLWh.mjs} +4 -2
  46. package/fesm2022/{edvak99-voice-receptionist-widget-wizard-page.component-C1Z6yC0q.mjs.map → edvak99-voice-receptionist-widget-wizard-page.component-BZVlWLWh.mjs.map} +1 -1
  47. package/fesm2022/edvak99-voice-receptionist-widget.mjs +1 -1
  48. package/lib/components/call-conversation-panel/call-conversation-panel.component.d.ts +9 -6
  49. package/lib/components/call-tasks-panel/call-tasks-panel.component.d.ts +8 -8
  50. package/lib/components/knowledge-test-modal/knowledge-test-modal.component.d.ts +28 -0
  51. package/lib/components/live-transcript-panel/live-transcript-panel.component.d.ts +7 -4
  52. package/lib/components/outbound-dial-modal/outbound-dial-modal.component.d.ts +26 -0
  53. package/lib/layout/eds-module-nav.component.d.ts +0 -3
  54. package/lib/mocks/agent-activity.seed.d.ts +2 -0
  55. package/lib/models/agent-activity.types.d.ts +54 -0
  56. package/lib/models/ai-receptionist.types.d.ts +25 -0
  57. package/lib/models/api-dto.types.d.ts +18 -0
  58. package/lib/models/live-transcript.types.d.ts +2 -1
  59. package/lib/models/outbound-patient.types.d.ts +23 -0
  60. package/lib/models/outbound-purpose.types.d.ts +2 -0
  61. package/lib/models/staff-media.types.d.ts +36 -0
  62. package/lib/pages/activities-page/activities-page.component.d.ts +57 -0
  63. package/lib/pages/analytics-page/analytics-page.component.d.ts +38 -14
  64. package/lib/pages/call-history-detail-page/call-history-detail-page.component.d.ts +2 -3
  65. package/lib/pages/call-history-page/call-history-page.component.d.ts +9 -6
  66. package/lib/pages/dashboard-page/dashboard-page.component.d.ts +12 -8
  67. package/lib/pages/live-calls-page/live-calls-page.component.d.ts +14 -24
  68. package/lib/pages/live-monitor-page/live-monitor-page.component.d.ts +34 -1
  69. package/lib/pages/settings-page/settings-fields.config.d.ts +16 -0
  70. package/lib/pages/settings-page/settings-page.component.d.ts +48 -14
  71. package/lib/services/ai-receptionist-api.mock.d.ts +34 -0
  72. package/lib/services/ai-receptionist-api.service.d.ts +56 -1
  73. package/lib/services/live-transcript.service.d.ts +1 -0
  74. package/lib/services/staff-media.service.d.ts +52 -0
  75. package/lib/ui/eds-avatar.component.d.ts +5 -2
  76. package/lib/ui/eds-button.component.d.ts +1 -1
  77. package/lib/utils/call-history-display.util.d.ts +21 -0
  78. package/lib/utils/outbound-purpose.util.d.ts +19 -0
  79. package/lib/utils/transcript-timing.util.d.ts +1 -1
  80. package/lib/utils/welcome-message.util.d.ts +1 -1
  81. package/package.json +2 -1
  82. package/styles/eds.scss +95 -66
  83. package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-Dsujn5az.mjs +0 -113
  84. package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-Dsujn5az.mjs.map +0 -1
  85. package/fesm2022/edvak99-voice-receptionist-widget-audit-page.component-xoLlHj8K.mjs +0 -67
  86. package/fesm2022/edvak99-voice-receptionist-widget-audit-page.component-xoLlHj8K.mjs.map +0 -1
  87. package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-2K9W77As.mjs +0 -316
  88. package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-2K9W77As.mjs.map +0 -1
  89. package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-B5AnEjoh.mjs +0 -174
  90. package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-B5AnEjoh.mjs.map +0 -1
  91. package/fesm2022/edvak99-voice-receptionist-widget-call-review.util-BgsBCKgZ.mjs +0 -70
  92. package/fesm2022/edvak99-voice-receptionist-widget-call-review.util-BgsBCKgZ.mjs.map +0 -1
  93. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-BNdnbAVM.mjs +0 -248
  94. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-BNdnbAVM.mjs.map +0 -1
  95. package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-CbphhP6o.mjs +0 -27
  96. package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-CbphhP6o.mjs.map +0 -1
  97. package/fesm2022/edvak99-voice-receptionist-widget-eds-elapsed.pipe-BgLsnq6A.mjs.map +0 -1
  98. package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-Dn0GrZ6_.mjs.map +0 -1
  99. package/fesm2022/edvak99-voice-receptionist-widget-knowledge-hub.page-BngGx_Lc.mjs +0 -609
  100. package/fesm2022/edvak99-voice-receptionist-widget-knowledge-hub.page-BngGx_Lc.mjs.map +0 -1
  101. package/fesm2022/edvak99-voice-receptionist-widget-live-calls-list.service-0kGWO6sh.mjs.map +0 -1
  102. package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-DfLmK89M.mjs +0 -115
  103. package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-DfLmK89M.mjs.map +0 -1
  104. package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-DNpLFqBw.mjs +0 -574
  105. package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-DNpLFqBw.mjs.map +0 -1
  106. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purpose.util-BQECIuJu.mjs +0 -54
  107. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purpose.util-BQECIuJu.mjs.map +0 -1
  108. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-CYSZ832p.mjs +0 -265
  109. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-CYSZ832p.mjs.map +0 -1
  110. package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-B9nv6wtB.mjs +0 -509
  111. package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-B9nv6wtB.mjs.map +0 -1
  112. package/fesm2022/edvak99-voice-receptionist-widget-task.util-DdtI0CR3.mjs +0 -52
  113. package/fesm2022/edvak99-voice-receptionist-widget-task.util-DdtI0CR3.mjs.map +0 -1
  114. package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-DXxjMVUv.mjs.map +0 -1
  115. package/lib/knowledge/components/faq-empty-state/faq-empty-state.component.d.ts +0 -22
  116. package/lib/knowledge/components/faq-form/faq-form.component.d.ts +0 -27
  117. package/lib/knowledge/components/faq-search-tester/faq-search-tester.component.d.ts +0 -17
  118. package/lib/knowledge/mocks/practice-faq.seed.d.ts +0 -2
  119. package/lib/knowledge/models/practice-faq.model.d.ts +0 -50
  120. package/lib/knowledge/pages/knowledge-hub.page.d.ts +0 -54
  121. package/lib/knowledge/services/practice-faq-api.mock.d.ts +0 -16
  122. package/lib/knowledge/services/practice-faq-api.service.d.ts +0 -26
  123. package/lib/knowledge/utils/faq.util.d.ts +0 -3
  124. package/lib/layout/eds-topbar.component.d.ts +0 -29
  125. package/lib/pages/audit-page/audit-page.component.d.ts +0 -21
  126. package/lib/ui/eds-waveform.component.d.ts +0 -8
@@ -1,574 +0,0 @@
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-Dn0GrZ6_.mjs';
8
- import { D as DashboardSocketService, E as EdsElapsedPipe } from './edvak99-voice-receptionist-widget-eds-elapsed.pipe-BgLsnq6A.mjs';
9
- import { C as CallRecordingPlayerComponent } from './edvak99-voice-receptionist-widget-call-recording-player.component-B8_qp7Fe.mjs';
10
- import { E as EdsAvatarComponent } from './edvak99-voice-receptionist-widget-eds-avatar.component-CbphhP6o.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(--eds-brand-400, #5aa3f6)}.eds-waveform[data-tone=violet] .eds-waveform__bar{background:var(--eds-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(--eds-brand-400, #5aa3f6)}.eds-waveform[data-tone=violet] .eds-waveform__bar{background:var(--eds-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(--eds-slate-900);border:1px solid var(--eds-slate-800);border-radius:var(--eds-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(--eds-slate-800);flex-shrink:0}.live-transcript-panel__title{margin:0;font:600 11px/1 var(--eds-font-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--eds-slate-400)}.live-transcript-panel__rec{display:flex;align-items:center;gap:10px}.live-transcript-panel__rec-label{font:600 11px/1 var(--eds-font-mono);letter-spacing:.06em;color:var(--eds-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(--eds-font-body);color:var(--eds-slate-500)}.live-transcript-panel__system{align-self:center;display:inline-flex;align-items:center;gap:6px;padding:6px 12px;border-radius:var(--eds-radius-pill);border:1px solid var(--eds-slate-800);font:500 12px/1 var(--eds-font-mono);max-width:90%;text-align:center}.live-transcript-panel__system--verify{color:var(--eds-green-400);background:#0abf521a;border-color:#0abf524d}.live-transcript-panel__system--warn{color:var(--eds-amber-300);background:#fe9a001a;border-color:#fe9a004d}.live-transcript-panel__system--info{color:var(--eds-slate-300);background:var(--eds-slate-900);border-color:var(--eds-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(--eds-brand-500),var(--eds-violet-500))}.live-transcript-panel__avatar--staff{background:#8e51ff2e;color:var(--eds-violet-400);border:1px solid rgba(142,81,255,.35)}.live-transcript-panel__avatar--patient{background:var(--eds-slate-800);color:var(--eds-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(--eds-font-mono);letter-spacing:.04em;color:var(--eds-slate-500)}.live-transcript-panel__msg--ai .live-transcript-panel__label{color:var(--eds-brand-400)}.live-transcript-panel__msg--staff .live-transcript-panel__label{color:var(--eds-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(--eds-font-sans);color:var(--eds-gray-100)}.live-transcript-panel__bubble--interim{opacity:.85;font-style:italic}.live-transcript-panel__msg--patient .live-transcript-panel__bubble{background:var(--eds-slate-900);border-color:var(--eds-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(--eds-purple-300)}.live-transcript-panel__caret{display:inline-block;width:7px;height:14px;margin-left:2px;vertical-align:text-bottom;background:var(--eds-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(--eds-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(--eds-slate-800);flex-shrink:0}.live-transcript-panel__input{flex:1;height:38px;padding:0 14px;border-radius:var(--eds-radius-md);border:1px solid var(--eds-slate-700);background:var(--eds-slate-950);color:var(--eds-gray-100);font:400 13px/1 var(--eds-font-sans)}.live-transcript-panel__input::placeholder{color:var(--eds-slate-500)}.live-transcript-panel__input:focus{outline:none;border-color:var(--eds-violet-400)}.live-transcript-panel__send{display:inline-flex;align-items:center;gap:6px;height:38px;padding:0 14px;border:none;border-radius:var(--eds-radius-md);background:#8e51ff38;color:var(--eds-purple-300);font:600 13px/1 var(--eds-font-sans);cursor:pointer}.live-transcript-panel__send:hover{background:var(--eds-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(--eds-slate-900);border:1px solid var(--eds-slate-800);border-radius:var(--eds-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(--eds-slate-800);flex-shrink:0}.live-transcript-panel__title{margin:0;font:600 11px/1 var(--eds-font-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--eds-slate-400)}.live-transcript-panel__rec{display:flex;align-items:center;gap:10px}.live-transcript-panel__rec-label{font:600 11px/1 var(--eds-font-mono);letter-spacing:.06em;color:var(--eds-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(--eds-font-body);color:var(--eds-slate-500)}.live-transcript-panel__system{align-self:center;display:inline-flex;align-items:center;gap:6px;padding:6px 12px;border-radius:var(--eds-radius-pill);border:1px solid var(--eds-slate-800);font:500 12px/1 var(--eds-font-mono);max-width:90%;text-align:center}.live-transcript-panel__system--verify{color:var(--eds-green-400);background:#0abf521a;border-color:#0abf524d}.live-transcript-panel__system--warn{color:var(--eds-amber-300);background:#fe9a001a;border-color:#fe9a004d}.live-transcript-panel__system--info{color:var(--eds-slate-300);background:var(--eds-slate-900);border-color:var(--eds-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(--eds-brand-500),var(--eds-violet-500))}.live-transcript-panel__avatar--staff{background:#8e51ff2e;color:var(--eds-violet-400);border:1px solid rgba(142,81,255,.35)}.live-transcript-panel__avatar--patient{background:var(--eds-slate-800);color:var(--eds-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(--eds-font-mono);letter-spacing:.04em;color:var(--eds-slate-500)}.live-transcript-panel__msg--ai .live-transcript-panel__label{color:var(--eds-brand-400)}.live-transcript-panel__msg--staff .live-transcript-panel__label{color:var(--eds-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(--eds-font-sans);color:var(--eds-gray-100)}.live-transcript-panel__bubble--interim{opacity:.85;font-style:italic}.live-transcript-panel__msg--patient .live-transcript-panel__bubble{background:var(--eds-slate-900);border-color:var(--eds-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(--eds-purple-300)}.live-transcript-panel__caret{display:inline-block;width:7px;height:14px;margin-left:2px;vertical-align:text-bottom;background:var(--eds-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(--eds-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(--eds-slate-800);flex-shrink:0}.live-transcript-panel__input{flex:1;height:38px;padding:0 14px;border-radius:var(--eds-radius-md);border:1px solid var(--eds-slate-700);background:var(--eds-slate-950);color:var(--eds-gray-100);font:400 13px/1 var(--eds-font-sans)}.live-transcript-panel__input::placeholder{color:var(--eds-slate-500)}.live-transcript-panel__input:focus{outline:none;border-color:var(--eds-violet-400)}.live-transcript-panel__send{display:inline-flex;align-items:center;gap:6px;height:38px;padding:0 14px;border:none;border-radius:var(--eds-radius-md);background:#8e51ff38;color:var(--eds-purple-300);font:600 13px/1 var(--eds-font-sans);cursor:pointer}.live-transcript-panel__send:hover{background:var(--eds-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(--eds-space-5);color:var(--eds-gray-200)}.live-monitor-page__loading{font:400 13px/1.4 var(--eds-font-body);color:var(--eds-slate-400)}.live-monitor-page__header{display:flex;align-items:flex-start;gap:12px;margin-bottom:var(--eds-space-4)}.live-monitor-page__back{width:36px;height:36px;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--eds-radius-md);background:var(--eds-slate-900);border:1px solid var(--eds-slate-800);color:var(--eds-gray-300);text-decoration:none;flex-shrink:0}.live-monitor-page__back:hover{background:var(--eds-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(--eds-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(--eds-font-sans);color:var(--eds-gray-100)}.live-monitor-page__patient-tag{display:inline-flex;align-items:center;gap:4px;padding:4px 10px;border-radius:var(--eds-radius-pill);font:500 11px/1 var(--eds-font-mono);border:1px solid transparent}.live-monitor-page__patient-tag[data-kind=registered]{color:var(--eds-green-400);background:#0abf5224;border-color:#0abf5247}.live-monitor-page__patient-tag[data-kind=new]{color:var(--eds-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(--eds-font-mono);color:var(--eds-slate-400);flex-wrap:wrap}.live-monitor-page__sep{color:var(--eds-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(--eds-font-mono);color:var(--eds-gray-100)}.live-monitor-page__duration-dot{width:9px;height:9px;border-radius:50%;background:var(--eds-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(--eds-space-4);border-radius:var(--eds-radius-lg);background:#0abf521f;color:var(--eds-green-400);font:400 13px/1.4 var(--eds-font-body)}.live-monitor-page__grid{display:grid;grid-template-columns:1fr 340px;gap:var(--eds-space-4);flex:1;min-height:0}.live-monitor-page__main{display:flex;flex-direction:column;gap:var(--eds-space-4);min-height:0}.live-monitor-page__main-tabs{display:flex;gap:4px;padding:4px;border-radius:var(--eds-radius-md);background:var(--eds-slate-900);border:1px solid var(--eds-slate-800);width:fit-content}.live-monitor-page__main-tab{border:none;background:transparent;color:var(--eds-slate-400);font:600 12px/1 var(--eds-font-body);padding:8px 14px;border-radius:var(--eds-radius-sm);cursor:pointer}.live-monitor-page__main-tab--active{background:var(--eds-slate-800);color:var(--eds-gray-100)}.live-monitor-page__api-logs{flex:1;min-height:280px;padding:16px;background:var(--eds-slate-900);border:1px solid var(--eds-slate-800)}.live-monitor-page__post-call{display:flex;flex-direction:column;gap:var(--eds-space-4)}.live-monitor-page__summary{padding:16px;border-radius:var(--eds-radius-lg);background:var(--eds-slate-900);border:1px solid var(--eds-slate-800);font:400 13px/1.5 var(--eds-font-body);color:var(--eds-slate-300)}.live-monitor-page__muted{color:var(--eds-slate-500);margin:0}.live-monitor-page__sidebar{display:flex;flex-direction:column;gap:var(--eds-space-4);min-height:0}.live-monitor-page__sidebar-block{padding:16px;border-radius:var(--eds-radius-lg);background:var(--eds-slate-900);border:1px solid var(--eds-slate-800)}.live-monitor-page__section-title{margin:0 0 12px;font:600 11px/1 var(--eds-font-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--eds-slate-400)}.live-monitor-page__orb-wrap{display:flex;flex-direction:column;align-items:center;gap:10px;padding:20px 16px;border-radius:var(--eds-radius-lg);background:var(--eds-slate-900);border:1px solid var(--eds-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(--eds-brand-500),var(--eds-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(--eds-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(--eds-amber-400),var(--eds-violet-500))}.live-monitor-page__confidence{font:500 12px/1 var(--eds-font-mono);color:var(--eds-slate-400)}.live-monitor-page__confidence[data-level=high]{color:var(--eds-green-400)}.live-monitor-page__confidence[data-level=low]{color:var(--eds-amber-400)}.live-monitor-page__sentiment-bar{height:6px;background:var(--eds-slate-800);border-radius:3px;overflow:hidden;margin-bottom:8px}.live-monitor-page__sentiment-fill{display:block;height:100%;border-radius:3px;background:var(--eds-green-400);transition:width .3s ease}.live-monitor-page__sentiment-fill[data-sentiment=frustrated],.live-monitor-page__sentiment-fill[data-sentiment=urgent]{background:var(--eds-red-400)}.live-monitor-page__sentiment-fill[data-sentiment=neutral]{background:var(--eds-amber-400)}.live-monitor-page__sentiment-label{font:500 12px/1 var(--eds-font-mono);color:var(--eds-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(--eds-radius-md);background:var(--eds-slate-950);border-left:3px solid var(--eds-slate-700)}.live-monitor-page__tool-time{font:500 11px/1 var(--eds-font-mono);color:var(--eds-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(--eds-font-sans);color:var(--eds-gray-100)}.live-monitor-page__tool-result{font:400 12px/1.4 var(--eds-font-body);color:var(--eds-slate-400)}.live-monitor-page__tool-tech{font:500 10px/1 var(--eds-font-mono);color:var(--eds-slate-500)}.live-monitor-page__tool-spin{animation:eds-spin 1s linear infinite;color:var(--eds-brand-400)}.live-monitor-page__tool--ok{border-left-color:var(--eds-green-400)}.live-monitor-page__tool--warn{border-left-color:var(--eds-amber-400)}.live-monitor-page__tool--error{border-left-color:var(--eds-red-400)}.live-monitor-page__tool--active{border-left-color:var(--eds-brand-400)}.live-monitor-page__tools-empty{border-left:none!important;background:transparent!important;padding:8px 0!important;font:400 13px/1.4 var(--eds-font-body);color:var(--eds-slate-500)}.live-monitor-page__goal{margin:0;font:400 13px/1.5 var(--eds-font-body);color:var(--eds-slate-300)}.live-monitor-page__control-bar{position:sticky;bottom:0;display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:var(--eds-space-4);padding:12px 16px;border-radius:var(--eds-radius-lg);background:var(--eds-slate-900);border:1px solid var(--eds-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(--eds-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(--eds-radius-md);font:600 13px/1 var(--eds-font-sans);cursor:pointer;white-space:nowrap}.live-monitor-page__ctrl:disabled{opacity:.45;cursor:not-allowed}.live-monitor-page__ctrl--neutral{background:var(--eds-slate-800);color:var(--eds-gray-200)}.live-monitor-page__ctrl--neutral:hover:not(:disabled){background:var(--eds-slate-700)}.live-monitor-page__ctrl--primary{background:var(--eds-brand-600);color:#fff}.live-monitor-page__ctrl--primary:hover:not(:disabled){background:var(--eds-brand-500)}.live-monitor-page__ctrl--violet{background:#8e51ff2e;color:var(--eds-purple-300)}.live-monitor-page__ctrl--violet:hover:not(:disabled){background:var(--eds-violet-500);color:#fff}.live-monitor-page__ctrl--danger{margin-left:auto;background:var(--eds-red-500);color:#fff}.live-monitor-page__ctrl--danger:hover:not(:disabled){background:var(--eds-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(--eds-space-5);color:var(--eds-gray-200)}.live-monitor-page__loading{font:400 13px/1.4 var(--eds-font-body);color:var(--eds-slate-400)}.live-monitor-page__header{display:flex;align-items:flex-start;gap:12px;margin-bottom:var(--eds-space-4)}.live-monitor-page__back{width:36px;height:36px;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--eds-radius-md);background:var(--eds-slate-900);border:1px solid var(--eds-slate-800);color:var(--eds-gray-300);text-decoration:none;flex-shrink:0}.live-monitor-page__back:hover{background:var(--eds-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(--eds-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(--eds-font-sans);color:var(--eds-gray-100)}.live-monitor-page__patient-tag{display:inline-flex;align-items:center;gap:4px;padding:4px 10px;border-radius:var(--eds-radius-pill);font:500 11px/1 var(--eds-font-mono);border:1px solid transparent}.live-monitor-page__patient-tag[data-kind=registered]{color:var(--eds-green-400);background:#0abf5224;border-color:#0abf5247}.live-monitor-page__patient-tag[data-kind=new]{color:var(--eds-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(--eds-font-mono);color:var(--eds-slate-400);flex-wrap:wrap}.live-monitor-page__sep{color:var(--eds-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(--eds-font-mono);color:var(--eds-gray-100)}.live-monitor-page__duration-dot{width:9px;height:9px;border-radius:50%;background:var(--eds-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(--eds-space-4);border-radius:var(--eds-radius-lg);background:#0abf521f;color:var(--eds-green-400);font:400 13px/1.4 var(--eds-font-body)}.live-monitor-page__grid{display:grid;grid-template-columns:1fr 340px;gap:var(--eds-space-4);flex:1;min-height:0}.live-monitor-page__main{display:flex;flex-direction:column;gap:var(--eds-space-4);min-height:0}.live-monitor-page__main-tabs{display:flex;gap:4px;padding:4px;border-radius:var(--eds-radius-md);background:var(--eds-slate-900);border:1px solid var(--eds-slate-800);width:fit-content}.live-monitor-page__main-tab{border:none;background:transparent;color:var(--eds-slate-400);font:600 12px/1 var(--eds-font-body);padding:8px 14px;border-radius:var(--eds-radius-sm);cursor:pointer}.live-monitor-page__main-tab--active{background:var(--eds-slate-800);color:var(--eds-gray-100)}.live-monitor-page__api-logs{flex:1;min-height:280px;padding:16px;background:var(--eds-slate-900);border:1px solid var(--eds-slate-800)}.live-monitor-page__post-call{display:flex;flex-direction:column;gap:var(--eds-space-4)}.live-monitor-page__summary{padding:16px;border-radius:var(--eds-radius-lg);background:var(--eds-slate-900);border:1px solid var(--eds-slate-800);font:400 13px/1.5 var(--eds-font-body);color:var(--eds-slate-300)}.live-monitor-page__muted{color:var(--eds-slate-500);margin:0}.live-monitor-page__sidebar{display:flex;flex-direction:column;gap:var(--eds-space-4);min-height:0}.live-monitor-page__sidebar-block{padding:16px;border-radius:var(--eds-radius-lg);background:var(--eds-slate-900);border:1px solid var(--eds-slate-800)}.live-monitor-page__section-title{margin:0 0 12px;font:600 11px/1 var(--eds-font-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--eds-slate-400)}.live-monitor-page__orb-wrap{display:flex;flex-direction:column;align-items:center;gap:10px;padding:20px 16px;border-radius:var(--eds-radius-lg);background:var(--eds-slate-900);border:1px solid var(--eds-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(--eds-brand-500),var(--eds-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(--eds-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(--eds-amber-400),var(--eds-violet-500))}.live-monitor-page__confidence{font:500 12px/1 var(--eds-font-mono);color:var(--eds-slate-400)}.live-monitor-page__confidence[data-level=high]{color:var(--eds-green-400)}.live-monitor-page__confidence[data-level=low]{color:var(--eds-amber-400)}.live-monitor-page__sentiment-bar{height:6px;background:var(--eds-slate-800);border-radius:3px;overflow:hidden;margin-bottom:8px}.live-monitor-page__sentiment-fill{display:block;height:100%;border-radius:3px;background:var(--eds-green-400);transition:width .3s ease}.live-monitor-page__sentiment-fill[data-sentiment=frustrated],.live-monitor-page__sentiment-fill[data-sentiment=urgent]{background:var(--eds-red-400)}.live-monitor-page__sentiment-fill[data-sentiment=neutral]{background:var(--eds-amber-400)}.live-monitor-page__sentiment-label{font:500 12px/1 var(--eds-font-mono);color:var(--eds-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(--eds-radius-md);background:var(--eds-slate-950);border-left:3px solid var(--eds-slate-700)}.live-monitor-page__tool-time{font:500 11px/1 var(--eds-font-mono);color:var(--eds-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(--eds-font-sans);color:var(--eds-gray-100)}.live-monitor-page__tool-result{font:400 12px/1.4 var(--eds-font-body);color:var(--eds-slate-400)}.live-monitor-page__tool-tech{font:500 10px/1 var(--eds-font-mono);color:var(--eds-slate-500)}.live-monitor-page__tool-spin{animation:eds-spin 1s linear infinite;color:var(--eds-brand-400)}.live-monitor-page__tool--ok{border-left-color:var(--eds-green-400)}.live-monitor-page__tool--warn{border-left-color:var(--eds-amber-400)}.live-monitor-page__tool--error{border-left-color:var(--eds-red-400)}.live-monitor-page__tool--active{border-left-color:var(--eds-brand-400)}.live-monitor-page__tools-empty{border-left:none!important;background:transparent!important;padding:8px 0!important;font:400 13px/1.4 var(--eds-font-body);color:var(--eds-slate-500)}.live-monitor-page__goal{margin:0;font:400 13px/1.5 var(--eds-font-body);color:var(--eds-slate-300)}.live-monitor-page__control-bar{position:sticky;bottom:0;display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:var(--eds-space-4);padding:12px 16px;border-radius:var(--eds-radius-lg);background:var(--eds-slate-900);border:1px solid var(--eds-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(--eds-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(--eds-radius-md);font:600 13px/1 var(--eds-font-sans);cursor:pointer;white-space:nowrap}.live-monitor-page__ctrl:disabled{opacity:.45;cursor:not-allowed}.live-monitor-page__ctrl--neutral{background:var(--eds-slate-800);color:var(--eds-gray-200)}.live-monitor-page__ctrl--neutral:hover:not(:disabled){background:var(--eds-slate-700)}.live-monitor-page__ctrl--primary{background:var(--eds-brand-600);color:#fff}.live-monitor-page__ctrl--primary:hover:not(:disabled){background:var(--eds-brand-500)}.live-monitor-page__ctrl--violet{background:#8e51ff2e;color:var(--eds-purple-300)}.live-monitor-page__ctrl--violet:hover:not(:disabled){background:var(--eds-violet-500);color:#fff}.live-monitor-page__ctrl--danger{margin-left:auto;background:var(--eds-red-500);color:#fff}.live-monitor-page__ctrl--danger:hover:not(:disabled){background:var(--eds-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-DNpLFqBw.mjs.map