@edvak99/voice-receptionist-widget 1.1.4 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-CdrW0He4.mjs +306 -0
- package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-CdrW0He4.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-ai-receptionist-api.service-48cifVPF.mjs → edvak99-voice-receptionist-widget-ai-receptionist-api.service-DokIb-9r.mjs} +2 -2
- package/fesm2022/{edvak99-voice-receptionist-widget-ai-receptionist-api.service-48cifVPF.mjs.map → edvak99-voice-receptionist-widget-ai-receptionist-api.service-DokIb-9r.mjs.map} +1 -1
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-4GK1vsom.mjs +277 -0
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-4GK1vsom.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-Bw_Ade1s.mjs +416 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-Bw_Ade1s.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-DnwYnjIE.mjs +193 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-DnwYnjIE.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-recording-player.component-Pla2UgeZ.mjs +111 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-recording-player.component-Pla2UgeZ.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-CIMcjGKX.mjs → edvak99-voice-receptionist-widget-call-review.service-Di7GLG9k.mjs} +2 -2
- package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-CIMcjGKX.mjs.map → edvak99-voice-receptionist-widget-call-review.service-Di7GLG9k.mjs.map} +1 -1
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-DVz0romd.mjs +439 -0
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-DVz0romd.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-dashboard-socket.service-BX91r74O.mjs → edvak99-voice-receptionist-widget-dashboard-socket.service-D2pelzu4.mjs} +3 -3
- package/fesm2022/{edvak99-voice-receptionist-widget-dashboard-socket.service-BX91r74O.mjs.map → edvak99-voice-receptionist-widget-dashboard-socket.service-D2pelzu4.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-eds-avatar.component-CWRiJGfv.mjs → edvak99-voice-receptionist-widget-eds-avatar.component-BJs_TH8X.mjs} +5 -5
- package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-BJs_TH8X.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-eds-pulse-dot.component-BPY6xN7C.mjs → edvak99-voice-receptionist-widget-eds-pulse-dot.component-Ig0Eoq1z.mjs} +6 -6
- package/fesm2022/edvak99-voice-receptionist-widget-eds-pulse-dot.component-Ig0Eoq1z.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-Bx7WQ2dg.mjs +81 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-Bx7WQ2dg.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-DWbA5yAG.mjs +1059 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-DWbA5yAG.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-modal.component-Cb-FoQ1N.mjs +48 -0
- package/fesm2022/edvak99-voice-receptionist-widget-modal.component-Cb-FoQ1N.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-DhoyrHSe.mjs +267 -0
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-DhoyrHSe.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-CZUDgZ1_.mjs +967 -0
- package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-CZUDgZ1_.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-DyNG2DZZ.mjs +316 -0
- package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-DyNG2DZZ.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-DjaurkbO.mjs +46 -0
- package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-DjaurkbO.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget.mjs +228 -1
- package/fesm2022/edvak99-voice-receptionist-widget.mjs.map +1 -1
- package/lib/components/call-conversation-panel/call-conversation-panel.component.d.ts +1 -2
- package/lib/components/live-transcript-panel/live-transcript-panel.component.d.ts +0 -3
- package/package.json +1 -1
- package/public-api.d.ts +3 -0
- package/styles/chat-ui.scss +0 -1
- package/styles/eds.scss +0 -10
- package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-DT9UKvQO.mjs +0 -307
- package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-DT9UKvQO.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-C5inEHJw.mjs +0 -278
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-C5inEHJw.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-sYhM20CE.mjs +0 -418
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-sYhM20CE.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-DpwajEOE.mjs +0 -194
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-DpwajEOE.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-call-recording-player.component-CgkRcFOl.mjs +0 -111
- package/fesm2022/edvak99-voice-receptionist-widget-call-recording-player.component-CgkRcFOl.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-CxbO0eDS.mjs +0 -440
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-CxbO0eDS.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-CWRiJGfv.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-eds-button.component-Bg5T5BN8.mjs +0 -27
- package/fesm2022/edvak99-voice-receptionist-widget-eds-button.component-Bg5T5BN8.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-eds-pulse-dot.component-BPY6xN7C.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-BsBQIS_f.mjs +0 -203
- package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-BsBQIS_f.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-CaSDMxpV.mjs +0 -81
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-CaSDMxpV.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-Cy3ND-c-.mjs +0 -1070
- package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-Cy3ND-c-.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-modal.component-COfi7biN.mjs +0 -48
- package/fesm2022/edvak99-voice-receptionist-widget-modal.component-COfi7biN.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-fHVzpiVk.mjs +0 -268
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-fHVzpiVk.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-Bok64X2q.mjs +0 -968
- package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-Bok64X2q.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-Bb9lPcYi.mjs +0 -317
- package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-Bb9lPcYi.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-BZVlWLWh.mjs +0 -47
- package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-BZVlWLWh.mjs.map +0 -1
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { DatePipe } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { inject, signal, Input, ChangeDetectionStrategy, Component, input, output, computed } from '@angular/core';
|
|
4
|
+
import { RouterLink, ActivatedRoute } from '@angular/router';
|
|
5
|
+
import { ToastService, EdsBadgeComponent, EdsButtonComponent, EdsIconComponent } from './edvak99-voice-receptionist-widget.mjs';
|
|
6
|
+
import { A as AiReceptionistApiService, D as DEFAULT_PAGE_SIZE, k as totalPages } from './edvak99-voice-receptionist-widget-ai-receptionist-api.service-DokIb-9r.mjs';
|
|
7
|
+
|
|
8
|
+
class CopyButtonComponent {
|
|
9
|
+
constructor() {
|
|
10
|
+
/** Raw value to copy (full ID / phone / etc.) */
|
|
11
|
+
this.value = '';
|
|
12
|
+
/** Accessible label, e.g. "patient ID" */
|
|
13
|
+
this.label = 'value';
|
|
14
|
+
this.toast = inject(ToastService);
|
|
15
|
+
this.copied = signal(false);
|
|
16
|
+
}
|
|
17
|
+
get copyValue() {
|
|
18
|
+
const v = this.value?.trim();
|
|
19
|
+
return v && v !== '—' ? v : '';
|
|
20
|
+
}
|
|
21
|
+
get ariaLabel() {
|
|
22
|
+
return this.copied() ? 'Copied' : `Copy ${this.label}`;
|
|
23
|
+
}
|
|
24
|
+
async onCopy(event) {
|
|
25
|
+
event.stopPropagation();
|
|
26
|
+
const text = this.copyValue;
|
|
27
|
+
if (!text)
|
|
28
|
+
return;
|
|
29
|
+
try {
|
|
30
|
+
await navigator.clipboard.writeText(text);
|
|
31
|
+
this.copied.set(true);
|
|
32
|
+
setTimeout(() => this.copied.set(false), 1500);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
this.toast.show('Could not copy — clipboard unavailable', 'error', 2500);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: CopyButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
39
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: CopyButtonComponent, isStandalone: true, selector: "app-copy-btn", inputs: { value: "value", label: "label" }, ngImport: i0, template: `
|
|
40
|
+
@if (copyValue) {
|
|
41
|
+
<button
|
|
42
|
+
type="button"
|
|
43
|
+
class="copy-btn inline-flex shrink-0 items-center justify-center p-0"
|
|
44
|
+
(click)="onCopy($event)"
|
|
45
|
+
[attr.aria-label]="ariaLabel"
|
|
46
|
+
[title]="copied() ? 'Copied' : 'Copy to clipboard'"
|
|
47
|
+
>
|
|
48
|
+
@if (copied()) {
|
|
49
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
50
|
+
stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
51
|
+
<polyline points="20 6 9 17 4 12" />
|
|
52
|
+
</svg>
|
|
53
|
+
} @else {
|
|
54
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
55
|
+
stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
56
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
|
|
57
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
58
|
+
</svg>
|
|
59
|
+
}
|
|
60
|
+
</button>
|
|
61
|
+
}
|
|
62
|
+
`, isInline: true, styles: [".copy-btn{width:22px;height:22px;border:1px solid var(--eds-color-border);border-radius:4px;background:var(--eds-color-surface);color:var(--eds-color-text-muted);cursor:pointer;transition:background .1s ease,color .1s ease,border-color .1s ease}.copy-btn:hover{background:var(--eds-color-surface-alt);color:var(--eds-color-primary);border-color:var(--eds-color-border-strong)}.copy-btn:focus-visible{outline:2px solid var(--eds-color-primary);outline-offset:1px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
63
|
+
}
|
|
64
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: CopyButtonComponent, decorators: [{
|
|
65
|
+
type: Component,
|
|
66
|
+
args: [{ selector: 'app-copy-btn', standalone: true, template: `
|
|
67
|
+
@if (copyValue) {
|
|
68
|
+
<button
|
|
69
|
+
type="button"
|
|
70
|
+
class="copy-btn inline-flex shrink-0 items-center justify-center p-0"
|
|
71
|
+
(click)="onCopy($event)"
|
|
72
|
+
[attr.aria-label]="ariaLabel"
|
|
73
|
+
[title]="copied() ? 'Copied' : 'Copy to clipboard'"
|
|
74
|
+
>
|
|
75
|
+
@if (copied()) {
|
|
76
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
77
|
+
stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
78
|
+
<polyline points="20 6 9 17 4 12" />
|
|
79
|
+
</svg>
|
|
80
|
+
} @else {
|
|
81
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
82
|
+
stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
83
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
|
|
84
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
85
|
+
</svg>
|
|
86
|
+
}
|
|
87
|
+
</button>
|
|
88
|
+
}
|
|
89
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".copy-btn{width:22px;height:22px;border:1px solid var(--eds-color-border);border-radius:4px;background:var(--eds-color-surface);color:var(--eds-color-text-muted);cursor:pointer;transition:background .1s ease,color .1s ease,border-color .1s ease}.copy-btn:hover{background:var(--eds-color-surface-alt);color:var(--eds-color-primary);border-color:var(--eds-color-border-strong)}.copy-btn:focus-visible{outline:2px solid var(--eds-color-primary);outline-offset:1px}\n"] }]
|
|
90
|
+
}], propDecorators: { value: [{
|
|
91
|
+
type: Input
|
|
92
|
+
}], label: [{
|
|
93
|
+
type: Input
|
|
94
|
+
}] } });
|
|
95
|
+
|
|
96
|
+
const TASK_TYPE_OPTIONS = [
|
|
97
|
+
'Refill request',
|
|
98
|
+
'Callback',
|
|
99
|
+
'Billing',
|
|
100
|
+
'Scheduling',
|
|
101
|
+
'Clinical',
|
|
102
|
+
'Other',
|
|
103
|
+
];
|
|
104
|
+
function taskStatusLabel(status) {
|
|
105
|
+
if (status === 'in_progress')
|
|
106
|
+
return 'In progress';
|
|
107
|
+
if (status === 'done')
|
|
108
|
+
return 'Done';
|
|
109
|
+
return 'Open';
|
|
110
|
+
}
|
|
111
|
+
function taskStatusTone(status) {
|
|
112
|
+
const map = {
|
|
113
|
+
open: 'brand',
|
|
114
|
+
in_progress: 'notice',
|
|
115
|
+
done: 'pos',
|
|
116
|
+
};
|
|
117
|
+
return map[status];
|
|
118
|
+
}
|
|
119
|
+
function taskPriorityTone(priority) {
|
|
120
|
+
return priority === 'urgent' ? 'neg' : 'gray';
|
|
121
|
+
}
|
|
122
|
+
function taskPriorityLabel(priority) {
|
|
123
|
+
return priority === 'urgent' ? 'Urgent' : 'Normal';
|
|
124
|
+
}
|
|
125
|
+
function taskSourceLabel(source) {
|
|
126
|
+
if (source === 'voice')
|
|
127
|
+
return 'Voice';
|
|
128
|
+
if (source === 'sync')
|
|
129
|
+
return 'Sync';
|
|
130
|
+
return 'Manual';
|
|
131
|
+
}
|
|
132
|
+
function taskTypeTone(type) {
|
|
133
|
+
const key = type.toLowerCase();
|
|
134
|
+
if (key.includes('refill'))
|
|
135
|
+
return 'violet';
|
|
136
|
+
if (key.includes('callback'))
|
|
137
|
+
return 'brand';
|
|
138
|
+
if (key.includes('billing'))
|
|
139
|
+
return 'notice';
|
|
140
|
+
return 'gray';
|
|
141
|
+
}
|
|
142
|
+
function taskCallRoute(callId) {
|
|
143
|
+
return callId.startsWith('LC') ? ['../live', callId] : ['../history', callId];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
class TaskDetailDrawerComponent {
|
|
147
|
+
constructor() {
|
|
148
|
+
this.task = input.required();
|
|
149
|
+
this.closed = output();
|
|
150
|
+
this.statusChange = output();
|
|
151
|
+
this.taskStatusLabel = taskStatusLabel;
|
|
152
|
+
this.taskStatusTone = taskStatusTone;
|
|
153
|
+
this.taskPriorityLabel = taskPriorityLabel;
|
|
154
|
+
this.taskPriorityTone = taskPriorityTone;
|
|
155
|
+
this.taskSourceLabel = taskSourceLabel;
|
|
156
|
+
this.taskTypeTone = taskTypeTone;
|
|
157
|
+
this.taskCallRoute = taskCallRoute;
|
|
158
|
+
}
|
|
159
|
+
close() {
|
|
160
|
+
this.closed.emit();
|
|
161
|
+
}
|
|
162
|
+
markDone() {
|
|
163
|
+
const t = this.task();
|
|
164
|
+
this.statusChange.emit(t.status === 'done' ? 'open' : 'done');
|
|
165
|
+
}
|
|
166
|
+
onBackdropClick(event) {
|
|
167
|
+
if (event.target === event.currentTarget)
|
|
168
|
+
this.close();
|
|
169
|
+
}
|
|
170
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TaskDetailDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
171
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: TaskDetailDrawerComponent, isStandalone: true, selector: "eds-task-detail-drawer", inputs: { task: { classPropertyName: "task", publicName: "task", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { closed: "closed", statusChange: "statusChange" }, ngImport: i0, template: "<div class=\"fixed inset-0 z-[200] flex justify-end bg-[rgba(15,23,42,0.4)]\" (click)=\"onBackdropClick($event)\">\n <aside class=\"task-drawer flex h-full w-[min(440px,100%)] flex-col bg-white shadow-[-8px_0_32px_rgba(15,23,42,0.12)]\" role=\"dialog\" aria-modal=\"true\" [attr.aria-label]=\"task().title\">\n <header class=\"flex items-start justify-between gap-3 border-b border-[var(--eds-color-border,#e2e8f0)] px-6 py-5\">\n <div>\n <eds-badge [tone]=\"taskTypeTone(task().type)\">{{ task().type }}</eds-badge>\n <h2 class=\"mt-2 text-xl font-semibold leading-snug text-[var(--eds-color-text,#0f172a)]\">{{ task().title }}</h2>\n </div>\n <button\n type=\"button\"\n class=\"cursor-pointer rounded-md border-none bg-transparent p-1 text-[var(--eds-color-text-muted,#64748b)] hover:bg-[var(--eds-color-surface-alt,#f1f5f9)]\"\n (click)=\"close()\"\n aria-label=\"Close\"\n >\n <eds-icon name=\"close\" [size]=\"22\" />\n </button>\n </header>\n\n <div class=\"flex flex-1 flex-col gap-6 overflow-y-auto px-6 py-5\">\n <section>\n <h3 class=\"eds-label\">Description</h3>\n <p class=\"mt-2 whitespace-pre-wrap text-[15px] leading-relaxed text-[var(--eds-color-text,#0f172a)]\">{{ task().description || '\u2014' }}</p>\n </section>\n\n <dl class=\"m-0 grid grid-cols-2 gap-4\">\n <div>\n <dt class=\"eds-label mb-1\">Patient</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">{{ task().patient }}</dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Assignee</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">{{ task().assignee }}</dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Status</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\"><eds-badge [tone]=\"taskStatusTone(task().status)\">{{ taskStatusLabel(task().status) }}</eds-badge></dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Priority</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\"><eds-badge [tone]=\"taskPriorityTone(task().priority)\">{{ taskPriorityLabel(task().priority) }}</eds-badge></dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Source</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">{{ taskSourceLabel(task().source) }}</dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Created</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">{{ task().created | date: 'medium' }}</dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Call</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">\n @if (task().callId) {\n <a [routerLink]=\"taskCallRoute(task().callId!)\" class=\"font-medium text-[var(--eds-color-primary,#3681f4)] no-underline hover:underline\">{{ task().callId }}</a>\n } @else {\n \u2014\n }\n </dd>\n </div>\n </dl>\n\n @if (task().edvakTaskId || task().edvakStatus || task().medicationName || task().medicationId) {\n <section class=\"rounded-lg border border-[var(--eds-color-border,#e2e8f0)] bg-[var(--eds-color-surface-alt,#f8fafc)] p-3.5\">\n <h3 class=\"eds-label\">Edvak / refill details</h3>\n <dl class=\"m-0 grid grid-cols-2 gap-4\">\n @if (task().edvakTaskId) {\n <div>\n <dt class=\"eds-label mb-1\">Edvak task ID</dt>\n <dd class=\"inline-flex items-center gap-1.5 text-sm\">\n <code class=\"rounded bg-[var(--eds-color-surface-alt,#f1f5f9)] px-1.5 py-0.5 font-mono text-xs\">{{ task().edvakTaskId }}</code>\n <app-copy-btn [value]=\"task().edvakTaskId\" label=\"Edvak task ID\" />\n </dd>\n </div>\n }\n @if (task().edvakStatus) {\n <div>\n <dt class=\"eds-label mb-1\">Edvak status</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\"><eds-badge tone=\"brand\">{{ task().edvakStatus }}</eds-badge></dd>\n </div>\n }\n @if (task().medicationName) {\n <div>\n <dt class=\"eds-label mb-1\">Medication</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">{{ task().medicationName }}</dd>\n </div>\n }\n @if (task().medicationId) {\n <div>\n <dt class=\"eds-label mb-1\">Medication ID</dt>\n <dd class=\"inline-flex items-center gap-1.5 text-sm\">\n <code class=\"rounded bg-[var(--eds-color-surface-alt,#f1f5f9)] px-1.5 py-0.5 font-mono text-xs\">{{ task().medicationId }}</code>\n <app-copy-btn [value]=\"task().medicationId\" label=\"medication ID\" />\n </dd>\n </div>\n }\n </dl>\n </section>\n }\n </div>\n\n <footer class=\"border-t border-[var(--eds-color-border,#e2e8f0)] px-6 py-4\">\n <eds-button variant=\"secondary\" type=\"button\" (click)=\"markDone()\">\n {{ task().status === 'done' ? 'Reopen task' : 'Mark done' }}\n </eds-button>\n </footer>\n </aside>\n</div>\n", styles: [".task-drawer{animation:task-drawer-in .2s ease}@keyframes task-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: CopyButtonComponent, selector: "app-copy-btn", inputs: ["value", "label"] }, { kind: "component", type: EdsBadgeComponent, selector: "eds-badge", inputs: ["tone"] }, { kind: "component", type: EdsButtonComponent, selector: "eds-button", inputs: ["variant", "type", "disabled"] }, { kind: "component", type: EdsIconComponent, selector: "eds-icon", inputs: ["name", "size", "fill", "weight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
172
|
+
}
|
|
173
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TaskDetailDrawerComponent, decorators: [{
|
|
174
|
+
type: Component,
|
|
175
|
+
args: [{ selector: 'eds-task-detail-drawer', imports: [
|
|
176
|
+
DatePipe,
|
|
177
|
+
RouterLink,
|
|
178
|
+
CopyButtonComponent,
|
|
179
|
+
EdsBadgeComponent,
|
|
180
|
+
EdsButtonComponent,
|
|
181
|
+
EdsIconComponent,
|
|
182
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fixed inset-0 z-[200] flex justify-end bg-[rgba(15,23,42,0.4)]\" (click)=\"onBackdropClick($event)\">\n <aside class=\"task-drawer flex h-full w-[min(440px,100%)] flex-col bg-white shadow-[-8px_0_32px_rgba(15,23,42,0.12)]\" role=\"dialog\" aria-modal=\"true\" [attr.aria-label]=\"task().title\">\n <header class=\"flex items-start justify-between gap-3 border-b border-[var(--eds-color-border,#e2e8f0)] px-6 py-5\">\n <div>\n <eds-badge [tone]=\"taskTypeTone(task().type)\">{{ task().type }}</eds-badge>\n <h2 class=\"mt-2 text-xl font-semibold leading-snug text-[var(--eds-color-text,#0f172a)]\">{{ task().title }}</h2>\n </div>\n <button\n type=\"button\"\n class=\"cursor-pointer rounded-md border-none bg-transparent p-1 text-[var(--eds-color-text-muted,#64748b)] hover:bg-[var(--eds-color-surface-alt,#f1f5f9)]\"\n (click)=\"close()\"\n aria-label=\"Close\"\n >\n <eds-icon name=\"close\" [size]=\"22\" />\n </button>\n </header>\n\n <div class=\"flex flex-1 flex-col gap-6 overflow-y-auto px-6 py-5\">\n <section>\n <h3 class=\"eds-label\">Description</h3>\n <p class=\"mt-2 whitespace-pre-wrap text-[15px] leading-relaxed text-[var(--eds-color-text,#0f172a)]\">{{ task().description || '\u2014' }}</p>\n </section>\n\n <dl class=\"m-0 grid grid-cols-2 gap-4\">\n <div>\n <dt class=\"eds-label mb-1\">Patient</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">{{ task().patient }}</dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Assignee</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">{{ task().assignee }}</dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Status</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\"><eds-badge [tone]=\"taskStatusTone(task().status)\">{{ taskStatusLabel(task().status) }}</eds-badge></dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Priority</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\"><eds-badge [tone]=\"taskPriorityTone(task().priority)\">{{ taskPriorityLabel(task().priority) }}</eds-badge></dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Source</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">{{ taskSourceLabel(task().source) }}</dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Created</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">{{ task().created | date: 'medium' }}</dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Call</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">\n @if (task().callId) {\n <a [routerLink]=\"taskCallRoute(task().callId!)\" class=\"font-medium text-[var(--eds-color-primary,#3681f4)] no-underline hover:underline\">{{ task().callId }}</a>\n } @else {\n \u2014\n }\n </dd>\n </div>\n </dl>\n\n @if (task().edvakTaskId || task().edvakStatus || task().medicationName || task().medicationId) {\n <section class=\"rounded-lg border border-[var(--eds-color-border,#e2e8f0)] bg-[var(--eds-color-surface-alt,#f8fafc)] p-3.5\">\n <h3 class=\"eds-label\">Edvak / refill details</h3>\n <dl class=\"m-0 grid grid-cols-2 gap-4\">\n @if (task().edvakTaskId) {\n <div>\n <dt class=\"eds-label mb-1\">Edvak task ID</dt>\n <dd class=\"inline-flex items-center gap-1.5 text-sm\">\n <code class=\"rounded bg-[var(--eds-color-surface-alt,#f1f5f9)] px-1.5 py-0.5 font-mono text-xs\">{{ task().edvakTaskId }}</code>\n <app-copy-btn [value]=\"task().edvakTaskId\" label=\"Edvak task ID\" />\n </dd>\n </div>\n }\n @if (task().edvakStatus) {\n <div>\n <dt class=\"eds-label mb-1\">Edvak status</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\"><eds-badge tone=\"brand\">{{ task().edvakStatus }}</eds-badge></dd>\n </div>\n }\n @if (task().medicationName) {\n <div>\n <dt class=\"eds-label mb-1\">Medication</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">{{ task().medicationName }}</dd>\n </div>\n }\n @if (task().medicationId) {\n <div>\n <dt class=\"eds-label mb-1\">Medication ID</dt>\n <dd class=\"inline-flex items-center gap-1.5 text-sm\">\n <code class=\"rounded bg-[var(--eds-color-surface-alt,#f1f5f9)] px-1.5 py-0.5 font-mono text-xs\">{{ task().medicationId }}</code>\n <app-copy-btn [value]=\"task().medicationId\" label=\"medication ID\" />\n </dd>\n </div>\n }\n </dl>\n </section>\n }\n </div>\n\n <footer class=\"border-t border-[var(--eds-color-border,#e2e8f0)] px-6 py-4\">\n <eds-button variant=\"secondary\" type=\"button\" (click)=\"markDone()\">\n {{ task().status === 'done' ? 'Reopen task' : 'Mark done' }}\n </eds-button>\n </footer>\n </aside>\n</div>\n", styles: [".task-drawer{animation:task-drawer-in .2s ease}@keyframes task-drawer-in{0%{transform:translate(100%)}to{transform:translate(0)}}\n"] }]
|
|
183
|
+
}] });
|
|
184
|
+
|
|
185
|
+
class TasksPageComponent {
|
|
186
|
+
constructor() {
|
|
187
|
+
this.api = inject(AiReceptionistApiService);
|
|
188
|
+
this.route = inject(ActivatedRoute);
|
|
189
|
+
this.pageSize = DEFAULT_PAGE_SIZE;
|
|
190
|
+
this.tasks = signal([]);
|
|
191
|
+
this.total = signal(0);
|
|
192
|
+
this.page = signal(1);
|
|
193
|
+
this.loading = signal(true);
|
|
194
|
+
this.selectedTask = signal(null);
|
|
195
|
+
this.statusFilter = signal('');
|
|
196
|
+
this.typeFilter = signal('');
|
|
197
|
+
this.sourceFilter = signal('');
|
|
198
|
+
this.callIdFilter = signal('');
|
|
199
|
+
this.typeOptions = TASK_TYPE_OPTIONS;
|
|
200
|
+
this.taskStatusLabel = taskStatusLabel;
|
|
201
|
+
this.taskStatusTone = taskStatusTone;
|
|
202
|
+
this.taskPriorityTone = taskPriorityTone;
|
|
203
|
+
this.taskSourceLabel = taskSourceLabel;
|
|
204
|
+
this.taskTypeTone = taskTypeTone;
|
|
205
|
+
this.taskCallRoute = taskCallRoute;
|
|
206
|
+
this.hasActiveFilters = computed(() => !!this.statusFilter() ||
|
|
207
|
+
!!this.typeFilter() ||
|
|
208
|
+
!!this.sourceFilter() ||
|
|
209
|
+
!!this.callIdFilter());
|
|
210
|
+
}
|
|
211
|
+
ngOnInit() {
|
|
212
|
+
const callId = this.route.snapshot.queryParamMap.get('callId') ?? '';
|
|
213
|
+
if (callId)
|
|
214
|
+
this.callIdFilter.set(callId);
|
|
215
|
+
this.load();
|
|
216
|
+
}
|
|
217
|
+
setStatusFilter(value) {
|
|
218
|
+
this.statusFilter.set(value);
|
|
219
|
+
this.page.set(1);
|
|
220
|
+
this.load();
|
|
221
|
+
}
|
|
222
|
+
setTypeFilter(value) {
|
|
223
|
+
this.typeFilter.set(value);
|
|
224
|
+
this.page.set(1);
|
|
225
|
+
this.load();
|
|
226
|
+
}
|
|
227
|
+
setSourceFilter(value) {
|
|
228
|
+
this.sourceFilter.set(value);
|
|
229
|
+
this.page.set(1);
|
|
230
|
+
this.load();
|
|
231
|
+
}
|
|
232
|
+
setCallIdFilter(value) {
|
|
233
|
+
this.callIdFilter.set(value);
|
|
234
|
+
this.page.set(1);
|
|
235
|
+
this.load();
|
|
236
|
+
}
|
|
237
|
+
clearFilters() {
|
|
238
|
+
this.statusFilter.set('');
|
|
239
|
+
this.typeFilter.set('');
|
|
240
|
+
this.sourceFilter.set('');
|
|
241
|
+
this.callIdFilter.set('');
|
|
242
|
+
this.page.set(1);
|
|
243
|
+
this.load();
|
|
244
|
+
}
|
|
245
|
+
openTask(task) {
|
|
246
|
+
this.selectedTask.set(task);
|
|
247
|
+
}
|
|
248
|
+
closeDrawer() {
|
|
249
|
+
this.selectedTask.set(null);
|
|
250
|
+
}
|
|
251
|
+
onStatusChange(status) {
|
|
252
|
+
const task = this.selectedTask();
|
|
253
|
+
if (!task)
|
|
254
|
+
return;
|
|
255
|
+
this.api.patchTask(task.id, status).subscribe(({ data }) => {
|
|
256
|
+
if (data) {
|
|
257
|
+
this.selectedTask.set(data);
|
|
258
|
+
this.tasks.update((rows) => rows.map((r) => (r.id === data.id ? data : r)));
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
totalPages() {
|
|
263
|
+
return totalPages(this.total(), DEFAULT_PAGE_SIZE);
|
|
264
|
+
}
|
|
265
|
+
prevPage() {
|
|
266
|
+
if (this.page() <= 1)
|
|
267
|
+
return;
|
|
268
|
+
this.page.update((p) => p - 1);
|
|
269
|
+
this.load();
|
|
270
|
+
}
|
|
271
|
+
nextPage() {
|
|
272
|
+
if (this.page() >= this.totalPages())
|
|
273
|
+
return;
|
|
274
|
+
this.page.update((p) => p + 1);
|
|
275
|
+
this.load();
|
|
276
|
+
}
|
|
277
|
+
load() {
|
|
278
|
+
this.loading.set(true);
|
|
279
|
+
const filters = {
|
|
280
|
+
page: this.page(),
|
|
281
|
+
limit: DEFAULT_PAGE_SIZE,
|
|
282
|
+
};
|
|
283
|
+
if (this.statusFilter())
|
|
284
|
+
filters.status = this.statusFilter();
|
|
285
|
+
if (this.typeFilter())
|
|
286
|
+
filters.type = this.typeFilter();
|
|
287
|
+
if (this.sourceFilter())
|
|
288
|
+
filters.source = this.sourceFilter();
|
|
289
|
+
if (this.callIdFilter().trim())
|
|
290
|
+
filters.callId = this.callIdFilter().trim();
|
|
291
|
+
this.api.getTasks(filters).subscribe({
|
|
292
|
+
next: ({ items, total, page }) => {
|
|
293
|
+
this.tasks.set(items);
|
|
294
|
+
this.total.set(total);
|
|
295
|
+
this.page.set(page);
|
|
296
|
+
this.loading.set(false);
|
|
297
|
+
},
|
|
298
|
+
error: () => this.loading.set(false),
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TasksPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
302
|
+
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 <section class=\"eds-card overflow-hidden shadow-sm\">\n <div class=\"flex flex-wrap items-end gap-3.5 border-b border-zinc-200 px-6 py-4.5 max-[920px]:px-4.5 max-[920px]:py-4\">\n <div class=\"flex flex-col gap-1\">\n <label class=\"text-xs font-medium text-zinc-500\" for=\"task-status\">Status</label>\n <select\n id=\"task-status\"\n class=\"min-w-36 rounded-md border border-zinc-200 bg-white px-2.5 py-2 text-sm\"\n [value]=\"statusFilter()\"\n (change)=\"setStatusFilter($any($event.target).value)\" autocomplete=\"off\">\n <option value=\"\">All</option>\n <option value=\"open\">Open</option>\n <option value=\"in_progress\">In progress</option>\n <option value=\"done\">Done</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-type\">Type</label>\n <select\n id=\"task-type\"\n class=\"min-w-36 rounded-md border border-zinc-200 bg-white px-2.5 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-2.5 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-2.5 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-4.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-4.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-4.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-4.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-4.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-4.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-4.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-4.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-4.5 py-3.5 align-middle text-sm text-zinc-500\">{{ task.created | date: 'MM/dd/yy, h:mm a' }}</td>\n <td class=\"max-w-72 px-4.5 py-3.5 align-middle text-sm font-medium text-zinc-900\">{{ task.title }}</td>\n <td class=\"px-4.5 py-3.5 align-middle text-sm text-zinc-900\"><eds-badge [tone]=\"taskTypeTone(task.type)\">{{ task.type }}</eds-badge></td>\n <td class=\"px-4.5 py-3.5 align-middle text-sm text-zinc-900\">{{ task.patient }}</td>\n <td class=\"px-4.5 py-3.5 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-4.5 py-3.5 align-middle text-sm text-zinc-900\"><eds-badge [tone]=\"taskStatusTone(task.status)\">{{ taskStatusLabel(task.status) }}</eds-badge></td>\n <td class=\"px-4.5 py-3.5 align-middle text-sm text-zinc-500\">{{ taskSourceLabel(task.source) }}</td>\n <td class=\"px-4.5 py-3.5 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-4.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-3.5 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 }); }
|
|
303
|
+
}
|
|
304
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TasksPageComponent, decorators: [{
|
|
305
|
+
type: Component,
|
|
306
|
+
args: [{ selector: 'eds-tasks-page', imports: [
|
|
307
|
+
DatePipe,
|
|
308
|
+
RouterLink,
|
|
309
|
+
EdsBadgeComponent,
|
|
310
|
+
EdsButtonComponent,
|
|
311
|
+
TaskDetailDrawerComponent,
|
|
312
|
+
], 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 <section class=\"eds-card overflow-hidden shadow-sm\">\n <div class=\"flex flex-wrap items-end gap-3.5 border-b border-zinc-200 px-6 py-4.5 max-[920px]:px-4.5 max-[920px]:py-4\">\n <div class=\"flex flex-col gap-1\">\n <label class=\"text-xs font-medium text-zinc-500\" for=\"task-status\">Status</label>\n <select\n id=\"task-status\"\n class=\"min-w-36 rounded-md border border-zinc-200 bg-white px-2.5 py-2 text-sm\"\n [value]=\"statusFilter()\"\n (change)=\"setStatusFilter($any($event.target).value)\" autocomplete=\"off\">\n <option value=\"\">All</option>\n <option value=\"open\">Open</option>\n <option value=\"in_progress\">In progress</option>\n <option value=\"done\">Done</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-type\">Type</label>\n <select\n id=\"task-type\"\n class=\"min-w-36 rounded-md border border-zinc-200 bg-white px-2.5 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-2.5 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-2.5 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-4.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-4.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-4.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-4.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-4.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-4.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-4.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-4.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-4.5 py-3.5 align-middle text-sm text-zinc-500\">{{ task.created | date: 'MM/dd/yy, h:mm a' }}</td>\n <td class=\"max-w-72 px-4.5 py-3.5 align-middle text-sm font-medium text-zinc-900\">{{ task.title }}</td>\n <td class=\"px-4.5 py-3.5 align-middle text-sm text-zinc-900\"><eds-badge [tone]=\"taskTypeTone(task.type)\">{{ task.type }}</eds-badge></td>\n <td class=\"px-4.5 py-3.5 align-middle text-sm text-zinc-900\">{{ task.patient }}</td>\n <td class=\"px-4.5 py-3.5 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-4.5 py-3.5 align-middle text-sm text-zinc-900\"><eds-badge [tone]=\"taskStatusTone(task.status)\">{{ taskStatusLabel(task.status) }}</eds-badge></td>\n <td class=\"px-4.5 py-3.5 align-middle text-sm text-zinc-500\">{{ taskSourceLabel(task.source) }}</td>\n <td class=\"px-4.5 py-3.5 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-4.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-3.5 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" }]
|
|
313
|
+
}] });
|
|
314
|
+
|
|
315
|
+
export { TasksPageComponent };
|
|
316
|
+
//# sourceMappingURL=edvak99-voice-receptionist-widget-tasks-page.component-DyNG2DZZ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edvak99-voice-receptionist-widget-tasks-page.component-DyNG2DZZ.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/shared/copy-button.component.ts","../../../projects/voice-receptionist-widget/src/lib/utils/task.util.ts","../../../projects/voice-receptionist-widget/src/lib/components/task-detail-drawer/task-detail-drawer.component.ts","../../../projects/voice-receptionist-widget/src/lib/components/task-detail-drawer/task-detail-drawer.component.html","../../../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 {\n ChangeDetectionStrategy,\n Component,\n Input,\n inject,\n signal,\n} from '@angular/core';\n\nimport { ToastService } from './toast.service';\n\n@Component({\n selector: 'app-copy-btn',\n standalone: true,\n template: `\n @if (copyValue) {\n <button\n type=\"button\"\n class=\"copy-btn inline-flex shrink-0 items-center justify-center p-0\"\n (click)=\"onCopy($event)\"\n [attr.aria-label]=\"ariaLabel\"\n [title]=\"copied() ? 'Copied' : 'Copy to clipboard'\"\n >\n @if (copied()) {\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n } @else {\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\" />\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\" />\n </svg>\n }\n </button>\n }\n `,\n styleUrl: './copy-button.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class CopyButtonComponent {\n /** Raw value to copy (full ID / phone / etc.) */\n @Input() value: string | null | undefined = '';\n /** Accessible label, e.g. \"patient ID\" */\n @Input() label = 'value';\n\n private readonly toast = inject(ToastService);\n\n protected readonly copied = signal(false);\n\n protected get copyValue(): string {\n const v = this.value?.trim();\n return v && v !== '—' ? v : '';\n }\n\n protected get ariaLabel(): string {\n return this.copied() ? 'Copied' : `Copy ${this.label}`;\n }\n\n async onCopy(event: MouseEvent): Promise<void> {\n event.stopPropagation();\n const text = this.copyValue;\n if (!text) return;\n\n try {\n await navigator.clipboard.writeText(text);\n this.copied.set(true);\n setTimeout(() => this.copied.set(false), 1500);\n } catch {\n this.toast.show('Could not copy — clipboard unavailable', 'error', 2500);\n }\n }\n}\n","import { BadgeTone } from '../ui/eds-badge.component';\nimport { TaskPriority, TaskSource, TaskStatus } from '../models/ai-receptionist.types';\n\nexport const TASK_TYPE_OPTIONS = [\n 'Refill request',\n 'Callback',\n 'Billing',\n 'Scheduling',\n 'Clinical',\n 'Other',\n] as const;\n\nexport function taskStatusLabel(status: TaskStatus): string {\n if (status === 'in_progress') return 'In progress';\n if (status === 'done') return 'Done';\n return 'Open';\n}\n\nexport function taskStatusTone(status: TaskStatus): BadgeTone {\n const map: Record<TaskStatus, BadgeTone> = {\n open: 'brand',\n in_progress: 'notice',\n done: 'pos',\n };\n return map[status];\n}\n\nexport function taskPriorityTone(priority: TaskPriority): BadgeTone {\n return priority === 'urgent' ? 'neg' : 'gray';\n}\n\nexport function taskPriorityLabel(priority: TaskPriority): string {\n return priority === 'urgent' ? 'Urgent' : 'Normal';\n}\n\nexport function taskSourceLabel(source: TaskSource): string {\n if (source === 'voice') return 'Voice';\n if (source === 'sync') return 'Sync';\n return 'Manual';\n}\n\nexport function taskTypeTone(type: string): BadgeTone {\n const key = type.toLowerCase();\n if (key.includes('refill')) return 'violet';\n if (key.includes('callback')) return 'brand';\n if (key.includes('billing')) return 'notice';\n return 'gray';\n}\n\nexport function taskCallRoute(callId: string): string[] {\n return callId.startsWith('LC') ? ['../live', callId] : ['../history', callId];\n}\n","import { DatePipe } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n input,\n output,\n} from '@angular/core';\nimport { RouterLink } from '@angular/router';\n\nimport { TaskItem, TaskStatus } from '../../models/ai-receptionist.types';\nimport { CopyButtonComponent } from '../../shared/copy-button.component';\nimport { EdsBadgeComponent } from '../../ui/eds-badge.component';\nimport { EdsButtonComponent } from '../../ui/eds-button.component';\nimport { EdsIconComponent } from '../../ui/eds-icon.component';\nimport {\n taskCallRoute,\n taskPriorityLabel,\n taskPriorityTone,\n taskSourceLabel,\n taskStatusLabel,\n taskStatusTone,\n taskTypeTone,\n} from '../../utils/task.util';\n\n@Component({\n selector: 'eds-task-detail-drawer',\n imports: [\n DatePipe,\n RouterLink,\n CopyButtonComponent,\n EdsBadgeComponent,\n EdsButtonComponent,\n EdsIconComponent,\n ],\n templateUrl: './task-detail-drawer.component.html',\n styleUrl: './task-detail-drawer.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class TaskDetailDrawerComponent {\n readonly task = input.required<TaskItem>();\n\n readonly closed = output<void>();\n readonly statusChange = output<TaskStatus>();\n\n protected readonly taskStatusLabel = taskStatusLabel;\n protected readonly taskStatusTone = taskStatusTone;\n protected readonly taskPriorityLabel = taskPriorityLabel;\n protected readonly taskPriorityTone = taskPriorityTone;\n protected readonly taskSourceLabel = taskSourceLabel;\n protected readonly taskTypeTone = taskTypeTone;\n protected readonly taskCallRoute = taskCallRoute;\n\n protected close(): void {\n this.closed.emit();\n }\n\n protected markDone(): void {\n const t = this.task();\n this.statusChange.emit(t.status === 'done' ? 'open' : 'done');\n }\n\n protected onBackdropClick(event: MouseEvent): void {\n if (event.target === event.currentTarget) this.close();\n }\n}\n","<div class=\"fixed inset-0 z-[200] flex justify-end bg-[rgba(15,23,42,0.4)]\" (click)=\"onBackdropClick($event)\">\n <aside class=\"task-drawer flex h-full w-[min(440px,100%)] flex-col bg-white shadow-[-8px_0_32px_rgba(15,23,42,0.12)]\" role=\"dialog\" aria-modal=\"true\" [attr.aria-label]=\"task().title\">\n <header class=\"flex items-start justify-between gap-3 border-b border-[var(--eds-color-border,#e2e8f0)] px-6 py-5\">\n <div>\n <eds-badge [tone]=\"taskTypeTone(task().type)\">{{ task().type }}</eds-badge>\n <h2 class=\"mt-2 text-xl font-semibold leading-snug text-[var(--eds-color-text,#0f172a)]\">{{ task().title }}</h2>\n </div>\n <button\n type=\"button\"\n class=\"cursor-pointer rounded-md border-none bg-transparent p-1 text-[var(--eds-color-text-muted,#64748b)] hover:bg-[var(--eds-color-surface-alt,#f1f5f9)]\"\n (click)=\"close()\"\n aria-label=\"Close\"\n >\n <eds-icon name=\"close\" [size]=\"22\" />\n </button>\n </header>\n\n <div class=\"flex flex-1 flex-col gap-6 overflow-y-auto px-6 py-5\">\n <section>\n <h3 class=\"eds-label\">Description</h3>\n <p class=\"mt-2 whitespace-pre-wrap text-[15px] leading-relaxed text-[var(--eds-color-text,#0f172a)]\">{{ task().description || '—' }}</p>\n </section>\n\n <dl class=\"m-0 grid grid-cols-2 gap-4\">\n <div>\n <dt class=\"eds-label mb-1\">Patient</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">{{ task().patient }}</dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Assignee</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">{{ task().assignee }}</dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Status</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\"><eds-badge [tone]=\"taskStatusTone(task().status)\">{{ taskStatusLabel(task().status) }}</eds-badge></dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Priority</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\"><eds-badge [tone]=\"taskPriorityTone(task().priority)\">{{ taskPriorityLabel(task().priority) }}</eds-badge></dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Source</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">{{ taskSourceLabel(task().source) }}</dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Created</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">{{ task().created | date: 'medium' }}</dd>\n </div>\n <div>\n <dt class=\"eds-label mb-1\">Call</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">\n @if (task().callId) {\n <a [routerLink]=\"taskCallRoute(task().callId!)\" class=\"font-medium text-[var(--eds-color-primary,#3681f4)] no-underline hover:underline\">{{ task().callId }}</a>\n } @else {\n —\n }\n </dd>\n </div>\n </dl>\n\n @if (task().edvakTaskId || task().edvakStatus || task().medicationName || task().medicationId) {\n <section class=\"rounded-lg border border-[var(--eds-color-border,#e2e8f0)] bg-[var(--eds-color-surface-alt,#f8fafc)] p-3.5\">\n <h3 class=\"eds-label\">Edvak / refill details</h3>\n <dl class=\"m-0 grid grid-cols-2 gap-4\">\n @if (task().edvakTaskId) {\n <div>\n <dt class=\"eds-label mb-1\">Edvak task ID</dt>\n <dd class=\"inline-flex items-center gap-1.5 text-sm\">\n <code class=\"rounded bg-[var(--eds-color-surface-alt,#f1f5f9)] px-1.5 py-0.5 font-mono text-xs\">{{ task().edvakTaskId }}</code>\n <app-copy-btn [value]=\"task().edvakTaskId\" label=\"Edvak task ID\" />\n </dd>\n </div>\n }\n @if (task().edvakStatus) {\n <div>\n <dt class=\"eds-label mb-1\">Edvak status</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\"><eds-badge tone=\"brand\">{{ task().edvakStatus }}</eds-badge></dd>\n </div>\n }\n @if (task().medicationName) {\n <div>\n <dt class=\"eds-label mb-1\">Medication</dt>\n <dd class=\"m-0 text-sm text-[var(--eds-color-text,#0f172a)]\">{{ task().medicationName }}</dd>\n </div>\n }\n @if (task().medicationId) {\n <div>\n <dt class=\"eds-label mb-1\">Medication ID</dt>\n <dd class=\"inline-flex items-center gap-1.5 text-sm\">\n <code class=\"rounded bg-[var(--eds-color-surface-alt,#f1f5f9)] px-1.5 py-0.5 font-mono text-xs\">{{ task().medicationId }}</code>\n <app-copy-btn [value]=\"task().medicationId\" label=\"medication ID\" />\n </dd>\n </div>\n }\n </dl>\n </section>\n }\n </div>\n\n <footer class=\"border-t border-[var(--eds-color-border,#e2e8f0)] px-6 py-4\">\n <eds-button variant=\"secondary\" type=\"button\" (click)=\"markDone()\">\n {{ task().status === 'done' ? 'Reopen task' : 'Mark done' }}\n </eds-button>\n </footer>\n </aside>\n</div>\n","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 protected readonly statusFilter = signal<'' | TaskStatus>('');\n protected readonly typeFilter = signal('');\n protected readonly sourceFilter = signal<'' | TaskSource>('');\n protected readonly callIdFilter = signal('');\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.statusFilter() ||\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('');\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 this.tasks.update((rows) => rows.map((r) => (r.id === data.id ? data : r)));\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 <section class=\"eds-card overflow-hidden shadow-sm\">\n <div class=\"flex flex-wrap items-end gap-3.5 border-b border-zinc-200 px-6 py-4.5 max-[920px]:px-4.5 max-[920px]:py-4\">\n <div class=\"flex flex-col gap-1\">\n <label class=\"text-xs font-medium text-zinc-500\" for=\"task-status\">Status</label>\n <select\n id=\"task-status\"\n class=\"min-w-36 rounded-md border border-zinc-200 bg-white px-2.5 py-2 text-sm\"\n [value]=\"statusFilter()\"\n (change)=\"setStatusFilter($any($event.target).value)\" autocomplete=\"off\">\n <option value=\"\">All</option>\n <option value=\"open\">Open</option>\n <option value=\"in_progress\">In progress</option>\n <option value=\"done\">Done</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-type\">Type</label>\n <select\n id=\"task-type\"\n class=\"min-w-36 rounded-md border border-zinc-200 bg-white px-2.5 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-2.5 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-2.5 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-4.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-4.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-4.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-4.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-4.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-4.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-4.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-4.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-4.5 py-3.5 align-middle text-sm text-zinc-500\">{{ task.created | date: 'MM/dd/yy, h:mm a' }}</td>\n <td class=\"max-w-72 px-4.5 py-3.5 align-middle text-sm font-medium text-zinc-900\">{{ task.title }}</td>\n <td class=\"px-4.5 py-3.5 align-middle text-sm text-zinc-900\"><eds-badge [tone]=\"taskTypeTone(task.type)\">{{ task.type }}</eds-badge></td>\n <td class=\"px-4.5 py-3.5 align-middle text-sm text-zinc-900\">{{ task.patient }}</td>\n <td class=\"px-4.5 py-3.5 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-4.5 py-3.5 align-middle text-sm text-zinc-900\"><eds-badge [tone]=\"taskStatusTone(task.status)\">{{ taskStatusLabel(task.status) }}</eds-badge></td>\n <td class=\"px-4.5 py-3.5 align-middle text-sm text-zinc-500\">{{ taskSourceLabel(task.source) }}</td>\n <td class=\"px-4.5 py-3.5 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-4.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-3.5 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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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, signal, computed, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { A as AiReceptionistApiService } from './edvak99-voice-receptionist-widget-ai-receptionist-api.service-DokIb-9r.mjs';
|
|
4
|
+
import { EdsBadgeComponent, EdsButtonComponent, EdsIconComponent } from './edvak99-voice-receptionist-widget.mjs';
|
|
5
|
+
|
|
6
|
+
class WizardPageComponent {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.api = inject(AiReceptionistApiService);
|
|
9
|
+
this.setup = signal(null);
|
|
10
|
+
this.activeStep = signal(1);
|
|
11
|
+
this.loading = signal(true);
|
|
12
|
+
this.activated = signal(false);
|
|
13
|
+
this.completedCount = computed(() => {
|
|
14
|
+
return this.setup()?.steps.filter((s) => s.complete).length ?? 0;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
ngOnInit() {
|
|
18
|
+
this.api.getSetup().subscribe(({ data }) => {
|
|
19
|
+
this.setup.set(data);
|
|
20
|
+
const firstIncomplete = data.steps.find((s) => !s.complete);
|
|
21
|
+
this.activeStep.set(firstIncomplete?.id ?? 1);
|
|
22
|
+
this.loading.set(false);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
selectStep(step) {
|
|
26
|
+
this.activeStep.set(step.id);
|
|
27
|
+
}
|
|
28
|
+
progressPercent() {
|
|
29
|
+
return this.setup()?.readiness ?? 0;
|
|
30
|
+
}
|
|
31
|
+
activate() {
|
|
32
|
+
this.api.activateSetup().subscribe(() => {
|
|
33
|
+
this.api.setAgentOn(true);
|
|
34
|
+
this.activated.set(true);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: WizardPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
38
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: WizardPageComponent, isStandalone: true, selector: "eds-wizard-page", ngImport: i0, template: "<div class=\"eds-page wizard-page\">\n <header class=\"mb-5 flex items-start justify-between gap-5\">\n <div>\n <h1 class=\"eds-h1\">Setup wizard</h1>\n <p class=\"eds-body-sm\">Configure and activate your AI receptionist</p>\n </div>\n <div class=\"flex min-w-52 flex-col items-end gap-1.5\">\n <span class=\"eds-label\">Readiness</span>\n <div class=\"h-2 w-52 overflow-hidden rounded bg-zinc-100\">\n <span\n class=\"block h-full rounded bg-gradient-to-r from-blue-600 to-violet-600 transition-[width] duration-300 ease-in-out\"\n [style.width.%]=\"progressPercent()\"\n ></span>\n </div>\n <span class=\"eds-body-sm-bold\">{{ progressPercent() }}%</span>\n </div>\n </header>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading setup\u2026</p>\n } @else if (setup()) {\n <div class=\"wizard-page__layout\">\n <nav class=\"eds-card wizard-page__steps flex flex-col gap-1 overflow-y-auto p-2 shadow-sm\">\n @for (step of setup()!.steps; track step.id) {\n <button\n type=\"button\"\n class=\"wizard-page__step flex w-full cursor-pointer items-start gap-2.5 rounded-md border-none bg-transparent p-2.5 text-left hover:bg-zinc-50\"\n [class.wizard-page__step--active]=\"activeStep() === step.id\"\n [class.wizard-page__step--done]=\"step.complete\"\n (click)=\"selectStep(step)\"\n >\n <span class=\"wizard-page__step-num flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-zinc-100 text-xs font-semibold\">\n @if (step.complete) {\n <eds-icon name=\"check\" [size]=\"16\" />\n } @else {\n {{ step.id }}\n }\n </span>\n <div class=\"flex flex-col gap-0.5\">\n <span class=\"eds-body-sm-bold\">{{ step.title }}</span>\n <span class=\"eds-body-sm\">{{ step.description }}</span>\n </div>\n </button>\n }\n </nav>\n\n <article class=\"eds-card p-5 shadow-sm\">\n @for (step of setup()!.steps; track step.id) {\n @if (activeStep() === step.id) {\n <header class=\"mb-3 flex items-center justify-between gap-3\">\n <h2 class=\"eds-h2\">Step {{ step.id }}: {{ step.title }}</h2>\n <eds-badge [tone]=\"step.complete ? 'pos' : 'notice'\">\n {{ step.complete ? 'Complete' : 'Incomplete' }}\n </eds-badge>\n </header>\n <p class=\"eds-body\">{{ step.description }}</p>\n }\n }\n\n <section class=\"mt-6 border-t border-zinc-200 pt-5\">\n <h3 class=\"eds-h3\">Test transcript preview</h3>\n <div class=\"mt-3 flex flex-col gap-2.5\">\n @for (line of setup()!.testTranscript; track $index) {\n <div class=\"wizard-page__line rounded-md bg-zinc-50 px-3 py-2.5\" [attr.data-who]=\"line.who\">\n <span class=\"eds-label\">{{ line.who }}</span>\n <p class=\"m-0 mt-1 text-sm\">{{ line.text }}</p>\n </div>\n }\n </div>\n </section>\n\n <footer class=\"mt-6 flex items-center justify-between border-t border-zinc-200 pt-4\">\n <p class=\"eds-body-sm\">\n {{ completedCount() }} of {{ setup()!.steps.length }} steps complete\n </p>\n @if (activated()) {\n <eds-badge tone=\"pos\">Agent activated</eds-badge>\n } @else {\n <eds-button variant=\"primary\" (click)=\"activate()\">\n <eds-icon name=\"rocket_launch\" [size]=\"18\" /> Activate AI receptionist\n </eds-button>\n }\n </footer>\n </article>\n </div>\n }\n</div>\n", styles: [".wizard-page__steps{max-height:600px}.wizard-page__layout{display:grid;grid-template-columns:300px 1fr;gap:16px;align-items:start}.wizard-page__step--active{background:#2563eb1a;outline:1px solid rgba(37,99,235,.25)}.wizard-page__step--done .wizard-page__step-num{background:#22c55e;color:#fff}.wizard-page__line[data-who=ai]{border-left:3px solid #2563eb}@media(max-width:900px){.wizard-page__layout{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "component", type: EdsBadgeComponent, selector: "eds-badge", inputs: ["tone"] }, { kind: "component", type: EdsButtonComponent, selector: "eds-button", inputs: ["variant", "type", "disabled"] }, { kind: "component", type: EdsIconComponent, selector: "eds-icon", inputs: ["name", "size", "fill", "weight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
39
|
+
}
|
|
40
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: WizardPageComponent, decorators: [{
|
|
41
|
+
type: Component,
|
|
42
|
+
args: [{ selector: 'eds-wizard-page', imports: [EdsBadgeComponent, EdsButtonComponent, EdsIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"eds-page wizard-page\">\n <header class=\"mb-5 flex items-start justify-between gap-5\">\n <div>\n <h1 class=\"eds-h1\">Setup wizard</h1>\n <p class=\"eds-body-sm\">Configure and activate your AI receptionist</p>\n </div>\n <div class=\"flex min-w-52 flex-col items-end gap-1.5\">\n <span class=\"eds-label\">Readiness</span>\n <div class=\"h-2 w-52 overflow-hidden rounded bg-zinc-100\">\n <span\n class=\"block h-full rounded bg-gradient-to-r from-blue-600 to-violet-600 transition-[width] duration-300 ease-in-out\"\n [style.width.%]=\"progressPercent()\"\n ></span>\n </div>\n <span class=\"eds-body-sm-bold\">{{ progressPercent() }}%</span>\n </div>\n </header>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading setup\u2026</p>\n } @else if (setup()) {\n <div class=\"wizard-page__layout\">\n <nav class=\"eds-card wizard-page__steps flex flex-col gap-1 overflow-y-auto p-2 shadow-sm\">\n @for (step of setup()!.steps; track step.id) {\n <button\n type=\"button\"\n class=\"wizard-page__step flex w-full cursor-pointer items-start gap-2.5 rounded-md border-none bg-transparent p-2.5 text-left hover:bg-zinc-50\"\n [class.wizard-page__step--active]=\"activeStep() === step.id\"\n [class.wizard-page__step--done]=\"step.complete\"\n (click)=\"selectStep(step)\"\n >\n <span class=\"wizard-page__step-num flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-zinc-100 text-xs font-semibold\">\n @if (step.complete) {\n <eds-icon name=\"check\" [size]=\"16\" />\n } @else {\n {{ step.id }}\n }\n </span>\n <div class=\"flex flex-col gap-0.5\">\n <span class=\"eds-body-sm-bold\">{{ step.title }}</span>\n <span class=\"eds-body-sm\">{{ step.description }}</span>\n </div>\n </button>\n }\n </nav>\n\n <article class=\"eds-card p-5 shadow-sm\">\n @for (step of setup()!.steps; track step.id) {\n @if (activeStep() === step.id) {\n <header class=\"mb-3 flex items-center justify-between gap-3\">\n <h2 class=\"eds-h2\">Step {{ step.id }}: {{ step.title }}</h2>\n <eds-badge [tone]=\"step.complete ? 'pos' : 'notice'\">\n {{ step.complete ? 'Complete' : 'Incomplete' }}\n </eds-badge>\n </header>\n <p class=\"eds-body\">{{ step.description }}</p>\n }\n }\n\n <section class=\"mt-6 border-t border-zinc-200 pt-5\">\n <h3 class=\"eds-h3\">Test transcript preview</h3>\n <div class=\"mt-3 flex flex-col gap-2.5\">\n @for (line of setup()!.testTranscript; track $index) {\n <div class=\"wizard-page__line rounded-md bg-zinc-50 px-3 py-2.5\" [attr.data-who]=\"line.who\">\n <span class=\"eds-label\">{{ line.who }}</span>\n <p class=\"m-0 mt-1 text-sm\">{{ line.text }}</p>\n </div>\n }\n </div>\n </section>\n\n <footer class=\"mt-6 flex items-center justify-between border-t border-zinc-200 pt-4\">\n <p class=\"eds-body-sm\">\n {{ completedCount() }} of {{ setup()!.steps.length }} steps complete\n </p>\n @if (activated()) {\n <eds-badge tone=\"pos\">Agent activated</eds-badge>\n } @else {\n <eds-button variant=\"primary\" (click)=\"activate()\">\n <eds-icon name=\"rocket_launch\" [size]=\"18\" /> Activate AI receptionist\n </eds-button>\n }\n </footer>\n </article>\n </div>\n }\n</div>\n", styles: [".wizard-page__steps{max-height:600px}.wizard-page__layout{display:grid;grid-template-columns:300px 1fr;gap:16px;align-items:start}.wizard-page__step--active{background:#2563eb1a;outline:1px solid rgba(37,99,235,.25)}.wizard-page__step--done .wizard-page__step-num{background:#22c55e;color:#fff}.wizard-page__line[data-who=ai]{border-left:3px solid #2563eb}@media(max-width:900px){.wizard-page__layout{grid-template-columns:1fr}}\n"] }]
|
|
43
|
+
}] });
|
|
44
|
+
|
|
45
|
+
export { WizardPageComponent };
|
|
46
|
+
//# sourceMappingURL=edvak99-voice-receptionist-widget-wizard-page.component-DjaurkbO.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edvak99-voice-receptionist-widget-wizard-page.component-DjaurkbO.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/pages/wizard-page/wizard-page.component.ts","../../../projects/voice-receptionist-widget/src/lib/pages/wizard-page/wizard-page.component.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, inject, OnInit, signal } from '@angular/core';\n\nimport { SetupData, SetupStep } from '../../models/ai-receptionist.types';\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport { EdsBadgeComponent } from '../../ui/eds-badge.component';\nimport { EdsButtonComponent } from '../../ui/eds-button.component';\nimport { EdsIconComponent } from '../../ui/eds-icon.component';\n\n@Component({\n selector: 'eds-wizard-page',\n imports: [EdsBadgeComponent, EdsButtonComponent, EdsIconComponent],\n templateUrl: './wizard-page.component.html',\n styleUrl: './wizard-page.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class WizardPageComponent implements OnInit {\n private readonly api = inject(AiReceptionistApiService);\n\n protected readonly setup = signal<SetupData | null>(null);\n protected readonly activeStep = signal(1);\n protected readonly loading = signal(true);\n protected readonly activated = signal(false);\n\n protected readonly completedCount = computed(() => {\n return this.setup()?.steps.filter((s) => s.complete).length ?? 0;\n });\n\n ngOnInit(): void {\n this.api.getSetup().subscribe(({ data }) => {\n this.setup.set(data);\n const firstIncomplete = data.steps.find((s) => !s.complete);\n this.activeStep.set(firstIncomplete?.id ?? 1);\n this.loading.set(false);\n });\n }\n\n protected selectStep(step: SetupStep): void {\n this.activeStep.set(step.id);\n }\n\n protected progressPercent(): number {\n return this.setup()?.readiness ?? 0;\n }\n\n protected activate(): void {\n this.api.activateSetup().subscribe(() => {\n this.api.setAgentOn(true);\n this.activated.set(true);\n });\n }\n}\n","<div class=\"eds-page wizard-page\">\n <header class=\"mb-5 flex items-start justify-between gap-5\">\n <div>\n <h1 class=\"eds-h1\">Setup wizard</h1>\n <p class=\"eds-body-sm\">Configure and activate your AI receptionist</p>\n </div>\n <div class=\"flex min-w-52 flex-col items-end gap-1.5\">\n <span class=\"eds-label\">Readiness</span>\n <div class=\"h-2 w-52 overflow-hidden rounded bg-zinc-100\">\n <span\n class=\"block h-full rounded bg-gradient-to-r from-blue-600 to-violet-600 transition-[width] duration-300 ease-in-out\"\n [style.width.%]=\"progressPercent()\"\n ></span>\n </div>\n <span class=\"eds-body-sm-bold\">{{ progressPercent() }}%</span>\n </div>\n </header>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading setup…</p>\n } @else if (setup()) {\n <div class=\"wizard-page__layout\">\n <nav class=\"eds-card wizard-page__steps flex flex-col gap-1 overflow-y-auto p-2 shadow-sm\">\n @for (step of setup()!.steps; track step.id) {\n <button\n type=\"button\"\n class=\"wizard-page__step flex w-full cursor-pointer items-start gap-2.5 rounded-md border-none bg-transparent p-2.5 text-left hover:bg-zinc-50\"\n [class.wizard-page__step--active]=\"activeStep() === step.id\"\n [class.wizard-page__step--done]=\"step.complete\"\n (click)=\"selectStep(step)\"\n >\n <span class=\"wizard-page__step-num flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-zinc-100 text-xs font-semibold\">\n @if (step.complete) {\n <eds-icon name=\"check\" [size]=\"16\" />\n } @else {\n {{ step.id }}\n }\n </span>\n <div class=\"flex flex-col gap-0.5\">\n <span class=\"eds-body-sm-bold\">{{ step.title }}</span>\n <span class=\"eds-body-sm\">{{ step.description }}</span>\n </div>\n </button>\n }\n </nav>\n\n <article class=\"eds-card p-5 shadow-sm\">\n @for (step of setup()!.steps; track step.id) {\n @if (activeStep() === step.id) {\n <header class=\"mb-3 flex items-center justify-between gap-3\">\n <h2 class=\"eds-h2\">Step {{ step.id }}: {{ step.title }}</h2>\n <eds-badge [tone]=\"step.complete ? 'pos' : 'notice'\">\n {{ step.complete ? 'Complete' : 'Incomplete' }}\n </eds-badge>\n </header>\n <p class=\"eds-body\">{{ step.description }}</p>\n }\n }\n\n <section class=\"mt-6 border-t border-zinc-200 pt-5\">\n <h3 class=\"eds-h3\">Test transcript preview</h3>\n <div class=\"mt-3 flex flex-col gap-2.5\">\n @for (line of setup()!.testTranscript; track $index) {\n <div class=\"wizard-page__line rounded-md bg-zinc-50 px-3 py-2.5\" [attr.data-who]=\"line.who\">\n <span class=\"eds-label\">{{ line.who }}</span>\n <p class=\"m-0 mt-1 text-sm\">{{ line.text }}</p>\n </div>\n }\n </div>\n </section>\n\n <footer class=\"mt-6 flex items-center justify-between border-t border-zinc-200 pt-4\">\n <p class=\"eds-body-sm\">\n {{ completedCount() }} of {{ setup()!.steps.length }} steps complete\n </p>\n @if (activated()) {\n <eds-badge tone=\"pos\">Agent activated</eds-badge>\n } @else {\n <eds-button variant=\"primary\" (click)=\"activate()\">\n <eds-icon name=\"rocket_launch\" [size]=\"18\" /> Activate AI receptionist\n </eds-button>\n }\n </footer>\n </article>\n </div>\n }\n</div>\n"],"names":[],"mappings":"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"}
|