@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
@@ -1,20 +1,23 @@
1
1
  import { ChangeDetectorRef, Component, Input, ViewEncapsulation, inject } from '@angular/core';
2
2
  import { CommonModule } from '@angular/common';
3
+ import { AngularSplitModule } from 'angular-split';
3
4
  import { SharedGenericModule } from '@memberjunction/ng-shared-generic';
4
5
  import { MJButtonDirective } from '@memberjunction/ng-ui-components';
5
6
  import { RunView } from '@memberjunction/core';
6
7
  import { UUIDsEqual } from '@memberjunction/global';
8
+ import { UserInfoEngine } from '@memberjunction/core-entities';
7
9
  import { BaseAngularComponent } from '@memberjunction/ng-base-types';
8
- import { primaryAuc } from '../predictive-studio.view-models';
10
+ import { primaryModelScore, formatMetricValue } from '../predictive-studio.view-models';
9
11
  import { humanizeCron } from '../production-distribution';
10
12
  import { PSOperateDialogComponent } from './ps-operate-dialog.component';
13
+ import { PSPredictionsGridComponent } from './ps-predictions-grid.component';
11
14
  import * as i0 from "@angular/core";
12
- import * as i1 from "@memberjunction/ng-shared-generic";
13
- import * as i2 from "@angular/common";
15
+ import * as i1 from "angular-split";
16
+ import * as i2 from "@memberjunction/ng-shared-generic";
17
+ import * as i3 from "@angular/common";
14
18
  const _forTrack0 = ($index, $item) => $item.modelId;
15
19
  const _forTrack1 = ($index, $item) => $item.id;
16
- const _forTrack2 = ($index, $item) => $item.recordId;
17
- const _forTrack3 = ($index, $item) => $item.ID;
20
+ const _forTrack2 = ($index, $item) => $item.ID;
18
21
  function PSProductionComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
19
22
  i0.ɵɵelementStart(0, "div", 1)(1, "span", 2);
20
23
  i0.ɵɵelement(2, "i", 3);
@@ -26,48 +29,213 @@ function PSProductionComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
26
29
  i0.ɵɵtext(6, " When you publish a trained model in the Model Registry it shows up here \u2014 ready to schedule, bind to a column, and run. Train a pipeline and promote the model to Published to get started. ");
27
30
  i0.ɵɵelementEnd()();
28
31
  } }
29
- function PSProductionComponent_Conditional_2_For_30_Template(rf, ctx) { if (rf & 1) {
30
- const _r2 = i0.ɵɵgetCurrentView();
31
- i0.ɵɵelementStart(0, "div", 32);
32
- i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_For_30_Template_div_click_0_listener() { const m_r3 = i0.ɵɵrestoreView(_r2).$implicit; const ctx_r3 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r3.select(m_r3.modelId)); });
33
- i0.ɵɵelementStart(1, "div", 33);
32
+ function PSProductionComponent_Conditional_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
33
+ i0.ɵɵelementStart(0, "div", 4)(1, "div", 32)(2, "div", 33);
34
+ i0.ɵɵtext(3, "Published");
35
+ i0.ɵɵelementEnd();
36
+ i0.ɵɵelementStart(4, "div", 34);
37
+ i0.ɵɵtext(5);
38
+ i0.ɵɵelementEnd()();
39
+ i0.ɵɵelementStart(6, "div", 32)(7, "div", 33);
40
+ i0.ɵɵtext(8, "Scheduled");
41
+ i0.ɵɵelementEnd();
42
+ i0.ɵɵelementStart(9, "div", 34);
43
+ i0.ɵɵtext(10);
44
+ i0.ɵɵelementEnd()();
45
+ i0.ɵɵelementStart(11, "div", 32)(12, "div", 33);
46
+ i0.ɵɵtext(13, "Bound (write-back)");
47
+ i0.ɵɵelementEnd();
48
+ i0.ɵɵelementStart(14, "div", 34);
49
+ i0.ɵɵtext(15);
50
+ i0.ɵɵelementEnd()();
51
+ i0.ɵɵelementStart(16, "div", 32)(17, "div", 33);
52
+ i0.ɵɵtext(18, "Idle");
53
+ i0.ɵɵelementEnd();
54
+ i0.ɵɵelementStart(19, "div", 34);
55
+ i0.ɵɵtext(20);
56
+ i0.ɵɵelementEnd()()();
57
+ } if (rf & 2) {
58
+ const ctx_r1 = i0.ɵɵnextContext(2);
59
+ i0.ɵɵadvance(5);
60
+ i0.ɵɵtextInterpolate(ctx_r1.models.length);
61
+ i0.ɵɵadvance(5);
62
+ i0.ɵɵtextInterpolate(ctx_r1.scheduledModelCount);
63
+ i0.ɵɵadvance(5);
64
+ i0.ɵɵtextInterpolate(ctx_r1.boundModelCount);
65
+ i0.ɵɵadvance(5);
66
+ i0.ɵɵtextInterpolate(ctx_r1.idleModelCount);
67
+ } }
68
+ function PSProductionComponent_Conditional_2_Conditional_2_Template(rf, ctx) { if (rf & 1) {
69
+ const _r3 = i0.ɵɵgetCurrentView();
70
+ i0.ɵɵelementStart(0, "div", 6)(1, "button", 35);
71
+ i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Conditional_2_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.toggleList()); });
72
+ i0.ɵɵelement(2, "i", 36);
73
+ i0.ɵɵelementEnd();
74
+ i0.ɵɵelementStart(3, "div", 37);
75
+ i0.ɵɵelement(4, "i", 3);
76
+ i0.ɵɵelementEnd()();
77
+ } }
78
+ function PSProductionComponent_Conditional_2_Conditional_4_For_12_Template(rf, ctx) { if (rf & 1) {
79
+ const _r5 = i0.ɵɵgetCurrentView();
80
+ i0.ɵɵelementStart(0, "div", 45);
81
+ i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Conditional_4_For_12_Template_div_click_0_listener() { const m_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.select(m_r6.modelId)); });
82
+ i0.ɵɵelementStart(1, "div", 46);
34
83
  i0.ɵɵelement(2, "i");
35
84
  i0.ɵɵelementEnd();
36
- i0.ɵɵelementStart(3, "div", 34)(4, "div", 35);
85
+ i0.ɵɵelementStart(3, "div", 47)(4, "div", 48);
37
86
  i0.ɵɵtext(5);
38
87
  i0.ɵɵelementEnd();
39
- i0.ɵɵelementStart(6, "div", 36);
88
+ i0.ɵɵelementStart(6, "div", 49);
40
89
  i0.ɵɵtext(7);
41
90
  i0.ɵɵelementEnd()();
42
- i0.ɵɵelementStart(8, "div", 37)(9, "span", 22);
91
+ i0.ɵɵelementStart(8, "div", 50)(9, "span", 51);
43
92
  i0.ɵɵtext(10);
44
93
  i0.ɵɵelementEnd();
45
- i0.ɵɵelementStart(11, "div", 38);
94
+ i0.ɵɵelementStart(11, "div", 52);
46
95
  i0.ɵɵtext(12);
47
96
  i0.ɵɵelementEnd()()();
48
97
  } if (rf & 2) {
49
- const m_r3 = ctx.$implicit;
50
- const ctx_r3 = i0.ɵɵnextContext(2);
51
- i0.ɵɵclassProp("sel", m_r3.modelId === ctx_r3.selectedModelId);
98
+ const m_r6 = ctx.$implicit;
99
+ const ctx_r1 = i0.ɵɵnextContext(3);
100
+ i0.ɵɵclassProp("sel", m_r6.modelId === ctx_r1.selectedModelId);
52
101
  i0.ɵɵadvance();
53
- i0.ɵɵclassMap("st-" + m_r3.deployState);
102
+ i0.ɵɵclassMap("st-" + m_r6.deployState);
54
103
  i0.ɵɵadvance();
55
- i0.ɵɵclassMap(ctx_r3.deployIcon(m_r3.deployState));
104
+ i0.ɵɵclassMap(ctx_r1.deployIcon(m_r6.deployState));
56
105
  i0.ɵɵadvance(3);
57
- i0.ɵɵtextInterpolate(m_r3.label);
106
+ i0.ɵɵtextInterpolate(m_r6.label);
58
107
  i0.ɵɵadvance(2);
59
- i0.ɵɵtextInterpolate2("v", m_r3.version, " \u00B7 ", m_r3.algorithm);
108
+ i0.ɵɵtextInterpolate2("v", m_r6.version, " \u00B7 ", m_r6.algorithm);
60
109
  i0.ɵɵadvance(2);
61
- i0.ɵɵclassMap(ctx_r3.deployBadge(m_r3.deployState));
110
+ i0.ɵɵclassMap(ctx_r1.deployBadge(m_r6.deployState));
111
+ i0.ɵɵadvance();
112
+ i0.ɵɵtextInterpolate(ctx_r1.deployLabel(m_r6.deployState));
113
+ i0.ɵɵadvance(2);
114
+ i0.ɵɵtextInterpolate(m_r6.holdoutMetric);
115
+ } }
116
+ function PSProductionComponent_Conditional_2_Conditional_4_Template(rf, ctx) { if (rf & 1) {
117
+ const _r4 = i0.ɵɵgetCurrentView();
118
+ i0.ɵɵelementStart(0, "as-split-area", 8)(1, "div", 38)(2, "div", 25)(3, "h3");
119
+ i0.ɵɵtext(4, "Published models");
120
+ i0.ɵɵelementEnd();
121
+ i0.ɵɵelementStart(5, "div", 39)(6, "span", 40);
122
+ i0.ɵɵtext(7);
123
+ i0.ɵɵelementEnd();
124
+ i0.ɵɵelementStart(8, "button", 41);
125
+ i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Conditional_4_Template_button_click_8_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.toggleList()); });
126
+ i0.ɵɵelement(9, "i", 42);
127
+ i0.ɵɵelementEnd()()();
128
+ i0.ɵɵelementStart(10, "div", 43);
129
+ i0.ɵɵrepeaterCreate(11, PSProductionComponent_Conditional_2_Conditional_4_For_12_Template, 13, 13, "div", 44, _forTrack0);
130
+ i0.ɵɵelementEnd()()();
131
+ } if (rf & 2) {
132
+ const ctx_r1 = i0.ɵɵnextContext(2);
133
+ i0.ɵɵproperty("size", ctx_r1.listSizePct)("minSize", 18)("maxSize", 50);
134
+ i0.ɵɵadvance(7);
135
+ i0.ɵɵtextInterpolate(ctx_r1.models.length);
136
+ i0.ɵɵadvance(4);
137
+ i0.ɵɵrepeater(ctx_r1.models);
138
+ } }
139
+ function PSProductionComponent_Conditional_2_Conditional_8_Template(rf, ctx) { if (rf & 1) {
140
+ const _r7 = i0.ɵɵgetCurrentView();
141
+ i0.ɵɵelementStart(0, "div", 12)(1, "div", 53);
142
+ i0.ɵɵelement(2, "i");
143
+ i0.ɵɵelementEnd();
144
+ i0.ɵɵelementStart(3, "div", 47)(4, "h2", 54);
145
+ i0.ɵɵtext(5);
146
+ i0.ɵɵelementStart(6, "span", 55);
147
+ i0.ɵɵtext(7);
148
+ i0.ɵɵelementEnd()();
149
+ i0.ɵɵelementStart(8, "div", 56);
150
+ i0.ɵɵtext(9);
151
+ i0.ɵɵelementEnd()();
152
+ i0.ɵɵelementStart(10, "span", 51);
153
+ i0.ɵɵtext(11);
154
+ i0.ɵɵelementEnd();
155
+ i0.ɵɵelementStart(12, "button", 57);
156
+ i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Conditional_8_Template_button_click_12_listener() { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.operateOpen = true); });
157
+ i0.ɵɵelement(13, "i", 58);
158
+ i0.ɵɵtext(14, " Operate ");
159
+ i0.ɵɵelementEnd();
160
+ i0.ɵɵelementStart(15, "button", 59);
161
+ i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Conditional_8_Template_button_click_15_listener() { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.toggleHeaderCollapse()); });
162
+ i0.ɵɵelement(16, "i", 60);
163
+ i0.ɵɵelementEnd()();
164
+ } if (rf & 2) {
165
+ const ctx_r1 = i0.ɵɵnextContext(2);
166
+ i0.ɵɵadvance();
167
+ i0.ɵɵclassMap("st-" + ctx_r1.selected.deployState);
62
168
  i0.ɵɵadvance();
63
- i0.ɵɵtextInterpolate(ctx_r3.deployLabel(m_r3.deployState));
169
+ i0.ɵɵclassMap(ctx_r1.deployIcon(ctx_r1.selected.deployState));
170
+ i0.ɵɵadvance(3);
171
+ i0.ɵɵtextInterpolate1("", ctx_r1.selected.label, " ");
64
172
  i0.ɵɵadvance(2);
65
- i0.ɵɵtextInterpolate(m_r3.holdoutMetric);
173
+ i0.ɵɵtextInterpolate1("v", ctx_r1.selected.version);
174
+ i0.ɵɵadvance(2);
175
+ i0.ɵɵtextInterpolate3("", ctx_r1.selected.algorithm, " \u00B7 ", ctx_r1.selected.problemType, " \u00B7 holdout ", ctx_r1.selected.holdoutMetric);
176
+ i0.ɵɵadvance();
177
+ i0.ɵɵclassMap(ctx_r1.deployBadge(ctx_r1.selected.deployState));
178
+ i0.ɵɵadvance();
179
+ i0.ɵɵtextInterpolate(ctx_r1.deployLabel(ctx_r1.selected.deployState));
66
180
  } }
