@memberjunction/ng-core-entity-forms 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 (73) hide show
  1. package/README.md +22 -0
  2. package/dist/lib/custom/MLModels/ml-model-form.component.d.ts +27 -0
  3. package/dist/lib/custom/MLModels/ml-model-form.component.d.ts.map +1 -0
  4. package/dist/lib/custom/MLModels/ml-model-form.component.js +193 -0
  5. package/dist/lib/custom/MLModels/ml-model-form.component.js.map +1 -0
  6. package/dist/lib/custom/MLModels/ml-model-view-models.d.ts +44 -0
  7. package/dist/lib/custom/MLModels/ml-model-view-models.d.ts.map +1 -0
  8. package/dist/lib/custom/MLModels/ml-model-view-models.js +208 -0
  9. package/dist/lib/custom/MLModels/ml-model-view-models.js.map +1 -0
  10. package/dist/lib/custom/MLModels/ps-model-detail.component.d.ts +76 -0
  11. package/dist/lib/custom/MLModels/ps-model-detail.component.d.ts.map +1 -0
  12. package/dist/lib/custom/MLModels/ps-model-detail.component.js +693 -0
  13. package/dist/lib/custom/MLModels/ps-model-detail.component.js.map +1 -0
  14. package/dist/lib/custom/RecordProcesses/record-process-form.component.d.ts +27 -7
  15. package/dist/lib/custom/RecordProcesses/record-process-form.component.d.ts.map +1 -1
  16. package/dist/lib/custom/RecordProcesses/record-process-form.component.js +496 -19
  17. package/dist/lib/custom/RecordProcesses/record-process-form.component.js.map +1 -1
  18. package/dist/lib/custom/custom-forms.module.d.ts +32 -30
  19. package/dist/lib/custom/custom-forms.module.d.ts.map +1 -1
  20. package/dist/lib/custom/custom-forms.module.js +21 -4
  21. package/dist/lib/custom/custom-forms.module.js.map +1 -1
  22. package/dist/lib/generated/Entities/MJAIPrompt/mjaiprompt.form.component.d.ts.map +1 -1
  23. package/dist/lib/generated/Entities/MJAIPrompt/mjaiprompt.form.component.js +62 -26
  24. package/dist/lib/generated/Entities/MJAIPrompt/mjaiprompt.form.component.js.map +1 -1
  25. package/dist/lib/generated/Entities/MJCredential/mjcredential.form.component.d.ts.map +1 -1
  26. package/dist/lib/generated/Entities/MJCredential/mjcredential.form.component.js +33 -15
  27. package/dist/lib/generated/Entities/MJCredential/mjcredential.form.component.js.map +1 -1
  28. package/dist/lib/generated/Entities/MJEntity/mjentity.form.component.d.ts.map +1 -1
  29. package/dist/lib/generated/Entities/MJEntity/mjentity.form.component.js +100 -82
  30. package/dist/lib/generated/Entities/MJEntity/mjentity.form.component.js.map +1 -1
  31. package/dist/lib/generated/Entities/MJEntityField/mjentityfield.form.component.js +24 -20
  32. package/dist/lib/generated/Entities/MJEntityField/mjentityfield.form.component.js.map +1 -1
  33. package/dist/lib/generated/Entities/MJFeatureValue/mjfeaturevalue.form.component.d.ts +10 -0
  34. package/dist/lib/generated/Entities/MJFeatureValue/mjfeaturevalue.form.component.d.ts.map +1 -0
  35. package/dist/lib/generated/Entities/MJFeatureValue/mjfeaturevalue.form.component.js +122 -0
  36. package/dist/lib/generated/Entities/MJFeatureValue/mjfeaturevalue.form.component.js.map +1 -0
  37. package/dist/lib/generated/Entities/MJFeatureValueCache/mjfeaturevaluecache.form.component.d.ts +10 -0
  38. package/dist/lib/generated/Entities/MJFeatureValueCache/mjfeaturevaluecache.form.component.d.ts.map +1 -0
  39. package/dist/lib/generated/Entities/MJFeatureValueCache/mjfeaturevaluecache.form.component.js +125 -0
  40. package/dist/lib/generated/Entities/MJFeatureValueCache/mjfeaturevaluecache.form.component.js.map +1 -0
  41. package/dist/lib/generated/Entities/MJProcessRun/mjprocessrun.form.component.d.ts.map +1 -1
  42. package/dist/lib/generated/Entities/MJProcessRun/mjprocessrun.form.component.js +23 -5
  43. package/dist/lib/generated/Entities/MJProcessRun/mjprocessrun.form.component.js.map +1 -1
  44. package/dist/lib/generated/Entities/MJProcessRunDetail/mjprocessrundetail.form.component.d.ts.map +1 -1
  45. package/dist/lib/generated/Entities/MJProcessRunDetail/mjprocessrundetail.form.component.js +25 -6
  46. package/dist/lib/generated/Entities/MJProcessRunDetail/mjprocessrundetail.form.component.js.map +1 -1
  47. package/dist/lib/generated/Entities/MJQuery/mjquery.form.component.d.ts.map +1 -1
  48. package/dist/lib/generated/Entities/MJQuery/mjquery.form.component.js +11 -15
  49. package/dist/lib/generated/Entities/MJQuery/mjquery.form.component.js.map +1 -1
  50. package/dist/lib/generated/Entities/MJRecordProcess/mjrecordprocess.form.component.d.ts.map +1 -1
  51. package/dist/lib/generated/Entities/MJRecordProcess/mjrecordprocess.form.component.js +43 -7
  52. package/dist/lib/generated/Entities/MJRecordProcess/mjrecordprocess.form.component.js.map +1 -1
  53. package/dist/lib/generated/Entities/MJWebSearchProvider/mjwebsearchprovider.form.component.d.ts +10 -0
  54. package/dist/lib/generated/Entities/MJWebSearchProvider/mjwebsearchprovider.form.component.d.ts.map +1 -0
  55. package/dist/lib/generated/Entities/MJWebSearchProvider/mjwebsearchprovider.form.component.js +78 -0
  56. package/dist/lib/generated/Entities/MJWebSearchProvider/mjwebsearchprovider.form.component.js.map +1 -0
  57. package/dist/lib/generated/generated-forms.module.d.ts +240 -237
  58. package/dist/lib/generated/generated-forms.module.d.ts.map +1 -1
  59. package/dist/lib/generated/generated-forms.module.js +11 -2
  60. package/dist/lib/generated/generated-forms.module.js.map +1 -1
  61. package/dist/lib/panels/model-predictions/model-prediction.logic.d.ts +95 -0
  62. package/dist/lib/panels/model-predictions/model-prediction.logic.d.ts.map +1 -1
  63. package/dist/lib/panels/model-predictions/model-prediction.logic.js +173 -0
  64. package/dist/lib/panels/model-predictions/model-prediction.logic.js.map +1 -1
  65. package/dist/lib/panels/model-predictions/model-prediction.panel.d.ts +57 -2
  66. package/dist/lib/panels/model-predictions/model-prediction.panel.d.ts.map +1 -1
  67. package/dist/lib/panels/model-predictions/model-prediction.panel.js +555 -64
  68. package/dist/lib/panels/model-predictions/model-prediction.panel.js.map +1 -1
  69. package/dist/public-api.d.ts +4 -0
  70. package/dist/public-api.d.ts.map +1 -1
  71. package/dist/public-api.js +4 -0
  72. package/dist/public-api.js.map +1 -1
  73. package/package.json +42 -41
@@ -4,32 +4,56 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { Component } from '@angular/core';
7
+ import { ChangeDetectorRef, Component, inject } from '@angular/core';
8
8
  import { RegisterClassEx } from '@memberjunction/global';
9
9
  import { BaseFormPanel } from '@memberjunction/ng-base-forms';
10
- import { LogError, Metadata, RunView } from '@memberjunction/core';
11
- import { bandFor, formatLastScored, formatValue, gaugePct, parseDrivers, resolveLabel, toNumber, valueKind, } from './model-prediction.logic';
10
+ import { CompositeKey, LogError, Metadata, RunView } from '@memberjunction/core';
11
+ import { resolveOutcomeConfig, resolveOutcomeStyle, resolveScoreBand, } from '@memberjunction/predictive-studio-core';
12
+ import { bandFor, filterHistoryByModel, formatLastScored, formatValue, gaugePct, getDistinctModelsFromHistory, parseDrivers, parseHistoryItem, resolveLabel, toNumber, valueKind, } from './model-prediction.logic';
12
13
  import * as i0 from "@angular/core";
13
14
  import * as i1 from "@memberjunction/ng-base-forms";
15
+ import * as i2 from "@angular/common";
14
16
  const _forTrack0 = ($index, $item) => $item.bindingId;
15
17
  const _forTrack1 = ($index, $item) => $item.name;
