@edvak99/voice-receptionist-widget 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -43
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-CoVjr5Y3.mjs +113 -0
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-CoVjr5Y3.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-audit-page.component-ou9oxnJp.mjs +35 -0
- package/fesm2022/edvak99-voice-receptionist-widget-audit-page.component-ou9oxnJp.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-BV7KOhd9.mjs +316 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-BV7KOhd9.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-CWWma-6r.mjs +138 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-CWWma-6r.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-recording-player.component-B2IuwsVH.mjs +110 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-recording-player.component-B2IuwsVH.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-review.service-D-GutOar.mjs +29 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-review.service-D-GutOar.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-review.util-DQLIw7xb.mjs +70 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-review.util-DQLIw7xb.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-B93giErR.mjs +248 -0
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-B93giErR.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-PCnIWeOA.mjs +27 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-PCnIWeOA.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-elapsed.pipe-BrMNsxB5.mjs +136 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-elapsed.pipe-BrMNsxB5.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-DClMfxeY.mjs +2666 -0
- package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-DClMfxeY.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-knowledge-hub.page-BNsM-o3z.mjs +575 -0
- package/fesm2022/edvak99-voice-receptionist-widget-knowledge-hub.page-BNsM-o3z.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-list.service-C2UZJ5wF.mjs +120 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-list.service-C2UZJ5wF.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-DQTVJjsP.mjs +115 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-DQTVJjsP.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-C3iTtVdn.mjs +574 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-C3iTtVdn.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purpose.util-BQECIuJu.mjs +54 -0
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purpose.util-BQECIuJu.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-C10N-x5g.mjs +265 -0
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-C10N-x5g.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-BBE_H2jb.mjs +509 -0
- package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-BBE_H2jb.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-task.util-DdtI0CR3.mjs +52 -0
- package/fesm2022/edvak99-voice-receptionist-widget-task.util-DdtI0CR3.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-COnYcaMT.mjs +237 -0
- package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-COnYcaMT.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-Box7X-Cq.mjs +45 -0
- package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-Box7X-Cq.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget.mjs +1 -41
- package/fesm2022/edvak99-voice-receptionist-widget.mjs.map +1 -1
- package/lib/api-paths.d.ts +11 -0
- package/lib/components/call-conversation-panel/call-conversation-panel.component.d.ts +45 -0
- package/lib/components/call-recording-player/call-recording-player.component.d.ts +29 -0
- package/lib/components/call-tasks-panel/call-tasks-panel.component.d.ts +19 -0
- package/lib/components/live-transcript-panel/live-transcript-panel.component.d.ts +36 -0
- package/lib/components/outbound-dial-modal/outbound-dial-modal.component.d.ts +37 -0
- package/lib/components/outbound-purpose-form-modal/outbound-purpose-form-modal.component.d.ts +38 -0
- package/lib/components/task-detail-drawer/task-detail-drawer.component.d.ts +20 -0
- package/lib/config.d.ts +23 -0
- package/lib/extract-api-rows.d.ts +2 -0
- package/lib/knowledge/components/faq-empty-state/faq-empty-state.component.d.ts +22 -0
- package/lib/knowledge/components/faq-form/faq-form.component.d.ts +27 -0
- package/lib/knowledge/components/faq-search-tester/faq-search-tester.component.d.ts +17 -0
- package/lib/knowledge/mocks/practice-faq.seed.d.ts +2 -0
- package/lib/knowledge/models/practice-faq.model.d.ts +47 -0
- package/lib/knowledge/pages/knowledge-hub.page.d.ts +47 -0
- package/lib/knowledge/services/practice-faq-api.mock.d.ts +16 -0
- package/lib/knowledge/services/practice-faq-api.service.d.ts +22 -0
- package/lib/knowledge/utils/faq.util.d.ts +3 -0
- package/lib/layout/eds-app-rail.component.d.ts +6 -0
- package/lib/layout/eds-app-shell.component.d.ts +5 -0
- package/lib/layout/eds-module-nav.component.d.ts +24 -0
- package/lib/layout/eds-topbar.component.d.ts +29 -0
- package/lib/mocks/api-log.seed.d.ts +5 -0
- package/lib/mocks/outbound-purpose.seed.d.ts +3 -0
- package/lib/mocks/seed-data.d.ts +38 -0
- package/lib/models/ai-receptionist.types.d.ts +257 -0
- package/lib/models/api-dto.types.d.ts +215 -0
- package/lib/models/api-log.types.d.ts +53 -0
- package/lib/models/live-transcript.types.d.ts +26 -0
- package/lib/models/outbound-purpose.types.d.ts +50 -0
- package/lib/pages/analytics-page/analytics-page.component.d.ts +44 -0
- package/lib/pages/audit-page/audit-page.component.d.ts +14 -0
- package/lib/pages/call-history-detail-page/call-history-detail-page.component.d.ts +25 -0
- package/lib/pages/call-history-page/call-history-page.component.d.ts +42 -0
- package/lib/pages/dashboard-page/dashboard-page.component.d.ts +60 -0
- package/lib/pages/live-calls-page/live-calls-page.component.d.ts +37 -0
- package/lib/pages/live-monitor-page/live-monitor-page.component.d.ts +43 -0
- package/lib/pages/outbound-purposes-page/outbound-purposes-page.component.d.ts +24 -0
- package/lib/pages/settings-page/settings-fields.config.d.ts +35 -0
- package/lib/pages/settings-page/settings-page.component.d.ts +50 -0
- package/lib/pages/settings-page/tts-voice-presets.d.ts +7 -0
- package/lib/pages/tasks-page/tasks-page.component.d.ts +35 -0
- package/lib/pages/wizard-page/wizard-page.component.d.ts +17 -0
- package/lib/pipes/eds-elapsed.pipe.d.ts +7 -0
- package/lib/provide-voice-receptionist.d.ts +6 -0
- package/lib/routes.d.ts +8 -0
- package/lib/services/agent-online.service.d.ts +29 -0
- package/lib/services/ai-receptionist-api.mappers.d.ts +45 -0
- package/lib/services/ai-receptionist-api.mock.d.ts +150 -0
- package/lib/services/ai-receptionist-api.service.d.ts +156 -0
- package/lib/services/call-review.service.d.ts +12 -0
- package/lib/services/dashboard-socket.service.d.ts +26 -0
- package/lib/services/live-calls-list.service.d.ts +29 -0
- package/lib/services/live-transcript.service.d.ts +39 -0
- package/lib/shared/copy-button.component.d.ts +14 -0
- package/lib/shared/modal.component.d.ts +20 -0
- package/lib/shared/toast-container.component.d.ts +7 -0
- package/lib/shared/toast.service.d.ts +16 -0
- package/lib/ui/eds-avatar.component.d.ts +8 -0
- package/lib/ui/eds-badge.component.d.ts +7 -0
- package/lib/ui/eds-button.component.d.ts +9 -0
- package/lib/ui/eds-icon.component.d.ts +10 -0
- package/lib/ui/eds-pulse-dot.component.d.ts +5 -0
- package/lib/ui/eds-waveform.component.d.ts +8 -0
- package/lib/utils/call-outcome.util.d.ts +8 -0
- package/lib/utils/call-review.util.d.ts +8 -0
- package/lib/utils/outbound-purpose.util.d.ts +14 -0
- package/lib/utils/task.util.d.ts +10 -0
- package/lib/utils/tool-labels.util.d.ts +14 -0
- package/lib/utils/transcript-timing.util.d.ts +17 -0
- package/lib/utils/welcome-message.util.d.ts +5 -0
- package/package.json +12 -5
- package/public-api.d.ts +6 -2
- package/styles/_index.scss +55 -0
- package/styles/chat-ui.scss +133 -0
- package/styles/eds.scss +328 -0
- package/lib/voice-receptionist-widget.component.d.ts +0 -5
- package/lib/voice-receptionist-widget.service.d.ts +0 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edvak99-voice-receptionist-widget-knowledge-hub.page-BNsM-o3z.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/knowledge/models/practice-faq.model.ts","../../../projects/voice-receptionist-widget/src/lib/knowledge/mocks/practice-faq.seed.ts","../../../projects/voice-receptionist-widget/src/lib/knowledge/services/practice-faq-api.mock.ts","../../../projects/voice-receptionist-widget/src/lib/knowledge/services/practice-faq-api.service.ts","../../../projects/voice-receptionist-widget/src/lib/knowledge/utils/faq.util.ts","../../../projects/voice-receptionist-widget/src/lib/knowledge/components/faq-empty-state/faq-empty-state.component.ts","../../../projects/voice-receptionist-widget/src/lib/knowledge/components/faq-empty-state/faq-empty-state.component.html","../../../projects/voice-receptionist-widget/src/lib/knowledge/components/faq-form/faq-form.component.ts","../../../projects/voice-receptionist-widget/src/lib/knowledge/components/faq-form/faq-form.component.html","../../../projects/voice-receptionist-widget/src/lib/knowledge/components/faq-search-tester/faq-search-tester.component.ts","../../../projects/voice-receptionist-widget/src/lib/knowledge/components/faq-search-tester/faq-search-tester.component.html","../../../projects/voice-receptionist-widget/src/lib/knowledge/pages/knowledge-hub.page.ts","../../../projects/voice-receptionist-widget/src/lib/knowledge/pages/knowledge-hub.page.html"],"sourcesContent":["export interface PracticeFaq {\n faqId: string;\n question: string;\n answer: string;\n category: string | null;\n sortOrder: number;\n isActive: boolean;\n hasEmbedding: boolean;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreatePracticeFaqRequest {\n question: string;\n answer: string;\n category?: string;\n sortOrder?: number;\n isActive?: boolean;\n}\n\nexport interface UpdatePracticeFaqRequest {\n question?: string;\n answer?: string;\n category?: string | null;\n sortOrder?: number;\n isActive?: boolean;\n}\n\nexport interface PracticeFaqSearchResult {\n found: boolean;\n items: PracticeFaqSearchItem[];\n}\n\nexport interface PracticeFaqSearchItem {\n faqId: string;\n question: string;\n answer: string;\n category: string | null;\n score: number;\n}\n\nexport interface PracticeFaqListResponse {\n items: PracticeFaq[];\n}\n\nexport type FaqActiveFilter = 'all' | 'active' | 'inactive';\n\nexport const FAQ_CATEGORY_PRESETS = [\n 'hours',\n 'location',\n 'parking',\n 'insurance',\n 'services',\n 'policies',\n 'general',\n] as const;\n\nexport const FAQ_STARTER_TEMPLATES: Array<{\n label: string;\n question: string;\n answer: string;\n category: string;\n}> = [\n {\n label: 'Office hours',\n question: 'What are your office hours?',\n answer: 'We are open Monday through Friday, 9 AM to 6 PM Eastern.',\n category: 'hours',\n },\n {\n label: 'Parking / directions',\n question: 'Where should I park when I visit?',\n answer: 'Free parking is available in the lot behind the building on Oak Street.',\n category: 'parking',\n },\n {\n label: 'Insurance accepted',\n question: 'Do you accept my insurance?',\n answer: 'We accept most major insurance plans. Please call reception to confirm your specific plan.',\n category: 'insurance',\n },\n];\n","import { PracticeFaq } from '../models/practice-faq.model';\n\nconst now = Date.now();\n\nexport const SEED_PRACTICE_FAQS: PracticeFaq[] = [\n {\n faqId: 'faq-1',\n question: 'What are your office hours?',\n answer: 'We are open Monday through Friday, 9 AM to 6 PM Eastern.',\n category: 'hours',\n sortOrder: 0,\n isActive: true,\n hasEmbedding: true,\n createdAt: new Date(now - 86400000 * 14).toISOString(),\n updatedAt: new Date(now - 86400000 * 2).toISOString(),\n },\n {\n faqId: 'faq-2',\n question: 'Where is the clinic located?',\n answer: 'We are at 1200 Riverside Drive, Suite 200, Austin, Texas.',\n category: 'location',\n sortOrder: 1,\n isActive: true,\n hasEmbedding: true,\n createdAt: new Date(now - 86400000 * 10).toISOString(),\n updatedAt: new Date(now - 86400000 * 5).toISOString(),\n },\n {\n faqId: 'faq-3',\n question: 'Do you have parking?',\n answer: 'Yes, free patient parking is available in the lot behind the building.',\n category: 'parking',\n sortOrder: 2,\n isActive: true,\n hasEmbedding: true,\n createdAt: new Date(now - 86400000 * 8).toISOString(),\n updatedAt: new Date(now - 86400000 * 8).toISOString(),\n },\n {\n faqId: 'faq-4',\n question: 'Which insurance plans do you accept?',\n answer: 'We accept most major plans including Blue Cross, Aetna, and UnitedHealthcare.',\n category: 'insurance',\n sortOrder: 3,\n isActive: true,\n hasEmbedding: false,\n createdAt: new Date(now - 86400000 * 6).toISOString(),\n updatedAt: new Date(now - 86400000 * 1).toISOString(),\n },\n {\n faqId: 'faq-5',\n question: 'How do I request a callback?',\n answer: 'Leave a message with your name and number and we will call you back within one business day.',\n category: 'general',\n sortOrder: 4,\n isActive: false,\n hasEmbedding: true,\n createdAt: new Date(now - 86400000 * 3).toISOString(),\n updatedAt: new Date(now - 86400000 * 3).toISOString(),\n },\n];\n","import { Injectable, signal } from '@angular/core';\nimport { delay, Observable, of } from 'rxjs';\n\nimport { SEED_PRACTICE_FAQS } from '../mocks/practice-faq.seed';\nimport {\n CreatePracticeFaqRequest,\n PracticeFaq,\n PracticeFaqSearchResult,\n UpdatePracticeFaqRequest,\n} from '../models/practice-faq.model';\n\nconst MOCK_DELAY_MS = 200;\n\n@Injectable({ providedIn: 'root' })\nexport class PracticeFaqApiMockService {\n private readonly faqs = signal<PracticeFaq[]>(structuredClone(SEED_PRACTICE_FAQS));\n\n list(activeOnly = false): Observable<PracticeFaq[]> {\n let rows = [...this.faqs()].sort((a, b) => a.sortOrder - b.sortOrder);\n if (activeOnly) rows = rows.filter((f) => f.isActive);\n return of(structuredClone(rows)).pipe(delay(MOCK_DELAY_MS));\n }\n\n get(faqId: string): Observable<PracticeFaq | null> {\n const row = this.faqs().find((f) => f.faqId === faqId) ?? null;\n return of(row ? structuredClone(row) : null).pipe(delay(MOCK_DELAY_MS));\n }\n\n create(body: CreatePracticeFaqRequest): Observable<PracticeFaq> {\n const now = new Date().toISOString();\n const faq: PracticeFaq = {\n faqId: `faq-${Date.now()}`,\n question: body.question.trim(),\n answer: body.answer.trim(),\n category: body.category?.trim() || null,\n sortOrder: body.sortOrder ?? this.faqs().length,\n isActive: body.isActive ?? true,\n hasEmbedding: true,\n createdAt: now,\n updatedAt: now,\n };\n this.faqs.update((list) => [...list, faq]);\n return of(structuredClone(faq)).pipe(delay(MOCK_DELAY_MS));\n }\n\n update(faqId: string, body: UpdatePracticeFaqRequest): Observable<PracticeFaq> {\n let updated: PracticeFaq | undefined;\n this.faqs.update((list) =>\n list.map((f) => {\n if (f.faqId !== faqId) return f;\n updated = {\n ...f,\n question: body.question?.trim() ?? f.question,\n answer: body.answer?.trim() ?? f.answer,\n category: body.category !== undefined ? body.category : f.category,\n sortOrder: body.sortOrder ?? f.sortOrder,\n isActive: body.isActive ?? f.isActive,\n hasEmbedding: true,\n updatedAt: new Date().toISOString(),\n };\n return updated;\n }),\n );\n if (!updated) throw new Error('FAQ not found');\n const saved: PracticeFaq = updated;\n return of(structuredClone(saved)).pipe(delay(MOCK_DELAY_MS));\n }\n\n delete(faqId: string): Observable<{ success: boolean }> {\n this.faqs.update((list) => list.filter((f) => f.faqId !== faqId));\n return of({ success: true }).pipe(delay(MOCK_DELAY_MS));\n }\n\n search(q: string, limit = 3): Observable<PracticeFaqSearchResult> {\n const query = q.trim().toLowerCase();\n if (!query) {\n return of({ found: false, items: [] }).pipe(delay(MOCK_DELAY_MS));\n }\n const scored = this.faqs()\n .filter((f) => f.isActive)\n .map((f) => {\n const hay = `${f.question} ${f.answer}`.toLowerCase();\n const hit = hay.includes(query);\n const score = hit ? Math.min(0.95, 0.55 + query.length / Math.max(hay.length, 1)) : 0;\n return { faq: f, score };\n })\n .filter((r) => r.score > 0)\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n\n const items = scored.map(({ faq, score }) => ({\n faqId: faq.faqId,\n question: faq.question,\n answer: faq.answer,\n category: faq.category,\n score: Math.round(score * 100) / 100,\n }));\n\n return of({ found: items.length > 0, items }).pipe(delay(MOCK_DELAY_MS));\n }\n}\n","import { HttpClient, HttpParams } from '@angular/common/http';\nimport { Injectable, inject } from '@angular/core';\nimport { Observable, map } from 'rxjs';\n\nimport { createVoiceReceptionistPaths } from '../../api-paths';\nimport { VOICE_RECEPTIONIST_CONFIG } from '../../config';\nimport {\n CreatePracticeFaqRequest,\n PracticeFaq,\n PracticeFaqListResponse,\n PracticeFaqSearchResult,\n UpdatePracticeFaqRequest,\n} from '../models/practice-faq.model';\nimport { PracticeFaqApiMockService } from './practice-faq-api.mock';\n\n@Injectable({ providedIn: 'root' })\nexport class PracticeFaqApiService {\n private readonly http = inject(HttpClient);\n private readonly mock = inject(PracticeFaqApiMockService);\n private readonly config = inject(VOICE_RECEPTIONIST_CONFIG);\n private readonly paths = createVoiceReceptionistPaths(this.config);\n private readonly base = `${this.paths.aiReceptionistPath()}/knowledge/faqs`;\n\n private get useMock(): boolean {\n return !!this.config.useMockApi;\n }\n\n list(activeOnly = false): Observable<PracticeFaq[]> {\n if (this.useMock) return this.mock.list(activeOnly);\n let params = new HttpParams();\n if (activeOnly) params = params.set('activeOnly', 'true');\n return this.http.get<PracticeFaqListResponse | PracticeFaq[]>(this.base, { params }).pipe(\n map((body) => this.asList(body)),\n );\n }\n\n get(faqId: string): Observable<PracticeFaq | null> {\n if (this.useMock) return this.mock.get(faqId);\n const enc = encodeURIComponent(faqId);\n return this.http.get<PracticeFaq>(`${this.base}/${enc}`);\n }\n\n create(body: CreatePracticeFaqRequest): Observable<PracticeFaq> {\n if (this.useMock) return this.mock.create(body);\n return this.http.post<PracticeFaq>(this.base, body);\n }\n\n update(faqId: string, body: UpdatePracticeFaqRequest): Observable<PracticeFaq> {\n if (this.useMock) return this.mock.update(faqId, body);\n const enc = encodeURIComponent(faqId);\n return this.http.patch<PracticeFaq>(`${this.base}/${enc}`, body);\n }\n\n delete(faqId: string): Observable<{ success: boolean }> {\n if (this.useMock) return this.mock.delete(faqId);\n const enc = encodeURIComponent(faqId);\n return this.http.delete<{ success: boolean }>(`${this.base}/${enc}`);\n }\n\n search(q: string, limit = 3): Observable<PracticeFaqSearchResult> {\n if (this.useMock) return this.mock.search(q, limit);\n const params = new HttpParams().set('q', q).set('limit', String(limit));\n return this.http.get<PracticeFaqSearchResult>(`${this.base}/search`, { params });\n }\n\n private asList(body: PracticeFaqListResponse | PracticeFaq[]): PracticeFaq[] {\n if (Array.isArray(body)) return body;\n return body.items ?? [];\n }\n}\n","export function categoryBadgeClass(category: string | null): string {\n if (!category) return 'faq-chip--general';\n const safe = category.toLowerCase().replace(/[^a-z0-9-]/g, '');\n return `faq-chip--${safe || 'general'}`;\n}\n\nexport function truncateText(text: string, max = 80): string {\n const t = text?.trim() ?? '';\n if (t.length <= max) return t;\n return `${t.slice(0, max)}…`;\n}\n\nexport function scorePercent(score: number): number {\n return Math.round(Math.min(1, Math.max(0, score)) * 100);\n}\n","import { ChangeDetectionStrategy, Component, EventEmitter, Output } from '@angular/core';\n\nimport { FAQ_STARTER_TEMPLATES } from '../../models/practice-faq.model';\nimport { EdsButtonComponent } from '../../../ui/eds-button.component';\n\n@Component({\n selector: 'eds-faq-empty-state',\n imports: [EdsButtonComponent],\n templateUrl: './faq-empty-state.component.html',\n styleUrl: './faq-empty-state.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class FaqEmptyStateComponent {\n protected readonly starters = FAQ_STARTER_TEMPLATES;\n\n @Output() readonly add = new EventEmitter<void>();\n @Output() readonly useTemplate = new EventEmitter<(typeof FAQ_STARTER_TEMPLATES)[number]>();\n\n protected onAdd(): void {\n this.add.emit();\n }\n\n protected onTemplate(t: (typeof FAQ_STARTER_TEMPLATES)[number]): void {\n this.useTemplate.emit(t);\n }\n}\n","<div class=\"faq-empty-state\">\n <div class=\"faq-empty-state__icon\" aria-hidden=\"true\">?</div>\n <h2 class=\"eds-h2\">No FAQs yet</h2>\n <p class=\"eds-body-sm\">Add answers your AI receptionist can use on live calls.</p>\n <eds-button variant=\"primary\" (click)=\"onAdd()\">Add your first FAQ</eds-button>\n\n <p class=\"eds-label faq-empty-state__suggest\">Suggested starters</p>\n <ul class=\"faq-empty-state__starters\">\n @for (t of starters; track t.label) {\n <li>\n <button type=\"button\" class=\"faq-empty-state__starter\" (click)=\"onTemplate(t)\">\n {{ t.label }}\n </button>\n </li>\n }\n </ul>\n</div>\n","import {\n ChangeDetectionStrategy,\n Component,\n EventEmitter,\n Input,\n OnChanges,\n Output,\n signal,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\n\nimport {\n CreatePracticeFaqRequest,\n FAQ_CATEGORY_PRESETS,\n PracticeFaq,\n UpdatePracticeFaqRequest,\n} from '../../models/practice-faq.model';\nimport { EdsButtonComponent } from '../../../ui/eds-button.component';\nimport { ModalComponent } from '../../../shared/modal.component';\n\n@Component({\n selector: 'eds-faq-form',\n imports: [FormsModule, ModalComponent, EdsButtonComponent],\n templateUrl: './faq-form.component.html',\n styleUrl: './faq-form.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class FaqFormComponent implements OnChanges {\n @Input() open = false;\n @Input() faq: PracticeFaq | null = null;\n @Input() initialQuestion = '';\n @Input() initialAnswer = '';\n @Input() initialCategory = '';\n @Output() readonly closed = new EventEmitter<void>();\n @Output() readonly saved = new EventEmitter<CreatePracticeFaqRequest | UpdatePracticeFaqRequest>();\n\n protected readonly categories = FAQ_CATEGORY_PRESETS;\n protected readonly saving = signal(false);\n protected readonly error = signal<string | null>(null);\n\n protected question = '';\n protected answer = '';\n protected category = '';\n protected sortOrder = 0;\n protected isActive = true;\n\n protected get isEdit(): boolean {\n return !!this.faq;\n }\n\n ngOnChanges(): void {\n if (this.open) this.resetForm();\n }\n\n protected close(): void {\n this.closed.emit();\n }\n\n protected submit(): void {\n this.error.set(null);\n const q = this.question.trim();\n const a = this.answer.trim();\n if (q.length < 3 || q.length > 500) {\n this.error.set('Question must be 3–500 characters.');\n return;\n }\n if (a.length < 3 || a.length > 2000) {\n this.error.set('Answer must be 3–2000 characters.');\n return;\n }\n const cat = this.category.trim();\n if (cat.length > 64) {\n this.error.set('Category must be 64 characters or fewer.');\n return;\n }\n\n const payload = {\n question: q,\n answer: a,\n category: cat || undefined,\n sortOrder: this.sortOrder,\n isActive: this.isActive,\n };\n\n this.saved.emit(payload);\n }\n\n private resetForm(): void {\n this.error.set(null);\n this.saving.set(false);\n if (this.faq) {\n this.question = this.faq.question;\n this.answer = this.faq.answer;\n this.category = this.faq.category ?? '';\n this.sortOrder = this.faq.sortOrder;\n this.isActive = this.faq.isActive;\n } else {\n this.question = this.initialQuestion;\n this.answer = this.initialAnswer;\n this.category = this.initialCategory;\n this.sortOrder = 0;\n this.isActive = true;\n }\n }\n}\n","@if (open) {\n <app-modal\n [title]=\"isEdit ? 'Edit FAQ' : 'Add FAQ'\"\n subtitle=\"Short, speakable answers work best on phone calls\"\n maxWidth=\"640px\"\n (closed)=\"close()\"\n >\n <form class=\"faq-form\" (ngSubmit)=\"submit()\" novalidate>\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Question <span class=\"faq-form__req\">*</span></span>\n <span class=\"faq-form__hint\">How a caller might ask</span>\n <textarea\n class=\"faq-form__textarea\"\n rows=\"2\"\n maxlength=\"500\"\n [(ngModel)]=\"question\"\n name=\"question\"\n required\n ></textarea>\n <span class=\"faq-form__count\">{{ question.length }}/500</span>\n </label>\n\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Answer <span class=\"faq-form__req\">*</span></span>\n <span class=\"faq-form__hint\">Write how you want the AI to speak on the phone — short, clear, no bullet points.</span>\n <textarea\n class=\"faq-form__textarea\"\n rows=\"4\"\n maxlength=\"2000\"\n [(ngModel)]=\"answer\"\n name=\"answer\"\n required\n ></textarea>\n <span class=\"faq-form__count\">{{ answer.length }}/2000</span>\n </label>\n\n <div class=\"faq-form__row\">\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Category</span>\n <select class=\"faq-form__input\" [(ngModel)]=\"category\" name=\"category\">\n <option value=\"\">— None —</option>\n @for (c of categories; track c) {\n <option [value]=\"c\">{{ c }}</option>\n }\n </select>\n </label>\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Sort order</span>\n <input type=\"number\" class=\"faq-form__input\" [(ngModel)]=\"sortOrder\" name=\"sortOrder\" min=\"0\" />\n </label>\n </div>\n\n <label class=\"faq-form__check\">\n <input type=\"checkbox\" [(ngModel)]=\"isActive\" name=\"isActive\" />\n Active (voice agent can use this answer)\n </label>\n\n <section class=\"faq-form__preview eds-card\">\n <h3 class=\"eds-label\">Live preview</h3>\n <p class=\"faq-form__preview-line\"><strong>Caller asks:</strong> “{{ question || '…' }}”</p>\n <p class=\"faq-form__preview-line\"><strong>AI would say:</strong> “{{ answer || '…' }}”</p>\n </section>\n\n @if (error()) {\n <p class=\"faq-form__error\" role=\"alert\">{{ error() }}</p>\n }\n\n <footer class=\"faq-form__footer\">\n <eds-button variant=\"secondary\" type=\"button\" (click)=\"close()\">Cancel</eds-button>\n <eds-button variant=\"primary\" type=\"submit\">Save</eds-button>\n </footer>\n </form>\n </app-modal>\n}\n","import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\n\nimport { PracticeFaqSearchItem } from '../../models/practice-faq.model';\nimport { PracticeFaqApiService } from '../../services/practice-faq-api.service';\nimport { categoryBadgeClass, scorePercent } from '../../utils/faq.util';\nimport { EdsButtonComponent } from '../../../ui/eds-button.component';\n\n@Component({\n selector: 'eds-faq-search-tester',\n imports: [FormsModule, EdsButtonComponent],\n templateUrl: './faq-search-tester.component.html',\n styleUrl: './faq-search-tester.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class FaqSearchTesterComponent {\n private readonly api = inject(PracticeFaqApiService);\n\n protected query = '';\n protected readonly loading = signal(false);\n protected readonly searched = signal(false);\n protected readonly found = signal(false);\n protected readonly results = signal<PracticeFaqSearchItem[]>([]);\n protected readonly error = signal<string | null>(null);\n\n protected categoryClass = categoryBadgeClass;\n protected scorePercent = scorePercent;\n\n protected search(): void {\n const q = this.query.trim();\n if (!q) return;\n this.loading.set(true);\n this.error.set(null);\n this.searched.set(false);\n this.api.search(q, 3).subscribe({\n next: (res) => {\n this.loading.set(false);\n this.searched.set(true);\n this.found.set(res.found);\n this.results.set(res.items);\n },\n error: () => {\n this.loading.set(false);\n this.error.set('Search failed. Please try again.');\n },\n });\n }\n}\n","<aside class=\"eds-card faq-search-tester\">\n <h2 class=\"eds-h2\">Test voice search</h2>\n <p class=\"eds-body-sm faq-search-tester__help\">This uses the same search as live calls.</p>\n\n <label class=\"faq-search-tester__field\">\n <span class=\"eds-label\">What a caller might ask…</span>\n <input\n type=\"search\"\n class=\"faq-search-tester__input\"\n placeholder=\"Do you have Saturday hours?\"\n [(ngModel)]=\"query\"\n (keyup.enter)=\"search()\"\n />\n </label>\n\n <eds-button variant=\"primary\" [disabled]=\"loading() || !query.trim()\" (click)=\"search()\">\n {{ loading() ? 'Searching…' : 'Test search' }}\n </eds-button>\n\n @if (error()) {\n <p class=\"faq-search-tester__error\" role=\"alert\">{{ error() }}</p>\n }\n\n @if (searched()) {\n @if (found()) {\n <ul class=\"faq-search-tester__results\">\n @for (item of results(); track item.faqId) {\n <li class=\"faq-search-tester__result\">\n <div class=\"faq-search-tester__score-row\">\n <span class=\"eds-label\">Relevance</span>\n <div class=\"faq-search-tester__score-bar\">\n <span class=\"faq-search-tester__score-fill\" [style.width.%]=\"scorePercent(item.score)\"></span>\n </div>\n <span class=\"eds-mono-sm\">{{ scorePercent(item.score) }}%</span>\n </div>\n @if (item.category) {\n <span class=\"faq-chip\" [class]=\"categoryClass(item.category)\">{{ item.category }}</span>\n }\n <p class=\"faq-search-tester__question\">{{ item.question }}</p>\n <p class=\"faq-search-tester__answer\">{{ item.answer }}</p>\n </li>\n }\n </ul>\n } @else {\n <p class=\"faq-search-tester__no-match\" role=\"status\">\n No match — voice agent will offer to transfer to reception\n </p>\n }\n }\n</aside>\n","import { ChangeDetectionStrategy, Component, computed, inject, OnInit, signal } from '@angular/core';\nimport { DatePipe } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\n\nimport {\n CreatePracticeFaqRequest,\n FAQ_CATEGORY_PRESETS,\n FaqActiveFilter,\n PracticeFaq,\n UpdatePracticeFaqRequest,\n} from '../models/practice-faq.model';\nimport { PracticeFaqApiService } from '../services/practice-faq-api.service';\nimport { categoryBadgeClass, truncateText } from '../utils/faq.util';\nimport { FaqEmptyStateComponent } from '../components/faq-empty-state/faq-empty-state.component';\nimport { FaqFormComponent } from '../components/faq-form/faq-form.component';\nimport { FaqSearchTesterComponent } from '../components/faq-search-tester/faq-search-tester.component';\nimport { EdsButtonComponent } from '../../ui/eds-button.component';\nimport { EdsIconComponent } from '../../ui/eds-icon.component';\nimport { ToastService } from '../../shared/toast.service';\n\n@Component({\n selector: 'eds-knowledge-hub-page',\n imports: [\n DatePipe,\n FormsModule,\n EdsButtonComponent,\n EdsIconComponent,\n FaqEmptyStateComponent,\n FaqFormComponent,\n FaqSearchTesterComponent,\n ],\n templateUrl: './knowledge-hub.page.html',\n styleUrl: './knowledge-hub.page.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class KnowledgeHubPageComponent implements OnInit {\n private readonly api = inject(PracticeFaqApiService);\n private readonly toast = inject(ToastService);\n\n protected readonly allFaqs = signal<PracticeFaq[]>([]);\n protected readonly loading = signal(true);\n protected readonly error = signal<string | null>(null);\n protected readonly activeFilter = signal<FaqActiveFilter>('all');\n protected readonly categoryFilter = signal<string>('');\n protected readonly listSearch = signal('');\n protected readonly formOpen = signal(false);\n protected readonly editing = signal<PracticeFaq | null>(null);\n protected readonly formSeed = signal<{ question: string; answer: string; category: string }>({\n question: '',\n answer: '',\n category: '',\n });\n\n protected readonly categories = FAQ_CATEGORY_PRESETS;\n protected readonly activeTabs: { id: FaqActiveFilter; label: string }[] = [\n { id: 'all', label: 'All' },\n { id: 'active', label: 'Active only' },\n { id: 'inactive', label: 'Inactive' },\n ];\n\n protected readonly faqs = computed(() => {\n let rows = [...this.allFaqs()];\n const active = this.activeFilter();\n if (active === 'active') rows = rows.filter((f) => f.isActive);\n if (active === 'inactive') rows = rows.filter((f) => !f.isActive);\n const cat = this.categoryFilter();\n if (cat) rows = rows.filter((f) => (f.category ?? '') === cat);\n const q = this.listSearch().trim().toLowerCase();\n if (q) {\n rows = rows.filter(\n (f) =>\n f.question.toLowerCase().includes(q) ||\n f.answer.toLowerCase().includes(q) ||\n (f.category?.toLowerCase().includes(q) ?? false),\n );\n }\n return rows.sort((a, b) => a.sortOrder - b.sortOrder);\n });\n\n protected categoryClass = categoryBadgeClass;\n protected truncate = truncateText;\n\n ngOnInit(): void {\n this.load();\n }\n\n protected load(): void {\n this.loading.set(true);\n this.error.set(null);\n this.api.list().subscribe({\n next: (items) => {\n this.allFaqs.set(items);\n this.loading.set(false);\n },\n error: () => {\n this.loading.set(false);\n this.error.set('Failed to load FAQs.');\n },\n });\n }\n\n protected setActiveFilter(id: FaqActiveFilter): void {\n this.activeFilter.set(id);\n }\n\n protected toggleCategory(cat: string): void {\n this.categoryFilter.update((c) => (c === cat ? '' : cat));\n }\n\n protected isCategorySelected(cat: string): boolean {\n return this.categoryFilter() === cat;\n }\n\n protected openCreate(): void {\n this.editing.set(null);\n this.formSeed.set({ question: '', answer: '', category: '' });\n this.formOpen.set(true);\n }\n\n protected openEdit(faq: PracticeFaq): void {\n this.editing.set(faq);\n this.formOpen.set(true);\n }\n\n protected closeForm(): void {\n this.formOpen.set(false);\n this.editing.set(null);\n }\n\n protected onStarterTemplate(t: { question: string; answer: string; category: string }): void {\n this.editing.set(null);\n this.formSeed.set({ question: t.question, answer: t.answer, category: t.category });\n this.formOpen.set(true);\n }\n\n protected onSave(payload: CreatePracticeFaqRequest | UpdatePracticeFaqRequest): void {\n const edit = this.editing();\n if (edit) {\n this.api.update(edit.faqId, payload).subscribe({\n next: (updated) => {\n this.allFaqs.update((list) => list.map((f) => (f.faqId === updated.faqId ? updated : f)));\n this.closeForm();\n this.toast.show('FAQ saved — voice agent updated', 'success');\n },\n error: () => this.toast.show('Failed to save FAQ', 'error'),\n });\n return;\n }\n this.api.create(payload as CreatePracticeFaqRequest).subscribe({\n next: (created) => {\n this.allFaqs.update((list) => [...list, created]);\n this.closeForm();\n this.toast.show('Your AI receptionist can now answer this on live calls.', 'success');\n },\n error: () => this.toast.show('Failed to create FAQ', 'error'),\n });\n }\n\n protected toggleActive(faq: PracticeFaq, active: boolean): void {\n this.allFaqs.update((list) =>\n list.map((f) => (f.faqId === faq.faqId ? { ...f, isActive: active } : f)),\n );\n this.api.update(faq.faqId, { isActive: active }).subscribe({\n next: (updated) => {\n this.allFaqs.update((list) => list.map((f) => (f.faqId === updated.faqId ? updated : f)));\n },\n error: () => {\n this.allFaqs.update((list) =>\n list.map((f) => (f.faqId === faq.faqId ? { ...f, isActive: !active } : f)),\n );\n this.toast.show('Failed to update FAQ status', 'error');\n },\n });\n }\n\n protected deleteFaq(faq: PracticeFaq): void {\n const msg =\n 'Remove this FAQ from the knowledge base? The voice agent will no longer use it.';\n if (!confirm(msg)) return;\n this.api.delete(faq.faqId).subscribe({\n next: () => {\n this.allFaqs.update((list) => list.filter((f) => f.faqId !== faq.faqId));\n this.toast.show('FAQ removed', 'success');\n },\n error: () => this.toast.show('Failed to delete FAQ', 'error'),\n });\n }\n}\n","<div class=\"eds-page knowledge-hub\">\n <header class=\"knowledge-hub__header\">\n <div>\n <h1 class=\"eds-h1\">Practice knowledge base</h1>\n <p class=\"eds-body-sm\">\n Answers here are used by your AI receptionist on phone calls. Keep replies short and factual.\n </p>\n </div>\n <eds-button variant=\"primary\" class=\"knowledge-hub__add-desktop\" (click)=\"openCreate()\">\n <eds-icon name=\"add\" [size]=\"18\" /> Add FAQ\n </eds-button>\n </header>\n\n <div class=\"knowledge-hub__banner eds-card\" role=\"note\">\n <eds-icon name=\"info\" [size]=\"18\" />\n <p class=\"eds-body-sm\">\n Do not put patient-specific or clinical advice here. Use general practice information only.\n </p>\n </div>\n\n <div class=\"knowledge-hub__layout\">\n <main class=\"knowledge-hub__main\">\n <nav class=\"knowledge-hub__tabs\" aria-label=\"FAQ status\">\n @for (t of activeTabs; track t.id) {\n <button\n type=\"button\"\n class=\"knowledge-hub__tab\"\n [class.knowledge-hub__tab--active]=\"activeFilter() === t.id\"\n (click)=\"setActiveFilter(t.id)\"\n >\n {{ t.label }}\n </button>\n }\n </nav>\n\n <div class=\"knowledge-hub__filters eds-card\">\n <input\n type=\"search\"\n class=\"knowledge-hub__search\"\n placeholder=\"Filter by question or answer…\"\n [ngModel]=\"listSearch()\"\n (ngModelChange)=\"listSearch.set($event)\"\n />\n <div class=\"knowledge-hub__chips\">\n @for (cat of categories; track cat) {\n <button\n type=\"button\"\n class=\"faq-chip knowledge-hub__chip\"\n [class]=\"'faq-chip--' + cat\"\n [class.knowledge-hub__chip--on]=\"isCategorySelected(cat)\"\n (click)=\"toggleCategory(cat)\"\n >\n {{ cat }}\n </button>\n }\n </div>\n </div>\n\n @if (loading()) {\n <div class=\"knowledge-hub__skeleton eds-card\">\n @for (i of [1, 2, 3]; track i) {\n <div class=\"knowledge-hub__skel-row\"></div>\n }\n </div>\n } @else if (error()) {\n <p class=\"knowledge-hub__error eds-body-sm\" role=\"alert\">{{ error() }}</p>\n <eds-button variant=\"secondary\" (click)=\"load()\">Retry</eds-button>\n } @else if (allFaqs().length === 0) {\n <div class=\"eds-card\">\n <eds-faq-empty-state (add)=\"openCreate()\" (useTemplate)=\"onStarterTemplate($event)\" />\n </div>\n } @else if (faqs().length === 0) {\n <p class=\"eds-body-sm knowledge-hub__empty-filter\">No FAQs match your filters.</p>\n } @else {\n <div class=\"eds-card knowledge-hub__table-wrap\">\n <table class=\"eds-table knowledge-hub__table\">\n <thead>\n <tr>\n <th>Question</th>\n <th>Answer</th>\n <th>Category</th>\n <th>Active</th>\n <th>Updated</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n @for (faq of faqs(); track faq.faqId) {\n <tr [class.knowledge-hub__row--inactive]=\"!faq.isActive\">\n <td class=\"knowledge-hub__question\" [title]=\"faq.question\">{{ faq.question }}</td>\n <td class=\"knowledge-hub__answer\" [title]=\"faq.answer\">{{ truncate(faq.answer, 72) }}</td>\n <td>\n @if (faq.category) {\n <span class=\"faq-chip\" [class]=\"categoryClass(faq.category)\">{{ faq.category }}</span>\n } @else {\n <span class=\"eds-body-sm\">—</span>\n }\n </td>\n <td>\n <label class=\"knowledge-hub__toggle\">\n <input\n type=\"checkbox\"\n [checked]=\"faq.isActive\"\n (change)=\"toggleActive(faq, $any($event.target).checked)\"\n [attr.aria-label]=\"'Toggle active for ' + faq.question\"\n />\n <span class=\"knowledge-hub__status\" [class.knowledge-hub__status--on]=\"faq.isActive\">\n {{ faq.isActive ? 'Active' : 'Inactive' }}\n </span>\n </label>\n </td>\n <td class=\"eds-mono-sm\">{{ faq.updatedAt | date: 'mediumDate' }}</td>\n <td class=\"knowledge-hub__actions\">\n <button type=\"button\" class=\"knowledge-hub__link\" (click)=\"openEdit(faq)\">Edit</button>\n <button type=\"button\" class=\"knowledge-hub__link knowledge-hub__link--danger\" (click)=\"deleteFaq(faq)\">\n Delete\n </button>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </main>\n\n <eds-faq-search-tester />\n </div>\n\n <button type=\"button\" class=\"knowledge-hub__fab\" aria-label=\"Add FAQ\" (click)=\"openCreate()\">\n <eds-icon name=\"add\" [size]=\"24\" />\n </button>\n\n <eds-faq-form\n [open]=\"formOpen()\"\n [faq]=\"editing()\"\n [initialQuestion]=\"formSeed().question\"\n [initialAnswer]=\"formSeed().answer\"\n [initialCategory]=\"formSeed().category\"\n (closed)=\"closeForm()\"\n (saved)=\"onSave($event)\"\n />\n</div>\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, signal, Injectable } from '@angular/core';
|
|
3
|
+
import { interval, filter, switchMap, catchError, of } from 'rxjs';
|
|
4
|
+
import { A as AiReceptionistApiService, V as VOICE_RECEPTIONIST_CONFIG } from './edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-DClMfxeY.mjs';
|
|
5
|
+
import { D as DashboardSocketService } from './edvak99-voice-receptionist-widget-eds-elapsed.pipe-BrMNsxB5.mjs';
|
|
6
|
+
|
|
7
|
+
const DEFAULT_POLL_MS = 12_000;
|
|
8
|
+
/**
|
|
9
|
+
* Shared live calls list — REST + `live_calls_updated` socket + fallback poll.
|
|
10
|
+
*/
|
|
11
|
+
class LiveCallsListService {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.api = inject(AiReceptionistApiService);
|
|
14
|
+
this.socket = inject(DashboardSocketService);
|
|
15
|
+
this.config = inject(VOICE_RECEPTIONIST_CONFIG);
|
|
16
|
+
this.calls = signal([]);
|
|
17
|
+
this.loading = signal(false);
|
|
18
|
+
this.error = signal(null);
|
|
19
|
+
this.lastUpdated = signal(null);
|
|
20
|
+
this.subscriberCount = 0;
|
|
21
|
+
this.mockPollTimer = null;
|
|
22
|
+
this.onLiveCallsUpdated = () => this.refresh(false);
|
|
23
|
+
this.onSocketConnect = () => this.refresh(false);
|
|
24
|
+
}
|
|
25
|
+
start() {
|
|
26
|
+
this.subscriberCount++;
|
|
27
|
+
if (this.subscriberCount > 1) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
this.socket.acquire();
|
|
31
|
+
this.socket.on('live_calls_updated', this.onLiveCallsUpdated);
|
|
32
|
+
this.socket.onConnect(this.onSocketConnect);
|
|
33
|
+
this.bindVisibility();
|
|
34
|
+
this.refresh(true);
|
|
35
|
+
this.startFallbackPoll();
|
|
36
|
+
}
|
|
37
|
+
stop() {
|
|
38
|
+
this.subscriberCount = Math.max(0, this.subscriberCount - 1);
|
|
39
|
+
if (this.subscriberCount > 0) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
this.stopFallbackPoll();
|
|
43
|
+
this.unbindVisibility();
|
|
44
|
+
this.socket.off('live_calls_updated', this.onLiveCallsUpdated);
|
|
45
|
+
this.socket.offConnect(this.onSocketConnect);
|
|
46
|
+
this.socket.release();
|
|
47
|
+
}
|
|
48
|
+
refresh(showLoading = true) {
|
|
49
|
+
if (showLoading && this.calls().length === 0) {
|
|
50
|
+
this.loading.set(true);
|
|
51
|
+
}
|
|
52
|
+
this.api.getLiveCalls().subscribe({
|
|
53
|
+
next: ({ data }) => {
|
|
54
|
+
this.calls.set(data);
|
|
55
|
+
this.lastUpdated.set(Date.now());
|
|
56
|
+
this.loading.set(false);
|
|
57
|
+
this.error.set(null);
|
|
58
|
+
},
|
|
59
|
+
error: (err) => {
|
|
60
|
+
this.loading.set(false);
|
|
61
|
+
const message = err instanceof Error ? err.message : 'Could not load live calls.';
|
|
62
|
+
this.error.set(message);
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
startFallbackPoll() {
|
|
67
|
+
const pollMs = this.config.liveCallsPollIntervalMs ?? DEFAULT_POLL_MS;
|
|
68
|
+
if (this.config.useMockApi) {
|
|
69
|
+
this.mockPollTimer = setInterval(() => {
|
|
70
|
+
if (!document.hidden) {
|
|
71
|
+
this.refresh(false);
|
|
72
|
+
}
|
|
73
|
+
}, pollMs);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
this.pollSub = interval(pollMs)
|
|
77
|
+
.pipe(filter(() => !document.hidden), switchMap(() => this.api.getLiveCalls().pipe(catchError(() => {
|
|
78
|
+
this.error.set('Live calls refresh failed.');
|
|
79
|
+
return of(null);
|
|
80
|
+
}))))
|
|
81
|
+
.subscribe((result) => {
|
|
82
|
+
if (!result)
|
|
83
|
+
return;
|
|
84
|
+
this.calls.set(result.data);
|
|
85
|
+
this.lastUpdated.set(Date.now());
|
|
86
|
+
this.error.set(null);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
stopFallbackPoll() {
|
|
90
|
+
this.pollSub?.unsubscribe();
|
|
91
|
+
this.pollSub = undefined;
|
|
92
|
+
if (this.mockPollTimer) {
|
|
93
|
+
clearInterval(this.mockPollTimer);
|
|
94
|
+
this.mockPollTimer = null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
bindVisibility() {
|
|
98
|
+
this.visibilityHandler = () => {
|
|
99
|
+
if (!document.hidden) {
|
|
100
|
+
this.refresh(false);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
document.addEventListener('visibilitychange', this.visibilityHandler);
|
|
104
|
+
}
|
|
105
|
+
unbindVisibility() {
|
|
106
|
+
if (this.visibilityHandler) {
|
|
107
|
+
document.removeEventListener('visibilitychange', this.visibilityHandler);
|
|
108
|
+
this.visibilityHandler = undefined;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LiveCallsListService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
112
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LiveCallsListService, providedIn: 'root' }); }
|
|
113
|
+
}
|
|
114
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LiveCallsListService, decorators: [{
|
|
115
|
+
type: Injectable,
|
|
116
|
+
args: [{ providedIn: 'root' }]
|
|
117
|
+
}] });
|
|
118
|
+
|
|
119
|
+
export { LiveCallsListService as L };
|
|
120
|
+
//# sourceMappingURL=edvak99-voice-receptionist-widget-live-calls-list.service-C2UZJ5wF.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edvak99-voice-receptionist-widget-live-calls-list.service-C2UZJ5wF.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/services/live-calls-list.service.ts"],"sourcesContent":["import { Injectable, inject, signal } from '@angular/core';\nimport { Subscription, catchError, filter, interval, of, switchMap } from 'rxjs';\n\nimport { VOICE_RECEPTIONIST_CONFIG } from '../config';\nimport { LiveCall } from '../models/ai-receptionist.types';\nimport { AiReceptionistApiService } from './ai-receptionist-api.service';\nimport { DashboardSocketService } from './dashboard-socket.service';\n\nconst DEFAULT_POLL_MS = 12_000;\n\n/**\n * Shared live calls list — REST + `live_calls_updated` socket + fallback poll.\n */\n@Injectable({ providedIn: 'root' })\nexport class LiveCallsListService {\n private readonly api = inject(AiReceptionistApiService);\n private readonly socket = inject(DashboardSocketService);\n private readonly config = inject(VOICE_RECEPTIONIST_CONFIG);\n\n readonly calls = signal<LiveCall[]>([]);\n readonly loading = signal(false);\n readonly error = signal<string | null>(null);\n readonly lastUpdated = signal<number | null>(null);\n\n private subscriberCount = 0;\n private pollSub?: Subscription;\n private visibilityHandler?: () => void;\n private mockPollTimer: ReturnType<typeof setInterval> | null = null;\n\n private readonly onLiveCallsUpdated = () => this.refresh(false);\n private readonly onSocketConnect = () => this.refresh(false);\n\n start(): void {\n this.subscriberCount++;\n if (this.subscriberCount > 1) {\n return;\n }\n\n this.socket.acquire();\n this.socket.on('live_calls_updated', this.onLiveCallsUpdated);\n this.socket.onConnect(this.onSocketConnect);\n this.bindVisibility();\n this.refresh(true);\n this.startFallbackPoll();\n }\n\n stop(): void {\n this.subscriberCount = Math.max(0, this.subscriberCount - 1);\n if (this.subscriberCount > 0) {\n return;\n }\n\n this.stopFallbackPoll();\n this.unbindVisibility();\n this.socket.off('live_calls_updated', this.onLiveCallsUpdated);\n this.socket.offConnect(this.onSocketConnect);\n this.socket.release();\n }\n\n refresh(showLoading = true): void {\n if (showLoading && this.calls().length === 0) {\n this.loading.set(true);\n }\n\n this.api.getLiveCalls().subscribe({\n next: ({ data }) => {\n this.calls.set(data);\n this.lastUpdated.set(Date.now());\n this.loading.set(false);\n this.error.set(null);\n },\n error: (err: unknown) => {\n this.loading.set(false);\n const message =\n err instanceof Error ? err.message : 'Could not load live calls.';\n this.error.set(message);\n },\n });\n }\n\n private startFallbackPoll(): void {\n const pollMs = this.config.liveCallsPollIntervalMs ?? DEFAULT_POLL_MS;\n\n if (this.config.useMockApi) {\n this.mockPollTimer = setInterval(() => {\n if (!document.hidden) {\n this.refresh(false);\n }\n }, pollMs);\n return;\n }\n\n this.pollSub = interval(pollMs)\n .pipe(\n filter(() => !document.hidden),\n switchMap(() =>\n this.api.getLiveCalls().pipe(\n catchError(() => {\n this.error.set('Live calls refresh failed.');\n return of(null);\n }),\n ),\n ),\n )\n .subscribe((result) => {\n if (!result) return;\n this.calls.set(result.data);\n this.lastUpdated.set(Date.now());\n this.error.set(null);\n });\n }\n\n private stopFallbackPoll(): void {\n this.pollSub?.unsubscribe();\n this.pollSub = undefined;\n if (this.mockPollTimer) {\n clearInterval(this.mockPollTimer);\n this.mockPollTimer = null;\n }\n }\n\n private bindVisibility(): void {\n this.visibilityHandler = () => {\n if (!document.hidden) {\n this.refresh(false);\n }\n };\n document.addEventListener('visibilitychange', this.visibilityHandler);\n }\n\n private unbindVisibility(): void {\n if (this.visibilityHandler) {\n document.removeEventListener('visibilitychange', this.visibilityHandler);\n this.visibilityHandler = undefined;\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, signal, computed, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { Router, ActivatedRoute } from '@angular/router';
|
|
4
|
+
import { A as AiReceptionistApiService, a as EdsBadgeComponent, E as EdsButtonComponent, b as EdsIconComponent, c as EdsPulseDotComponent } from './edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-DClMfxeY.mjs';
|
|
5
|
+
import { L as LiveCallsListService } from './edvak99-voice-receptionist-widget-live-calls-list.service-C2UZJ5wF.mjs';
|
|
6
|
+
import { E as EdsElapsedPipe } from './edvak99-voice-receptionist-widget-eds-elapsed.pipe-BrMNsxB5.mjs';
|
|
7
|
+
import { E as EdsAvatarComponent } from './edvak99-voice-receptionist-widget-eds-avatar.component-PCnIWeOA.mjs';
|
|
8
|
+
|
|
9
|
+
class LiveCallsPageComponent {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.api = inject(AiReceptionistApiService);
|
|
12
|
+
this.router = inject(Router);
|
|
13
|
+
this.route = inject(ActivatedRoute);
|
|
14
|
+
this.liveCallsList = inject(LiveCallsListService);
|
|
15
|
+
this.filter = signal('all');
|
|
16
|
+
this.calls = this.liveCallsList.calls;
|
|
17
|
+
this.loading = this.liveCallsList.loading;
|
|
18
|
+
this.error = this.liveCallsList.error;
|
|
19
|
+
this.lastUpdated = this.liveCallsList.lastUpdated;
|
|
20
|
+
this.filters = [
|
|
21
|
+
{ id: 'all', label: 'All' },
|
|
22
|
+
{ id: 'ai', label: 'AI handling' },
|
|
23
|
+
{ id: 'needs-staff', label: 'Needs staff' },
|
|
24
|
+
];
|
|
25
|
+
this.filteredCalls = computed(() => {
|
|
26
|
+
const rows = this.calls();
|
|
27
|
+
const f = this.filter();
|
|
28
|
+
if (f === 'ai')
|
|
29
|
+
return rows.filter((c) => (c.status || 'ai') === 'ai');
|
|
30
|
+
if (f === 'needs-staff') {
|
|
31
|
+
return rows.filter((c) => ['needs-help', 'human-requested', 'urgent', 'takeover', 'transferring'].includes(c.status || 'ai'));
|
|
32
|
+
}
|
|
33
|
+
return rows;
|
|
34
|
+
});
|
|
35
|
+
this.emptyState = computed(() => {
|
|
36
|
+
if (this.filteredCalls().length > 0)
|
|
37
|
+
return null;
|
|
38
|
+
const f = this.filter();
|
|
39
|
+
const total = this.calls().length;
|
|
40
|
+
if (total === 0 || f === 'all') {
|
|
41
|
+
return {
|
|
42
|
+
title: 'No active calls right now',
|
|
43
|
+
detail: 'When a patient calls your practice, their conversation will appear here for real-time monitoring.',
|
|
44
|
+
icon: 'phone_disabled',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (f === 'ai') {
|
|
48
|
+
const noun = total === 1 ? 'call' : 'calls';
|
|
49
|
+
const pronoun = total === 1 ? 'it' : 'them';
|
|
50
|
+
return {
|
|
51
|
+
title: 'No calls are being handled by AI',
|
|
52
|
+
detail: `${total} active ${noun} — switch to All or Needs staff to view ${pronoun}.`,
|
|
53
|
+
icon: 'smart_toy',
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
title: 'No calls need staff attention',
|
|
58
|
+
detail: 'The AI is handling all active calls. Switch to All to see what’s in progress.',
|
|
59
|
+
icon: 'support_agent',
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
this.waveHeights = [0.4, 0.7, 1, 0.6, 0.85, 0.5, 0.9, 0.65];
|
|
63
|
+
}
|
|
64
|
+
ngOnInit() {
|
|
65
|
+
this.liveCallsList.start();
|
|
66
|
+
}
|
|
67
|
+
ngOnDestroy() {
|
|
68
|
+
this.liveCallsList.stop();
|
|
69
|
+
}
|
|
70
|
+
setFilter(id) {
|
|
71
|
+
this.filter.set(id);
|
|
72
|
+
}
|
|
73
|
+
refresh() {
|
|
74
|
+
this.liveCallsList.refresh(false);
|
|
75
|
+
}
|
|
76
|
+
statusTone(status) {
|
|
77
|
+
const normalized = status || 'ai';
|
|
78
|
+
const map = {
|
|
79
|
+
ai: 'brand',
|
|
80
|
+
'needs-help': 'notice',
|
|
81
|
+
'human-requested': 'violet',
|
|
82
|
+
urgent: 'neg',
|
|
83
|
+
takeover: 'violet',
|
|
84
|
+
transferring: 'notice',
|
|
85
|
+
};
|
|
86
|
+
return map[normalized] ?? 'brand';
|
|
87
|
+
}
|
|
88
|
+
statusLabel(status) {
|
|
89
|
+
return status || 'ai';
|
|
90
|
+
}
|
|
91
|
+
monitor(id) {
|
|
92
|
+
void this.router.navigate(['../live', id], { relativeTo: this.route });
|
|
93
|
+
}
|
|
94
|
+
takeover(id, event) {
|
|
95
|
+
event.preventDefault();
|
|
96
|
+
event.stopPropagation();
|
|
97
|
+
this.api.postTakeover(id).subscribe(() => this.liveCallsList.refresh(false));
|
|
98
|
+
}
|
|
99
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LiveCallsPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
100
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: LiveCallsPageComponent, isStandalone: true, selector: "eds-live-calls-page", ngImport: i0, template: "<div class=\"eds-page live-calls-page\">\n <header class=\"live-calls-page__header\">\n <div>\n <h1 class=\"eds-h1\">Live calls</h1>\n <p class=\"eds-body-sm\">Monitor active conversations in real time</p>\n </div>\n <div class=\"live-calls-page__header-actions\">\n <eds-button variant=\"secondary\" (click)=\"refresh()\">\n <eds-icon name=\"refresh\" [size]=\"18\" /> Refresh\n </eds-button>\n <eds-pulse-dot />\n </div>\n </header>\n\n @if (error()) {\n <p class=\"live-calls-page__error eds-body-sm\" role=\"alert\">{{ error() }}</p>\n }\n\n <div class=\"live-calls-page__filters\">\n @for (chip of filters; track chip.id) {\n <button\n type=\"button\"\n class=\"live-calls-page__chip\"\n [class.live-calls-page__chip--active]=\"filter() === chip.id\"\n (click)=\"setFilter(chip.id)\"\n >\n {{ chip.label }}\n </button>\n }\n </div>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading live calls\u2026</p>\n } @else {\n <div class=\"live-calls-page__grid\">\n @for (call of filteredCalls(); track call.id) {\n <article class=\"eds-card live-calls-page__card\">\n <div class=\"live-calls-page__card-top\">\n <eds-avatar [initials]=\"call.initials\" tone=\"brand\" [gradient]=\"true\" />\n <div class=\"live-calls-page__meta\">\n <span class=\"eds-body-sm-bold\">{{ call.name }}</span>\n <span class=\"eds-mono-sm\">{{ call.phone }}</span>\n </div>\n <eds-badge [tone]=\"statusTone(call.status)\">{{ statusLabel(call.status) }}</eds-badge>\n </div>\n\n <p class=\"eds-body-sm\">{{ call.reason }}</p>\n\n <div class=\"live-calls-page__wave\" aria-hidden=\"true\">\n @for (h of waveHeights; track $index) {\n <span\n class=\"live-calls-page__bar\"\n [style.animation-delay.ms]=\"$index * 120\"\n [style.--bar-scale]=\"h\"\n ></span>\n }\n </div>\n\n <div class=\"live-calls-page__footer\">\n <span class=\"live-calls-page__elapsed eds-mono-sm\">\n <span class=\"live-calls-page__elapsed-dot\" aria-hidden=\"true\"></span>\n {{ call.started | edsElapsed }}\n </span>\n <span class=\"eds-body-sm\">{{ call.sentiment }}</span>\n </div>\n\n <div class=\"live-calls-page__actions\">\n <eds-button variant=\"secondary\" (click)=\"monitor(call.id)\">\n <eds-icon name=\"visibility\" [size]=\"18\" /> Monitor\n </eds-button>\n <eds-button variant=\"primary\" (click)=\"takeover(call.id, $event)\">\n <eds-icon name=\"support_agent\" [size]=\"18\" /> Take over\n </eds-button>\n </div>\n </article>\n } @empty {\n @if (emptyState(); as state) {\n <div class=\"eds-card live-calls-page__empty\" role=\"status\">\n <eds-icon [name]=\"state.icon\" [size]=\"32\" />\n <p class=\"eds-h2 live-calls-page__empty-title\">{{ state.title }}</p>\n <p class=\"eds-body-sm live-calls-page__empty-detail\">{{ state.detail }}</p>\n </div>\n }\n }\n </div>\n }\n</div>\n", styles: [".live-calls-page__header{display:flex;align-items:center;justify-content:space-between;margin-bottom:20px}.live-calls-page__header-actions{display:flex;align-items:center;gap:12px}.live-calls-page__error{color:var(--red-500, #dc2626);margin-bottom:12px}.live-calls-page__filters{display:flex;gap:8px;margin-bottom:20px}.live-calls-page__chip{padding:6px 14px;border:1px solid var(--border-default);border-radius:999px;background:var(--surface-l0);font-size:13px;font-weight:500;color:var(--text-secondary);cursor:pointer}.live-calls-page__chip--active{background:var(--brand-500);border-color:var(--brand-500);color:#fff}.live-calls-page__grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}.live-calls-page__card{display:flex;flex-direction:column;gap:12px;padding:16px}.live-calls-page__card-top{display:flex;align-items:center;gap:10px}.live-calls-page__meta{flex:1;min-width:0;display:flex;flex-direction:column}.live-calls-page__wave{display:flex;align-items:flex-end;justify-content:center;gap:4px;height:40px;padding:8px 0}.live-calls-page__bar{width:4px;height:32px;border-radius:2px;background:linear-gradient(180deg,var(--brand-500),var(--violet-500));transform-origin:bottom;animation:eds-wave 1.2s ease-in-out infinite;transform:scaleY(var(--bar-scale, .5))}.live-calls-page__footer{display:flex;justify-content:space-between;align-items:center}.live-calls-page__elapsed{display:inline-flex;align-items:center;gap:7px}.live-calls-page__elapsed-dot{width:8px;height:8px;border-radius:50%;background:var(--green-500, #22c55e);box-shadow:0 0 0 3px #22c55e24}.live-calls-page__actions{display:flex;gap:8px;margin-top:4px}.live-calls-page__empty{grid-column:1/-1;display:flex;flex-direction:column;align-items:center;gap:8px;text-align:center;padding:48px 24px;color:var(--text-secondary)}.live-calls-page__empty-title{margin:0 0 8px;color:var(--text-primary, #333)}.live-calls-page__empty-detail{margin:0;max-width:420px}@media(max-width:1100px){.live-calls-page__grid{grid-template-columns:repeat(2,1fr)}}@media(max-width:700px){.live-calls-page__grid{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "component", type: EdsAvatarComponent, selector: "eds-avatar", inputs: ["initials", "tone", "gradient"] }, { 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: EdsPulseDotComponent, selector: "eds-pulse-dot" }, { kind: "pipe", type: EdsElapsedPipe, name: "edsElapsed" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
101
|
+
}
|
|
102
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LiveCallsPageComponent, decorators: [{
|
|
103
|
+
type: Component,
|
|
104
|
+
args: [{ selector: 'eds-live-calls-page', imports: [
|
|
105
|
+
EdsAvatarComponent,
|
|
106
|
+
EdsBadgeComponent,
|
|
107
|
+
EdsButtonComponent,
|
|
108
|
+
EdsIconComponent,
|
|
109
|
+
EdsPulseDotComponent,
|
|
110
|
+
EdsElapsedPipe,
|
|
111
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"eds-page live-calls-page\">\n <header class=\"live-calls-page__header\">\n <div>\n <h1 class=\"eds-h1\">Live calls</h1>\n <p class=\"eds-body-sm\">Monitor active conversations in real time</p>\n </div>\n <div class=\"live-calls-page__header-actions\">\n <eds-button variant=\"secondary\" (click)=\"refresh()\">\n <eds-icon name=\"refresh\" [size]=\"18\" /> Refresh\n </eds-button>\n <eds-pulse-dot />\n </div>\n </header>\n\n @if (error()) {\n <p class=\"live-calls-page__error eds-body-sm\" role=\"alert\">{{ error() }}</p>\n }\n\n <div class=\"live-calls-page__filters\">\n @for (chip of filters; track chip.id) {\n <button\n type=\"button\"\n class=\"live-calls-page__chip\"\n [class.live-calls-page__chip--active]=\"filter() === chip.id\"\n (click)=\"setFilter(chip.id)\"\n >\n {{ chip.label }}\n </button>\n }\n </div>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading live calls\u2026</p>\n } @else {\n <div class=\"live-calls-page__grid\">\n @for (call of filteredCalls(); track call.id) {\n <article class=\"eds-card live-calls-page__card\">\n <div class=\"live-calls-page__card-top\">\n <eds-avatar [initials]=\"call.initials\" tone=\"brand\" [gradient]=\"true\" />\n <div class=\"live-calls-page__meta\">\n <span class=\"eds-body-sm-bold\">{{ call.name }}</span>\n <span class=\"eds-mono-sm\">{{ call.phone }}</span>\n </div>\n <eds-badge [tone]=\"statusTone(call.status)\">{{ statusLabel(call.status) }}</eds-badge>\n </div>\n\n <p class=\"eds-body-sm\">{{ call.reason }}</p>\n\n <div class=\"live-calls-page__wave\" aria-hidden=\"true\">\n @for (h of waveHeights; track $index) {\n <span\n class=\"live-calls-page__bar\"\n [style.animation-delay.ms]=\"$index * 120\"\n [style.--bar-scale]=\"h\"\n ></span>\n }\n </div>\n\n <div class=\"live-calls-page__footer\">\n <span class=\"live-calls-page__elapsed eds-mono-sm\">\n <span class=\"live-calls-page__elapsed-dot\" aria-hidden=\"true\"></span>\n {{ call.started | edsElapsed }}\n </span>\n <span class=\"eds-body-sm\">{{ call.sentiment }}</span>\n </div>\n\n <div class=\"live-calls-page__actions\">\n <eds-button variant=\"secondary\" (click)=\"monitor(call.id)\">\n <eds-icon name=\"visibility\" [size]=\"18\" /> Monitor\n </eds-button>\n <eds-button variant=\"primary\" (click)=\"takeover(call.id, $event)\">\n <eds-icon name=\"support_agent\" [size]=\"18\" /> Take over\n </eds-button>\n </div>\n </article>\n } @empty {\n @if (emptyState(); as state) {\n <div class=\"eds-card live-calls-page__empty\" role=\"status\">\n <eds-icon [name]=\"state.icon\" [size]=\"32\" />\n <p class=\"eds-h2 live-calls-page__empty-title\">{{ state.title }}</p>\n <p class=\"eds-body-sm live-calls-page__empty-detail\">{{ state.detail }}</p>\n </div>\n }\n }\n </div>\n }\n</div>\n", styles: [".live-calls-page__header{display:flex;align-items:center;justify-content:space-between;margin-bottom:20px}.live-calls-page__header-actions{display:flex;align-items:center;gap:12px}.live-calls-page__error{color:var(--red-500, #dc2626);margin-bottom:12px}.live-calls-page__filters{display:flex;gap:8px;margin-bottom:20px}.live-calls-page__chip{padding:6px 14px;border:1px solid var(--border-default);border-radius:999px;background:var(--surface-l0);font-size:13px;font-weight:500;color:var(--text-secondary);cursor:pointer}.live-calls-page__chip--active{background:var(--brand-500);border-color:var(--brand-500);color:#fff}.live-calls-page__grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}.live-calls-page__card{display:flex;flex-direction:column;gap:12px;padding:16px}.live-calls-page__card-top{display:flex;align-items:center;gap:10px}.live-calls-page__meta{flex:1;min-width:0;display:flex;flex-direction:column}.live-calls-page__wave{display:flex;align-items:flex-end;justify-content:center;gap:4px;height:40px;padding:8px 0}.live-calls-page__bar{width:4px;height:32px;border-radius:2px;background:linear-gradient(180deg,var(--brand-500),var(--violet-500));transform-origin:bottom;animation:eds-wave 1.2s ease-in-out infinite;transform:scaleY(var(--bar-scale, .5))}.live-calls-page__footer{display:flex;justify-content:space-between;align-items:center}.live-calls-page__elapsed{display:inline-flex;align-items:center;gap:7px}.live-calls-page__elapsed-dot{width:8px;height:8px;border-radius:50%;background:var(--green-500, #22c55e);box-shadow:0 0 0 3px #22c55e24}.live-calls-page__actions{display:flex;gap:8px;margin-top:4px}.live-calls-page__empty{grid-column:1/-1;display:flex;flex-direction:column;align-items:center;gap:8px;text-align:center;padding:48px 24px;color:var(--text-secondary)}.live-calls-page__empty-title{margin:0 0 8px;color:var(--text-primary, #333)}.live-calls-page__empty-detail{margin:0;max-width:420px}@media(max-width:1100px){.live-calls-page__grid{grid-template-columns:repeat(2,1fr)}}@media(max-width:700px){.live-calls-page__grid{grid-template-columns:1fr}}\n"] }]
|
|
112
|
+
}] });
|
|
113
|
+
|
|
114
|
+
export { LiveCallsPageComponent };
|
|
115
|
+
//# sourceMappingURL=edvak99-voice-receptionist-widget-live-calls-page.component-DQTVJjsP.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edvak99-voice-receptionist-widget-live-calls-page.component-DQTVJjsP.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/pages/live-calls-page/live-calls-page.component.ts","../../../projects/voice-receptionist-widget/src/lib/pages/live-calls-page/live-calls-page.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n OnDestroy,\n OnInit,\n signal,\n} from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\n\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport { LiveCallsListService } from '../../services/live-calls-list.service';\nimport { EdsElapsedPipe } from '../../pipes/eds-elapsed.pipe';\nimport { EdsAvatarComponent } from '../../ui/eds-avatar.component';\nimport { EdsBadgeComponent } from '../../ui/eds-badge.component';\nimport { EdsButtonComponent } from '../../ui/eds-button.component';\nimport { EdsIconComponent } from '../../ui/eds-icon.component';\nimport { EdsPulseDotComponent } from '../../ui/eds-pulse-dot.component';\n\ntype LiveFilter = 'all' | 'ai' | 'needs-staff';\n\ninterface LiveCallsEmptyState {\n title: string;\n detail: string;\n icon: string;\n}\n\n@Component({\n selector: 'eds-live-calls-page',\n imports: [\n EdsAvatarComponent,\n EdsBadgeComponent,\n EdsButtonComponent,\n EdsIconComponent,\n EdsPulseDotComponent,\n EdsElapsedPipe,\n ],\n templateUrl: './live-calls-page.component.html',\n styleUrl: './live-calls-page.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class LiveCallsPageComponent implements OnInit, OnDestroy {\n private readonly api = inject(AiReceptionistApiService);\n private readonly router = inject(Router);\n private readonly route = inject(ActivatedRoute);\n private readonly liveCallsList = inject(LiveCallsListService);\n\n protected readonly filter = signal<LiveFilter>('all');\n\n protected readonly calls = this.liveCallsList.calls;\n protected readonly loading = this.liveCallsList.loading;\n protected readonly error = this.liveCallsList.error;\n protected readonly lastUpdated = this.liveCallsList.lastUpdated;\n\n protected readonly filters: { id: LiveFilter; label: string }[] = [\n { id: 'all', label: 'All' },\n { id: 'ai', label: 'AI handling' },\n { id: 'needs-staff', label: 'Needs staff' },\n ];\n\n protected readonly filteredCalls = computed(() => {\n const rows = this.calls();\n const f = this.filter();\n if (f === 'ai') return rows.filter((c) => (c.status || 'ai') === 'ai');\n if (f === 'needs-staff') {\n return rows.filter((c) =>\n ['needs-help', 'human-requested', 'urgent', 'takeover', 'transferring'].includes(\n c.status || 'ai',\n ),\n );\n }\n return rows;\n });\n\n protected readonly emptyState = computed((): LiveCallsEmptyState | null => {\n if (this.filteredCalls().length > 0) return null;\n\n const f = this.filter();\n const total = this.calls().length;\n\n if (total === 0 || f === 'all') {\n return {\n title: 'No active calls right now',\n detail:\n 'When a patient calls your practice, their conversation will appear here for real-time monitoring.',\n icon: 'phone_disabled',\n };\n }\n\n if (f === 'ai') {\n const noun = total === 1 ? 'call' : 'calls';\n const pronoun = total === 1 ? 'it' : 'them';\n return {\n title: 'No calls are being handled by AI',\n detail: `${total} active ${noun} — switch to All or Needs staff to view ${pronoun}.`,\n icon: 'smart_toy',\n };\n }\n\n return {\n title: 'No calls need staff attention',\n detail: 'The AI is handling all active calls. Switch to All to see what’s in progress.',\n icon: 'support_agent',\n };\n });\n\n protected readonly waveHeights = [0.4, 0.7, 1, 0.6, 0.85, 0.5, 0.9, 0.65];\n\n ngOnInit(): void {\n this.liveCallsList.start();\n }\n\n ngOnDestroy(): void {\n this.liveCallsList.stop();\n }\n\n protected setFilter(id: LiveFilter): void {\n this.filter.set(id);\n }\n\n protected refresh(): void {\n this.liveCallsList.refresh(false);\n }\n\n protected statusTone(status: string): 'brand' | 'pos' | 'notice' | 'neg' | 'violet' | 'gray' {\n const normalized = status || 'ai';\n const map: Record<string, 'brand' | 'pos' | 'notice' | 'neg' | 'violet'> = {\n ai: 'brand',\n 'needs-help': 'notice',\n 'human-requested': 'violet',\n urgent: 'neg',\n takeover: 'violet',\n transferring: 'notice',\n };\n return map[normalized] ?? 'brand';\n }\n\n protected statusLabel(status: string): string {\n return status || 'ai';\n }\n\n protected monitor(id: string): void {\n void this.router.navigate(['../live', id], { relativeTo: this.route });\n }\n\n protected takeover(id: string, event: Event): void {\n event.preventDefault();\n event.stopPropagation();\n this.api.postTakeover(id).subscribe(() => this.liveCallsList.refresh(false));\n }\n}\n","<div class=\"eds-page live-calls-page\">\n <header class=\"live-calls-page__header\">\n <div>\n <h1 class=\"eds-h1\">Live calls</h1>\n <p class=\"eds-body-sm\">Monitor active conversations in real time</p>\n </div>\n <div class=\"live-calls-page__header-actions\">\n <eds-button variant=\"secondary\" (click)=\"refresh()\">\n <eds-icon name=\"refresh\" [size]=\"18\" /> Refresh\n </eds-button>\n <eds-pulse-dot />\n </div>\n </header>\n\n @if (error()) {\n <p class=\"live-calls-page__error eds-body-sm\" role=\"alert\">{{ error() }}</p>\n }\n\n <div class=\"live-calls-page__filters\">\n @for (chip of filters; track chip.id) {\n <button\n type=\"button\"\n class=\"live-calls-page__chip\"\n [class.live-calls-page__chip--active]=\"filter() === chip.id\"\n (click)=\"setFilter(chip.id)\"\n >\n {{ chip.label }}\n </button>\n }\n </div>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading live calls…</p>\n } @else {\n <div class=\"live-calls-page__grid\">\n @for (call of filteredCalls(); track call.id) {\n <article class=\"eds-card live-calls-page__card\">\n <div class=\"live-calls-page__card-top\">\n <eds-avatar [initials]=\"call.initials\" tone=\"brand\" [gradient]=\"true\" />\n <div class=\"live-calls-page__meta\">\n <span class=\"eds-body-sm-bold\">{{ call.name }}</span>\n <span class=\"eds-mono-sm\">{{ call.phone }}</span>\n </div>\n <eds-badge [tone]=\"statusTone(call.status)\">{{ statusLabel(call.status) }}</eds-badge>\n </div>\n\n <p class=\"eds-body-sm\">{{ call.reason }}</p>\n\n <div class=\"live-calls-page__wave\" aria-hidden=\"true\">\n @for (h of waveHeights; track $index) {\n <span\n class=\"live-calls-page__bar\"\n [style.animation-delay.ms]=\"$index * 120\"\n [style.--bar-scale]=\"h\"\n ></span>\n }\n </div>\n\n <div class=\"live-calls-page__footer\">\n <span class=\"live-calls-page__elapsed eds-mono-sm\">\n <span class=\"live-calls-page__elapsed-dot\" aria-hidden=\"true\"></span>\n {{ call.started | edsElapsed }}\n </span>\n <span class=\"eds-body-sm\">{{ call.sentiment }}</span>\n </div>\n\n <div class=\"live-calls-page__actions\">\n <eds-button variant=\"secondary\" (click)=\"monitor(call.id)\">\n <eds-icon name=\"visibility\" [size]=\"18\" /> Monitor\n </eds-button>\n <eds-button variant=\"primary\" (click)=\"takeover(call.id, $event)\">\n <eds-icon name=\"support_agent\" [size]=\"18\" /> Take over\n </eds-button>\n </div>\n </article>\n } @empty {\n @if (emptyState(); as state) {\n <div class=\"eds-card live-calls-page__empty\" role=\"status\">\n <eds-icon [name]=\"state.icon\" [size]=\"32\" />\n <p class=\"eds-h2 live-calls-page__empty-title\">{{ state.title }}</p>\n <p class=\"eds-body-sm live-calls-page__empty-detail\">{{ state.detail }}</p>\n </div>\n }\n }\n </div>\n }\n</div>\n"],"names":[],"mappings":"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"}
|