@edvak99/voice-receptionist-widget 1.1.4 → 1.1.6

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 (77) hide show
  1. package/README.md +37 -0
  2. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-CdrW0He4.mjs +306 -0
  3. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-CdrW0He4.mjs.map +1 -0
  4. package/fesm2022/{edvak99-voice-receptionist-widget-ai-receptionist-api.service-48cifVPF.mjs → edvak99-voice-receptionist-widget-ai-receptionist-api.service-DokIb-9r.mjs} +2 -2
  5. package/fesm2022/{edvak99-voice-receptionist-widget-ai-receptionist-api.service-48cifVPF.mjs.map → edvak99-voice-receptionist-widget-ai-receptionist-api.service-DokIb-9r.mjs.map} +1 -1
  6. package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-4GK1vsom.mjs +277 -0
  7. package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-4GK1vsom.mjs.map +1 -0
  8. package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-Bw_Ade1s.mjs +416 -0
  9. package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-Bw_Ade1s.mjs.map +1 -0
  10. package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-DnwYnjIE.mjs +193 -0
  11. package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-DnwYnjIE.mjs.map +1 -0
  12. package/fesm2022/edvak99-voice-receptionist-widget-call-recording-player.component-Pla2UgeZ.mjs +111 -0
  13. package/fesm2022/edvak99-voice-receptionist-widget-call-recording-player.component-Pla2UgeZ.mjs.map +1 -0
  14. package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-CIMcjGKX.mjs → edvak99-voice-receptionist-widget-call-review.service-Di7GLG9k.mjs} +2 -2
  15. package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-CIMcjGKX.mjs.map → edvak99-voice-receptionist-widget-call-review.service-Di7GLG9k.mjs.map} +1 -1
  16. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-DVz0romd.mjs +439 -0
  17. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-DVz0romd.mjs.map +1 -0
  18. package/fesm2022/{edvak99-voice-receptionist-widget-dashboard-socket.service-BX91r74O.mjs → edvak99-voice-receptionist-widget-dashboard-socket.service-D2pelzu4.mjs} +3 -3
  19. package/fesm2022/{edvak99-voice-receptionist-widget-dashboard-socket.service-BX91r74O.mjs.map → edvak99-voice-receptionist-widget-dashboard-socket.service-D2pelzu4.mjs.map} +1 -1
  20. package/fesm2022/{edvak99-voice-receptionist-widget-eds-avatar.component-CWRiJGfv.mjs → edvak99-voice-receptionist-widget-eds-avatar.component-BJs_TH8X.mjs} +5 -5
  21. package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-BJs_TH8X.mjs.map +1 -0
  22. package/fesm2022/{edvak99-voice-receptionist-widget-eds-pulse-dot.component-BPY6xN7C.mjs → edvak99-voice-receptionist-widget-eds-pulse-dot.component-Ig0Eoq1z.mjs} +6 -6
  23. package/fesm2022/edvak99-voice-receptionist-widget-eds-pulse-dot.component-Ig0Eoq1z.mjs.map +1 -0
  24. package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-Bx7WQ2dg.mjs +81 -0
  25. package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-Bx7WQ2dg.mjs.map +1 -0
  26. package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-DWbA5yAG.mjs +1059 -0
  27. package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-DWbA5yAG.mjs.map +1 -0
  28. package/fesm2022/edvak99-voice-receptionist-widget-modal.component-Cb-FoQ1N.mjs +48 -0
  29. package/fesm2022/edvak99-voice-receptionist-widget-modal.component-Cb-FoQ1N.mjs.map +1 -0
  30. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-DhoyrHSe.mjs +267 -0
  31. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-DhoyrHSe.mjs.map +1 -0
  32. package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-CZUDgZ1_.mjs +967 -0
  33. package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-CZUDgZ1_.mjs.map +1 -0
  34. package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-DyNG2DZZ.mjs +316 -0
  35. package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-DyNG2DZZ.mjs.map +1 -0
  36. package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-DjaurkbO.mjs +46 -0
  37. package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-DjaurkbO.mjs.map +1 -0
  38. package/fesm2022/edvak99-voice-receptionist-widget.mjs +228 -1
  39. package/fesm2022/edvak99-voice-receptionist-widget.mjs.map +1 -1
  40. package/lib/components/call-conversation-panel/call-conversation-panel.component.d.ts +1 -2
  41. package/lib/components/live-transcript-panel/live-transcript-panel.component.d.ts +0 -3
  42. package/package.json +1 -1
  43. package/public-api.d.ts +3 -0
  44. package/styles/chat-ui.scss +0 -1
  45. package/styles/eds.scss +0 -10
  46. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-DT9UKvQO.mjs +0 -307
  47. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-DT9UKvQO.mjs.map +0 -1
  48. package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-C5inEHJw.mjs +0 -278
  49. package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-C5inEHJw.mjs.map +0 -1
  50. package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-sYhM20CE.mjs +0 -418
  51. package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-sYhM20CE.mjs.map +0 -1
  52. package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-DpwajEOE.mjs +0 -194
  53. package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-DpwajEOE.mjs.map +0 -1
  54. package/fesm2022/edvak99-voice-receptionist-widget-call-recording-player.component-CgkRcFOl.mjs +0 -111
  55. package/fesm2022/edvak99-voice-receptionist-widget-call-recording-player.component-CgkRcFOl.mjs.map +0 -1
  56. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-CxbO0eDS.mjs +0 -440
  57. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-CxbO0eDS.mjs.map +0 -1
  58. package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-CWRiJGfv.mjs.map +0 -1
  59. package/fesm2022/edvak99-voice-receptionist-widget-eds-button.component-Bg5T5BN8.mjs +0 -27
  60. package/fesm2022/edvak99-voice-receptionist-widget-eds-button.component-Bg5T5BN8.mjs.map +0 -1
  61. package/fesm2022/edvak99-voice-receptionist-widget-eds-pulse-dot.component-BPY6xN7C.mjs.map +0 -1
  62. package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-BsBQIS_f.mjs +0 -203
  63. package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-BsBQIS_f.mjs.map +0 -1
  64. package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-CaSDMxpV.mjs +0 -81
  65. package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-CaSDMxpV.mjs.map +0 -1
  66. package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-Cy3ND-c-.mjs +0 -1070
  67. package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-Cy3ND-c-.mjs.map +0 -1
  68. package/fesm2022/edvak99-voice-receptionist-widget-modal.component-COfi7biN.mjs +0 -48
  69. package/fesm2022/edvak99-voice-receptionist-widget-modal.component-COfi7biN.mjs.map +0 -1
  70. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-fHVzpiVk.mjs +0 -268
  71. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-fHVzpiVk.mjs.map +0 -1
  72. package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-Bok64X2q.mjs +0 -968
  73. package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-Bok64X2q.mjs.map +0 -1
  74. package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-Bb9lPcYi.mjs +0 -317
  75. package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-Bb9lPcYi.mjs.map +0 -1
  76. package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-BZVlWLWh.mjs +0 -47
  77. package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-BZVlWLWh.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"edvak99-voice-receptionist-widget-live-monitor-page.component-Cy3ND-c-.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/models/live-transcript.types.ts","../../../projects/voice-receptionist-widget/src/lib/services/live-transcript.service.ts","../../../projects/voice-receptionist-widget/src/lib/components/live-transcript-panel/live-transcript-panel.component.ts","../../../projects/voice-receptionist-widget/src/lib/components/live-transcript-panel/live-transcript-panel.component.html","../../../projects/voice-receptionist-widget/src/lib/services/staff-media.service.ts","../../../projects/voice-receptionist-widget/src/lib/pages/live-monitor-page/live-monitor-page.component.ts","../../../projects/voice-receptionist-widget/src/lib/pages/live-monitor-page/live-monitor-page.component.html"],"sourcesContent":["import { ToolEvent, TranscriptLine } from './ai-receptionist.types';\nimport { resolveToolDisplayName, resolveToolKey } from '../utils/tool-labels.util';\n\n/** Socket.io `new_chunk` payload from /dashboard namespace */\nexport interface TranscriptChunk {\n /** `staff` is a human agent speaking after a softphone takeover. */\n role: 'user' | 'assistant' | 'staff' | 'system';\n text: string;\n isFinal: boolean;\n timestamp: string;\n}\n\n/** Socket.io `call_event` payload — tools + lifecycle */\nexport interface CallEventStreamPayload {\n eventType: string;\n occurredAt: string;\n payload: Record<string, unknown>;\n}\n\nexport interface LiveTranscriptState {\n lines: TranscriptLine[];\n patientInterim: string | null;\n aiInterim: string | null;\n connected: boolean;\n error: string | null;\n}\n\nexport function mapChunkToLine(chunk: TranscriptChunk): TranscriptLine {\n const at = Date.parse(chunk.timestamp);\n return {\n who:\n chunk.role === 'staff'\n ? 'staff'\n : chunk.role === 'system'\n ? 'system'\n : chunk.role === 'assistant'\n ? 'ai'\n : 'patient',\n text: chunk.text,\n at: Number.isFinite(at) ? at : Date.now(),\n };\n}\n\nexport function formatEventTime(iso: string): string {\n return new Date(iso).toLocaleTimeString('en-US', {\n hour: 'numeric',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n });\n}\n\nexport function mapCallEventToTool(event: CallEventStreamPayload): ToolEvent {\n const p = event.payload;\n const state = p['state'];\n const rawName = String(p['name'] ?? event.eventType.replace(/^tool_/, ''));\n const tech = String(p['tech'] ?? event.eventType);\n const key = resolveToolKey(\n p['key'] != null ? String(p['key']) : undefined,\n rawName,\n tech,\n );\n return {\n t: formatEventTime(event.occurredAt),\n key,\n name: resolveToolDisplayName(key, rawName),\n tech,\n result: String(p['result'] ?? ''),\n state:\n state === 'ok' || state === 'warn' || state === 'error' || state === 'active'\n ? state\n : 'ok',\n };\n}\n\nexport function mapLifecycleEventToTool(event: CallEventStreamPayload): ToolEvent {\n const p = event.payload;\n return {\n t: formatEventTime(event.occurredAt),\n name: event.eventType.replace(/_/g, ' '),\n tech: event.eventType,\n result: String(p['detail'] ?? p['message'] ?? p['result'] ?? ''),\n state: 'ok',\n };\n}\n\nexport function isDuplicateTranscriptLine(lines: TranscriptLine[], line: TranscriptLine): boolean {\n return lines.some((l) => l.who === line.who && l.text.trim() === line.text.trim());\n}\n","import { Injectable, inject, signal } from '@angular/core';\n\nimport { VOICE_RECEPTIONIST_CONFIG } from '../config';\nimport { ToolEvent, TranscriptLine } from '../models/ai-receptionist.types';\nimport {\n CallEventStreamPayload,\n isDuplicateTranscriptLine,\n mapCallEventToTool,\n mapChunkToLine,\n mapLifecycleEventToTool,\n TranscriptChunk,\n} from '../models/live-transcript.types';\nimport { DashboardSocketService } from './dashboard-socket.service';\n\n/**\n * Live call stream over shared Socket.io (`/dashboard` namespace).\n * REST seeds transcript/tools once; socket delivers `new_chunk` + `call_event`.\n */\n@Injectable({ providedIn: 'root' })\nexport class LiveTranscriptService {\n private readonly socket = inject(DashboardSocketService);\n private readonly config = inject(VOICE_RECEPTIONIST_CONFIG);\n\n readonly lines = signal<TranscriptLine[]>([]);\n readonly patientInterim = signal<string | null>(null);\n readonly aiInterim = signal<string | null>(null);\n readonly staffInterim = signal<string | null>(null);\n readonly tools = signal<ToolEvent[]>([]);\n readonly connected = this.socket.connected;\n readonly error = signal<string | null>(null);\n readonly callEnded = signal(false);\n readonly summaryRequested = signal(false);\n\n private activeCallId: string | null = null;\n private watching = false;\n private mockTimer: ReturnType<typeof setInterval> | null = null;\n private mockIndex = 0;\n\n private readonly chunkHandler = (chunk: TranscriptChunk) => this.handleChunk(chunk);\n private readonly eventHandler = (event: CallEventStreamPayload) => this.handleCallEvent(event);\n private readonly onSocketConnect = () => {\n if (this.activeCallId) {\n this.socket.emit('watch_call', { callId: this.activeCallId });\n }\n };\n\n seed(transcript: TranscriptLine[], toolEvents: ToolEvent[]): void {\n this.lines.set(transcript.map((line) => ({ ...line })));\n this.tools.set(toolEvents.map((tool) => ({ ...tool })));\n }\n\n watch(callId: string, live = true): void {\n const id = callId.trim();\n if (!id || !live) return;\n\n const switchingCall = this.activeCallId !== id;\n if (switchingCall) {\n this.unsubscribeSocketEvents();\n this.clearInterim();\n this.callEnded.set(false);\n this.summaryRequested.set(false);\n this.activeCallId = id;\n } else if (this.watching) {\n return;\n }\n\n if (this.config.useMockApi) {\n this.watching = true;\n this.startMockStream(id);\n return;\n }\n\n if (!this.watching) {\n this.socket.acquire();\n this.socket.on('new_chunk', this.chunkHandler);\n this.socket.on('call_event', this.eventHandler);\n this.socket.onConnect(this.onSocketConnect);\n this.watching = true;\n }\n\n if (this.socket.connected()) {\n this.socket.emit('watch_call', { callId: id });\n }\n }\n\n stop(): void {\n this.activeCallId = null;\n this.unsubscribeSocketEvents();\n this.resetStreamState();\n }\n\n markCompleted(): void {\n this.callEnded.set(true);\n this.unsubscribeSocketEvents();\n this.clearInterim();\n }\n\n private unsubscribeSocketEvents(): void {\n this.stopMockStream();\n if (!this.watching) return;\n\n this.socket.off('new_chunk', this.chunkHandler);\n this.socket.off('call_event', this.eventHandler);\n this.socket.offConnect(this.onSocketConnect);\n this.socket.release();\n this.watching = false;\n }\n\n private handleChunk(chunk: TranscriptChunk): void {\n if (!chunk?.text?.trim()) return;\n\n if (chunk.role === 'user') {\n if (chunk.isFinal) {\n this.patientInterim.set(null);\n this.appendLineIfNew(mapChunkToLine(chunk));\n } else {\n this.patientInterim.set(chunk.text);\n }\n return;\n }\n\n if (chunk.role === 'system') {\n this.appendLineIfNew(mapChunkToLine(chunk));\n return;\n }\n\n if (chunk.role === 'staff') {\n if (chunk.isFinal) {\n this.staffInterim.set(null);\n this.appendLineIfNew(mapChunkToLine(chunk));\n } else {\n this.staffInterim.set(chunk.text);\n }\n return;\n }\n\n if (chunk.isFinal) {\n this.aiInterim.set(null);\n this.appendLineIfNew(mapChunkToLine(chunk));\n } else {\n this.aiInterim.set(chunk.text);\n }\n }\n\n private handleCallEvent(event: CallEventStreamPayload): void {\n if (!event?.eventType) return;\n\n if (event.eventType.startsWith('tool_')) {\n this.tools.update((prev) => [...prev, mapCallEventToTool(event)]);\n return;\n }\n\n if (event.eventType === 'call_completed') {\n this.markCompleted();\n return;\n }\n\n if (event.eventType === 'summary_generated') {\n this.summaryRequested.set(true);\n return;\n }\n\n this.tools.update((prev) => [...prev, mapLifecycleEventToTool(event)]);\n }\n\n private appendLineIfNew(line: TranscriptLine): void {\n this.lines.update((prev) => {\n if (isDuplicateTranscriptLine(prev, line)) return prev;\n return [...prev, line];\n });\n }\n\n private startMockStream(callId: string): void {\n this.stopMockStream();\n this.mockIndex = 0;\n\n const mockChunks: TranscriptChunk[] = [\n {\n role: 'user',\n text: 'Hi, I need to schedule an appointment.',\n isFinal: false,\n timestamp: new Date().toISOString(),\n },\n {\n role: 'user',\n text: 'Hi, I need to schedule an appointment.',\n isFinal: true,\n timestamp: new Date().toISOString(),\n },\n {\n role: 'assistant',\n text: 'Of course — I can help with that. Which provider would you prefer?',\n isFinal: true,\n timestamp: new Date().toISOString(),\n },\n ];\n\n const mockEvents: CallEventStreamPayload[] = [\n {\n eventType: 'tool_check_doctor_availability',\n occurredAt: new Date().toISOString(),\n payload: {\n key: 'check_doctor_availability',\n name: 'Checked Doctor Availability',\n tech: 'tool_check_doctor_availability',\n result: 'Found 3 available time slots',\n state: 'ok',\n },\n },\n ];\n\n this.mockTimer = setInterval(() => {\n if (this.activeCallId !== callId) {\n this.stopMockStream();\n return;\n }\n if (this.mockIndex < mockChunks.length) {\n this.handleChunk(mockChunks[this.mockIndex]);\n this.mockIndex++;\n return;\n }\n if (this.mockIndex === mockChunks.length) {\n this.handleCallEvent(mockEvents[0]);\n this.mockIndex++;\n }\n }, 1500);\n }\n\n private stopMockStream(): void {\n if (this.mockTimer) {\n clearInterval(this.mockTimer);\n this.mockTimer = null;\n }\n }\n\n private clearInterim(): void {\n this.patientInterim.set(null);\n this.aiInterim.set(null);\n this.staffInterim.set(null);\n }\n\n private resetStreamState(): void {\n this.lines.set([]);\n this.tools.set([]);\n this.clearInterim();\n this.error.set(null);\n this.callEnded.set(false);\n this.summaryRequested.set(false);\n }\n}\n","import {\n AfterViewChecked,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n ViewChild,\n computed,\n effect,\n inject,\n input,\n output,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\n\nimport { TranscriptWho } from '../../models/ai-receptionist.types';\nimport { LiveTranscriptService } from '../../services/live-transcript.service';\nimport { isUnknownCaller } from '../../utils/call-history-display.util';\nimport { EdsIconComponent } from '../../ui/eds-icon.component';\n\n@Component({\n selector: 'eds-live-transcript-panel',\n imports: [FormsModule, EdsIconComponent],\n templateUrl: './live-transcript-panel.component.html',\n styleUrl: './live-transcript-panel.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class LiveTranscriptPanelComponent implements AfterViewChecked {\n readonly live = input(true);\n readonly paused = input(false);\n readonly humanTakeover = input(false);\n readonly showStaffInput = input(false);\n readonly staffMessage = input('');\n /** Resolved patient display name from the live call. */\n readonly callerName = input<string | null>(null);\n /** Whether the caller was matched to a patient chart. */\n readonly callerMatched = input(false);\n readonly staffMessageChange = output<string>();\n readonly sendMessage = output<void>();\n\n private readonly stream = inject(LiveTranscriptService);\n\n @ViewChild('scrollHost') private scrollHost?: ElementRef<HTMLElement>;\n\n protected readonly lines = this.stream.lines;\n protected readonly patientInterim = this.stream.patientInterim;\n protected readonly aiInterim = this.stream.aiInterim;\n protected readonly staffInterim = this.stream.staffInterim;\n protected readonly connected = this.stream.connected;\n protected readonly error = this.stream.error;\n\n protected readonly callerLabel = computed(() => {\n const name = this.callerName()?.trim() ?? '';\n if (isUnknownCaller(name, this.callerMatched())) return 'Unknown caller';\n return name || 'Unknown caller';\n });\n\n private shouldScroll = false;\n\n constructor() {\n effect(() => {\n this.lines();\n this.patientInterim();\n this.aiInterim();\n this.staffInterim();\n this.requestScroll();\n });\n }\n\n ngAfterViewChecked(): void {\n if (this.shouldScroll && this.scrollHost) {\n const el = this.scrollHost.nativeElement;\n el.scrollTop = el.scrollHeight;\n this.shouldScroll = false;\n }\n }\n\n protected roleLabel(who: TranscriptWho): string {\n switch (who) {\n case 'ai':\n return 'Ava · AI';\n case 'patient':\n return this.callerLabel();\n case 'staff':\n return 'Staff';\n default:\n return 'System';\n }\n }\n\n protected speakerIcon(who: TranscriptWho): string {\n switch (who) {\n case 'ai':\n return 'graphic_eq';\n case 'staff':\n return 'support_agent';\n case 'patient':\n return 'person';\n default:\n return 'info';\n }\n }\n\n protected systemIcon(flag?: string): string {\n if (flag === 'verify') return 'verified_user';\n if (flag === 'fail' || flag === 'escalate') return 'bolt';\n return 'info';\n }\n\n protected systemClass(flag?: string): string {\n if (flag === 'verify') return 'live-transcript-panel__system--verify';\n if (flag === 'fail' || flag === 'escalate') return 'live-transcript-panel__system--warn';\n return 'live-transcript-panel__system--info';\n }\n\n protected msgClass(who: TranscriptWho): string {\n return `live-transcript-panel__msg--${who}`;\n }\n\n protected avatarClass(who: TranscriptWho): string {\n return `live-transcript-panel__avatar--${who}`;\n }\n\n protected onStaffInput(value: string): void {\n this.staffMessageChange.emit(value);\n }\n\n protected onSend(): void {\n this.sendMessage.emit();\n }\n\n private requestScroll(): void {\n this.shouldScroll = true;\n }\n}\n","<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 </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\">{{ callerLabel() }} · 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 (staffInterim(); as interim) {\n <article class=\"live-transcript-panel__msg live-transcript-panel__msg--staff eds-animate-fade-up\">\n <div class=\"live-transcript-panel__avatar live-transcript-panel__avatar--staff\">\n <eds-icon name=\"support_agent\" [size]=\"16\" />\n </div>\n <div class=\"live-transcript-panel__content\">\n <span class=\"live-transcript-panel__label\">Staff · speaking</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 · 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() && !staffInterim()) {\n <p class=\"live-transcript-panel__empty\">Waiting for conversation…</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…\"\n [ngModel]=\"staffMessage()\"\n (ngModelChange)=\"onStaffInput($event)\"\n (keyup.enter)=\"onSend()\"\n autocomplete=\"off\"\n />\n </footer>\n }\n</section>\n","import { Injectable, signal } from '@angular/core';\nimport {\n ConnectionState,\n RemoteParticipant,\n RemoteTrack,\n RemoteTrackPublication,\n Room,\n RoomEvent,\n Track,\n} from 'livekit-client';\n\nimport { StaffMediaJoinResult, StaffMediaMode } from '../models/staff-media.types';\n\nexport type StaffMediaConnectionState =\n | 'idle'\n | 'connecting'\n | 'connected'\n | 'disconnecting'\n | 'error';\n\nconst STAFF_IDENTITY_PREFIX = 'staff-';\n\n/**\n * Browser softphone for staff Listen / Take Over via LiveKit.\n * Secrets stay on the server — FE only uses short-lived clientToken + livekitUrl.\n *\n * Root-provided on purpose: the session must survive navigating away from the\n * Live Monitor page, otherwise switching dashboard tabs would drop the call.\n */\n@Injectable({ providedIn: 'root' })\nexport class StaffMediaService {\n readonly connectionState = signal<StaffMediaConnectionState>('idle');\n readonly mode = signal<StaffMediaMode | null>(null);\n readonly micEnabled = signal(false);\n readonly error = signal<string | null>(null);\n readonly activeCallId = signal<string | null>(null);\n /** False when the browser blocks autoplay — UI should offer an \"enable sound\" action. */\n readonly audioPlaybackAllowed = signal(true);\n /** True once remote caller audio is actually attached and playing. */\n readonly remoteAudioAttached = signal(false);\n\n private room: Room | null = null;\n private audioContainer: HTMLElement | null = null;\n private readonly attached = new Map<string, HTMLMediaElement[]>();\n\n get isConnected(): boolean {\n return this.connectionState() === 'connected';\n }\n\n /** True when a softphone session is live for this call, regardless of page. */\n isConnectedTo(callId: string): boolean {\n return this.isConnected && this.activeCallId() === callId;\n }\n\n /**\n * getUserMedia only exists in a secure context (https or localhost) and, inside an\n * iframe, requires allow=\"microphone\". Returns null when capture is possible.\n */\n microphoneBlockedReason(): string | null {\n if (typeof navigator === 'undefined') return 'Microphone is not available in this environment.';\n if (!navigator.mediaDevices?.getUserMedia) {\n const secure = typeof window !== 'undefined' && window.isSecureContext;\n return secure\n ? 'Microphone access is blocked by the browser. If the dashboard is embedded, the host page must allow microphone access.'\n : 'Microphone access needs a secure connection. Open the dashboard over https (or localhost) to talk to callers.';\n }\n return null;\n }\n\n async join(join: StaffMediaJoinResult): Promise<void> {\n if (join.provider && join.provider !== 'livekit') {\n throw new Error(`Unsupported media provider: ${join.provider}`);\n }\n if (!join.livekitUrl || !join.clientToken) {\n throw new Error('Missing LiveKit URL or client token from media/join');\n }\n if (join.mode === 'takeover') {\n const blocked = this.microphoneBlockedReason();\n if (blocked) throw new Error(blocked);\n }\n\n await this.disconnect();\n\n this.connectionState.set('connecting');\n this.error.set(null);\n this.mode.set(join.mode);\n this.activeCallId.set(join.callId || null);\n this.micEnabled.set(false);\n\n const room = new Room({\n adaptiveStream: true,\n dynacast: true,\n audioCaptureDefaults: {\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n },\n });\n\n room.on(RoomEvent.Disconnected, () => {\n if (this.room !== room) return;\n void this.cleanupLocal(false);\n this.connectionState.set('idle');\n this.mode.set(null);\n this.activeCallId.set(null);\n this.micEnabled.set(false);\n });\n\n room.on(RoomEvent.ConnectionStateChanged, (state: ConnectionState) => {\n if (this.room !== room) return;\n if (state === ConnectionState.Connected) {\n this.connectionState.set('connected');\n } else if (state === ConnectionState.Connecting || state === ConnectionState.Reconnecting) {\n this.connectionState.set('connecting');\n }\n });\n\n room.on(RoomEvent.AudioPlaybackStatusChanged, () => {\n if (this.room === room) {\n this.audioPlaybackAllowed.set(room.canPlaybackAudio);\n }\n });\n\n room.on(RoomEvent.TrackSubscribed, this.onTrackSubscribed);\n room.on(RoomEvent.TrackUnsubscribed, this.onTrackUnsubscribed);\n room.on(RoomEvent.ParticipantDisconnected, this.onParticipantDisconnected);\n room.on(RoomEvent.TrackPublished, this.onTrackPublished);\n\n try {\n this.room = room;\n this.ensureAudioContainer();\n await room.connect(join.livekitUrl, join.clientToken);\n\n // Publish mic only for takeover (listen stays receive-only).\n if (join.mode === 'takeover') {\n await this.enableMicrophone(true);\n }\n\n this.attachExistingRemoteTracks(room);\n await this.startAudioPlayback();\n this.connectionState.set('connected');\n } catch (err) {\n const message = this.friendlyError(err);\n this.error.set(message);\n this.connectionState.set('error');\n await this.cleanupLocal(true);\n this.mode.set(null);\n this.activeCallId.set(null);\n throw new Error(message);\n }\n }\n\n async setMicrophoneEnabled(enabled: boolean): Promise<void> {\n if (!this.room || this.connectionState() !== 'connected') return;\n if (this.mode() !== 'takeover' && enabled) {\n // Listen mode stays muted unless upgraded to takeover.\n return;\n }\n await this.enableMicrophone(enabled);\n }\n\n async toggleMicrophone(): Promise<void> {\n await this.setMicrophoneEnabled(!this.micEnabled());\n }\n\n /** Retry blocked autoplay — must be called from a user gesture. */\n async startAudioPlayback(): Promise<void> {\n const room = this.room;\n if (!room) return;\n try {\n await room.startAudio();\n this.audioPlaybackAllowed.set(true);\n } catch {\n this.audioPlaybackAllowed.set(room.canPlaybackAudio);\n }\n for (const elements of this.attached.values()) {\n for (const el of elements) {\n void el.play().catch(() => undefined);\n }\n }\n }\n\n async disconnect(): Promise<void> {\n if (!this.room && this.connectionState() === 'idle') return;\n this.connectionState.set('disconnecting');\n await this.cleanupLocal(true);\n this.connectionState.set('idle');\n this.mode.set(null);\n this.activeCallId.set(null);\n this.micEnabled.set(false);\n this.error.set(null);\n this.audioPlaybackAllowed.set(true);\n }\n\n private readonly onTrackSubscribed = (\n track: RemoteTrack,\n publication: RemoteTrackPublication,\n participant: RemoteParticipant,\n ): void => {\n if (track.kind !== Track.Kind.Audio) return;\n\n // Never play another staff softphone back — that is a direct feedback loop\n // where the person taking over hears their own voice.\n if (this.isStaff(participant.identity)) {\n try {\n publication.setSubscribed(false);\n } catch {\n /* server may already have unsubscribed */\n }\n return;\n }\n\n const container = this.ensureAudioContainer();\n const element = track.attach();\n element.autoplay = true;\n element.setAttribute('playsinline', 'true');\n (element as HTMLAudioElement).volume = 1;\n container.appendChild(element);\n\n const key = publication.trackSid || track.sid || participant.identity;\n this.attached.set(key, [...(this.attached.get(key) ?? []), element]);\n this.remoteAudioAttached.set(true);\n void element.play().catch(() => {\n // Autoplay blocked — surfaced via the \"Enable sound\" action.\n this.audioPlaybackAllowed.set(false);\n });\n };\n\n private readonly onTrackUnsubscribed = (\n track: RemoteTrack,\n publication: RemoteTrackPublication,\n participant: RemoteParticipant,\n ): void => {\n if (track.kind !== Track.Kind.Audio) return;\n const key = publication.trackSid || track.sid || participant.identity;\n this.detachKey(key, track);\n };\n\n private readonly onParticipantDisconnected = (participant: RemoteParticipant): void => {\n for (const publication of participant.trackPublications.values()) {\n const key = publication.trackSid;\n if (key) this.detachKey(key);\n }\n };\n\n /** Staff tracks are dropped server-side as soon as they appear. */\n private readonly onTrackPublished = (\n publication: RemoteTrackPublication,\n participant: RemoteParticipant,\n ): void => {\n if (!this.isStaff(participant.identity)) return;\n try {\n publication.setSubscribed(false);\n } catch {\n /* nothing to unsubscribe */\n }\n };\n\n private attachExistingRemoteTracks(room: Room): void {\n for (const participant of room.remoteParticipants.values()) {\n for (const publication of participant.trackPublications.values()) {\n if (publication.track && publication.isSubscribed) {\n this.onTrackSubscribed(publication.track, publication, participant);\n }\n }\n }\n }\n\n private isStaff(identity?: string | null): boolean {\n return !!identity && identity.startsWith(STAFF_IDENTITY_PREFIX);\n }\n\n private detachKey(key: string, track?: RemoteTrack): void {\n const elements = this.attached.get(key);\n if (!elements) return;\n this.attached.delete(key);\n for (const el of elements) {\n try {\n track?.detach(el);\n } catch {\n /* already detached */\n }\n el.remove();\n }\n if (this.attached.size === 0) this.remoteAudioAttached.set(false);\n }\n\n private ensureAudioContainer(): HTMLElement {\n if (this.audioContainer?.isConnected) return this.audioContainer;\n const el = document.createElement('div');\n el.dataset['edsStaffAudio'] = 'true';\n el.style.position = 'fixed';\n el.style.width = '0';\n el.style.height = '0';\n el.style.overflow = 'hidden';\n document.body.appendChild(el);\n this.audioContainer = el;\n return el;\n }\n\n private async enableMicrophone(enabled: boolean): Promise<void> {\n const room = this.room;\n if (!room) return;\n await room.localParticipant.setMicrophoneEnabled(enabled);\n this.micEnabled.set(enabled);\n }\n\n private friendlyError(err: unknown): string {\n const name = (err as { name?: string } | null)?.name ?? '';\n if (name === 'NotAllowedError' || name === 'SecurityError') {\n return 'Microphone permission was denied. Allow microphone access and try again.';\n }\n if (name === 'NotFoundError' || name === 'OverconstrainedError') {\n return 'No microphone was found on this device.';\n }\n if (name === 'NotReadableError' || name === 'TrackStartError') {\n return 'Your microphone is in use by another app.';\n }\n const message = err instanceof Error ? err.message : '';\n if (message.includes('getUserMedia')) {\n return this.microphoneBlockedReason() ?? message;\n }\n return message || 'Failed to join the call audio.';\n }\n\n private async cleanupLocal(disconnectRoom: boolean): Promise<void> {\n const room = this.room;\n this.room = null;\n\n for (const key of [...this.attached.keys()]) {\n this.detachKey(key);\n }\n this.remoteAudioAttached.set(false);\n this.audioContainer?.remove();\n this.audioContainer = null;\n\n if (!room) return;\n try {\n await room.localParticipant.setMicrophoneEnabled(false);\n } catch {\n /* mic may never have been published */\n }\n try {\n if (disconnectRoom) {\n await room.disconnect(true);\n }\n room.removeAllListeners();\n } catch {\n /* already gone */\n }\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n effect,\n inject,\n OnDestroy,\n OnInit,\n signal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { ActivatedRoute, RouterLink } from '@angular/router';\nimport { FormsModule } from '@angular/forms';\nimport { firstValueFrom } from 'rxjs';\n\nimport { LiveTranscriptPanelComponent } from '../../components/live-transcript-panel/live-transcript-panel.component';\nimport { CallRecordingPlayerComponent } from '../../components/call-recording-player/call-recording-player.component';\nimport {\n CallControlMode,\n LiveCall,\n Sentiment,\n} from '../../models/ai-receptionist.types';\nimport {\n CallControlChangedEvent,\n StaffMediaMode,\n} from '../../models/staff-media.types';\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport { DashboardSocketService } from '../../services/dashboard-socket.service';\nimport { LiveTranscriptService } from '../../services/live-transcript.service';\nimport { StaffMediaService } from '../../services/staff-media.service';\nimport { ToastService } from '../../shared/toast.service';\nimport {\n ageFromDob,\n callerMetaLine,\n isUnknownCaller,\n} from '../../utils/call-history-display.util';\nimport { toolEventTrackKey } from '../../utils/tool-labels.util';\nimport { EdsElapsedPipe } from '../../pipes/eds-elapsed.pipe';\nimport { EdsAvatarComponent } from '../../ui/eds-avatar.component';\nimport { EdsIconComponent } from '../../ui/eds-icon.component';\n\n@Component({\n selector: 'eds-live-monitor-page',\n imports: [\n RouterLink,\n FormsModule,\n EdsAvatarComponent,\n EdsIconComponent,\n EdsElapsedPipe,\n LiveTranscriptPanelComponent,\n CallRecordingPlayerComponent,\n ],\n templateUrl: './live-monitor-page.component.html',\n styleUrl: './live-monitor-page.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class LiveMonitorPageComponent implements OnInit, OnDestroy {\n private readonly api = inject(AiReceptionistApiService);\n private readonly route = inject(ActivatedRoute);\n private readonly stream = inject(LiveTranscriptService);\n private readonly staffMedia = inject(StaffMediaService);\n private readonly socket = inject(DashboardSocketService);\n private readonly toast = inject(ToastService);\n private readonly destroyRef = inject(DestroyRef);\n\n protected readonly call = signal<LiveCall | null>(null);\n protected readonly staffMessage = signal('');\n protected readonly loading = signal(true);\n protected readonly callId = signal('');\n protected readonly summary = signal<string | null>(null);\n protected readonly summaryLoading = signal(false);\n protected readonly mediaBusy = signal(false);\n\n protected readonly liveTools = this.stream.tools;\n protected readonly callEnded = this.stream.callEnded;\n protected readonly mediaState = this.staffMedia.connectionState;\n protected readonly mediaMode = this.staffMedia.mode;\n protected readonly micEnabled = this.staffMedia.micEnabled;\n protected readonly audioPlaybackAllowed = this.staffMedia.audioPlaybackAllowed;\n\n private summaryLoaded = false;\n private staffJoined = false;\n\n private readonly onControlChanged = (evt: CallControlChangedEvent): void => {\n if (!evt?.callId || evt.callId !== this.callId()) return;\n this.call.update((c) => {\n if (!c) return c;\n const handedOff = evt.aiHandedOff === true || c.aiHandedOff === true;\n const controlMode = (evt.controlMode as CallControlMode | null | undefined) ?? c.controlMode;\n const next: LiveCall = {\n ...c,\n controlMode,\n aiPaused: typeof evt.aiPaused === 'boolean' ? evt.aiPaused : c.aiPaused,\n aiHandedOff: handedOff,\n assigned: evt.assigned !== undefined ? evt.assigned : c.assigned,\n };\n if (controlMode === 'takeover' || handedOff) {\n next.status = 'takeover';\n } else if (controlMode === 'ai' || controlMode === 'listen') {\n if (c.status === 'takeover') next.status = 'ai';\n }\n return next;\n });\n };\n\n constructor() {\n effect(() => {\n if (this.stream.callEnded()) {\n void this.staffMedia.disconnect();\n this.staffJoined = false;\n this.loadCallMetadataOnce();\n }\n });\n\n effect(() => {\n if (this.stream.summaryRequested() && !this.summaryLoaded) {\n this.loadPostCallSummary();\n }\n });\n }\n\n ngOnInit(): void {\n this.socket.acquire();\n this.socket.on<CallControlChangedEvent>('call_control_changed', this.onControlChanged);\n\n this.route.paramMap.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => {\n const id = params.get('id') ?? '';\n this.callId.set(id);\n // The softphone lives in a root service, so a session started earlier is still\n // running when this page is re-created.\n this.staffJoined = this.staffMedia.isConnectedTo(id);\n this.loading.set(true);\n this.summary.set(null);\n this.summaryLoaded = false;\n this.stream.stop();\n this.loadCallMetadata();\n });\n }\n\n /**\n * Leaving the page must never drop the caller. The LiveKit session is owned by the\n * root StaffMediaService and keeps running until End Call or an explicit leave.\n */\n ngOnDestroy(): void {\n this.socket.off('call_control_changed', this.onControlChanged);\n this.socket.release();\n this.stream.stop();\n }\n\n /** True when this page's call is the one the softphone is connected to. */\n protected isMediaForThisCall(): boolean {\n return this.staffMedia.activeCallId() === this.callId();\n }\n\n protected isTakeover(): boolean {\n if (this.mediaMode() === 'takeover' && this.isMediaForThisCall()) return true;\n const c = this.call();\n if (!c) return false;\n if (c.aiHandedOff) return true;\n if (c.controlMode === 'listen') return false;\n return c.controlMode === 'takeover' || c.status === 'takeover' || !!c.assigned;\n }\n\n /** After takeover the AI is gone for good — no resume path is offered. */\n protected isHandedOff(): boolean {\n return this.call()?.aiHandedOff === true || this.isTakeover();\n }\n\n protected isPaused(): boolean {\n const c = this.call();\n return c?.controlMode === 'paused' || !!c?.aiPaused;\n }\n\n protected isLive(): boolean {\n return !!this.call() && !this.callEnded();\n }\n\n protected confidenceLevel(confidence: number): 'high' | 'low' | 'mid' {\n if (confidence >= 70) return 'high';\n if (confidence < 50) return 'low';\n return 'mid';\n }\n\n protected sentimentPercent(sentiment: Sentiment): number {\n const map: Record<Sentiment, number> = {\n calm: 85,\n neutral: 60,\n frustrated: 35,\n urgent: 15,\n };\n return map[sentiment];\n }\n\n protected toolStateClass(state: string): string {\n return `live-monitor-page__tool--${state}`;\n }\n\n protected readonly toolTrackKey = toolEventTrackKey;\n\n protected isUnknownCaller(call: LiveCall): boolean {\n return isUnknownCaller(call.name, call.matched);\n }\n\n protected displayName(call: LiveCall): string {\n return this.isUnknownCaller(call) ? 'Unknown Caller' : call.name.trim();\n }\n\n protected profileInitials(call: LiveCall): string {\n return this.isUnknownCaller(call) ? '?' : call.initials || '?';\n }\n\n protected profileMeta(call: LiveCall): string {\n return callerMetaLine({\n name: call.name,\n matched: call.matched,\n phone: call.phone,\n dob: call.dob,\n age: call.age ?? ageFromDob(call.dob),\n gender: call.gender,\n });\n }\n\n protected goalBadge(call: LiveCall): string | null {\n const raw = (call.goal || call.reason || '').trim();\n if (!raw) return null;\n return raw.replace(/[_-]+/g, ' ').toUpperCase();\n }\n\n protected takeover(): void {\n void this.joinMedia('takeover');\n }\n\n protected toggleMic(): void {\n void this.staffMedia.toggleMicrophone();\n }\n\n protected transfer(): void {\n this.api.postTransfer(this.callId()).subscribe(() => this.refreshMetadata());\n }\n\n protected endCall(): void {\n const id = this.callId();\n void this.staffMedia.disconnect().then(() => {\n this.staffJoined = false;\n this.api.postEnd(id).subscribe(() => {\n this.stream.markCompleted();\n this.loadCallMetadataOnce();\n this.loadPostCallSummary();\n });\n });\n }\n\n protected sendMessage(): void {\n const text = this.staffMessage().trim();\n if (!text) return;\n this.api.postMessage(this.callId(), text).subscribe(() => {\n this.staffMessage.set('');\n this.refreshMetadata();\n });\n }\n\n protected enableSound(): void {\n void this.staffMedia.startAudioPlayback();\n }\n\n private async joinMedia(mode: StaffMediaMode): Promise<void> {\n if (this.mediaBusy() || this.callEnded()) return;\n const id = this.callId();\n if (!id) return;\n\n // Check the mic before joining so a blocked browser never leaves the AI paused.\n if (mode === 'takeover' && !this.api.usesMockStreaming()) {\n const blocked = this.staffMedia.microphoneBlockedReason();\n if (blocked) {\n this.toast.show(blocked, 'error', 6000);\n return;\n }\n }\n\n this.mediaBusy.set(true);\n try {\n const { data } = await firstValueFrom(this.api.postMediaJoin(id, mode));\n this.applyJoinMetadata(data);\n\n if (!this.api.usesMockStreaming()) {\n await this.staffMedia.join(data);\n }\n this.staffJoined = true;\n this.toast.show(\n mode === 'takeover' ? 'You are on the call' : 'Listening to the call',\n 'success',\n );\n } catch (err) {\n const message =\n err instanceof Error ? err.message : `Failed to ${mode === 'takeover' ? 'take over' : 'listen'}`;\n this.toast.show(message, 'error');\n try {\n await this.staffMedia.disconnect();\n await firstValueFrom(this.api.postMediaLeave(id));\n } catch {\n /* ignore cleanup errors */\n }\n this.staffJoined = false;\n } finally {\n this.mediaBusy.set(false);\n }\n }\n\n /**\n * Disconnect LiveKit first, then tell the server to drop staff and restore AI.\n * Only valid in listen mode — a taken-over call stays with the human.\n * Does not hang up the patient.\n */\n private async leaveMedia(notifyServer: boolean): Promise<void> {\n const id = this.callId();\n const wasJoined = this.staffJoined || this.staffMedia.isConnectedTo(id);\n try {\n await this.staffMedia.disconnect();\n } catch {\n /* ignore */\n }\n this.staffJoined = false;\n\n if (!notifyServer || !wasJoined || !id || this.callEnded()) return;\n\n this.mediaBusy.set(true);\n try {\n await firstValueFrom(this.api.postResumeAi(id));\n this.call.update((c) =>\n c\n ? {\n ...c,\n status: c.status === 'takeover' ? 'ai' : c.status,\n controlMode: 'ai',\n assigned: null,\n aiPaused: false,\n }\n : c,\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to resume AI';\n this.toast.show(message, 'error');\n } finally {\n this.mediaBusy.set(false);\n }\n }\n\n private applyJoinMetadata(data: {\n controlMode?: string | null;\n aiPaused?: boolean;\n aiHandedOff?: boolean;\n assigned?: string | null;\n mode: StaffMediaMode;\n }): void {\n this.call.update((c) => {\n if (!c) return c;\n const controlMode = (data.controlMode as CallControlMode) || data.mode;\n return {\n ...c,\n controlMode,\n aiPaused: typeof data.aiPaused === 'boolean' ? data.aiPaused : data.mode === 'takeover',\n aiHandedOff:\n data.aiHandedOff === true || data.mode === 'takeover' || c.aiHandedOff === true,\n assigned:\n data.assigned !== undefined && data.assigned !== null\n ? data.assigned\n : data.mode === 'takeover'\n ? c.assigned || 'You'\n : c.assigned,\n status: data.mode === 'takeover' ? 'takeover' : c.status,\n };\n });\n }\n\n /** Initial load — seeds transcript/tools from REST, then starts socket. */\n private loadCallMetadata(): void {\n const id = this.callId();\n if (!id) return;\n this.api.getLiveCall(id).subscribe(({ data }) => {\n this.call.set(data);\n this.loading.set(false);\n if (data) {\n this.stream.seed(data.transcript, data.tools);\n if (!this.callEnded()) {\n this.stream.watch(id, true);\n }\n }\n });\n }\n\n /** Metadata-only refresh after staff actions — does not overwrite live transcript/tools. */\n private refreshMetadata(): void {\n const id = this.callId();\n if (!id) return;\n this.api.getLiveCall(id).subscribe(({ data }) => {\n if (data) this.call.set(data);\n });\n }\n\n /** One-time metadata refresh after call ends (no socket). */\n private loadCallMetadataOnce(): void {\n const id = this.callId();\n if (!id) return;\n this.api.getLiveCall(id).subscribe(({ data }) => {\n if (data) {\n this.call.set(data);\n }\n });\n }\n\n private loadPostCallSummary(): void {\n const id = this.callId();\n if (!id || this.summaryLoaded) return;\n this.summaryLoading.set(true);\n this.api.getCallSummary(id).subscribe(({ data }) => {\n this.summaryLoading.set(false);\n this.summaryLoaded = true;\n if (!data) return;\n this.summary.set(data.summary);\n this.stream.seed(data.transcript, data.tools);\n });\n }\n}\n","<div class=\"eds-page live-monitor-page\">\n @if (loading()) {\n <p class=\"live-monitor-page__loading\">Loading call…</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__profile\">\n <eds-avatar\n class=\"live-monitor-page__avatar\"\n [initials]=\"profileInitials(c)\"\n [tone]=\"isUnknownCaller(c) ? 'gray' : 'brand'\"\n />\n\n <div class=\"live-monitor-page__profile-text\">\n <div class=\"live-monitor-page__name-row\">\n <h1 class=\"live-monitor-page__name\">{{ displayName(c) }}</h1>\n @if (goalBadge(c); as badge) {\n <span class=\"live-monitor-page__goal-badge\">{{ badge }}</span>\n }\n </div>\n <p class=\"live-monitor-page__profile-meta\">{{ profileMeta(c) }}</p>\n </div>\n </div>\n\n <div class=\"live-monitor-page__header-actions\">\n @if (!callEnded()) {\n @if (mediaState() === 'connected' && isMediaForThisCall() && !audioPlaybackAllowed()) {\n <button\n type=\"button\"\n class=\"live-monitor-page__header-btn live-monitor-page__header-btn--ghost\"\n (click)=\"enableSound()\"\n >\n <eds-icon name=\"volume_up\" [size]=\"18\" />\n Enable sound\n </button>\n }\n @if (isMediaForThisCall() && (mediaState() === 'connected' || mediaState() === 'connecting')) {\n <span\n class=\"live-monitor-page__media-pill\"\n [attr.data-mode]=\"mediaMode() || 'idle'\"\n [attr.data-state]=\"mediaState()\"\n >\n @if (mediaState() === 'connecting') {\n Connecting…\n } @else if (mediaMode() === 'takeover') {\n {{ micEnabled() ? 'On call · mic on' : 'On call · mic off' }}\n } @else {\n Joined\n }\n </span>\n }\n <button\n type=\"button\"\n class=\"live-monitor-page__header-btn live-monitor-page__header-btn--danger\"\n [disabled]=\"mediaBusy()\"\n (click)=\"endCall()\"\n >\n <eds-icon name=\"call_end\" [size]=\"18\" />\n End Call\n </button>\n @if (isTakeover()) {\n @if (mediaState() === 'connected' && isMediaForThisCall()) {\n <button\n type=\"button\"\n class=\"live-monitor-page__header-btn live-monitor-page__header-btn--ghost\"\n (click)=\"toggleMic()\"\n >\n <eds-icon [name]=\"micEnabled() ? 'mic' : 'mic_off'\" [size]=\"18\" />\n {{ micEnabled() ? 'Mute' : 'Unmute' }}\n </button>\n } @else {\n <button\n type=\"button\"\n class=\"live-monitor-page__header-btn live-monitor-page__header-btn--primary\"\n [disabled]=\"mediaBusy()\"\n (click)=\"takeover()\"\n >\n <eds-icon name=\"phone_in_talk\" [size]=\"18\" />\n Rejoin Audio\n </button>\n }\n } @else {\n <button\n type=\"button\"\n class=\"live-monitor-page__header-btn live-monitor-page__header-btn--primary\"\n [disabled]=\"mediaBusy()\"\n (click)=\"takeover()\"\n >\n <eds-icon name=\"phone_in_talk\" [size]=\"18\" />\n Take Over\n </button>\n }\n } @else {\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 }\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 [callerName]=\"c.name\"\n [callerMatched]=\"c.matched\"\n (staffMessageChange)=\"staffMessage.set($event)\"\n (sendMessage)=\"sendMessage()\"\n />\n\n @if (callEnded()) {\n <section class=\"live-monitor-page__post-call\">\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…</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]=\"22\" />\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 live-monitor-page__sidebar-block--grow\">\n <h3 class=\"live-monitor-page__section-title\">Goal Timeline</h3>\n <p class=\"live-monitor-page__goal\">{{ c.goal || '—' }}</p>\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]=\"14\" 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 </aside>\n </div>\n } @else {\n <p class=\"live-monitor-page__loading\">Call ended or not found.</p>\n }\n }\n</div>\n"],"names":[],"mappings":"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"}
