@edvak99/voice-receptionist-widget 1.1.3 → 1.1.4
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/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-DT9UKvQO.mjs +307 -0
- package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-DT9UKvQO.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-Dn0GrZ6_.mjs → edvak99-voice-receptionist-widget-ai-receptionist-api.service-48cifVPF.mjs} +1275 -561
- package/fesm2022/edvak99-voice-receptionist-widget-ai-receptionist-api.service-48cifVPF.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-C5inEHJw.mjs +278 -0
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-C5inEHJw.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-sYhM20CE.mjs +418 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-sYhM20CE.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-DpwajEOE.mjs +194 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-DpwajEOE.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-call-recording-player.component-B8_qp7Fe.mjs → edvak99-voice-receptionist-widget-call-recording-player.component-CgkRcFOl.mjs} +3 -2
- package/fesm2022/{edvak99-voice-receptionist-widget-call-recording-player.component-B8_qp7Fe.mjs.map → edvak99-voice-receptionist-widget-call-recording-player.component-CgkRcFOl.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-Cj2XTeK4.mjs → edvak99-voice-receptionist-widget-call-review.service-CIMcjGKX.mjs} +2 -2
- package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-Cj2XTeK4.mjs.map → edvak99-voice-receptionist-widget-call-review.service-CIMcjGKX.mjs.map} +1 -1
- package/fesm2022/edvak99-voice-receptionist-widget-call-review.util-Deh3jPgo.mjs +21 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-review.util-Deh3jPgo.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-CxbO0eDS.mjs +440 -0
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-CxbO0eDS.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-eds-elapsed.pipe-BgLsnq6A.mjs → edvak99-voice-receptionist-widget-dashboard-socket.service-BX91r74O.mjs} +5 -21
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-socket.service-BX91r74O.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-CWRiJGfv.mjs +38 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-CWRiJGfv.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-button.component-Bg5T5BN8.mjs +27 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-button.component-Bg5T5BN8.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-elapsed.pipe-BFCiJOT3.mjs +22 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-elapsed.pipe-BFCiJOT3.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-live-calls-list.service-0kGWO6sh.mjs → edvak99-voice-receptionist-widget-eds-pulse-dot.component-BPY6xN7C.mjs} +15 -5
- package/fesm2022/edvak99-voice-receptionist-widget-eds-pulse-dot.component-BPY6xN7C.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-BsBQIS_f.mjs +203 -0
- package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-BsBQIS_f.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-CaSDMxpV.mjs +81 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-CaSDMxpV.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-Cy3ND-c-.mjs +1070 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-Cy3ND-c-.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-modal.component-COfi7biN.mjs +48 -0
- package/fesm2022/edvak99-voice-receptionist-widget-modal.component-COfi7biN.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purpose.util-Bdh4QfFe.mjs +127 -0
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purpose.util-Bdh4QfFe.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-fHVzpiVk.mjs +268 -0
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-fHVzpiVk.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-Bok64X2q.mjs +968 -0
- package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-Bok64X2q.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-tasks-page.component-DXxjMVUv.mjs → edvak99-voice-receptionist-widget-tasks-page.component-Bb9lPcYi.mjs} +56 -5
- package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-Bb9lPcYi.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-wizard-page.component-C1Z6yC0q.mjs → edvak99-voice-receptionist-widget-wizard-page.component-BZVlWLWh.mjs} +4 -2
- package/fesm2022/{edvak99-voice-receptionist-widget-wizard-page.component-C1Z6yC0q.mjs.map → edvak99-voice-receptionist-widget-wizard-page.component-BZVlWLWh.mjs.map} +1 -1
- package/fesm2022/edvak99-voice-receptionist-widget.mjs +1 -1
- package/lib/components/call-conversation-panel/call-conversation-panel.component.d.ts +9 -6
- package/lib/components/call-tasks-panel/call-tasks-panel.component.d.ts +8 -8
- package/lib/components/knowledge-test-modal/knowledge-test-modal.component.d.ts +28 -0
- package/lib/components/live-transcript-panel/live-transcript-panel.component.d.ts +7 -4
- package/lib/components/outbound-dial-modal/outbound-dial-modal.component.d.ts +26 -0
- package/lib/layout/eds-module-nav.component.d.ts +0 -3
- package/lib/mocks/agent-activity.seed.d.ts +2 -0
- package/lib/models/agent-activity.types.d.ts +54 -0
- package/lib/models/ai-receptionist.types.d.ts +25 -0
- package/lib/models/api-dto.types.d.ts +18 -0
- package/lib/models/live-transcript.types.d.ts +2 -1
- package/lib/models/outbound-patient.types.d.ts +23 -0
- package/lib/models/outbound-purpose.types.d.ts +2 -0
- package/lib/models/staff-media.types.d.ts +36 -0
- package/lib/pages/activities-page/activities-page.component.d.ts +57 -0
- package/lib/pages/analytics-page/analytics-page.component.d.ts +38 -14
- package/lib/pages/call-history-detail-page/call-history-detail-page.component.d.ts +2 -3
- package/lib/pages/call-history-page/call-history-page.component.d.ts +9 -6
- package/lib/pages/dashboard-page/dashboard-page.component.d.ts +12 -8
- package/lib/pages/live-calls-page/live-calls-page.component.d.ts +14 -24
- package/lib/pages/live-monitor-page/live-monitor-page.component.d.ts +34 -1
- package/lib/pages/settings-page/settings-fields.config.d.ts +16 -0
- package/lib/pages/settings-page/settings-page.component.d.ts +48 -14
- package/lib/services/ai-receptionist-api.mock.d.ts +34 -0
- package/lib/services/ai-receptionist-api.service.d.ts +56 -1
- package/lib/services/live-transcript.service.d.ts +1 -0
- package/lib/services/staff-media.service.d.ts +52 -0
- package/lib/ui/eds-avatar.component.d.ts +5 -2
- package/lib/ui/eds-button.component.d.ts +1 -1
- package/lib/utils/call-history-display.util.d.ts +21 -0
- package/lib/utils/outbound-purpose.util.d.ts +19 -0
- package/lib/utils/transcript-timing.util.d.ts +1 -1
- package/lib/utils/welcome-message.util.d.ts +1 -1
- package/package.json +2 -1
- package/styles/eds.scss +95 -66
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-Dsujn5az.mjs +0 -113
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-Dsujn5az.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-audit-page.component-xoLlHj8K.mjs +0 -67
- package/fesm2022/edvak99-voice-receptionist-widget-audit-page.component-xoLlHj8K.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-2K9W77As.mjs +0 -316
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-2K9W77As.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-B5AnEjoh.mjs +0 -174
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-B5AnEjoh.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-call-review.util-BgsBCKgZ.mjs +0 -70
- package/fesm2022/edvak99-voice-receptionist-widget-call-review.util-BgsBCKgZ.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-BNdnbAVM.mjs +0 -248
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-BNdnbAVM.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-CbphhP6o.mjs +0 -27
- package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-CbphhP6o.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-eds-elapsed.pipe-BgLsnq6A.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-Dn0GrZ6_.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-knowledge-hub.page-BngGx_Lc.mjs +0 -609
- package/fesm2022/edvak99-voice-receptionist-widget-knowledge-hub.page-BngGx_Lc.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-list.service-0kGWO6sh.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-DfLmK89M.mjs +0 -115
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-DfLmK89M.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-DNpLFqBw.mjs +0 -574
- package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-DNpLFqBw.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purpose.util-BQECIuJu.mjs +0 -54
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purpose.util-BQECIuJu.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-CYSZ832p.mjs +0 -265
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-CYSZ832p.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-B9nv6wtB.mjs +0 -509
- package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-B9nv6wtB.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-task.util-DdtI0CR3.mjs +0 -52
- package/fesm2022/edvak99-voice-receptionist-widget-task.util-DdtI0CR3.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-DXxjMVUv.mjs.map +0 -1
- package/lib/knowledge/components/faq-empty-state/faq-empty-state.component.d.ts +0 -22
- package/lib/knowledge/components/faq-form/faq-form.component.d.ts +0 -27
- package/lib/knowledge/components/faq-search-tester/faq-search-tester.component.d.ts +0 -17
- package/lib/knowledge/mocks/practice-faq.seed.d.ts +0 -2
- package/lib/knowledge/models/practice-faq.model.d.ts +0 -50
- package/lib/knowledge/pages/knowledge-hub.page.d.ts +0 -54
- package/lib/knowledge/services/practice-faq-api.mock.d.ts +0 -16
- package/lib/knowledge/services/practice-faq-api.service.d.ts +0 -26
- package/lib/knowledge/utils/faq.util.d.ts +0 -3
- package/lib/layout/eds-topbar.component.d.ts +0 -29
- package/lib/pages/audit-page/audit-page.component.d.ts +0 -21
- package/lib/ui/eds-waveform.component.d.ts +0 -8
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, signal, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
-
import { ActivatedRoute, Router } from '@angular/router';
|
|
4
|
-
import { DatePipe } from '@angular/common';
|
|
5
|
-
import * as i1 from '@angular/forms';
|
|
6
|
-
import { FormsModule } from '@angular/forms';
|
|
7
|
-
import { A as AiReceptionistApiService, T as ToastService, D as DEFAULT_PAGE_SIZE, f as totalPages, a as EdsBadgeComponent, E as EdsButtonComponent, b as EdsIconComponent } from './edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-Dn0GrZ6_.mjs';
|
|
8
|
-
import { C as CallReviewService } from './edvak99-voice-receptionist-widget-call-review.service-Cj2XTeK4.mjs';
|
|
9
|
-
import { r as reviewLabel, a as reviewBadgeTone, i as isNeedsReview, o as outcomeBadgeTone, f as formatOutcomeLabel } from './edvak99-voice-receptionist-widget-call-review.util-BgsBCKgZ.mjs';
|
|
10
|
-
import { E as EdsAvatarComponent } from './edvak99-voice-receptionist-widget-eds-avatar.component-CbphhP6o.mjs';
|
|
11
|
-
|
|
12
|
-
class CallHistoryPageComponent {
|
|
13
|
-
constructor() {
|
|
14
|
-
this.api = inject(AiReceptionistApiService);
|
|
15
|
-
this.reviewService = inject(CallReviewService);
|
|
16
|
-
this.toast = inject(ToastService);
|
|
17
|
-
this.route = inject(ActivatedRoute);
|
|
18
|
-
this.router = inject(Router);
|
|
19
|
-
this.pageSize = DEFAULT_PAGE_SIZE;
|
|
20
|
-
this.rows = signal([]);
|
|
21
|
-
this.total = signal(0);
|
|
22
|
-
this.page = signal(1);
|
|
23
|
-
this.loading = signal(true);
|
|
24
|
-
this.reviewingId = signal(null);
|
|
25
|
-
this.search = signal('');
|
|
26
|
-
this.direction = signal('');
|
|
27
|
-
this.outcome = signal('');
|
|
28
|
-
this.location = signal('');
|
|
29
|
-
this.reviewTab = signal('all');
|
|
30
|
-
this.reviewTabs = [
|
|
31
|
-
{ id: 'all', label: 'All' },
|
|
32
|
-
{ id: 'needs', label: 'Needs review' },
|
|
33
|
-
{ id: 'reviewed', label: 'Reviewed' },
|
|
34
|
-
];
|
|
35
|
-
this.reviewLabel = reviewLabel;
|
|
36
|
-
this.reviewTone = reviewBadgeTone;
|
|
37
|
-
this.isNeedsReview = isNeedsReview;
|
|
38
|
-
this.outcomeTone = outcomeBadgeTone;
|
|
39
|
-
this.outcomeLabel = formatOutcomeLabel;
|
|
40
|
-
}
|
|
41
|
-
ngOnInit() {
|
|
42
|
-
const reviewParam = this.route.snapshot.queryParamMap.get('review');
|
|
43
|
-
if (reviewParam === 'needs' || reviewParam === 'reviewed') {
|
|
44
|
-
this.reviewTab.set(reviewParam);
|
|
45
|
-
}
|
|
46
|
-
this.load();
|
|
47
|
-
}
|
|
48
|
-
load() {
|
|
49
|
-
this.loading.set(true);
|
|
50
|
-
const tab = this.reviewTab();
|
|
51
|
-
this.api
|
|
52
|
-
.getHistory({
|
|
53
|
-
q: this.search() || undefined,
|
|
54
|
-
dir: this.direction() || undefined,
|
|
55
|
-
outcome: this.outcome() || undefined,
|
|
56
|
-
location: this.location() || undefined,
|
|
57
|
-
review: tab === 'all' ? undefined : tab,
|
|
58
|
-
page: this.page(),
|
|
59
|
-
limit: DEFAULT_PAGE_SIZE,
|
|
60
|
-
})
|
|
61
|
-
.subscribe({
|
|
62
|
-
next: ({ items, total, page }) => {
|
|
63
|
-
this.rows.set(items);
|
|
64
|
-
this.total.set(total);
|
|
65
|
-
this.page.set(page);
|
|
66
|
-
this.loading.set(false);
|
|
67
|
-
},
|
|
68
|
-
error: () => this.loading.set(false),
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
applyFilters() {
|
|
72
|
-
this.page.set(1);
|
|
73
|
-
this.load();
|
|
74
|
-
}
|
|
75
|
-
setReviewTab(tab) {
|
|
76
|
-
this.reviewTab.set(tab);
|
|
77
|
-
this.page.set(1);
|
|
78
|
-
void this.router.navigate([], {
|
|
79
|
-
relativeTo: this.route,
|
|
80
|
-
queryParams: tab === 'all' ? { review: null } : { review: tab },
|
|
81
|
-
queryParamsHandling: 'merge',
|
|
82
|
-
replaceUrl: true,
|
|
83
|
-
});
|
|
84
|
-
this.load();
|
|
85
|
-
}
|
|
86
|
-
tabBadgeCount(tab) {
|
|
87
|
-
if (tab !== 'needs' || this.reviewTab() !== 'needs')
|
|
88
|
-
return null;
|
|
89
|
-
const count = this.total();
|
|
90
|
-
return count > 0 ? count : null;
|
|
91
|
-
}
|
|
92
|
-
totalPages() {
|
|
93
|
-
return totalPages(this.total(), DEFAULT_PAGE_SIZE);
|
|
94
|
-
}
|
|
95
|
-
prevPage() {
|
|
96
|
-
if (this.page() <= 1)
|
|
97
|
-
return;
|
|
98
|
-
this.page.update((p) => p - 1);
|
|
99
|
-
this.load();
|
|
100
|
-
}
|
|
101
|
-
nextPage() {
|
|
102
|
-
if (this.page() >= this.totalPages())
|
|
103
|
-
return;
|
|
104
|
-
this.page.update((p) => p + 1);
|
|
105
|
-
this.load();
|
|
106
|
-
}
|
|
107
|
-
markReviewed(row, event) {
|
|
108
|
-
event.preventDefault();
|
|
109
|
-
event.stopPropagation();
|
|
110
|
-
if (!isNeedsReview(row.review) || this.reviewingId())
|
|
111
|
-
return;
|
|
112
|
-
this.reviewingId.set(row.id);
|
|
113
|
-
this.rows.update((list) => list.map((r) => (r.id === row.id ? { ...r, review: 'reviewed' } : r)));
|
|
114
|
-
this.reviewService.markReviewed(row.id).subscribe({
|
|
115
|
-
next: () => {
|
|
116
|
-
this.reviewingId.set(null);
|
|
117
|
-
this.toast.show('Call marked as reviewed', 'success');
|
|
118
|
-
if (this.reviewTab() === 'needs')
|
|
119
|
-
this.load();
|
|
120
|
-
},
|
|
121
|
-
error: () => {
|
|
122
|
-
this.reviewingId.set(null);
|
|
123
|
-
this.rows.update((list) => list.map((r) => (r.id === row.id ? { ...r, review: 'needs' } : r)));
|
|
124
|
-
this.toast.show('Failed to mark call as reviewed', 'error');
|
|
125
|
-
},
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
formatDuration(sec) {
|
|
129
|
-
const m = Math.floor(sec / 60);
|
|
130
|
-
const s = sec % 60;
|
|
131
|
-
return `${m}:${s.toString().padStart(2, '0')}`;
|
|
132
|
-
}
|
|
133
|
-
openCallDetail(id) {
|
|
134
|
-
void this.router.navigate(['../history', id], { relativeTo: this.route });
|
|
135
|
-
}
|
|
136
|
-
exportCsv() {
|
|
137
|
-
const header = ['id', 'name', 'phone', 'direction', 'outcome', 'duration', 'started'];
|
|
138
|
-
const csvRows = this.rows().map((r) => [
|
|
139
|
-
r.id,
|
|
140
|
-
r.name,
|
|
141
|
-
r.phone,
|
|
142
|
-
r.direction,
|
|
143
|
-
r.outcome,
|
|
144
|
-
String(r.duration),
|
|
145
|
-
String(r.started),
|
|
146
|
-
]);
|
|
147
|
-
const csv = [header, ...csvRows]
|
|
148
|
-
.map((row) => row.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(','))
|
|
149
|
-
.join('\n');
|
|
150
|
-
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
|
|
151
|
-
const url = URL.createObjectURL(blob);
|
|
152
|
-
const a = document.createElement('a');
|
|
153
|
-
a.href = url;
|
|
154
|
-
a.download = `call-history-page-${this.page()}.csv`;
|
|
155
|
-
a.click();
|
|
156
|
-
URL.revokeObjectURL(url);
|
|
157
|
-
}
|
|
158
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: CallHistoryPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
159
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: CallHistoryPageComponent, isStandalone: true, selector: "eds-call-history-page", ngImport: i0, template: "<div class=\"eds-page call-history-page\">\n\n <header class=\"call-history-page__header\">\n\n <div>\n\n <h1 class=\"eds-h1\">Call history</h1>\n\n <p class=\"eds-body-sm\">Search and review completed conversations</p>\n\n </div>\n\n <eds-button variant=\"secondary\" (click)=\"exportCsv()\">\n\n <eds-icon name=\"download\" [size]=\"18\" /> Export CSV\n\n </eds-button>\n\n </header>\n\n\n\n <nav class=\"call-history-page__review-tabs\" aria-label=\"Review status\">\n\n @for (t of reviewTabs; track t.id) {\n\n <button\n\n type=\"button\"\n\n class=\"call-history-page__review-tab\"\n\n [class.call-history-page__review-tab--active]=\"reviewTab() === t.id\"\n\n (click)=\"setReviewTab(t.id)\"\n\n >\n\n {{ t.label }}\n\n @if (tabBadgeCount(t.id); as count) {\n\n <span class=\"call-history-page__tab-badge\">{{ count }}</span>\n\n }\n\n </button>\n\n }\n\n </nav>\n\n\n\n <div class=\"eds-card call-history-page__filters\">\n\n <input\n\n type=\"search\"\n\n class=\"call-history-page__search\"\n\n placeholder=\"Search name, phone, reason\u2026\"\n\n [ngModel]=\"search()\"\n\n (ngModelChange)=\"search.set($event)\"\n\n (keyup.enter)=\"applyFilters()\"\n\n />\n\n <select class=\"call-history-page__select\" [ngModel]=\"direction()\" (ngModelChange)=\"direction.set($event)\">\n\n <option value=\"\">All directions</option>\n\n <option value=\"inbound\">Inbound</option>\n\n <option value=\"outbound\">Outbound</option>\n\n </select>\n\n <input\n\n type=\"text\"\n\n class=\"call-history-page__select\"\n\n placeholder=\"Outcome\"\n\n [ngModel]=\"outcome()\"\n\n (ngModelChange)=\"outcome.set($event)\"\n\n />\n\n <input\n\n type=\"text\"\n\n class=\"call-history-page__select\"\n\n placeholder=\"Location\"\n\n [ngModel]=\"location()\"\n\n (ngModelChange)=\"location.set($event)\"\n\n />\n\n <eds-button variant=\"primary\" (click)=\"applyFilters()\">Apply</eds-button>\n\n </div>\n\n\n\n @if (loading()) {\n\n <p class=\"eds-body-sm\">Loading history\u2026</p>\n\n } @else {\n\n <div class=\"eds-card call-history-page__table-wrap\">\n\n <table class=\"eds-table\">\n\n <thead>\n\n <tr>\n\n <th>Caller</th>\n\n <th>Direction</th>\n\n <th>Reason</th>\n\n <th>Status</th>\n\n <th>Duration</th>\n\n <th>Date & time</th>\n\n <th>Review</th>\n\n <th></th>\n\n </tr>\n\n </thead>\n\n <tbody>\n\n @for (row of rows(); track row.id) {\n\n <tr\n class=\"call-history-page__row\"\n [class.call-history-page__row--needs-review]=\"isNeedsReview(row.review)\"\n tabindex=\"0\"\n role=\"link\"\n [attr.aria-label]=\"'View call details for ' + row.name\"\n (click)=\"openCallDetail(row.id)\"\n (keyup.enter)=\"openCallDetail(row.id)\"\n >\n <td>\n <div class=\"call-history-page__caller\">\n <eds-avatar [initials]=\"row.initials\" />\n <span>{{ row.name }}</span>\n </div>\n </td>\n\n <td>{{ row.direction }}</td>\n\n <td>{{ row.reason }}</td>\n\n <td>\n\n <eds-badge [tone]=\"outcomeTone(row.outcome)\">\n\n {{ outcomeLabel(row.outcome) }}\n\n </eds-badge>\n\n </td>\n\n <td class=\"eds-mono-sm\">{{ row.durationLabel ?? formatDuration(row.duration) }}</td>\n\n <td class=\"eds-mono-sm\">\n {{ row.started ? (row.started | date: 'medium') : (row.whenLabel ?? '\u2014') }}\n </td>\n\n <td>\n\n <eds-badge [tone]=\"reviewTone(row.review)\">\n\n {{ reviewLabel(row.review) }}\n\n </eds-badge>\n\n </td>\n\n <td class=\"call-history-page__actions\">\n\n @if (isNeedsReview(row.review)) {\n\n <button\n\n type=\"button\"\n\n class=\"call-history-page__mark-btn\"\n\n title=\"Mark as reviewed\"\n\n [disabled]=\"reviewingId() === row.id\"\n\n (click)=\"markReviewed(row, $event)\"\n\n >\n\n <eds-icon name=\"check_circle\" [size]=\"18\" />\n\n </button>\n\n }\n\n </td>\n\n </tr>\n\n } @empty {\n\n <tr>\n\n <td colspan=\"8\" class=\"eds-body-sm\">No calls match this filter.</td>\n\n </tr>\n\n }\n\n </tbody>\n\n </table>\n\n </div>\n\n }\n\n\n <footer class=\"call-history-page__pager\">\n <span class=\"eds-body-sm\">\n Page {{ page() }} of {{ totalPages() }} \u00B7 {{ total() }} total\n </span>\n <div class=\"call-history-page__pager-actions\">\n <eds-button variant=\"secondary\" [disabled]=\"page() <= 1\" (click)=\"prevPage()\">Previous</eds-button>\n <eds-button variant=\"secondary\" [disabled]=\"page() >= totalPages()\" (click)=\"nextPage()\">Next</eds-button>\n </div>\n </footer>\n\n</div>\n\n\n", styles: [".call-history-page__header{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:20px}.call-history-page__review-tabs{display:flex;gap:4px;margin-bottom:12px;padding:4px;width:fit-content;background:var(--eds-surface-l1);border-radius:var(--eds-radius-md)}.call-history-page__review-tab{border:none;background:transparent;padding:8px 14px;border-radius:var(--eds-radius-sm);font-size:13px;font-weight:500;cursor:pointer;color:var(--eds-text-secondary);display:inline-flex;align-items:center;gap:6px}.call-history-page__review-tab--active{background:var(--eds-surface-l0);color:var(--eds-text-primary);box-shadow:0 1px 2px #0000000f}.call-history-page__tab-badge{min-width:18px;padding:0 6px;border-radius:999px;background:#f59e0b;color:#fff;font-size:11px;font-weight:700;line-height:18px;text-align:center}.call-history-page__row{cursor:pointer}.call-history-page__row:hover{background:var(--eds-surface-l1)}.call-history-page__row:focus-visible{outline:2px solid var(--eds-brand-500, #0d9488);outline-offset:-2px}.call-history-page__row--needs-review{border-left:3px solid #f59e0b}.call-history-page__actions{width:40px;text-align:center}.call-history-page__mark-btn{border:none;background:transparent;color:var(--eds-green-600, #16a34a);cursor:pointer;padding:4px;border-radius:6px}.call-history-page__mark-btn:hover:not(:disabled){background:#16a34a1a}.call-history-page__mark-btn:disabled{opacity:.5;cursor:wait}.call-history-page__filters{display:flex;flex-wrap:wrap;gap:10px;padding:14px;margin-bottom:16px;align-items:center}.call-history-page__search{flex:1;min-width:200px;padding:8px 12px;border:1px solid var(--eds-border-default);border-radius:var(--eds-radius-md);font-size:14px}.call-history-page__select{padding:8px 12px;border:1px solid var(--eds-border-default);border-radius:var(--eds-radius-md);font-size:13px;background:var(--eds-surface-l0)}.call-history-page__table-wrap{overflow-x:auto;padding:0}.call-history-page__caller{display:flex;align-items:center;gap:8px;font-weight:500}.call-history-page__pager{display:flex;align-items:center;justify-content:space-between;margin-top:16px;padding-top:12px;border-top:1px solid var(--eds-border-subtle, #e4e4e7)}.call-history-page__pager-actions{display:flex;gap:8px}\n"], dependencies: [{ kind: "pipe", type: DatePipe, name: "date" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: EdsAvatarComponent, selector: "eds-avatar", inputs: ["initials", "tone", "gradient"] }, { kind: "component", type: EdsBadgeComponent, selector: "eds-badge", inputs: ["tone"] }, { kind: "component", type: EdsButtonComponent, selector: "eds-button", inputs: ["variant", "type", "disabled"] }, { kind: "component", type: EdsIconComponent, selector: "eds-icon", inputs: ["name", "size", "fill", "weight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
160
|
-
}
|
|
161
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: CallHistoryPageComponent, decorators: [{
|
|
162
|
-
type: Component,
|
|
163
|
-
args: [{ selector: 'eds-call-history-page', imports: [
|
|
164
|
-
DatePipe,
|
|
165
|
-
FormsModule,
|
|
166
|
-
EdsAvatarComponent,
|
|
167
|
-
EdsBadgeComponent,
|
|
168
|
-
EdsButtonComponent,
|
|
169
|
-
EdsIconComponent,
|
|
170
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"eds-page call-history-page\">\n\n <header class=\"call-history-page__header\">\n\n <div>\n\n <h1 class=\"eds-h1\">Call history</h1>\n\n <p class=\"eds-body-sm\">Search and review completed conversations</p>\n\n </div>\n\n <eds-button variant=\"secondary\" (click)=\"exportCsv()\">\n\n <eds-icon name=\"download\" [size]=\"18\" /> Export CSV\n\n </eds-button>\n\n </header>\n\n\n\n <nav class=\"call-history-page__review-tabs\" aria-label=\"Review status\">\n\n @for (t of reviewTabs; track t.id) {\n\n <button\n\n type=\"button\"\n\n class=\"call-history-page__review-tab\"\n\n [class.call-history-page__review-tab--active]=\"reviewTab() === t.id\"\n\n (click)=\"setReviewTab(t.id)\"\n\n >\n\n {{ t.label }}\n\n @if (tabBadgeCount(t.id); as count) {\n\n <span class=\"call-history-page__tab-badge\">{{ count }}</span>\n\n }\n\n </button>\n\n }\n\n </nav>\n\n\n\n <div class=\"eds-card call-history-page__filters\">\n\n <input\n\n type=\"search\"\n\n class=\"call-history-page__search\"\n\n placeholder=\"Search name, phone, reason\u2026\"\n\n [ngModel]=\"search()\"\n\n (ngModelChange)=\"search.set($event)\"\n\n (keyup.enter)=\"applyFilters()\"\n\n />\n\n <select class=\"call-history-page__select\" [ngModel]=\"direction()\" (ngModelChange)=\"direction.set($event)\">\n\n <option value=\"\">All directions</option>\n\n <option value=\"inbound\">Inbound</option>\n\n <option value=\"outbound\">Outbound</option>\n\n </select>\n\n <input\n\n type=\"text\"\n\n class=\"call-history-page__select\"\n\n placeholder=\"Outcome\"\n\n [ngModel]=\"outcome()\"\n\n (ngModelChange)=\"outcome.set($event)\"\n\n />\n\n <input\n\n type=\"text\"\n\n class=\"call-history-page__select\"\n\n placeholder=\"Location\"\n\n [ngModel]=\"location()\"\n\n (ngModelChange)=\"location.set($event)\"\n\n />\n\n <eds-button variant=\"primary\" (click)=\"applyFilters()\">Apply</eds-button>\n\n </div>\n\n\n\n @if (loading()) {\n\n <p class=\"eds-body-sm\">Loading history\u2026</p>\n\n } @else {\n\n <div class=\"eds-card call-history-page__table-wrap\">\n\n <table class=\"eds-table\">\n\n <thead>\n\n <tr>\n\n <th>Caller</th>\n\n <th>Direction</th>\n\n <th>Reason</th>\n\n <th>Status</th>\n\n <th>Duration</th>\n\n <th>Date & time</th>\n\n <th>Review</th>\n\n <th></th>\n\n </tr>\n\n </thead>\n\n <tbody>\n\n @for (row of rows(); track row.id) {\n\n <tr\n class=\"call-history-page__row\"\n [class.call-history-page__row--needs-review]=\"isNeedsReview(row.review)\"\n tabindex=\"0\"\n role=\"link\"\n [attr.aria-label]=\"'View call details for ' + row.name\"\n (click)=\"openCallDetail(row.id)\"\n (keyup.enter)=\"openCallDetail(row.id)\"\n >\n <td>\n <div class=\"call-history-page__caller\">\n <eds-avatar [initials]=\"row.initials\" />\n <span>{{ row.name }}</span>\n </div>\n </td>\n\n <td>{{ row.direction }}</td>\n\n <td>{{ row.reason }}</td>\n\n <td>\n\n <eds-badge [tone]=\"outcomeTone(row.outcome)\">\n\n {{ outcomeLabel(row.outcome) }}\n\n </eds-badge>\n\n </td>\n\n <td class=\"eds-mono-sm\">{{ row.durationLabel ?? formatDuration(row.duration) }}</td>\n\n <td class=\"eds-mono-sm\">\n {{ row.started ? (row.started | date: 'medium') : (row.whenLabel ?? '\u2014') }}\n </td>\n\n <td>\n\n <eds-badge [tone]=\"reviewTone(row.review)\">\n\n {{ reviewLabel(row.review) }}\n\n </eds-badge>\n\n </td>\n\n <td class=\"call-history-page__actions\">\n\n @if (isNeedsReview(row.review)) {\n\n <button\n\n type=\"button\"\n\n class=\"call-history-page__mark-btn\"\n\n title=\"Mark as reviewed\"\n\n [disabled]=\"reviewingId() === row.id\"\n\n (click)=\"markReviewed(row, $event)\"\n\n >\n\n <eds-icon name=\"check_circle\" [size]=\"18\" />\n\n </button>\n\n }\n\n </td>\n\n </tr>\n\n } @empty {\n\n <tr>\n\n <td colspan=\"8\" class=\"eds-body-sm\">No calls match this filter.</td>\n\n </tr>\n\n }\n\n </tbody>\n\n </table>\n\n </div>\n\n }\n\n\n <footer class=\"call-history-page__pager\">\n <span class=\"eds-body-sm\">\n Page {{ page() }} of {{ totalPages() }} \u00B7 {{ total() }} total\n </span>\n <div class=\"call-history-page__pager-actions\">\n <eds-button variant=\"secondary\" [disabled]=\"page() <= 1\" (click)=\"prevPage()\">Previous</eds-button>\n <eds-button variant=\"secondary\" [disabled]=\"page() >= totalPages()\" (click)=\"nextPage()\">Next</eds-button>\n </div>\n </footer>\n\n</div>\n\n\n", styles: [".call-history-page__header{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:20px}.call-history-page__review-tabs{display:flex;gap:4px;margin-bottom:12px;padding:4px;width:fit-content;background:var(--eds-surface-l1);border-radius:var(--eds-radius-md)}.call-history-page__review-tab{border:none;background:transparent;padding:8px 14px;border-radius:var(--eds-radius-sm);font-size:13px;font-weight:500;cursor:pointer;color:var(--eds-text-secondary);display:inline-flex;align-items:center;gap:6px}.call-history-page__review-tab--active{background:var(--eds-surface-l0);color:var(--eds-text-primary);box-shadow:0 1px 2px #0000000f}.call-history-page__tab-badge{min-width:18px;padding:0 6px;border-radius:999px;background:#f59e0b;color:#fff;font-size:11px;font-weight:700;line-height:18px;text-align:center}.call-history-page__row{cursor:pointer}.call-history-page__row:hover{background:var(--eds-surface-l1)}.call-history-page__row:focus-visible{outline:2px solid var(--eds-brand-500, #0d9488);outline-offset:-2px}.call-history-page__row--needs-review{border-left:3px solid #f59e0b}.call-history-page__actions{width:40px;text-align:center}.call-history-page__mark-btn{border:none;background:transparent;color:var(--eds-green-600, #16a34a);cursor:pointer;padding:4px;border-radius:6px}.call-history-page__mark-btn:hover:not(:disabled){background:#16a34a1a}.call-history-page__mark-btn:disabled{opacity:.5;cursor:wait}.call-history-page__filters{display:flex;flex-wrap:wrap;gap:10px;padding:14px;margin-bottom:16px;align-items:center}.call-history-page__search{flex:1;min-width:200px;padding:8px 12px;border:1px solid var(--eds-border-default);border-radius:var(--eds-radius-md);font-size:14px}.call-history-page__select{padding:8px 12px;border:1px solid var(--eds-border-default);border-radius:var(--eds-radius-md);font-size:13px;background:var(--eds-surface-l0)}.call-history-page__table-wrap{overflow-x:auto;padding:0}.call-history-page__caller{display:flex;align-items:center;gap:8px;font-weight:500}.call-history-page__pager{display:flex;align-items:center;justify-content:space-between;margin-top:16px;padding-top:12px;border-top:1px solid var(--eds-border-subtle, #e4e4e7)}.call-history-page__pager-actions{display:flex;gap:8px}\n"] }]
|
|
171
|
-
}] });
|
|
172
|
-
|
|
173
|
-
export { CallHistoryPageComponent };
|
|
174
|
-
//# sourceMappingURL=edvak99-voice-receptionist-widget-call-history-page.component-B5AnEjoh.mjs.map
|
package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-B5AnEjoh.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"edvak99-voice-receptionist-widget-call-history-page.component-B5AnEjoh.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/pages/call-history-page/call-history-page.component.ts","../../../projects/voice-receptionist-widget/src/lib/pages/call-history-page/call-history-page.component.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { DatePipe } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\n\nimport { HistoryCall } from '../../models/ai-receptionist.types';\nimport { DEFAULT_PAGE_SIZE, totalPages as calcTotalPages } from '../../models/pagination';\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport { CallReviewService } from '../../services/call-review.service';\nimport { formatOutcomeLabel, outcomeBadgeTone } from '../../utils/call-outcome.util';\nimport {\n isNeedsReview,\n reviewBadgeTone,\n reviewLabel,\n ReviewFilterTab,\n} from '../../utils/call-review.util';\nimport { EdsAvatarComponent } from '../../ui/eds-avatar.component';\nimport { EdsBadgeComponent } from '../../ui/eds-badge.component';\nimport { EdsButtonComponent } from '../../ui/eds-button.component';\nimport { EdsIconComponent } from '../../ui/eds-icon.component';\nimport { ToastService } from '../../shared/toast.service';\n\n@Component({\n selector: 'eds-call-history-page',\n imports: [\n DatePipe,\n FormsModule,\n EdsAvatarComponent,\n EdsBadgeComponent,\n EdsButtonComponent,\n EdsIconComponent,\n ],\n templateUrl: './call-history-page.component.html',\n styleUrl: './call-history-page.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class CallHistoryPageComponent implements OnInit {\n private readonly api = inject(AiReceptionistApiService);\n private readonly reviewService = inject(CallReviewService);\n private readonly toast = inject(ToastService);\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n\n protected readonly pageSize = DEFAULT_PAGE_SIZE;\n protected readonly rows = signal<HistoryCall[]>([]);\n protected readonly total = signal(0);\n protected readonly page = signal(1);\n protected readonly loading = signal(true);\n protected readonly reviewingId = signal<string | null>(null);\n protected readonly search = signal('');\n protected readonly direction = signal('');\n protected readonly outcome = signal('');\n protected readonly location = signal('');\n protected readonly reviewTab = signal<ReviewFilterTab>('all');\n\n protected readonly reviewTabs: { id: ReviewFilterTab; label: string }[] = [\n { id: 'all', label: 'All' },\n { id: 'needs', label: 'Needs review' },\n { id: 'reviewed', label: 'Reviewed' },\n ];\n\n protected reviewLabel = reviewLabel;\n protected reviewTone = reviewBadgeTone;\n protected isNeedsReview = isNeedsReview;\n protected outcomeTone = outcomeBadgeTone;\n protected outcomeLabel = formatOutcomeLabel;\n\n ngOnInit(): void {\n const reviewParam = this.route.snapshot.queryParamMap.get('review');\n if (reviewParam === 'needs' || reviewParam === 'reviewed') {\n this.reviewTab.set(reviewParam);\n }\n this.load();\n }\n\n protected load(): void {\n this.loading.set(true);\n const tab = this.reviewTab();\n this.api\n .getHistory({\n q: this.search() || undefined,\n dir: this.direction() || undefined,\n outcome: this.outcome() || undefined,\n location: this.location() || undefined,\n review: tab === 'all' ? undefined : tab,\n page: this.page(),\n limit: DEFAULT_PAGE_SIZE,\n })\n .subscribe({\n next: ({ items, total, page }) => {\n this.rows.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 protected applyFilters(): void {\n this.page.set(1);\n this.load();\n }\n\n protected setReviewTab(tab: ReviewFilterTab): void {\n this.reviewTab.set(tab);\n this.page.set(1);\n void this.router.navigate([], {\n relativeTo: this.route,\n queryParams: tab === 'all' ? { review: null } : { review: tab },\n queryParamsHandling: 'merge',\n replaceUrl: true,\n });\n this.load();\n }\n\n protected tabBadgeCount(tab: ReviewFilterTab): number | null {\n if (tab !== 'needs' || this.reviewTab() !== 'needs') return null;\n const count = this.total();\n return count > 0 ? count : null;\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 protected markReviewed(row: HistoryCall, event: Event): void {\n event.preventDefault();\n event.stopPropagation();\n if (!isNeedsReview(row.review) || this.reviewingId()) return;\n\n this.reviewingId.set(row.id);\n this.rows.update((list) =>\n list.map((r) => (r.id === row.id ? { ...r, review: 'reviewed' as const } : r)),\n );\n\n this.reviewService.markReviewed(row.id).subscribe({\n next: () => {\n this.reviewingId.set(null);\n this.toast.show('Call marked as reviewed', 'success');\n if (this.reviewTab() === 'needs') this.load();\n },\n error: () => {\n this.reviewingId.set(null);\n this.rows.update((list) =>\n list.map((r) => (r.id === row.id ? { ...r, review: 'needs' as const } : r)),\n );\n this.toast.show('Failed to mark call as reviewed', 'error');\n },\n });\n }\n\n protected formatDuration(sec: number): string {\n const m = Math.floor(sec / 60);\n const s = sec % 60;\n return `${m}:${s.toString().padStart(2, '0')}`;\n }\n\n protected openCallDetail(id: string): void {\n void this.router.navigate(['../history', id], { relativeTo: this.route });\n }\n\n protected exportCsv(): void {\n const header = ['id', 'name', 'phone', 'direction', 'outcome', 'duration', 'started'];\n const csvRows = this.rows().map((r) => [\n r.id,\n r.name,\n r.phone,\n r.direction,\n r.outcome,\n String(r.duration),\n String(r.started),\n ]);\n const csv = [header, ...csvRows]\n .map((row) => row.map((cell) => `\"${String(cell).replace(/\"/g, '\"\"')}\"`).join(','))\n .join('\\n');\n const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `call-history-page-${this.page()}.csv`;\n a.click();\n URL.revokeObjectURL(url);\n }\n}\n","<div class=\"eds-page call-history-page\">\n\n <header class=\"call-history-page__header\">\n\n <div>\n\n <h1 class=\"eds-h1\">Call history</h1>\n\n <p class=\"eds-body-sm\">Search and review completed conversations</p>\n\n </div>\n\n <eds-button variant=\"secondary\" (click)=\"exportCsv()\">\n\n <eds-icon name=\"download\" [size]=\"18\" /> Export CSV\n\n </eds-button>\n\n </header>\n\n\n\n <nav class=\"call-history-page__review-tabs\" aria-label=\"Review status\">\n\n @for (t of reviewTabs; track t.id) {\n\n <button\n\n type=\"button\"\n\n class=\"call-history-page__review-tab\"\n\n [class.call-history-page__review-tab--active]=\"reviewTab() === t.id\"\n\n (click)=\"setReviewTab(t.id)\"\n\n >\n\n {{ t.label }}\n\n @if (tabBadgeCount(t.id); as count) {\n\n <span class=\"call-history-page__tab-badge\">{{ count }}</span>\n\n }\n\n </button>\n\n }\n\n </nav>\n\n\n\n <div class=\"eds-card call-history-page__filters\">\n\n <input\n\n type=\"search\"\n\n class=\"call-history-page__search\"\n\n placeholder=\"Search name, phone, reason…\"\n\n [ngModel]=\"search()\"\n\n (ngModelChange)=\"search.set($event)\"\n\n (keyup.enter)=\"applyFilters()\"\n\n />\n\n <select class=\"call-history-page__select\" [ngModel]=\"direction()\" (ngModelChange)=\"direction.set($event)\">\n\n <option value=\"\">All directions</option>\n\n <option value=\"inbound\">Inbound</option>\n\n <option value=\"outbound\">Outbound</option>\n\n </select>\n\n <input\n\n type=\"text\"\n\n class=\"call-history-page__select\"\n\n placeholder=\"Outcome\"\n\n [ngModel]=\"outcome()\"\n\n (ngModelChange)=\"outcome.set($event)\"\n\n />\n\n <input\n\n type=\"text\"\n\n class=\"call-history-page__select\"\n\n placeholder=\"Location\"\n\n [ngModel]=\"location()\"\n\n (ngModelChange)=\"location.set($event)\"\n\n />\n\n <eds-button variant=\"primary\" (click)=\"applyFilters()\">Apply</eds-button>\n\n </div>\n\n\n\n @if (loading()) {\n\n <p class=\"eds-body-sm\">Loading history…</p>\n\n } @else {\n\n <div class=\"eds-card call-history-page__table-wrap\">\n\n <table class=\"eds-table\">\n\n <thead>\n\n <tr>\n\n <th>Caller</th>\n\n <th>Direction</th>\n\n <th>Reason</th>\n\n <th>Status</th>\n\n <th>Duration</th>\n\n <th>Date & time</th>\n\n <th>Review</th>\n\n <th></th>\n\n </tr>\n\n </thead>\n\n <tbody>\n\n @for (row of rows(); track row.id) {\n\n <tr\n class=\"call-history-page__row\"\n [class.call-history-page__row--needs-review]=\"isNeedsReview(row.review)\"\n tabindex=\"0\"\n role=\"link\"\n [attr.aria-label]=\"'View call details for ' + row.name\"\n (click)=\"openCallDetail(row.id)\"\n (keyup.enter)=\"openCallDetail(row.id)\"\n >\n <td>\n <div class=\"call-history-page__caller\">\n <eds-avatar [initials]=\"row.initials\" />\n <span>{{ row.name }}</span>\n </div>\n </td>\n\n <td>{{ row.direction }}</td>\n\n <td>{{ row.reason }}</td>\n\n <td>\n\n <eds-badge [tone]=\"outcomeTone(row.outcome)\">\n\n {{ outcomeLabel(row.outcome) }}\n\n </eds-badge>\n\n </td>\n\n <td class=\"eds-mono-sm\">{{ row.durationLabel ?? formatDuration(row.duration) }}</td>\n\n <td class=\"eds-mono-sm\">\n {{ row.started ? (row.started | date: 'medium') : (row.whenLabel ?? '—') }}\n </td>\n\n <td>\n\n <eds-badge [tone]=\"reviewTone(row.review)\">\n\n {{ reviewLabel(row.review) }}\n\n </eds-badge>\n\n </td>\n\n <td class=\"call-history-page__actions\">\n\n @if (isNeedsReview(row.review)) {\n\n <button\n\n type=\"button\"\n\n class=\"call-history-page__mark-btn\"\n\n title=\"Mark as reviewed\"\n\n [disabled]=\"reviewingId() === row.id\"\n\n (click)=\"markReviewed(row, $event)\"\n\n >\n\n <eds-icon name=\"check_circle\" [size]=\"18\" />\n\n </button>\n\n }\n\n </td>\n\n </tr>\n\n } @empty {\n\n <tr>\n\n <td colspan=\"8\" class=\"eds-body-sm\">No calls match this filter.</td>\n\n </tr>\n\n }\n\n </tbody>\n\n </table>\n\n </div>\n\n }\n\n\n <footer class=\"call-history-page__pager\">\n <span class=\"eds-body-sm\">\n Page {{ page() }} of {{ totalPages() }} · {{ total() }} total\n </span>\n <div class=\"call-history-page__pager-actions\">\n <eds-button variant=\"secondary\" [disabled]=\"page() <= 1\" (click)=\"prevPage()\">Previous</eds-button>\n <eds-button variant=\"secondary\" [disabled]=\"page() >= totalPages()\" (click)=\"nextPage()\">Next</eds-button>\n </div>\n </footer>\n\n</div>\n\n\n"],"names":["calcTotalPages"],"mappings":";;;;;;;;;;;MAoCa,wBAAwB,CAAA;AAdrC,IAAA,WAAA,GAAA;AAemB,QAAA,IAAA,CAAA,GAAG,GAAG,MAAM,CAAC,wBAAwB,CAAC;AACtC,QAAA,IAAA,CAAA,aAAa,GAAG,MAAM,CAAC,iBAAiB,CAAC;AACzC,QAAA,IAAA,CAAA,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC;AAC5B,QAAA,IAAA,CAAA,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC;AAC9B,QAAA,IAAA,CAAA,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAErB,IAAA,CAAA,QAAQ,GAAG,iBAAiB;AAC5B,QAAA,IAAA,CAAA,IAAI,GAAG,MAAM,CAAgB,EAAE,CAAC;AAChC,QAAA,IAAA,CAAA,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC;AACjB,QAAA,IAAA,CAAA,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC;AAChB,QAAA,IAAA,CAAA,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC;AACtB,QAAA,IAAA,CAAA,WAAW,GAAG,MAAM,CAAgB,IAAI,CAAC;AACzC,QAAA,IAAA,CAAA,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;AACnB,QAAA,IAAA,CAAA,SAAS,GAAG,MAAM,CAAC,EAAE,CAAC;AACtB,QAAA,IAAA,CAAA,OAAO,GAAG,MAAM,CAAC,EAAE,CAAC;AACpB,QAAA,IAAA,CAAA,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC;AACrB,QAAA,IAAA,CAAA,SAAS,GAAG,MAAM,CAAkB,KAAK,CAAC;AAE1C,QAAA,IAAA,CAAA,UAAU,GAA6C;AACxE,YAAA,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE;AAC3B,YAAA,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE;AACtC,YAAA,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE;SACtC;QAES,IAAA,CAAA,WAAW,GAAG,WAAW;QACzB,IAAA,CAAA,UAAU,GAAG,eAAe;QAC5B,IAAA,CAAA,aAAa,GAAG,aAAa;QAC7B,IAAA,CAAA,WAAW,GAAG,gBAAgB;QAC9B,IAAA,CAAA,YAAY,GAAG,kBAAkB;AAmI5C,IAAA;IAjIC,QAAQ,GAAA;AACN,QAAA,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnE,IAAI,WAAW,KAAK,OAAO,IAAI,WAAW,KAAK,UAAU,EAAE;AACzD,YAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC;QACjC;QACA,IAAI,CAAC,IAAI,EAAE;IACb;IAEU,IAAI,GAAA;AACZ,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;AACtB,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,EAAE;AAC5B,QAAA,IAAI,CAAC;AACF,aAAA,UAAU,CAAC;AACV,YAAA,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,SAAS;AAC7B,YAAA,GAAG,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,SAAS;AAClC,YAAA,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,SAAS;AACpC,YAAA,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,SAAS;YACtC,MAAM,EAAE,GAAG,KAAK,KAAK,GAAG,SAAS,GAAG,GAAG;AACvC,YAAA,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;AACjB,YAAA,KAAK,EAAE,iBAAiB;SACzB;AACA,aAAA,SAAS,CAAC;YACT,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAI;AAC/B,gBAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;AACpB,gBAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;AACrB,gBAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;AACnB,gBAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;YACzB,CAAC;YACD,KAAK,EAAE,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;AACrC,SAAA,CAAC;IACN;IAEU,YAAY,GAAA;AACpB,QAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAChB,IAAI,CAAC,IAAI,EAAE;IACb;AAEU,IAAA,YAAY,CAAC,GAAoB,EAAA;AACzC,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;AACvB,QAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAChB,QAAA,KAAK,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE;YAC5B,UAAU,EAAE,IAAI,CAAC,KAAK;AACtB,YAAA,WAAW,EAAE,GAAG,KAAK,KAAK,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE;AAC/D,YAAA,mBAAmB,EAAE,OAAO;AAC5B,YAAA,UAAU,EAAE,IAAI;AACjB,SAAA,CAAC;QACF,IAAI,CAAC,IAAI,EAAE;IACb;AAEU,IAAA,aAAa,CAAC,GAAoB,EAAA;QAC1C,IAAI,GAAG,KAAK,OAAO,IAAI,IAAI,CAAC,SAAS,EAAE,KAAK,OAAO;AAAE,YAAA,OAAO,IAAI;AAChE,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE;QAC1B,OAAO,KAAK,GAAG,CAAC,GAAG,KAAK,GAAG,IAAI;IACjC;IAEU,UAAU,GAAA;QAClB,OAAOA,UAAc,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,iBAAiB,CAAC;IACxD;IAEU,QAAQ,GAAA;AAChB,QAAA,IAAI,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;YAAE;AACtB,QAAA,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,EAAE;IACb;IAEU,QAAQ,GAAA;QAChB,IAAI,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,UAAU,EAAE;YAAE;AACtC,QAAA,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,EAAE;IACb;IAEU,YAAY,CAAC,GAAgB,EAAE,KAAY,EAAA;QACnD,KAAK,CAAC,cAAc,EAAE;QACtB,KAAK,CAAC,eAAe,EAAE;QACvB,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE;YAAE;QAEtD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,KACpB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,UAAmB,EAAE,GAAG,CAAC,CAAC,CAAC,CAC/E;QAED,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;YAChD,IAAI,EAAE,MAAK;AACT,gBAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;gBAC1B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,yBAAyB,EAAE,SAAS,CAAC;AACrD,gBAAA,IAAI,IAAI,CAAC,SAAS,EAAE,KAAK,OAAO;oBAAE,IAAI,CAAC,IAAI,EAAE;YAC/C,CAAC;YACD,KAAK,EAAE,MAAK;AACV,gBAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;gBAC1B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,KACpB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,OAAgB,EAAE,GAAG,CAAC,CAAC,CAAC,CAC5E;gBACD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,iCAAiC,EAAE,OAAO,CAAC;YAC7D,CAAC;AACF,SAAA,CAAC;IACJ;AAEU,IAAA,cAAc,CAAC,GAAW,EAAA;QAClC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC;AAC9B,QAAA,MAAM,CAAC,GAAG,GAAG,GAAG,EAAE;AAClB,QAAA,OAAO,CAAA,EAAG,CAAC,CAAA,CAAA,EAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;IAChD;AAEU,IAAA,cAAc,CAAC,EAAU,EAAA;QACjC,KAAK,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IAC3E;IAEU,SAAS,GAAA;AACjB,QAAA,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC;AACrF,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK;AACrC,YAAA,CAAC,CAAC,EAAE;AACJ,YAAA,CAAC,CAAC,IAAI;AACN,YAAA,CAAC,CAAC,KAAK;AACP,YAAA,CAAC,CAAC,SAAS;AACX,YAAA,CAAC,CAAC,OAAO;AACT,YAAA,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;AAClB,YAAA,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;AAClB,SAAA,CAAC;AACF,QAAA,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,OAAO;AAC5B,aAAA,GAAG,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAA,CAAA,EAAI,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA,CAAA,CAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;aACjF,IAAI,CAAC,IAAI,CAAC;AACb,QAAA,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,wBAAwB,EAAE,CAAC;QAChE,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC;QACrC,MAAM,CAAC,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC;AACrC,QAAA,CAAC,CAAC,IAAI,GAAG,GAAG;QACZ,CAAC,CAAC,QAAQ,GAAG,CAAA,kBAAA,EAAqB,IAAI,CAAC,IAAI,EAAE,CAAA,IAAA,CAAM;QACnD,CAAC,CAAC,KAAK,EAAE;AACT,QAAA,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC;IAC1B;+GA/JW,wBAAwB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAAxB,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,wBAAwB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,uBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECpCrC,u0KAoQA,EAAA,MAAA,EAAA,CAAA,wtEAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,ED3OI,QAAQ,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EACR,WAAW,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,cAAA,EAAA,QAAA,EAAA,QAAA,EAAA,MAAA,EAAA,CAAA,SAAA,EAAA,OAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,uBAAA,EAAA,QAAA,EAAA,QAAA,EAAA,MAAA,EAAA,CAAA,SAAA,EAAA,OAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,oBAAA,EAAA,QAAA,EAAA,8MAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,0BAAA,EAAA,QAAA,EAAA,6GAAA,EAAA,MAAA,EAAA,CAAA,aAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,2CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,OAAA,EAAA,QAAA,EAAA,qDAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,UAAA,EAAA,SAAA,EAAA,gBAAA,CAAA,EAAA,OAAA,EAAA,CAAA,eAAA,CAAA,EAAA,QAAA,EAAA,CAAA,SAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACX,kBAAkB,EAAA,QAAA,EAAA,YAAA,EAAA,MAAA,EAAA,CAAA,UAAA,EAAA,MAAA,EAAA,UAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAClB,iBAAiB,EAAA,QAAA,EAAA,WAAA,EAAA,MAAA,EAAA,CAAA,MAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACjB,kBAAkB,gGAClB,gBAAgB,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,MAAA,EAAA,MAAA,EAAA,QAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;4FAMP,wBAAwB,EAAA,UAAA,EAAA,CAAA;kBAdpC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,uBAAuB,EAAA,OAAA,EACxB;wBACP,QAAQ;wBACR,WAAW;wBACX,kBAAkB;wBAClB,iBAAiB;wBACjB,kBAAkB;wBAClB,gBAAgB;qBACjB,EAAA,eAAA,EAGgB,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,u0KAAA,EAAA,MAAA,EAAA,CAAA,wtEAAA,CAAA,EAAA;;;;;"}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/** Known history outcomes from GET /calls/history (HistoryCallDto.outcome). */
|
|
2
|
-
const HISTORY_OUTCOMES = [
|
|
3
|
-
'completed',
|
|
4
|
-
'failed',
|
|
5
|
-
'transferred',
|
|
6
|
-
'escalated',
|
|
7
|
-
];
|
|
8
|
-
function normalizeHistoryOutcome(outcome) {
|
|
9
|
-
const key = outcome.trim().toLowerCase();
|
|
10
|
-
if (HISTORY_OUTCOMES.includes(key)) {
|
|
11
|
-
return key;
|
|
12
|
-
}
|
|
13
|
-
return 'custom';
|
|
14
|
-
}
|
|
15
|
-
/** Badge tone for history table / detail — separate from lifecycle or live UI status. */
|
|
16
|
-
function outcomeBadgeTone(outcome) {
|
|
17
|
-
switch (normalizeHistoryOutcome(outcome)) {
|
|
18
|
-
case 'completed':
|
|
19
|
-
return 'pos';
|
|
20
|
-
case 'failed':
|
|
21
|
-
return 'neg';
|
|
22
|
-
case 'transferred':
|
|
23
|
-
return 'violet';
|
|
24
|
-
case 'escalated':
|
|
25
|
-
return 'neg';
|
|
26
|
-
default: {
|
|
27
|
-
const key = outcome.trim().toLowerCase();
|
|
28
|
-
if (key.includes('transfer'))
|
|
29
|
-
return 'violet';
|
|
30
|
-
if (key.includes('fail') || key.includes('escalat') || key.includes('abandon'))
|
|
31
|
-
return 'neg';
|
|
32
|
-
if (key.includes('voicemail'))
|
|
33
|
-
return 'gray';
|
|
34
|
-
if (key.includes('complete') || key.includes('book') || key.includes('task'))
|
|
35
|
-
return 'pos';
|
|
36
|
-
return 'brand';
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
function formatOutcomeLabel(outcome) {
|
|
41
|
-
const trimmed = outcome.trim();
|
|
42
|
-
if (!trimmed)
|
|
43
|
-
return 'Unknown';
|
|
44
|
-
const known = normalizeHistoryOutcome(trimmed);
|
|
45
|
-
if (known === 'custom')
|
|
46
|
-
return trimmed;
|
|
47
|
-
return known.charAt(0).toUpperCase() + known.slice(1);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function reviewLabel(status) {
|
|
51
|
-
return status === 'reviewed' ? 'Reviewed' : 'Pending review';
|
|
52
|
-
}
|
|
53
|
-
function reviewBadgeTone(status) {
|
|
54
|
-
return status === 'reviewed' ? 'pos' : 'notice';
|
|
55
|
-
}
|
|
56
|
-
function isNeedsReview(status) {
|
|
57
|
-
return status === 'needs';
|
|
58
|
-
}
|
|
59
|
-
/** Normalize legacy or API values to CallReviewStatus. */
|
|
60
|
-
function normalizeReviewStatus(value) {
|
|
61
|
-
return value === 'reviewed' ? 'reviewed' : 'needs';
|
|
62
|
-
}
|
|
63
|
-
function matchesReviewFilter(status, tab) {
|
|
64
|
-
if (tab === 'all')
|
|
65
|
-
return true;
|
|
66
|
-
return status === tab;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export { reviewBadgeTone as a, formatOutcomeLabel as f, isNeedsReview as i, outcomeBadgeTone as o, reviewLabel as r };
|
|
70
|
-
//# sourceMappingURL=edvak99-voice-receptionist-widget-call-review.util-BgsBCKgZ.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"edvak99-voice-receptionist-widget-call-review.util-BgsBCKgZ.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/utils/call-outcome.util.ts","../../../projects/voice-receptionist-widget/src/lib/utils/call-review.util.ts"],"sourcesContent":["import { BadgeTone } from '../ui/eds-badge.component';\n\n/** Known history outcomes from GET /calls/history (HistoryCallDto.outcome). */\nexport const HISTORY_OUTCOMES = [\n 'completed',\n 'failed',\n 'transferred',\n 'escalated',\n] as const;\n\nexport type HistoryOutcome = (typeof HISTORY_OUTCOMES)[number] | 'custom';\n\nexport function normalizeHistoryOutcome(outcome: string): HistoryOutcome {\n const key = outcome.trim().toLowerCase();\n if ((HISTORY_OUTCOMES as readonly string[]).includes(key)) {\n return key as HistoryOutcome;\n }\n return 'custom';\n}\n\n/** Badge tone for history table / detail — separate from lifecycle or live UI status. */\nexport function outcomeBadgeTone(outcome: string): BadgeTone {\n switch (normalizeHistoryOutcome(outcome)) {\n case 'completed':\n return 'pos';\n case 'failed':\n return 'neg';\n case 'transferred':\n return 'violet';\n case 'escalated':\n return 'neg';\n default: {\n const key = outcome.trim().toLowerCase();\n if (key.includes('transfer')) return 'violet';\n if (key.includes('fail') || key.includes('escalat') || key.includes('abandon')) return 'neg';\n if (key.includes('voicemail')) return 'gray';\n if (key.includes('complete') || key.includes('book') || key.includes('task')) return 'pos';\n return 'brand';\n }\n }\n}\n\nexport function formatOutcomeLabel(outcome: string): string {\n const trimmed = outcome.trim();\n if (!trimmed) return 'Unknown';\n const known = normalizeHistoryOutcome(trimmed);\n if (known === 'custom') return trimmed;\n return known.charAt(0).toUpperCase() + known.slice(1);\n}\n","export type CallReviewStatus = 'needs' | 'reviewed';\n\nexport function reviewLabel(status: CallReviewStatus): string {\n return status === 'reviewed' ? 'Reviewed' : 'Pending review';\n}\n\nexport function reviewBadgeTone(status: CallReviewStatus): 'pos' | 'notice' {\n return status === 'reviewed' ? 'pos' : 'notice';\n}\n\nexport function isNeedsReview(status: CallReviewStatus): boolean {\n return status === 'needs';\n}\n\n/** Normalize legacy or API values to CallReviewStatus. */\nexport function normalizeReviewStatus(value: string | undefined | null): CallReviewStatus {\n return value === 'reviewed' ? 'reviewed' : 'needs';\n}\n\nexport type ReviewFilterTab = 'all' | 'needs' | 'reviewed';\n\nexport function matchesReviewFilter(\n status: CallReviewStatus,\n tab: ReviewFilterTab,\n): boolean {\n if (tab === 'all') return true;\n return status === tab;\n}\n"],"names":[],"mappings":"AAEA;AACO,MAAM,gBAAgB,GAAG;IAC9B,WAAW;IACX,QAAQ;IACR,aAAa;IACb,WAAW;CACH;AAIJ,SAAU,uBAAuB,CAAC,OAAe,EAAA;IACrD,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;AACxC,IAAA,IAAK,gBAAsC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;AACzD,QAAA,OAAO,GAAqB;IAC9B;AACA,IAAA,OAAO,QAAQ;AACjB;AAEA;AACM,SAAU,gBAAgB,CAAC,OAAe,EAAA;AAC9C,IAAA,QAAQ,uBAAuB,CAAC,OAAO,CAAC;AACtC,QAAA,KAAK,WAAW;AACd,YAAA,OAAO,KAAK;AACd,QAAA,KAAK,QAAQ;AACX,YAAA,OAAO,KAAK;AACd,QAAA,KAAK,aAAa;AAChB,YAAA,OAAO,QAAQ;AACjB,QAAA,KAAK,WAAW;AACd,YAAA,OAAO,KAAK;QACd,SAAS;YACP,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;AACxC,YAAA,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC;AAAE,gBAAA,OAAO,QAAQ;AAC7C,YAAA,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC;AAAE,gBAAA,OAAO,KAAK;AAC5F,YAAA,IAAI,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC;AAAE,gBAAA,OAAO,MAAM;AAC5C,YAAA,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC;AAAE,gBAAA,OAAO,KAAK;AAC1F,YAAA,OAAO,OAAO;QAChB;;AAEJ;AAEM,SAAU,kBAAkB,CAAC,OAAe,EAAA;AAChD,IAAA,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE;AAC9B,IAAA,IAAI,CAAC,OAAO;AAAE,QAAA,OAAO,SAAS;AAC9B,IAAA,MAAM,KAAK,GAAG,uBAAuB,CAAC,OAAO,CAAC;IAC9C,IAAI,KAAK,KAAK,QAAQ;AAAE,QAAA,OAAO,OAAO;AACtC,IAAA,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;AACvD;;AC9CM,SAAU,WAAW,CAAC,MAAwB,EAAA;IAClD,OAAO,MAAM,KAAK,UAAU,GAAG,UAAU,GAAG,gBAAgB;AAC9D;AAEM,SAAU,eAAe,CAAC,MAAwB,EAAA;IACtD,OAAO,MAAM,KAAK,UAAU,GAAG,KAAK,GAAG,QAAQ;AACjD;AAEM,SAAU,aAAa,CAAC,MAAwB,EAAA;IACpD,OAAO,MAAM,KAAK,OAAO;AAC3B;AAEA;AACM,SAAU,qBAAqB,CAAC,KAAgC,EAAA;IACpE,OAAO,KAAK,KAAK,UAAU,GAAG,UAAU,GAAG,OAAO;AACpD;AAIM,SAAU,mBAAmB,CACjC,MAAwB,EACxB,GAAoB,EAAA;IAEpB,IAAI,GAAG,KAAK,KAAK;AAAE,QAAA,OAAO,IAAI;IAC9B,OAAO,MAAM,KAAK,GAAG;AACvB;;;;"}
|