@memberjunction/ng-dashboards 6.1.2 → 6.2.0-edge.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 (99) hide show
  1. package/dist/AI/components/vectors/vector-management-resource.component.d.ts +6 -1
  2. package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
  3. package/dist/AI/components/vectors/vector-management-resource.component.js +99 -45
  4. package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
  5. package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.d.ts +4 -0
  6. package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.d.ts.map +1 -1
  7. package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.js +16 -6
  8. package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.js.map +1 -1
  9. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +4 -0
  10. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
  11. package/dist/DataExplorer/data-explorer-dashboard.component.js +14 -4
  12. package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
  13. package/dist/DataExplorer/services/explorer-state.service.js +2 -2
  14. package/dist/DataExplorer/services/explorer-state.service.js.map +1 -1
  15. package/dist/DevTools/graphql-console.component.d.ts.map +1 -1
  16. package/dist/DevTools/graphql-console.component.js +2 -0
  17. package/dist/DevTools/graphql-console.component.js.map +1 -1
  18. package/dist/Integration/components/connections/connections.component.d.ts.map +1 -1
  19. package/dist/Integration/components/connections/connections.component.js +2 -0
  20. package/dist/Integration/components/connections/connections.component.js.map +1 -1
  21. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.d.ts +4 -12
  22. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.d.ts.map +1 -1
  23. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js +39 -78
  24. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js.map +1 -1
  25. package/dist/Lists/components/lists-browse-resource.component.d.ts +3 -0
  26. package/dist/Lists/components/lists-browse-resource.component.d.ts.map +1 -1
  27. package/dist/Lists/components/lists-browse-resource.component.js +7 -6
  28. package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
  29. package/dist/Lists/components/lists-my-lists-resource.component.d.ts +3 -0
  30. package/dist/Lists/components/lists-my-lists-resource.component.d.ts.map +1 -1
  31. package/dist/Lists/components/lists-my-lists-resource.component.js +7 -6
  32. package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
  33. package/dist/PredictiveStudio/at-risk.view-models.d.ts +41 -3
  34. package/dist/PredictiveStudio/at-risk.view-models.d.ts.map +1 -1
  35. package/dist/PredictiveStudio/at-risk.view-models.js +99 -6
  36. package/dist/PredictiveStudio/at-risk.view-models.js.map +1 -1
  37. package/dist/PredictiveStudio/components/ps-catalog.component.d.ts +4 -1
  38. package/dist/PredictiveStudio/components/ps-catalog.component.d.ts.map +1 -1
  39. package/dist/PredictiveStudio/components/ps-catalog.component.js +61 -37
  40. package/dist/PredictiveStudio/components/ps-catalog.component.js.map +1 -1
  41. package/dist/PredictiveStudio/components/ps-experiments.component.d.ts +12 -0
  42. package/dist/PredictiveStudio/components/ps-experiments.component.d.ts.map +1 -1
  43. package/dist/PredictiveStudio/components/ps-experiments.component.js +426 -184
  44. package/dist/PredictiveStudio/components/ps-experiments.component.js.map +1 -1
  45. package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.d.ts +84 -0
  46. package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.d.ts.map +1 -0
  47. package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.js +1397 -0
  48. package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.js.map +1 -0
  49. package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts +66 -34
  50. package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts.map +1 -1
  51. package/dist/PredictiveStudio/components/ps-pipelines.component.js +2456 -743
  52. package/dist/PredictiveStudio/components/ps-pipelines.component.js.map +1 -1
  53. package/dist/PredictiveStudio/components/ps-predictions-grid.component.d.ts +194 -0
  54. package/dist/PredictiveStudio/components/ps-predictions-grid.component.d.ts.map +1 -0
  55. package/dist/PredictiveStudio/components/ps-predictions-grid.component.js +1902 -0
  56. package/dist/PredictiveStudio/components/ps-predictions-grid.component.js.map +1 -0
  57. package/dist/PredictiveStudio/components/ps-production.component.d.ts +17 -1
  58. package/dist/PredictiveStudio/components/ps-production.component.d.ts.map +1 -1
  59. package/dist/PredictiveStudio/components/ps-production.component.js +653 -372
  60. package/dist/PredictiveStudio/components/ps-production.component.js.map +1 -1
  61. package/dist/PredictiveStudio/components/ps-registry.component.d.ts +21 -61
  62. package/dist/PredictiveStudio/components/ps-registry.component.d.ts.map +1 -1
  63. package/dist/PredictiveStudio/components/ps-registry.component.js +214 -681
  64. package/dist/PredictiveStudio/components/ps-registry.component.js.map +1 -1
  65. package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.d.ts +49 -0
  66. package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.d.ts.map +1 -0
  67. package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.js +658 -0
  68. package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.js.map +1 -0
  69. package/dist/PredictiveStudio/predictive-studio-score-history.service.d.ts +42 -0
  70. package/dist/PredictiveStudio/predictive-studio-score-history.service.d.ts.map +1 -0
  71. package/dist/PredictiveStudio/predictive-studio-score-history.service.js +90 -0
  72. package/dist/PredictiveStudio/predictive-studio-score-history.service.js.map +1 -0
  73. package/dist/PredictiveStudio/predictive-studio.view-models.d.ts +14 -2
  74. package/dist/PredictiveStudio/predictive-studio.view-models.d.ts.map +1 -1
  75. package/dist/PredictiveStudio/predictive-studio.view-models.js +41 -5
  76. package/dist/PredictiveStudio/predictive-studio.view-models.js.map +1 -1
  77. package/dist/PredictiveStudio/resources/ps-models-resource.component.d.ts +9 -1
  78. package/dist/PredictiveStudio/resources/ps-models-resource.component.d.ts.map +1 -1
  79. package/dist/PredictiveStudio/resources/ps-models-resource.component.js +178 -65
  80. package/dist/PredictiveStudio/resources/ps-models-resource.component.js.map +1 -1
  81. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts +9 -1
  82. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts.map +1 -1
  83. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js +307 -369
  84. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js.map +1 -1
  85. package/dist/PredictiveStudio/resources/ps-resource-base.js +1 -1
  86. package/dist/PredictiveStudio/resources/ps-resource-base.js.map +1 -1
  87. package/dist/PredictiveStudio/resources/ps-studio-resource.component.d.ts +17 -1
  88. package/dist/PredictiveStudio/resources/ps-studio-resource.component.d.ts.map +1 -1
  89. package/dist/PredictiveStudio/resources/ps-studio-resource.component.js +309 -138
  90. package/dist/PredictiveStudio/resources/ps-studio-resource.component.js.map +1 -1
  91. package/dist/VersionHistory/components/graph-resource.component.d.ts +1 -0
  92. package/dist/VersionHistory/components/graph-resource.component.d.ts.map +1 -1
  93. package/dist/VersionHistory/components/graph-resource.component.js +6 -5
  94. package/dist/VersionHistory/components/graph-resource.component.js.map +1 -1
  95. package/dist/predictive-studio-dashboards.module.d.ts +10 -8
  96. package/dist/predictive-studio-dashboards.module.d.ts.map +1 -1
  97. package/dist/predictive-studio-dashboards.module.js +13 -3
  98. package/dist/predictive-studio-dashboards.module.js.map +1 -1
  99. package/package.json +64 -63
