@memberjunction/ng-dashboards 5.25.0 → 5.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/dist/AI/components/analytics/agent-runs/agent-run-analysis.component.d.ts +96 -0
  2. package/dist/AI/components/analytics/agent-runs/agent-run-analysis.component.d.ts.map +1 -0
  3. package/dist/AI/components/analytics/agent-runs/agent-run-analysis.component.js +710 -0
  4. package/dist/AI/components/analytics/agent-runs/agent-run-analysis.component.js.map +1 -0
  5. package/dist/AI/components/analytics/ai-analytics-resource.component.d.ts +52 -0
  6. package/dist/AI/components/analytics/ai-analytics-resource.component.d.ts.map +1 -0
  7. package/dist/AI/components/analytics/ai-analytics-resource.component.js +356 -0
  8. package/dist/AI/components/analytics/ai-analytics-resource.component.js.map +1 -0
  9. package/dist/AI/components/analytics/analytics-filter-bar.component.d.ts +52 -0
  10. package/dist/AI/components/analytics/analytics-filter-bar.component.d.ts.map +1 -0
  11. package/dist/AI/components/analytics/analytics-filter-bar.component.js +306 -0
  12. package/dist/AI/components/analytics/analytics-filter-bar.component.js.map +1 -0
  13. package/dist/AI/components/analytics/cost-budget/cost-budget.component.d.ts +81 -0
  14. package/dist/AI/components/analytics/cost-budget/cost-budget.component.d.ts.map +1 -0
  15. package/dist/AI/components/analytics/cost-budget/cost-budget.component.js +744 -0
  16. package/dist/AI/components/analytics/cost-budget/cost-budget.component.js.map +1 -0
  17. package/dist/AI/components/analytics/error-analysis/error-analysis.component.d.ts +61 -0
  18. package/dist/AI/components/analytics/error-analysis/error-analysis.component.d.ts.map +1 -0
  19. package/dist/AI/components/analytics/error-analysis/error-analysis.component.js +490 -0
  20. package/dist/AI/components/analytics/error-analysis/error-analysis.component.js.map +1 -0
  21. package/dist/AI/components/analytics/executive-summary/executive-summary.component.d.ts +77 -0
  22. package/dist/AI/components/analytics/executive-summary/executive-summary.component.d.ts.map +1 -0
  23. package/dist/AI/components/analytics/executive-summary/executive-summary.component.js +673 -0
  24. package/dist/AI/components/analytics/executive-summary/executive-summary.component.js.map +1 -0
  25. package/dist/AI/components/analytics/model-performance/model-performance.component.d.ts +65 -0
  26. package/dist/AI/components/analytics/model-performance/model-performance.component.d.ts.map +1 -0
  27. package/dist/AI/components/analytics/model-performance/model-performance.component.js +537 -0
  28. package/dist/AI/components/analytics/model-performance/model-performance.component.js.map +1 -0
  29. package/dist/AI/components/analytics/prompt-runs/prompt-run-analysis.component.d.ts +131 -0
  30. package/dist/AI/components/analytics/prompt-runs/prompt-run-analysis.component.d.ts.map +1 -0
  31. package/dist/AI/components/analytics/prompt-runs/prompt-run-analysis.component.js +1030 -0
  32. package/dist/AI/components/analytics/prompt-runs/prompt-run-analysis.component.js.map +1 -0
  33. package/dist/AI/components/analytics/usage-patterns/usage-patterns.component.d.ts +78 -0
  34. package/dist/AI/components/analytics/usage-patterns/usage-patterns.component.d.ts.map +1 -0
  35. package/dist/AI/components/analytics/usage-patterns/usage-patterns.component.js +569 -0
  36. package/dist/AI/components/analytics/usage-patterns/usage-patterns.component.js.map +1 -0
  37. package/dist/AI/components/execution-monitoring.component.d.ts.map +1 -1
  38. package/dist/AI/components/execution-monitoring.component.js +4 -14
  39. package/dist/AI/components/execution-monitoring.component.js.map +1 -1
  40. package/dist/AI/components/overview/ai-overview-hub.component.d.ts +58 -0
  41. package/dist/AI/components/overview/ai-overview-hub.component.d.ts.map +1 -0
  42. package/dist/AI/components/overview/ai-overview-hub.component.js +315 -0
  43. package/dist/AI/components/overview/ai-overview-hub.component.js.map +1 -0
  44. package/dist/AI/components/prompts/prompt-management.component.js +1 -1
  45. package/dist/AI/components/prompts/prompt-management.component.js.map +1 -1
  46. package/dist/AI/index.d.ts +11 -0
  47. package/dist/AI/index.d.ts.map +1 -1
  48. package/dist/AI/index.js +13 -0
  49. package/dist/AI/index.js.map +1 -1
  50. package/dist/AI/interfaces/analytics-preferences.interface.d.ts +50 -0
  51. package/dist/AI/interfaces/analytics-preferences.interface.d.ts.map +1 -0
  52. package/dist/AI/interfaces/analytics-preferences.interface.js +9 -0
  53. package/dist/AI/interfaces/analytics-preferences.interface.js.map +1 -0
  54. package/dist/ComponentStudio/components/artifact-load-dialog.component.d.ts.map +1 -1
  55. package/dist/ComponentStudio/components/artifact-load-dialog.component.js +1 -1
  56. package/dist/ComponentStudio/components/artifact-load-dialog.component.js.map +1 -1
  57. package/dist/Home/home-dashboard.component.js +2 -2
  58. package/dist/MCP/index.d.ts +1 -0
  59. package/dist/MCP/index.d.ts.map +1 -1
  60. package/dist/MCP/index.js +2 -0
  61. package/dist/MCP/index.js.map +1 -1
  62. package/dist/MCP/mcp-dashboard.component.d.ts +1 -0
  63. package/dist/MCP/mcp-dashboard.component.d.ts.map +1 -1
  64. package/dist/MCP/mcp-dashboard.component.js +5 -4
  65. package/dist/MCP/mcp-dashboard.component.js.map +1 -1
  66. package/dist/MCP/mcp-resource.component.d.ts +6 -5
  67. package/dist/MCP/mcp-resource.component.d.ts.map +1 -1
  68. package/dist/MCP/mcp-resource.component.js +7 -8
  69. package/dist/MCP/mcp-resource.component.js.map +1 -1
  70. package/dist/ai-dashboards.module.d.ts +27 -17
  71. package/dist/ai-dashboards.module.d.ts.map +1 -1
  72. package/dist/ai-dashboards.module.js +66 -3
  73. package/dist/ai-dashboards.module.js.map +1 -1
  74. package/dist/public-api.d.ts +1 -1
  75. package/dist/public-api.d.ts.map +1 -1
  76. package/dist/public-api.js +1 -1
  77. package/dist/public-api.js.map +1 -1
  78. package/package.json +49 -48
  79. package/dist/__tests__/analytics-resource.test.d.ts +0 -2
  80. package/dist/__tests__/analytics-resource.test.d.ts.map +0 -1
  81. package/dist/__tests__/analytics-resource.test.js +0 -181
  82. package/dist/__tests__/analytics-resource.test.js.map +0 -1
  83. package/dist/__tests__/dashboards.test.d.ts +0 -2
  84. package/dist/__tests__/dashboards.test.d.ts.map +0 -1
  85. package/dist/__tests__/dashboards.test.js +0 -40
  86. package/dist/__tests__/dashboards.test.js.map +0 -1
  87. package/dist/__tests__/integration-data-service.test.d.ts +0 -2
  88. package/dist/__tests__/integration-data-service.test.d.ts.map +0 -1
  89. package/dist/__tests__/integration-data-service.test.js +0 -132
  90. package/dist/__tests__/integration-data-service.test.js.map +0 -1
  91. package/dist/__tests__/mapping-validation.test.d.ts +0 -2
  92. package/dist/__tests__/mapping-validation.test.d.ts.map +0 -1
  93. package/dist/__tests__/mapping-validation.test.js +0 -170
  94. package/dist/__tests__/mapping-validation.test.js.map +0 -1
  95. package/dist/__tests__/scheduling.test.d.ts +0 -2
  96. package/dist/__tests__/scheduling.test.d.ts.map +0 -1
  97. package/dist/__tests__/scheduling.test.js +0 -205
  98. package/dist/__tests__/scheduling.test.js.map +0 -1
