@edvak99/voice-receptionist-widget 1.1.9 → 1.1.10

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.
@@ -0,0 +1,455 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, output, ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
3
+ import { RouterLink, ActivatedRoute, Router } from '@angular/router';
4
+ import { DatePipe } from '@angular/common';
5
+ import { E as EdsAvatarComponent } from './edvak99-voice-receptionist-widget-eds-avatar.component-BJs_TH8X.mjs';
6
+ import { EdsIconComponent, EdsButtonComponent } from './edvak99-voice-receptionist-widget.mjs';
7
+ import { T as TaskDetailDrawerComponent } from './edvak99-voice-receptionist-widget-task-detail-drawer.component-B3KmFJNH.mjs';
8
+ import { A as AiReceptionistApiService, D as DEFAULT_PAGE_SIZE, j as totalPages, d as ageFromDob, l as initialsFromName } from './edvak99-voice-receptionist-widget-ai-receptionist-api.service-CK6tM862.mjs';
9
+
10
+ class PatientProfileDrawerComponent {
11
+ constructor() {
12
+ this.patient = input.required();
13
+ this.closed = output();
14
+ }
15
+ close() {
16
+ this.closed.emit();
17
+ }
18
+ onBackdropClick(event) {
19
+ if (event.target === event.currentTarget)
20
+ this.close();
21
+ }
22
+ display(value) {
23
+ const s = String(value ?? '').trim();
24
+ return s || '—';
25
+ }
26
+ insuranceLabel() {
27
+ const p = this.patient();
28
+ if (p.hasInsurance === true) {
29
+ return p.insuranceName ? `Insured — ${p.insuranceName}` : 'Insured';
30
+ }
31
+ if (p.hasInsurance === false)
32
+ return 'No insurance on file';
33
+ return p.insuranceName || 'Not verified';
34
+ }
35
+ insuranceTone() {
36
+ const p = this.patient();
37
+ if (p.hasInsurance === true)
38
+ return 'covered';
39
+ if (p.hasInsurance === false)
40
+ return 'none';
41
+ return 'unknown';
42
+ }
43
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: PatientProfileDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
44
+ 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 class=\"fixed inset-0 z-[200] flex justify-end bg-[rgba(15,23,42,0.4)]\"\n (click)=\"onBackdropClick($event)\"\n>\n <aside\n class=\"patient-profile-drawer flex h-full w-[min(520px,100%)] flex-col bg-[#f7f8fa] shadow-[-8px_0_32px_rgba(15,23,42,0.12)]\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"display(patient().patientName)\"\n >\n <header class=\"flex items-center justify-between gap-3 border-b border-zinc-200 bg-white px-4 py-3\">\n <h2 class=\"m-0 text-base font-semibold text-zinc-900\">Patient profile</h2>\n <button\n type=\"button\"\n class=\"inline-flex h-9 w-9 cursor-pointer items-center justify-center rounded-md border-none bg-transparent text-zinc-500 hover:bg-zinc-100 hover:text-zinc-800\"\n (click)=\"close()\"\n aria-label=\"Close\"\n >\n <eds-icon name=\"close\" [size]=\"22\" />\n </button>\n </header>\n\n <div class=\"flex min-h-0 flex-1 flex-col gap-3 overflow-y-auto p-4\">\n <section class=\"rounded-xl border border-zinc-200 bg-white p-4\">\n <div class=\"flex items-start gap-3\">\n <eds-avatar\n shape=\"box\"\n [initials]=\"patient().patientInitials || '?'\"\n tone=\"brand\"\n />\n <div class=\"min-w-0 flex-1\">\n <h3 class=\"m-0 text-xl font-semibold leading-snug text-zinc-900\">\n {{ display(patient().patientName) }}\n </h3>\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=\"patient-profile-drawer__insurance 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 <a\n [routerLink]=\"['../history', patient().callId]\"\n class=\"text-sm font-medium text-blue-600 no-underline hover:underline\"\n (click)=\"close()\"\n >\n {{ patient().callId }}\n </a>\n </div>\n </section>\n }\n </div>\n </aside>\n</div>\n", styles: [".patient-profile-drawer{animation:patient-profile-drawer-in .2s ease}.patient-profile-drawer__insurance[data-state=covered]{background:#ecfdf5;border-color:#bbf7d0;color:#15803d}.patient-profile-drawer__insurance[data-state=none]{background:#fef2f2;border-color:#fecaca;color:#b91c1c}.patient-profile-drawer__insurance[data-state=unknown]{background:#f4f4f5;border-color:#e4e4e7;color:#71717a}@keyframes patient-profile-drawer-in{0%{transform:translate(100%)}to{transform:translate(0)}}\n"], dependencies: [{ kind: "pipe", type: DatePipe, name: "date" }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: EdsAvatarComponent, selector: "eds-avatar", inputs: ["initials", "tone", "shape", "gradient"] }, { kind: "component", type: EdsIconComponent, selector: "eds-icon", inputs: ["name", "size", "fill", "weight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
45
+ }
46
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: PatientProfileDrawerComponent, decorators: [{
47
+ type: Component,
48
+ args: [{ selector: 'eds-patient-profile-drawer', imports: [DatePipe, RouterLink, EdsAvatarComponent, EdsIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"fixed inset-0 z-[200] flex justify-end bg-[rgba(15,23,42,0.4)]\"\n (click)=\"onBackdropClick($event)\"\n>\n <aside\n class=\"patient-profile-drawer flex h-full w-[min(520px,100%)] flex-col bg-[#f7f8fa] shadow-[-8px_0_32px_rgba(15,23,42,0.12)]\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"display(patient().patientName)\"\n >\n <header class=\"flex items-center justify-between gap-3 border-b border-zinc-200 bg-white px-4 py-3\">\n <h2 class=\"m-0 text-base font-semibold text-zinc-900\">Patient profile</h2>\n <button\n type=\"button\"\n class=\"inline-flex h-9 w-9 cursor-pointer items-center justify-center rounded-md border-none bg-transparent text-zinc-500 hover:bg-zinc-100 hover:text-zinc-800\"\n (click)=\"close()\"\n aria-label=\"Close\"\n >\n <eds-icon name=\"close\" [size]=\"22\" />\n </button>\n </header>\n\n <div class=\"flex min-h-0 flex-1 flex-col gap-3 overflow-y-auto p-4\">\n <section class=\"rounded-xl border border-zinc-200 bg-white p-4\">\n <div class=\"flex items-start gap-3\">\n <eds-avatar\n shape=\"box\"\n [initials]=\"patient().patientInitials || '?'\"\n tone=\"brand\"\n />\n <div class=\"min-w-0 flex-1\">\n <h3 class=\"m-0 text-xl font-semibold leading-snug text-zinc-900\">\n {{ display(patient().patientName) }}\n </h3>\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=\"patient-profile-drawer__insurance 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 <a\n [routerLink]=\"['../history', patient().callId]\"\n class=\"text-sm font-medium text-blue-600 no-underline hover:underline\"\n (click)=\"close()\"\n >\n {{ patient().callId }}\n </a>\n </div>\n </section>\n }\n </div>\n </aside>\n</div>\n", styles: [".patient-profile-drawer{animation:patient-profile-drawer-in .2s ease}.patient-profile-drawer__insurance[data-state=covered]{background:#ecfdf5;border-color:#bbf7d0;color:#15803d}.patient-profile-drawer__insurance[data-state=none]{background:#fef2f2;border-color:#fecaca;color:#b91c1c}.patient-profile-drawer__insurance[data-state=unknown]{background:#f4f4f5;border-color:#e4e4e7;color:#71717a}@keyframes patient-profile-drawer-in{0%{transform:translate(100%)}to{transform:translate(0)}}\n"] }]
49
+ }] });
50
+
51
+ const TABS = [
52
+ { id: 'task_created', label: 'Tasks' },
53
+ { id: 'appointment_booked', label: 'Appointments' },
54
+ { id: 'patient_created', label: 'Registrations' },
55
+ ];
56
+ const AVATAR_TONES = ['green', 'amber', 'brand', 'violet', 'rose'];
57
+ class ActivitiesPageComponent {
58
+ constructor() {
59
+ this.api = inject(AiReceptionistApiService);
60
+ this.route = inject(ActivatedRoute);
61
+ this.router = inject(Router);
62
+ this.tabs = TABS;
63
+ this.pageSize = DEFAULT_PAGE_SIZE;
64
+ this.items = signal([]);
65
+ this.total = signal(0);
66
+ this.page = signal(1);
67
+ this.activeTab = signal('task_created');
68
+ this.callIdFilter = signal(null);
69
+ this.loading = signal(true);
70
+ this.loadError = signal(null);
71
+ this.selectedTask = signal(null);
72
+ this.selectedPatient = signal(null);
73
+ }
74
+ ngOnInit() {
75
+ const callId = this.route.snapshot.queryParamMap.get('callId')?.trim() || null;
76
+ if (callId)
77
+ this.callIdFilter.set(callId);
78
+ const tabParam = (this.route.snapshot.queryParamMap.get('tab') || '').trim().toLowerCase();
79
+ const tabFromQuery = this.tabFromQuery(tabParam);
80
+ if (tabFromQuery) {
81
+ this.activeTab.set(tabFromQuery);
82
+ }
83
+ else {
84
+ this.activeTab.set('task_created');
85
+ void this.router.navigate([], {
86
+ relativeTo: this.route,
87
+ queryParams: { tab: 'tasks' },
88
+ queryParamsHandling: 'merge',
89
+ replaceUrl: true,
90
+ });
91
+ }
92
+ this.load();
93
+ }
94
+ setTab(tab) {
95
+ if (this.activeTab() === tab)
96
+ return;
97
+ this.activeTab.set(tab);
98
+ this.page.set(1);
99
+ void this.router.navigate([], {
100
+ relativeTo: this.route,
101
+ queryParams: { tab: this.tabQueryValue(tab) },
102
+ queryParamsHandling: 'merge',
103
+ replaceUrl: true,
104
+ });
105
+ this.load();
106
+ }
107
+ tabFromQuery(raw) {
108
+ switch (raw) {
109
+ case 'tasks':
110
+ case 'task':
111
+ case 'task_created':
112
+ return 'task_created';
113
+ case 'appointments':
114
+ case 'appointment':
115
+ case 'appointment_booked':
116
+ return 'appointment_booked';
117
+ case 'registrations':
118
+ case 'registration':
119
+ case 'patient_created':
120
+ return 'patient_created';
121
+ default:
122
+ return null;
123
+ }
124
+ }
125
+ tabQueryValue(tab) {
126
+ switch (tab) {
127
+ case 'task_created':
128
+ return 'tasks';
129
+ case 'patient_created':
130
+ return 'registrations';
131
+ default:
132
+ return 'appointments';
133
+ }
134
+ }
135
+ clearCallFilter() {
136
+ this.callIdFilter.set(null);
137
+ this.page.set(1);
138
+ this.load();
139
+ }
140
+ load() {
141
+ this.loading.set(true);
142
+ this.loadError.set(null);
143
+ const tab = this.activeTab();
144
+ this.api
145
+ .getActivities({
146
+ page: this.page(),
147
+ limit: DEFAULT_PAGE_SIZE,
148
+ types: tab,
149
+ callId: this.callIdFilter() || undefined,
150
+ })
151
+ .subscribe({
152
+ next: ({ items, total, page }) => {
153
+ this.items.set(items);
154
+ this.total.set(total);
155
+ this.page.set(page);
156
+ this.loading.set(false);
157
+ },
158
+ error: () => {
159
+ this.loading.set(false);
160
+ this.items.set([]);
161
+ this.total.set(0);
162
+ this.loadError.set('Activities feed is unavailable. Confirm GET /api/ai-receptionist/activities is deployed.');
163
+ },
164
+ });
165
+ }
166
+ totalPages() {
167
+ return totalPages(this.total(), DEFAULT_PAGE_SIZE);
168
+ }
169
+ prevPage() {
170
+ if (this.page() <= 1)
171
+ return;
172
+ this.page.update((p) => p - 1);
173
+ this.load();
174
+ }
175
+ nextPage() {
176
+ if (this.page() >= this.totalPages())
177
+ return;
178
+ this.page.update((p) => p + 1);
179
+ this.load();
180
+ }
181
+ historyLink(callId) {
182
+ return callId ? ['../history', callId] : null;
183
+ }
184
+ openCallHistory(callId, ev) {
185
+ if (!callId)
186
+ return;
187
+ if (ev && ev.target.closest('a,button'))
188
+ return;
189
+ void this.router.navigate(['../history', callId], { relativeTo: this.route });
190
+ }
191
+ openTask(item) {
192
+ this.selectedPatient.set(null);
193
+ this.selectedTask.set(this.activityToTask(item));
194
+ }
195
+ openPatient(item) {
196
+ this.selectedTask.set(null);
197
+ this.selectedPatient.set(item);
198
+ }
199
+ closeTaskDrawer() {
200
+ this.selectedTask.set(null);
201
+ }
202
+ closePatientDrawer() {
203
+ this.selectedPatient.set(null);
204
+ }
205
+ onTaskStatusChange(status) {
206
+ const task = this.selectedTask();
207
+ if (!task)
208
+ return;
209
+ this.api.patchTask(task.id, status).subscribe({
210
+ next: ({ data }) => {
211
+ if (data) {
212
+ this.selectedTask.set(data);
213
+ }
214
+ else {
215
+ this.selectedTask.set({ ...task, status });
216
+ }
217
+ },
218
+ error: () => {
219
+ // Optimistic local update if patch fails (activity-sourced ids may not exist).
220
+ this.selectedTask.set({ ...task, status });
221
+ },
222
+ });
223
+ }
224
+ activityToTask(item) {
225
+ const priority = item.priority === 'urgent' || item.priority === 'high' ? 'urgent' : 'normal';
226
+ const created = this.parseDate(item.occurredAt)?.getTime() ?? Date.now();
227
+ return {
228
+ id: item.taskId || item.id,
229
+ created,
230
+ title: this.taskTitle(item),
231
+ type: 'task',
232
+ description: item.detail || '',
233
+ patient: item.patientName || 'Unknown caller',
234
+ priority,
235
+ status: 'open',
236
+ source: 'voice',
237
+ assignee: 'AI Receptionist',
238
+ callId: item.callId || undefined,
239
+ };
240
+ }
241
+ displayMrn(item) {
242
+ return item.patientMrn?.trim() || '—';
243
+ }
244
+ providerSpecialtyLabel(item) {
245
+ const s = item.providerSpecialty?.trim();
246
+ if (!s)
247
+ return null;
248
+ // Hide stray numeric IDs that sometimes land in specialty.
249
+ if (/^\d+(\.\d+)?$/.test(s))
250
+ return null;
251
+ return s;
252
+ }
253
+ /** Day-anchored stamp — a bare clock time gave no idea which day a row belonged to. */
254
+ timestampLabel(item) {
255
+ const start = this.parseDate(item.startAt || item.occurredAt);
256
+ if (!start)
257
+ return '—';
258
+ const stamp = `${this.formatDay(start)}, ${this.formatClock(start)}`;
259
+ const end = this.parseDate(item.endAt);
260
+ if (end && end.getTime() > start.getTime()) {
261
+ return `${stamp} – ${this.formatClock(end)}`;
262
+ }
263
+ return stamp;
264
+ }
265
+ durationLabel(item) {
266
+ const mins = item.durationMinutes;
267
+ if (mins == null || mins <= 0) {
268
+ if (item.startAt && item.endAt) {
269
+ const ms = Date.parse(item.endAt) - Date.parse(item.startAt);
270
+ if (Number.isFinite(ms) && ms > 0) {
271
+ return `${Math.round(ms / 60000)} MIN`;
272
+ }
273
+ }
274
+ return null;
275
+ }
276
+ return `${mins} MIN`;
277
+ }
278
+ categoryLabel(item) {
279
+ if (item.visitType)
280
+ return item.visitType;
281
+ switch (item.type) {
282
+ case 'appointment_booked':
283
+ return 'Appointment';
284
+ case 'patient_created':
285
+ return 'Registration';
286
+ case 'task_created':
287
+ return item.title && !/^task created$/i.test(item.title) ? item.title : 'Task';
288
+ default:
289
+ return item.title || 'Activity';
290
+ }
291
+ }
292
+ patientMeta(item) {
293
+ const parts = [];
294
+ const dob = item.patientDob;
295
+ const age = item.patientAge ?? (dob ? ageFromDob(dob) : null);
296
+ if (dob && age != null)
297
+ parts.push(`DOB: ${this.formatDob(dob)} · ${age}Y`);
298
+ else if (dob)
299
+ parts.push(`DOB: ${this.formatDob(dob)}`);
300
+ else if (age != null)
301
+ parts.push(`${age}Y`);
302
+ if (item.patientGender)
303
+ parts.push(item.patientGender);
304
+ return parts.length ? parts.join(' | ') : null;
305
+ }
306
+ patientInitials(item) {
307
+ return item.patientInitials || initialsFromName(item.patientName) || '?';
308
+ }
309
+ providerInitials(item) {
310
+ return item.providerInitials || initialsFromName(item.providerName) || '?';
311
+ }
312
+ avatarTone(key, salt) {
313
+ const raw = `${key || 'x'}:${salt}`;
314
+ let hash = 0;
315
+ for (let i = 0; i < raw.length; i++)
316
+ hash = (hash + raw.charCodeAt(i) * (i + 1)) % 997;
317
+ return AVATAR_TONES[hash % AVATAR_TONES.length];
318
+ }
319
+ insuranceState(item) {
320
+ if (item.hasInsurance === true)
321
+ return 'covered';
322
+ if (item.hasInsurance === false)
323
+ return 'none';
324
+ return 'unknown';
325
+ }
326
+ insuranceIcon(item) {
327
+ switch (this.insuranceState(item)) {
328
+ case 'covered':
329
+ return 'verified_user';
330
+ case 'none':
331
+ return 'gpp_bad';
332
+ default:
333
+ return 'gpp_maybe';
334
+ }
335
+ }
336
+ insuranceLabel(item) {
337
+ switch (this.insuranceState(item)) {
338
+ case 'covered':
339
+ return 'Insured';
340
+ case 'none':
341
+ return 'No insurance';
342
+ default:
343
+ return 'Unverified';
344
+ }
345
+ }
346
+ insuranceTitle(item) {
347
+ switch (this.insuranceState(item)) {
348
+ case 'covered':
349
+ return item.insuranceName ? `Insured — ${item.insuranceName}` : 'Insured';
350
+ case 'none':
351
+ return 'No insurance on file';
352
+ default:
353
+ return 'Insurance not verified';
354
+ }
355
+ }
356
+ isTaskRow(item) {
357
+ return item.type === 'task_created';
358
+ }
359
+ taskTitle(item) {
360
+ const title = (item.title || '').trim();
361
+ if (title && !/^task created$/i.test(title))
362
+ return title;
363
+ return item.visitType || 'Task';
364
+ }
365
+ priorityLabel(item) {
366
+ switch (item.priority) {
367
+ case 'urgent':
368
+ return 'Urgent';
369
+ case 'high':
370
+ return 'High';
371
+ case 'low':
372
+ return 'Low';
373
+ case 'medium':
374
+ case 'normal':
375
+ default:
376
+ return 'Medium';
377
+ }
378
+ }
379
+ priorityTone(item) {
380
+ switch (item.priority) {
381
+ case 'urgent':
382
+ return 'urgent';
383
+ case 'high':
384
+ return 'high';
385
+ case 'low':
386
+ return 'low';
387
+ default:
388
+ return 'medium';
389
+ }
390
+ }
391
+ isRegistrationTab() {
392
+ return this.activeTab() === 'patient_created';
393
+ }
394
+ emptyLabel() {
395
+ if (this.callIdFilter())
396
+ return 'No activities found for this call.';
397
+ switch (this.activeTab()) {
398
+ case 'task_created':
399
+ return 'No tasks created by the AI yet.';
400
+ case 'patient_created':
401
+ return 'No patient registrations yet.';
402
+ default:
403
+ return 'No appointments booked by the AI yet.';
404
+ }
405
+ }
406
+ parseDate(iso) {
407
+ if (!iso)
408
+ return null;
409
+ const d = new Date(iso);
410
+ return Number.isNaN(d.getTime()) ? null : d;
411
+ }
412
+ formatDay(d) {
413
+ const midnight = (x) => new Date(x.getFullYear(), x.getMonth(), x.getDate()).getTime();
414
+ const now = new Date();
415
+ const days = Math.round((midnight(d) - midnight(now)) / 86_400_000);
416
+ if (days === 0)
417
+ return 'Today';
418
+ if (days === 1)
419
+ return 'Tomorrow';
420
+ if (days === -1)
421
+ return 'Yesterday';
422
+ return d.toLocaleDateString(undefined, d.getFullYear() === now.getFullYear()
423
+ ? { weekday: 'short', month: 'short', day: 'numeric' }
424
+ : { month: 'short', day: 'numeric', year: 'numeric' });
425
+ }
426
+ formatClock(d) {
427
+ return d.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' });
428
+ }
429
+ formatDob(iso) {
430
+ const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(iso.trim());
431
+ if (m)
432
+ return `${m[2]}-${m[3]}-${m[1]}`;
433
+ const d = new Date(iso);
434
+ if (Number.isNaN(d.getTime()))
435
+ return iso;
436
+ const mm = String(d.getMonth() + 1).padStart(2, '0');
437
+ const dd = String(d.getDate()).padStart(2, '0');
438
+ return `${mm}-${dd}-${d.getFullYear()}`;
439
+ }
440
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ActivitiesPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
441
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: ActivitiesPageComponent, isStandalone: true, selector: "eds-activities-page", ngImport: i0, template: "<div class=\"eds-page activities-page\">\n <header class=\"mb-5\">\n <div>\n <h1 class=\"eds-h1\">Activities</h1>\n <p class=\"eds-body-sm\">Review all the activities done by the AI at one place</p>\n </div>\n </header>\n\n @if (callIdFilter(); as callId) {\n <div\n class=\"mb-4 flex flex-wrap items-center justify-between gap-3 rounded-lg bg-blue-50 px-4 py-3 text-sm font-normal leading-snug text-zinc-600\"\n role=\"status\"\n >\n <span>Showing activities for call <strong class=\"font-medium text-zinc-700\">{{ callId }}</strong></span>\n <button\n type=\"button\"\n class=\"cursor-pointer border-none bg-transparent px-0 py-1 text-sm font-medium text-blue-600 hover:underline\"\n (click)=\"clearCallFilter()\"\n >\n Clear filter\n </button>\n </div>\n }\n\n <nav class=\"mb-5 flex flex-wrap gap-2\" 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-blue-50 px-4 py-2 text-sm font-medium text-blue-600 transition-colors'\n : 'cursor-pointer rounded-full border-none bg-transparent px-4 py-2 text-sm font-medium text-zinc-500 transition-colors hover:bg-white hover:text-zinc-700'\n \"\n (click)=\"setTab(tab.id)\"\n >\n {{ tab.label }}\n </button>\n }\n </nav>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading activities\u2026</p>\n } @else if (loadError()) {\n <p class=\"text-sm text-red-700\" role=\"alert\">{{ loadError() }}</p>\n } @else {\n @if (activeTab() === 'task_created') {\n <div\n class=\"activities-page__col-head activities-page__col-head--tasks grid gap-x-7 gap-y-4 pb-2 text-sm font-semibold leading-tight text-zinc-600 max-lg:hidden\"\n aria-hidden=\"true\"\n >\n <span>Priority</span>\n <span>Task</span>\n <span>Patient / Caller</span>\n </div>\n } @else if (activeTab() === 'appointment_booked') {\n <div\n class=\"activities-page__col-head activities-page__col-head--appointments grid gap-x-7 gap-y-4 pb-2 text-sm font-semibold leading-tight text-zinc-600 max-lg:hidden\"\n aria-hidden=\"true\"\n >\n <span>When</span>\n <span>Patient</span>\n <span>Provider</span>\n <span class=\"justify-self-end text-right\">Insurance</span>\n </div>\n } @else if (activeTab() === 'patient_created') {\n <div\n class=\"activities-page__col-head activities-page__col-head--registration grid gap-x-7 gap-y-4 pb-2 text-sm font-semibold leading-tight text-zinc-600 max-lg:hidden\"\n aria-hidden=\"true\"\n >\n <span>When</span>\n <span>Patient</span>\n <span>MRN</span>\n <span class=\"justify-self-end text-right\">Insurance</span>\n </div>\n }\n\n <ul class=\"m-0 flex list-none flex-col gap-3 p-0\">\n @for (item of items(); track item.id; let i = $index) {\n @if (isTaskRow(item)) {\n <li\n class=\"activities-page__card activities-page__card--task grid cursor-pointer items-center gap-x-7 gap-y-4 rounded-xl border border-zinc-200 bg-white px-4 py-4 max-lg:gap-x-4 max-lg:gap-y-4 hover:border-blue-200 hover:shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'View task: ' + taskTitle(item)\"\n (click)=\"openTask(item)\"\n (keyup.enter)=\"openTask(item)\"\n >\n <div class=\"inline-flex items-center gap-2 text-sm font-medium leading-snug text-zinc-700\" [attr.data-tone]=\"priorityTone(item)\">\n <span class=\"activities-page__priority-mark h-2.5 w-2.5 shrink-0 rotate-45 rounded-sm\" aria-hidden=\"true\"></span>\n <span class=\"capitalize\">{{ priorityLabel(item) }}</span>\n </div>\n\n <div class=\"text-sm font-medium leading-snug text-zinc-700 max-lg:col-span-full\">{{ taskTitle(item) }}</div>\n\n <div class=\"flex min-w-0 items-center gap-3 max-lg:col-span-full\">\n <eds-avatar\n shape=\"box\"\n [initials]=\"patientInitials(item)\"\n [tone]=\"avatarTone(item.patientId || item.patientName || item.id, i)\"\n />\n <div class=\"flex min-w-0 flex-col gap-1\">\n <span class=\"truncate text-sm font-medium leading-snug text-zinc-700\">{{ item.patientName || 'Unknown caller' }}</span>\n @if (patientMeta(item); as meta) {\n <span class=\"truncate text-xs leading-snug text-zinc-500\">{{ meta }}</span>\n }\n </div>\n </div>\n </li>\n } @else if (isRegistrationTab()) {\n <li\n class=\"activities-page__card activities-page__card--registration grid cursor-pointer items-center gap-x-7 gap-y-4 rounded-xl border border-zinc-200 bg-white px-4 py-4 max-lg:gap-x-4 max-lg:gap-y-4 hover:border-blue-200 hover:shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'View patient: ' + (item.patientName || 'Unknown patient')\"\n (click)=\"openPatient(item)\"\n (keyup.enter)=\"openPatient(item)\"\n >\n <div class=\"min-w-0\">\n <div class=\"mb-1 flex flex-wrap items-center gap-2\">\n <span class=\"whitespace-nowrap text-base font-medium leading-snug text-zinc-700 max-md:whitespace-normal\">{{ timestampLabel(item) }}</span>\n </div>\n <p class=\"m-0 text-sm leading-snug text-zinc-500\">{{ categoryLabel(item) }}</p>\n </div>\n\n <div class=\"flex min-w-0 items-center gap-3 max-lg:col-span-full\">\n <eds-avatar\n shape=\"box\"\n [initials]=\"patientInitials(item)\"\n [tone]=\"avatarTone(item.patientId || item.patientName || item.id, i)\"\n />\n <div class=\"flex min-w-0 flex-col gap-1\">\n <span class=\"truncate text-sm font-medium leading-snug text-zinc-700\">{{ item.patientName || 'Unknown patient' }}</span>\n @if (patientMeta(item); as meta) {\n <span class=\"truncate text-xs leading-snug text-zinc-500\">{{ meta }}</span>\n }\n </div>\n </div>\n\n <div class=\"text-sm font-normal leading-snug text-zinc-600\">{{ displayMrn(item) }}</div>\n\n <div class=\"flex items-center justify-end gap-2.5\">\n <span\n class=\"activities-page__insurance inline-flex h-8 w-8 items-center justify-center rounded-full border border-transparent max-md:px-1.5\"\n [attr.data-state]=\"insuranceState(item)\"\n [title]=\"insuranceTitle(item)\"\n [attr.aria-label]=\"insuranceLabel(item)\"\n >\n <eds-icon [name]=\"insuranceIcon(item)\" [size]=\"18\" />\n </span>\n </div>\n </li>\n } @else {\n <li\n [class]=\"\n 'activities-page__card grid items-center gap-x-7 gap-y-4 rounded-xl border border-zinc-200 bg-white px-4 py-4 max-lg:gap-x-4 max-lg:gap-y-4' +\n (item.callId\n ? ' cursor-pointer hover:border-blue-200 hover:shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600'\n : '')\n \"\n [attr.role]=\"item.callId ? 'link' : null\"\n [attr.tabindex]=\"item.callId ? 0 : null\"\n (click)=\"openCallHistory(item.callId, $event)\"\n (keyup.enter)=\"openCallHistory(item.callId)\"\n >\n <div class=\"min-w-0\">\n <div class=\"mb-1 flex flex-wrap items-center gap-2\">\n <span class=\"whitespace-nowrap text-base font-medium leading-snug text-zinc-700 max-md:whitespace-normal\">{{ timestampLabel(item) }}</span>\n </div>\n <p class=\"m-0 text-sm leading-snug text-zinc-500\">{{ categoryLabel(item) }}</p>\n </div>\n\n <div class=\"flex min-w-0 items-center gap-3 max-lg:col-span-full\">\n <eds-avatar\n shape=\"box\"\n [initials]=\"patientInitials(item)\"\n [tone]=\"avatarTone(item.patientId || item.patientName || item.id, i)\"\n />\n <div class=\"flex min-w-0 flex-col gap-1\">\n <span class=\"truncate text-sm font-medium leading-snug text-zinc-700\">{{ item.patientName || 'Unknown patient' }}</span>\n @if (patientMeta(item); as meta) {\n <span class=\"truncate text-xs leading-snug text-zinc-500\">{{ meta }}</span>\n }\n </div>\n </div>\n\n <div class=\"flex min-w-0 items-center gap-3 max-lg:col-span-full\">\n @if (item.providerName) {\n <eds-avatar\n shape=\"circle\"\n [initials]=\"providerInitials(item)\"\n [tone]=\"avatarTone(item.providerId || item.providerName, i + 3)\"\n />\n <div class=\"flex min-w-0 flex-col gap-1\">\n <span class=\"truncate text-sm font-medium leading-snug text-zinc-700\">{{ item.providerName }}</span>\n @if (providerSpecialtyLabel(item); as specialty) {\n <span class=\"truncate text-xs leading-snug text-zinc-500\">{{ specialty }}</span>\n }\n </div>\n } @else {\n <div class=\"flex min-h-9 items-center\">\n <span class=\"truncate text-xs leading-snug text-zinc-500\">No provider assigned</span>\n </div>\n }\n </div>\n\n <div class=\"flex items-center justify-end gap-3\">\n <span\n class=\"activities-page__insurance inline-flex h-8 w-8 items-center justify-center rounded-full border border-transparent max-md:px-2\"\n [attr.data-state]=\"insuranceState(item)\"\n [title]=\"insuranceTitle(item)\"\n [attr.aria-label]=\"insuranceLabel(item)\"\n >\n <eds-icon [name]=\"insuranceIcon(item)\" [size]=\"18\" />\n </span>\n </div>\n </li>\n }\n } @empty {\n <li class=\"rounded-xl border border-dashed border-zinc-200 bg-white px-4 py-10 text-center text-sm leading-snug text-zinc-500\">\n {{ emptyLabel() }}\n </li>\n }\n </ul>\n\n @if (total() > pageSize) {\n <footer class=\"mt-4 flex flex-wrap items-center justify-between gap-3\">\n <span class=\"eds-body-sm\">\n Page {{ page() }} of {{ totalPages() }} \u00B7 {{ total() }} total\n </span>\n <div class=\"flex gap-2\">\n <eds-button variant=\"secondary\" [disabled]=\"page() <= 1\" (click)=\"prevPage()\">Previous</eds-button>\n <eds-button variant=\"secondary\" [disabled]=\"page() >= totalPages()\" (click)=\"nextPage()\">Next</eds-button>\n </div>\n </footer>\n }\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__card{grid-template-columns:minmax(180px,1.1fr) minmax(200px,1.2fr) minmax(200px,1.2fr) auto}.activities-page__card--task{grid-template-columns:minmax(120px,.7fr) minmax(220px,1.4fr) minmax(240px,1.3fr)}.activities-page__card--registration{grid-template-columns:minmax(180px,1.1fr) minmax(240px,1.4fr) minmax(120px,.8fr) auto}.activities-page__col-head{box-sizing:border-box;border:1px solid transparent;padding-left:1rem;padding-right:1rem;padding-bottom:.5rem;column-gap:1.75rem}.activities-page__col-head--tasks{grid-template-columns:minmax(120px,.7fr) minmax(220px,1.4fr) minmax(240px,1.3fr)}.activities-page__col-head--appointments{grid-template-columns:minmax(180px,1.1fr) minmax(200px,1.2fr) minmax(200px,1.2fr) auto}.activities-page__col-head--registration{grid-template-columns:minmax(180px,1.1fr) minmax(240px,1.4fr) minmax(120px,.8fr) auto}.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}@media(max-width:1023px){.activities-page__card,.activities-page__card--task{grid-template-columns:1fr auto}}\n"], dependencies: [{ kind: "component", type: EdsAvatarComponent, selector: "eds-avatar", inputs: ["initials", "tone", "shape", "gradient"] }, { 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: 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 }); }
442
+ }
443
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ActivitiesPageComponent, decorators: [{
444
+ type: Component,
445
+ args: [{ selector: 'eds-activities-page', imports: [
446
+ EdsAvatarComponent,
447
+ EdsButtonComponent,
448
+ EdsIconComponent,
449
+ TaskDetailDrawerComponent,
450
+ PatientProfileDrawerComponent,
451
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"eds-page activities-page\">\n <header class=\"mb-5\">\n <div>\n <h1 class=\"eds-h1\">Activities</h1>\n <p class=\"eds-body-sm\">Review all the activities done by the AI at one place</p>\n </div>\n </header>\n\n @if (callIdFilter(); as callId) {\n <div\n class=\"mb-4 flex flex-wrap items-center justify-between gap-3 rounded-lg bg-blue-50 px-4 py-3 text-sm font-normal leading-snug text-zinc-600\"\n role=\"status\"\n >\n <span>Showing activities for call <strong class=\"font-medium text-zinc-700\">{{ callId }}</strong></span>\n <button\n type=\"button\"\n class=\"cursor-pointer border-none bg-transparent px-0 py-1 text-sm font-medium text-blue-600 hover:underline\"\n (click)=\"clearCallFilter()\"\n >\n Clear filter\n </button>\n </div>\n }\n\n <nav class=\"mb-5 flex flex-wrap gap-2\" 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-blue-50 px-4 py-2 text-sm font-medium text-blue-600 transition-colors'\n : 'cursor-pointer rounded-full border-none bg-transparent px-4 py-2 text-sm font-medium text-zinc-500 transition-colors hover:bg-white hover:text-zinc-700'\n \"\n (click)=\"setTab(tab.id)\"\n >\n {{ tab.label }}\n </button>\n }\n </nav>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading activities\u2026</p>\n } @else if (loadError()) {\n <p class=\"text-sm text-red-700\" role=\"alert\">{{ loadError() }}</p>\n } @else {\n @if (activeTab() === 'task_created') {\n <div\n class=\"activities-page__col-head activities-page__col-head--tasks grid gap-x-7 gap-y-4 pb-2 text-sm font-semibold leading-tight text-zinc-600 max-lg:hidden\"\n aria-hidden=\"true\"\n >\n <span>Priority</span>\n <span>Task</span>\n <span>Patient / Caller</span>\n </div>\n } @else if (activeTab() === 'appointment_booked') {\n <div\n class=\"activities-page__col-head activities-page__col-head--appointments grid gap-x-7 gap-y-4 pb-2 text-sm font-semibold leading-tight text-zinc-600 max-lg:hidden\"\n aria-hidden=\"true\"\n >\n <span>When</span>\n <span>Patient</span>\n <span>Provider</span>\n <span class=\"justify-self-end text-right\">Insurance</span>\n </div>\n } @else if (activeTab() === 'patient_created') {\n <div\n class=\"activities-page__col-head activities-page__col-head--registration grid gap-x-7 gap-y-4 pb-2 text-sm font-semibold leading-tight text-zinc-600 max-lg:hidden\"\n aria-hidden=\"true\"\n >\n <span>When</span>\n <span>Patient</span>\n <span>MRN</span>\n <span class=\"justify-self-end text-right\">Insurance</span>\n </div>\n }\n\n <ul class=\"m-0 flex list-none flex-col gap-3 p-0\">\n @for (item of items(); track item.id; let i = $index) {\n @if (isTaskRow(item)) {\n <li\n class=\"activities-page__card activities-page__card--task grid cursor-pointer items-center gap-x-7 gap-y-4 rounded-xl border border-zinc-200 bg-white px-4 py-4 max-lg:gap-x-4 max-lg:gap-y-4 hover:border-blue-200 hover:shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'View task: ' + taskTitle(item)\"\n (click)=\"openTask(item)\"\n (keyup.enter)=\"openTask(item)\"\n >\n <div class=\"inline-flex items-center gap-2 text-sm font-medium leading-snug text-zinc-700\" [attr.data-tone]=\"priorityTone(item)\">\n <span class=\"activities-page__priority-mark h-2.5 w-2.5 shrink-0 rotate-45 rounded-sm\" aria-hidden=\"true\"></span>\n <span class=\"capitalize\">{{ priorityLabel(item) }}</span>\n </div>\n\n <div class=\"text-sm font-medium leading-snug text-zinc-700 max-lg:col-span-full\">{{ taskTitle(item) }}</div>\n\n <div class=\"flex min-w-0 items-center gap-3 max-lg:col-span-full\">\n <eds-avatar\n shape=\"box\"\n [initials]=\"patientInitials(item)\"\n [tone]=\"avatarTone(item.patientId || item.patientName || item.id, i)\"\n />\n <div class=\"flex min-w-0 flex-col gap-1\">\n <span class=\"truncate text-sm font-medium leading-snug text-zinc-700\">{{ item.patientName || 'Unknown caller' }}</span>\n @if (patientMeta(item); as meta) {\n <span class=\"truncate text-xs leading-snug text-zinc-500\">{{ meta }}</span>\n }\n </div>\n </div>\n </li>\n } @else if (isRegistrationTab()) {\n <li\n class=\"activities-page__card activities-page__card--registration grid cursor-pointer items-center gap-x-7 gap-y-4 rounded-xl border border-zinc-200 bg-white px-4 py-4 max-lg:gap-x-4 max-lg:gap-y-4 hover:border-blue-200 hover:shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'View patient: ' + (item.patientName || 'Unknown patient')\"\n (click)=\"openPatient(item)\"\n (keyup.enter)=\"openPatient(item)\"\n >\n <div class=\"min-w-0\">\n <div class=\"mb-1 flex flex-wrap items-center gap-2\">\n <span class=\"whitespace-nowrap text-base font-medium leading-snug text-zinc-700 max-md:whitespace-normal\">{{ timestampLabel(item) }}</span>\n </div>\n <p class=\"m-0 text-sm leading-snug text-zinc-500\">{{ categoryLabel(item) }}</p>\n </div>\n\n <div class=\"flex min-w-0 items-center gap-3 max-lg:col-span-full\">\n <eds-avatar\n shape=\"box\"\n [initials]=\"patientInitials(item)\"\n [tone]=\"avatarTone(item.patientId || item.patientName || item.id, i)\"\n />\n <div class=\"flex min-w-0 flex-col gap-1\">\n <span class=\"truncate text-sm font-medium leading-snug text-zinc-700\">{{ item.patientName || 'Unknown patient' }}</span>\n @if (patientMeta(item); as meta) {\n <span class=\"truncate text-xs leading-snug text-zinc-500\">{{ meta }}</span>\n }\n </div>\n </div>\n\n <div class=\"text-sm font-normal leading-snug text-zinc-600\">{{ displayMrn(item) }}</div>\n\n <div class=\"flex items-center justify-end gap-2.5\">\n <span\n class=\"activities-page__insurance inline-flex h-8 w-8 items-center justify-center rounded-full border border-transparent max-md:px-1.5\"\n [attr.data-state]=\"insuranceState(item)\"\n [title]=\"insuranceTitle(item)\"\n [attr.aria-label]=\"insuranceLabel(item)\"\n >\n <eds-icon [name]=\"insuranceIcon(item)\" [size]=\"18\" />\n </span>\n </div>\n </li>\n } @else {\n <li\n [class]=\"\n 'activities-page__card grid items-center gap-x-7 gap-y-4 rounded-xl border border-zinc-200 bg-white px-4 py-4 max-lg:gap-x-4 max-lg:gap-y-4' +\n (item.callId\n ? ' cursor-pointer hover:border-blue-200 hover:shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600'\n : '')\n \"\n [attr.role]=\"item.callId ? 'link' : null\"\n [attr.tabindex]=\"item.callId ? 0 : null\"\n (click)=\"openCallHistory(item.callId, $event)\"\n (keyup.enter)=\"openCallHistory(item.callId)\"\n >\n <div class=\"min-w-0\">\n <div class=\"mb-1 flex flex-wrap items-center gap-2\">\n <span class=\"whitespace-nowrap text-base font-medium leading-snug text-zinc-700 max-md:whitespace-normal\">{{ timestampLabel(item) }}</span>\n </div>\n <p class=\"m-0 text-sm leading-snug text-zinc-500\">{{ categoryLabel(item) }}</p>\n </div>\n\n <div class=\"flex min-w-0 items-center gap-3 max-lg:col-span-full\">\n <eds-avatar\n shape=\"box\"\n [initials]=\"patientInitials(item)\"\n [tone]=\"avatarTone(item.patientId || item.patientName || item.id, i)\"\n />\n <div class=\"flex min-w-0 flex-col gap-1\">\n <span class=\"truncate text-sm font-medium leading-snug text-zinc-700\">{{ item.patientName || 'Unknown patient' }}</span>\n @if (patientMeta(item); as meta) {\n <span class=\"truncate text-xs leading-snug text-zinc-500\">{{ meta }}</span>\n }\n </div>\n </div>\n\n <div class=\"flex min-w-0 items-center gap-3 max-lg:col-span-full\">\n @if (item.providerName) {\n <eds-avatar\n shape=\"circle\"\n [initials]=\"providerInitials(item)\"\n [tone]=\"avatarTone(item.providerId || item.providerName, i + 3)\"\n />\n <div class=\"flex min-w-0 flex-col gap-1\">\n <span class=\"truncate text-sm font-medium leading-snug text-zinc-700\">{{ item.providerName }}</span>\n @if (providerSpecialtyLabel(item); as specialty) {\n <span class=\"truncate text-xs leading-snug text-zinc-500\">{{ specialty }}</span>\n }\n </div>\n } @else {\n <div class=\"flex min-h-9 items-center\">\n <span class=\"truncate text-xs leading-snug text-zinc-500\">No provider assigned</span>\n </div>\n }\n </div>\n\n <div class=\"flex items-center justify-end gap-3\">\n <span\n class=\"activities-page__insurance inline-flex h-8 w-8 items-center justify-center rounded-full border border-transparent max-md:px-2\"\n [attr.data-state]=\"insuranceState(item)\"\n [title]=\"insuranceTitle(item)\"\n [attr.aria-label]=\"insuranceLabel(item)\"\n >\n <eds-icon [name]=\"insuranceIcon(item)\" [size]=\"18\" />\n </span>\n </div>\n </li>\n }\n } @empty {\n <li class=\"rounded-xl border border-dashed border-zinc-200 bg-white px-4 py-10 text-center text-sm leading-snug text-zinc-500\">\n {{ emptyLabel() }}\n </li>\n }\n </ul>\n\n @if (total() > pageSize) {\n <footer class=\"mt-4 flex flex-wrap items-center justify-between gap-3\">\n <span class=\"eds-body-sm\">\n Page {{ page() }} of {{ totalPages() }} \u00B7 {{ total() }} total\n </span>\n <div class=\"flex gap-2\">\n <eds-button variant=\"secondary\" [disabled]=\"page() <= 1\" (click)=\"prevPage()\">Previous</eds-button>\n <eds-button variant=\"secondary\" [disabled]=\"page() >= totalPages()\" (click)=\"nextPage()\">Next</eds-button>\n </div>\n </footer>\n }\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__card{grid-template-columns:minmax(180px,1.1fr) minmax(200px,1.2fr) minmax(200px,1.2fr) auto}.activities-page__card--task{grid-template-columns:minmax(120px,.7fr) minmax(220px,1.4fr) minmax(240px,1.3fr)}.activities-page__card--registration{grid-template-columns:minmax(180px,1.1fr) minmax(240px,1.4fr) minmax(120px,.8fr) auto}.activities-page__col-head{box-sizing:border-box;border:1px solid transparent;padding-left:1rem;padding-right:1rem;padding-bottom:.5rem;column-gap:1.75rem}.activities-page__col-head--tasks{grid-template-columns:minmax(120px,.7fr) minmax(220px,1.4fr) minmax(240px,1.3fr)}.activities-page__col-head--appointments{grid-template-columns:minmax(180px,1.1fr) minmax(200px,1.2fr) minmax(200px,1.2fr) auto}.activities-page__col-head--registration{grid-template-columns:minmax(180px,1.1fr) minmax(240px,1.4fr) minmax(120px,.8fr) auto}.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}@media(max-width:1023px){.activities-page__card,.activities-page__card--task{grid-template-columns:1fr auto}}\n"] }]
452
+ }] });
453
+
454
+ export { ActivitiesPageComponent };
455
+ //# sourceMappingURL=edvak99-voice-receptionist-widget-activities-page.component-BQS9JITk.mjs.map