67
- function PSProductionComponent_Conditional_2_Conditional_55_Template(rf, ctx) { if (rf & 1) {
68
- i0.ɵɵelementStart(0, "div", 27);
69
- i0.ɵɵelement(1, "i", 39);
70
- i0.ɵɵelementStart(2, "div", 40);
181
+ function PSProductionComponent_Conditional_2_Conditional_9_Template(rf, ctx) { if (rf & 1) {
182
+ const _r8 = i0.ɵɵgetCurrentView();
183
+ i0.ɵɵelementStart(0, "div", 61);
184
+ i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Conditional_9_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.toggleHeaderCollapse()); });
185
+ i0.ɵɵelementStart(1, "div", 62);
186
+ i0.ɵɵelement(2, "i");
187
+ i0.ɵɵelementEnd();
188
+ i0.ɵɵelementStart(3, "div", 63)(4, "strong");
189
+ i0.ɵɵtext(5);
190
+ i0.ɵɵelementEnd();
191
+ i0.ɵɵelementStart(6, "span", 64);
192
+ i0.ɵɵtext(7);
193
+ i0.ɵɵelementEnd();
194
+ i0.ɵɵelementStart(8, "span", 18);
195
+ i0.ɵɵtext(9);
196
+ i0.ɵɵelementEnd()();
197
+ i0.ɵɵelementStart(10, "span", 51);
198
+ i0.ɵɵtext(11);
199
+ i0.ɵɵelementEnd();
200
+ i0.ɵɵelementStart(12, "button", 65);
201
+ i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Conditional_9_Template_button_click_12_listener($event) { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(2); $event.stopPropagation(); return i0.ɵɵresetView(ctx_r1.operateOpen = true); });
202
+ i0.ɵɵelement(13, "i", 58);
203
+ i0.ɵɵtext(14, " Operate ");
204
+ i0.ɵɵelementEnd();
205
+ i0.ɵɵelementStart(15, "button", 66);
206
+ i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Conditional_9_Template_button_click_15_listener($event) { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(2); $event.stopPropagation(); return i0.ɵɵresetView(ctx_r1.toggleHeaderCollapse()); });
207
+ i0.ɵɵelement(16, "i", 67);
208
+ i0.ɵɵelementEnd()();
209
+ } if (rf & 2) {
210
+ const ctx_r1 = i0.ɵɵnextContext(2);
211
+ i0.ɵɵadvance();
212
+ i0.ɵɵclassMap("st-" + ctx_r1.selected.deployState);
213
+ i0.ɵɵadvance();
214
+ i0.ɵɵclassMap(ctx_r1.deployIcon(ctx_r1.selected.deployState));
215
+ i0.ɵɵadvance(3);
216
+ i0.ɵɵtextInterpolate(ctx_r1.selected.label);
217
+ i0.ɵɵadvance(2);
218
+ i0.ɵɵtextInterpolate1("v", ctx_r1.selected.version);
219
+ i0.ɵɵadvance(2);
220
+ i0.ɵɵtextInterpolate1("\u00B7 ", ctx_r1.selected.algorithm);
221
+ i0.ɵɵadvance();
222
+ i0.ɵɵclassMap(ctx_r1.deployBadge(ctx_r1.selected.deployState));
223
+ i0.ɵɵadvance();
224
+ i0.ɵɵtextInterpolate(ctx_r1.deployLabel(ctx_r1.selected.deployState));
225
+ } }
226
+ function PSProductionComponent_Conditional_2_Conditional_18_Template(rf, ctx) { if (rf & 1) {
227
+ i0.ɵɵelementStart(0, "span", 19);
228
+ i0.ɵɵtext(1);
229
+ i0.ɵɵelementEnd();
230
+ } if (rf & 2) {
231
+ const ctx_r1 = i0.ɵɵnextContext(2);
232
+ i0.ɵɵadvance();
233
+ i0.ɵɵtextInterpolate2("", ctx_r1.selectedBindings[0].targetEntity, ".", ctx_r1.selectedBindings[0].targetColumn);
234
+ } }
235
+ function PSProductionComponent_Conditional_2_Conditional_22_Conditional_1_Template(rf, ctx) { if (rf & 1) {
236
+ i0.ɵɵelementStart(0, "div", 68);
237
+ i0.ɵɵelement(1, "i", 69);
238
+ i0.ɵɵelementStart(2, "div", 70);
71
239
  i0.ɵɵtext(3, "This model is published but ");
72
240
  i0.ɵɵelementStart(4, "strong");
73
241
  i0.ɵɵtext(5, "not operating");
@@ -79,209 +247,172 @@ function PSProductionComponent_Conditional_2_Conditional_55_Template(rf, ctx) {
79
247
  i0.ɵɵtext(9, " above to run it now, schedule it to run regularly, or write predictions back to a column.");
80
248
  i0.ɵɵelementEnd()();
81
249
  } }
82
- function PSProductionComponent_Conditional_2_Conditional_56_Conditional_0_For_3_Template(rf, ctx) { if (rf & 1) {
83
- i0.ɵɵelementStart(0, "div", 42);
84
- i0.ɵɵelement(1, "i", 43);
85
- i0.ɵɵelementStart(2, "div", 18)(3, "div", 40)(4, "strong");
250
+ function PSProductionComponent_Conditional_2_Conditional_22_Conditional_2_Conditional_0_For_3_Template(rf, ctx) { if (rf & 1) {
251
+ i0.ɵɵelementStart(0, "div", 72);
252
+ i0.ɵɵelement(1, "i", 73);
253
+ i0.ɵɵelementStart(2, "div", 74)(3, "div", 70)(4, "strong");
86
254
  i0.ɵɵtext(5);
87
255
  i0.ɵɵelementEnd();
88
256
  i0.ɵɵtext(6);
89
257
  i0.ɵɵelementEnd();
90
- i0.ɵɵelementStart(7, "div", 25);
258
+ i0.ɵɵelementStart(7, "div", 18);
91
259
  i0.ɵɵtext(8);
92
260
  i0.ɵɵelementEnd()();
93
- i0.ɵɵelementStart(9, "span", 22);
261
+ i0.ɵɵelementStart(9, "span", 51);
94
262
  i0.ɵɵtext(10);
95
263
  i0.ɵɵelementEnd()();
96
264
  } if (rf & 2) {
97
- const b_r5 = ctx.$implicit;
98
- const ctx_r3 = i0.ɵɵnextContext(4);
265
+ const b_r9 = ctx.$implicit;
266
+ const ctx_r1 = i0.ɵɵnextContext(5);
99
267
  i0.ɵɵadvance(5);
100
- i0.ɵɵtextInterpolate(b_r5.targetEntity);
268
+ i0.ɵɵtextInterpolate(b_r9.targetEntity);
101
269
  i0.ɵɵadvance();
102
- i0.ɵɵtextInterpolate1(".", b_r5.targetColumn);
270
+ i0.ɵɵtextInterpolate1(".", b_r9.targetColumn);
103
271
  i0.ɵɵadvance(2);
104
- i0.ɵɵtextInterpolate2("", b_r5.mode, "", b_r5.schedulePhrase ? " \u00B7 " + b_r5.schedulePhrase : "");
272
+ i0.ɵɵtextInterpolate2("", b_r9.mode, "", b_r9.schedulePhrase ? " \u00B7 " + b_r9.schedulePhrase : "");
105
273
  i0.ɵɵadvance();
106
- i0.ɵɵclassMap(ctx_r3.modeBadge(b_r5.mode));
274
+ i0.ɵɵclassMap(ctx_r1.modeBadge(b_r9.mode));
107
275
  i0.ɵɵadvance();
108
- i0.ɵɵtextInterpolate(b_r5.mode);
276
+ i0.ɵɵtextInterpolate(b_r9.mode);
109
277
  } }
110
- function PSProductionComponent_Conditional_2_Conditional_56_Conditional_0_Template(rf, ctx) { if (rf & 1) {
111
- i0.ɵɵelementStart(0, "div", 41);
278
+ function PSProductionComponent_Conditional_2_Conditional_22_Conditional_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
279
+ i0.ɵɵelementStart(0, "div", 71);
112
280
  i0.ɵɵtext(1, "Scoring bindings");
113
281
  i0.ɵɵelementEnd();
114
- i0.ɵɵrepeaterCreate(2, PSProductionComponent_Conditional_2_Conditional_56_Conditional_0_For_3_Template, 11, 7, "div", 42, _forTrack1);
282
+ i0.ɵɵrepeaterCreate(2, PSProductionComponent_Conditional_2_Conditional_22_Conditional_2_Conditional_0_For_3_Template, 11, 7, "div", 72, _forTrack1);
115
283
  } if (rf & 2) {
116
- const ctx_r3 = i0.ɵɵnextContext(3);
284
+ const ctx_r1 = i0.ɵɵnextContext(4);
117
285
  i0.ɵɵadvance(2);
118
- i0.ɵɵrepeater(ctx_r3.selectedBindings);
286
+ i0.ɵɵrepeater(ctx_r1.selectedBindings);
119
287
  } }
120
- function PSProductionComponent_Conditional_2_Conditional_56_Conditional_1_Template(rf, ctx) { if (rf & 1) {
288
+ function PSProductionComponent_Conditional_2_Conditional_22_Conditional_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
121
289
  i0.ɵɵelementStart(0, "div", 29);
122
290
  i0.ɵɵtext(1, "Scheduled to run, writing generic output to the process run history (no write-back column).");
123
291
  i0.ɵɵelementEnd();
124
292
  } }
125
- function PSProductionComponent_Conditional_2_Conditional_56_Conditional_2_Template(rf, ctx) { if (rf & 1) {
126
- i0.ɵɵelement(0, "div", 44);
127
- i0.ɵɵelementStart(1, "div", 45)(2, "span", 25);
293
+ function PSProductionComponent_Conditional_2_Conditional_22_Conditional_2_Conditional_2_Template(rf, ctx) { if (rf & 1) {
294
+ i0.ɵɵelement(0, "div", 75);
295
+ i0.ɵɵelementStart(1, "div", 76)(2, "span", 18);
128
296
  i0.ɵɵtext(3, "Last scored");
129
297
  i0.ɵɵelementEnd();
130
- i0.ɵɵelementStart(4, "span", 40);
298
+ i0.ɵɵelementStart(4, "span", 70);
131
299
  i0.ɵɵtext(5);
132
300
  i0.ɵɵpipe(6, "date");
133
301
  i0.ɵɵelementEnd()();
134
302
  } if (rf & 2) {
135
- const ctx_r3 = i0.ɵɵnextContext(3);
303
+ const ctx_r1 = i0.ɵɵnextContext(4);
136
304
  i0.ɵɵadvance(5);
137
- i0.ɵɵtextInterpolate2("", i0.ɵɵpipeBind2(6, 2, ctx_r3.selected.lastScoredAt, "medium"), "", ctx_r3.selected.lastRowCount != null ? " \u00B7 " + ctx_r3.selected.lastRowCount + " rows" : "");
305
+ i0.ɵɵtextInterpolate2("", i0.ɵɵpipeBind2(6, 2, ctx_r1.selected.lastScoredAt, "medium"), "", ctx_r1.selected.lastRowCount != null ? " \u00B7 " + ctx_r1.selected.lastRowCount + " rows" : "");
138
306
  } }
139
- function PSProductionComponent_Conditional_2_Conditional_56_Template(rf, ctx) { if (rf & 1) {
140
- i0.ɵɵconditionalCreate(0, PSProductionComponent_Conditional_2_Conditional_56_Conditional_0_Template, 4, 0)(1, PSProductionComponent_Conditional_2_Conditional_56_Conditional_1_Template, 2, 0, "div", 29);
141
- i0.ɵɵconditionalCreate(2, PSProductionComponent_Conditional_2_Conditional_56_Conditional_2_Template, 7, 5);
307
+ function PSProductionComponent_Conditional_2_Conditional_22_Conditional_2_Template(rf, ctx) { if (rf & 1) {
308
+ i0.ɵɵconditionalCreate(0, PSProductionComponent_Conditional_2_Conditional_22_Conditional_2_Conditional_0_Template, 4, 0)(1, PSProductionComponent_Conditional_2_Conditional_22_Conditional_2_Conditional_1_Template, 2, 0, "div", 29);
309
+ i0.ɵɵconditionalCreate(2, PSProductionComponent_Conditional_2_Conditional_22_Conditional_2_Conditional_2_Template, 7, 5);
142
310
  } if (rf & 2) {
143
- const ctx_r3 = i0.ɵɵnextContext(2);
144
- i0.ɵɵconditional(ctx_r3.selectedBindings.length > 0 ? 0 : 1);
311
+ const ctx_r1 = i0.ɵɵnextContext(3);
312
+ i0.ɵɵconditional(ctx_r1.selectedBindings.length > 0 ? 0 : 1);
145
313
  i0.ɵɵadvance(2);
146
- i0.ɵɵconditional(ctx_r3.selected.lastScoredAt ? 2 : -1);
314
+ i0.ɵɵconditional(ctx_r1.selected.lastScoredAt ? 2 : -1);
147
315
  } }
148
- function PSProductionComponent_Conditional_2_Conditional_64_Conditional_3_Template(rf, ctx) { if (rf & 1) {
149
- i0.ɵɵelement(0, "mj-loading", 48);
150
- } }
151
- function PSProductionComponent_Conditional_2_Conditional_64_Conditional_4_Template(rf, ctx) { if (rf & 1) {
152
- i0.ɵɵelementStart(0, "div", 29);
153
- i0.ɵɵtext(1, "No per-record predictions recorded for this run.");
154
- i0.ɵɵelementEnd();
155
- } }
156
- function PSProductionComponent_Conditional_2_Conditional_64_Conditional_5_For_13_Conditional_8_Template(rf, ctx) { if (rf & 1) {
157
- i0.ɵɵelementStart(0, "span", 52);
158
- i0.ɵɵtext(1);
316
+ function PSProductionComponent_Conditional_2_Conditional_22_Template(rf, ctx) { if (rf & 1) {
317
+ i0.ɵɵelementStart(0, "div", 23);
318
+ i0.ɵɵconditionalCreate(1, PSProductionComponent_Conditional_2_Conditional_22_Conditional_1_Template, 10, 0, "div", 68)(2, PSProductionComponent_Conditional_2_Conditional_22_Conditional_2_Template, 3, 2);
159
319
  i0.ɵɵelementEnd();
160
320
  } if (rf & 2) {
161
- const d_r7 = i0.ɵɵnextContext().$implicit;
321
+ const ctx_r1 = i0.ɵɵnextContext(2);
162
322
  i0.ɵɵadvance();
163
- i0.ɵɵtextInterpolate1(" \u00B7 ", d_r7.class);
323
+ i0.ɵɵconditional(ctx_r1.selected.deployState === "idle" ? 1 : 2);
164
324
  } }
165
- function PSProductionComponent_Conditional_2_Conditional_64_Conditional_5_For_13_Template(rf, ctx) { if (rf & 1) {
166
- i0.ɵɵelementStart(0, "tr")(1, "td", 50);
167
- i0.ɵɵtext(2);
168
- i0.ɵɵelementEnd();
169
- i0.ɵɵelementStart(3, "td")(4, "span", 22);
170
- i0.ɵɵtext(5);
171
- i0.ɵɵelementEnd()();
172
- i0.ɵɵelementStart(6, "td", 51);
173
- i0.ɵɵtext(7);
174
- i0.ɵɵconditionalCreate(8, PSProductionComponent_Conditional_2_Conditional_64_Conditional_5_For_13_Conditional_8_Template, 2, 1, "span", 52);
175
- i0.ɵɵelementEnd();
176
- i0.ɵɵelementStart(9, "td", 29);
177
- i0.ɵɵtext(10);
178
- i0.ɵɵpipe(11, "date");
179
- i0.ɵɵelementEnd()();
325
+ function PSProductionComponent_Conditional_2_Conditional_31_Template(rf, ctx) { if (rf & 1) {
326
+ const _r10 = i0.ɵɵgetCurrentView();
327
+ i0.ɵɵelementStart(0, "div", 16)(1, "button", 77);
328
+ i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Conditional_31_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.toggleRunFocus()); });
329
+ i0.ɵɵelement(2, "i", 22);
330
+ i0.ɵɵelementStart(3, "span");
331
+ i0.ɵɵtext(4);
332
+ i0.ɵɵelementEnd()()();
180
333
  } if (rf & 2) {
181
- const d_r7 = ctx.$implicit;
182
- const ctx_r3 = i0.ɵɵnextContext(4);
183
- i0.ɵɵadvance(2);
184
- i0.ɵɵtextInterpolate(d_r7.recordId);
185
- i0.ɵɵadvance(2);
186
- i0.ɵɵclassMap(ctx_r3.runBadge(d_r7.status));
334
+ const ctx_r1 = i0.ɵɵnextContext(2);
187
335
  i0.ɵɵadvance();
188
- i0.ɵɵtextInterpolate(d_r7.status);
189
- i0.ɵɵadvance(2);
190
- i0.ɵɵtextInterpolate(d_r7.score);
336
+ i0.ɵɵclassProp("active", ctx_r1.isRunFocused);
337
+ i0.ɵɵproperty("title", ctx_r1.isRunFocused ? "Exit focus (expand header & deployment)" : "Focus mode (collapse header & deployment)");
191
338
  i0.ɵɵadvance();
192
- i0.ɵɵconditional(d_r7.class ? 8 : -1);
339
+ i0.ɵɵclassProp("fa-compress", ctx_r1.isRunFocused)("fa-expand", !ctx_r1.isRunFocused);
193
340
  i0.ɵɵadvance(2);
194
- i0.ɵɵtextInterpolate(d_r7.scoredAt ? i0.ɵɵpipeBind2(11, 7, d_r7.scoredAt, "short") : "\u2014");
341
+ i0.ɵɵtextInterpolate(ctx_r1.isRunFocused ? "Exit Focus" : "Focus");
195
342
  } }
196
- function PSProductionComponent_Conditional_2_Conditional_64_Conditional_5_Template(rf, ctx) { if (rf & 1) {
197
- i0.ɵɵelementStart(0, "table", 49)(1, "thead")(2, "tr")(3, "th");
198
- i0.ɵɵtext(4, "Record");
343
+ function PSProductionComponent_Conditional_2_Conditional_33_Template(rf, ctx) { if (rf & 1) {
344
+ const _r11 = i0.ɵɵgetCurrentView();
345
+ i0.ɵɵelementStart(0, "div", 78)(1, "button", 79);
346
+ i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Conditional_33_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r11); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.closeRun()); });
347
+ i0.ɵɵelement(2, "i", 80);
348
+ i0.ɵɵtext(3, " All runs ");
199
349
  i0.ɵɵelementEnd();
200
- i0.ɵɵelementStart(5, "th");
201
- i0.ɵɵtext(6, "Status");
202
- i0.ɵɵelementEnd();
203
- i0.ɵɵelementStart(7, "th");
204
- i0.ɵɵtext(8, "Prediction");
205
- i0.ɵɵelementEnd();
206
- i0.ɵɵelementStart(9, "th");
207
- i0.ɵɵtext(10, "Scored");
208
- i0.ɵɵelementEnd()()();
209
- i0.ɵɵelementStart(11, "tbody");
210
- i0.ɵɵrepeaterCreate(12, PSProductionComponent_Conditional_2_Conditional_64_Conditional_5_For_13_Template, 12, 10, "tr", null, _forTrack2);
350
+ i0.ɵɵelementStart(4, "span", 64);
351
+ i0.ɵɵtext(5);
211
352
  i0.ɵɵelementEnd()();
353
+ i0.ɵɵelement(6, "ps-predictions-grid", 81);
212
354
  } if (rf & 2) {
213
- const ctx_r3 = i0.ɵɵnextContext(3);
214
- i0.ɵɵadvance(12);
215
- i0.ɵɵrepeater(ctx_r3.runDetails);
216
- } }
217
- function PSProductionComponent_Conditional_2_Conditional_64_Template(rf, ctx) { if (rf & 1) {
218
- const _r6 = i0.ɵɵgetCurrentView();
219
- i0.ɵɵelementStart(0, "button", 46);
220
- i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Conditional_64_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r6); const ctx_r3 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r3.closeRun()); });
221
- i0.ɵɵelement(1, "i", 47);
222
- i0.ɵɵtext(2, " All runs ");
223
- i0.ɵɵelementEnd();
224
- i0.ɵɵconditionalCreate(3, PSProductionComponent_Conditional_2_Conditional_64_Conditional_3_Template, 1, 0, "mj-loading", 48)(4, PSProductionComponent_Conditional_2_Conditional_64_Conditional_4_Template, 2, 0, "div", 29)(5, PSProductionComponent_Conditional_2_Conditional_64_Conditional_5_Template, 14, 0, "table", 49);
225
- } if (rf & 2) {
226
- const ctx_r3 = i0.ɵɵnextContext(2);
227
- i0.ɵɵadvance(3);
228
- i0.ɵɵconditional(ctx_r3.loadingDetails ? 3 : ctx_r3.runDetails.length === 0 ? 4 : 5);
355
+ const ctx_r1 = i0.ɵɵnextContext(2);
356
+ i0.ɵɵadvance(5);
357
+ i0.ɵɵtextInterpolate1("Run: ", ctx_r1.selectedRunId);
358
+ i0.ɵɵadvance();
359
+ i0.ɵɵproperty("modelId", ctx_r1.selected.modelId)("runId", ctx_r1.selectedRunId)("entityName", ctx_r1.selectedTargetEntityName)("problemType", ctx_r1.selected.problemType)("title", ctx_r1.selected.label + " \u2014 Run Predictions")("height", ctx_r1.runGridHeight);
229
360
  } }
230
- function PSProductionComponent_Conditional_2_Conditional_65_Template(rf, ctx) { if (rf & 1) {
361
+ function PSProductionComponent_Conditional_2_Conditional_34_Template(rf, ctx) { if (rf & 1) {
231
362
  i0.ɵɵelement(0, "mj-loading", 28);
232
363
  } }
233
- function PSProductionComponent_Conditional_2_Conditional_66_Template(rf, ctx) { if (rf & 1) {
364
+ function PSProductionComponent_Conditional_2_Conditional_35_Template(rf, ctx) { if (rf & 1) {
234
365
  i0.ɵɵelementStart(0, "div", 29);
235
366
  i0.ɵɵtext(1, "No runs recorded yet. Each time this model runs as a Record Process, its predictions are saved to the process run history \u2014 even without a scoring binding.");
236
367
  i0.ɵɵelementEnd();
237
368
  } }
238
- function PSProductionComponent_Conditional_2_Conditional_67_For_14_Conditional_14_Template(rf, ctx) { if (rf & 1) {
239
- i0.ɵɵelementStart(0, "span", 56);
369
+ function PSProductionComponent_Conditional_2_Conditional_36_For_14_Conditional_14_Template(rf, ctx) { if (rf & 1) {
370
+ i0.ɵɵelementStart(0, "span", 87);
240
371
  i0.ɵɵtext(1, "dry run");
241
372
  i0.ɵɵelementEnd();
242
373
  } }
243
- function PSProductionComponent_Conditional_2_Conditional_67_For_14_Template(rf, ctx) { if (rf & 1) {
244
- const _r8 = i0.ɵɵgetCurrentView();
245
- i0.ɵɵelementStart(0, "tr", 54);
246
- i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Conditional_67_For_14_Template_tr_click_0_listener() { const r_r9 = i0.ɵɵrestoreView(_r8).$implicit; const ctx_r3 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r3.openRun(r_r9.ID)); });
374
+ function PSProductionComponent_Conditional_2_Conditional_36_For_14_Template(rf, ctx) { if (rf & 1) {
375
+ const _r12 = i0.ɵɵgetCurrentView();
376
+ i0.ɵɵelementStart(0, "tr", 83);
377
+ i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Conditional_36_For_14_Template_tr_click_0_listener() { const r_r13 = i0.ɵɵrestoreView(_r12).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.openRun(r_r13.ID)); });
247
378
  i0.ɵɵelementStart(1, "td");
248
379
  i0.ɵɵtext(2);
249
380
  i0.ɵɵpipe(3, "date");
250
381
  i0.ɵɵelementEnd();
251
- i0.ɵɵelementStart(4, "td")(5, "span", 22);
382
+ i0.ɵɵelementStart(4, "td")(5, "span", 51);
252
383
  i0.ɵɵtext(6);
253
384
  i0.ɵɵelementEnd()();
254
- i0.ɵɵelementStart(7, "td", 51);
385
+ i0.ɵɵelementStart(7, "td", 84);
255
386
  i0.ɵɵtext(8);
256
- i0.ɵɵelementStart(9, "span", 52);
387
+ i0.ɵɵelementStart(9, "span", 85);
257
388
  i0.ɵɵtext(10);
258
389
  i0.ɵɵelementEnd()();
259
390
  i0.ɵɵelementStart(11, "td", 29);
260
391
  i0.ɵɵtext(12);
261
392
  i0.ɵɵelementEnd();
262
- i0.ɵɵelementStart(13, "td", 55);
263
- i0.ɵɵconditionalCreate(14, PSProductionComponent_Conditional_2_Conditional_67_For_14_Conditional_14_Template, 2, 0, "span", 56);
264
- i0.ɵɵelement(15, "i", 57);
393
+ i0.ɵɵelementStart(13, "td", 86);
394
+ i0.ɵɵconditionalCreate(14, PSProductionComponent_Conditional_2_Conditional_36_For_14_Conditional_14_Template, 2, 0, "span", 87);
395
+ i0.ɵɵelement(15, "i", 88);
265
396
  i0.ɵɵelementEnd()();
266
397
  } if (rf & 2) {
267
- const r_r9 = ctx.$implicit;
268
- const ctx_r3 = i0.ɵɵnextContext(3);
398
+ const r_r13 = ctx.$implicit;
399
+ const ctx_r1 = i0.ɵɵnextContext(3);
269
400
  i0.ɵɵadvance(2);
270
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(3, 8, r_r9.StartTime || r_r9.CreatedAt, "short"));
401
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(3, 8, r_r13.StartTime || r_r13.CreatedAt, "short"));
271
402
  i0.ɵɵadvance(3);
272
- i0.ɵɵclassMap(ctx_r3.runBadge(r_r9.Status));
403
+ i0.ɵɵclassMap(ctx_r1.runBadge(r_r13.Status));
273
404
  i0.ɵɵadvance();
274
- i0.ɵɵtextInterpolate(r_r9.Status);
405
+ i0.ɵɵtextInterpolate(r_r13.Status);
275
406
  i0.ɵɵadvance(2);
276
- i0.ɵɵtextInterpolate(r_r9.SuccessCount);
407
+ i0.ɵɵtextInterpolate(r_r13.SuccessCount);
277
408
  i0.ɵɵadvance(2);
278
- i0.ɵɵtextInterpolate1("/", r_r9.TotalItemCount ?? 0);
409
+ i0.ɵɵtextInterpolate1("/", r_r13.TotalItemCount ?? 0);
279
410
  i0.ɵɵadvance(2);
280
- i0.ɵɵtextInterpolate(r_r9.ProcessName || "\u2014");
411
+ i0.ɵɵtextInterpolate(r_r13.ProcessName || "\u2014");
281
412
  i0.ɵɵadvance(2);
282
- i0.ɵɵconditional(r_r9.DryRun ? 14 : -1);
413
+ i0.ɵɵconditional(r_r13.DryRun ? 14 : -1);
283
414
  } }
284
- function PSProductionComponent_Conditional_2_Conditional_67_Template(rf, ctx) { if (rf & 1) {
415
+ function PSProductionComponent_Conditional_2_Conditional_36_Template(rf, ctx) { if (rf & 1) {
285
416
  i0.ɵɵelementStart(0, "table", 30)(1, "thead")(2, "tr")(3, "th");
286
417
  i0.ɵɵtext(4, "When");
287
418
  i0.ɵɵelementEnd();
@@ -297,124 +428,98 @@ function PSProductionComponent_Conditional_2_Conditional_67_Template(rf, ctx) {
297
428
  i0.ɵɵelement(11, "th");
298
429
  i0.ɵɵelementEnd()();
299
430
  i0.ɵɵelementStart(12, "tbody");
300
- i0.ɵɵrepeaterCreate(13, PSProductionComponent_Conditional_2_Conditional_67_For_14_Template, 16, 11, "tr", 53, _forTrack3);
431
+ i0.ɵɵrepeaterCreate(13, PSProductionComponent_Conditional_2_Conditional_36_For_14_Template, 16, 11, "tr", 82, _forTrack2);
301
432
  i0.ɵɵelementEnd()();
302
433
  } if (rf & 2) {
303
- const ctx_r3 = i0.ɵɵnextContext(2);
434
+ const ctx_r1 = i0.ɵɵnextContext(2);
304
435
  i0.ɵɵadvance(13);
305
- i0.ɵɵrepeater(ctx_r3.runs);
436
+ i0.ɵɵrepeater(ctx_r1.runs);
306
437
  } }
307
438
  function PSProductionComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
308
439
  const _r1 = i0.ɵɵgetCurrentView();
309
- i0.ɵɵelementStart(0, "div", 4)(1, "div", 5)(2, "div", 6);
310
- i0.ɵɵtext(3, "Published");
440
+ i0.ɵɵconditionalCreate(0, PSProductionComponent_Conditional_2_Conditional_0_Template, 21, 4, "div", 4);
441
+ i0.ɵɵelementStart(1, "div", 5);
442
+ i0.ɵɵconditionalCreate(2, PSProductionComponent_Conditional_2_Conditional_2_Template, 5, 0, "div", 6);
443
+ i0.ɵɵelementStart(3, "as-split", 7);
444
+ i0.ɵɵlistener("dragEnd", function PSProductionComponent_Conditional_2_Template_as_split_dragEnd_3_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onSplitDragEnd($event.sizes)); });
445
+ i0.ɵɵconditionalCreate(4, PSProductionComponent_Conditional_2_Conditional_4_Template, 13, 4, "as-split-area", 8);
446
+ i0.ɵɵelementStart(5, "as-split-area", 9)(6, "div", 10)(7, "div", 11);
447
+ i0.ɵɵconditionalCreate(8, PSProductionComponent_Conditional_2_Conditional_8_Template, 17, 12, "div", 12)(9, PSProductionComponent_Conditional_2_Conditional_9_Template, 17, 10, "div", 13);
311
448
  i0.ɵɵelementEnd();
312
- i0.ɵɵelementStart(4, "div", 7);
313
- i0.ɵɵtext(5);
314
- i0.ɵɵelementEnd()();
315
- i0.ɵɵelementStart(6, "div", 5)(7, "div", 6);
316
- i0.ɵɵtext(8, "Scheduled");
449
+ i0.ɵɵelementStart(10, "div", 14)(11, "div", 15);
450
+ i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Template_div_click_11_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.toggleDeploymentCollapse()); });
451
+ i0.ɵɵelementStart(12, "div", 16);
452
+ i0.ɵɵelement(13, "i", 17);
453
+ i0.ɵɵelementStart(14, "h3");
454
+ i0.ɵɵtext(15, "Deployment");
317
455
  i0.ɵɵelementEnd();
318
- i0.ɵɵelementStart(9, "div", 7);
319
- i0.ɵɵtext(10);
320
- i0.ɵɵelementEnd()();
321
- i0.ɵɵelementStart(11, "div", 5)(12, "div", 6);
322
- i0.ɵɵtext(13, "Bound (write-back)");
456
+ i0.ɵɵelementStart(16, "span", 18);
457
+ i0.ɵɵtext(17);
323
458
  i0.ɵɵelementEnd();
324
- i0.ɵɵelementStart(14, "div", 7);
325
- i0.ɵɵtext(15);
326
- i0.ɵɵelementEnd()();
327
- i0.ɵɵelementStart(16, "div", 5)(17, "div", 6);
328
- i0.ɵɵtext(18, "Idle");
459
+ i0.ɵɵconditionalCreate(18, PSProductionComponent_Conditional_2_Conditional_18_Template, 2, 2, "span", 19);
329
460
  i0.ɵɵelementEnd();
330
- i0.ɵɵelementStart(19, "div", 7);
331
- i0.ɵɵtext(20);
461
+ i0.ɵɵelementStart(19, "div", 20)(20, "button", 21);
462
+ i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Template_button_click_20_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); $event.stopPropagation(); return i0.ɵɵresetView(ctx_r1.toggleDeploymentCollapse()); });
463
+ i0.ɵɵelement(21, "i", 22);
332
464
  i0.ɵɵelementEnd()()();