@@ -0,0 +1,744 @@
1
+ /**
2
+ * @fileoverview Cost & Budget Analytics -- Option B.
3
+ *
4
+ * Displays cost KPIs with period deltas, a daily cost bar chart with anomaly detection,
5
+ * a cost breakdown treemap, and a detailed cost-by-model table with CSV export.
6
+ */
7
+ import { Component, Input, Output, EventEmitter, ChangeDetectorRef, inject } from '@angular/core';
8
+ import { Subject } from 'rxjs';
9
+ import { RunView } from '@memberjunction/core';
10
+ import * as i0 from "@angular/core";
11
+ import * as i1 from "@memberjunction/ng-shared-generic";
12
+ import * as i2 from "../analytics-filter-bar.component";
13
+ import * as i3 from "@angular/common";
14
+ const _forTrack0 = ($index, $item) => $item.Label;
15
+ const _forTrack1 = ($index, $item) => $item.Model;
16
+ const _forTrack2 = ($index, $item) => $item.Date;
17
+ function AnalyticsCostBudgetComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
18
+ i0.ɵɵelementStart(0, "div", 1);
19
+ i0.ɵɵelement(1, "mj-loading", 2);
20
+ i0.ɵɵelementEnd();
21
+ } }
22
+ function AnalyticsCostBudgetComponent_Conditional_2_For_2_Conditional_8_Conditional_1_Template(rf, ctx) { if (rf & 1) {
23
+ i0.ɵɵelement(0, "i", 31);
24
+ } }
25
+ function AnalyticsCostBudgetComponent_Conditional_2_For_2_Conditional_8_Conditional_2_Template(rf, ctx) { if (rf & 1) {
26
+ i0.ɵɵelement(0, "i", 32);
27
+ } }
28
+ function AnalyticsCostBudgetComponent_Conditional_2_For_2_Conditional_8_Template(rf, ctx) { if (rf & 1) {
29
+ i0.ɵɵelementStart(0, "div", 30);
30
+ i0.ɵɵconditionalCreate(1, AnalyticsCostBudgetComponent_Conditional_2_For_2_Conditional_8_Conditional_1_Template, 1, 0, "i", 31)(2, AnalyticsCostBudgetComponent_Conditional_2_For_2_Conditional_8_Conditional_2_Template, 1, 0, "i", 32);
31
+ i0.ɵɵtext(3);
32
+ i0.ɵɵpipe(4, "number");
33
+ i0.ɵɵelementEnd();
34
+ } if (rf & 2) {
35
+ const kpi_r2 = i0.ɵɵnextContext().$implicit;
36
+ i0.ɵɵclassProp("kpi-delta--up", kpi_r2.DeltaDirection === "up")("kpi-delta--down", kpi_r2.DeltaDirection === "down");
37
+ i0.ɵɵadvance();
38
+ i0.ɵɵconditional(kpi_r2.DeltaDirection === "up" ? 1 : kpi_r2.DeltaDirection === "down" ? 2 : -1);
39
+ i0.ɵɵadvance(2);
40
+ i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind2(4, 6, kpi_r2.Delta, "1.1-1"), "% vs prev ");
41
+ } }
42
+ function AnalyticsCostBudgetComponent_Conditional_2_For_2_Template(rf, ctx) { if (rf & 1) {
43
+ i0.ɵɵelementStart(0, "div", 24)(1, "div", 25);
44
+ i0.ɵɵelement(2, "i");
45
+ i0.ɵɵelementEnd();
46
+ i0.ɵɵelementStart(3, "div", 26)(4, "div", 27);
47
+ i0.ɵɵtext(5);
48
+ i0.ɵɵelementEnd();
49
+ i0.ɵɵelementStart(6, "div", 28);
50
+ i0.ɵɵtext(7);
51
+ i0.ɵɵelementEnd();
52
+ i0.ɵɵconditionalCreate(8, AnalyticsCostBudgetComponent_Conditional_2_For_2_Conditional_8_Template, 5, 9, "div", 29);
53
+ i0.ɵɵelementEnd()();
54
+ } if (rf & 2) {
55
+ const kpi_r2 = ctx.$implicit;
56
+ i0.ɵɵclassProp("kpi-card--highlighted", kpi_r2.Highlighted);
57
+ i0.ɵɵadvance(2);
58
+ i0.ɵɵclassMap(kpi_r2.Icon);
59
+ i0.ɵɵadvance(3);
60
+ i0.ɵɵtextInterpolate(kpi_r2.Label);
61
+ i0.ɵɵadvance(2);
62
+ i0.ɵɵtextInterpolate(kpi_r2.Value);
63
+ i0.ɵɵadvance();
64
+ i0.ɵɵconditional(kpi_r2.Delta != null ? 8 : -1);
65
+ } }
66
+ function AnalyticsCostBudgetComponent_Conditional_2_Conditional_10_Template(rf, ctx) { if (rf & 1) {
67
+ i0.ɵɵelementStart(0, "div", 11);
68
+ i0.ɵɵtext(1, "No cost data for selected period");
69
+ i0.ɵɵelementEnd();
70
+ } }
71
+ function AnalyticsCostBudgetComponent_Conditional_2_Conditional_11_Conditional_2_Template(rf, ctx) { if (rf & 1) {
72
+ i0.ɵɵelementStart(0, "div", 36)(1, "span", 37);
73
+ i0.ɵɵtext(2, "avg");
74
+ i0.ɵɵelementEnd()();
75
+ } if (rf & 2) {
76
+ const ctx_r2 = i0.ɵɵnextContext(3);
77
+ i0.ɵɵstyleProp("bottom", ctx_r2.AvgLinePercent, "%");
78
+ } }
79
+ function AnalyticsCostBudgetComponent_Conditional_2_Conditional_11_For_4_Template(rf, ctx) { if (rf & 1) {
80
+ i0.ɵɵelementStart(0, "div", 35);
81
+ i0.ɵɵelement(1, "div", 38);
82
+ i0.ɵɵelementStart(2, "div", 39);
83
+ i0.ɵɵtext(3);
84
+ i0.ɵɵelementEnd()();
85
+ } if (rf & 2) {
86
+ const bar_r4 = ctx.$implicit;
87
+ const ctx_r2 = i0.ɵɵnextContext(3);
88
+ i0.ɵɵproperty("title", bar_r4.Label + ": " + ctx_r2.FormatCurrency(bar_r4.Cost));
89
+ i0.ɵɵadvance();
90
+ i0.ɵɵstyleProp("height", bar_r4.HeightPercent, "%");
91
+ i0.ɵɵclassProp("bar--anomaly", bar_r4.IsAnomaly);
92
+ i0.ɵɵadvance(2);
93
+ i0.ɵɵtextInterpolate(bar_r4.Label);
94
+ } }
95
+ function AnalyticsCostBudgetComponent_Conditional_2_Conditional_11_Template(rf, ctx) { if (rf & 1) {
96
+ i0.ɵɵelementStart(0, "div", 12)(1, "div", 33);
97
+ i0.ɵɵconditionalCreate(2, AnalyticsCostBudgetComponent_Conditional_2_Conditional_11_Conditional_2_Template, 3, 2, "div", 34);
98
+ i0.ɵɵrepeaterCreate(3, AnalyticsCostBudgetComponent_Conditional_2_Conditional_11_For_4_Template, 4, 6, "div", 35, _forTrack2);
99
+ i0.ɵɵelementEnd()();
100
+ } if (rf & 2) {
101
+ const ctx_r2 = i0.ɵɵnextContext(2);
102
+ i0.ɵɵadvance(2);
103
+ i0.ɵɵconditional(ctx_r2.AvgLinePercent > 0 ? 2 : -1);
104
+ i0.ɵɵadvance();
105
+ i0.ɵɵrepeater(ctx_r2.DailyBars);
106
+ } }
107
+ function AnalyticsCostBudgetComponent_Conditional_2_Conditional_18_Template(rf, ctx) { if (rf & 1) {
108
+ i0.ɵɵelementStart(0, "div", 11);
109
+ i0.ɵɵtext(1, "No vendor cost data");
110
+ i0.ɵɵelementEnd();
111
+ } }
112
+ function AnalyticsCostBudgetComponent_Conditional_2_Conditional_19_For_2_Template(rf, ctx) { if (rf & 1) {
113
+ i0.ɵɵelementStart(0, "div", 41);
114
+ i0.ɵɵpipe(1, "number");
115
+ i0.ɵɵelementStart(2, "span", 42);
116
+ i0.ɵɵtext(3);
117
+ i0.ɵɵelementEnd();
118
+ i0.ɵɵelementStart(4, "span", 43);
119
+ i0.ɵɵtext(5);
120
+ i0.ɵɵelementEnd();
121
+ i0.ɵɵelementStart(6, "span", 44);
122
+ i0.ɵɵtext(7);
123
+ i0.ɵɵpipe(8, "number");
124
+ i0.ɵɵelementEnd()();
125
+ } if (rf & 2) {
126
+ const cell_r5 = ctx.$implicit;
127
+ const ctx_r2 = i0.ɵɵnextContext(3);
128
+ i0.ɵɵstyleProp("background", cell_r5.Color)("flex-basis", cell_r5.Percent, "%");
129
+ i0.ɵɵproperty("title", cell_r5.Label + ": " + ctx_r2.FormatCurrency(cell_r5.Cost) + " (" + i0.ɵɵpipeBind2(1, 8, cell_r5.Percent, "1.0-0") + "%)");
130
+ i0.ɵɵadvance(3);
131
+ i0.ɵɵtextInterpolate(cell_r5.Label);
132
+ i0.ɵɵadvance(2);
133
+ i0.ɵɵtextInterpolate(ctx_r2.FormatCurrency(cell_r5.Cost));
134
+ i0.ɵɵadvance(2);
135
+ i0.ɵɵtextInterpolate1("", i0.ɵɵpipeBind2(8, 11, cell_r5.Percent, "1.0-0"), "%");
136
+ } }
137
+ function AnalyticsCostBudgetComponent_Conditional_2_Conditional_19_Template(rf, ctx) { if (rf & 1) {
138
+ i0.ɵɵelementStart(0, "div", 16);
139
+ i0.ɵɵrepeaterCreate(1, AnalyticsCostBudgetComponent_Conditional_2_Conditional_19_For_2_Template, 9, 14, "div", 40, _forTrack0);
140
+ i0.ɵɵelementEnd();
141
+ } if (rf & 2) {
142
+ const ctx_r2 = i0.ɵɵnextContext(2);
143
+ i0.ɵɵadvance();
144
+ i0.ɵɵrepeater(ctx_r2.TreemapCells);
145
+ } }
146
+ function AnalyticsCostBudgetComponent_Conditional_2_Conditional_51_Template(rf, ctx) { if (rf & 1) {
147
+ i0.ɵɵelementStart(0, "tr")(1, "td", 45);
148
+ i0.ɵɵtext(2, "No data available");
149
+ i0.ɵɵelementEnd()();
150
+ } }
151
+ function AnalyticsCostBudgetComponent_Conditional_2_For_53_Template(rf, ctx) { if (rf & 1) {
152
+ i0.ɵɵelementStart(0, "tr")(1, "td", 46);
153
+ i0.ɵɵtext(2);
154
+ i0.ɵɵelementEnd();
155
+ i0.ɵɵelementStart(3, "td", 47);
156
+ i0.ɵɵtext(4);
157
+ i0.ɵɵelementEnd();
158
+ i0.ɵɵelementStart(5, "td", 48);
159
+ i0.ɵɵtext(6);
160
+ i0.ɵɵpipe(7, "number");
161
+ i0.ɵɵelementEnd();
162
+ i0.ɵɵelementStart(8, "td", 48);
163
+ i0.ɵɵtext(9);
164
+ i0.ɵɵpipe(10, "number");
165
+ i0.ɵɵelementEnd();
166
+ i0.ɵɵelementStart(11, "td", 48);
167
+ i0.ɵɵtext(12);
168
+ i0.ɵɵpipe(13, "number");
169
+ i0.ɵɵelementEnd();
170
+ i0.ɵɵelementStart(14, "td", 48);
171
+ i0.ɵɵtext(15);
172
+ i0.ɵɵelementEnd();
173
+ i0.ɵɵelementStart(16, "td", 48);
174
+ i0.ɵɵtext(17);
175
+ i0.ɵɵelementEnd();
176
+ i0.ɵɵelementStart(18, "td", 49);
177
+ i0.ɵɵtext(19);
178
+ i0.ɵɵelementEnd();
179
+ i0.ɵɵelementStart(20, "td", 48);
180
+ i0.ɵɵtext(21);
181
+ i0.ɵɵpipe(22, "number");
182
+ i0.ɵɵelementEnd()();
183
+ } if (rf & 2) {
184
+ const row_r6 = ctx.$implicit;
185
+ const ctx_r2 = i0.ɵɵnextContext(2);
186
+ i0.ɵɵadvance(2);
187
+ i0.ɵɵtextInterpolate(row_r6.Model);
188
+ i0.ɵɵadvance(2);
189
+ i0.ɵɵtextInterpolate(row_r6.Vendor);
190
+ i0.ɵɵadvance(2);
191
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(7, 9, row_r6.Runs));
192
+ i0.ɵɵadvance(3);
193
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(10, 11, row_r6.InputTokens));
194
+ i0.ɵɵadvance(3);
195
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(13, 13, row_r6.OutputTokens));
196
+ i0.ɵɵadvance(3);
197
+ i0.ɵɵtextInterpolate(ctx_r2.FormatCurrency(row_r6.InputCost, 4));
198
+ i0.ɵɵadvance(2);
199
+ i0.ɵɵtextInterpolate(ctx_r2.FormatCurrency(row_r6.OutputCost, 4));
200
+ i0.ɵɵadvance(2);
201
+ i0.ɵɵtextInterpolate(ctx_r2.FormatCurrency(row_r6.TotalCost));
202
+ i0.ɵɵadvance(2);
203
+ i0.ɵɵtextInterpolate1("", i0.ɵɵpipeBind2(22, 15, row_r6.PercentOfTotal, "1.1-1"), "%");
204
+ } }
205
+ function AnalyticsCostBudgetComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
206
+ const _r1 = i0.ɵɵgetCurrentView();
207
+ i0.ɵɵelementStart(0, "div", 3);
208
+ i0.ɵɵrepeaterCreate(1, AnalyticsCostBudgetComponent_Conditional_2_For_2_Template, 9, 7, "div", 4, _forTrack0);
209
+ i0.ɵɵelementEnd();
210
+ i0.ɵɵelementStart(3, "div", 5)(4, "div", 6)(5, "div", 7)(6, "div", 8);
211
+ i0.ɵɵelement(7, "i", 9);
212
+ i0.ɵɵtext(8, " Daily Cost Trend ");
213
+ i0.ɵɵelementEnd()();
214
+ i0.ɵɵelementStart(9, "div", 10);
215
+ i0.ɵɵconditionalCreate(10, AnalyticsCostBudgetComponent_Conditional_2_Conditional_10_Template, 2, 0, "div", 11)(11, AnalyticsCostBudgetComponent_Conditional_2_Conditional_11_Template, 5, 1, "div", 12);
216
+ i0.ɵɵelementEnd()();
217
+ i0.ɵɵelementStart(12, "div", 13)(13, "div", 7)(14, "div", 8);
218
+ i0.ɵɵelement(15, "i", 14);
219
+ i0.ɵɵtext(16, " Cost by Vendor ");
220
+ i0.ɵɵelementEnd()();
221
+ i0.ɵɵelementStart(17, "div", 15);
222
+ i0.ɵɵconditionalCreate(18, AnalyticsCostBudgetComponent_Conditional_2_Conditional_18_Template, 2, 0, "div", 11)(19, AnalyticsCostBudgetComponent_Conditional_2_Conditional_19_Template, 3, 0, "div", 16);
223
+ i0.ɵɵelementEnd()()();
224
+ i0.ɵɵelementStart(20, "div", 17)(21, "div", 7)(22, "div", 8);
225
+ i0.ɵɵelement(23, "i", 18);
226
+ i0.ɵɵtext(24, " Cost by Model ");
227
+ i0.ɵɵelementEnd();
228
+ i0.ɵɵelementStart(25, "button", 19);
229
+ i0.ɵɵlistener("click", function AnalyticsCostBudgetComponent_Conditional_2_Template_button_click_25_listener() { i0.ɵɵrestoreView(_r1); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.ExportCSV()); });
230
+ i0.ɵɵelement(26, "i", 20);
231
+ i0.ɵɵtext(27, " Export CSV ");
232
+ i0.ɵɵelementEnd()();
233
+ i0.ɵɵelementStart(28, "div", 21)(29, "table", 22)(30, "thead")(31, "tr")(32, "th");
234
+ i0.ɵɵtext(33, "Model");
235
+ i0.ɵɵelementEnd();
236
+ i0.ɵɵelementStart(34, "th");
237
+ i0.ɵɵtext(35, "Vendor");
238
+ i0.ɵɵelementEnd();
239
+ i0.ɵɵelementStart(36, "th", 23);
240
+ i0.ɵɵtext(37, "Runs");
241
+ i0.ɵɵelementEnd();
242
+ i0.ɵɵelementStart(38, "th", 23);
243
+ i0.ɵɵtext(39, "Input Tokens");
244
+ i0.ɵɵelementEnd();
245
+ i0.ɵɵelementStart(40, "th", 23);
246
+ i0.ɵɵtext(41, "Output Tokens");
247
+ i0.ɵɵelementEnd();
248
+ i0.ɵɵelementStart(42, "th", 23);
249
+ i0.ɵɵtext(43, "Input Cost");
250
+ i0.ɵɵelementEnd();
251
+ i0.ɵɵelementStart(44, "th", 23);
252
+ i0.ɵɵtext(45, "Output Cost");
253
+ i0.ɵɵelementEnd();
254
+ i0.ɵɵelementStart(46, "th", 23);
255
+ i0.ɵɵtext(47, "Total Cost");
256
+ i0.ɵɵelementEnd();
257
+ i0.ɵɵelementStart(48, "th", 23);
258
+ i0.ɵɵtext(49, "% of Total");
259
+ i0.ɵɵelementEnd()()();
260
+ i0.ɵɵelementStart(50, "tbody");
261
+ i0.ɵɵconditionalCreate(51, AnalyticsCostBudgetComponent_Conditional_2_Conditional_51_Template, 3, 0, "tr");
262
+ i0.ɵɵrepeaterCreate(52, AnalyticsCostBudgetComponent_Conditional_2_For_53_Template, 23, 18, "tr", null, _forTrack1);
263
+ i0.ɵɵelementEnd()()()();
264
+ } if (rf & 2) {
265
+ const ctx_r2 = i0.ɵɵnextContext();
266
+ i0.ɵɵadvance();
267
+ i0.ɵɵrepeater(ctx_r2.CostKpis);
268
+ i0.ɵɵadvance(9);
269
+ i0.ɵɵconditional(ctx_r2.DailyBars.length === 0 ? 10 : 11);
270
+ i0.ɵɵadvance(8);
271
+ i0.ɵɵconditional(ctx_r2.TreemapCells.length === 0 ? 18 : 19);
272
+ i0.ɵɵadvance(33);
273
+ i0.ɵɵconditional(ctx_r2.CostByModelRows.length === 0 ? 51 : -1);
274
+ i0.ɵɵadvance();
275
+ i0.ɵɵrepeater(ctx_r2.CostByModelRows);
276
+ } }
277
+ const FIELDS = [
278
+ 'ID', 'RunAt', 'Cost', 'TotalCost', 'TokensPrompt', 'TokensCompletion',
279
+ 'TokensUsed', 'ModelID', 'Model', 'VendorID', 'Vendor', 'Success'
280
+ ];
281
+ const TIME_RANGE_OPTIONS = ['Today', '7d', '30d', 'MTD'];
282
+ const TREEMAP_COLORS = [
283
+ 'var(--mj-brand-primary)',
284
+ 'var(--mj-brand-accent, var(--mj-brand-primary-hover))',
285
+ 'var(--mj-status-info)',
286
+ 'var(--mj-status-success)',
287
+ 'var(--mj-status-warning)',
288
+ 'var(--mj-text-disabled)'
289
+ ];
290
+ export class AnalyticsCostBudgetComponent {
291
+ TimeRange = '7d';
292
+ Filters = { Models: [], Agents: [], Prompts: [], Statuses: [] };
293
+ TimeRangeChange = new EventEmitter();
294
+ FiltersChange = new EventEmitter();
295
+ cdr = inject(ChangeDetectorRef);
296
+ destroy$ = new Subject();
297
+ IsLoading = false;
298
+ TimeRangeOptionsList = TIME_RANGE_OPTIONS;
299
+ CostKpis = [];
300
+ DailyBars = [];
301
+ AvgLinePercent = 0;
302
+ TreemapCells = [];
303
+ CostByModelRows = [];
304
+ allRuns = [];
305
+ previousPeriodRuns = [];
306
+ ngOnInit() {
307
+ this.LoadData();
308
+ }
309
+ ngOnDestroy() {
310
+ this.destroy$.next();
311
+ this.destroy$.complete();
312
+ }
313
+ // ── Public Handlers ──
314
+ OnTimeRangeChange(range) {
315
+ this.TimeRange = range;
316
+ this.TimeRangeChange.emit(range);
317
+ this.LoadData();
318
+ }
319
+ OnFiltersChange(filters) {
320
+ this.Filters = filters;
321
+ this.FiltersChange.emit(filters);
322
+ this.LoadData();
323
+ }
324
+ FormatCurrency(value, decimals = 2) {
325
+ if (value === 0)
326
+ return '$0.00';
327
+ if (value < 0.01 && decimals < 4)
328
+ decimals = 4;
329
+ return '$' + value.toFixed(decimals);
330
+ }
331
+ ExportCSV() {
332
+ const header = 'Model,Vendor,Runs,Input Tokens,Output Tokens,Input Cost,Output Cost,Total Cost,% of Total';
333
+ const rows = this.CostByModelRows.map(r => `"${r.Model}","${r.Vendor}",${r.Runs},${r.InputTokens},${r.OutputTokens},${r.InputCost.toFixed(6)},${r.OutputCost.toFixed(6)},${r.TotalCost.toFixed(6)},${r.PercentOfTotal.toFixed(1)}`);
334
+ const csv = [header, ...rows].join('\n');
335
+ this.downloadCSV(csv, 'cost-by-model.csv');
336
+ }
337
+ // ── Data Loading ──
338
+ async LoadData() {
339
+ this.IsLoading = true;
340
+ this.cdr.detectChanges();
341
+ try {
342
+ const rv = new RunView();
343
+ const { currentStart, previousStart } = this.getDateBounds();
344
+ const now = new Date();
345
+ const modelFilter = this.buildModelFilter();
346
+ const currentFilter = this.combineDateAndModelFilter(currentStart, now, modelFilter);
347
+ const prevFilter = this.combineDateAndModelFilter(previousStart, currentStart, modelFilter);
348
+ const [currentResult, prevResult] = await rv.RunViews([
349
+ {
350
+ EntityName: 'MJ: AI Prompt Runs',
351
+ ExtraFilter: currentFilter,
352
+ Fields: FIELDS,
353
+ OrderBy: 'RunAt ASC',
354
+ ResultType: 'simple'
355
+ },
356
+ {
357
+ EntityName: 'MJ: AI Prompt Runs',
358
+ ExtraFilter: prevFilter,
359
+ Fields: FIELDS,
360
+ OrderBy: 'RunAt ASC',
361
+ ResultType: 'simple'
362
+ }
363
+ ]);
364
+ this.allRuns = (currentResult?.Results ?? []);
365
+ this.previousPeriodRuns = (prevResult?.Results ?? []);
366
+ this.computeKpis();
367
+ this.computeDailyBars();
368
+ this.computeTreemap();
369
+ this.computeCostByModel();
370
+ }
371
+ catch (e) {
372
+ console.error('Cost & Budget load error:', e);
373
+ }
374
+ finally {
375
+ this.IsLoading = false;
376
+ this.cdr.detectChanges();
377
+ }
378
+ }
379
+ // ── Computations ──
380
+ computeKpis() {
381
+ const now = new Date();
382
+ const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
383
+ const weekStart = new Date(todayStart.getTime() - 6 * 86400000);
384
+ const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
385
+ const todaySpend = this.sumCostInRange(this.allRuns, todayStart, now);
386
+ const weekSpend = this.sumCostInRange(this.allRuns, weekStart, now);
387
+ const monthSpend = this.sumCostInRange(this.allRuns, monthStart, now);
388
+ const prevTotalCost = this.previousPeriodRuns.reduce((s, r) => s + (r.Cost ?? r.TotalCost ?? 0), 0);
389
+ const currentTotalCost = this.allRuns.reduce((s, r) => s + (r.Cost ?? r.TotalCost ?? 0), 0);
390
+ // Project monthly cost based on current daily average
391
+ const daysIntoMonth = Math.max(1, now.getDate());
392
+ const projectedMonthly = (monthSpend / daysIntoMonth) * new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
393
+ const delta = prevTotalCost > 0 ? ((currentTotalCost - prevTotalCost) / prevTotalCost) * 100 : null;
394
+ this.CostKpis = [
395
+ {
396
+ Label: "Today's Spend",
397
+ Value: this.FormatCurrency(todaySpend),
398
+ Delta: null,
399
+ DeltaDirection: 'stable',
400
+ Highlighted: false,
401
+ Icon: 'fa-solid fa-calendar-day'
402
+ },
403
+ {
404
+ Label: 'This Week',
405
+ Value: this.FormatCurrency(weekSpend),
406
+ Delta: null,
407
+ DeltaDirection: 'stable',
408
+ Highlighted: false,
409
+ Icon: 'fa-solid fa-calendar-week'
410
+ },
411
+ {
412
+ Label: 'This Month',
413
+ Value: this.FormatCurrency(monthSpend),
414
+ Delta: delta,
415
+ DeltaDirection: delta != null ? (delta > 0 ? 'up' : delta < 0 ? 'down' : 'stable') : 'stable',
416
+ Highlighted: false,
417
+ Icon: 'fa-solid fa-calendar'
418
+ },
419
+ {
420
+ Label: 'Projected Monthly',
421
+ Value: this.FormatCurrency(projectedMonthly),
422
+ Delta: null,
423
+ DeltaDirection: 'stable',
424
+ Highlighted: true,
425
+ Icon: 'fa-solid fa-chart-line'
426
+ }
427
+ ];
428
+ }
429
+ computeDailyBars() {
430
+ const buckets = new Map();
431
+ for (const run of this.allRuns) {
432
+ const date = new Date(run.RunAt);
433
+ const key = date.toISOString().slice(0, 10);
434
+ buckets.set(key, (buckets.get(key) ?? 0) + (run.Cost ?? run.TotalCost ?? 0));
435
+ }
436
+ const sortedKeys = Array.from(buckets.keys()).sort();
437
+ const values = sortedKeys.map(k => buckets.get(k) ?? 0);
438
+ const maxVal = Math.max(...values, 0.001);
439
+ // Anomaly detection: > 2 standard deviations from mean
440
+ const mean = values.length > 0 ? values.reduce((s, v) => s + v, 0) / values.length : 0;
441
+ const variance = values.length > 1
442
+ ? values.reduce((s, v) => s + Math.pow(v - mean, 2), 0) / values.length
443
+ : 0;
444
+ const stdDev = Math.sqrt(variance);
445
+ const anomalyThreshold = mean + 2 * stdDev;
446
+ this.DailyBars = sortedKeys.map((key, i) => ({
447
+ Date: key,
448
+ Label: this.formatBarLabel(key),
449
+ Cost: values[i],
450
+ HeightPercent: maxVal > 0 ? (values[i] / maxVal) * 100 : 0,
451
+ IsAnomaly: stdDev > 0 && values[i] > anomalyThreshold
452
+ }));
453
+ this.AvgLinePercent = maxVal > 0 ? (mean / maxVal) * 100 : 0;
454
+ }
455
+ computeTreemap() {
456
+ const vendorCosts = new Map();
457
+ for (const run of this.allRuns) {
458
+ const vendor = run.Vendor ?? 'Other';
459
+ vendorCosts.set(vendor, (vendorCosts.get(vendor) ?? 0) + (run.Cost ?? run.TotalCost ?? 0));
460
+ }
461
+ const total = Array.from(vendorCosts.values()).reduce((s, v) => s + v, 0);
462
+ const sorted = Array.from(vendorCosts.entries()).sort((a, b) => b[1] - a[1]);
463
+ this.TreemapCells = sorted.map(([vendor, cost], i) => ({
464
+ Label: vendor,
465
+ Cost: cost,
466
+ Percent: total > 0 ? (cost / total) * 100 : 0,
467
+ Color: TREEMAP_COLORS[i % TREEMAP_COLORS.length],
468
+ GridArea: ''
469
+ }));
470
+ }
471
+ computeCostByModel() {
472
+ const groups = new Map();
473
+ for (const run of this.allRuns) {
474
+ const key = run.ModelID ?? 'unknown';
475
+ if (!groups.has(key))
476
+ groups.set(key, []);
477
+ groups.get(key).push(run);
478
+ }
479
+ const totalCost = this.allRuns.reduce((s, r) => s + (r.Cost ?? r.TotalCost ?? 0), 0);
480
+ const rows = [];
481
+ for (const [, modelRuns] of groups) {
482
+ const inputTokens = modelRuns.reduce((s, r) => s + (r.TokensPrompt ?? 0), 0);
483
+ const outputTokens = modelRuns.reduce((s, r) => s + (r.TokensCompletion ?? 0), 0);
484
+ const cost = modelRuns.reduce((s, r) => s + (r.Cost ?? r.TotalCost ?? 0), 0);
485
+ // Approximate input/output cost split based on token ratio
486
+ const totalTk = inputTokens + outputTokens;
487
+ const inputCost = totalTk > 0 ? cost * (inputTokens / totalTk) : 0;
488
+ const outputCost = totalTk > 0 ? cost * (outputTokens / totalTk) : 0;
489
+ rows.push({
490
+ Model: modelRuns[0].Model ?? 'Unknown',
491
+ Vendor: modelRuns[0].Vendor ?? 'Unknown',
492
+ Runs: modelRuns.length,
493
+ InputTokens: inputTokens,
494
+ OutputTokens: outputTokens,
495
+ InputCost: inputCost,
496
+ OutputCost: outputCost,
497
+ TotalCost: cost,
498
+ PercentOfTotal: totalCost > 0 ? (cost / totalCost) * 100 : 0
499
+ });
500
+ }
501
+ this.CostByModelRows = rows.sort((a, b) => b.TotalCost - a.TotalCost);
502
+ }
503
+ // ── Helpers ──
504
+ sumCostInRange(runs, start, end) {
505
+ return runs
506
+ .filter(r => {
507
+ const d = new Date(r.RunAt);
508
+ return d >= start && d <= end;
509
+ })
510
+ .reduce((s, r) => s + (r.Cost ?? r.TotalCost ?? 0), 0);
511
+ }
512
+ getDateBounds() {
513
+ const now = new Date();
514
+ const msMap = {
515
+ 'Today': 86400000,
516
+ '7d': 604800000,
517
+ '30d': 2592000000,
518
+ 'MTD': now.getDate() * 86400000
519
+ };
520
+ const ms = msMap[this.TimeRange] ?? 604800000;
521
+ const currentStart = new Date(now.getTime() - ms);
522
+ const previousStart = new Date(currentStart.getTime() - ms);
523
+ return { currentStart, previousStart };
524
+ }
525
+ buildModelFilter() {
526
+ if (this.Filters.Models.length === 0)
527
+ return '';
528
+ const ids = this.Filters.Models.map(id => `'${id}'`).join(',');
529
+ return `ModelID IN (${ids})`;
530
+ }
531
+ combineDateAndModelFilter(start, end, modelFilter) {
532
+ const parts = [
533
+ `RunAt >= '${start.toISOString()}'`,
534
+ `RunAt <= '${end.toISOString()}'`
535
+ ];
536
+ if (modelFilter)
537
+ parts.push(modelFilter);
538
+ return parts.join(' AND ');
539
+ }
540
+ formatBarLabel(dateStr) {
541
+ const d = new Date(dateStr + 'T00:00:00');
542
+ const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
543
+ return months[d.getMonth()] + ' ' + d.getDate();
544
+ }
545
+ downloadCSV(csv, filename) {
546
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
547
+ const url = URL.createObjectURL(blob);
548
+ const link = document.createElement('a');
549
+ link.href = url;
550
+ link.download = filename;
551
+ link.click();
552
+ URL.revokeObjectURL(url);
553
+ }
554
+ static ɵfac = function AnalyticsCostBudgetComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || AnalyticsCostBudgetComponent)(); };
555
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AnalyticsCostBudgetComponent, selectors: [["app-analytics-cost-budget"]], inputs: { TimeRange: "TimeRange", Filters: "Filters" }, outputs: { TimeRangeChange: "TimeRangeChange", FiltersChange: "FiltersChange" }, standalone: false, decls: 3, vars: 10, consts: [[3, "TimeRangeChange", "FiltersChange", "TimeRange", "TimeRangeOptions", "Filters", "ShowAgentFilter", "ShowPromptFilter", "ShowModelFilter", "ShowStatusFilter", "ShowCompareToggle", "ShowExportButton"], [1, "loading-container"], ["text", "Loading cost data..."], [1, "kpi-row"], [1, "kpi-card", 3, "kpi-card--highlighted"], [1, "two-col"], [1, "panel", "panel-chart"], [1, "panel-header"], [1, "panel-header__title"], [1, "fa-solid", "fa-chart-column", "panel-header__icon"], [1, "chart-body"], [1, "panel-empty"], [1, "bar-chart"], [1, "panel", "panel-treemap"], [1, "fa-solid", "fa-chart-pie", "panel-header__icon"], [1, "treemap-body"], [1, "treemap-grid"], [1, "panel"], [1, "fa-solid", "fa-table", "panel-header__icon"], [1, "export-btn", 3, "click"], [1, "fa-solid", "fa-download"], [1, "table-wrapper"], [1, "data-table"], [1, "col-numeric"], [1, "kpi-card"], [1, "kpi-icon"], [1, "kpi-content"], [1, "kpi-label"], [1, "kpi-value"], [1, "kpi-delta", 3, "kpi-delta--up", "kpi-delta--down"], [1, "kpi-delta"], [1, "fa-solid", "fa-arrow-up"], [1, "fa-solid", "fa-arrow-down"], [1, "bar-chart-area"], [1, "avg-line", 3, "bottom"], [1, "bar-col", 3, "title"], [1, "avg-line"], [1, "avg-label"], [1, "bar"], [1, "bar-label"], [1, "treemap-cell", 3, "background", "flex-basis", "title"], [1, "treemap-cell", 3, "title"], [1, "treemap-label"], [1, "treemap-value"], [1, "treemap-pct"], ["colspan", "9", 1, "empty-row"], [1, "cell-model"], [1, "cell-vendor"], [1, "cell-numeric"], [1, "cell-numeric", "cell-cost"]], template: function AnalyticsCostBudgetComponent_Template(rf, ctx) { if (rf & 1) {
556
+ i0.ɵɵelementStart(0, "app-analytics-filter-bar", 0);
557
+ i0.ɵɵlistener("TimeRangeChange", function AnalyticsCostBudgetComponent_Template_app_analytics_filter_bar_TimeRangeChange_0_listener($event) { return ctx.OnTimeRangeChange($event); })("FiltersChange", function AnalyticsCostBudgetComponent_Template_app_analytics_filter_bar_FiltersChange_0_listener($event) { return ctx.OnFiltersChange($event); });
558
+ i0.ɵɵelementEnd();
559
+ i0.ɵɵconditionalCreate(1, AnalyticsCostBudgetComponent_Conditional_1_Template, 2, 0, "div", 1)(2, AnalyticsCostBudgetComponent_Conditional_2_Template, 54, 3);
560
+ } if (rf & 2) {
561
+ i0.ɵɵproperty("TimeRange", ctx.TimeRange)("TimeRangeOptions", ctx.TimeRangeOptionsList)("Filters", ctx.Filters)("ShowAgentFilter", false)("ShowPromptFilter", false)("ShowModelFilter", true)("ShowStatusFilter", false)("ShowCompareToggle", false)("ShowExportButton", false);
562
+ i0.ɵɵadvance();
563
+ i0.ɵɵconditional(ctx.IsLoading ? 1 : 2);
564
+ } }, dependencies: [i1.LoadingComponent, i2.AnalyticsFilterBarComponent, i3.DecimalPipe], styles: ["[_nghost-%COMP%] { display: block; }\n\n .loading-container[_ngcontent-%COMP%] {\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 300px;\n }\n\n \n\n .kpi-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 12px;\n margin: 16px 0;\n }\n\n .kpi-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n padding: 16px;\n display: flex;\n align-items: flex-start;\n gap: 12px;\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n }\n\n .kpi-card[_ngcontent-%COMP%]:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-text-primary) 8%, transparent);\n }\n\n .kpi-card--highlighted[_ngcontent-%COMP%] {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n }\n\n .kpi-icon[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 14px;\n flex-shrink: 0;\n }\n\n .kpi-card--highlighted[_ngcontent-%COMP%] .kpi-icon[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 20%, var(--mj-bg-surface));\n }\n\n .kpi-label[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .kpi-value[_ngcontent-%COMP%] {\n font-size: 22px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 2px 0;\n letter-spacing: -0.02em;\n }\n\n .kpi-delta[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n display: flex;\n align-items: center;\n gap: 3px;\n }\n\n .kpi-delta--up[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n }\n\n .kpi-delta--down[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n }\n\n \n\n .two-col[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 3fr 2fr;\n gap: 16px;\n margin-bottom: 16px;\n }\n\n \n\n .panel[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n overflow: hidden;\n margin-bottom: 16px;\n }\n\n .panel-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 18px;\n border-bottom: 1px solid var(--mj-border-subtle);\n }\n\n .panel-header__title[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .panel-header__icon[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-brand-primary);\n }\n\n .panel-empty[_ngcontent-%COMP%] {\n text-align: center;\n padding: 32px;\n color: var(--mj-text-disabled);\n font-size: 13px;\n }\n\n .export-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 13px;\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n }\n\n .export-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n }\n\n \n\n .chart-body[_ngcontent-%COMP%] {\n padding: 16px 18px;\n }\n\n .bar-chart[_ngcontent-%COMP%] {\n width: 100%;\n }\n\n .bar-chart-area[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-end;\n gap: 4px;\n height: 180px;\n position: relative;\n padding-bottom: 24px;\n }\n\n .bar-col[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n height: 100%;\n justify-content: flex-end;\n }\n\n .bar[_ngcontent-%COMP%] {\n width: 100%;\n max-width: 32px;\n border-radius: 4px 4px 0 0;\n background: var(--mj-brand-primary);\n transition: height 0.4s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 2px;\n }\n\n .bar--anomaly[_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n }\n\n .bar-label[_ngcontent-%COMP%] {\n font-size: 10px;\n color: var(--mj-text-muted);\n margin-top: 4px;\n white-space: nowrap;\n }\n\n .avg-line[_ngcontent-%COMP%] {\n position: absolute;\n left: 0;\n right: 0;\n border-top: 2px dashed var(--mj-text-disabled);\n z-index: 1;\n pointer-events: none;\n }\n\n .avg-label[_ngcontent-%COMP%] {\n position: absolute;\n right: 0;\n top: -14px;\n font-size: 10px;\n color: var(--mj-text-disabled);\n font-weight: 600;\n }\n\n \n\n .treemap-body[_ngcontent-%COMP%] {\n padding: 16px 18px;\n }\n\n .treemap-grid[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n min-height: 160px;\n }\n\n .treemap-cell[_ngcontent-%COMP%] {\n border-radius: 8px;\n padding: 12px;\n display: flex;\n flex-direction: column;\n justify-content: center;\n min-width: 80px;\n min-height: 70px;\n flex-grow: 1;\n color: var(--mj-text-inverse, white);\n transition: opacity 0.2s;\n }\n\n .treemap-cell[_ngcontent-%COMP%]:hover {\n opacity: 0.85;\n }\n\n .treemap-label[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 600;\n opacity: 0.95;\n }\n\n .treemap-value[_ngcontent-%COMP%] {\n font-size: 16px;\n font-weight: 700;\n margin-top: 2px;\n }\n\n .treemap-pct[_ngcontent-%COMP%] {\n font-size: 11px;\n opacity: 0.8;\n }\n\n \n\n .table-wrapper[_ngcontent-%COMP%] {\n overflow-x: auto;\n }\n\n .data-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n }\n\n .data-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%], \n .data-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 10px 14px;\n text-align: left;\n border-bottom: 1px solid var(--mj-border-subtle);\n }\n\n .data-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.4px;\n background: var(--mj-bg-surface-card);\n position: sticky;\n top: 0;\n white-space: nowrap;\n }\n\n .col-numeric[_ngcontent-%COMP%] { text-align: right; }\n\n .data-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] {\n transition: background 0.15s;\n }\n\n .data-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n }\n\n .cell-model[_ngcontent-%COMP%] {\n font-weight: 500;\n color: var(--mj-text-primary);\n }\n\n .cell-vendor[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n }\n\n .cell-numeric[_ngcontent-%COMP%] {\n text-align: right;\n font-variant-numeric: tabular-nums;\n color: var(--mj-text-secondary);\n }\n\n .cell-cost[_ngcontent-%COMP%] {\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n\n .empty-row[_ngcontent-%COMP%] {\n text-align: center;\n color: var(--mj-text-disabled);\n padding: 24px;\n }\n\n \n\n @media (max-width: 1200px) {\n .two-col[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n }\n\n @media (max-width: 768px) {\n .kpi-row[_ngcontent-%COMP%] {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .kpi-value[_ngcontent-%COMP%] {\n font-size: 18px;\n }\n }"] });
565
+ }
566
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AnalyticsCostBudgetComponent, [{
567
+ type: Component,
568
+ args: [{ standalone: false, selector: 'app-analytics-cost-budget', template: `
569
+ <!-- Filter Bar -->
570
+ <app-analytics-filter-bar
571
+ [TimeRange]="TimeRange"
572
+ [TimeRangeOptions]="TimeRangeOptionsList"
573
+ [Filters]="Filters"
574
+ [ShowAgentFilter]="false"
575
+ [ShowPromptFilter]="false"
576
+ [ShowModelFilter]="true"
577
+ [ShowStatusFilter]="false"
578
+ [ShowCompareToggle]="false"
579
+ [ShowExportButton]="false"
580
+ (TimeRangeChange)="OnTimeRangeChange($event)"
581
+ (FiltersChange)="OnFiltersChange($event)"
582
+ ></app-analytics-filter-bar>
583
+
584
+ @if (IsLoading) {
585
+ <div class="loading-container">
586
+ <mj-loading text="Loading cost data..."></mj-loading>
587
+ </div>
588
+ } @else {
589
+ <!-- Cost KPI Row -->
590
+ <div class="kpi-row">
591
+ @for (kpi of CostKpis; track kpi.Label) {
592
+ <div class="kpi-card" [class.kpi-card--highlighted]="kpi.Highlighted">
593
+ <div class="kpi-icon">
594
+ <i [class]="kpi.Icon"></i>
595
+ </div>
596
+ <div class="kpi-content">
597
+ <div class="kpi-label">{{ kpi.Label }}</div>
598
+ <div class="kpi-value">{{ kpi.Value }}</div>
599
+ @if (kpi.Delta != null) {
600
+ <div class="kpi-delta"
601
+ [class.kpi-delta--up]="kpi.DeltaDirection === 'up'"
602
+ [class.kpi-delta--down]="kpi.DeltaDirection === 'down'">
603
+ @if (kpi.DeltaDirection === 'up') {
604
+ <i class="fa-solid fa-arrow-up"></i>
605
+ } @else if (kpi.DeltaDirection === 'down') {
606
+ <i class="fa-solid fa-arrow-down"></i>
607
+ }
608
+ {{ kpi.Delta | number:'1.1-1' }}% vs prev
609
+ </div>
610
+ }
611
+ </div>
612
+ </div>
613
+ }
614
+ </div>
615
+
616
+ <!-- Two-Column Layout -->
617
+ <div class="two-col">
618
+ <!-- Daily Cost Trend -->
619
+ <div class="panel panel-chart">
620
+ <div class="panel-header">
621
+ <div class="panel-header__title">
622
+ <i class="fa-solid fa-chart-column panel-header__icon"></i>
623
+ Daily Cost Trend
624
+ </div>
625
+ </div>
626
+ <div class="chart-body">
627
+ @if (DailyBars.length === 0) {
628
+ <div class="panel-empty">No cost data for selected period</div>
629
+ } @else {
630
+ <div class="bar-chart">
631
+ <div class="bar-chart-area">
632
+ @if (AvgLinePercent > 0) {
633
+ <div class="avg-line" [style.bottom.%]="AvgLinePercent">
634
+ <span class="avg-label">avg</span>
635
+ </div>
636
+ }
637
+ @for (bar of DailyBars; track bar.Date) {
638
+ <div class="bar-col" [title]="bar.Label + ': ' + FormatCurrency(bar.Cost)">
639
+ <div
640
+ class="bar"
641
+ [class.bar--anomaly]="bar.IsAnomaly"
642
+ [style.height.%]="bar.HeightPercent"
643
+ ></div>
644
+ <div class="bar-label">{{ bar.Label }}</div>
645
+ </div>
646
+ }
647
+ </div>
648
+ </div>
649
+ }
650
+ </div>
651
+ </div>
652
+
653
+ <!-- Cost Breakdown Treemap -->
654
+ <div class="panel panel-treemap">
655
+ <div class="panel-header">
656
+ <div class="panel-header__title">
657
+ <i class="fa-solid fa-chart-pie panel-header__icon"></i>
658
+ Cost by Vendor
659
+ </div>
660
+ </div>
661
+ <div class="treemap-body">
662
+ @if (TreemapCells.length === 0) {
663
+ <div class="panel-empty">No vendor cost data</div>
664
+ } @else {
665
+ <div class="treemap-grid">
666
+ @for (cell of TreemapCells; track cell.Label) {
667
+ <div
668
+ class="treemap-cell"
669
+ [style.background]="cell.Color"
670
+ [style.flex-basis.%]="cell.Percent"
671
+ [title]="cell.Label + ': ' + FormatCurrency(cell.Cost) + ' (' + (cell.Percent | number:'1.0-0') + '%)'">
672
+ <span class="treemap-label">{{ cell.Label }}</span>
673
+ <span class="treemap-value">{{ FormatCurrency(cell.Cost) }}</span>
674
+ <span class="treemap-pct">{{ cell.Percent | number:'1.0-0' }}%</span>
675
+ </div>
676
+ }
677
+ </div>
678
+ }
679
+ </div>
680
+ </div>
681
+ </div>
682
+
683
+ <!-- Cost by Model Table -->
684
+ <div class="panel">
685
+ <div class="panel-header">
686
+ <div class="panel-header__title">
687
+ <i class="fa-solid fa-table panel-header__icon"></i>
688
+ Cost by Model
689
+ </div>
690
+ <button class="export-btn" (click)="ExportCSV()">
691
+ <i class="fa-solid fa-download"></i>
692
+ Export CSV
693
+ </button>
694
+ </div>
695
+ <div class="table-wrapper">
696
+ <table class="data-table">
697
+ <thead>
698
+ <tr>
699
+ <th>Model</th>
700
+ <th>Vendor</th>
701
+ <th class="col-numeric">Runs</th>
702
+ <th class="col-numeric">Input Tokens</th>
703
+ <th class="col-numeric">Output Tokens</th>
704
+ <th class="col-numeric">Input Cost</th>
705
+ <th class="col-numeric">Output Cost</th>
706
+ <th class="col-numeric">Total Cost</th>
707
+ <th class="col-numeric">% of Total</th>
708
+ </tr>
709
+ </thead>
710
+ <tbody>
711
+ @if (CostByModelRows.length === 0) {
712
+ <tr><td colspan="9" class="empty-row">No data available</td></tr>
713
+ }
714
+ @for (row of CostByModelRows; track row.Model) {
715
+ <tr>
716
+ <td class="cell-model">{{ row.Model }}</td>
717
+ <td class="cell-vendor">{{ row.Vendor }}</td>
718
+ <td class="cell-numeric">{{ row.Runs | number }}</td>
719
+ <td class="cell-numeric">{{ row.InputTokens | number }}</td>
720
+ <td class="cell-numeric">{{ row.OutputTokens | number }}</td>
721
+ <td class="cell-numeric">{{ FormatCurrency(row.InputCost, 4) }}</td>
722
+ <td class="cell-numeric">{{ FormatCurrency(row.OutputCost, 4) }}</td>
723
+ <td class="cell-numeric cell-cost">{{ FormatCurrency(row.TotalCost) }}</td>
724
+ <td class="cell-numeric">{{ row.PercentOfTotal | number:'1.1-1' }}%</td>
725
+ </tr>
726
+ }
727
+ </tbody>
728
+ </table>
729
+ </div>
730
+ </div>
731
+ }
732
+ `, styles: ["\n :host { display: block; }\n\n .loading-container {\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 300px;\n }\n\n /* \u2500\u2500 KPI Row \u2500\u2500 */\n .kpi-row {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 12px;\n margin: 16px 0;\n }\n\n .kpi-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n padding: 16px;\n display: flex;\n align-items: flex-start;\n gap: 12px;\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n }\n\n .kpi-card:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-text-primary) 8%, transparent);\n }\n\n .kpi-card--highlighted {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n }\n\n .kpi-icon {\n width: 36px;\n height: 36px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 14px;\n flex-shrink: 0;\n }\n\n .kpi-card--highlighted .kpi-icon {\n background: color-mix(in srgb, var(--mj-brand-primary) 20%, var(--mj-bg-surface));\n }\n\n .kpi-label {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .kpi-value {\n font-size: 22px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 2px 0;\n letter-spacing: -0.02em;\n }\n\n .kpi-delta {\n font-size: 11px;\n color: var(--mj-text-muted);\n display: flex;\n align-items: center;\n gap: 3px;\n }\n\n .kpi-delta--up {\n color: var(--mj-status-error);\n }\n\n .kpi-delta--down {\n color: var(--mj-status-success);\n }\n\n /* \u2500\u2500 Two-Column Layout \u2500\u2500 */\n .two-col {\n display: grid;\n grid-template-columns: 3fr 2fr;\n gap: 16px;\n margin-bottom: 16px;\n }\n\n /* \u2500\u2500 Panel \u2500\u2500 */\n .panel {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n overflow: hidden;\n margin-bottom: 16px;\n }\n\n .panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 18px;\n border-bottom: 1px solid var(--mj-border-subtle);\n }\n\n .panel-header__title {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .panel-header__icon {\n font-size: 13px;\n color: var(--mj-brand-primary);\n }\n\n .panel-empty {\n text-align: center;\n padding: 32px;\n color: var(--mj-text-disabled);\n font-size: 13px;\n }\n\n .export-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 13px;\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n }\n\n .export-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n }\n\n /* \u2500\u2500 Bar Chart \u2500\u2500 */\n .chart-body {\n padding: 16px 18px;\n }\n\n .bar-chart {\n width: 100%;\n }\n\n .bar-chart-area {\n display: flex;\n align-items: flex-end;\n gap: 4px;\n height: 180px;\n position: relative;\n padding-bottom: 24px;\n }\n\n .bar-col {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n height: 100%;\n justify-content: flex-end;\n }\n\n .bar {\n width: 100%;\n max-width: 32px;\n border-radius: 4px 4px 0 0;\n background: var(--mj-brand-primary);\n transition: height 0.4s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 2px;\n }\n\n .bar--anomaly {\n background: var(--mj-status-error);\n }\n\n .bar-label {\n font-size: 10px;\n color: var(--mj-text-muted);\n margin-top: 4px;\n white-space: nowrap;\n }\n\n .avg-line {\n position: absolute;\n left: 0;\n right: 0;\n border-top: 2px dashed var(--mj-text-disabled);\n z-index: 1;\n pointer-events: none;\n }\n\n .avg-label {\n position: absolute;\n right: 0;\n top: -14px;\n font-size: 10px;\n color: var(--mj-text-disabled);\n font-weight: 600;\n }\n\n /* \u2500\u2500 Treemap \u2500\u2500 */\n .treemap-body {\n padding: 16px 18px;\n }\n\n .treemap-grid {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n min-height: 160px;\n }\n\n .treemap-cell {\n border-radius: 8px;\n padding: 12px;\n display: flex;\n flex-direction: column;\n justify-content: center;\n min-width: 80px;\n min-height: 70px;\n flex-grow: 1;\n color: var(--mj-text-inverse, white);\n transition: opacity 0.2s;\n }\n\n .treemap-cell:hover {\n opacity: 0.85;\n }\n\n .treemap-label {\n font-size: 12px;\n font-weight: 600;\n opacity: 0.95;\n }\n\n .treemap-value {\n font-size: 16px;\n font-weight: 700;\n margin-top: 2px;\n }\n\n .treemap-pct {\n font-size: 11px;\n opacity: 0.8;\n }\n\n /* \u2500\u2500 Table \u2500\u2500 */\n .table-wrapper {\n overflow-x: auto;\n }\n\n .data-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n }\n\n .data-table th,\n .data-table td {\n padding: 10px 14px;\n text-align: left;\n border-bottom: 1px solid var(--mj-border-subtle);\n }\n\n .data-table th {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.4px;\n background: var(--mj-bg-surface-card);\n position: sticky;\n top: 0;\n white-space: nowrap;\n }\n\n .col-numeric { text-align: right; }\n\n .data-table tbody tr {\n transition: background 0.15s;\n }\n\n .data-table tbody tr:hover {\n background: var(--mj-bg-surface-hover);\n }\n\n .cell-model {\n font-weight: 500;\n color: var(--mj-text-primary);\n }\n\n .cell-vendor {\n color: var(--mj-text-secondary);\n }\n\n .cell-numeric {\n text-align: right;\n font-variant-numeric: tabular-nums;\n color: var(--mj-text-secondary);\n }\n\n .cell-cost {\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n\n .empty-row {\n text-align: center;\n color: var(--mj-text-disabled);\n padding: 24px;\n }\n\n /* \u2500\u2500 Responsive \u2500\u2500 */\n @media (max-width: 1200px) {\n .two-col {\n grid-template-columns: 1fr;\n }\n }\n\n @media (max-width: 768px) {\n .kpi-row {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .kpi-value {\n font-size: 18px;\n }\n }\n "] }]
733
+ }], null, { TimeRange: [{
734
+ type: Input
735
+ }], Filters: [{
736
+ type: Input
737
+ }], TimeRangeChange: [{
738
+ type: Output
739
+ }], FiltersChange: [{
740
+ type: Output
741
+ }] }); })();
742
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AnalyticsCostBudgetComponent, { className: "AnalyticsCostBudgetComponent", filePath: "src/AI/components/analytics/cost-budget/cost-budget.component.ts", lineNumber: 602 }); })();
743
+ export function LoadAnalyticsCostBudget() { }
744
+ //# sourceMappingURL=cost-budget.component.js.map