@edvak99/voice-receptionist-widget 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-analytics-page.component-CW5OKmik.mjs → edvak99-voice-receptionist-widget-analytics-page.component-Dsujn5az.mjs} +4 -4
- package/fesm2022/{edvak99-voice-receptionist-widget-analytics-page.component-CW5OKmik.mjs.map → edvak99-voice-receptionist-widget-analytics-page.component-Dsujn5az.mjs.map} +1 -1
- package/fesm2022/edvak99-voice-receptionist-widget-audit-page.component-xoLlHj8K.mjs +67 -0
- package/fesm2022/edvak99-voice-receptionist-widget-audit-page.component-xoLlHj8K.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-call-history-detail-page.component-CTwfUHUf.mjs → edvak99-voice-receptionist-widget-call-history-detail-page.component-2K9W77As.mjs} +11 -11
- package/fesm2022/{edvak99-voice-receptionist-widget-call-history-detail-page.component-CTwfUHUf.mjs.map → edvak99-voice-receptionist-widget-call-history-detail-page.component-2K9W77As.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-call-history-page.component-Cj4wxd4e.mjs → edvak99-voice-receptionist-widget-call-history-page.component-B5AnEjoh.mjs} +62 -26
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-B5AnEjoh.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-call-recording-player.component-AN6GcSDq.mjs → edvak99-voice-receptionist-widget-call-recording-player.component-B8_qp7Fe.mjs} +4 -4
- package/fesm2022/{edvak99-voice-receptionist-widget-call-recording-player.component-AN6GcSDq.mjs.map → edvak99-voice-receptionist-widget-call-recording-player.component-B8_qp7Fe.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-Cy_Bf7AO.mjs → edvak99-voice-receptionist-widget-call-review.service-Cj2XTeK4.mjs} +2 -2
- package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-Cy_Bf7AO.mjs.map → edvak99-voice-receptionist-widget-call-review.service-Cj2XTeK4.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-call-review.util-DQLIw7xb.mjs → edvak99-voice-receptionist-widget-call-review.util-BgsBCKgZ.mjs} +2 -2
- package/fesm2022/{edvak99-voice-receptionist-widget-call-review.util-DQLIw7xb.mjs.map → edvak99-voice-receptionist-widget-call-review.util-BgsBCKgZ.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-dashboard-page.component-DzdIt2A7.mjs → edvak99-voice-receptionist-widget-dashboard-page.component-BNdnbAVM.mjs} +10 -10
- package/fesm2022/{edvak99-voice-receptionist-widget-dashboard-page.component-DzdIt2A7.mjs.map → edvak99-voice-receptionist-widget-dashboard-page.component-BNdnbAVM.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-eds-avatar.component-PCnIWeOA.mjs → edvak99-voice-receptionist-widget-eds-avatar.component-CbphhP6o.mjs} +3 -3
- package/fesm2022/{edvak99-voice-receptionist-widget-eds-avatar.component-PCnIWeOA.mjs.map → edvak99-voice-receptionist-widget-eds-avatar.component-CbphhP6o.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-eds-elapsed.pipe-ouRlPpwT.mjs → edvak99-voice-receptionist-widget-eds-elapsed.pipe-BgLsnq6A.mjs} +2 -2
- package/fesm2022/{edvak99-voice-receptionist-widget-eds-elapsed.pipe-ouRlPpwT.mjs.map → edvak99-voice-receptionist-widget-eds-elapsed.pipe-BgLsnq6A.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-CMhknpGf.mjs → edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-Dn0GrZ6_.mjs} +106 -48
- package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-Dn0GrZ6_.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-knowledge-hub.page-cZqBuull.mjs → edvak99-voice-receptionist-widget-knowledge-hub.page-BngGx_Lc.mjs} +70 -36
- package/fesm2022/edvak99-voice-receptionist-widget-knowledge-hub.page-BngGx_Lc.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-live-calls-list.service-CDtFRW8w.mjs → edvak99-voice-receptionist-widget-live-calls-list.service-0kGWO6sh.mjs} +3 -3
- package/fesm2022/{edvak99-voice-receptionist-widget-live-calls-list.service-CDtFRW8w.mjs.map → edvak99-voice-receptionist-widget-live-calls-list.service-0kGWO6sh.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-live-calls-page.component-DrYzXJQW.mjs → edvak99-voice-receptionist-widget-live-calls-page.component-DfLmK89M.mjs} +7 -7
- package/fesm2022/{edvak99-voice-receptionist-widget-live-calls-page.component-DrYzXJQW.mjs.map → edvak99-voice-receptionist-widget-live-calls-page.component-DfLmK89M.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-live-monitor-page.component-D7dlk0y2.mjs → edvak99-voice-receptionist-widget-live-monitor-page.component-DNpLFqBw.mjs} +11 -11
- package/fesm2022/{edvak99-voice-receptionist-widget-live-monitor-page.component-D7dlk0y2.mjs.map → edvak99-voice-receptionist-widget-live-monitor-page.component-DNpLFqBw.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-outbound-purposes-page.component-CJ8NEjcj.mjs → edvak99-voice-receptionist-widget-outbound-purposes-page.component-CYSZ832p.mjs} +6 -6
- package/fesm2022/{edvak99-voice-receptionist-widget-outbound-purposes-page.component-CJ8NEjcj.mjs.map → edvak99-voice-receptionist-widget-outbound-purposes-page.component-CYSZ832p.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-settings-page.component-Bo3XNK3b.mjs → edvak99-voice-receptionist-widget-settings-page.component-B9nv6wtB.mjs} +4 -4
- package/fesm2022/{edvak99-voice-receptionist-widget-settings-page.component-Bo3XNK3b.mjs.map → edvak99-voice-receptionist-widget-settings-page.component-B9nv6wtB.mjs.map} +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-tasks-page.component-CBwl15NP.mjs → edvak99-voice-receptionist-widget-tasks-page.component-DXxjMVUv.mjs} +40 -11
- package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-DXxjMVUv.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-wizard-page.component-CJLa5pia.mjs → edvak99-voice-receptionist-widget-wizard-page.component-C1Z6yC0q.mjs} +4 -4
- package/fesm2022/{edvak99-voice-receptionist-widget-wizard-page.component-CJLa5pia.mjs.map → edvak99-voice-receptionist-widget-wizard-page.component-C1Z6yC0q.mjs.map} +1 -1
- package/fesm2022/edvak99-voice-receptionist-widget.mjs +1 -1
- package/lib/knowledge/models/practice-faq.model.d.ts +3 -0
- package/lib/knowledge/pages/knowledge-hub.page.d.ts +8 -1
- package/lib/knowledge/services/practice-faq-api.service.d.ts +6 -2
- package/lib/models/ai-receptionist.types.d.ts +1 -0
- package/lib/models/pagination.d.ts +11 -0
- package/lib/pages/audit-page/audit-page.component.d.ts +7 -0
- package/lib/pages/call-history-page/call-history-page.component.d.ts +7 -3
- package/lib/pages/tasks-page/tasks-page.component.d.ts +6 -0
- package/lib/services/ai-receptionist-api.mock.d.ts +10 -9
- package/lib/services/ai-receptionist-api.service.d.ts +9 -9
- package/package.json +1 -1
- package/styles/_index.scss +5 -40
- package/styles/chat-ui.scss +18 -18
- package/styles/eds.scss +135 -95
- package/fesm2022/edvak99-voice-receptionist-widget-audit-page.component-DL1GfQyF.mjs +0 -35
- package/fesm2022/edvak99-voice-receptionist-widget-audit-page.component-DL1GfQyF.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-Cj4wxd4e.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-CMhknpGf.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-knowledge-hub.page-cZqBuull.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-CBwl15NP.mjs.map +0 -1
|
@@ -5,7 +5,7 @@ import * as i1 from '@angular/forms';
|
|
|
5
5
|
import { FormsModule } from '@angular/forms';
|
|
6
6
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
|
7
7
|
import { of, delay, map } from 'rxjs';
|
|
8
|
-
import { V as VOICE_RECEPTIONIST_CONFIG, e as createVoiceReceptionistPaths, E as EdsButtonComponent, M as ModalComponent, T as ToastService, b as EdsIconComponent } from './edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-
|
|
8
|
+
import { V as VOICE_RECEPTIONIST_CONFIG, e as createVoiceReceptionistPaths, D as DEFAULT_PAGE_SIZE, j as asPaginatedResult, E as EdsButtonComponent, M as ModalComponent, T as ToastService, f as totalPages, b as EdsIconComponent } from './edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-Dn0GrZ6_.mjs';
|
|
9
9
|
|
|
10
10
|
const FAQ_CATEGORY_PRESETS = [
|
|
11
11
|
'hours',
|
|
@@ -197,13 +197,24 @@ class PracticeFaqApiService {
|
|
|
197
197
|
get useMock() {
|
|
198
198
|
return !!this.config.useMockApi;
|
|
199
199
|
}
|
|
200
|
-
list(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
200
|
+
list(opts) {
|
|
201
|
+
const page = opts?.page ?? 1;
|
|
202
|
+
const limit = opts?.limit ?? DEFAULT_PAGE_SIZE;
|
|
203
|
+
if (this.useMock) {
|
|
204
|
+
return this.mock.list(!!opts?.activeOnly).pipe(map((items) => {
|
|
205
|
+
const start = (page - 1) * limit;
|
|
206
|
+
return {
|
|
207
|
+
items: items.slice(start, start + limit),
|
|
208
|
+
total: items.length,
|
|
209
|
+
page,
|
|
210
|
+
limit,
|
|
211
|
+
};
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
let params = new HttpParams().set('page', String(page)).set('limit', String(limit));
|
|
215
|
+
if (opts?.activeOnly)
|
|
205
216
|
params = params.set('activeOnly', 'true');
|
|
206
|
-
return this.http.get(this.base, { params }).pipe(map((body) =>
|
|
217
|
+
return this.http.get(this.base, { params }).pipe(map((body) => asPaginatedResult(body, page, limit)));
|
|
207
218
|
}
|
|
208
219
|
get(faqId) {
|
|
209
220
|
if (this.useMock)
|
|
@@ -234,11 +245,6 @@ class PracticeFaqApiService {
|
|
|
234
245
|
const params = new HttpParams().set('q', q).set('limit', String(limit));
|
|
235
246
|
return this.http.get(`${this.base}/search`, { params });
|
|
236
247
|
}
|
|
237
|
-
asList(body) {
|
|
238
|
-
if (Array.isArray(body))
|
|
239
|
-
return body;
|
|
240
|
-
return body.items ?? [];
|
|
241
|
-
}
|
|
242
248
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: PracticeFaqApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
243
249
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: PracticeFaqApiService, providedIn: 'root' }); }
|
|
244
250
|
}
|
|
@@ -276,11 +282,11 @@ class FaqEmptyStateComponent {
|
|
|
276
282
|
this.useTemplate.emit(t);
|
|
277
283
|
}
|
|
278
284
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FaqEmptyStateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
279
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: FaqEmptyStateComponent, isStandalone: true, selector: "eds-faq-empty-state", outputs: { add: "add", useTemplate: "useTemplate" }, ngImport: i0, template: "<div class=\"faq-empty-state\">\n <div class=\"faq-empty-state__icon\" aria-hidden=\"true\">?</div>\n <h2 class=\"eds-h2\">No FAQs yet</h2>\n <p class=\"eds-body-sm\">Add answers your AI receptionist can use on live calls.</p>\n <eds-button variant=\"primary\" (click)=\"onAdd()\">Add your first FAQ</eds-button>\n\n <p class=\"eds-label faq-empty-state__suggest\">Suggested starters</p>\n <ul class=\"faq-empty-state__starters\">\n @for (t of starters; track t.label) {\n <li>\n <button type=\"button\" class=\"faq-empty-state__starter\" (click)=\"onTemplate(t)\">\n {{ t.label }}\n </button>\n </li>\n }\n </ul>\n</div>\n", styles: [".faq-empty-state{text-align:center;padding:48px 24px}.faq-empty-state__icon{width:56px;height:56px;margin:0 auto 12px;border-radius:50%;background:#0d94881f;color:#0d9488;font-size:28px;font-weight:700;display:flex;align-items:center;justify-content:center}.faq-empty-state__suggest{margin:24px 0 8px}.faq-empty-state__starters{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;justify-content:center;gap:8px}.faq-empty-state__starter{border:1px dashed var(--border-subtle, #d1d5db);background:var(--surface-l0, #fff);border-radius:999px;padding:6px 12px;font-size:13px;cursor:pointer}.faq-empty-state__starter:hover{border-color:var(--eds-brand, #0d9488);color:var(--eds-brand, #0d9488)}\n"], dependencies: [{ kind: "component", type: EdsButtonComponent, selector: "eds-button", inputs: ["variant", "type", "disabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
285
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: FaqEmptyStateComponent, isStandalone: true, selector: "eds-faq-empty-state", outputs: { add: "add", useTemplate: "useTemplate" }, ngImport: i0, template: "<div class=\"faq-empty-state\">\n <div class=\"faq-empty-state__icon\" aria-hidden=\"true\">?</div>\n <h2 class=\"eds-h2\">No FAQs yet</h2>\n <p class=\"eds-body-sm\">Add answers your AI receptionist can use on live calls.</p>\n <eds-button variant=\"primary\" (click)=\"onAdd()\">Add your first FAQ</eds-button>\n\n <p class=\"eds-label faq-empty-state__suggest\">Suggested starters</p>\n <ul class=\"faq-empty-state__starters\">\n @for (t of starters; track t.label) {\n <li>\n <button type=\"button\" class=\"faq-empty-state__starter\" (click)=\"onTemplate(t)\">\n {{ t.label }}\n </button>\n </li>\n }\n </ul>\n</div>\n", styles: [".faq-empty-state{text-align:center;padding:48px 24px}.faq-empty-state__icon{width:56px;height:56px;margin:0 auto 12px;border-radius:50%;background:#0d94881f;color:#0d9488;font-size:28px;font-weight:700;display:flex;align-items:center;justify-content:center}.faq-empty-state__suggest{margin:24px 0 8px}.faq-empty-state__starters{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;justify-content:center;gap:8px}.faq-empty-state__starter{border:1px dashed var(--eds-border-subtle, #d1d5db);background:var(--eds-surface-l0, #fff);border-radius:999px;padding:6px 12px;font-size:13px;cursor:pointer}.faq-empty-state__starter:hover{border-color:var(--eds-brand, #0d9488);color:var(--eds-brand, #0d9488)}\n"], dependencies: [{ kind: "component", type: EdsButtonComponent, selector: "eds-button", inputs: ["variant", "type", "disabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
280
286
|
}
|
|
281
287
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FaqEmptyStateComponent, decorators: [{
|
|
282
288
|
type: Component,
|
|
283
|
-
args: [{ selector: 'eds-faq-empty-state', imports: [EdsButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"faq-empty-state\">\n <div class=\"faq-empty-state__icon\" aria-hidden=\"true\">?</div>\n <h2 class=\"eds-h2\">No FAQs yet</h2>\n <p class=\"eds-body-sm\">Add answers your AI receptionist can use on live calls.</p>\n <eds-button variant=\"primary\" (click)=\"onAdd()\">Add your first FAQ</eds-button>\n\n <p class=\"eds-label faq-empty-state__suggest\">Suggested starters</p>\n <ul class=\"faq-empty-state__starters\">\n @for (t of starters; track t.label) {\n <li>\n <button type=\"button\" class=\"faq-empty-state__starter\" (click)=\"onTemplate(t)\">\n {{ t.label }}\n </button>\n </li>\n }\n </ul>\n</div>\n", styles: [".faq-empty-state{text-align:center;padding:48px 24px}.faq-empty-state__icon{width:56px;height:56px;margin:0 auto 12px;border-radius:50%;background:#0d94881f;color:#0d9488;font-size:28px;font-weight:700;display:flex;align-items:center;justify-content:center}.faq-empty-state__suggest{margin:24px 0 8px}.faq-empty-state__starters{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;justify-content:center;gap:8px}.faq-empty-state__starter{border:1px dashed var(--border-subtle, #d1d5db);background:var(--surface-l0, #fff);border-radius:999px;padding:6px 12px;font-size:13px;cursor:pointer}.faq-empty-state__starter:hover{border-color:var(--eds-brand, #0d9488);color:var(--eds-brand, #0d9488)}\n"] }]
|
|
289
|
+
args: [{ selector: 'eds-faq-empty-state', imports: [EdsButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"faq-empty-state\">\n <div class=\"faq-empty-state__icon\" aria-hidden=\"true\">?</div>\n <h2 class=\"eds-h2\">No FAQs yet</h2>\n <p class=\"eds-body-sm\">Add answers your AI receptionist can use on live calls.</p>\n <eds-button variant=\"primary\" (click)=\"onAdd()\">Add your first FAQ</eds-button>\n\n <p class=\"eds-label faq-empty-state__suggest\">Suggested starters</p>\n <ul class=\"faq-empty-state__starters\">\n @for (t of starters; track t.label) {\n <li>\n <button type=\"button\" class=\"faq-empty-state__starter\" (click)=\"onTemplate(t)\">\n {{ t.label }}\n </button>\n </li>\n }\n </ul>\n</div>\n", styles: [".faq-empty-state{text-align:center;padding:48px 24px}.faq-empty-state__icon{width:56px;height:56px;margin:0 auto 12px;border-radius:50%;background:#0d94881f;color:#0d9488;font-size:28px;font-weight:700;display:flex;align-items:center;justify-content:center}.faq-empty-state__suggest{margin:24px 0 8px}.faq-empty-state__starters{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;justify-content:center;gap:8px}.faq-empty-state__starter{border:1px dashed var(--eds-border-subtle, #d1d5db);background:var(--eds-surface-l0, #fff);border-radius:999px;padding:6px 12px;font-size:13px;cursor:pointer}.faq-empty-state__starter:hover{border-color:var(--eds-brand, #0d9488);color:var(--eds-brand, #0d9488)}\n"] }]
|
|
284
290
|
}], propDecorators: { add: [{
|
|
285
291
|
type: Output
|
|
286
292
|
}], useTemplate: [{
|
|
@@ -360,11 +366,11 @@ class FaqFormComponent {
|
|
|
360
366
|
}
|
|
361
367
|
}
|
|
362
368
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FaqFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
363
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: FaqFormComponent, isStandalone: true, selector: "eds-faq-form", inputs: { open: "open", faq: "faq", initialQuestion: "initialQuestion", initialAnswer: "initialAnswer", initialCategory: "initialCategory" }, outputs: { closed: "closed", saved: "saved" }, usesOnChanges: true, ngImport: i0, template: "@if (open) {\n <app-modal\n [title]=\"isEdit ? 'Edit FAQ' : 'Add FAQ'\"\n subtitle=\"Short, speakable answers work best on phone calls\"\n maxWidth=\"640px\"\n (closed)=\"close()\"\n >\n <form class=\"faq-form\" (ngSubmit)=\"submit()\" novalidate>\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Question <span class=\"faq-form__req\">*</span></span>\n <span class=\"faq-form__hint\">How a caller might ask</span>\n <textarea\n class=\"faq-form__textarea\"\n rows=\"2\"\n maxlength=\"500\"\n [(ngModel)]=\"question\"\n name=\"question\"\n required\n ></textarea>\n <span class=\"faq-form__count\">{{ question.length }}/500</span>\n </label>\n\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Answer <span class=\"faq-form__req\">*</span></span>\n <span class=\"faq-form__hint\">Write how you want the AI to speak on the phone \u2014 short, clear, no bullet points.</span>\n <textarea\n class=\"faq-form__textarea\"\n rows=\"4\"\n maxlength=\"2000\"\n [(ngModel)]=\"answer\"\n name=\"answer\"\n required\n ></textarea>\n <span class=\"faq-form__count\">{{ answer.length }}/2000</span>\n </label>\n\n <div class=\"faq-form__row\">\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Category</span>\n <select class=\"faq-form__input\" [(ngModel)]=\"category\" name=\"category\">\n <option value=\"\">\u2014 None \u2014</option>\n @for (c of categories; track c) {\n <option [value]=\"c\">{{ c }}</option>\n }\n </select>\n </label>\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Sort order</span>\n <input type=\"number\" class=\"faq-form__input\" [(ngModel)]=\"sortOrder\" name=\"sortOrder\" min=\"0\" />\n </label>\n </div>\n\n <label class=\"faq-form__check\">\n <input type=\"checkbox\" [(ngModel)]=\"isActive\" name=\"isActive\" />\n Active (voice agent can use this answer)\n </label>\n\n <section class=\"faq-form__preview eds-card\">\n <h3 class=\"eds-label\">Live preview</h3>\n <p class=\"faq-form__preview-line\"><strong>Caller asks:</strong> \u201C{{ question || '\u2026' }}\u201D</p>\n <p class=\"faq-form__preview-line\"><strong>AI would say:</strong> \u201C{{ answer || '\u2026' }}\u201D</p>\n </section>\n\n @if (error()) {\n <p class=\"faq-form__error\" role=\"alert\">{{ error() }}</p>\n }\n\n <footer class=\"faq-form__footer\">\n <eds-button variant=\"secondary\" type=\"button\" (click)=\"close()\">Cancel</eds-button>\n <eds-button variant=\"primary\" type=\"submit\">Save</eds-button>\n </footer>\n </form>\n </app-modal>\n}\n", styles: [".faq-form{display:flex;flex-direction:column;gap:14px}.faq-form__field{display:flex;flex-direction:column;gap:4px}.faq-form__hint{font-size:12px;color:var(--text-secondary)}.faq-form__textarea,.faq-form__input{padding:8px 10px;border:1px solid var(--border-subtle, #e4e4e7);border-radius:8px;font:inherit}.faq-form__textarea{resize:vertical}.faq-form__count{font-size:11px;color:var(--text-secondary);text-align:right}.faq-form__req{color:#dc2626}.faq-form__row{display:grid;grid-template-columns:1fr 120px;gap:12px}.faq-form__check{display:flex;align-items:center;gap:8px;font-size:13px}.faq-form__preview{padding:12px;background:var(--surface-muted, #f8fafc)}.faq-form__preview-line{margin:6px 0 0;font-size:14px;line-height:1.5}.faq-form__error{color:#b91c1c;font-size:13px;margin:0}.faq-form__footer{display:flex;justify-content:flex-end;gap:8px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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.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.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.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 }); }
|
|
369
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: FaqFormComponent, isStandalone: true, selector: "eds-faq-form", inputs: { open: "open", faq: "faq", initialQuestion: "initialQuestion", initialAnswer: "initialAnswer", initialCategory: "initialCategory" }, outputs: { closed: "closed", saved: "saved" }, usesOnChanges: true, ngImport: i0, template: "@if (open) {\n <app-modal\n [title]=\"isEdit ? 'Edit FAQ' : 'Add FAQ'\"\n subtitle=\"Short, speakable answers work best on phone calls\"\n maxWidth=\"640px\"\n (closed)=\"close()\"\n >\n <form class=\"faq-form\" (ngSubmit)=\"submit()\" novalidate>\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Question <span class=\"faq-form__req\">*</span></span>\n <span class=\"faq-form__hint\">How a caller might ask</span>\n <textarea\n class=\"faq-form__textarea\"\n rows=\"2\"\n maxlength=\"500\"\n [(ngModel)]=\"question\"\n name=\"question\"\n required\n ></textarea>\n <span class=\"faq-form__count\">{{ question.length }}/500</span>\n </label>\n\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Answer <span class=\"faq-form__req\">*</span></span>\n <span class=\"faq-form__hint\">Write how you want the AI to speak on the phone \u2014 short, clear, no bullet points.</span>\n <textarea\n class=\"faq-form__textarea\"\n rows=\"4\"\n maxlength=\"2000\"\n [(ngModel)]=\"answer\"\n name=\"answer\"\n required\n ></textarea>\n <span class=\"faq-form__count\">{{ answer.length }}/2000</span>\n </label>\n\n <div class=\"faq-form__row\">\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Category</span>\n <select class=\"faq-form__input\" [(ngModel)]=\"category\" name=\"category\">\n <option value=\"\">\u2014 None \u2014</option>\n @for (c of categories; track c) {\n <option [value]=\"c\">{{ c }}</option>\n }\n </select>\n </label>\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Sort order</span>\n <input type=\"number\" class=\"faq-form__input\" [(ngModel)]=\"sortOrder\" name=\"sortOrder\" min=\"0\" />\n </label>\n </div>\n\n <label class=\"faq-form__check\">\n <input type=\"checkbox\" [(ngModel)]=\"isActive\" name=\"isActive\" />\n Active (voice agent can use this answer)\n </label>\n\n <section class=\"faq-form__preview eds-card\">\n <h3 class=\"eds-label\">Live preview</h3>\n <p class=\"faq-form__preview-line\"><strong>Caller asks:</strong> \u201C{{ question || '\u2026' }}\u201D</p>\n <p class=\"faq-form__preview-line\"><strong>AI would say:</strong> \u201C{{ answer || '\u2026' }}\u201D</p>\n </section>\n\n @if (error()) {\n <p class=\"faq-form__error\" role=\"alert\">{{ error() }}</p>\n }\n\n <footer class=\"faq-form__footer\">\n <eds-button variant=\"secondary\" type=\"button\" (click)=\"close()\">Cancel</eds-button>\n <eds-button variant=\"primary\" type=\"submit\">Save</eds-button>\n </footer>\n </form>\n </app-modal>\n}\n", styles: [".faq-form{display:flex;flex-direction:column;gap:14px}.faq-form__field{display:flex;flex-direction:column;gap:4px}.faq-form__hint{font-size:12px;color:var(--eds-text-secondary)}.faq-form__textarea,.faq-form__input{padding:8px 10px;border:1px solid var(--eds-border-subtle, #e4e4e7);border-radius:8px;font:inherit}.faq-form__textarea{resize:vertical}.faq-form__count{font-size:11px;color:var(--eds-text-secondary);text-align:right}.faq-form__req{color:#dc2626}.faq-form__row{display:grid;grid-template-columns:1fr 120px;gap:12px}.faq-form__check{display:flex;align-items:center;gap:8px;font-size:13px}.faq-form__preview{padding:12px;background:var(--eds-surface-muted, #f8fafc)}.faq-form__preview-line{margin:6px 0 0;font-size:14px;line-height:1.5}.faq-form__error{color:#b91c1c;font-size:13px;margin:0}.faq-form__footer{display:flex;justify-content:flex-end;gap:8px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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.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.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.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 }); }
|
|
364
370
|
}
|
|
365
371
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FaqFormComponent, decorators: [{
|
|
366
372
|
type: Component,
|
|
367
|
-
args: [{ selector: 'eds-faq-form', imports: [FormsModule, ModalComponent, EdsButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (open) {\n <app-modal\n [title]=\"isEdit ? 'Edit FAQ' : 'Add FAQ'\"\n subtitle=\"Short, speakable answers work best on phone calls\"\n maxWidth=\"640px\"\n (closed)=\"close()\"\n >\n <form class=\"faq-form\" (ngSubmit)=\"submit()\" novalidate>\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Question <span class=\"faq-form__req\">*</span></span>\n <span class=\"faq-form__hint\">How a caller might ask</span>\n <textarea\n class=\"faq-form__textarea\"\n rows=\"2\"\n maxlength=\"500\"\n [(ngModel)]=\"question\"\n name=\"question\"\n required\n ></textarea>\n <span class=\"faq-form__count\">{{ question.length }}/500</span>\n </label>\n\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Answer <span class=\"faq-form__req\">*</span></span>\n <span class=\"faq-form__hint\">Write how you want the AI to speak on the phone \u2014 short, clear, no bullet points.</span>\n <textarea\n class=\"faq-form__textarea\"\n rows=\"4\"\n maxlength=\"2000\"\n [(ngModel)]=\"answer\"\n name=\"answer\"\n required\n ></textarea>\n <span class=\"faq-form__count\">{{ answer.length }}/2000</span>\n </label>\n\n <div class=\"faq-form__row\">\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Category</span>\n <select class=\"faq-form__input\" [(ngModel)]=\"category\" name=\"category\">\n <option value=\"\">\u2014 None \u2014</option>\n @for (c of categories; track c) {\n <option [value]=\"c\">{{ c }}</option>\n }\n </select>\n </label>\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Sort order</span>\n <input type=\"number\" class=\"faq-form__input\" [(ngModel)]=\"sortOrder\" name=\"sortOrder\" min=\"0\" />\n </label>\n </div>\n\n <label class=\"faq-form__check\">\n <input type=\"checkbox\" [(ngModel)]=\"isActive\" name=\"isActive\" />\n Active (voice agent can use this answer)\n </label>\n\n <section class=\"faq-form__preview eds-card\">\n <h3 class=\"eds-label\">Live preview</h3>\n <p class=\"faq-form__preview-line\"><strong>Caller asks:</strong> \u201C{{ question || '\u2026' }}\u201D</p>\n <p class=\"faq-form__preview-line\"><strong>AI would say:</strong> \u201C{{ answer || '\u2026' }}\u201D</p>\n </section>\n\n @if (error()) {\n <p class=\"faq-form__error\" role=\"alert\">{{ error() }}</p>\n }\n\n <footer class=\"faq-form__footer\">\n <eds-button variant=\"secondary\" type=\"button\" (click)=\"close()\">Cancel</eds-button>\n <eds-button variant=\"primary\" type=\"submit\">Save</eds-button>\n </footer>\n </form>\n </app-modal>\n}\n", styles: [".faq-form{display:flex;flex-direction:column;gap:14px}.faq-form__field{display:flex;flex-direction:column;gap:4px}.faq-form__hint{font-size:12px;color:var(--text-secondary)}.faq-form__textarea,.faq-form__input{padding:8px 10px;border:1px solid var(--border-subtle, #e4e4e7);border-radius:8px;font:inherit}.faq-form__textarea{resize:vertical}.faq-form__count{font-size:11px;color:var(--text-secondary);text-align:right}.faq-form__req{color:#dc2626}.faq-form__row{display:grid;grid-template-columns:1fr 120px;gap:12px}.faq-form__check{display:flex;align-items:center;gap:8px;font-size:13px}.faq-form__preview{padding:12px;background:var(--surface-muted, #f8fafc)}.faq-form__preview-line{margin:6px 0 0;font-size:14px;line-height:1.5}.faq-form__error{color:#b91c1c;font-size:13px;margin:0}.faq-form__footer{display:flex;justify-content:flex-end;gap:8px}\n"] }]
|
|
373
|
+
args: [{ selector: 'eds-faq-form', imports: [FormsModule, ModalComponent, EdsButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (open) {\n <app-modal\n [title]=\"isEdit ? 'Edit FAQ' : 'Add FAQ'\"\n subtitle=\"Short, speakable answers work best on phone calls\"\n maxWidth=\"640px\"\n (closed)=\"close()\"\n >\n <form class=\"faq-form\" (ngSubmit)=\"submit()\" novalidate>\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Question <span class=\"faq-form__req\">*</span></span>\n <span class=\"faq-form__hint\">How a caller might ask</span>\n <textarea\n class=\"faq-form__textarea\"\n rows=\"2\"\n maxlength=\"500\"\n [(ngModel)]=\"question\"\n name=\"question\"\n required\n ></textarea>\n <span class=\"faq-form__count\">{{ question.length }}/500</span>\n </label>\n\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Answer <span class=\"faq-form__req\">*</span></span>\n <span class=\"faq-form__hint\">Write how you want the AI to speak on the phone \u2014 short, clear, no bullet points.</span>\n <textarea\n class=\"faq-form__textarea\"\n rows=\"4\"\n maxlength=\"2000\"\n [(ngModel)]=\"answer\"\n name=\"answer\"\n required\n ></textarea>\n <span class=\"faq-form__count\">{{ answer.length }}/2000</span>\n </label>\n\n <div class=\"faq-form__row\">\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Category</span>\n <select class=\"faq-form__input\" [(ngModel)]=\"category\" name=\"category\">\n <option value=\"\">\u2014 None \u2014</option>\n @for (c of categories; track c) {\n <option [value]=\"c\">{{ c }}</option>\n }\n </select>\n </label>\n <label class=\"faq-form__field\">\n <span class=\"eds-label\">Sort order</span>\n <input type=\"number\" class=\"faq-form__input\" [(ngModel)]=\"sortOrder\" name=\"sortOrder\" min=\"0\" />\n </label>\n </div>\n\n <label class=\"faq-form__check\">\n <input type=\"checkbox\" [(ngModel)]=\"isActive\" name=\"isActive\" />\n Active (voice agent can use this answer)\n </label>\n\n <section class=\"faq-form__preview eds-card\">\n <h3 class=\"eds-label\">Live preview</h3>\n <p class=\"faq-form__preview-line\"><strong>Caller asks:</strong> \u201C{{ question || '\u2026' }}\u201D</p>\n <p class=\"faq-form__preview-line\"><strong>AI would say:</strong> \u201C{{ answer || '\u2026' }}\u201D</p>\n </section>\n\n @if (error()) {\n <p class=\"faq-form__error\" role=\"alert\">{{ error() }}</p>\n }\n\n <footer class=\"faq-form__footer\">\n <eds-button variant=\"secondary\" type=\"button\" (click)=\"close()\">Cancel</eds-button>\n <eds-button variant=\"primary\" type=\"submit\">Save</eds-button>\n </footer>\n </form>\n </app-modal>\n}\n", styles: [".faq-form{display:flex;flex-direction:column;gap:14px}.faq-form__field{display:flex;flex-direction:column;gap:4px}.faq-form__hint{font-size:12px;color:var(--eds-text-secondary)}.faq-form__textarea,.faq-form__input{padding:8px 10px;border:1px solid var(--eds-border-subtle, #e4e4e7);border-radius:8px;font:inherit}.faq-form__textarea{resize:vertical}.faq-form__count{font-size:11px;color:var(--eds-text-secondary);text-align:right}.faq-form__req{color:#dc2626}.faq-form__row{display:grid;grid-template-columns:1fr 120px;gap:12px}.faq-form__check{display:flex;align-items:center;gap:8px;font-size:13px}.faq-form__preview{padding:12px;background:var(--eds-surface-muted, #f8fafc)}.faq-form__preview-line{margin:6px 0 0;font-size:14px;line-height:1.5}.faq-form__error{color:#b91c1c;font-size:13px;margin:0}.faq-form__footer{display:flex;justify-content:flex-end;gap:8px}\n"] }]
|
|
368
374
|
}], propDecorators: { open: [{
|
|
369
375
|
type: Input
|
|
370
376
|
}], faq: [{
|
|
@@ -414,18 +420,21 @@ class FaqSearchTesterComponent {
|
|
|
414
420
|
});
|
|
415
421
|
}
|
|
416
422
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FaqSearchTesterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
417
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: FaqSearchTesterComponent, isStandalone: true, selector: "eds-faq-search-tester", ngImport: i0, template: "<aside class=\"eds-card faq-search-tester\">\n <h2 class=\"eds-h2\">Test voice search</h2>\n <p class=\"eds-body-sm faq-search-tester__help\">This uses the same search as live calls.</p>\n\n <label class=\"faq-search-tester__field\">\n <span class=\"eds-label\">What a caller might ask\u2026</span>\n <input\n type=\"search\"\n class=\"faq-search-tester__input\"\n placeholder=\"Do you have Saturday hours?\"\n [(ngModel)]=\"query\"\n (keyup.enter)=\"search()\"\n />\n </label>\n\n <eds-button variant=\"primary\" [disabled]=\"loading() || !query.trim()\" (click)=\"search()\">\n {{ loading() ? 'Searching\u2026' : 'Test search' }}\n </eds-button>\n\n @if (error()) {\n <p class=\"faq-search-tester__error\" role=\"alert\">{{ error() }}</p>\n }\n\n @if (searched()) {\n @if (found()) {\n <ul class=\"faq-search-tester__results\">\n @for (item of results(); track item.faqId) {\n <li class=\"faq-search-tester__result\">\n <div class=\"faq-search-tester__score-row\">\n <span class=\"eds-label\">Relevance</span>\n <div class=\"faq-search-tester__score-bar\">\n <span class=\"faq-search-tester__score-fill\" [style.width.%]=\"scorePercent(item.score)\"></span>\n </div>\n <span class=\"eds-mono-sm\">{{ scorePercent(item.score) }}%</span>\n </div>\n @if (item.category) {\n <span class=\"faq-chip\" [class]=\"categoryClass(item.category)\">{{ item.category }}</span>\n }\n <p class=\"faq-search-tester__question\">{{ item.question }}</p>\n <p class=\"faq-search-tester__answer\">{{ item.answer }}</p>\n </li>\n }\n </ul>\n } @else {\n <p class=\"faq-search-tester__no-match\" role=\"status\">\n No match \u2014 voice agent will offer to transfer to reception\n </p>\n }\n }\n</aside>\n", styles: [".faq-chip{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:600;text-transform:capitalize}.faq-chip--hours{background:#3b82f61f;color:#2563eb}.faq-chip--location{background:#14b8a61f;color:#0d9488}.faq-chip--parking{background:#6b72801f;color:#4b5563}.faq-chip--insurance{background:#8b5cf61f;color:#7c3aed}.faq-chip--services{background:#22c55e1f;color:#16a34a}.faq-chip--policies{background:#f973161f;color:#ea580c}.faq-chip--general{background:#6b72801f;color:#6b7280}.faq-search-tester{padding:16px;position:sticky;top:calc(var(--eds-topbar-height, 56px) + 16px)}.faq-search-tester__help{color:var(--text-secondary);margin:4px 0 12px}.faq-search-tester__field{display:flex;flex-direction:column;gap:6px;margin-bottom:10px}.faq-search-tester__input{padding:8px 10px;border:1px solid var(--border-subtle, #e4e4e7);border-radius:8px;font:inherit}.faq-search-tester__error{color:#b91c1c;font-size:13px;margin-top:10px}.faq-search-tester__no-match{margin-top:14px;padding:12px;border-radius:8px;background:#f59e0b1f;color:#b45309;font-size:13px}.faq-search-tester__results{list-style:none;margin:14px 0 0;padding:0;display:flex;flex-direction:column;gap:12px}.faq-search-tester__result{padding:12px;border:1px solid var(--border-subtle, #e4e4e7);border-radius:8px}.faq-search-tester__score-row{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:8px;margin-bottom:8px}.faq-search-tester__score-bar{height:6px;border-radius:999px;background:var(--surface-muted, #e5e7eb);overflow:hidden}.faq-search-tester__score-fill{display:block;height:100%;background:var(--eds-brand, #0d9488);border-radius:999px}.faq-search-tester__question{font-weight:600;font-size:13px;margin:6px 0 4px}.faq-search-tester__answer{font-size:13px;color:var(--text-secondary);margin:0;line-height:1.5}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.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: EdsButtonComponent, selector: "eds-button", inputs: ["variant", "type", "disabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
423
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: FaqSearchTesterComponent, isStandalone: true, selector: "eds-faq-search-tester", ngImport: i0, template: "<aside class=\"eds-card faq-search-tester\">\n <h2 class=\"eds-h2\">Test voice search</h2>\n <p class=\"eds-body-sm faq-search-tester__help\">This uses the same search as live calls.</p>\n\n <label class=\"faq-search-tester__field\">\n <span class=\"eds-label\">What a caller might ask\u2026</span>\n <input\n type=\"search\"\n class=\"faq-search-tester__input\"\n placeholder=\"Do you have Saturday hours?\"\n [(ngModel)]=\"query\"\n (keyup.enter)=\"search()\"\n />\n </label>\n\n <eds-button variant=\"primary\" [disabled]=\"loading() || !query.trim()\" (click)=\"search()\">\n {{ loading() ? 'Searching\u2026' : 'Test search' }}\n </eds-button>\n\n @if (error()) {\n <p class=\"faq-search-tester__error\" role=\"alert\">{{ error() }}</p>\n }\n\n @if (searched()) {\n @if (found()) {\n <ul class=\"faq-search-tester__results\">\n @for (item of results(); track item.faqId) {\n <li class=\"faq-search-tester__result\">\n <div class=\"faq-search-tester__score-row\">\n <span class=\"eds-label\">Relevance</span>\n <div class=\"faq-search-tester__score-bar\">\n <span class=\"faq-search-tester__score-fill\" [style.width.%]=\"scorePercent(item.score)\"></span>\n </div>\n <span class=\"eds-mono-sm\">{{ scorePercent(item.score) }}%</span>\n </div>\n @if (item.category) {\n <span class=\"faq-chip\" [class]=\"categoryClass(item.category)\">{{ item.category }}</span>\n }\n <p class=\"faq-search-tester__question\">{{ item.question }}</p>\n <p class=\"faq-search-tester__answer\">{{ item.answer }}</p>\n </li>\n }\n </ul>\n } @else {\n <p class=\"faq-search-tester__no-match\" role=\"status\">\n No match \u2014 voice agent will offer to transfer to reception\n </p>\n }\n }\n</aside>\n", styles: [".faq-chip{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:600;text-transform:capitalize}.faq-chip--hours{background:#3b82f61f;color:#2563eb}.faq-chip--location{background:#14b8a61f;color:#0d9488}.faq-chip--parking{background:#6b72801f;color:#4b5563}.faq-chip--insurance{background:#8b5cf61f;color:#7c3aed}.faq-chip--services{background:#22c55e1f;color:#16a34a}.faq-chip--policies{background:#f973161f;color:#ea580c}.faq-chip--general{background:#6b72801f;color:#6b7280}.faq-search-tester{padding:16px;position:sticky;top:calc(var(--eds-topbar-height, 56px) + 16px)}.faq-search-tester__help{color:var(--eds-text-secondary);margin:4px 0 12px}.faq-search-tester__field{display:flex;flex-direction:column;gap:6px;margin-bottom:10px}.faq-search-tester__input{padding:8px 10px;border:1px solid var(--eds-border-subtle, #e4e4e7);border-radius:8px;font:inherit}.faq-search-tester__error{color:#b91c1c;font-size:13px;margin-top:10px}.faq-search-tester__no-match{margin-top:14px;padding:12px;border-radius:8px;background:#f59e0b1f;color:#b45309;font-size:13px}.faq-search-tester__results{list-style:none;margin:14px 0 0;padding:0;display:flex;flex-direction:column;gap:12px}.faq-search-tester__result{padding:12px;border:1px solid var(--eds-border-subtle, #e4e4e7);border-radius:8px}.faq-search-tester__score-row{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:8px;margin-bottom:8px}.faq-search-tester__score-bar{height:6px;border-radius:999px;background:var(--eds-surface-muted, #e5e7eb);overflow:hidden}.faq-search-tester__score-fill{display:block;height:100%;background:var(--eds-brand, #0d9488);border-radius:999px}.faq-search-tester__question{font-weight:600;font-size:13px;margin:6px 0 4px}.faq-search-tester__answer{font-size:13px;color:var(--eds-text-secondary);margin:0;line-height:1.5}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.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: EdsButtonComponent, selector: "eds-button", inputs: ["variant", "type", "disabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
418
424
|
}
|
|
419
425
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FaqSearchTesterComponent, decorators: [{
|
|
420
426
|
type: Component,
|
|
421
|
-
args: [{ selector: 'eds-faq-search-tester', imports: [FormsModule, EdsButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<aside class=\"eds-card faq-search-tester\">\n <h2 class=\"eds-h2\">Test voice search</h2>\n <p class=\"eds-body-sm faq-search-tester__help\">This uses the same search as live calls.</p>\n\n <label class=\"faq-search-tester__field\">\n <span class=\"eds-label\">What a caller might ask\u2026</span>\n <input\n type=\"search\"\n class=\"faq-search-tester__input\"\n placeholder=\"Do you have Saturday hours?\"\n [(ngModel)]=\"query\"\n (keyup.enter)=\"search()\"\n />\n </label>\n\n <eds-button variant=\"primary\" [disabled]=\"loading() || !query.trim()\" (click)=\"search()\">\n {{ loading() ? 'Searching\u2026' : 'Test search' }}\n </eds-button>\n\n @if (error()) {\n <p class=\"faq-search-tester__error\" role=\"alert\">{{ error() }}</p>\n }\n\n @if (searched()) {\n @if (found()) {\n <ul class=\"faq-search-tester__results\">\n @for (item of results(); track item.faqId) {\n <li class=\"faq-search-tester__result\">\n <div class=\"faq-search-tester__score-row\">\n <span class=\"eds-label\">Relevance</span>\n <div class=\"faq-search-tester__score-bar\">\n <span class=\"faq-search-tester__score-fill\" [style.width.%]=\"scorePercent(item.score)\"></span>\n </div>\n <span class=\"eds-mono-sm\">{{ scorePercent(item.score) }}%</span>\n </div>\n @if (item.category) {\n <span class=\"faq-chip\" [class]=\"categoryClass(item.category)\">{{ item.category }}</span>\n }\n <p class=\"faq-search-tester__question\">{{ item.question }}</p>\n <p class=\"faq-search-tester__answer\">{{ item.answer }}</p>\n </li>\n }\n </ul>\n } @else {\n <p class=\"faq-search-tester__no-match\" role=\"status\">\n No match \u2014 voice agent will offer to transfer to reception\n </p>\n }\n }\n</aside>\n", styles: [".faq-chip{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:600;text-transform:capitalize}.faq-chip--hours{background:#3b82f61f;color:#2563eb}.faq-chip--location{background:#14b8a61f;color:#0d9488}.faq-chip--parking{background:#6b72801f;color:#4b5563}.faq-chip--insurance{background:#8b5cf61f;color:#7c3aed}.faq-chip--services{background:#22c55e1f;color:#16a34a}.faq-chip--policies{background:#f973161f;color:#ea580c}.faq-chip--general{background:#6b72801f;color:#6b7280}.faq-search-tester{padding:16px;position:sticky;top:calc(var(--eds-topbar-height, 56px) + 16px)}.faq-search-tester__help{color:var(--text-secondary);margin:4px 0 12px}.faq-search-tester__field{display:flex;flex-direction:column;gap:6px;margin-bottom:10px}.faq-search-tester__input{padding:8px 10px;border:1px solid var(--border-subtle, #e4e4e7);border-radius:8px;font:inherit}.faq-search-tester__error{color:#b91c1c;font-size:13px;margin-top:10px}.faq-search-tester__no-match{margin-top:14px;padding:12px;border-radius:8px;background:#f59e0b1f;color:#b45309;font-size:13px}.faq-search-tester__results{list-style:none;margin:14px 0 0;padding:0;display:flex;flex-direction:column;gap:12px}.faq-search-tester__result{padding:12px;border:1px solid var(--border-subtle, #e4e4e7);border-radius:8px}.faq-search-tester__score-row{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:8px;margin-bottom:8px}.faq-search-tester__score-bar{height:6px;border-radius:999px;background:var(--surface-muted, #e5e7eb);overflow:hidden}.faq-search-tester__score-fill{display:block;height:100%;background:var(--eds-brand, #0d9488);border-radius:999px}.faq-search-tester__question{font-weight:600;font-size:13px;margin:6px 0 4px}.faq-search-tester__answer{font-size:13px;color:var(--text-secondary);margin:0;line-height:1.5}\n"] }]
|
|
427
|
+
args: [{ selector: 'eds-faq-search-tester', imports: [FormsModule, EdsButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<aside class=\"eds-card faq-search-tester\">\n <h2 class=\"eds-h2\">Test voice search</h2>\n <p class=\"eds-body-sm faq-search-tester__help\">This uses the same search as live calls.</p>\n\n <label class=\"faq-search-tester__field\">\n <span class=\"eds-label\">What a caller might ask\u2026</span>\n <input\n type=\"search\"\n class=\"faq-search-tester__input\"\n placeholder=\"Do you have Saturday hours?\"\n [(ngModel)]=\"query\"\n (keyup.enter)=\"search()\"\n />\n </label>\n\n <eds-button variant=\"primary\" [disabled]=\"loading() || !query.trim()\" (click)=\"search()\">\n {{ loading() ? 'Searching\u2026' : 'Test search' }}\n </eds-button>\n\n @if (error()) {\n <p class=\"faq-search-tester__error\" role=\"alert\">{{ error() }}</p>\n }\n\n @if (searched()) {\n @if (found()) {\n <ul class=\"faq-search-tester__results\">\n @for (item of results(); track item.faqId) {\n <li class=\"faq-search-tester__result\">\n <div class=\"faq-search-tester__score-row\">\n <span class=\"eds-label\">Relevance</span>\n <div class=\"faq-search-tester__score-bar\">\n <span class=\"faq-search-tester__score-fill\" [style.width.%]=\"scorePercent(item.score)\"></span>\n </div>\n <span class=\"eds-mono-sm\">{{ scorePercent(item.score) }}%</span>\n </div>\n @if (item.category) {\n <span class=\"faq-chip\" [class]=\"categoryClass(item.category)\">{{ item.category }}</span>\n }\n <p class=\"faq-search-tester__question\">{{ item.question }}</p>\n <p class=\"faq-search-tester__answer\">{{ item.answer }}</p>\n </li>\n }\n </ul>\n } @else {\n <p class=\"faq-search-tester__no-match\" role=\"status\">\n No match \u2014 voice agent will offer to transfer to reception\n </p>\n }\n }\n</aside>\n", styles: [".faq-chip{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:600;text-transform:capitalize}.faq-chip--hours{background:#3b82f61f;color:#2563eb}.faq-chip--location{background:#14b8a61f;color:#0d9488}.faq-chip--parking{background:#6b72801f;color:#4b5563}.faq-chip--insurance{background:#8b5cf61f;color:#7c3aed}.faq-chip--services{background:#22c55e1f;color:#16a34a}.faq-chip--policies{background:#f973161f;color:#ea580c}.faq-chip--general{background:#6b72801f;color:#6b7280}.faq-search-tester{padding:16px;position:sticky;top:calc(var(--eds-topbar-height, 56px) + 16px)}.faq-search-tester__help{color:var(--eds-text-secondary);margin:4px 0 12px}.faq-search-tester__field{display:flex;flex-direction:column;gap:6px;margin-bottom:10px}.faq-search-tester__input{padding:8px 10px;border:1px solid var(--eds-border-subtle, #e4e4e7);border-radius:8px;font:inherit}.faq-search-tester__error{color:#b91c1c;font-size:13px;margin-top:10px}.faq-search-tester__no-match{margin-top:14px;padding:12px;border-radius:8px;background:#f59e0b1f;color:#b45309;font-size:13px}.faq-search-tester__results{list-style:none;margin:14px 0 0;padding:0;display:flex;flex-direction:column;gap:12px}.faq-search-tester__result{padding:12px;border:1px solid var(--eds-border-subtle, #e4e4e7);border-radius:8px}.faq-search-tester__score-row{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:8px;margin-bottom:8px}.faq-search-tester__score-bar{height:6px;border-radius:999px;background:var(--eds-surface-muted, #e5e7eb);overflow:hidden}.faq-search-tester__score-fill{display:block;height:100%;background:var(--eds-brand, #0d9488);border-radius:999px}.faq-search-tester__question{font-weight:600;font-size:13px;margin:6px 0 4px}.faq-search-tester__answer{font-size:13px;color:var(--eds-text-secondary);margin:0;line-height:1.5}\n"] }]
|
|
422
428
|
}] });
|
|
423
429
|
|
|
424
430
|
class KnowledgeHubPageComponent {
|
|
425
431
|
constructor() {
|
|
426
432
|
this.api = inject(PracticeFaqApiService);
|
|
427
433
|
this.toast = inject(ToastService);
|
|
428
|
-
this.
|
|
434
|
+
this.pageSize = DEFAULT_PAGE_SIZE;
|
|
435
|
+
this.pageFaqs = signal([]);
|
|
436
|
+
this.total = signal(0);
|
|
437
|
+
this.page = signal(1);
|
|
429
438
|
this.loading = signal(true);
|
|
430
439
|
this.error = signal(null);
|
|
431
440
|
this.activeFilter = signal('all');
|
|
@@ -444,11 +453,10 @@ class KnowledgeHubPageComponent {
|
|
|
444
453
|
{ id: 'active', label: 'Active only' },
|
|
445
454
|
{ id: 'inactive', label: 'Inactive' },
|
|
446
455
|
];
|
|
456
|
+
/** Client filters still applied on the current page (category / local search / inactive). */
|
|
447
457
|
this.faqs = computed(() => {
|
|
448
|
-
let rows = [...this.
|
|
458
|
+
let rows = [...this.pageFaqs()];
|
|
449
459
|
const active = this.activeFilter();
|
|
450
|
-
if (active === 'active')
|
|
451
|
-
rows = rows.filter((f) => f.isActive);
|
|
452
460
|
if (active === 'inactive')
|
|
453
461
|
rows = rows.filter((f) => !f.isActive);
|
|
454
462
|
const cat = this.categoryFilter();
|
|
@@ -471,9 +479,17 @@ class KnowledgeHubPageComponent {
|
|
|
471
479
|
load() {
|
|
472
480
|
this.loading.set(true);
|
|
473
481
|
this.error.set(null);
|
|
474
|
-
this.api
|
|
475
|
-
|
|
476
|
-
|
|
482
|
+
this.api
|
|
483
|
+
.list({
|
|
484
|
+
activeOnly: this.activeFilter() === 'active',
|
|
485
|
+
page: this.page(),
|
|
486
|
+
limit: DEFAULT_PAGE_SIZE,
|
|
487
|
+
})
|
|
488
|
+
.subscribe({
|
|
489
|
+
next: ({ items, total, page }) => {
|
|
490
|
+
this.pageFaqs.set(items);
|
|
491
|
+
this.total.set(total);
|
|
492
|
+
this.page.set(page);
|
|
477
493
|
this.loading.set(false);
|
|
478
494
|
},
|
|
479
495
|
error: () => {
|
|
@@ -484,6 +500,8 @@ class KnowledgeHubPageComponent {
|
|
|
484
500
|
}
|
|
485
501
|
setActiveFilter(id) {
|
|
486
502
|
this.activeFilter.set(id);
|
|
503
|
+
this.page.set(1);
|
|
504
|
+
this.load();
|
|
487
505
|
}
|
|
488
506
|
toggleCategory(cat) {
|
|
489
507
|
this.categoryFilter.update((c) => (c === cat ? '' : cat));
|
|
@@ -491,6 +509,21 @@ class KnowledgeHubPageComponent {
|
|
|
491
509
|
isCategorySelected(cat) {
|
|
492
510
|
return this.categoryFilter() === cat;
|
|
493
511
|
}
|
|
512
|
+
totalPages() {
|
|
513
|
+
return totalPages(this.total(), DEFAULT_PAGE_SIZE);
|
|
514
|
+
}
|
|
515
|
+
prevPage() {
|
|
516
|
+
if (this.page() <= 1)
|
|
517
|
+
return;
|
|
518
|
+
this.page.update((p) => p - 1);
|
|
519
|
+
this.load();
|
|
520
|
+
}
|
|
521
|
+
nextPage() {
|
|
522
|
+
if (this.page() >= this.totalPages())
|
|
523
|
+
return;
|
|
524
|
+
this.page.update((p) => p + 1);
|
|
525
|
+
this.load();
|
|
526
|
+
}
|
|
494
527
|
openCreate() {
|
|
495
528
|
this.editing.set(null);
|
|
496
529
|
this.formSeed.set({ question: '', answer: '', category: '' });
|
|
@@ -513,32 +546,33 @@ class KnowledgeHubPageComponent {
|
|
|
513
546
|
const edit = this.editing();
|
|
514
547
|
if (edit) {
|
|
515
548
|
this.api.update(edit.faqId, payload).subscribe({
|
|
516
|
-
next: (
|
|
517
|
-
this.allFaqs.update((list) => list.map((f) => (f.faqId === updated.faqId ? updated : f)));
|
|
549
|
+
next: () => {
|
|
518
550
|
this.closeForm();
|
|
519
551
|
this.toast.show('FAQ saved — voice agent updated', 'success');
|
|
552
|
+
this.load();
|
|
520
553
|
},
|
|
521
554
|
error: () => this.toast.show('Failed to save FAQ', 'error'),
|
|
522
555
|
});
|
|
523
556
|
return;
|
|
524
557
|
}
|
|
525
558
|
this.api.create(payload).subscribe({
|
|
526
|
-
next: (
|
|
527
|
-
this.allFaqs.update((list) => [...list, created]);
|
|
559
|
+
next: () => {
|
|
528
560
|
this.closeForm();
|
|
529
561
|
this.toast.show('Your AI receptionist can now answer this on live calls.', 'success');
|
|
562
|
+
this.page.set(1);
|
|
563
|
+
this.load();
|
|
530
564
|
},
|
|
531
565
|
error: () => this.toast.show('Failed to create FAQ', 'error'),
|
|
532
566
|
});
|
|
533
567
|
}
|
|
534
568
|
toggleActive(faq, active) {
|
|
535
|
-
this.
|
|
569
|
+
this.pageFaqs.update((list) => list.map((f) => (f.faqId === faq.faqId ? { ...f, isActive: active } : f)));
|
|
536
570
|
this.api.update(faq.faqId, { isActive: active }).subscribe({
|
|
537
571
|
next: (updated) => {
|
|
538
|
-
this.
|
|
572
|
+
this.pageFaqs.update((list) => list.map((f) => (f.faqId === updated.faqId ? updated : f)));
|
|
539
573
|
},
|
|
540
574
|
error: () => {
|
|
541
|
-
this.
|
|
575
|
+
this.pageFaqs.update((list) => list.map((f) => (f.faqId === faq.faqId ? { ...f, isActive: !active } : f)));
|
|
542
576
|
this.toast.show('Failed to update FAQ status', 'error');
|
|
543
577
|
},
|
|
544
578
|
});
|
|
@@ -549,14 +583,14 @@ class KnowledgeHubPageComponent {
|
|
|
549
583
|
return;
|
|
550
584
|
this.api.delete(faq.faqId).subscribe({
|
|
551
585
|
next: () => {
|
|
552
|
-
this.allFaqs.update((list) => list.filter((f) => f.faqId !== faq.faqId));
|
|
553
586
|
this.toast.show('FAQ removed', 'success');
|
|
587
|
+
this.load();
|
|
554
588
|
},
|
|
555
589
|
error: () => this.toast.show('Failed to delete FAQ', 'error'),
|
|
556
590
|
});
|
|
557
591
|
}
|
|
558
592
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: KnowledgeHubPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
559
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: KnowledgeHubPageComponent, isStandalone: true, selector: "eds-knowledge-hub-page", ngImport: i0, template: "<div class=\"eds-page knowledge-hub\">\n <header class=\"knowledge-hub__header\">\n <div>\n <h1 class=\"eds-h1\">Practice knowledge base</h1>\n <p class=\"eds-body-sm\">\n Answers here are used by your AI receptionist on phone calls. Keep replies short and factual.\n </p>\n </div>\n <eds-button variant=\"primary\" class=\"knowledge-hub__add-desktop\" (click)=\"openCreate()\">\n <eds-icon name=\"add\" [size]=\"18\" /> Add FAQ\n </eds-button>\n </header>\n\n <div class=\"knowledge-hub__banner eds-card\" role=\"note\">\n <eds-icon name=\"info\" [size]=\"18\" />\n <p class=\"eds-body-sm\">\n Do not put patient-specific or clinical advice here. Use general practice information only.\n </p>\n </div>\n\n <div class=\"knowledge-hub__layout\">\n <main class=\"knowledge-hub__main\">\n <nav class=\"knowledge-hub__tabs\" aria-label=\"FAQ status\">\n @for (t of activeTabs; track t.id) {\n <button\n type=\"button\"\n class=\"knowledge-hub__tab\"\n [class.knowledge-hub__tab--active]=\"activeFilter() === t.id\"\n (click)=\"setActiveFilter(t.id)\"\n >\n {{ t.label }}\n </button>\n }\n </nav>\n\n <div class=\"knowledge-hub__filters eds-card\">\n <input\n type=\"search\"\n class=\"knowledge-hub__search\"\n placeholder=\"Filter by question or answer\u2026\"\n [ngModel]=\"listSearch()\"\n (ngModelChange)=\"listSearch.set($event)\"\n />\n <div class=\"knowledge-hub__chips\">\n @for (cat of categories; track cat) {\n <button\n type=\"button\"\n class=\"faq-chip knowledge-hub__chip\"\n [class]=\"'faq-chip--' + cat\"\n [class.knowledge-hub__chip--on]=\"isCategorySelected(cat)\"\n (click)=\"toggleCategory(cat)\"\n >\n {{ cat }}\n </button>\n }\n </div>\n </div>\n\n @if (loading()) {\n <div class=\"knowledge-hub__skeleton eds-card\">\n @for (i of [1, 2, 3]; track i) {\n <div class=\"knowledge-hub__skel-row\"></div>\n }\n </div>\n } @else if (error()) {\n <p class=\"knowledge-hub__error eds-body-sm\" role=\"alert\">{{ error() }}</p>\n <eds-button variant=\"secondary\" (click)=\"load()\">Retry</eds-button>\n } @else if (allFaqs().length === 0) {\n <div class=\"eds-card\">\n <eds-faq-empty-state (add)=\"openCreate()\" (useTemplate)=\"onStarterTemplate($event)\" />\n </div>\n } @else if (faqs().length === 0) {\n <p class=\"eds-body-sm knowledge-hub__empty-filter\">No FAQs match your filters.</p>\n } @else {\n <div class=\"eds-card knowledge-hub__table-wrap\">\n <table class=\"eds-table knowledge-hub__table\">\n <thead>\n <tr>\n <th>Question</th>\n <th>Answer</th>\n <th>Category</th>\n <th>Active</th>\n <th>Updated</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n @for (faq of faqs(); track faq.faqId) {\n <tr [class.knowledge-hub__row--inactive]=\"!faq.isActive\">\n <td class=\"knowledge-hub__question\" [title]=\"faq.question\">{{ faq.question }}</td>\n <td class=\"knowledge-hub__answer\" [title]=\"faq.answer\">{{ truncate(faq.answer, 72) }}</td>\n <td>\n @if (faq.category) {\n <span class=\"faq-chip\" [class]=\"categoryClass(faq.category)\">{{ faq.category }}</span>\n } @else {\n <span class=\"eds-body-sm\">\u2014</span>\n }\n </td>\n <td>\n <label class=\"knowledge-hub__toggle\">\n <input\n type=\"checkbox\"\n [checked]=\"faq.isActive\"\n (change)=\"toggleActive(faq, $any($event.target).checked)\"\n [attr.aria-label]=\"'Toggle active for ' + faq.question\"\n />\n <span class=\"knowledge-hub__status\" [class.knowledge-hub__status--on]=\"faq.isActive\">\n {{ faq.isActive ? 'Active' : 'Inactive' }}\n </span>\n </label>\n </td>\n <td class=\"eds-mono-sm\">{{ faq.updatedAt | date: 'mediumDate' }}</td>\n <td class=\"knowledge-hub__actions\">\n <button type=\"button\" class=\"knowledge-hub__link\" (click)=\"openEdit(faq)\">Edit</button>\n <button type=\"button\" class=\"knowledge-hub__link knowledge-hub__link--danger\" (click)=\"deleteFaq(faq)\">\n Delete\n </button>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </main>\n\n <eds-faq-search-tester />\n </div>\n\n <button type=\"button\" class=\"knowledge-hub__fab\" aria-label=\"Add FAQ\" (click)=\"openCreate()\">\n <eds-icon name=\"add\" [size]=\"24\" />\n </button>\n\n <eds-faq-form\n [open]=\"formOpen()\"\n [faq]=\"editing()\"\n [initialQuestion]=\"formSeed().question\"\n [initialAnswer]=\"formSeed().answer\"\n [initialCategory]=\"formSeed().category\"\n (closed)=\"closeForm()\"\n (saved)=\"onSave($event)\"\n />\n</div>\n", styles: [".faq-chip{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:600;text-transform:capitalize}.faq-chip--hours{background:#3b82f61f;color:#2563eb}.faq-chip--location{background:#14b8a61f;color:#0d9488}.faq-chip--parking{background:#6b72801f;color:#4b5563}.faq-chip--insurance{background:#8b5cf61f;color:#7c3aed}.faq-chip--services{background:#22c55e1f;color:#16a34a}.faq-chip--policies{background:#f973161f;color:#ea580c}.faq-chip--general{background:#6b72801f;color:#6b7280}.knowledge-hub__header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:16px}.knowledge-hub__banner{display:flex;align-items:flex-start;gap:10px;padding:12px 16px;margin-bottom:20px;background:#3b82f60f;border-color:#3b82f633}.knowledge-hub__banner p{margin:0}.knowledge-hub__layout{display:grid;grid-template-columns:1fr 320px;gap:20px;align-items:start}.knowledge-hub__tabs{display:flex;gap:4px;margin-bottom:12px;padding:4px;width:fit-content;background:var(--surface-l1);border-radius:var(--eds-radius-md)}.knowledge-hub__tab{border:none;background:transparent;padding:8px 14px;border-radius:var(--eds-radius-sm);font-size:13px;cursor:pointer;color:var(--text-secondary)}.knowledge-hub__tab--active{background:var(--surface-l0);color:var(--text-primary);font-weight:600;box-shadow:0 1px 2px #0000000f}.knowledge-hub__filters{padding:12px;margin-bottom:12px}.knowledge-hub__search{width:100%;padding:8px 12px;border:1px solid var(--border-default);border-radius:var(--eds-radius-md);font-size:14px;margin-bottom:10px}.knowledge-hub__chips{display:flex;flex-wrap:wrap;gap:6px}.knowledge-hub__chip{border:1px solid transparent;cursor:pointer;opacity:.6}.knowledge-hub__chip--on{opacity:1;border-color:currentColor}.knowledge-hub__table-wrap{overflow-x:auto;padding:0}.knowledge-hub__question{font-weight:600;max-width:220px}.knowledge-hub__answer{color:var(--text-secondary);max-width:260px}.knowledge-hub__row--inactive{opacity:.65}.knowledge-hub__toggle{display:flex;align-items:center;gap:6px;cursor:pointer}.knowledge-hub__status{font-size:12px;color:var(--text-secondary)}.knowledge-hub__status--on{color:#16a34a}.knowledge-hub__status:before{content:\"\";display:inline-block;width:6px;height:6px;border-radius:50%;background:#9ca3af;margin-right:4px;vertical-align:middle}.knowledge-hub__status--on:before{background:#22c55e}.knowledge-hub__actions{white-space:nowrap;display:flex;gap:8px}.knowledge-hub__link{border:none;background:none;color:var(--eds-brand, #0d9488);font-size:13px;cursor:pointer;padding:0}.knowledge-hub__link--danger{color:#dc2626}.knowledge-hub__error{color:#b91c1c}.knowledge-hub__empty-filter{padding:24px;text-align:center;color:var(--text-secondary)}.knowledge-hub__skeleton{padding:16px}.knowledge-hub__skel-row{height:48px;border-radius:8px;background:linear-gradient(90deg,#f3f4f6 25%,#e5e7eb,#f3f4f6 75%);background-size:200% 100%;animation:faq-shimmer 1.2s infinite;margin-bottom:8px}@keyframes faq-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.knowledge-hub__fab{display:none;position:fixed;right:24px;bottom:24px;width:56px;height:56px;border-radius:50%;border:none;background:var(--eds-brand, #0d9488);color:#fff;box-shadow:0 4px 14px #0003;cursor:pointer;align-items:center;justify-content:center}@media(max-width:960px){.knowledge-hub__layout{grid-template-columns:1fr}.knowledge-hub__add-desktop{display:none}.knowledge-hub__fab{display:inline-flex}}@media(min-width:961px){.knowledge-hub__fab{display:none}}\n"], dependencies: [{ kind: "pipe", type: DatePipe, name: "date" }, { kind: "ngmodule", type: FormsModule }, { 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.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: EdsButtonComponent, selector: "eds-button", inputs: ["variant", "type", "disabled"] }, { kind: "component", type: EdsIconComponent, selector: "eds-icon", inputs: ["name", "size", "fill", "weight"] }, { kind: "component", type: FaqEmptyStateComponent, selector: "eds-faq-empty-state", outputs: ["add", "useTemplate"] }, { kind: "component", type: FaqFormComponent, selector: "eds-faq-form", inputs: ["open", "faq", "initialQuestion", "initialAnswer", "initialCategory"], outputs: ["closed", "saved"] }, { kind: "component", type: FaqSearchTesterComponent, selector: "eds-faq-search-tester" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
593
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: KnowledgeHubPageComponent, isStandalone: true, selector: "eds-knowledge-hub-page", ngImport: i0, template: "<div class=\"eds-page knowledge-hub\">\n <header class=\"knowledge-hub__header\">\n <div>\n <h1 class=\"eds-h1\">Practice knowledge base</h1>\n <p class=\"eds-body-sm\">\n Answers here are used by your AI receptionist on phone calls. Keep replies short and factual.\n </p>\n </div>\n <eds-button variant=\"primary\" class=\"knowledge-hub__add-desktop\" (click)=\"openCreate()\">\n <eds-icon name=\"add\" [size]=\"18\" /> Add FAQ\n </eds-button>\n </header>\n\n <div class=\"knowledge-hub__banner eds-card\" role=\"note\">\n <eds-icon name=\"info\" [size]=\"18\" />\n <p class=\"eds-body-sm\">\n Do not put patient-specific or clinical advice here. Use general practice information only.\n </p>\n </div>\n\n <div class=\"knowledge-hub__layout\">\n <main class=\"knowledge-hub__main\">\n <nav class=\"knowledge-hub__tabs\" aria-label=\"FAQ status\">\n @for (t of activeTabs; track t.id) {\n <button\n type=\"button\"\n class=\"knowledge-hub__tab\"\n [class.knowledge-hub__tab--active]=\"activeFilter() === t.id\"\n (click)=\"setActiveFilter(t.id)\"\n >\n {{ t.label }}\n </button>\n }\n </nav>\n\n <div class=\"knowledge-hub__filters eds-card\">\n <input\n type=\"search\"\n class=\"knowledge-hub__search\"\n placeholder=\"Filter by question or answer\u2026\"\n [ngModel]=\"listSearch()\"\n (ngModelChange)=\"listSearch.set($event)\"\n />\n <div class=\"knowledge-hub__chips\">\n @for (cat of categories; track cat) {\n <button\n type=\"button\"\n class=\"faq-chip knowledge-hub__chip\"\n [class]=\"'faq-chip--' + cat\"\n [class.knowledge-hub__chip--on]=\"isCategorySelected(cat)\"\n (click)=\"toggleCategory(cat)\"\n >\n {{ cat }}\n </button>\n }\n </div>\n </div>\n\n @if (loading()) {\n <div class=\"knowledge-hub__skeleton eds-card\">\n @for (i of [1, 2, 3]; track i) {\n <div class=\"knowledge-hub__skel-row\"></div>\n }\n </div>\n } @else if (error()) {\n <p class=\"knowledge-hub__error eds-body-sm\" role=\"alert\">{{ error() }}</p>\n <eds-button variant=\"secondary\" (click)=\"load()\">Retry</eds-button>\n } @else if (total() === 0 && !categoryFilter() && !listSearch() && activeFilter() === 'all') {\n <div class=\"eds-card\">\n <eds-faq-empty-state (add)=\"openCreate()\" (useTemplate)=\"onStarterTemplate($event)\" />\n </div>\n } @else if (faqs().length === 0) {\n <p class=\"eds-body-sm knowledge-hub__empty-filter\">No FAQs match your filters.</p>\n } @else {\n <div class=\"eds-card knowledge-hub__table-wrap\">\n <table class=\"eds-table knowledge-hub__table\">\n <thead>\n <tr>\n <th>Question</th>\n <th>Answer</th>\n <th>Category</th>\n <th>Active</th>\n <th>Updated</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n @for (faq of faqs(); track faq.faqId) {\n <tr [class.knowledge-hub__row--inactive]=\"!faq.isActive\">\n <td class=\"knowledge-hub__question\" [title]=\"faq.question\">{{ faq.question }}</td>\n <td class=\"knowledge-hub__answer\" [title]=\"faq.answer\">{{ truncate(faq.answer, 72) }}</td>\n <td>\n @if (faq.category) {\n <span class=\"faq-chip\" [class]=\"categoryClass(faq.category)\">{{ faq.category }}</span>\n } @else {\n <span class=\"eds-body-sm\">\u2014</span>\n }\n </td>\n <td>\n <label class=\"knowledge-hub__toggle\">\n <input\n type=\"checkbox\"\n [checked]=\"faq.isActive\"\n (change)=\"toggleActive(faq, $any($event.target).checked)\"\n [attr.aria-label]=\"'Toggle active for ' + faq.question\"\n />\n <span class=\"knowledge-hub__status\" [class.knowledge-hub__status--on]=\"faq.isActive\">\n {{ faq.isActive ? 'Active' : 'Inactive' }}\n </span>\n </label>\n </td>\n <td class=\"eds-mono-sm\">{{ faq.updatedAt | date: 'mediumDate' }}</td>\n <td class=\"knowledge-hub__actions\">\n <button type=\"button\" class=\"knowledge-hub__link\" (click)=\"openEdit(faq)\">Edit</button>\n <button type=\"button\" class=\"knowledge-hub__link knowledge-hub__link--danger\" (click)=\"deleteFaq(faq)\">\n Delete\n </button>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <footer class=\"knowledge-hub__pager\">\n <span class=\"eds-body-sm\">\n Page {{ page() }} of {{ totalPages() }} \u00B7 {{ total() }} total\n </span>\n <div class=\"knowledge-hub__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 </main>\n\n <eds-faq-search-tester />\n </div>\n\n <button type=\"button\" class=\"knowledge-hub__fab\" aria-label=\"Add FAQ\" (click)=\"openCreate()\">\n <eds-icon name=\"add\" [size]=\"24\" />\n </button>\n\n <eds-faq-form\n [open]=\"formOpen()\"\n [faq]=\"editing()\"\n [initialQuestion]=\"formSeed().question\"\n [initialAnswer]=\"formSeed().answer\"\n [initialCategory]=\"formSeed().category\"\n (closed)=\"closeForm()\"\n (saved)=\"onSave($event)\"\n />\n</div>\n", styles: [".faq-chip{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:600;text-transform:capitalize}.faq-chip--hours{background:#3b82f61f;color:#2563eb}.faq-chip--location{background:#14b8a61f;color:#0d9488}.faq-chip--parking{background:#6b72801f;color:#4b5563}.faq-chip--insurance{background:#8b5cf61f;color:#7c3aed}.faq-chip--services{background:#22c55e1f;color:#16a34a}.faq-chip--policies{background:#f973161f;color:#ea580c}.faq-chip--general{background:#6b72801f;color:#6b7280}.knowledge-hub__header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:16px}.knowledge-hub__banner{display:flex;align-items:flex-start;gap:10px;padding:12px 16px;margin-bottom:20px;background:#3b82f60f;border-color:#3b82f633}.knowledge-hub__banner p{margin:0}.knowledge-hub__layout{display:grid;grid-template-columns:1fr 320px;gap:20px;align-items:start}.knowledge-hub__tabs{display:flex;gap:4px;margin-bottom:12px;padding:4px;width:fit-content;background:var(--eds-surface-l1);border-radius:var(--eds-radius-md)}.knowledge-hub__tab{border:none;background:transparent;padding:8px 14px;border-radius:var(--eds-radius-sm);font-size:13px;cursor:pointer;color:var(--eds-text-secondary)}.knowledge-hub__tab--active{background:var(--eds-surface-l0);color:var(--eds-text-primary);font-weight:600;box-shadow:0 1px 2px #0000000f}.knowledge-hub__filters{padding:12px;margin-bottom:12px}.knowledge-hub__search{width:100%;padding:8px 12px;border:1px solid var(--eds-border-default);border-radius:var(--eds-radius-md);font-size:14px;margin-bottom:10px}.knowledge-hub__chips{display:flex;flex-wrap:wrap;gap:6px}.knowledge-hub__chip{border:1px solid transparent;cursor:pointer;opacity:.6}.knowledge-hub__chip--on{opacity:1;border-color:currentColor}.knowledge-hub__table-wrap{overflow-x:auto;padding:0}.knowledge-hub__question{font-weight:600;max-width:220px}.knowledge-hub__answer{color:var(--eds-text-secondary);max-width:260px}.knowledge-hub__row--inactive{opacity:.65}.knowledge-hub__toggle{display:flex;align-items:center;gap:6px;cursor:pointer}.knowledge-hub__status{font-size:12px;color:var(--eds-text-secondary)}.knowledge-hub__status--on{color:#16a34a}.knowledge-hub__status:before{content:\"\";display:inline-block;width:6px;height:6px;border-radius:50%;background:#9ca3af;margin-right:4px;vertical-align:middle}.knowledge-hub__status--on:before{background:#22c55e}.knowledge-hub__actions{white-space:nowrap;display:flex;gap:8px}.knowledge-hub__link{border:none;background:none;color:var(--eds-brand, #0d9488);font-size:13px;cursor:pointer;padding:0}.knowledge-hub__link--danger{color:#dc2626}.knowledge-hub__error{color:#b91c1c}.knowledge-hub__empty-filter{padding:24px;text-align:center;color:var(--eds-text-secondary)}.knowledge-hub__skeleton{padding:16px}.knowledge-hub__skel-row{height:48px;border-radius:8px;background:linear-gradient(90deg,#f3f4f6 25%,#e5e7eb,#f3f4f6 75%);background-size:200% 100%;animation:faq-shimmer 1.2s infinite;margin-bottom:8px}@keyframes faq-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.knowledge-hub__fab{display:none;position:fixed;right:24px;bottom:24px;width:56px;height:56px;border-radius:50%;border:none;background:var(--eds-brand, #0d9488);color:#fff;box-shadow:0 4px 14px #0003;cursor:pointer;align-items:center;justify-content:center}@media(max-width:960px){.knowledge-hub__layout{grid-template-columns:1fr}.knowledge-hub__add-desktop{display:none}.knowledge-hub__fab{display:inline-flex}}@media(min-width:961px){.knowledge-hub__fab{display:none}}.knowledge-hub__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)}.knowledge-hub__pager-actions{display:flex;gap:8px}\n"], dependencies: [{ kind: "pipe", type: DatePipe, name: "date" }, { kind: "ngmodule", type: FormsModule }, { 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.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: EdsButtonComponent, selector: "eds-button", inputs: ["variant", "type", "disabled"] }, { kind: "component", type: EdsIconComponent, selector: "eds-icon", inputs: ["name", "size", "fill", "weight"] }, { kind: "component", type: FaqEmptyStateComponent, selector: "eds-faq-empty-state", outputs: ["add", "useTemplate"] }, { kind: "component", type: FaqFormComponent, selector: "eds-faq-form", inputs: ["open", "faq", "initialQuestion", "initialAnswer", "initialCategory"], outputs: ["closed", "saved"] }, { kind: "component", type: FaqSearchTesterComponent, selector: "eds-faq-search-tester" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
560
594
|
}
|
|
561
595
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: KnowledgeHubPageComponent, decorators: [{
|
|
562
596
|
type: Component,
|
|
@@ -568,8 +602,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
568
602
|
FaqEmptyStateComponent,
|
|
569
603
|
FaqFormComponent,
|
|
570
604
|
FaqSearchTesterComponent,
|
|
571
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"eds-page knowledge-hub\">\n <header class=\"knowledge-hub__header\">\n <div>\n <h1 class=\"eds-h1\">Practice knowledge base</h1>\n <p class=\"eds-body-sm\">\n Answers here are used by your AI receptionist on phone calls. Keep replies short and factual.\n </p>\n </div>\n <eds-button variant=\"primary\" class=\"knowledge-hub__add-desktop\" (click)=\"openCreate()\">\n <eds-icon name=\"add\" [size]=\"18\" /> Add FAQ\n </eds-button>\n </header>\n\n <div class=\"knowledge-hub__banner eds-card\" role=\"note\">\n <eds-icon name=\"info\" [size]=\"18\" />\n <p class=\"eds-body-sm\">\n Do not put patient-specific or clinical advice here. Use general practice information only.\n </p>\n </div>\n\n <div class=\"knowledge-hub__layout\">\n <main class=\"knowledge-hub__main\">\n <nav class=\"knowledge-hub__tabs\" aria-label=\"FAQ status\">\n @for (t of activeTabs; track t.id) {\n <button\n type=\"button\"\n class=\"knowledge-hub__tab\"\n [class.knowledge-hub__tab--active]=\"activeFilter() === t.id\"\n (click)=\"setActiveFilter(t.id)\"\n >\n {{ t.label }}\n </button>\n }\n </nav>\n\n <div class=\"knowledge-hub__filters eds-card\">\n <input\n type=\"search\"\n class=\"knowledge-hub__search\"\n placeholder=\"Filter by question or answer\u2026\"\n [ngModel]=\"listSearch()\"\n (ngModelChange)=\"listSearch.set($event)\"\n />\n <div class=\"knowledge-hub__chips\">\n @for (cat of categories; track cat) {\n <button\n type=\"button\"\n class=\"faq-chip knowledge-hub__chip\"\n [class]=\"'faq-chip--' + cat\"\n [class.knowledge-hub__chip--on]=\"isCategorySelected(cat)\"\n (click)=\"toggleCategory(cat)\"\n >\n {{ cat }}\n </button>\n }\n </div>\n </div>\n\n @if (loading()) {\n <div class=\"knowledge-hub__skeleton eds-card\">\n @for (i of [1, 2, 3]; track i) {\n <div class=\"knowledge-hub__skel-row\"></div>\n }\n </div>\n } @else if (error()) {\n <p class=\"knowledge-hub__error eds-body-sm\" role=\"alert\">{{ error() }}</p>\n <eds-button variant=\"secondary\" (click)=\"load()\">Retry</eds-button>\n } @else if (allFaqs().length === 0) {\n <div class=\"eds-card\">\n <eds-faq-empty-state (add)=\"openCreate()\" (useTemplate)=\"onStarterTemplate($event)\" />\n </div>\n } @else if (faqs().length === 0) {\n <p class=\"eds-body-sm knowledge-hub__empty-filter\">No FAQs match your filters.</p>\n } @else {\n <div class=\"eds-card knowledge-hub__table-wrap\">\n <table class=\"eds-table knowledge-hub__table\">\n <thead>\n <tr>\n <th>Question</th>\n <th>Answer</th>\n <th>Category</th>\n <th>Active</th>\n <th>Updated</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n @for (faq of faqs(); track faq.faqId) {\n <tr [class.knowledge-hub__row--inactive]=\"!faq.isActive\">\n <td class=\"knowledge-hub__question\" [title]=\"faq.question\">{{ faq.question }}</td>\n <td class=\"knowledge-hub__answer\" [title]=\"faq.answer\">{{ truncate(faq.answer, 72) }}</td>\n <td>\n @if (faq.category) {\n <span class=\"faq-chip\" [class]=\"categoryClass(faq.category)\">{{ faq.category }}</span>\n } @else {\n <span class=\"eds-body-sm\">\u2014</span>\n }\n </td>\n <td>\n <label class=\"knowledge-hub__toggle\">\n <input\n type=\"checkbox\"\n [checked]=\"faq.isActive\"\n (change)=\"toggleActive(faq, $any($event.target).checked)\"\n [attr.aria-label]=\"'Toggle active for ' + faq.question\"\n />\n <span class=\"knowledge-hub__status\" [class.knowledge-hub__status--on]=\"faq.isActive\">\n {{ faq.isActive ? 'Active' : 'Inactive' }}\n </span>\n </label>\n </td>\n <td class=\"eds-mono-sm\">{{ faq.updatedAt | date: 'mediumDate' }}</td>\n <td class=\"knowledge-hub__actions\">\n <button type=\"button\" class=\"knowledge-hub__link\" (click)=\"openEdit(faq)\">Edit</button>\n <button type=\"button\" class=\"knowledge-hub__link knowledge-hub__link--danger\" (click)=\"deleteFaq(faq)\">\n Delete\n </button>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </main>\n\n <eds-faq-search-tester />\n </div>\n\n <button type=\"button\" class=\"knowledge-hub__fab\" aria-label=\"Add FAQ\" (click)=\"openCreate()\">\n <eds-icon name=\"add\" [size]=\"24\" />\n </button>\n\n <eds-faq-form\n [open]=\"formOpen()\"\n [faq]=\"editing()\"\n [initialQuestion]=\"formSeed().question\"\n [initialAnswer]=\"formSeed().answer\"\n [initialCategory]=\"formSeed().category\"\n (closed)=\"closeForm()\"\n (saved)=\"onSave($event)\"\n />\n</div>\n", styles: [".faq-chip{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:600;text-transform:capitalize}.faq-chip--hours{background:#3b82f61f;color:#2563eb}.faq-chip--location{background:#14b8a61f;color:#0d9488}.faq-chip--parking{background:#6b72801f;color:#4b5563}.faq-chip--insurance{background:#8b5cf61f;color:#7c3aed}.faq-chip--services{background:#22c55e1f;color:#16a34a}.faq-chip--policies{background:#f973161f;color:#ea580c}.faq-chip--general{background:#6b72801f;color:#6b7280}.knowledge-hub__header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:16px}.knowledge-hub__banner{display:flex;align-items:flex-start;gap:10px;padding:12px 16px;margin-bottom:20px;background:#3b82f60f;border-color:#3b82f633}.knowledge-hub__banner p{margin:0}.knowledge-hub__layout{display:grid;grid-template-columns:1fr 320px;gap:20px;align-items:start}.knowledge-hub__tabs{display:flex;gap:4px;margin-bottom:12px;padding:4px;width:fit-content;background:var(--surface-l1);border-radius:var(--eds-radius-md)}.knowledge-hub__tab{border:none;background:transparent;padding:8px 14px;border-radius:var(--eds-radius-sm);font-size:13px;cursor:pointer;color:var(--text-secondary)}.knowledge-hub__tab--active{background:var(--surface-l0);color:var(--text-primary);font-weight:600;box-shadow:0 1px 2px #0000000f}.knowledge-hub__filters{padding:12px;margin-bottom:12px}.knowledge-hub__search{width:100%;padding:8px 12px;border:1px solid var(--border-default);border-radius:var(--eds-radius-md);font-size:14px;margin-bottom:10px}.knowledge-hub__chips{display:flex;flex-wrap:wrap;gap:6px}.knowledge-hub__chip{border:1px solid transparent;cursor:pointer;opacity:.6}.knowledge-hub__chip--on{opacity:1;border-color:currentColor}.knowledge-hub__table-wrap{overflow-x:auto;padding:0}.knowledge-hub__question{font-weight:600;max-width:220px}.knowledge-hub__answer{color:var(--text-secondary);max-width:260px}.knowledge-hub__row--inactive{opacity:.65}.knowledge-hub__toggle{display:flex;align-items:center;gap:6px;cursor:pointer}.knowledge-hub__status{font-size:12px;color:var(--text-secondary)}.knowledge-hub__status--on{color:#16a34a}.knowledge-hub__status:before{content:\"\";display:inline-block;width:6px;height:6px;border-radius:50%;background:#9ca3af;margin-right:4px;vertical-align:middle}.knowledge-hub__status--on:before{background:#22c55e}.knowledge-hub__actions{white-space:nowrap;display:flex;gap:8px}.knowledge-hub__link{border:none;background:none;color:var(--eds-brand, #0d9488);font-size:13px;cursor:pointer;padding:0}.knowledge-hub__link--danger{color:#dc2626}.knowledge-hub__error{color:#b91c1c}.knowledge-hub__empty-filter{padding:24px;text-align:center;color:var(--text-secondary)}.knowledge-hub__skeleton{padding:16px}.knowledge-hub__skel-row{height:48px;border-radius:8px;background:linear-gradient(90deg,#f3f4f6 25%,#e5e7eb,#f3f4f6 75%);background-size:200% 100%;animation:faq-shimmer 1.2s infinite;margin-bottom:8px}@keyframes faq-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.knowledge-hub__fab{display:none;position:fixed;right:24px;bottom:24px;width:56px;height:56px;border-radius:50%;border:none;background:var(--eds-brand, #0d9488);color:#fff;box-shadow:0 4px 14px #0003;cursor:pointer;align-items:center;justify-content:center}@media(max-width:960px){.knowledge-hub__layout{grid-template-columns:1fr}.knowledge-hub__add-desktop{display:none}.knowledge-hub__fab{display:inline-flex}}@media(min-width:961px){.knowledge-hub__fab{display:none}}\n"] }]
|
|
605
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"eds-page knowledge-hub\">\n <header class=\"knowledge-hub__header\">\n <div>\n <h1 class=\"eds-h1\">Practice knowledge base</h1>\n <p class=\"eds-body-sm\">\n Answers here are used by your AI receptionist on phone calls. Keep replies short and factual.\n </p>\n </div>\n <eds-button variant=\"primary\" class=\"knowledge-hub__add-desktop\" (click)=\"openCreate()\">\n <eds-icon name=\"add\" [size]=\"18\" /> Add FAQ\n </eds-button>\n </header>\n\n <div class=\"knowledge-hub__banner eds-card\" role=\"note\">\n <eds-icon name=\"info\" [size]=\"18\" />\n <p class=\"eds-body-sm\">\n Do not put patient-specific or clinical advice here. Use general practice information only.\n </p>\n </div>\n\n <div class=\"knowledge-hub__layout\">\n <main class=\"knowledge-hub__main\">\n <nav class=\"knowledge-hub__tabs\" aria-label=\"FAQ status\">\n @for (t of activeTabs; track t.id) {\n <button\n type=\"button\"\n class=\"knowledge-hub__tab\"\n [class.knowledge-hub__tab--active]=\"activeFilter() === t.id\"\n (click)=\"setActiveFilter(t.id)\"\n >\n {{ t.label }}\n </button>\n }\n </nav>\n\n <div class=\"knowledge-hub__filters eds-card\">\n <input\n type=\"search\"\n class=\"knowledge-hub__search\"\n placeholder=\"Filter by question or answer\u2026\"\n [ngModel]=\"listSearch()\"\n (ngModelChange)=\"listSearch.set($event)\"\n />\n <div class=\"knowledge-hub__chips\">\n @for (cat of categories; track cat) {\n <button\n type=\"button\"\n class=\"faq-chip knowledge-hub__chip\"\n [class]=\"'faq-chip--' + cat\"\n [class.knowledge-hub__chip--on]=\"isCategorySelected(cat)\"\n (click)=\"toggleCategory(cat)\"\n >\n {{ cat }}\n </button>\n }\n </div>\n </div>\n\n @if (loading()) {\n <div class=\"knowledge-hub__skeleton eds-card\">\n @for (i of [1, 2, 3]; track i) {\n <div class=\"knowledge-hub__skel-row\"></div>\n }\n </div>\n } @else if (error()) {\n <p class=\"knowledge-hub__error eds-body-sm\" role=\"alert\">{{ error() }}</p>\n <eds-button variant=\"secondary\" (click)=\"load()\">Retry</eds-button>\n } @else if (total() === 0 && !categoryFilter() && !listSearch() && activeFilter() === 'all') {\n <div class=\"eds-card\">\n <eds-faq-empty-state (add)=\"openCreate()\" (useTemplate)=\"onStarterTemplate($event)\" />\n </div>\n } @else if (faqs().length === 0) {\n <p class=\"eds-body-sm knowledge-hub__empty-filter\">No FAQs match your filters.</p>\n } @else {\n <div class=\"eds-card knowledge-hub__table-wrap\">\n <table class=\"eds-table knowledge-hub__table\">\n <thead>\n <tr>\n <th>Question</th>\n <th>Answer</th>\n <th>Category</th>\n <th>Active</th>\n <th>Updated</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n @for (faq of faqs(); track faq.faqId) {\n <tr [class.knowledge-hub__row--inactive]=\"!faq.isActive\">\n <td class=\"knowledge-hub__question\" [title]=\"faq.question\">{{ faq.question }}</td>\n <td class=\"knowledge-hub__answer\" [title]=\"faq.answer\">{{ truncate(faq.answer, 72) }}</td>\n <td>\n @if (faq.category) {\n <span class=\"faq-chip\" [class]=\"categoryClass(faq.category)\">{{ faq.category }}</span>\n } @else {\n <span class=\"eds-body-sm\">\u2014</span>\n }\n </td>\n <td>\n <label class=\"knowledge-hub__toggle\">\n <input\n type=\"checkbox\"\n [checked]=\"faq.isActive\"\n (change)=\"toggleActive(faq, $any($event.target).checked)\"\n [attr.aria-label]=\"'Toggle active for ' + faq.question\"\n />\n <span class=\"knowledge-hub__status\" [class.knowledge-hub__status--on]=\"faq.isActive\">\n {{ faq.isActive ? 'Active' : 'Inactive' }}\n </span>\n </label>\n </td>\n <td class=\"eds-mono-sm\">{{ faq.updatedAt | date: 'mediumDate' }}</td>\n <td class=\"knowledge-hub__actions\">\n <button type=\"button\" class=\"knowledge-hub__link\" (click)=\"openEdit(faq)\">Edit</button>\n <button type=\"button\" class=\"knowledge-hub__link knowledge-hub__link--danger\" (click)=\"deleteFaq(faq)\">\n Delete\n </button>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <footer class=\"knowledge-hub__pager\">\n <span class=\"eds-body-sm\">\n Page {{ page() }} of {{ totalPages() }} \u00B7 {{ total() }} total\n </span>\n <div class=\"knowledge-hub__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 </main>\n\n <eds-faq-search-tester />\n </div>\n\n <button type=\"button\" class=\"knowledge-hub__fab\" aria-label=\"Add FAQ\" (click)=\"openCreate()\">\n <eds-icon name=\"add\" [size]=\"24\" />\n </button>\n\n <eds-faq-form\n [open]=\"formOpen()\"\n [faq]=\"editing()\"\n [initialQuestion]=\"formSeed().question\"\n [initialAnswer]=\"formSeed().answer\"\n [initialCategory]=\"formSeed().category\"\n (closed)=\"closeForm()\"\n (saved)=\"onSave($event)\"\n />\n</div>\n", styles: [".faq-chip{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:600;text-transform:capitalize}.faq-chip--hours{background:#3b82f61f;color:#2563eb}.faq-chip--location{background:#14b8a61f;color:#0d9488}.faq-chip--parking{background:#6b72801f;color:#4b5563}.faq-chip--insurance{background:#8b5cf61f;color:#7c3aed}.faq-chip--services{background:#22c55e1f;color:#16a34a}.faq-chip--policies{background:#f973161f;color:#ea580c}.faq-chip--general{background:#6b72801f;color:#6b7280}.knowledge-hub__header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:16px}.knowledge-hub__banner{display:flex;align-items:flex-start;gap:10px;padding:12px 16px;margin-bottom:20px;background:#3b82f60f;border-color:#3b82f633}.knowledge-hub__banner p{margin:0}.knowledge-hub__layout{display:grid;grid-template-columns:1fr 320px;gap:20px;align-items:start}.knowledge-hub__tabs{display:flex;gap:4px;margin-bottom:12px;padding:4px;width:fit-content;background:var(--eds-surface-l1);border-radius:var(--eds-radius-md)}.knowledge-hub__tab{border:none;background:transparent;padding:8px 14px;border-radius:var(--eds-radius-sm);font-size:13px;cursor:pointer;color:var(--eds-text-secondary)}.knowledge-hub__tab--active{background:var(--eds-surface-l0);color:var(--eds-text-primary);font-weight:600;box-shadow:0 1px 2px #0000000f}.knowledge-hub__filters{padding:12px;margin-bottom:12px}.knowledge-hub__search{width:100%;padding:8px 12px;border:1px solid var(--eds-border-default);border-radius:var(--eds-radius-md);font-size:14px;margin-bottom:10px}.knowledge-hub__chips{display:flex;flex-wrap:wrap;gap:6px}.knowledge-hub__chip{border:1px solid transparent;cursor:pointer;opacity:.6}.knowledge-hub__chip--on{opacity:1;border-color:currentColor}.knowledge-hub__table-wrap{overflow-x:auto;padding:0}.knowledge-hub__question{font-weight:600;max-width:220px}.knowledge-hub__answer{color:var(--eds-text-secondary);max-width:260px}.knowledge-hub__row--inactive{opacity:.65}.knowledge-hub__toggle{display:flex;align-items:center;gap:6px;cursor:pointer}.knowledge-hub__status{font-size:12px;color:var(--eds-text-secondary)}.knowledge-hub__status--on{color:#16a34a}.knowledge-hub__status:before{content:\"\";display:inline-block;width:6px;height:6px;border-radius:50%;background:#9ca3af;margin-right:4px;vertical-align:middle}.knowledge-hub__status--on:before{background:#22c55e}.knowledge-hub__actions{white-space:nowrap;display:flex;gap:8px}.knowledge-hub__link{border:none;background:none;color:var(--eds-brand, #0d9488);font-size:13px;cursor:pointer;padding:0}.knowledge-hub__link--danger{color:#dc2626}.knowledge-hub__error{color:#b91c1c}.knowledge-hub__empty-filter{padding:24px;text-align:center;color:var(--eds-text-secondary)}.knowledge-hub__skeleton{padding:16px}.knowledge-hub__skel-row{height:48px;border-radius:8px;background:linear-gradient(90deg,#f3f4f6 25%,#e5e7eb,#f3f4f6 75%);background-size:200% 100%;animation:faq-shimmer 1.2s infinite;margin-bottom:8px}@keyframes faq-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.knowledge-hub__fab{display:none;position:fixed;right:24px;bottom:24px;width:56px;height:56px;border-radius:50%;border:none;background:var(--eds-brand, #0d9488);color:#fff;box-shadow:0 4px 14px #0003;cursor:pointer;align-items:center;justify-content:center}@media(max-width:960px){.knowledge-hub__layout{grid-template-columns:1fr}.knowledge-hub__add-desktop{display:none}.knowledge-hub__fab{display:inline-flex}}@media(min-width:961px){.knowledge-hub__fab{display:none}}.knowledge-hub__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)}.knowledge-hub__pager-actions{display:flex;gap:8px}\n"] }]
|
|
572
606
|
}] });
|
|
573
607
|
|
|
574
608
|
export { KnowledgeHubPageComponent };
|
|
575
|
-
//# sourceMappingURL=edvak99-voice-receptionist-widget-knowledge-hub.page-
|
|
609
|
+
//# sourceMappingURL=edvak99-voice-receptionist-widget-knowledge-hub.page-BngGx_Lc.mjs.map
|