333
- i0.ɵɵelementStart(21, "div", 8)(22, "div", 9)(23, "div", 10)(24, "h3");
334
- i0.ɵɵtext(25, "Published models");
335
- i0.ɵɵelementEnd();
336
- i0.ɵɵelementStart(26, "span", 11);
337
- i0.ɵɵtext(27);
338
- i0.ɵɵelementEnd()();
339
- i0.ɵɵelementStart(28, "div", 12);
340
- i0.ɵɵrepeaterCreate(29, PSProductionComponent_Conditional_2_For_30_Template, 13, 13, "div", 13, _forTrack0);
341
- i0.ɵɵelementEnd()();
342
- i0.ɵɵelementStart(31, "div", 14)(32, "div", 15)(33, "div", 16)(34, "div", 17);
343
- i0.ɵɵelement(35, "i");
344
- i0.ɵɵelementEnd();
345
- i0.ɵɵelementStart(36, "div", 18)(37, "h2", 19);
346
- i0.ɵɵtext(38);
347
- i0.ɵɵelementStart(39, "span", 20);
348
- i0.ɵɵtext(40);
349
- i0.ɵɵelementEnd()();
350
- i0.ɵɵelementStart(41, "div", 21);
351
- i0.ɵɵtext(42);
352
- i0.ɵɵelementEnd()();
353
- i0.ɵɵelementStart(43, "span", 22);
354
- i0.ɵɵtext(44);
465
+ i0.ɵɵconditionalCreate(22, PSProductionComponent_Conditional_2_Conditional_22_Template, 3, 1, "div", 23);
355
466
  i0.ɵɵelementEnd();
