@edvak99/voice-receptionist-widget 1.1.8 → 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.
Files changed (24) hide show
  1. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-BQS9JITk.mjs +455 -0
  2. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-BQS9JITk.mjs.map +1 -0
  3. package/fesm2022/{edvak99-voice-receptionist-widget-call-history-detail-page.component-BuNyPmhn.mjs → edvak99-voice-receptionist-widget-call-history-detail-page.component-BkT3Qfsk.mjs} +3 -3
  4. package/fesm2022/{edvak99-voice-receptionist-widget-call-history-detail-page.component-BuNyPmhn.mjs.map → edvak99-voice-receptionist-widget-call-history-detail-page.component-BkT3Qfsk.mjs.map} +1 -1
  5. package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-9qSZL9mc.mjs +1059 -0
  6. package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-9qSZL9mc.mjs.map +1 -0
  7. package/fesm2022/edvak99-voice-receptionist-widget-task-detail-drawer.component-B3KmFJNH.mjs +194 -0
  8. package/fesm2022/edvak99-voice-receptionist-widget-task-detail-drawer.component-B3KmFJNH.mjs.map +1 -0
  9. package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-s3fXlWkQ.mjs +147 -0
  10. package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-s3fXlWkQ.mjs.map +1 -0
  11. package/fesm2022/edvak99-voice-receptionist-widget.mjs +5 -5
  12. package/lib/components/patient-profile-drawer/patient-profile-drawer.component.d.ts +13 -0
  13. package/lib/components/task-detail-drawer/task-detail-drawer.component.d.ts +1 -0
  14. package/lib/pages/activities-page/activities-page.component.d.ts +9 -0
  15. package/lib/pages/settings-page/settings-page.component.d.ts +11 -2
  16. package/lib/pages/settings-page/tts-voice-presets.d.ts +2 -0
  17. package/lib/pages/tasks-page/tasks-page.component.d.ts +5 -0
  18. package/package.json +1 -1
  19. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-CtaPoY1k.mjs +0 -344
  20. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-CtaPoY1k.mjs.map +0 -1
  21. package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-CspheAr8.mjs +0 -992
  22. package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-CspheAr8.mjs.map +0 -1
  23. package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-Dfz-_0Jz.mjs +0 -316
  24. package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-Dfz-_0Jz.mjs.map +0 -1