@@ -1,48 +0,0 @@
1
- import * as i0 from '@angular/core';
2
- import { EventEmitter, HostListener, Output, Input, ChangeDetectionStrategy, Component } from '@angular/core';
3
-
4
- /**
5
- * Lightweight, reusable modal shell.
6
- *
7
- * Renders a centered card on top of a dimmed backdrop. Closes on backdrop
8
- * click, ESC key, or via the close button. Slot the modal body using
9
- * <ng-content>.
10
- */
11
- class ModalComponent {
12
- constructor() {
13
- this.maxWidth = '640px';
14
- this.closed = new EventEmitter();
15
- }
16
- onEscape() {
17
- this.closed.emit();
18
- }
19
- onBackdropClick(event) {
20
- if (event.target === event.currentTarget) {
21
- this.closed.emit();
22
- }
23
- }
24
- close() {
25
- this.closed.emit();
26
- }
27
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
28
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: ModalComponent, isStandalone: true, selector: "app-modal", inputs: { title: "title", subtitle: "subtitle", maxWidth: "maxWidth" }, outputs: { closed: "closed" }, host: { listeners: { "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: "<div class=\"modal-backdrop\" (click)=\"onBackdropClick($event)\" role=\"presentation\">\n <div\n class=\"modal-card\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"title\"\n [style.max-width]=\"maxWidth\"\n >\n <header class=\"modal-header\">\n <div class=\"modal-titles\">\n <h2 class=\"modal-title\">{{ title }}</h2>\n @if (subtitle) {\n <p class=\"modal-subtitle\">{{ subtitle }}</p>\n }\n </div>\n <button type=\"button\" class=\"modal-close\" (click)=\"close()\" aria-label=\"Close dialog\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </header>\n\n <div class=\"modal-body\">\n <ng-content />\n </div>\n </div>\n</div>\n", styles: [":host{display:contents}.modal-backdrop{position:fixed;inset:0;background:#141e326b;backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;padding:24px;z-index:1000;animation:fade-in .14s ease}.modal-card{width:100%;background:var(--eds-color-surface);border-radius:var(--eds-radius-lg);border:1px solid var(--eds-color-border);box-shadow:var(--eds-shadow-modal);max-height:calc(100vh - 48px);display:flex;flex-direction:column;overflow:hidden;animation:pop-in .14s ease}.modal-header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:20px 24px 14px;border-bottom:1px solid var(--eds-color-border)}.modal-titles{display:flex;flex-direction:column;gap:4px;min-width:0}.modal-title{margin:0;font-size:16px;font-weight:600;letter-spacing:-.005em;color:var(--eds-color-text)}.modal-subtitle{margin:0;font-size:13px;color:var(--eds-color-text-muted)}.modal-close{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:var(--eds-radius-md);border:1px solid transparent;background:transparent;color:var(--eds-color-text-muted);transition:background .12s ease,color .12s ease}.modal-close:hover{background:var(--eds-color-surface-alt);color:var(--eds-color-text)}.modal-close:focus-visible{outline:2px solid var(--eds-color-primary);outline-offset:2px}.modal-body{padding:20px 24px 24px;overflow-y:auto}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes pop-in{0%{opacity:0;transform:translateY(6px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
29
- }
30
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ModalComponent, decorators: [{
31
- type: Component,
32
- args: [{ selector: 'app-modal', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"modal-backdrop\" (click)=\"onBackdropClick($event)\" role=\"presentation\">\n <div\n class=\"modal-card\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"title\"\n [style.max-width]=\"maxWidth\"\n >\n <header class=\"modal-header\">\n <div class=\"modal-titles\">\n <h2 class=\"modal-title\">{{ title }}</h2>\n @if (subtitle) {\n <p class=\"modal-subtitle\">{{ subtitle }}</p>\n }\n </div>\n <button type=\"button\" class=\"modal-close\" (click)=\"close()\" aria-label=\"Close dialog\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </header>\n\n <div class=\"modal-body\">\n <ng-content />\n </div>\n </div>\n</div>\n", styles: [":host{display:contents}.modal-backdrop{position:fixed;inset:0;background:#141e326b;backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;padding:24px;z-index:1000;animation:fade-in .14s ease}.modal-card{width:100%;background:var(--eds-color-surface);border-radius:var(--eds-radius-lg);border:1px solid var(--eds-color-border);box-shadow:var(--eds-shadow-modal);max-height:calc(100vh - 48px);display:flex;flex-direction:column;overflow:hidden;animation:pop-in .14s ease}.modal-header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:20px 24px 14px;border-bottom:1px solid var(--eds-color-border)}.modal-titles{display:flex;flex-direction:column;gap:4px;min-width:0}.modal-title{margin:0;font-size:16px;font-weight:600;letter-spacing:-.005em;color:var(--eds-color-text)}.modal-subtitle{margin:0;font-size:13px;color:var(--eds-color-text-muted)}.modal-close{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:var(--eds-radius-md);border:1px solid transparent;background:transparent;color:var(--eds-color-text-muted);transition:background .12s ease,color .12s ease}.modal-close:hover{background:var(--eds-color-surface-alt);color:var(--eds-color-text)}.modal-close:focus-visible{outline:2px solid var(--eds-color-primary);outline-offset:2px}.modal-body{padding:20px 24px 24px;overflow-y:auto}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes pop-in{0%{opacity:0;transform:translateY(6px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}\n"] }]
33
- }], propDecorators: { title: [{
34
- type: Input,
35
- args: [{ required: true }]
36
- }], subtitle: [{
37
- type: Input
38
- }], maxWidth: [{
39
- type: Input
40
- }], closed: [{
41
- type: Output
42
- }], onEscape: [{
43
- type: HostListener,
44
- args: ['document:keydown.escape']
45
- }] } });
46
-
47
- export { ModalComponent as M };
48
- //# sourceMappingURL=edvak99-voice-receptionist-widget-modal.component-COfi7biN.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"edvak99-voice-receptionist-widget-modal.component-COfi7biN.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/shared/modal.component.ts","../../../projects/voice-receptionist-widget/src/lib/shared/modal.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n EventEmitter,\n HostListener,\n Input,\n Output,\n} from '@angular/core';\n\n/**\n * Lightweight, reusable modal shell.\n *\n * Renders a centered card on top of a dimmed backdrop. Closes on backdrop\n * click, ESC key, or via the close button. Slot the modal body using\n * <ng-content>.\n */\n@Component({\n selector: 'app-modal',\n standalone: true,\n templateUrl: './modal.component.html',\n styleUrl: './modal.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ModalComponent {\n @Input({ required: true }) title!: string;\n @Input() subtitle?: string;\n @Input() maxWidth = '640px';\n @Output() readonly closed = new EventEmitter<void>();\n\n @HostListener('document:keydown.escape')\n onEscape(): void {\n this.closed.emit();\n }\n\n onBackdropClick(event: MouseEvent): void {\n if (event.target === event.currentTarget) {\n this.closed.emit();\n }\n }\n\n close(): void {\n this.closed.emit();\n }\n}\n","<div class=\"modal-backdrop\" (click)=\"onBackdropClick($event)\" role=\"presentation\">\n <div\n class=\"modal-card\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"title\"\n [style.max-width]=\"maxWidth\"\n >\n <header class=\"modal-header\">\n <div class=\"modal-titles\">\n <h2 class=\"modal-title\">{{ title }}</h2>\n @if (subtitle) {\n <p class=\"modal-subtitle\">{{ subtitle }}</p>\n }\n </div>\n <button type=\"button\" class=\"modal-close\" (click)=\"close()\" aria-label=\"Close dialog\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </header>\n\n <div class=\"modal-body\">\n <ng-content />\n </div>\n </div>\n</div>\n"],"names":[],"mappings":";;;AASA;;;;;;AAMG;MAQU,cAAc,CAAA;AAP3B,IAAA,WAAA,GAAA;QAUW,IAAA,CAAA,QAAQ,GAAG,OAAO;AACR,QAAA,IAAA,CAAA,MAAM,GAAG,IAAI,YAAY,EAAQ;AAgBrD,IAAA;IAbC,QAAQ,GAAA;AACN,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;IACpB;AAEA,IAAA,eAAe,CAAC,KAAiB,EAAA;QAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,aAAa,EAAE;AACxC,YAAA,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;QACpB;IACF;IAEA,KAAK,GAAA;AACH,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;IACpB;+GAnBW,cAAc,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAAd,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,cAAc,8OCvB3B,+gCA6BA,EAAA,MAAA,EAAA,CAAA,wiDAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;4FDNa,cAAc,EAAA,UAAA,EAAA,CAAA;kBAP1B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,WAAW,EAAA,UAAA,EACT,IAAI,EAAA,eAAA,EAGC,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,+gCAAA,EAAA,MAAA,EAAA,CAAA,wiDAAA,CAAA,EAAA;8BAGpB,KAAK,EAAA,CAAA;sBAA/B,KAAK;uBAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAChB,QAAQ,EAAA,CAAA;sBAAhB;gBACQ,QAAQ,EAAA,CAAA;sBAAhB;gBACkB,MAAM,EAAA,CAAA;sBAAxB;gBAGD,QAAQ,EAAA,CAAA;sBADP,YAAY;uBAAC,yBAAyB;;;;;"}
@@ -1,268 +0,0 @@
1
- import * as i0 from '@angular/core';
2
- import { inject, EventEmitter, signal, ViewChild, Output, Input, ChangeDetectionStrategy, Component } from '@angular/core';
3
- import { RouterLink } from '@angular/router';
4
- import { A as AiReceptionistApiService, p as DEFAULT_PLACEHOLDERS } from './edvak99-voice-receptionist-widget-ai-receptionist-api.service-48cifVPF.mjs';
5
- import { R as REQUIRED_FIELD_OPTIONS, d as renderOpeningPreview, e as formatRequiredFields } from './edvak99-voice-receptionist-widget-outbound-purpose.util-Bdh4QfFe.mjs';
6
- import * as i1 from '@angular/forms';
7
- import { FormsModule } from '@angular/forms';
8
- import { E as EdsButtonComponent } from './edvak99-voice-receptionist-widget-eds-button.component-Bg5T5BN8.mjs';
9
- import { M as ModalComponent } from './edvak99-voice-receptionist-widget-modal.component-COfi7biN.mjs';
10
- import { a as EdsBadgeComponent, E as EdsIconComponent } from './edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-BsBQIS_f.mjs';
11
-
12
- class OutboundPurposeFormModalComponent {
13
- constructor() {
14
- this.api = inject(AiReceptionistApiService);
15
- this.open = false;
16
- this.purpose = null;
17
- this.closed = new EventEmitter();
18
- this.saved = new EventEmitter();
19
- this.fieldOptions = REQUIRED_FIELD_OPTIONS;
20
- this.saving = signal(false);
21
- this.error = signal(null);
22
- this.label = '';
23
- this.description = '';
24
- this.sampleOpening = '';
25
- this.openingTemplate = '';
26
- this.agentContextHint = '';
27
- this.requiredFields = new Set();
28
- this.allowsCustomScript = false;
29
- this.sortOrder = 1;
30
- this.isActive = true;
31
- this.placeholders = DEFAULT_PLACEHOLDERS;
32
- }
33
- get isEdit() {
34
- return !!this.purpose;
35
- }
36
- get previewText() {
37
- return renderOpeningPreview(this.sampleOpening, this.openingTemplate, this.placeholders);
38
- }
39
- ngOnChanges(changes) {
40
- if (changes['open']?.currentValue || changes['purpose']) {
41
- this.resetForm();
42
- }
43
- }
44
- close() {
45
- this.closed.emit();
46
- }
47
- toggleRequired(field) {
48
- const next = new Set(this.requiredFields);
49
- if (next.has(field))
50
- next.delete(field);
51
- else
52
- next.add(field);
53
- this.requiredFields = next;
54
- }
55
- isRequiredSelected(field) {
56
- return this.requiredFields.has(field);
57
- }
58
- insertPlaceholder(token) {
59
- const el = this.templateInput?.nativeElement;
60
- if (!el) {
61
- this.openingTemplate += token;
62
- return;
63
- }
64
- const start = el.selectionStart ?? this.openingTemplate.length;
65
- const end = el.selectionEnd ?? start;
66
- this.openingTemplate =
67
- this.openingTemplate.slice(0, start) + token + this.openingTemplate.slice(end);
68
- const pos = start + token.length;
69
- requestAnimationFrame(() => {
70
- el.focus();
71
- el.setSelectionRange(pos, pos);
72
- });
73
- }
74
- submit() {
75
- this.error.set(null);
76
- const trimmedLabel = this.label.trim();
77
- const trimmedTemplate = this.openingTemplate.trim();
78
- if (!trimmedLabel) {
79
- this.error.set('Label is required.');
80
- return;
81
- }
82
- if (!trimmedTemplate) {
83
- this.error.set('Opening template is required.');
84
- return;
85
- }
86
- if (this.sampleOpening.length > 500) {
87
- this.error.set('Sample opening must be 500 characters or fewer.');
88
- return;
89
- }
90
- this.saving.set(true);
91
- const fields = [...this.requiredFields];
92
- if (this.isEdit && this.purpose) {
93
- const body = {
94
- label: trimmedLabel,
95
- description: this.description.trim(),
96
- sampleOpening: this.sampleOpening.trim(),
97
- openingTemplate: trimmedTemplate,
98
- agentContextHint: this.agentContextHint.trim() || null,
99
- requiredFields: fields,
100
- allowsCustomScript: this.allowsCustomScript,
101
- sortOrder: this.sortOrder,
102
- isActive: this.isActive,
103
- };
104
- this.api.updateOutboundPurpose(this.purpose.purposeId, body).subscribe({
105
- next: (updated) => {
106
- this.saving.set(false);
107
- this.saved.emit(updated);
108
- this.close();
109
- },
110
- error: () => {
111
- this.saving.set(false);
112
- this.error.set('Failed to save purpose template.');
113
- },
114
- });
115
- return;
116
- }
117
- const body = {
118
- label: trimmedLabel,
119
- description: this.description.trim(),
120
- sampleOpening: this.sampleOpening.trim(),
121
- openingTemplate: trimmedTemplate,
122
- agentContextHint: this.agentContextHint.trim() || null,
123
- requiredFields: fields,
124
- allowsCustomScript: this.allowsCustomScript,
125
- sortOrder: this.sortOrder,
126
- };
127
- this.api.createOutboundPurpose(body).subscribe({
128
- next: (created) => {
129
- this.saving.set(false);
130
- this.saved.emit(created);
131
- this.close();
132
- },
133
- error: () => {
134
- this.saving.set(false);
135
- this.error.set('Failed to create purpose template.');
136
- },
137
- });
138
- }
139
- resetForm() {
140
- this.error.set(null);
141
- this.saving.set(false);
142
- if (this.purpose) {
143
- this.label = this.purpose.label;
144
- this.description = this.purpose.description;
145
- this.sampleOpening = this.purpose.sampleOpening;
146
- this.openingTemplate = this.purpose.openingTemplate;
147
- this.agentContextHint = this.purpose.agentContextHint ?? '';
148
- this.requiredFields = new Set(this.purpose.requiredFields);
149
- this.allowsCustomScript = this.purpose.allowsCustomScript;
150
- this.sortOrder = this.purpose.sortOrder;
151
- this.isActive = this.purpose.isActive;
152
- this.placeholders = this.purpose.placeholders?.length
153
- ? [...this.purpose.placeholders]
154
- : [...DEFAULT_PLACEHOLDERS];
155
- }
156
- else {
157
- this.label = '';
158
- this.description = '';
159
- this.sampleOpening = '';
160
- this.openingTemplate = 'Hi {{patientName}}, this is {{practiceName}}.';
161
- this.agentContextHint = '';
162
- this.requiredFields = new Set();
163
- this.allowsCustomScript = false;
164
- this.sortOrder = 1;
165
- this.isActive = true;
166
- this.placeholders = [...DEFAULT_PLACEHOLDERS];
167
- }
168
- }
169
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: OutboundPurposeFormModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
170
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: OutboundPurposeFormModalComponent, isStandalone: true, selector: "eds-outbound-purpose-form-modal", inputs: { open: "open", purpose: "purpose" }, outputs: { closed: "closed", saved: "saved" }, viewQueries: [{ propertyName: "templateInput", first: true, predicate: ["templateInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (open) {\n <app-modal\n [title]=\"isEdit ? 'Edit outbound purpose' : 'New outbound purpose'\"\n subtitle=\"Configure dialer fields and AI opening script\"\n maxWidth=\"720px\"\n (closed)=\"close()\"\n >\n <form class=\"purpose-form\" (ngSubmit)=\"submit()\" novalidate autocomplete=\"off\">\n <div class=\"purpose-form__grid\">\n <label class=\"purpose-form__field purpose-form__field--full\">\n <span class=\"eds-label\">Label <span class=\"purpose-form__req\">*</span></span>\n <input type=\"text\" class=\"purpose-form__input\" [(ngModel)]=\"label\" name=\"label\" required autocomplete=\"off\" />\n </label>\n\n <label class=\"purpose-form__field purpose-form__field--full\">\n <span class=\"eds-label\">Description</span>\n <input type=\"text\" class=\"purpose-form__input\" [(ngModel)]=\"description\" name=\"description\" autocomplete=\"off\" />\n </label>\n\n <label class=\"purpose-form__field purpose-form__field--full\">\n <span class=\"eds-label\">Sample opening (plain preview, max 500)</span>\n <textarea\n class=\"purpose-form__textarea\"\n rows=\"3\"\n maxlength=\"500\"\n [(ngModel)]=\"sampleOpening\"\n name=\"sampleOpening\" autocomplete=\"off\"></textarea>\n <span class=\"purpose-form__hint\">{{ sampleOpening.length }}/500</span>\n </label>\n\n <div class=\"purpose-form__field purpose-form__field--full\">\n <span class=\"eds-label\">Opening template <span class=\"purpose-form__req\">*</span></span>\n <div class=\"purpose-form__chips\">\n @for (chip of placeholders; track chip.token) {\n <button type=\"button\" class=\"purpose-form__chip\" (click)=\"insertPlaceholder(chip.token)\">\n {{ chip.label }}\n </button>\n }\n </div>\n <textarea\n #templateInput\n class=\"purpose-form__textarea\"\n rows=\"4\"\n [(ngModel)]=\"openingTemplate\"\n name=\"openingTemplate\"\n required autocomplete=\"off\"></textarea>\n </div>\n\n <label class=\"purpose-form__field purpose-form__field--full\">\n <span class=\"eds-label\">Agent context hint</span>\n <textarea\n class=\"purpose-form__textarea\"\n rows=\"2\"\n [(ngModel)]=\"agentContextHint\"\n name=\"agentContextHint\"\n placeholder=\"Optional guidance for the AI after the opening\" autocomplete=\"off\"></textarea>\n </label>\n\n <fieldset class=\"purpose-form__field purpose-form__field--full\">\n <legend class=\"eds-label\">Required fields</legend>\n <div class=\"purpose-form__checks\">\n @for (opt of fieldOptions; track opt.value) {\n <label class=\"purpose-form__check\">\n <input\n type=\"checkbox\"\n [checked]=\"isRequiredSelected(opt.value)\"\n (change)=\"toggleRequired(opt.value)\" autocomplete=\"off\" />\n {{ opt.label }}\n </label>\n }\n </div>\n </fieldset>\n\n <label class=\"purpose-form__check purpose-form__field--full\">\n <input type=\"checkbox\" [(ngModel)]=\"allowsCustomScript\" name=\"allowsCustomScript\" autocomplete=\"off\" />\n Allows custom script (dialer shows free-text script field)\n </label>\n\n <label class=\"purpose-form__field\">\n <span class=\"eds-label\">Sort order</span>\n <input type=\"number\" class=\"purpose-form__input\" [(ngModel)]=\"sortOrder\" name=\"sortOrder\" min=\"0\" autocomplete=\"off\" />\n </label>\n\n @if (isEdit) {\n <label class=\"purpose-form__check\">\n <input type=\"checkbox\" [(ngModel)]=\"isActive\" name=\"isActive\" autocomplete=\"off\" />\n Active\n </label>\n }\n </div>\n\n <section class=\"purpose-form__preview eds-card\">\n <h3 class=\"eds-label\">Live preview</h3>\n <p class=\"purpose-form__preview-text\">{{ previewText }}</p>\n </section>\n\n @if (error()) {\n <p class=\"purpose-form__error\" role=\"alert\">{{ error() }}</p>\n }\n\n <footer class=\"purpose-form__footer\">\n <eds-button variant=\"secondary\" type=\"button\" (click)=\"close()\" [disabled]=\"saving()\">\n Cancel\n </eds-button>\n <eds-button variant=\"primary\" type=\"submit\" [disabled]=\"saving()\">\n {{ saving() ? 'Saving\u2026' : isEdit ? 'Save changes' : 'Create purpose' }}\n </eds-button>\n </footer>\n </form>\n </app-modal>\n}\n", styles: [".purpose-form{display:flex;flex-direction:column;gap:16px}.purpose-form__grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.purpose-form__field{display:flex;flex-direction:column;gap:6px}.purpose-form__field--full{grid-column:1/-1}.purpose-form__input,.purpose-form__textarea{padding:8px 10px;border:1px solid var(--eds-border-subtle, #e4e4e7);border-radius:8px;font:inherit}.purpose-form__textarea{resize:vertical;min-height:72px}.purpose-form__hint{font-size:12px;color:var(--eds-text-secondary)}.purpose-form__req{color:#dc2626}.purpose-form__chips{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}.purpose-form__chip{border:1px solid var(--eds-border-subtle, #e4e4e7);background:var(--eds-surface-muted, #f4f4f5);border-radius:999px;padding:4px 10px;font-size:12px;cursor:pointer}.purpose-form__chip:hover{border-color:var(--eds-brand, #6366f1)}.purpose-form__checks{display:grid;grid-template-columns:1fr 1fr;gap:8px}.purpose-form__check{display:flex;align-items:center;gap:8px;font-size:13px}.purpose-form__preview{padding:12px;background:var(--eds-surface-muted, #f8fafc)}.purpose-form__preview-text{margin:8px 0 0;font-size:14px;line-height:1.5;white-space:pre-wrap}.purpose-form__error{color:#b91c1c;font-size:13px;margin:0}.purpose-form__footer{display:flex;justify-content:flex-end;gap:8px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: ModalComponent, selector: "app-modal", inputs: ["title", "subtitle", "maxWidth"], outputs: ["closed"] }, { kind: "component", type: EdsButtonComponent, selector: "eds-button", inputs: ["variant", "type", "disabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
171
- }
172
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: OutboundPurposeFormModalComponent, decorators: [{
173
- type: Component,
174
- args: [{ selector: 'eds-outbound-purpose-form-modal', imports: [FormsModule, ModalComponent, EdsButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (open) {\n <app-modal\n [title]=\"isEdit ? 'Edit outbound purpose' : 'New outbound purpose'\"\n subtitle=\"Configure dialer fields and AI opening script\"\n maxWidth=\"720px\"\n (closed)=\"close()\"\n >\n <form class=\"purpose-form\" (ngSubmit)=\"submit()\" novalidate autocomplete=\"off\">\n <div class=\"purpose-form__grid\">\n <label class=\"purpose-form__field purpose-form__field--full\">\n <span class=\"eds-label\">Label <span class=\"purpose-form__req\">*</span></span>\n <input type=\"text\" class=\"purpose-form__input\" [(ngModel)]=\"label\" name=\"label\" required autocomplete=\"off\" />\n </label>\n\n <label class=\"purpose-form__field purpose-form__field--full\">\n <span class=\"eds-label\">Description</span>\n <input type=\"text\" class=\"purpose-form__input\" [(ngModel)]=\"description\" name=\"description\" autocomplete=\"off\" />\n </label>\n\n <label class=\"purpose-form__field purpose-form__field--full\">\n <span class=\"eds-label\">Sample opening (plain preview, max 500)</span>\n <textarea\n class=\"purpose-form__textarea\"\n rows=\"3\"\n maxlength=\"500\"\n [(ngModel)]=\"sampleOpening\"\n name=\"sampleOpening\" autocomplete=\"off\"></textarea>\n <span class=\"purpose-form__hint\">{{ sampleOpening.length }}/500</span>\n </label>\n\n <div class=\"purpose-form__field purpose-form__field--full\">\n <span class=\"eds-label\">Opening template <span class=\"purpose-form__req\">*</span></span>\n <div class=\"purpose-form__chips\">\n @for (chip of placeholders; track chip.token) {\n <button type=\"button\" class=\"purpose-form__chip\" (click)=\"insertPlaceholder(chip.token)\">\n {{ chip.label }}\n </button>\n }\n </div>\n <textarea\n #templateInput\n class=\"purpose-form__textarea\"\n rows=\"4\"\n [(ngModel)]=\"openingTemplate\"\n name=\"openingTemplate\"\n required autocomplete=\"off\"></textarea>\n </div>\n\n <label class=\"purpose-form__field purpose-form__field--full\">\n <span class=\"eds-label\">Agent context hint</span>\n <textarea\n class=\"purpose-form__textarea\"\n rows=\"2\"\n [(ngModel)]=\"agentContextHint\"\n name=\"agentContextHint\"\n placeholder=\"Optional guidance for the AI after the opening\" autocomplete=\"off\"></textarea>\n </label>\n\n <fieldset class=\"purpose-form__field purpose-form__field--full\">\n <legend class=\"eds-label\">Required fields</legend>\n <div class=\"purpose-form__checks\">\n @for (opt of fieldOptions; track opt.value) {\n <label class=\"purpose-form__check\">\n <input\n type=\"checkbox\"\n [checked]=\"isRequiredSelected(opt.value)\"\n (change)=\"toggleRequired(opt.value)\" autocomplete=\"off\" />\n {{ opt.label }}\n </label>\n }\n </div>\n </fieldset>\n\n <label class=\"purpose-form__check purpose-form__field--full\">\n <input type=\"checkbox\" [(ngModel)]=\"allowsCustomScript\" name=\"allowsCustomScript\" autocomplete=\"off\" />\n Allows custom script (dialer shows free-text script field)\n </label>\n\n <label class=\"purpose-form__field\">\n <span class=\"eds-label\">Sort order</span>\n <input type=\"number\" class=\"purpose-form__input\" [(ngModel)]=\"sortOrder\" name=\"sortOrder\" min=\"0\" autocomplete=\"off\" />\n </label>\n\n @if (isEdit) {\n <label class=\"purpose-form__check\">\n <input type=\"checkbox\" [(ngModel)]=\"isActive\" name=\"isActive\" autocomplete=\"off\" />\n Active\n </label>\n }\n </div>\n\n <section class=\"purpose-form__preview eds-card\">\n <h3 class=\"eds-label\">Live preview</h3>\n <p class=\"purpose-form__preview-text\">{{ previewText }}</p>\n </section>\n\n @if (error()) {\n <p class=\"purpose-form__error\" role=\"alert\">{{ error() }}</p>\n }\n\n <footer class=\"purpose-form__footer\">\n <eds-button variant=\"secondary\" type=\"button\" (click)=\"close()\" [disabled]=\"saving()\">\n Cancel\n </eds-button>\n <eds-button variant=\"primary\" type=\"submit\" [disabled]=\"saving()\">\n {{ saving() ? 'Saving\u2026' : isEdit ? 'Save changes' : 'Create purpose' }}\n </eds-button>\n </footer>\n </form>\n </app-modal>\n}\n", styles: [".purpose-form{display:flex;flex-direction:column;gap:16px}.purpose-form__grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.purpose-form__field{display:flex;flex-direction:column;gap:6px}.purpose-form__field--full{grid-column:1/-1}.purpose-form__input,.purpose-form__textarea{padding:8px 10px;border:1px solid var(--eds-border-subtle, #e4e4e7);border-radius:8px;font:inherit}.purpose-form__textarea{resize:vertical;min-height:72px}.purpose-form__hint{font-size:12px;color:var(--eds-text-secondary)}.purpose-form__req{color:#dc2626}.purpose-form__chips{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}.purpose-form__chip{border:1px solid var(--eds-border-subtle, #e4e4e7);background:var(--eds-surface-muted, #f4f4f5);border-radius:999px;padding:4px 10px;font-size:12px;cursor:pointer}.purpose-form__chip:hover{border-color:var(--eds-brand, #6366f1)}.purpose-form__checks{display:grid;grid-template-columns:1fr 1fr;gap:8px}.purpose-form__check{display:flex;align-items:center;gap:8px;font-size:13px}.purpose-form__preview{padding:12px;background:var(--eds-surface-muted, #f8fafc)}.purpose-form__preview-text{margin:8px 0 0;font-size:14px;line-height:1.5;white-space:pre-wrap}.purpose-form__error{color:#b91c1c;font-size:13px;margin:0}.purpose-form__footer{display:flex;justify-content:flex-end;gap:8px}\n"] }]
175
- }], propDecorators: { open: [{
176
- type: Input
177
- }], purpose: [{
178
- type: Input
179
- }], closed: [{
180
- type: Output
181
- }], saved: [{
182
- type: Output
183
- }], templateInput: [{
184
- type: ViewChild,
185
- args: ['templateInput']
186
- }] } });
187
-
188
- class OutboundPurposesPageComponent {
189
- constructor() {
190
- this.api = inject(AiReceptionistApiService);
191
- this.purposes = signal([]);
192
- this.loading = signal(true);
193
- this.error = signal(null);
194
- this.modalOpen = signal(false);
195
- this.editing = signal(null);
196
- this.formatRequiredFields = formatRequiredFields;
197
- }
198
- ngOnInit() {
199
- this.load();
200
- }
201
- load() {
202
- this.loading.set(true);
203
- this.error.set(null);
204
- this.api.getOutboundPurposes(true).subscribe({
205
- next: ({ purposes }) => {
206
- this.purposes.set(purposes);
207
- this.loading.set(false);
208
- },
209
- error: () => {
210
- this.loading.set(false);
211
- this.error.set('Failed to load outbound purposes.');
212
- },
213
- });
214
- }
215
- openCreate() {
216
- this.editing.set(null);
217
- this.modalOpen.set(true);
218
- }
219
- openEdit(purpose) {
220
- this.editing.set(purpose);
221
- this.modalOpen.set(true);
222
- }
223
- closeModal() {
224
- this.modalOpen.set(false);
225
- this.editing.set(null);
226
- }
227
- onSaved() {
228
- this.load();
229
- }
230
- toggleActive(purpose, active) {
231
- this.api.updateOutboundPurpose(purpose.purposeId, { isActive: active }).subscribe({
232
- next: (updated) => {
233
- this.purposes.update((list) => list.map((p) => (p.purposeId === updated.purposeId ? updated : p)));
234
- },
235
- error: () => this.load(),
236
- });
237
- }
238
- deletePurpose(purpose) {
239
- if (purpose.isSystem) {
240
- this.toggleActive(purpose, false);
241
- return;
242
- }
243
- if (!confirm(`Delete "${purpose.label}"? This cannot be undone.`))
244
- return;
245
- this.api.deleteOutboundPurpose(purpose.purposeId).subscribe({
246
- next: () => this.load(),
247
- error: () => this.error.set('Failed to delete purpose.'),
248
- });
249
- }
250
- actionLabel(purpose) {
251
- return purpose.isSystem ? 'Deactivate' : 'Delete';
252
- }
253
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: OutboundPurposesPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
254
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: OutboundPurposesPageComponent, isStandalone: true, selector: "eds-outbound-purposes-page", ngImport: i0, template: "<div class=\"eds-page outbound-purposes-page\">\n <header class=\"outbound-purposes-page__header\">\n <div>\n <a routerLink=\"../settings\" class=\"outbound-purposes-page__back eds-body-sm\">\n <eds-icon name=\"arrow_back\" [size]=\"16\" /> Settings\n </a>\n <h1 class=\"eds-h1\">Outbound Call Purposes</h1>\n <p class=\"eds-body-sm\">Templates that drive outbound dialer fields and AI opening scripts</p>\n </div>\n <eds-button variant=\"primary\" (click)=\"openCreate()\">\n <eds-icon name=\"add\" [size]=\"18\" /> New purpose\n </eds-button>\n </header>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading purposes\u2026</p>\n } @else if (error()) {\n <p class=\"outbound-purposes-page__error eds-body-sm\" role=\"alert\">{{ error() }}</p>\n } @else {\n <article class=\"eds-card outbound-purposes-page__table-card\">\n <table class=\"eds-table\">\n <thead>\n <tr>\n <th>Label</th>\n <th>Description</th>\n <th>Required fields</th>\n <th>System</th>\n <th>Active</th>\n <th>Sort</th>\n <th>Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (p of purposes(); track p.purposeId) {\n <tr>\n <td class=\"eds-body-sm-bold\">{{ p.label }}</td>\n <td class=\"eds-body-sm\">{{ p.description || '\u2014' }}</td>\n <td class=\"eds-body-sm\">{{ formatRequiredFields(p.requiredFields) }}</td>\n <td>\n @if (p.isSystem) {\n <eds-badge tone=\"brand\">System</eds-badge>\n } @else {\n <span class=\"eds-body-sm\">\u2014</span>\n }\n </td>\n <td>\n <label class=\"outbound-purposes-page__toggle\">\n <input\n type=\"checkbox\"\n [checked]=\"p.isActive\"\n (change)=\"toggleActive(p, $any($event.target).checked)\" autocomplete=\"off\" />\n </label>\n </td>\n <td class=\"eds-mono-sm\">{{ p.sortOrder }}</td>\n <td class=\"outbound-purposes-page__actions\">\n <button type=\"button\" class=\"outbound-purposes-page__link\" (click)=\"openEdit(p)\">\n Edit\n </button>\n <button type=\"button\" class=\"outbound-purposes-page__link\" (click)=\"deletePurpose(p)\">\n {{ actionLabel(p) }}\n </button>\n </td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"7\" class=\"eds-body-sm\">No outbound purposes configured.</td>\n </tr>\n }\n </tbody>\n </table>\n </article>\n }\n\n <eds-outbound-purpose-form-modal\n [open]=\"modalOpen()\"\n [purpose]=\"editing()\"\n (closed)=\"closeModal()\"\n (saved)=\"onSaved()\"\n />\n</div>\n", styles: [".outbound-purposes-page__header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:20px}.outbound-purposes-page__back{display:inline-flex;align-items:center;gap:4px;margin-bottom:8px;text-decoration:none;color:var(--eds-text-secondary)}.outbound-purposes-page__table-card{padding:16px;overflow-x:auto}.outbound-purposes-page__toggle input{width:16px;height:16px}.outbound-purposes-page__actions{display:flex;gap:10px;white-space:nowrap}.outbound-purposes-page__link{border:none;background:none;color:var(--eds-brand, #6366f1);font-size:13px;cursor:pointer;padding:0}.outbound-purposes-page__error{color:#b91c1c}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: EdsBadgeComponent, selector: "eds-badge", inputs: ["tone"] }, { kind: "component", type: EdsButtonComponent, selector: "eds-button", inputs: ["variant", "type", "disabled"] }, { kind: "component", type: EdsIconComponent, selector: "eds-icon", inputs: ["name", "size", "fill", "weight"] }, { kind: "component", type: OutboundPurposeFormModalComponent, selector: "eds-outbound-purpose-form-modal", inputs: ["open", "purpose"], outputs: ["closed", "saved"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
255
- }
256
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: OutboundPurposesPageComponent, decorators: [{
257
- type: Component,
258
- args: [{ selector: 'eds-outbound-purposes-page', imports: [
259
- RouterLink,
260
- EdsBadgeComponent,
261
- EdsButtonComponent,
262
- EdsIconComponent,
263
- OutboundPurposeFormModalComponent,
264
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"eds-page outbound-purposes-page\">\n <header class=\"outbound-purposes-page__header\">\n <div>\n <a routerLink=\"../settings\" class=\"outbound-purposes-page__back eds-body-sm\">\n <eds-icon name=\"arrow_back\" [size]=\"16\" /> Settings\n </a>\n <h1 class=\"eds-h1\">Outbound Call Purposes</h1>\n <p class=\"eds-body-sm\">Templates that drive outbound dialer fields and AI opening scripts</p>\n </div>\n <eds-button variant=\"primary\" (click)=\"openCreate()\">\n <eds-icon name=\"add\" [size]=\"18\" /> New purpose\n </eds-button>\n </header>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading purposes\u2026</p>\n } @else if (error()) {\n <p class=\"outbound-purposes-page__error eds-body-sm\" role=\"alert\">{{ error() }}</p>\n } @else {\n <article class=\"eds-card outbound-purposes-page__table-card\">\n <table class=\"eds-table\">\n <thead>\n <tr>\n <th>Label</th>\n <th>Description</th>\n <th>Required fields</th>\n <th>System</th>\n <th>Active</th>\n <th>Sort</th>\n <th>Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (p of purposes(); track p.purposeId) {\n <tr>\n <td class=\"eds-body-sm-bold\">{{ p.label }}</td>\n <td class=\"eds-body-sm\">{{ p.description || '\u2014' }}</td>\n <td class=\"eds-body-sm\">{{ formatRequiredFields(p.requiredFields) }}</td>\n <td>\n @if (p.isSystem) {\n <eds-badge tone=\"brand\">System</eds-badge>\n } @else {\n <span class=\"eds-body-sm\">\u2014</span>\n }\n </td>\n <td>\n <label class=\"outbound-purposes-page__toggle\">\n <input\n type=\"checkbox\"\n [checked]=\"p.isActive\"\n (change)=\"toggleActive(p, $any($event.target).checked)\" autocomplete=\"off\" />\n </label>\n </td>\n <td class=\"eds-mono-sm\">{{ p.sortOrder }}</td>\n <td class=\"outbound-purposes-page__actions\">\n <button type=\"button\" class=\"outbound-purposes-page__link\" (click)=\"openEdit(p)\">\n Edit\n </button>\n <button type=\"button\" class=\"outbound-purposes-page__link\" (click)=\"deletePurpose(p)\">\n {{ actionLabel(p) }}\n </button>\n </td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"7\" class=\"eds-body-sm\">No outbound purposes configured.</td>\n </tr>\n }\n </tbody>\n </table>\n </article>\n }\n\n <eds-outbound-purpose-form-modal\n [open]=\"modalOpen()\"\n [purpose]=\"editing()\"\n (closed)=\"closeModal()\"\n (saved)=\"onSaved()\"\n />\n</div>\n", styles: [".outbound-purposes-page__header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:20px}.outbound-purposes-page__back{display:inline-flex;align-items:center;gap:4px;margin-bottom:8px;text-decoration:none;color:var(--eds-text-secondary)}.outbound-purposes-page__table-card{padding:16px;overflow-x:auto}.outbound-purposes-page__toggle input{width:16px;height:16px}.outbound-purposes-page__actions{display:flex;gap:10px;white-space:nowrap}.outbound-purposes-page__link{border:none;background:none;color:var(--eds-brand, #6366f1);font-size:13px;cursor:pointer;padding:0}.outbound-purposes-page__error{color:#b91c1c}\n"] }]
265
- }] });
266
-
267
- export { OutboundPurposesPageComponent };
268
- //# sourceMappingURL=edvak99-voice-receptionist-widget-outbound-purposes-page.component-fHVzpiVk.mjs.map