356
- i0.ɵɵelementStart(45, "button", 23);
357
- i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Template_button_click_45_listener() { i0.ɵɵrestoreView(_r1); const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.operateOpen = true); });
358
- i0.ɵɵelement(46, "i", 24);
359
- i0.ɵɵtext(47, " Operate ");
360
- i0.ɵɵelementEnd()()();
361
- i0.ɵɵelementStart(48, "div", 15)(49, "div", 10)(50, "h3");
362
- i0.ɵɵtext(51, "Deployment");
467
+ i0.ɵɵelementStart(23, "div", 24)(24, "div", 25)(25, "div", 16);
468
+ i0.ɵɵelement(26, "i", 26);
469
+ i0.ɵɵelementStart(27, "h3");
470
+ i0.ɵɵtext(28, "Run history");
363
471
  i0.ɵɵelementEnd();
364
- i0.ɵɵelementStart(52, "span", 25);
365
- i0.ɵɵtext(53);
366
- i0.ɵɵelementEnd()();
367
- i0.ɵɵelementStart(54, "div", 26);
368
- i0.ɵɵconditionalCreate(55, PSProductionComponent_Conditional_2_Conditional_55_Template, 10, 0, "div", 27)(56, PSProductionComponent_Conditional_2_Conditional_56_Template, 3, 2);
472
+ i0.ɵɵelementStart(29, "span", 18);
473
+ i0.ɵɵtext(30, "last 90 days \u00B7 newest first");
369
474
  i0.ɵɵelementEnd()();
370
- i0.ɵɵelementStart(57, "div", 15)(58, "div", 10)(59, "h3");
371
- i0.ɵɵtext(60, "Run history");
475
+ i0.ɵɵconditionalCreate(31, PSProductionComponent_Conditional_2_Conditional_31_Template, 5, 8, "div", 16);
372
476
  i0.ɵɵelementEnd();
373
- i0.ɵɵelementStart(61, "span", 25);
374
- i0.ɵɵtext(62, "last 90 days \u00B7 newest first");
375
- i0.ɵɵelementEnd()();
376
- i0.ɵɵelementStart(63, "div", 26);
377
- i0.ɵɵconditionalCreate(64, PSProductionComponent_Conditional_2_Conditional_64_Template, 6, 1)(65, PSProductionComponent_Conditional_2_Conditional_65_Template, 1, 0, "mj-loading", 28)(66, PSProductionComponent_Conditional_2_Conditional_66_Template, 2, 0, "div", 29)(67, PSProductionComponent_Conditional_2_Conditional_67_Template, 15, 0, "table", 30);
378
- i0.ɵɵelementEnd()()()();
379
- i0.ɵɵelementStart(68, "ps-operate-dialog", 31);
380
- i0.ɵɵlistener("Close", function PSProductionComponent_Conditional_2_Template_ps_operate_dialog_Close_68_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.onOperateClose($event)); });
477
+ i0.ɵɵelementStart(32, "div", 27);
478
+ i0.ɵɵconditionalCreate(33, PSProductionComponent_Conditional_2_Conditional_33_Template, 7, 7)(34, PSProductionComponent_Conditional_2_Conditional_34_Template, 1, 0, "mj-loading", 28)(35, PSProductionComponent_Conditional_2_Conditional_35_Template, 2, 0, "div", 29)(36, PSProductionComponent_Conditional_2_Conditional_36_Template, 15, 0, "table", 30);
479
+ i0.ɵɵelementEnd()()()()()();
480
+ i0.ɵɵelementStart(37, "ps-operate-dialog", 31);
481
+ i0.ɵɵlistener("Close", function PSProductionComponent_Conditional_2_Template_ps_operate_dialog_Close_37_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onOperateClose($event)); });
381
482
  i0.ɵɵelementEnd();
382
483
  } if (rf & 2) {
383
- const ctx_r3 = i0.ɵɵnextContext();
384
- i0.ɵɵadvance(5);
385
- i0.ɵɵtextInterpolate(ctx_r3.models.length);
386
- i0.ɵɵadvance(5);
387
- i0.ɵɵtextInterpolate(ctx_r3.scheduledModelCount);
388
- i0.ɵɵadvance(5);
389
- i0.ɵɵtextInterpolate(ctx_r3.boundModelCount);
390
- i0.ɵɵadvance(5);
391
- i0.ɵɵtextInterpolate(ctx_r3.idleModelCount);
392
- i0.ɵɵadvance(7);
393
- i0.ɵɵtextInterpolate(ctx_r3.models.length);
394
- i0.ɵɵadvance(2);
395
- i0.ɵɵrepeater(ctx_r3.models);
396
- i0.ɵɵadvance(5);
397
- i0.ɵɵclassMap("st-" + ctx_r3.selected.deployState);
484
+ const ctx_r1 = i0.ɵɵnextContext();
485
+ i0.ɵɵconditional(!ctx_r1.isRunFocused ? 0 : -1);
398
486
  i0.ɵɵadvance();
399
- i0.ɵɵclassMap(ctx_r3.deployIcon(ctx_r3.selected.deployState));
400
- i0.ɵɵadvance(3);
401
- i0.ɵɵtextInterpolate1("", ctx_r3.selected.label, " ");
487
+ i0.ɵɵclassProp("list-collapsed", ctx_r1.isListCollapsed);
488
+ i0.ɵɵadvance();
489
+ i0.ɵɵconditional(ctx_r1.isListCollapsed ? 2 : -1);
490
+ i0.ɵɵadvance();
491
+ i0.ɵɵproperty("gutterSize", 6);
492
+ i0.ɵɵadvance();
493
+ i0.ɵɵconditional(!ctx_r1.isListCollapsed ? 4 : -1);
494
+ i0.ɵɵadvance();
495
+ i0.ɵɵproperty("size", ctx_r1.isListCollapsed ? 100 : ctx_r1.detailSizePct)("minSize", 50);
496
+ i0.ɵɵadvance();
497
+ i0.ɵɵclassProp("focused-run", ctx_r1.selectedRunId && ctx_r1.isRunFocused);
498
+ i0.ɵɵadvance();
499
+ i0.ɵɵclassProp("collapsed", ctx_r1.isHeaderCollapsed);
500
+ i0.ɵɵadvance();
501
+ i0.ɵɵconditional(!ctx_r1.isHeaderCollapsed ? 8 : 9);
402
502
  i0.ɵɵadvance(2);
403
- i0.ɵɵtextInterpolate1("v", ctx_r3.selected.version);
503
+ i0.ɵɵclassProp("collapsed", ctx_r1.isDeploymentCollapsed);
504
+ i0.ɵɵadvance(7);
505
+ i0.ɵɵtextInterpolate4("", ctx_r1.selected.processCount, " process", ctx_r1.selected.processCount === 1 ? "" : "es", " \u00B7 ", ctx_r1.selected.bindingCount, " binding", ctx_r1.selected.bindingCount === 1 ? "" : "s");
506
+ i0.ɵɵadvance();
507
+ i0.ɵɵconditional(ctx_r1.isDeploymentCollapsed && ctx_r1.selectedBindings.length > 0 ? 18 : -1);
404
508
  i0.ɵɵadvance(2);
405
- i0.ɵɵtextInterpolate3("", ctx_r3.selected.algorithm, " \u00B7 ", ctx_r3.selected.problemType, " \u00B7 holdout ", ctx_r3.selected.holdoutMetric);
509
+ i0.ɵɵproperty("title", ctx_r1.isDeploymentCollapsed ? "Expand deployment" : "Collapse deployment");
510
+ i0.ɵɵattribute("aria-label", ctx_r1.isDeploymentCollapsed ? "Expand deployment" : "Collapse deployment");
511
+ i0.ɵɵadvance();
512
+ i0.ɵɵclassProp("fa-chevron-up", !ctx_r1.isDeploymentCollapsed)("fa-chevron-down", ctx_r1.isDeploymentCollapsed);
406
513
  i0.ɵɵadvance();
407
- i0.ɵɵclassMap(ctx_r3.deployBadge(ctx_r3.selected.deployState));
514
+ i0.ɵɵconditional(!ctx_r1.isDeploymentCollapsed ? 22 : -1);
408
515
  i0.ɵɵadvance();
409
- i0.ɵɵtextInterpolate(ctx_r3.deployLabel(ctx_r3.selected.deployState));
410
- i0.ɵɵadvance(9);
411
- i0.ɵɵtextInterpolate4("", ctx_r3.selected.processCount, " process", ctx_r3.selected.processCount === 1 ? "" : "es", " \u00B7 ", ctx_r3.selected.bindingCount, " binding", ctx_r3.selected.bindingCount === 1 ? "" : "s");
516
+ i0.ɵɵclassProp("drilldown-active", ctx_r1.selectedRunId)("focus-fill", ctx_r1.selectedRunId && ctx_r1.isRunFocused);
517
+ i0.ɵɵadvance(8);
518
+ i0.ɵɵconditional(ctx_r1.selectedRunId ? 31 : -1);
412
519
  i0.ɵɵadvance(2);
413
- i0.ɵɵconditional(ctx_r3.selected.deployState === "idle" ? 55 : 56);
414
- i0.ɵɵadvance(9);
415
- i0.ɵɵconditional(ctx_r3.selectedRunId ? 64 : ctx_r3.loadingRuns ? 65 : ctx_r3.runs.length === 0 ? 66 : 67);
520
+ i0.ɵɵconditional(ctx_r1.selectedRunId ? 33 : ctx_r1.loadingRuns ? 34 : ctx_r1.runs.length === 0 ? 35 : 36);
416
521
  i0.ɵɵadvance(4);
417
- i0.ɵɵproperty("Visible", ctx_r3.operateOpen)("modelId", ctx_r3.selected.modelId)("modelLabel", ctx_r3.selected.label)("engine", ctx_r3.engine)("provider", ctx_r3.ProviderToUse)("currentUser", ctx_r3.ProviderToUse.CurrentUser);
522
+ i0.ɵɵproperty("Visible", ctx_r1.operateOpen)("modelId", ctx_r1.selected.modelId)("modelLabel", ctx_r1.selected.label)("engine", ctx_r1.engine)("provider", ctx_r1.ProviderToUse)("currentUser", ctx_r1.ProviderToUse.CurrentUser);
418
523
  } }
