@edvak99/voice-receptionist-widget 1.1.11 → 1.1.13
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 +23 -14
- package/fesm2022/{edvak99-voice-receptionist-widget-activities-page.component-CACfxM4x.mjs → edvak99-voice-receptionist-widget-activities-page.component-Br2Vi1Im.mjs} +3 -4
- package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-Br2Vi1Im.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-analytics-page.component-Dj9vw97B.mjs → edvak99-voice-receptionist-widget-analytics-page.component-CglgGV6H.mjs} +2 -3
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-CglgGV6H.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-api-log.util-C0TOKF1q.mjs.map +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-api-logs-page.component-jKBA1N2A.mjs → edvak99-voice-receptionist-widget-api-logs-page.component-DPJiuNjk.mjs} +7 -6
- package/fesm2022/edvak99-voice-receptionist-widget-api-logs-page.component-DPJiuNjk.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-api-logs-table.component-CEf4NPe5.mjs → edvak99-voice-receptionist-widget-api-logs-table.component-CiNc65dO.mjs} +2 -3
- package/fesm2022/edvak99-voice-receptionist-widget-api-logs-table.component-CiNc65dO.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-call-history-detail-page.component-DEplWBbz.mjs → edvak99-voice-receptionist-widget-call-history-detail-page.component-ENq2c-sm.mjs} +5 -7
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-ENq2c-sm.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-DoYJFpSa.mjs +200 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-DoYJFpSa.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-call-recording-player.component-j1mY5wU0.mjs → edvak99-voice-receptionist-widget-call-recording-player.component-GVg1g-Vq.mjs} +2 -3
- package/fesm2022/edvak99-voice-receptionist-widget-call-recording-player.component-GVg1g-Vq.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-call-review.service-DQ4TT_Su.mjs → edvak99-voice-receptionist-widget-call-review.service-WG30w6Jl.mjs} +2 -2
- package/fesm2022/edvak99-voice-receptionist-widget-call-review.service-WG30w6Jl.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-call-review.util-Deh3jPgo.mjs.map +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-dashboard-page.component-DPxQ-wst.mjs → edvak99-voice-receptionist-widget-dashboard-page.component-B_EYljRs.mjs} +4 -5
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-B_EYljRs.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-avatar.component-BJs_TH8X.mjs.map +1 -1
- package/fesm2022/edvak99-voice-receptionist-widget-eds-elapsed.pipe-BFCiJOT3.mjs.map +1 -1
- package/fesm2022/edvak99-voice-receptionist-widget-eds-pulse-dot.component-B8yxY-7p.mjs +14 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-pulse-dot.component-B8yxY-7p.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-search-field.component-OGRDvYBp.mjs +83 -0
- package/fesm2022/edvak99-voice-receptionist-widget-eds-search-field.component-OGRDvYBp.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-ai-receptionist-api.service-DcRiHn8q.mjs → edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-pS2jeB4x.mjs} +612 -8
- package/fesm2022/edvak99-voice-receptionist-widget-edvak99-voice-receptionist-widget-pS2jeB4x.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-1woGuh0J.mjs +74 -0
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-1woGuh0J.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-live-monitor-page.component-BOQeTD3P.mjs → edvak99-voice-receptionist-widget-live-monitor-page.component-ihwm1LNn.mjs} +5 -7
- package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-ihwm1LNn.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-modal.component-uYApl1KF.mjs.map +1 -1
- package/fesm2022/{edvak99-voice-receptionist-widget-outbound-purposes-page.component-D407n7l0.mjs → edvak99-voice-receptionist-widget-outbound-purposes-page.component-B2koBFU3.mjs} +2 -3
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-B2koBFU3.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-settings-page.component-YAvQ_XV6.mjs → edvak99-voice-receptionist-widget-settings-page.component-C6F-Hgmk.mjs} +2 -3
- package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-C6F-Hgmk.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-task-detail-drawer.component-B3KmFJNH.mjs → edvak99-voice-receptionist-widget-task-detail-drawer.component-BojdiNNx.mjs} +2 -2
- package/fesm2022/edvak99-voice-receptionist-widget-task-detail-drawer.component-BojdiNNx.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-tasks-page.component-BIOGiTgM.mjs → edvak99-voice-receptionist-widget-tasks-page.component-Cf76XyCz.mjs} +3 -4
- package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-Cf76XyCz.mjs.map +1 -0
- package/fesm2022/{edvak99-voice-receptionist-widget-wizard-page.component-DpF6y1go.mjs → edvak99-voice-receptionist-widget-wizard-page.component-BQTlz2F5.mjs} +2 -3
- package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-BQTlz2F5.mjs.map +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget.mjs +1 -262
- package/fesm2022/edvak99-voice-receptionist-widget.mjs.map +1 -1
- package/lib/layout/eds-app-shell.component.d.ts +5 -0
- package/lib/layout/eds-route-progress.component.d.ts +15 -0
- package/lib/models/ai-receptionist.types.d.ts +2 -0
- package/lib/models/api-dto.types.d.ts +2 -0
- package/lib/pages/live-calls-page/live-calls-page.component.d.ts +1 -4
- package/lib/pages/live-calls-page/live-calls-shell.component.d.ts +13 -0
- package/lib/services/live-calls-list.service.d.ts +2 -0
- package/lib/ui/eds-search-field.component.d.ts +22 -0
- package/lib/utils/call-history-display.util.d.ts +1 -0
- package/package.json +1 -1
- package/public-api.d.ts +1 -0
- package/fesm2022/edvak99-voice-receptionist-widget-activities-page.component-CACfxM4x.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-ai-receptionist-api.service-DcRiHn8q.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-Dj9vw97B.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-api-logs-page.component-jKBA1N2A.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-api-logs-table.component-CEf4NPe5.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-detail-page.component-DEplWBbz.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-CkjKCteR.mjs +0 -193
- package/fesm2022/edvak99-voice-receptionist-widget-call-history-page.component-CkjKCteR.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-call-recording-player.component-j1mY5wU0.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-call-review.service-DQ4TT_Su.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-page.component-DPxQ-wst.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-socket.service-tTQIsZO6.mjs +0 -120
- package/fesm2022/edvak99-voice-receptionist-widget-dashboard-socket.service-tTQIsZO6.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-eds-pulse-dot.component-CUWZfYRR.mjs +0 -130
- package/fesm2022/edvak99-voice-receptionist-widget-eds-pulse-dot.component-CUWZfYRR.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-BG9cB3zW.mjs +0 -81
- package/fesm2022/edvak99-voice-receptionist-widget-live-calls-page.component-BG9cB3zW.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-live-monitor-page.component-BOQeTD3P.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-outbound-purposes-page.component-D407n7l0.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-settings-page.component-YAvQ_XV6.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-task-detail-drawer.component-B3KmFJNH.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-tasks-page.component-BIOGiTgM.mjs.map +0 -1
- package/fesm2022/edvak99-voice-receptionist-widget-wizard-page.component-DpF6y1go.mjs.map +0 -1
package/fesm2022/edvak99-voice-receptionist-widget-analytics-page.component-Dj9vw97B.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"edvak99-voice-receptionist-widget-analytics-page.component-Dj9vw97B.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, computed, inject, OnInit, signal } from '@angular/core';\n\nimport {\n AnalyticsData,\n AnalyticsKpiCard,\n AnalyticsPeakPoint,\n AnalyticsVolumePoint,\n} from '../../models/ai-receptionist.types';\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport { EdsButtonComponent } from '../../ui/eds-button.component';\nimport { EdsIconComponent } from '../../ui/eds-icon.component';\n\nconst REASON_COLORS = ['#8E51FF', '#3681F4', '#F472B6', '#7DD3FC', '#FDA4AF', '#34D399', '#FBBF24'];\nconst STUCK_COLORS = ['#8E51FF', '#3681F4', '#7DD3FC', '#FBBF24'];\n\n@Component({\n selector: 'eds-analytics-page',\n imports: [EdsIconComponent, EdsButtonComponent],\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 protected readonly chartsReady = signal(false);\n protected readonly hoveredVolume = signal<number | null>(null);\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 protected readonly impactKpi = computed(() =>\n this.findKpi(/(impact|hours|front-desk|time saved)/i),\n );\n protected readonly resolutionKpi = computed(() =>\n this.findKpi(/(autonomous|resolution)/i),\n );\n protected readonly secondaryKpis = computed(() => {\n const cards = this.data()?.kpiCards ?? [];\n const preferred = [\n /appointment/i,\n /task/i,\n /patient|registr/i,\n /calls?\\s*handled|total\\s*calls|^calls$/i,\n ];\n const picked: AnalyticsKpiCard[] = [];\n for (const re of preferred) {\n const hit = cards.find((c) => re.test(c.label) && !picked.includes(c));\n if (hit) picked.push(hit);\n }\n if (picked.length >= 4) return picked.slice(0, 4);\n const rest = cards.filter(\n (c) =>\n !picked.includes(c) &&\n !/(impact|hours|front-desk|autonomous|resolution)/i.test(c.label),\n );\n return [...picked, ...rest].slice(0, 4);\n });\n\n protected readonly distribution = computed(() => {\n const outcomes = this.data()?.outcomes ?? [];\n const total = outcomes.reduce((s, o) => s + o.value, 0) || 1;\n return outcomes.map((o) => ({\n ...o,\n pct: Math.round((o.value / total) * 100),\n width: (o.value / total) * 100,\n }));\n });\n\n protected readonly totalCallsLabel = computed(() => {\n const handled = this.findKpi(/calls?\\s*handled|total\\s*calls|^calls$/i);\n if (handled?.value) return handled.value.includes('Call') ? handled.value : `${handled.value} Calls`;\n const n = this.data()?.kpis.totalCalls;\n return n ? `${n.toLocaleString()} Calls` : 'Calls';\n });\n\n protected readonly directionSub = computed(() => {\n const handled = this.findKpi(/calls?\\s*handled|total\\s*calls|^calls$/i);\n if (handled?.sub && /inbound|outbound/i.test(handled.sub)) return handled.sub;\n return null;\n });\n\n protected readonly volumePoints = computed(() => this.data()?.volume.points ?? []);\n\n /** Human date span for the selected analytics window (month + year included). */\n protected readonly periodLabel = computed(() => {\n const vol = this.data()?.volume;\n const start =\n this.parseIsoDate(vol?.startDate) ?? this.parseIsoDate(vol?.points?.[0]?.date) ?? null;\n const end =\n this.parseIsoDate(vol?.endDate) ??\n this.parseIsoDate(vol?.points?.[vol.points.length - 1]?.date) ??\n null;\n if (start && end) return this.formatPeriodRange(start, end);\n\n const days = this.range() === '7d' ? 7 : this.range() === '90d' ? 90 : 30;\n const endD = new Date();\n const startD = new Date();\n startD.setDate(endD.getDate() - (days - 1));\n return this.formatPeriodRange(startD, endD);\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 protected maxValue(items: { value: number }[]): number {\n return Math.max(...items.map((i) => i.value), 1);\n }\n\n protected reasonColor(index: number): string {\n return REASON_COLORS[index % REASON_COLORS.length];\n }\n\n protected stuckColor(index: number): string {\n return STUCK_COLORS[index % STUCK_COLORS.length];\n }\n\n protected peakTooltip(point: AnalyticsPeakPoint): string {\n return `${point.label}: ${point.count} call${point.count === 1 ? '' : 's'}`;\n }\n\n protected volumeTooltip(point: AnalyticsVolumePoint): string {\n const inbound = point.inbound ?? Math.round(point.count * 0.8);\n const outbound = point.outbound ?? Math.max(0, point.count - inbound);\n const headline = this.volumePointHeadline(point);\n return `${headline}\\nTotal ${point.count} · In ${inbound} · Out ${outbound}`;\n }\n\n protected hbarTooltip(label: string, value: number): string {\n return `${label}: ${value}%`;\n }\n\n protected distributionTooltip(label: string, value: number): string {\n const calls = this.estimatedCalls(value);\n return `${label}: ${value}% (${calls.toLocaleString()} calls)`;\n }\n\n protected showPeakAxisLabel(index: number, total: number): boolean {\n if (total <= 12) return true;\n const hour = this.data()?.peakHours.points[index]?.hour;\n if (hour != null && [9, 12, 15, 18, 21].includes(hour)) return true;\n const step = total <= 24 ? 3 : 4;\n return index % step === 0 || index === total - 1;\n }\n\n protected volumePath(series: 'total' | 'inbound' | 'outbound'): string {\n const points = this.volumePoints();\n if (!points.length) return '';\n const max = Math.max(\n ...points.map((p) => Math.max(p.count, p.inbound ?? 0, p.outbound ?? 0)),\n 1,\n );\n const w = 100;\n const h = 100;\n const step = points.length === 1 ? 0 : w / (points.length - 1);\n return points\n .map((p, i) => {\n const yVal =\n series === 'inbound'\n ? (p.inbound ?? Math.round(p.count * 0.8))\n : series === 'outbound'\n ? (p.outbound ?? Math.round(p.count * 0.2))\n : p.count;\n const x = i * step;\n const y = h - (yVal / max) * h;\n return `${i === 0 ? 'M' : 'L'}${x.toFixed(2)},${y.toFixed(2)}`;\n })\n .join(' ');\n }\n\n protected volumeAreaPath(): string {\n const line = this.volumePath('total');\n if (!line) return '';\n return `${line} L100,100 L0,100 Z`;\n }\n\n protected volumeAxisLabels(): string[] {\n const points = this.volumePoints();\n return points.map((p, i) => this.volumeAxisLabel(p, i, points.length));\n }\n\n protected volumeAxisLabel(point: AnalyticsVolumePoint, index: number, total: number): string {\n if (/week/i.test(point.day) || /week/i.test(point.label)) {\n const week = point.day || point.label;\n const month = point.date ? this.formatMonthYear(point.date) : '';\n return month ? `${week} · ${month}` : week;\n }\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 return point.day || point.label;\n }\n\n protected deltaTone(kpi: AnalyticsKpiCard | null | undefined): string {\n if (!kpi?.delta) return 'neutral';\n if (kpi.tone === 'pos' || kpi.tone === 'warn' || kpi.tone === 'neg') return kpi.tone;\n if (/^\\+/.test(kpi.delta.trim())) return 'pos';\n if (/^-|−/.test(kpi.delta.trim())) return 'warn';\n return 'neutral';\n }\n\n protected formatChartDate(date: string): string {\n const iso = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(date.trim());\n if (iso) return `${iso[2]}/${iso[3]}`;\n return date;\n }\n\n protected estimatedCalls(pct: number): number {\n const total = this.data()?.kpis.totalCalls ?? 0;\n if (!total) return Math.round(pct);\n return Math.round((total * pct) / 100);\n }\n\n protected onVolumeEnter(index: number): void {\n this.hoveredVolume.set(index);\n }\n\n protected onVolumeLeave(): void {\n this.hoveredVolume.set(null);\n }\n\n private volumePointHeadline(point: AnalyticsVolumePoint): string {\n const raw = (point.label || point.day || '').trim();\n if (/week/i.test(raw) && point.date) {\n return `${raw} · ${this.formatMonthYear(point.date)}`;\n }\n if (point.date) return this.formatFullChartDate(point.date);\n return raw || 'Period';\n }\n\n private formatMonthYear(date: string): string {\n const d = this.parseIsoDate(date);\n if (!d) return '';\n return d.toLocaleDateString(undefined, { month: 'short', year: 'numeric' });\n }\n\n private formatFullChartDate(date: string): string {\n const d = this.parseIsoDate(date);\n if (!d) return this.formatChartDate(date);\n return d.toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n }\n\n private formatPeriodRange(start: Date, end: Date): string {\n const sameYear = start.getFullYear() === end.getFullYear();\n const sameMonth = sameYear && start.getMonth() === end.getMonth();\n if (sameMonth) {\n return `${start.toLocaleDateString(undefined, { month: 'short', day: 'numeric' })} – ${end.getDate()}, ${end.getFullYear()}`;\n }\n if (sameYear) {\n return `${start.toLocaleDateString(undefined, { month: 'short', day: 'numeric' })} – ${end.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })}`;\n }\n return `${start.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })} – ${end.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })}`;\n }\n\n private parseIsoDate(value: string | null | undefined): Date | null {\n if (!value?.trim()) return null;\n const iso = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(value.trim());\n if (iso) {\n const d = new Date(Number(iso[1]), Number(iso[2]) - 1, Number(iso[3]));\n return Number.isNaN(d.getTime()) ? null : d;\n }\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n\n private findKpi(re: RegExp): AnalyticsKpiCard | null {\n return this.data()?.kpiCards.find((c) => re.test(c.label)) ?? null;\n }\n\n private load(): void {\n this.loading.set(true);\n this.chartsReady.set(false);\n this.hoveredVolume.set(null);\n this.api.getAnalytics(this.range()).subscribe(({ data }) => {\n this.data.set(data);\n this.loading.set(false);\n // Next frame so CSS enter animations run after paint.\n requestAnimationFrame(() => this.chartsReady.set(true));\n });\n }\n}\n","<div class=\"eds-page analytics-page\">\n <header class=\"mb-5 flex flex-wrap items-start justify-between gap-4\">\n <div>\n <h1 class=\"eds-h1\">Analytics</h1>\n <p class=\"eds-body-sm\">How the AI is doing over time, and what’s worth changing.</p>\n <p class=\"mt-1.5 text-sm font-medium text-zinc-500\">\n Showing data for\n <span class=\"text-zinc-700\">{{ periodLabel() }}</span>\n </p>\n </div>\n <div class=\"flex flex-wrap items-center gap-2\">\n <div class=\"analytics-page__range flex gap-1 rounded-md border p-1\" role=\"group\" aria-label=\"Date range\">\n @for (r of ranges; track r.id) {\n <button\n type=\"button\"\n class=\"analytics-page__range-btn cursor-pointer rounded border-none bg-transparent px-3 py-2 text-sm text-zinc-500\"\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 <eds-button variant=\"secondary\" type=\"button\">\n <eds-icon name=\"download\" [size]=\"18\" />\n Export\n </eds-button>\n </div>\n </header>\n\n @if (loading()) {\n <div class=\"flex flex-col gap-4\" aria-busy=\"true\" aria-live=\"polite\">\n <div class=\"analytics-page__skeleton analytics-page__skeleton--hero h-36 rounded-2xl border\"></div>\n <div class=\"flex gap-3\">\n <div class=\"analytics-page__skeleton analytics-page__skeleton--stat h-24 flex-1 rounded-2xl border\"></div>\n <div class=\"analytics-page__skeleton analytics-page__skeleton--stat h-24 flex-1 rounded-2xl border\"></div>\n <div class=\"analytics-page__skeleton analytics-page__skeleton--stat h-24 flex-1 rounded-2xl border\"></div>\n <div class=\"analytics-page__skeleton analytics-page__skeleton--stat h-24 flex-1 rounded-2xl border\"></div>\n </div>\n <div class=\"flex flex-wrap gap-3\">\n <div class=\"analytics-page__skeleton analytics-page__skeleton--chart h-72 flex-1 rounded-2xl border\"></div>\n <div class=\"analytics-page__skeleton analytics-page__skeleton--chart h-72 flex-1 rounded-2xl border\"></div>\n </div>\n <p class=\"eds-body-sm mt-1\">Loading analytics…</p>\n </div>\n } @else {\n @if (data(); as d) {\n <div class=\"analytics-page__body\" [class.analytics-page__body--ready]=\"chartsReady()\">\n <section\n class=\"analytics-page__hero eds-animate-fade-up mb-3 grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-3\"\n aria-label=\"Key outcomes\"\n >\n <article class=\"analytics-page__impact flex min-h-36 flex-col gap-2 rounded-2xl p-5 text-white\">\n <span class=\"text-sm font-medium leading-snug opacity-90\">\n {{ impactKpi()?.label || 'Estimated Impact This Month' }}\n </span>\n <strong class=\"text-4xl font-bold leading-tight tracking-tight\">\n {{ impactKpi()?.value || '—' }}\n </strong>\n <p class=\"m-0 max-w-sm text-sm leading-normal opacity-90\">\n {{ impactKpi()?.sub || 'of front-desk time handled by the AI' }}\n </p>\n </article>\n\n <article class=\"analytics-page__card flex flex-col justify-center gap-2 rounded-2xl border bg-white px-4 py-4\">\n <span class=\"eds-label\">{{ resolutionKpi()?.label || 'AI Autonomous Resolution' }}</span>\n <div class=\"flex items-baseline gap-3\">\n <strong class=\"text-4xl font-bold leading-none text-zinc-900\">\n {{ resolutionKpi()?.value || '—' }}\n </strong>\n @if (resolutionKpi()?.delta) {\n <span\n class=\"analytics-page__trend inline-flex items-center gap-1 text-xs font-semibold leading-none\"\n [attr.data-tone]=\"deltaTone(resolutionKpi())\"\n >\n <eds-icon\n [name]=\"deltaTone(resolutionKpi()) === 'pos' ? 'trending_up' : 'trending_down'\"\n [size]=\"16\"\n />\n {{ resolutionKpi()!.delta }}\n </span>\n }\n </div>\n <p class=\"m-0 text-xs leading-normal text-zinc-500\">\n {{ resolutionKpi()?.sub || 'without staff intervention' }}\n </p>\n </article>\n\n <article\n class=\"analytics-page__distribution analytics-page__card flex flex-col gap-3 rounded-2xl border bg-white px-4 py-4 md:col-span-2 lg:col-span-1\"\n >\n <div class=\"flex flex-wrap items-baseline justify-between gap-3\">\n <strong class=\"text-xl font-bold leading-snug text-zinc-900\">{{ totalCallsLabel() }}</strong>\n @if (directionSub(); as dir) {\n <span class=\"text-xs leading-normal text-zinc-500\">{{ dir }}</span>\n }\n </div>\n <div class=\"analytics-page__stack flex h-3.5 rounded-full\" role=\"img\" aria-label=\"Call distribution\">\n @for (seg of distribution(); track seg.label) {\n <span\n class=\"analytics-page__stack-seg relative block h-full min-w-0.5 cursor-default\"\n [style.width.%]=\"seg.width\"\n [style.background]=\"seg.color\"\n [attr.aria-label]=\"distributionTooltip(seg.label, seg.value)\"\n >\n <span class=\"analytics-page__tooltip\">{{ distributionTooltip(seg.label, seg.value) }}</span>\n </span>\n }\n </div>\n <ul class=\"m-0 flex list-none flex-wrap gap-x-4 gap-y-2 p-0 text-xs text-zinc-500\">\n @for (seg of distribution(); track seg.label) {\n <li class=\"inline-flex items-center gap-2\">\n <span class=\"h-2 w-2 shrink-0 rounded-full\" [style.background]=\"seg.color\"></span>\n <span>\n {{ seg.label }}\n <strong class=\"font-semibold text-zinc-900\">{{ estimatedCalls(seg.value).toLocaleString() }}</strong>\n calls\n </span>\n <em class=\"not-italic font-semibold\">{{ seg.value }}%</em>\n </li>\n }\n </ul>\n </article>\n </section>\n\n <section\n class=\"eds-animate-fade-up mb-4 grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-4\"\n aria-label=\"Activity KPIs\"\n >\n @for (kpi of secondaryKpis(); track kpi.label) {\n <article class=\"analytics-page__card flex flex-col gap-2 rounded-2xl border bg-white px-4 py-4\">\n <div class=\"flex items-center justify-between gap-2\">\n <span class=\"eds-label\">{{ kpi.label }}</span>\n @if (kpi.delta) {\n <span\n class=\"analytics-page__trend inline-flex items-center gap-1 text-xs font-semibold leading-none\"\n [attr.data-tone]=\"deltaTone(kpi)\"\n >\n <eds-icon\n [name]=\"deltaTone(kpi) === 'pos' ? 'trending_up' : 'trending_down'\"\n [size]=\"14\"\n />\n {{ kpi.delta }}\n </span>\n }\n </div>\n <strong class=\"text-3xl font-bold leading-tight text-zinc-900\">{{ kpi.value }}</strong>\n @if (kpi.sub) {\n <span class=\"text-xs leading-normal text-zinc-500\">{{ kpi.sub }}</span>\n }\n </article>\n }\n </section>\n\n <div class=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <article class=\"eds-card analytics-page__chart eds-animate-fade-up px-4 py-4 shadow-sm\">\n <div class=\"analytics-page__chart-head mb-3 flex flex-wrap items-baseline justify-between gap-3\">\n <div class=\"flex min-w-0 flex-wrap items-baseline gap-3\">\n <h2 class=\"eds-h2\">Call Volume</h2>\n <span\n class=\"analytics-page__chart-period inline-flex items-center rounded-full border px-3 py-1 text-xs font-medium text-zinc-500\"\n >\n {{ periodLabel() }}\n </span>\n </div>\n <ul class=\"m-0 flex list-none flex-wrap gap-x-4 gap-y-3 p-0 text-xs font-medium text-zinc-500\">\n <li>\n <span class=\"analytics-page__dot analytics-page__dot--total mr-2 inline-block h-2 w-2 rounded-full\"></span>\n Total Calls\n </li>\n <li>\n <span class=\"analytics-page__dot analytics-page__dot--in mr-2 inline-block h-2 w-2 rounded-full\"></span>\n Inbound Calls\n </li>\n <li>\n <span class=\"analytics-page__dot analytics-page__dot--out mr-2 inline-block h-2 w-2 rounded-full\"></span>\n Outbound Calls\n </li>\n </ul>\n </div>\n <div class=\"analytics-page__line-chart relative h-56\">\n <svg viewBox=\"0 0 100 100\" preserveAspectRatio=\"none\" aria-hidden=\"true\">\n <path class=\"analytics-page__area\" [attr.d]=\"volumeAreaPath()\" />\n <path class=\"analytics-page__line analytics-page__line--total\" [attr.d]=\"volumePath('total')\" fill=\"none\" />\n <path class=\"analytics-page__line analytics-page__line--in\" [attr.d]=\"volumePath('inbound')\" fill=\"none\" />\n <path class=\"analytics-page__line analytics-page__line--out\" [attr.d]=\"volumePath('outbound')\" fill=\"none\" />\n </svg>\n <div\n class=\"analytics-page__volume-hit flex\"\n role=\"list\"\n aria-label=\"Call volume by period\"\n (mouseleave)=\"onVolumeLeave()\"\n >\n @for (point of volumePoints(); track point.date || point.label; let i = $index) {\n <button\n type=\"button\"\n class=\"analytics-page__volume-col relative min-w-0 flex-1 cursor-default border-none bg-transparent p-0\"\n role=\"listitem\"\n [class.analytics-page__volume-col--active]=\"hoveredVolume() === i\"\n [attr.aria-label]=\"volumeTooltip(point)\"\n (mouseenter)=\"onVolumeEnter(i)\"\n (focus)=\"onVolumeEnter(i)\"\n (blur)=\"onVolumeLeave()\"\n >\n <span class=\"analytics-page__tooltip analytics-page__tooltip--multiline\">\n {{ volumeTooltip(point) }}\n </span>\n </button>\n }\n </div>\n <div class=\"mt-2 flex justify-between gap-1\">\n @for (label of volumeAxisLabels(); track $index) {\n <span class=\"flex-1 text-center font-mono text-xs font-medium uppercase text-zinc-500\">\n {{ label }}\n </span>\n }\n </div>\n </div>\n </article>\n\n <article class=\"eds-card analytics-page__chart eds-animate-fade-up px-4 py-4 shadow-sm\">\n <h2 class=\"eds-h2 mb-4\">Top Call Reasons</h2>\n <div class=\"flex flex-col gap-3\">\n @for (item of d.reasons; track item.label; let i = $index) {\n <div\n class=\"analytics-page__hbar-row grid items-center gap-3\"\n [attr.aria-label]=\"hbarTooltip(item.label, item.value)\"\n >\n <span class=\"text-sm font-medium text-zinc-900\">{{ item.label }}</span>\n <div class=\"analytics-page__hbar-track relative h-2.5 rounded-full\">\n <span\n class=\"analytics-page__hbar-fill block h-full min-w-1 rounded-full\"\n [style.width.%]=\"(item.value / maxValue(d.reasons)) * 100\"\n [style.background]=\"reasonColor(i)\"\n [style.animation-delay.ms]=\"i * 40\"\n ></span>\n <span class=\"analytics-page__tooltip\">{{ hbarTooltip(item.label, item.value) }}</span>\n </div>\n <span class=\"text-right font-mono text-xs font-semibold text-zinc-500\">{{ item.value }}%</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"eds-card analytics-page__chart eds-animate-fade-up px-4 py-4 shadow-sm\">\n <h2 class=\"eds-h2 mb-4\">Reasons AI got stuck</h2>\n <div class=\"flex flex-col gap-3\">\n @for (item of d.transfers; track item.label; let i = $index) {\n <div\n class=\"analytics-page__hbar-row grid items-center gap-3\"\n [attr.aria-label]=\"hbarTooltip(item.label, item.value)\"\n >\n <span class=\"text-sm font-medium text-zinc-900\">{{ item.label }}</span>\n <div class=\"analytics-page__hbar-track relative h-2.5 rounded-full\">\n <span\n class=\"analytics-page__hbar-fill block h-full min-w-1 rounded-full\"\n [style.width.%]=\"(item.value / maxValue(d.transfers)) * 100\"\n [style.background]=\"stuckColor(i)\"\n [style.animation-delay.ms]=\"i * 40\"\n ></span>\n <span class=\"analytics-page__tooltip\">{{ hbarTooltip(item.label, item.value) }}</span>\n </div>\n <span class=\"text-right font-mono text-xs font-semibold text-zinc-500\">{{ item.value }}%</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"eds-card analytics-page__chart eds-animate-fade-up px-4 py-4 shadow-sm\">\n <div class=\"analytics-page__chart-head mb-3 flex flex-wrap items-baseline justify-between gap-3\">\n <h2 class=\"eds-h2\">Peak Hours</h2>\n <span class=\"eds-label\">\n @if (d.peakHours.timezone) {\n {{ d.peakHours.timezone }}\n } @else {\n Calls per hour\n }\n </span>\n </div>\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex h-40 items-end gap-1\" role=\"list\" aria-label=\"Call volume by hour\">\n @for (point of d.peakHours.points; track point.hour; let i = $index) {\n <div\n class=\"analytics-page__peak-col flex h-full min-w-0 flex-1 flex-col\"\n role=\"listitem\"\n [attr.aria-label]=\"peakTooltip(point)\"\n >\n <div class=\"analytics-page__peak-wrap relative flex min-h-0 flex-1 items-end\">\n <div\n class=\"analytics-page__peak-bar w-full min-h-0.5 rounded-t bg-blue-300 transition-colors duration-150\"\n [style.height.%]=\"barHeightPercent(point, d.peakHours.points)\"\n [style.animation-delay.ms]=\"i * 18\"\n ></div>\n <span class=\"analytics-page__tooltip\">{{ peakTooltip(point) }}</span>\n </div>\n </div>\n }\n </div>\n <div class=\"flex gap-1\">\n @for (point of d.peakHours.points; track point.hour; let i = $index) {\n <span class=\"min-h-3.5 flex-1 text-center font-mono text-xs font-medium text-zinc-500\">\n @if (showPeakAxisLabel(i, d.peakHours.points.length)) {\n {{ point.label }}\n }\n </span>\n }\n </div>\n </div>\n </article>\n </div>\n </div>\n }\n }\n</div>\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"edvak99-voice-receptionist-widget-api-logs-page.component-jKBA1N2A.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/models/api-log.types.ts","../../../projects/voice-receptionist-widget/src/lib/pages/api-logs-page/api-logs-page.component.ts","../../../projects/voice-receptionist-widget/src/lib/pages/api-logs-page/api-logs-page.component.html"],"sourcesContent":["export type ApiProvider = 'edvak' | 'vonage' | 'openai' | 'livekit';\n\nexport interface ApiLogListItem {\n id: string;\n callId: string | null;\n /** Present when viewing cross-practice (super-admin). */\n clientPracticeId?: string | null;\n provider: ApiProvider;\n operation: string;\n method: string;\n urlPath: string;\n statusCode: number | null;\n success: boolean;\n durationMs: number;\n toolName: string | null;\n errorMessage: string | null;\n startedAt: string;\n requestPreview: string;\n responsePreview: string;\n}\n\nexport interface ApiLogDetail extends ApiLogListItem {\n traceId: string | null;\n requestBody: Record<string, unknown> | null;\n responseBody: Record<string, unknown> | null;\n metadata: Record<string, unknown> | null;\n completedAt: string | null;\n}\n\nexport interface ApiLogStats {\n last24h: { total: number; failed: number; avgDurationMs: number };\n byProvider: { provider: ApiProvider; total: number; failed: number }[];\n recentFailures: ApiLogListItem[];\n}\n\nexport interface ApiLogListResponse {\n items: ApiLogListItem[];\n total: number;\n page?: number;\n limit?: number;\n /** Filter applied; null = all practices (super-admin). */\n practiceId?: string | null;\n}\n\nexport interface ApiLogFilters {\n provider?: string;\n success?: boolean;\n statusCode?: number;\n operation?: string;\n callId?: string;\n from?: string;\n to?: string;\n search?: string;\n page?: number;\n limit?: number;\n /** Super-admin only. Omit for all practices. */\n practiceId?: string;\n}\n\n/** Roles that may pass `practiceId` / view all practices. */\nexport function isSuperAdminRole(role: string | null | undefined): boolean {\n const key = String(role ?? '')\n .trim()\n .toLowerCase()\n .replace(/[\\s-]+/g, '_');\n return key === 'super_admin' || key === 'superadmin' || key === 'platform_admin';\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n OnInit,\n signal,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { ActivatedRoute } from '@angular/router';\n\nimport { ApiLogDetailDrawerComponent } from '../../components/api-log-detail-drawer/api-log-detail-drawer.component';\nimport { ApiLogsTableComponent } from '../../components/api-logs-table/api-logs-table.component';\nimport { VOICE_RECEPTIONIST_SESSION } from '../../config';\nimport {\n ApiLogListItem,\n ApiLogStats,\n ApiProvider,\n isSuperAdminRole,\n} from '../../models/api-log.types';\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport { formatApiDuration, providerChipClass, providerLabel } from '../../utils/api-log.util';\nimport { EdsButtonComponent } from '../../ui/eds-button.component';\nimport { EdsIconComponent } from '../../ui/eds-icon.component';\n\nconst PROVIDERS: ApiProvider[] = ['edvak', 'vonage', 'openai', 'livekit'];\nconst PAGE_SIZE = 50;\n\n@Component({\n selector: 'eds-api-logs-page',\n imports: [\n FormsModule,\n ApiLogsTableComponent,\n ApiLogDetailDrawerComponent,\n EdsButtonComponent,\n EdsIconComponent,\n ],\n templateUrl: './api-logs-page.component.html',\n styleUrl: './api-logs-page.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ApiLogsPageComponent implements OnInit {\n private readonly api = inject(AiReceptionistApiService);\n private readonly route = inject(ActivatedRoute);\n private readonly session = inject(VOICE_RECEPTIONIST_SESSION);\n\n protected readonly providers = PROVIDERS;\n protected readonly pageSize = PAGE_SIZE;\n protected readonly providerLabel = providerLabel;\n protected readonly providerChipClass = providerChipClass;\n protected readonly formatDuration = formatApiDuration;\n\n protected readonly items = signal<ApiLogListItem[]>([]);\n protected readonly total = signal(0);\n protected readonly page = signal(1);\n protected readonly loading = signal(true);\n protected readonly error = signal<string | null>(null);\n protected readonly stats = signal<ApiLogStats | null>(null);\n protected readonly statsError = signal<string | null>(null);\n\n protected readonly selectedProviders = signal<Set<ApiProvider>>(new Set());\n protected readonly successFilter = signal<'all' | 'ok' | 'fail'>('all');\n protected readonly statusCode = signal('');\n protected readonly search = signal('');\n protected readonly callIdFilter = signal('');\n protected readonly fromDate = signal('');\n protected readonly toDate = signal('');\n /** Empty string = all practices (super-admin). */\n protected readonly practiceIdFilter = signal('');\n\n protected readonly selected = signal<ApiLogListItem | null>(null);\n protected readonly drawerOpen = signal(false);\n\n protected readonly isSuperAdmin = computed(() => {\n if (this.session?.isSuperAdmin?.()) return true;\n const role = this.session?.role?.() ?? null;\n return isSuperAdminRole(role);\n });\n\n protected readonly showPracticeColumn = computed(\n () => this.isSuperAdmin() && !this.practiceIdFilter().trim(),\n );\n\n ngOnInit(): void {\n const callId = this.route.snapshot.queryParamMap.get('callId');\n if (callId) this.callIdFilter.set(callId);\n const practiceId = this.route.snapshot.queryParamMap.get('practiceId');\n if (practiceId && this.isSuperAdmin()) this.practiceIdFilter.set(practiceId);\n this.loadStats();\n this.load();\n }\n\n protected toggleProvider(p: ApiProvider): void {\n this.selectedProviders.update((set) => {\n const next = new Set(set);\n if (next.has(p)) next.delete(p);\n else next.add(p);\n return next;\n });\n }\n\n protected isProviderSelected(p: ApiProvider): boolean {\n return this.selectedProviders().has(p);\n }\n\n protected applyFilters(): void {\n this.page.set(1);\n this.loadStats();\n this.load();\n }\n\n protected clearFilters(): void {\n this.selectedProviders.set(new Set());\n this.successFilter.set('all');\n this.statusCode.set('');\n this.search.set('');\n this.callIdFilter.set('');\n this.fromDate.set('');\n this.toDate.set('');\n this.practiceIdFilter.set('');\n this.page.set(1);\n this.loadStats();\n this.load();\n }\n\n protected prevPage(): void {\n if (this.page() > 1) {\n this.page.update((p) => p - 1);\n this.load();\n }\n }\n\n protected nextPage(): void {\n if (this.page() < this.totalPages()) {\n this.page.update((p) => p + 1);\n this.load();\n }\n }\n\n protected onRowSelected(item: ApiLogListItem): void {\n this.selected.set(item);\n this.drawerOpen.set(true);\n }\n\n protected closeDrawer(): void {\n this.drawerOpen.set(false);\n this.selected.set(null);\n }\n\n protected exportCsv(): void {\n const header = [\n 'time',\n 'provider',\n 'operation',\n 'method',\n 'status',\n 'duration',\n 'success',\n 'callId',\n 'practiceId',\n 'error',\n ];\n const rows = this.items().map((r) => [\n r.startedAt,\n r.provider,\n r.operation,\n r.method,\n r.statusCode != null ? String(r.statusCode) : r.success ? 'OK' : 'TIMEOUT',\n String(r.durationMs),\n r.success ? 'true' : 'false',\n r.callId ?? '',\n r.clientPracticeId ?? '',\n r.errorMessage ?? '',\n ]);\n const csv = [header, ...rows]\n .map((row) => row.map((cell) => `\"${String(cell).replace(/\"/g, '\"\"')}\"`).join(','))\n .join('\\n');\n const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `api-logs-page-${this.page()}.csv`;\n a.click();\n URL.revokeObjectURL(url);\n }\n\n protected totalPages(): number {\n return Math.max(1, Math.ceil(this.total() / PAGE_SIZE));\n }\n\n private practiceIdParam(): string | undefined {\n if (!this.isSuperAdmin()) return undefined;\n const id = this.practiceIdFilter().trim();\n return id || undefined;\n }\n\n private loadStats(): void {\n this.statsError.set(null);\n this.api.getApiLogStats(this.practiceIdParam()).subscribe({\n next: ({ data }) => this.stats.set(data),\n error: () => {\n this.stats.set(null);\n this.statsError.set('Stats unavailable');\n },\n });\n }\n\n private load(): void {\n this.loading.set(true);\n this.error.set(null);\n const providers = [...this.selectedProviders()];\n this.api\n .getApiLogs({\n provider: providers.length ? providers.join(',') : undefined,\n success:\n this.successFilter() === 'all'\n ? undefined\n : this.successFilter() === 'ok',\n statusCode: this.statusCode() ? Number(this.statusCode()) : undefined,\n callId: this.callIdFilter() || undefined,\n from: this.fromDate() ? new Date(this.fromDate()).toISOString() : undefined,\n to: this.toDate() ? new Date(this.toDate()).toISOString() : undefined,\n search: this.search() || undefined,\n page: this.page(),\n limit: PAGE_SIZE,\n practiceId: this.practiceIdParam(),\n })\n .subscribe({\n next: ({ items, total }) => {\n this.items.set(items);\n this.total.set(total);\n this.loading.set(false);\n },\n error: (err: { status?: number; message?: string }) => {\n this.loading.set(false);\n if (err?.status === 403) {\n this.error.set('You do not have access to that practice’s API logs.');\n } else {\n this.error.set(err?.message ?? 'Failed to load API logs.');\n }\n },\n });\n }\n}\n","<div class=\"eds-page api-logs-page\">\n <header class=\"api-logs-page__header mb-5 flex items-start justify-between gap-4\">\n <div>\n <h1 class=\"eds-h1\">API Logs</h1>\n <p class=\"eds-body-sm\">Edvak EHR, Vonage, OpenAI, and LiveKit request history</p>\n </div>\n <eds-button variant=\"secondary\" (click)=\"exportCsv()\">\n <eds-icon name=\"download\" [size]=\"18\" /> Export CSV\n </eds-button>\n </header>\n\n <section class=\"mb-4 grid grid-cols-2 gap-3 md:grid-cols-4\" aria-label=\"API health last 24 hours\">\n @if (stats(); as s) {\n <article class=\"flex flex-col gap-1.5 rounded-2xl border bg-white px-4 py-4\">\n <span class=\"eds-label\">Total (24h)</span>\n <strong class=\"text-3xl font-bold leading-tight text-zinc-900\">{{ s.last24h.total }}</strong>\n </article>\n <article class=\"flex flex-col gap-1.5 rounded-2xl border bg-white px-4 py-4\">\n <span class=\"eds-label\">Failed (24h)</span>\n <strong class=\"text-3xl font-bold leading-tight text-red-600\">{{ s.last24h.failed }}</strong>\n </article>\n <article class=\"flex flex-col gap-1.5 rounded-2xl border bg-white px-4 py-4\">\n <span class=\"eds-label\">Avg latency</span>\n <strong class=\"text-3xl font-bold leading-tight text-zinc-900\">{{ formatDuration(s.last24h.avgDurationMs) }}</strong>\n </article>\n <article class=\"flex flex-col gap-1.5 rounded-2xl border bg-white px-4 py-4\">\n <span class=\"eds-label\">By provider</span>\n <ul class=\"m-0 mt-1 flex list-none flex-wrap gap-1.5 p-0\">\n @for (row of s.byProvider; track row.provider) {\n <li class=\"inline-flex items-center gap-1 text-xs text-zinc-600\">\n <span class=\"api-log-chip\" [class]=\"providerChipClass(row.provider)\">{{ providerLabel(row.provider) }}</span>\n {{ row.total }}\n </li>\n } @empty {\n <li class=\"text-sm text-zinc-500\">—</li>\n }\n </ul>\n </article>\n } @else if (statsError()) {\n <p class=\"col-span-full eds-body-sm text-zinc-500\">{{ statsError() }}</p>\n } @else {\n <p class=\"col-span-full eds-body-sm\">Loading stats…</p>\n }\n </section>\n\n <section class=\"eds-card api-logs-page__filters mb-4 p-4 shadow-sm\">\n <div class=\"api-logs-page__filter-row mb-3\">\n <span class=\"eds-label\">Provider</span>\n <div class=\"api-logs-page__chips mt-2 flex flex-wrap gap-2\">\n @for (p of providers; track p) {\n <button\n type=\"button\"\n class=\"api-log-chip api-logs-page__chip\"\n [class]=\"'api-log-chip--' + p\"\n [class.api-logs-page__chip--on]=\"isProviderSelected(p)\"\n (click)=\"toggleProvider(p)\"\n >\n {{ p }}\n </button>\n }\n </div>\n </div>\n\n <div class=\"api-logs-page__filter-grid mb-3 grid grid-cols-2 gap-3 md:grid-cols-3\">\n <label class=\"api-logs-page__field flex flex-col gap-1\">\n <span class=\"eds-label\">Result</span>\n <select [ngModel]=\"successFilter()\" (ngModelChange)=\"successFilter.set($event)\" autocomplete=\"off\">\n <option value=\"all\">All</option>\n <option value=\"ok\">Success</option>\n <option value=\"fail\">Failed</option>\n </select>\n </label>\n\n <label class=\"api-logs-page__field flex flex-col gap-1\">\n <span class=\"eds-label\">Status code</span>\n <input type=\"number\" placeholder=\"e.g. 500\" [ngModel]=\"statusCode()\" (ngModelChange)=\"statusCode.set($event)\" autocomplete=\"off\" />\n </label>\n\n <label class=\"api-logs-page__field flex flex-col gap-1\">\n <span class=\"eds-label\">Call ID</span>\n <input type=\"text\" placeholder=\"LC-4821\" [ngModel]=\"callIdFilter()\" (ngModelChange)=\"callIdFilter.set($event)\" autocomplete=\"off\" />\n </label>\n\n <label class=\"api-logs-page__field flex flex-col gap-1\">\n <span class=\"eds-label\">Search</span>\n <input type=\"search\" placeholder=\"operation, path, error…\" [ngModel]=\"search()\" (ngModelChange)=\"search.set($event)\" autocomplete=\"off\" />\n </label>\n\n <label class=\"api-logs-page__field flex flex-col gap-1\">\n <span class=\"eds-label\">From</span>\n <input type=\"datetime-local\" [ngModel]=\"fromDate()\" (ngModelChange)=\"fromDate.set($event)\" autocomplete=\"off\" />\n </label>\n\n <label class=\"api-logs-page__field flex flex-col gap-1\">\n <span class=\"eds-label\">To</span>\n <input type=\"datetime-local\" [ngModel]=\"toDate()\" (ngModelChange)=\"toDate.set($event)\" autocomplete=\"off\" />\n </label>\n\n @if (isSuperAdmin()) {\n <label class=\"api-logs-page__field flex flex-col gap-1 md:col-span-2\">\n <span class=\"eds-label\">Practice ID (super-admin)</span>\n <input\n type=\"text\"\n placeholder=\"Leave empty for all practices\"\n [ngModel]=\"practiceIdFilter()\"\n (ngModelChange)=\"practiceIdFilter.set($event)\"\n autocomplete=\"off\"\n />\n </label>\n }\n </div>\n\n <div class=\"api-logs-page__filter-actions flex gap-2\">\n <eds-button variant=\"primary\" (click)=\"applyFilters()\">Apply filters</eds-button>\n <eds-button variant=\"secondary\" (click)=\"clearFilters()\">Clear</eds-button>\n </div>\n </section>\n\n <article class=\"eds-card api-logs-page__table-card p-4 shadow-sm\">\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading…</p>\n } @else if (error()) {\n <p class=\"api-logs-page__error eds-body-sm text-red-700\" role=\"alert\">{{ error() }}</p>\n } @else if (items().length === 0) {\n <p class=\"eds-body-sm\">No API logs match your filters.</p>\n } @else {\n <eds-api-logs-table\n [items]=\"items()\"\n [showCallId]=\"true\"\n [showPracticeId]=\"showPracticeColumn()\"\n (rowSelected)=\"onRowSelected($event)\"\n />\n }\n\n <footer class=\"api-logs-page__pager mt-4 flex items-center justify-between pt-3\">\n <span class=\"eds-body-sm\">\n Page {{ page() }} of {{ totalPages() }} · {{ total() }} total\n </span>\n <div class=\"api-logs-page__pager-actions flex gap-2\">\n <eds-button variant=\"secondary\" [disabled]=\"page() <= 1\" (click)=\"prevPage()\">Previous</eds-button>\n <eds-button variant=\"secondary\" [disabled]=\"page() >= totalPages()\" (click)=\"nextPage()\">Next</eds-button>\n </div>\n </footer>\n </article>\n\n <eds-api-log-detail-drawer\n [open]=\"drawerOpen()\"\n [listItem]=\"selected()\"\n (closed)=\"closeDrawer()\"\n />\n</div>\n"],"names":[],"mappings":"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"}
|
package/fesm2022/edvak99-voice-receptionist-widget-api-logs-table.component-CEf4NPe5.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"edvak99-voice-receptionist-widget-api-logs-table.component-CEf4NPe5.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/components/api-log-detail-drawer/api-log-detail-drawer.component.ts","../../../projects/voice-receptionist-widget/src/lib/components/api-log-detail-drawer/api-log-detail-drawer.component.html","../../../projects/voice-receptionist-widget/src/lib/components/api-logs-table/api-logs-table.component.ts","../../../projects/voice-receptionist-widget/src/lib/components/api-logs-table/api-logs-table.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';\nimport { DatePipe } from '@angular/common';\nimport { RouterLink } from '@angular/router';\nimport { catchError, map, of, switchMap, tap } from 'rxjs';\n\nimport { ApiLogDetail, ApiLogListItem } from '../../models/api-log.types';\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport {\n formatApiDuration,\n formatPreview,\n prettyJson,\n providerChipClass,\n providerLabel,\n statusBadgeTone,\n statusLabel,\n} from '../../utils/api-log.util';\nimport { EdsBadgeComponent } from '../../ui/eds-badge.component';\nimport { EdsButtonComponent } from '../../ui/eds-button.component';\nimport { EdsIconComponent } from '../../ui/eds-icon.component';\n\n@Component({\n selector: 'eds-api-log-detail-drawer',\n imports: [DatePipe, RouterLink, EdsBadgeComponent, EdsButtonComponent, EdsIconComponent],\n templateUrl: './api-log-detail-drawer.component.html',\n styleUrl: './api-log-detail-drawer.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class ApiLogDetailDrawerComponent {\n private readonly api = inject(AiReceptionistApiService);\n private readonly destroyRef = inject(DestroyRef);\n\n readonly open = input(false);\n readonly listItem = input<ApiLogListItem | null>(null);\n /** When true, show list-item previews only (skip GET /api-logs/:id). */\n readonly listOnly = input(false);\n readonly closed = output<void>();\n\n protected readonly detail = signal<ApiLogDetail | null>(null);\n protected readonly loading = signal(false);\n protected readonly error = signal<string | null>(null);\n\n protected providerLabel = providerLabel;\n protected providerChipClass = providerChipClass;\n protected formatDuration = formatApiDuration;\n protected statusTone = statusBadgeTone;\n protected statusLabel = statusLabel;\n protected prettyJson = prettyJson;\n protected formatPreview = formatPreview;\n\n /** Fetch key changes when drawer opens with a new log (non-listOnly). */\n private readonly fetchKey = computed(() => {\n if (!this.open()) return null;\n const item = this.listItem();\n if (!item || this.listOnly()) return null;\n return item.id;\n });\n\n constructor() {\n toObservable(this.fetchKey)\n .pipe(\n switchMap((logId) => {\n if (!logId) {\n this.detail.set(null);\n this.error.set(null);\n this.loading.set(false);\n return of(null);\n }\n this.loading.set(true);\n this.error.set(null);\n return this.api.getApiLogDetail(logId).pipe(\n map(({ data }) => data),\n tap((data) => {\n this.detail.set(data);\n this.loading.set(false);\n if (!data) this.error.set('Log entry not found.');\n }),\n catchError(() => {\n this.loading.set(false);\n this.error.set('Failed to load log detail.');\n return of(null);\n }),\n );\n }),\n takeUntilDestroyed(this.destroyRef),\n )\n .subscribe();\n }\n\n protected close(): void {\n this.closed.emit();\n }\n\n protected onBackdropClick(event: MouseEvent): void {\n if (event.target === event.currentTarget) {\n this.close();\n }\n }\n}\n","@if (open()) {\n <div class=\"api-log-drawer__backdrop fixed inset-0 z-[1200] flex justify-end bg-black/45\" (click)=\"onBackdropClick($event)\" role=\"presentation\">\n <aside class=\"api-log-drawer flex h-full w-[min(520px,100vw)] flex-col border-l bg-[var(--eds-surface-card,#fff)] shadow-[-8px_0_32px_rgba(0,0,0,0.12)]\" role=\"dialog\" aria-modal=\"true\" aria-label=\"API log detail\">\n <header class=\"flex items-start justify-between gap-3 border-b px-5 pb-3 pt-5\">\n <div>\n @if (listItem(); as item) {\n <span class=\"api-log-chip\" [class]=\"providerChipClass(item.provider)\">\n {{ providerLabel(item.provider) }}\n </span>\n <h2 class=\"eds-h2 api-log-drawer__title my-2 mb-1\">{{ item.operation }}</h2>\n <p class=\"eds-body-sm api-log-drawer__meta m-0\">\n {{ item.method }} {{ item.urlPath }} · {{ item.startedAt | date: 'medium' }}\n </p>\n }\n </div>\n <button type=\"button\" class=\"api-log-drawer__close cursor-pointer border-none bg-transparent p-1\" aria-label=\"Close\" (click)=\"close()\">\n <eds-icon name=\"close\" [size]=\"20\" />\n </button>\n </header>\n\n @if (listItem(); as item) {\n @if (!listOnly() && loading()) {\n <p class=\"eds-body-sm flex-1 overflow-y-auto px-5 pb-6 pt-4\">Loading detail…</p>\n } @else if (!listOnly() && error()) {\n <p class=\"eds-body-sm px-5 py-4 text-[#b91c1c]\" role=\"alert\">{{ error() }}</p>\n } @else if (listOnly() || detail()) {\n <div class=\"flex-1 overflow-y-auto px-5 pb-6 pt-4\">\n <div class=\"mb-4 flex flex-wrap items-center gap-3\">\n <eds-badge [tone]=\"statusTone(listOnly() ? item.success : detail()!.success)\">\n {{ statusLabel(listOnly() ? item.success : detail()!.success, listOnly() ? item.statusCode : detail()!.statusCode) }}\n </eds-badge>\n <span class=\"eds-mono-sm\">{{ formatDuration(listOnly() ? item.durationMs : detail()!.durationMs) }}</span>\n @if (!listOnly() && detail()!.traceId) {\n <span class=\"eds-mono-sm\">trace: {{ detail()!.traceId }}</span>\n }\n @if (item.toolName) {\n <span class=\"eds-body-sm\">tool: {{ item.toolName }}</span>\n }\n </div>\n\n @if ((listOnly() ? item.errorMessage : detail()!.errorMessage)) {\n <section class=\"api-log-drawer__section api-log-drawer__section--error mb-4 rounded-lg p-3\">\n <h3 class=\"eds-label\">Error</h3>\n <p>{{ listOnly() ? item.errorMessage : detail()!.errorMessage }}</p>\n </section>\n }\n\n <section class=\"mb-4\">\n <h3 class=\"eds-label\">Request</h3>\n <pre class=\"api-log-drawer__json mt-2 overflow-x-auto whitespace-pre-wrap break-words rounded-lg p-3 font-[family-name:var(--eds-font-mono,ui-monospace,monospace)] text-xs leading-normal\">{{\n listOnly() ? formatPreview(item.requestPreview) : prettyJson(detail()!.requestBody)\n }}</pre>\n </section>\n\n <section class=\"mb-4\">\n <h3 class=\"eds-label\">Response</h3>\n <pre class=\"api-log-drawer__json mt-2 overflow-x-auto whitespace-pre-wrap break-words rounded-lg p-3 font-[family-name:var(--eds-font-mono,ui-monospace,monospace)] text-xs leading-normal\">{{\n listOnly() ? formatPreview(item.responsePreview) : prettyJson(detail()!.responseBody)\n }}</pre>\n </section>\n\n @if (!listOnly() && detail()!.metadata) {\n <section class=\"mb-4\">\n <h3 class=\"eds-label\">Metadata</h3>\n <pre class=\"api-log-drawer__json mt-2 overflow-x-auto whitespace-pre-wrap break-words rounded-lg p-3 font-[family-name:var(--eds-font-mono,ui-monospace,monospace)] text-xs leading-normal\">{{ prettyJson(detail()!.metadata) }}</pre>\n </section>\n }\n\n @if (!listOnly() && detail()!.callId) {\n <div class=\"mt-2\">\n <eds-button variant=\"secondary\" [routerLink]=\"['../history', detail()!.callId]\">\n View call\n </eds-button>\n </div>\n }\n </div>\n }\n }\n </aside>\n </div>\n}\n","import { ChangeDetectionStrategy, Component, input, output } from '@angular/core';\nimport { DatePipe } from '@angular/common';\nimport { RouterLink } from '@angular/router';\n\nimport { ApiLogListItem } from '../../models/api-log.types';\nimport {\n formatApiDuration,\n providerChipClass,\n providerLabel,\n statusBadgeTone,\n statusLabel,\n truncatePreview,\n} from '../../utils/api-log.util';\nimport { EdsBadgeComponent } from '../../ui/eds-badge.component';\n\n@Component({\n selector: 'eds-api-logs-table',\n imports: [DatePipe, RouterLink, EdsBadgeComponent],\n templateUrl: './api-logs-table.component.html',\n styleUrl: './api-logs-table.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ApiLogsTableComponent {\n readonly items = input.required<ApiLogListItem[]>();\n readonly showCallId = input(false);\n readonly showPracticeId = input(false);\n readonly callLinkBase = input<string[]>(['../history']);\n readonly rowSelected = output<ApiLogListItem>();\n\n protected providerLabel = providerLabel;\n protected providerChipClass = providerChipClass;\n protected formatDuration = formatApiDuration;\n protected statusTone = statusBadgeTone;\n protected statusLabel = statusLabel;\n protected truncate = truncatePreview;\n\n protected onRowClick(item: ApiLogListItem): void {\n this.rowSelected.emit(item);\n }\n\n protected onCallClick(event: Event): void {\n event.stopPropagation();\n }\n\n protected callLink(callId: string): string[] {\n return [...this.callLinkBase(), callId];\n }\n}\n","<div class=\"api-logs-table__wrap overflow-x-auto\">\n <table class=\"eds-table api-logs-table\">\n <thead>\n <tr>\n <th>Time</th>\n <th>Provider</th>\n <th>Operation</th>\n <th>Method</th>\n <th>Status</th>\n <th>Duration</th>\n <th>Success</th>\n <th>Tool</th>\n @if (showCallId()) {\n <th>Call</th>\n }\n @if (showPracticeId()) {\n <th>Practice</th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of items(); track row.id) {\n <tr class=\"api-logs-table__row\" tabindex=\"0\" (click)=\"onRowClick(row)\" (keyup.enter)=\"onRowClick(row)\">\n <td class=\"eds-mono-sm whitespace-nowrap\">{{ row.startedAt | date: 'MMM d, HH:mm:ss' }}</td>\n <td>\n <span class=\"api-log-chip\" [class]=\"providerChipClass(row.provider)\">\n {{ providerLabel(row.provider) }}\n </span>\n </td>\n <td>\n <span class=\"api-logs-table__op block\">{{ row.operation }}</span>\n <span class=\"api-logs-table__preview eds-body-sm block mt-1\">{{ truncate(row.requestPreview) }}</span>\n </td>\n <td class=\"eds-mono-sm\">{{ row.method }}</td>\n <td>\n <eds-badge [tone]=\"statusTone(row.success)\">\n {{ statusLabel(row.success, row.statusCode) }}\n </eds-badge>\n </td>\n <td class=\"eds-mono-sm\">{{ formatDuration(row.durationMs) }}</td>\n <td class=\"eds-body-sm\">{{ row.success ? 'OK' : 'Fail' }}</td>\n <td class=\"eds-body-sm\">{{ row.toolName ?? '—' }}</td>\n @if (showCallId()) {\n <td class=\"eds-mono-sm\" (click)=\"onCallClick($event)\">\n @if (row.callId) {\n <a\n [routerLink]=\"callLink(row.callId)\"\n class=\"font-medium text-blue-600 no-underline hover:underline\"\n >{{ row.callId }}</a>\n } @else {\n —\n }\n </td>\n }\n @if (showPracticeId()) {\n <td class=\"eds-mono-sm\">{{ row.clientPracticeId || '—' }}</td>\n }\n </tr>\n }\n </tbody>\n </table>\n</div>\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"edvak99-voice-receptionist-widget-call-history-detail-page.component-DEplWBbz.mjs","sources":["../../../projects/voice-receptionist-widget/src/lib/components/api-logs-call-panel/api-logs-call-panel.component.ts","../../../projects/voice-receptionist-widget/src/lib/components/api-logs-call-panel/api-logs-call-panel.component.html","../../../projects/voice-receptionist-widget/src/lib/utils/transcript-timing.util.ts","../../../projects/voice-receptionist-widget/src/lib/components/call-conversation-panel/call-conversation-panel.component.ts","../../../projects/voice-receptionist-widget/src/lib/components/call-conversation-panel/call-conversation-panel.component.html","../../../projects/voice-receptionist-widget/src/lib/components/call-tasks-panel/call-tasks-panel.component.ts","../../../projects/voice-receptionist-widget/src/lib/components/call-tasks-panel/call-tasks-panel.component.html","../../../projects/voice-receptionist-widget/src/lib/utils/call-outcome.util.ts","../../../projects/voice-receptionist-widget/src/lib/pages/call-history-detail-page/call-history-detail-page.component.ts","../../../projects/voice-receptionist-widget/src/lib/pages/call-history-detail-page/call-history-detail-page.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n inject,\n input,\n signal,\n} from '@angular/core';\nimport { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';\nimport {\n catchError,\n EMPTY,\n finalize,\n interval,\n startWith,\n switchMap,\n tap,\n} from 'rxjs';\n\nimport { ApiLogListItem } from '../../models/api-log.types';\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport { DashboardSocketService } from '../../services/dashboard-socket.service';\nimport { ApiLogDetailDrawerComponent } from '../api-log-detail-drawer/api-log-detail-drawer.component';\nimport { ApiLogsTableComponent } from '../api-logs-table/api-logs-table.component';\n\nconst POLL_MS = 5000;\n\n@Component({\n selector: 'eds-api-logs-call-panel',\n imports: [ApiLogsTableComponent, ApiLogDetailDrawerComponent],\n templateUrl: './api-logs-call-panel.component.html',\n styleUrl: './api-logs-call-panel.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class ApiLogsCallPanelComponent {\n private readonly api = inject(AiReceptionistApiService);\n private readonly socket = inject(DashboardSocketService);\n private readonly destroyRef = inject(DestroyRef);\n\n readonly callId = input.required<string>();\n readonly live = input(false);\n readonly dark = input(false);\n\n protected readonly items = signal<ApiLogListItem[]>([]);\n protected readonly loading = signal(true);\n protected readonly error = signal<string | null>(null);\n protected readonly selected = signal<ApiLogListItem | null>(null);\n protected readonly drawerOpen = signal(false);\n\n private readonly panelState = computed(() => ({\n callId: this.callId(),\n live: this.live(),\n }));\n\n private readonly onApiLogUpdated = (payload: { callId?: string }) => {\n if (payload.callId === this.callId()) {\n this.refetchQuietly();\n }\n };\n\n constructor() {\n toObservable(this.panelState)\n .pipe(\n switchMap(({ callId, live }) => {\n if (!callId) {\n this.items.set([]);\n this.loading.set(false);\n this.error.set(null);\n return EMPTY;\n }\n\n if (live) {\n this.socket.acquire();\n this.socket.on('api_log_updated', this.onApiLogUpdated);\n }\n\n const logs$ = live\n ? interval(POLL_MS).pipe(\n startWith(0),\n switchMap(() => this.api.getCallApiLogs(callId)),\n )\n : this.api.getCallApiLogs(callId);\n\n return logs$.pipe(\n tap(({ items }) => {\n this.items.set(items);\n this.loading.set(false);\n this.error.set(null);\n }),\n catchError(() => {\n this.loading.set(false);\n this.error.set('Failed to load API logs.');\n return EMPTY;\n }),\n finalize(() => {\n if (live) {\n this.socket.off('api_log_updated', this.onApiLogUpdated);\n this.socket.release();\n }\n }),\n );\n }),\n takeUntilDestroyed(this.destroyRef),\n )\n .subscribe();\n\n // Show loading when callId changes (before first emission).\n toObservable(this.callId)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe((id) => {\n if (id) {\n this.loading.set(true);\n this.error.set(null);\n }\n });\n }\n\n protected onRowSelected(item: ApiLogListItem): void {\n this.selected.set(item);\n this.drawerOpen.set(true);\n }\n\n protected closeDrawer(): void {\n this.drawerOpen.set(false);\n this.selected.set(null);\n }\n\n private refetchQuietly(): void {\n const id = this.callId();\n if (!id) return;\n this.api.getCallApiLogs(id).subscribe({\n next: ({ items }) => this.items.set(items),\n });\n }\n}\n","<div class=\"api-logs-call-panel min-h-[120px]\" [class.api-logs-call-panel--dark]=\"dark()\">\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading API logs…</p>\n } @else if (error()) {\n <p class=\"api-logs-call-panel__error eds-body-sm\" role=\"alert\">{{ error() }}</p>\n } @else if (items().length === 0) {\n <p class=\"eds-body-sm api-logs-call-panel__empty py-6 text-center\">No external API calls recorded for this call.</p>\n } @else {\n <eds-api-logs-table [items]=\"items()\" (rowSelected)=\"onRowSelected($event)\" />\n }\n\n <eds-api-log-detail-drawer\n [open]=\"drawerOpen()\"\n [listItem]=\"selected()\"\n (closed)=\"closeDrawer()\"\n />\n</div>\n","import { ToolEvent, TranscriptLine } from '../models/ai-receptionist.types';\n\nexport interface TimedTranscriptLine extends TranscriptLine {\n startSec: number;\n endSec: number;\n index: number;\n}\n\n/** Parse tool timestamp strings like \"1:02\" or \"0:42\" into seconds. */\nexport function parseClockTime(value: string): number | null {\n const trimmed = value?.trim();\n if (!trimmed) return null;\n const parts = trimmed.split(':').map((p) => Number(p));\n if (parts.some((n) => Number.isNaN(n))) return null;\n if (parts.length === 2) return parts[0] * 60 + parts[1];\n if (parts.length === 3) return parts[0] * 3600 + parts[1] * 60 + parts[2];\n return null;\n}\n\nfunction resolveLineStart(line: TranscriptLine): number | null {\n if (line.at != null && line.at >= 0) return line.at;\n const raw = line as TranscriptLine & { offsetSeconds?: number; startTime?: number };\n if (raw.offsetSeconds != null && raw.offsetSeconds >= 0) return raw.offsetSeconds;\n if (raw.startTime != null && raw.startTime >= 0) return raw.startTime;\n return null;\n}\n\n/**\n * Assign start/end seconds to transcript lines for audio sync.\n * Uses explicit `at` when present; otherwise estimates from call duration.\n */\nexport function buildTimedTranscript(\n lines: TranscriptLine[],\n durationSeconds: number,\n): TimedTranscriptLine[] {\n if (!lines.length) return [];\n\n const total = Math.max(durationSeconds, 1);\n const explicit = lines.map((line) => resolveLineStart(line));\n const allExplicit = explicit.every((t) => t != null);\n\n const starts: number[] = new Array(lines.length).fill(0);\n\n if (allExplicit) {\n for (let i = 0; i < lines.length; i += 1) {\n starts[i] = explicit[i]!;\n }\n } else {\n const speakableCount = lines.filter((l) => l.who !== 'system').length;\n const step = total / Math.max(speakableCount, 1);\n let speakableIdx = 0;\n\n for (let i = 0; i < lines.length; i += 1) {\n if (explicit[i] != null) {\n starts[i] = explicit[i]!;\n continue;\n }\n const line = lines[i];\n if (line.who === 'system') {\n starts[i] = i > 0 ? starts[i - 1] : 0;\n continue;\n }\n starts[i] = speakableIdx * step;\n speakableIdx += 1;\n }\n }\n\n return lines.map((line, index) => {\n const startSec = starts[index];\n const nextStart = starts.slice(index + 1).find((s) => s > startSec);\n const endSec = nextStart ?? total;\n return { ...line, startSec, endSec: Math.max(endSec, startSec + 0.5), index };\n });\n}\n\nexport function activeTranscriptIndex(lines: TimedTranscriptLine[], currentSec: number): number {\n if (!lines.length || currentSec < 0) return -1;\n for (let i = lines.length - 1; i >= 0; i -= 1) {\n if (currentSec >= lines[i].startSec) return i;\n }\n return 0;\n}\n\nexport function formatPlaybackTime(seconds: number): string {\n const s = Math.max(0, Math.floor(seconds));\n const m = Math.floor(s / 60);\n const r = s % 60;\n return `${m}:${r.toString().padStart(2, '0')}`;\n}\n\nexport function toolStateClass(state: ToolEvent['state']): string {\n const map: Record<ToolEvent['state'], string> = {\n ok: 'call-conversation__tool--ok',\n warn: 'call-conversation__tool--warn',\n error: 'call-conversation__tool--error',\n active: 'call-conversation__tool--active',\n };\n return map[state] ?? '';\n}\n\nexport function transcriptRoleLabel(\n who: TranscriptLine['who'],\n callerName?: string | null,\n callerMatched?: boolean,\n): string {\n if (who === 'patient') {\n const name = String(callerName ?? '').trim();\n if (!name || callerMatched === false || /^unknown/i.test(name) || name === '?') {\n return 'Unknown caller';\n }\n return name;\n }\n const labels: Record<Exclude<TranscriptLine['who'], 'patient'>, string> = {\n ai: 'AI receptionist',\n staff: 'Staff',\n system: 'System',\n };\n return labels[who] ?? who;\n}\n","import { DatePipe } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n ElementRef,\n inject,\n input,\n output,\n signal,\n viewChild,\n} from '@angular/core';\n\nimport { ToolEvent, TranscriptLine, TranscriptWho } from '../../models/ai-receptionist.types';\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport { ToastService } from '../../shared/toast.service';\nimport {\n activeTranscriptIndex,\n buildTimedTranscript,\n formatPlaybackTime,\n TimedTranscriptLine,\n transcriptRoleLabel,\n} from '../../utils/transcript-timing.util';\nimport { toolEventTrackKey } from '../../utils/tool-labels.util';\nimport { CallRecordingPlayerComponent } from '../call-recording-player/call-recording-player.component';\nimport { EdsBadgeComponent, type BadgeTone } from '../../ui/eds-badge.component';\nimport { EdsButtonComponent } from '../../ui/eds-button.component';\nimport { EdsIconComponent } from '../../ui/eds-icon.component';\n\n@Component({\n selector: 'eds-call-conversation-panel',\n imports: [\n DatePipe,\n CallRecordingPlayerComponent,\n EdsBadgeComponent,\n EdsButtonComponent,\n EdsIconComponent,\n ],\n templateUrl: './call-conversation-panel.component.html',\n styleUrl: './call-conversation-panel.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class CallConversationPanelComponent {\n private readonly api = inject(AiReceptionistApiService);\n private readonly toast = inject(ToastService);\n\n readonly callId = input.required<string>();\n readonly summary = input.required<string>();\n readonly transcript = input.required<TranscriptLine[]>();\n readonly tools = input.required<ToolEvent[]>();\n readonly callDurationSec = input(0);\n readonly allowRegenerate = input(true);\n readonly callerName = input<string | null>(null);\n readonly callerMatched = input<boolean | null>(null);\n\n readonly summaryRegenerated = output<void>();\n\n private readonly player = viewChild(CallRecordingPlayerComponent);\n private readonly scrollHost = viewChild<ElementRef<HTMLElement>>('scrollHost');\n\n protected readonly currentTime = signal(0);\n protected readonly playbackDuration = signal(0);\n protected readonly summaryExpanded = signal(false);\n private readonly regeneratedSummary = signal<string | null>(null);\n protected readonly displaySummary = computed(\n () => this.regeneratedSummary() ?? this.summary(),\n );\n protected readonly regenerating = signal(false);\n protected readonly summaryMeta = signal<{\n model?: string;\n generatedAt?: string;\n turnCount?: number;\n } | null>(null);\n\n protected readonly timedLines = computed(() =>\n buildTimedTranscript(\n this.transcript(),\n this.playbackDuration() || this.callDurationSec() || 120,\n ),\n );\n\n protected readonly activeIndex = computed(() =>\n activeTranscriptIndex(this.timedLines(), this.currentTime()),\n );\n\n protected readonly hasExplicitTiming = computed(() =>\n this.transcript().some((line) => line.at != null),\n );\n\n protected readonly formatTime = formatPlaybackTime;\n protected readonly toolTrackKey = toolEventTrackKey;\n\n protected roleLabel(who: TranscriptLine['who']): string {\n return transcriptRoleLabel(who, this.callerName(), this.callerMatched() ?? undefined);\n }\n\n protected speakerIcon(who: TranscriptWho | string): string {\n switch (who) {\n case 'ai':\n return 'graphic_eq';\n case 'staff':\n return 'support_agent';\n case 'patient':\n return 'person';\n default:\n return 'info';\n }\n }\n\n protected toolTone(state: string): BadgeTone {\n switch (state) {\n case 'ok':\n return 'pos';\n case 'warn':\n return 'notice';\n case 'error':\n return 'neg';\n case 'active':\n return 'brand';\n default:\n return 'gray';\n }\n }\n\n constructor() {\n effect(\n () => {\n this.callId();\n this.regeneratedSummary.set(null);\n },\n { allowSignalWrites: true },\n );\n\n effect(() => {\n const idx = this.activeIndex();\n const host = this.scrollHost()?.nativeElement;\n if (idx < 0 || !host) return;\n const row = host.querySelector(`[data-line-index=\"${idx}\"]`);\n if (row instanceof HTMLElement) {\n row.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\n }\n });\n }\n\n protected onPlaybackTime(sec: number): void {\n this.currentTime.set(sec);\n }\n\n protected onPlaybackDuration(sec: number): void {\n this.playbackDuration.set(sec);\n }\n\n protected seekLine(line: TimedTranscriptLine): void {\n if (line.who === 'system') return;\n this.player()?.seekTo(line.startSec);\n }\n\n protected toggleSummary(): void {\n this.summaryExpanded.update((v) => !v);\n }\n\n protected regenerateSummary(): void {\n if (this.regenerating()) return;\n\n this.regenerating.set(true);\n this.api.regenerateCallSummary(this.callId()).subscribe({\n next: ({ data }) => {\n this.regenerating.set(false);\n this.regeneratedSummary.set(data.summary);\n this.summaryExpanded.set(true);\n this.summaryMeta.set({\n model: data.summaryModel,\n generatedAt: data.summaryGeneratedAt,\n turnCount: data.transcriptTurnCount,\n });\n this.summaryRegenerated.emit();\n this.toast.show('Call summary regenerated', 'success');\n },\n error: (err: { message?: string }) => {\n this.regenerating.set(false);\n this.toast.show(err.message ?? 'Failed to regenerate summary', 'error');\n },\n });\n }\n}\n","<div class=\"flex flex-col gap-4\">\n <div class=\"grid grid-cols-1 items-stretch gap-4 lg:grid-cols-2\">\n <section class=\"eds-card p-4 shadow-sm\">\n <div class=\"flex items-center justify-between gap-3\">\n <button type=\"button\" class=\"flex min-w-0 flex-1 cursor-pointer items-center gap-1 border-none bg-transparent p-0 text-left\" (click)=\"toggleSummary()\">\n <eds-icon [name]=\"summaryExpanded() ? 'expand_less' : 'expand_more'\" [size]=\"20\" />\n <h2 class=\"eds-h2 m-0\">Call summary</h2>\n </button>\n @if (allowRegenerate()) {\n <eds-button\n variant=\"secondary\"\n type=\"button\"\n [disabled]=\"regenerating()\"\n (click)=\"regenerateSummary()\"\n >\n <eds-icon name=\"autorenew\" [size]=\"18\" />\n {{ regenerating() ? 'Regenerating…' : 'Regenerate' }}\n </eds-button>\n }\n </div>\n @if (summaryExpanded() || displaySummary().length < 180) {\n <p class=\"eds-body mt-3 leading-relaxed\">{{ displaySummary() }}</p>\n } @else {\n <p class=\"eds-body mt-3 line-clamp-2 leading-relaxed\">\n {{ displaySummary() }}\n </p>\n <button\n type=\"button\"\n class=\"cursor-pointer border-none bg-transparent px-0 pt-1 text-sm text-blue-600\"\n (click)=\"toggleSummary()\"\n >\n Read full summary\n </button>\n }\n @if (summaryMeta(); as meta) {\n <p class=\"eds-body-sm mt-3 flex flex-wrap gap-3 text-zinc-500\">\n @if (meta.model) {\n <span>Model: {{ meta.model }}</span>\n }\n @if (meta.generatedAt) {\n <span>Generated {{ meta.generatedAt | date: 'medium' }}</span>\n }\n @if (meta.turnCount != null) {\n <span>{{ meta.turnCount }} transcript turns</span>\n }\n </p>\n }\n </section>\n <ng-content select=\"[summary-side]\" />\n </div>\n\n <section class=\"eds-card overflow-hidden bg-white p-0 shadow-sm\">\n <eds-call-recording-player\n [callId]=\"callId()\"\n (currentTimeChange)=\"onPlaybackTime($event)\"\n (durationChange)=\"onPlaybackDuration($event)\"\n />\n </section>\n\n <div class=\"grid grid-cols-1 items-start gap-4 lg:grid-cols-[1fr_300px]\">\n <section class=\"eds-card p-4 shadow-sm\" aria-labelledby=\"call-transcript-title\">\n <header class=\"mb-3 flex items-center justify-between gap-3\">\n <h2 id=\"call-transcript-title\" class=\"eds-h2 m-0\">Transcript</h2>\n @if (playbackDuration() > 0) {\n <span class=\"eds-mono-sm text-zinc-500\">\n {{ formatTime(currentTime()) }} / {{ formatTime(playbackDuration()) }}\n </span>\n }\n </header>\n\n <div #scrollHost class=\"flex max-h-[62vh] flex-col gap-3 overflow-y-auto rounded-xl border border-zinc-200 bg-zinc-50 p-3\" role=\"log\">\n @for (line of timedLines(); track line.index) {\n @if (line.who === 'system') {\n <div\n [class]=\"\n 'inline-flex max-w-[90%] items-center gap-2 self-center rounded-full bg-zinc-100 px-3 py-2 text-xs leading-snug text-zinc-500' +\n (line.index === activeIndex() ? ' outline outline-2 outline-blue-200' : '')\n \"\n [attr.data-line-index]=\"line.index\"\n >\n <eds-icon name=\"info\" [size]=\"14\" />\n <span>{{ line.text }}</span>\n </div>\n } @else {\n <article\n [class]=\"\n 'flex max-w-[72%] cursor-pointer gap-3 border-none bg-transparent p-0 text-left max-lg:max-w-[92%] ' +\n (line.who === 'patient' ? 'mr-auto self-start' : 'ml-auto flex-row-reverse self-end')\n \"\n [attr.data-line-index]=\"line.index\"\n [attr.data-who]=\"line.who\"\n tabindex=\"0\"\n role=\"button\"\n [attr.aria-current]=\"line.index === activeIndex() ? 'true' : null\"\n (click)=\"seekLine(line)\"\n (keyup.enter)=\"seekLine(line)\"\n >\n <div\n [class]=\"\n 'flex h-8 w-8 shrink-0 items-center justify-center rounded-full ' +\n (line.who === 'ai'\n ? 'call-conversation__avatar--ai text-white'\n : line.who === 'staff'\n ? 'border border-violet-200 bg-violet-50 text-violet-700'\n : 'bg-zinc-200 text-zinc-600')\n \"\n >\n <eds-icon [name]=\"speakerIcon(line.who)\" [size]=\"16\" />\n </div>\n <div\n [class]=\"\n 'flex min-w-0 flex-col gap-1 ' +\n (line.who === 'patient' ? 'items-start' : 'items-end')\n \"\n >\n <span\n [class]=\"\n 'inline-flex items-center gap-2 text-xs font-medium leading-none ' +\n (line.who === 'ai'\n ? 'text-blue-600'\n : line.who === 'staff'\n ? 'text-violet-700'\n : 'text-zinc-500')\n \"\n >\n {{ roleLabel(line.who) }}\n <span class=\"font-mono text-xs font-normal leading-none text-zinc-400\">{{ formatTime(line.startSec) }}</span>\n </span>\n <div\n [class]=\"\n 'rounded-xl border px-4 py-2 ' +\n (line.who === 'patient'\n ? 'rounded-tl rounded-tr-xl rounded-br-xl rounded-bl-xl border-zinc-200 bg-white'\n : line.who === 'staff'\n ? 'rounded-tl-xl rounded-tr rounded-br-xl rounded-bl-xl border-violet-200 bg-violet-50'\n : 'rounded-tl-xl rounded-tr rounded-br-xl rounded-bl-xl border-blue-200 bg-blue-50') +\n (line.index === activeIndex() ? ' ring-2 ring-blue-200' : '')\n \"\n >\n <p\n [class]=\"\n 'm-0 text-sm leading-snug ' +\n (line.who === 'staff' ? 'text-violet-900' : 'text-zinc-900')\n \"\n >{{ line.text }}</p>\n </div>\n </div>\n </article>\n }\n } @empty {\n <p class=\"eds-body-sm py-3 text-zinc-500\">No transcript available for this call.</p>\n }\n </div>\n </section>\n\n <aside class=\"eds-card p-4 shadow-sm\" aria-labelledby=\"call-goal-timeline-title\">\n <header class=\"mb-3 flex items-center justify-between gap-3\">\n <h2 id=\"call-goal-timeline-title\" class=\"eds-h2 m-0\">Goal Timeline</h2>\n <span class=\"eds-body-sm\">{{ tools().length }}</span>\n </header>\n\n <ul class=\"flex max-h-[62vh] list-none flex-col gap-3 overflow-y-auto p-0\">\n @for (tool of tools(); track toolTrackKey(tool, $index)) {\n <li\n [class]=\"\n 'grid grid-cols-[auto_1fr_auto] items-start gap-3 rounded-md border-l-4 bg-zinc-50 p-3 ' +\n (tool.state === 'ok'\n ? 'border-l-green-500'\n : tool.state === 'warn'\n ? 'border-l-amber-500'\n : tool.state === 'error'\n ? 'border-l-red-500'\n : tool.state === 'active'\n ? 'border-l-blue-600'\n : 'border-l-zinc-400')\n \"\n >\n <span class=\"eds-mono-sm whitespace-nowrap text-zinc-500\">{{ tool.t }}</span>\n <div class=\"flex min-w-0 flex-col gap-1\">\n <span class=\"eds-body-sm-bold\">{{ tool.name }}</span>\n <span class=\"eds-body-sm\">{{ tool.result }}</span>\n </div>\n <eds-badge [tone]=\"toolTone(tool.state)\">{{ tool.state }}</eds-badge>\n </li>\n } @empty {\n <li class=\"eds-body-sm py-3 text-zinc-500\">No goal activity recorded.</li>\n }\n </ul>\n </aside>\n </div>\n</div>\n","import { ChangeDetectionStrategy, Component, computed, inject, input, OnInit, signal } from '@angular/core';\nimport { RouterLink } from '@angular/router';\n\nimport {\n AgentActivity,\n activityTypeLabel,\n} from '../../models/agent-activity.types';\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport { EdsBadgeComponent } from '../../ui/eds-badge.component';\n\nconst PREVIEW_LIMIT = 4;\n\n@Component({\n selector: 'eds-call-tasks-panel',\n imports: [RouterLink, EdsBadgeComponent],\n templateUrl: './call-tasks-panel.component.html',\n styleUrl: './call-tasks-panel.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class CallTasksPanelComponent implements OnInit {\n private readonly api = inject(AiReceptionistApiService);\n\n readonly callId = input.required<string>();\n readonly compact = input(false);\n\n protected readonly activities = signal<AgentActivity[]>([]);\n protected readonly total = signal(0);\n protected readonly loading = signal(true);\n protected readonly previewLimit = PREVIEW_LIMIT;\n protected readonly activityTypeLabel = activityTypeLabel;\n\n protected readonly previewActivities = computed(() =>\n this.activities().slice(0, PREVIEW_LIMIT),\n );\n\n ngOnInit(): void {\n this.api\n .getActivities({\n callId: this.callId(),\n page: 1,\n limit: 50,\n })\n .subscribe({\n next: ({ items, total }) => {\n this.activities.set(items);\n this.total.set(total);\n this.loading.set(false);\n },\n error: () => this.loading.set(false),\n });\n }\n\n protected activitySubline(item: AgentActivity): string {\n const bits = [\n item.patientName,\n item.visitType,\n item.providerName,\n ].filter(Boolean);\n return bits.join(' · ') || item.detail || '—';\n }\n\n protected activityTone(type: string): 'brand' | 'pos' | 'notice' | 'violet' | 'gray' {\n switch (type) {\n case 'appointment_booked':\n return 'brand';\n case 'patient_created':\n return 'pos';\n case 'task_created':\n return 'notice';\n default:\n return 'gray';\n }\n }\n}\n","<section class=\"call-tasks eds-card px-5 py-4 mt-5 shadow-sm\" [class.call-tasks--compact]=\"compact()\">\n <header class=\"mb-4 flex items-center justify-between gap-3\">\n <h2 class=\"eds-h2\">Activities from this call</h2>\n <a\n routerLink=\"../activities\"\n [queryParams]=\"{ callId: callId() }\"\n class=\"call-tasks__all-link eds-body-sm\"\n >\n View all Activities\n </a>\n </header>\n\n @if (loading()) {\n <p class=\"eds-body-sm call-tasks__empty\">Loading activities…</p>\n } @else if (activities().length === 0) {\n <p class=\"eds-body-sm call-tasks__empty\">No activities linked to this call.</p>\n } @else {\n <ul class=\"call-tasks__list overflow-hidden rounded-lg border border-[var(--eds-border-default,#e5e5e5)]\">\n @for (item of previewActivities(); track item.id) {\n <li class=\"call-tasks__item border-b border-[var(--eds-border-default,#e5e5e5)] last:border-b-0\">\n <div class=\"call-tasks__row grid w-full grid-cols-[minmax(0,1fr)_auto] items-center gap-3 border-none bg-transparent px-4 py-3 text-left font-[inherit]\">\n <div class=\"flex min-w-0 flex-col gap-1\">\n <span class=\"call-tasks__title min-w-0 overflow-hidden text-ellipsis whitespace-nowrap text-sm font-semibold\">{{ item.title || activityTypeLabel(item.type) }}</span>\n <span class=\"call-tasks__sub overflow-hidden text-ellipsis whitespace-nowrap text-xs\">{{ activitySubline(item) }}</span>\n </div>\n <eds-badge [tone]=\"activityTone(item.type)\">{{ activityTypeLabel(item.type) }}</eds-badge>\n </div>\n </li>\n }\n </ul>\n @if (total() > previewLimit) {\n <p class=\"call-tasks__more eds-body-sm mt-3\">\n +{{ total() - previewLimit }} more —\n <a routerLink=\"../activities\" [queryParams]=\"{ callId: callId() }\" class=\"call-tasks__all-link\">\n View all Activities\n </a>\n </p>\n }\n }\n</section>\n","import { BadgeTone } from '../ui/eds-badge.component';\n\n/** Known history outcomes from GET /calls/history (HistoryCallDto.outcome). */\nexport const HISTORY_OUTCOMES = [\n 'completed',\n 'failed',\n 'transferred',\n 'escalated',\n] as const;\n\nexport type HistoryOutcome = (typeof HISTORY_OUTCOMES)[number] | 'custom';\n\nexport function normalizeHistoryOutcome(outcome: string): HistoryOutcome {\n const key = outcome.trim().toLowerCase();\n if ((HISTORY_OUTCOMES as readonly string[]).includes(key)) {\n return key as HistoryOutcome;\n }\n return 'custom';\n}\n\n/** Badge tone for history table / detail — separate from lifecycle or live UI status. */\nexport function outcomeBadgeTone(outcome: string): BadgeTone {\n switch (normalizeHistoryOutcome(outcome)) {\n case 'completed':\n return 'pos';\n case 'failed':\n return 'neg';\n case 'transferred':\n return 'violet';\n case 'escalated':\n return 'neg';\n default: {\n const key = outcome.trim().toLowerCase();\n if (key.includes('transfer')) return 'violet';\n if (key.includes('fail') || key.includes('escalat') || key.includes('abandon')) return 'neg';\n if (key.includes('voicemail')) return 'gray';\n if (key.includes('complete') || key.includes('book') || key.includes('task')) return 'pos';\n return 'brand';\n }\n }\n}\n\nexport function formatOutcomeLabel(outcome: string): string {\n const trimmed = outcome.trim();\n if (!trimmed) return 'Unknown';\n const known = normalizeHistoryOutcome(trimmed);\n if (known === 'custom') return trimmed;\n return known.charAt(0).toUpperCase() + known.slice(1);\n}\n","import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core';\nimport { ActivatedRoute, RouterLink } from '@angular/router';\n\nimport { ApiLogsCallPanelComponent } from '../../components/api-logs-call-panel/api-logs-call-panel.component';\nimport { CallConversationPanelComponent } from '../../components/call-conversation-panel/call-conversation-panel.component';\nimport { CallTasksPanelComponent } from '../../components/call-tasks-panel/call-tasks-panel.component';\nimport { VOICE_RECEPTIONIST_CONFIG } from '../../config';\nimport { HistoryDetail } from '../../models/ai-receptionist.types';\nimport { AiReceptionistApiService } from '../../services/ai-receptionist-api.service';\nimport { CallReviewService } from '../../services/call-review.service';\nimport { formatOutcomeLabel, outcomeBadgeTone } from '../../utils/call-outcome.util';\nimport {\n isNeedsReview,\n} from '../../utils/call-review.util';\nimport { isUnknownCaller } from '../../utils/call-history-display.util';\nimport { EdsBadgeComponent } from '../../ui/eds-badge.component';\nimport { EdsButtonComponent } from '../../ui/eds-button.component';\nimport { EdsIconComponent } from '../../ui/eds-icon.component';\nimport { ToastService } from '../../shared/toast.service';\n\n@Component({\n selector: 'eds-call-history-detail-page',\n imports: [\n RouterLink,\n EdsBadgeComponent,\n EdsButtonComponent,\n EdsIconComponent,\n CallConversationPanelComponent,\n CallTasksPanelComponent,\n ApiLogsCallPanelComponent,\n ],\n templateUrl: './call-history-detail-page.component.html',\n styleUrl: './call-history-detail-page.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class CallHistoryDetailPageComponent implements OnInit {\n private readonly api = inject(AiReceptionistApiService);\n private readonly reviewService = inject(CallReviewService);\n private readonly toast = inject(ToastService);\n private readonly route = inject(ActivatedRoute);\n private readonly config = inject(VOICE_RECEPTIONIST_CONFIG);\n\n /** Standalone dashboard only — hidden in EHR embed. */\n protected readonly showApiLogs = !!this.config.showModuleNav;\n\n /** Parent shell route — back targets sibling `history`, not `history/history`. */\n protected readonly historyListLink: string[] = ['history'];\n protected readonly historyListRelativeTo = this.route.parent;\n\n protected readonly detail = signal<HistoryDetail | null>(null);\n protected readonly loading = signal(true);\n protected readonly markingReview = signal(false);\n\n protected isNeedsReview = isNeedsReview;\n protected outcomeTone = outcomeBadgeTone;\n protected outcomeLabel = formatOutcomeLabel;\n\n protected isUnknownName(name: string | null | undefined): boolean {\n return isUnknownCaller(name);\n }\n\n ngOnInit(): void {\n const id = this.route.snapshot.paramMap.get('id') ?? '';\n this.api.getCallDetail(id).subscribe(({ data }) => {\n this.detail.set(data);\n this.loading.set(false);\n });\n }\n\n protected markReviewed(): void {\n const d = this.detail();\n if (!d || !isNeedsReview(d.review) || this.markingReview()) return;\n\n this.markingReview.set(true);\n const prev = d.review;\n this.detail.update((current) =>\n current ? { ...current, review: 'reviewed' } : current,\n );\n\n this.reviewService.markReviewed(d.id).subscribe({\n next: () => {\n this.markingReview.set(false);\n this.toast.show('Call marked as reviewed', 'success');\n },\n error: () => {\n this.markingReview.set(false);\n this.detail.update((current) =>\n current ? { ...current, review: prev } : current,\n );\n this.toast.show('Failed to mark call as reviewed', 'error');\n },\n });\n }\n\n protected refreshCallDetail(): void {\n const id = this.detail()?.id;\n if (!id) return;\n this.api.getCallDetail(id).subscribe(({ data }) => {\n if (data) this.detail.set(data);\n });\n }\n\n protected formatDuration(sec: number): string {\n const m = Math.floor(sec / 60);\n const s = sec % 60;\n return `${m}:${s.toString().padStart(2, '0')}`;\n }\n}\n","<div class=\"eds-page call-history-detail-page\">\n <a\n class=\"call-history-detail-page__back eds-body-sm mb-4 inline-flex items-center gap-1 no-underline\"\n [routerLink]=\"historyListLink\"\n [relativeTo]=\"historyListRelativeTo\"\n >\n <eds-icon name=\"arrow_back\" [size]=\"18\" /> Back to history\n </a>\n\n @if (loading()) {\n <p class=\"eds-body-sm\">Loading call detail…</p>\n } @else if (detail()) {\n <header class=\"mb-4 flex flex-wrap items-start justify-between gap-4\">\n <div>\n <h1 class=\"eds-h1\">{{ detail()!.name }}</h1>\n <p class=\"eds-body-sm\">{{ detail()!.phone }} · {{ detail()!.reason }}</p>\n </div>\n @if (isNeedsReview(detail()!.review)) {\n <eds-button\n variant=\"primary\"\n [disabled]=\"markingReview()\"\n (click)=\"markReviewed()\"\n >\n <eds-icon name=\"check_circle\" [size]=\"18\" />\n {{ markingReview() ? 'Saving…' : 'Mark as reviewed' }}\n </eds-button>\n }\n </header>\n\n <dl class=\"call-history-detail-page__meta mx-0 mt-0 mb-5 grid grid-cols-2 gap-4 sm:grid-cols-2\">\n <div><dt class=\"eds-label\">Outcome</dt><dd>\n <eds-badge [tone]=\"outcomeTone(detail()!.outcome)\">\n {{ outcomeLabel(detail()!.outcome) }}\n </eds-badge>\n </dd></div>\n <div><dt class=\"eds-label\">Duration</dt><dd>{{ detail()!.durationLabel ?? formatDuration(detail()!.duration) }}</dd></div>\n </dl>\n\n <eds-call-conversation-panel\n [callId]=\"detail()!.id\"\n [summary]=\"detail()!.summary\"\n [transcript]=\"detail()!.transcript\"\n [tools]=\"detail()!.tools\"\n [callDurationSec]=\"detail()!.duration\"\n [callerName]=\"detail()!.name\"\n [callerMatched]=\"!isUnknownName(detail()!.name)\"\n (summaryRegenerated)=\"refreshCallDetail()\"\n >\n <eds-call-tasks-panel\n summary-side\n [callId]=\"detail()!.id\"\n [compact]=\"true\"\n />\n </eds-call-conversation-panel>\n\n @if (showApiLogs) {\n <section class=\"mt-5\">\n <h2 class=\"eds-h2 mb-3\">API Logs</h2>\n <eds-api-logs-call-panel [callId]=\"detail()!.id\" />\n </section>\n }\n } @else {\n <p class=\"eds-body-sm\">Call not found.</p>\n }\n</div>\n"],"names":[],"mappings":";;;;;;;;;;;;;;AA0BA,MAAM,OAAO,GAAG,IAAI;MASP,yBAAyB,CAAA;AA0BpC,IAAA,WAAA,GAAA;AAzBiB,QAAA,IAAA,CAAA,GAAG,GAAG,MAAM,CAAC,wBAAwB,CAAC;AACtC,QAAA,IAAA,CAAA,MAAM,GAAG,MAAM,CAAC,sBAAsB,CAAC;AACvC,QAAA,IAAA,CAAA,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;AAEvC,QAAA,IAAA,CAAA,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAU;AACjC,QAAA,IAAA,CAAA,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC;AACnB,QAAA,IAAA,CAAA,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC;AAET,QAAA,IAAA,CAAA,KAAK,GAAG,MAAM,CAAmB,EAAE,CAAC;AACpC,QAAA,IAAA,CAAA,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC;AACtB,QAAA,IAAA,CAAA,KAAK,GAAG,MAAM,CAAgB,IAAI,CAAC;AACnC,QAAA,IAAA,CAAA,QAAQ,GAAG,MAAM,CAAwB,IAAI,CAAC;AAC9C,QAAA,IAAA,CAAA,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC;AAE5B,QAAA,IAAA,CAAA,UAAU,GAAG,QAAQ,CAAC,OAAO;AAC5C,YAAA,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;AACrB,YAAA,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;AAClB,SAAA,CAAC,CAAC;AAEc,QAAA,IAAA,CAAA,eAAe,GAAG,CAAC,OAA4B,KAAI;YAClE,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,EAAE;gBACpC,IAAI,CAAC,cAAc,EAAE;YACvB;AACF,QAAA,CAAC;AAGC,QAAA,YAAY,CAAC,IAAI,CAAC,UAAU;aACzB,IAAI,CACH,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAI;YAC7B,IAAI,CAAC,MAAM,EAAE;AACX,gBAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;AAClB,gBAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;AACvB,gBAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AACpB,gBAAA,OAAO,KAAK;YACd;YAEA,IAAI,IAAI,EAAE;AACR,gBAAA,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;gBACrB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,iBAAiB,EAAE,IAAI,CAAC,eAAe,CAAC;YACzD;YAEA,MAAM,KAAK,GAAG;kBACV,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,CACpB,SAAS,CAAC,CAAC,CAAC,EACZ,SAAS,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;kBAElD,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC;YAEnC,OAAO,KAAK,CAAC,IAAI,CACf,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,KAAI;AAChB,gBAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;AACrB,gBAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;AACvB,gBAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AACtB,YAAA,CAAC,CAAC,EACF,UAAU,CAAC,MAAK;AACd,gBAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;AACvB,gBAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,0BAA0B,CAAC;AAC1C,gBAAA,OAAO,KAAK;AACd,YAAA,CAAC,CAAC,EACF,QAAQ,CAAC,MAAK;gBACZ,IAAI,IAAI,EAAE;oBACR,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,eAAe,CAAC;AACxD,oBAAA,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;gBACvB;YACF,CAAC,CAAC,CACH;QACH,CAAC,CAAC,EACF,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC;AAEpC,aAAA,SAAS,EAAE;;AAGd,QAAA,YAAY,CAAC,IAAI,CAAC,MAAM;AACrB,aAAA,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC;AACxC,aAAA,SAAS,CAAC,CAAC,EAAE,KAAI;YAChB,IAAI,EAAE,EAAE;AACN,gBAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;AACtB,gBAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;YACtB;AACF,QAAA,CAAC,CAAC;IACN;AAEU,IAAA,aAAa,CAAC,IAAoB,EAAA;AAC1C,QAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;AACvB,QAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;IAC3B;IAEU,WAAW,GAAA;AACnB,QAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;AAC1B,QAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;IACzB;IAEQ,cAAc,GAAA;AACpB,QAAA,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE;AACxB,QAAA,IAAI,CAAC,EAAE;YAAE;QACT,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;AACpC,YAAA,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;AAC3C,SAAA,CAAC;IACJ;+GAnGW,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAAzB,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,yBAAyB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,yBAAA,EAAA,MAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECnCtC,2rBAiBA,EAAA,MAAA,EAAA,CAAA,4KAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDac,qBAAqB,4JAAE,2BAA2B,EAAA,QAAA,EAAA,2BAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,UAAA,EAAA,UAAA,CAAA,EAAA,OAAA,EAAA,CAAA,QAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;4FAKnD,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBAPrC,SAAS;+BACI,yBAAyB,EAAA,OAAA,EAC1B,CAAC,qBAAqB,EAAE,2BAA2B,CAAC,EAAA,eAAA,EAG5C,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,2rBAAA,EAAA,MAAA,EAAA,CAAA,4KAAA,CAAA,EAAA;;;AEzBnD;AACM,SAAU,cAAc,CAAC,KAAa,EAAA;AAC1C,IAAA,MAAM,OAAO,GAAG,KAAK,EAAE,IAAI,EAAE;AAC7B,IAAA,IAAI,CAAC,OAAO;AAAE,QAAA,OAAO,IAAI;IACzB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC;AACtD,IAAA,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAAE,QAAA,OAAO,IAAI;AACnD,IAAA,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC;AACvD,IAAA,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC;AACzE,IAAA,OAAO,IAAI;AACb;AAEA,SAAS,gBAAgB,CAAC,IAAoB,EAAA;IAC5C,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,EAAE;IACnD,MAAM,GAAG,GAAG,IAAuE;IACnF,IAAI,GAAG,CAAC,aAAa,IAAI,IAAI,IAAI,GAAG,CAAC,aAAa,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,aAAa;IACjF,IAAI,GAAG,CAAC,SAAS,IAAI,IAAI,IAAI,GAAG,CAAC,SAAS,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,SAAS;AACrE,IAAA,OAAO,IAAI;AACb;AAEA;;;AAGG;AACG,SAAU,oBAAoB,CAClC,KAAuB,EACvB,eAAuB,EAAA;IAEvB,IAAI,CAAC,KAAK,CAAC,MAAM;AAAE,QAAA,OAAO,EAAE;IAE5B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC,CAAC;AAC1C,IAAA,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC,IAAI,CAAC,CAAC;AAC5D,IAAA,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;AAEpD,IAAA,MAAM,MAAM,GAAa,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAExD,IAAI,WAAW,EAAE;AACf,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE;YACxC,MAAM,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAE;QAC1B;IACF;SAAO;AACL,QAAA,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,MAAM;AACrE,QAAA,MAAM,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC;QAChD,IAAI,YAAY,GAAG,CAAC;AAEpB,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE;AACxC,YAAA,IAAI,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE;gBACvB,MAAM,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAE;gBACxB;YACF;AACA,YAAA,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC;AACrB,YAAA,IAAI,IAAI,CAAC,GAAG,KAAK,QAAQ,EAAE;gBACzB,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;gBACrC;YACF;AACA,YAAA,MAAM,CAAC,CAAC,CAAC,GAAG,YAAY,GAAG,IAAI;YAC/B,YAAY,IAAI,CAAC;QACnB;IACF;IAEA,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,KAAI;AAC/B,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC;QAC9B,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC;AACnE,QAAA,MAAM,MAAM,GAAG,SAAS,IAAI,KAAK;QACjC,OAAO,EAAE,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC/E,IAAA,CAAC,CAAC;AACJ;AAEM,SAAU,qBAAqB,CAAC,KAA4B,EAAE,UAAkB,EAAA;AACpF,IAAA,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,UAAU,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;AAC9C,IAAA,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE;AAC7C,QAAA,IAAI,UAAU,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ;AAAE,YAAA,OAAO,CAAC;IAC/C;AACA,IAAA,OAAO,CAAC;AACV;AAEM,SAAU,kBAAkB,CAAC,OAAe,EAAA;AAChD,IAAA,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC;AAC5B,IAAA,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE;AAChB,IAAA,OAAO,CAAA,EAAG,CAAC,CAAA,CAAA,EAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;AAChD;AAEM,SAAU,cAAc,CAAC,KAAyB,EAAA;AACtD,IAAA,MAAM,GAAG,GAAuC;AAC9C,QAAA,EAAE,EAAE,6BAA6B;AACjC,QAAA,IAAI,EAAE,+BAA+B;AACrC,QAAA,KAAK,EAAE,gCAAgC;AACvC,QAAA,MAAM,EAAE,iCAAiC;KAC1C;AACD,IAAA,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE;AACzB;SAEgB,mBAAmB,CACjC,GAA0B,EAC1B,UAA0B,EAC1B,aAAuB,EAAA;AAEvB,IAAA,IAAI,GAAG,KAAK,SAAS,EAAE;QACrB,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;AAC5C,QAAA,IAAI,CAAC,IAAI,IAAI,aAAa,KAAK,KAAK,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,KAAK,GAAG,EAAE;AAC9E,YAAA,OAAO,gBAAgB;QACzB;AACA,QAAA,OAAO,IAAI;IACb;AACA,IAAA,MAAM,MAAM,GAA8D;AACxE,QAAA,EAAE,EAAE,iBAAiB;AACrB,QAAA,KAAK,EAAE,OAAO;AACd,QAAA,MAAM,EAAE,QAAQ;KACjB;AACD,IAAA,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,GAAG;AAC3B;;MC3Ea,8BAA8B,CAAA;AAkD/B,IAAA,SAAS,CAAC,GAA0B,EAAA;AAC5C,QAAA,OAAO,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,SAAS,CAAC;IACvF;AAEU,IAAA,WAAW,CAAC,GAA2B,EAAA;QAC/C,QAAQ,GAAG;AACT,YAAA,KAAK,IAAI;AACP,gBAAA,OAAO,YAAY;AACrB,YAAA,KAAK,OAAO;AACV,gBAAA,OAAO,eAAe;AACxB,YAAA,KAAK,SAAS;AACZ,gBAAA,OAAO,QAAQ;AACjB,YAAA;AACE,gBAAA,OAAO,MAAM;;IAEnB;AAEU,IAAA,QAAQ,CAAC,KAAa,EAAA;QAC9B,QAAQ,KAAK;AACX,YAAA,KAAK,IAAI;AACP,gBAAA,OAAO,KAAK;AACd,YAAA,KAAK,MAAM;AACT,gBAAA,OAAO,QAAQ;AACjB,YAAA,KAAK,OAAO;AACV,gBAAA,OAAO,KAAK;AACd,YAAA,KAAK,QAAQ;AACX,gBAAA,OAAO,OAAO;AAChB,YAAA;AACE,gBAAA,OAAO,MAAM;;IAEnB;AAEA,IAAA,WAAA,GAAA;AAjFiB,QAAA,IAAA,CAAA,GAAG,GAAG,MAAM,CAAC,wBAAwB,CAAC;AACtC,QAAA,IAAA,CAAA,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC;AAEpC,QAAA,IAAA,CAAA,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAU;AACjC,QAAA,IAAA,CAAA,OAAO,GAAG,KAAK,CAAC,QAAQ,EAAU;AAClC,QAAA,IAAA,CAAA,UAAU,GAAG,KAAK,CAAC,QAAQ,EAAoB;AAC/C,QAAA,IAAA,CAAA,KAAK,GAAG,KAAK,CAAC,QAAQ,EAAe;AACrC,QAAA,IAAA,CAAA,eAAe,GAAG,KAAK,CAAC,CAAC,CAAC;AAC1B,QAAA,IAAA,CAAA,eAAe,GAAG,KAAK,CAAC,IAAI,CAAC;AAC7B,QAAA,IAAA,CAAA,UAAU,GAAG,KAAK,CAAgB,IAAI,CAAC;AACvC,QAAA,IAAA,CAAA,aAAa,GAAG,KAAK,CAAiB,IAAI,CAAC;QAE3C,IAAA,CAAA,kBAAkB,GAAG,MAAM,EAAQ;AAE3B,QAAA,IAAA,CAAA,MAAM,GAAG,SAAS,CAAC,4BAA4B,CAAC;AAChD,QAAA,IAAA,CAAA,UAAU,GAAG,SAAS,CAA0B,YAAY,CAAC;AAE3D,QAAA,IAAA,CAAA,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC;AACvB,QAAA,IAAA,CAAA,gBAAgB,GAAG,MAAM,CAAC,CAAC,CAAC;AAC5B,QAAA,IAAA,CAAA,eAAe,GAAG,MAAM,CAAC,KAAK,CAAC;AACjC,QAAA,IAAA,CAAA,kBAAkB,GAAG,MAAM,CAAgB,IAAI,CAAC;AAC9C,QAAA,IAAA,CAAA,cAAc,GAAG,QAAQ,CAC1C,MAAM,IAAI,CAAC,kBAAkB,EAAE,IAAI,IAAI,CAAC,OAAO,EAAE,CAClD;AACkB,QAAA,IAAA,CAAA,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC;AAC5B,QAAA,IAAA,CAAA,WAAW,GAAG,MAAM,CAI7B,IAAI,CAAC;QAEI,IAAA,CAAA,UAAU,GAAG,QAAQ,CAAC,MACvC,oBAAoB,CAClB,IAAI,CAAC,UAAU,EAAE,EACjB,IAAI,CAAC,gBAAgB,EAAE,IAAI,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,CACzD,CACF;AAEkB,QAAA,IAAA,CAAA,WAAW,GAAG,QAAQ,CAAC,MACxC,qBAAqB,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAC7D;QAEkB,IAAA,CAAA,iBAAiB,GAAG,QAAQ,CAAC,MAC9C,IAAI,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,CAClD;QAEkB,IAAA,CAAA,UAAU,GAAG,kBAAkB;QAC/B,IAAA,CAAA,YAAY,GAAG,iBAAiB;QAmCjD,MAAM,CACJ,MAAK;YACH,IAAI,CAAC,MAAM,EAAE;AACb,YAAA,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC;AACnC,QAAA,CAAC,EACD,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAC5B;QAED,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE;YAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,EAAE,aAAa;AAC7C,YAAA,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI;gBAAE;YACtB,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,CAAA,kBAAA,EAAqB,GAAG,CAAA,EAAA,CAAI,CAAC;AAC5D,YAAA,IAAI,GAAG,YAAY,WAAW,EAAE;AAC9B,gBAAA,GAAG,CAAC,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;YAC9D;AACF,QAAA,CAAC,CAAC;IACJ;AAEU,IAAA,cAAc,CAAC,GAAW,EAAA;AAClC,QAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC;IAC3B;AAEU,IAAA,kBAAkB,CAAC,GAAW,EAAA;AACtC,QAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC;IAChC;AAEU,IAAA,QAAQ,CAAC,IAAyB,EAAA;AAC1C,QAAA,IAAI,IAAI,CAAC,GAAG,KAAK,QAAQ;YAAE;QAC3B,IAAI,CAAC,MAAM,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;IACtC;IAEU,aAAa,GAAA;AACrB,QAAA,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACxC;IAEU,iBAAiB,GAAA;QACzB,IAAI,IAAI,CAAC,YAAY,EAAE;YAAE;AAEzB,QAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;AAC3B,QAAA,IAAI,CAAC,GAAG,CAAC,qBAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC;AACtD,YAAA,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,KAAI;AACjB,gBAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC;gBAC5B,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC;AACzC,gBAAA,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC;AAC9B,gBAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC;oBACnB,KAAK,EAAE,IAAI,CAAC,YAAY;oBACxB,WAAW,EAAE,IAAI,CAAC,kBAAkB;oBACpC,SAAS,EAAE,IAAI,CAAC,mBAAmB;AACpC,iBAAA,CAAC;AACF,gBAAA,IAAI,CAAC,kBAAkB,CAAC,IAAI,EAAE;gBAC9B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,0BAA0B,EAAE,SAAS,CAAC;YACxD,CAAC;AACD,YAAA,KAAK,EAAE,CAAC,GAAyB,KAAI;AACnC,gBAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC;AAC5B,gBAAA,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,IAAI,8BAA8B,EAAE,OAAO,CAAC;YACzE,CAAC;AACF,SAAA,CAAC;IACJ;+GA7IW,8BAA8B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAA9B,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,8BAA8B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,6BAAA,EAAA,MAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,eAAA,EAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,eAAA,EAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,aAAA,EAAA,EAAA,iBAAA,EAAA,eAAA,EAAA,UAAA,EAAA,eAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,kBAAA,EAAA,oBAAA,EAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,QAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAeL,4BAA4B,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,EAAA,EAAA,YAAA,EAAA,YAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,YAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EC1DlE,8qQA+LA,EAAA,MAAA,EAAA,CAAA,sFAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,ED9JI,QAAQ,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACR,4BAA4B,EAAA,QAAA,EAAA,2BAAA,EAAA,MAAA,EAAA,CAAA,QAAA,CAAA,EAAA,OAAA,EAAA,CAAA,mBAAA,EAAA,gBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAC5B,iBAAiB,EAAA,QAAA,EAAA,WAAA,EAAA,MAAA,EAAA,CAAA,MAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACjB,kBAAkB,gGAClB,gBAAgB,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,MAAA,EAAA,MAAA,EAAA,QAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;4FAMP,8BAA8B,EAAA,UAAA,EAAA,CAAA;kBAb1C,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,6BAA6B,EAAA,OAAA,EAC9B;wBACP,QAAQ;wBACR,4BAA4B;wBAC5B,iBAAiB;wBACjB,kBAAkB;wBAClB,gBAAgB;qBACjB,EAAA,eAAA,EAGgB,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,8qQAAA,EAAA,MAAA,EAAA,CAAA,sFAAA,CAAA,EAAA;;;AE/BjD,MAAM,aAAa,GAAG,CAAC;MASV,uBAAuB,CAAA;AAPpC,IAAA,WAAA,GAAA;AAQmB,QAAA,IAAA,CAAA,GAAG,GAAG,MAAM,CAAC,wBAAwB,CAAC;AAE9C,QAAA,IAAA,CAAA,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAU;AACjC,QAAA,IAAA,CAAA,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC;AAEZ,QAAA,IAAA,CAAA,UAAU,GAAG,MAAM,CAAkB,EAAE,CAAC;AACxC,QAAA,IAAA,CAAA,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC;AACjB,QAAA,IAAA,CAAA,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC;QACtB,IAAA,CAAA,YAAY,GAAG,aAAa;QAC5B,IAAA,CAAA,iBAAiB,GAAG,iBAAiB;AAErC,QAAA,IAAA,CAAA,iBAAiB,GAAG,QAAQ,CAAC,MAC9C,IAAI,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,CAC1C;AAwCF,IAAA;IAtCC,QAAQ,GAAA;AACN,QAAA,IAAI,CAAC;AACF,aAAA,aAAa,CAAC;AACb,YAAA,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;AACrB,YAAA,IAAI,EAAE,CAAC;AACP,YAAA,KAAK,EAAE,EAAE;SACV;AACA,aAAA,SAAS,CAAC;YACT,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAI;AACzB,gBAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;AAC1B,gBAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;AACrB,gBAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;YACzB,CAAC;YACD,KAAK,EAAE,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;AACrC,SAAA,CAAC;IACN;AAEU,IAAA,eAAe,CAAC,IAAmB,EAAA;AAC3C,QAAA,MAAM,IAAI,GAAG;AACX,YAAA,IAAI,CAAC,WAAW;AAChB,YAAA,IAAI,CAAC,SAAS;AACd,YAAA,IAAI,CAAC,YAAY;AAClB,SAAA,CAAC,MAAM,CAAC,OAAO,CAAC;AACjB,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,GAAG;IAC/C;AAEU,IAAA,YAAY,CAAC,IAAY,EAAA;QACjC,QAAQ,IAAI;AACV,YAAA,KAAK,oBAAoB;AACvB,gBAAA,OAAO,OAAO;AAChB,YAAA,KAAK,iBAAiB;AACpB,gBAAA,OAAO,KAAK;AACd,YAAA,KAAK,cAAc;AACjB,gBAAA,OAAO,QAAQ;AACjB,YAAA;AACE,gBAAA,OAAO,MAAM;;IAEnB;+GArDW,uBAAuB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAAvB,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,uBAAuB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,sBAAA,EAAA,MAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECnBpC,g+DAwCA,EAAA,MAAA,EAAA,CAAA,mhBAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,ED1BY,UAAU,oOAAE,iBAAiB,EAAA,QAAA,EAAA,WAAA,EAAA,MAAA,EAAA,CAAA,MAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;4FAK5B,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBAPnC,SAAS;+BACE,sBAAsB,EAAA,OAAA,EACvB,CAAC,UAAU,EAAE,iBAAiB,CAAC,EAAA,eAAA,EAGvB,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,g+DAAA,EAAA,MAAA,EAAA,CAAA,mhBAAA,CAAA,EAAA;;;AEfjD;AACO,MAAM,gBAAgB,GAAG;IAC9B,WAAW;IACX,QAAQ;IACR,aAAa;IACb,WAAW;CACH;AAIJ,SAAU,uBAAuB,CAAC,OAAe,EAAA;IACrD,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;AACxC,IAAA,IAAK,gBAAsC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;AACzD,QAAA,OAAO,GAAqB;IAC9B;AACA,IAAA,OAAO,QAAQ;AACjB;AAEA;AACM,SAAU,gBAAgB,CAAC,OAAe,EAAA;AAC9C,IAAA,QAAQ,uBAAuB,CAAC,OAAO,CAAC;AACtC,QAAA,KAAK,WAAW;AACd,YAAA,OAAO,KAAK;AACd,QAAA,KAAK,QAAQ;AACX,YAAA,OAAO,KAAK;AACd,QAAA,KAAK,aAAa;AAChB,YAAA,OAAO,QAAQ;AACjB,QAAA,KAAK,WAAW;AACd,YAAA,OAAO,KAAK;QACd,SAAS;YACP,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;AACxC,YAAA,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC;AAAE,gBAAA,OAAO,QAAQ;AAC7C,YAAA,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC;AAAE,gBAAA,OAAO,KAAK;AAC5F,YAAA,IAAI,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC;AAAE,gBAAA,OAAO,MAAM;AAC5C,YAAA,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC;AAAE,gBAAA,OAAO,KAAK;AAC1F,YAAA,OAAO,OAAO;QAChB;;AAEJ;AAEM,SAAU,kBAAkB,CAAC,OAAe,EAAA;AAChD,IAAA,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE;AAC9B,IAAA,IAAI,CAAC,OAAO;AAAE,QAAA,OAAO,SAAS;AAC9B,IAAA,MAAM,KAAK,GAAG,uBAAuB,CAAC,OAAO,CAAC;IAC9C,IAAI,KAAK,KAAK,QAAQ;AAAE,QAAA,OAAO,OAAO;AACtC,IAAA,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;AACvD;;MCba,8BAA8B,CAAA;AAf3C,IAAA,WAAA,GAAA;AAgBmB,QAAA,IAAA,CAAA,GAAG,GAAG,MAAM,CAAC,wBAAwB,CAAC;AACtC,QAAA,IAAA,CAAA,aAAa,GAAG,MAAM,CAAC,iBAAiB,CAAC;AACzC,QAAA,IAAA,CAAA,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC;AAC5B,QAAA,IAAA,CAAA,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC;AAC9B,QAAA,IAAA,CAAA,MAAM,GAAG,MAAM,CAAC,yBAAyB,CAAC;;QAGxC,IAAA,CAAA,WAAW,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa;;AAGzC,QAAA,IAAA,CAAA,eAAe,GAAa,CAAC,SAAS,CAAC;AACvC,QAAA,IAAA,CAAA,qBAAqB,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM;AAEzC,QAAA,IAAA,CAAA,MAAM,GAAG,MAAM,CAAuB,IAAI,CAAC;AAC3C,QAAA,IAAA,CAAA,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC;AACtB,QAAA,IAAA,CAAA,aAAa,GAAG,MAAM,CAAC,KAAK,CAAC;QAEtC,IAAA,CAAA,aAAa,GAAG,aAAa;QAC7B,IAAA,CAAA,WAAW,GAAG,gBAAgB;QAC9B,IAAA,CAAA,YAAY,GAAG,kBAAkB;AAoD5C,IAAA;AAlDW,IAAA,aAAa,CAAC,IAA+B,EAAA;AACrD,QAAA,OAAO,eAAe,CAAC,IAAI,CAAC;IAC9B;IAEA,QAAQ,GAAA;AACN,QAAA,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE;AACvD,QAAA,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,KAAI;AAChD,YAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;AACrB,YAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;AACzB,QAAA,CAAC,CAAC;IACJ;IAEU,YAAY,GAAA;AACpB,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE;AACvB,QAAA,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE;YAAE;AAE5D,QAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;AAC5B,QAAA,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM;QACrB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,KACzB,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CACvD;QAED,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;YAC9C,IAAI,EAAE,MAAK;AACT,gBAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC;gBAC7B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,yBAAyB,EAAE,SAAS,CAAC;YACvD,CAAC;YACD,KAAK,EAAE,MAAK;AACV,gBAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC;gBAC7B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,KACzB,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CACjD;gBACD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,iCAAiC,EAAE,OAAO,CAAC;YAC7D,CAAC;AACF,SAAA,CAAC;IACJ;IAEU,iBAAiB,GAAA;QACzB,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE;AAC5B,QAAA,IAAI,CAAC,EAAE;YAAE;AACT,QAAA,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,KAAI;AAChD,YAAA,IAAI,IAAI;AAAE,gBAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;AACjC,QAAA,CAAC,CAAC;IACJ;AAEU,IAAA,cAAc,CAAC,GAAW,EAAA;QAClC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC;AAC9B,QAAA,MAAM,CAAC,GAAG,GAAG,GAAG,EAAE;AAClB,QAAA,OAAO,CAAA,EAAG,CAAC,CAAA,CAAA,EAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;IAChD;+GAvEW,8BAA8B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAA9B,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,8BAA8B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,8BAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECnC3C,iyEAiEA,EAAA,MAAA,EAAA,CAAA,ozBAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,ED1CQ,UAAU,oOACV,iBAAiB,EAAA,QAAA,EAAA,WAAA,EAAA,MAAA,EAAA,CAAA,MAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACjB,kBAAkB,EAAA,QAAA,EAAA,YAAA,EAAA,MAAA,EAAA,CAAA,SAAA,EAAA,MAAA,EAAA,UAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAClB,gBAAgB,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,MAAA,EAAA,MAAA,EAAA,QAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAChB,8BAA8B,EAAA,QAAA,EAAA,6BAAA,EAAA,MAAA,EAAA,CAAA,QAAA,EAAA,SAAA,EAAA,YAAA,EAAA,OAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,eAAA,CAAA,EAAA,OAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAC9B,uBAAuB,gGACvB,yBAAyB,EAAA,QAAA,EAAA,yBAAA,EAAA,MAAA,EAAA,CAAA,QAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;4FAMpB,8BAA8B,EAAA,UAAA,EAAA,CAAA;kBAf1C,SAAS;AACI,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,8BAA8B,EAAA,OAAA,EAC/B;wBACL,UAAU;wBACV,iBAAiB;wBACjB,kBAAkB;wBAClB,gBAAgB;wBAChB,8BAA8B;wBAC9B,uBAAuB;wBACvB,yBAAyB;qBAC5B,EAAA,eAAA,EAGgB,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,iyEAAA,EAAA,MAAA,EAAA,CAAA,ozBAAA,CAAA,EAAA;;;;;"}
|