@@ -0,0 +1,147 @@
1
+ import { DatePipe } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { inject, signal, computed, ChangeDetectionStrategy, Component } from '@angular/core';
4
+ import { ActivatedRoute, RouterLink } from '@angular/router';
5
+ import { a as TASK_TYPE_OPTIONS, t as taskStatusLabel, b as taskStatusTone, c as taskPriorityTone, d as taskSourceLabel, e as taskTypeTone, f as taskCallRoute, T as TaskDetailDrawerComponent } from './edvak99-voice-receptionist-widget-task-detail-drawer.component-B3KmFJNH.mjs';
6
+ import { A as AiReceptionistApiService, D as DEFAULT_PAGE_SIZE, j as totalPages } from './edvak99-voice-receptionist-widget-ai-receptionist-api.service-CK6tM862.mjs';
7
+ import { EdsBadgeComponent, EdsButtonComponent } from './edvak99-voice-receptionist-widget.mjs';
8
+
9
+ class TasksPageComponent {
10
+ constructor() {
11
+ this.api = inject(AiReceptionistApiService);
12
+ this.route = inject(ActivatedRoute);
13
+ this.pageSize = DEFAULT_PAGE_SIZE;
14
+ this.tasks = signal([]);
15
+ this.total = signal(0);
16
+ this.page = signal(1);
17
+ this.loading = signal(true);
18
+ this.selectedTask = signal(null);
19
+ /** Default to Open so staff see actionable work; Done tab for completed. */
20
+ this.statusFilter = signal('open');
21
+ this.typeFilter = signal('');
22
+ this.sourceFilter = signal('');
23
+ this.callIdFilter = signal('');
24
+ this.statusTabs = [
25
+ { id: 'open', label: 'Open' },
26
+ { id: 'in_progress', label: 'In progress' },
27
+ { id: 'done', label: 'Done' },
28
+ { id: '', label: 'All' },
29
+ ];
30
+ this.typeOptions = TASK_TYPE_OPTIONS;
31
+ this.taskStatusLabel = taskStatusLabel;
32
+ this.taskStatusTone = taskStatusTone;
33
+ this.taskPriorityTone = taskPriorityTone;
34
+ this.taskSourceLabel = taskSourceLabel;
35
+ this.taskTypeTone = taskTypeTone;
36
+ this.taskCallRoute = taskCallRoute;
37
+ this.hasActiveFilters = computed(() => !!this.typeFilter() ||
38
+ !!this.sourceFilter() ||
39
+ !!this.callIdFilter());
40
+ }
41
+ ngOnInit() {
42
+ const callId = this.route.snapshot.queryParamMap.get('callId') ?? '';
43
+ if (callId)
44
+ this.callIdFilter.set(callId);
45
+ this.load();
46
+ }
47
+ setStatusFilter(value) {
48
+ this.statusFilter.set(value);
49
+ this.page.set(1);
50
+ this.load();
51
+ }
52
+ setTypeFilter(value) {
53
+ this.typeFilter.set(value);
54
+ this.page.set(1);
55
+ this.load();
56
+ }
57
+ setSourceFilter(value) {
58
+ this.sourceFilter.set(value);
59
+ this.page.set(1);
60
+ this.load();
61
+ }
62
+ setCallIdFilter(value) {
63
+ this.callIdFilter.set(value);
64
+ this.page.set(1);
65
+ this.load();
66
+ }
67
+ clearFilters() {
68
+ this.statusFilter.set('open');
69
+ this.typeFilter.set('');
70
+ this.sourceFilter.set('');
71
+ this.callIdFilter.set('');
72
+ this.page.set(1);
73
+ this.load();
74
+ }
75
+ openTask(task) {
76
+ this.selectedTask.set(task);
77
+ }
78
+ closeDrawer() {
79
+ this.selectedTask.set(null);
80
+ }
81
+ onStatusChange(status) {
82
+ const task = this.selectedTask();
83
+ if (!task)
84
+ return;
85
+ this.api.patchTask(task.id, status).subscribe(({ data }) => {
86
+ if (data) {
87
+ this.selectedTask.set(data);
88
+ // Refresh list so Open/Done tabs stay accurate after status change.
89
+ this.load();
90
+ }
91
+ });
92
+ }
93
+ totalPages() {
94
+ return totalPages(this.total(), DEFAULT_PAGE_SIZE);
95
+ }
96
+ prevPage() {
97
+ if (this.page() <= 1)
98
+ return;
99
+ this.page.update((p) => p - 1);
100
+ this.load();
101
+ }
102
+ nextPage() {
103
+ if (this.page() >= this.totalPages())
104
+ return;
105
+ this.page.update((p) => p + 1);
106
+ this.load();
107
+ }
108
+ load() {
109
+ this.loading.set(true);
110
+ const filters = {
111
+ page: this.page(),
112
+ limit: DEFAULT_PAGE_SIZE,
113
+ };
114
+ if (this.statusFilter())
115
+ filters.status = this.statusFilter();
116
+ if (this.typeFilter())
117
+ filters.type = this.typeFilter();
118
+ if (this.sourceFilter())
119
+ filters.source = this.sourceFilter();
120
+ if (this.callIdFilter().trim())
121
+ filters.callId = this.callIdFilter().trim();
122
+ this.api.getTasks(filters).subscribe({
123
+ next: ({ items, total, page }) => {
124
+ this.tasks.set(items);
125
+ this.total.set(total);
126
+ this.page.set(page);
127
+ this.loading.set(false);
128
+ },
129
+ error: () => this.loading.set(false),
130
+ });
131
+ }
132
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TasksPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
133
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: TasksPageComponent, isStandalone: true, selector: "eds-tasks-page", ngImport: i0, template: "<div class=\"eds-page tasks-page\">\n <header class=\"mb-5 flex items-start justify-between gap-4\">\n <div>\n <h1 class=\"eds-h1\">Tasks & follow-ups</h1>\n <p class=\"eds-body-sm\">Action items from calls \u2014 all types, not just refills</p>\n </div>\n </header>\n\n <nav class=\"mb-4 flex flex-wrap gap-2\" aria-label=\"Task status\">\n @for (tab of statusTabs; track tab.id) {\n <button\n type=\"button\"\n [class]=\"\n statusFilter() === 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)=\"setStatusFilter(tab.id)\"\n >\n {{ tab.label }}\n </button>\n }\n </nav>\n\n <section class=\"eds-card overflow-hidden shadow-sm\">\n <div class=\"flex flex-wrap items-end gap-4 border-b border-zinc-200 px-6 py-4 max-[920px]:px-4 max-[920px]:py-4\">\n <div class=\"flex flex-col gap-1\">\n <label class=\"text-xs font-medium text-zinc-500\" for=\"task-type\">Type</label>\n <select\n id=\"task-type\"\n class=\"min-w-36 rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm\"\n [value]=\"typeFilter()\"\n (change)=\"setTypeFilter($any($event.target).value)\" autocomplete=\"off\">\n <option value=\"\">All types</option>\n @for (t of typeOptions; track t) {\n <option [value]=\"t\">{{ t }}</option>\n }\n </select>\n </div>\n <div class=\"flex flex-col gap-1\">\n <label class=\"text-xs font-medium text-zinc-500\" for=\"task-source\">Source</label>\n <select\n id=\"task-source\"\n class=\"min-w-36 rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm\"\n [value]=\"sourceFilter()\"\n (change)=\"setSourceFilter($any($event.target).value)\" autocomplete=\"off\">\n <option value=\"\">All sources</option>\n <option value=\"voice\">Voice</option>\n <option value=\"sync\">Sync</option>\n <option value=\"manual\">Manual</option>\n </select>\n </div>\n <div class=\"flex flex-col gap-1\">\n <label class=\"text-xs font-medium text-zinc-500\" for=\"task-call\">Call ID</label>\n <input\n id=\"task-call\"\n type=\"text\"\n class=\"min-w-36 rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm\"\n [value]=\"callIdFilter()\"\n placeholder=\"Filter by call\"\n (change)=\"setCallIdFilter($any($event.target).value)\" autocomplete=\"off\" />\n </div>\n @if (hasActiveFilters()) {\n <button\n type=\"button\"\n class=\"cursor-pointer border-none bg-transparent p-0 text-sm font-medium text-blue-600 hover:underline\"\n (click)=\"clearFilters()\"\n >\n Clear filters\n </button>\n }\n </div>\n\n @if (loading()) {\n <p class=\"eds-body-sm m-0 p-6\">Loading tasks\u2026</p>\n } @else {\n <div class=\"overflow-x-auto\">\n <table class=\"w-full min-w-3xl border-collapse\">\n <thead>\n <tr>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Created</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Title</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Type</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Patient</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Priority</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Status</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Source</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Call</th>\n </tr>\n </thead>\n <tbody class=\"divide-y divide-zinc-200\">\n @for (task of tasks(); track task.id) {\n <tr class=\"cursor-pointer transition-colors hover:bg-zinc-50\" (click)=\"openTask(task)\">\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-500\">{{ task.created | date: 'MM/dd/yy, h:mm a' }}</td>\n <td class=\"max-w-72 px-5 py-4 align-middle text-sm font-medium text-zinc-900\">{{ task.title }}</td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-900\"><eds-badge [tone]=\"taskTypeTone(task.type)\">{{ task.type }}</eds-badge></td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-900\">{{ task.patient }}</td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-900\">\n <eds-badge [tone]=\"taskPriorityTone(task.priority)\">\n {{ task.priority === 'urgent' ? 'Urgent' : 'Normal' }}\n </eds-badge>\n </td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-900\"><eds-badge [tone]=\"taskStatusTone(task.status)\">{{ taskStatusLabel(task.status) }}</eds-badge></td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-500\">{{ taskSourceLabel(task.source) }}</td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-900\" (click)=\"$event.stopPropagation()\">\n @if (task.callId) {\n <a\n [routerLink]=\"taskCallRoute(task.callId)\"\n class=\"text-sm font-medium text-blue-600 no-underline hover:underline\"\n >{{ task.callId }}</a>\n } @else {\n <span class=\"text-zinc-500\">\u2014</span>\n }\n </td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"8\" class=\"px-5 py-12 text-center text-sm text-zinc-400\">No tasks match these filters.</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n <footer class=\"flex items-center justify-between border-t border-zinc-200 px-6 py-4 text-sm text-zinc-500\">\n <span>Page {{ page() }} of {{ totalPages() }} \u00B7 {{ total() }} total</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 </section>\n</div>\n\n@if (selectedTask(); as task) {\n <eds-task-detail-drawer\n [task]=\"task\"\n (closed)=\"closeDrawer()\"\n (statusChange)=\"onStatusChange($event)\"\n />\n}\n", styles: [""], 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: EdsBadgeComponent, selector: "eds-badge", inputs: ["tone"] }, { kind: "component", type: EdsButtonComponent, selector: "eds-button", inputs: ["variant", "type", "disabled"] }, { kind: "component", type: TaskDetailDrawerComponent, selector: "eds-task-detail-drawer", inputs: ["task"], outputs: ["closed", "statusChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
134
+ }
135
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TasksPageComponent, decorators: [{
136
+ type: Component,
137
+ args: [{ selector: 'eds-tasks-page', imports: [
138
+ DatePipe,
139
+ RouterLink,
140
+ EdsBadgeComponent,
141
+ EdsButtonComponent,
142
+ TaskDetailDrawerComponent,
143
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"eds-page tasks-page\">\n <header class=\"mb-5 flex items-start justify-between gap-4\">\n <div>\n <h1 class=\"eds-h1\">Tasks & follow-ups</h1>\n <p class=\"eds-body-sm\">Action items from calls \u2014 all types, not just refills</p>\n </div>\n </header>\n\n <nav class=\"mb-4 flex flex-wrap gap-2\" aria-label=\"Task status\">\n @for (tab of statusTabs; track tab.id) {\n <button\n type=\"button\"\n [class]=\"\n statusFilter() === 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)=\"setStatusFilter(tab.id)\"\n >\n {{ tab.label }}\n </button>\n }\n </nav>\n\n <section class=\"eds-card overflow-hidden shadow-sm\">\n <div class=\"flex flex-wrap items-end gap-4 border-b border-zinc-200 px-6 py-4 max-[920px]:px-4 max-[920px]:py-4\">\n <div class=\"flex flex-col gap-1\">\n <label class=\"text-xs font-medium text-zinc-500\" for=\"task-type\">Type</label>\n <select\n id=\"task-type\"\n class=\"min-w-36 rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm\"\n [value]=\"typeFilter()\"\n (change)=\"setTypeFilter($any($event.target).value)\" autocomplete=\"off\">\n <option value=\"\">All types</option>\n @for (t of typeOptions; track t) {\n <option [value]=\"t\">{{ t }}</option>\n }\n </select>\n </div>\n <div class=\"flex flex-col gap-1\">\n <label class=\"text-xs font-medium text-zinc-500\" for=\"task-source\">Source</label>\n <select\n id=\"task-source\"\n class=\"min-w-36 rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm\"\n [value]=\"sourceFilter()\"\n (change)=\"setSourceFilter($any($event.target).value)\" autocomplete=\"off\">\n <option value=\"\">All sources</option>\n <option value=\"voice\">Voice</option>\n <option value=\"sync\">Sync</option>\n <option value=\"manual\">Manual</option>\n </select>\n </div>\n <div class=\"flex flex-col gap-1\">\n <label class=\"text-xs font-medium text-zinc-500\" for=\"task-call\">Call ID</label>\n <input\n id=\"task-call\"\n type=\"text\"\n class=\"min-w-36 rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm\"\n [value]=\"callIdFilter()\"\n placeholder=\"Filter by call\"\n (change)=\"setCallIdFilter($any($event.target).value)\" autocomplete=\"off\" />\n </div>\n @if (hasActiveFilters()) {\n <button\n type=\"button\"\n class=\"cursor-pointer border-none bg-transparent p-0 text-sm font-medium text-blue-600 hover:underline\"\n (click)=\"clearFilters()\"\n >\n Clear filters\n </button>\n }\n </div>\n\n @if (loading()) {\n <p class=\"eds-body-sm m-0 p-6\">Loading tasks\u2026</p>\n } @else {\n <div class=\"overflow-x-auto\">\n <table class=\"w-full min-w-3xl border-collapse\">\n <thead>\n <tr>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Created</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Title</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Type</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Patient</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Priority</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Status</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Source</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Call</th>\n </tr>\n </thead>\n <tbody class=\"divide-y divide-zinc-200\">\n @for (task of tasks(); track task.id) {\n <tr class=\"cursor-pointer transition-colors hover:bg-zinc-50\" (click)=\"openTask(task)\">\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-500\">{{ task.created | date: 'MM/dd/yy, h:mm a' }}</td>\n <td class=\"max-w-72 px-5 py-4 align-middle text-sm font-medium text-zinc-900\">{{ task.title }}</td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-900\"><eds-badge [tone]=\"taskTypeTone(task.type)\">{{ task.type }}</eds-badge></td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-900\">{{ task.patient }}</td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-900\">\n <eds-badge [tone]=\"taskPriorityTone(task.priority)\">\n {{ task.priority === 'urgent' ? 'Urgent' : 'Normal' }}\n </eds-badge>\n </td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-900\"><eds-badge [tone]=\"taskStatusTone(task.status)\">{{ taskStatusLabel(task.status) }}</eds-badge></td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-500\">{{ taskSourceLabel(task.source) }}</td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-900\" (click)=\"$event.stopPropagation()\">\n @if (task.callId) {\n <a\n [routerLink]=\"taskCallRoute(task.callId)\"\n class=\"text-sm font-medium text-blue-600 no-underline hover:underline\"\n >{{ task.callId }}</a>\n } @else {\n <span class=\"text-zinc-500\">\u2014</span>\n }\n </td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"8\" class=\"px-5 py-12 text-center text-sm text-zinc-400\">No tasks match these filters.</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n <footer class=\"flex items-center justify-between border-t border-zinc-200 px-6 py-4 text-sm text-zinc-500\">\n <span>Page {{ page() }} of {{ totalPages() }} \u00B7 {{ total() }} total</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 </section>\n</div>\n\n@if (selectedTask(); as task) {\n <eds-task-detail-drawer\n [task]=\"task\"\n (closed)=\"closeDrawer()\"\n (statusChange)=\"onStatusChange($event)\"\n />\n}\n" }]
144
+ }] });
145
+
146
+ export { TasksPageComponent };
147
+ //# sourceMappingURL=edvak99-voice-receptionist-widget-tasks-page.component-s3fXlWkQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edvak99-voice-receptionist-widget-tasks-page.component-s3fXlWkQ.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/pages/tasks-page/tasks-page.component.ts","../../../projects/voice-receptionist-widget/src/lib/pages/tasks-page/tasks-page.component.html"],"sourcesContent":["import { DatePipe } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n OnInit,\n signal,\n} from '@angular/core';\nimport { ActivatedRoute, RouterLink } from '@angular/router';\n\nimport { TaskDetailDrawerComponent } from '../../components/task-detail-drawer/task-detail-drawer.component';\nimport {\n TaskFilters,\n TaskItem,\n TaskSource,\n TaskStatus,\n} from '../../models/ai-receptionist.types';\nimport { DEFAULT_PAGE_SIZE, totalPages as calcTotalPages } from '../../models/pagination';\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport { EdsBadgeComponent } from '../../ui/eds-badge.component';\nimport { EdsButtonComponent } from '../../ui/eds-button.component';\nimport {\n TASK_TYPE_OPTIONS,\n taskCallRoute,\n taskPriorityTone,\n taskSourceLabel,\n taskStatusLabel,\n taskStatusTone,\n taskTypeTone,\n} from '../../utils/task.util';\n\n@Component({\n selector: 'eds-tasks-page',\n imports: [\n DatePipe,\n RouterLink,\n EdsBadgeComponent,\n EdsButtonComponent,\n TaskDetailDrawerComponent,\n ],\n templateUrl: './tasks-page.component.html',\n styleUrl: './tasks-page.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class TasksPageComponent implements OnInit {\n private readonly api = inject(AiReceptionistApiService);\n private readonly route = inject(ActivatedRoute);\n\n protected readonly pageSize = DEFAULT_PAGE_SIZE;\n protected readonly tasks = signal<TaskItem[]>([]);\n protected readonly total = signal(0);\n protected readonly page = signal(1);\n protected readonly loading = signal(true);\n protected readonly selectedTask = signal<TaskItem | null>(null);\n\n /** Default to Open so staff see actionable work; Done tab for completed. */\n protected readonly statusFilter = signal<'' | TaskStatus>('open');\n protected readonly typeFilter = signal('');\n protected readonly sourceFilter = signal<'' | TaskSource>('');\n protected readonly callIdFilter = signal('');\n\n protected readonly statusTabs: { id: '' | TaskStatus; label: string }[] = [\n { id: 'open', label: 'Open' },\n { id: 'in_progress', label: 'In progress' },\n { id: 'done', label: 'Done' },\n { id: '', label: 'All' },\n ];\n\n protected readonly typeOptions = TASK_TYPE_OPTIONS;\n protected readonly taskStatusLabel = taskStatusLabel;\n protected readonly taskStatusTone = taskStatusTone;\n protected readonly taskPriorityTone = taskPriorityTone;\n protected readonly taskSourceLabel = taskSourceLabel;\n protected readonly taskTypeTone = taskTypeTone;\n protected readonly taskCallRoute = taskCallRoute;\n\n protected readonly hasActiveFilters = computed(\n () =>\n !!this.typeFilter() ||\n !!this.sourceFilter() ||\n !!this.callIdFilter(),\n );\n\n ngOnInit(): void {\n const callId = this.route.snapshot.queryParamMap.get('callId') ?? '';\n if (callId) this.callIdFilter.set(callId);\n this.load();\n }\n\n protected setStatusFilter(value: string): void {\n this.statusFilter.set(value as '' | TaskStatus);\n this.page.set(1);\n this.load();\n }\n\n protected setTypeFilter(value: string): void {\n this.typeFilter.set(value);\n this.page.set(1);\n this.load();\n }\n\n protected setSourceFilter(value: string): void {\n this.sourceFilter.set(value as '' | TaskSource);\n this.page.set(1);\n this.load();\n }\n\n protected setCallIdFilter(value: string): void {\n this.callIdFilter.set(value);\n this.page.set(1);\n this.load();\n }\n\n protected clearFilters(): void {\n this.statusFilter.set('open');\n this.typeFilter.set('');\n this.sourceFilter.set('');\n this.callIdFilter.set('');\n this.page.set(1);\n this.load();\n }\n\n protected openTask(task: TaskItem): void {\n this.selectedTask.set(task);\n }\n\n protected closeDrawer(): void {\n this.selectedTask.set(null);\n }\n\n protected onStatusChange(status: TaskStatus): void {\n const task = this.selectedTask();\n if (!task) return;\n this.api.patchTask(task.id, status).subscribe(({ data }) => {\n if (data) {\n this.selectedTask.set(data);\n // Refresh list so Open/Done tabs stay accurate after status change.\n this.load();\n }\n });\n }\n\n protected totalPages(): number {\n return calcTotalPages(this.total(), DEFAULT_PAGE_SIZE);\n }\n\n protected prevPage(): void {\n if (this.page() <= 1) return;\n this.page.update((p) => p - 1);\n this.load();\n }\n\n protected nextPage(): void {\n if (this.page() >= this.totalPages()) return;\n this.page.update((p) => p + 1);\n this.load();\n }\n\n private load(): void {\n this.loading.set(true);\n const filters: TaskFilters = {\n page: this.page(),\n limit: DEFAULT_PAGE_SIZE,\n };\n if (this.statusFilter()) filters.status = this.statusFilter() as TaskStatus;\n if (this.typeFilter()) filters.type = this.typeFilter();\n if (this.sourceFilter()) filters.source = this.sourceFilter() as TaskSource;\n if (this.callIdFilter().trim()) filters.callId = this.callIdFilter().trim();\n\n this.api.getTasks(filters).subscribe({\n next: ({ items, total, page }) => {\n this.tasks.set(items);\n this.total.set(total);\n this.page.set(page);\n this.loading.set(false);\n },\n error: () => this.loading.set(false),\n });\n }\n}\n","<div class=\"eds-page tasks-page\">\n <header class=\"mb-5 flex items-start justify-between gap-4\">\n <div>\n <h1 class=\"eds-h1\">Tasks & follow-ups</h1>\n <p class=\"eds-body-sm\">Action items from calls — all types, not just refills</p>\n </div>\n </header>\n\n <nav class=\"mb-4 flex flex-wrap gap-2\" aria-label=\"Task status\">\n @for (tab of statusTabs; track tab.id) {\n <button\n type=\"button\"\n [class]=\"\n statusFilter() === 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)=\"setStatusFilter(tab.id)\"\n >\n {{ tab.label }}\n </button>\n }\n </nav>\n\n <section class=\"eds-card overflow-hidden shadow-sm\">\n <div class=\"flex flex-wrap items-end gap-4 border-b border-zinc-200 px-6 py-4 max-[920px]:px-4 max-[920px]:py-4\">\n <div class=\"flex flex-col gap-1\">\n <label class=\"text-xs font-medium text-zinc-500\" for=\"task-type\">Type</label>\n <select\n id=\"task-type\"\n class=\"min-w-36 rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm\"\n [value]=\"typeFilter()\"\n (change)=\"setTypeFilter($any($event.target).value)\" autocomplete=\"off\">\n <option value=\"\">All types</option>\n @for (t of typeOptions; track t) {\n <option [value]=\"t\">{{ t }}</option>\n }\n </select>\n </div>\n <div class=\"flex flex-col gap-1\">\n <label class=\"text-xs font-medium text-zinc-500\" for=\"task-source\">Source</label>\n <select\n id=\"task-source\"\n class=\"min-w-36 rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm\"\n [value]=\"sourceFilter()\"\n (change)=\"setSourceFilter($any($event.target).value)\" autocomplete=\"off\">\n <option value=\"\">All sources</option>\n <option value=\"voice\">Voice</option>\n <option value=\"sync\">Sync</option>\n <option value=\"manual\">Manual</option>\n </select>\n </div>\n <div class=\"flex flex-col gap-1\">\n <label class=\"text-xs font-medium text-zinc-500\" for=\"task-call\">Call ID</label>\n <input\n id=\"task-call\"\n type=\"text\"\n class=\"min-w-36 rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm\"\n [value]=\"callIdFilter()\"\n placeholder=\"Filter by call\"\n (change)=\"setCallIdFilter($any($event.target).value)\" autocomplete=\"off\" />\n </div>\n @if (hasActiveFilters()) {\n <button\n type=\"button\"\n class=\"cursor-pointer border-none bg-transparent p-0 text-sm font-medium text-blue-600 hover:underline\"\n (click)=\"clearFilters()\"\n >\n Clear filters\n </button>\n }\n </div>\n\n @if (loading()) {\n <p class=\"eds-body-sm m-0 p-6\">Loading tasks…</p>\n } @else {\n <div class=\"overflow-x-auto\">\n <table class=\"w-full min-w-3xl border-collapse\">\n <thead>\n <tr>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Created</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Title</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Type</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Patient</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Priority</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Status</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Source</th>\n <th class=\"whitespace-nowrap border-b border-zinc-200 bg-zinc-100 px-5 py-3 text-left text-sm font-semibold text-zinc-600\">Call</th>\n </tr>\n </thead>\n <tbody class=\"divide-y divide-zinc-200\">\n @for (task of tasks(); track task.id) {\n <tr class=\"cursor-pointer transition-colors hover:bg-zinc-50\" (click)=\"openTask(task)\">\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-500\">{{ task.created | date: 'MM/dd/yy, h:mm a' }}</td>\n <td class=\"max-w-72 px-5 py-4 align-middle text-sm font-medium text-zinc-900\">{{ task.title }}</td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-900\"><eds-badge [tone]=\"taskTypeTone(task.type)\">{{ task.type }}</eds-badge></td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-900\">{{ task.patient }}</td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-900\">\n <eds-badge [tone]=\"taskPriorityTone(task.priority)\">\n {{ task.priority === 'urgent' ? 'Urgent' : 'Normal' }}\n </eds-badge>\n </td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-900\"><eds-badge [tone]=\"taskStatusTone(task.status)\">{{ taskStatusLabel(task.status) }}</eds-badge></td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-500\">{{ taskSourceLabel(task.source) }}</td>\n <td class=\"px-5 py-4 align-middle text-sm text-zinc-900\" (click)=\"$event.stopPropagation()\">\n @if (task.callId) {\n <a\n [routerLink]=\"taskCallRoute(task.callId)\"\n class=\"text-sm font-medium text-blue-600 no-underline hover:underline\"\n >{{ task.callId }}</a>\n } @else {\n <span class=\"text-zinc-500\">—</span>\n }\n </td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"8\" class=\"px-5 py-12 text-center text-sm text-zinc-400\">No tasks match these filters.</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n <footer class=\"flex items-center justify-between border-t border-zinc-200 px-6 py-4 text-sm text-zinc-500\">\n <span>Page {{ page() }} of {{ totalPages() }} · {{ total() }} total</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 </section>\n</div>\n\n@if (selectedTask(); as task) {\n <eds-task-detail-drawer\n [task]=\"task\"\n (closed)=\"closeDrawer()\"\n (statusChange)=\"onStatusChange($event)\"\n />\n}\n"],"names":["calcTotalPages"],"mappings":"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"}
@@ -135,15 +135,15 @@ const VOICE_RECEPTIONIST_FEATURE_ROUTES = [
135
135
  },