419
524
  /**
420
525
  * Models in Production — the **model-centric** control tower. Every approved (Published) model appears
@@ -437,13 +542,26 @@ export class PSProductionComponent extends BaseAngularComponent {
437
542
  selectedBindings = [];
438
543
  runs = [];
439
544
  loadingRuns = false;
545
+ loadingDetails = false;
440
546
  /** Whether the "Operate this model" dialog is open for the selected model. */
441
547
  operateOpen = false;
442
548
  /** The run whose per-record predictions are drilled into (null = the run list). */
443
549
  selectedRunId = null;
444
550
  runDetails = [];
445
- loadingDetails = false;
551
+ listSizePct = 30;
552
+ detailSizePct = 70;
553
+ isListCollapsed = false;
554
+ isHeaderCollapsed = false;
555
+ isDeploymentCollapsed = false;
556
+ isRunFocused = false;
557
+ get runGridHeight() {
558
+ if (this.isRunFocused || (this.isHeaderCollapsed && this.isDeploymentCollapsed)) {
559
+ return '740px';
560
+ }
561
+ return '520px';
562
+ }
446
563
  ngOnInit() {
564
+ this.loadLayoutPrefs();
447
565
  // Reactive: rebuild the published-model list on any MJ: ML Models change (save/delete/remote-invalidate).
448
566
  this.modelsSub = this.engine
449
567
  .ObserveProperty('_Models')
@@ -455,6 +573,83 @@ export class PSProductionComponent extends BaseAngularComponent {
455
573
  ngOnDestroy() {
456
574
  this.modelsSub?.unsubscribe();
457
575
  }
576
+ // ---- layout splitter resizing & persistence ----
577
+ toggleHeaderCollapse() {
578
+ this.isHeaderCollapsed = !this.isHeaderCollapsed;
579
+ this.updateFocusState();
580
+ this.saveLayoutPrefs();
581
+ this.cdr.detectChanges();
582
+ }
583
+ toggleDeploymentCollapse() {
584
+ this.isDeploymentCollapsed = !this.isDeploymentCollapsed;
585
+ this.updateFocusState();
586
+ this.saveLayoutPrefs();
587
+ this.cdr.detectChanges();
588
+ }
589
+ toggleRunFocus() {
590
+ this.isRunFocused = !this.isRunFocused;
591
+ if (this.isRunFocused) {
592
+ this.isHeaderCollapsed = true;
593
+ this.isDeploymentCollapsed = true;
594
+ }
595
+ else {
596
+ this.isHeaderCollapsed = false;
597
+ this.isDeploymentCollapsed = false;
598
+ }
599
+ this.saveLayoutPrefs();
600
+ this.cdr.detectChanges();
601
+ }
602
+ updateFocusState() {
603
+ this.isRunFocused = this.isHeaderCollapsed && this.isDeploymentCollapsed;
604
+ }
605
+ loadLayoutPrefs() {
606
+ const raw = UserInfoEngine.Instance.GetSetting('mj.predictiveStudio.production.layout');
607
+ if (raw) {
608
+ try {
609
+ const parsed = JSON.parse(raw);
610
+ if (typeof parsed.listSizePct === 'number' && parsed.listSizePct >= 15 && parsed.listSizePct <= 60) {
611
+ this.listSizePct = parsed.listSizePct;
612
+ this.detailSizePct = 100 - parsed.listSizePct;
613
+ }
614
+ if (typeof parsed.isListCollapsed === 'boolean') {
615
+ this.isListCollapsed = parsed.isListCollapsed;
616
+ }
617
+ if (typeof parsed.isHeaderCollapsed === 'boolean') {
618
+ this.isHeaderCollapsed = parsed.isHeaderCollapsed;
619
+ }
620
+ if (typeof parsed.isDeploymentCollapsed === 'boolean') {
621
+ this.isDeploymentCollapsed = parsed.isDeploymentCollapsed;
622
+ }
623
+ if (typeof parsed.isRunFocused === 'boolean') {
624
+ this.isRunFocused = parsed.isRunFocused;
625
+ }
626
+ }
627
+ catch { }
628
+ }
629
+ }
630
+ onSplitDragEnd(sizes) {
631
+ if (Array.isArray(sizes) && sizes.length === 2 && typeof sizes[0] === 'number' && typeof sizes[1] === 'number') {
632
+ this.listSizePct = Math.round(sizes[0]);
633
+ this.detailSizePct = Math.round(sizes[1]);
634
+ this.saveLayoutPrefs();
635
+ }
636
+ }
637
+ toggleList() {
638
+ this.isListCollapsed = !this.isListCollapsed;
639
+ this.saveLayoutPrefs();
640
+ this.cdr.detectChanges();
641
+ }
642
+ saveLayoutPrefs() {
643
+ const prefs = {
644
+ listSizePct: this.listSizePct,
645
+ detailSizePct: this.detailSizePct,
646
+ isListCollapsed: this.isListCollapsed,
647
+ isHeaderCollapsed: this.isHeaderCollapsed,
648
+ isDeploymentCollapsed: this.isDeploymentCollapsed,
649
+ isRunFocused: this.isRunFocused,
650
+ };
651
+ UserInfoEngine.Instance.SetSettingDebounced('mj.predictiveStudio.production.layout', JSON.stringify(prefs));
652
+ }
458
653
  // ---- KPIs ----
459
654
  get scheduledModelCount() {
460
655
  return this.models.filter((m) => m.deployState === 'scheduled' || m.scheduledCount > 0).length;
@@ -490,14 +685,15 @@ export class PSProductionComponent extends BaseAngularComponent {
490
685
  + bindings.filter((b) => b.Mode === 'Scheduled').length;
491
686
  const lastScoredAt = bindings.reduce((acc, b) => (b.LastScoredAt && (!acc || b.LastScoredAt.getTime() > acc.getTime()) ? b.LastScoredAt : acc), null);
492
687
  const lastRowCount = bindings.find((b) => b.LastRowCount != null)?.LastRowCount ?? null;
493
- const auc = primaryAuc(m);
688
+ const score = primaryModelScore(m);
689
+ const holdoutMetric = score != null ? formatMetricValue(score.key, score.value) : '—';
494
690
  return {
495
691
  modelId: m.ID,
496
692
  label: this.engine.ModelDisplayName(m),
497
693
  algorithm: this.engine.AlgorithmName(m.AlgorithmID),
498
694
  problemType: m.ProblemType ?? '—',
499
695
  version: m.Version,
500
- holdoutMetric: auc != null ? auc.toFixed(3) : '—',
696
+ holdoutMetric,
501
697
  deployState: bindings.length > 0 ? 'bound' : scheduledCount > 0 ? 'scheduled' : 'idle',
502
698
  bindingCount: bindings.length,
503
699
  processCount: processes.length,
@@ -531,6 +727,21 @@ export class PSProductionComponent extends BaseAngularComponent {
531
727
  }
532
728
  return '—';
533
729
  }
730
+ get selectedTargetEntityName() {
731
+ if (this.selectedBindings.length > 0 && this.selectedBindings[0].targetEntity !== '—') {
732
+ return this.selectedBindings[0].targetEntity;
733
+ }
734
+ const model = this.engine?.PublishedModels?.find((m) => UUIDsEqual(m.ID, this.selected.modelId));
735
+ if (model?.PipelineID) {
736
+ const pipeline = this.engine?.Pipelines?.find((p) => UUIDsEqual(p.ID, model.PipelineID));
737
+ if (pipeline?.TargetEntityID) {
738
+ const ent = this.ProviderToUse.Entities.find((e) => UUIDsEqual(e.ID, pipeline.TargetEntityID));
739
+ if (ent)
740
+ return ent.Name;
741
+ }
742
+ }
743
+ return null;
744
+ }
534
745
  // ---- selection + on-demand run history ----
535
746
  get selected() {
536
747
  return (this.models.find((m) => UUIDsEqual(m.modelId, this.selectedModelId)) ??
@@ -657,18 +868,18 @@ export class PSProductionComponent extends BaseAngularComponent {
657
868
  return 'gray';
658
869
  }
659
870
  static ɵfac = /*@__PURE__*/ (() => { let ɵPSProductionComponent_BaseFactory; return function PSProductionComponent_Factory(__ngFactoryType__) { return (ɵPSProductionComponent_BaseFactory || (ɵPSProductionComponent_BaseFactory = i0.ɵɵgetInheritedFactory(PSProductionComponent)))(__ngFactoryType__ || PSProductionComponent); }; })();
660
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PSProductionComponent, selectors: [["ps-production"]], inputs: { engine: "engine" }, features: [i0.ɵɵInheritDefinitionFeature], decls: 3, vars: 1, consts: [["data-testid", "ps-production-panel", 1, "ps-panel", "ps-production"], ["data-testid", "ps-production-empty", 1, "ps-empty"], [1, "ps-empty-ico"], [1, "fa-solid", "fa-satellite-dish"], ["data-testid", "ps-production-kpis", 1, "prod-kpis"], [1, "ps-kpi"], [1, "label"], [1, "val"], [1, "prod-layout"], ["data-testid", "ps-production-list", 1, "ps-card", "mlist"], [1, "ps-card-head"], [1, "ps-badge", "gray"], [1, "ps-card-body", 2, "padding", "8px"], ["data-testid", "ps-production-row", 1, "mrow", 3, "sel"], ["data-testid", "ps-production-detail", 1, "ps-col", "detail"], [1, "ps-card"], [1, "ps-card-body", "dh"], [1, "big-ico"], [2, "flex", "1"], ["data-testid", "ps-production-detail-name"], [1, "ps-tag", "ps-mono"], [1, "ps-muted", "ps-small", "sub"], [1, "ps-badge"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-production-operate", 3, "click"], [1, "fa-solid", "fa-rocket"], [1, "ps-muted", "ps-small"], [1, "ps-card-body"], [1, "ps-callout", "info"], ["text", "Loading runs...", "size", "small"], [1, "ps-small", "ps-muted"], [1, "runs-table"], [3, "Close", "Visible", "modelId", "modelLabel", "engine", "provider", "currentUser"], ["data-testid", "ps-production-row", 1, "mrow", 3, "click"], [1, "ico"], [2, "flex", "1", "min-width", "0"], [1, "nm"], [1, "ln2", "ps-muted", "ps-small"], [1, "meta"], [1, "ps-mono", "ps-small", "ps-muted"], [1, "fa-solid", "fa-circle-info"], [1, "ps-small"], [1, "ps-section-title"], [1, "bind-row"], [1, "fa-solid", "fa-arrow-right-to-bracket", "ps-muted"], [1, "ps-divider"], [1, "ps-row", 2, "justify-content", "space-between"], ["data-testid", "ps-production-run-back", 1, "run-back", 3, "click"], [1, "fa-solid", "fa-arrow-left"], ["text", "Loading predictions...", "size", "small"], ["data-testid", "ps-production-run-detail", 1, "runs-table"], [1, "ps-mono", "ps-small"], [1, "ps-mono"], [1, "ps-muted"], ["data-testid", "ps-production-run", 1, "run-row"], ["data-testid", "ps-production-run", 1, "run-row", 3, "click"], [1, "run-right"], [1, "ps-tag", "amber"], [1, "fa-solid", "fa-chevron-right", "ps-muted"]], template: function PSProductionComponent_Template(rf, ctx) { if (rf & 1) {
871
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PSProductionComponent, selectors: [["ps-production"]], inputs: { engine: "engine" }, features: [i0.ɵɵInheritDefinitionFeature], decls: 3, vars: 1, consts: [["data-testid", "ps-production-panel", 1, "ps-panel", "ps-production"], ["data-testid", "ps-production-empty", 1, "ps-empty"], [1, "ps-empty-ico"], [1, "fa-solid", "fa-satellite-dish"], ["data-testid", "ps-production-kpis", 1, "prod-kpis"], [1, "prod-layout"], ["data-testid", "ps-production-collapsed-rail", 1, "prod-collapsed-strip"], ["direction", "horizontal", "unit", "percent", 1, "prod-splitter", 3, "dragEnd", "gutterSize"], [3, "size", "minSize", "maxSize"], [3, "size", "minSize"], ["data-testid", "ps-production-detail", 1, "ps-col", "detail"], [1, "ps-card", "prod-header-card"], [1, "ps-card-body", "dh"], [1, "ps-card-body", "dh-compact"], [1, "ps-card", "prod-deployment-card"], [1, "ps-card-head", "clickable", 3, "click"], [2, "display", "flex", "align-items", "center", "gap", "8px"], [1, "fa-solid", "fa-network-wired", 2, "color", "var(--mj-brand-primary)"], [1, "ps-muted", "ps-small"], [1, "ps-badge", "gray", "ps-small"], [2, "display", "flex", "align-items", "center", "gap", "6px"], ["type", "button", 1, "ps-card-collapse-btn", 3, "click", "title"], [1, "fa-solid"], [1, "ps-card-body"], [1, "ps-card", "run-card"], [1, "ps-card-head"], [1, "fa-solid", "fa-clock-rotate-left", 2, "color", "var(--mj-status-info)"], [1, "ps-card-body", "run-card-body"], ["text", "Loading runs...", "size", "small"], [1, "ps-small", "ps-muted"], [1, "runs-table"], [3, "Close", "Visible", "modelId", "modelLabel", "engine", "provider", "currentUser"], [1, "ps-kpi"], [1, "label"], [1, "val"], ["type", "button", "aria-label", "Expand models list", "title", "Expand list", 1, "prod-rail-collapse", 3, "click"], [1, "fa-solid", "fa-chevron-right"], [1, "prod-collapsed-strip-label"], ["data-testid", "ps-production-list", 1, "ps-card", "mlist"], [2, "display", "flex", "gap", "6px", "align-items", "center"], [1, "ps-badge", "gray"], ["type", "button", "title", "Collapse list", "aria-label", "Collapse list", 1, "prod-collapse-btn", 3, "click"], [1, "fa-solid", "fa-chevron-left"], [1, "ps-card-body", 2, "padding", "8px"], ["data-testid", "ps-production-row", 1, "mrow", 3, "sel"], ["data-testid", "ps-production-row", 1, "mrow", 3, "click"], [1, "ico"], [2, "flex", "1", "min-width", "0"], [1, "nm"], [1, "ln2", "ps-muted", "ps-small"], [1, "meta"], [1, "ps-badge"], [1, "ps-mono", "ps-small", "ps-muted"], [1, "big-ico"], ["data-testid", "ps-production-detail-name"], [1, "ps-tag", "ps-mono"], [1, "ps-muted", "ps-small", "sub"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-production-operate", 3, "click"], [1, "fa-solid", "fa-rocket"], ["type", "button", "title", "Collapse model header", "aria-label", "Collapse model header", 1, "ps-card-collapse-btn", 3, "click"], [1, "fa-solid", "fa-chevron-up"], [1, "ps-card-body", "dh-compact", 3, "click"], [1, "mini-ico"], [1, "compact-info"], [1, "ps-tag", "ps-mono", "ps-small"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-production-operate-compact", 3, "click"], ["type", "button", "title", "Expand model header", "aria-label", "Expand model header", 1, "ps-card-collapse-btn", 3, "click"], [1, "fa-solid", "fa-chevron-down"], [1, "ps-callout", "info"], [1, "fa-solid", "fa-circle-info"], [1, "ps-small"], [1, "ps-section-title"], [1, "bind-row"], [1, "fa-solid", "fa-arrow-right-to-bracket", "ps-muted"], [2, "flex", "1"], [1, "ps-divider"], [1, "ps-row", 2, "justify-content", "space-between"], ["type", "button", 1, "ps-focus-toggle-btn", 3, "click", "title"], [1, "run-drill-nav"], ["data-testid", "ps-production-run-back", 1, "run-back", 3, "click"], [1, "fa-solid", "fa-arrow-left"], ["data-testid", "ps-production-run-grid", 3, "modelId", "runId", "entityName", "problemType", "title", "height"], ["data-testid", "ps-production-run", 1, "run-row"], ["data-testid", "ps-production-run", 1, "run-row", 3, "click"], [1, "ps-mono"], [1, "ps-muted"], [1, "run-right"], [1, "ps-tag", "amber"], [1, "fa-solid", "fa-chevron-right", "ps-muted"]], template: function PSProductionComponent_Template(rf, ctx) { if (rf & 1) {
661
872
  i0.ɵɵelementStart(0, "div", 0);
662
- i0.ɵɵconditionalCreate(1, PSProductionComponent_Conditional_1_Template, 7, 0, "div", 1)(2, PSProductionComponent_Conditional_2_Template, 69, 29);
873
+ i0.ɵɵconditionalCreate(1, PSProductionComponent_Conditional_1_Template, 7, 0, "div", 1)(2, PSProductionComponent_Conditional_2_Template, 38, 39);
663
874
  i0.ɵɵelementEnd();
664
875
  } if (rf & 2) {
665
876
  i0.ɵɵadvance();
666
877
  i0.ɵɵconditional(ctx.models.length === 0 ? 1 : 2);
667
- } }, dependencies: [CommonModule, SharedGenericModule, i1.LoadingComponent, MJButtonDirective, PSOperateDialogComponent, i2.DatePipe], 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", "/* Models in Production \u2014 model-centric control tower (master/detail). All colors via --mj-* tokens. */\n\n.ps-production {\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 4px;\n}\n\n.prod-kpis {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 14px;\n}\n\n.prod-layout {\n display: grid;\n grid-template-columns: minmax(280px, 340px) minmax(0, 1fr);\n gap: 16px;\n align-items: start;\n}\n@media (max-width: 900px) {\n .prod-layout { grid-template-columns: 1fr; }\n}\n\n/* master list */\n.prod-layout .mlist .mrow {\n display: flex;\n align-items: center;\n gap: 11px;\n padding: 10px 10px;\n border-radius: 9px;\n cursor: pointer;\n}\n.prod-layout .mlist .mrow:hover { background: var(--mj-bg-surface-hover); }\n.prod-layout .mlist .mrow.sel { background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface)); }\n.prod-layout .mlist .mrow .ico {\n width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 13px; flex: 0 0 auto;\n}\n.prod-layout .mlist .mrow .nm { font-weight: 640; font-size: 14px; color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.prod-layout .mlist .mrow .ln2 { margin-top: 2px; }\n.prod-layout .mlist .mrow .meta { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; flex: 0 0 auto; }\n\n/* deploy-state icon colors (master + detail) */\n.st-bound { background: var(--mj-status-success); }\n.st-scheduled { background: var(--mj-status-info); }\n.st-idle { background: var(--mj-text-disabled); }\n\n/* detail */\n.prod-layout .detail { display: flex; flex-direction: column; gap: 14px; }\n.prod-layout .detail .dh { display: flex; align-items: center; gap: 13px; }\n.prod-layout .detail .dh h2 { margin: 0; font-size: 17px; font-weight: 680; color: var(--mj-text-primary); }\n.prod-layout .detail .dh .sub { margin-top: 3px; }\n.prod-layout .detail .big-ico {\n width: 40px; height: 40px; border-radius: 11px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 16px; flex: 0 0 auto;\n}\n\n/* binding rows */\n.bind-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--mj-border-subtle); }\n.bind-row:last-child { border-bottom: none; }\n\n/* run history table */\n.runs-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.runs-table th {\n text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 6px 8px; border-bottom: 1px solid var(--mj-border-default);\n}\n.runs-table td { padding: 7px 8px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-secondary); }\n.runs-table tr:last-child td { border-bottom: none; }\n.runs-table tr.run-row { cursor: pointer; }\n.runs-table tr.run-row:hover td { background: var(--mj-bg-surface-hover); }\n.runs-table .run-right { text-align: right; white-space: nowrap; }\n.runs-table .run-right i { margin-left: 8px; opacity: .5; }\n\n.run-back {\n display: inline-flex; align-items: center; gap: 6px; cursor: pointer;\n background: none; border: none; color: var(--mj-text-link); font-size: 13px; padding: 0 0 10px;\n}\n\n/* empty state CTA spacing (shared .ps-empty) */\n.ps-production .ps-empty p { max-width: 520px; }\n"], encapsulation: 2 });
878
+ } }, dependencies: [CommonModule, AngularSplitModule, i1.SplitComponent, i1.SplitAreaComponent, SharedGenericModule, i2.LoadingComponent, MJButtonDirective, PSOperateDialogComponent, PSPredictionsGridComponent, i3.DatePipe], 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", "/* Models in Production \u2014 model-centric control tower (master/detail). All colors via --mj-* tokens. */\n\n.ps-production {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n width: 100%;\n overflow: hidden;\n gap: 16px;\n padding: 4px;\n}\n\n.prod-kpis {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 14px;\n flex: 0 0 auto;\n}\n\n.prod-layout {\n display: flex;\n flex-direction: row;\n flex: 1;\n min-height: 0;\n height: 100%;\n overflow: hidden;\n position: relative;\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-lg);\n border: 1px solid var(--mj-border-default);\n}\n\n.prod-splitter {\n width: 100%;\n height: 100%;\n background: var(--mj-bg-page);\n}\n\n.prod-collapsed-strip {\n width: 44px;\n flex: 0 0 44px;\n background: var(--mj-bg-surface);\n border-right: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 10px 0;\n z-index: 10;\n}\n\n.prod-rail-collapse {\n background: transparent;\n border: 1px solid var(--mj-border-default);\n color: var(--mj-text-secondary);\n border-radius: var(--mj-radius-sm);\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.prod-rail-collapse:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.prod-collapsed-strip-label {\n color: var(--mj-text-muted);\n font-size: 16px;\n margin-top: 8px;\n}\n\n.prod-collapse-btn {\n background: transparent;\n border: 1px solid transparent;\n color: var(--mj-text-muted);\n border-radius: var(--mj-radius-sm);\n width: 26px;\n height: 26px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.prod-collapse-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-default);\n}\n\n/* master list \u2014 own scroll column */\n.prod-layout .mlist {\n min-height: 0;\n overflow-y: auto;\n align-self: stretch;\n height: 100%;\n border-radius: 0;\n border: none;\n border-right: 1px solid var(--mj-border-default);\n}\n\n.prod-layout .mlist .mrow {\n display: flex;\n align-items: center;\n gap: 11px;\n padding: 10px 10px;\n border-radius: 9px;\n cursor: pointer;\n}\n.prod-layout .mlist .mrow:hover { background: var(--mj-bg-surface-hover); }\n.prod-layout .mlist .mrow.sel { background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface)); }\n.prod-layout .mlist .mrow .ico {\n width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 13px; flex: 0 0 auto;\n}\n.prod-layout .mlist .mrow .nm { font-weight: 640; font-size: 14px; color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.prod-layout .mlist .mrow .ln2 { margin-top: 2px; }\n.prod-layout .mlist .mrow .meta { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; flex: 0 0 auto; }\n\n/* deploy-state icon colors (master + detail) */\n.st-bound { background: var(--mj-status-success); }\n.st-scheduled { background: var(--mj-status-info); }\n.st-idle { background: var(--mj-text-disabled); }\n\n/* detail \u2014 own scroll column */\n.prod-layout .detail {\n min-height: 0;\n overflow-y: auto;\n align-self: stretch;\n height: 100%;\n padding: 14px;\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n.prod-layout .detail .dh { display: flex; align-items: center; gap: 13px; }\n.prod-layout .detail .dh h2 { margin: 0; font-size: 17px; font-weight: 680; color: var(--mj-text-primary); }\n.prod-layout .detail .dh .sub { margin-top: 3px; }\n.prod-layout .detail .big-ico {\n width: 40px; height: 40px; border-radius: 11px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 16px; flex: 0 0 auto;\n}\n\n/* binding rows */\n.bind-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--mj-border-subtle); }\n.bind-row:last-child { border-bottom: none; }\n\n/* run history table */\n.runs-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.runs-table th {\n text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 6px 8px; border-bottom: 1px solid var(--mj-border-default);\n}\n.runs-table td { padding: 7px 8px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-secondary); }\n.runs-table tr:last-child td { border-bottom: none; }\n.runs-table tr.run-row { cursor: pointer; }\n.runs-table tr.run-row:hover td { background: var(--mj-bg-surface-hover); }\n.runs-table .run-right { text-align: right; white-space: nowrap; }\n.runs-table .run-right i { margin-left: 8px; opacity: .5; }\n\n.run-back {\n display: inline-flex; align-items: center; gap: 6px; cursor: pointer;\n background: none; border: none; color: var(--mj-text-link); font-size: 13px; padding: 0 0 10px;\n}\n\n/* collapsible cards and focus mode */\n.prod-header-card.collapsed {\n padding: 0;\n overflow: visible;\n}\n\n.dh-compact {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 6px 14px;\n min-height: 40px;\n box-sizing: border-box;\n cursor: pointer;\n transition: background-color 0.15s ease;\n}\n\n.dh-compact:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.dh-compact .mini-ico {\n width: 24px;\n height: 24px;\n border-radius: 6px;\n display: grid;\n place-items: center;\n color: var(--mj-text-inverse);\n font-size: 11px;\n flex: 0 0 auto;\n}\n\n.dh-compact .compact-info {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 13px;\n line-height: 1.4;\n}\n\n.dh-compact .compact-info strong {\n color: var(--mj-text-primary);\n font-size: 13px;\n font-weight: 600;\n}\n\n.prod-deployment-card.collapsed {\n background: var(--mj-bg-surface-card);\n}\n\n.prod-deployment-card.collapsed .ps-card-head {\n min-height: 40px;\n padding: 6px 14px;\n box-sizing: border-box;\n}\n\n.prod-deployment-card .ps-card-head.clickable {\n cursor: pointer;\n user-select: none;\n transition: background-color 0.15s ease;\n}\n\n.prod-deployment-card .ps-card-head.clickable:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.prod-layout .detail.focused-run {\n gap: 8px;\n}\n\n.run-card.focus-fill {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.run-card.focus-fill .run-card-body {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.ps-card-collapse-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.15s ease;\n padding: 0;\n flex: 0 0 auto;\n}\n\n.ps-card-collapse-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-strong);\n}\n\n.ps-focus-toggle-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.ps-focus-toggle-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-strong);\n}\n\n.ps-focus-toggle-btn.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.run-drill-nav {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 12px;\n}\n\n/* empty state CTA spacing (shared .ps-empty) */\n.ps-production .ps-empty p { max-width: 520px; }\n\n"], encapsulation: 2 });
668
879
  }