@@ -0,0 +1,1902 @@
1
+ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, inject, } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormsModule } from '@angular/forms';
4
+ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
5
+ import { NavigationService } from '@memberjunction/ng-shared';
6
+ import { MJButtonDirective, MJViewToggleComponent, MjSlidePanelComponent, } from '@memberjunction/ng-ui-components';
7
+ import { SharedGenericModule } from '@memberjunction/ng-shared-generic';
8
+ import { CompositeKey, LogError, RunView } from '@memberjunction/core';
9
+ import { NormalizeUUID, UUIDsEqual } from '@memberjunction/global';
10
+ import { UserInfoEngine } from '@memberjunction/core-entities';
11
+ import { AgGridModule } from 'ag-grid-angular';
12
+ import { AllCommunityModule, ModuleRegistry, themeAlpine, } from 'ag-grid-community';
13
+ import { parseAtRiskRows, resolveRenewalPolarity, } from '../at-risk.view-models';
14
+ import { resolveOutcomeConfig, resolveScoreBand, resolveOutcomeStyle, formatPredictionScore, } from '@memberjunction/predictive-studio-core';
15
+ import { PredictiveStudioScoreHistoryService, } from '../predictive-studio-score-history.service';
16
+ import { PredictiveStudioEngine } from '../engine/predictive-studio.engine';
17
+ import * as i0 from "@angular/core";
18
+ import * as i1 from "@memberjunction/ng-shared-generic";
19
+ import * as i2 from "ag-grid-angular";
20
+ import * as i3 from "@angular/common";
21
+ const _forTrack0 = ($index, $item) => $item.key;
22
+ const _forTrack1 = ($index, $item) => $item.label;
23
+ const _forTrack2 = ($index, $item) => $item.name;
24
+ const _forTrack3 = ($index, $item) => $item.runId;
25
+ function PSPredictionsGridComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
26
+ const _r1 = i0.ɵɵgetCurrentView();
27
+ i0.ɵɵelementStart(0, "button", 21);
28
+ i0.ɵɵlistener("click", function PSPredictionsGridComponent_Conditional_6_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.clearSearch()); });
29
+ i0.ɵɵelement(1, "i", 22);
30
+ i0.ɵɵelementEnd();
31
+ } }
32
+ function PSPredictionsGridComponent_For_14_Template(rf, ctx) { if (rf & 1) {
33
+ const _r3 = i0.ɵɵgetCurrentView();
34
+ i0.ɵɵelementStart(0, "button", 8);
35
+ i0.ɵɵlistener("click", function PSPredictionsGridComponent_For_14_Template_button_click_0_listener() { const b_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setTier(b_r4.key)); });
36
+ i0.ɵɵtext(1);
37
+ i0.ɵɵelementStart(2, "span", 9);
38
+ i0.ɵɵtext(3);
39
+ i0.ɵɵpipe(4, "number");
40
+ i0.ɵɵelementEnd()();
41
+ } if (rf & 2) {
42
+ const b_r4 = ctx.$implicit;
43
+ const ctx_r1 = i0.ɵɵnextContext();
44
+ i0.ɵɵclassProp("active", ctx_r1.selectedTier === b_r4.key);
45
+ i0.ɵɵadvance();
46
+ i0.ɵɵtextInterpolate1(" ", b_r4.label, " ");
47
+ i0.ɵɵadvance(2);
48
+ i0.ɵɵtextInterpolate1("(", i0.ɵɵpipeBind1(4, 4, b_r4.count), ")");
49
+ } }
50
+ function PSPredictionsGridComponent_Conditional_15_Template(rf, ctx) { if (rf & 1) {
51
+ const _r5 = i0.ɵɵgetCurrentView();
52
+ i0.ɵɵelementStart(0, "div", 11);
53
+ i0.ɵɵelement(1, "i", 23);
54
+ i0.ɵɵelementStart(2, "span");
55
+ i0.ɵɵtext(3);
56
+ i0.ɵɵelementEnd();
57
+ i0.ɵɵelementStart(4, "button", 24);
58
+ i0.ɵɵlistener("click", function PSPredictionsGridComponent_Conditional_15_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.clearDecileFilter()); });
59
+ i0.ɵɵelement(5, "i", 22);
60
+ i0.ɵɵelementEnd()();
61
+ } if (rf & 2) {
62
+ const ctx_r1 = i0.ɵɵnextContext();
63
+ i0.ɵɵadvance(3);
64
+ i0.ɵɵtextInterpolate1("Decile ", ctx_r1.selectedDecileLabel);
65
+ } }
66
+ function PSPredictionsGridComponent_Conditional_22_Template(rf, ctx) { if (rf & 1) {
67
+ i0.ɵɵelement(0, "mj-loading", 17);
68
+ } }
69
+ function PSPredictionsGridComponent_Conditional_23_Template(rf, ctx) { if (rf & 1) {
70
+ const _r6 = i0.ɵɵgetCurrentView();
71
+ i0.ɵɵelementStart(0, "div", 18);
72
+ i0.ɵɵelement(1, "i", 25);
73
+ i0.ɵɵelementStart(2, "div", 26)(3, "strong");
74
+ i0.ɵɵtext(4, "Couldn't load predictions");
75
+ i0.ɵɵelementEnd();
76
+ i0.ɵɵelementStart(5, "span", 27);
77
+ i0.ɵɵtext(6);
78
+ i0.ɵɵelementEnd()();
79
+ i0.ɵɵelementStart(7, "button", 28);
80
+ i0.ɵɵlistener("click", function PSPredictionsGridComponent_Conditional_23_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.reload()); });
81
+ i0.ɵɵelement(8, "i", 29);
82
+ i0.ɵɵtext(9, " Try again");
83
+ i0.ɵɵelementEnd()();
84
+ } if (rf & 2) {
85
+ const ctx_r1 = i0.ɵɵnextContext();
86
+ i0.ɵɵadvance(6);
87
+ i0.ɵɵtextInterpolate(ctx_r1.loadError);
88
+ } }
89
+ function PSPredictionsGridComponent_Conditional_24_Template(rf, ctx) { if (rf & 1) {
90
+ i0.ɵɵelementStart(0, "div", 19)(1, "span", 30);
91
+ i0.ɵɵelement(2, "i", 31);
92
+ i0.ɵɵelementEnd();
93
+ i0.ɵɵelementStart(3, "h3");
94
+ i0.ɵɵtext(4, "No predictions available");
95
+ i0.ɵɵelementEnd();
96
+ i0.ɵɵelementStart(5, "p");
97
+ i0.ɵɵtext(6, "Run this model from Models in Production or wait for the scheduled job to produce predictions.");
98
+ i0.ɵɵelementEnd()();
99
+ } }
100
+ function PSPredictionsGridComponent_Conditional_25_Conditional_0_Conditional_7_Template(rf, ctx) { if (rf & 1) {
101
+ i0.ɵɵelementStart(0, "span", 35);
102
+ i0.ɵɵtext(1);
103
+ i0.ɵɵelementEnd();
104
+ } if (rf & 2) {
105
+ const ctx_r1 = i0.ɵɵnextContext(3);
106
+ i0.ɵɵadvance();
107
+ i0.ɵɵtextInterpolate2("Filtered by ", ctx_r1.selectedTier !== "all" ? ctx_r1.selectedTier + " risk" : "", "", ctx_r1.searchText ? " query \"" + ctx_r1.searchText + "\"" : "");
108
+ } }
109
+ function PSPredictionsGridComponent_Conditional_25_Conditional_0_Template(rf, ctx) { if (rf & 1) {
110
+ const _r7 = i0.ɵɵgetCurrentView();
111
+ i0.ɵɵelementStart(0, "ag-grid-angular", 33);
112
+ i0.ɵɵlistener("gridReady", function PSPredictionsGridComponent_Conditional_25_Conditional_0_Template_ag_grid_angular_gridReady_0_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onGridReady($event)); })("rowClicked", function PSPredictionsGridComponent_Conditional_25_Conditional_0_Template_ag_grid_angular_rowClicked_0_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onRowClicked($event)); })("cellClicked", function PSPredictionsGridComponent_Conditional_25_Conditional_0_Template_ag_grid_angular_cellClicked_0_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onCellClicked($event)); });
113
+ i0.ɵɵelementEnd();
114
+ i0.ɵɵelementStart(1, "div", 34)(2, "span");
115
+ i0.ɵɵtext(3, "Showing ");
116
+ i0.ɵɵelementStart(4, "strong");
117
+ i0.ɵɵtext(5);
118
+ i0.ɵɵelementEnd();
119
+ i0.ɵɵtext(6);
120
+ i0.ɵɵelementEnd();
121
+ i0.ɵɵconditionalCreate(7, PSPredictionsGridComponent_Conditional_25_Conditional_0_Conditional_7_Template, 2, 2, "span", 35);
122
+ i0.ɵɵelementStart(8, "span", 35);
123
+ i0.ɵɵtext(9, "Click any row to inspect details & score history");
124
+ i0.ɵɵelementEnd()();
125
+ } if (rf & 2) {
126
+ const ctx_r1 = i0.ɵɵnextContext(2);
127
+ i0.ɵɵproperty("theme", ctx_r1.Theme)("columnDefs", ctx_r1.columnDefs)("defaultColDef", ctx_r1.defaultColDef)("gridOptions", ctx_r1.GridOptions)("rowData", ctx_r1.filteredRows);
128
+ i0.ɵɵadvance(5);
129
+ i0.ɵɵtextInterpolate(ctx_r1.filteredRows.length);
130
+ i0.ɵɵadvance();
131
+ i0.ɵɵtextInterpolate1(" of ", ctx_r1.allRows.length, " predictions");
132
+ i0.ɵɵadvance();
133
+ i0.ɵɵconditional(ctx_r1.selectedTier !== "all" || ctx_r1.searchText ? 7 : -1);
134
+ } }
135
+ function PSPredictionsGridComponent_Conditional_25_Conditional_1_For_12_Conditional_2_Template(rf, ctx) { if (rf & 1) {
136
+ i0.ɵɵelement(0, "i");
137
+ } if (rf & 2) {
138
+ const b_r9 = i0.ɵɵnextContext().$implicit;
139
+ const ctx_r1 = i0.ɵɵnextContext(3);
140
+ i0.ɵɵclassMap(i0.ɵɵinterpolate1("fa-solid ", b_r9.icon));
141
+ i0.ɵɵstyleProp("color", ctx_r1.getBadgeColorVar(b_r9.badgeColor));
142
+ } }
143
+ function PSPredictionsGridComponent_Conditional_25_Conditional_1_For_12_Template(rf, ctx) { if (rf & 1) {
144
+ const _r8 = i0.ɵɵgetCurrentView();
145
+ i0.ɵɵelementStart(0, "div", 54);
146
+ i0.ɵɵlistener("click", function PSPredictionsGridComponent_Conditional_25_Conditional_1_For_12_Template_div_click_0_listener() { const b_r9 = i0.ɵɵrestoreView(_r8).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setTierAndGrid(b_r9.key)); });
147
+ i0.ɵɵelementStart(1, "span", 38);
148
+ i0.ɵɵconditionalCreate(2, PSPredictionsGridComponent_Conditional_25_Conditional_1_For_12_Conditional_2_Template, 1, 5, "i", 55);
149
+ i0.ɵɵtext(3);
150
+ i0.ɵɵelementEnd();
151
+ i0.ɵɵelementStart(4, "span", 40);
152
+ i0.ɵɵtext(5);
153
+ i0.ɵɵpipe(6, "number");
154
+ i0.ɵɵelementEnd();
155
+ i0.ɵɵelementStart(7, "span", 41);
156
+ i0.ɵɵtext(8);
157
+ i0.ɵɵelementEnd()();
158
+ } if (rf & 2) {
159
+ const b_r9 = ctx.$implicit;
160
+ const ctx_r1 = i0.ɵɵnextContext(3);
161
+ i0.ɵɵclassMap("tier-" + b_r9.key);
162
+ i0.ɵɵadvance(2);
163
+ i0.ɵɵconditional(b_r9.icon ? 2 : -1);
164
+ i0.ɵɵadvance();
165
+ i0.ɵɵtextInterpolate1(" ", b_r9.label, " ");
166
+ i0.ɵɵadvance();
167
+ i0.ɵɵstyleProp("color", ctx_r1.getBadgeColorVar(b_r9.badgeColor));
168
+ i0.ɵɵadvance();
169
+ i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind1(6, 10, b_r9.count), " ");
170
+ i0.ɵɵadvance(3);
171
+ i0.ɵɵtextInterpolate3("", b_r9.pct, "% of cohort (avg ", b_r9.avgScoreFormatted, " ", ctx_r1.outcomeConfig.ScoreLabel, ")");
172
+ } }
173
+ function PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_24_Template(rf, ctx) { if (rf & 1) {
174
+ const _r10 = i0.ɵɵgetCurrentView();
175
+ i0.ɵɵelementStart(0, "button", 56);
176
+ i0.ɵɵlistener("click", function PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_24_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.clearDecileFilter()); });
177
+ i0.ɵɵelement(1, "i", 22);
178
+ i0.ɵɵtext(2, " Clear Decile Filter ");
179
+ i0.ɵɵelementEnd();
180
+ } }
181
+ function PSPredictionsGridComponent_Conditional_25_Conditional_1_For_28_Template(rf, ctx) { if (rf & 1) {
182
+ const _r11 = i0.ɵɵgetCurrentView();
183
+ i0.ɵɵelementStart(0, "div", 57);
184
+ i0.ɵɵlistener("click", function PSPredictionsGridComponent_Conditional_25_Conditional_1_For_28_Template_div_click_0_listener() { const b_r12 = i0.ɵɵrestoreView(_r11).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.selectDecile(b_r12.idx)); });
185
+ i0.ɵɵelementStart(1, "span", 58);
186
+ i0.ɵɵtext(2);
187
+ i0.ɵɵpipe(3, "number");
188
+ i0.ɵɵelementEnd();
189
+ i0.ɵɵelementStart(4, "div", 59);
190
+ i0.ɵɵelement(5, "div", 60);
191
+ i0.ɵɵelementEnd();
192
+ i0.ɵɵelementStart(6, "div", 61)(7, "span", 62);
193
+ i0.ɵɵtext(8);
194
+ i0.ɵɵelementEnd();
195
+ i0.ɵɵelementStart(9, "span", 63);
196
+ i0.ɵɵtext(10);
197
+ i0.ɵɵelementEnd()();
198
+ i0.ɵɵelementStart(11, "div", 64)(12, "div", 65);
199
+ i0.ɵɵtext(13);
200
+ i0.ɵɵelementEnd();
201
+ i0.ɵɵelementStart(14, "div", 66)(15, "span", 67);
202
+ i0.ɵɵtext(16);
203
+ i0.ɵɵpipe(17, "number");
204
+ i0.ɵɵelementEnd();
205
+ i0.ɵɵelementStart(18, "span", 68);
206
+ i0.ɵɵtext(19);
207
+ i0.ɵɵelementEnd()();
208
+ i0.ɵɵelementStart(20, "div", 69);
209
+ i0.ɵɵtext(21);
210
+ i0.ɵɵelementEnd();
211
+ i0.ɵɵelementStart(22, "div", 70);
212
+ i0.ɵɵtext(23);
213
+ i0.ɵɵelementEnd()()();
214
+ } if (rf & 2) {
215
+ const b_r12 = ctx.$implicit;
216
+ const ctx_r1 = i0.ɵɵnextContext(3);
217
+ i0.ɵɵclassProp("active", ctx_r1.selectedDecileIndex === b_r12.idx)("empty", b_r12.count === 0);
218
+ i0.ɵɵadvance();
219
+ i0.ɵɵproperty("title", b_r12.count + " records");
220
+ i0.ɵɵadvance();
221
+ i0.ɵɵtextInterpolate1(" ", b_r12.count > 0 ? i0.ɵɵpipeBind1(3, 20, b_r12.count) : "0", " ");
222
+ i0.ɵɵadvance(3);
223
+ i0.ɵɵstyleProp("height", b_r12.height, "px")("background", b_r12.color);
224
+ i0.ɵɵadvance(3);
225
+ i0.ɵɵtextInterpolate(b_r12.shortLabel);
226
+ i0.ɵɵadvance(2);
227
+ i0.ɵɵtextInterpolate(b_r12.pctFormatted);
228
+ i0.ɵɵadvance(3);
229
+ i0.ɵɵtextInterpolate2("", b_r12.label, " ", ctx_r1.outcomeConfig.ScoreLabel);
230
+ i0.ɵɵadvance(3);
231
+ i0.ɵɵtextInterpolate1("", i0.ɵɵpipeBind1(17, 22, b_r12.count), " records");
232
+ i0.ɵɵadvance(3);
233
+ i0.ɵɵtextInterpolate1("(", b_r12.pctFormatted, " of cohort)");
234
+ i0.ɵɵadvance();
235
+ i0.ɵɵstyleProp("color", b_r12.color);
236
+ i0.ɵɵadvance();
237
+ i0.ɵɵtextInterpolate(b_r12.riskLabel);
238
+ i0.ɵɵadvance(2);
239
+ i0.ɵɵtextInterpolate(ctx_r1.selectedDecileIndex === b_r12.idx ? "Click to clear filter" : "Click to filter grid");
240
+ } }
241
+ function PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_29_For_7_Template(rf, ctx) { if (rf & 1) {
242
+ i0.ɵɵelement(0, "div", 76);
243
+ } if (rf & 2) {
244
+ const c_r13 = ctx.$implicit;
245
+ i0.ɵɵstyleProp("width", c_r13.pct, "%")("background", c_r13.color);
246
+ i0.ɵɵproperty("title", c_r13.name + ": " + c_r13.count + " (" + c_r13.pct + "%)");
247
+ } }
248
+ function PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_29_For_10_Template(rf, ctx) { if (rf & 1) {
249
+ i0.ɵɵelementStart(0, "div", 75);
250
+ i0.ɵɵelement(1, "span", 77);
251
+ i0.ɵɵelementStart(2, "strong");
252
+ i0.ɵɵtext(3);
253
+ i0.ɵɵelementEnd();
254
+ i0.ɵɵelementStart(4, "span", 35);
255
+ i0.ɵɵtext(5);
256
+ i0.ɵɵelementEnd()();
257
+ } if (rf & 2) {
258
+ const c_r14 = ctx.$implicit;
259
+ i0.ɵɵadvance();
260
+ i0.ɵɵstyleProp("background", c_r14.color);
261
+ i0.ɵɵadvance(2);
262
+ i0.ɵɵtextInterpolate(c_r14.name);
263
+ i0.ɵɵadvance(2);
264
+ i0.ɵɵtextInterpolate2("", c_r14.count, " (", c_r14.pct, "%)");
265
+ } }
266
+ function PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_29_Template(rf, ctx) { if (rf & 1) {
267
+ i0.ɵɵelementStart(0, "div", 43)(1, "div", 44)(2, "span");
268
+ i0.ɵɵelement(3, "i", 71);
269
+ i0.ɵɵtext(4, " Predicted Outcome Classes");
270
+ i0.ɵɵelementEnd()();
271
+ i0.ɵɵelementStart(5, "div", 72);
272
+ i0.ɵɵrepeaterCreate(6, PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_29_For_7_Template, 1, 5, "div", 73, _forTrack2);
273
+ i0.ɵɵelementEnd();
274
+ i0.ɵɵelementStart(8, "div", 74);
275
+ i0.ɵɵrepeaterCreate(9, PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_29_For_10_Template, 6, 5, "div", 75, _forTrack2);
276
+ i0.ɵɵelementEnd()();
277
+ } if (rf & 2) {
278
+ const ctx_r1 = i0.ɵɵnextContext(3);
279
+ i0.ɵɵadvance(6);
280
+ i0.ɵɵrepeater(ctx_r1.classBreakdown);
281
+ i0.ɵɵadvance(3);
282
+ i0.ɵɵrepeater(ctx_r1.classBreakdown);
283
+ } }
284
+ function PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_30_Conditional_9_Template(rf, ctx) { if (rf & 1) {
285
+ i0.ɵɵelementStart(0, "span", 84);
286
+ i0.ɵɵtext(1);
287
+ i0.ɵɵelementEnd();
288
+ } if (rf & 2) {
289
+ const ctx_r1 = i0.ɵɵnextContext(4);
290
+ i0.ɵɵstyleProp("color", ctx_r1.histogramBins[ctx_r1.selectedDecileIndex].color);
291
+ i0.ɵɵadvance();
292
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.histogramBins[ctx_r1.selectedDecileIndex].riskLabel, " ");
293
+ } }
294
+ function PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_30_Template(rf, ctx) { if (rf & 1) {
295
+ const _r15 = i0.ɵɵgetCurrentView();
296
+ i0.ɵɵelementStart(0, "div", 53)(1, "div", 44)(2, "div", 45);
297
+ i0.ɵɵelement(3, "i", 78);
298
+ i0.ɵɵelementStart(4, "span");
299
+ i0.ɵɵtext(5, " Decile Drill-Down: ");
300
+ i0.ɵɵelementStart(6, "strong", 79);
301
+ i0.ɵɵtext(7);
302
+ i0.ɵɵelementEnd();
303
+ i0.ɵɵtext(8);
304
+ i0.ɵɵconditionalCreate(9, PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_30_Conditional_9_Template, 2, 3, "span", 80);
305
+ i0.ɵɵelementEnd()();
306
+ i0.ɵɵelementStart(10, "div", 47)(11, "span", 48);
307
+ i0.ɵɵtext(12, " Showing ");
308
+ i0.ɵɵelementStart(13, "strong");
309
+ i0.ɵɵtext(14);
310
+ i0.ɵɵpipe(15, "number");
311
+ i0.ɵɵelementEnd();
312
+ i0.ɵɵtext(16);
313
+ i0.ɵɵpipe(17, "number");
314
+ i0.ɵɵelementEnd();
315
+ i0.ɵɵelementStart(18, "button", 56);
316
+ i0.ɵɵlistener("click", function PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_30_Template_button_click_18_listener() { i0.ɵɵrestoreView(_r15); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.clearDecileFilter()); });
317
+ i0.ɵɵelement(19, "i", 22);
318
+ i0.ɵɵtext(20, " Show All Deciles ");
319
+ i0.ɵɵelementEnd()()();
320
+ i0.ɵɵelementStart(21, "div", 81)(22, "ag-grid-angular", 82);
321
+ i0.ɵɵlistener("gridReady", function PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_30_Template_ag_grid_angular_gridReady_22_listener($event) { i0.ɵɵrestoreView(_r15); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.onChartGridReady($event)); })("rowClicked", function PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_30_Template_ag_grid_angular_rowClicked_22_listener($event) { i0.ɵɵrestoreView(_r15); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.onRowClicked($event)); })("cellClicked", function PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_30_Template_ag_grid_angular_cellClicked_22_listener($event) { i0.ɵɵrestoreView(_r15); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.onCellClicked($event)); });
322
+ i0.ɵɵelementEnd()();
323
+ i0.ɵɵelementStart(23, "div", 83)(24, "span", 35);
324
+ i0.ɵɵtext(25, "Click any row to inspect explainability drivers and score history in the slide-in drawer");
325
+ i0.ɵɵelementEnd()()();
326
+ } if (rf & 2) {
327
+ const ctx_r1 = i0.ɵɵnextContext(3);
328
+ i0.ɵɵadvance(7);
329
+ i0.ɵɵtextInterpolate(ctx_r1.selectedDecileLabel);
330
+ i0.ɵɵadvance();
331
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.outcomeConfig.ScoreLabel, " ");
332
+ i0.ɵɵadvance();
333
+ i0.ɵɵconditional(ctx_r1.histogramBins[ctx_r1.selectedDecileIndex] ? 9 : -1);
334
+ i0.ɵɵadvance(5);
335
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(15, 10, ctx_r1.filteredRows.length));
336
+ i0.ɵɵadvance(2);
337
+ i0.ɵɵtextInterpolate1(" of ", i0.ɵɵpipeBind1(17, 12, ctx_r1.allRows.length), " records ");
338
+ i0.ɵɵadvance(6);
339
+ i0.ɵɵproperty("theme", ctx_r1.Theme)("columnDefs", ctx_r1.columnDefs)("defaultColDef", ctx_r1.defaultColDef)("gridOptions", ctx_r1.chartGridOptions)("rowData", ctx_r1.filteredRows);
340
+ } }
341
+ function PSPredictionsGridComponent_Conditional_25_Conditional_1_Template(rf, ctx) { if (rf & 1) {
342
+ i0.ɵɵelementStart(0, "div", 32)(1, "div", 36)(2, "div", 37)(3, "span", 38);
343
+ i0.ɵɵelement(4, "i", 39);
344
+ i0.ɵɵtext(5, " Scored Cohort");
345
+ i0.ɵɵelementEnd();
346
+ i0.ɵɵelementStart(6, "span", 40);
347
+ i0.ɵɵtext(7);
348
+ i0.ɵɵpipe(8, "number");
349
+ i0.ɵɵelementEnd();
350
+ i0.ɵɵelementStart(9, "span", 41);
351
+ i0.ɵɵtext(10, "Total records");
352
+ i0.ɵɵelementEnd()();
353
+ i0.ɵɵrepeaterCreate(11, PSPredictionsGridComponent_Conditional_25_Conditional_1_For_12_Template, 9, 12, "div", 42, _forTrack0);
354
+ i0.ɵɵelementEnd();
355
+ i0.ɵɵelementStart(13, "div", 43)(14, "div", 44)(15, "div", 45);
356
+ i0.ɵɵelement(16, "i", 46);
357
+ i0.ɵɵelementStart(17, "span");
358
+ i0.ɵɵtext(18);
359
+ i0.ɵɵelementEnd()();
360
+ i0.ɵɵelementStart(19, "div", 47)(20, "span", 48);
361
+ i0.ɵɵtext(21, "Cohort Mean: ");
362
+ i0.ɵɵelementStart(22, "strong");
363
+ i0.ɵɵtext(23);
364
+ i0.ɵɵelementEnd()();
365
+ i0.ɵɵconditionalCreate(24, PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_24_Template, 3, 0, "button", 49);
366
+ i0.ɵɵelementEnd()();
367
+ i0.ɵɵelementStart(25, "div", 50)(26, "div", 51);
368
+ i0.ɵɵrepeaterCreate(27, PSPredictionsGridComponent_Conditional_25_Conditional_1_For_28_Template, 24, 24, "div", 52, _forTrack1);
369
+ i0.ɵɵelementEnd()()();
370
+ i0.ɵɵconditionalCreate(29, PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_29_Template, 11, 0, "div", 43);
371
+ i0.ɵɵconditionalCreate(30, PSPredictionsGridComponent_Conditional_25_Conditional_1_Conditional_30_Template, 26, 14, "div", 53);
372
+ i0.ɵɵelementEnd();
373
+ } if (rf & 2) {
374
+ const ctx_r1 = i0.ɵɵnextContext(2);
375
+ i0.ɵɵadvance(7);
376
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(8, 6, ctx_r1.allRows.length));
377
+ i0.ɵɵadvance(4);
378
+ i0.ɵɵrepeater(ctx_r1.visibleBands);
379
+ i0.ɵɵadvance(7);
380
+ i0.ɵɵtextInterpolate1("", ctx_r1.outcomeConfig.ScoreLabel, " Distribution (10 Bins)");
381
+ i0.ɵɵadvance(5);
382
+ i0.ɵɵtextInterpolate(ctx_r1.meanScoreFormatted);
383
+ i0.ɵɵadvance();
384
+ i0.ɵɵconditional(ctx_r1.selectedDecileIndex !== null ? 24 : -1);
385
+ i0.ɵɵadvance(3);
386
+ i0.ɵɵrepeater(ctx_r1.histogramBins);
387
+ i0.ɵɵadvance(2);
388
+ i0.ɵɵconditional(ctx_r1.classBreakdown.length > 0 ? 29 : -1);
389
+ i0.ɵɵadvance();
390
+ i0.ɵɵconditional(ctx_r1.selectedDecileIndex !== null ? 30 : -1);
391
+ } }
392
+ function PSPredictionsGridComponent_Conditional_25_Template(rf, ctx) { if (rf & 1) {
393
+ i0.ɵɵconditionalCreate(0, PSPredictionsGridComponent_Conditional_25_Conditional_0_Template, 10, 8);
394
+ i0.ɵɵconditionalCreate(1, PSPredictionsGridComponent_Conditional_25_Conditional_1_Template, 31, 8, "div", 32);
395
+ } if (rf & 2) {
396
+ const ctx_r1 = i0.ɵɵnextContext();
397
+ i0.ɵɵconditional(ctx_r1.activeView === "grid" ? 0 : -1);
398
+ i0.ɵɵadvance();
399
+ i0.ɵɵconditional(ctx_r1.activeView === "chart" ? 1 : -1);
400
+ } }
401
+ function PSPredictionsGridComponent_Conditional_26_Conditional_15_Template(rf, ctx) { if (rf & 1) {
402
+ const _r17 = i0.ɵɵgetCurrentView();
403
+ i0.ɵɵelementStart(0, "button", 110);
404
+ i0.ɵɵlistener("click", function PSPredictionsGridComponent_Conditional_26_Conditional_15_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.openSubjectRecord()); });
405
+ i0.ɵɵelement(1, "i", 111);
406
+ i0.ɵɵtext(2);
407
+ i0.ɵɵelementEnd();
408
+ } if (rf & 2) {
409
+ const ctx_r1 = i0.ɵɵnextContext(2);
410
+ i0.ɵɵadvance(2);
411
+ i0.ɵɵtextInterpolate2(" Open ", ctx_r1.subjectEntityName.replace("MJ_BizApps_Common: ", "").replace("MJ_BizApps_", ""), " (", ctx_r1.subjectRecordName || "Profile", ") ");
412
+ } }
413
+ function PSPredictionsGridComponent_Conditional_26_Conditional_24_Template(rf, ctx) { if (rf & 1) {
414
+ i0.ɵɵelement(0, "i");
415
+ } if (rf & 2) {
416
+ const ctx_r1 = i0.ɵɵnextContext(2);
417
+ i0.ɵɵclassMap(i0.ɵɵinterpolate1("fa-solid ", ctx_r1.selectedRow.icon));
418
+ } }
419
+ function PSPredictionsGridComponent_Conditional_26_Conditional_26_Template(rf, ctx) { if (rf & 1) {
420
+ i0.ɵɵelementStart(0, "div", 98)(1, "strong");
421
+ i0.ɵɵtext(2, "Predicted Outcome:");
422
+ i0.ɵɵelementEnd();
423
+ i0.ɵɵelementStart(3, "span", 112);
424
+ i0.ɵɵtext(4);
425
+ i0.ɵɵelementEnd()();
426
+ } if (rf & 2) {
427
+ const ctx_r1 = i0.ɵɵnextContext(2);
428
+ i0.ɵɵadvance(3);
429
+ i0.ɵɵclassMap(ctx_r1.getOutcomeBadgeClass(ctx_r1.selectedRow.class));
430
+ i0.ɵɵadvance();
431
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.selectedRow.class, " ");
432
+ } }
433
+ function PSPredictionsGridComponent_Conditional_26_Conditional_39_Template(rf, ctx) { if (rf & 1) {
434
+ i0.ɵɵelementStart(0, "div", 48);
435
+ i0.ɵɵtext(1, "No individual driver breakdown was recorded for this row.");
436
+ i0.ɵɵelementEnd();
437
+ } }
438
+ function PSPredictionsGridComponent_Conditional_26_Conditional_40_For_2_Template(rf, ctx) { if (rf & 1) {
439
+ i0.ɵɵelementStart(0, "div", 113)(1, "div", 114);
440
+ i0.ɵɵelement(2, "i", 115);
441
+ i0.ɵɵtext(3);
442
+ i0.ɵɵelementEnd();
443
+ i0.ɵɵelementStart(4, "span", 112);
444
+ i0.ɵɵtext(5);
445
+ i0.ɵɵelementEnd()();
446
+ } if (rf & 2) {
447
+ const d_r18 = ctx.$implicit;
448
+ i0.ɵɵadvance(2);
449
+ i0.ɵɵstyleProp("color", d_r18.up ? "var(--mj-status-error)" : "var(--mj-status-success)");
450
+ i0.ɵɵclassProp("fa-arrow-up", d_r18.up)("fa-arrow-down", !d_r18.up);
451
+ i0.ɵɵadvance();
452
+ i0.ɵɵtextInterpolate1(" ", d_r18.label, " ");
453
+ i0.ɵɵadvance();
454
+ i0.ɵɵclassMap(d_r18.up ? "red" : "green");
455
+ i0.ɵɵadvance();
456
+ i0.ɵɵtextInterpolate1(" ", d_r18.up ? "Increases Risk" : "Lowers Risk", " ");
457
+ } }
458
+ function PSPredictionsGridComponent_Conditional_26_Conditional_40_Template(rf, ctx) { if (rf & 1) {
459
+ i0.ɵɵelementStart(0, "div", 105);
460
+ i0.ɵɵrepeaterCreate(1, PSPredictionsGridComponent_Conditional_26_Conditional_40_For_2_Template, 6, 10, "div", 113, _forTrack1);
461
+ i0.ɵɵelementEnd();
462
+ } if (rf & 2) {
463
+ const ctx_r1 = i0.ɵɵnextContext(2);
464
+ i0.ɵɵadvance();
465
+ i0.ɵɵrepeater(ctx_r1.selectedRow.drivers);
466
+ } }
467
+ function PSPredictionsGridComponent_Conditional_26_Conditional_48_Template(rf, ctx) { if (rf & 1) {
468
+ i0.ɵɵelement(0, "mj-loading", 109);
469
+ } }
470
+ function PSPredictionsGridComponent_Conditional_26_Conditional_49_For_13_Template(rf, ctx) { if (rf & 1) {
471
+ i0.ɵɵnamespaceSVG();
472
+ i0.ɵɵelementStart(0, "circle", 126)(1, "title");
473
+ i0.ɵɵtext(2);
474
+ i0.ɵɵelementEnd()();
475
+ i0.ɵɵelementStart(3, "text", 127);
476
+ i0.ɵɵtext(4);
477
+ i0.ɵɵelementEnd();
478
+ } if (rf & 2) {
479
+ const p_r19 = ctx.$implicit;
480
+ i0.ɵɵattribute("cx", p_r19.x)("cy", p_r19.y)("fill", p_r19.color);
481
+ i0.ɵɵadvance(2);
482
+ i0.ɵɵtextInterpolate3("", p_r19.dateStr, ": ", p_r19.scoreFormatted, " (", p_r19.class || "No class", ")");
483
+ i0.ɵɵadvance();
484
+ i0.ɵɵattribute("cx", p_r19.x)("cy", p_r19.y - 9);
485
+ i0.ɵɵadvance();
486
+ i0.ɵɵtextInterpolate1(" ", p_r19.scoreFormatted, " ");
487
+ } }
488
+ function PSPredictionsGridComponent_Conditional_26_Conditional_49_For_29_Conditional_11_Template(rf, ctx) { if (rf & 1) {
489
+ i0.ɵɵelementStart(0, "span", 129);
490
+ i0.ɵɵtext(1, "Baseline");
491
+ i0.ɵɵelementEnd();
492
+ } }
493
+ function PSPredictionsGridComponent_Conditional_26_Conditional_49_For_29_Conditional_12_Template(rf, ctx) { if (rf & 1) {
494
+ i0.ɵɵelementStart(0, "span", 130);
495
+ i0.ɵɵelement(1, "i", 132);
496
+ i0.ɵɵtext(2);
497
+ i0.ɵɵelementEnd();
498
+ } if (rf & 2) {
499
+ const pt_r20 = i0.ɵɵnextContext().$implicit;
500
+ i0.ɵɵadvance(2);
501
+ i0.ɵɵtextInterpolate1(" +", (pt_r20.delta * 100).toFixed(1), "%");
502
+ } }
503
+ function PSPredictionsGridComponent_Conditional_26_Conditional_49_For_29_Conditional_13_Template(rf, ctx) { if (rf & 1) {
504
+ i0.ɵɵelementStart(0, "span", 131);
505
+ i0.ɵɵelement(1, "i", 133);
506
+ i0.ɵɵtext(2);
507
+ i0.ɵɵelementEnd();
508
+ } if (rf & 2) {
509
+ const pt_r20 = i0.ɵɵnextContext().$implicit;
510
+ i0.ɵɵadvance(2);
511
+ i0.ɵɵtextInterpolate1(" ", (pt_r20.delta * 100).toFixed(1), "%");
512
+ } }
513
+ function PSPredictionsGridComponent_Conditional_26_Conditional_49_For_29_Conditional_14_Template(rf, ctx) { if (rf & 1) {
514
+ i0.ɵɵelementStart(0, "span", 129);
515
+ i0.ɵɵtext(1, "0%");
516
+ i0.ɵɵelementEnd();
517
+ } }
518
+ function PSPredictionsGridComponent_Conditional_26_Conditional_49_For_29_Template(rf, ctx) { if (rf & 1) {
519
+ i0.ɵɵelementStart(0, "tr")(1, "td");
520
+ i0.ɵɵtext(2);
521
+ i0.ɵɵpipe(3, "date");
522
+ i0.ɵɵelementEnd();
523
+ i0.ɵɵelementStart(4, "td")(5, "span", 112);
524
+ i0.ɵɵtext(6);
525
+ i0.ɵɵelementEnd()();
526
+ i0.ɵɵelementStart(7, "td")(8, "strong", 128);
527
+ i0.ɵɵtext(9);
528
+ i0.ɵɵelementEnd()();
529
+ i0.ɵɵelementStart(10, "td");
530
+ i0.ɵɵconditionalCreate(11, PSPredictionsGridComponent_Conditional_26_Conditional_49_For_29_Conditional_11_Template, 2, 0, "span", 129)(12, PSPredictionsGridComponent_Conditional_26_Conditional_49_For_29_Conditional_12_Template, 3, 1, "span", 130)(13, PSPredictionsGridComponent_Conditional_26_Conditional_49_For_29_Conditional_13_Template, 3, 1, "span", 131)(14, PSPredictionsGridComponent_Conditional_26_Conditional_49_For_29_Conditional_14_Template, 2, 0, "span", 129);
531
+ i0.ɵɵelementEnd();
532
+ i0.ɵɵelementStart(15, "td");
533
+ i0.ɵɵtext(16);
534
+ i0.ɵɵelementEnd()();
535
+ } if (rf & 2) {
536
+ const pt_r20 = ctx.$implicit;
537
+ i0.ɵɵadvance(2);
538
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(3, 7, pt_r20.runDate, "short"));
539
+ i0.ɵɵadvance(3);
540
+ i0.ɵɵclassMap(pt_r20.status === "Succeeded" ? "green" : "gray");
541
+ i0.ɵɵadvance();
542
+ i0.ɵɵtextInterpolate(pt_r20.status);
543
+ i0.ɵɵadvance(3);
544
+ i0.ɵɵtextInterpolate(pt_r20.scoreFormatted);
545
+ i0.ɵɵadvance(2);
546
+ i0.ɵɵconditional(pt_r20.delta == null ? 11 : pt_r20.delta > 0.005 ? 12 : pt_r20.delta < -0.005 ? 13 : 14);
547
+ i0.ɵɵadvance(5);
548
+ i0.ɵɵtextInterpolate(pt_r20.class || "\u2014");
549
+ } }
550
+ function PSPredictionsGridComponent_Conditional_26_Conditional_49_Template(rf, ctx) { if (rf & 1) {
551
+ i0.ɵɵnamespaceSVG();
552
+ i0.ɵɵelementStart(0, "svg", 116);
553
+ i0.ɵɵelement(1, "line", 117)(2, "line", 118)(3, "line", 119);
554
+ i0.ɵɵelementStart(4, "text", 120);
555
+ i0.ɵɵtext(5, "100%");
556
+ i0.ɵɵelementEnd();
557
+ i0.ɵɵelementStart(6, "text", 121);
558
+ i0.ɵɵtext(7, "50%");
559
+ i0.ɵɵelementEnd();
560
+ i0.ɵɵelementStart(8, "text", 122);
561
+ i0.ɵɵtext(9, "0%");
562
+ i0.ɵɵelementEnd();
563
+ i0.ɵɵelement(10, "polygon", 123)(11, "polyline", 124);
564
+ i0.ɵɵrepeaterCreate(12, PSPredictionsGridComponent_Conditional_26_Conditional_49_For_13_Template, 5, 9, null, null, _forTrack3);
565
+ i0.ɵɵelementEnd();
566
+ i0.ɵɵnamespaceHTML();
567
+ i0.ɵɵelementStart(14, "table", 125)(15, "thead")(16, "tr")(17, "th");
568
+ i0.ɵɵtext(18, "When");
569
+ i0.ɵɵelementEnd();
570
+ i0.ɵɵelementStart(19, "th");
571
+ i0.ɵɵtext(20, "Status");
572
+ i0.ɵɵelementEnd();
573
+ i0.ɵɵelementStart(21, "th");
574
+ i0.ɵɵtext(22, "Score");
575
+ i0.ɵɵelementEnd();
576
+ i0.ɵɵelementStart(23, "th");
577
+ i0.ɵɵtext(24, "Change");
578
+ i0.ɵɵelementEnd();
579
+ i0.ɵɵelementStart(25, "th");
580
+ i0.ɵɵtext(26, "Class");
581
+ i0.ɵɵelementEnd()()();
582
+ i0.ɵɵelementStart(27, "tbody");
583
+ i0.ɵɵrepeaterCreate(28, PSPredictionsGridComponent_Conditional_26_Conditional_49_For_29_Template, 17, 10, "tr", null, _forTrack3);
584
+ i0.ɵɵelementEnd()();
585
+ } if (rf & 2) {
586
+ const ctx_r1 = i0.ɵɵnextContext(2);
587
+ i0.ɵɵadvance(10);
588
+ i0.ɵɵattribute("points", ctx_r1.trendlineAreaPoints);
589
+ i0.ɵɵadvance();
590
+ i0.ɵɵattribute("points", ctx_r1.trendlineLinePoints);
591
+ i0.ɵɵadvance();
592
+ i0.ɵɵrepeater(ctx_r1.trendlinePlotPoints);
593
+ i0.ɵɵadvance(16);
594
+ i0.ɵɵrepeater(ctx_r1.historyPoints);
595
+ } }
596
+ function PSPredictionsGridComponent_Conditional_26_Conditional_50_Template(rf, ctx) { if (rf & 1) {
597
+ i0.ɵɵelementStart(0, "div", 48);
598
+ i0.ɵɵtext(1);
599
+ i0.ɵɵpipe(2, "date");
600
+ i0.ɵɵelementEnd();
601
+ } if (rf & 2) {
602
+ const ctx_r1 = i0.ɵɵnextContext(2);
603
+ i0.ɵɵadvance();
604
+ i0.ɵɵtextInterpolate1(" This record has been scored once so far (", i0.ɵɵpipeBind2(2, 1, ctx_r1.historyPoints[0].runDate, "medium"), "). As more runs occur over time, the score trendline will track changes here. ");
605
+ } }
606
+ function PSPredictionsGridComponent_Conditional_26_Conditional_51_Template(rf, ctx) { if (rf & 1) {
607
+ i0.ɵɵelementStart(0, "div", 48);
608
+ i0.ɵɵtext(1, "No historical runs found for this record.");
609
+ i0.ɵɵelementEnd();
610
+ } }
611
+ function PSPredictionsGridComponent_Conditional_26_Template(rf, ctx) { if (rf & 1) {
612
+ const _r16 = i0.ɵɵgetCurrentView();
613
+ i0.ɵɵelementStart(0, "mj-slide-panel", 85);
614
+ i0.ɵɵlistener("WidthChanged", function PSPredictionsGridComponent_Conditional_26_Template_mj_slide_panel_WidthChanged_0_listener($event) { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onDrawerWidthChanged($event)); })("Closed", function PSPredictionsGridComponent_Conditional_26_Template_mj_slide_panel_Closed_0_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.closeDetail()); });
615
+ i0.ɵɵelementStart(1, "div", 86)(2, "div", 87)(3, "div", 88)(4, "h3");
616
+ i0.ɵɵtext(5);
617
+ i0.ɵɵelementEnd();
618
+ i0.ɵɵelementStart(6, "div", 45)(7, "span", 89);
619
+ i0.ɵɵtext(8);
620
+ i0.ɵɵelementEnd();
621
+ i0.ɵɵelementStart(9, "span", 90);
622
+ i0.ɵɵtext(10);
623
+ i0.ɵɵelementEnd()()();
624
+ i0.ɵɵelementStart(11, "div", 91)(12, "button", 92);
625
+ i0.ɵɵlistener("click", function PSPredictionsGridComponent_Conditional_26_Template_button_click_12_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.drillThrough(ctx_r1.selectedRow)); });
626
+ i0.ɵɵelement(13, "i", 93);
627
+ i0.ɵɵtext(14);
628
+ i0.ɵɵelementEnd();
629
+ i0.ɵɵconditionalCreate(15, PSPredictionsGridComponent_Conditional_26_Conditional_15_Template, 3, 2, "button", 94);
630
+ i0.ɵɵelementEnd()();
631
+ i0.ɵɵelementStart(16, "div", 95)(17, "div", 96);
632
+ i0.ɵɵtext(18);
633
+ i0.ɵɵelementEnd();
634
+ i0.ɵɵelementStart(19, "div", 97)(20, "div", 98)(21, "strong");
635
+ i0.ɵɵtext(22);
636
+ i0.ɵɵelementEnd();
637
+ i0.ɵɵelementStart(23, "span", 84);
638
+ i0.ɵɵconditionalCreate(24, PSPredictionsGridComponent_Conditional_26_Conditional_24_Template, 1, 3, "i", 99);
639
+ i0.ɵɵtext(25);
640
+ i0.ɵɵelementEnd()();
641
+ i0.ɵɵconditionalCreate(26, PSPredictionsGridComponent_Conditional_26_Conditional_26_Template, 5, 3, "div", 98);
642
+ i0.ɵɵelementStart(27, "div", 98)(28, "strong");
643
+ i0.ɵɵtext(29);
644
+ i0.ɵɵelementEnd();
645
+ i0.ɵɵelementStart(30, "span", 100);
646
+ i0.ɵɵtext(31);
647
+ i0.ɵɵelementEnd()();
648
+ i0.ɵɵelementStart(32, "div", 101);
649
+ i0.ɵɵtext(33);
650
+ i0.ɵɵpipe(34, "date");
651
+ i0.ɵɵelementEnd()()();
652
+ i0.ɵɵelementStart(35, "div", 102)(36, "div", 103);
653
+ i0.ɵɵelement(37, "i", 104);
654
+ i0.ɵɵtext(38, " Key Decision Drivers (Explainability) ");
655
+ i0.ɵɵelementEnd();
656
+ i0.ɵɵconditionalCreate(39, PSPredictionsGridComponent_Conditional_26_Conditional_39_Template, 2, 0, "div", 48)(40, PSPredictionsGridComponent_Conditional_26_Conditional_40_Template, 3, 0, "div", 105);
657
+ i0.ɵɵelementEnd();
658
+ i0.ɵɵelementStart(41, "div", 106)(42, "div", 107)(43, "span");
659
+ i0.ɵɵelement(44, "i", 108);
660
+ i0.ɵɵtext(45, " Historical Score Trendline");
661
+ i0.ɵɵelementEnd();
662
+ i0.ɵɵelementStart(46, "span", 48);
663
+ i0.ɵɵtext(47);
664
+ i0.ɵɵelementEnd()();
665
+ i0.ɵɵconditionalCreate(48, PSPredictionsGridComponent_Conditional_26_Conditional_48_Template, 1, 0, "mj-loading", 109)(49, PSPredictionsGridComponent_Conditional_26_Conditional_49_Template, 30, 2)(50, PSPredictionsGridComponent_Conditional_26_Conditional_50_Template, 3, 4, "div", 48)(51, PSPredictionsGridComponent_Conditional_26_Conditional_51_Template, 2, 0, "div", 48);
666
+ i0.ɵɵelementEnd()()();
667
+ } if (rf & 2) {
668
+ const ctx_r1 = i0.ɵɵnextContext();
669
+ i0.ɵɵproperty("Resizable", true)("WidthPx", ctx_r1.drawerWidthPx);
670
+ i0.ɵɵadvance(5);
671
+ i0.ɵɵtextInterpolate(ctx_r1.selectedRow.recordName || ctx_r1.selectedRow.recordId);
672
+ i0.ɵɵadvance(3);
673
+ i0.ɵɵtextInterpolate(ctx_r1.entityDisplayName || "Entity Record");
674
+ i0.ɵɵadvance(2);
675
+ i0.ɵɵtextInterpolate(ctx_r1.selectedRow.recordId);
676
+ i0.ɵɵadvance(4);
677
+ i0.ɵɵtextInterpolate1(" Open ", ctx_r1.entityDisplayName || "Record", " ");
678
+ i0.ɵɵadvance();
679
+ i0.ɵɵconditional(ctx_r1.subjectEntityName && ctx_r1.subjectRecordId ? 15 : -1);
680
+ i0.ɵɵadvance(2);
681
+ i0.ɵɵclassMap("badge-" + (ctx_r1.selectedRow.badgeColor || "gray"));
682
+ i0.ɵɵadvance();
683
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.selectedRow.scoreFormatted, " ");
684
+ i0.ɵɵadvance(4);
685
+ i0.ɵɵtextInterpolate1("", ctx_r1.outcomeConfig.StatusLabel, ":");
686
+ i0.ɵɵadvance();
687
+ i0.ɵɵclassMap(ctx_r1.selectedRow.badgeColor || "gray");
688
+ i0.ɵɵadvance();
689
+ i0.ɵɵconditional(ctx_r1.selectedRow.icon ? 24 : -1);
690
+ i0.ɵɵadvance();
691
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.selectedRow.status || ctx_r1.selectedRow.band, " ");
692
+ i0.ɵɵadvance();
693
+ i0.ɵɵconditional(ctx_r1.selectedRow.class ? 26 : -1);
694
+ i0.ɵɵadvance(3);
695
+ i0.ɵɵtextInterpolate1("", ctx_r1.outcomeConfig.ScoreLabel, ":");
696
+ i0.ɵɵadvance(2);
697
+ i0.ɵɵtextInterpolate(ctx_r1.selectedRow.scoreFormatted);
698
+ i0.ɵɵadvance(2);
699
+ i0.ɵɵtextInterpolate1(" Scored: ", ctx_r1.selectedRow.scoredAtDate ? i0.ɵɵpipeBind2(34, 23, ctx_r1.selectedRow.scoredAtDate, "medium") : "\u2014", " ");
700
+ i0.ɵɵadvance(6);
701
+ i0.ɵɵconditional(!ctx_r1.selectedRow.drivers || ctx_r1.selectedRow.drivers.length === 0 ? 39 : 40);
702
+ i0.ɵɵadvance(8);
703
+ i0.ɵɵtextInterpolate2("", ctx_r1.historyPoints.length, " run", ctx_r1.historyPoints.length === 1 ? "" : "s", " recorded");
704
+ i0.ɵɵadvance();
705
+ i0.ɵɵconditional(ctx_r1.historyLoading ? 48 : ctx_r1.historyPoints.length > 1 ? 49 : ctx_r1.historyPoints.length === 1 ? 50 : 51);
706
+ } }
707
+ ModuleRegistry.registerModules([AllCommunityModule]);
708
+ /**
709
+ * **PSPredictionsGridComponent** — Unified enterprise grid and analytics component for
710
+ * model predictions. Shared between:
711
+ * 1. **Predictions tab** (`PSPredictionsResourceComponent`) — business view of all at-risk records.
712
+ * 2. **Models tab** (`PSProductionComponent`) — analyst run drill-in view.
713
+ *
714
+ * Supports:
715
+ * - Virtualized AG Grid scrolling for thousands of rows (no artificial 50/200 cap).
716
+ * - Automatic target entity record display name resolution (e.g. member/customer names).
717
+ * - Direct drill-through to the entity record in MemberJunction Explorer.
718
+ * - Slide-in detail drawer (`mj-slide-panel`) with local explainability and historical trendlines across runs.
719
+ * - Segmented view toggle (`mj-view-toggle`) between Grid View and Chart View (10-decile histogram + risk tiers).
720
+ */
721
+ export class PSPredictionsGridComponent extends BaseAngularComponent {
722
+ modelId = null;
723
+ runId = null;
724
+ entityName = null;
725
+ problemType = 'classification';
726
+ title = null;
727
+ height = '560px';
728
+ readOnly = false;
729
+ cdr = inject(ChangeDetectorRef);
730
+ navigationService = inject(NavigationService);
731
+ scoreHistoryService = inject(PredictiveStudioScoreHistoryService);
732
+ Math = Math;
733
+ isLoading = false;
734
+ loadError = null;
735
+ allRows = [];
736
+ filteredRows = [];
737
+ searchText = '';
738
+ selectedTier = 'all';
739
+ outcomeConfig = resolveOutcomeConfig();
740
+ visibleBands = [];
741
+ activeView = 'grid';
742
+ viewOptions = [
743
+ { key: 'grid', icon: 'fa-solid fa-table', title: 'Grid View' },
744
+ { key: 'chart', icon: 'fa-solid fa-chart-column', title: 'Chart View' },
745
+ ];
746
+ // Slide-in detail state
747
+ detailOpen = false;
748
+ drawerWidthPx = 640;
749
+ selectedRow = null;
750
+ historyLoading = false;
751
+ historyPoints = [];
752
+ resolvedEntityId = null;
753
+ resolvedEntityName = null;
754
+ subjectEntityName = null;
755
+ subjectRecordId = null;
756
+ subjectRecordName = null;
757
+ gridApi = null;
758
+ chartGridApi = null;
759
+ // Viewport-visible record name lookup cache and state
760
+ recordNameCache = new Map();
761
+ inFlightLookups = new Set();
762
+ visibleLookupDebounceTimer = null;
763
+ // ── AG Grid Configuration ──
764
+ Theme = themeAlpine.withParams({
765
+ backgroundColor: 'var(--mj-bg-surface)',
766
+ foregroundColor: 'var(--mj-text-primary)',
767
+ textColor: 'var(--mj-text-primary)',
768
+ borderColor: 'var(--mj-border-default)',
769
+ chromeBackgroundColor: 'var(--mj-bg-surface-card)',
770
+ headerBackgroundColor: 'var(--mj-bg-surface-card)',
771
+ headerTextColor: 'var(--mj-text-secondary)',
772
+ cellTextColor: 'var(--mj-text-primary)',
773
+ subtleTextColor: 'var(--mj-text-muted)',
774
+ dataBackgroundColor: 'var(--mj-bg-surface)',
775
+ oddRowBackgroundColor: 'var(--mj-bg-surface-card)',
776
+ rowHoverColor: 'var(--mj-bg-surface-hover, color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface)))',
777
+ selectedRowBackgroundColor: 'color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface))',
778
+ accentColor: 'var(--mj-brand-primary)',
779
+ borderRadius: 'var(--mj-radius-sm)',
780
+ browserColorScheme: 'inherit',
781
+ });
782
+ GridOptions = {
783
+ animateRows: true,
784
+ rowHeight: 46,
785
+ headerHeight: 40,
786
+ suppressCellFocus: true,
787
+ enableCellTextSelection: true,
788
+ suppressNoRowsOverlay: true,
789
+ rowSelection: { mode: 'singleRow', enableClickSelection: false },
790
+ getRowId: (params) => params.data.recordId,
791
+ onBodyScrollEnd: () => this.scheduleVisibleRowsLookup(),
792
+ onViewportChanged: () => this.scheduleVisibleRowsLookup(),
793
+ onFirstDataRendered: () => this.scheduleVisibleRowsLookup(),
794
+ onModelUpdated: () => this.scheduleVisibleRowsLookup(),
795
+ onCellClicked: (event) => this.onCellClicked(event),
796
+ };
797
+ chartGridOptions = {
798
+ animateRows: true,
799
+ rowHeight: 46,
800
+ headerHeight: 40,
801
+ suppressCellFocus: true,
802
+ enableCellTextSelection: true,
803
+ suppressNoRowsOverlay: true,
804
+ rowSelection: { mode: 'singleRow', enableClickSelection: false },
805
+ getRowId: (params) => params.data.recordId,
806
+ onBodyScrollEnd: () => this.scheduleVisibleRowsLookup(),
807
+ onViewportChanged: () => this.scheduleVisibleRowsLookup(),
808
+ onFirstDataRendered: () => this.scheduleVisibleRowsLookup(),
809
+ onModelUpdated: () => this.scheduleVisibleRowsLookup(),
810
+ onCellClicked: (event) => this.onCellClicked(event),
811
+ };
812
+ defaultColDef = {
813
+ sortable: true,
814
+ resizable: true,
815
+ minWidth: 80,
816
+ };
817
+ isRenewalModel = false;
818
+ selectedDecileIndex = null;
819
+ get isRegression() {
820
+ return ((this.problemType ?? '').toLowerCase() === 'regression' ||
821
+ this.outcomeConfig?.Format === 'currency' ||
822
+ this.outcomeConfig?.Format === 'number');
823
+ }
824
+ columnDefs = [];
825
+ setupColumnDefs() {
826
+ const scoreLabel = this.outcomeConfig.ScoreLabel || 'Prediction Score';
827
+ const statusLabel = this.outcomeConfig.StatusLabel || 'Risk Level';
828
+ this.columnDefs = [
829
+ {
830
+ field: 'recordName',
831
+ headerName: 'Record',
832
+ flex: 2,
833
+ minWidth: 220,
834
+ cellRenderer: (params) => {
835
+ if (!params.data)
836
+ return '';
837
+ const hasCustomName = Boolean(params.data.recordName && params.data.recordName !== params.data.recordId);
838
+ const name = this.escapeHtml(params.data.recordName || params.data.recordId);
839
+ const id = this.escapeHtml(params.data.recordId);
840
+ return `
841
+ <div class="pg-record-cell">
842
+ <div class="pg-record-title-row">
843
+ <a class="pg-record-link" data-action="open-record" href="javascript:void(0)" title="Open record in Explorer">
844
+ <span class="pg-record-name">${name}</span>
845
+ <i class="fa-solid fa-arrow-up-right-from-square pg-record-open-icon"></i>
846
+ </a>
847
+ </div>
848
+ ${hasCustomName ? `<span class="pg-record-id ps-mono" title="${id}">${id}</span>` : ''}
849
+ </div>
850
+ `;
851
+ },
852
+ },
853
+ {
854
+ field: 'score',
855
+ headerName: scoreLabel,
856
+ width: 175,
857
+ cellRenderer: (params) => {
858
+ if (!params.data)
859
+ return '';
860
+ const badgeColor = params.data.badgeColor || 'gray';
861
+ const colorClass = `badge-${badgeColor}`;
862
+ const formatted = this.escapeHtml(params.data.scoreFormatted);
863
+ if (this.isRegression) {
864
+ return `
865
+ <div class="pg-score-cell">
866
+ <span class="pg-score-val ${colorClass}" style="font-weight: 600; font-size: 13.5px;">${formatted}</span>
867
+ </div>
868
+ `;
869
+ }
870
+ const pct = Math.min(100, Math.max(0, Math.round(params.data.score * 100)));
871
+ return `
872
+ <div class="pg-score-cell">
873
+ <span class="pg-score-val ${colorClass}">${formatted}</span>
874
+ <div class="pg-score-bar">
875
+ <div class="pg-score-fill ${colorClass}" style="width: ${pct}%"></div>
876
+ </div>
877
+ </div>
878
+ `;
879
+ },
880
+ },
881
+ {
882
+ field: 'riskPct',
883
+ headerName: statusLabel,
884
+ width: 155,
885
+ cellRenderer: (params) => {
886
+ if (!params.data)
887
+ return '';
888
+ const badgeColor = params.data.badgeColor || 'gray';
889
+ const icon = params.data.icon ? `<i class="fa-solid ${this.escapeHtml(params.data.icon)}"></i> ` : '';
890
+ const label = this.escapeHtml(params.data.status || params.data.band);
891
+ if (this.isRegression) {
892
+ return `<span class="ps-badge ${badgeColor}" title="${scoreLabel}: ${this.escapeHtml(params.data.scoreFormatted)}">${icon}${label}</span>`;
893
+ }
894
+ const pct = Math.round(params.data.score * 100);
895
+ return `<span class="ps-badge ${badgeColor}" title="${pct}% ${scoreLabel}">${icon}${label} (${pct}%)</span>`;
896
+ },
897
+ },
898
+ {
899
+ field: 'class',
900
+ headerName: 'Predicted Outcome',
901
+ width: 135,
902
+ cellRenderer: (params) => {
903
+ if (!params.data || !params.data.class)
904
+ return '<span class="ps-muted">—</span>';
905
+ const cls = this.escapeHtml(params.data.class);
906
+ const style = resolveOutcomeStyle(params.data.class, this.outcomeConfig);
907
+ const icon = style.Icon ? `<i class="fa-solid ${style.Icon}"></i> ` : '';
908
+ return `<span class="ps-badge ${style.BadgeColor}">${icon}${cls}</span>`;
909
+ },
910
+ },
911
+ {
912
+ field: 'drivers',
913
+ headerName: 'Key Factors',
914
+ flex: 2,
915
+ minWidth: 220,
916
+ sortable: false,
917
+ cellDataType: false,
918
+ cellRenderer: (params) => {
919
+ if (!params.data || !params.data.drivers || params.data.drivers.length === 0) {
920
+ return '<span class="ps-muted">—</span>';
921
+ }
922
+ const chips = params.data.drivers.slice(0, 2).map((d) => {
923
+ const cls = d.up ? 'up' : 'down';
924
+ const icon = d.up ? 'fa-arrow-up' : 'fa-arrow-down';
925
+ const label = this.escapeHtml(d.label);
926
+ return `<span class="ps-why-chip ${cls}" title="${label}"><i class="fa-solid ${icon}"></i> ${label}</span>`;
927
+ }).join('');
928
+ return `<div class="pg-drivers-cell">${chips}</div>`;
929
+ },
930
+ },
931
+ {
932
+ field: 'scoredAtDate',
933
+ headerName: 'Scored At',
934
+ width: 145,
935
+ comparator: (_a, _b, nodeA, nodeB) => (nodeA.data?.scoredAtDate?.getTime() ?? 0) - (nodeB.data?.scoredAtDate?.getTime() ?? 0),
936
+ cellRenderer: (params) => {
937
+ if (!params.data || !params.data.scoredAtDate)
938
+ return '<span class="ps-muted">—</span>';
939
+ const d = params.data.scoredAtDate;
940
+ return `<span class="ps-small ps-muted">${d.toLocaleDateString()} ${d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</span>`;
941
+ },
942
+ },
943
+ {
944
+ headerName: '',
945
+ width: 50,
946
+ sortable: false,
947
+ resizable: false,
948
+ cellRenderer: () => {
949
+ return `<button class="pg-action-btn" title="View details & score history"><i class="fa-solid fa-chevron-right"></i></button>`;
950
+ },
951
+ },
952
+ ];
953
+ if (this.gridApi) {
954
+ this.gridApi.setGridOption('columnDefs', this.columnDefs);
955
+ }
956
+ if (this.chartGridApi) {
957
+ this.chartGridApi.setGridOption('columnDefs', this.columnDefs);
958
+ }
959
+ }
960
+ ngOnInit() {
961
+ const pref = UserInfoEngine.Instance.GetSetting('mj.predictiveStudio.predictions.drawerWidth');
962
+ if (pref) {
963
+ const w = parseInt(pref, 10);
964
+ if (!isNaN(w) && w >= 360 && w <= 1400) {
965
+ this.drawerWidthPx = w;
966
+ }
967
+ }
968
+ this.setupColumnDefs();
969
+ void this.loadPredictions();
970
+ }
971
+ ngOnChanges(changes) {
972
+ if ((changes['modelId'] && !changes['modelId'].firstChange) ||
973
+ (changes['runId'] && !changes['runId'].firstChange) ||
974
+ (changes['entityName'] && !changes['entityName'].firstChange)) {
975
+ void this.loadPredictions();
976
+ }
977
+ }
978
+ ngOnDestroy() {
979
+ if (this.visibleLookupDebounceTimer) {
980
+ clearTimeout(this.visibleLookupDebounceTimer);
981
+ this.visibleLookupDebounceTimer = null;
982
+ }
983
+ this.recordNameCache.clear();
984
+ this.inFlightLookups.clear();
985
+ this.gridApi = null;
986
+ this.chartGridApi = null;
987
+ }
988
+ // ── Data Loading & Name Resolution ──
989
+ async reload() {
990
+ await this.loadPredictions();
991
+ }
992
+ async loadPredictions() {
993
+ if (!this.modelId && !this.runId) {
994
+ this.allRows = [];
995
+ this.applyFilter();
996
+ return;
997
+ }
998
+ this.isLoading = true;
999
+ this.loadError = null;
1000
+ this.cdr.markForCheck();
1001
+ try {
1002
+ const provider = this.ProviderToUse;
1003
+ const user = provider.CurrentUser ?? undefined;
1004
+ // 1. Resolve effective run ID and model entity
1005
+ let effectiveRunId = this.runId;
1006
+ let model = null;
1007
+ if (this.modelId) {
1008
+ const modelsRes = await RunView.FromMetadataProvider(provider).RunView({
1009
+ EntityName: 'MJ: ML Models',
1010
+ ExtraFilter: `ID = '${this.modelId.replace(/'/g, "''")}'`,
1011
+ MaxRows: 1,
1012
+ ResultType: 'entity_object',
1013
+ }, user);
1014
+ model = modelsRes.Success && modelsRes.Results ? modelsRes.Results[0] : null;
1015
+ }
1016
+ if (!effectiveRunId && this.modelId) {
1017
+ // Query latest process run for this model using engine
1018
+ try {
1019
+ const engine = PredictiveStudioEngine.Instance;
1020
+ const runs = await engine.LoadRecentRunsForModel(this.modelId, provider, user, { maxRows: 1 });
1021
+ if (runs && runs.length > 0) {
1022
+ effectiveRunId = runs[0].ID;
1023
+ }
1024
+ }
1025
+ catch {
1026
+ // Fallback if engine is not yet primed
1027
+ }
1028
+ }
1029
+ if (!effectiveRunId) {
1030
+ this.allRows = [];
1031
+ this.applyFilter();
1032
+ this.isLoading = false;
1033
+ this.cdr.markForCheck();
1034
+ return;
1035
+ }
1036
+ // 2. Resolve target entity
1037
+ await this.resolveTargetEntity(model);
1038
+ // 3. Load full process run details (removing 50/200 cap)
1039
+ const detailsRes = await RunView.FromMetadataProvider(provider).RunView({
1040
+ EntityName: 'MJ: Process Run Details',
1041
+ ExtraFilter: `ProcessRunID = '${effectiveRunId.replace(/'/g, "''")}'`,
1042
+ OrderBy: '__mj_CreatedAt ASC',
1043
+ MaxRows: 10000,
1044
+ ResultType: 'entity_object',
1045
+ }, user);
1046
+ if (!detailsRes.Success || !detailsRes.Results) {
1047
+ this.allRows = [];
1048
+ this.applyFilter();
1049
+ this.isLoading = false;
1050
+ this.cdr.markForCheck();
1051
+ return;
1052
+ }
1053
+ // 4. Resolve OutcomeConfig and determine polarity
1054
+ if (model?.ProblemType) {
1055
+ this.problemType = model.ProblemType.toLowerCase();
1056
+ }
1057
+ this.outcomeConfig = resolveOutcomeConfig(model ?? undefined);
1058
+ this.isRenewalModel = this.outcomeConfig.Polarity === 'positive' && !this.isRegression;
1059
+ this.setupColumnDefs();
1060
+ const polarity = resolveRenewalPolarity(detailsRes.Results.map((d) => d.ResultPayload), model?.TargetVariable);
1061
+ // When the model outputs P(Renewed) (scoreIsLapseRisk = false), adverse risk is inverted (1 - score).
1062
+ // When the model outputs P(Lapse) (scoreIsLapseRisk = true), adverse risk is ALREADY score (invertedRisk = false).
1063
+ const shouldInvertRisk = polarity.isRenewalModel ? !polarity.scoreIsLapseRisk : false;
1064
+ const parsedAtRisk = parseAtRiskRows(detailsRes.Results.map((d) => ({ recordId: d.RecordID, ResultPayload: d.ResultPayload })), { invertedRisk: shouldInvertRisk, outcomeConfig: this.outcomeConfig });
1065
+ // Map details into indexed lookup for scoredAt timestamp
1066
+ const detailByRecord = new Map();
1067
+ for (const d of detailsRes.Results) {
1068
+ detailByRecord.set(NormalizeUUID(d.RecordID), d);
1069
+ }
1070
+ this.allRows = parsedAtRisk.map((r) => {
1071
+ const detail = detailByRecord.get(NormalizeUUID(r.recordId));
1072
+ const dt = detail?.CompletedAt ?? detail?.__mj_CreatedAt ?? null;
1073
+ // For renewal models, the primary displayed score is Renewal Probability (0–1).
1074
+ // If the raw score was lapse risk, renewal probability is 1 - r.score.
1075
+ // Otherwise, raw score is already renewal probability.
1076
+ const displayScore = polarity.isRenewalModel
1077
+ ? (polarity.scoreIsLapseRisk ? Math.max(0, Math.min(1, 1 - r.score)) : r.score)
1078
+ : r.score;
1079
+ const resolved = this.outcomeConfig
1080
+ ? resolveScoreBand(displayScore, this.outcomeConfig)
1081
+ : null;
1082
+ const formatted = formatPredictionScore(displayScore, this.outcomeConfig, this.problemType);
1083
+ return {
1084
+ recordId: r.recordId,
1085
+ recordName: r.label ?? r.recordId,
1086
+ score: displayScore,
1087
+ scoreFormatted: formatted,
1088
+ riskPct: r.riskPct,
1089
+ band: resolved?.Key ?? r.band,
1090
+ badgeColor: resolved?.BadgeColor ?? r.badgeColor,
1091
+ icon: resolved?.Icon ?? r.icon,
1092
+ class: r.class,
1093
+ drivers: r.drivers ?? [],
1094
+ status: resolved?.Label ?? r.status ?? detail?.Status ?? 'Succeeded',
1095
+ scoredAtDate: dt instanceof Date ? dt : dt ? new Date(dt) : null,
1096
+ };
1097
+ });
1098
+ this.computeVisibleBands();
1099
+ this.applyFilter();
1100
+ // 5. Schedule lazy lookup of record names for visible rows
1101
+ this.scheduleVisibleRowsLookup(0);
1102
+ }
1103
+ catch (err) {
1104
+ this.loadError = err instanceof Error ? err.message : String(err);
1105
+ LogError(`PSPredictionsGridComponent.loadPredictions: ${this.loadError}`);
1106
+ }
1107
+ finally {
1108
+ this.isLoading = false;
1109
+ this.cdr.markForCheck();
1110
+ }
1111
+ }
1112
+ async resolveTargetEntity(model) {
1113
+ const provider = this.ProviderToUse;
1114
+ if (this.entityName) {
1115
+ const e = provider.EntityByName(this.entityName);
1116
+ if (e) {
1117
+ this.resolvedEntityId = e.ID;
1118
+ this.resolvedEntityName = e.Name;
1119
+ return;
1120
+ }
1121
+ }
1122
+ if (model?.PipelineID) {
1123
+ const pRes = await RunView.FromMetadataProvider(provider).RunView({
1124
+ EntityName: 'MJ: ML Training Pipelines',
1125
+ ExtraFilter: `ID = '${model.PipelineID.replace(/'/g, "''")}'`,
1126
+ MaxRows: 1,
1127
+ ResultType: 'simple',
1128
+ Fields: ['TargetEntityID'],
1129
+ }, provider.CurrentUser ?? undefined);
1130
+ if (pRes.Success && pRes.Results && pRes.Results.length > 0) {
1131
+ const tId = pRes.Results[0]['TargetEntityID'];
1132
+ if (typeof tId === 'string' && tId) {
1133
+ const e = provider.Entities.find((ent) => UUIDsEqual(ent.ID, tId));
1134
+ if (e) {
1135
+ this.resolvedEntityId = e.ID;
1136
+ this.resolvedEntityName = e.Name;
1137
+ return;
1138
+ }
1139
+ }
1140
+ }
1141
+ }
1142
+ }
1143
+ /**
1144
+ * Returns row nodes currently rendered in the AG Grid viewport.
1145
+ * Virtualized grids only render rows visible on screen (plus a small buffer).
1146
+ */
1147
+ getVisibleRowNodes() {
1148
+ const apis = [this.gridApi, this.chartGridApi].filter((api) => Boolean(api));
1149
+ if (apis.length === 0)
1150
+ return [];
1151
+ const nodes = [];
1152
+ for (const api of apis) {
1153
+ const rendered = api.getRenderedNodes();
1154
+ if (rendered && rendered.length > 0) {
1155
+ nodes.push(...rendered);
1156
+ continue;
1157
+ }
1158
+ const firstIdx = api.getFirstDisplayedRowIndex();
1159
+ const lastIdx = api.getLastDisplayedRowIndex();
1160
+ if (firstIdx < 0 || lastIdx < 0)
1161
+ continue;
1162
+ for (let i = firstIdx; i <= lastIdx; i++) {
1163
+ const node = api.getDisplayedRowAtIndex(i);
1164
+ if (node) {
1165
+ nodes.push(node);
1166
+ }
1167
+ }
1168
+ }
1169
+ return nodes;
1170
+ }
1171
+ /**
1172
+ * Schedule lazy lookup of record names for rows currently visible in the AG Grid viewport.
1173
+ * Debounced so rapid scrolling does not trigger redundant batches.
1174
+ */
1175
+ scheduleVisibleRowsLookup(delayMs = 60) {
1176
+ if (this.visibleLookupDebounceTimer) {
1177
+ clearTimeout(this.visibleLookupDebounceTimer);
1178
+ }
1179
+ this.visibleLookupDebounceTimer = setTimeout(() => {
1180
+ void this.resolveVisibleRecordNames();
1181
+ }, delayMs);
1182
+ }
1183
+ /**
1184
+ * Resolve display names (e.g. member name, company name) ONLY for visible rows in the viewport
1185
+ * using ProviderToUse.GetEntityRecordNames.
1186
+ */
1187
+ async resolveVisibleRecordNames() {
1188
+ if ((!this.gridApi && !this.chartGridApi) || !this.resolvedEntityName) {
1189
+ return;
1190
+ }
1191
+ const provider = this.ProviderToUse;
1192
+ const entity = provider.EntityByName(this.resolvedEntityName);
1193
+ if (!entity) {
1194
+ return;
1195
+ }
1196
+ const visibleNodes = this.getVisibleRowNodes();
1197
+ if (visibleNodes.length === 0) {
1198
+ return;
1199
+ }
1200
+ const needed = [];
1201
+ const cachedToUpdate = [];
1202
+ for (const node of visibleNodes) {
1203
+ const data = node.data;
1204
+ if (!data || !data.recordId)
1205
+ continue;
1206
+ const normId = NormalizeUUID(data.recordId);
1207
+ if (this.recordNameCache.has(normId)) {
1208
+ const cachedName = this.recordNameCache.get(normId);
1209
+ if (cachedName && data.recordName !== cachedName) {
1210
+ data.recordName = cachedName;
1211
+ cachedToUpdate.push(node);
1212
+ }
1213
+ continue;
1214
+ }
1215
+ if (this.inFlightLookups.has(normId)) {
1216
+ continue;
1217
+ }
1218
+ needed.push({ normId, recordId: data.recordId, node });
1219
+ }
1220
+ if (cachedToUpdate.length > 0) {
1221
+ if (this.gridApi) {
1222
+ this.gridApi.refreshCells({ rowNodes: cachedToUpdate, columns: ['recordName'], force: true });
1223
+ }
1224
+ if (this.chartGridApi) {
1225
+ this.chartGridApi.refreshCells({ rowNodes: cachedToUpdate, columns: ['recordName'], force: true });
1226
+ }
1227
+ }
1228
+ if (needed.length === 0) {
1229
+ return;
1230
+ }
1231
+ // Mark as in-flight
1232
+ for (const item of needed) {
1233
+ this.inFlightLookups.add(item.normId);
1234
+ }
1235
+ try {
1236
+ const inputs = needed.map((item) => ({
1237
+ EntityName: entity.Name,
1238
+ CompositeKey: CompositeKey.FromURLSegment(entity, item.recordId),
1239
+ }));
1240
+ const results = await provider.GetEntityRecordNames(inputs, provider.CurrentUser ?? undefined);
1241
+ const nodesToRefresh = [];
1242
+ for (let i = 0; i < results.length; i++) {
1243
+ const res = results[i];
1244
+ const item = needed[i];
1245
+ if (!item)
1246
+ continue;
1247
+ if (res && res.Success && res.RecordName) {
1248
+ this.recordNameCache.set(item.normId, res.RecordName);
1249
+ if (item.node.data) {
1250
+ item.node.data.recordName = res.RecordName;
1251
+ nodesToRefresh.push(item.node);
1252
+ }
1253
+ }
1254
+ else {
1255
+ // Fallback: cache the recordId to avoid repeatedly re-querying failed records
1256
+ this.recordNameCache.set(item.normId, item.recordId);
1257
+ }
1258
+ }
1259
+ // Keep allRows in sync so client-side filter and view toggle retain resolved names
1260
+ for (const row of this.allRows) {
1261
+ const cached = this.recordNameCache.get(NormalizeUUID(row.recordId));
1262
+ if (cached && row.recordName !== cached) {
1263
+ row.recordName = cached;
1264
+ }
1265
+ }
1266
+ if (nodesToRefresh.length > 0) {
1267
+ if (this.gridApi) {
1268
+ this.gridApi.refreshCells({ rowNodes: nodesToRefresh, columns: ['recordName'], force: true });
1269
+ }
1270
+ if (this.chartGridApi) {
1271
+ this.chartGridApi.refreshCells({ rowNodes: nodesToRefresh, columns: ['recordName'], force: true });
1272
+ }
1273
+ }
1274
+ this.cdr.markForCheck();
1275
+ }
1276
+ catch (err) {
1277
+ LogError(`PSPredictionsGridComponent.resolveVisibleRecordNames failed: ${err instanceof Error ? err.message : String(err)}`);
1278
+ }
1279
+ finally {
1280
+ for (const item of needed) {
1281
+ this.inFlightLookups.delete(item.normId);
1282
+ }
1283
+ }
1284
+ }
1285
+ // ── Grid & Toolbar Event Handlers ──
1286
+ onGridReady(event) {
1287
+ this.gridApi = event.api;
1288
+ this.scheduleVisibleRowsLookup(50);
1289
+ }
1290
+ onChartGridReady(event) {
1291
+ this.chartGridApi = event.api;
1292
+ this.scheduleVisibleRowsLookup(50);
1293
+ }
1294
+ onCellClicked(event) {
1295
+ const target = event.event?.target;
1296
+ if (target && target.closest('[data-action="open-record"]')) {
1297
+ if (event.data) {
1298
+ this.drillThrough(event.data);
1299
+ }
1300
+ }
1301
+ }
1302
+ onRowClicked(event) {
1303
+ const target = event.event?.target;
1304
+ if (target && target.closest('[data-action="open-record"]')) {
1305
+ return; // Handled by cell link click; do not open detail drawer
1306
+ }
1307
+ if (event.data) {
1308
+ this.openDetail(event.data);
1309
+ }
1310
+ }
1311
+ onSearchInput(event) {
1312
+ const target = event.target;
1313
+ this.searchText = target.value ?? '';
1314
+ this.applyFilter();
1315
+ }
1316
+ setTier(tier) {
1317
+ this.selectedTier = tier;
1318
+ this.applyFilter();
1319
+ }
1320
+ setTierAndGrid(tier) {
1321
+ this.selectedTier = tier;
1322
+ this.activeView = 'grid';
1323
+ this.applyFilter();
1324
+ }
1325
+ getOutcomeBadgeClass(className) {
1326
+ return resolveOutcomeStyle(className, this.outcomeConfig).BadgeColor;
1327
+ }
1328
+ getBadgeColorVar(color) {
1329
+ switch (color) {
1330
+ case 'green':
1331
+ return 'var(--mj-status-success)';
1332
+ case 'red':
1333
+ return 'var(--mj-status-error)';
1334
+ case 'amber':
1335
+ return 'var(--mj-status-warning)';
1336
+ case 'blue':
1337
+ return 'var(--mj-brand-primary)';
1338
+ default:
1339
+ return 'var(--mj-text-muted)';
1340
+ }
1341
+ }
1342
+ computeVisibleBands() {
1343
+ const total = this.allRows.length;
1344
+ const bands = this.outcomeConfig.Bands ?? [];
1345
+ this.visibleBands = bands.map((b) => {
1346
+ const matching = this.allRows.filter((r) => r.band === b.Key);
1347
+ const count = matching.length;
1348
+ const pct = total > 0 ? Math.round((count / total) * 100) : 0;
1349
+ const rawAvg = count > 0 ? matching.reduce((sum, r) => sum + r.score, 0) / count : 0;
1350
+ const avgScore = Math.round(rawAvg * 100);
1351
+ const avgScoreFormatted = formatPredictionScore(rawAvg, this.outcomeConfig, this.problemType);
1352
+ return {
1353
+ key: b.Key,
1354
+ label: b.Label,
1355
+ count,
1356
+ pct,
1357
+ avgScore,
1358
+ avgScoreFormatted,
1359
+ badgeColor: b.BadgeColor,
1360
+ icon: b.Icon,
1361
+ description: b.Description,
1362
+ };
1363
+ });
1364
+ const knownKeys = new Set(bands.map((b) => b.Key));
1365
+ const extraKeys = new Set(this.allRows.map((r) => r.band).filter((k) => !knownKeys.has(k)));
1366
+ for (const k of extraKeys) {
1367
+ const matching = this.allRows.filter((r) => r.band === k);
1368
+ const count = matching.length;
1369
+ const pct = total > 0 ? Math.round((count / total) * 100) : 0;
1370
+ const rawAvg = count > 0 ? matching.reduce((sum, r) => sum + r.score, 0) / count : 0;
1371
+ const avgScore = Math.round(rawAvg * 100);
1372
+ const avgScoreFormatted = formatPredictionScore(rawAvg, this.outcomeConfig, this.problemType);
1373
+ this.visibleBands.push({
1374
+ key: k,
1375
+ label: k.charAt(0).toUpperCase() + k.slice(1),
1376
+ count,
1377
+ pct,
1378
+ avgScore,
1379
+ avgScoreFormatted,
1380
+ badgeColor: 'gray',
1381
+ });
1382
+ }
1383
+ }
1384
+ onViewToggle(key) {
1385
+ this.activeView = key === 'chart' ? 'chart' : 'grid';
1386
+ this.cdr.markForCheck();
1387
+ }
1388
+ clearSearch() {
1389
+ this.searchText = '';
1390
+ this.applyFilter();
1391
+ }
1392
+ selectDecile(idx) {
1393
+ if (this.selectedDecileIndex === idx) {
1394
+ this.selectedDecileIndex = null;
1395
+ }
1396
+ else {
1397
+ this.selectedDecileIndex = idx;
1398
+ }
1399
+ this.applyFilter();
1400
+ if (this.selectedDecileIndex !== null) {
1401
+ setTimeout(() => {
1402
+ const el = document.getElementById('pg-drilldown-anchor');
1403
+ if (el) {
1404
+ el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
1405
+ }
1406
+ }, 50);
1407
+ }
1408
+ }
1409
+ clearDecileFilter() {
1410
+ this.selectedDecileIndex = null;
1411
+ this.applyFilter();
1412
+ }
1413
+ get selectedDecileLabel() {
1414
+ if (this.selectedDecileIndex === null)
1415
+ return '';
1416
+ return `${this.selectedDecileIndex * 10}%–${(this.selectedDecileIndex + 1) * 10}%`;
1417
+ }
1418
+ applyFilter() {
1419
+ let rows = this.allRows;
1420
+ if (this.selectedTier !== 'all') {
1421
+ rows = rows.filter((r) => r.band === this.selectedTier);
1422
+ }
1423
+ if (this.selectedDecileIndex !== null) {
1424
+ const minScore = this.selectedDecileIndex * 0.10;
1425
+ const maxScore = this.selectedDecileIndex === 9 ? 1.01 : (this.selectedDecileIndex + 1) * 0.10;
1426
+ rows = rows.filter((r) => r.score >= minScore && r.score < maxScore);
1427
+ }
1428
+ if (this.searchText.trim()) {
1429
+ const q = this.searchText.trim().toLowerCase();
1430
+ rows = rows.filter((r) => r.recordName.toLowerCase().includes(q) ||
1431
+ r.recordId.toLowerCase().includes(q) ||
1432
+ (r.class && r.class.toLowerCase().includes(q)) ||
1433
+ r.drivers.some((d) => d.label.toLowerCase().includes(q)));
1434
+ }
1435
+ this.filteredRows = rows;
1436
+ this.cdr.markForCheck();
1437
+ this.scheduleVisibleRowsLookup(30);
1438
+ }
1439
+ // ── Drill-Through Navigation ──
1440
+ drillThrough(row) {
1441
+ const entName = this.resolvedEntityName || this.entityName;
1442
+ if (!entName)
1443
+ return;
1444
+ try {
1445
+ const entity = this.ProviderToUse.EntityByName(entName);
1446
+ const ck = CompositeKey.FromURLSegment(entity, row.recordId);
1447
+ this.navigationService.OpenEntityRecord(entName, ck);
1448
+ }
1449
+ catch (err) {
1450
+ LogError(`PSPredictionsGridComponent.drillThrough failed: ${err instanceof Error ? err.message : String(err)}`);
1451
+ }
1452
+ }
1453
+ // ── Slide-In Detail & Score History ──
1454
+ onDrawerWidthChanged(width) {
1455
+ this.drawerWidthPx = width;
1456
+ UserInfoEngine.Instance.SetSettingDebounced('mj.predictiveStudio.predictions.drawerWidth', width.toString());
1457
+ }
1458
+ openDetail(row) {
1459
+ this.selectedRow = row;
1460
+ this.detailOpen = true;
1461
+ this.historyPoints = [];
1462
+ this.historyLoading = true;
1463
+ this.subjectEntityName = null;
1464
+ this.subjectRecordId = null;
1465
+ this.subjectRecordName = null;
1466
+ this.cdr.markForCheck();
1467
+ void this.loadScoreHistory(row.recordId);
1468
+ const entName = this.resolvedEntityName || this.entityName;
1469
+ if (entName) {
1470
+ void this.resolveSubjectRecord(entName, row.recordId);
1471
+ }
1472
+ }
1473
+ closeDetail() {
1474
+ this.detailOpen = false;
1475
+ this.selectedRow = null;
1476
+ this.historyPoints = [];
1477
+ this.subjectEntityName = null;
1478
+ this.subjectRecordId = null;
1479
+ this.subjectRecordName = null;
1480
+ this.cdr.markForCheck();
1481
+ }
1482
+ openSubjectRecord() {
1483
+ if (!this.subjectEntityName || !this.subjectRecordId)
1484
+ return;
1485
+ try {
1486
+ const entity = this.ProviderToUse.EntityByName(this.subjectEntityName);
1487
+ if (!entity)
1488
+ return;
1489
+ const ck = CompositeKey.FromURLSegment(entity, this.subjectRecordId);
1490
+ this.navigationService.OpenEntityRecord(this.subjectEntityName, ck);
1491
+ }
1492
+ catch (err) {
1493
+ LogError(`PSPredictionsGridComponent.openSubjectRecord failed: ${err instanceof Error ? err.message : String(err)}`);
1494
+ }
1495
+ }
1496
+ async resolveSubjectRecord(targetEntityName, recordId) {
1497
+ try {
1498
+ const provider = this.ProviderToUse;
1499
+ const entity = provider.EntityByName(targetEntityName);
1500
+ if (!entity)
1501
+ return;
1502
+ const candidates = [
1503
+ { field: 'PersonID', entity: 'MJ_BizApps_Common: People' },
1504
+ { field: 'MemberID', entity: 'MoreCheese: Member Profiles' },
1505
+ { field: 'ContactID', entity: 'MJ_BizApps_Common: Contacts' },
1506
+ { field: 'AccountID', entity: 'MJ_BizApps_Common: Accounts' },
1507
+ { field: 'CustomerID', entity: 'MJ_BizApps_Common: Customers' },
1508
+ ];
1509
+ let matchedField = null;
1510
+ let targetRelatedEntityName = null;
1511
+ for (const c of candidates) {
1512
+ const f = entity.Fields.find((fld) => fld.Name.toLowerCase() === c.field.toLowerCase());
1513
+ if (f) {
1514
+ matchedField = f.Name;
1515
+ targetRelatedEntityName = f.RelatedEntity || c.entity;
1516
+ break;
1517
+ }
1518
+ }
1519
+ if (!matchedField || !targetRelatedEntityName) {
1520
+ const personField = entity.Fields.find((fld) => fld.RelatedEntity && fld.RelatedEntity.toLowerCase().includes('people'));
1521
+ if (personField) {
1522
+ matchedField = personField.Name;
1523
+ targetRelatedEntityName = personField.RelatedEntity;
1524
+ }
1525
+ }
1526
+ if (!matchedField || !targetRelatedEntityName)
1527
+ return;
1528
+ const recRes = await RunView.FromMetadataProvider(provider).RunView({
1529
+ EntityName: entity.Name,
1530
+ ExtraFilter: `${entity.FirstPrimaryKey.Name} = '${recordId.replace(/'/g, "''")}'`, // first-pk-ok: Single-key entity lookup by recordId
1531
+ MaxRows: 1,
1532
+ ResultType: 'simple',
1533
+ Fields: [matchedField],
1534
+ }, provider.CurrentUser ?? undefined);
1535
+ if (recRes.Success && recRes.Results && recRes.Results.length > 0) {
1536
+ const subjectId = recRes.Results[0][matchedField];
1537
+ if (typeof subjectId === 'string' && subjectId) {
1538
+ this.subjectEntityName = targetRelatedEntityName;
1539
+ this.subjectRecordId = subjectId;
1540
+ const relatedEntity = provider.EntityByName(targetRelatedEntityName);
1541
+ if (relatedEntity) {
1542
+ const names = await provider.GetEntityRecordNames([
1543
+ {
1544
+ EntityName: targetRelatedEntityName,
1545
+ CompositeKey: CompositeKey.FromURLSegment(relatedEntity, subjectId),
1546
+ },
1547
+ ], provider.CurrentUser ?? undefined);
1548
+ if (names && names.length > 0 && names[0].Success) {
1549
+ this.subjectRecordName = names[0].RecordName ?? null;
1550
+ }
1551
+ }
1552
+ this.cdr.markForCheck();
1553
+ }
1554
+ }
1555
+ }
1556
+ catch (err) {
1557
+ LogError(`PSPredictionsGridComponent.resolveSubjectRecord failed: ${err instanceof Error ? err.message : String(err)}`);
1558
+ }
1559
+ }
1560
+ async loadScoreHistory(recordId) {
1561
+ this.historyLoading = true;
1562
+ try {
1563
+ this.historyPoints = await this.scoreHistoryService.LoadRecordScoreHistory({
1564
+ provider: this.ProviderToUse,
1565
+ user: this.ProviderToUse.CurrentUser ?? undefined,
1566
+ entityId: this.resolvedEntityId,
1567
+ recordId,
1568
+ modelId: this.modelId,
1569
+ outcomeConfig: this.outcomeConfig,
1570
+ problemType: this.problemType,
1571
+ });
1572
+ }
1573
+ finally {
1574
+ this.historyLoading = false;
1575
+ this.cdr.markForCheck();
1576
+ }
1577
+ }
1578
+ get entityDisplayName() {
1579
+ return this.resolvedEntityName || this.entityName || 'Entity Record';
1580
+ }
1581
+ // ── KPI & Chart Computations ──
1582
+ get highCount() {
1583
+ return this.allRows.filter((r) => r.band === 'high').length;
1584
+ }
1585
+ get mediumCount() {
1586
+ return this.allRows.filter((r) => r.band === 'medium').length;
1587
+ }
1588
+ get lowCount() {
1589
+ return this.allRows.filter((r) => r.band === 'low').length;
1590
+ }
1591
+ get highPct() {
1592
+ return this.allRows.length > 0 ? Math.round((this.highCount / this.allRows.length) * 100) : 0;
1593
+ }
1594
+ get mediumPct() {
1595
+ return this.allRows.length > 0 ? Math.round((this.mediumCount / this.allRows.length) * 100) : 0;
1596
+ }
1597
+ get lowPct() {
1598
+ return this.allRows.length > 0 ? Math.round((this.lowCount / this.allRows.length) * 100) : 0;
1599
+ }
1600
+ get highAvg() {
1601
+ return this.highAvgRisk;
1602
+ }
1603
+ get mediumAvg() {
1604
+ return this.mediumAvgRisk;
1605
+ }
1606
+ get lowAvg() {
1607
+ return this.lowAvgRisk;
1608
+ }
1609
+ get highAvgRisk() {
1610
+ const list = this.allRows.filter((r) => r.band === 'high');
1611
+ return list.length > 0 ? Math.round(list.reduce((acc, r) => acc + r.riskPct, 0) / list.length) : 0;
1612
+ }
1613
+ get mediumAvgRisk() {
1614
+ const list = this.allRows.filter((r) => r.band === 'medium');
1615
+ return list.length > 0 ? Math.round(list.reduce((acc, r) => acc + r.riskPct, 0) / list.length) : 0;
1616
+ }
1617
+ get lowAvgRisk() {
1618
+ const list = this.allRows.filter((r) => r.band === 'low');
1619
+ return list.length > 0 ? Math.round(list.reduce((acc, r) => acc + r.riskPct, 0) / list.length) : 0;
1620
+ }
1621
+ get meanScorePct() {
1622
+ if (this.allRows.length === 0)
1623
+ return 0;
1624
+ const total = this.allRows.reduce((acc, r) => acc + r.score, 0);
1625
+ return Math.round((total / this.allRows.length) * 100);
1626
+ }
1627
+ get meanScoreFormatted() {
1628
+ if (this.allRows.length === 0)
1629
+ return '—';
1630
+ const total = this.allRows.reduce((acc, r) => acc + r.score, 0);
1631
+ const avg = total / this.allRows.length;
1632
+ return formatPredictionScore(avg, this.outcomeConfig, this.problemType);
1633
+ }
1634
+ get histogramBins() {
1635
+ const total = this.allRows.length;
1636
+ if (total === 0)
1637
+ return [];
1638
+ const isReg = this.isRegression;
1639
+ let minScore = 0;
1640
+ let maxScore = 1;
1641
+ if (isReg) {
1642
+ const scores = this.allRows.map((r) => r.score);
1643
+ minScore = Math.min(...scores);
1644
+ maxScore = Math.max(...scores);
1645
+ if (maxScore <= minScore) {
1646
+ maxScore = minScore + 1;
1647
+ }
1648
+ }
1649
+ const range = maxScore - minScore;
1650
+ const step = range / 10;
1651
+ const counts = new Array(10).fill(0);
1652
+ for (const r of this.allRows) {
1653
+ const normalized = isReg ? (r.score - minScore) / range : r.score;
1654
+ const idx = Math.min(9, Math.max(0, Math.floor(normalized * 10)));
1655
+ counts[idx]++;
1656
+ }
1657
+ const maxCount = Math.max(...counts, 1);
1658
+ return counts.map((count, idx) => {
1659
+ const binMin = minScore + idx * step;
1660
+ const binMax = minScore + (idx + 1) * step;
1661
+ const pct = (count / total) * 100;
1662
+ const pctFormatted = pct < 0.1 && count > 0 ? '<0.1%' : pct.toFixed(1) + '%';
1663
+ // Power scale height so smaller decile bins (e.g. 2, 21, 35) are visible alongside 5,390
1664
+ const height = count === 0 ? 0 : Math.round(14 + Math.pow(count / maxCount, 0.45) * 116);
1665
+ const centerScore = (binMin + binMax) / 2;
1666
+ const band = resolveScoreBand(centerScore, this.outcomeConfig);
1667
+ const color = band ? this.getBadgeColorVar(band.BadgeColor) : 'var(--mj-brand-primary)';
1668
+ let label;
1669
+ let shortLabel;
1670
+ if (isReg) {
1671
+ const fmtMin = formatPredictionScore(binMin, this.outcomeConfig, this.problemType);
1672
+ const fmtMax = formatPredictionScore(binMax, this.outcomeConfig, this.problemType);
1673
+ label = `${fmtMin}–${fmtMax}`;
1674
+ shortLabel = fmtMin;
1675
+ }
1676
+ else {
1677
+ const lower = idx * 10;
1678
+ const upper = (idx + 1) * 10;
1679
+ label = `${lower}%–${upper}%`;
1680
+ shortLabel = `${lower}%`;
1681
+ }
1682
+ const riskLabel = band ? band.Label : label;
1683
+ return {
1684
+ idx,
1685
+ label,
1686
+ shortLabel,
1687
+ count,
1688
+ pct: Math.round(pct),
1689
+ pctFormatted,
1690
+ height,
1691
+ color,
1692
+ riskLabel,
1693
+ };
1694
+ });
1695
+ }
1696
+ get classBreakdown() {
1697
+ const total = this.allRows.length;
1698
+ if (total === 0)
1699
+ return [];
1700
+ const counts = new Map();
1701
+ for (const r of this.allRows) {
1702
+ if (r.class) {
1703
+ counts.set(r.class, (counts.get(r.class) ?? 0) + 1);
1704
+ }
1705
+ }
1706
+ const palette = [
1707
+ 'var(--mj-brand-primary)',
1708
+ 'var(--mj-status-error)',
1709
+ 'var(--mj-status-warning)',
1710
+ 'var(--mj-status-success)',
1711
+ '#7b3ff0',
1712
+ '#d98213',
1713
+ ];
1714
+ return [...counts.entries()]
1715
+ .sort((a, b) => b[1] - a[1])
1716
+ .map(([name, count], i) => ({
1717
+ name,
1718
+ count,
1719
+ pct: Math.round((count / total) * 100),
1720
+ color: palette[i % palette.length],
1721
+ }));
1722
+ }
1723
+ // ── Trendline SVG Plotting ──
1724
+ get trendlinePlotPoints() {
1725
+ const pts = this.historyPoints;
1726
+ if (pts.length === 0)
1727
+ return [];
1728
+ const isReg = this.isRegression;
1729
+ let minScore = 0;
1730
+ let maxScore = 1;
1731
+ if (isReg) {
1732
+ const scores = pts.map((p) => p.score);
1733
+ minScore = Math.min(...scores);
1734
+ maxScore = Math.max(...scores);
1735
+ if (maxScore <= minScore) {
1736
+ maxScore = minScore + 1;
1737
+ }
1738
+ }
1739
+ const range = maxScore - minScore;
1740
+ const xStart = 60;
1741
+ const xEnd = 500;
1742
+ const yTop = 20;
1743
+ const yBottom = 130;
1744
+ const ySpan = yBottom - yTop;
1745
+ const step = pts.length > 1 ? (xEnd - xStart) / (pts.length - 1) : 0;
1746
+ return pts.map((pt, i) => {
1747
+ const x = pts.length === 1 ? (xStart + xEnd) / 2 : Math.round(xStart + i * step);
1748
+ const clampedScore = isReg
1749
+ ? Math.max(0, Math.min(1, (pt.score - minScore) / range))
1750
+ : Math.max(0, Math.min(1, pt.score));
1751
+ const y = Math.round(yBottom - clampedScore * ySpan);
1752
+ const band = resolveScoreBand(pt.score, this.outcomeConfig);
1753
+ const color = band ? this.getBadgeColorVar(band.BadgeColor) : 'var(--mj-brand-primary)';
1754
+ return {
1755
+ runId: pt.runId,
1756
+ x,
1757
+ y,
1758
+ scoreFormatted: pt.scoreFormatted,
1759
+ dateStr: pt.runDate.toLocaleDateString(),
1760
+ class: pt.class,
1761
+ color,
1762
+ };
1763
+ });
1764
+ }
1765
+ get trendlineLinePoints() {
1766
+ return this.trendlinePlotPoints.map((p) => `${p.x},${p.y}`).join(' ');
1767
+ }
1768
+ get trendlineAreaPoints() {
1769
+ const plot = this.trendlinePlotPoints;
1770
+ if (plot.length === 0)
1771
+ return '';
1772
+ const first = plot[0];
1773
+ const last = plot[plot.length - 1];
1774
+ return `${first.x},130 ` + plot.map((p) => `${p.x},${p.y}`).join(' ') + ` ${last.x},130`;
1775
+ }
1776
+ // ── CSV Export ──
1777
+ exportCSV() {
1778
+ if (this.allRows.length === 0)
1779
+ return;
1780
+ const scoreHeader = this.outcomeConfig.ScoreLabel || 'Prediction Score';
1781
+ const statusHeader = this.outcomeConfig.StatusLabel || 'Risk Level';
1782
+ const headers = ['Record ID', 'Record Name', scoreHeader, statusHeader, 'Predicted Class', 'Top Drivers', 'Scored At'];
1783
+ const lines = [headers.join(',')];
1784
+ for (const r of this.filteredRows) {
1785
+ const driversStr = r.drivers.map((d) => `${d.up ? '+' : '-'}${d.label}`).join('; ');
1786
+ const statusStr = `${r.status || r.band} (${r.scoreFormatted})`;
1787
+ const rowData = [
1788
+ `"${r.recordId.replace(/"/g, '""')}"`,
1789
+ `"${(r.recordName || r.recordId).replace(/"/g, '""')}"`,
1790
+ r.scoreFormatted,
1791
+ statusStr,
1792
+ `"${(r.class || '').replace(/"/g, '""')}"`,
1793
+ `"${driversStr.replace(/"/g, '""')}"`,
1794
+ `"${r.scoredAtDate ? r.scoredAtDate.toISOString() : ''}"`,
1795
+ ];
1796
+ lines.push(rowData.join(','));
1797
+ }
1798
+ const blob = new Blob([lines.join('\n')], { type: 'text/csv;charset=utf-8;' });
1799
+ const url = URL.createObjectURL(blob);
1800
+ const a = document.createElement('a');
1801
+ a.href = url;
1802
+ a.download = `predictions_${this.resolvedEntityName || 'records'}_${new Date().toISOString().slice(0, 10)}.csv`;
1803
+ a.click();
1804
+ URL.revokeObjectURL(url);
1805
+ }
1806
+ escapeHtml(str) {
1807
+ return str
1808
+ .replace(/&/g, '&amp;')
1809
+ .replace(/</g, '&lt;')
1810
+ .replace(/>/g, '&gt;')
1811
+ .replace(/"/g, '&quot;')
1812
+ .replace(/'/g, '&#039;');
1813
+ }
1814
+ static ɵfac = /*@__PURE__*/ (() => { let ɵPSPredictionsGridComponent_BaseFactory; return function PSPredictionsGridComponent_Factory(__ngFactoryType__) { return (ɵPSPredictionsGridComponent_BaseFactory || (ɵPSPredictionsGridComponent_BaseFactory = i0.ɵɵgetInheritedFactory(PSPredictionsGridComponent)))(__ngFactoryType__ || PSPredictionsGridComponent); }; })();
1815
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PSPredictionsGridComponent, selectors: [["ps-predictions-grid"]], inputs: { modelId: "modelId", runId: "runId", entityName: "entityName", problemType: "problemType", title: "title", height: "height", readOnly: "readOnly" }, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 27, vars: 15, consts: [[1, "ps-pred-grid-host"], [1, "pg-toolbar"], [1, "pg-toolbar-left"], [1, "pg-search-box"], [1, "fa-solid", "fa-magnifying-glass"], ["type", "text", "placeholder", "Filter records, outcome, factors...", 1, "pg-search-input", 3, "input", "value"], ["type", "button", "title", "Clear filter", 1, "pg-search-clear"], [1, "pg-tier-filters"], ["type", "button", 1, "pg-tier-btn", 3, "click"], [1, "pg-count-pill"], ["type", "button", 1, "pg-tier-btn", 3, "active"], [1, "pg-decile-pill"], [1, "pg-toolbar-right"], [3, "KeyChange", "Options", "ActiveKey"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click", "disabled"], [1, "fa-solid", "fa-file-export"], [1, "pg-view-container"], ["text", "Loading predictions...", "size", "medium"], ["role", "alert", 1, "ps-load-error"], [1, "ps-empty", 2, "padding", "32px"], ["Mode", "slide", "Title", "Record Prediction & Score History", 3, "Resizable", "WidthPx"], ["type", "button", "title", "Clear filter", 1, "pg-search-clear", 3, "click"], [1, "fa-solid", "fa-xmark"], [1, "fa-solid", "fa-filter"], ["type", "button", "title", "Clear decile filter", 3, "click"], [1, "fa-solid", "fa-triangle-exclamation"], [1, "ps-load-error-text"], [1, "ps-load-error-detail"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click"], [1, "fa-solid", "fa-rotate-right"], [1, "ps-empty-ico"], [1, "fa-solid", "fa-chart-line"], [1, "pg-chart-container"], [1, "pg-grid", 3, "gridReady", "rowClicked", "cellClicked", "theme", "columnDefs", "defaultColDef", "gridOptions", "rowData"], [1, "pg-footer"], [1, "ps-muted"], [1, "pg-chart-cards"], [1, "pg-chart-card", "total"], [1, "pg-chart-card-lbl"], [1, "fa-solid", "fa-users", "ps-muted"], [1, "pg-chart-card-val"], [1, "pg-chart-card-sub"], [1, "pg-chart-card", 3, "class"], [1, "pg-chart-section"], [1, "pg-chart-section-title"], [2, "display", "flex", "align-items", "center", "gap", "8px"], [1, "fa-solid", "fa-chart-simple", "ps-muted"], [1, "pg-chart-meta"], [1, "ps-small", "ps-muted"], ["type", "button", 1, "pg-clear-decile-btn"], [1, "pg-histogram"], [1, "pg-h-columns"], [1, "pg-h-col", 3, "active", "empty"], ["id", "pg-drilldown-anchor", 1, "pg-chart-section", "pg-chart-drilldown-section"], [1, "pg-chart-card", 3, "click"], [3, "class", "color"], ["type", "button", 1, "pg-clear-decile-btn", 3, "click"], [1, "pg-h-col", 3, "click"], [1, "pg-h-count", 3, "title"], [1, "pg-h-bar-track"], [1, "pg-h-bar"], [1, "pg-h-footer"], [1, "pg-h-range"], [1, "pg-h-pct"], [1, "pg-h-tooltip"], [1, "pg-h-tt-head"], [1, "pg-h-tt-row"], [1, "pg-h-tt-val"], [1, "pg-h-tt-pct"], [1, "pg-h-tt-badge"], [1, "pg-h-tt-hint"], [1, "fa-solid", "fa-layer-group", "ps-muted"], [2, "display", "flex", "height", "24px", "border-radius", "var(--mj-radius-full)", "overflow", "hidden", "background", "var(--mj-bg-surface-sunken)", "margin-bottom", "8px"], [3, "width", "background", "title"], [2, "display", "flex", "gap", "16px", "flex-wrap", "wrap"], [2, "display", "flex", "align-items", "center", "gap", "6px", "font-size", "var(--mj-text-xs)"], [3, "title"], [2, "width", "10px", "height", "10px", "border-radius", "2px", "display", "inline-block"], [1, "fa-solid", "fa-table-list", "ps-muted"], [2, "color", "var(--mj-brand-primary)"], [1, "ps-badge", 2, "margin-left", "6px", 3, "color"], [1, "pg-chart-grid-host"], [1, "pg-grid", "pg-chart-ag-grid", 3, "gridReady", "rowClicked", "cellClicked", "theme", "columnDefs", "defaultColDef", "gridOptions", "rowData"], [1, "pg-drilldown-footer"], [1, "ps-badge", 2, "margin-left", "6px"], ["Mode", "slide", "Title", "Record Prediction & Score History", 3, "WidthChanged", "Closed", "Resizable", "WidthPx"], [1, "pg-detail-body"], [1, "pg-detail-record-head"], [1, "pg-detail-record-info"], [1, "ps-badge", "blue"], [1, "ps-mono", "ps-small", "ps-muted"], [1, "pg-detail-record-actions", 2, "display", "flex", "gap", "8px", "align-items", "center", "flex-wrap", "wrap"], ["mjButton", "", "variant", "primary", "size", "sm", "title", "Open record in Explorer", 3, "click"], [1, "fa-solid", "fa-arrow-up-right-from-square"], ["mjButton", "", "variant", "secondary", "size", "sm", "title", "Open subject record (e.g. Person, Account) in Explorer"], [1, "pg-detail-pred-card"], [1, "pg-detail-score-circle"], [1, "pg-detail-meta"], [1, "pg-detail-meta-line"], [3, "class"], [2, "font-weight", "700", "color", "var(--mj-text-primary)"], [1, "pg-detail-meta-sub"], [1, "pg-detail-drivers-wrap"], [1, "ps-section-title"], [1, "fa-solid", "fa-lightbulb", 2, "color", "var(--mj-status-warning)"], [1, "pg-detail-drivers-list"], [1, "pg-trendline-wrap"], [1, "ps-section-title", 2, "display", "flex", "align-items", "center", "justify-content", "space-between"], [1, "fa-solid", "fa-clock-rotate-left", "ps-muted"], ["text", "Loading score history...", "size", "small"], ["mjButton", "", "variant", "secondary", "size", "sm", "title", "Open subject record (e.g. Person, Account) in Explorer", 3, "click"], [1, "fa-solid", "fa-user"], [1, "ps-badge"], [1, "pg-detail-driver-item"], [1, "pg-detail-driver-label"], [1, "fa-solid"], ["viewBox", "0 0 540 160", "preserveAspectRatio", "none", 1, "pg-trendline-svg"], ["x1", "40", "y1", "20", "x2", "520", "y2", "20", "stroke", "var(--mj-border-subtle)", "stroke-dasharray", "3,3"], ["x1", "40", "y1", "75", "x2", "520", "y2", "75", "stroke", "var(--mj-border-subtle)", "stroke-dasharray", "3,3"], ["x1", "40", "y1", "130", "x2", "520", "y2", "130", "stroke", "var(--mj-border-default)"], ["x", "32", "y", "24", "font-size", "10", "text-anchor", "end", "fill", "var(--mj-text-muted)"], ["x", "32", "y", "79", "font-size", "10", "text-anchor", "end", "fill", "var(--mj-text-muted)"], ["x", "32", "y", "134", "font-size", "10", "text-anchor", "end", "fill", "var(--mj-text-muted)"], ["fill", "color-mix(in srgb, var(--mj-brand-primary) 12%, transparent)"], ["fill", "none", "stroke", "var(--mj-brand-primary)", "stroke-width", "2.5"], [1, "pg-history-table"], ["r", "5", "stroke", "var(--mj-bg-surface)", "stroke-width", "2"], ["font-size", "10", "font-weight", "700", "text-anchor", "middle", "fill", "var(--mj-text-primary)"], [1, "ps-mono"], [1, "pg-delta-pill", "neutral"], [1, "pg-delta-pill", "up"], [1, "pg-delta-pill", "down"], [1, "fa-solid", "fa-arrow-up"], [1, "fa-solid", "fa-arrow-down"]], template: function PSPredictionsGridComponent_Template(rf, ctx) { if (rf & 1) {
1816
+ i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "div", 3);
1817
+ i0.ɵɵelement(4, "i", 4);
1818
+ i0.ɵɵelementStart(5, "input", 5);
1819
+ i0.ɵɵlistener("input", function PSPredictionsGridComponent_Template_input_input_5_listener($event) { return ctx.onSearchInput($event); });
1820
+ i0.ɵɵelementEnd();
1821
+ i0.ɵɵconditionalCreate(6, PSPredictionsGridComponent_Conditional_6_Template, 2, 0, "button", 6);
1822
+ i0.ɵɵelementEnd();
1823
+ i0.ɵɵelementStart(7, "div", 7)(8, "button", 8);
1824
+ i0.ɵɵlistener("click", function PSPredictionsGridComponent_Template_button_click_8_listener() { return ctx.setTier("all"); });
1825
+ i0.ɵɵtext(9, " All ");
1826
+ i0.ɵɵelementStart(10, "span", 9);
1827
+ i0.ɵɵtext(11);
1828
+ i0.ɵɵpipe(12, "number");
1829
+ i0.ɵɵelementEnd()();
1830
+ i0.ɵɵrepeaterCreate(13, PSPredictionsGridComponent_For_14_Template, 5, 6, "button", 10, _forTrack0);
1831
+ i0.ɵɵconditionalCreate(15, PSPredictionsGridComponent_Conditional_15_Template, 6, 1, "div", 11);
1832
+ i0.ɵɵelementEnd()();
1833
+ i0.ɵɵelementStart(16, "div", 12)(17, "mj-view-toggle", 13);
1834
+ i0.ɵɵlistener("KeyChange", function PSPredictionsGridComponent_Template_mj_view_toggle_KeyChange_17_listener($event) { return ctx.onViewToggle($event); });
1835
+ i0.ɵɵelementEnd();
1836
+ i0.ɵɵelementStart(18, "button", 14);
1837
+ i0.ɵɵlistener("click", function PSPredictionsGridComponent_Template_button_click_18_listener() { return ctx.exportCSV(); });
1838
+ i0.ɵɵelement(19, "i", 15);
1839
+ i0.ɵɵtext(20, " Export ");
1840
+ i0.ɵɵelementEnd()()();
1841
+ i0.ɵɵelementStart(21, "div", 16);
1842
+ i0.ɵɵconditionalCreate(22, PSPredictionsGridComponent_Conditional_22_Template, 1, 0, "mj-loading", 17)(23, PSPredictionsGridComponent_Conditional_23_Template, 10, 1, "div", 18)(24, PSPredictionsGridComponent_Conditional_24_Template, 7, 0, "div", 19)(25, PSPredictionsGridComponent_Conditional_25_Template, 2, 2);
1843
+ i0.ɵɵelementEnd();
1844
+ i0.ɵɵconditionalCreate(26, PSPredictionsGridComponent_Conditional_26_Template, 52, 26, "mj-slide-panel", 20);
1845
+ i0.ɵɵelementEnd();
1846
+ } if (rf & 2) {
1847
+ i0.ɵɵstyleProp("height", ctx.height);
1848
+ i0.ɵɵadvance(5);
1849
+ i0.ɵɵproperty("value", ctx.searchText);
1850
+ i0.ɵɵadvance();
1851
+ i0.ɵɵconditional(ctx.searchText ? 6 : -1);
1852
+ i0.ɵɵadvance(2);
1853
+ i0.ɵɵclassProp("active", ctx.selectedTier === "all");
1854
+ i0.ɵɵadvance(3);
1855
+ i0.ɵɵtextInterpolate1("(", i0.ɵɵpipeBind1(12, 13, ctx.allRows.length), ")");
1856
+ i0.ɵɵadvance(2);
1857
+ i0.ɵɵrepeater(ctx.visibleBands);
1858
+ i0.ɵɵadvance(2);
1859
+ i0.ɵɵconditional(ctx.selectedDecileIndex !== null ? 15 : -1);
1860
+ i0.ɵɵadvance(2);
1861
+ i0.ɵɵproperty("Options", ctx.viewOptions)("ActiveKey", ctx.activeView);
1862
+ i0.ɵɵadvance();
1863
+ i0.ɵɵproperty("disabled", ctx.allRows.length === 0);
1864
+ i0.ɵɵadvance(4);
1865
+ i0.ɵɵconditional(ctx.isLoading ? 22 : ctx.loadError ? 23 : ctx.allRows.length === 0 ? 24 : 25);
1866
+ i0.ɵɵadvance(4);
1867
+ i0.ɵɵconditional(ctx.detailOpen && ctx.selectedRow ? 26 : -1);
1868
+ } }, dependencies: [CommonModule,
1869
+ FormsModule,
1870
+ SharedGenericModule, i1.LoadingComponent, MJButtonDirective,
1871
+ MJViewToggleComponent,
1872
+ MjSlidePanelComponent,
1873
+ AgGridModule, i2.AgGridAngular, i3.DecimalPipe, i3.DatePipe], styles: ["\n\n\n\n\n\n\n\n\n\n\n\n\n.ps-panel[_ngcontent-%COMP%] {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n \n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel[_ngcontent-%COMP%] {\n display: block;\n height: 100%;\n}\n\n\n.ps-row[_ngcontent-%COMP%] { display: flex; gap: 16px; }\n.ps-col[_ngcontent-%COMP%] { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap[_ngcontent-%COMP%] { flex-wrap: wrap; }\n.ps-spacer[_ngcontent-%COMP%] { flex: 1; }\n.ps-muted[_ngcontent-%COMP%] { color: var(--mj-text-muted); }\n.ps-small[_ngcontent-%COMP%] { font-size: 12.5px; }\n.ps-mono[_ngcontent-%COMP%] { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title[_ngcontent-%COMP%] {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n\n.ps-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n.ps-card-head[_ngcontent-%COMP%] {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body[_ngcontent-%COMP%] { padding: 16px; }\n\n\n.ps-kpi[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 8px; padding: 16px 18px;\n}\n.ps-kpi[_ngcontent-%COMP%] .label[_ngcontent-%COMP%] { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi[_ngcontent-%COMP%] .val[_ngcontent-%COMP%] { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n\n.ps-badge[_ngcontent-%COMP%] {\n display: inline-flex; align-items: center; gap: 5px;\n padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray[_ngcontent-%COMP%] { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue[_ngcontent-%COMP%] { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green[_ngcontent-%COMP%] { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber[_ngcontent-%COMP%] { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red[_ngcontent-%COMP%] { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot[_ngcontent-%COMP%] { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n\n.ps-lvl[_ngcontent-%COMP%] {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 92px; padding: 4px 10px; border-radius: 7px;\n font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary[_ngcontent-%COMP%] { background: var(--mj-status-success); }\n.ps-lvl-Strong[_ngcontent-%COMP%] { background: var(--mj-status-info); }\n.ps-lvl-Viable[_ngcontent-%COMP%] { background: var(--mj-status-warning); }\n.ps-lvl-Weak[_ngcontent-%COMP%] { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended[_ngcontent-%COMP%] { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n\n.ps-tag[_ngcontent-%COMP%] {\n display: inline-block; padding: 2px 8px; border-radius: 6px;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted[_ngcontent-%COMP%] { opacity: .6; }\n.ps-divider[_ngcontent-%COMP%] { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n\n.ps-table[_ngcontent-%COMP%] { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:hover td[_ngcontent-%COMP%] { background: var(--mj-bg-surface-hover); }\n.ps-table[_ngcontent-%COMP%] .num[_ngcontent-%COMP%] { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank[_ngcontent-%COMP%] {\n width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold[_ngcontent-%COMP%] { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n\n.ps-bar[_ngcontent-%COMP%] { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar[_ngcontent-%COMP%] > span[_ngcontent-%COMP%] { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn[_ngcontent-%COMP%] > span[_ngcontent-%COMP%] { background: var(--mj-status-warning); }\n.ps-bar.run[_ngcontent-%COMP%] > span[_ngcontent-%COMP%] { background: var(--mj-brand-primary); }\n\n.ps-fbar[_ngcontent-%COMP%] { display: grid; grid-template-columns: minmax(150px, 1.3fr) 2fr 56px; align-items: center; gap: 14px; padding: 8px 0; }\n.ps-fbar[_ngcontent-%COMP%] + .ps-fbar[_ngcontent-%COMP%] { border-top: 1px solid var(--mj-border-subtle); }\n.ps-fbar[_ngcontent-%COMP%] .track[_ngcontent-%COMP%] { height: 10px; border-radius: var(--mj-radius-full); background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar[_ngcontent-%COMP%] .track[_ngcontent-%COMP%] > span[_ngcontent-%COMP%] { display: block; height: 100%; border-radius: var(--mj-radius-full); background: var(--mj-brand-primary); }\n.ps-fbar[_ngcontent-%COMP%] .track[_ngcontent-%COMP%] > span.warn[_ngcontent-%COMP%] { background: var(--mj-status-warning); }\n.ps-fbar[_ngcontent-%COMP%] .name[_ngcontent-%COMP%] { color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ps-fbar[_ngcontent-%COMP%] > .ps-mono[_ngcontent-%COMP%] { text-align: right; color: var(--mj-text-secondary); font-variant-numeric: tabular-nums; }\n\n\n.ps-gauge[_ngcontent-%COMP%] {\n position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n display: grid; place-items: center;\n}\n.ps-gauge[_ngcontent-%COMP%]::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge[_ngcontent-%COMP%] .g-val[_ngcontent-%COMP%] { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n\n.ps-callout[_ngcontent-%COMP%] { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn[_ngcontent-%COMP%] { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info[_ngcontent-%COMP%] { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success[_ngcontent-%COMP%] { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { font-size: 16px; margin-top: 1px; }\n\n\n.ps-stepper[_ngcontent-%COMP%] { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step[_ngcontent-%COMP%] { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step[_ngcontent-%COMP%] .pip[_ngcontent-%COMP%] {\n width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done[_ngcontent-%COMP%] { color: var(--mj-status-success-text); }\n.ps-step.done[_ngcontent-%COMP%] .pip[_ngcontent-%COMP%] { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr[_ngcontent-%COMP%] { color: var(--mj-brand-primary); }\n.ps-step.curr[_ngcontent-%COMP%] .pip[_ngcontent-%COMP%] { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step[_ngcontent-%COMP%] .ln[_ngcontent-%COMP%] { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n\n.ps-flow[_ngcontent-%COMP%] {\n position: relative;\n background:\n linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default); border-radius: 10px;\n min-height: 620px; overflow: hidden;\n}\n.ps-node[_ngcontent-%COMP%] {\n position: absolute; width: 190px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 8px;\n cursor: pointer;\n}\n.ps-node[_ngcontent-%COMP%] .nh[_ngcontent-%COMP%] {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node[_ngcontent-%COMP%] .nh[_ngcontent-%COMP%] i.tile[_ngcontent-%COMP%] {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node[_ngcontent-%COMP%] .nh[_ngcontent-%COMP%] .ps-tag[_ngcontent-%COMP%] { margin-left: auto; }\n.ps-node[_ngcontent-%COMP%] .nb[_ngcontent-%COMP%] { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node[_ngcontent-%COMP%] .nb-row[_ngcontent-%COMP%] { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node[_ngcontent-%COMP%] .nb-row[_ngcontent-%COMP%] .k[_ngcontent-%COMP%] { color: var(--mj-text-muted); }\n.ps-node.src[_ngcontent-%COMP%] { border-left-color: var(--ps-node-source); }\n.ps-node.src[_ngcontent-%COMP%] .nh[_ngcontent-%COMP%] i.tile[_ngcontent-%COMP%] { background: var(--ps-node-source); }\n.ps-node.feat[_ngcontent-%COMP%] { border-left-color: var(--ps-node-feature); }\n.ps-node.feat[_ngcontent-%COMP%] .nh[_ngcontent-%COMP%] i.tile[_ngcontent-%COMP%] { background: var(--ps-node-feature); }\n.ps-node.emb[_ngcontent-%COMP%] { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb[_ngcontent-%COMP%] .nh[_ngcontent-%COMP%] i.tile[_ngcontent-%COMP%] { background: var(--ps-node-embedding); }\n.ps-node.target[_ngcontent-%COMP%] { border-left-color: var(--ps-node-target); }\n.ps-node.target[_ngcontent-%COMP%] .nh[_ngcontent-%COMP%] i.tile[_ngcontent-%COMP%] { background: var(--ps-node-target); }\n.ps-node.algo[_ngcontent-%COMP%] { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo[_ngcontent-%COMP%] .nh[_ngcontent-%COMP%] i.tile[_ngcontent-%COMP%] { background: var(--ps-node-algorithm); }\n.ps-node.output[_ngcontent-%COMP%] { border-left-color: var(--mj-status-success); }\n.ps-node.output[_ngcontent-%COMP%] .nh[_ngcontent-%COMP%] i.tile[_ngcontent-%COMP%] { background: var(--mj-status-success); }\n.ps-node.selected[_ngcontent-%COMP%] {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node[_ngcontent-%COMP%] .port[_ngcontent-%COMP%] {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node[_ngcontent-%COMP%] .port.in[_ngcontent-%COMP%] { left: -6px; }\n.ps-node[_ngcontent-%COMP%] .port.out[_ngcontent-%COMP%] { right: -6px; }\nsvg.ps-edges[_ngcontent-%COMP%] { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges[_ngcontent-%COMP%] path[_ngcontent-%COMP%] { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n\n.ps-pchip[_ngcontent-%COMP%] {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { background: var(--ps-node-source); }\n.ps-pchip.f[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { background: var(--ps-node-feature); }\n.ps-pchip.e[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { background: var(--ps-node-embedding); }\n.ps-pchip.t[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { background: var(--ps-node-target); }\n.ps-pchip.a[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { background: var(--ps-node-algorithm); }\n\n\n.ps-seg[_ngcontent-%COMP%] { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg[_ngcontent-%COMP%] button[_ngcontent-%COMP%] {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg[_ngcontent-%COMP%] button[_ngcontent-%COMP%]:last-child { border-right: none; }\n.ps-seg[_ngcontent-%COMP%] button.on[_ngcontent-%COMP%] { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n\n.ps-field[_ngcontent-%COMP%] { margin-bottom: 14px; }\n.ps-field[_ngcontent-%COMP%] > label[_ngcontent-%COMP%] {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input[_ngcontent-%COMP%] {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input[_ngcontent-%COMP%]:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n\n.ps-loading[_ngcontent-%COMP%] { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n\n.ps-empty[_ngcontent-%COMP%] {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty[_ngcontent-%COMP%] .ps-empty-ico[_ngcontent-%COMP%] {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty[_ngcontent-%COMP%] p[_ngcontent-%COMP%] { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n\n.ps-modal-backdrop[_ngcontent-%COMP%] {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal[_ngcontent-%COMP%] {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head[_ngcontent-%COMP%] {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head[_ngcontent-%COMP%] .ico[_ngcontent-%COMP%] {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn[_ngcontent-%COMP%] .ico[_ngcontent-%COMP%] { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger[_ngcontent-%COMP%] .ico[_ngcontent-%COMP%] { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body[_ngcontent-%COMP%] { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body[_ngcontent-%COMP%] .ps-field[_ngcontent-%COMP%] { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot[_ngcontent-%COMP%] {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot[_ngcontent-%COMP%] .ps-spacer[_ngcontent-%COMP%] { flex: 1; }", "\n\n\n\n\n.ps-pred-grid-host[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n}\n\n\n.pg-toolbar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 8px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n margin-bottom: 12px;\n flex-wrap: wrap;\n}\n\n.pg-toolbar-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 240px;\n}\n\n.pg-search-box[_ngcontent-%COMP%] {\n position: relative;\n display: flex;\n align-items: center;\n max-width: 260px;\n width: 100%;\n}\n\n.pg-search-box[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n position: absolute;\n left: 10px;\n color: var(--mj-text-muted);\n font-size: 12px;\n pointer-events: none;\n}\n\n.pg-search-input[_ngcontent-%COMP%] {\n width: 100%;\n padding: 6px 10px 6px 30px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n background: var(--mj-bg-surface);\n font-size: var(--mj-text-xs);\n color: var(--mj-text-primary);\n outline: none;\n transition: border-color .15s;\n}\n\n.pg-search-input[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-brand-primary);\n}\n\n.pg-search-clear[_ngcontent-%COMP%] {\n position: absolute;\n right: 8px;\n background: transparent;\n border: none;\n cursor: pointer;\n padding: 2px 4px;\n color: var(--mj-text-muted);\n font-size: 11px;\n}\n\n.pg-search-clear[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n}\n\n.pg-decile-pill[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 2px 8px;\n border-radius: var(--mj-radius-full);\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n border: 1px solid var(--mj-brand-primary);\n font-size: var(--mj-text-xs);\n font-weight: 600;\n color: var(--mj-brand-primary);\n}\n\n.pg-decile-pill[_ngcontent-%COMP%] button[_ngcontent-%COMP%] {\n background: transparent;\n border: none;\n cursor: pointer;\n padding: 0;\n color: var(--mj-brand-primary);\n font-size: 10px;\n}\n\n.pg-tier-filters[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.pg-tier-btn[_ngcontent-%COMP%] {\n cursor: pointer;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n border-radius: var(--mj-radius-full);\n padding: 3px 10px;\n font-size: var(--mj-text-xs);\n font-weight: 600;\n color: var(--mj-text-secondary);\n transition: all .15s;\n display: inline-flex;\n align-items: center;\n gap: 5px;\n}\n\n.pg-tier-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.pg-tier-btn.active[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n.pg-tier-btn.tier-high.active[_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n border-color: var(--mj-status-error);\n}\n\n.pg-tier-btn.tier-medium.active[_ngcontent-%COMP%] {\n background: var(--mj-status-warning);\n border-color: var(--mj-status-warning);\n}\n\n.pg-tier-btn.tier-low.active[_ngcontent-%COMP%] {\n background: var(--mj-status-success);\n border-color: var(--mj-status-success);\n}\n\n.pg-count-pill[_ngcontent-%COMP%] {\n font-size: 10px;\n opacity: .85;\n}\n\n.pg-toolbar-right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n\n.pg-view-container[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n.pg-grid[_ngcontent-%COMP%] {\n width: 100%;\n flex: 1;\n min-height: 380px;\n}\n\n\n.pg-record-cell[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: 2px;\n height: 100%;\n min-width: 0;\n line-height: 1.2;\n}\n\n.pg-record-title-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n min-width: 0;\n}\n\n.pg-record-link[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n max-width: 100%;\n text-decoration: none;\n cursor: pointer;\n min-width: 0;\n}\n\n.pg-record-name[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: var(--mj-text-sm);\n color: var(--mj-text-link);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.pg-record-link[_ngcontent-%COMP%]:hover .pg-record-name[_ngcontent-%COMP%] {\n text-decoration: underline;\n}\n\n.pg-record-open-icon[_ngcontent-%COMP%] {\n font-size: 10px;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n transition: transform .15s, color .15s;\n}\n\n.pg-record-link[_ngcontent-%COMP%]:hover .pg-record-open-icon[_ngcontent-%COMP%] {\n transform: translateY(-1px) translateX(1px);\n color: var(--mj-text-primary);\n}\n\n.pg-record-id[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.pg-score-cell[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 100%;\n min-width: 0;\n}\n\n.pg-score-bar[_ngcontent-%COMP%] {\n flex: 1;\n height: 8px;\n border-radius: var(--mj-radius-full);\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n}\n\n.pg-score-fill[_ngcontent-%COMP%] {\n display: block;\n height: 100%;\n border-radius: var(--mj-radius-full);\n}\n\n.pg-score-fill.risk-high[_ngcontent-%COMP%], \n.pg-score-fill.badge-red[_ngcontent-%COMP%] { background: var(--mj-status-error); }\n.pg-score-fill.risk-medium[_ngcontent-%COMP%], \n.pg-score-fill.badge-amber[_ngcontent-%COMP%] { background: var(--mj-status-warning); }\n.pg-score-fill.risk-low[_ngcontent-%COMP%], \n.pg-score-fill.badge-green[_ngcontent-%COMP%] { background: var(--mj-status-success); }\n.pg-score-fill.badge-blue[_ngcontent-%COMP%] { background: var(--mj-brand-primary); }\n.pg-score-fill.badge-gray[_ngcontent-%COMP%] { background: var(--mj-text-muted); }\n\n.pg-score-val[_ngcontent-%COMP%] {\n font-weight: 700;\n font-size: var(--mj-text-xs);\n font-variant-numeric: tabular-nums;\n min-width: 44px;\n text-align: right;\n}\n\n.pg-score-val.risk-high[_ngcontent-%COMP%], \n.pg-score-val.badge-red[_ngcontent-%COMP%] { color: var(--mj-status-error-text, var(--mj-status-error)); }\n.pg-score-val.risk-medium[_ngcontent-%COMP%], \n.pg-score-val.badge-amber[_ngcontent-%COMP%] { color: var(--mj-status-warning-text, var(--mj-status-warning)); }\n.pg-score-val.risk-low[_ngcontent-%COMP%], \n.pg-score-val.badge-green[_ngcontent-%COMP%] { color: var(--mj-status-success-text, var(--mj-status-success)); }\n.pg-score-val.badge-blue[_ngcontent-%COMP%] { color: var(--mj-brand-primary); }\n.pg-score-val.badge-gray[_ngcontent-%COMP%] { color: var(--mj-text-muted); }\n\n.pg-drivers-cell[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n height: 100%;\n overflow: hidden;\n flex-wrap: nowrap;\n}\n\n.pg-action-btn[_ngcontent-%COMP%] {\n background: transparent;\n border: none;\n cursor: pointer;\n padding: 6px 8px;\n border-radius: 6px;\n color: var(--mj-text-muted);\n font-size: 12px;\n transition: all .15s;\n}\n\n.pg-action-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-brand-primary);\n}\n\n\n.pg-footer[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-subtle);\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n}\n\n\n.pg-chart-container[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 4px 0 12px;\n}\n\n.pg-chart-cards[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 12px;\n}\n\n.pg-chart-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 14px 16px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n cursor: pointer;\n transition: all .15s;\n}\n\n.pg-chart-card[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.pg-chart-card.total[_ngcontent-%COMP%] { border-left: 4px solid var(--mj-brand-primary); }\n.pg-chart-card.tier-high[_ngcontent-%COMP%] { border-left: 4px solid var(--mj-status-error); }\n.pg-chart-card.tier-medium[_ngcontent-%COMP%] { border-left: 4px solid var(--mj-status-warning); }\n.pg-chart-card.tier-low[_ngcontent-%COMP%] { border-left: 4px solid var(--mj-status-success); }\n\n.pg-chart-card-val[_ngcontent-%COMP%] {\n font-size: var(--mj-text-xl);\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.pg-chart-card-lbl[_ngcontent-%COMP%] {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-secondary);\n font-weight: 600;\n}\n\n.pg-chart-card-sub[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.pg-chart-section[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 18px 20px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.pg-chart-section-title[_ngcontent-%COMP%] {\n font-size: var(--mj-text-sm);\n font-weight: 650;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.pg-chart-meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.pg-clear-decile-btn[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-full);\n padding: 2px 10px;\n font-size: 11px;\n font-weight: 600;\n cursor: pointer;\n color: var(--mj-text-secondary);\n transition: all .15s;\n}\n\n.pg-clear-decile-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-subtle);\n}\n\n\n.pg-histogram[_ngcontent-%COMP%] {\n position: relative;\n width: 100%;\n padding-top: 8px;\n padding-bottom: 28px;\n}\n\n.pg-h-columns[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n gap: 8px;\n height: 200px;\n padding: 0 4px;\n border-bottom: 2px solid var(--mj-border-default);\n}\n\n.pg-h-col[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: flex-end;\n height: 100%;\n cursor: pointer;\n position: relative;\n transition: transform .15s;\n user-select: none;\n}\n\n.pg-h-col[_ngcontent-%COMP%]:hover {\n transform: translateY(-2px);\n}\n\n.pg-h-count[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin-bottom: 6px;\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n}\n\n.pg-h-bar-track[_ngcontent-%COMP%] {\n width: 100%;\n max-width: 52px;\n display: flex;\n align-items: flex-end;\n justify-content: center;\n}\n\n.pg-h-bar[_ngcontent-%COMP%] {\n width: 100%;\n border-radius: 4px 4px 0 0;\n transition: height .3s cubic-bezier(0.4, 0, 0.2, 1), opacity .15s, box-shadow .15s;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);\n}\n\n.pg-h-col[_ngcontent-%COMP%]:hover .pg-h-bar[_ngcontent-%COMP%] {\n filter: brightness(1.1);\n box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);\n}\n\n.pg-h-col.active[_ngcontent-%COMP%] .pg-h-bar[_ngcontent-%COMP%] {\n outline: 3px solid var(--mj-brand-primary);\n outline-offset: 1px;\n}\n\n.pg-h-footer[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n margin-top: 8px;\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n}\n\n.pg-h-range[_ngcontent-%COMP%] {\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n}\n\n.pg-h-pct[_ngcontent-%COMP%] {\n font-size: 10px;\n color: var(--mj-text-muted);\n}\n\n\n.pg-h-tooltip[_ngcontent-%COMP%] {\n position: absolute;\n bottom: calc(100% + 4px);\n left: 50%;\n transform: translateX(-50%);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n border-radius: var(--mj-radius-md);\n padding: 8px 12px;\n white-space: nowrap;\n pointer-events: none;\n opacity: 0;\n visibility: hidden;\n transition: opacity .15s, visibility .15s;\n z-index: 50;\n}\n\n.pg-h-col[_ngcontent-%COMP%]:hover .pg-h-tooltip[_ngcontent-%COMP%] {\n opacity: 1;\n visibility: visible;\n}\n\n.pg-h-tt-head[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin-bottom: 2px;\n}\n\n.pg-h-tt-row[_ngcontent-%COMP%] {\n display: flex;\n gap: 6px;\n align-items: center;\n font-size: 11px;\n}\n\n.pg-h-tt-val[_ngcontent-%COMP%] {\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.pg-h-tt-pct[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n.pg-h-tt-badge[_ngcontent-%COMP%] {\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-top: 3px;\n}\n\n.pg-h-tt-hint[_ngcontent-%COMP%] {\n font-size: 9px;\n color: var(--mj-text-muted);\n font-style: italic;\n margin-top: 4px;\n}\n\n\n.pg-detail-body[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 20px;\n padding: 18px 20px 32px;\n}\n\n.pg-detail-record-head[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 14px;\n padding-bottom: 14px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.pg-detail-record-info[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 4px;\n font-size: var(--mj-text-lg);\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.pg-detail-pred-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 20px;\n padding: 16px 18px;\n border-radius: var(--mj-radius-md);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n}\n\n.pg-detail-score-circle[_ngcontent-%COMP%] {\n width: 72px;\n height: 72px;\n border-radius: 50%;\n display: grid;\n place-items: center;\n font-size: 19px;\n font-weight: 800;\n color: var(--mj-text-inverse);\n flex-shrink: 0;\n box-shadow: 0 2px 8px rgba(0, 0, 0, .12);\n}\n\n.pg-detail-score-circle.risk-high[_ngcontent-%COMP%], \n.pg-detail-score-circle.badge-red[_ngcontent-%COMP%] { background: var(--mj-status-error); }\n.pg-detail-score-circle.risk-medium[_ngcontent-%COMP%], \n.pg-detail-score-circle.badge-amber[_ngcontent-%COMP%] { background: var(--mj-status-warning); }\n.pg-detail-score-circle.risk-low[_ngcontent-%COMP%], \n.pg-detail-score-circle.badge-green[_ngcontent-%COMP%] { background: var(--mj-status-success); }\n.pg-detail-score-circle.badge-blue[_ngcontent-%COMP%] { background: var(--mj-brand-primary); }\n.pg-detail-score-circle.badge-gray[_ngcontent-%COMP%] { background: var(--mj-text-muted); }\n\n.pg-detail-meta[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n flex: 1;\n}\n\n.pg-detail-meta-line[_ngcontent-%COMP%] {\n font-size: var(--mj-text-sm);\n color: var(--mj-text-primary);\n}\n\n.pg-detail-meta-sub[_ngcontent-%COMP%] {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n}\n\n.pg-detail-drivers-wrap[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.pg-detail-drivers-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.pg-detail-driver-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: var(--mj-radius-sm);\n}\n\n.pg-detail-driver-label[_ngcontent-%COMP%] {\n font-size: var(--mj-text-xs);\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n\n.pg-trendline-wrap[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n}\n\n.pg-trendline-svg[_ngcontent-%COMP%] {\n width: 100%;\n height: 180px;\n overflow: visible;\n}\n\n.pg-history-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--mj-text-xs);\n margin-top: 6px;\n}\n\n.pg-history-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n text-align: left;\n padding: 6px 8px;\n border-bottom: 1px solid var(--mj-border-default);\n color: var(--mj-text-muted);\n font-weight: 600;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: .03em;\n}\n\n.pg-history-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 7px 8px;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-secondary);\n}\n\n.pg-history-table[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:last-child td[_ngcontent-%COMP%] {\n border-bottom: none;\n}\n\n.pg-delta-pill[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n font-weight: 700;\n font-size: 11px;\n padding: 1px 6px;\n border-radius: var(--mj-radius-full);\n}\n\n.pg-delta-pill.up[_ngcontent-%COMP%] { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.pg-delta-pill.down[_ngcontent-%COMP%] { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.pg-delta-pill.neutral[_ngcontent-%COMP%] { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n\n.pg-chart-drilldown-section[_ngcontent-%COMP%] {\n padding: 16px 18px;\n gap: 12px;\n}\n\n.pg-chart-grid-host[_ngcontent-%COMP%] {\n width: 100%;\n height: 440px;\n min-height: 320px;\n border-radius: var(--mj-radius-sm);\n overflow: hidden;\n border: 1px solid var(--mj-border-default);\n}\n\n.pg-chart-ag-grid[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n}\n\n.pg-drilldown-footer[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-end;\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n padding-top: 2px;\n}"], changeDetection: 0 });
1874
+ }
1875
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PSPredictionsGridComponent, [{
1876
+ type: Component,
1877
+ args: [{ standalone: true, selector: 'ps-predictions-grid', imports: [
1878
+ CommonModule,
1879
+ FormsModule,
1880
+ SharedGenericModule,
1881
+ MJButtonDirective,
1882
+ MJViewToggleComponent,
1883
+ MjSlidePanelComponent,
1884
+ AgGridModule,
1885
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ps-pred-grid-host\" [style.height]=\"height\">\n <!-- \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 TOOLBAR \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"pg-toolbar\">\n <div class=\"pg-toolbar-left\">\n <div class=\"pg-search-box\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <input\n type=\"text\"\n class=\"pg-search-input\"\n placeholder=\"Filter records, outcome, factors...\"\n [value]=\"searchText\"\n (input)=\"onSearchInput($event)\" />\n @if (searchText) {\n <button type=\"button\" class=\"pg-search-clear\" (click)=\"clearSearch()\" title=\"Clear filter\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n }\n </div>\n\n <div class=\"pg-tier-filters\">\n <button\n type=\"button\"\n class=\"pg-tier-btn\"\n [class.active]=\"selectedTier === 'all'\"\n (click)=\"setTier('all')\">\n All <span class=\"pg-count-pill\">({{ allRows.length | number }})</span>\n </button>\n @for (b of visibleBands; track b.key) {\n <button\n type=\"button\"\n class=\"pg-tier-btn\"\n [class.active]=\"selectedTier === b.key\"\n (click)=\"setTier(b.key)\">\n {{ b.label }} <span class=\"pg-count-pill\">({{ b.count | number }})</span>\n </button>\n }\n\n @if (selectedDecileIndex !== null) {\n <div class=\"pg-decile-pill\">\n <i class=\"fa-solid fa-filter\"></i>\n <span>Decile {{ selectedDecileLabel }}</span>\n <button type=\"button\" (click)=\"clearDecileFilter()\" title=\"Clear decile filter\"><i class=\"fa-solid fa-xmark\"></i></button>\n </div>\n }\n </div>\n </div>\n\n <div class=\"pg-toolbar-right\">\n <mj-view-toggle\n [Options]=\"viewOptions\"\n [ActiveKey]=\"activeView\"\n (KeyChange)=\"onViewToggle($event)\">\n </mj-view-toggle>\n\n <button\n mjButton\n variant=\"secondary\"\n size=\"sm\"\n [disabled]=\"allRows.length === 0\"\n (click)=\"exportCSV()\">\n <i class=\"fa-solid fa-file-export\"></i> Export\n </button>\n </div>\n </div>\n\n <!-- \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 MAIN VIEW CONTAINER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"pg-view-container\">\n @if (isLoading) {\n <mj-loading text=\"Loading predictions...\" size=\"medium\"></mj-loading>\n } @else if (loadError) {\n <div class=\"ps-load-error\" role=\"alert\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <div class=\"ps-load-error-text\">\n <strong>Couldn't load predictions</strong>\n <span class=\"ps-load-error-detail\">{{ loadError }}</span>\n </div>\n <button mjButton variant=\"secondary\" size=\"sm\" (click)=\"reload()\"><i class=\"fa-solid fa-rotate-right\"></i> Try again</button>\n </div>\n } @else if (allRows.length === 0) {\n <div class=\"ps-empty\" style=\"padding: 32px\">\n <span class=\"ps-empty-ico\"><i class=\"fa-solid fa-chart-line\"></i></span>\n <h3>No predictions available</h3>\n <p>Run this model from Models in Production or wait for the scheduled job to produce predictions.</p>\n </div>\n } @else {\n <!-- GRID VIEW -->\n @if (activeView === 'grid') {\n <ag-grid-angular\n class=\"pg-grid\"\n [theme]=\"Theme\"\n [columnDefs]=\"columnDefs\"\n [defaultColDef]=\"defaultColDef\"\n [gridOptions]=\"GridOptions\"\n [rowData]=\"filteredRows\"\n (gridReady)=\"onGridReady($event)\"\n (rowClicked)=\"onRowClicked($event)\"\n (cellClicked)=\"onCellClicked($event)\">\n </ag-grid-angular>\n\n <div class=\"pg-footer\">\n <span>Showing <strong>{{ filteredRows.length }}</strong> of {{ allRows.length }} predictions</span>\n @if (selectedTier !== 'all' || searchText) {\n <span class=\"ps-muted\">Filtered by {{ selectedTier !== 'all' ? selectedTier + ' risk' : '' }}{{ searchText ? ' query \"' + searchText + '\"' : '' }}</span>\n }\n <span class=\"ps-muted\">Click any row to inspect details & score history</span>\n </div>\n }\n\n <!-- CHART VIEW -->\n @if (activeView === 'chart') {\n <div class=\"pg-chart-container\">\n <!-- KPI Summary Cards -->\n <div class=\"pg-chart-cards\">\n <div class=\"pg-chart-card total\">\n <span class=\"pg-chart-card-lbl\"><i class=\"fa-solid fa-users ps-muted\"></i> Scored Cohort</span>\n <span class=\"pg-chart-card-val\">{{ allRows.length | number }}</span>\n <span class=\"pg-chart-card-sub\">Total records</span>\n </div>\n @for (b of visibleBands; track b.key) {\n <div class=\"pg-chart-card\" [class]=\"'tier-' + b.key\" (click)=\"setTierAndGrid(b.key)\">\n <span class=\"pg-chart-card-lbl\">\n @if (b.icon) {\n <i class=\"fa-solid {{ b.icon }}\" [style.color]=\"getBadgeColorVar(b.badgeColor)\"></i>\n }\n {{ b.label }}\n </span>\n <span class=\"pg-chart-card-val\" [style.color]=\"getBadgeColorVar(b.badgeColor)\">\n {{ b.count | number }}\n </span>\n <span class=\"pg-chart-card-sub\">{{ b.pct }}% of cohort (avg {{ b.avgScoreFormatted }} {{ outcomeConfig.ScoreLabel }})</span>\n </div>\n }\n </div>\n\n <!-- Score Distribution Histogram -->\n <div class=\"pg-chart-section\">\n <div class=\"pg-chart-section-title\">\n <div style=\"display:flex;align-items:center;gap:8px\">\n <i class=\"fa-solid fa-chart-simple ps-muted\"></i>\n <span>{{ outcomeConfig.ScoreLabel }} Distribution (10 Bins)</span>\n </div>\n <div class=\"pg-chart-meta\">\n <span class=\"ps-small ps-muted\">Cohort Mean: <strong>{{ meanScoreFormatted }}</strong></span>\n @if (selectedDecileIndex !== null) {\n <button type=\"button\" class=\"pg-clear-decile-btn\" (click)=\"clearDecileFilter()\">\n <i class=\"fa-solid fa-xmark\"></i> Clear Decile Filter\n </button>\n }\n </div>\n </div>\n\n <!-- Modern Interactive Flex Histogram -->\n <div class=\"pg-histogram\">\n <div class=\"pg-h-columns\">\n @for (b of histogramBins; track b.label) {\n <div\n class=\"pg-h-col\"\n [class.active]=\"selectedDecileIndex === b.idx\"\n [class.empty]=\"b.count === 0\"\n (click)=\"selectDecile(b.idx)\">\n\n <!-- Count label above bar -->\n <span class=\"pg-h-count\" [title]=\"b.count + ' records'\">\n {{ b.count > 0 ? (b.count | number) : '0' }}\n </span>\n\n <!-- Bar track & fill -->\n <div class=\"pg-h-bar-track\">\n <div\n class=\"pg-h-bar\"\n [style.height.px]=\"b.height\"\n [style.background]=\"b.color\">\n </div>\n </div>\n\n <!-- Footer labels -->\n <div class=\"pg-h-footer\">\n <span class=\"pg-h-range\">{{ b.shortLabel }}</span>\n <span class=\"pg-h-pct\">{{ b.pctFormatted }}</span>\n </div>\n\n <!-- Tooltip card -->\n <div class=\"pg-h-tooltip\">\n <div class=\"pg-h-tt-head\">{{ b.label }} {{ outcomeConfig.ScoreLabel }}</div>\n <div class=\"pg-h-tt-row\">\n <span class=\"pg-h-tt-val\">{{ b.count | number }} records</span>\n <span class=\"pg-h-tt-pct\">({{ b.pctFormatted }} of cohort)</span>\n </div>\n <div class=\"pg-h-tt-badge\" [style.color]=\"b.color\">{{ b.riskLabel }}</div>\n <div class=\"pg-h-tt-hint\">{{ selectedDecileIndex === b.idx ? 'Click to clear filter' : 'Click to filter grid' }}</div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n\n <!-- Predicted Class Breakdown (for Classification) -->\n @if (classBreakdown.length > 0) {\n <div class=\"pg-chart-section\">\n <div class=\"pg-chart-section-title\">\n <span><i class=\"fa-solid fa-layer-group ps-muted\"></i> Predicted Outcome Classes</span>\n </div>\n <div style=\"display:flex;height:24px;border-radius:var(--mj-radius-full);overflow:hidden;background:var(--mj-bg-surface-sunken);margin-bottom:8px\">\n @for (c of classBreakdown; track c.name) {\n <div\n [style.width.%]=\"c.pct\"\n [style.background]=\"c.color\"\n [title]=\"c.name + ': ' + c.count + ' (' + c.pct + '%)'\">\n </div>\n }\n </div>\n <div style=\"display:flex;gap:16px;flex-wrap:wrap\">\n @for (c of classBreakdown; track c.name) {\n <div style=\"display:flex;align-items:center;gap:6px;font-size:var(--mj-text-xs)\">\n <span [style.background]=\"c.color\" style=\"width:10px;height:10px;border-radius:2px;display:inline-block\"></span>\n <strong>{{ c.name }}</strong>\n <span class=\"ps-muted\">{{ c.count }} ({{ c.pct }}%)</span>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Segment Drilldown Grid Section \u2014 Only shown when a decile segment is selected -->\n @if (selectedDecileIndex !== null) {\n <div id=\"pg-drilldown-anchor\" class=\"pg-chart-section pg-chart-drilldown-section\">\n <div class=\"pg-chart-section-title\">\n <div style=\"display:flex;align-items:center;gap:8px\">\n <i class=\"fa-solid fa-table-list ps-muted\"></i>\n <span>\n Decile Drill-Down:\n <strong style=\"color:var(--mj-brand-primary)\">{{ selectedDecileLabel }}</strong>\n {{ outcomeConfig.ScoreLabel }}\n @if (histogramBins[selectedDecileIndex]) {\n <span class=\"ps-badge\" [style.color]=\"histogramBins[selectedDecileIndex].color\" style=\"margin-left:6px\">\n {{ histogramBins[selectedDecileIndex].riskLabel }}\n </span>\n }\n </span>\n </div>\n <div class=\"pg-chart-meta\">\n <span class=\"ps-small ps-muted\">\n Showing <strong>{{ filteredRows.length | number }}</strong> of {{ allRows.length | number }} records\n </span>\n <button type=\"button\" class=\"pg-clear-decile-btn\" (click)=\"clearDecileFilter()\">\n <i class=\"fa-solid fa-xmark\"></i> Show All Deciles\n </button>\n </div>\n </div>\n\n <!-- Drill-Down AG Grid -->\n <div class=\"pg-chart-grid-host\">\n <ag-grid-angular\n class=\"pg-grid pg-chart-ag-grid\"\n [theme]=\"Theme\"\n [columnDefs]=\"columnDefs\"\n [defaultColDef]=\"defaultColDef\"\n [gridOptions]=\"chartGridOptions\"\n [rowData]=\"filteredRows\"\n (gridReady)=\"onChartGridReady($event)\"\n (rowClicked)=\"onRowClicked($event)\"\n (cellClicked)=\"onCellClicked($event)\">\n </ag-grid-angular>\n </div>\n\n <div class=\"pg-drilldown-footer\">\n <span class=\"ps-muted\">Click any row to inspect explainability drivers and score history in the slide-in drawer</span>\n </div>\n </div>\n }\n </div>\n }\n }\n </div>\n\n <!-- \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 SLIDE-IN DETAIL PANEL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (detailOpen && selectedRow) {\n <mj-slide-panel\n Mode=\"slide\"\n Title=\"Record Prediction & Score History\"\n [Resizable]=\"true\"\n [WidthPx]=\"drawerWidthPx\"\n (WidthChanged)=\"onDrawerWidthChanged($event)\"\n (Closed)=\"closeDetail()\">\n\n <div class=\"pg-detail-body\">\n <!-- Record header -->\n <div class=\"pg-detail-record-head\">\n <div class=\"pg-detail-record-info\">\n <h3>{{ selectedRow.recordName || selectedRow.recordId }}</h3>\n <div style=\"display:flex;align-items:center;gap:8px\">\n <span class=\"ps-badge blue\">{{ entityDisplayName || 'Entity Record' }}</span>\n <span class=\"ps-mono ps-small ps-muted\">{{ selectedRow.recordId }}</span>\n </div>\n </div>\n <div class=\"pg-detail-record-actions\" style=\"display:flex;gap:8px;align-items:center;flex-wrap:wrap\">\n <button\n mjButton\n variant=\"primary\"\n size=\"sm\"\n (click)=\"drillThrough(selectedRow)\"\n title=\"Open record in Explorer\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Open {{ entityDisplayName || 'Record' }}\n </button>\n @if (subjectEntityName && subjectRecordId) {\n <button\n mjButton\n variant=\"secondary\"\n size=\"sm\"\n (click)=\"openSubjectRecord()\"\n title=\"Open subject record (e.g. Person, Account) in Explorer\">\n <i class=\"fa-solid fa-user\"></i> Open {{ subjectEntityName.replace('MJ_BizApps_Common: ', '').replace('MJ_BizApps_', '') }} ({{ subjectRecordName || 'Profile' }})\n </button>\n }\n </div>\n </div>\n\n <!-- Prediction Summary Card -->\n <div class=\"pg-detail-pred-card\">\n <div class=\"pg-detail-score-circle\" [class]=\"'badge-' + (selectedRow.badgeColor || 'gray')\">\n {{ selectedRow.scoreFormatted }}\n </div>\n <div class=\"pg-detail-meta\">\n <div class=\"pg-detail-meta-line\">\n <strong>{{ outcomeConfig.StatusLabel }}:</strong>\n <span class=\"ps-badge\" [class]=\"selectedRow.badgeColor || 'gray'\" style=\"margin-left:6px\">\n @if (selectedRow.icon) {\n <i class=\"fa-solid {{ selectedRow.icon }}\"></i>\n }\n {{ selectedRow.status || selectedRow.band }}\n </span>\n </div>\n @if (selectedRow.class) {\n <div class=\"pg-detail-meta-line\">\n <strong>Predicted Outcome:</strong>\n <span class=\"ps-badge\" [class]=\"getOutcomeBadgeClass(selectedRow.class)\">\n {{ selectedRow.class }}\n </span>\n </div>\n }\n <div class=\"pg-detail-meta-line\">\n <strong>{{ outcomeConfig.ScoreLabel }}:</strong>\n <span style=\"font-weight:700;color:var(--mj-text-primary)\">{{ selectedRow.scoreFormatted }}</span>\n </div>\n <div class=\"pg-detail-meta-sub\">\n Scored: {{ selectedRow.scoredAtDate ? (selectedRow.scoredAtDate | date:'medium') : '\u2014' }}\n </div>\n </div>\n </div>\n\n <!-- Explainability: Local Feature Drivers -->\n <div class=\"pg-detail-drivers-wrap\">\n <div class=\"ps-section-title\">\n <i class=\"fa-solid fa-lightbulb\" style=\"color:var(--mj-status-warning)\"></i> Key Decision Drivers (Explainability)\n </div>\n @if (!selectedRow.drivers || selectedRow.drivers.length === 0) {\n <div class=\"ps-small ps-muted\">No individual driver breakdown was recorded for this row.</div>\n } @else {\n <div class=\"pg-detail-drivers-list\">\n @for (d of selectedRow.drivers; track d.label) {\n <div class=\"pg-detail-driver-item\">\n <div class=\"pg-detail-driver-label\">\n <i class=\"fa-solid\" [class.fa-arrow-up]=\"d.up\" [class.fa-arrow-down]=\"!d.up\" [style.color]=\"d.up ? 'var(--mj-status-error)' : 'var(--mj-status-success)'\"></i>\n {{ d.label }}\n </div>\n <span class=\"ps-badge\" [class]=\"d.up ? 'red' : 'green'\">\n {{ d.up ? 'Increases Risk' : 'Lowers Risk' }}\n </span>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Historical Trendline across Runs -->\n <div class=\"pg-trendline-wrap\">\n <div class=\"ps-section-title\" style=\"display:flex;align-items:center;justify-content:space-between\">\n <span><i class=\"fa-solid fa-clock-rotate-left ps-muted\"></i> Historical Score Trendline</span>\n <span class=\"ps-small ps-muted\">{{ historyPoints.length }} run{{ historyPoints.length === 1 ? '' : 's' }} recorded</span>\n </div>\n\n @if (historyLoading) {\n <mj-loading text=\"Loading score history...\" size=\"small\"></mj-loading>\n } @else if (historyPoints.length > 1) {\n <!-- Interactive SVG Trendline -->\n <svg class=\"pg-trendline-svg\" viewBox=\"0 0 540 160\" preserveAspectRatio=\"none\">\n <!-- Background grid lines -->\n <line x1=\"40\" y1=\"20\" x2=\"520\" y2=\"20\" stroke=\"var(--mj-border-subtle)\" stroke-dasharray=\"3,3\" />\n <line x1=\"40\" y1=\"75\" x2=\"520\" y2=\"75\" stroke=\"var(--mj-border-subtle)\" stroke-dasharray=\"3,3\" />\n <line x1=\"40\" y1=\"130\" x2=\"520\" y2=\"130\" stroke=\"var(--mj-border-default)\" />\n\n <!-- Y-axis labels -->\n <text x=\"32\" y=\"24\" font-size=\"10\" text-anchor=\"end\" fill=\"var(--mj-text-muted)\">100%</text>\n <text x=\"32\" y=\"79\" font-size=\"10\" text-anchor=\"end\" fill=\"var(--mj-text-muted)\">50%</text>\n <text x=\"32\" y=\"134\" font-size=\"10\" text-anchor=\"end\" fill=\"var(--mj-text-muted)\">0%</text>\n\n <!-- Filled area under curve -->\n <polygon [attr.points]=\"trendlineAreaPoints\" fill=\"color-mix(in srgb, var(--mj-brand-primary) 12%, transparent)\" />\n\n <!-- Trend line path -->\n <polyline [attr.points]=\"trendlineLinePoints\" fill=\"none\" stroke=\"var(--mj-brand-primary)\" stroke-width=\"2.5\" />\n\n <!-- Data points -->\n @for (p of trendlinePlotPoints; track p.runId) {\n <circle\n [attr.cx]=\"p.x\"\n [attr.cy]=\"p.y\"\n r=\"5\"\n [attr.fill]=\"p.color\"\n stroke=\"var(--mj-bg-surface)\"\n stroke-width=\"2\">\n <title>{{ p.dateStr }}: {{ p.scoreFormatted }} ({{ p.class || 'No class' }})</title>\n </circle>\n <text\n [attr.cx]=\"p.x\"\n [attr.cy]=\"p.y - 9\"\n font-size=\"10\"\n font-weight=\"700\"\n text-anchor=\"middle\"\n fill=\"var(--mj-text-primary)\">\n {{ p.scoreFormatted }}\n </text>\n }\n </svg>\n\n <!-- Runs Table -->\n <table class=\"pg-history-table\">\n <thead>\n <tr>\n <th>When</th>\n <th>Status</th>\n <th>Score</th>\n <th>Change</th>\n <th>Class</th>\n </tr>\n </thead>\n <tbody>\n @for (pt of historyPoints; track pt.runId) {\n <tr>\n <td>{{ pt.runDate | date:'short' }}</td>\n <td><span class=\"ps-badge\" [class]=\"pt.status === 'Succeeded' ? 'green' : 'gray'\">{{ pt.status }}</span></td>\n <td><strong class=\"ps-mono\">{{ pt.scoreFormatted }}</strong></td>\n <td>\n @if (pt.delta == null) {\n <span class=\"pg-delta-pill neutral\">Baseline</span>\n } @else if (pt.delta > 0.005) {\n <span class=\"pg-delta-pill up\"><i class=\"fa-solid fa-arrow-up\"></i> +{{ (pt.delta * 100).toFixed(1) }}%</span>\n } @else if (pt.delta < -0.005) {\n <span class=\"pg-delta-pill down\"><i class=\"fa-solid fa-arrow-down\"></i> {{ (pt.delta * 100).toFixed(1) }}%</span>\n } @else {\n <span class=\"pg-delta-pill neutral\">0%</span>\n }\n </td>\n <td>{{ pt.class || '\u2014' }}</td>\n </tr>\n }\n </tbody>\n </table>\n } @else if (historyPoints.length === 1) {\n <div class=\"ps-small ps-muted\">\n This record has been scored once so far ({{ historyPoints[0].runDate | date:'medium' }}). As more runs occur over time, the score trendline will track changes here.\n </div>\n } @else {\n <div class=\"ps-small ps-muted\">No historical runs found for this record.</div>\n }\n </div>\n </div>\n </mj-slide-panel>\n }\n</div>\n", styles: ["/* ============================================================================\n Predictive Studio \u2014 shared panel styles (design-token compliant)\n Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n so the class names are global within the PS dashboard subtree.\n No hardcoded colors except the intentional categorical node/algorithm palette\n (declared once as --ps-* custom props below) which must stay distinct across\n light/dark to remain readable as diagram accents.\n ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n data-visualization colors that must remain distinct regardless of theme\n (allowed exception #3 in the design-token rules). */\n.ps-panel {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n display: block;\n height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n.ps-card-head {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 8px; padding: 16px 18px;\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n display: inline-flex; align-items: center; gap: 5px;\n padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 92px; padding: 4px 10px; border-radius: 7px;\n font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n display: inline-block; padding: 2px 8px; border-radius: 6px;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: minmax(150px, 1.3fr) 2fr 56px; align-items: center; gap: 14px; padding: 8px 0; }\n.ps-fbar + .ps-fbar { border-top: 1px solid var(--mj-border-subtle); }\n.ps-fbar .track { height: 10px; border-radius: var(--mj-radius-full); background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; border-radius: var(--mj-radius-full); background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ps-fbar > .ps-mono { text-align: right; color: var(--mj-text-secondary); font-variant-numeric: tabular-nums; }\n\n/* gauge */\n.ps-gauge {\n position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n position: relative;\n background:\n linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default); border-radius: 10px;\n min-height: 620px; overflow: hidden;\n}\n.ps-node {\n position: absolute; width: 190px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 8px;\n cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n", "/* ============================================================================\n Predictive Studio \u2014 Unified Predictions Grid & Detail Panel styles\n Design-token compliant (all --mj-* semantic tokens).\n ============================================================================ */\n\n.ps-pred-grid-host {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n}\n\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 TOOLBAR \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pg-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 8px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n margin-bottom: 12px;\n flex-wrap: wrap;\n}\n\n.pg-toolbar-left {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 240px;\n}\n\n.pg-search-box {\n position: relative;\n display: flex;\n align-items: center;\n max-width: 260px;\n width: 100%;\n}\n\n.pg-search-box i {\n position: absolute;\n left: 10px;\n color: var(--mj-text-muted);\n font-size: 12px;\n pointer-events: none;\n}\n\n.pg-search-input {\n width: 100%;\n padding: 6px 10px 6px 30px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n background: var(--mj-bg-surface);\n font-size: var(--mj-text-xs);\n color: var(--mj-text-primary);\n outline: none;\n transition: border-color .15s;\n}\n\n.pg-search-input:focus {\n border-color: var(--mj-brand-primary);\n}\n\n.pg-search-clear {\n position: absolute;\n right: 8px;\n background: transparent;\n border: none;\n cursor: pointer;\n padding: 2px 4px;\n color: var(--mj-text-muted);\n font-size: 11px;\n}\n\n.pg-search-clear:hover {\n color: var(--mj-text-primary);\n}\n\n.pg-decile-pill {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 2px 8px;\n border-radius: var(--mj-radius-full);\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n border: 1px solid var(--mj-brand-primary);\n font-size: var(--mj-text-xs);\n font-weight: 600;\n color: var(--mj-brand-primary);\n}\n\n.pg-decile-pill button {\n background: transparent;\n border: none;\n cursor: pointer;\n padding: 0;\n color: var(--mj-brand-primary);\n font-size: 10px;\n}\n\n.pg-tier-filters {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.pg-tier-btn {\n cursor: pointer;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n border-radius: var(--mj-radius-full);\n padding: 3px 10px;\n font-size: var(--mj-text-xs);\n font-weight: 600;\n color: var(--mj-text-secondary);\n transition: all .15s;\n display: inline-flex;\n align-items: center;\n gap: 5px;\n}\n\n.pg-tier-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.pg-tier-btn.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n.pg-tier-btn.tier-high.active {\n background: var(--mj-status-error);\n border-color: var(--mj-status-error);\n}\n\n.pg-tier-btn.tier-medium.active {\n background: var(--mj-status-warning);\n border-color: var(--mj-status-warning);\n}\n\n.pg-tier-btn.tier-low.active {\n background: var(--mj-status-success);\n border-color: var(--mj-status-success);\n}\n\n.pg-count-pill {\n font-size: 10px;\n opacity: .85;\n}\n\n.pg-toolbar-right {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 GRID CONTAINER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pg-view-container {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n.pg-grid {\n width: 100%;\n flex: 1;\n min-height: 380px;\n}\n\n/* AG Grid cell styling overrides */\n.pg-record-cell {\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: 2px;\n height: 100%;\n min-width: 0;\n line-height: 1.2;\n}\n\n.pg-record-title-row {\n display: flex;\n align-items: center;\n min-width: 0;\n}\n\n.pg-record-link {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n max-width: 100%;\n text-decoration: none;\n cursor: pointer;\n min-width: 0;\n}\n\n.pg-record-name {\n font-weight: 600;\n font-size: var(--mj-text-sm);\n color: var(--mj-text-link);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.pg-record-link:hover .pg-record-name {\n text-decoration: underline;\n}\n\n.pg-record-open-icon {\n font-size: 10px;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n transition: transform .15s, color .15s;\n}\n\n.pg-record-link:hover .pg-record-open-icon {\n transform: translateY(-1px) translateX(1px);\n color: var(--mj-text-primary);\n}\n\n.pg-record-id {\n font-size: 11px;\n color: var(--mj-text-muted);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.pg-score-cell {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 100%;\n min-width: 0;\n}\n\n.pg-score-bar {\n flex: 1;\n height: 8px;\n border-radius: var(--mj-radius-full);\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n}\n\n.pg-score-fill {\n display: block;\n height: 100%;\n border-radius: var(--mj-radius-full);\n}\n\n.pg-score-fill.risk-high,\n.pg-score-fill.badge-red { background: var(--mj-status-error); }\n.pg-score-fill.risk-medium,\n.pg-score-fill.badge-amber { background: var(--mj-status-warning); }\n.pg-score-fill.risk-low,\n.pg-score-fill.badge-green { background: var(--mj-status-success); }\n.pg-score-fill.badge-blue { background: var(--mj-brand-primary); }\n.pg-score-fill.badge-gray { background: var(--mj-text-muted); }\n\n.pg-score-val {\n font-weight: 700;\n font-size: var(--mj-text-xs);\n font-variant-numeric: tabular-nums;\n min-width: 44px;\n text-align: right;\n}\n\n.pg-score-val.risk-high,\n.pg-score-val.badge-red { color: var(--mj-status-error-text, var(--mj-status-error)); }\n.pg-score-val.risk-medium,\n.pg-score-val.badge-amber { color: var(--mj-status-warning-text, var(--mj-status-warning)); }\n.pg-score-val.risk-low,\n.pg-score-val.badge-green { color: var(--mj-status-success-text, var(--mj-status-success)); }\n.pg-score-val.badge-blue { color: var(--mj-brand-primary); }\n.pg-score-val.badge-gray { color: var(--mj-text-muted); }\n\n.pg-drivers-cell {\n display: flex;\n align-items: center;\n gap: 6px;\n height: 100%;\n overflow: hidden;\n flex-wrap: nowrap;\n}\n\n.pg-action-btn {\n background: transparent;\n border: none;\n cursor: pointer;\n padding: 6px 8px;\n border-radius: 6px;\n color: var(--mj-text-muted);\n font-size: 12px;\n transition: all .15s;\n}\n\n.pg-action-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-brand-primary);\n}\n\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 FOOTER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pg-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-subtle);\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n}\n\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 CHART VIEW \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pg-chart-container {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 4px 0 12px;\n}\n\n.pg-chart-cards {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 12px;\n}\n\n.pg-chart-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 14px 16px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n cursor: pointer;\n transition: all .15s;\n}\n\n.pg-chart-card:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.pg-chart-card.total { border-left: 4px solid var(--mj-brand-primary); }\n.pg-chart-card.tier-high { border-left: 4px solid var(--mj-status-error); }\n.pg-chart-card.tier-medium { border-left: 4px solid var(--mj-status-warning); }\n.pg-chart-card.tier-low { border-left: 4px solid var(--mj-status-success); }\n\n.pg-chart-card-val {\n font-size: var(--mj-text-xl);\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.pg-chart-card-lbl {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-secondary);\n font-weight: 600;\n}\n\n.pg-chart-card-sub {\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.pg-chart-section {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 18px 20px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.pg-chart-section-title {\n font-size: var(--mj-text-sm);\n font-weight: 650;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.pg-chart-meta {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.pg-clear-decile-btn {\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-full);\n padding: 2px 10px;\n font-size: 11px;\n font-weight: 600;\n cursor: pointer;\n color: var(--mj-text-secondary);\n transition: all .15s;\n}\n\n.pg-clear-decile-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-subtle);\n}\n\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 MODERN HISTOGRAM \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pg-histogram {\n position: relative;\n width: 100%;\n padding-top: 8px;\n padding-bottom: 28px;\n}\n\n.pg-h-columns {\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n gap: 8px;\n height: 200px;\n padding: 0 4px;\n border-bottom: 2px solid var(--mj-border-default);\n}\n\n.pg-h-col {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: flex-end;\n height: 100%;\n cursor: pointer;\n position: relative;\n transition: transform .15s;\n user-select: none;\n}\n\n.pg-h-col:hover {\n transform: translateY(-2px);\n}\n\n.pg-h-count {\n font-size: 11px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin-bottom: 6px;\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n}\n\n.pg-h-bar-track {\n width: 100%;\n max-width: 52px;\n display: flex;\n align-items: flex-end;\n justify-content: center;\n}\n\n.pg-h-bar {\n width: 100%;\n border-radius: 4px 4px 0 0;\n transition: height .3s cubic-bezier(0.4, 0, 0.2, 1), opacity .15s, box-shadow .15s;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);\n}\n\n.pg-h-col:hover .pg-h-bar {\n filter: brightness(1.1);\n box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);\n}\n\n.pg-h-col.active .pg-h-bar {\n outline: 3px solid var(--mj-brand-primary);\n outline-offset: 1px;\n}\n\n.pg-h-footer {\n display: flex;\n flex-direction: column;\n align-items: center;\n margin-top: 8px;\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n}\n\n.pg-h-range {\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n}\n\n.pg-h-pct {\n font-size: 10px;\n color: var(--mj-text-muted);\n}\n\n/* Tooltip card */\n.pg-h-tooltip {\n position: absolute;\n bottom: calc(100% + 4px);\n left: 50%;\n transform: translateX(-50%);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n border-radius: var(--mj-radius-md);\n padding: 8px 12px;\n white-space: nowrap;\n pointer-events: none;\n opacity: 0;\n visibility: hidden;\n transition: opacity .15s, visibility .15s;\n z-index: 50;\n}\n\n.pg-h-col:hover .pg-h-tooltip {\n opacity: 1;\n visibility: visible;\n}\n\n.pg-h-tt-head {\n font-size: 11px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin-bottom: 2px;\n}\n\n.pg-h-tt-row {\n display: flex;\n gap: 6px;\n align-items: center;\n font-size: 11px;\n}\n\n.pg-h-tt-val {\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.pg-h-tt-pct {\n color: var(--mj-text-muted);\n}\n\n.pg-h-tt-badge {\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-top: 3px;\n}\n\n.pg-h-tt-hint {\n font-size: 9px;\n color: var(--mj-text-muted);\n font-style: italic;\n margin-top: 4px;\n}\n\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 SLIDE PANEL DETAIL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pg-detail-body {\n display: flex;\n flex-direction: column;\n gap: 20px;\n padding: 18px 20px 32px;\n}\n\n.pg-detail-record-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 14px;\n padding-bottom: 14px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.pg-detail-record-info h3 {\n margin: 0 0 4px;\n font-size: var(--mj-text-lg);\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.pg-detail-pred-card {\n display: flex;\n align-items: center;\n gap: 20px;\n padding: 16px 18px;\n border-radius: var(--mj-radius-md);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n}\n\n.pg-detail-score-circle {\n width: 72px;\n height: 72px;\n border-radius: 50%;\n display: grid;\n place-items: center;\n font-size: 19px;\n font-weight: 800;\n color: var(--mj-text-inverse);\n flex-shrink: 0;\n box-shadow: 0 2px 8px rgba(0, 0, 0, .12);\n}\n\n.pg-detail-score-circle.risk-high,\n.pg-detail-score-circle.badge-red { background: var(--mj-status-error); }\n.pg-detail-score-circle.risk-medium,\n.pg-detail-score-circle.badge-amber { background: var(--mj-status-warning); }\n.pg-detail-score-circle.risk-low,\n.pg-detail-score-circle.badge-green { background: var(--mj-status-success); }\n.pg-detail-score-circle.badge-blue { background: var(--mj-brand-primary); }\n.pg-detail-score-circle.badge-gray { background: var(--mj-text-muted); }\n\n.pg-detail-meta {\n display: flex;\n flex-direction: column;\n gap: 4px;\n flex: 1;\n}\n\n.pg-detail-meta-line {\n font-size: var(--mj-text-sm);\n color: var(--mj-text-primary);\n}\n\n.pg-detail-meta-sub {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n}\n\n.pg-detail-drivers-wrap {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.pg-detail-drivers-list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.pg-detail-driver-item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: var(--mj-radius-sm);\n}\n\n.pg-detail-driver-label {\n font-size: var(--mj-text-xs);\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 TRENDLINE CHART & TABLE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pg-trendline-wrap {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n}\n\n.pg-trendline-svg {\n width: 100%;\n height: 180px;\n overflow: visible;\n}\n\n.pg-history-table {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--mj-text-xs);\n margin-top: 6px;\n}\n\n.pg-history-table th {\n text-align: left;\n padding: 6px 8px;\n border-bottom: 1px solid var(--mj-border-default);\n color: var(--mj-text-muted);\n font-weight: 600;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: .03em;\n}\n\n.pg-history-table td {\n padding: 7px 8px;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-secondary);\n}\n\n.pg-history-table tr:last-child td {\n border-bottom: none;\n}\n\n.pg-delta-pill {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n font-weight: 700;\n font-size: 11px;\n padding: 1px 6px;\n border-radius: var(--mj-radius-full);\n}\n\n.pg-delta-pill.up { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.pg-delta-pill.down { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.pg-delta-pill.neutral { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 CHART DRILLDOWN GRID \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.pg-chart-drilldown-section {\n padding: 16px 18px;\n gap: 12px;\n}\n\n.pg-chart-grid-host {\n width: 100%;\n height: 440px;\n min-height: 320px;\n border-radius: var(--mj-radius-sm);\n overflow: hidden;\n border: 1px solid var(--mj-border-default);\n}\n\n.pg-chart-ag-grid {\n width: 100%;\n height: 100%;\n}\n\n.pg-drilldown-footer {\n display: flex;\n justify-content: flex-end;\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n padding-top: 2px;\n}\n"] }]
1886
+ }], null, { modelId: [{
1887
+ type: Input
1888
+ }], runId: [{
1889
+ type: Input
1890
+ }], entityName: [{
1891
+ type: Input
1892
+ }], problemType: [{
1893
+ type: Input
1894
+ }], title: [{
1895
+ type: Input
1896
+ }], height: [{
1897
+ type: Input
1898
+ }], readOnly: [{
1899
+ type: Input
1900
+ }] }); })();
1901
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PSPredictionsGridComponent, { className: "PSPredictionsGridComponent", filePath: "src/PredictiveStudio/components/ps-predictions-grid.component.ts", lineNumber: 151 }); })();
1902
+ //# sourceMappingURL=ps-predictions-grid.component.js.map