16
- function ModelPredictionPanel_Conditional_0_For_3_Conditional_8_Template(rf, ctx) { if (rf & 1) {
17
- i0.ɵɵelementStart(0, "span", 11);
18
+ const _forTrack2 = ($index, $item) => $item.id;
19
+ const _forTrack3 = ($index, $item) => $item.modelId;
20
+ function ModelPredictionPanel_Conditional_0_Conditional_1_For_2_Conditional_9_Template(rf, ctx) { if (rf & 1) {
21
+ i0.ɵɵelementStart(0, "span", 21);
18
22
  i0.ɵɵtext(1, "\u00B7");
19
23
  i0.ɵɵelementEnd();
20
- i0.ɵɵelementStart(2, "span", 12);
24
+ i0.ɵɵelementStart(2, "span", 22);
21
25
  i0.ɵɵtext(3);
22
26
  i0.ɵɵelementEnd();
23
27
  } if (rf & 2) {
24
- const card_r1 = i0.ɵɵnextContext().$implicit;
28
+ const card_r3 = i0.ɵɵnextContext().$implicit;
25
29
  i0.ɵɵadvance(3);
26
- i0.ɵɵtextInterpolate1("Scored ", card_r1.lastScored);
30
+ i0.ɵɵtextInterpolate1("Scored ", card_r3.lastScored);
27
31
  } }
28
- function ModelPredictionPanel_Conditional_0_For_3_Conditional_12_Template(rf, ctx) { if (rf & 1) {
29
- i0.ɵɵelementStart(0, "div", 9)(1, "div", 13);
30
- i0.ɵɵelement(2, "div", 14);
32
+ function ModelPredictionPanel_Conditional_0_Conditional_1_For_2_Conditional_16_Conditional_1_Template(rf, ctx) { if (rf & 1) {
33
+ i0.ɵɵelement(0, "i");
34
+ } if (rf & 2) {
35
+ const card_r3 = i0.ɵɵnextContext(2).$implicit;
36
+ i0.ɵɵclassMap("fa-solid " + card_r3.badgeIcon);
37
+ } }
38
+ function ModelPredictionPanel_Conditional_0_Conditional_1_For_2_Conditional_16_Template(rf, ctx) { if (rf & 1) {
39
+ i0.ɵɵelementStart(0, "span", 23);
40
+ i0.ɵɵconditionalCreate(1, ModelPredictionPanel_Conditional_0_Conditional_1_For_2_Conditional_16_Conditional_1_Template, 1, 2, "i", 24);
41
+ i0.ɵɵelementStart(2, "span");
42
+ i0.ɵɵtext(3);
43
+ i0.ɵɵelementEnd()();
44
+ } if (rf & 2) {
45
+ const card_r3 = i0.ɵɵnextContext().$implicit;
46
+ i0.ɵɵclassMap("mpp-badge-" + card_r3.badgeColor);
47
+ i0.ɵɵadvance();
48
+ i0.ɵɵconditional(card_r3.badgeIcon ? 1 : -1);
49
+ i0.ɵɵadvance(2);
50
+ i0.ɵɵtextInterpolate(card_r3.statusLabel);
51
+ } }
52
+ function ModelPredictionPanel_Conditional_0_Conditional_1_For_2_Conditional_17_Template(rf, ctx) { if (rf & 1) {
53
+ i0.ɵɵelementStart(0, "div", 19)(1, "div", 25);
54
+ i0.ɵɵelement(2, "div", 26);
31
55
  i0.ɵɵelementEnd();
32
- i0.ɵɵelementStart(3, "div", 15)(4, "span");
56
+ i0.ɵɵelementStart(3, "div", 27)(4, "span");
33
57
  i0.ɵɵtext(5, "0%");
34
58
  i0.ɵɵelementEnd();
35
59
  i0.ɵɵelementStart(6, "span");
@@ -39,83 +63,357 @@ function ModelPredictionPanel_Conditional_0_For_3_Conditional_12_Template(rf, ct
39
63
  i0.ɵɵtext(9, "100%");
40
64
  i0.ɵɵelementEnd()()();
41
65
  } if (rf & 2) {
42
- const card_r1 = i0.ɵɵnextContext().$implicit;
43
- i0.ɵɵattribute("aria-label", card_r1.label + " " + card_r1.displayValue + ", " + card_r1.band + " band");
66
+ const card_r3 = i0.ɵɵnextContext().$implicit;
67
+ i0.ɵɵattribute("aria-label", card_r3.label + " " + card_r3.displayValue + (card_r3.statusLabel ? ", " + card_r3.statusLabel : ""));
44
68
  i0.ɵɵadvance(2);
45
- i0.ɵɵstyleProp("width", card_r1.gaugePct, "%");
46
- i0.ɵɵclassProp("mpp-band-low", card_r1.band === "low")("mpp-band-mid", card_r1.band === "mid")("mpp-band-high", card_r1.band === "high");
69
+ i0.ɵɵclassMap("mpp-color-" + card_r3.badgeColor);
70
+ i0.ɵɵstyleProp("width", card_r3.gaugePct, "%");
71
+ i0.ɵɵclassProp("mpp-band-low", card_r3.band === "low")("mpp-band-mid", card_r3.band === "mid")("mpp-band-high", card_r3.band === "high");
47
72
  } }
48
- function ModelPredictionPanel_Conditional_0_For_3_Conditional_13_For_5_Template(rf, ctx) { if (rf & 1) {
49
- i0.ɵɵelementStart(0, "div", 18)(1, "span", 19);
73
+ function ModelPredictionPanel_Conditional_0_Conditional_1_For_2_Conditional_18_For_5_Template(rf, ctx) { if (rf & 1) {
74
+ i0.ɵɵelementStart(0, "div", 30)(1, "span", 31);
50
75
  i0.ɵɵtext(2);
51
76
  i0.ɵɵelementEnd();
52
- i0.ɵɵelementStart(3, "span", 20);
53
- i0.ɵɵelement(4, "span", 21);
77
+ i0.ɵɵelementStart(3, "span", 32);
78
+ i0.ɵɵelement(4, "span", 33);
54
79
  i0.ɵɵelementEnd()();
55
80
  } if (rf & 2) {
56
- const driver_r2 = ctx.$implicit;
81
+ const driver_r5 = ctx.$implicit;
57
82
  i0.ɵɵadvance();
58
- i0.ɵɵproperty("title", driver_r2.name);
83
+ i0.ɵɵproperty("title", driver_r5.name);
59
84
  i0.ɵɵadvance();
60
- i0.ɵɵtextInterpolate(driver_r2.name);
85
+ i0.ɵɵtextInterpolate(driver_r5.name);
61
86
  i0.ɵɵadvance(2);
62
- i0.ɵɵstyleProp("width", driver_r2.relativePct, "%");
87
+ i0.ɵɵstyleProp("width", driver_r5.relativePct, "%");
63
88
  } }
64
- function ModelPredictionPanel_Conditional_0_For_3_Conditional_13_Template(rf, ctx) { if (rf & 1) {
65
- i0.ɵɵelementStart(0, "div", 10)(1, "div", 16);
66
- i0.ɵɵelement(2, "i", 17);
89
+ function ModelPredictionPanel_Conditional_0_Conditional_1_For_2_Conditional_18_Template(rf, ctx) { if (rf & 1) {
90
+ i0.ɵɵelementStart(0, "div", 20)(1, "div", 28);
91
+ i0.ɵɵelement(2, "i", 29);
67
92
  i0.ɵɵtext(3, " Top drivers ");
68
93
  i0.ɵɵelementEnd();
69
- i0.ɵɵrepeaterCreate(4, ModelPredictionPanel_Conditional_0_For_3_Conditional_13_For_5_Template, 5, 4, "div", 18, _forTrack1);
94
+ i0.ɵɵrepeaterCreate(4, ModelPredictionPanel_Conditional_0_Conditional_1_For_2_Conditional_18_For_5_Template, 5, 4, "div", 30, _forTrack1);
70
95
  i0.ɵɵelementEnd();
71
96
  } if (rf & 2) {
72
- const card_r1 = i0.ɵɵnextContext().$implicit;
97
+ const card_r3 = i0.ɵɵnextContext().$implicit;
73
98
  i0.ɵɵadvance(4);
74
- i0.ɵɵrepeater(card_r1.drivers);
99
+ i0.ɵɵrepeater(card_r3.drivers);
100
+ } }
101
+ function ModelPredictionPanel_Conditional_0_Conditional_1_For_2_Template(rf, ctx) { if (rf & 1) {
102
+ const _r2 = i0.ɵɵgetCurrentView();
103
+ i0.ɵɵelementStart(0, "div", 9)(1, "div", 10)(2, "div", 11)(3, "div", 12);
104
+ i0.ɵɵtext(4);
105
+ i0.ɵɵelementEnd();
106
+ i0.ɵɵelementStart(5, "div", 13);
107
+ i0.ɵɵelement(6, "i", 14);
108
+ i0.ɵɵelementStart(7, "span");
109
+ i0.ɵɵtext(8);
110
+ i0.ɵɵelementEnd();
111
+ i0.ɵɵconditionalCreate(9, ModelPredictionPanel_Conditional_0_Conditional_1_For_2_Conditional_9_Template, 4, 1);
112
+ i0.ɵɵelementEnd()();
113
+ i0.ɵɵelementStart(10, "button", 15);
114
+ i0.ɵɵlistener("click", function ModelPredictionPanel_Conditional_0_Conditional_1_For_2_Template_button_click_10_listener($event) { const card_r3 = i0.ɵɵrestoreView(_r2).$implicit; const ctx_r3 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r3.OpenHistoryForModel(card_r3.modelId, $event)); });
115
+ i0.ɵɵelement(11, "i", 5);
116
+ i0.ɵɵtext(12, " History ");
117
+ i0.ɵɵelementEnd()();
118
+ i0.ɵɵelementStart(13, "div", 16)(14, "span", 17);
119
+ i0.ɵɵtext(15);
120
+ i0.ɵɵelementEnd();
121
+ i0.ɵɵconditionalCreate(16, ModelPredictionPanel_Conditional_0_Conditional_1_For_2_Conditional_16_Template, 4, 4, "span", 18);
122
+ i0.ɵɵelementEnd();
123
+ i0.ɵɵconditionalCreate(17, ModelPredictionPanel_Conditional_0_Conditional_1_For_2_Conditional_17_Template, 10, 11, "div", 19);
124
+ i0.ɵɵconditionalCreate(18, ModelPredictionPanel_Conditional_0_Conditional_1_For_2_Conditional_18_Template, 6, 0, "div", 20);
125
+ i0.ɵɵelementEnd();
126
+ } if (rf & 2) {
127
+ const card_r3 = ctx.$implicit;
128
+ i0.ɵɵadvance(4);
129
+ i0.ɵɵtextInterpolate(card_r3.label);
130
+ i0.ɵɵadvance(4);
131
+ i0.ɵɵtextInterpolate(card_r3.provenance);
132
+ i0.ɵɵadvance();
133
+ i0.ɵɵconditional(card_r3.lastScored ? 9 : -1);
134
+ i0.ɵɵadvance(5);
135
+ i0.ɵɵclassProp("mpp-value-muted", card_r3.displayValue === "\u2014");
136
+ i0.ɵɵadvance();
137
+ i0.ɵɵtextInterpolate1(" ", card_r3.displayValue, " ");
138
+ i0.ɵɵadvance();
139
+ i0.ɵɵconditional(card_r3.statusLabel ? 16 : -1);
140
+ i0.ɵɵadvance();
141
+ i0.ɵɵconditional(card_r3.isProbability ? 17 : -1);
142
+ i0.ɵɵadvance();
143
+ i0.ɵɵconditional(card_r3.drivers.length > 0 ? 18 : -1);
144
+ } }
145
+ function ModelPredictionPanel_Conditional_0_Conditional_1_Template(rf, ctx) { if (rf & 1) {
146
+ i0.ɵɵelementStart(0, "div", 1);
147
+ i0.ɵɵrepeaterCreate(1, ModelPredictionPanel_Conditional_0_Conditional_1_For_2_Template, 19, 9, "div", 9, _forTrack0);
148
+ i0.ɵɵelementEnd();
149
+ } if (rf & 2) {
150
+ const ctx_r3 = i0.ɵɵnextContext(2);
151
+ i0.ɵɵadvance();
152
+ i0.ɵɵrepeater(ctx_r3.Cards);
153
+ } }
154
+ function ModelPredictionPanel_Conditional_0_Conditional_8_Template(rf, ctx) { if (rf & 1) {
155
+ i0.ɵɵelementStart(0, "span", 6);
156
+ i0.ɵɵtext(1);
157
+ i0.ɵɵelementEnd();
158
+ } if (rf & 2) {
159
+ const ctx_r3 = i0.ɵɵnextContext(2);
160
+ i0.ɵɵadvance();
161
+ i0.ɵɵtextInterpolate(ctx_r3.HistoryItems.length);
75
162
  } }
76
- function ModelPredictionPanel_Conditional_0_For_3_Template(rf, ctx) { if (rf & 1) {
77
- i0.ɵɵelementStart(0, "div", 2)(1, "div", 3)(2, "div", 4);
163
+ function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_1_Template(rf, ctx) { if (rf & 1) {
164
+ i0.ɵɵelementStart(0, "div", 34);
165
+ i0.ɵɵelement(1, "i", 36);
166
+ i0.ɵɵtext(2, " Loading prediction history\u2026 ");
167
+ i0.ɵɵelementEnd();
168
+ } }
169
+ function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_2_Template(rf, ctx) { if (rf & 1) {
170
+ i0.ɵɵelementStart(0, "div", 35);
171
+ i0.ɵɵtext(1, " No historical prediction runs recorded for this record yet. ");
172
+ i0.ɵɵelementEnd();
173
+ } }
174
+ function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_Conditional_0_For_4_Template(rf, ctx) { if (rf & 1) {
175
+ const _r7 = i0.ɵɵgetCurrentView();
176
+ i0.ɵɵelementStart(0, "button", 41);
177
+ i0.ɵɵlistener("click", function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_Conditional_0_For_4_Template_button_click_0_listener() { const mod_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r3 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r3.SelectModelFilter(mod_r8.modelId)); });
178
+ i0.ɵɵtext(1);
179
+ i0.ɵɵelementEnd();
180
+ } if (rf & 2) {
181
+ const mod_r8 = ctx.$implicit;
182
+ const ctx_r3 = i0.ɵɵnextContext(5);
183
+ i0.ɵɵclassProp("active", ctx_r3.SelectedModelId === mod_r8.modelId);
184
+ i0.ɵɵadvance();
185
+ i0.ɵɵtextInterpolate2(" ", mod_r8.modelName, " (", mod_r8.count, ") ");
186
+ } }
187
+ function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_Conditional_0_Template(rf, ctx) { if (rf & 1) {
188
+ const _r6 = i0.ɵɵgetCurrentView();
189
+ i0.ɵɵelementStart(0, "div", 37)(1, "button", 41);
190
+ i0.ɵɵlistener("click", function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_Conditional_0_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r6); const ctx_r3 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r3.SelectModelFilter(null)); });
191
+ i0.ɵɵtext(2);
192
+ i0.ɵɵelementEnd();
193
+ i0.ɵɵrepeaterCreate(3, ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_Conditional_0_For_4_Template, 2, 4, "button", 42, _forTrack3);
194
+ i0.ɵɵelementEnd();
195
+ } if (rf & 2) {
196
+ const ctx_r3 = i0.ɵɵnextContext(4);
197
+ i0.ɵɵadvance();
198
+ i0.ɵɵclassProp("active", ctx_r3.SelectedModelId === null || ctx_r3.SelectedModelId === "ALL");
199
+ i0.ɵɵadvance();
200
+ i0.ɵɵtextInterpolate1(" All Models (", ctx_r3.HistoryItems.length, ") ");
201
+ i0.ɵɵadvance();
202
+ i0.ɵɵrepeater(ctx_r3.ModelSummaries);
203
+ } }
204
+ function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Conditional_12_Template(rf, ctx) { if (rf & 1) {
205
+ i0.ɵɵelementStart(0, "span", 51);
206
+ i0.ɵɵtext(1);
207
+ i0.ɵɵpipe(2, "number");
208
+ i0.ɵɵelementEnd();
209
+ } if (rf & 2) {
210
+ const item_r10 = i0.ɵɵnextContext().$implicit;
211
+ i0.ɵɵadvance();
212
+ i0.ɵɵtextInterpolate1("(", i0.ɵɵpipeBind2(2, 1, item_r10.numericValue * 100, "1.0-1"), "%)");
213
+ } }
214
+ function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Conditional_13_Conditional_1_Template(rf, ctx) { if (rf & 1) {
215
+ i0.ɵɵelement(0, "i");
216
+ } if (rf & 2) {
217
+ const item_r10 = i0.ɵɵnextContext(2).$implicit;
218
+ i0.ɵɵclassMap("fa-solid " + item_r10.badgeIcon);
219
+ } }
220
+ function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Conditional_13_Template(rf, ctx) { if (rf & 1) {
221
+ i0.ɵɵelementStart(0, "span", 60);
222
+ i0.ɵɵconditionalCreate(1, ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Conditional_13_Conditional_1_Template, 1, 2, "i", 24);
223
+ i0.ɵɵelementStart(2, "span");
224
+ i0.ɵɵtext(3);
225
+ i0.ɵɵelementEnd()();
226
+ } if (rf & 2) {
227
+ const item_r10 = i0.ɵɵnextContext().$implicit;
228
+ i0.ɵɵclassMap("mpp-badge-" + item_r10.badgeColor);
229
+ i0.ɵɵadvance();
230
+ i0.ɵɵconditional(item_r10.badgeIcon ? 1 : -1);
231
+ i0.ɵɵadvance(2);
232
+ i0.ɵɵtextInterpolate(item_r10.statusLabel);
233
+ } }
234
+ function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Conditional_24_Conditional_3_Template(rf, ctx) { if (rf & 1) {
235
+ i0.ɵɵelementStart(0, "div", 63)(1, "strong");
236
+ i0.ɵɵtext(2, "Error:");
237
+ i0.ɵɵelementEnd();
78
238
  i0.ɵɵtext(3);
79
239
  i0.ɵɵelementEnd();
80
- i0.ɵɵelementStart(4, "div", 5);
81
- i0.ɵɵelement(5, "i", 6);
82
- i0.ɵɵelementStart(6, "span");
83
- i0.ɵɵtext(7);
240
+ } if (rf & 2) {
241
+ const item_r10 = i0.ɵɵnextContext(2).$implicit;
242
+ i0.ɵɵadvance(3);
243
+ i0.ɵɵtextInterpolate1(" ", item_r10.errorMessage, " ");
244
+ } }
245
+ function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Conditional_24_Conditional_4_For_6_Template(rf, ctx) { if (rf & 1) {
246
+ i0.ɵɵelementStart(0, "div", 71)(1, "span", 72);
247
+ i0.ɵɵtext(2);
248
+ i0.ɵɵelementEnd();
249
+ i0.ɵɵelementStart(3, "span", 32);
250
+ i0.ɵɵelement(4, "span", 33);
251
+ i0.ɵɵelementEnd()();
252
+ } if (rf & 2) {
253
+ const d_r11 = ctx.$implicit;
254
+ i0.ɵɵadvance(2);
255
+ i0.ɵɵtextInterpolate(d_r11.name);
256
+ i0.ɵɵadvance(2);
257
+ i0.ɵɵstyleProp("width", d_r11.relativePct, "%");
258
+ } }
259
+ function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Conditional_24_Conditional_4_Template(rf, ctx) { if (rf & 1) {
260
+ i0.ɵɵelementStart(0, "div", 64)(1, "div", 66);
261
+ i0.ɵɵelement(2, "i", 69);
262
+ i0.ɵɵtext(3, " Attributed Drivers");
263
+ i0.ɵɵelementEnd();
264
+ i0.ɵɵelementStart(4, "div", 70);
265
+ i0.ɵɵrepeaterCreate(5, ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Conditional_24_Conditional_4_For_6_Template, 5, 3, "div", 71, _forTrack1);
266
+ i0.ɵɵelementEnd()();
267
+ } if (rf & 2) {
268
+ const item_r10 = i0.ɵɵnextContext(2).$implicit;
269
+ i0.ɵɵadvance(5);
270
+ i0.ɵɵrepeater(item_r10.drivers);
271
+ } }
272
+ function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Conditional_24_Template(rf, ctx) { if (rf & 1) {
273
+ i0.ɵɵelementStart(0, "tr", 59)(1, "td", 61)(2, "div", 62);
274
+ i0.ɵɵconditionalCreate(3, ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Conditional_24_Conditional_3_Template, 4, 1, "div", 63);
275
+ i0.ɵɵconditionalCreate(4, ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Conditional_24_Conditional_4_Template, 7, 0, "div", 64);
276
+ i0.ɵɵelementStart(5, "div", 65)(6, "div", 66);
277
+ i0.ɵɵelement(7, "i", 67);
278
+ i0.ɵɵtext(8, " Result Payload");
279
+ i0.ɵɵelementEnd();
280
+ i0.ɵɵelementStart(9, "pre", 68);
281
+ i0.ɵɵtext(10);
282
+ i0.ɵɵpipe(11, "json");
283
+ i0.ɵɵelementEnd()()()()();
284
+ } if (rf & 2) {
285
+ const item_r10 = i0.ɵɵnextContext().$implicit;
286
+ i0.ɵɵadvance(3);
287
+ i0.ɵɵconditional(item_r10.errorMessage ? 3 : -1);
288
+ i0.ɵɵadvance();
289
+ i0.ɵɵconditional(item_r10.drivers.length > 0 ? 4 : -1);
290
+ i0.ɵɵadvance(6);
291
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(11, 3, item_r10.rawPayload));
292
+ } }
293
+ function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Template(rf, ctx) { if (rf & 1) {
294
+ const _r9 = i0.ɵɵgetCurrentView();
295
+ i0.ɵɵelementStart(0, "tr", 43)(1, "td", 44);
296
+ i0.ɵɵtext(2);
84
297
  i0.ɵɵelementEnd();
85
- i0.ɵɵconditionalCreate(8, ModelPredictionPanel_Conditional_0_For_3_Conditional_8_Template, 4, 1);
298
+ i0.ɵɵelementStart(3, "td", 45)(4, "div", 46);
299
+ i0.ɵɵtext(5);
300
+ i0.ɵɵelementEnd();
301
+ i0.ɵɵelementStart(6, "div", 47);
302
+ i0.ɵɵtext(7);
86
303
  i0.ɵɵelementEnd()();
87
- i0.ɵɵelementStart(9, "div", 7)(10, "span", 8);
304
+ i0.ɵɵelementStart(8, "td", 48)(9, "div", 49)(10, "span", 50);
88
305
  i0.ɵɵtext(11);
306
+ i0.ɵɵelementEnd();
307
+ i0.ɵɵconditionalCreate(12, ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Conditional_12_Template, 3, 4, "span", 51);
308
+ i0.ɵɵconditionalCreate(13, ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Conditional_13_Template, 4, 4, "span", 52);
89
309
  i0.ɵɵelementEnd()();
90
- i0.ɵɵconditionalCreate(12, ModelPredictionPanel_Conditional_0_For_3_Conditional_12_Template, 10, 9, "div", 9);
91
- i0.ɵɵconditionalCreate(13, ModelPredictionPanel_Conditional_0_For_3_Conditional_13_Template, 6, 0, "div", 10);
310
+ i0.ɵɵelementStart(14, "td", 53)(15, "span", 54);
311
+ i0.ɵɵtext(16);
312
+ i0.ɵɵelementEnd()();
313
+ i0.ɵɵelementStart(17, "td", 55)(18, "button", 56);
314
+ i0.ɵɵlistener("click", function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Template_button_click_18_listener($event) { const item_r10 = i0.ɵɵrestoreView(_r9).$implicit; const ctx_r3 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r3.ToggleHistoryDetails(item_r10, $event)); });
315
+ i0.ɵɵelement(19, "i");
316
+ i0.ɵɵtext(20);
92
317
  i0.ɵɵelementEnd();
318
+ i0.ɵɵelementStart(21, "button", 57);
319
+ i0.ɵɵlistener("click", function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Template_button_click_21_listener($event) { const item_r10 = i0.ɵɵrestoreView(_r9).$implicit; const ctx_r3 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r3.DrilldownToPredictionRecord(item_r10, $event)); });
320
+ i0.ɵɵelement(22, "i", 58);
321
+ i0.ɵɵtext(23, " Open Row ");
322
+ i0.ɵɵelementEnd()()();
323
+ i0.ɵɵconditionalCreate(24, ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Conditional_24_Template, 12, 5, "tr", 59);
93
324
  } if (rf & 2) {
94
- const card_r1 = ctx.$implicit;
325
+ const item_r10 = ctx.$implicit;
326
+ const ctx_r3 = i0.ɵɵnextContext(4);
327
+ i0.ɵɵclassProp("expanded", ctx_r3.ExpandedHistoryItemId === item_r10.id);
328
+ i0.ɵɵadvance(2);
329
+ i0.ɵɵtextInterpolate(item_r10.formattedTime);
95
330
  i0.ɵɵadvance(3);
96
- i0.ɵɵtextInterpolate(card_r1.label);
331
+ i0.ɵɵtextInterpolate(item_r10.modelName);
332
+ i0.ɵɵadvance(2);
333
+ i0.ɵɵtextInterpolate(item_r10.provenance);
97
334
  i0.ɵɵadvance(4);
98
- i0.ɵɵtextInterpolate(card_r1.provenance);
335
+ i0.ɵɵtextInterpolate(item_r10.displayValue);
336
+ i0.ɵɵadvance();
337
+ i0.ɵɵconditional(item_r10.predictedClass && item_r10.numericValue != null && item_r10.isProbability ? 12 : -1);
99
338
  i0.ɵɵadvance();
100
- i0.ɵɵconditional(card_r1.lastScored ? 8 : -1);
339
+ i0.ɵɵconditional(item_r10.statusLabel ? 13 : -1);
101
340
  i0.ɵɵadvance(2);
102
- i0.ɵɵclassProp("mpp-value-muted", card_r1.displayValue === "\u2014");
341
+ i0.ɵɵclassMap("mpp-status-" + item_r10.status.toLowerCase());
103
342
  i0.ɵɵadvance();
104
- i0.ɵɵtextInterpolate1(" ", card_r1.displayValue, " ");
343
+ i0.ɵɵtextInterpolate1(" ", item_r10.status, " ");
344
+ i0.ɵɵadvance(2);
345
+ i0.ɵɵproperty("title", ctx_r3.ExpandedHistoryItemId === item_r10.id ? "Hide details" : "Inspect payload & drivers");
105
346
  i0.ɵɵadvance();
106
- i0.ɵɵconditional(card_r1.isProbability ? 12 : -1);
347
+ i0.ɵɵclassMap(ctx_r3.ExpandedHistoryItemId === item_r10.id ? "fa-solid fa-chevron-up" : "fa-solid fa-code");
348
+ i0.ɵɵadvance();
349
+ i0.ɵɵtextInterpolate1(" ", ctx_r3.ExpandedHistoryItemId === item_r10.id ? "Close" : "Inspect", " ");
350
+ i0.ɵɵadvance(4);
351
+ i0.ɵɵconditional(ctx_r3.ExpandedHistoryItemId === item_r10.id ? 24 : -1);
352
+ } }
353
+ function ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_Template(rf, ctx) { if (rf & 1) {
354
+ i0.ɵɵconditionalCreate(0, ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_Conditional_0_Template, 5, 3, "div", 37);
355
+ i0.ɵɵelementStart(1, "div", 38)(2, "table", 39)(3, "thead")(4, "tr")(5, "th");
356
+ i0.ɵɵtext(6, "Date");
357
+ i0.ɵɵelementEnd();
358
+ i0.ɵɵelementStart(7, "th");
359
+ i0.ɵɵtext(8, "Model");
360
+ i0.ɵɵelementEnd();
361
+ i0.ɵɵelementStart(9, "th");
362
+ i0.ɵɵtext(10, "Prediction");
363
+ i0.ɵɵelementEnd();
364
+ i0.ɵɵelementStart(11, "th");
365
+ i0.ɵɵtext(12, "Status");
366
+ i0.ɵɵelementEnd();
367
+ i0.ɵɵelementStart(13, "th", 40);
368
+ i0.ɵɵtext(14, "Actions");
369
+ i0.ɵɵelementEnd()()();
370
+ i0.ɵɵelementStart(15, "tbody");
371
+ i0.ɵɵrepeaterCreate(16, ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_For_17_Template, 25, 16, null, null, _forTrack2);
372
+ i0.ɵɵelementEnd()()();
373
+ } if (rf & 2) {
374
+ const ctx_r3 = i0.ɵɵnextContext(3);
375
+ i0.ɵɵconditional(ctx_r3.ModelSummaries.length > 1 ? 0 : -1);
376
+ i0.ɵɵadvance(16);
377
+ i0.ɵɵrepeater(ctx_r3.FilteredHistory);
378
+ } }
379
+ function ModelPredictionPanel_Conditional_0_Conditional_11_Template(rf, ctx) { if (rf & 1) {
380
+ i0.ɵɵelementStart(0, "div", 8);
381
+ i0.ɵɵconditionalCreate(1, ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_1_Template, 3, 0, "div", 34)(2, ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_2_Template, 2, 0, "div", 35)(3, ModelPredictionPanel_Conditional_0_Conditional_11_Conditional_3_Template, 18, 1);
382
+ i0.ɵɵelementEnd();
383
+ } if (rf & 2) {
384
+ const ctx_r3 = i0.ɵɵnextContext(2);
107
385
  i0.ɵɵadvance();
108
- i0.ɵɵconditional(card_r1.drivers.length > 0 ? 13 : -1);
386
+ i0.ɵɵconditional(ctx_r3.LoadingHistory ? 1 : ctx_r3.HistoryItems.length === 0 ? 2 : 3);
109
387
  } }
110
388
  function ModelPredictionPanel_Conditional_0_Template(rf, ctx) { if (rf & 1) {
111
- i0.ɵɵelementStart(0, "mj-collapsible-panel", 0)(1, "div", 1);
112
- i0.ɵɵrepeaterCreate(2, ModelPredictionPanel_Conditional_0_For_3_Template, 14, 8, "div", 2, _forTrack0);
389
+ const _r1 = i0.ɵɵgetCurrentView();
390
+ i0.ɵɵelementStart(0, "mj-collapsible-panel", 0);
391
+ i0.ɵɵconditionalCreate(1, ModelPredictionPanel_Conditional_0_Conditional_1_Template, 3, 0, "div", 1);
392
+ i0.ɵɵelementStart(2, "div", 2)(3, "div", 3);
393
+ i0.ɵɵlistener("click", function ModelPredictionPanel_Conditional_0_Template_div_click_3_listener() { i0.ɵɵrestoreView(_r1); const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.ToggleHistorySection()); });
394
+ i0.ɵɵelementStart(4, "div", 4);
395
+ i0.ɵɵelement(5, "i", 5);
396
+ i0.ɵɵelementStart(6, "span");
397
+ i0.ɵɵtext(7, "Prediction History");
398
+ i0.ɵɵelementEnd();
399
+ i0.ɵɵconditionalCreate(8, ModelPredictionPanel_Conditional_0_Conditional_8_Template, 2, 1, "span", 6);
400
+ i0.ɵɵelementEnd();
401
+ i0.ɵɵelementStart(9, "div", 7);
402
+ i0.ɵɵelement(10, "i");
403
+ i0.ɵɵelementEnd()();
404
+ i0.ɵɵconditionalCreate(11, ModelPredictionPanel_Conditional_0_Conditional_11_Template, 4, 1, "div", 8);
113
405
  i0.ɵɵelementEnd()();
114
406
  } if (rf & 2) {
115
- const ctx_r2 = i0.ɵɵnextContext();
116
- i0.ɵɵproperty("Form", ctx_r2.FormComponent)("FormContext", ctx_r2.FormContext)("BadgeCount", ctx_r2.Cards.length);
407
+ const ctx_r3 = i0.ɵɵnextContext();
408
+ i0.ɵɵproperty("Form", ctx_r3.FormComponent)("FormContext", ctx_r3.FormContext)("BadgeCount", ctx_r3.Cards.length);
409
+ i0.ɵɵadvance();
410
+ i0.ɵɵconditional(ctx_r3.Cards.length > 0 ? 1 : -1);
411
+ i0.ɵɵadvance(7);
412
+ i0.ɵɵconditional(ctx_r3.HistoryItems.length > 0 ? 8 : -1);
117
413
  i0.ɵɵadvance(2);
118
- i0.ɵɵrepeater(ctx_r2.Cards);
414
+ i0.ɵɵclassMap(ctx_r3.ShowHistorySection ? "fa-solid fa-chevron-up" : "fa-solid fa-chevron-down");
415
+ i0.ɵɵadvance();
416
+ i0.ɵɵconditional(ctx_r3.ShowHistorySection ? 11 : -1);
119
417
  } }
120
418
  /**
121
419
  * Model Prediction panel — an **entity-agnostic** form panel that surfaces any
@@ -137,19 +435,40 @@ function ModelPredictionPanel_Conditional_0_Template(rf, ctx) { if (rf & 1) {
137
435
  let ModelPredictionPanel = class ModelPredictionPanel extends BaseFormPanel {
138
436
  constructor() {
139
437
  super(...arguments);
438
+ this.cdr = inject(ChangeDetectorRef);
140
439
  /** The resolved cards to render. Empty → panel renders nothing. */
141
440
  this.Cards = [];
142
441
  /** True once the binding lookup has completed (success or empty). */
143
442
  this.Loaded = false;
443
+ /** Resolved models by ID for lineage and history decoration. */
444
+ this.modelsById = new Map();
445
+ /** Historical prediction runs for this record across models. */
446
+ this.HistoryItems = [];
447
+ /** Loading flag for historical predictions lookup. */
448
+ this.LoadingHistory = false;
449
+ /** Whether the prediction history section is expanded in the template. */
450
+ this.ShowHistorySection = false;
451
+ /** Selected model ID for history filtering (null = all models). */
452
+ this.SelectedModelId = null;
453
+ /** Expanded history item ID for in-place inspection of payload & drivers. */
454
+ this.ExpandedHistoryItemId = null;
144
455
  /** Entity-ID we last loaded cards for, to avoid redundant lookups. */
145
456
  this.loadedForEntityId = null;
146
457
  }
147
458
  async ngOnInit() {
148
459
  await this.loadCards();
149
460
  }
150
- /** True when there is at least one card to show — gates the whole template. */
461
+ /** True when there is at least one card or history item to show — gates the whole template. */
151
462
  get HasPredictions() {
152
- return this.Cards.length > 0;
463
+ return this.Cards.length > 0 || this.HistoryItems.length > 0;
464
+ }
465
+ /** Filtered history items based on currently selected model. */
466
+ get FilteredHistory() {
467
+ return filterHistoryByModel(this.HistoryItems, this.SelectedModelId);
468
+ }
469
+ /** Distinct models in prediction history with counts. */
470
+ get ModelSummaries() {
471
+ return getDistinctModelsFromHistory(this.HistoryItems);
153
472
  }
154
473
  /**
155
474
  * Look up active scoring bindings for THIS record's entity and build a card
@@ -170,11 +489,14 @@ let ModelPredictionPanel = class ModelPredictionPanel extends BaseFormPanel {
170
489
  const bindings = await this.loadBindingsForEntity(entityId);
171
490
  if (bindings.length === 0) {
172
491
  this.Cards = [];
173
- this.Loaded = true;
174
- return;
175
492
  }
176
- const models = await this.loadModelsForBindings(bindings);
177
- this.Cards = this.buildCards(bindings, models);
493
+ else {
494
+ const models = await this.loadModelsForBindings(bindings);
495
+ this.modelsById = models;
496
+ this.Cards = this.buildCards(bindings, models);
497
+ }
498
+ // Preload history in the background for this record
499
+ void this.LoadHistory();
178
500
  }
179
501
  catch (e) {
180
502
  LogError(`[ModelPredictionPanel] Failed to load predictions: ${e instanceof Error ? e.message : String(e)}`);
@@ -182,6 +504,111 @@ let ModelPredictionPanel = class ModelPredictionPanel extends BaseFormPanel {
182
504
  }
183
505
  finally {
184
506
  this.Loaded = true;
507
+ this.cdr.markForCheck();
508
+ }
509
+ }
510
+ /**
511
+ * Load historical prediction runs for this specific record across all models.
512
+ */
513
+ async LoadHistory() {
514
+ const entityId = this.Record?.EntityInfo?.ID ?? null;
515
+ const pkVal = this.Record?.FirstPrimaryKey?.Value; // first-pk-ok: Target entity single PK value for RecordID filter
516
+ const recordId = pkVal != null ? String(pkVal) : '';
517
+ if (!entityId || !recordId) {
518
+ this.HistoryItems = [];
519
+ this.cdr.markForCheck();
520
+ return;
521
+ }
522
+ this.LoadingHistory = true;
523
+ this.cdr.markForCheck();
524
+ try {
525
+ const rv = this.runView();
526
+ const result = await rv.RunView({
527
+ EntityName: 'MJ: Process Run Details',
528
+ ExtraFilter: `EntityID='${entityId}' AND RecordID='${recordId}'`,
529
+ OrderBy: 'CompletedAt DESC',
530
+ MaxRows: 50,
531
+ ResultType: 'entity_object',
532
+ }, this.contextUser());
533
+ if (!result.Success) {
534
+ LogError(`[ModelPredictionPanel] Failed to load prediction history: ${result.ErrorMessage}`);
535
+ this.HistoryItems = [];
536
+ return;
537
+ }
538
+ const rawDetails = result.Results ?? [];
539
+ if (rawDetails.length === 0) {
540
+ this.HistoryItems = [];
541
+ return;
542
+ }
543
+ // Ensure any models referenced in historical payloads are loaded
544
+ await this.ensureModelsLoadedForHistory(rawDetails);
545
+ this.HistoryItems = rawDetails.map(d => parseHistoryItem({
546
+ ID: d.ID,
547
+ ProcessRunID: d.ProcessRunID,
548
+ Status: d.Status,
549
+ CompletedAt: d.CompletedAt,
550
+ ResultPayload: d.ResultPayload,
551
+ ErrorMessage: d.ErrorMessage,
552
+ }, this.modelsById));
553
+ }
554
+ catch (e) {
555
+ LogError(`[ModelPredictionPanel] Error loading prediction history: ${e instanceof Error ? e.message : String(e)}`);
556
+ this.HistoryItems = [];
557
+ }
558
+ finally {
559
+ this.LoadingHistory = false;
560
+ this.cdr.markForCheck();
561
+ }
562
+ }
563
+ /**
564
+ * Select a model ID filter for history (null = all models).
565
+ */
566
+ SelectModelFilter(modelId) {
567
+ this.SelectedModelId = modelId;
568
+ this.cdr.markForCheck();
569
+ }
570
+ /**
571
+ * Toggle the prediction history section.
572
+ */
573
+ ToggleHistorySection() {
574
+ this.ShowHistorySection = !this.ShowHistorySection;
575
+ this.cdr.markForCheck();
576
+ if (this.ShowHistorySection && this.HistoryItems.length === 0 && !this.LoadingHistory) {
577
+ void this.LoadHistory();
578
+ }
579
+ }
580
+ /**
581
+ * Focus prediction history on a specific model from a card action.
582
+ */
583
+ OpenHistoryForModel(modelId, event) {
584
+ event?.stopPropagation();
585
+ this.ShowHistorySection = true;
586
+ this.SelectedModelId = modelId;
587
+ this.cdr.markForCheck();
588
+ if (this.HistoryItems.length === 0 && !this.LoadingHistory) {
589
+ void this.LoadHistory();
590
+ }
591
+ }
592
+ /**
593
+ * Toggle inline inspection drawer for a prediction history row.
594
+ */
595
+ ToggleHistoryDetails(item, event) {
596
+ event?.stopPropagation();
597
+ this.ExpandedHistoryItemId = this.ExpandedHistoryItemId === item.id ? null : item.id;
598
+ this.cdr.markForCheck();
599
+ }
600
+ /**
601
+ * Drill down into the underlying MJ: Process Run Details record in Explorer.
602
+ */
603
+ DrilldownToPredictionRecord(item, event) {
604
+ event?.stopPropagation();
605
+ if (this.FormComponent?.OnFormNavigate) {
606
+ this.FormComponent.OnFormNavigate({
607
+ Kind: 'record',
608
+ EntityName: 'MJ: Process Run Details',
609
+ PrimaryKey: CompositeKey.FromID(item.id),
610
+ OpenInNewTab: event ? (event.ctrlKey || event.metaKey) : true,
611
+ });
185
612
  }
186
613
  }
187
614
  /** Active scoring bindings whose target entity is the current record's entity. */
@@ -222,6 +649,42 @@ let ModelPredictionPanel = class ModelPredictionPanel extends BaseFormPanel {
222
649
  }
223
650
  return byId;
224
651
  }
