@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
package/README.md
CHANGED
|
@@ -67,6 +67,9 @@ In the host global stylesheet:
|
|
|
67
67
|
@use '@edvak99/voice-receptionist-widget/styles';
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
Design tokens are **`--eds-*` only**, scoped to `eds-app-shell` / `.voice-receptionist-host`.
|
|
71
|
+
Importing this package **does not** set `:root --color-primary` (safe for EHR Tailwind `hsl(var(--color-primary))`).
|
|
72
|
+
|
|
70
73
|
Also load Material Symbols + fonts (Inter, DM Sans, Figtree, IBM Plex Mono) in `index.html`, same as the Voice Hub app.
|
|
71
74
|
|
|
72
75
|
## Build this package
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { inject, signal, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
-
import { A as AiReceptionistApiService, b as EdsIconComponent } from './edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-
|
|
3
|
+
import { A as AiReceptionistApiService, b as EdsIconComponent } from './edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-Dn0GrZ6_.mjs';
|
|
4
4
|
|
|
5
5
|
class AnalyticsPageComponent {
|
|
6
6
|
constructor() {
|
|
@@ -102,12 +102,12 @@ class AnalyticsPageComponent {
|
|
|
102
102
|
});
|
|
103
103
|
}
|
|
104
104
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AnalyticsPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
105
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: AnalyticsPageComponent, isStandalone: true, selector: "eds-analytics-page", ngImport: i0, template: "<div class=\"eds-page analytics-page\">\n <header class=\"analytics-page__header\">\n <div>\n <h1 class=\"eds-h1\">Analytics</h1>\n <p class=\"eds-body-sm\">Call volume, outcomes, and performance KPIs</p>\n </div>\n <div class=\"analytics-page__range\">\n @for (r of ranges; track r.id) {\n <button\n type=\"button\"\n class=\"analytics-page__range-btn\"\n [class.analytics-page__range-btn--active]=\"range() === r.id\"\n (click)=\"setRange(r.id)\"\n >\n {{ r.label }}\n </button>\n }\n </div>\n </header>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading analytics\u2026</p>\n } @else if (data()) {\n <section class=\"analytics-page__kpis\">\n @for (kpi of data()!.kpiCards; track kpi.label) {\n <article class=\"eds-card analytics-page__kpi\">\n <eds-icon [name]=\"kpiIcon(kpi.label)\" [size]=\"22\" />\n <span class=\"eds-label\">{{ kpi.label }}</span>\n <span class=\"analytics-page__kpi-value\">{{ kpi.value }}</span>\n @if (kpi.delta) {\n <span class=\"analytics-page__kpi-delta\" [attr.data-tone]=\"kpi.tone\">{{ kpi.delta }}</span>\n }\n @if (kpi.sub) {\n <span class=\"analytics-page__kpi-sub\">{{ kpi.sub }}</span>\n }\n </article>\n }\n </section>\n\n <div class=\"analytics-page__charts\">\n <article class=\"eds-card analytics-page__chart\">\n <div class=\"analytics-page__chart-head\">\n <h2 class=\"eds-h2\">Call volume</h2>\n <span class=\"eds-label\">{{ volumeRangeLabel() }}</span>\n </div>\n <div class=\"analytics-page__volume-chart\">\n <div class=\"analytics-page__plot\">\n <div class=\"analytics-page__grid\" aria-hidden=\"true\"></div>\n <div class=\"analytics-page__bars\" role=\"list\" aria-label=\"Call volume by day\">\n @for (point of data()!.volume.points; track point.date || point.label; let i = $index) {\n <div\n class=\"analytics-page__bar-col\"\n role=\"listitem\"\n [attr.aria-label]=\"volumeTooltip(point)\"\n >\n <div class=\"analytics-page__bar-wrap\">\n @if (point.count > 0) {\n <div\n class=\"analytics-page__bar-stack\"\n [style.height.%]=\"barHeightPercent(point, data()!.volume.points)\"\n >\n @if (showBarValue(point, data()!.volume.points)) {\n <span class=\"analytics-page__bar-value eds-mono-sm\">{{ point.count }}</span>\n }\n <div class=\"analytics-page__bar\"></div>\n </div>\n }\n <span class=\"analytics-page__tooltip\">{{ volumeTooltip(point) }}</span>\n </div>\n <span class=\"eds-mono-sm analytics-page__bar-axis\">\n {{ volumeAxisLabel(point, i, data()!.volume.points.length) }}\n </span>\n </div>\n }\n </div>\n </div>\n </div>\n <!-- <p class=\"analytics-page__axis-note eds-label\">(MM/DD)/calls per day</p> -->\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <h2 class=\"eds-h2\">Outcomes</h2>\n <div class=\"analytics-page__bars analytics-page__bars--horizontal\">\n @for (item of data()!.outcomes; track item.label) {\n <div class=\"analytics-page__hbar-row\">\n <span class=\"eds-body-sm\">{{ item.label }}</span>\n <div class=\"analytics-page__hbar-track\">\n <span\n class=\"analytics-page__hbar-fill\"\n [style.width.%]=\"(item.value / maxValue(data()!.outcomes)) * 100\"\n [style.background]=\"item.color\"\n ></span>\n </div>\n <span class=\"eds-mono-sm\">{{ item.value }}%</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <h2 class=\"eds-h2\">Top reasons</h2>\n <div class=\"analytics-page__bars analytics-page__bars--horizontal\">\n @for (item of data()!.reasons; track item.label) {\n <div class=\"analytics-page__hbar-row\">\n <span class=\"eds-body-sm\">{{ item.label }}</span>\n <div class=\"analytics-page__hbar-track\">\n <span\n class=\"analytics-page__hbar-fill\"\n [style.width.%]=\"(item.value / maxValue(data()!.reasons)) * 100\"\n ></span>\n </div>\n <span class=\"eds-mono-sm\">{{ item.value }}%</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <div class=\"analytics-page__chart-head\">\n <h2 class=\"eds-h2\">Peak hours</h2>\n <span class=\"eds-label\">\n @if (data()!.peakHours.timezone) {\n {{ data()!.peakHours.timezone }}\n } @else {\n Calls per hour\n }\n </span>\n </div>\n <div class=\"analytics-page__volume-chart analytics-page__volume-chart--peak\">\n <div class=\"analytics-page__plot\">\n <div class=\"analytics-page__grid\" aria-hidden=\"true\"></div>\n <div class=\"analytics-page__bars analytics-page__bars--peak\" role=\"list\" aria-label=\"Call volume by hour\">\n @for (point of data()!.peakHours.points; track point.hour) {\n <div\n class=\"analytics-page__bar-col analytics-page__bar-col--peak\"\n role=\"listitem\"\n [attr.aria-label]=\"peakTooltip(point)\"\n >\n <div class=\"analytics-page__bar-wrap\">\n @if (point.count > 0) {\n <div\n class=\"analytics-page__bar-stack\"\n [style.height.%]=\"barHeightPercent(point, data()!.peakHours.points)\"\n >\n <div class=\"analytics-page__bar\"></div>\n </div>\n }\n <span class=\"analytics-page__tooltip\">{{ peakTooltip(point) }}</span>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <div class=\"analytics-page__peak-axis\">\n @for (point of data()!.peakHours.points; track point.hour; let i = $index) {\n <span class=\"analytics-page__peak-tick eds-mono-sm\">\n @if (showPeakAxisLabel(i, data()!.peakHours.points.length)) {\n {{ point.label }}\n }\n </span>\n }\n </div>\n </article>\n </div>\n }\n</div>\n", styles: [".analytics-page__header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:20px}.analytics-page__range{display:flex;gap:4px;background:var(--surface-l0);border:1px solid var(--border-default);border-radius:var(--eds-radius-md);padding:4px}.analytics-page__range-btn{padding:6px 12px;border:none;background:none;font-size:13px;border-radius:var(--eds-radius-sm);cursor:pointer;color:var(--text-secondary)}.analytics-page__range-btn--active{background:var(--brand-500);color:#fff}.analytics-page__kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:20px}.analytics-page__kpi{display:flex;flex-direction:column;gap:6px;padding:16px}.analytics-page__kpi-value{font-size:24px;font-weight:600;color:var(--text-primary)}.analytics-page__kpi-delta{font-size:12px;font-weight:600}.analytics-page__kpi-delta[data-tone=pos]{color:var(--green-500, #16a34a)}.analytics-page__kpi-delta[data-tone=warn]{color:var(--amber-500, #d97706)}.analytics-page__kpi-delta[data-tone=neutral]{color:var(--text-secondary)}.analytics-page__kpi-sub{font-size:12px;color:var(--text-secondary)}.analytics-page__charts{display:grid;grid-template-columns:1fr 1fr;gap:16px}.analytics-page__chart{padding:16px}.analytics-page__chart .eds-h2{margin-bottom:0}.analytics-page__chart-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:16px;flex-wrap:wrap}.analytics-page__volume-chart--peak .analytics-page__grid{inset:0}.analytics-page__plot{position:relative;width:100%;min-width:0}.analytics-page__grid{position:absolute;inset:0 0 24px;pointer-events:none;background-image:linear-gradient(to top,var(--border-default) 1px,transparent 1px);background-size:100% 25%;opacity:.45}.analytics-page__bars{display:flex;align-items:flex-end;justify-content:space-between;gap:8px;height:160px;position:relative;z-index:1}.analytics-page__bars--horizontal{flex-direction:column;height:auto;align-items:stretch;gap:10px}.analytics-page__bars--peak{height:100px}.analytics-page__bars--peak .analytics-page__bar{max-width:100%}.analytics-page__bar-col{flex:1;display:flex;flex-direction:column;align-items:center;gap:6px;height:100%;min-width:0;cursor:default}.analytics-page__bar-col:hover .analytics-page__tooltip,.analytics-page__bar-col:focus-within .analytics-page__tooltip{opacity:1}.analytics-page__bar-col--peak{min-width:0}.analytics-page__bar-value{flex-shrink:0;color:var(--text-primary);font-size:11px;font-weight:600;line-height:1}.analytics-page__bar-wrap{position:relative;width:100%;flex:1;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;min-height:0}.analytics-page__bar-stack{width:100%;max-width:32px;display:flex;flex-direction:column;justify-content:flex-end;align-items:stretch;gap:4px;min-height:0}.analytics-page__bar-axis{flex-shrink:0;min-height:16px;color:var(--text-secondary);font-size:10px;text-align:center;line-height:1.2}.analytics-page__tooltip{position:absolute;bottom:calc(100% + 6px);left:50%;transform:translate(-50%);padding:4px 8px;background:var(--gray-900, #18181b);color:#fafafa;font-size:11px;font-weight:500;line-height:1.3;border-radius:4px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s ease;z-index:2;box-shadow:0 2px 8px #00000026}.analytics-page__tooltip:after{content:\"\";position:absolute;top:100%;left:50%;transform:translate(-50%);border:4px solid transparent;border-top-color:var(--gray-900, #18181b)}.analytics-page__peak-axis{display:flex;justify-content:space-between;gap:2px;margin-top:8px}.analytics-page__peak-tick{flex:1;text-align:center;min-height:16px;color:var(--text-secondary);font-size:10px}.analytics-page__bar{width:100%;flex:1 1 auto;min-height:3px;background:linear-gradient(180deg,var(--brand-500),var(--violet-500));border-radius:4px 4px 0 0;animation:eds-bar-grow .5s ease forwards;transform-origin:bottom}.analytics-page__hbar-row{display:grid;grid-template-columns:120px 1fr 40px;align-items:center;gap:10px}.analytics-page__hbar-track{height:8px;background:var(--surface-l2);border-radius:4px;overflow:hidden}.analytics-page__hbar-fill{display:block;height:100%;background:var(--brand-500);border-radius:4px;animation:eds-bar-grow .5s ease forwards;transform-origin:left}@media(max-width:1000px){.analytics-page__kpis{grid-template-columns:repeat(2,1fr)}.analytics-page__charts{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "component", type: EdsIconComponent, selector: "eds-icon", inputs: ["name", "size", "fill", "weight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
105
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: AnalyticsPageComponent, isStandalone: true, selector: "eds-analytics-page", ngImport: i0, template: "<div class=\"eds-page analytics-page\">\n <header class=\"analytics-page__header\">\n <div>\n <h1 class=\"eds-h1\">Analytics</h1>\n <p class=\"eds-body-sm\">Call volume, outcomes, and performance KPIs</p>\n </div>\n <div class=\"analytics-page__range\">\n @for (r of ranges; track r.id) {\n <button\n type=\"button\"\n class=\"analytics-page__range-btn\"\n [class.analytics-page__range-btn--active]=\"range() === r.id\"\n (click)=\"setRange(r.id)\"\n >\n {{ r.label }}\n </button>\n }\n </div>\n </header>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading analytics\u2026</p>\n } @else if (data()) {\n <section class=\"analytics-page__kpis\">\n @for (kpi of data()!.kpiCards; track kpi.label) {\n <article class=\"eds-card analytics-page__kpi\">\n <eds-icon [name]=\"kpiIcon(kpi.label)\" [size]=\"22\" />\n <span class=\"eds-label\">{{ kpi.label }}</span>\n <span class=\"analytics-page__kpi-value\">{{ kpi.value }}</span>\n @if (kpi.delta) {\n <span class=\"analytics-page__kpi-delta\" [attr.data-tone]=\"kpi.tone\">{{ kpi.delta }}</span>\n }\n @if (kpi.sub) {\n <span class=\"analytics-page__kpi-sub\">{{ kpi.sub }}</span>\n }\n </article>\n }\n </section>\n\n <div class=\"analytics-page__charts\">\n <article class=\"eds-card analytics-page__chart\">\n <div class=\"analytics-page__chart-head\">\n <h2 class=\"eds-h2\">Call volume</h2>\n <span class=\"eds-label\">{{ volumeRangeLabel() }}</span>\n </div>\n <div class=\"analytics-page__volume-chart\">\n <div class=\"analytics-page__plot\">\n <div class=\"analytics-page__grid\" aria-hidden=\"true\"></div>\n <div class=\"analytics-page__bars\" role=\"list\" aria-label=\"Call volume by day\">\n @for (point of data()!.volume.points; track point.date || point.label; let i = $index) {\n <div\n class=\"analytics-page__bar-col\"\n role=\"listitem\"\n [attr.aria-label]=\"volumeTooltip(point)\"\n >\n <div class=\"analytics-page__bar-wrap\">\n @if (point.count > 0) {\n <div\n class=\"analytics-page__bar-stack\"\n [style.height.%]=\"barHeightPercent(point, data()!.volume.points)\"\n >\n @if (showBarValue(point, data()!.volume.points)) {\n <span class=\"analytics-page__bar-value eds-mono-sm\">{{ point.count }}</span>\n }\n <div class=\"analytics-page__bar\"></div>\n </div>\n }\n <span class=\"analytics-page__tooltip\">{{ volumeTooltip(point) }}</span>\n </div>\n <span class=\"eds-mono-sm analytics-page__bar-axis\">\n {{ volumeAxisLabel(point, i, data()!.volume.points.length) }}\n </span>\n </div>\n }\n </div>\n </div>\n </div>\n <!-- <p class=\"analytics-page__axis-note eds-label\">(MM/DD)/calls per day</p> -->\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <h2 class=\"eds-h2\">Outcomes</h2>\n <div class=\"analytics-page__bars analytics-page__bars--horizontal\">\n @for (item of data()!.outcomes; track item.label) {\n <div class=\"analytics-page__hbar-row\">\n <span class=\"eds-body-sm\">{{ item.label }}</span>\n <div class=\"analytics-page__hbar-track\">\n <span\n class=\"analytics-page__hbar-fill\"\n [style.width.%]=\"(item.value / maxValue(data()!.outcomes)) * 100\"\n [style.background]=\"item.color\"\n ></span>\n </div>\n <span class=\"eds-mono-sm\">{{ item.value }}%</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <h2 class=\"eds-h2\">Top reasons</h2>\n <div class=\"analytics-page__bars analytics-page__bars--horizontal\">\n @for (item of data()!.reasons; track item.label) {\n <div class=\"analytics-page__hbar-row\">\n <span class=\"eds-body-sm\">{{ item.label }}</span>\n <div class=\"analytics-page__hbar-track\">\n <span\n class=\"analytics-page__hbar-fill\"\n [style.width.%]=\"(item.value / maxValue(data()!.reasons)) * 100\"\n ></span>\n </div>\n <span class=\"eds-mono-sm\">{{ item.value }}%</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <div class=\"analytics-page__chart-head\">\n <h2 class=\"eds-h2\">Peak hours</h2>\n <span class=\"eds-label\">\n @if (data()!.peakHours.timezone) {\n {{ data()!.peakHours.timezone }}\n } @else {\n Calls per hour\n }\n </span>\n </div>\n <div class=\"analytics-page__volume-chart analytics-page__volume-chart--peak\">\n <div class=\"analytics-page__plot\">\n <div class=\"analytics-page__grid\" aria-hidden=\"true\"></div>\n <div class=\"analytics-page__bars analytics-page__bars--peak\" role=\"list\" aria-label=\"Call volume by hour\">\n @for (point of data()!.peakHours.points; track point.hour) {\n <div\n class=\"analytics-page__bar-col analytics-page__bar-col--peak\"\n role=\"listitem\"\n [attr.aria-label]=\"peakTooltip(point)\"\n >\n <div class=\"analytics-page__bar-wrap\">\n @if (point.count > 0) {\n <div\n class=\"analytics-page__bar-stack\"\n [style.height.%]=\"barHeightPercent(point, data()!.peakHours.points)\"\n >\n <div class=\"analytics-page__bar\"></div>\n </div>\n }\n <span class=\"analytics-page__tooltip\">{{ peakTooltip(point) }}</span>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <div class=\"analytics-page__peak-axis\">\n @for (point of data()!.peakHours.points; track point.hour; let i = $index) {\n <span class=\"analytics-page__peak-tick eds-mono-sm\">\n @if (showPeakAxisLabel(i, data()!.peakHours.points.length)) {\n {{ point.label }}\n }\n </span>\n }\n </div>\n </article>\n </div>\n }\n</div>\n", styles: [".analytics-page__header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:20px}.analytics-page__range{display:flex;gap:4px;background:var(--eds-surface-l0);border:1px solid var(--eds-border-default);border-radius:var(--eds-radius-md);padding:4px}.analytics-page__range-btn{padding:6px 12px;border:none;background:none;font-size:13px;border-radius:var(--eds-radius-sm);cursor:pointer;color:var(--eds-text-secondary)}.analytics-page__range-btn--active{background:var(--eds-brand-500);color:#fff}.analytics-page__kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:20px}.analytics-page__kpi{display:flex;flex-direction:column;gap:6px;padding:16px}.analytics-page__kpi-value{font-size:24px;font-weight:600;color:var(--eds-text-primary)}.analytics-page__kpi-delta{font-size:12px;font-weight:600}.analytics-page__kpi-delta[data-tone=pos]{color:var(--eds-green-500, #16a34a)}.analytics-page__kpi-delta[data-tone=warn]{color:var(--eds-amber-500, #d97706)}.analytics-page__kpi-delta[data-tone=neutral]{color:var(--eds-text-secondary)}.analytics-page__kpi-sub{font-size:12px;color:var(--eds-text-secondary)}.analytics-page__charts{display:grid;grid-template-columns:1fr 1fr;gap:16px}.analytics-page__chart{padding:16px}.analytics-page__chart .eds-h2{margin-bottom:0}.analytics-page__chart-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:16px;flex-wrap:wrap}.analytics-page__volume-chart--peak .analytics-page__grid{inset:0}.analytics-page__plot{position:relative;width:100%;min-width:0}.analytics-page__grid{position:absolute;inset:0 0 24px;pointer-events:none;background-image:linear-gradient(to top,var(--eds-border-default) 1px,transparent 1px);background-size:100% 25%;opacity:.45}.analytics-page__bars{display:flex;align-items:flex-end;justify-content:space-between;gap:8px;height:160px;position:relative;z-index:1}.analytics-page__bars--horizontal{flex-direction:column;height:auto;align-items:stretch;gap:10px}.analytics-page__bars--peak{height:100px}.analytics-page__bars--peak .analytics-page__bar{max-width:100%}.analytics-page__bar-col{flex:1;display:flex;flex-direction:column;align-items:center;gap:6px;height:100%;min-width:0;cursor:default}.analytics-page__bar-col:hover .analytics-page__tooltip,.analytics-page__bar-col:focus-within .analytics-page__tooltip{opacity:1}.analytics-page__bar-col--peak{min-width:0}.analytics-page__bar-value{flex-shrink:0;color:var(--eds-text-primary);font-size:11px;font-weight:600;line-height:1}.analytics-page__bar-wrap{position:relative;width:100%;flex:1;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;min-height:0}.analytics-page__bar-stack{width:100%;max-width:32px;display:flex;flex-direction:column;justify-content:flex-end;align-items:stretch;gap:4px;min-height:0}.analytics-page__bar-axis{flex-shrink:0;min-height:16px;color:var(--eds-text-secondary);font-size:10px;text-align:center;line-height:1.2}.analytics-page__tooltip{position:absolute;bottom:calc(100% + 6px);left:50%;transform:translate(-50%);padding:4px 8px;background:var(--eds-gray-900, #18181b);color:#fafafa;font-size:11px;font-weight:500;line-height:1.3;border-radius:4px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s ease;z-index:2;box-shadow:0 2px 8px #00000026}.analytics-page__tooltip:after{content:\"\";position:absolute;top:100%;left:50%;transform:translate(-50%);border:4px solid transparent;border-top-color:var(--eds-gray-900, #18181b)}.analytics-page__peak-axis{display:flex;justify-content:space-between;gap:2px;margin-top:8px}.analytics-page__peak-tick{flex:1;text-align:center;min-height:16px;color:var(--eds-text-secondary);font-size:10px}.analytics-page__bar{width:100%;flex:1 1 auto;min-height:3px;background:linear-gradient(180deg,var(--eds-brand-500),var(--eds-violet-500));border-radius:4px 4px 0 0;animation:eds-bar-grow .5s ease forwards;transform-origin:bottom}.analytics-page__hbar-row{display:grid;grid-template-columns:120px 1fr 40px;align-items:center;gap:10px}.analytics-page__hbar-track{height:8px;background:var(--eds-surface-l2);border-radius:4px;overflow:hidden}.analytics-page__hbar-fill{display:block;height:100%;background:var(--eds-brand-500);border-radius:4px;animation:eds-bar-grow .5s ease forwards;transform-origin:left}@media(max-width:1000px){.analytics-page__kpis{grid-template-columns:repeat(2,1fr)}.analytics-page__charts{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "component", type: EdsIconComponent, selector: "eds-icon", inputs: ["name", "size", "fill", "weight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
106
106
|
}
|
|
107
107
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AnalyticsPageComponent, decorators: [{
|
|
108
108
|
type: Component,
|
|
109
|
-
args: [{ selector: 'eds-analytics-page', imports: [EdsIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"eds-page analytics-page\">\n <header class=\"analytics-page__header\">\n <div>\n <h1 class=\"eds-h1\">Analytics</h1>\n <p class=\"eds-body-sm\">Call volume, outcomes, and performance KPIs</p>\n </div>\n <div class=\"analytics-page__range\">\n @for (r of ranges; track r.id) {\n <button\n type=\"button\"\n class=\"analytics-page__range-btn\"\n [class.analytics-page__range-btn--active]=\"range() === r.id\"\n (click)=\"setRange(r.id)\"\n >\n {{ r.label }}\n </button>\n }\n </div>\n </header>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading analytics\u2026</p>\n } @else if (data()) {\n <section class=\"analytics-page__kpis\">\n @for (kpi of data()!.kpiCards; track kpi.label) {\n <article class=\"eds-card analytics-page__kpi\">\n <eds-icon [name]=\"kpiIcon(kpi.label)\" [size]=\"22\" />\n <span class=\"eds-label\">{{ kpi.label }}</span>\n <span class=\"analytics-page__kpi-value\">{{ kpi.value }}</span>\n @if (kpi.delta) {\n <span class=\"analytics-page__kpi-delta\" [attr.data-tone]=\"kpi.tone\">{{ kpi.delta }}</span>\n }\n @if (kpi.sub) {\n <span class=\"analytics-page__kpi-sub\">{{ kpi.sub }}</span>\n }\n </article>\n }\n </section>\n\n <div class=\"analytics-page__charts\">\n <article class=\"eds-card analytics-page__chart\">\n <div class=\"analytics-page__chart-head\">\n <h2 class=\"eds-h2\">Call volume</h2>\n <span class=\"eds-label\">{{ volumeRangeLabel() }}</span>\n </div>\n <div class=\"analytics-page__volume-chart\">\n <div class=\"analytics-page__plot\">\n <div class=\"analytics-page__grid\" aria-hidden=\"true\"></div>\n <div class=\"analytics-page__bars\" role=\"list\" aria-label=\"Call volume by day\">\n @for (point of data()!.volume.points; track point.date || point.label; let i = $index) {\n <div\n class=\"analytics-page__bar-col\"\n role=\"listitem\"\n [attr.aria-label]=\"volumeTooltip(point)\"\n >\n <div class=\"analytics-page__bar-wrap\">\n @if (point.count > 0) {\n <div\n class=\"analytics-page__bar-stack\"\n [style.height.%]=\"barHeightPercent(point, data()!.volume.points)\"\n >\n @if (showBarValue(point, data()!.volume.points)) {\n <span class=\"analytics-page__bar-value eds-mono-sm\">{{ point.count }}</span>\n }\n <div class=\"analytics-page__bar\"></div>\n </div>\n }\n <span class=\"analytics-page__tooltip\">{{ volumeTooltip(point) }}</span>\n </div>\n <span class=\"eds-mono-sm analytics-page__bar-axis\">\n {{ volumeAxisLabel(point, i, data()!.volume.points.length) }}\n </span>\n </div>\n }\n </div>\n </div>\n </div>\n <!-- <p class=\"analytics-page__axis-note eds-label\">(MM/DD)/calls per day</p> -->\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <h2 class=\"eds-h2\">Outcomes</h2>\n <div class=\"analytics-page__bars analytics-page__bars--horizontal\">\n @for (item of data()!.outcomes; track item.label) {\n <div class=\"analytics-page__hbar-row\">\n <span class=\"eds-body-sm\">{{ item.label }}</span>\n <div class=\"analytics-page__hbar-track\">\n <span\n class=\"analytics-page__hbar-fill\"\n [style.width.%]=\"(item.value / maxValue(data()!.outcomes)) * 100\"\n [style.background]=\"item.color\"\n ></span>\n </div>\n <span class=\"eds-mono-sm\">{{ item.value }}%</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <h2 class=\"eds-h2\">Top reasons</h2>\n <div class=\"analytics-page__bars analytics-page__bars--horizontal\">\n @for (item of data()!.reasons; track item.label) {\n <div class=\"analytics-page__hbar-row\">\n <span class=\"eds-body-sm\">{{ item.label }}</span>\n <div class=\"analytics-page__hbar-track\">\n <span\n class=\"analytics-page__hbar-fill\"\n [style.width.%]=\"(item.value / maxValue(data()!.reasons)) * 100\"\n ></span>\n </div>\n <span class=\"eds-mono-sm\">{{ item.value }}%</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <div class=\"analytics-page__chart-head\">\n <h2 class=\"eds-h2\">Peak hours</h2>\n <span class=\"eds-label\">\n @if (data()!.peakHours.timezone) {\n {{ data()!.peakHours.timezone }}\n } @else {\n Calls per hour\n }\n </span>\n </div>\n <div class=\"analytics-page__volume-chart analytics-page__volume-chart--peak\">\n <div class=\"analytics-page__plot\">\n <div class=\"analytics-page__grid\" aria-hidden=\"true\"></div>\n <div class=\"analytics-page__bars analytics-page__bars--peak\" role=\"list\" aria-label=\"Call volume by hour\">\n @for (point of data()!.peakHours.points; track point.hour) {\n <div\n class=\"analytics-page__bar-col analytics-page__bar-col--peak\"\n role=\"listitem\"\n [attr.aria-label]=\"peakTooltip(point)\"\n >\n <div class=\"analytics-page__bar-wrap\">\n @if (point.count > 0) {\n <div\n class=\"analytics-page__bar-stack\"\n [style.height.%]=\"barHeightPercent(point, data()!.peakHours.points)\"\n >\n <div class=\"analytics-page__bar\"></div>\n </div>\n }\n <span class=\"analytics-page__tooltip\">{{ peakTooltip(point) }}</span>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <div class=\"analytics-page__peak-axis\">\n @for (point of data()!.peakHours.points; track point.hour; let i = $index) {\n <span class=\"analytics-page__peak-tick eds-mono-sm\">\n @if (showPeakAxisLabel(i, data()!.peakHours.points.length)) {\n {{ point.label }}\n }\n </span>\n }\n </div>\n </article>\n </div>\n }\n</div>\n", styles: [".analytics-page__header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:20px}.analytics-page__range{display:flex;gap:4px;background:var(--surface-l0);border:1px solid var(--border-default);border-radius:var(--eds-radius-md);padding:4px}.analytics-page__range-btn{padding:6px 12px;border:none;background:none;font-size:13px;border-radius:var(--eds-radius-sm);cursor:pointer;color:var(--text-secondary)}.analytics-page__range-btn--active{background:var(--brand-500);color:#fff}.analytics-page__kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:20px}.analytics-page__kpi{display:flex;flex-direction:column;gap:6px;padding:16px}.analytics-page__kpi-value{font-size:24px;font-weight:600;color:var(--text-primary)}.analytics-page__kpi-delta{font-size:12px;font-weight:600}.analytics-page__kpi-delta[data-tone=pos]{color:var(--green-500, #16a34a)}.analytics-page__kpi-delta[data-tone=warn]{color:var(--amber-500, #d97706)}.analytics-page__kpi-delta[data-tone=neutral]{color:var(--text-secondary)}.analytics-page__kpi-sub{font-size:12px;color:var(--text-secondary)}.analytics-page__charts{display:grid;grid-template-columns:1fr 1fr;gap:16px}.analytics-page__chart{padding:16px}.analytics-page__chart .eds-h2{margin-bottom:0}.analytics-page__chart-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:16px;flex-wrap:wrap}.analytics-page__volume-chart--peak .analytics-page__grid{inset:0}.analytics-page__plot{position:relative;width:100%;min-width:0}.analytics-page__grid{position:absolute;inset:0 0 24px;pointer-events:none;background-image:linear-gradient(to top,var(--border-default) 1px,transparent 1px);background-size:100% 25%;opacity:.45}.analytics-page__bars{display:flex;align-items:flex-end;justify-content:space-between;gap:8px;height:160px;position:relative;z-index:1}.analytics-page__bars--horizontal{flex-direction:column;height:auto;align-items:stretch;gap:10px}.analytics-page__bars--peak{height:100px}.analytics-page__bars--peak .analytics-page__bar{max-width:100%}.analytics-page__bar-col{flex:1;display:flex;flex-direction:column;align-items:center;gap:6px;height:100%;min-width:0;cursor:default}.analytics-page__bar-col:hover .analytics-page__tooltip,.analytics-page__bar-col:focus-within .analytics-page__tooltip{opacity:1}.analytics-page__bar-col--peak{min-width:0}.analytics-page__bar-value{flex-shrink:0;color:var(--text-primary);font-size:11px;font-weight:600;line-height:1}.analytics-page__bar-wrap{position:relative;width:100%;flex:1;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;min-height:0}.analytics-page__bar-stack{width:100%;max-width:32px;display:flex;flex-direction:column;justify-content:flex-end;align-items:stretch;gap:4px;min-height:0}.analytics-page__bar-axis{flex-shrink:0;min-height:16px;color:var(--text-secondary);font-size:10px;text-align:center;line-height:1.2}.analytics-page__tooltip{position:absolute;bottom:calc(100% + 6px);left:50%;transform:translate(-50%);padding:4px 8px;background:var(--gray-900, #18181b);color:#fafafa;font-size:11px;font-weight:500;line-height:1.3;border-radius:4px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s ease;z-index:2;box-shadow:0 2px 8px #00000026}.analytics-page__tooltip:after{content:\"\";position:absolute;top:100%;left:50%;transform:translate(-50%);border:4px solid transparent;border-top-color:var(--gray-900, #18181b)}.analytics-page__peak-axis{display:flex;justify-content:space-between;gap:2px;margin-top:8px}.analytics-page__peak-tick{flex:1;text-align:center;min-height:16px;color:var(--text-secondary);font-size:10px}.analytics-page__bar{width:100%;flex:1 1 auto;min-height:3px;background:linear-gradient(180deg,var(--brand-500),var(--violet-500));border-radius:4px 4px 0 0;animation:eds-bar-grow .5s ease forwards;transform-origin:bottom}.analytics-page__hbar-row{display:grid;grid-template-columns:120px 1fr 40px;align-items:center;gap:10px}.analytics-page__hbar-track{height:8px;background:var(--surface-l2);border-radius:4px;overflow:hidden}.analytics-page__hbar-fill{display:block;height:100%;background:var(--brand-500);border-radius:4px;animation:eds-bar-grow .5s ease forwards;transform-origin:left}@media(max-width:1000px){.analytics-page__kpis{grid-template-columns:repeat(2,1fr)}.analytics-page__charts{grid-template-columns:1fr}}\n"] }]
|
|
109
|
+
args: [{ selector: 'eds-analytics-page', imports: [EdsIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"eds-page analytics-page\">\n <header class=\"analytics-page__header\">\n <div>\n <h1 class=\"eds-h1\">Analytics</h1>\n <p class=\"eds-body-sm\">Call volume, outcomes, and performance KPIs</p>\n </div>\n <div class=\"analytics-page__range\">\n @for (r of ranges; track r.id) {\n <button\n type=\"button\"\n class=\"analytics-page__range-btn\"\n [class.analytics-page__range-btn--active]=\"range() === r.id\"\n (click)=\"setRange(r.id)\"\n >\n {{ r.label }}\n </button>\n }\n </div>\n </header>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading analytics\u2026</p>\n } @else if (data()) {\n <section class=\"analytics-page__kpis\">\n @for (kpi of data()!.kpiCards; track kpi.label) {\n <article class=\"eds-card analytics-page__kpi\">\n <eds-icon [name]=\"kpiIcon(kpi.label)\" [size]=\"22\" />\n <span class=\"eds-label\">{{ kpi.label }}</span>\n <span class=\"analytics-page__kpi-value\">{{ kpi.value }}</span>\n @if (kpi.delta) {\n <span class=\"analytics-page__kpi-delta\" [attr.data-tone]=\"kpi.tone\">{{ kpi.delta }}</span>\n }\n @if (kpi.sub) {\n <span class=\"analytics-page__kpi-sub\">{{ kpi.sub }}</span>\n }\n </article>\n }\n </section>\n\n <div class=\"analytics-page__charts\">\n <article class=\"eds-card analytics-page__chart\">\n <div class=\"analytics-page__chart-head\">\n <h2 class=\"eds-h2\">Call volume</h2>\n <span class=\"eds-label\">{{ volumeRangeLabel() }}</span>\n </div>\n <div class=\"analytics-page__volume-chart\">\n <div class=\"analytics-page__plot\">\n <div class=\"analytics-page__grid\" aria-hidden=\"true\"></div>\n <div class=\"analytics-page__bars\" role=\"list\" aria-label=\"Call volume by day\">\n @for (point of data()!.volume.points; track point.date || point.label; let i = $index) {\n <div\n class=\"analytics-page__bar-col\"\n role=\"listitem\"\n [attr.aria-label]=\"volumeTooltip(point)\"\n >\n <div class=\"analytics-page__bar-wrap\">\n @if (point.count > 0) {\n <div\n class=\"analytics-page__bar-stack\"\n [style.height.%]=\"barHeightPercent(point, data()!.volume.points)\"\n >\n @if (showBarValue(point, data()!.volume.points)) {\n <span class=\"analytics-page__bar-value eds-mono-sm\">{{ point.count }}</span>\n }\n <div class=\"analytics-page__bar\"></div>\n </div>\n }\n <span class=\"analytics-page__tooltip\">{{ volumeTooltip(point) }}</span>\n </div>\n <span class=\"eds-mono-sm analytics-page__bar-axis\">\n {{ volumeAxisLabel(point, i, data()!.volume.points.length) }}\n </span>\n </div>\n }\n </div>\n </div>\n </div>\n <!-- <p class=\"analytics-page__axis-note eds-label\">(MM/DD)/calls per day</p> -->\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <h2 class=\"eds-h2\">Outcomes</h2>\n <div class=\"analytics-page__bars analytics-page__bars--horizontal\">\n @for (item of data()!.outcomes; track item.label) {\n <div class=\"analytics-page__hbar-row\">\n <span class=\"eds-body-sm\">{{ item.label }}</span>\n <div class=\"analytics-page__hbar-track\">\n <span\n class=\"analytics-page__hbar-fill\"\n [style.width.%]=\"(item.value / maxValue(data()!.outcomes)) * 100\"\n [style.background]=\"item.color\"\n ></span>\n </div>\n <span class=\"eds-mono-sm\">{{ item.value }}%</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <h2 class=\"eds-h2\">Top reasons</h2>\n <div class=\"analytics-page__bars analytics-page__bars--horizontal\">\n @for (item of data()!.reasons; track item.label) {\n <div class=\"analytics-page__hbar-row\">\n <span class=\"eds-body-sm\">{{ item.label }}</span>\n <div class=\"analytics-page__hbar-track\">\n <span\n class=\"analytics-page__hbar-fill\"\n [style.width.%]=\"(item.value / maxValue(data()!.reasons)) * 100\"\n ></span>\n </div>\n <span class=\"eds-mono-sm\">{{ item.value }}%</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <div class=\"analytics-page__chart-head\">\n <h2 class=\"eds-h2\">Peak hours</h2>\n <span class=\"eds-label\">\n @if (data()!.peakHours.timezone) {\n {{ data()!.peakHours.timezone }}\n } @else {\n Calls per hour\n }\n </span>\n </div>\n <div class=\"analytics-page__volume-chart analytics-page__volume-chart--peak\">\n <div class=\"analytics-page__plot\">\n <div class=\"analytics-page__grid\" aria-hidden=\"true\"></div>\n <div class=\"analytics-page__bars analytics-page__bars--peak\" role=\"list\" aria-label=\"Call volume by hour\">\n @for (point of data()!.peakHours.points; track point.hour) {\n <div\n class=\"analytics-page__bar-col analytics-page__bar-col--peak\"\n role=\"listitem\"\n [attr.aria-label]=\"peakTooltip(point)\"\n >\n <div class=\"analytics-page__bar-wrap\">\n @if (point.count > 0) {\n <div\n class=\"analytics-page__bar-stack\"\n [style.height.%]=\"barHeightPercent(point, data()!.peakHours.points)\"\n >\n <div class=\"analytics-page__bar\"></div>\n </div>\n }\n <span class=\"analytics-page__tooltip\">{{ peakTooltip(point) }}</span>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <div class=\"analytics-page__peak-axis\">\n @for (point of data()!.peakHours.points; track point.hour; let i = $index) {\n <span class=\"analytics-page__peak-tick eds-mono-sm\">\n @if (showPeakAxisLabel(i, data()!.peakHours.points.length)) {\n {{ point.label }}\n }\n </span>\n }\n </div>\n </article>\n </div>\n }\n</div>\n", styles: [".analytics-page__header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:20px}.analytics-page__range{display:flex;gap:4px;background:var(--eds-surface-l0);border:1px solid var(--eds-border-default);border-radius:var(--eds-radius-md);padding:4px}.analytics-page__range-btn{padding:6px 12px;border:none;background:none;font-size:13px;border-radius:var(--eds-radius-sm);cursor:pointer;color:var(--eds-text-secondary)}.analytics-page__range-btn--active{background:var(--eds-brand-500);color:#fff}.analytics-page__kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:20px}.analytics-page__kpi{display:flex;flex-direction:column;gap:6px;padding:16px}.analytics-page__kpi-value{font-size:24px;font-weight:600;color:var(--eds-text-primary)}.analytics-page__kpi-delta{font-size:12px;font-weight:600}.analytics-page__kpi-delta[data-tone=pos]{color:var(--eds-green-500, #16a34a)}.analytics-page__kpi-delta[data-tone=warn]{color:var(--eds-amber-500, #d97706)}.analytics-page__kpi-delta[data-tone=neutral]{color:var(--eds-text-secondary)}.analytics-page__kpi-sub{font-size:12px;color:var(--eds-text-secondary)}.analytics-page__charts{display:grid;grid-template-columns:1fr 1fr;gap:16px}.analytics-page__chart{padding:16px}.analytics-page__chart .eds-h2{margin-bottom:0}.analytics-page__chart-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:16px;flex-wrap:wrap}.analytics-page__volume-chart--peak .analytics-page__grid{inset:0}.analytics-page__plot{position:relative;width:100%;min-width:0}.analytics-page__grid{position:absolute;inset:0 0 24px;pointer-events:none;background-image:linear-gradient(to top,var(--eds-border-default) 1px,transparent 1px);background-size:100% 25%;opacity:.45}.analytics-page__bars{display:flex;align-items:flex-end;justify-content:space-between;gap:8px;height:160px;position:relative;z-index:1}.analytics-page__bars--horizontal{flex-direction:column;height:auto;align-items:stretch;gap:10px}.analytics-page__bars--peak{height:100px}.analytics-page__bars--peak .analytics-page__bar{max-width:100%}.analytics-page__bar-col{flex:1;display:flex;flex-direction:column;align-items:center;gap:6px;height:100%;min-width:0;cursor:default}.analytics-page__bar-col:hover .analytics-page__tooltip,.analytics-page__bar-col:focus-within .analytics-page__tooltip{opacity:1}.analytics-page__bar-col--peak{min-width:0}.analytics-page__bar-value{flex-shrink:0;color:var(--eds-text-primary);font-size:11px;font-weight:600;line-height:1}.analytics-page__bar-wrap{position:relative;width:100%;flex:1;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;min-height:0}.analytics-page__bar-stack{width:100%;max-width:32px;display:flex;flex-direction:column;justify-content:flex-end;align-items:stretch;gap:4px;min-height:0}.analytics-page__bar-axis{flex-shrink:0;min-height:16px;color:var(--eds-text-secondary);font-size:10px;text-align:center;line-height:1.2}.analytics-page__tooltip{position:absolute;bottom:calc(100% + 6px);left:50%;transform:translate(-50%);padding:4px 8px;background:var(--eds-gray-900, #18181b);color:#fafafa;font-size:11px;font-weight:500;line-height:1.3;border-radius:4px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s ease;z-index:2;box-shadow:0 2px 8px #00000026}.analytics-page__tooltip:after{content:\"\";position:absolute;top:100%;left:50%;transform:translate(-50%);border:4px solid transparent;border-top-color:var(--eds-gray-900, #18181b)}.analytics-page__peak-axis{display:flex;justify-content:space-between;gap:2px;margin-top:8px}.analytics-page__peak-tick{flex:1;text-align:center;min-height:16px;color:var(--eds-text-secondary);font-size:10px}.analytics-page__bar{width:100%;flex:1 1 auto;min-height:3px;background:linear-gradient(180deg,var(--eds-brand-500),var(--eds-violet-500));border-radius:4px 4px 0 0;animation:eds-bar-grow .5s ease forwards;transform-origin:bottom}.analytics-page__hbar-row{display:grid;grid-template-columns:120px 1fr 40px;align-items:center;gap:10px}.analytics-page__hbar-track{height:8px;background:var(--eds-surface-l2);border-radius:4px;overflow:hidden}.analytics-page__hbar-fill{display:block;height:100%;background:var(--eds-brand-500);border-radius:4px;animation:eds-bar-grow .5s ease forwards;transform-origin:left}@media(max-width:1000px){.analytics-page__kpis{grid-template-columns:repeat(2,1fr)}.analytics-page__charts{grid-template-columns:1fr}}\n"] }]
|
|
110
110
|
}] });
|
|
111
111
|
|
|
112
112
|
export { AnalyticsPageComponent };
|
|
113
|
-
//# sourceMappingURL=edvak99-voice-receptionist-widget-analytics-page.component-
|
|
113
|
+
//# sourceMappingURL=edvak99-voice-receptionist-widget-analytics-page.component-Dsujn5az.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edvak99-voice-receptionist-widget-analytics-page.component-CW5OKmik.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/pages/analytics-page/analytics-page.component.ts","../../../projects/voice-receptionist-widget/src/lib/pages/analytics-page/analytics-page.component.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core';\n\nimport {\n AnalyticsData,\n AnalyticsPeakPoint,\n AnalyticsVolumePoint,\n} from '../../models/ai-receptionist.types';\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport { EdsIconComponent } from '../../ui/eds-icon.component';\n\n@Component({\n selector: 'eds-analytics-page',\n imports: [EdsIconComponent],\n templateUrl: './analytics-page.component.html',\n styleUrl: './analytics-page.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AnalyticsPageComponent implements OnInit {\n private readonly api = inject(AiReceptionistApiService);\n\n protected readonly data = signal<AnalyticsData | null>(null);\n protected readonly range = signal('30d');\n protected readonly loading = signal(true);\n\n protected readonly ranges = [\n { id: '7d', label: '7 days' },\n { id: '30d', label: '30 days' },\n { id: '90d', label: '90 days' },\n ];\n\n ngOnInit(): void {\n this.load();\n }\n\n protected setRange(id: string): void {\n this.range.set(id);\n this.load();\n }\n\n protected maxCount(items: { count: number }[]): number {\n return Math.max(...items.map((i) => i.count), 1);\n }\n\n protected barHeightPercent(point: { count: number }, points: { count: number }[]): number {\n if (point.count <= 0) return 0;\n return (point.count / this.maxCount(points)) * 100;\n }\n\n /** Hide inline counts when bars are too narrow to stay readable. */\n protected showBarValue(point: { count: number }, points: { count: number }[]): boolean {\n return point.count > 0 && points.length <= 21;\n }\n\n protected maxValue(items: { value: number }[]): number {\n return Math.max(...items.map((i) => i.value), 1);\n }\n\n protected volumeTooltip(point: AnalyticsVolumePoint): string {\n const datePart = point.date ? this.formatChartDate(point.date) : point.label;\n return `${datePart}: ${point.count} call${point.count === 1 ? '' : 's'}`;\n }\n\n protected peakTooltip(point: AnalyticsPeakPoint): string {\n return `${point.label}: ${point.count} call${point.count === 1 ? '' : 's'}`;\n }\n\n /** ISO or YYYY-MM-DD → MM/DD (zero-padded). */\n protected formatChartDate(date: string): string {\n const iso = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(date.trim());\n if (iso) {\n return `${iso[2]}/${iso[3]}`;\n }\n return date;\n }\n\n /** Compact x-axis for volume — full labels on 7d, sparse dates on longer ranges. */\n protected volumeAxisLabel(point: AnalyticsVolumePoint, index: number, total: number): string {\n if (point.date) {\n if (total <= 7) return this.formatChartDate(point.date);\n const step = total <= 14 ? 2 : total <= 31 ? 5 : 7;\n if (index % step !== 0 && index !== total - 1) return '';\n return this.formatChartDate(point.date);\n }\n if (total <= 7) return point.day;\n const step = total <= 14 ? 2 : total <= 31 ? 5 : 7;\n if (index % step !== 0 && index !== total - 1) return '';\n return point.day;\n }\n\n protected showPeakAxisLabel(index: number, total: number): boolean {\n if (total <= 12) return true;\n const step = total <= 24 ? 3 : 4;\n return index % step === 0 || index === total - 1;\n }\n\n protected volumeRangeLabel(): string {\n const vol = this.data()?.volume;\n if (!vol?.startDate || !vol?.endDate) return 'Calls per day';\n const start = this.formatChartDate(vol.startDate);\n const end = this.formatChartDate(vol.endDate);\n return `${start} – ${end}${vol.timezone ? ` (${vol.timezone})` : ''}`;\n }\n\n protected kpiIcon(label: string): string {\n const key = label.toLowerCase();\n if (key.includes('call') || key.includes('handled')) return 'call';\n if (key.includes('time') || key.includes('duration')) return 'schedule';\n if (key.includes('resolution') || key.includes('book')) return 'event_available';\n if (key.includes('transfer')) return 'phone_forwarded';\n return 'monitoring';\n }\n\n private load(): void {\n this.loading.set(true);\n this.api.getAnalytics(this.range()).subscribe(({ data }) => {\n this.data.set(data);\n this.loading.set(false);\n });\n }\n}\n","<div class=\"eds-page analytics-page\">\n <header class=\"analytics-page__header\">\n <div>\n <h1 class=\"eds-h1\">Analytics</h1>\n <p class=\"eds-body-sm\">Call volume, outcomes, and performance KPIs</p>\n </div>\n <div class=\"analytics-page__range\">\n @for (r of ranges; track r.id) {\n <button\n type=\"button\"\n class=\"analytics-page__range-btn\"\n [class.analytics-page__range-btn--active]=\"range() === r.id\"\n (click)=\"setRange(r.id)\"\n >\n {{ r.label }}\n </button>\n }\n </div>\n </header>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading analytics…</p>\n } @else if (data()) {\n <section class=\"analytics-page__kpis\">\n @for (kpi of data()!.kpiCards; track kpi.label) {\n <article class=\"eds-card analytics-page__kpi\">\n <eds-icon [name]=\"kpiIcon(kpi.label)\" [size]=\"22\" />\n <span class=\"eds-label\">{{ kpi.label }}</span>\n <span class=\"analytics-page__kpi-value\">{{ kpi.value }}</span>\n @if (kpi.delta) {\n <span class=\"analytics-page__kpi-delta\" [attr.data-tone]=\"kpi.tone\">{{ kpi.delta }}</span>\n }\n @if (kpi.sub) {\n <span class=\"analytics-page__kpi-sub\">{{ kpi.sub }}</span>\n }\n </article>\n }\n </section>\n\n <div class=\"analytics-page__charts\">\n <article class=\"eds-card analytics-page__chart\">\n <div class=\"analytics-page__chart-head\">\n <h2 class=\"eds-h2\">Call volume</h2>\n <span class=\"eds-label\">{{ volumeRangeLabel() }}</span>\n </div>\n <div class=\"analytics-page__volume-chart\">\n <div class=\"analytics-page__plot\">\n <div class=\"analytics-page__grid\" aria-hidden=\"true\"></div>\n <div class=\"analytics-page__bars\" role=\"list\" aria-label=\"Call volume by day\">\n @for (point of data()!.volume.points; track point.date || point.label; let i = $index) {\n <div\n class=\"analytics-page__bar-col\"\n role=\"listitem\"\n [attr.aria-label]=\"volumeTooltip(point)\"\n >\n <div class=\"analytics-page__bar-wrap\">\n @if (point.count > 0) {\n <div\n class=\"analytics-page__bar-stack\"\n [style.height.%]=\"barHeightPercent(point, data()!.volume.points)\"\n >\n @if (showBarValue(point, data()!.volume.points)) {\n <span class=\"analytics-page__bar-value eds-mono-sm\">{{ point.count }}</span>\n }\n <div class=\"analytics-page__bar\"></div>\n </div>\n }\n <span class=\"analytics-page__tooltip\">{{ volumeTooltip(point) }}</span>\n </div>\n <span class=\"eds-mono-sm analytics-page__bar-axis\">\n {{ volumeAxisLabel(point, i, data()!.volume.points.length) }}\n </span>\n </div>\n }\n </div>\n </div>\n </div>\n <!-- <p class=\"analytics-page__axis-note eds-label\">(MM/DD)/calls per day</p> -->\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <h2 class=\"eds-h2\">Outcomes</h2>\n <div class=\"analytics-page__bars analytics-page__bars--horizontal\">\n @for (item of data()!.outcomes; track item.label) {\n <div class=\"analytics-page__hbar-row\">\n <span class=\"eds-body-sm\">{{ item.label }}</span>\n <div class=\"analytics-page__hbar-track\">\n <span\n class=\"analytics-page__hbar-fill\"\n [style.width.%]=\"(item.value / maxValue(data()!.outcomes)) * 100\"\n [style.background]=\"item.color\"\n ></span>\n </div>\n <span class=\"eds-mono-sm\">{{ item.value }}%</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <h2 class=\"eds-h2\">Top reasons</h2>\n <div class=\"analytics-page__bars analytics-page__bars--horizontal\">\n @for (item of data()!.reasons; track item.label) {\n <div class=\"analytics-page__hbar-row\">\n <span class=\"eds-body-sm\">{{ item.label }}</span>\n <div class=\"analytics-page__hbar-track\">\n <span\n class=\"analytics-page__hbar-fill\"\n [style.width.%]=\"(item.value / maxValue(data()!.reasons)) * 100\"\n ></span>\n </div>\n <span class=\"eds-mono-sm\">{{ item.value }}%</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <div class=\"analytics-page__chart-head\">\n <h2 class=\"eds-h2\">Peak hours</h2>\n <span class=\"eds-label\">\n @if (data()!.peakHours.timezone) {\n {{ data()!.peakHours.timezone }}\n } @else {\n Calls per hour\n }\n </span>\n </div>\n <div class=\"analytics-page__volume-chart analytics-page__volume-chart--peak\">\n <div class=\"analytics-page__plot\">\n <div class=\"analytics-page__grid\" aria-hidden=\"true\"></div>\n <div class=\"analytics-page__bars analytics-page__bars--peak\" role=\"list\" aria-label=\"Call volume by hour\">\n @for (point of data()!.peakHours.points; track point.hour) {\n <div\n class=\"analytics-page__bar-col analytics-page__bar-col--peak\"\n role=\"listitem\"\n [attr.aria-label]=\"peakTooltip(point)\"\n >\n <div class=\"analytics-page__bar-wrap\">\n @if (point.count > 0) {\n <div\n class=\"analytics-page__bar-stack\"\n [style.height.%]=\"barHeightPercent(point, data()!.peakHours.points)\"\n >\n <div class=\"analytics-page__bar\"></div>\n </div>\n }\n <span class=\"analytics-page__tooltip\">{{ peakTooltip(point) }}</span>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <div class=\"analytics-page__peak-axis\">\n @for (point of data()!.peakHours.points; track point.hour; let i = $index) {\n <span class=\"analytics-page__peak-tick eds-mono-sm\">\n @if (showPeakAxisLabel(i, data()!.peakHours.points.length)) {\n {{ point.label }}\n }\n </span>\n }\n </div>\n </article>\n </div>\n }\n</div>\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"edvak99-voice-receptionist-widget-analytics-page.component-Dsujn5az.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/pages/analytics-page/analytics-page.component.ts","../../../projects/voice-receptionist-widget/src/lib/pages/analytics-page/analytics-page.component.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core';\n\nimport {\n AnalyticsData,\n AnalyticsPeakPoint,\n AnalyticsVolumePoint,\n} from '../../models/ai-receptionist.types';\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport { EdsIconComponent } from '../../ui/eds-icon.component';\n\n@Component({\n selector: 'eds-analytics-page',\n imports: [EdsIconComponent],\n templateUrl: './analytics-page.component.html',\n styleUrl: './analytics-page.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AnalyticsPageComponent implements OnInit {\n private readonly api = inject(AiReceptionistApiService);\n\n protected readonly data = signal<AnalyticsData | null>(null);\n protected readonly range = signal('30d');\n protected readonly loading = signal(true);\n\n protected readonly ranges = [\n { id: '7d', label: '7 days' },\n { id: '30d', label: '30 days' },\n { id: '90d', label: '90 days' },\n ];\n\n ngOnInit(): void {\n this.load();\n }\n\n protected setRange(id: string): void {\n this.range.set(id);\n this.load();\n }\n\n protected maxCount(items: { count: number }[]): number {\n return Math.max(...items.map((i) => i.count), 1);\n }\n\n protected barHeightPercent(point: { count: number }, points: { count: number }[]): number {\n if (point.count <= 0) return 0;\n return (point.count / this.maxCount(points)) * 100;\n }\n\n /** Hide inline counts when bars are too narrow to stay readable. */\n protected showBarValue(point: { count: number }, points: { count: number }[]): boolean {\n return point.count > 0 && points.length <= 21;\n }\n\n protected maxValue(items: { value: number }[]): number {\n return Math.max(...items.map((i) => i.value), 1);\n }\n\n protected volumeTooltip(point: AnalyticsVolumePoint): string {\n const datePart = point.date ? this.formatChartDate(point.date) : point.label;\n return `${datePart}: ${point.count} call${point.count === 1 ? '' : 's'}`;\n }\n\n protected peakTooltip(point: AnalyticsPeakPoint): string {\n return `${point.label}: ${point.count} call${point.count === 1 ? '' : 's'}`;\n }\n\n /** ISO or YYYY-MM-DD → MM/DD (zero-padded). */\n protected formatChartDate(date: string): string {\n const iso = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(date.trim());\n if (iso) {\n return `${iso[2]}/${iso[3]}`;\n }\n return date;\n }\n\n /** Compact x-axis for volume — full labels on 7d, sparse dates on longer ranges. */\n protected volumeAxisLabel(point: AnalyticsVolumePoint, index: number, total: number): string {\n if (point.date) {\n if (total <= 7) return this.formatChartDate(point.date);\n const step = total <= 14 ? 2 : total <= 31 ? 5 : 7;\n if (index % step !== 0 && index !== total - 1) return '';\n return this.formatChartDate(point.date);\n }\n if (total <= 7) return point.day;\n const step = total <= 14 ? 2 : total <= 31 ? 5 : 7;\n if (index % step !== 0 && index !== total - 1) return '';\n return point.day;\n }\n\n protected showPeakAxisLabel(index: number, total: number): boolean {\n if (total <= 12) return true;\n const step = total <= 24 ? 3 : 4;\n return index % step === 0 || index === total - 1;\n }\n\n protected volumeRangeLabel(): string {\n const vol = this.data()?.volume;\n if (!vol?.startDate || !vol?.endDate) return 'Calls per day';\n const start = this.formatChartDate(vol.startDate);\n const end = this.formatChartDate(vol.endDate);\n return `${start} – ${end}${vol.timezone ? ` (${vol.timezone})` : ''}`;\n }\n\n protected kpiIcon(label: string): string {\n const key = label.toLowerCase();\n if (key.includes('call') || key.includes('handled')) return 'call';\n if (key.includes('time') || key.includes('duration')) return 'schedule';\n if (key.includes('resolution') || key.includes('book')) return 'event_available';\n if (key.includes('transfer')) return 'phone_forwarded';\n return 'monitoring';\n }\n\n private load(): void {\n this.loading.set(true);\n this.api.getAnalytics(this.range()).subscribe(({ data }) => {\n this.data.set(data);\n this.loading.set(false);\n });\n }\n}\n","<div class=\"eds-page analytics-page\">\n <header class=\"analytics-page__header\">\n <div>\n <h1 class=\"eds-h1\">Analytics</h1>\n <p class=\"eds-body-sm\">Call volume, outcomes, and performance KPIs</p>\n </div>\n <div class=\"analytics-page__range\">\n @for (r of ranges; track r.id) {\n <button\n type=\"button\"\n class=\"analytics-page__range-btn\"\n [class.analytics-page__range-btn--active]=\"range() === r.id\"\n (click)=\"setRange(r.id)\"\n >\n {{ r.label }}\n </button>\n }\n </div>\n </header>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading analytics…</p>\n } @else if (data()) {\n <section class=\"analytics-page__kpis\">\n @for (kpi of data()!.kpiCards; track kpi.label) {\n <article class=\"eds-card analytics-page__kpi\">\n <eds-icon [name]=\"kpiIcon(kpi.label)\" [size]=\"22\" />\n <span class=\"eds-label\">{{ kpi.label }}</span>\n <span class=\"analytics-page__kpi-value\">{{ kpi.value }}</span>\n @if (kpi.delta) {\n <span class=\"analytics-page__kpi-delta\" [attr.data-tone]=\"kpi.tone\">{{ kpi.delta }}</span>\n }\n @if (kpi.sub) {\n <span class=\"analytics-page__kpi-sub\">{{ kpi.sub }}</span>\n }\n </article>\n }\n </section>\n\n <div class=\"analytics-page__charts\">\n <article class=\"eds-card analytics-page__chart\">\n <div class=\"analytics-page__chart-head\">\n <h2 class=\"eds-h2\">Call volume</h2>\n <span class=\"eds-label\">{{ volumeRangeLabel() }}</span>\n </div>\n <div class=\"analytics-page__volume-chart\">\n <div class=\"analytics-page__plot\">\n <div class=\"analytics-page__grid\" aria-hidden=\"true\"></div>\n <div class=\"analytics-page__bars\" role=\"list\" aria-label=\"Call volume by day\">\n @for (point of data()!.volume.points; track point.date || point.label; let i = $index) {\n <div\n class=\"analytics-page__bar-col\"\n role=\"listitem\"\n [attr.aria-label]=\"volumeTooltip(point)\"\n >\n <div class=\"analytics-page__bar-wrap\">\n @if (point.count > 0) {\n <div\n class=\"analytics-page__bar-stack\"\n [style.height.%]=\"barHeightPercent(point, data()!.volume.points)\"\n >\n @if (showBarValue(point, data()!.volume.points)) {\n <span class=\"analytics-page__bar-value eds-mono-sm\">{{ point.count }}</span>\n }\n <div class=\"analytics-page__bar\"></div>\n </div>\n }\n <span class=\"analytics-page__tooltip\">{{ volumeTooltip(point) }}</span>\n </div>\n <span class=\"eds-mono-sm analytics-page__bar-axis\">\n {{ volumeAxisLabel(point, i, data()!.volume.points.length) }}\n </span>\n </div>\n }\n </div>\n </div>\n </div>\n <!-- <p class=\"analytics-page__axis-note eds-label\">(MM/DD)/calls per day</p> -->\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <h2 class=\"eds-h2\">Outcomes</h2>\n <div class=\"analytics-page__bars analytics-page__bars--horizontal\">\n @for (item of data()!.outcomes; track item.label) {\n <div class=\"analytics-page__hbar-row\">\n <span class=\"eds-body-sm\">{{ item.label }}</span>\n <div class=\"analytics-page__hbar-track\">\n <span\n class=\"analytics-page__hbar-fill\"\n [style.width.%]=\"(item.value / maxValue(data()!.outcomes)) * 100\"\n [style.background]=\"item.color\"\n ></span>\n </div>\n <span class=\"eds-mono-sm\">{{ item.value }}%</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <h2 class=\"eds-h2\">Top reasons</h2>\n <div class=\"analytics-page__bars analytics-page__bars--horizontal\">\n @for (item of data()!.reasons; track item.label) {\n <div class=\"analytics-page__hbar-row\">\n <span class=\"eds-body-sm\">{{ item.label }}</span>\n <div class=\"analytics-page__hbar-track\">\n <span\n class=\"analytics-page__hbar-fill\"\n [style.width.%]=\"(item.value / maxValue(data()!.reasons)) * 100\"\n ></span>\n </div>\n <span class=\"eds-mono-sm\">{{ item.value }}%</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"eds-card analytics-page__chart\">\n <div class=\"analytics-page__chart-head\">\n <h2 class=\"eds-h2\">Peak hours</h2>\n <span class=\"eds-label\">\n @if (data()!.peakHours.timezone) {\n {{ data()!.peakHours.timezone }}\n } @else {\n Calls per hour\n }\n </span>\n </div>\n <div class=\"analytics-page__volume-chart analytics-page__volume-chart--peak\">\n <div class=\"analytics-page__plot\">\n <div class=\"analytics-page__grid\" aria-hidden=\"true\"></div>\n <div class=\"analytics-page__bars analytics-page__bars--peak\" role=\"list\" aria-label=\"Call volume by hour\">\n @for (point of data()!.peakHours.points; track point.hour) {\n <div\n class=\"analytics-page__bar-col analytics-page__bar-col--peak\"\n role=\"listitem\"\n [attr.aria-label]=\"peakTooltip(point)\"\n >\n <div class=\"analytics-page__bar-wrap\">\n @if (point.count > 0) {\n <div\n class=\"analytics-page__bar-stack\"\n [style.height.%]=\"barHeightPercent(point, data()!.peakHours.points)\"\n >\n <div class=\"analytics-page__bar\"></div>\n </div>\n }\n <span class=\"analytics-page__tooltip\">{{ peakTooltip(point) }}</span>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <div class=\"analytics-page__peak-axis\">\n @for (point of data()!.peakHours.points; track point.hour; let i = $index) {\n <span class=\"analytics-page__peak-tick eds-mono-sm\">\n @if (showPeakAxisLabel(i, data()!.peakHours.points.length)) {\n {{ point.label }}\n }\n </span>\n }\n </div>\n </article>\n </div>\n }\n</div>\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, signal, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { DatePipe } from '@angular/common';
|
|
4
|
+
import * as i1 from '@angular/forms';
|
|
5
|
+
import { FormsModule } from '@angular/forms';
|
|
6
|
+
import { A as AiReceptionistApiService, D as DEFAULT_PAGE_SIZE, f as totalPages, E as EdsButtonComponent, b as EdsIconComponent } from './edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-Dn0GrZ6_.mjs';
|
|
7
|
+
|
|
8
|
+
class AuditPageComponent {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.api = inject(AiReceptionistApiService);
|
|
11
|
+
this.pageSize = DEFAULT_PAGE_SIZE;
|
|
12
|
+
this.events = signal([]);
|
|
13
|
+
this.total = signal(0);
|
|
14
|
+
this.page = signal(1);
|
|
15
|
+
this.search = signal('');
|
|
16
|
+
this.actor = signal('');
|
|
17
|
+
this.loading = signal(true);
|
|
18
|
+
}
|
|
19
|
+
ngOnInit() {
|
|
20
|
+
this.load();
|
|
21
|
+
}
|
|
22
|
+
load() {
|
|
23
|
+
this.loading.set(true);
|
|
24
|
+
this.api
|
|
25
|
+
.getAudit(this.search() || undefined, {
|
|
26
|
+
page: this.page(),
|
|
27
|
+
limit: DEFAULT_PAGE_SIZE,
|
|
28
|
+
})
|
|
29
|
+
.subscribe({
|
|
30
|
+
next: ({ items, total, page }) => {
|
|
31
|
+
this.events.set(items);
|
|
32
|
+
this.total.set(total);
|
|
33
|
+
this.page.set(page);
|
|
34
|
+
this.loading.set(false);
|
|
35
|
+
},
|
|
36
|
+
error: () => this.loading.set(false),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
searchSubmit() {
|
|
40
|
+
this.page.set(1);
|
|
41
|
+
this.load();
|
|
42
|
+
}
|
|
43
|
+
totalPages() {
|
|
44
|
+
return totalPages(this.total(), DEFAULT_PAGE_SIZE);
|
|
45
|
+
}
|
|
46
|
+
prevPage() {
|
|
47
|
+
if (this.page() <= 1)
|
|
48
|
+
return;
|
|
49
|
+
this.page.update((p) => p - 1);
|
|
50
|
+
this.load();
|
|
51
|
+
}
|
|
52
|
+
nextPage() {
|
|
53
|
+
if (this.page() >= this.totalPages())
|
|
54
|
+
return;
|
|
55
|
+
this.page.update((p) => p + 1);
|
|
56
|
+
this.load();
|
|
57
|
+
}
|
|
58
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AuditPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
59
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: AuditPageComponent, isStandalone: true, selector: "eds-audit-page", ngImport: i0, template: "<div class=\"eds-page audit-page\">\n <header class=\"audit-page__header\">\n <div>\n <h1 class=\"eds-h1\">Audit log</h1>\n <p class=\"eds-body-sm\">Track configuration and agent activity</p>\n </div>\n </header>\n\n <div class=\"eds-card audit-page__filters\">\n <input\n type=\"search\"\n class=\"audit-page__input\"\n placeholder=\"Search action, target, detail\u2026\"\n [ngModel]=\"search()\"\n (ngModelChange)=\"search.set($event)\"\n (keyup.enter)=\"searchSubmit()\"\n />\n <input\n type=\"text\"\n class=\"audit-page__input\"\n placeholder=\"Filter by actor\"\n [ngModel]=\"actor()\"\n (ngModelChange)=\"actor.set($event)\"\n (keyup.enter)=\"searchSubmit()\"\n />\n <eds-button variant=\"primary\" (click)=\"searchSubmit()\">\n <eds-icon name=\"search\" [size]=\"18\" /> Search\n </eds-button>\n </div>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading audit events\u2026</p>\n } @else {\n <div class=\"eds-card audit-page__table-wrap\">\n <table class=\"eds-table\">\n <thead>\n <tr>\n <th>When</th>\n <th>Actor</th>\n <th>Action</th>\n <th>Target</th>\n <th>Detail</th>\n </tr>\n </thead>\n <tbody>\n @for (event of events(); track event.id) {\n <tr>\n <td class=\"eds-mono-sm\">{{ event.at | date: 'medium' }}</td>\n <td>{{ event.actor }}</td>\n <td class=\"eds-body-sm-bold\">{{ event.action }}</td>\n <td class=\"eds-mono-sm\">{{ event.target }}</td>\n <td>{{ event.detail }}</td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"5\" class=\"audit-page__empty\">No events match your filters.</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <footer class=\"audit-page__pager\">\n <span class=\"eds-body-sm\">\n Page {{ page() }} of {{ totalPages() }} \u00B7 {{ total() }} total\n </span>\n <div class=\"audit-page__pager-actions\">\n <eds-button variant=\"secondary\" [disabled]=\"page() <= 1\" (click)=\"prevPage()\">Previous</eds-button>\n <eds-button variant=\"secondary\" [disabled]=\"page() >= totalPages()\" (click)=\"nextPage()\">Next</eds-button>\n </div>\n </footer>\n }\n</div>\n", styles: [".audit-page__header{margin-bottom:20px}.audit-page__filters{display:flex;flex-wrap:wrap;gap:10px;padding:14px;margin-bottom:16px;align-items:center}.audit-page__input{flex:1;min-width:180px;padding:8px 12px;border:1px solid var(--eds-border-default);border-radius:var(--eds-radius-md);font-size:14px}.audit-page__table-wrap{overflow-x:auto;padding:0}.audit-page__empty{text-align:center;padding:32px!important;color:var(--eds-text-secondary)}.audit-page__pager{display:flex;align-items:center;justify-content:space-between;margin-top:16px;padding-top:12px;border-top:1px solid var(--eds-border-subtle, #e4e4e7)}.audit-page__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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
60
|
+
}
|
|
61
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AuditPageComponent, decorators: [{
|
|
62
|
+
type: Component,
|
|
63
|
+
args: [{ selector: 'eds-audit-page', imports: [DatePipe, FormsModule, EdsButtonComponent, EdsIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"eds-page audit-page\">\n <header class=\"audit-page__header\">\n <div>\n <h1 class=\"eds-h1\">Audit log</h1>\n <p class=\"eds-body-sm\">Track configuration and agent activity</p>\n </div>\n </header>\n\n <div class=\"eds-card audit-page__filters\">\n <input\n type=\"search\"\n class=\"audit-page__input\"\n placeholder=\"Search action, target, detail\u2026\"\n [ngModel]=\"search()\"\n (ngModelChange)=\"search.set($event)\"\n (keyup.enter)=\"searchSubmit()\"\n />\n <input\n type=\"text\"\n class=\"audit-page__input\"\n placeholder=\"Filter by actor\"\n [ngModel]=\"actor()\"\n (ngModelChange)=\"actor.set($event)\"\n (keyup.enter)=\"searchSubmit()\"\n />\n <eds-button variant=\"primary\" (click)=\"searchSubmit()\">\n <eds-icon name=\"search\" [size]=\"18\" /> Search\n </eds-button>\n </div>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading audit events\u2026</p>\n } @else {\n <div class=\"eds-card audit-page__table-wrap\">\n <table class=\"eds-table\">\n <thead>\n <tr>\n <th>When</th>\n <th>Actor</th>\n <th>Action</th>\n <th>Target</th>\n <th>Detail</th>\n </tr>\n </thead>\n <tbody>\n @for (event of events(); track event.id) {\n <tr>\n <td class=\"eds-mono-sm\">{{ event.at | date: 'medium' }}</td>\n <td>{{ event.actor }}</td>\n <td class=\"eds-body-sm-bold\">{{ event.action }}</td>\n <td class=\"eds-mono-sm\">{{ event.target }}</td>\n <td>{{ event.detail }}</td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"5\" class=\"audit-page__empty\">No events match your filters.</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <footer class=\"audit-page__pager\">\n <span class=\"eds-body-sm\">\n Page {{ page() }} of {{ totalPages() }} \u00B7 {{ total() }} total\n </span>\n <div class=\"audit-page__pager-actions\">\n <eds-button variant=\"secondary\" [disabled]=\"page() <= 1\" (click)=\"prevPage()\">Previous</eds-button>\n <eds-button variant=\"secondary\" [disabled]=\"page() >= totalPages()\" (click)=\"nextPage()\">Next</eds-button>\n </div>\n </footer>\n }\n</div>\n", styles: [".audit-page__header{margin-bottom:20px}.audit-page__filters{display:flex;flex-wrap:wrap;gap:10px;padding:14px;margin-bottom:16px;align-items:center}.audit-page__input{flex:1;min-width:180px;padding:8px 12px;border:1px solid var(--eds-border-default);border-radius:var(--eds-radius-md);font-size:14px}.audit-page__table-wrap{overflow-x:auto;padding:0}.audit-page__empty{text-align:center;padding:32px!important;color:var(--eds-text-secondary)}.audit-page__pager{display:flex;align-items:center;justify-content:space-between;margin-top:16px;padding-top:12px;border-top:1px solid var(--eds-border-subtle, #e4e4e7)}.audit-page__pager-actions{display:flex;gap:8px}\n"] }]
|
|
64
|
+
}] });
|
|
65
|
+
|
|
66
|
+
export { AuditPageComponent };
|
|
67
|
+
//# sourceMappingURL=edvak99-voice-receptionist-widget-audit-page.component-xoLlHj8K.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edvak99-voice-receptionist-widget-audit-page.component-xoLlHj8K.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/pages/audit-page/audit-page.component.ts","../../../projects/voice-receptionist-widget/src/lib/pages/audit-page/audit-page.component.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core';\nimport { DatePipe } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\n\nimport { AuditEvent } from '../../models/ai-receptionist.types';\nimport { DEFAULT_PAGE_SIZE, totalPages as calcTotalPages } from '../../models/pagination';\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport { EdsButtonComponent } from '../../ui/eds-button.component';\nimport { EdsIconComponent } from '../../ui/eds-icon.component';\n\n@Component({\n selector: 'eds-audit-page',\n imports: [DatePipe, FormsModule, EdsButtonComponent, EdsIconComponent],\n templateUrl: './audit-page.component.html',\n styleUrl: './audit-page.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AuditPageComponent implements OnInit {\n private readonly api = inject(AiReceptionistApiService);\n\n protected readonly pageSize = DEFAULT_PAGE_SIZE;\n protected readonly events = signal<AuditEvent[]>([]);\n protected readonly total = signal(0);\n protected readonly page = signal(1);\n protected readonly search = signal('');\n protected readonly actor = signal('');\n protected readonly loading = signal(true);\n\n ngOnInit(): void {\n this.load();\n }\n\n protected load(): void {\n this.loading.set(true);\n this.api\n .getAudit(this.search() || undefined, {\n page: this.page(),\n limit: DEFAULT_PAGE_SIZE,\n })\n .subscribe({\n next: ({ items, total, page }) => {\n this.events.set(items);\n this.total.set(total);\n this.page.set(page);\n this.loading.set(false);\n },\n error: () => this.loading.set(false),\n });\n }\n\n protected searchSubmit(): void {\n this.page.set(1);\n this.load();\n }\n\n protected totalPages(): number {\n return calcTotalPages(this.total(), DEFAULT_PAGE_SIZE);\n }\n\n protected prevPage(): void {\n if (this.page() <= 1) return;\n this.page.update((p) => p - 1);\n this.load();\n }\n\n protected nextPage(): void {\n if (this.page() >= this.totalPages()) return;\n this.page.update((p) => p + 1);\n this.load();\n }\n}\n","<div class=\"eds-page audit-page\">\n <header class=\"audit-page__header\">\n <div>\n <h1 class=\"eds-h1\">Audit log</h1>\n <p class=\"eds-body-sm\">Track configuration and agent activity</p>\n </div>\n </header>\n\n <div class=\"eds-card audit-page__filters\">\n <input\n type=\"search\"\n class=\"audit-page__input\"\n placeholder=\"Search action, target, detail…\"\n [ngModel]=\"search()\"\n (ngModelChange)=\"search.set($event)\"\n (keyup.enter)=\"searchSubmit()\"\n />\n <input\n type=\"text\"\n class=\"audit-page__input\"\n placeholder=\"Filter by actor\"\n [ngModel]=\"actor()\"\n (ngModelChange)=\"actor.set($event)\"\n (keyup.enter)=\"searchSubmit()\"\n />\n <eds-button variant=\"primary\" (click)=\"searchSubmit()\">\n <eds-icon name=\"search\" [size]=\"18\" /> Search\n </eds-button>\n </div>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading audit events…</p>\n } @else {\n <div class=\"eds-card audit-page__table-wrap\">\n <table class=\"eds-table\">\n <thead>\n <tr>\n <th>When</th>\n <th>Actor</th>\n <th>Action</th>\n <th>Target</th>\n <th>Detail</th>\n </tr>\n </thead>\n <tbody>\n @for (event of events(); track event.id) {\n <tr>\n <td class=\"eds-mono-sm\">{{ event.at | date: 'medium' }}</td>\n <td>{{ event.actor }}</td>\n <td class=\"eds-body-sm-bold\">{{ event.action }}</td>\n <td class=\"eds-mono-sm\">{{ event.target }}</td>\n <td>{{ event.detail }}</td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"5\" class=\"audit-page__empty\">No events match your filters.</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <footer class=\"audit-page__pager\">\n <span class=\"eds-body-sm\">\n Page {{ page() }} of {{ totalPages() }} · {{ total() }} total\n </span>\n <div class=\"audit-page__pager-actions\">\n <eds-button variant=\"secondary\" [disabled]=\"page() <= 1\" (click)=\"prevPage()\">Previous</eds-button>\n <eds-button variant=\"secondary\" [disabled]=\"page() >= totalPages()\" (click)=\"nextPage()\">Next</eds-button>\n </div>\n </footer>\n }\n</div>\n"],"names":["calcTotalPages"],"mappings":"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"}
|