669
880
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PSProductionComponent, [{
670
881
  type: Component,
671
- args: [{ standalone: true, selector: 'ps-production', imports: [CommonModule, SharedGenericModule, MJButtonDirective, PSOperateDialogComponent], encapsulation: ViewEncapsulation.None, template: `
882
+ args: [{ standalone: true, selector: 'ps-production', imports: [CommonModule, AngularSplitModule, SharedGenericModule, MJButtonDirective, PSOperateDialogComponent, PSPredictionsGridComponent], encapsulation: ViewEncapsulation.None, template: `
672
883
  <div class="ps-panel ps-production" data-testid="ps-production-panel">
673
884
  @if (models.length === 0) {
674
885
  <div class="ps-empty" data-testid="ps-production-empty">
@@ -681,137 +892,207 @@ export class PSProductionComponent extends BaseAngularComponent {
681
892
  </div>
682
893
  } @else {
683
894
  <!-- KPI strip -->
684
- <div class="prod-kpis" data-testid="ps-production-kpis">
685
- <div class="ps-kpi"><div class="label">Published</div><div class="val">{{ models.length }}</div></div>
686
- <div class="ps-kpi"><div class="label">Scheduled</div><div class="val">{{ scheduledModelCount }}</div></div>
687
- <div class="ps-kpi"><div class="label">Bound (write-back)</div><div class="val">{{ boundModelCount }}</div></div>
688
- <div class="ps-kpi"><div class="label">Idle</div><div class="val">{{ idleModelCount }}</div></div>
689
- </div>
690
-
691
- <div class="prod-layout">
692
- <!-- master list -->
693
- <div class="ps-card mlist" data-testid="ps-production-list">
694
- <div class="ps-card-head"><h3>Published models</h3><span class="ps-badge gray">{{ models.length }}</span></div>
695
- <div class="ps-card-body" style="padding:8px">
696
- @for (m of models; track m.modelId) {
697
- <div class="mrow" data-testid="ps-production-row" [class.sel]="m.modelId === selectedModelId" (click)="select(m.modelId)">
698
- <div class="ico" [class]="'st-' + m.deployState"><i [class]="deployIcon(m.deployState)"></i></div>
699
- <div style="flex:1;min-width:0">
700
- <div class="nm">{{ m.label }}</div>
701
- <div class="ln2 ps-muted ps-small">v{{ m.version }} · {{ m.algorithm }}</div>
702
- </div>
703
- <div class="meta">
704
- <span class="ps-badge" [class]="deployBadge(m.deployState)">{{ deployLabel(m.deployState) }}</span>
705
- <div class="ps-mono ps-small ps-muted">{{ m.holdoutMetric }}</div>
706
- </div>
707
- </div>
708
- }
709
- </div>
895
+ @if (!isRunFocused) {
896
+ <div class="prod-kpis" data-testid="ps-production-kpis">
897
+ <div class="ps-kpi"><div class="label">Published</div><div class="val">{{ models.length }}</div></div>
898
+ <div class="ps-kpi"><div class="label">Scheduled</div><div class="val">{{ scheduledModelCount }}</div></div>
899
+ <div class="ps-kpi"><div class="label">Bound (write-back)</div><div class="val">{{ boundModelCount }}</div></div>
900
+ <div class="ps-kpi"><div class="label">Idle</div><div class="val">{{ idleModelCount }}</div></div>
710
901
  </div>
902
+ }
711
903
 
712
- <!-- detail -->
713
- <div class="ps-col detail" data-testid="ps-production-detail">
714
- <div class="ps-card">
715
- <div class="ps-card-body dh">
716
- <div class="big-ico" [class]="'st-' + selected.deployState"><i [class]="deployIcon(selected.deployState)"></i></div>
717
- <div style="flex:1">
718
- <h2 data-testid="ps-production-detail-name">{{ selected.label }} <span class="ps-tag ps-mono">v{{ selected.version }}</span></h2>
719
- <div class="ps-muted ps-small sub">{{ selected.algorithm }} · {{ selected.problemType }} · holdout {{ selected.holdoutMetric }}</div>
720
- </div>
721
- <span class="ps-badge" [class]="deployBadge(selected.deployState)">{{ deployLabel(selected.deployState) }}</span>
722
- <button mjButton variant="primary" size="sm" data-testid="ps-production-operate" (click)="operateOpen = true">
723
- <i class="fa-solid fa-rocket"></i> Operate
724
- </button>
725
- </div>
904
+ <div class="prod-layout" [class.list-collapsed]="isListCollapsed">
905
+ @if (isListCollapsed) {
906
+ <div class="prod-collapsed-strip" data-testid="ps-production-collapsed-rail">
907
+ <button class="prod-rail-collapse" type="button" (click)="toggleList()" aria-label="Expand models list" title="Expand list">
908
+ <i class="fa-solid fa-chevron-right"></i>
909
+ </button>
910
+ <div class="prod-collapsed-strip-label"><i class="fa-solid fa-satellite-dish"></i></div>
726
911
  </div>
912
+ }
727
913
 
728
- <!-- deployment status -->
729
- <div class="ps-card">
730
- <div class="ps-card-head"><h3>Deployment</h3>
731
- <span class="ps-muted ps-small">{{ selected.processCount }} process{{ selected.processCount === 1 ? '' : 'es' }} · {{ selected.bindingCount }} binding{{ selected.bindingCount === 1 ? '' : 's' }}</span>
732
- </div>
733
- <div class="ps-card-body">
734
- @if (selected.deployState === 'idle') {
735
- <div class="ps-callout info">
736
- <i class="fa-solid fa-circle-info"></i>
737
- <div class="ps-small">This model is published but <strong>not operating</strong> yet — nothing scores with it. Click <strong>Operate</strong> above to run it now, schedule it to run regularly, or write predictions back to a column.</div>
914
+ <as-split direction="horizontal" class="prod-splitter" unit="percent" [gutterSize]="6" (dragEnd)="onSplitDragEnd($event.sizes)">
915
+ @if (!isListCollapsed) {
916
+ <as-split-area [size]="listSizePct" [minSize]="18" [maxSize]="50">
917
+ <!-- master list -->
918
+ <div class="ps-card mlist" data-testid="ps-production-list">
919
+ <div class="ps-card-head">
920
+ <h3>Published models</h3>
921
+ <div style="display:flex;gap:6px;align-items:center">
922
+ <span class="ps-badge gray">{{ models.length }}</span>
923
+ <button class="prod-collapse-btn" type="button" (click)="toggleList()" title="Collapse list" aria-label="Collapse list">
924
+ <i class="fa-solid fa-chevron-left"></i>
925
+ </button>
926
+ </div>
738
927
  </div>
739
- } @else {
740
- @if (selectedBindings.length > 0) {
741
- <div class="ps-section-title">Scoring bindings</div>
742
- @for (b of selectedBindings; track b.id) {
743
- <div class="bind-row">
744
- <i class="fa-solid fa-arrow-right-to-bracket ps-muted"></i>
745
- <div style="flex:1">
746
- <div class="ps-small"><strong>{{ b.targetEntity }}</strong>.{{ b.targetColumn }}</div>
747
- <div class="ps-muted ps-small">{{ b.mode }}{{ b.schedulePhrase ? ' · ' + b.schedulePhrase : '' }}</div>
928
+ <div class="ps-card-body" style="padding:8px">
929
+ @for (m of models; track m.modelId) {
930
+ <div class="mrow" data-testid="ps-production-row" [class.sel]="m.modelId === selectedModelId" (click)="select(m.modelId)">
931
+ <div class="ico" [class]="'st-' + m.deployState"><i [class]="deployIcon(m.deployState)"></i></div>
932
+ <div style="flex:1;min-width:0">
933
+ <div class="nm">{{ m.label }}</div>
934
+ <div class="ln2 ps-muted ps-small">v{{ m.version }} · {{ m.algorithm }}</div>
935
+ </div>
936
+ <div class="meta">
937
+ <span class="ps-badge" [class]="deployBadge(m.deployState)">{{ deployLabel(m.deployState) }}</span>
938
+ <div class="ps-mono ps-small ps-muted">{{ m.holdoutMetric }}</div>
748
939
  </div>
749
- <span class="ps-badge" [class]="modeBadge(b.mode)">{{ b.mode }}</span>
750
940
  </div>
751
941
  }
942
+ </div>
943
+ </div>
944
+ </as-split-area>
945
+ }
946
+
947
+ <as-split-area [size]="isListCollapsed ? 100 : detailSizePct" [minSize]="50">
948
+ <!-- detail -->
949
+ <div class="ps-col detail" [class.focused-run]="selectedRunId && isRunFocused" data-testid="ps-production-detail">
950
+ <!-- 1. MODEL HEADER CARD -->
951
+ <div class="ps-card prod-header-card" [class.collapsed]="isHeaderCollapsed">
952
+ @if (!isHeaderCollapsed) {
953
+ <div class="ps-card-body dh">
954
+ <div class="big-ico" [class]="'st-' + selected.deployState"><i [class]="deployIcon(selected.deployState)"></i></div>
955
+ <div style="flex:1;min-width:0">
956
+ <h2 data-testid="ps-production-detail-name">{{ selected.label }} <span class="ps-tag ps-mono">v{{ selected.version }}</span></h2>
957
+ <div class="ps-muted ps-small sub">{{ selected.algorithm }} · {{ selected.problemType }} · holdout {{ selected.holdoutMetric }}</div>
958
+ </div>
959
+ <span class="ps-badge" [class]="deployBadge(selected.deployState)">{{ deployLabel(selected.deployState) }}</span>
960
+ <button mjButton variant="primary" size="sm" data-testid="ps-production-operate" (click)="operateOpen = true">
961
+ <i class="fa-solid fa-rocket"></i> Operate
962
+ </button>
963
+ <button class="ps-card-collapse-btn" type="button" (click)="toggleHeaderCollapse()" title="Collapse model header" aria-label="Collapse model header">
964
+ <i class="fa-solid fa-chevron-up"></i>
965
+ </button>
966
+ </div>
752
967
  } @else {
753
- <div class="ps-small ps-muted">Scheduled to run, writing generic output to the process run history (no write-back column).</div>
754
- }
755
- @if (selected.lastScoredAt) {
756
- <div class="ps-divider"></div>
757
- <div class="ps-row" style="justify-content:space-between"><span class="ps-muted ps-small">Last scored</span><span class="ps-small">{{ selected.lastScoredAt | date:'medium' }}{{ selected.lastRowCount != null ? ' · ' + selected.lastRowCount + ' rows' : '' }}</span></div>
968
+ <div class="ps-card-body dh-compact" (click)="toggleHeaderCollapse()">
969
+ <div class="mini-ico" [class]="'st-' + selected.deployState"><i [class]="deployIcon(selected.deployState)"></i></div>
970
+ <div class="compact-info">
971
+ <strong>{{ selected.label }}</strong>
972
+ <span class="ps-tag ps-mono ps-small">v{{ selected.version }}</span>
973
+ <span class="ps-muted ps-small">· {{ selected.algorithm }}</span>
974
+ </div>
975
+ <span class="ps-badge" [class]="deployBadge(selected.deployState)">{{ deployLabel(selected.deployState) }}</span>
976
+ <button mjButton variant="primary" size="sm" data-testid="ps-production-operate-compact" (click)="$event.stopPropagation(); operateOpen = true">
977
+ <i class="fa-solid fa-rocket"></i> Operate
978
+ </button>
979
+ <button class="ps-card-collapse-btn" type="button" (click)="$event.stopPropagation(); toggleHeaderCollapse()" title="Expand model header" aria-label="Expand model header">
980
+ <i class="fa-solid fa-chevron-down"></i>
981
+ </button>
982
+ </div>
758
983
  }
759
- }
760
- </div>
761
- </div>
984
+ </div>
762
985
 
763
- <!-- past runs (on demand) -->
764
- <div class="ps-card">
765
- <div class="ps-card-head"><h3>Run history</h3><span class="ps-muted ps-small">last 90 days · newest first</span></div>
766
- <div class="ps-card-body">
767
- @if (selectedRunId) {
768
- <!-- run drill-in: per-record predictions from Process Run Details -->
769
- <button class="run-back" data-testid="ps-production-run-back" (click)="closeRun()">
770
- <i class="fa-solid fa-arrow-left"></i> All runs
771
- </button>
772
- @if (loadingDetails) {
773
- <mj-loading text="Loading predictions..." size="small"></mj-loading>
774
- } @else if (runDetails.length === 0) {
775
- <div class="ps-small ps-muted">No per-record predictions recorded for this run.</div>
776
- } @else {
777
- <table class="runs-table" data-testid="ps-production-run-detail">
778
- <thead><tr><th>Record</th><th>Status</th><th>Prediction</th><th>Scored</th></tr></thead>
779
- <tbody>
780
- @for (d of runDetails; track d.recordId) {
781
- <tr>
782
- <td class="ps-mono ps-small">{{ d.recordId }}</td>
783
- <td><span class="ps-badge" [class]="runBadge(d.status)">{{ d.status }}</span></td>
784
- <td class="ps-mono">{{ d.score }}@if (d.class) { <span class="ps-muted"> · {{ d.class }}</span> }</td>
785
- <td class="ps-small ps-muted">{{ d.scoredAt ? (d.scoredAt | date:'short') : '—' }}</td>
786
- </tr>
986
+ <!-- 2. DEPLOYMENT STATUS CARD -->
987
+ <div class="ps-card prod-deployment-card" [class.collapsed]="isDeploymentCollapsed">
988
+ <div class="ps-card-head clickable" (click)="toggleDeploymentCollapse()">
989
+ <div style="display:flex;align-items:center;gap:8px">
990
+ <i class="fa-solid fa-network-wired" style="color:var(--mj-brand-primary)"></i>
991
+ <h3>Deployment</h3>
992
+ <span class="ps-muted ps-small">{{ selected.processCount }} process{{ selected.processCount === 1 ? '' : 'es' }} · {{ selected.bindingCount }} binding{{ selected.bindingCount === 1 ? '' : 's' }}</span>
993
+ @if (isDeploymentCollapsed && selectedBindings.length > 0) {
994
+ <span class="ps-badge gray ps-small">{{ selectedBindings[0].targetEntity }}.{{ selectedBindings[0].targetColumn }}</span>
995
+ }
996
+ </div>
997
+ <div style="display:flex;align-items:center;gap:6px">
998
+ <button class="ps-card-collapse-btn" type="button" (click)="$event.stopPropagation(); toggleDeploymentCollapse()" [title]="isDeploymentCollapsed ? 'Expand deployment' : 'Collapse deployment'" [attr.aria-label]="isDeploymentCollapsed ? 'Expand deployment' : 'Collapse deployment'">
999
+ <i class="fa-solid" [class.fa-chevron-up]="!isDeploymentCollapsed" [class.fa-chevron-down]="isDeploymentCollapsed"></i>
1000
+ </button>
1001
+ </div>
1002
+ </div>
1003
+ @if (!isDeploymentCollapsed) {
1004
+ <div class="ps-card-body">
1005
+ @if (selected.deployState === 'idle') {
1006
+ <div class="ps-callout info">
1007
+ <i class="fa-solid fa-circle-info"></i>
1008
+ <div class="ps-small">This model is published but <strong>not operating</strong> yet — nothing scores with it. Click <strong>Operate</strong> above to run it now, schedule it to run regularly, or write predictions back to a column.</div>
1009
+ </div>
1010
+ } @else {
1011
+ @if (selectedBindings.length > 0) {
1012
+ <div class="ps-section-title">Scoring bindings</div>
1013
+ @for (b of selectedBindings; track b.id) {
1014
+ <div class="bind-row">
1015
+ <i class="fa-solid fa-arrow-right-to-bracket ps-muted"></i>
1016
+ <div style="flex:1">
1017
+ <div class="ps-small"><strong>{{ b.targetEntity }}</strong>.{{ b.targetColumn }}</div>
1018
+ <div class="ps-muted ps-small">{{ b.mode }}{{ b.schedulePhrase ? ' · ' + b.schedulePhrase : '' }}</div>
1019
+ </div>
1020
+ <span class="ps-badge" [class]="modeBadge(b.mode)">{{ b.mode }}</span>
1021
+ </div>
1022
+ }
1023
+ } @else {
1024
+ <div class="ps-small ps-muted">Scheduled to run, writing generic output to the process run history (no write-back column).</div>
1025
+ }
1026
+ @if (selected.lastScoredAt) {
1027
+ <div class="ps-divider"></div>
1028
+ <div class="ps-row" style="justify-content:space-between"><span class="ps-muted ps-small">Last scored</span><span class="ps-small">{{ selected.lastScoredAt | date:'medium' }}{{ selected.lastRowCount != null ? ' · ' + selected.lastRowCount + ' rows' : '' }}</span></div>
787
1029
  }
788
- </tbody>
789
- </table>
790
- }
791
- } @else if (loadingRuns) {
792
- <mj-loading text="Loading runs..." size="small"></mj-loading>
793
- } @else if (runs.length === 0) {
794
- <div class="ps-small ps-muted">No runs recorded yet. Each time this model runs as a Record Process, its predictions are saved to the process run history — even without a scoring binding.</div>
795
- } @else {
796
- <table class="runs-table">
797
- <thead><tr><th>When</th><th>Status</th><th>Scored</th><th>Process</th><th></th></tr></thead>
798
- <tbody>
799
- @for (r of runs; track r.ID) {
800
- <tr data-testid="ps-production-run" class="run-row" (click)="openRun(r.ID)">
801
- <td>{{ (r.StartTime || r.CreatedAt) | date:'short' }}</td>
802
- <td><span class="ps-badge" [class]="runBadge(r.Status)">{{ r.Status }}</span></td>
803
- <td class="ps-mono">{{ r.SuccessCount }}<span class="ps-muted">/{{ r.TotalItemCount ?? 0 }}</span></td>
804
- <td class="ps-small ps-muted">{{ r.ProcessName || '—' }}</td>
805
- <td class="run-right">@if (r.DryRun) { <span class="ps-tag amber">dry run</span> }<i class="fa-solid fa-chevron-right ps-muted"></i></td>
806
- </tr>
807
1030
  }
808
- </tbody>
809
- </table>
810
- }
1031
+ </div>
1032
+ }
1033
+ </div>
1034
+
1035
+ <!-- 3. RUN HISTORY CARD -->
1036
+ <div class="ps-card run-card" [class.drilldown-active]="selectedRunId" [class.focus-fill]="selectedRunId && isRunFocused">
1037
+ <div class="ps-card-head">
1038
+ <div style="display:flex;align-items:center;gap:8px">
1039
+ <i class="fa-solid fa-clock-rotate-left" style="color:var(--mj-status-info)"></i>
1040
+ <h3>Run history</h3>
1041
+ <span class="ps-muted ps-small">last 90 days · newest first</span>
1042
+ </div>
1043
+ @if (selectedRunId) {
1044
+ <div style="display:flex;align-items:center;gap:8px">
1045
+ <button class="ps-focus-toggle-btn" type="button" (click)="toggleRunFocus()" [class.active]="isRunFocused" [title]="isRunFocused ? 'Exit focus (expand header & deployment)' : 'Focus mode (collapse header & deployment)'">
1046
+ <i class="fa-solid" [class.fa-compress]="isRunFocused" [class.fa-expand]="!isRunFocused"></i>
1047
+ <span>{{ isRunFocused ? 'Exit Focus' : 'Focus' }}</span>
1048
+ </button>
1049
+ </div>
1050
+ }
1051
+ </div>
1052
+ <div class="ps-card-body run-card-body">
1053
+ @if (selectedRunId) {
1054
+ <!-- run drill-in: unified predictions grid with virtual scrolling, name resolution, charts & history -->
1055
+ <div class="run-drill-nav">
1056
+ <button class="run-back" data-testid="ps-production-run-back" (click)="closeRun()">
1057
+ <i class="fa-solid fa-arrow-left"></i> All runs
1058
+ </button>
1059
+ <span class="ps-tag ps-mono ps-small">Run: {{ selectedRunId }}</span>
1060
+ </div>
1061
+ <ps-predictions-grid
1062
+ data-testid="ps-production-run-grid"
1063
+ [modelId]="selected.modelId"
1064
+ [runId]="selectedRunId"
1065
+ [entityName]="selectedTargetEntityName"
1066
+ [problemType]="selected.problemType"
1067
+ [title]="selected.label + ' — Run Predictions'"
1068
+ [height]="runGridHeight">
1069
+ </ps-predictions-grid>
1070
+ } @else if (loadingRuns) {
1071
+ <mj-loading text="Loading runs..." size="small"></mj-loading>
1072
+ } @else if (runs.length === 0) {
1073
+ <div class="ps-small ps-muted">No runs recorded yet. Each time this model runs as a Record Process, its predictions are saved to the process run history — even without a scoring binding.</div>
1074
+ } @else {
1075
+ <table class="runs-table">
1076
+ <thead><tr><th>When</th><th>Status</th><th>Scored</th><th>Process</th><th></th></tr></thead>
1077
+ <tbody>
1078
+ @for (r of runs; track r.ID) {
1079
+ <tr data-testid="ps-production-run" class="run-row" (click)="openRun(r.ID)">
1080
+ <td>{{ (r.StartTime || r.CreatedAt) | date:'short' }}</td>
1081
+ <td><span class="ps-badge" [class]="runBadge(r.Status)">{{ r.Status }}</span></td>
1082
+ <td class="ps-mono">{{ r.SuccessCount }}<span class="ps-muted">/{{ r.TotalItemCount ?? 0 }}</span></td>
1083
+ <td class="ps-small ps-muted">{{ r.ProcessName || '—' }}</td>
1084
+ <td class="run-right">@if (r.DryRun) { <span class="ps-tag amber">dry run</span> }<i class="fa-solid fa-chevron-right ps-muted"></i></td>
1085
+ </tr>
1086
+ }
1087
+ </tbody>
1088
+ </table>
1089
+ }
1090
+ </div>
1091
+ </div>
811
1092
  </div>
812
- </div>
813
- </div>
814
- </div>
1093
+ </as-split-area>
1094
+ </as-split>
1095
+ </div>
815
1096
 
816
1097
  <ps-operate-dialog
817
1098
  [Visible]="operateOpen"
@@ -824,9 +1105,9 @@ export class PSProductionComponent extends BaseAngularComponent {
824
1105
  </ps-operate-dialog>
825
1106
  }
826
1107
  </div>
827
- `, 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", "/* Models in Production \u2014 model-centric control tower (master/detail). All colors via --mj-* tokens. */\n\n.ps-production {\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 4px;\n}\n\n.prod-kpis {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 14px;\n}\n\n.prod-layout {\n display: grid;\n grid-template-columns: minmax(280px, 340px) minmax(0, 1fr);\n gap: 16px;\n align-items: start;\n}\n@media (max-width: 900px) {\n .prod-layout { grid-template-columns: 1fr; }\n}\n\n/* master list */\n.prod-layout .mlist .mrow {\n display: flex;\n align-items: center;\n gap: 11px;\n padding: 10px 10px;\n border-radius: 9px;\n cursor: pointer;\n}\n.prod-layout .mlist .mrow:hover { background: var(--mj-bg-surface-hover); }\n.prod-layout .mlist .mrow.sel { background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface)); }\n.prod-layout .mlist .mrow .ico {\n width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 13px; flex: 0 0 auto;\n}\n.prod-layout .mlist .mrow .nm { font-weight: 640; font-size: 14px; color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.prod-layout .mlist .mrow .ln2 { margin-top: 2px; }\n.prod-layout .mlist .mrow .meta { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; flex: 0 0 auto; }\n\n/* deploy-state icon colors (master + detail) */\n.st-bound { background: var(--mj-status-success); }\n.st-scheduled { background: var(--mj-status-info); }\n.st-idle { background: var(--mj-text-disabled); }\n\n/* detail */\n.prod-layout .detail { display: flex; flex-direction: column; gap: 14px; }\n.prod-layout .detail .dh { display: flex; align-items: center; gap: 13px; }\n.prod-layout .detail .dh h2 { margin: 0; font-size: 17px; font-weight: 680; color: var(--mj-text-primary); }\n.prod-layout .detail .dh .sub { margin-top: 3px; }\n.prod-layout .detail .big-ico {\n width: 40px; height: 40px; border-radius: 11px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 16px; flex: 0 0 auto;\n}\n\n/* binding rows */\n.bind-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--mj-border-subtle); }\n.bind-row:last-child { border-bottom: none; }\n\n/* run history table */\n.runs-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.runs-table th {\n text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 6px 8px; border-bottom: 1px solid var(--mj-border-default);\n}\n.runs-table td { padding: 7px 8px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-secondary); }\n.runs-table tr:last-child td { border-bottom: none; }\n.runs-table tr.run-row { cursor: pointer; }\n.runs-table tr.run-row:hover td { background: var(--mj-bg-surface-hover); }\n.runs-table .run-right { text-align: right; white-space: nowrap; }\n.runs-table .run-right i { margin-left: 8px; opacity: .5; }\n\n.run-back {\n display: inline-flex; align-items: center; gap: 6px; cursor: pointer;\n background: none; border: none; color: var(--mj-text-link); font-size: 13px; padding: 0 0 10px;\n}\n\n/* empty state CTA spacing (shared .ps-empty) */\n.ps-production .ps-empty p { max-width: 520px; }\n"] }]
1108
+ `, 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", "/* Models in Production \u2014 model-centric control tower (master/detail). All colors via --mj-* tokens. */\n\n.ps-production {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n width: 100%;\n overflow: hidden;\n gap: 16px;\n padding: 4px;\n}\n\n.prod-kpis {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 14px;\n flex: 0 0 auto;\n}\n\n.prod-layout {\n display: flex;\n flex-direction: row;\n flex: 1;\n min-height: 0;\n height: 100%;\n overflow: hidden;\n position: relative;\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-lg);\n border: 1px solid var(--mj-border-default);\n}\n\n.prod-splitter {\n width: 100%;\n height: 100%;\n background: var(--mj-bg-page);\n}\n\n.prod-collapsed-strip {\n width: 44px;\n flex: 0 0 44px;\n background: var(--mj-bg-surface);\n border-right: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 10px 0;\n z-index: 10;\n}\n\n.prod-rail-collapse {\n background: transparent;\n border: 1px solid var(--mj-border-default);\n color: var(--mj-text-secondary);\n border-radius: var(--mj-radius-sm);\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.prod-rail-collapse:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.prod-collapsed-strip-label {\n color: var(--mj-text-muted);\n font-size: 16px;\n margin-top: 8px;\n}\n\n.prod-collapse-btn {\n background: transparent;\n border: 1px solid transparent;\n color: var(--mj-text-muted);\n border-radius: var(--mj-radius-sm);\n width: 26px;\n height: 26px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.prod-collapse-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-default);\n}\n\n/* master list \u2014 own scroll column */\n.prod-layout .mlist {\n min-height: 0;\n overflow-y: auto;\n align-self: stretch;\n height: 100%;\n border-radius: 0;\n border: none;\n border-right: 1px solid var(--mj-border-default);\n}\n\n.prod-layout .mlist .mrow {\n display: flex;\n align-items: center;\n gap: 11px;\n padding: 10px 10px;\n border-radius: 9px;\n cursor: pointer;\n}\n.prod-layout .mlist .mrow:hover { background: var(--mj-bg-surface-hover); }\n.prod-layout .mlist .mrow.sel { background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface)); }\n.prod-layout .mlist .mrow .ico {\n width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 13px; flex: 0 0 auto;\n}\n.prod-layout .mlist .mrow .nm { font-weight: 640; font-size: 14px; color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.prod-layout .mlist .mrow .ln2 { margin-top: 2px; }\n.prod-layout .mlist .mrow .meta { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; flex: 0 0 auto; }\n\n/* deploy-state icon colors (master + detail) */\n.st-bound { background: var(--mj-status-success); }\n.st-scheduled { background: var(--mj-status-info); }\n.st-idle { background: var(--mj-text-disabled); }\n\n/* detail \u2014 own scroll column */\n.prod-layout .detail {\n min-height: 0;\n overflow-y: auto;\n align-self: stretch;\n height: 100%;\n padding: 14px;\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n.prod-layout .detail .dh { display: flex; align-items: center; gap: 13px; }\n.prod-layout .detail .dh h2 { margin: 0; font-size: 17px; font-weight: 680; color: var(--mj-text-primary); }\n.prod-layout .detail .dh .sub { margin-top: 3px; }\n.prod-layout .detail .big-ico {\n width: 40px; height: 40px; border-radius: 11px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 16px; flex: 0 0 auto;\n}\n\n/* binding rows */\n.bind-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--mj-border-subtle); }\n.bind-row:last-child { border-bottom: none; }\n\n/* run history table */\n.runs-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.runs-table th {\n text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 6px 8px; border-bottom: 1px solid var(--mj-border-default);\n}\n.runs-table td { padding: 7px 8px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-secondary); }\n.runs-table tr:last-child td { border-bottom: none; }\n.runs-table tr.run-row { cursor: pointer; }\n.runs-table tr.run-row:hover td { background: var(--mj-bg-surface-hover); }\n.runs-table .run-right { text-align: right; white-space: nowrap; }\n.runs-table .run-right i { margin-left: 8px; opacity: .5; }\n\n.run-back {\n display: inline-flex; align-items: center; gap: 6px; cursor: pointer;\n background: none; border: none; color: var(--mj-text-link); font-size: 13px; padding: 0 0 10px;\n}\n\n/* collapsible cards and focus mode */\n.prod-header-card.collapsed {\n padding: 0;\n overflow: visible;\n}\n\n.dh-compact {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 6px 14px;\n min-height: 40px;\n box-sizing: border-box;\n cursor: pointer;\n transition: background-color 0.15s ease;\n}\n\n.dh-compact:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.dh-compact .mini-ico {\n width: 24px;\n height: 24px;\n border-radius: 6px;\n display: grid;\n place-items: center;\n color: var(--mj-text-inverse);\n font-size: 11px;\n flex: 0 0 auto;\n}\n\n.dh-compact .compact-info {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 13px;\n line-height: 1.4;\n}\n\n.dh-compact .compact-info strong {\n color: var(--mj-text-primary);\n font-size: 13px;\n font-weight: 600;\n}\n\n.prod-deployment-card.collapsed {\n background: var(--mj-bg-surface-card);\n}\n\n.prod-deployment-card.collapsed .ps-card-head {\n min-height: 40px;\n padding: 6px 14px;\n box-sizing: border-box;\n}\n\n.prod-deployment-card .ps-card-head.clickable {\n cursor: pointer;\n user-select: none;\n transition: background-color 0.15s ease;\n}\n\n.prod-deployment-card .ps-card-head.clickable:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.prod-layout .detail.focused-run {\n gap: 8px;\n}\n\n.run-card.focus-fill {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.run-card.focus-fill .run-card-body {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.ps-card-collapse-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.15s ease;\n padding: 0;\n flex: 0 0 auto;\n}\n\n.ps-card-collapse-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-strong);\n}\n\n.ps-focus-toggle-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.ps-focus-toggle-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-strong);\n}\n\n.ps-focus-toggle-btn.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.run-drill-nav {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 12px;\n}\n\n/* empty state CTA spacing (shared .ps-empty) */\n.ps-production .ps-empty p { max-width: 520px; }\n\n"] }]
828
1109
  }], null, { engine: [{
829
1110
  type: Input
830
1111
  }] }); })();
831
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PSProductionComponent, { className: "PSProductionComponent", filePath: "src/PredictiveStudio/components/ps-production.component.ts", lineNumber: 232 }); })();
1112
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PSProductionComponent, { className: "PSProductionComponent", filePath: "src/PredictiveStudio/components/ps-production.component.ts", lineNumber: 304 }); })();
832
1113
  //# sourceMappingURL=ps-production.component.js.map