652
+ /** Ensure models found in process run detail payloads are loaded for metadata decoration. */
653
+ async ensureModelsLoadedForHistory(details) {
654
+ const missingModelIds = [];
655
+ for (const d of details) {
656
+ if (!d.ResultPayload)
657
+ continue;
658
+ try {
659
+ const parsed = JSON.parse(d.ResultPayload);
660
+ if (parsed && typeof parsed === 'object') {
661
+ const rec = parsed;
662
+ const out = (rec['output'] && typeof rec['output'] === 'object' ? rec['output'] : rec);
663
+ const mid = typeof out['modelId'] === 'string' ? out['modelId'] : null;
664
+ if (mid && !this.modelsById.has(mid) && !missingModelIds.includes(mid)) {
665
+ missingModelIds.push(mid);
666
+ }
667
+ }
668
+ }
669
+ catch {
670
+ // Ignore parse errors
671
+ }
672
+ }
673
+ if (missingModelIds.length === 0)
674
+ return;
675
+ const inList = missingModelIds.map(id => `'${id}'`).join(',');
676
+ const rv = this.runView();
677
+ const result = await rv.RunView({
678
+ EntityName: 'MJ: ML Models',
679
+ ExtraFilter: `ID IN (${inList})`,
680
+ ResultType: 'entity_object',
681
+ }, this.contextUser());
682
+ if (result.Success && result.Results) {
683
+ for (const model of result.Results) {
684
+ this.modelsById.set(model.ID, model);
685
+ }
686
+ }
687
+ }
225
688
  /** Map each binding (+ its model) to a display card. Skips bindings whose model is missing. */