136
136
  {
137
137
  path: 'history/:id',
138
- loadComponent: () => import('./edvak99-voice-receptionist-widget-call-history-detail-page.component-BuNyPmhn.mjs').then((m) => m.CallHistoryDetailPageComponent),
138
+ loadComponent: () => import('./edvak99-voice-receptionist-widget-call-history-detail-page.component-BkT3Qfsk.mjs').then((m) => m.CallHistoryDetailPageComponent),
139
139
  },
140
140
  {
141
141
  path: 'activities',
142
- loadComponent: () => import('./edvak99-voice-receptionist-widget-activities-page.component-CtaPoY1k.mjs').then((m) => m.ActivitiesPageComponent),
142
+ loadComponent: () => import('./edvak99-voice-receptionist-widget-activities-page.component-BQS9JITk.mjs').then((m) => m.ActivitiesPageComponent),
143
143
  },
144
144
  {
145
145
  path: 'tasks',
146
- loadComponent: () => import('./edvak99-voice-receptionist-widget-tasks-page.component-Dfz-_0Jz.mjs').then((m) => m.TasksPageComponent),
146
+ loadComponent: () => import('./edvak99-voice-receptionist-widget-tasks-page.component-s3fXlWkQ.mjs').then((m) => m.TasksPageComponent),
147
147
  },
