@edvak99/voice-receptionist-widget 1.1.21 → 1.1.22

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 (63) hide show
  1. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-dcDXT2KG.mjs +533 -0
  2. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-dcDXT2KG.mjs.map +1 -0
  3. package/fesm2022/{edvak99-voice-receptionist-widget-analytics-page.component-CmImX5FN.mjs → edvak99-voice-receptionist-widget-analytics-page.component-B25yZJZz.mjs} +2 -2
  4. package/fesm2022/{edvak99-voice-receptionist-widget-analytics-page.component-CmImX5FN.mjs.map → edvak99-voice-receptionist-widget-analytics-page.component-B25yZJZz.mjs.map} +1 -1
  5. package/fesm2022/edvak99-voice-receptionist-widget-audit-page.component-Df-dtgR3.mjs +96 -0
  6. package/fesm2022/edvak99-voice-receptionist-widget-audit-page.component-Df-dtgR3.mjs.map +1 -0
  7. package/fesm2022/{edvak99-voice-receptionist-widget-call-history-detail-page.component-BT4clUO-.mjs → edvak99-voice-receptionist-widget-call-history-detail-page.component-BJGzgVxG.mjs} +21 -10
  8. package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-BJGzgVxG.mjs.map +1 -0
  9. package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-CcwwO5SO.mjs +254 -0
  10. package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-CcwwO5SO.mjs.map +1 -0
  11. package/fesm2022/{edvak99-voice-receptionist-widget-call-recording-player.component-4fL-zUWy.mjs → edvak99-voice-receptionist-widget-call-recording-player.component-BepxNLNQ.mjs} +2 -2
  12. package/fesm2022/{edvak99-voice-receptionist-widget-call-recording-player.component-4fL-zUWy.mjs.map → edvak99-voice-receptionist-widget-call-recording-player.component-BepxNLNQ.mjs.map} +1 -1
  13. package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-BhbCmB1S.mjs → edvak99-voice-receptionist-widget-call-review.service-nFqc30t3.mjs} +2 -2
  14. package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-BhbCmB1S.mjs.map → edvak99-voice-receptionist-widget-call-review.service-nFqc30t3.mjs.map} +1 -1
  15. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-CMgOu0aw.mjs +311 -0
  16. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-CMgOu0aw.mjs.map +1 -0
  17. package/fesm2022/{edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-DDS7VdnD.mjs → edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-CJ1L6QeE.mjs} +54 -25
  18. package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-CJ1L6QeE.mjs.map +1 -0
  19. package/fesm2022/{edvak99-voice-receptionist-widget-live-calls-page.component-4LtgT4lS.mjs → edvak99-voice-receptionist-widget-live-calls-page.component-MnBMu8iA.mjs} +4 -4
  20. package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-MnBMu8iA.mjs.map +1 -0
  21. package/fesm2022/{edvak99-voice-receptionist-widget-live-monitor-page.component-B1lKfw-j.mjs → edvak99-voice-receptionist-widget-live-monitor-page.component-BzvMkbC4.mjs} +10 -7
  22. package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-BzvMkbC4.mjs.map +1 -0
  23. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-Aws6TaaE.mjs +273 -0
  24. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-Aws6TaaE.mjs.map +1 -0
  25. package/fesm2022/{edvak99-voice-receptionist-widget-task-detail-drawer.component-aDTKTV79.mjs → edvak99-voice-receptionist-widget-task-detail-drawer.component-DVWdArqj.mjs} +14 -6
  26. package/fesm2022/edvak99-voice-receptionist-widget-task-detail-drawer.component-DVWdArqj.mjs.map +1 -0
  27. package/fesm2022/{edvak99-voice-receptionist-widget-tasks-page.component-Dj1nWx6V.mjs → edvak99-voice-receptionist-widget-tasks-page.component-CBDmGLol.mjs} +11 -7
  28. package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-CBDmGLol.mjs.map +1 -0
  29. package/fesm2022/{edvak99-voice-receptionist-widget-wizard-page.component-CQttbEzW.mjs → edvak99-voice-receptionist-widget-wizard-page.component-C-hdBQIV.mjs} +2 -2
  30. package/fesm2022/{edvak99-voice-receptionist-widget-wizard-page.component-CQttbEzW.mjs.map → edvak99-voice-receptionist-widget-wizard-page.component-C-hdBQIV.mjs.map} +1 -1
  31. package/fesm2022/edvak99-voice-receptionist-widget.mjs +1 -1
  32. package/lib/components/call-tasks-panel/call-tasks-panel.component.d.ts +3 -0
  33. package/lib/components/patient-profile-drawer/patient-profile-drawer.component.d.ts +3 -0
  34. package/lib/components/task-detail-drawer/task-detail-drawer.component.d.ts +3 -0
  35. package/lib/pages/activities-page/activities-page.component.d.ts +5 -2
  36. package/lib/pages/audit-page/audit-page.component.d.ts +3 -0
  37. package/lib/pages/call-history-detail-page/call-history-detail-page.component.d.ts +2 -0
  38. package/lib/pages/call-history-page/call-history-page.component.d.ts +1 -0
  39. package/lib/pages/dashboard-page/dashboard-page.component.d.ts +4 -0
  40. package/lib/pages/live-monitor-page/live-monitor-page.component.d.ts +2 -0
  41. package/lib/pages/outbound-purposes-page/outbound-purposes-page.component.d.ts +3 -0
  42. package/lib/pages/settings-page/settings-page.component.d.ts +1 -0
  43. package/lib/pages/tasks-page/tasks-page.component.d.ts +2 -0
  44. package/lib/services/ai-receptionist-api.service.d.ts +1 -0
  45. package/package.json +1 -1
  46. package/src/assets/css/_tables.scss +12 -0
  47. package/styles/embed.scss +27 -0
  48. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-CpXa1J9-.mjs +0 -510
  49. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-CpXa1J9-.mjs.map +0 -1
  50. package/fesm2022/edvak99-voice-receptionist-widget-audit-page.component-DTAB2WTX.mjs +0 -78
  51. package/fesm2022/edvak99-voice-receptionist-widget-audit-page.component-DTAB2WTX.mjs.map +0 -1
  52. package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-BT4clUO-.mjs.map +0 -1
  53. package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-BACON4uq.mjs +0 -247
  54. package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-BACON4uq.mjs.map +0 -1
  55. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-CxNYLBbg.mjs +0 -291
  56. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-CxNYLBbg.mjs.map +0 -1
  57. package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-DDS7VdnD.mjs.map +0 -1
  58. package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-4LtgT4lS.mjs.map +0 -1
  59. package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-B1lKfw-j.mjs.map +0 -1
  60. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-BkIHblSu.mjs +0 -269
  61. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-BkIHblSu.mjs.map +0 -1
  62. package/fesm2022/edvak99-voice-receptionist-widget-task-detail-drawer.component-aDTKTV79.mjs.map +0 -1
  63. package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-Dj1nWx6V.mjs.map +0 -1
