@edvak99/voice-receptionist-widget 1.1.5 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +37 -0
  2. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-4UrqnTE3.mjs +306 -0
  3. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-4UrqnTE3.mjs.map +1 -0
  4. package/fesm2022/{edvak99-voice-receptionist-widget-ai-receptionist-api.service-srM0jC46.mjs → edvak99-voice-receptionist-widget-ai-receptionist-api.service-DokIb-9r.mjs} +2 -2
  5. package/fesm2022/{edvak99-voice-receptionist-widget-ai-receptionist-api.service-srM0jC46.mjs.map → edvak99-voice-receptionist-widget-ai-receptionist-api.service-DokIb-9r.mjs.map} +1 -1
  6. package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-DUR5f5nc.mjs +277 -0
  7. package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-DUR5f5nc.mjs.map +1 -0
  8. package/fesm2022/{edvak99-voice-receptionist-widget-call-history-detail-page.component-0rW-PPWM.mjs → edvak99-voice-receptionist-widget-call-history-detail-page.component-A-vx_M3l.mjs} +9 -10
  9. package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-A-vx_M3l.mjs.map +1 -0
  10. package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-gROZVIt-.mjs +193 -0
  11. package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-gROZVIt-.mjs.map +1 -0
  12. package/fesm2022/{edvak99-voice-receptionist-widget-call-recording-player.component-DGg_nI--.mjs → edvak99-voice-receptionist-widget-call-recording-player.component-Pla2UgeZ.mjs} +3 -3
  13. package/fesm2022/{edvak99-voice-receptionist-widget-call-recording-player.component-DGg_nI--.mjs.map → edvak99-voice-receptionist-widget-call-recording-player.component-Pla2UgeZ.mjs.map} +1 -1
  14. package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-j_XV5a8S.mjs → edvak99-voice-receptionist-widget-call-review.service-Di7GLG9k.mjs} +2 -2
  15. package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-j_XV5a8S.mjs.map → edvak99-voice-receptionist-widget-call-review.service-Di7GLG9k.mjs.map} +1 -1
  16. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-E30a8p3d.mjs +439 -0
  17. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-E30a8p3d.mjs.map +1 -0
  18. package/fesm2022/{edvak99-voice-receptionist-widget-dashboard-socket.service-CsfNZeQ4.mjs → edvak99-voice-receptionist-widget-dashboard-socket.service-D2pelzu4.mjs} +3 -3
  19. package/fesm2022/{edvak99-voice-receptionist-widget-dashboard-socket.service-CsfNZeQ4.mjs.map → edvak99-voice-receptionist-widget-dashboard-socket.service-D2pelzu4.mjs.map} +1 -1
  20. package/fesm2022/{edvak99-voice-receptionist-widget-eds-pulse-dot.component-DymAM4YH.mjs → edvak99-voice-receptionist-widget-eds-pulse-dot.component-Ig0Eoq1z.mjs} +4 -4
  21. package/fesm2022/{edvak99-voice-receptionist-widget-eds-pulse-dot.component-DymAM4YH.mjs.map → edvak99-voice-receptionist-widget-eds-pulse-dot.component-Ig0Eoq1z.mjs.map} +1 -1
  22. package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-BDtAQSyF.mjs +81 -0
  23. package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-BDtAQSyF.mjs.map +1 -0
  24. package/fesm2022/{edvak99-voice-receptionist-widget-live-monitor-page.component-1elUtWOw.mjs → edvak99-voice-receptionist-widget-live-monitor-page.component-7e1HY6sB.mjs} +9 -9
  25. package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-7e1HY6sB.mjs.map +1 -0
  26. package/fesm2022/edvak99-voice-receptionist-widget-modal.component-uYApl1KF.mjs +48 -0
  27. package/fesm2022/edvak99-voice-receptionist-widget-modal.component-uYApl1KF.mjs.map +1 -0
  28. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-2GHT8NT9.mjs +267 -0
  29. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-2GHT8NT9.mjs.map +1 -0
  30. package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-BwCz1dlx.mjs +967 -0
  31. package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-BwCz1dlx.mjs.map +1 -0
  32. package/fesm2022/{edvak99-voice-receptionist-widget-tasks-page.component-BGSupnFa.mjs → edvak99-voice-receptionist-widget-tasks-page.component-Mqo1UmfK.mjs} +7 -8
  33. package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-Mqo1UmfK.mjs.map +1 -0
  34. package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-DskeqJwB.mjs +46 -0
  35. package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-DskeqJwB.mjs.map +1 -0
  36. package/fesm2022/edvak99-voice-receptionist-widget.mjs +228 -1
  37. package/fesm2022/edvak99-voice-receptionist-widget.mjs.map +1 -1
  38. package/package.json +1 -1
  39. package/public-api.d.ts +3 -0
  40. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-pQM1L7lO.mjs +0 -307
  41. package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-pQM1L7lO.mjs.map +0 -1
  42. package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-B6O-PZHz.mjs +0 -278
  43. package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-B6O-PZHz.mjs.map +0 -1
  44. package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-0rW-PPWM.mjs.map +0 -1
  45. package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-Cec1ngRf.mjs +0 -194
  46. package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-Cec1ngRf.mjs.map +0 -1
  47. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-DD0tMAah.mjs +0 -440
  48. package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-DD0tMAah.mjs.map +0 -1
  49. package/fesm2022/edvak99-voice-receptionist-widget-eds-button.component-CAfTt7zB.mjs +0 -27
  50. package/fesm2022/edvak99-voice-receptionist-widget-eds-button.component-CAfTt7zB.mjs.map +0 -1
  51. package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-JWqRMt9T.mjs +0 -203
  52. package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-JWqRMt9T.mjs.map +0 -1
  53. package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-D5znr-Sx.mjs +0 -81
  54. package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-D5znr-Sx.mjs.map +0 -1
  55. package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-1elUtWOw.mjs.map +0 -1
  56. package/fesm2022/edvak99-voice-receptionist-widget-modal.component-Cb-FoQ1N.mjs +0 -48
  57. package/fesm2022/edvak99-voice-receptionist-widget-modal.component-Cb-FoQ1N.mjs.map +0 -1
  58. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-CwLxW1W9.mjs +0 -268
  59. package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-CwLxW1W9.mjs.map +0 -1
  60. package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-DAr4wuUk.mjs +0 -968
  61. package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-DAr4wuUk.mjs.map +0 -1
  62. package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-BGSupnFa.mjs.map +0 -1
  63. package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-5iG9eQRi.mjs +0 -47
  64. package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-5iG9eQRi.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edvak99-voice-receptionist-widget-modal.component-uYApl1KF.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/shared/modal.component.ts","../../../projects/voice-receptionist-widget/src/lib/shared/modal.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n EventEmitter,\n HostListener,\n Input,\n Output,\n} from '@angular/core';\n\n/**\n * Lightweight, reusable modal shell.\n *\n * Renders a centered card on top of a dimmed backdrop. Closes on backdrop\n * click, ESC key, or via the close button. Slot the modal body using\n * <ng-content>.\n */\n@Component({\n selector: 'app-modal',\n standalone: true,\n templateUrl: './modal.component.html',\n styleUrl: './modal.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ModalComponent {\n @Input({ required: true }) title!: string;\n @Input() subtitle?: string;\n @Input() maxWidth = '640px';\n @Output() readonly closed = new EventEmitter<void>();\n\n @HostListener('document:keydown.escape')\n onEscape(): void {\n this.closed.emit();\n }\n\n onBackdropClick(event: MouseEvent): void {\n if (event.target === event.currentTarget) {\n this.closed.emit();\n }\n }\n\n close(): void {\n this.closed.emit();\n }\n}\n","<div\n class=\"modal-backdrop fixed inset-0 z-[1000] flex items-center justify-center bg-[rgba(20,30,50,0.42)] p-6 backdrop-blur-[2px]\"\n (click)=\"onBackdropClick($event)\"\n role=\"presentation\"\n>\n <div\n class=\"modal-card flex w-full max-h-[calc(100vh-48px)] flex-col overflow-hidden rounded-[var(--eds-radius-lg)] border border-[var(--eds-color-border)] bg-[var(--eds-color-surface)] shadow-sm\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"title\"\n [style.max-width]=\"maxWidth\"\n >\n <header class=\"flex items-start justify-between gap-4 border-b border-[var(--eds-color-border)] px-6 pb-4 pt-5\">\n <div class=\"flex min-w-0 flex-col gap-1\">\n <h2 class=\"m-0 text-base font-semibold tracking-tight text-[var(--eds-color-text)]\">{{ title }}</h2>\n @if (subtitle) {\n <p class=\"m-0 text-[13px] text-[var(--eds-color-text-muted)]\">{{ subtitle }}</p>\n }\n </div>\n <button\n type=\"button\"\n class=\"inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-[var(--eds-radius-md)] border border-transparent bg-transparent text-[var(--eds-color-text-muted)] transition-colors duration-150 hover:bg-[var(--eds-color-surface-alt)] hover:text-[var(--eds-color-text)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--eds-color-primary)]\"\n (click)=\"close()\"\n aria-label=\"Close dialog\"\n >\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </header>\n\n <div class=\"overflow-y-auto px-6 pb-6 pt-5\">\n <ng-content />\n </div>\n </div>\n</div>\n"],"names":[],"mappings":";;;AASA;;;;;;AAMG;MAQU,cAAc,CAAA;AAP3B,IAAA,WAAA,GAAA;QAUW,IAAA,CAAA,QAAQ,GAAG,OAAO;AACR,QAAA,IAAA,CAAA,MAAM,GAAG,IAAI,YAAY,EAAQ;AAgBrD,IAAA;IAbC,QAAQ,GAAA;AACN,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;IACpB;AAEA,IAAA,eAAe,CAAC,KAAiB,EAAA;QAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,aAAa,EAAE;AACxC,YAAA,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;QACpB;IACF;IAEA,KAAK,GAAA;AACH,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;IACpB;+GAnBW,cAAc,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAAd,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,cAAc,8OCvB3B,k7DAsCA,EAAA,MAAA,EAAA,CAAA,+QAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;4FDfa,cAAc,EAAA,UAAA,EAAA,CAAA;kBAP1B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,WAAW,EAAA,UAAA,EACT,IAAI,EAAA,eAAA,EAGC,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,k7DAAA,EAAA,MAAA,EAAA,CAAA,+QAAA,CAAA,EAAA;8BAGpB,KAAK,EAAA,CAAA;sBAA/B,KAAK;uBAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAChB,QAAQ,EAAA,CAAA;sBAAhB;gBACQ,QAAQ,EAAA,CAAA;sBAAhB;gBACkB,MAAM,EAAA,CAAA;sBAAxB;gBAGD,QAAQ,EAAA,CAAA;sBADP,YAAY;uBAAC,yBAAyB;;;;;"}
@@ -0,0 +1,267 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, EventEmitter, signal, ViewChild, Output, Input, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { RouterLink } from '@angular/router';
4
+ import { A as AiReceptionistApiService, p as DEFAULT_PLACEHOLDERS } from './edvak99-voice-receptionist-widget-ai-receptionist-api.service-DokIb-9r.mjs';
5
+ import { R as REQUIRED_FIELD_OPTIONS, d as renderOpeningPreview, e as formatRequiredFields } from './edvak99-voice-receptionist-widget-outbound-purpose.util-Bdh4QfFe.mjs';
6
+ import * as i1 from '@angular/forms';
7
+ import { FormsModule } from '@angular/forms';
8
+ import { EdsButtonComponent, EdsBadgeComponent, EdsIconComponent } from './edvak99-voice-receptionist-widget.mjs';
9
+ import { M as ModalComponent } from './edvak99-voice-receptionist-widget-modal.component-uYApl1KF.mjs';
10
+
11
+ class OutboundPurposeFormModalComponent {
12
+ constructor() {
13
+ this.api = inject(AiReceptionistApiService);
14
+ this.open = false;
15
+ this.purpose = null;
16
+ this.closed = new EventEmitter();
17
+ this.saved = new EventEmitter();
18
+ this.fieldOptions = REQUIRED_FIELD_OPTIONS;
19
+ this.saving = signal(false);
20
+ this.error = signal(null);
21
+ this.label = '';
22
+ this.description = '';
23
+ this.sampleOpening = '';
24
+ this.openingTemplate = '';
25
+ this.agentContextHint = '';
26
+ this.requiredFields = new Set();
27
+ this.allowsCustomScript = false;
28
+ this.sortOrder = 1;
29
+ this.isActive = true;
30
+ this.placeholders = DEFAULT_PLACEHOLDERS;
31
+ }
32
+ get isEdit() {
33
+ return !!this.purpose;
34
+ }
35
+ get previewText() {
36
+ return renderOpeningPreview(this.sampleOpening, this.openingTemplate, this.placeholders);
37
+ }
38
+ ngOnChanges(changes) {
39
+ if (changes['open']?.currentValue || changes['purpose']) {
40
+ this.resetForm();
41
+ }
42
+ }
43
+ close() {
44
+ this.closed.emit();
45
+ }
46
+ toggleRequired(field) {
47
+ const next = new Set(this.requiredFields);
48
+ if (next.has(field))
49
+ next.delete(field);
50
+ else
51
+ next.add(field);
52
+ this.requiredFields = next;
53
+ }
54
+ isRequiredSelected(field) {
55
+ return this.requiredFields.has(field);
56
+ }
57
+ insertPlaceholder(token) {
58
+ const el = this.templateInput?.nativeElement;
59
+ if (!el) {
60
+ this.openingTemplate += token;
61
+ return;
62
+ }
63
+ const start = el.selectionStart ?? this.openingTemplate.length;
64
+ const end = el.selectionEnd ?? start;
65
+ this.openingTemplate =
66
+ this.openingTemplate.slice(0, start) + token + this.openingTemplate.slice(end);
67
+ const pos = start + token.length;
68
+ requestAnimationFrame(() => {
69
+ el.focus();
70
+ el.setSelectionRange(pos, pos);
71
+ });
72
+ }
73
+ submit() {
74
+ this.error.set(null);
75
+ const trimmedLabel = this.label.trim();
76
+ const trimmedTemplate = this.openingTemplate.trim();
77
+ if (!trimmedLabel) {
78
+ this.error.set('Label is required.');
79
+ return;
80
+ }
81
+ if (!trimmedTemplate) {
82
+ this.error.set('Opening template is required.');
83
+ return;
84
+ }
85
+ if (this.sampleOpening.length > 500) {
86
+ this.error.set('Sample opening must be 500 characters or fewer.');
87
+ return;
88
+ }
89
+ this.saving.set(true);
90
+ const fields = [...this.requiredFields];
91
+ if (this.isEdit && this.purpose) {
92
+ const body = {
93
+ label: trimmedLabel,
94
+ description: this.description.trim(),
95
+ sampleOpening: this.sampleOpening.trim(),
96
+ openingTemplate: trimmedTemplate,
97
+ agentContextHint: this.agentContextHint.trim() || null,
98
+ requiredFields: fields,
99
+ allowsCustomScript: this.allowsCustomScript,
100
+ sortOrder: this.sortOrder,
101
+ isActive: this.isActive,
102
+ };
103
+ this.api.updateOutboundPurpose(this.purpose.purposeId, body).subscribe({
104
+ next: (updated) => {
105
+ this.saving.set(false);
106
+ this.saved.emit(updated);
107
+ this.close();
108
+ },
109
+ error: () => {
110
+ this.saving.set(false);
111
+ this.error.set('Failed to save purpose template.');
112
+ },
113
+ });
114
+ return;
115
+ }
116
+ const body = {
117
+ label: trimmedLabel,
118
+ description: this.description.trim(),
119
+ sampleOpening: this.sampleOpening.trim(),
120
+ openingTemplate: trimmedTemplate,
121
+ agentContextHint: this.agentContextHint.trim() || null,
122
+ requiredFields: fields,
123
+ allowsCustomScript: this.allowsCustomScript,
124
+ sortOrder: this.sortOrder,
125
+ };
126
+ this.api.createOutboundPurpose(body).subscribe({
127
+ next: (created) => {
128
+ this.saving.set(false);
129
+ this.saved.emit(created);
130
+ this.close();
131
+ },
132
+ error: () => {
133
+ this.saving.set(false);
134
+ this.error.set('Failed to create purpose template.');
135
+ },
136
+ });
137
+ }
138
+ resetForm() {
139
+ this.error.set(null);
140
+ this.saving.set(false);
141
+ if (this.purpose) {
142
+ this.label = this.purpose.label;
143
+ this.description = this.purpose.description;
144
+ this.sampleOpening = this.purpose.sampleOpening;
145
+ this.openingTemplate = this.purpose.openingTemplate;
146
+ this.agentContextHint = this.purpose.agentContextHint ?? '';
147
+ this.requiredFields = new Set(this.purpose.requiredFields);
148
+ this.allowsCustomScript = this.purpose.allowsCustomScript;
149
+ this.sortOrder = this.purpose.sortOrder;
150
+ this.isActive = this.purpose.isActive;
151
+ this.placeholders = this.purpose.placeholders?.length
152
+ ? [...this.purpose.placeholders]
153
+ : [...DEFAULT_PLACEHOLDERS];
154
+ }
155
+ else {
156
+ this.label = '';
157
+ this.description = '';
158
+ this.sampleOpening = '';
159
+ this.openingTemplate = 'Hi {{patientName}}, this is {{practiceName}}.';
160
+ this.agentContextHint = '';
161
+ this.requiredFields = new Set();
162
+ this.allowsCustomScript = false;
163
+ this.sortOrder = 1;
164
+ this.isActive = true;
165
+ this.placeholders = [...DEFAULT_PLACEHOLDERS];
166
+ }
167
+ }
168
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: OutboundPurposeFormModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
169
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: OutboundPurposeFormModalComponent, isStandalone: true, selector: "eds-outbound-purpose-form-modal", inputs: { open: "open", purpose: "purpose" }, outputs: { closed: "closed", saved: "saved" }, viewQueries: [{ propertyName: "templateInput", first: true, predicate: ["templateInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (open) {\n <app-modal\n [title]=\"isEdit ? 'Edit outbound purpose' : 'New outbound purpose'\"\n subtitle=\"Configure dialer fields and AI opening script\"\n maxWidth=\"720px\"\n (closed)=\"close()\"\n >\n <form class=\"flex flex-col gap-4\" (ngSubmit)=\"submit()\" novalidate autocomplete=\"off\">\n <div class=\"grid grid-cols-2 gap-3\">\n <label class=\"col-span-full flex flex-col gap-2\">\n <span class=\"eds-label\">Label <span class=\"text-[#dc2626]\">*</span></span>\n <input type=\"text\" class=\"purpose-form__input rounded-lg border px-3 py-2 font-[inherit]\" [(ngModel)]=\"label\" name=\"label\" required autocomplete=\"off\" />\n </label>\n\n <label class=\"col-span-full flex flex-col gap-2\">\n <span class=\"eds-label\">Description</span>\n <input type=\"text\" class=\"purpose-form__input rounded-lg border px-3 py-2 font-[inherit]\" [(ngModel)]=\"description\" name=\"description\" autocomplete=\"off\" />\n </label>\n\n <label class=\"col-span-full flex flex-col gap-2\">\n <span class=\"eds-label\">Sample opening (plain preview, max 500)</span>\n <textarea\n class=\"purpose-form__textarea min-h-[72px] resize-y rounded-lg border px-3 py-2 font-[inherit]\"\n rows=\"3\"\n maxlength=\"500\"\n [(ngModel)]=\"sampleOpening\"\n name=\"sampleOpening\" autocomplete=\"off\"></textarea>\n <span class=\"purpose-form__hint text-xs\">{{ sampleOpening.length }}/500</span>\n </label>\n\n <div class=\"col-span-full flex flex-col gap-2\">\n <span class=\"eds-label\">Opening template <span class=\"text-[#dc2626]\">*</span></span>\n <div class=\"mb-2 flex flex-wrap gap-2\">\n @for (chip of placeholders; track chip.token) {\n <button type=\"button\" class=\"purpose-form__chip cursor-pointer rounded-full border px-3 py-1 text-xs\" (click)=\"insertPlaceholder(chip.token)\">\n {{ chip.label }}\n </button>\n }\n </div>\n <textarea\n #templateInput\n class=\"purpose-form__textarea min-h-[72px] resize-y rounded-lg border px-3 py-2 font-[inherit]\"\n rows=\"4\"\n [(ngModel)]=\"openingTemplate\"\n name=\"openingTemplate\"\n required autocomplete=\"off\"></textarea>\n </div>\n\n <label class=\"col-span-full flex flex-col gap-2\">\n <span class=\"eds-label\">Agent context hint</span>\n <textarea\n class=\"purpose-form__textarea min-h-[72px] resize-y rounded-lg border px-3 py-2 font-[inherit]\"\n rows=\"2\"\n [(ngModel)]=\"agentContextHint\"\n name=\"agentContextHint\"\n placeholder=\"Optional guidance for the AI after the opening\" autocomplete=\"off\"></textarea>\n </label>\n\n <fieldset class=\"col-span-full flex flex-col gap-2\">\n <legend class=\"eds-label\">Required fields</legend>\n <div class=\"grid grid-cols-2 gap-2\">\n @for (opt of fieldOptions; track opt.value) {\n <label class=\"flex items-center gap-2 text-[13px]\">\n <input\n type=\"checkbox\"\n [checked]=\"isRequiredSelected(opt.value)\"\n (change)=\"toggleRequired(opt.value)\" autocomplete=\"off\" />\n {{ opt.label }}\n </label>\n }\n </div>\n </fieldset>\n\n <label class=\"col-span-full flex items-center gap-2 text-[13px]\">\n <input type=\"checkbox\" [(ngModel)]=\"allowsCustomScript\" name=\"allowsCustomScript\" autocomplete=\"off\" />\n Allows custom script (dialer shows free-text script field)\n </label>\n\n <label class=\"flex flex-col gap-2\">\n <span class=\"eds-label\">Sort order</span>\n <input type=\"number\" class=\"purpose-form__input rounded-lg border px-3 py-2 font-[inherit]\" [(ngModel)]=\"sortOrder\" name=\"sortOrder\" min=\"0\" autocomplete=\"off\" />\n </label>\n\n @if (isEdit) {\n <label class=\"flex items-center gap-2 text-[13px]\">\n <input type=\"checkbox\" [(ngModel)]=\"isActive\" name=\"isActive\" autocomplete=\"off\" />\n Active\n </label>\n }\n </div>\n\n <section class=\"purpose-form__preview eds-card rounded-lg p-3 shadow-sm\">\n <h3 class=\"eds-label\">Live preview</h3>\n <p class=\"purpose-form__preview-text mt-2 whitespace-pre-wrap text-sm leading-normal\">{{ previewText }}</p>\n </section>\n\n @if (error()) {\n <p class=\"m-0 text-[13px] text-[#b91c1c]\" role=\"alert\">{{ error() }}</p>\n }\n\n <footer class=\"flex justify-end gap-2\">\n <eds-button variant=\"secondary\" type=\"button\" (click)=\"close()\" [disabled]=\"saving()\">\n Cancel\n </eds-button>\n <eds-button variant=\"primary\" type=\"submit\" [disabled]=\"saving()\">\n {{ saving() ? 'Saving\u2026' : isEdit ? 'Save changes' : 'Create purpose' }}\n </eds-button>\n </footer>\n </form>\n </app-modal>\n}\n", styles: [".purpose-form__input,.purpose-form__textarea{border-color:var(--eds-border-subtle, #e4e4e7)}.purpose-form__hint{color:var(--eds-text-secondary)}.purpose-form__chip{border-color:var(--eds-border-subtle, #e4e4e7);background:var(--eds-surface-muted, #f4f4f5)}.purpose-form__chip:hover{border-color:var(--eds-brand, #6366f1)}.purpose-form__preview{background:var(--eds-surface-muted, #f8fafc)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: ModalComponent, selector: "app-modal", inputs: ["title", "subtitle", "maxWidth"], outputs: ["closed"] }, { kind: "component", type: EdsButtonComponent, selector: "eds-button", inputs: ["variant", "type", "disabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
170
+ }
171
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: OutboundPurposeFormModalComponent, decorators: [{
172
+ type: Component,
173
+ args: [{ selector: 'eds-outbound-purpose-form-modal', imports: [FormsModule, ModalComponent, EdsButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (open) {\n <app-modal\n [title]=\"isEdit ? 'Edit outbound purpose' : 'New outbound purpose'\"\n subtitle=\"Configure dialer fields and AI opening script\"\n maxWidth=\"720px\"\n (closed)=\"close()\"\n >\n <form class=\"flex flex-col gap-4\" (ngSubmit)=\"submit()\" novalidate autocomplete=\"off\">\n <div class=\"grid grid-cols-2 gap-3\">\n <label class=\"col-span-full flex flex-col gap-2\">\n <span class=\"eds-label\">Label <span class=\"text-[#dc2626]\">*</span></span>\n <input type=\"text\" class=\"purpose-form__input rounded-lg border px-3 py-2 font-[inherit]\" [(ngModel)]=\"label\" name=\"label\" required autocomplete=\"off\" />\n </label>\n\n <label class=\"col-span-full flex flex-col gap-2\">\n <span class=\"eds-label\">Description</span>\n <input type=\"text\" class=\"purpose-form__input rounded-lg border px-3 py-2 font-[inherit]\" [(ngModel)]=\"description\" name=\"description\" autocomplete=\"off\" />\n </label>\n\n <label class=\"col-span-full flex flex-col gap-2\">\n <span class=\"eds-label\">Sample opening (plain preview, max 500)</span>\n <textarea\n class=\"purpose-form__textarea min-h-[72px] resize-y rounded-lg border px-3 py-2 font-[inherit]\"\n rows=\"3\"\n maxlength=\"500\"\n [(ngModel)]=\"sampleOpening\"\n name=\"sampleOpening\" autocomplete=\"off\"></textarea>\n <span class=\"purpose-form__hint text-xs\">{{ sampleOpening.length }}/500</span>\n </label>\n\n <div class=\"col-span-full flex flex-col gap-2\">\n <span class=\"eds-label\">Opening template <span class=\"text-[#dc2626]\">*</span></span>\n <div class=\"mb-2 flex flex-wrap gap-2\">\n @for (chip of placeholders; track chip.token) {\n <button type=\"button\" class=\"purpose-form__chip cursor-pointer rounded-full border px-3 py-1 text-xs\" (click)=\"insertPlaceholder(chip.token)\">\n {{ chip.label }}\n </button>\n }\n </div>\n <textarea\n #templateInput\n class=\"purpose-form__textarea min-h-[72px] resize-y rounded-lg border px-3 py-2 font-[inherit]\"\n rows=\"4\"\n [(ngModel)]=\"openingTemplate\"\n name=\"openingTemplate\"\n required autocomplete=\"off\"></textarea>\n </div>\n\n <label class=\"col-span-full flex flex-col gap-2\">\n <span class=\"eds-label\">Agent context hint</span>\n <textarea\n class=\"purpose-form__textarea min-h-[72px] resize-y rounded-lg border px-3 py-2 font-[inherit]\"\n rows=\"2\"\n [(ngModel)]=\"agentContextHint\"\n name=\"agentContextHint\"\n placeholder=\"Optional guidance for the AI after the opening\" autocomplete=\"off\"></textarea>\n </label>\n\n <fieldset class=\"col-span-full flex flex-col gap-2\">\n <legend class=\"eds-label\">Required fields</legend>\n <div class=\"grid grid-cols-2 gap-2\">\n @for (opt of fieldOptions; track opt.value) {\n <label class=\"flex items-center gap-2 text-[13px]\">\n <input\n type=\"checkbox\"\n [checked]=\"isRequiredSelected(opt.value)\"\n (change)=\"toggleRequired(opt.value)\" autocomplete=\"off\" />\n {{ opt.label }}\n </label>\n }\n </div>\n </fieldset>\n\n <label class=\"col-span-full flex items-center gap-2 text-[13px]\">\n <input type=\"checkbox\" [(ngModel)]=\"allowsCustomScript\" name=\"allowsCustomScript\" autocomplete=\"off\" />\n Allows custom script (dialer shows free-text script field)\n </label>\n\n <label class=\"flex flex-col gap-2\">\n <span class=\"eds-label\">Sort order</span>\n <input type=\"number\" class=\"purpose-form__input rounded-lg border px-3 py-2 font-[inherit]\" [(ngModel)]=\"sortOrder\" name=\"sortOrder\" min=\"0\" autocomplete=\"off\" />\n </label>\n\n @if (isEdit) {\n <label class=\"flex items-center gap-2 text-[13px]\">\n <input type=\"checkbox\" [(ngModel)]=\"isActive\" name=\"isActive\" autocomplete=\"off\" />\n Active\n </label>\n }\n </div>\n\n <section class=\"purpose-form__preview eds-card rounded-lg p-3 shadow-sm\">\n <h3 class=\"eds-label\">Live preview</h3>\n <p class=\"purpose-form__preview-text mt-2 whitespace-pre-wrap text-sm leading-normal\">{{ previewText }}</p>\n </section>\n\n @if (error()) {\n <p class=\"m-0 text-[13px] text-[#b91c1c]\" role=\"alert\">{{ error() }}</p>\n }\n\n <footer class=\"flex justify-end gap-2\">\n <eds-button variant=\"secondary\" type=\"button\" (click)=\"close()\" [disabled]=\"saving()\">\n Cancel\n </eds-button>\n <eds-button variant=\"primary\" type=\"submit\" [disabled]=\"saving()\">\n {{ saving() ? 'Saving\u2026' : isEdit ? 'Save changes' : 'Create purpose' }}\n </eds-button>\n </footer>\n </form>\n </app-modal>\n}\n", styles: [".purpose-form__input,.purpose-form__textarea{border-color:var(--eds-border-subtle, #e4e4e7)}.purpose-form__hint{color:var(--eds-text-secondary)}.purpose-form__chip{border-color:var(--eds-border-subtle, #e4e4e7);background:var(--eds-surface-muted, #f4f4f5)}.purpose-form__chip:hover{border-color:var(--eds-brand, #6366f1)}.purpose-form__preview{background:var(--eds-surface-muted, #f8fafc)}\n"] }]
174
+ }], propDecorators: { open: [{
175
+ type: Input
176
+ }], purpose: [{
177
+ type: Input
178
+ }], closed: [{
179
+ type: Output
180
+ }], saved: [{
181
+ type: Output
182
+ }], templateInput: [{
183
+ type: ViewChild,
184
+ args: ['templateInput']
185
+ }] } });
186
+
187
+ class OutboundPurposesPageComponent {
188
+ constructor() {
189
+ this.api = inject(AiReceptionistApiService);
190
+ this.purposes = signal([]);
191
+ this.loading = signal(true);
192
+ this.error = signal(null);
193
+ this.modalOpen = signal(false);
194
+ this.editing = signal(null);
195
+ this.formatRequiredFields = formatRequiredFields;
196
+ }
197
+ ngOnInit() {
198
+ this.load();
199
+ }
200
+ load() {
201
+ this.loading.set(true);
202
+ this.error.set(null);
203
+ this.api.getOutboundPurposes(true).subscribe({
204
+ next: ({ purposes }) => {
205
+ this.purposes.set(purposes);
206
+ this.loading.set(false);
207
+ },
208
+ error: () => {
209
+ this.loading.set(false);
210
+ this.error.set('Failed to load outbound purposes.');
211
+ },
212
+ });
213
+ }
214
+ openCreate() {
215
+ this.editing.set(null);
216
+ this.modalOpen.set(true);
217
+ }
218
+ openEdit(purpose) {
219
+ this.editing.set(purpose);
220
+ this.modalOpen.set(true);
221
+ }
222
+ closeModal() {
223
+ this.modalOpen.set(false);
224
+ this.editing.set(null);
225
+ }
226
+ onSaved() {
227
+ this.load();
228
+ }
229
+ toggleActive(purpose, active) {
230
+ this.api.updateOutboundPurpose(purpose.purposeId, { isActive: active }).subscribe({
231
+ next: (updated) => {
232
+ this.purposes.update((list) => list.map((p) => (p.purposeId === updated.purposeId ? updated : p)));
233
+ },
234
+ error: () => this.load(),
235
+ });
236
+ }
237
+ deletePurpose(purpose) {
238
+ if (purpose.isSystem) {
239
+ this.toggleActive(purpose, false);
240
+ return;
241
+ }
242
+ if (!confirm(`Delete "${purpose.label}"? This cannot be undone.`))
243
+ return;
244
+ this.api.deleteOutboundPurpose(purpose.purposeId).subscribe({
245
+ next: () => this.load(),
246
+ error: () => this.error.set('Failed to delete purpose.'),
247
+ });
248
+ }
249
+ actionLabel(purpose) {
250
+ return purpose.isSystem ? 'Deactivate' : 'Delete';
251
+ }
252
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: OutboundPurposesPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
253
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: OutboundPurposesPageComponent, isStandalone: true, selector: "eds-outbound-purposes-page", ngImport: i0, template: "<div class=\"eds-page outbound-purposes-page\">\n <header class=\"mb-5 flex items-start justify-between gap-4\">\n <div>\n <a routerLink=\"../settings\" class=\"outbound-purposes-page__back eds-body-sm mb-2 inline-flex items-center gap-1 no-underline\">\n <eds-icon name=\"arrow_back\" [size]=\"16\" /> Settings\n </a>\n <h1 class=\"eds-h1\">Outbound Call Purposes</h1>\n <p class=\"eds-body-sm\">Templates that drive outbound dialer fields and AI opening scripts</p>\n </div>\n <eds-button variant=\"primary\" (click)=\"openCreate()\">\n <eds-icon name=\"add\" [size]=\"18\" /> New purpose\n </eds-button>\n </header>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading purposes\u2026</p>\n } @else if (error()) {\n <p class=\"eds-body-sm text-red-700\" role=\"alert\">{{ error() }}</p>\n } @else {\n <article class=\"eds-card overflow-x-auto p-4 shadow-sm\">\n <table class=\"eds-table\">\n <thead>\n <tr>\n <th>Label</th>\n <th>Description</th>\n <th>Required fields</th>\n <th>System</th>\n <th>Active</th>\n <th>Sort</th>\n <th>Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (p of purposes(); track p.purposeId) {\n <tr>\n <td class=\"eds-body-sm-bold\">{{ p.label }}</td>\n <td class=\"eds-body-sm\">{{ p.description || '\u2014' }}</td>\n <td class=\"eds-body-sm\">{{ formatRequiredFields(p.requiredFields) }}</td>\n <td>\n @if (p.isSystem) {\n <eds-badge tone=\"brand\">System</eds-badge>\n } @else {\n <span class=\"eds-body-sm\">\u2014</span>\n }\n </td>\n <td>\n <label>\n <input\n type=\"checkbox\"\n class=\"h-4 w-4\"\n [checked]=\"p.isActive\"\n (change)=\"toggleActive(p, $any($event.target).checked)\" autocomplete=\"off\" />\n </label>\n </td>\n <td class=\"eds-mono-sm\">{{ p.sortOrder }}</td>\n <td class=\"flex gap-3 whitespace-nowrap\">\n <button type=\"button\" class=\"outbound-purposes-page__link cursor-pointer border-none bg-transparent p-0\" (click)=\"openEdit(p)\">\n Edit\n </button>\n <button type=\"button\" class=\"outbound-purposes-page__link cursor-pointer border-none bg-transparent p-0\" (click)=\"deletePurpose(p)\">\n {{ actionLabel(p) }}\n </button>\n </td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"7\" class=\"eds-body-sm\">No outbound purposes configured.</td>\n </tr>\n }\n </tbody>\n </table>\n </article>\n }\n\n <eds-outbound-purpose-form-modal\n [open]=\"modalOpen()\"\n [purpose]=\"editing()\"\n (closed)=\"closeModal()\"\n (saved)=\"onSaved()\"\n />\n</div>\n", styles: [".outbound-purposes-page__back{color:var(--eds-text-secondary)}.outbound-purposes-page__link{color:var(--eds-brand, #6366f1);font-size:13px}\n"], dependencies: [{ 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: EdsIconComponent, selector: "eds-icon", inputs: ["name", "size", "fill", "weight"] }, { kind: "component", type: OutboundPurposeFormModalComponent, selector: "eds-outbound-purpose-form-modal", inputs: ["open", "purpose"], outputs: ["closed", "saved"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
254
+ }
255
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: OutboundPurposesPageComponent, decorators: [{
256
+ type: Component,
257
+ args: [{ selector: 'eds-outbound-purposes-page', imports: [
258
+ RouterLink,
259
+ EdsBadgeComponent,
260
+ EdsButtonComponent,
261
+ EdsIconComponent,
262
+ OutboundPurposeFormModalComponent,
263
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"eds-page outbound-purposes-page\">\n <header class=\"mb-5 flex items-start justify-between gap-4\">\n <div>\n <a routerLink=\"../settings\" class=\"outbound-purposes-page__back eds-body-sm mb-2 inline-flex items-center gap-1 no-underline\">\n <eds-icon name=\"arrow_back\" [size]=\"16\" /> Settings\n </a>\n <h1 class=\"eds-h1\">Outbound Call Purposes</h1>\n <p class=\"eds-body-sm\">Templates that drive outbound dialer fields and AI opening scripts</p>\n </div>\n <eds-button variant=\"primary\" (click)=\"openCreate()\">\n <eds-icon name=\"add\" [size]=\"18\" /> New purpose\n </eds-button>\n </header>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading purposes\u2026</p>\n } @else if (error()) {\n <p class=\"eds-body-sm text-red-700\" role=\"alert\">{{ error() }}</p>\n } @else {\n <article class=\"eds-card overflow-x-auto p-4 shadow-sm\">\n <table class=\"eds-table\">\n <thead>\n <tr>\n <th>Label</th>\n <th>Description</th>\n <th>Required fields</th>\n <th>System</th>\n <th>Active</th>\n <th>Sort</th>\n <th>Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (p of purposes(); track p.purposeId) {\n <tr>\n <td class=\"eds-body-sm-bold\">{{ p.label }}</td>\n <td class=\"eds-body-sm\">{{ p.description || '\u2014' }}</td>\n <td class=\"eds-body-sm\">{{ formatRequiredFields(p.requiredFields) }}</td>\n <td>\n @if (p.isSystem) {\n <eds-badge tone=\"brand\">System</eds-badge>\n } @else {\n <span class=\"eds-body-sm\">\u2014</span>\n }\n </td>\n <td>\n <label>\n <input\n type=\"checkbox\"\n class=\"h-4 w-4\"\n [checked]=\"p.isActive\"\n (change)=\"toggleActive(p, $any($event.target).checked)\" autocomplete=\"off\" />\n </label>\n </td>\n <td class=\"eds-mono-sm\">{{ p.sortOrder }}</td>\n <td class=\"flex gap-3 whitespace-nowrap\">\n <button type=\"button\" class=\"outbound-purposes-page__link cursor-pointer border-none bg-transparent p-0\" (click)=\"openEdit(p)\">\n Edit\n </button>\n <button type=\"button\" class=\"outbound-purposes-page__link cursor-pointer border-none bg-transparent p-0\" (click)=\"deletePurpose(p)\">\n {{ actionLabel(p) }}\n </button>\n </td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"7\" class=\"eds-body-sm\">No outbound purposes configured.</td>\n </tr>\n }\n </tbody>\n </table>\n </article>\n }\n\n <eds-outbound-purpose-form-modal\n [open]=\"modalOpen()\"\n [purpose]=\"editing()\"\n (closed)=\"closeModal()\"\n (saved)=\"onSaved()\"\n />\n</div>\n", styles: [".outbound-purposes-page__back{color:var(--eds-text-secondary)}.outbound-purposes-page__link{color:var(--eds-brand, #6366f1);font-size:13px}\n"] }]
264
+ }] });
265
+
266
+ export { OutboundPurposesPageComponent };
267
+ //# sourceMappingURL=edvak99-voice-receptionist-widget-outbound-purposes-page.component-2GHT8NT9.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edvak99-voice-receptionist-widget-outbound-purposes-page.component-2GHT8NT9.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/components/outbound-purpose-form-modal/outbound-purpose-form-modal.component.ts","../../../projects/voice-receptionist-widget/src/lib/components/outbound-purpose-form-modal/outbound-purpose-form-modal.component.html","../../../projects/voice-receptionist-widget/src/lib/pages/outbound-purposes-page/outbound-purposes-page.component.ts","../../../projects/voice-receptionist-widget/src/lib/pages/outbound-purposes-page/outbound-purposes-page.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n EventEmitter,\n Input,\n OnChanges,\n Output,\n SimpleChanges,\n ViewChild,\n inject,\n signal,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\n\nimport {\n CreateOutboundPurposeRequest,\n OutboundPurpose,\n OutboundPurposePlaceholder,\n OutboundRequiredField,\n UpdateOutboundPurposeRequest,\n} from '../../models/outbound-purpose.types';\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport {\n REQUIRED_FIELD_OPTIONS,\n renderOpeningPreview,\n} from '../../utils/outbound-purpose.util';\nimport { DEFAULT_PLACEHOLDERS } from '../../mocks/outbound-purpose.seed';\nimport { EdsButtonComponent } from '../../ui/eds-button.component';\nimport { ModalComponent } from '../../shared/modal.component';\n\n@Component({\n selector: 'eds-outbound-purpose-form-modal',\n imports: [FormsModule, ModalComponent, EdsButtonComponent],\n templateUrl: './outbound-purpose-form-modal.component.html',\n styleUrl: './outbound-purpose-form-modal.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class OutboundPurposeFormModalComponent implements OnChanges {\n private readonly api = inject(AiReceptionistApiService);\n\n @Input() open = false;\n @Input() purpose: OutboundPurpose | null = null;\n @Output() readonly closed = new EventEmitter<void>();\n @Output() readonly saved = new EventEmitter<OutboundPurpose>();\n\n @ViewChild('templateInput') templateInput?: ElementRef<HTMLTextAreaElement>;\n\n protected readonly fieldOptions = REQUIRED_FIELD_OPTIONS;\n protected readonly saving = signal(false);\n protected readonly error = signal<string | null>(null);\n\n protected label = '';\n protected description = '';\n protected sampleOpening = '';\n protected openingTemplate = '';\n protected agentContextHint = '';\n protected requiredFields = new Set<OutboundRequiredField>();\n protected allowsCustomScript = false;\n protected sortOrder = 1;\n protected isActive = true;\n protected placeholders: OutboundPurposePlaceholder[] = DEFAULT_PLACEHOLDERS;\n\n protected get isEdit(): boolean {\n return !!this.purpose;\n }\n\n protected get previewText(): string {\n return renderOpeningPreview(this.sampleOpening, this.openingTemplate, this.placeholders);\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['open']?.currentValue || changes['purpose']) {\n this.resetForm();\n }\n }\n\n protected close(): void {\n this.closed.emit();\n }\n\n protected toggleRequired(field: OutboundRequiredField): void {\n const next = new Set(this.requiredFields);\n if (next.has(field)) next.delete(field);\n else next.add(field);\n this.requiredFields = next;\n }\n\n protected isRequiredSelected(field: OutboundRequiredField): boolean {\n return this.requiredFields.has(field);\n }\n\n protected insertPlaceholder(token: string): void {\n const el = this.templateInput?.nativeElement;\n if (!el) {\n this.openingTemplate += token;\n return;\n }\n const start = el.selectionStart ?? this.openingTemplate.length;\n const end = el.selectionEnd ?? start;\n this.openingTemplate =\n this.openingTemplate.slice(0, start) + token + this.openingTemplate.slice(end);\n const pos = start + token.length;\n requestAnimationFrame(() => {\n el.focus();\n el.setSelectionRange(pos, pos);\n });\n }\n\n protected submit(): void {\n this.error.set(null);\n const trimmedLabel = this.label.trim();\n const trimmedTemplate = this.openingTemplate.trim();\n if (!trimmedLabel) {\n this.error.set('Label is required.');\n return;\n }\n if (!trimmedTemplate) {\n this.error.set('Opening template is required.');\n return;\n }\n if (this.sampleOpening.length > 500) {\n this.error.set('Sample opening must be 500 characters or fewer.');\n return;\n }\n\n this.saving.set(true);\n const fields = [...this.requiredFields];\n\n if (this.isEdit && this.purpose) {\n const body: UpdateOutboundPurposeRequest = {\n label: trimmedLabel,\n description: this.description.trim(),\n sampleOpening: this.sampleOpening.trim(),\n openingTemplate: trimmedTemplate,\n agentContextHint: this.agentContextHint.trim() || null,\n requiredFields: fields,\n allowsCustomScript: this.allowsCustomScript,\n sortOrder: this.sortOrder,\n isActive: this.isActive,\n };\n this.api.updateOutboundPurpose(this.purpose.purposeId, body).subscribe({\n next: (updated) => {\n this.saving.set(false);\n this.saved.emit(updated);\n this.close();\n },\n error: () => {\n this.saving.set(false);\n this.error.set('Failed to save purpose template.');\n },\n });\n return;\n }\n\n const body: CreateOutboundPurposeRequest = {\n label: trimmedLabel,\n description: this.description.trim(),\n sampleOpening: this.sampleOpening.trim(),\n openingTemplate: trimmedTemplate,\n agentContextHint: this.agentContextHint.trim() || null,\n requiredFields: fields,\n allowsCustomScript: this.allowsCustomScript,\n sortOrder: this.sortOrder,\n };\n this.api.createOutboundPurpose(body).subscribe({\n next: (created) => {\n this.saving.set(false);\n this.saved.emit(created);\n this.close();\n },\n error: () => {\n this.saving.set(false);\n this.error.set('Failed to create purpose template.');\n },\n });\n }\n\n private resetForm(): void {\n this.error.set(null);\n this.saving.set(false);\n if (this.purpose) {\n this.label = this.purpose.label;\n this.description = this.purpose.description;\n this.sampleOpening = this.purpose.sampleOpening;\n this.openingTemplate = this.purpose.openingTemplate;\n this.agentContextHint = this.purpose.agentContextHint ?? '';\n this.requiredFields = new Set(this.purpose.requiredFields);\n this.allowsCustomScript = this.purpose.allowsCustomScript;\n this.sortOrder = this.purpose.sortOrder;\n this.isActive = this.purpose.isActive;\n this.placeholders = this.purpose.placeholders?.length\n ? [...this.purpose.placeholders]\n : [...DEFAULT_PLACEHOLDERS];\n } else {\n this.label = '';\n this.description = '';\n this.sampleOpening = '';\n this.openingTemplate = 'Hi {{patientName}}, this is {{practiceName}}.';\n this.agentContextHint = '';\n this.requiredFields = new Set();\n this.allowsCustomScript = false;\n this.sortOrder = 1;\n this.isActive = true;\n this.placeholders = [...DEFAULT_PLACEHOLDERS];\n }\n }\n}\n","@if (open) {\n <app-modal\n [title]=\"isEdit ? 'Edit outbound purpose' : 'New outbound purpose'\"\n subtitle=\"Configure dialer fields and AI opening script\"\n maxWidth=\"720px\"\n (closed)=\"close()\"\n >\n <form class=\"flex flex-col gap-4\" (ngSubmit)=\"submit()\" novalidate autocomplete=\"off\">\n <div class=\"grid grid-cols-2 gap-3\">\n <label class=\"col-span-full flex flex-col gap-2\">\n <span class=\"eds-label\">Label <span class=\"text-[#dc2626]\">*</span></span>\n <input type=\"text\" class=\"purpose-form__input rounded-lg border px-3 py-2 font-[inherit]\" [(ngModel)]=\"label\" name=\"label\" required autocomplete=\"off\" />\n </label>\n\n <label class=\"col-span-full flex flex-col gap-2\">\n <span class=\"eds-label\">Description</span>\n <input type=\"text\" class=\"purpose-form__input rounded-lg border px-3 py-2 font-[inherit]\" [(ngModel)]=\"description\" name=\"description\" autocomplete=\"off\" />\n </label>\n\n <label class=\"col-span-full flex flex-col gap-2\">\n <span class=\"eds-label\">Sample opening (plain preview, max 500)</span>\n <textarea\n class=\"purpose-form__textarea min-h-[72px] resize-y rounded-lg border px-3 py-2 font-[inherit]\"\n rows=\"3\"\n maxlength=\"500\"\n [(ngModel)]=\"sampleOpening\"\n name=\"sampleOpening\" autocomplete=\"off\"></textarea>\n <span class=\"purpose-form__hint text-xs\">{{ sampleOpening.length }}/500</span>\n </label>\n\n <div class=\"col-span-full flex flex-col gap-2\">\n <span class=\"eds-label\">Opening template <span class=\"text-[#dc2626]\">*</span></span>\n <div class=\"mb-2 flex flex-wrap gap-2\">\n @for (chip of placeholders; track chip.token) {\n <button type=\"button\" class=\"purpose-form__chip cursor-pointer rounded-full border px-3 py-1 text-xs\" (click)=\"insertPlaceholder(chip.token)\">\n {{ chip.label }}\n </button>\n }\n </div>\n <textarea\n #templateInput\n class=\"purpose-form__textarea min-h-[72px] resize-y rounded-lg border px-3 py-2 font-[inherit]\"\n rows=\"4\"\n [(ngModel)]=\"openingTemplate\"\n name=\"openingTemplate\"\n required autocomplete=\"off\"></textarea>\n </div>\n\n <label class=\"col-span-full flex flex-col gap-2\">\n <span class=\"eds-label\">Agent context hint</span>\n <textarea\n class=\"purpose-form__textarea min-h-[72px] resize-y rounded-lg border px-3 py-2 font-[inherit]\"\n rows=\"2\"\n [(ngModel)]=\"agentContextHint\"\n name=\"agentContextHint\"\n placeholder=\"Optional guidance for the AI after the opening\" autocomplete=\"off\"></textarea>\n </label>\n\n <fieldset class=\"col-span-full flex flex-col gap-2\">\n <legend class=\"eds-label\">Required fields</legend>\n <div class=\"grid grid-cols-2 gap-2\">\n @for (opt of fieldOptions; track opt.value) {\n <label class=\"flex items-center gap-2 text-[13px]\">\n <input\n type=\"checkbox\"\n [checked]=\"isRequiredSelected(opt.value)\"\n (change)=\"toggleRequired(opt.value)\" autocomplete=\"off\" />\n {{ opt.label }}\n </label>\n }\n </div>\n </fieldset>\n\n <label class=\"col-span-full flex items-center gap-2 text-[13px]\">\n <input type=\"checkbox\" [(ngModel)]=\"allowsCustomScript\" name=\"allowsCustomScript\" autocomplete=\"off\" />\n Allows custom script (dialer shows free-text script field)\n </label>\n\n <label class=\"flex flex-col gap-2\">\n <span class=\"eds-label\">Sort order</span>\n <input type=\"number\" class=\"purpose-form__input rounded-lg border px-3 py-2 font-[inherit]\" [(ngModel)]=\"sortOrder\" name=\"sortOrder\" min=\"0\" autocomplete=\"off\" />\n </label>\n\n @if (isEdit) {\n <label class=\"flex items-center gap-2 text-[13px]\">\n <input type=\"checkbox\" [(ngModel)]=\"isActive\" name=\"isActive\" autocomplete=\"off\" />\n Active\n </label>\n }\n </div>\n\n <section class=\"purpose-form__preview eds-card rounded-lg p-3 shadow-sm\">\n <h3 class=\"eds-label\">Live preview</h3>\n <p class=\"purpose-form__preview-text mt-2 whitespace-pre-wrap text-sm leading-normal\">{{ previewText }}</p>\n </section>\n\n @if (error()) {\n <p class=\"m-0 text-[13px] text-[#b91c1c]\" role=\"alert\">{{ error() }}</p>\n }\n\n <footer class=\"flex justify-end gap-2\">\n <eds-button variant=\"secondary\" type=\"button\" (click)=\"close()\" [disabled]=\"saving()\">\n Cancel\n </eds-button>\n <eds-button variant=\"primary\" type=\"submit\" [disabled]=\"saving()\">\n {{ saving() ? 'Saving…' : isEdit ? 'Save changes' : 'Create purpose' }}\n </eds-button>\n </footer>\n </form>\n </app-modal>\n}\n","import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core';\nimport { RouterLink } from '@angular/router';\n\nimport { OutboundPurpose } from '../../models/outbound-purpose.types';\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport { formatRequiredFields } from '../../utils/outbound-purpose.util';\nimport { OutboundPurposeFormModalComponent } from '../../components/outbound-purpose-form-modal/outbound-purpose-form-modal.component';\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-outbound-purposes-page',\n imports: [\n RouterLink,\n EdsBadgeComponent,\n EdsButtonComponent,\n EdsIconComponent,\n OutboundPurposeFormModalComponent,\n ],\n templateUrl: './outbound-purposes-page.component.html',\n styleUrl: './outbound-purposes-page.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class OutboundPurposesPageComponent implements OnInit {\n private readonly api = inject(AiReceptionistApiService);\n\n protected readonly purposes = signal<OutboundPurpose[]>([]);\n protected readonly loading = signal(true);\n protected readonly error = signal<string | null>(null);\n protected readonly modalOpen = signal(false);\n protected readonly editing = signal<OutboundPurpose | null>(null);\n\n protected formatRequiredFields = formatRequiredFields;\n\n ngOnInit(): void {\n this.load();\n }\n\n protected load(): void {\n this.loading.set(true);\n this.error.set(null);\n this.api.getOutboundPurposes(true).subscribe({\n next: ({ purposes }) => {\n this.purposes.set(purposes);\n this.loading.set(false);\n },\n error: () => {\n this.loading.set(false);\n this.error.set('Failed to load outbound purposes.');\n },\n });\n }\n\n protected openCreate(): void {\n this.editing.set(null);\n this.modalOpen.set(true);\n }\n\n protected openEdit(purpose: OutboundPurpose): void {\n this.editing.set(purpose);\n this.modalOpen.set(true);\n }\n\n protected closeModal(): void {\n this.modalOpen.set(false);\n this.editing.set(null);\n }\n\n protected onSaved(): void {\n this.load();\n }\n\n protected toggleActive(purpose: OutboundPurpose, active: boolean): void {\n this.api.updateOutboundPurpose(purpose.purposeId, { isActive: active }).subscribe({\n next: (updated) => {\n this.purposes.update((list) =>\n list.map((p) => (p.purposeId === updated.purposeId ? updated : p)),\n );\n },\n error: () => this.load(),\n });\n }\n\n protected deletePurpose(purpose: OutboundPurpose): void {\n if (purpose.isSystem) {\n this.toggleActive(purpose, false);\n return;\n }\n if (!confirm(`Delete \"${purpose.label}\"? This cannot be undone.`)) return;\n this.api.deleteOutboundPurpose(purpose.purposeId).subscribe({\n next: () => this.load(),\n error: () => this.error.set('Failed to delete purpose.'),\n });\n }\n\n protected actionLabel(purpose: OutboundPurpose): string {\n return purpose.isSystem ? 'Deactivate' : 'Delete';\n }\n}\n","<div class=\"eds-page outbound-purposes-page\">\n <header class=\"mb-5 flex items-start justify-between gap-4\">\n <div>\n <a routerLink=\"../settings\" class=\"outbound-purposes-page__back eds-body-sm mb-2 inline-flex items-center gap-1 no-underline\">\n <eds-icon name=\"arrow_back\" [size]=\"16\" /> Settings\n </a>\n <h1 class=\"eds-h1\">Outbound Call Purposes</h1>\n <p class=\"eds-body-sm\">Templates that drive outbound dialer fields and AI opening scripts</p>\n </div>\n <eds-button variant=\"primary\" (click)=\"openCreate()\">\n <eds-icon name=\"add\" [size]=\"18\" /> New purpose\n </eds-button>\n </header>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading purposes…</p>\n } @else if (error()) {\n <p class=\"eds-body-sm text-red-700\" role=\"alert\">{{ error() }}</p>\n } @else {\n <article class=\"eds-card overflow-x-auto p-4 shadow-sm\">\n <table class=\"eds-table\">\n <thead>\n <tr>\n <th>Label</th>\n <th>Description</th>\n <th>Required fields</th>\n <th>System</th>\n <th>Active</th>\n <th>Sort</th>\n <th>Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (p of purposes(); track p.purposeId) {\n <tr>\n <td class=\"eds-body-sm-bold\">{{ p.label }}</td>\n <td class=\"eds-body-sm\">{{ p.description || '—' }}</td>\n <td class=\"eds-body-sm\">{{ formatRequiredFields(p.requiredFields) }}</td>\n <td>\n @if (p.isSystem) {\n <eds-badge tone=\"brand\">System</eds-badge>\n } @else {\n <span class=\"eds-body-sm\">—</span>\n }\n </td>\n <td>\n <label>\n <input\n type=\"checkbox\"\n class=\"h-4 w-4\"\n [checked]=\"p.isActive\"\n (change)=\"toggleActive(p, $any($event.target).checked)\" autocomplete=\"off\" />\n </label>\n </td>\n <td class=\"eds-mono-sm\">{{ p.sortOrder }}</td>\n <td class=\"flex gap-3 whitespace-nowrap\">\n <button type=\"button\" class=\"outbound-purposes-page__link cursor-pointer border-none bg-transparent p-0\" (click)=\"openEdit(p)\">\n Edit\n </button>\n <button type=\"button\" class=\"outbound-purposes-page__link cursor-pointer border-none bg-transparent p-0\" (click)=\"deletePurpose(p)\">\n {{ actionLabel(p) }}\n </button>\n </td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"7\" class=\"eds-body-sm\">No outbound purposes configured.</td>\n </tr>\n }\n </tbody>\n </table>\n </article>\n }\n\n <eds-outbound-purpose-form-modal\n [open]=\"modalOpen()\"\n [purpose]=\"editing()\"\n (closed)=\"closeModal()\"\n (saved)=\"onSaved()\"\n />\n</div>\n"],"names":[],"mappings":"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"}