148
148
  {
149
149
  path: 'analytics',
@@ -151,7 +151,7 @@ const VOICE_RECEPTIONIST_FEATURE_ROUTES = [
151
151
  },
152
152
  {
153
153
  path: 'settings',
154
- loadComponent: () => import('./edvak99-voice-receptionist-widget-settings-page.component-CspheAr8.mjs').then((m) => m.SettingsPageComponent),
154
+ loadComponent: () => import('./edvak99-voice-receptionist-widget-settings-page.component-9qSZL9mc.mjs').then((m) => m.SettingsPageComponent),
155
155
  },
156
156
  {
157
157
  path: 'settings/outbound-purposes',
@@ -159,7 +159,7 @@ const VOICE_RECEPTIONIST_FEATURE_ROUTES = [
159
159
  },
160
160
  {
161
161
  path: 'knowledge',
162
- loadComponent: () => import('./edvak99-voice-receptionist-widget-settings-page.component-CspheAr8.mjs').then((m) => m.SettingsPageComponent),
162
+ loadComponent: () => import('./edvak99-voice-receptionist-widget-settings-page.component-9qSZL9mc.mjs').then((m) => m.SettingsPageComponent),
163
163
  data: { settingsTab: 'knowledge' },
164
164
  },
165
165
  {
@@ -0,0 +1,13 @@
1
+ import { AgentActivity } from '../../models/agent-activity.types';
2
+ import * as i0 from "@angular/core";
3
+ export declare class PatientProfileDrawerComponent {
4
+ readonly patient: import("@angular/core").InputSignal<AgentActivity>;
5
+ readonly closed: import("@angular/core").OutputEmitterRef<void>;
6
+ protected close(): void;
7
+ protected onBackdropClick(event: MouseEvent): void;
8
+ protected display(value: string | null | undefined): string;
9
+ protected insuranceLabel(): string;
10
+ protected insuranceTone(): 'covered' | 'none' | 'unknown';
11
+ static ɵfac: i0.ɵɵFactoryDeclaration<PatientProfileDrawerComponent, never>;
12
+ static ɵcmp: i0.ɵɵComponentDeclaration<PatientProfileDrawerComponent, "eds-patient-profile-drawer", never, { "patient": { "alias": "patient"; "required": true; "isSignal": true; }; }, { "closed": "closed"; }, never, never, true, never>;
13
+ }
@@ -12,6 +12,7 @@ export declare class TaskDetailDrawerComponent {
12
12
  protected readonly taskSourceLabel: typeof taskSourceLabel;
13
13
  protected readonly taskTypeTone: typeof taskTypeTone;
14
14
  protected readonly taskCallRoute: typeof taskCallRoute;
15
+ protected assigneeInitials(): string;
15
16
  protected close(): void;
16
17
  protected markDone(): void;
17
18
  protected onBackdropClick(event: MouseEvent): void;
@@ -1,5 +1,6 @@
1
1
  import { OnInit } from '@angular/core';
2
2
  import { ActivitiesTab, AgentActivity } from '../../models/agent-activity.types';
3
+ import { TaskItem, TaskStatus } from '../../models/ai-receptionist.types';
3
4
  import * as i0 from "@angular/core";
4
5
  type TabId = ActivitiesTab;
5
6
  export declare class ActivitiesPageComponent implements OnInit {
@@ -18,6 +19,8 @@ export declare class ActivitiesPageComponent implements OnInit {
18
19
  protected readonly callIdFilter: import("@angular/core").WritableSignal<string | null>;
19
20
  protected readonly loading: import("@angular/core").WritableSignal<boolean>;
20
21
  protected readonly loadError: import("@angular/core").WritableSignal<string | null>;
22
+ protected readonly selectedTask: import("@angular/core").WritableSignal<TaskItem | null>;
23
+ protected readonly selectedPatient: import("@angular/core").WritableSignal<AgentActivity | null>;
21
24
  ngOnInit(): void;
22
25
  protected setTab(tab: TabId): void;
23
26
  private tabFromQuery;
@@ -29,6 +32,12 @@ export declare class ActivitiesPageComponent implements OnInit {
29
32
  protected nextPage(): void;
30
33
  protected historyLink(callId: string | null | undefined): string[] | null;
31
34
  protected openCallHistory(callId: string | null | undefined, ev?: Event): void;
35
+ protected openTask(item: AgentActivity): void;
36
+ protected openPatient(item: AgentActivity): void;
37
+ protected closeTaskDrawer(): void;
38
+ protected closePatientDrawer(): void;
39
+ protected onTaskStatusChange(status: TaskStatus): void;
40
+ private activityToTask;
32
41
  protected displayMrn(item: AgentActivity): string;
33
42
  protected providerSpecialtyLabel(item: AgentActivity): string | null;
34
43
  /** Day-anchored stamp — a bare clock time gave no idea which day a row belonged to. */
@@ -1,14 +1,16 @@
1
- import { OnInit } from '@angular/core';
1
+ import { OnDestroy, OnInit } from '@angular/core';
2
2
  import { AgentSettings, LocationOption } from '../../models/ai-receptionist.types';
3
3
  import { isFieldReadOnly, SettingsFieldMeta } from './settings-fields.config';
4
4
  import * as i0 from "@angular/core";
5
5
  type SettingsTab = keyof AgentSettings;
6
- export declare class SettingsPageComponent implements OnInit {
6
+ export declare class SettingsPageComponent implements OnInit, OnDestroy {
7
7
  private readonly api;
8
8
  private readonly toast;
9
9
  private readonly route;
10
10
  private readonly agentOnline;
11
11
  private originalSettings;
12
+ private voiceAudio;
13
+ private voiceObjectUrl;
12
14
  protected readonly isFieldReadOnly: typeof isFieldReadOnly;
13
15
  protected readonly knowledgeMax = 8000;
14
16
  protected readonly disclosureMax = 1000;
@@ -35,6 +37,8 @@ export declare class SettingsPageComponent implements OnInit {
35
37
  protected readonly locations: import("@angular/core").WritableSignal<LocationOption[]>;
36
38
  protected readonly locationsMenuOpen: import("@angular/core").WritableSignal<boolean>;
37
39
  protected readonly confirmOffOpen: import("@angular/core").WritableSignal<boolean>;
40
+ protected readonly voicePreviewBusy: import("@angular/core").WritableSignal<boolean>;
41
+ protected readonly voicePreviewPlaying: import("@angular/core").WritableSignal<boolean>;
38
42
  protected readonly online: import("@angular/core").Signal<import("../../services/agent-online.service").AgentOnlineKnownState>;
39
43
  protected readonly agentLoading: import("@angular/core").Signal<boolean>;
40
44
  protected readonly toggling: import("@angular/core").Signal<boolean>;
@@ -56,6 +60,7 @@ export declare class SettingsPageComponent implements OnInit {
56
60
  protected readonly disclosureText: import("@angular/core").Signal<string>;
57
61
  protected readonly agentSwitchDisabled: import("@angular/core").Signal<boolean>;
58
62
  ngOnInit(): void;
63
+ ngOnDestroy(): void;
59
64
  protected fieldIsReadOnly(section: SettingsTab, key: string): boolean;
60
65
  protected sectionIsReadOnly(section: SettingsTab): boolean;
61
66
  protected isTabDisabled(id: SettingsTab): boolean;
@@ -75,6 +80,10 @@ export declare class SettingsPageComponent implements OnInit {
75
80
  protected openTestCall(): void;
76
81
  protected closeTestCall(): void;
77
82
  protected selectedVoiceId(): string;
83
+ protected onVoiceChange(voiceId: string): void;
84
+ protected toggleVoicePreview(): void;
85
+ private playVoiceBlob;
86
+ private stopVoicePreview;
78
87
  protected onAgentToggleClick(): void;
79
88
  protected cancelConfirmOff(): void;
80
89
  protected confirmTurnOff(): void;
@@ -2,6 +2,8 @@
2
2
  export interface TtsVoicePreset {
3
3
  id: string;
4
4
  label: string;
5
+ /** Display gender hint next to the voice name. */
6
+ gender: 'M' | 'F';
5
7
  }
6
8
  export declare const DEFAULT_TTS_VOICE_ID = "7EzWGsX10sAS4c9m9cPf";
7
9
  export declare const TTS_VOICE_PRESETS: readonly TtsVoicePreset[];
@@ -11,10 +11,15 @@ export declare class TasksPageComponent implements OnInit {
11
11
  protected readonly page: import("@angular/core").WritableSignal<number>;
12
12
  protected readonly loading: import("@angular/core").WritableSignal<boolean>;
13
13
  protected readonly selectedTask: import("@angular/core").WritableSignal<TaskItem | null>;
14
+ /** Default to Open so staff see actionable work; Done tab for completed. */
14
15
  protected readonly statusFilter: import("@angular/core").WritableSignal<"" | TaskStatus>;
15
16
  protected readonly typeFilter: import("@angular/core").WritableSignal<string>;
16
17
  protected readonly sourceFilter: import("@angular/core").WritableSignal<"" | TaskSource>;
17
18
  protected readonly callIdFilter: import("@angular/core").WritableSignal<string>;
19
+ protected readonly statusTabs: {
20
+ id: '' | TaskStatus;
21
+ label: string;
22
+ }[];
18
23
  protected readonly typeOptions: readonly ["Refill request", "Callback", "Billing", "Scheduling", "Clinical", "Other"];
19
24
  protected readonly taskStatusLabel: typeof taskStatusLabel;
20
25
  protected readonly taskStatusTone: typeof taskStatusTone;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edvak99/voice-receptionist-widget",
3
- "version": "1.1.8",
3
+ "version": "1.1.10",
4
4
  "description": "Edvak AI Receptionist — embeddable Angular routes and UI for EHR hosts",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^19.2.0",