@@ -0,0 +1,533 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, input, output, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, signal } from '@angular/core';
3
+ import { Router, ActivatedRoute } from '@angular/router';
4
+ import { DatePipe } from '@angular/common';
5
+ import { E as EdAvatarComponent, p as EdIconButtonComponent, s as ViewportPortalDirective, I as IconComponent, A as AiReceptionistApiService, n as DEFAULT_PAGE_SIZE, o as totalPages, b as ageFromDob, u as initialsFromName, f as EdButtonComponent, g as EdSkeletonComponent } from './edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-CJ1L6QeE.mjs';
6
+ import { T as TaskDetailDrawerComponent } from './edvak99-voice-receptionist-widget-task-detail-drawer.component-DVWdArqj.mjs';
7
+ import { S as SmartTooltipDirective } from './edvak99-voice-receptionist-widget-smart-tooltip.directive-ChEdIem3.mjs';
8
+ import { T as TABLE_SKELETON_ROWS } from './edvak99-voice-receptionist-widget-table-skeleton.constants-C1Dg9Z_2.mjs';
9
+ import { E as EdsSearchFieldComponent } from './edvak99-voice-receptionist-widget-eds-search-field.component-BDGCmkoP.mjs';
10
+
11
+ class PatientProfileDrawerComponent {
12
+ constructor() {
13
+ this.router = inject(Router);
14
+ this.route = inject(ActivatedRoute);
15
+ this.patient = input.required();
16
+ this.closed = output();
17
+ }
18
+ close() {
19
+ this.closed.emit();
20
+ }
21
+ openCallHistory() {
22
+ const callId = this.patient().callId;
23
+ if (!callId)
24
+ return;
25
+ this.close();
26
+ void this.router.navigate(['../history', callId], { relativeTo: this.route });
27
+ }
28
+ onBackdropClick(event) {
29
+ if (event.target === event.currentTarget)
30
+ this.close();
31
+ }
32
+ display(value) {
33
+ const s = String(value ?? '').trim();
34
+ return s || '—';
35
+ }
36
+ insuranceLabel() {
37
+ const p = this.patient();
38
+ if (p.hasInsurance === true) {
39
+ return p.insuranceName ? `Insured — ${p.insuranceName}` : 'Insured';
40
+ }
41
+ if (p.hasInsurance === false)
42
+ return 'No insurance on file';
43
+ return p.insuranceName || 'Not verified';
44
+ }
45
+ insuranceTone() {
46
+ const p = this.patient();
47
+ if (p.hasInsurance === true)
48
+ return 'covered';
49
+ if (p.hasInsurance === false)
50
+ return 'none';
51
+ return 'unknown';
52
+ }
53
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: PatientProfileDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
54
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: PatientProfileDrawerComponent, isStandalone: true, selector: "eds-patient-profile-drawer", inputs: { patient: { classPropertyName: "patient", publicName: "patient", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { closed: "closed" }, ngImport: i0, template: "<div\n edsViewportPortal\n class=\"eds-drawer-backdrop fixed left-0 top-0 z-[100100] flex h-[100vh] w-[100vw] justify-end\"\n (click)=\"onBackdropClick($event)\"\n role=\"presentation\"\n>\n <ed-drawer\n size=\"base\"\n class=\"slide-in-rtl ed-main-sidepanel h-full bg-[#f7f8fa]\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"display(patient().patientName)\"\n >\n <ed-drawer-header class=\"border-b border-zinc-200 bg-white px-4 py-3 shadow-none\">\n <h6 class=\"m-0 text-base font-semibold text-zinc-900\">Patient profile</h6>\n <ed-icon-button\n name=\"close\"\n label=\"Close\"\n size=\"sm\"\n ghost\n (click$)=\"close()\"\n />\n </ed-drawer-header>\n\n <ed-drawer-body class=\"flex min-h-0 flex-1 flex-col gap-3 overflow-y-auto\">\n <section class=\"rounded-xl border border-zinc-200 bg-white p-4\">\n <div class=\"flex items-start gap-3\">\n <ed-avatar\n shape=\"box\"\n size=\"base\"\n [initials]=\"patient().patientInitials || '?'\"\n tone=\"brand\"\n />\n <div class=\"min-w-0 flex-1\">\n <h4>\n {{ display(patient().patientName) }}\n </h4>\n @if (patient().patientDob || patient().patientAge != null || patient().patientGender) {\n <p class=\"mt-1 mb-0 text-sm text-zinc-500\">\n @if (patient().patientDob) {\n DOB: {{ patient().patientDob | date: 'MM/dd/yyyy' }}\n }\n @if (patient().patientAge != null) {\n \u00B7 {{ patient().patientAge }}Y\n }\n @if (patient().patientGender) {\n \u00B7 {{ patient().patientGender }}\n }\n </p>\n }\n </div>\n </div>\n </section>\n\n <section class=\"rounded-xl border border-zinc-200 bg-white px-4 py-3\">\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"text-sm font-medium text-zinc-800\">MRN</span>\n <span class=\"text-sm text-zinc-600\">{{ display(patient().patientMrn) }}</span>\n </div>\n </section>\n\n @if (patient().patientId) {\n <section class=\"rounded-xl border border-zinc-200 bg-white px-4 py-3\">\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"text-sm font-medium text-zinc-800\">Patient ID</span>\n <span class=\"font-mono text-sm text-zinc-600\">{{ patient().patientId }}</span>\n </div>\n </section>\n }\n\n <section class=\"rounded-xl border border-zinc-200 bg-white px-4 py-3\">\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"text-sm font-medium text-zinc-800\">Insurance</span>\n <span\n class=\"vr-insurance-badge inline-flex items-center rounded-full border px-2.5 py-1 text-xs font-medium\"\n [attr.data-state]=\"insuranceTone()\"\n >\n {{ insuranceLabel() }}\n </span>\n </div>\n </section>\n\n <section class=\"rounded-xl border border-zinc-200 bg-white px-4 py-3\">\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"text-sm font-medium text-zinc-800\">Registered</span>\n <span class=\"text-sm text-zinc-600\">\n @if (patient().occurredAt) {\n {{ patient().occurredAt | date: 'MMM d, y, h:mm a' }}\n } @else {\n \u2014\n }\n </span>\n </div>\n </section>\n\n @if (patient().detail) {\n <section class=\"rounded-xl border border-zinc-200 bg-white p-4\">\n <h3 class=\"m-0 text-sm font-semibold text-zinc-800\">Details</h3>\n <p class=\"mt-2 mb-0 whitespace-pre-wrap text-sm leading-relaxed text-zinc-600\">\n {{ patient().detail }}\n </p>\n </section>\n }\n\n @if (patient().callId) {\n <section class=\"rounded-xl border border-zinc-200 bg-white px-4 py-3\">\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"text-sm font-medium text-zinc-800\">Call</span>\n <button\n type=\"button\"\n class=\"eds-plain-nav text-sm font-medium text-dark\"\n (click)=\"openCallHistory()\"\n >\n View call\n </button>\n </div>\n </section>\n }\n </ed-drawer-body>\n </ed-drawer>\n</div>\n", styles: ["@charset \"UTF-8\";.eds-drawer-backdrop{background-color:#0f172a66}\n"], dependencies: [{ kind: "pipe", type: DatePipe, name: "date" }, { kind: "component", type: EdAvatarComponent, selector: "ed-avatar", inputs: ["initials", "tone", "shape", "size", "gradient"] }, { kind: "component", type: EdIconButtonComponent, selector: "ed-icon-button", inputs: ["label", "name", "size", "variant", "allowBubble", "ghost", "outline", "solid", "subtle", "shape", "type", "disabled", "loading", "toggle", "pressed", "id", "title", "nameAttr", "value", "autofocus", "tabIndex"], outputs: ["click", "focus", "blur", "click$", "focus$", "blur$"] }, { kind: "directive", type: ViewportPortalDirective, selector: "[edsViewportPortal]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
55
+ }
56
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: PatientProfileDrawerComponent, decorators: [{
57
+ type: Component,
58
+ args: [{ selector: 'eds-patient-profile-drawer', imports: [
59
+ DatePipe,
60
+ EdAvatarComponent,
61
+ EdIconButtonComponent,
62
+ IconComponent,
63
+ ViewportPortalDirective,
64
+ ], changeDetection: ChangeDetectionStrategy.OnPush, schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<div\n edsViewportPortal\n class=\"eds-drawer-backdrop fixed left-0 top-0 z-[100100] flex h-[100vh] w-[100vw] justify-end\"\n (click)=\"onBackdropClick($event)\"\n role=\"presentation\"\n>\n <ed-drawer\n size=\"base\"\n class=\"slide-in-rtl ed-main-sidepanel h-full bg-[#f7f8fa]\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"display(patient().patientName)\"\n >\n <ed-drawer-header class=\"border-b border-zinc-200 bg-white px-4 py-3 shadow-none\">\n <h6 class=\"m-0 text-base font-semibold text-zinc-900\">Patient profile</h6>\n <ed-icon-button\n name=\"close\"\n label=\"Close\"\n size=\"sm\"\n ghost\n (click$)=\"close()\"\n />\n </ed-drawer-header>\n\n <ed-drawer-body class=\"flex min-h-0 flex-1 flex-col gap-3 overflow-y-auto\">\n <section class=\"rounded-xl border border-zinc-200 bg-white p-4\">\n <div class=\"flex items-start gap-3\">\n <ed-avatar\n shape=\"box\"\n size=\"base\"\n [initials]=\"patient().patientInitials || '?'\"\n tone=\"brand\"\n />\n <div class=\"min-w-0 flex-1\">\n <h4>\n {{ display(patient().patientName) }}\n </h4>\n @if (patient().patientDob || patient().patientAge != null || patient().patientGender) {\n <p class=\"mt-1 mb-0 text-sm text-zinc-500\">\n @if (patient().patientDob) {\n DOB: {{ patient().patientDob | date: 'MM/dd/yyyy' }}\n }\n @if (patient().patientAge != null) {\n \u00B7 {{ patient().patientAge }}Y\n }\n @if (patient().patientGender) {\n \u00B7 {{ patient().patientGender }}\n }\n </p>\n }\n </div>\n </div>\n </section>\n\n <section class=\"rounded-xl border border-zinc-200 bg-white px-4 py-3\">\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"text-sm font-medium text-zinc-800\">MRN</span>\n <span class=\"text-sm text-zinc-600\">{{ display(patient().patientMrn) }}</span>\n </div>\n </section>\n\n @if (patient().patientId) {\n <section class=\"rounded-xl border border-zinc-200 bg-white px-4 py-3\">\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"text-sm font-medium text-zinc-800\">Patient ID</span>\n <span class=\"font-mono text-sm text-zinc-600\">{{ patient().patientId }}</span>\n </div>\n </section>\n }\n\n <section class=\"rounded-xl border border-zinc-200 bg-white px-4 py-3\">\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"text-sm font-medium text-zinc-800\">Insurance</span>\n <span\n class=\"vr-insurance-badge inline-flex items-center rounded-full border px-2.5 py-1 text-xs font-medium\"\n [attr.data-state]=\"insuranceTone()\"\n >\n {{ insuranceLabel() }}\n </span>\n </div>\n </section>\n\n <section class=\"rounded-xl border border-zinc-200 bg-white px-4 py-3\">\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"text-sm font-medium text-zinc-800\">Registered</span>\n <span class=\"text-sm text-zinc-600\">\n @if (patient().occurredAt) {\n {{ patient().occurredAt | date: 'MMM d, y, h:mm a' }}\n } @else {\n \u2014\n }\n </span>\n </div>\n </section>\n\n @if (patient().detail) {\n <section class=\"rounded-xl border border-zinc-200 bg-white p-4\">\n <h3 class=\"m-0 text-sm font-semibold text-zinc-800\">Details</h3>\n <p class=\"mt-2 mb-0 whitespace-pre-wrap text-sm leading-relaxed text-zinc-600\">\n {{ patient().detail }}\n </p>\n </section>\n }\n\n @if (patient().callId) {\n <section class=\"rounded-xl border border-zinc-200 bg-white px-4 py-3\">\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"text-sm font-medium text-zinc-800\">Call</span>\n <button\n type=\"button\"\n class=\"eds-plain-nav text-sm font-medium text-dark\"\n (click)=\"openCallHistory()\"\n >\n View call\n </button>\n </div>\n </section>\n }\n </ed-drawer-body>\n </ed-drawer>\n</div>\n", styles: ["@charset \"UTF-8\";.eds-drawer-backdrop{background-color:#0f172a66}\n"] }]
65
+ }] });
66
+
67
+ const TABS = [
68
+ { id: 'task_created', label: 'Tasks' },
69
+ { id: 'appointment_booked', label: 'Appointments' },
70
+ { id: 'patient_created', label: 'Registrations' },
71
+ ];
72
+ const AVATAR_TONES = ['green', 'amber', 'brand', 'violet', 'rose'];
73
+ class ActivitiesPageComponent {
74
+ constructor() {
75
+ this.api = inject(AiReceptionistApiService);
76
+ this.route = inject(ActivatedRoute);
77
+ this.router = inject(Router);
78
+ this.tabs = TABS;
79
+ this.pageSize = DEFAULT_PAGE_SIZE;
80
+ this.items = signal([]);
81
+ this.total = signal(0);
82
+ this.page = signal(1);
83
+ this.activeTab = signal('task_created');
84
+ this.callIdFilter = signal(null);
85
+ this.search = signal('');
86
+ this.loading = signal(true);
87
+ this.loadError = signal(null);
88
+ this.skeletonRows = TABLE_SKELETON_ROWS;
89
+ this.selectedTask = signal(null);
90
+ this.selectedPatient = signal(null);
91
+ this.searchTimer = null;
92
+ }
93
+ ngOnInit() {
94
+ const callId = this.route.snapshot.queryParamMap.get('callId')?.trim() || null;
95
+ if (callId)
96
+ this.callIdFilter.set(callId);
97
+ const tabParam = (this.route.snapshot.queryParamMap.get('tab') || '').trim().toLowerCase();
98
+ const tabFromQuery = this.tabFromQuery(tabParam);
99
+ if (tabFromQuery) {
100
+ this.activeTab.set(tabFromQuery);
101
+ }
102
+ else {
103
+ this.activeTab.set('task_created');
104
+ void this.router.navigate([], {
105
+ relativeTo: this.route,
106
+ queryParams: { tab: 'tasks' },
107
+ queryParamsHandling: 'merge',
108
+ replaceUrl: true,
109
+ });
110
+ }
111
+ this.load();
112
+ }
113
+ setTab(tab) {
114
+ if (this.activeTab() === tab)
115
+ return;
116
+ this.activeTab.set(tab);
117
+ this.page.set(1);
118
+ void this.router.navigate([], {
119
+ relativeTo: this.route,
120
+ queryParams: { tab: this.tabQueryValue(tab) },
121
+ queryParamsHandling: 'merge',
122
+ replaceUrl: true,
123
+ });
124
+ this.load();
125
+ }
126
+ tabFromQuery(raw) {
127
+ switch (raw) {
128
+ case 'tasks':
129
+ case 'task':
130
+ case 'task_created':
131
+ return 'task_created';
132
+ case 'appointments':
133
+ case 'appointment':
134
+ case 'appointment_booked':
135
+ return 'appointment_booked';
136
+ case 'registrations':
137
+ case 'registration':
138
+ case 'patient_created':
139
+ return 'patient_created';
140
+ default:
141
+ return null;
142
+ }
143
+ }
144
+ tabQueryValue(tab) {
145
+ switch (tab) {
146
+ case 'task_created':
147
+ return 'tasks';
148
+ case 'patient_created':
149
+ return 'registrations';
150
+ default:
151
+ return 'appointments';
152
+ }
153
+ }
154
+ clearCallFilter() {
155
+ this.callIdFilter.set(null);
156
+ this.page.set(1);
157
+ this.load();
158
+ }
159
+ ngOnDestroy() {
160
+ if (this.searchTimer)
161
+ clearTimeout(this.searchTimer);
162
+ }
163
+ onSearchInput(value) {
164
+ this.search.set(value);
165
+ if (this.searchTimer)
166
+ clearTimeout(this.searchTimer);
167
+ this.searchTimer = setTimeout(() => {
168
+ this.page.set(1);
169
+ this.load();
170
+ }, value.trim() ? 300 : 0);
171
+ }
172
+ applySearch() {
173
+ if (this.searchTimer)
174
+ clearTimeout(this.searchTimer);
175
+ this.page.set(1);
176
+ this.load();
177
+ }
178
+ load() {
179
+ this.loading.set(true);
180
+ this.loadError.set(null);
181
+ const tab = this.activeTab();
182
+ this.api
183
+ .getActivities({
184
+ page: this.page(),
185
+ limit: DEFAULT_PAGE_SIZE,
186
+ types: tab,
187
+ callId: this.callIdFilter() || undefined,
188
+ search: this.search().trim() || undefined,
189
+ })
190
+ .subscribe({
191
+ next: ({ items, total, page }) => {
192
+ this.items.set(items);
193
+ this.total.set(total);
194
+ this.page.set(page);
195
+ this.loading.set(false);
196
+ },
197
+ error: () => {
198
+ this.loading.set(false);
199
+ this.items.set([]);
200
+ this.total.set(0);
201
+ this.loadError.set('Activities feed is unavailable. Confirm GET /api/ai-receptionist/activities is deployed.');
202
+ },
203
+ });
204
+ }
205
+ totalPages() {
206
+ return totalPages(this.total(), DEFAULT_PAGE_SIZE);
207
+ }
208
+ prevPage() {
209
+ if (this.page() <= 1)
210
+ return;
211
+ this.page.update((p) => p - 1);
212
+ this.load();
213
+ }
214
+ nextPage() {
215
+ if (this.page() >= this.totalPages())
216
+ return;
217
+ this.page.update((p) => p + 1);
218
+ this.load();
219
+ }
220
+ historyLink(callId) {
221
+ return callId ? ['../history', callId] : null;
222
+ }
223
+ openCallHistory(callId, ev) {
224
+ if (!callId)
225
+ return;
226
+ if (ev && ev.target.closest('a,button'))
227
+ return;
228
+ void this.router.navigate(['../history', callId], { relativeTo: this.route });
229
+ }
230
+ openTask(item) {
231
+ this.selectedPatient.set(null);
232
+ this.selectedTask.set(this.activityToTask(item));
233
+ }
234
+ openPatient(item) {
235
+ this.selectedTask.set(null);
236
+ this.selectedPatient.set(item);
237
+ }
238
+ closeTaskDrawer() {
239
+ this.selectedTask.set(null);
240
+ }
241
+ closePatientDrawer() {
242
+ this.selectedPatient.set(null);
243
+ }
244
+ onTaskStatusChange(status) {
245
+ const task = this.selectedTask();
246
+ if (!task)
247
+ return;
248
+ this.api.patchTask(task.id, status).subscribe({
249
+ next: ({ data }) => {
250
+ if (data) {
251
+ this.selectedTask.set(data);
252
+ }
253
+ else {
254
+ this.selectedTask.set({ ...task, status });
255
+ }
256
+ },
257
+ error: () => {
258
+ // Optimistic local update if patch fails (activity-sourced ids may not exist).
259
+ this.selectedTask.set({ ...task, status });
260
+ },
261
+ });
262
+ }
263
+ activityToTask(item) {
264
+ const priority = item.priority === 'urgent' || item.priority === 'high' ? 'urgent' : 'normal';
265
+ const created = this.parseDate(item.occurredAt)?.getTime() ?? Date.now();
266
+ return {
267
+ id: item.taskId || item.id,
268
+ created,
269
+ title: this.taskTitle(item),
270
+ type: 'task',
271
+ description: item.detail || '',
272
+ patient: item.patientName || 'Unknown caller',
273
+ priority,
274
+ status: 'open',
275
+ source: 'voice',
276
+ assignee: 'AI Receptionist',
277
+ callId: item.callId || undefined,
278
+ };
279
+ }
280
+ displayMrn(item) {
281
+ return item.patientMrn?.trim() || '—';
282
+ }
283
+ providerSpecialtyLabel(item) {
284
+ const s = item.providerSpecialty?.trim();
285
+ if (!s)
286
+ return null;
287
+ // Hide stray numeric IDs that sometimes land in specialty.
288
+ if (/^\d+(\.\d+)?$/.test(s))
289
+ return null;
290
+ return s;
291
+ }
292
+ /** Day-anchored stamp — a bare clock time gave no idea which day a row belonged to. */
293
+ timestampLabel(item) {
294
+ const start = this.parseDate(item.startAt || item.occurredAt);
295
+ if (!start)
296
+ return '—';
297
+ const stamp = `${this.formatDay(start)}, ${this.formatClock(start)}`;
298
+ const end = this.parseDate(item.endAt);
299
+ if (end && end.getTime() > start.getTime()) {
300
+ return `${stamp} – ${this.formatClock(end)}`;
301
+ }
302
+ return stamp;
303
+ }
304
+ durationLabel(item) {
305
+ const mins = item.durationMinutes;
306
+ if (mins == null || mins <= 0) {
307
+ if (item.startAt && item.endAt) {
308
+ const ms = Date.parse(item.endAt) - Date.parse(item.startAt);
309
+ if (Number.isFinite(ms) && ms > 0) {
310
+ return `${Math.round(ms / 60000)} MIN`;
311
+ }
312
+ }
313
+ return null;
314
+ }
315
+ return `${mins} MIN`;
316
+ }
317
+ appointmentTimeRange(item) {
318
+ const start = this.parseDate(item.startAt || item.occurredAt);
319
+ if (!start)
320
+ return '—';
321
+ const end = this.parseDate(item.endAt);
322
+ if (end && end.getTime() > start.getTime()) {
323
+ return `${this.formatClock(start)} - ${this.formatClock(end)}`;
324
+ }
325
+ return this.formatClock(start);
326
+ }
327
+ appointmentDayLabel(item) {
328
+ const start = this.parseDate(item.startAt || item.occurredAt);
329
+ return start ? this.formatDay(start) : '—';
330
+ }
331
+ dueLabel(item) {
332
+ const d = this.parseDate(item.occurredAt || item.startAt);
333
+ if (!d)
334
+ return '—';
335
+ return d.toLocaleDateString('en-US', {
336
+ month: 'short',
337
+ day: 'numeric',
338
+ year: 'numeric',
339
+ });
340
+ }
341
+ categoryLabel(item) {
342
+ if (item.visitType)
343
+ return item.visitType;
344
+ switch (item.type) {
345
+ case 'appointment_booked':
346
+ return 'Appointment';
347
+ case 'patient_created':
348
+ return 'Registration';
349
+ case 'task_created':
350
+ return item.title && !/^task created$/i.test(item.title) ? item.title : 'Task';
351
+ default:
352
+ return item.title || 'Activity';
353
+ }
354
+ }
355
+ patientMeta(item) {
356
+ const parts = [];
357
+ const dob = item.patientDob;
358
+ const age = item.patientAge ?? (dob ? ageFromDob(dob) : null);
359
+ if (dob && age != null)
360
+ parts.push(`DOB: ${this.formatDob(dob)} · ${age}Y`);
361
+ else if (dob)
362
+ parts.push(`DOB: ${this.formatDob(dob)}`);
363
+ else if (age != null)
364
+ parts.push(`${age}Y`);
365
+ if (item.patientGender)
366
+ parts.push(item.patientGender);
367
+ return parts.length ? parts.join(' | ') : null;
368
+ }
369
+ patientInitials(item) {
370
+ return item.patientInitials || initialsFromName(item.patientName) || '?';
371
+ }
372
+ providerInitials(item) {
373
+ return item.providerInitials || initialsFromName(item.providerName) || '?';
374
+ }
375
+ avatarTone(key, salt) {
376
+ const raw = `${key || 'x'}:${salt}`;
377
+ let hash = 0;
378
+ for (let i = 0; i < raw.length; i++)
379
+ hash = (hash + raw.charCodeAt(i) * (i + 1)) % 997;
380
+ return AVATAR_TONES[hash % AVATAR_TONES.length];
381
+ }
382
+ insuranceState(item) {
383
+ if (item.hasInsurance === true)
384
+ return 'covered';
385
+ if (item.hasInsurance === false)
386
+ return 'none';
387
+ return 'unknown';
388
+ }
389
+ insuranceIcon(item) {
390
+ switch (this.insuranceState(item)) {
391
+ case 'covered':
392
+ return 'verified_user';
393
+ case 'none':
394
+ return 'gpp_bad';
395
+ default:
396
+ return 'gpp_maybe';
397
+ }
398
+ }
399
+ insuranceLabel(item) {
400
+ switch (this.insuranceState(item)) {
401
+ case 'covered':
402
+ return 'Insured';
403
+ case 'none':
404
+ return 'No insurance';
405
+ default:
406
+ return 'Unverified';
407
+ }
408
+ }
409
+ insuranceTitle(item) {
410
+ switch (this.insuranceState(item)) {
411
+ case 'covered':
412
+ return item.insuranceName ? `Insured — ${item.insuranceName}` : 'Insured';
413
+ case 'none':
414
+ return 'No insurance on file';
415
+ default:
416
+ return 'Insurance not verified';
417
+ }
418
+ }
419
+ isTaskRow(item) {
420
+ return item.type === 'task_created';
421
+ }
422
+ taskTitle(item) {
423
+ const title = (item.title || '').trim();
424
+ if (title && !/^task created$/i.test(title))
425
+ return title;
426
+ return item.visitType || 'Task';
427
+ }
428
+ /** Subtitle under the task title; omitted when it duplicates the title. */
429
+ taskDetail(item) {
430
+ const detail = (item.detail || '').trim();
431
+ if (!detail)
432
+ return null;
433
+ const title = this.taskTitle(item).trim().toLowerCase();
434
+ if (detail.toLowerCase() === title)
435
+ return null;
436
+ if (/^task created$/i.test(detail))
437
+ return null;
438
+ return detail;
439
+ }
440
+ priorityLabel(item) {
441
+ switch (item.priority) {
442
+ case 'urgent':
443
+ return 'Urgent';
444
+ case 'high':
445
+ return 'High';
446
+ case 'low':
447
+ return 'Low';
448
+ case 'medium':
449
+ case 'normal':
450
+ default:
451
+ return 'Medium';
452
+ }
453
+ }
454
+ priorityTone(item) {
455
+ switch (item.priority) {
456
+ case 'urgent':
457
+ return 'urgent';
458
+ case 'high':
459
+ return 'high';
460
+ case 'low':
461
+ return 'low';
462
+ default:
463
+ return 'medium';
464
+ }
465
+ }
466
+ isRegistrationTab() {
467
+ return this.activeTab() === 'patient_created';
468
+ }
469
+ emptyLabel() {
470
+ if (this.callIdFilter())
471
+ return 'No activities found for this call.';
472
+ switch (this.activeTab()) {
473
+ case 'task_created':
474
+ return 'No tasks created by the AI yet.';
475
+ case 'patient_created':
476
+ return 'No patient registrations yet.';
477
+ default:
478
+ return 'No appointments booked by the AI yet.';
479
+ }
480
+ }
481
+ parseDate(iso) {
482
+ if (!iso)
483
+ return null;
484
+ const d = new Date(iso);
485
+ return Number.isNaN(d.getTime()) ? null : d;
486
+ }
487
+ formatDay(d) {
488
+ const midnight = (x) => new Date(x.getFullYear(), x.getMonth(), x.getDate()).getTime();
489
+ const now = new Date();
490
+ const days = Math.round((midnight(d) - midnight(now)) / 86_400_000);
491
+ if (days === 0)
492
+ return 'Today';
493
+ if (days === 1)
494
+ return 'Tomorrow';
495
+ if (days === -1)
496
+ return 'Yesterday';
497
+ return d.toLocaleDateString(undefined, d.getFullYear() === now.getFullYear()
498
+ ? { weekday: 'short', month: 'short', day: 'numeric' }
499
+ : { month: 'short', day: 'numeric', year: 'numeric' });
500
+ }
501
+ formatClock(d) {
502
+ return d.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' });
503
+ }
504
+ formatDob(iso) {
505
+ const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(iso.trim());
506
+ if (m)
507
+ return `${m[2]}-${m[3]}-${m[1]}`;
508
+ const d = new Date(iso);
509
+ if (Number.isNaN(d.getTime()))
510
+ return iso;
511
+ const mm = String(d.getMonth() + 1).padStart(2, '0');
512
+ const dd = String(d.getDate()).padStart(2, '0');
513
+ return `${mm}-${dd}-${d.getFullYear()}`;
514
+ }
515
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ActivitiesPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
516
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: ActivitiesPageComponent, isStandalone: true, selector: "eds-activities-page", host: { classAttribute: "block h-full min-h-0" }, ngImport: i0, template: "<div class=\"activities-page flex h-full min-h-0 min-w-0 max-w-full flex-col overflow-hidden bg-lighter p-base\">\n <div class=\"mb-sm flex shrink-0 flex-col gap-sm\">\n <header class=\"relative z-10 flex items-start justify-between gap-base bg-lighter\">\n <div class=\"grid gap-2xs\">\n <h4>Activities</h4>\n <p class=\"fs-sm text-light\">All activities done by the AI at one place</p>\n </div>\n <div class=\"flex shrink-0 items-center gap-base\">\n <div class=\"relative w-60\">\n <eds-search-field\n inputClass=\"form-input w-full pr-10\"\n placeholder=\"Search here\"\n ariaLabel=\"Search activities\"\n [value]=\"search()\"\n (valueChange)=\"onSearchInput($event)\"\n (enter)=\"applySearch()\" />\n <span class=\"pointer-events-none absolute inset-y-0 right-3 inline-flex items-center text-lighter\">\n <ed-icon name=\"search\" [size]=\"20\" color=\"currentColor\" />\n </span>\n </div>\n </div>\n </header>\n\n @if (callIdFilter(); as callId) {\n <div class=\"flex flex-wrap items-center justify-between gap-sm rounded-lg bg-primary-50 px-base py-sm fs-sm text-body\"\n role=\"status\">\n <span>Showing activities for call <strong class=\"text-dark\">{{ callId }}</strong></span>\n <button\n type=\"button\"\n class=\"cursor-pointer border-none bg-transparent px-0 py-2xs fs-sm text-primary hover:underline\"\n (click)=\"clearCallFilter()\">\n Clear filter\n </button>\n </div>\n }\n\n <nav class=\"flex flex-wrap gap-xs bg-white rounded-md px-xs py-xs\" aria-label=\"Activity type\">\n @for (tab of tabs; track tab.id) {\n <button\n type=\"button\"\n [class]=\"\n activeTab() === tab.id\n ? 'cursor-pointer rounded-full border-none bg-primary-50 px-base py-xs text-primary transition-colors'\n : 'cursor-pointer rounded-full border-none bg-transparent px-base py-xs text-light transition-colors hover:bg-white hover:text-dark'\"\n (click)=\"setTab(tab.id)\">\n {{ tab.label }}\n </button>\n }\n </nav>\n </div>\n\n @if (loadError()) {\n <p class=\"fs-sm text-danger\" role=\"alert\">{{ loadError() }}</p>\n } @else {\n <div class=\"flex min-h-0 min-w-0 max-w-full flex-1 flex-col overflow-hidden rounded-md bg-white\">\n <div class=\"eds-table-scroll min-h-0 min-w-0 flex-1\">\n @if (activeTab() === 'task_created') {\n <table class=\"table-default eds-sticky-table eds-centered-table min-w-5xl w-full\" aria-label=\"Tasks\">\n <thead>\n <tr>\n <th class=\"bg-light\">Priority</th>\n <th class=\"bg-light\">Task</th>\n <th class=\"bg-light\">Assigned by</th>\n <th class=\"bg-light\">Patient / Caller</th>\n <th class=\"bg-light\">Created date</th>\n </tr>\n </thead>\n @if (loading()) {\n <tbody aria-busy=\"true\">\n @for (_ of skeletonRows; track _) {\n <tr>\n <td>\n <div class=\"flex items-center gap-xs py-2xs\">\n <ed-skeleton class=\"h-2.5 w-2.5 !rounded-sm\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-14\"></ed-skeleton>\n </div>\n </td>\n <td>\n <div class=\"grid gap-2xs py-2xs\">\n <ed-skeleton class=\"h-3 w-44\"></ed-skeleton>\n <ed-skeleton class=\"h-5 w-24 !rounded-full\"></ed-skeleton>\n </div>\n </td>\n <td>\n <div class=\"flex items-center gap-xs py-2xs\">\n <ed-skeleton class=\"h-8 w-8 !rounded-full\"></ed-skeleton>\n <div class=\"grid gap-2xs\">\n <ed-skeleton class=\"h-3 w-24\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-16\"></ed-skeleton>\n </div>\n </div>\n </td>\n <td>\n <div class=\"flex items-center gap-xs py-2xs\">\n <ed-skeleton class=\"h-8 w-8 !rounded-md\"></ed-skeleton>\n <div class=\"grid gap-2xs\">\n <ed-skeleton class=\"h-3 w-28\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-36\"></ed-skeleton>\n </div>\n </div>\n </td>\n <td><ed-skeleton class=\"h-3 w-24\"></ed-skeleton></td>\n </tr>\n }\n </tbody>\n } @else {\n <tbody>\n @for (item of items(); track item.id; let i = $index) {\n <tr\n class=\"hover-parent relative cursor-pointer\"\n role=\"button\"\n [attr.tabindex]=\"i === 0 ? 0 : -1\"\n [attr.aria-label]=\"'View task: ' + taskTitle(item)\"\n (click)=\"openTask(item)\"\n (keyup.enter)=\"openTask(item)\"\n >\n <td>\n <div\n class=\"inline-flex items-center gap-xs text-dark\"\n [attr.data-tone]=\"priorityTone(item)\"\n >\n <span\n class=\"activities-page__priority-mark h-2.5 w-2.5 shrink-0 rotate-45 rounded-sm\"\n aria-hidden=\"true\"\n ></span>\n <span class=\"fs-sm capitalize\">{{ priorityLabel(item) }}</span>\n </div>\n </td>\n <td>\n <div class=\"grid min-w-0 gap-2xs\">\n <span\n class=\"inline-block min-w-0 max-w-72 truncate fs-sm font-semibold text-dark\"\n [smartTooltip]=\"taskTitle(item)\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ taskTitle(item) }}</span>\n <span\n class=\"inline-block min-w-0 max-w-72\"\n [smartTooltip]=\"categoryLabel(item)\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >\n <ed-badge-2 variant=\"light\" pill uppercase class=\"max-w-full\">\n <span class=\"block truncate text-dark\">{{ categoryLabel(item) }}</span>\n </ed-badge-2>\n </span>\n </div>\n </td>\n <td>\n <div class=\"flex min-w-0 items-center gap-xs\">\n <ed-avatar shape=\"circle\" size=\"base\" initials=\"AI\" tone=\"brand\" />\n <div class=\"grid min-w-0 gap-2xs\">\n <span class=\"fs-sm font-semibold text-dark\">AI Receptionist</span>\n <span class=\"fs-xs text-light\">Voice agent</span>\n </div>\n </div>\n </td>\n <td>\n <div class=\"flex min-w-0 items-center gap-xs\">\n <ed-avatar\n shape=\"box\"\n size=\"base\"\n [initials]=\"patientInitials(item)\"\n [tone]=\"avatarTone(item.patientId || item.patientName || item.id, i)\"\n />\n <div class=\"grid min-w-0 gap-2xs\">\n <span\n class=\"inline-block min-w-0 max-w-56 truncate fs-sm font-semibold text-dark\"\n [smartTooltip]=\"item.patientName || 'Unknown caller'\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ item.patientName || 'Unknown caller' }}</span>\n @if (patientMeta(item); as meta) {\n <span\n class=\"inline-block min-w-0 max-w-64 truncate fs-xs text-light\"\n [smartTooltip]=\"meta\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ meta }}</span>\n }\n </div>\n </div>\n </td>\n <td class=\"whitespace-nowrap fs-sm text-light\">{{ dueLabel(item) }}</td>\n </tr>\n } @empty {\n <tr class=\"empty-row\">\n <td colspan=\"5\" class=\"py-lg text-center fs-sm text-light\">{{ emptyLabel() }}</td>\n </tr>\n }\n </tbody>\n }\n </table>\n } @else if (activeTab() === 'appointment_booked') {\n <table class=\"table-default eds-sticky-table eds-centered-table min-w-5xl w-full\" aria-label=\"Appointments\">\n <thead>\n <tr>\n <th class=\"bg-light\">Date</th>\n <th class=\"bg-light\">Patient</th>\n <th class=\"bg-light\">Provider</th>\n <th class=\"bg-light text-right\">Insurance</th>\n </tr>\n </thead>\n @if (loading()) {\n <tbody aria-busy=\"true\">\n @for (_ of skeletonRows; track _) {\n <tr>\n <td>\n <div class=\"grid gap-2xs py-2xs\">\n <ed-skeleton class=\"h-3 w-40\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-28\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-20\"></ed-skeleton>\n </div>\n </td>\n <td>\n <div class=\"flex items-center gap-xs py-2xs\">\n <ed-skeleton class=\"h-8 w-8 !rounded-md\"></ed-skeleton>\n <div class=\"grid gap-2xs\">\n <ed-skeleton class=\"h-3 w-28\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-36\"></ed-skeleton>\n </div>\n </div>\n </td>\n <td>\n <div class=\"flex items-center gap-xs py-2xs\">\n <ed-skeleton class=\"h-8 w-8 !rounded-full\"></ed-skeleton>\n <div class=\"grid gap-2xs\">\n <ed-skeleton class=\"h-3 w-24\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-20\"></ed-skeleton>\n </div>\n </div>\n </td>\n <td class=\"text-right\">\n <div class=\"flex items-center justify-end\">\n <ed-skeleton class=\"h-8 w-8 !rounded-full\"></ed-skeleton>\n </div>\n </td>\n </tr>\n }\n </tbody>\n } @else {\n <tbody>\n @for (item of items(); track item.id; let i = $index) {\n <tr\n class=\"hover-parent relative\"\n [class.cursor-pointer]=\"!!item.callId\"\n [attr.role]=\"item.callId ? 'button' : null\"\n [attr.tabindex]=\"item.callId ? (i === 0 ? 0 : -1) : null\"\n [attr.aria-label]=\"item.callId ? 'Open call history' : null\"\n (click)=\"openCallHistory(item.callId, $event)\"\n (keyup.enter)=\"openCallHistory(item.callId)\"\n >\n <td>\n <div class=\"grid min-w-0 gap-2xs\">\n <div class=\"flex flex-wrap items-center gap-2xs\">\n <span class=\"whitespace-nowrap fs-sm font-semibold text-dark\">{{ appointmentTimeRange(item) }}</span>\n @if (durationLabel(item); as duration) {\n <span class=\"inline-flex h-5 items-center rounded bg-light px-2xs fs-xs font-medium text-dark\">\n {{ duration }}\n </span>\n }\n </div>\n <span class=\"fs-sm text-light\">{{ categoryLabel(item) }}</span>\n <span class=\"fs-xs text-light\">{{ appointmentDayLabel(item) }}</span>\n </div>\n </td>\n <td>\n <div class=\"flex min-w-0 items-center gap-xs\">\n <ed-avatar\n shape=\"box\"\n size=\"base\"\n [initials]=\"patientInitials(item)\"\n [tone]=\"avatarTone(item.patientId || item.patientName || item.id, i)\"\n />\n <div class=\"grid min-w-0 gap-2xs\">\n <span\n class=\"inline-block min-w-0 max-w-56 truncate fs-sm font-semibold text-dark\"\n [smartTooltip]=\"item.patientName || 'Unknown patient'\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ item.patientName || 'Unknown patient' }}</span>\n @if (patientMeta(item); as meta) {\n <span\n class=\"inline-block min-w-0 max-w-64 truncate fs-xs text-light\"\n [smartTooltip]=\"meta\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ meta }}</span>\n }\n </div>\n </div>\n </td>\n <td>\n @if (item.providerName) {\n <div class=\"flex min-w-0 items-center gap-xs\">\n <ed-avatar\n shape=\"circle\"\n size=\"base\"\n [initials]=\"providerInitials(item)\"\n [tone]=\"avatarTone(item.providerId || item.providerName, i + 3)\"\n />\n <div class=\"grid min-w-0 gap-2xs\">\n <span\n class=\"inline-block min-w-0 max-w-56 truncate fs-sm font-semibold text-dark\"\n [smartTooltip]=\"item.providerName\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ item.providerName }}</span>\n @if (providerSpecialtyLabel(item); as specialty) {\n <span\n class=\"inline-block min-w-0 max-w-64 truncate fs-xs text-light\"\n [smartTooltip]=\"specialty\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ specialty }}</span>\n }\n </div>\n </div>\n } @else {\n <span class=\"fs-xs text-light\">No provider assigned</span>\n }\n </td>\n <td>\n <div class=\"flex items-center justify-end\">\n <span\n class=\"activities-page__insurance inline-flex h-8 w-8 items-center justify-center rounded-full border border-transparent\"\n [attr.data-state]=\"insuranceState(item)\"\n [title]=\"insuranceTitle(item)\"\n [attr.aria-label]=\"insuranceLabel(item)\"\n >\n <ed-icon [name]=\"insuranceIcon(item)\" [size]=\"18\" />\n </span>\n </div>\n </td>\n </tr>\n } @empty {\n <tr class=\"empty-row\">\n <td colspan=\"4\" class=\"py-lg text-center fs-sm text-light\">{{ emptyLabel() }}</td>\n </tr>\n }\n </tbody>\n }\n </table>\n } @else {\n <table class=\"table-default eds-sticky-table eds-centered-table min-w-5xl w-full\" aria-label=\"Registrations\">\n <thead>\n <tr>\n <th class=\"bg-light\">Date</th>\n <th class=\"bg-light\">Name</th>\n <th class=\"bg-light\">MRN</th>\n <th class=\"bg-light\">Payer</th>\n <th class=\"bg-light text-right\">Insurance</th>\n </tr>\n </thead>\n @if (loading()) {\n <tbody aria-busy=\"true\">\n @for (_ of skeletonRows; track _) {\n <tr>\n <td>\n <div class=\"grid gap-2xs py-2xs\">\n <ed-skeleton class=\"h-3 w-28\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-20\"></ed-skeleton>\n </div>\n </td>\n <td>\n <div class=\"flex items-center gap-xs py-2xs\">\n <ed-skeleton class=\"h-8 w-8 !rounded-md\"></ed-skeleton>\n <div class=\"grid gap-2xs\">\n <ed-skeleton class=\"h-3 w-28\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-36\"></ed-skeleton>\n </div>\n </div>\n </td>\n <td><ed-skeleton class=\"h-3 w-16\"></ed-skeleton></td>\n <td><ed-skeleton class=\"h-3 w-24\"></ed-skeleton></td>\n <td class=\"text-right\">\n <div class=\"flex items-center justify-end\">\n <ed-skeleton class=\"h-8 w-8 !rounded-full\"></ed-skeleton>\n </div>\n </td>\n </tr>\n }\n </tbody>\n } @else {\n <tbody>\n @for (item of items(); track item.id; let i = $index) {\n <tr\n class=\"hover-parent relative cursor-pointer\"\n role=\"button\"\n [attr.tabindex]=\"i === 0 ? 0 : -1\"\n [attr.aria-label]=\"'View patient: ' + (item.patientName || 'Unknown patient')\"\n (click)=\"openPatient(item)\"\n (keyup.enter)=\"openPatient(item)\"\n >\n <td>\n <div class=\"grid min-w-0 gap-2xs\">\n <span class=\"whitespace-nowrap fs-sm font-semibold text-dark\">{{ timestampLabel(item) }}</span>\n <span class=\"fs-sm text-light\">{{ categoryLabel(item) }}</span>\n </div>\n </td>\n <td>\n <div class=\"flex min-w-0 items-center gap-xs\">\n <ed-avatar\n shape=\"box\"\n size=\"base\"\n [initials]=\"patientInitials(item)\"\n [tone]=\"avatarTone(item.patientId || item.patientName || item.id, i)\"\n />\n <div class=\"grid min-w-0 gap-2xs\">\n <span\n class=\"inline-block min-w-0 max-w-56 truncate fs-sm font-semibold text-dark\"\n [smartTooltip]=\"item.patientName || 'Unknown patient'\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ item.patientName || 'Unknown patient' }}</span>\n @if (patientMeta(item); as meta) {\n <span\n class=\"inline-block min-w-0 max-w-64 truncate fs-xs text-light\"\n [smartTooltip]=\"meta\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ meta }}</span>\n }\n </div>\n </div>\n </td>\n <td class=\"fs-sm text-body\">{{ displayMrn(item) }}</td>\n <td>\n @if (insuranceState(item) === 'covered') {\n <span class=\"fs-sm text-dark\">{{ item.insuranceName || 'Insured' }}</span>\n } @else {\n <span class=\"fs-sm text-dark\">Add Insurance</span>\n }\n </td>\n <td>\n <div class=\"flex items-center justify-end\">\n <span\n class=\"activities-page__insurance inline-flex h-8 w-8 items-center justify-center rounded-full border border-transparent\"\n [attr.data-state]=\"insuranceState(item)\"\n [title]=\"insuranceTitle(item)\"\n [attr.aria-label]=\"insuranceLabel(item)\"\n >\n <ed-icon [name]=\"insuranceIcon(item)\" [size]=\"18\" />\n </span>\n </div>\n </td>\n </tr>\n } @empty {\n <tr class=\"empty-row\">\n <td colspan=\"5\" class=\"py-lg text-center fs-sm text-light\">{{ emptyLabel() }}</td>\n </tr>\n }\n </tbody>\n }\n </table>\n }\n </div>\n\n @if (!loading() && total() > pageSize) {\n <footer class=\"mt-sm flex shrink-0 flex-wrap items-center justify-between gap-sm border-t border-light pt-sm\">\n <span class=\"fs-sm text-light\">\n Page {{ page() }} of {{ totalPages() }} \u00B7 {{ total() }} total\n </span>\n <div class=\"flex gap-xs\">\n <ed-button\n variant=\"secondary\"\n outline\n size=\"sm\"\n [disabled]=\"page() <= 1\"\n (click$)=\"prevPage()\"\n >\n Previous\n </ed-button>\n <ed-button\n variant=\"secondary\"\n outline\n size=\"sm\"\n [disabled]=\"page() >= totalPages()\"\n (click$)=\"nextPage()\"\n >\n Next\n </ed-button>\n </div>\n </footer>\n }\n </div>\n }\n</div>\n\n@if (selectedTask(); as task) {\n <eds-task-detail-drawer\n [task]=\"task\"\n (closed)=\"closeTaskDrawer()\"\n (statusChange)=\"onTaskStatusChange($event)\"\n />\n}\n\n@if (selectedPatient(); as patient) {\n <eds-patient-profile-drawer\n [patient]=\"patient\"\n (closed)=\"closePatientDrawer()\"\n />\n}\n", styles: [".activities-page__priority-mark{background:#f59e0b}[data-tone=urgent] .activities-page__priority-mark{background:#dc2626}[data-tone=high] .activities-page__priority-mark{background:#ea580c}[data-tone=medium] .activities-page__priority-mark{background:#f59e0b}[data-tone=low] .activities-page__priority-mark{background:#94a3b8}.activities-page__insurance[data-state=covered]{background:#ecfdf5;border-color:#bbf7d0;color:#15803d}.activities-page__insurance[data-state=none]{background:#fef2f2;border-color:#fecaca;color:#b91c1c}.activities-page__insurance[data-state=unknown]{background:#f4f4f5;border-color:#e4e4e7;color:#71717a}\n"], dependencies: [{ kind: "component", type: EdAvatarComponent, selector: "ed-avatar", inputs: ["initials", "tone", "shape", "size", "gradient"] }, { kind: "component", type: IconComponent, selector: "ed-icon", inputs: ["name", "size", "color", "cssClass", "decorative", "ariaHidden", "ariaLabel", "title", "desc", "focusable", "tabIndex"] }, { kind: "component", type: EdButtonComponent, selector: "ed-button", inputs: ["variant", "size", "disabled", "ghost", "outline", "subtle", "fill", "loading", "block", "elevated", "shape", "type", "href", "routerLink", "rel", "target", "download", "ariaLabel"], outputs: ["click$"] }, { kind: "component", type: EdsSearchFieldComponent, selector: "eds-search-field", inputs: ["value", "inputId", "type", "placeholder", "ariaLabel", "inputClass"], outputs: ["valueChange", "enter"] }, { kind: "directive", type: SmartTooltipDirective, selector: "[smartTooltip]", inputs: ["smartTooltip", "tooltipAllowHtml", "tooltipTemplate", "tooltipTemplateContext", "tooltipPlacement", "tooltipTrigger", "tooltipDelayShow", "tooltipDelayHide", "tooltipOffset", "tooltipMaxWidth", "tooltipZIndex", "tooltipClass", "tooltipDisabled", "tooltipArrow", "tooltipHoverIntentMs", "tooltipHoverIntentRadius", "tooltipId", "tooltipRole", "tooltipAnimEnterMs", "tooltipAnimExitMs", "tooltipAnimEasing", "tooltipHoverable", "tooltipHandoffMs", "tooltipOpen", "tooltipOnlyWhenTruncated"] }, { kind: "component", type: EdSkeletonComponent, selector: "ed-skeleton", inputs: ["class"] }, { kind: "component", type: TaskDetailDrawerComponent, selector: "eds-task-detail-drawer", inputs: ["task"], outputs: ["closed", "statusChange"] }, { kind: "component", type: PatientProfileDrawerComponent, selector: "eds-patient-profile-drawer", inputs: ["patient"], outputs: ["closed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
517
+ }
518
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ActivitiesPageComponent, decorators: [{
519
+ type: Component,
520
+ args: [{ selector: 'eds-activities-page', imports: [
521
+ EdAvatarComponent,
522
+ IconComponent,
523
+ EdButtonComponent,
524
+ EdsSearchFieldComponent,
525
+ SmartTooltipDirective,
526
+ EdSkeletonComponent,
527
+ TaskDetailDrawerComponent,
528
+ PatientProfileDrawerComponent,
529
+ ], changeDetection: ChangeDetectionStrategy.OnPush, schemas: [CUSTOM_ELEMENTS_SCHEMA], host: { class: 'block h-full min-h-0' }, template: "<div class=\"activities-page flex h-full min-h-0 min-w-0 max-w-full flex-col overflow-hidden bg-lighter p-base\">\n <div class=\"mb-sm flex shrink-0 flex-col gap-sm\">\n <header class=\"relative z-10 flex items-start justify-between gap-base bg-lighter\">\n <div class=\"grid gap-2xs\">\n <h4>Activities</h4>\n <p class=\"fs-sm text-light\">All activities done by the AI at one place</p>\n </div>\n <div class=\"flex shrink-0 items-center gap-base\">\n <div class=\"relative w-60\">\n <eds-search-field\n inputClass=\"form-input w-full pr-10\"\n placeholder=\"Search here\"\n ariaLabel=\"Search activities\"\n [value]=\"search()\"\n (valueChange)=\"onSearchInput($event)\"\n (enter)=\"applySearch()\" />\n <span class=\"pointer-events-none absolute inset-y-0 right-3 inline-flex items-center text-lighter\">\n <ed-icon name=\"search\" [size]=\"20\" color=\"currentColor\" />\n </span>\n </div>\n </div>\n </header>\n\n @if (callIdFilter(); as callId) {\n <div class=\"flex flex-wrap items-center justify-between gap-sm rounded-lg bg-primary-50 px-base py-sm fs-sm text-body\"\n role=\"status\">\n <span>Showing activities for call <strong class=\"text-dark\">{{ callId }}</strong></span>\n <button\n type=\"button\"\n class=\"cursor-pointer border-none bg-transparent px-0 py-2xs fs-sm text-primary hover:underline\"\n (click)=\"clearCallFilter()\">\n Clear filter\n </button>\n </div>\n }\n\n <nav class=\"flex flex-wrap gap-xs bg-white rounded-md px-xs py-xs\" aria-label=\"Activity type\">\n @for (tab of tabs; track tab.id) {\n <button\n type=\"button\"\n [class]=\"\n activeTab() === tab.id\n ? 'cursor-pointer rounded-full border-none bg-primary-50 px-base py-xs text-primary transition-colors'\n : 'cursor-pointer rounded-full border-none bg-transparent px-base py-xs text-light transition-colors hover:bg-white hover:text-dark'\"\n (click)=\"setTab(tab.id)\">\n {{ tab.label }}\n </button>\n }\n </nav>\n </div>\n\n @if (loadError()) {\n <p class=\"fs-sm text-danger\" role=\"alert\">{{ loadError() }}</p>\n } @else {\n <div class=\"flex min-h-0 min-w-0 max-w-full flex-1 flex-col overflow-hidden rounded-md bg-white\">\n <div class=\"eds-table-scroll min-h-0 min-w-0 flex-1\">\n @if (activeTab() === 'task_created') {\n <table class=\"table-default eds-sticky-table eds-centered-table min-w-5xl w-full\" aria-label=\"Tasks\">\n <thead>\n <tr>\n <th class=\"bg-light\">Priority</th>\n <th class=\"bg-light\">Task</th>\n <th class=\"bg-light\">Assigned by</th>\n <th class=\"bg-light\">Patient / Caller</th>\n <th class=\"bg-light\">Created date</th>\n </tr>\n </thead>\n @if (loading()) {\n <tbody aria-busy=\"true\">\n @for (_ of skeletonRows; track _) {\n <tr>\n <td>\n <div class=\"flex items-center gap-xs py-2xs\">\n <ed-skeleton class=\"h-2.5 w-2.5 !rounded-sm\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-14\"></ed-skeleton>\n </div>\n </td>\n <td>\n <div class=\"grid gap-2xs py-2xs\">\n <ed-skeleton class=\"h-3 w-44\"></ed-skeleton>\n <ed-skeleton class=\"h-5 w-24 !rounded-full\"></ed-skeleton>\n </div>\n </td>\n <td>\n <div class=\"flex items-center gap-xs py-2xs\">\n <ed-skeleton class=\"h-8 w-8 !rounded-full\"></ed-skeleton>\n <div class=\"grid gap-2xs\">\n <ed-skeleton class=\"h-3 w-24\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-16\"></ed-skeleton>\n </div>\n </div>\n </td>\n <td>\n <div class=\"flex items-center gap-xs py-2xs\">\n <ed-skeleton class=\"h-8 w-8 !rounded-md\"></ed-skeleton>\n <div class=\"grid gap-2xs\">\n <ed-skeleton class=\"h-3 w-28\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-36\"></ed-skeleton>\n </div>\n </div>\n </td>\n <td><ed-skeleton class=\"h-3 w-24\"></ed-skeleton></td>\n </tr>\n }\n </tbody>\n } @else {\n <tbody>\n @for (item of items(); track item.id; let i = $index) {\n <tr\n class=\"hover-parent relative cursor-pointer\"\n role=\"button\"\n [attr.tabindex]=\"i === 0 ? 0 : -1\"\n [attr.aria-label]=\"'View task: ' + taskTitle(item)\"\n (click)=\"openTask(item)\"\n (keyup.enter)=\"openTask(item)\"\n >\n <td>\n <div\n class=\"inline-flex items-center gap-xs text-dark\"\n [attr.data-tone]=\"priorityTone(item)\"\n >\n <span\n class=\"activities-page__priority-mark h-2.5 w-2.5 shrink-0 rotate-45 rounded-sm\"\n aria-hidden=\"true\"\n ></span>\n <span class=\"fs-sm capitalize\">{{ priorityLabel(item) }}</span>\n </div>\n </td>\n <td>\n <div class=\"grid min-w-0 gap-2xs\">\n <span\n class=\"inline-block min-w-0 max-w-72 truncate fs-sm font-semibold text-dark\"\n [smartTooltip]=\"taskTitle(item)\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ taskTitle(item) }}</span>\n <span\n class=\"inline-block min-w-0 max-w-72\"\n [smartTooltip]=\"categoryLabel(item)\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >\n <ed-badge-2 variant=\"light\" pill uppercase class=\"max-w-full\">\n <span class=\"block truncate text-dark\">{{ categoryLabel(item) }}</span>\n </ed-badge-2>\n </span>\n </div>\n </td>\n <td>\n <div class=\"flex min-w-0 items-center gap-xs\">\n <ed-avatar shape=\"circle\" size=\"base\" initials=\"AI\" tone=\"brand\" />\n <div class=\"grid min-w-0 gap-2xs\">\n <span class=\"fs-sm font-semibold text-dark\">AI Receptionist</span>\n <span class=\"fs-xs text-light\">Voice agent</span>\n </div>\n </div>\n </td>\n <td>\n <div class=\"flex min-w-0 items-center gap-xs\">\n <ed-avatar\n shape=\"box\"\n size=\"base\"\n [initials]=\"patientInitials(item)\"\n [tone]=\"avatarTone(item.patientId || item.patientName || item.id, i)\"\n />\n <div class=\"grid min-w-0 gap-2xs\">\n <span\n class=\"inline-block min-w-0 max-w-56 truncate fs-sm font-semibold text-dark\"\n [smartTooltip]=\"item.patientName || 'Unknown caller'\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ item.patientName || 'Unknown caller' }}</span>\n @if (patientMeta(item); as meta) {\n <span\n class=\"inline-block min-w-0 max-w-64 truncate fs-xs text-light\"\n [smartTooltip]=\"meta\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ meta }}</span>\n }\n </div>\n </div>\n </td>\n <td class=\"whitespace-nowrap fs-sm text-light\">{{ dueLabel(item) }}</td>\n </tr>\n } @empty {\n <tr class=\"empty-row\">\n <td colspan=\"5\" class=\"py-lg text-center fs-sm text-light\">{{ emptyLabel() }}</td>\n </tr>\n }\n </tbody>\n }\n </table>\n } @else if (activeTab() === 'appointment_booked') {\n <table class=\"table-default eds-sticky-table eds-centered-table min-w-5xl w-full\" aria-label=\"Appointments\">\n <thead>\n <tr>\n <th class=\"bg-light\">Date</th>\n <th class=\"bg-light\">Patient</th>\n <th class=\"bg-light\">Provider</th>\n <th class=\"bg-light text-right\">Insurance</th>\n </tr>\n </thead>\n @if (loading()) {\n <tbody aria-busy=\"true\">\n @for (_ of skeletonRows; track _) {\n <tr>\n <td>\n <div class=\"grid gap-2xs py-2xs\">\n <ed-skeleton class=\"h-3 w-40\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-28\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-20\"></ed-skeleton>\n </div>\n </td>\n <td>\n <div class=\"flex items-center gap-xs py-2xs\">\n <ed-skeleton class=\"h-8 w-8 !rounded-md\"></ed-skeleton>\n <div class=\"grid gap-2xs\">\n <ed-skeleton class=\"h-3 w-28\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-36\"></ed-skeleton>\n </div>\n </div>\n </td>\n <td>\n <div class=\"flex items-center gap-xs py-2xs\">\n <ed-skeleton class=\"h-8 w-8 !rounded-full\"></ed-skeleton>\n <div class=\"grid gap-2xs\">\n <ed-skeleton class=\"h-3 w-24\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-20\"></ed-skeleton>\n </div>\n </div>\n </td>\n <td class=\"text-right\">\n <div class=\"flex items-center justify-end\">\n <ed-skeleton class=\"h-8 w-8 !rounded-full\"></ed-skeleton>\n </div>\n </td>\n </tr>\n }\n </tbody>\n } @else {\n <tbody>\n @for (item of items(); track item.id; let i = $index) {\n <tr\n class=\"hover-parent relative\"\n [class.cursor-pointer]=\"!!item.callId\"\n [attr.role]=\"item.callId ? 'button' : null\"\n [attr.tabindex]=\"item.callId ? (i === 0 ? 0 : -1) : null\"\n [attr.aria-label]=\"item.callId ? 'Open call history' : null\"\n (click)=\"openCallHistory(item.callId, $event)\"\n (keyup.enter)=\"openCallHistory(item.callId)\"\n >\n <td>\n <div class=\"grid min-w-0 gap-2xs\">\n <div class=\"flex flex-wrap items-center gap-2xs\">\n <span class=\"whitespace-nowrap fs-sm font-semibold text-dark\">{{ appointmentTimeRange(item) }}</span>\n @if (durationLabel(item); as duration) {\n <span class=\"inline-flex h-5 items-center rounded bg-light px-2xs fs-xs font-medium text-dark\">\n {{ duration }}\n </span>\n }\n </div>\n <span class=\"fs-sm text-light\">{{ categoryLabel(item) }}</span>\n <span class=\"fs-xs text-light\">{{ appointmentDayLabel(item) }}</span>\n </div>\n </td>\n <td>\n <div class=\"flex min-w-0 items-center gap-xs\">\n <ed-avatar\n shape=\"box\"\n size=\"base\"\n [initials]=\"patientInitials(item)\"\n [tone]=\"avatarTone(item.patientId || item.patientName || item.id, i)\"\n />\n <div class=\"grid min-w-0 gap-2xs\">\n <span\n class=\"inline-block min-w-0 max-w-56 truncate fs-sm font-semibold text-dark\"\n [smartTooltip]=\"item.patientName || 'Unknown patient'\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ item.patientName || 'Unknown patient' }}</span>\n @if (patientMeta(item); as meta) {\n <span\n class=\"inline-block min-w-0 max-w-64 truncate fs-xs text-light\"\n [smartTooltip]=\"meta\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ meta }}</span>\n }\n </div>\n </div>\n </td>\n <td>\n @if (item.providerName) {\n <div class=\"flex min-w-0 items-center gap-xs\">\n <ed-avatar\n shape=\"circle\"\n size=\"base\"\n [initials]=\"providerInitials(item)\"\n [tone]=\"avatarTone(item.providerId || item.providerName, i + 3)\"\n />\n <div class=\"grid min-w-0 gap-2xs\">\n <span\n class=\"inline-block min-w-0 max-w-56 truncate fs-sm font-semibold text-dark\"\n [smartTooltip]=\"item.providerName\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ item.providerName }}</span>\n @if (providerSpecialtyLabel(item); as specialty) {\n <span\n class=\"inline-block min-w-0 max-w-64 truncate fs-xs text-light\"\n [smartTooltip]=\"specialty\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ specialty }}</span>\n }\n </div>\n </div>\n } @else {\n <span class=\"fs-xs text-light\">No provider assigned</span>\n }\n </td>\n <td>\n <div class=\"flex items-center justify-end\">\n <span\n class=\"activities-page__insurance inline-flex h-8 w-8 items-center justify-center rounded-full border border-transparent\"\n [attr.data-state]=\"insuranceState(item)\"\n [title]=\"insuranceTitle(item)\"\n [attr.aria-label]=\"insuranceLabel(item)\"\n >\n <ed-icon [name]=\"insuranceIcon(item)\" [size]=\"18\" />\n </span>\n </div>\n </td>\n </tr>\n } @empty {\n <tr class=\"empty-row\">\n <td colspan=\"4\" class=\"py-lg text-center fs-sm text-light\">{{ emptyLabel() }}</td>\n </tr>\n }\n </tbody>\n }\n </table>\n } @else {\n <table class=\"table-default eds-sticky-table eds-centered-table min-w-5xl w-full\" aria-label=\"Registrations\">\n <thead>\n <tr>\n <th class=\"bg-light\">Date</th>\n <th class=\"bg-light\">Name</th>\n <th class=\"bg-light\">MRN</th>\n <th class=\"bg-light\">Payer</th>\n <th class=\"bg-light text-right\">Insurance</th>\n </tr>\n </thead>\n @if (loading()) {\n <tbody aria-busy=\"true\">\n @for (_ of skeletonRows; track _) {\n <tr>\n <td>\n <div class=\"grid gap-2xs py-2xs\">\n <ed-skeleton class=\"h-3 w-28\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-20\"></ed-skeleton>\n </div>\n </td>\n <td>\n <div class=\"flex items-center gap-xs py-2xs\">\n <ed-skeleton class=\"h-8 w-8 !rounded-md\"></ed-skeleton>\n <div class=\"grid gap-2xs\">\n <ed-skeleton class=\"h-3 w-28\"></ed-skeleton>\n <ed-skeleton class=\"h-3 w-36\"></ed-skeleton>\n </div>\n </div>\n </td>\n <td><ed-skeleton class=\"h-3 w-16\"></ed-skeleton></td>\n <td><ed-skeleton class=\"h-3 w-24\"></ed-skeleton></td>\n <td class=\"text-right\">\n <div class=\"flex items-center justify-end\">\n <ed-skeleton class=\"h-8 w-8 !rounded-full\"></ed-skeleton>\n </div>\n </td>\n </tr>\n }\n </tbody>\n } @else {\n <tbody>\n @for (item of items(); track item.id; let i = $index) {\n <tr\n class=\"hover-parent relative cursor-pointer\"\n role=\"button\"\n [attr.tabindex]=\"i === 0 ? 0 : -1\"\n [attr.aria-label]=\"'View patient: ' + (item.patientName || 'Unknown patient')\"\n (click)=\"openPatient(item)\"\n (keyup.enter)=\"openPatient(item)\"\n >\n <td>\n <div class=\"grid min-w-0 gap-2xs\">\n <span class=\"whitespace-nowrap fs-sm font-semibold text-dark\">{{ timestampLabel(item) }}</span>\n <span class=\"fs-sm text-light\">{{ categoryLabel(item) }}</span>\n </div>\n </td>\n <td>\n <div class=\"flex min-w-0 items-center gap-xs\">\n <ed-avatar\n shape=\"box\"\n size=\"base\"\n [initials]=\"patientInitials(item)\"\n [tone]=\"avatarTone(item.patientId || item.patientName || item.id, i)\"\n />\n <div class=\"grid min-w-0 gap-2xs\">\n <span\n class=\"inline-block min-w-0 max-w-56 truncate fs-sm font-semibold text-dark\"\n [smartTooltip]=\"item.patientName || 'Unknown patient'\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ item.patientName || 'Unknown patient' }}</span>\n @if (patientMeta(item); as meta) {\n <span\n class=\"inline-block min-w-0 max-w-64 truncate fs-xs text-light\"\n [smartTooltip]=\"meta\"\n [tooltipOnlyWhenTruncated]=\"true\"\n >{{ meta }}</span>\n }\n </div>\n </div>\n </td>\n <td class=\"fs-sm text-body\">{{ displayMrn(item) }}</td>\n <td>\n @if (insuranceState(item) === 'covered') {\n <span class=\"fs-sm text-dark\">{{ item.insuranceName || 'Insured' }}</span>\n } @else {\n <span class=\"fs-sm text-dark\">Add Insurance</span>\n }\n </td>\n <td>\n <div class=\"flex items-center justify-end\">\n <span\n class=\"activities-page__insurance inline-flex h-8 w-8 items-center justify-center rounded-full border border-transparent\"\n [attr.data-state]=\"insuranceState(item)\"\n [title]=\"insuranceTitle(item)\"\n [attr.aria-label]=\"insuranceLabel(item)\"\n >\n <ed-icon [name]=\"insuranceIcon(item)\" [size]=\"18\" />\n </span>\n </div>\n </td>\n </tr>\n } @empty {\n <tr class=\"empty-row\">\n <td colspan=\"5\" class=\"py-lg text-center fs-sm text-light\">{{ emptyLabel() }}</td>\n </tr>\n }\n </tbody>\n }\n </table>\n }\n </div>\n\n @if (!loading() && total() > pageSize) {\n <footer class=\"mt-sm flex shrink-0 flex-wrap items-center justify-between gap-sm border-t border-light pt-sm\">\n <span class=\"fs-sm text-light\">\n Page {{ page() }} of {{ totalPages() }} \u00B7 {{ total() }} total\n </span>\n <div class=\"flex gap-xs\">\n <ed-button\n variant=\"secondary\"\n outline\n size=\"sm\"\n [disabled]=\"page() <= 1\"\n (click$)=\"prevPage()\"\n >\n Previous\n </ed-button>\n <ed-button\n variant=\"secondary\"\n outline\n size=\"sm\"\n [disabled]=\"page() >= totalPages()\"\n (click$)=\"nextPage()\"\n >\n Next\n </ed-button>\n </div>\n </footer>\n }\n </div>\n }\n</div>\n\n@if (selectedTask(); as task) {\n <eds-task-detail-drawer\n [task]=\"task\"\n (closed)=\"closeTaskDrawer()\"\n (statusChange)=\"onTaskStatusChange($event)\"\n />\n}\n\n@if (selectedPatient(); as patient) {\n <eds-patient-profile-drawer\n [patient]=\"patient\"\n (closed)=\"closePatientDrawer()\"\n />\n}\n", styles: [".activities-page__priority-mark{background:#f59e0b}[data-tone=urgent] .activities-page__priority-mark{background:#dc2626}[data-tone=high] .activities-page__priority-mark{background:#ea580c}[data-tone=medium] .activities-page__priority-mark{background:#f59e0b}[data-tone=low] .activities-page__priority-mark{background:#94a3b8}.activities-page__insurance[data-state=covered]{background:#ecfdf5;border-color:#bbf7d0;color:#15803d}.activities-page__insurance[data-state=none]{background:#fef2f2;border-color:#fecaca;color:#b91c1c}.activities-page__insurance[data-state=unknown]{background:#f4f4f5;border-color:#e4e4e7;color:#71717a}\n"] }]
530
+ }] });
531
+
532
+ export { ActivitiesPageComponent };
533
+ //# sourceMappingURL=edvak99-voice-receptionist-widget-activities-page.component-dcDXT2KG.mjs.map