226
689
  buildCards(bindings, models) {
227
690
  const cards = [];
@@ -241,8 +704,32 @@ let ModelPredictionPanel = class ModelPredictionPanel extends BaseFormPanel {
241
704
  const rawValue = binding.TargetColumn ? this.Record.Get(binding.TargetColumn) : null;
242
705
  const numeric = toNumber(rawValue);
243
706
  const kind = valueKind(model.ProblemType, numeric);
707
+ const outcomeConfig = resolveOutcomeConfig({
708
+ Lineage: model.Lineage,
709
+ TargetVariable: model.TargetVariable,
710
+ ProblemType: model.ProblemType,
711
+ });
712
+ let statusBand = null;
713
+ let badgeColor = 'gray';
714
+ let badgeIcon = null;
715
+ let statusLabel = null;
716
+ if (numeric != null) {
717
+ statusBand = resolveScoreBand(numeric, outcomeConfig);
718
+ if (statusBand) {
719
+ badgeColor = statusBand.BadgeColor;
720
+ badgeIcon = statusBand.Icon ?? null;
721
+ statusLabel = statusBand.Label;
722
+ }
723
+ }
724
+ else if (kind === 'class' && typeof rawValue === 'string') {
725
+ const style = resolveOutcomeStyle(rawValue, outcomeConfig);
726
+ badgeColor = style.BadgeColor;
727
+ badgeIcon = style.Icon ?? null;
728
+ statusLabel = style.DisplayLabel ?? rawValue;
729
+ }
244
730
  return {
245
731
  bindingId: binding.ID,
732
+ modelId: model.ID,
246
733
  label: resolveLabel(model.TargetVariable, binding.TargetColumn),
247
734
  isProbability: kind === 'probability',
248
735
  isNumeric: kind === 'numeric',
@@ -250,6 +737,10 @@ let ModelPredictionPanel = class ModelPredictionPanel extends BaseFormPanel {
250
737
  displayValue: formatValue(rawValue, numeric, kind),
251
738
  gaugePct: kind === 'probability' && numeric != null ? gaugePct(numeric) : 0,
252
739
  band: kind === 'probability' && numeric != null ? bandFor(numeric) : null,
740
+ statusBand,
741
+ statusLabel,
742
+ badgeColor,
743
+ badgeIcon,
253
744
  drivers: parseDrivers(model.FeatureImportance),
254
745
  provenance: `${model.Pipeline} v${model.Version}`,
255
746
  lastScored: formatLastScored(binding.LastScoredAt),
@@ -265,11 +756,11 @@ let ModelPredictionPanel = class ModelPredictionPanel extends BaseFormPanel {
265
756
  return this.FormComponent?.ProviderToUse?.CurrentUser ?? new Metadata().CurrentUser;
266
757
  }
267
758
  static { this.ɵfac = /*@__PURE__*/ (() => { let ɵModelPredictionPanel_BaseFactory; return function ModelPredictionPanel_Factory(__ngFactoryType__) { return (ɵModelPredictionPanel_BaseFactory || (ɵModelPredictionPanel_BaseFactory = i0.ɵɵgetInheritedFactory(ModelPredictionPanel)))(__ngFactoryType__ || ModelPredictionPanel); }; })(); }
268
- static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ModelPredictionPanel, selectors: [["mj-model-prediction-panel"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 1, vars: 1, consts: [["SectionKey", "modelPredictions", "SectionName", "Model Predictions", "Icon", "fa-solid fa-wand-magic-sparkles", 3, "Form", "FormContext", "BadgeCount"], [1, "mpp-cards"], [1, "mpp-card"], [1, "mpp-card-head"], [1, "mpp-label"], [1, "mpp-provenance"], ["aria-hidden", "true", 1, "fa-solid", "fa-diagram-project", "mpp-prov-icon"], [1, "mpp-value-row"], [1, "mpp-value"], ["role", "img", 1, "mpp-gauge"], [1, "mpp-drivers"], ["aria-hidden", "true", 1, "mpp-dot"], [1, "mpp-scored"], [1, "mpp-gauge-track"], [1, "mpp-gauge-fill"], [1, "mpp-gauge-scale"], [1, "mpp-drivers-title"], ["aria-hidden", "true", 1, "fa-solid", "fa-ranking-star"], [1, "mpp-driver"], [1, "mpp-driver-name", 3, "title"], [1, "mpp-driver-bar-track"], [1, "mpp-driver-bar-fill"]], template: function ModelPredictionPanel_Template(rf, ctx) { if (rf & 1) {
269
- i0.ɵɵconditionalCreate(0, ModelPredictionPanel_Conditional_0_Template, 4, 3, "mj-collapsible-panel", 0);
759
+ static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ModelPredictionPanel, selectors: [["mj-model-prediction-panel"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 1, vars: 1, consts: [["SectionKey", "modelPredictions", "SectionName", "Model Predictions", "Icon", "fa-solid fa-wand-magic-sparkles", 3, "Form", "FormContext", "BadgeCount"], [1, "mpp-cards"], [1, "mpp-history-section"], ["role", "button", "tabindex", "0", 1, "mpp-history-header", 3, "click"], [1, "mpp-history-title"], [1, "fa-solid", "fa-clock-rotate-left"], [1, "mpp-history-badge"], [1, "mpp-history-chevron"], [1, "mpp-history-body"], [1, "mpp-card"], [1, "mpp-card-head"], [1, "mpp-head-left"], [1, "mpp-label"], [1, "mpp-provenance"], ["aria-hidden", "true", 1, "fa-solid", "fa-diagram-project", "mpp-prov-icon"], ["type", "button", "title", "View prediction history for this model", 1, "mpp-card-history-btn", 3, "click"], [1, "mpp-value-row"], [1, "mpp-value"], [1, "mpp-status-badge", 3, "class"], ["role", "img", 1, "mpp-gauge"], [1, "mpp-drivers"], ["aria-hidden", "true", 1, "mpp-dot"], [1, "mpp-scored"], [1, "mpp-status-badge"], [3, "class"], [1, "mpp-gauge-track"], [1, "mpp-gauge-fill"], [1, "mpp-gauge-scale"], [1, "mpp-drivers-title"], ["aria-hidden", "true", 1, "fa-solid", "fa-ranking-star"], [1, "mpp-driver"], [1, "mpp-driver-name", 3, "title"], [1, "mpp-driver-bar-track"], [1, "mpp-driver-bar-fill"], [1, "mpp-history-loading"], [1, "mpp-history-empty"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "mpp-model-filters"], [1, "mpp-history-table-wrap"], [1, "mpp-history-table"], [1, "actions-th"], ["type", "button", 1, "mpp-filter-pill", 3, "click"], ["type", "button", 1, "mpp-filter-pill", 3, "active"], [1, "mpp-history-row"], [1, "mpp-col-date"], [1, "mpp-col-model"], [1, "mpp-history-model-name"], [1, "mpp-history-model-prov"], [1, "mpp-col-pred"], [1, "mpp-pred-cell"], [1, "mpp-history-pred-val"], [1, "mpp-history-pred-sub"], [1, "mpp-status-badge", "mpp-badge-sm", 3, "class"], [1, "mpp-col-status"], [1, "mpp-status-pill"], [1, "mpp-col-actions"], ["type", "button", 1, "mpp-action-btn", 3, "click", "title"], ["type", "button", "title", "Open underlying Process Run Detail record in Explorer", 1, "mpp-action-btn", "mpp-action-drilldown", 3, "click"], [1, "fa-solid", "fa-arrow-up-right-from-square"], [1, "mpp-history-detail-row"], [1, "mpp-status-badge", "mpp-badge-sm"], ["colspan", "5"], [1, "mpp-history-detail-pane"], [1, "mpp-detail-error"], [1, "mpp-detail-drivers"], [1, "mpp-detail-payload"], [1, "mpp-detail-subtitle"], [1, "fa-solid", "fa-file-code"], [1, "mpp-payload-pre"], [1, "fa-solid", "fa-ranking-star"], [1, "mpp-detail-driver-list"], [1, "mpp-detail-driver-item"], [1, "mpp-driver-name"]], template: function ModelPredictionPanel_Template(rf, ctx) { if (rf & 1) {
760
+ i0.ɵɵconditionalCreate(0, ModelPredictionPanel_Conditional_0_Template, 12, 8, "mj-collapsible-panel", 0);
270
761
  } if (rf & 2) {
271
762
  i0.ɵɵconditional(ctx.HasPredictions ? 0 : -1);
272
- } }, dependencies: [i1.MjCollapsiblePanelComponent], styles: ["\n\n\n\n\n.mpp-cards[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 4px 2px;\n}\n\n.mpp-card[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface-card);\n padding: 14px 16px;\n}\n\n\n.mpp-card-head[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n justify-content: space-between;\n gap: 8px;\n margin-bottom: 8px;\n}\n\n.mpp-label[_ngcontent-%COMP%] {\n font-size: 0.95rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mpp-provenance[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n}\n\n.mpp-prov-icon[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.mpp-dot[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n}\n\n\n.mpp-value-row[_ngcontent-%COMP%] {\n margin: 2px 0 10px;\n}\n\n.mpp-value[_ngcontent-%COMP%] {\n font-size: 1.75rem;\n font-weight: 700;\n line-height: 1.1;\n color: var(--mj-text-primary);\n font-variant-numeric: tabular-nums;\n}\n\n.mpp-value-muted[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n font-weight: 500;\n}\n\n\n.mpp-gauge[_ngcontent-%COMP%] {\n margin-bottom: 4px;\n}\n\n.mpp-gauge-track[_ngcontent-%COMP%] {\n position: relative;\n height: 10px;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n}\n\n.mpp-gauge-fill[_ngcontent-%COMP%] {\n height: 100%;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-brand-primary);\n transition: width 0.3s ease;\n}\n\n\n.mpp-band-low[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 55%, transparent);\n}\n.mpp-band-mid[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 78%, transparent);\n}\n.mpp-band-high[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n}\n\n.mpp-gauge-scale[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n margin-top: 4px;\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n font-variant-numeric: tabular-nums;\n}\n\n\n.mpp-drivers[_ngcontent-%COMP%] {\n margin-top: 12px;\n padding-top: 10px;\n border-top: 1px solid var(--mj-border-subtle, var(--mj-border-default));\n}\n\n.mpp-drivers-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 0.72rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-secondary);\n margin-bottom: 8px;\n}\n\n.mpp-driver[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: minmax(80px, 38%) 1fr;\n align-items: center;\n gap: 10px;\n margin-bottom: 6px;\n}\n\n.mpp-driver-name[_ngcontent-%COMP%] {\n font-size: 0.8rem;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.mpp-driver-bar-track[_ngcontent-%COMP%] {\n display: block;\n height: 7px;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n}\n\n.mpp-driver-bar-fill[_ngcontent-%COMP%] {\n display: block;\n height: 100%;\n border-radius: var(--mj-radius-full, 9999px);\n background: color-mix(in srgb, var(--mj-brand-primary) 70%, transparent);\n transition: width 0.3s ease;\n}"] }); }
763
+ } }, dependencies: [i1.MjCollapsiblePanelComponent, i2.JsonPipe, i2.DecimalPipe], styles: ["\n\n\n\n\n.mpp-cards[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 4px 2px;\n}\n\n.mpp-card[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface-card);\n padding: 14px 16px;\n}\n\n\n.mpp-card-head[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n justify-content: space-between;\n gap: 8px;\n margin-bottom: 8px;\n}\n\n.mpp-label[_ngcontent-%COMP%] {\n font-size: 0.95rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mpp-provenance[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n}\n\n.mpp-prov-icon[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.mpp-dot[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n}\n\n\n.mpp-value-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 2px 0 10px;\n}\n\n\n.mpp-status-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 2px 8px;\n font-size: 0.75rem;\n font-weight: 600;\n border-radius: var(--mj-radius-full, 9999px);\n line-height: 1.3;\n}\n\n.mpp-badge-sm[_ngcontent-%COMP%] {\n padding: 1px 6px;\n font-size: 0.7rem;\n}\n\n.mpp-badge-green[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success, #16a34a) 15%, transparent);\n color: var(--mj-status-success, #16a34a);\n}\n\n.mpp-badge-red[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-danger, #dc2626) 15%, transparent);\n color: var(--mj-status-danger, #dc2626);\n}\n\n.mpp-badge-amber[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-warning, #d97706) 15%, transparent);\n color: var(--mj-status-warning, #d97706);\n}\n\n.mpp-badge-blue[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-info, #2563eb) 15%, transparent);\n color: var(--mj-status-info, #2563eb);\n}\n\n.mpp-badge-gray[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.mpp-pred-cell[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n\n.mpp-value[_ngcontent-%COMP%] {\n font-size: 1.75rem;\n font-weight: 700;\n line-height: 1.1;\n color: var(--mj-text-primary);\n font-variant-numeric: tabular-nums;\n}\n\n.mpp-value-muted[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n font-weight: 500;\n}\n\n\n.mpp-gauge[_ngcontent-%COMP%] {\n margin-bottom: 4px;\n}\n\n.mpp-gauge-track[_ngcontent-%COMP%] {\n position: relative;\n height: 10px;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n}\n\n.mpp-gauge-fill[_ngcontent-%COMP%] {\n height: 100%;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-brand-primary);\n transition: width 0.3s ease;\n}\n\n\n.mpp-band-low[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 55%, transparent);\n}\n.mpp-band-mid[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 78%, transparent);\n}\n.mpp-band-high[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n}\n\n\n.mpp-gauge-fill.mpp-color-green[_ngcontent-%COMP%] {\n background: var(--mj-status-success, #16a34a);\n}\n.mpp-gauge-fill.mpp-color-red[_ngcontent-%COMP%] {\n background: var(--mj-status-danger, #dc2626);\n}\n.mpp-gauge-fill.mpp-color-amber[_ngcontent-%COMP%] {\n background: var(--mj-status-warning, #d97706);\n}\n.mpp-gauge-fill.mpp-color-blue[_ngcontent-%COMP%] {\n background: var(--mj-status-info, #2563eb);\n}\n.mpp-gauge-fill.mpp-color-gray[_ngcontent-%COMP%] {\n background: var(--mj-text-muted);\n}\n\n.mpp-gauge-scale[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n margin-top: 4px;\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n font-variant-numeric: tabular-nums;\n}\n\n\n.mpp-drivers[_ngcontent-%COMP%] {\n margin-top: 12px;\n padding-top: 10px;\n border-top: 1px solid var(--mj-border-subtle, var(--mj-border-default));\n}\n\n.mpp-drivers-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 0.72rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-secondary);\n margin-bottom: 8px;\n}\n\n.mpp-driver[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: minmax(80px, 38%) 1fr;\n align-items: center;\n gap: 10px;\n margin-bottom: 6px;\n}\n\n.mpp-driver-name[_ngcontent-%COMP%] {\n font-size: 0.8rem;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.mpp-driver-bar-track[_ngcontent-%COMP%] {\n display: block;\n height: 7px;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n}\n\n.mpp-driver-bar-fill[_ngcontent-%COMP%] {\n display: block;\n height: 100%;\n border-radius: var(--mj-radius-full, 9999px);\n background: color-mix(in srgb, var(--mj-brand-primary) 70%, transparent);\n transition: width 0.3s ease;\n}\n\n\n.mpp-head-left[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.mpp-card-history-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 3px 8px;\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 4px);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.mpp-card-history-btn[_ngcontent-%COMP%]:hover {\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface-hover);\n}\n\n\n.mpp-history-section[_ngcontent-%COMP%] {\n margin-top: 14px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface-card);\n overflow: hidden;\n}\n\n.mpp-history-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 14px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n user-select: none;\n transition: background 0.15s ease;\n}\n\n.mpp-history-header[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.mpp-history-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mpp-history-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.mpp-history-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 6px;\n font-size: 0.7rem;\n font-weight: 600;\n border-radius: 999px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #fff);\n}\n\n.mpp-history-chevron[_ngcontent-%COMP%] {\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n}\n\n.mpp-history-body[_ngcontent-%COMP%] {\n padding: 12px 14px;\n border-top: 1px solid var(--mj-border-default);\n}\n\n.mpp-history-loading[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n padding: 8px 0;\n}\n\n.mpp-history-empty[_ngcontent-%COMP%] {\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n padding: 8px 0;\n}\n\n\n.mpp-model-filters[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-bottom: 12px;\n}\n\n.mpp-filter-pill[_ngcontent-%COMP%] {\n padding: 3px 10px;\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 999px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.mpp-filter-pill[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n border-color: var(--mj-border-hover, var(--mj-brand-primary));\n}\n\n.mpp-filter-pill.active[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #fff);\n border-color: var(--mj-brand-primary);\n}\n\n\n.mpp-history-table-wrap[_ngcontent-%COMP%] {\n overflow-x: auto;\n}\n\n.mpp-history-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.8rem;\n}\n\n.mpp-history-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n text-align: left;\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-muted);\n padding: 6px 8px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.mpp-history-table[_ngcontent-%COMP%] th.actions-th[_ngcontent-%COMP%] {\n text-align: right;\n}\n\n.mpp-history-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 8px;\n border-bottom: 1px solid var(--mj-border-subtle, var(--mj-border-default));\n vertical-align: middle;\n}\n\n.mpp-history-row[_ngcontent-%COMP%]:hover td[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-hover);\n}\n\n.mpp-history-row.expanded[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n}\n\n.mpp-col-date[_ngcontent-%COMP%] {\n white-space: nowrap;\n color: var(--mj-text-secondary);\n font-variant-numeric: tabular-nums;\n}\n\n.mpp-col-model[_ngcontent-%COMP%] {\n min-width: 140px;\n}\n\n.mpp-history-model-name[_ngcontent-%COMP%] {\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mpp-history-model-prov[_ngcontent-%COMP%] {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n.mpp-col-pred[_ngcontent-%COMP%] {\n white-space: nowrap;\n}\n\n.mpp-history-pred-val[_ngcontent-%COMP%] {\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mpp-history-pred-sub[_ngcontent-%COMP%] {\n margin-left: 4px;\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n\n.mpp-status-pill[_ngcontent-%COMP%] {\n display: inline-block;\n padding: 1px 7px;\n font-size: 0.7rem;\n font-weight: 600;\n border-radius: 999px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.mpp-status-succeeded[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success, #16a34a) 15%, transparent);\n color: var(--mj-status-success, #16a34a);\n}\n\n.mpp-status-failed[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-danger, #dc2626) 15%, transparent);\n color: var(--mj-status-danger, #dc2626);\n}\n\n.mpp-status-pending[_ngcontent-%COMP%], \n.mpp-status-skipped[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n\n.mpp-col-actions[_ngcontent-%COMP%] {\n text-align: right;\n white-space: nowrap;\n}\n\n.mpp-action-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 7px;\n font-size: 0.72rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 4px);\n cursor: pointer;\n margin-left: 4px;\n transition: all 0.15s ease;\n}\n\n.mpp-action-btn[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n border-color: var(--mj-border-hover, var(--mj-brand-primary));\n}\n\n.mpp-action-drilldown[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.mpp-action-drilldown[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n border-color: var(--mj-brand-primary);\n}\n\n\n.mpp-history-detail-row[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 0;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.mpp-history-detail-pane[_ngcontent-%COMP%] {\n padding: 12px 16px;\n background: var(--mj-bg-surface-sunken);\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.mpp-detail-subtitle[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 0.72rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-secondary);\n margin-bottom: 6px;\n}\n\n.mpp-detail-error[_ngcontent-%COMP%] {\n font-size: 0.78rem;\n color: var(--mj-status-danger, #dc2626);\n background: color-mix(in srgb, var(--mj-status-danger, #dc2626) 8%, transparent);\n padding: 6px 10px;\n border-radius: var(--mj-radius-sm, 4px);\n}\n\n.mpp-detail-driver-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n max-width: 380px;\n}\n\n.mpp-detail-driver-item[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: minmax(80px, 45%) 1fr;\n align-items: center;\n gap: 8px;\n}\n\n.mpp-detail-payload[_ngcontent-%COMP%] {\n margin-top: 2px;\n}\n\n.mpp-payload-pre[_ngcontent-%COMP%] {\n margin: 0;\n padding: 8px 10px;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.72rem;\n line-height: 1.4;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 4px);\n overflow-x: auto;\n max-height: 200px;\n color: var(--mj-text-primary);\n}"] }); }
273
764
  };
274
765
  ModelPredictionPanel = __decorate([
275
766
  RegisterClassEx(BaseFormPanel, {
@@ -285,9 +776,9 @@ ModelPredictionPanel = __decorate([
285
776
  export { ModelPredictionPanel };
286
777
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ModelPredictionPanel, [{
287
778
  type: Component,
288
- args: [{ standalone: false, selector: 'mj-model-prediction-panel', template: "<!--\n Entity-agnostic model-prediction panel. Renders NOTHING unless the current\n record's entity has active scoring bindings (HasPredictions gate). Driven\n entirely by metadata \u2014 no entity-specific or domain-specific markup.\n-->\n@if (HasPredictions) {\n <mj-collapsible-panel\n SectionKey=\"modelPredictions\"\n SectionName=\"Model Predictions\"\n Icon=\"fa-solid fa-wand-magic-sparkles\"\n [Form]=\"FormComponent\"\n [FormContext]=\"FormContext\"\n [BadgeCount]=\"Cards.length\">\n\n <div class=\"mpp-cards\">\n @for (card of Cards; track card.bindingId) {\n <div class=\"mpp-card\">\n\n <!-- Header: label + provenance -->\n <div class=\"mpp-card-head\">\n <div class=\"mpp-label\">{{ card.label }}</div>\n <div class=\"mpp-provenance\">\n <i class=\"fa-solid fa-diagram-project mpp-prov-icon\" aria-hidden=\"true\"></i>\n <span>{{ card.provenance }}</span>\n @if (card.lastScored) {\n <span class=\"mpp-dot\" aria-hidden=\"true\">\u00B7</span>\n <span class=\"mpp-scored\">Scored {{ card.lastScored }}</span>\n }\n </div>\n </div>\n\n <!-- Value -->\n <div class=\"mpp-value-row\">\n <span class=\"mpp-value\" [class.mpp-value-muted]=\"card.displayValue === '\u2014'\">\n {{ card.displayValue }}\n </span>\n </div>\n\n <!-- Probability gauge \u2014 neutral 3-band tercile. No moral direction. -->\n @if (card.isProbability) {\n <div class=\"mpp-gauge\" role=\"img\"\n [attr.aria-label]=\"card.label + ' ' + card.displayValue + ', ' + card.band + ' band'\">\n <div class=\"mpp-gauge-track\">\n <div class=\"mpp-gauge-fill\"\n [class.mpp-band-low]=\"card.band === 'low'\"\n [class.mpp-band-mid]=\"card.band === 'mid'\"\n [class.mpp-band-high]=\"card.band === 'high'\"\n [style.width.%]=\"card.gaugePct\"></div>\n </div>\n <div class=\"mpp-gauge-scale\">\n <span>0%</span>\n <span>50%</span>\n <span>100%</span>\n </div>\n </div>\n }\n\n <!-- Top drivers (feature importance) \u2014 omitted cleanly when unavailable -->\n @if (card.drivers.length > 0) {\n <div class=\"mpp-drivers\">\n <div class=\"mpp-drivers-title\">\n <i class=\"fa-solid fa-ranking-star\" aria-hidden=\"true\"></i>\n Top drivers\n </div>\n @for (driver of card.drivers; track driver.name) {\n <div class=\"mpp-driver\">\n <span class=\"mpp-driver-name\" [title]=\"driver.name\">{{ driver.name }}</span>\n <span class=\"mpp-driver-bar-track\">\n <span class=\"mpp-driver-bar-fill\" [style.width.%]=\"driver.relativePct\"></span>\n </span>\n </div>\n }\n </div>\n }\n\n </div>\n }\n </div>\n\n </mj-collapsible-panel>\n}\n", styles: ["/* Entity-agnostic model-prediction panel.\n Design-token-clean: every color is a --mj-* semantic token. The gauge bands\n are intentionally NEUTRAL \u2014 they encode position on the value axis (low/mid/high)\n via opacity of a single brand color, never green=good / red=bad. */\n\n.mpp-cards {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 4px 2px;\n}\n\n.mpp-card {\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface-card);\n padding: 14px 16px;\n}\n\n/* ---- Header ---- */\n.mpp-card-head {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n justify-content: space-between;\n gap: 8px;\n margin-bottom: 8px;\n}\n\n.mpp-label {\n font-size: 0.95rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mpp-provenance {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n}\n\n.mpp-prov-icon {\n color: var(--mj-brand-primary);\n}\n\n.mpp-dot {\n color: var(--mj-text-disabled);\n}\n\n/* ---- Primary value ---- */\n.mpp-value-row {\n margin: 2px 0 10px;\n}\n\n.mpp-value {\n font-size: 1.75rem;\n font-weight: 700;\n line-height: 1.1;\n color: var(--mj-text-primary);\n font-variant-numeric: tabular-nums;\n}\n\n.mpp-value-muted {\n color: var(--mj-text-disabled);\n font-weight: 500;\n}\n\n/* ---- Gauge (neutral tercile) ---- */\n.mpp-gauge {\n margin-bottom: 4px;\n}\n\n.mpp-gauge-track {\n position: relative;\n height: 10px;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n}\n\n.mpp-gauge-fill {\n height: 100%;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-brand-primary);\n transition: width 0.3s ease;\n}\n\n/* Band shading: same brand hue, varied opacity = neutral position cue only. */\n.mpp-band-low {\n background: color-mix(in srgb, var(--mj-brand-primary) 55%, transparent);\n}\n.mpp-band-mid {\n background: color-mix(in srgb, var(--mj-brand-primary) 78%, transparent);\n}\n.mpp-band-high {\n background: var(--mj-brand-primary);\n}\n\n.mpp-gauge-scale {\n display: flex;\n justify-content: space-between;\n margin-top: 4px;\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n font-variant-numeric: tabular-nums;\n}\n\n/* ---- Drivers ---- */\n.mpp-drivers {\n margin-top: 12px;\n padding-top: 10px;\n border-top: 1px solid var(--mj-border-subtle, var(--mj-border-default));\n}\n\n.mpp-drivers-title {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 0.72rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-secondary);\n margin-bottom: 8px;\n}\n\n.mpp-driver {\n display: grid;\n grid-template-columns: minmax(80px, 38%) 1fr;\n align-items: center;\n gap: 10px;\n margin-bottom: 6px;\n}\n\n.mpp-driver-name {\n font-size: 0.8rem;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.mpp-driver-bar-track {\n display: block;\n height: 7px;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n}\n\n.mpp-driver-bar-fill {\n display: block;\n height: 100%;\n border-radius: var(--mj-radius-full, 9999px);\n background: color-mix(in srgb, var(--mj-brand-primary) 70%, transparent);\n transition: width 0.3s ease;\n}\n"] }]
779
+ args: [{ standalone: false, selector: 'mj-model-prediction-panel', template: "<!--\n Entity-agnostic model-prediction panel. Renders NOTHING unless the current\n record's entity has active scoring bindings or historical prediction runs.\n Driven entirely by metadata \u2014 no entity-specific or domain-specific markup.\n-->\n@if (HasPredictions) {\n <mj-collapsible-panel\n SectionKey=\"modelPredictions\"\n SectionName=\"Model Predictions\"\n Icon=\"fa-solid fa-wand-magic-sparkles\"\n [Form]=\"FormComponent\"\n [FormContext]=\"FormContext\"\n [BadgeCount]=\"Cards.length\">\n\n <!-- Active prediction cards -->\n @if (Cards.length > 0) {\n <div class=\"mpp-cards\">\n @for (card of Cards; track card.bindingId) {\n <div class=\"mpp-card\">\n\n <!-- Header: label + provenance + history button -->\n <div class=\"mpp-card-head\">\n <div class=\"mpp-head-left\">\n <div class=\"mpp-label\">{{ card.label }}</div>\n <div class=\"mpp-provenance\">\n <i class=\"fa-solid fa-diagram-project mpp-prov-icon\" aria-hidden=\"true\"></i>\n <span>{{ card.provenance }}</span>\n @if (card.lastScored) {\n <span class=\"mpp-dot\" aria-hidden=\"true\">\u00B7</span>\n <span class=\"mpp-scored\">Scored {{ card.lastScored }}</span>\n }\n </div>\n </div>\n <button type=\"button\" \n class=\"mpp-card-history-btn\" \n (click)=\"OpenHistoryForModel(card.modelId, $event)\" \n title=\"View prediction history for this model\">\n <i class=\"fa-solid fa-clock-rotate-left\"></i> History\n </button>\n </div>\n\n <!-- Value -->\n <div class=\"mpp-value-row\">\n <span class=\"mpp-value\" [class.mpp-value-muted]=\"card.displayValue === '\u2014'\">\n {{ card.displayValue }}\n </span>\n @if (card.statusLabel) {\n <span class=\"mpp-status-badge\" [class]=\"'mpp-badge-' + card.badgeColor\">\n @if (card.badgeIcon) {\n <i [class]=\"'fa-solid ' + card.badgeIcon\"></i>\n }\n <span>{{ card.statusLabel }}</span>\n </span>\n }\n </div>\n\n <!-- Probability gauge \u2014 semantic color matching outcome polarity -->\n @if (card.isProbability) {\n <div class=\"mpp-gauge\" role=\"img\"\n [attr.aria-label]=\"card.label + ' ' + card.displayValue + (card.statusLabel ? ', ' + card.statusLabel : '')\">\n <div class=\"mpp-gauge-track\">\n <div class=\"mpp-gauge-fill\"\n [class.mpp-band-low]=\"card.band === 'low'\"\n [class.mpp-band-mid]=\"card.band === 'mid'\"\n [class.mpp-band-high]=\"card.band === 'high'\"\n [class]=\"'mpp-color-' + card.badgeColor\"\n [style.width.%]=\"card.gaugePct\"></div>\n </div>\n <div class=\"mpp-gauge-scale\">\n <span>0%</span>\n <span>50%</span>\n <span>100%</span>\n </div>\n </div>\n }\n\n <!-- Top drivers (feature importance) \u2014 omitted cleanly when unavailable -->\n @if (card.drivers.length > 0) {\n <div class=\"mpp-drivers\">\n <div class=\"mpp-drivers-title\">\n <i class=\"fa-solid fa-ranking-star\" aria-hidden=\"true\"></i>\n Top drivers\n </div>\n @for (driver of card.drivers; track driver.name) {\n <div class=\"mpp-driver\">\n <span class=\"mpp-driver-name\" [title]=\"driver.name\">{{ driver.name }}</span>\n <span class=\"mpp-driver-bar-track\">\n <span class=\"mpp-driver-bar-fill\" [style.width.%]=\"driver.relativePct\"></span>\n </span>\n </div>\n }\n </div>\n }\n\n </div>\n }\n </div>\n }\n\n <!-- Prediction History Section -->\n <div class=\"mpp-history-section\">\n <div class=\"mpp-history-header\" (click)=\"ToggleHistorySection()\" role=\"button\" tabindex=\"0\">\n <div class=\"mpp-history-title\">\n <i class=\"fa-solid fa-clock-rotate-left\"></i>\n <span>Prediction History</span>\n @if (HistoryItems.length > 0) {\n <span class=\"mpp-history-badge\">{{ HistoryItems.length }}</span>\n }\n </div>\n <div class=\"mpp-history-chevron\">\n <i [class]=\"ShowHistorySection ? 'fa-solid fa-chevron-up' : 'fa-solid fa-chevron-down'\"></i>\n </div>\n </div>\n\n @if (ShowHistorySection) {\n <div class=\"mpp-history-body\">\n @if (LoadingHistory) {\n <div class=\"mpp-history-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Loading prediction history\u2026\n </div>\n } @else if (HistoryItems.length === 0) {\n <div class=\"mpp-history-empty\">\n No historical prediction runs recorded for this record yet.\n </div>\n } @else {\n <!-- Multi-model filter tabs if multiple models present -->\n @if (ModelSummaries.length > 1) {\n <div class=\"mpp-model-filters\">\n <button type=\"button\" \n class=\"mpp-filter-pill\" \n [class.active]=\"SelectedModelId === null || SelectedModelId === 'ALL'\"\n (click)=\"SelectModelFilter(null)\">\n All Models ({{ HistoryItems.length }})\n </button>\n @for (mod of ModelSummaries; track mod.modelId) {\n <button type=\"button\" \n class=\"mpp-filter-pill\" \n [class.active]=\"SelectedModelId === mod.modelId\"\n (click)=\"SelectModelFilter(mod.modelId)\">\n {{ mod.modelName }} ({{ mod.count }})\n </button>\n }\n </div>\n }\n\n <!-- History table -->\n <div class=\"mpp-history-table-wrap\">\n <table class=\"mpp-history-table\">\n <thead>\n <tr>\n <th>Date</th>\n <th>Model</th>\n <th>Prediction</th>\n <th>Status</th>\n <th class=\"actions-th\">Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (item of FilteredHistory; track item.id) {\n <tr class=\"mpp-history-row\" [class.expanded]=\"ExpandedHistoryItemId === item.id\">\n <td class=\"mpp-col-date\">{{ item.formattedTime }}</td>\n <td class=\"mpp-col-model\">\n <div class=\"mpp-history-model-name\">{{ item.modelName }}</div>\n <div class=\"mpp-history-model-prov\">{{ item.provenance }}</div>\n </td>\n <td class=\"mpp-col-pred\">\n <div class=\"mpp-pred-cell\">\n <span class=\"mpp-history-pred-val\">{{ item.displayValue }}</span>\n @if (item.predictedClass && item.numericValue != null && item.isProbability) {\n <span class=\"mpp-history-pred-sub\">({{ (item.numericValue * 100) | number:'1.0-1' }}%)</span>\n }\n @if (item.statusLabel) {\n <span class=\"mpp-status-badge mpp-badge-sm\" [class]=\"'mpp-badge-' + item.badgeColor\">\n @if (item.badgeIcon) {\n <i [class]=\"'fa-solid ' + item.badgeIcon\"></i>\n }\n <span>{{ item.statusLabel }}</span>\n </span>\n }\n </div>\n </td>\n <td class=\"mpp-col-status\">\n <span class=\"mpp-status-pill\" [class]=\"'mpp-status-' + item.status.toLowerCase()\">\n {{ item.status }}\n </span>\n </td>\n <td class=\"mpp-col-actions\">\n <button type=\"button\" \n class=\"mpp-action-btn\"\n (click)=\"ToggleHistoryDetails(item, $event)\" \n [title]=\"ExpandedHistoryItemId === item.id ? 'Hide details' : 'Inspect payload & drivers'\">\n <i [class]=\"ExpandedHistoryItemId === item.id ? 'fa-solid fa-chevron-up' : 'fa-solid fa-code'\"></i>\n {{ ExpandedHistoryItemId === item.id ? 'Close' : 'Inspect' }}\n </button>\n <button type=\"button\" \n class=\"mpp-action-btn mpp-action-drilldown\"\n (click)=\"DrilldownToPredictionRecord(item, $event)\"\n title=\"Open underlying Process Run Detail record in Explorer\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Open Row\n </button>\n </td>\n </tr>\n @if (ExpandedHistoryItemId === item.id) {\n <tr class=\"mpp-history-detail-row\">\n <td colspan=\"5\">\n <div class=\"mpp-history-detail-pane\">\n @if (item.errorMessage) {\n <div class=\"mpp-detail-error\">\n <strong>Error:</strong> {{ item.errorMessage }}\n </div>\n }\n @if (item.drivers.length > 0) {\n <div class=\"mpp-detail-drivers\">\n <div class=\"mpp-detail-subtitle\"><i class=\"fa-solid fa-ranking-star\"></i> Attributed Drivers</div>\n <div class=\"mpp-detail-driver-list\">\n @for (d of item.drivers; track d.name) {\n <div class=\"mpp-detail-driver-item\">\n <span class=\"mpp-driver-name\">{{ d.name }}</span>\n <span class=\"mpp-driver-bar-track\">\n <span class=\"mpp-driver-bar-fill\" [style.width.%]=\"d.relativePct\"></span>\n </span>\n </div>\n }\n </div>\n </div>\n }\n <div class=\"mpp-detail-payload\">\n <div class=\"mpp-detail-subtitle\"><i class=\"fa-solid fa-file-code\"></i> Result Payload</div>\n <pre class=\"mpp-payload-pre\">{{ item.rawPayload | json }}</pre>\n </div>\n </div>\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n }\n </div>\n\n </mj-collapsible-panel>\n}\n", styles: ["/* Entity-agnostic model-prediction panel.\n Design-token-clean: every color is a --mj-* semantic token. The gauge bands\n are intentionally NEUTRAL \u2014 they encode position on the value axis (low/mid/high)\n via opacity of a single brand color, never green=good / red=bad. */\n\n.mpp-cards {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 4px 2px;\n}\n\n.mpp-card {\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface-card);\n padding: 14px 16px;\n}\n\n/* ---- Header ---- */\n.mpp-card-head {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n justify-content: space-between;\n gap: 8px;\n margin-bottom: 8px;\n}\n\n.mpp-label {\n font-size: 0.95rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mpp-provenance {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n}\n\n.mpp-prov-icon {\n color: var(--mj-brand-primary);\n}\n\n.mpp-dot {\n color: var(--mj-text-disabled);\n}\n\n/* ---- Primary value ---- */\n.mpp-value-row {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 2px 0 10px;\n}\n\n/* ---- Status Badge ---- */\n.mpp-status-badge {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 2px 8px;\n font-size: 0.75rem;\n font-weight: 600;\n border-radius: var(--mj-radius-full, 9999px);\n line-height: 1.3;\n}\n\n.mpp-badge-sm {\n padding: 1px 6px;\n font-size: 0.7rem;\n}\n\n.mpp-badge-green {\n background: color-mix(in srgb, var(--mj-status-success, #16a34a) 15%, transparent);\n color: var(--mj-status-success, #16a34a);\n}\n\n.mpp-badge-red {\n background: color-mix(in srgb, var(--mj-status-danger, #dc2626) 15%, transparent);\n color: var(--mj-status-danger, #dc2626);\n}\n\n.mpp-badge-amber {\n background: color-mix(in srgb, var(--mj-status-warning, #d97706) 15%, transparent);\n color: var(--mj-status-warning, #d97706);\n}\n\n.mpp-badge-blue {\n background: color-mix(in srgb, var(--mj-status-info, #2563eb) 15%, transparent);\n color: var(--mj-status-info, #2563eb);\n}\n\n.mpp-badge-gray {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.mpp-pred-cell {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n\n.mpp-value {\n font-size: 1.75rem;\n font-weight: 700;\n line-height: 1.1;\n color: var(--mj-text-primary);\n font-variant-numeric: tabular-nums;\n}\n\n.mpp-value-muted {\n color: var(--mj-text-disabled);\n font-weight: 500;\n}\n\n/* ---- Gauge (neutral tercile) ---- */\n.mpp-gauge {\n margin-bottom: 4px;\n}\n\n.mpp-gauge-track {\n position: relative;\n height: 10px;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n}\n\n.mpp-gauge-fill {\n height: 100%;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-brand-primary);\n transition: width 0.3s ease;\n}\n\n/* Band shading: same brand hue, varied opacity = neutral position cue only. */\n.mpp-band-low {\n background: color-mix(in srgb, var(--mj-brand-primary) 55%, transparent);\n}\n.mpp-band-mid {\n background: color-mix(in srgb, var(--mj-brand-primary) 78%, transparent);\n}\n.mpp-band-high {\n background: var(--mj-brand-primary);\n}\n\n/* Outcome polarity gauge fill colors */\n.mpp-gauge-fill.mpp-color-green {\n background: var(--mj-status-success, #16a34a);\n}\n.mpp-gauge-fill.mpp-color-red {\n background: var(--mj-status-danger, #dc2626);\n}\n.mpp-gauge-fill.mpp-color-amber {\n background: var(--mj-status-warning, #d97706);\n}\n.mpp-gauge-fill.mpp-color-blue {\n background: var(--mj-status-info, #2563eb);\n}\n.mpp-gauge-fill.mpp-color-gray {\n background: var(--mj-text-muted);\n}\n\n.mpp-gauge-scale {\n display: flex;\n justify-content: space-between;\n margin-top: 4px;\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n font-variant-numeric: tabular-nums;\n}\n\n/* ---- Drivers ---- */\n.mpp-drivers {\n margin-top: 12px;\n padding-top: 10px;\n border-top: 1px solid var(--mj-border-subtle, var(--mj-border-default));\n}\n\n.mpp-drivers-title {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 0.72rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-secondary);\n margin-bottom: 8px;\n}\n\n.mpp-driver {\n display: grid;\n grid-template-columns: minmax(80px, 38%) 1fr;\n align-items: center;\n gap: 10px;\n margin-bottom: 6px;\n}\n\n.mpp-driver-name {\n font-size: 0.8rem;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.mpp-driver-bar-track {\n display: block;\n height: 7px;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n}\n\n.mpp-driver-bar-fill {\n display: block;\n height: 100%;\n border-radius: var(--mj-radius-full, 9999px);\n background: color-mix(in srgb, var(--mj-brand-primary) 70%, transparent);\n transition: width 0.3s ease;\n}\n\n/* ---- Card History Action Button ---- */\n.mpp-head-left {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.mpp-card-history-btn {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 3px 8px;\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 4px);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.mpp-card-history-btn:hover {\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface-hover);\n}\n\n/* ---- Prediction History Section ---- */\n.mpp-history-section {\n margin-top: 14px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface-card);\n overflow: hidden;\n}\n\n.mpp-history-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 14px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n user-select: none;\n transition: background 0.15s ease;\n}\n\n.mpp-history-header:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.mpp-history-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mpp-history-title i {\n color: var(--mj-brand-primary);\n}\n\n.mpp-history-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 6px;\n font-size: 0.7rem;\n font-weight: 600;\n border-radius: 999px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #fff);\n}\n\n.mpp-history-chevron {\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n}\n\n.mpp-history-body {\n padding: 12px 14px;\n border-top: 1px solid var(--mj-border-default);\n}\n\n.mpp-history-loading {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n padding: 8px 0;\n}\n\n.mpp-history-empty {\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n padding: 8px 0;\n}\n\n/* ---- Multi-Model Filter Tabs ---- */\n.mpp-model-filters {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-bottom: 12px;\n}\n\n.mpp-filter-pill {\n padding: 3px 10px;\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 999px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.mpp-filter-pill:hover {\n color: var(--mj-text-primary);\n border-color: var(--mj-border-hover, var(--mj-brand-primary));\n}\n\n.mpp-filter-pill.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #fff);\n border-color: var(--mj-brand-primary);\n}\n\n/* ---- History Table ---- */\n.mpp-history-table-wrap {\n overflow-x: auto;\n}\n\n.mpp-history-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.8rem;\n}\n\n.mpp-history-table th {\n text-align: left;\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-muted);\n padding: 6px 8px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.mpp-history-table th.actions-th {\n text-align: right;\n}\n\n.mpp-history-table td {\n padding: 8px;\n border-bottom: 1px solid var(--mj-border-subtle, var(--mj-border-default));\n vertical-align: middle;\n}\n\n.mpp-history-row:hover td {\n background: var(--mj-bg-surface-hover);\n}\n\n.mpp-history-row.expanded td {\n background: var(--mj-bg-surface-sunken);\n}\n\n.mpp-col-date {\n white-space: nowrap;\n color: var(--mj-text-secondary);\n font-variant-numeric: tabular-nums;\n}\n\n.mpp-col-model {\n min-width: 140px;\n}\n\n.mpp-history-model-name {\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mpp-history-model-prov {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n.mpp-col-pred {\n white-space: nowrap;\n}\n\n.mpp-history-pred-val {\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mpp-history-pred-sub {\n margin-left: 4px;\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n/* Status Pill */\n.mpp-status-pill {\n display: inline-block;\n padding: 1px 7px;\n font-size: 0.7rem;\n font-weight: 600;\n border-radius: 999px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.mpp-status-succeeded {\n background: color-mix(in srgb, var(--mj-status-success, #16a34a) 15%, transparent);\n color: var(--mj-status-success, #16a34a);\n}\n\n.mpp-status-failed {\n background: color-mix(in srgb, var(--mj-status-danger, #dc2626) 15%, transparent);\n color: var(--mj-status-danger, #dc2626);\n}\n\n.mpp-status-pending,\n.mpp-status-skipped {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n/* Actions in History Row */\n.mpp-col-actions {\n text-align: right;\n white-space: nowrap;\n}\n\n.mpp-action-btn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 7px;\n font-size: 0.72rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 4px);\n cursor: pointer;\n margin-left: 4px;\n transition: all 0.15s ease;\n}\n\n.mpp-action-btn:hover {\n color: var(--mj-text-primary);\n border-color: var(--mj-border-hover, var(--mj-brand-primary));\n}\n\n.mpp-action-drilldown {\n color: var(--mj-brand-primary);\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.mpp-action-drilldown:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n border-color: var(--mj-brand-primary);\n}\n\n/* Inspection Drawer */\n.mpp-history-detail-row td {\n padding: 0;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.mpp-history-detail-pane {\n padding: 12px 16px;\n background: var(--mj-bg-surface-sunken);\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.mpp-detail-subtitle {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 0.72rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-secondary);\n margin-bottom: 6px;\n}\n\n.mpp-detail-error {\n font-size: 0.78rem;\n color: var(--mj-status-danger, #dc2626);\n background: color-mix(in srgb, var(--mj-status-danger, #dc2626) 8%, transparent);\n padding: 6px 10px;\n border-radius: var(--mj-radius-sm, 4px);\n}\n\n.mpp-detail-driver-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n max-width: 380px;\n}\n\n.mpp-detail-driver-item {\n display: grid;\n grid-template-columns: minmax(80px, 45%) 1fr;\n align-items: center;\n gap: 8px;\n}\n\n.mpp-detail-payload {\n margin-top: 2px;\n}\n\n.mpp-payload-pre {\n margin: 0;\n padding: 8px 10px;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.72rem;\n line-height: 1.4;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 4px);\n overflow-x: auto;\n max-height: 200px;\n color: var(--mj-text-primary);\n}\n"] }]
289
780
  }], null, null); })();
290
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ModelPredictionPanel, { className: "ModelPredictionPanel", filePath: "src/lib/panels/model-predictions/model-prediction.panel.ts", lineNumber: 80 }); })();
781
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ModelPredictionPanel, { className: "ModelPredictionPanel", filePath: "src/lib/panels/model-predictions/model-prediction.panel.ts", lineNumber: 101 }); })();
291
782
  /** Tree-shake guard — call this from the consuming module's loader. */
292
783
  export function LoadModelPredictionPanel() { }
293
784
  //# sourceMappingURL=model-prediction.panel.js.map