@memberjunction/ng-entity-viewer 3.1.1 → 3.3.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.
- package/dist/lib/aggregate-panel/aggregate-panel.component.d.ts +118 -0
- package/dist/lib/aggregate-panel/aggregate-panel.component.d.ts.map +1 -0
- package/dist/lib/aggregate-panel/aggregate-panel.component.js +366 -0
- package/dist/lib/aggregate-panel/aggregate-panel.component.js.map +1 -0
- package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.d.ts +196 -0
- package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.d.ts.map +1 -0
- package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.js +948 -0
- package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.js.map +1 -0
- package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts +134 -8
- package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts.map +1 -1
- package/dist/lib/entity-data-grid/entity-data-grid.component.js +769 -150
- package/dist/lib/entity-data-grid/entity-data-grid.component.js.map +1 -1
- package/dist/lib/entity-data-grid/models/grid-types.d.ts +15 -1
- package/dist/lib/entity-data-grid/models/grid-types.d.ts.map +1 -1
- package/dist/lib/entity-data-grid/models/grid-types.js.map +1 -1
- package/dist/lib/entity-viewer/entity-viewer.component.d.ts +12 -3
- package/dist/lib/entity-viewer/entity-viewer.component.d.ts.map +1 -1
- package/dist/lib/entity-viewer/entity-viewer.component.js +29 -4
- package/dist/lib/entity-viewer/entity-viewer.component.js.map +1 -1
- package/dist/lib/types.d.ts +9 -21
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/view-config-panel/view-config-panel.component.d.ts +71 -3
- package/dist/lib/view-config-panel/view-config-panel.component.d.ts.map +1 -1
- package/dist/lib/view-config-panel/view-config-panel.component.js +726 -320
- package/dist/lib/view-config-panel/view-config-panel.component.js.map +1 -1
- package/dist/module.d.ts +9 -7
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +14 -4
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +2 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +4 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +9 -9
|
@@ -17,19 +17,19 @@ import * as i4 from "@memberjunction/ng-shared-generic";
|
|
|
17
17
|
const _c0 = ["gridContainer"];
|
|
18
18
|
function EntityDataGridComponent_div_2_div_2_button_3_Template(rf, ctx) { if (rf & 1) {
|
|
19
19
|
const _r4 = i0.ɵɵgetCurrentView();
|
|
20
|
-
i0.ɵɵelementStart(0, "button",
|
|
20
|
+
i0.ɵɵelementStart(0, "button", 38);
|
|
21
21
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_div_2_button_3_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.FilterText = ""); });
|
|
22
|
-
i0.ɵɵelement(1, "i",
|
|
22
|
+
i0.ɵɵelement(1, "i", 39);
|
|
23
23
|
i0.ɵɵelementEnd();
|
|
24
24
|
} }
|
|
25
25
|
function EntityDataGridComponent_div_2_div_2_Template(rf, ctx) { if (rf & 1) {
|
|
26
26
|
const _r2 = i0.ɵɵgetCurrentView();
|
|
27
|
-
i0.ɵɵelementStart(0, "div",
|
|
28
|
-
i0.ɵɵelement(1, "i",
|
|
29
|
-
i0.ɵɵelementStart(2, "input",
|
|
27
|
+
i0.ɵɵelementStart(0, "div", 34);
|
|
28
|
+
i0.ɵɵelement(1, "i", 35);
|
|
29
|
+
i0.ɵɵelementStart(2, "input", 36);
|
|
30
30
|
i0.ɵɵlistener("input", function EntityDataGridComponent_div_2_div_2_Template_input_input_2_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.FilterText = $event.target.value); });
|
|
31
31
|
i0.ɵɵelementEnd();
|
|
32
|
-
i0.ɵɵtemplate(3, EntityDataGridComponent_div_2_div_2_button_3_Template, 2, 0, "button",
|
|
32
|
+
i0.ɵɵtemplate(3, EntityDataGridComponent_div_2_div_2_button_3_Template, 2, 0, "button", 37);
|
|
33
33
|
i0.ɵɵelementEnd();
|
|
34
34
|
} if (rf & 2) {
|
|
35
35
|
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
@@ -55,9 +55,9 @@ function EntityDataGridComponent_div_2_ng_container_3_button_1_span_2_Template(r
|
|
|
55
55
|
} }
|
|
56
56
|
function EntityDataGridComponent_div_2_ng_container_3_button_1_Template(rf, ctx) { if (rf & 1) {
|
|
57
57
|
const _r5 = i0.ɵɵgetCurrentView();
|
|
58
|
-
i0.ɵɵelementStart(0, "button",
|
|
58
|
+
i0.ɵɵelementStart(0, "button", 41);
|
|
59
59
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_ng_container_3_button_1_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r5); const button_r6 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onToolbarButtonClick(button_r6)); });
|
|
60
|
-
i0.ɵɵtemplate(1, EntityDataGridComponent_div_2_ng_container_3_button_1_i_1_Template, 1, 2, "i",
|
|
60
|
+
i0.ɵɵtemplate(1, EntityDataGridComponent_div_2_ng_container_3_button_1_i_1_Template, 1, 2, "i", 42)(2, EntityDataGridComponent_div_2_ng_container_3_button_1_span_2_Template, 2, 1, "span", 43);
|
|
61
61
|
i0.ɵɵelementEnd();
|
|
62
62
|
} if (rf & 2) {
|
|
63
63
|
const button_r6 = i0.ɵɵnextContext().$implicit;
|
|
@@ -71,7 +71,7 @@ function EntityDataGridComponent_div_2_ng_container_3_button_1_Template(rf, ctx)
|
|
|
71
71
|
} }
|
|
72
72
|
function EntityDataGridComponent_div_2_ng_container_3_Template(rf, ctx) { if (rf & 1) {
|
|
73
73
|
i0.ɵɵelementContainerStart(0);
|
|
74
|
-
i0.ɵɵtemplate(1, EntityDataGridComponent_div_2_ng_container_3_button_1_Template, 3, 6, "button",
|
|
74
|
+
i0.ɵɵtemplate(1, EntityDataGridComponent_div_2_ng_container_3_button_1_Template, 3, 6, "button", 40);
|
|
75
75
|
i0.ɵɵelementContainerEnd();
|
|
76
76
|
} if (rf & 2) {
|
|
77
77
|
const button_r6 = ctx.$implicit;
|
|
@@ -80,7 +80,7 @@ function EntityDataGridComponent_div_2_ng_container_3_Template(rf, ctx) { if (rf
|
|
|
80
80
|
i0.ɵɵproperty("ngIf", button_r6.position !== "right" && ctx_r2.isButtonVisible(button_r6));
|
|
81
81
|
} }
|
|
82
82
|
function EntityDataGridComponent_div_2_span_5_Template(rf, ctx) { if (rf & 1) {
|
|
83
|
-
i0.ɵɵelementStart(0, "span",
|
|
83
|
+
i0.ɵɵelementStart(0, "span", 44);
|
|
84
84
|
i0.ɵɵtext(1);
|
|
85
85
|
i0.ɵɵelementEnd();
|
|
86
86
|
} if (rf & 2) {
|
|
@@ -89,7 +89,7 @@ function EntityDataGridComponent_div_2_span_5_Template(rf, ctx) { if (rf & 1) {
|
|
|
89
89
|
i0.ɵɵtextInterpolate2(" ", ctx_r2.totalRowCount, " ", ctx_r2.totalRowCount === 1 ? "row" : "rows", " ");
|
|
90
90
|
} }
|
|
91
91
|
function EntityDataGridComponent_div_2_span_6_Template(rf, ctx) { if (rf & 1) {
|
|
92
|
-
i0.ɵɵelementStart(0, "span",
|
|
92
|
+
i0.ɵɵelementStart(0, "span", 45);
|
|
93
93
|
i0.ɵɵtext(1);
|
|
94
94
|
i0.ɵɵelementEnd();
|
|
95
95
|
} if (rf & 2) {
|
|
@@ -99,19 +99,19 @@ function EntityDataGridComponent_div_2_span_6_Template(rf, ctx) { if (rf & 1) {
|
|
|
99
99
|
} }
|
|
100
100
|
function EntityDataGridComponent_div_2_button_8_Template(rf, ctx) { if (rf & 1) {
|
|
101
101
|
const _r7 = i0.ɵɵgetCurrentView();
|
|
102
|
-
i0.ɵɵelementStart(0, "button",
|
|
102
|
+
i0.ɵɵelementStart(0, "button", 46);
|
|
103
103
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_button_8_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r7); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onAddClick()); });
|
|
104
|
-
i0.ɵɵelement(1, "i",
|
|
105
|
-
i0.ɵɵelementStart(2, "span",
|
|
104
|
+
i0.ɵɵelement(1, "i", 47);
|
|
105
|
+
i0.ɵɵelementStart(2, "span", 48);
|
|
106
106
|
i0.ɵɵtext(3, "New");
|
|
107
107
|
i0.ɵɵelementEnd()();
|
|
108
108
|
} }
|
|
109
109
|
function EntityDataGridComponent_div_2_button_9_Template(rf, ctx) { if (rf & 1) {
|
|
110
110
|
const _r8 = i0.ɵɵgetCurrentView();
|
|
111
|
-
i0.ɵɵelementStart(0, "button",
|
|
111
|
+
i0.ɵɵelementStart(0, "button", 49);
|
|
112
112
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_button_9_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r8); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onRefreshClick()); });
|
|
113
|
-
i0.ɵɵelement(1, "i",
|
|
114
|
-
i0.ɵɵelementStart(2, "span",
|
|
113
|
+
i0.ɵɵelement(1, "i", 50);
|
|
114
|
+
i0.ɵɵelementStart(2, "span", 48);
|
|
115
115
|
i0.ɵɵtext(3, "Refresh");
|
|
116
116
|
i0.ɵɵelementEnd()();
|
|
117
117
|
} if (rf & 2) {
|
|
@@ -122,28 +122,28 @@ function EntityDataGridComponent_div_2_button_9_Template(rf, ctx) { if (rf & 1)
|
|
|
122
122
|
} }
|
|
123
123
|
function EntityDataGridComponent_div_2_button_10_Template(rf, ctx) { if (rf & 1) {
|
|
124
124
|
const _r9 = i0.ɵɵgetCurrentView();
|
|
125
|
-
i0.ɵɵelementStart(0, "button",
|
|
125
|
+
i0.ɵɵelementStart(0, "button", 51);
|
|
126
126
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_button_10_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r9); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onExportClick()); });
|
|
127
|
-
i0.ɵɵelement(1, "i",
|
|
128
|
-
i0.ɵɵelementStart(2, "span",
|
|
127
|
+
i0.ɵɵelement(1, "i", 52);
|
|
128
|
+
i0.ɵɵelementStart(2, "span", 48);
|
|
129
129
|
i0.ɵɵtext(3, "Export");
|
|
130
130
|
i0.ɵɵelementEnd()();
|
|
131
131
|
} }
|
|
132
132
|
function EntityDataGridComponent_div_2_button_11_Template(rf, ctx) { if (rf & 1) {
|
|
133
133
|
const _r10 = i0.ɵɵgetCurrentView();
|
|
134
|
-
i0.ɵɵelementStart(0, "button",
|
|
134
|
+
i0.ɵɵelementStart(0, "button", 53);
|
|
135
135
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_button_11_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r10); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onDeleteClick()); });
|
|
136
|
-
i0.ɵɵelement(1, "i",
|
|
137
|
-
i0.ɵɵelementStart(2, "span",
|
|
136
|
+
i0.ɵɵelement(1, "i", 54);
|
|
137
|
+
i0.ɵɵelementStart(2, "span", 48);
|
|
138
138
|
i0.ɵɵtext(3, "Delete");
|
|
139
139
|
i0.ɵɵelementEnd()();
|
|
140
140
|
} }
|
|
141
141
|
function EntityDataGridComponent_div_2_button_12_Template(rf, ctx) { if (rf & 1) {
|
|
142
142
|
const _r11 = i0.ɵɵgetCurrentView();
|
|
143
|
-
i0.ɵɵelementStart(0, "button",
|
|
143
|
+
i0.ɵɵelementStart(0, "button", 55);
|
|
144
144
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_button_12_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r11); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onCompareClick()); });
|
|
145
|
-
i0.ɵɵelement(1, "i",
|
|
146
|
-
i0.ɵɵelementStart(2, "span",
|
|
145
|
+
i0.ɵɵelement(1, "i", 56);
|
|
146
|
+
i0.ɵɵelementStart(2, "span", 48);
|
|
147
147
|
i0.ɵɵtext(3, "Compare");
|
|
148
148
|
i0.ɵɵelementEnd()();
|
|
149
149
|
} if (rf & 2) {
|
|
@@ -152,10 +152,10 @@ function EntityDataGridComponent_div_2_button_12_Template(rf, ctx) { if (rf & 1)
|
|
|
152
152
|
} }
|
|
153
153
|
function EntityDataGridComponent_div_2_button_13_Template(rf, ctx) { if (rf & 1) {
|
|
154
154
|
const _r12 = i0.ɵɵgetCurrentView();
|
|
155
|
-
i0.ɵɵelementStart(0, "button",
|
|
155
|
+
i0.ɵɵelementStart(0, "button", 57);
|
|
156
156
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_button_13_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r12); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onMergeClick()); });
|
|
157
|
-
i0.ɵɵelement(1, "i",
|
|
158
|
-
i0.ɵɵelementStart(2, "span",
|
|
157
|
+
i0.ɵɵelement(1, "i", 58);
|
|
158
|
+
i0.ɵɵelementStart(2, "span", 48);
|
|
159
159
|
i0.ɵɵtext(3, "Merge");
|
|
160
160
|
i0.ɵɵelementEnd()();
|
|
161
161
|
} if (rf & 2) {
|
|
@@ -164,10 +164,10 @@ function EntityDataGridComponent_div_2_button_13_Template(rf, ctx) { if (rf & 1)
|
|
|
164
164
|
} }
|
|
165
165
|
function EntityDataGridComponent_div_2_button_14_Template(rf, ctx) { if (rf & 1) {
|
|
166
166
|
const _r13 = i0.ɵɵgetCurrentView();
|
|
167
|
-
i0.ɵɵelementStart(0, "button",
|
|
167
|
+
i0.ɵɵelementStart(0, "button", 59);
|
|
168
168
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_button_14_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onAddToListClick()); });
|
|
169
|
-
i0.ɵɵelement(1, "i",
|
|
170
|
-
i0.ɵɵelementStart(2, "span",
|
|
169
|
+
i0.ɵɵelement(1, "i", 60);
|
|
170
|
+
i0.ɵɵelementStart(2, "span", 48);
|
|
171
171
|
i0.ɵɵtext(3, "Add to List");
|
|
172
172
|
i0.ɵɵelementEnd()();
|
|
173
173
|
} if (rf & 2) {
|
|
@@ -176,10 +176,10 @@ function EntityDataGridComponent_div_2_button_14_Template(rf, ctx) { if (rf & 1)
|
|
|
176
176
|
} }
|
|
177
177
|
function EntityDataGridComponent_div_2_button_15_Template(rf, ctx) { if (rf & 1) {
|
|
178
178
|
const _r14 = i0.ɵɵgetCurrentView();
|
|
179
|
-
i0.ɵɵelementStart(0, "button",
|
|
179
|
+
i0.ɵɵelementStart(0, "button", 61);
|
|
180
180
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_button_15_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r14); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onDuplicateSearchClick()); });
|
|
181
|
-
i0.ɵɵelement(1, "i",
|
|
182
|
-
i0.ɵɵelementStart(2, "span",
|
|
181
|
+
i0.ɵɵelement(1, "i", 62);
|
|
182
|
+
i0.ɵɵelementStart(2, "span", 48);
|
|
183
183
|
i0.ɵɵtext(3, "Find Duplicates");
|
|
184
184
|
i0.ɵɵelementEnd()();
|
|
185
185
|
} if (rf & 2) {
|
|
@@ -188,10 +188,10 @@ function EntityDataGridComponent_div_2_button_15_Template(rf, ctx) { if (rf & 1)
|
|
|
188
188
|
} }
|
|
189
189
|
function EntityDataGridComponent_div_2_button_16_Template(rf, ctx) { if (rf & 1) {
|
|
190
190
|
const _r15 = i0.ɵɵgetCurrentView();
|
|
191
|
-
i0.ɵɵelementStart(0, "button",
|
|
191
|
+
i0.ɵɵelementStart(0, "button", 63);
|
|
192
192
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_button_16_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r15); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onCommunicationClick()); });
|
|
193
|
-
i0.ɵɵelement(1, "i",
|
|
194
|
-
i0.ɵɵelementStart(2, "span",
|
|
193
|
+
i0.ɵɵelement(1, "i", 64);
|
|
194
|
+
i0.ɵɵelementStart(2, "span", 48);
|
|
195
195
|
i0.ɵɵtext(3, "Send Message");
|
|
196
196
|
i0.ɵɵelementEnd()();
|
|
197
197
|
} if (rf & 2) {
|
|
@@ -200,16 +200,16 @@ function EntityDataGridComponent_div_2_button_16_Template(rf, ctx) { if (rf & 1)
|
|
|
200
200
|
} }
|
|
201
201
|
function EntityDataGridComponent_div_2_button_17_Template(rf, ctx) { if (rf & 1) {
|
|
202
202
|
const _r16 = i0.ɵɵgetCurrentView();
|
|
203
|
-
i0.ɵɵelementStart(0, "button",
|
|
203
|
+
i0.ɵɵelementStart(0, "button", 65);
|
|
204
204
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_button_17_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r16); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onAddClick()); });
|
|
205
|
-
i0.ɵɵelement(1, "i",
|
|
205
|
+
i0.ɵɵelement(1, "i", 47);
|
|
206
206
|
i0.ɵɵelementEnd();
|
|
207
207
|
} }
|
|
208
208
|
function EntityDataGridComponent_div_2_button_18_Template(rf, ctx) { if (rf & 1) {
|
|
209
209
|
const _r17 = i0.ɵɵgetCurrentView();
|
|
210
|
-
i0.ɵɵelementStart(0, "button",
|
|
210
|
+
i0.ɵɵelementStart(0, "button", 66);
|
|
211
211
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_button_18_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r17); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onRefreshClick()); });
|
|
212
|
-
i0.ɵɵelement(1, "i",
|
|
212
|
+
i0.ɵɵelement(1, "i", 67);
|
|
213
213
|
i0.ɵɵelementEnd();
|
|
214
214
|
} if (rf & 2) {
|
|
215
215
|
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
@@ -219,23 +219,23 @@ function EntityDataGridComponent_div_2_button_18_Template(rf, ctx) { if (rf & 1)
|
|
|
219
219
|
} }
|
|
220
220
|
function EntityDataGridComponent_div_2_button_19_Template(rf, ctx) { if (rf & 1) {
|
|
221
221
|
const _r18 = i0.ɵɵgetCurrentView();
|
|
222
|
-
i0.ɵɵelementStart(0, "button",
|
|
222
|
+
i0.ɵɵelementStart(0, "button", 68);
|
|
223
223
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_button_19_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r18); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onDeleteClick()); });
|
|
224
|
-
i0.ɵɵelement(1, "i",
|
|
224
|
+
i0.ɵɵelement(1, "i", 54);
|
|
225
225
|
i0.ɵɵelementEnd();
|
|
226
226
|
} }
|
|
227
227
|
function EntityDataGridComponent_div_2_button_20_Template(rf, ctx) { if (rf & 1) {
|
|
228
228
|
const _r19 = i0.ɵɵgetCurrentView();
|
|
229
|
-
i0.ɵɵelementStart(0, "button",
|
|
229
|
+
i0.ɵɵelementStart(0, "button", 69);
|
|
230
230
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_button_20_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r19); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onExportClick()); });
|
|
231
|
-
i0.ɵɵelement(1, "i",
|
|
231
|
+
i0.ɵɵelement(1, "i", 70);
|
|
232
232
|
i0.ɵɵelementEnd();
|
|
233
233
|
} }
|
|
234
234
|
function EntityDataGridComponent_div_2_button_21_Template(rf, ctx) { if (rf & 1) {
|
|
235
235
|
const _r20 = i0.ɵɵgetCurrentView();
|
|
236
|
-
i0.ɵɵelementStart(0, "button",
|
|
236
|
+
i0.ɵɵelementStart(0, "button", 71);
|
|
237
237
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_button_21_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r20); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onColumnChooserClick()); });
|
|
238
|
-
i0.ɵɵelement(1, "i",
|
|
238
|
+
i0.ɵɵelement(1, "i", 72);
|
|
239
239
|
i0.ɵɵelementEnd();
|
|
240
240
|
} }
|
|
241
241
|
function EntityDataGridComponent_div_2_ng_container_22_button_1_i_1_Template(rf, ctx) { if (rf & 1) {
|
|
@@ -255,9 +255,9 @@ function EntityDataGridComponent_div_2_ng_container_22_button_1_span_2_Template(
|
|
|
255
255
|
} }
|
|
256
256
|
function EntityDataGridComponent_div_2_ng_container_22_button_1_Template(rf, ctx) { if (rf & 1) {
|
|
257
257
|
const _r21 = i0.ɵɵgetCurrentView();
|
|
258
|
-
i0.ɵɵelementStart(0, "button",
|
|
258
|
+
i0.ɵɵelementStart(0, "button", 41);
|
|
259
259
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_ng_container_22_button_1_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r21); const button_r22 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onToolbarButtonClick(button_r22)); });
|
|
260
|
-
i0.ɵɵtemplate(1, EntityDataGridComponent_div_2_ng_container_22_button_1_i_1_Template, 1, 2, "i",
|
|
260
|
+
i0.ɵɵtemplate(1, EntityDataGridComponent_div_2_ng_container_22_button_1_i_1_Template, 1, 2, "i", 42)(2, EntityDataGridComponent_div_2_ng_container_22_button_1_span_2_Template, 2, 1, "span", 43);
|
|
261
261
|
i0.ɵɵelementEnd();
|
|
262
262
|
} if (rf & 2) {
|
|
263
263
|
const button_r22 = i0.ɵɵnextContext().$implicit;
|
|
@@ -271,7 +271,7 @@ function EntityDataGridComponent_div_2_ng_container_22_button_1_Template(rf, ctx
|
|
|
271
271
|
} }
|
|
272
272
|
function EntityDataGridComponent_div_2_ng_container_22_Template(rf, ctx) { if (rf & 1) {
|
|
273
273
|
i0.ɵɵelementContainerStart(0);
|
|
274
|
-
i0.ɵɵtemplate(1, EntityDataGridComponent_div_2_ng_container_22_button_1_Template, 3, 6, "button",
|
|
274
|
+
i0.ɵɵtemplate(1, EntityDataGridComponent_div_2_ng_container_22_button_1_Template, 3, 6, "button", 40);
|
|
275
275
|
i0.ɵɵelementContainerEnd();
|
|
276
276
|
} if (rf & 2) {
|
|
277
277
|
const button_r22 = ctx.$implicit;
|
|
@@ -281,16 +281,16 @@ function EntityDataGridComponent_div_2_ng_container_22_Template(rf, ctx) { if (r
|
|
|
281
281
|
} }
|
|
282
282
|
function EntityDataGridComponent_div_2_div_23_div_3_button_1_Template(rf, ctx) { if (rf & 1) {
|
|
283
283
|
const _r24 = i0.ɵɵgetCurrentView();
|
|
284
|
-
i0.ɵɵelementStart(0, "button",
|
|
284
|
+
i0.ɵɵelementStart(0, "button", 80);
|
|
285
285
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_div_23_div_3_button_1_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r24); const ctx_r2 = i0.ɵɵnextContext(4); ctx_r2.onExportClick(); return i0.ɵɵresetView(ctx_r2.closeOverflowMenu()); });
|
|
286
|
-
i0.ɵɵelement(1, "i",
|
|
286
|
+
i0.ɵɵelement(1, "i", 52);
|
|
287
287
|
i0.ɵɵelementStart(2, "span");
|
|
288
288
|
i0.ɵɵtext(3, "Export to Excel");
|
|
289
289
|
i0.ɵɵelementEnd()();
|
|
290
290
|
} }
|
|
291
291
|
function EntityDataGridComponent_div_2_div_23_div_3_ng_container_2_button_4_Template(rf, ctx) { if (rf & 1) {
|
|
292
292
|
const _r25 = i0.ɵɵgetCurrentView();
|
|
293
|
-
i0.ɵɵelementStart(0, "button",
|
|
293
|
+
i0.ɵɵelementStart(0, "button", 84);
|
|
294
294
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_div_23_div_3_ng_container_2_button_4_Template_button_click_0_listener() { const action_r26 = i0.ɵɵrestoreView(_r25).$implicit; const ctx_r2 = i0.ɵɵnextContext(5); ctx_r2.onEntityActionClick(action_r26); return i0.ɵɵresetView(ctx_r2.closeOverflowMenu()); });
|
|
295
295
|
i0.ɵɵelement(1, "i");
|
|
296
296
|
i0.ɵɵelementStart(2, "span");
|
|
@@ -307,11 +307,11 @@ function EntityDataGridComponent_div_2_div_23_div_3_ng_container_2_button_4_Temp
|
|
|
307
307
|
} }
|
|
308
308
|
function EntityDataGridComponent_div_2_div_23_div_3_ng_container_2_Template(rf, ctx) { if (rf & 1) {
|
|
309
309
|
i0.ɵɵelementContainerStart(0);
|
|
310
|
-
i0.ɵɵelement(1, "div",
|
|
311
|
-
i0.ɵɵelementStart(2, "div",
|
|
310
|
+
i0.ɵɵelement(1, "div", 81);
|
|
311
|
+
i0.ɵɵelementStart(2, "div", 82);
|
|
312
312
|
i0.ɵɵtext(3, "Actions");
|
|
313
313
|
i0.ɵɵelementEnd();
|
|
314
|
-
i0.ɵɵtemplate(4, EntityDataGridComponent_div_2_div_23_div_3_ng_container_2_button_4_Template, 4, 4, "button",
|
|
314
|
+
i0.ɵɵtemplate(4, EntityDataGridComponent_div_2_div_23_div_3_ng_container_2_button_4_Template, 4, 4, "button", 83);
|
|
315
315
|
i0.ɵɵelementContainerEnd();
|
|
316
316
|
} if (rf & 2) {
|
|
317
317
|
const ctx_r2 = i0.ɵɵnextContext(4);
|
|
@@ -319,30 +319,30 @@ function EntityDataGridComponent_div_2_div_23_div_3_ng_container_2_Template(rf,
|
|
|
319
319
|
i0.ɵɵproperty("ngForOf", ctx_r2.EntityActions);
|
|
320
320
|
} }
|
|
321
321
|
function EntityDataGridComponent_div_2_div_23_div_3_div_3_Template(rf, ctx) { if (rf & 1) {
|
|
322
|
-
i0.ɵɵelement(0, "div",
|
|
322
|
+
i0.ɵɵelement(0, "div", 81);
|
|
323
323
|
} }
|
|
324
324
|
function EntityDataGridComponent_div_2_div_23_div_3_button_4_Template(rf, ctx) { if (rf & 1) {
|
|
325
325
|
const _r27 = i0.ɵɵgetCurrentView();
|
|
326
|
-
i0.ɵɵelementStart(0, "button",
|
|
326
|
+
i0.ɵɵelementStart(0, "button", 80);
|
|
327
327
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_div_23_div_3_button_4_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r27); const ctx_r2 = i0.ɵɵnextContext(4); ctx_r2.onColumnChooserClick(); return i0.ɵɵresetView(ctx_r2.closeOverflowMenu()); });
|
|
328
|
-
i0.ɵɵelement(1, "i",
|
|
328
|
+
i0.ɵɵelement(1, "i", 72);
|
|
329
329
|
i0.ɵɵelementStart(2, "span");
|
|
330
330
|
i0.ɵɵtext(3, "Manage Columns");
|
|
331
331
|
i0.ɵɵelementEnd()();
|
|
332
332
|
} }
|
|
333
333
|
function EntityDataGridComponent_div_2_div_23_div_3_ng_container_5_button_2_Template(rf, ctx) { if (rf & 1) {
|
|
334
334
|
const _r28 = i0.ɵɵgetCurrentView();
|
|
335
|
-
i0.ɵɵelementStart(0, "button",
|
|
335
|
+
i0.ɵɵelementStart(0, "button", 80);
|
|
336
336
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_div_23_div_3_ng_container_5_button_2_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r28); const ctx_r2 = i0.ɵɵnextContext(5); ctx_r2.onCommunicationClick(); return i0.ɵɵresetView(ctx_r2.closeOverflowMenu()); });
|
|
337
|
-
i0.ɵɵelement(1, "i",
|
|
337
|
+
i0.ɵɵelement(1, "i", 64);
|
|
338
338
|
i0.ɵɵelementStart(2, "span");
|
|
339
339
|
i0.ɵɵtext(3, "Send Message");
|
|
340
340
|
i0.ɵɵelementEnd()();
|
|
341
341
|
} }
|
|
342
342
|
function EntityDataGridComponent_div_2_div_23_div_3_ng_container_5_Template(rf, ctx) { if (rf & 1) {
|
|
343
343
|
i0.ɵɵelementContainerStart(0);
|
|
344
|
-
i0.ɵɵelement(1, "div",
|
|
345
|
-
i0.ɵɵtemplate(2, EntityDataGridComponent_div_2_div_23_div_3_ng_container_5_button_2_Template, 4, 0, "button",
|
|
344
|
+
i0.ɵɵelement(1, "div", 81);
|
|
345
|
+
i0.ɵɵtemplate(2, EntityDataGridComponent_div_2_div_23_div_3_ng_container_5_button_2_Template, 4, 0, "button", 78);
|
|
346
346
|
i0.ɵɵelementContainerEnd();
|
|
347
347
|
} if (rf & 2) {
|
|
348
348
|
const ctx_r2 = i0.ɵɵnextContext(4);
|
|
@@ -350,8 +350,8 @@ function EntityDataGridComponent_div_2_div_23_div_3_ng_container_5_Template(rf,
|
|
|
350
350
|
i0.ɵɵproperty("ngIf", ctx_r2.showCommunicationInOverflow);
|
|
351
351
|
} }
|
|
352
352
|
function EntityDataGridComponent_div_2_div_23_div_3_Template(rf, ctx) { if (rf & 1) {
|
|
353
|
-
i0.ɵɵelementStart(0, "div",
|
|
354
|
-
i0.ɵɵtemplate(1, EntityDataGridComponent_div_2_div_23_div_3_button_1_Template, 4, 0, "button",
|
|
353
|
+
i0.ɵɵelementStart(0, "div", 77);
|
|
354
|
+
i0.ɵɵtemplate(1, EntityDataGridComponent_div_2_div_23_div_3_button_1_Template, 4, 0, "button", 78)(2, EntityDataGridComponent_div_2_div_23_div_3_ng_container_2_Template, 5, 1, "ng-container", 43)(3, EntityDataGridComponent_div_2_div_23_div_3_div_3_Template, 1, 0, "div", 79)(4, EntityDataGridComponent_div_2_div_23_div_3_button_4_Template, 4, 0, "button", 78)(5, EntityDataGridComponent_div_2_div_23_div_3_ng_container_5_Template, 3, 1, "ng-container", 43);
|
|
355
355
|
i0.ɵɵelementEnd();
|
|
356
356
|
} if (rf & 2) {
|
|
357
357
|
const ctx_r2 = i0.ɵɵnextContext(3);
|
|
@@ -369,13 +369,13 @@ function EntityDataGridComponent_div_2_div_23_div_3_Template(rf, ctx) { if (rf &
|
|
|
369
369
|
} }
|
|
370
370
|
function EntityDataGridComponent_div_2_div_23_Template(rf, ctx) { if (rf & 1) {
|
|
371
371
|
const _r23 = i0.ɵɵgetCurrentView();
|
|
372
|
-
i0.ɵɵelementStart(0, "div",
|
|
372
|
+
i0.ɵɵelementStart(0, "div", 73);
|
|
373
373
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_div_23_Template_div_click_0_listener($event) { i0.ɵɵrestoreView(_r23); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
374
|
-
i0.ɵɵelementStart(1, "button",
|
|
374
|
+
i0.ɵɵelementStart(1, "button", 74);
|
|
375
375
|
i0.ɵɵlistener("click", function EntityDataGridComponent_div_2_div_23_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r23); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.toggleOverflowMenu()); });
|
|
376
|
-
i0.ɵɵelement(2, "i",
|
|
376
|
+
i0.ɵɵelement(2, "i", 75);
|
|
377
377
|
i0.ɵɵelementEnd();
|
|
378
|
-
i0.ɵɵtemplate(3, EntityDataGridComponent_div_2_div_23_div_3_Template, 6, 6, "div",
|
|
378
|
+
i0.ɵɵtemplate(3, EntityDataGridComponent_div_2_div_23_div_3_Template, 6, 6, "div", 76);
|
|
379
379
|
i0.ɵɵelementEnd();
|
|
380
380
|
} if (rf & 2) {
|
|
381
381
|
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
@@ -383,14 +383,14 @@ function EntityDataGridComponent_div_2_div_23_Template(rf, ctx) { if (rf & 1) {
|
|
|
383
383
|
i0.ɵɵproperty("ngIf", ctx_r2.showOverflowMenu);
|
|
384
384
|
} }
|
|
385
385
|
function EntityDataGridComponent_div_2_Template(rf, ctx) { if (rf & 1) {
|
|
386
|
-
i0.ɵɵelementStart(0, "div",
|
|
387
|
-
i0.ɵɵtemplate(2, EntityDataGridComponent_div_2_div_2_Template, 4, 3, "div",
|
|
386
|
+
i0.ɵɵelementStart(0, "div", 11)(1, "div", 12);
|
|
387
|
+
i0.ɵɵtemplate(2, EntityDataGridComponent_div_2_div_2_Template, 4, 3, "div", 13)(3, EntityDataGridComponent_div_2_ng_container_3_Template, 2, 1, "ng-container", 14);
|
|
388
388
|
i0.ɵɵelementEnd();
|
|
389
|
-
i0.ɵɵelementStart(4, "div",
|
|
390
|
-
i0.ɵɵtemplate(5, EntityDataGridComponent_div_2_span_5_Template, 2, 2, "span",
|
|
389
|
+
i0.ɵɵelementStart(4, "div", 15);
|
|
390
|
+
i0.ɵɵtemplate(5, EntityDataGridComponent_div_2_span_5_Template, 2, 2, "span", 16)(6, EntityDataGridComponent_div_2_span_6_Template, 2, 1, "span", 17);
|
|
391
391
|
i0.ɵɵelementEnd();
|
|
392
|
-
i0.ɵɵelementStart(7, "div",
|
|
393
|
-
i0.ɵɵtemplate(8, EntityDataGridComponent_div_2_button_8_Template, 4, 0, "button",
|
|
392
|
+
i0.ɵɵelementStart(7, "div", 18);
|
|
393
|
+
i0.ɵɵtemplate(8, EntityDataGridComponent_div_2_button_8_Template, 4, 0, "button", 19)(9, EntityDataGridComponent_div_2_button_9_Template, 4, 3, "button", 20)(10, EntityDataGridComponent_div_2_button_10_Template, 4, 0, "button", 21)(11, EntityDataGridComponent_div_2_button_11_Template, 4, 0, "button", 22)(12, EntityDataGridComponent_div_2_button_12_Template, 4, 1, "button", 23)(13, EntityDataGridComponent_div_2_button_13_Template, 4, 1, "button", 24)(14, EntityDataGridComponent_div_2_button_14_Template, 4, 1, "button", 25)(15, EntityDataGridComponent_div_2_button_15_Template, 4, 1, "button", 26)(16, EntityDataGridComponent_div_2_button_16_Template, 4, 1, "button", 27)(17, EntityDataGridComponent_div_2_button_17_Template, 2, 0, "button", 28)(18, EntityDataGridComponent_div_2_button_18_Template, 2, 3, "button", 29)(19, EntityDataGridComponent_div_2_button_19_Template, 2, 0, "button", 30)(20, EntityDataGridComponent_div_2_button_20_Template, 2, 0, "button", 31)(21, EntityDataGridComponent_div_2_button_21_Template, 2, 0, "button", 32)(22, EntityDataGridComponent_div_2_ng_container_22_Template, 2, 1, "ng-container", 14)(23, EntityDataGridComponent_div_2_div_23_Template, 4, 1, "div", 33);
|
|
394
394
|
i0.ɵɵelementEnd()();
|
|
395
395
|
} if (rf & 2) {
|
|
396
396
|
const ctx_r2 = i0.ɵɵnextContext();
|
|
@@ -435,20 +435,66 @@ function EntityDataGridComponent_div_2_Template(rf, ctx) { if (rf & 1) {
|
|
|
435
435
|
i0.ɵɵadvance();
|
|
436
436
|
i0.ɵɵproperty("ngIf", ctx_r2.hasOverflowMenuItems);
|
|
437
437
|
} }
|
|
438
|
-
function
|
|
439
|
-
i0.ɵɵelementStart(0, "div",
|
|
440
|
-
i0.ɵɵelement(1, "
|
|
438
|
+
function EntityDataGridComponent_div_3_ng_container_1_div_2_Template(rf, ctx) { if (rf & 1) {
|
|
439
|
+
i0.ɵɵelementStart(0, "div", 92);
|
|
440
|
+
i0.ɵɵelement(1, "i");
|
|
441
441
|
i0.ɵɵelementEnd();
|
|
442
|
+
} if (rf & 2) {
|
|
443
|
+
const agg_r29 = i0.ɵɵnextContext().$implicit;
|
|
444
|
+
i0.ɵɵadvance();
|
|
445
|
+
i0.ɵɵclassMap(agg_r29.icon);
|
|
442
446
|
} }
|
|
443
|
-
function
|
|
444
|
-
|
|
447
|
+
function EntityDataGridComponent_div_3_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
448
|
+
i0.ɵɵelementContainerStart(0);
|
|
449
|
+
i0.ɵɵelementStart(1, "div", 87);
|
|
450
|
+
i0.ɵɵtemplate(2, EntityDataGridComponent_div_3_ng_container_1_div_2_Template, 2, 2, "div", 88);
|
|
451
|
+
i0.ɵɵelementStart(3, "div", 89)(4, "span", 90);
|
|
452
|
+
i0.ɵɵtext(5);
|
|
453
|
+
i0.ɵɵelementEnd();
|
|
454
|
+
i0.ɵɵelementStart(6, "span", 91);
|
|
455
|
+
i0.ɵɵtext(7);
|
|
456
|
+
i0.ɵɵelementEnd()()();
|
|
457
|
+
i0.ɵɵelementContainerEnd();
|
|
458
|
+
} if (rf & 2) {
|
|
459
|
+
const agg_r29 = ctx.$implicit;
|
|
460
|
+
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
461
|
+
i0.ɵɵadvance(2);
|
|
462
|
+
i0.ɵɵproperty("ngIf", agg_r29.icon);
|
|
463
|
+
i0.ɵɵadvance(3);
|
|
464
|
+
i0.ɵɵtextInterpolate(agg_r29.label);
|
|
465
|
+
i0.ɵɵadvance(2);
|
|
466
|
+
i0.ɵɵtextInterpolate(ctx_r2.getAggregateValue(agg_r29));
|
|
467
|
+
} }
|
|
468
|
+
function EntityDataGridComponent_div_3_div_2_Template(rf, ctx) { if (rf & 1) {
|
|
469
|
+
i0.ɵɵelementStart(0, "div", 93);
|
|
470
|
+
i0.ɵɵelement(1, "i", 94);
|
|
471
|
+
i0.ɵɵelementEnd();
|
|
472
|
+
} }
|
|
473
|
+
function EntityDataGridComponent_div_3_Template(rf, ctx) { if (rf & 1) {
|
|
445
474
|
i0.ɵɵelementStart(0, "div", 85);
|
|
446
|
-
i0.ɵɵ
|
|
475
|
+
i0.ɵɵtemplate(1, EntityDataGridComponent_div_3_ng_container_1_Template, 8, 3, "ng-container", 14)(2, EntityDataGridComponent_div_3_div_2_Template, 2, 0, "div", 86);
|
|
476
|
+
i0.ɵɵelementEnd();
|
|
477
|
+
} if (rf & 2) {
|
|
478
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
479
|
+
i0.ɵɵadvance();
|
|
480
|
+
i0.ɵɵproperty("ngForOf", ctx_r2.CardAggregates);
|
|
481
|
+
i0.ɵɵadvance();
|
|
482
|
+
i0.ɵɵproperty("ngIf", ctx_r2.AggregatesLoading);
|
|
483
|
+
} }
|
|
484
|
+
function EntityDataGridComponent_div_5_Template(rf, ctx) { if (rf & 1) {
|
|
485
|
+
i0.ɵɵelementStart(0, "div", 95);
|
|
486
|
+
i0.ɵɵelement(1, "mj-loading", 96);
|
|
487
|
+
i0.ɵɵelementEnd();
|
|
488
|
+
} }
|
|
489
|
+
function EntityDataGridComponent_div_6_Template(rf, ctx) { if (rf & 1) {
|
|
490
|
+
const _r30 = i0.ɵɵgetCurrentView();
|
|
491
|
+
i0.ɵɵelementStart(0, "div", 97);
|
|
492
|
+
i0.ɵɵelement(1, "i", 98);
|
|
447
493
|
i0.ɵɵelementStart(2, "span");
|
|
448
494
|
i0.ɵɵtext(3);
|
|
449
495
|
i0.ɵɵelementEnd();
|
|
450
|
-
i0.ɵɵelementStart(4, "button",
|
|
451
|
-
i0.ɵɵlistener("click", function
|
|
496
|
+
i0.ɵɵelementStart(4, "button", 99);
|
|
497
|
+
i0.ɵɵlistener("click", function EntityDataGridComponent_div_6_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r30); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.Refresh()); });
|
|
452
498
|
i0.ɵɵtext(5, "Retry");
|
|
453
499
|
i0.ɵɵelementEnd()();
|
|
454
500
|
} if (rf & 2) {
|
|
@@ -456,31 +502,76 @@ function EntityDataGridComponent_div_5_Template(rf, ctx) { if (rf & 1) {
|
|
|
456
502
|
i0.ɵɵadvance(3);
|
|
457
503
|
i0.ɵɵtextInterpolate(ctx_r2.errorMessage);
|
|
458
504
|
} }
|
|
459
|
-
function
|
|
460
|
-
i0.ɵɵelementStart(0, "div",
|
|
461
|
-
i0.ɵɵelement(1, "i",
|
|
505
|
+
function EntityDataGridComponent_div_7_Template(rf, ctx) { if (rf & 1) {
|
|
506
|
+
i0.ɵɵelementStart(0, "div", 100);
|
|
507
|
+
i0.ɵɵelement(1, "i", 101);
|
|
462
508
|
i0.ɵɵelementStart(2, "span");
|
|
463
509
|
i0.ɵɵtext(3, "No data to display");
|
|
464
510
|
i0.ɵɵelementEnd()();
|
|
465
511
|
} }
|
|
466
|
-
function
|
|
467
|
-
const
|
|
468
|
-
i0.ɵɵelementStart(0, "ag-grid-angular",
|
|
469
|
-
i0.ɵɵlistener("gridReady", function
|
|
512
|
+
function EntityDataGridComponent_ag_grid_angular_8_Template(rf, ctx) { if (rf & 1) {
|
|
513
|
+
const _r31 = i0.ɵɵgetCurrentView();
|
|
514
|
+
i0.ɵɵelementStart(0, "ag-grid-angular", 102);
|
|
515
|
+
i0.ɵɵlistener("gridReady", function EntityDataGridComponent_ag_grid_angular_8_Template_ag_grid_angular_gridReady_0_listener($event) { i0.ɵɵrestoreView(_r31); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onGridReady($event)); })("cellClicked", function EntityDataGridComponent_ag_grid_angular_8_Template_ag_grid_angular_cellClicked_0_listener($event) { i0.ɵɵrestoreView(_r31); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAgCellClicked($event)); })("rowClicked", function EntityDataGridComponent_ag_grid_angular_8_Template_ag_grid_angular_rowClicked_0_listener($event) { i0.ɵɵrestoreView(_r31); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAgRowClicked($event)); })("rowDoubleClicked", function EntityDataGridComponent_ag_grid_angular_8_Template_ag_grid_angular_rowDoubleClicked_0_listener($event) { i0.ɵɵrestoreView(_r31); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAgRowDoubleClicked($event)); })("sortChanged", function EntityDataGridComponent_ag_grid_angular_8_Template_ag_grid_angular_sortChanged_0_listener($event) { i0.ɵɵrestoreView(_r31); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAgSortChanged($event)); })("selectionChanged", function EntityDataGridComponent_ag_grid_angular_8_Template_ag_grid_angular_selectionChanged_0_listener($event) { i0.ɵɵrestoreView(_r31); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAgSelectionChanged($event)); })("columnResized", function EntityDataGridComponent_ag_grid_angular_8_Template_ag_grid_angular_columnResized_0_listener($event) { i0.ɵɵrestoreView(_r31); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAgColumnResized($event)); })("columnMoved", function EntityDataGridComponent_ag_grid_angular_8_Template_ag_grid_angular_columnMoved_0_listener($event) { i0.ɵɵrestoreView(_r31); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAgColumnMoved($event)); });
|
|
470
516
|
i0.ɵɵelementEnd();
|
|
471
517
|
} if (rf & 2) {
|
|
472
518
|
const ctx_r2 = i0.ɵɵnextContext();
|
|
473
519
|
i0.ɵɵproperty("theme", ctx_r2.agGridTheme)("columnDefs", ctx_r2.agColumnDefs)("rowData", ctx_r2.rowData)("defaultColDef", ctx_r2.defaultColDef)("rowSelection", ctx_r2.agRowSelection)("getRowId", ctx_r2.getRowId)("suppressCellFocus", true)("rowHeight", ctx_r2.RowHeight)("headerHeight", ctx_r2.ShowHeader ? undefined : 0);
|
|
474
520
|
} }
|
|
475
|
-
function
|
|
476
|
-
const
|
|
477
|
-
i0.ɵɵelementStart(0, "ag-grid-angular",
|
|
478
|
-
i0.ɵɵlistener("gridReady", function
|
|
521
|
+
function EntityDataGridComponent_ag_grid_angular_9_Template(rf, ctx) { if (rf & 1) {
|
|
522
|
+
const _r32 = i0.ɵɵgetCurrentView();
|
|
523
|
+
i0.ɵɵelementStart(0, "ag-grid-angular", 103);
|
|
524
|
+
i0.ɵɵlistener("gridReady", function EntityDataGridComponent_ag_grid_angular_9_Template_ag_grid_angular_gridReady_0_listener($event) { i0.ɵɵrestoreView(_r32); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onGridReady($event)); })("cellClicked", function EntityDataGridComponent_ag_grid_angular_9_Template_ag_grid_angular_cellClicked_0_listener($event) { i0.ɵɵrestoreView(_r32); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAgCellClicked($event)); })("rowClicked", function EntityDataGridComponent_ag_grid_angular_9_Template_ag_grid_angular_rowClicked_0_listener($event) { i0.ɵɵrestoreView(_r32); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAgRowClicked($event)); })("rowDoubleClicked", function EntityDataGridComponent_ag_grid_angular_9_Template_ag_grid_angular_rowDoubleClicked_0_listener($event) { i0.ɵɵrestoreView(_r32); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAgRowDoubleClicked($event)); })("sortChanged", function EntityDataGridComponent_ag_grid_angular_9_Template_ag_grid_angular_sortChanged_0_listener($event) { i0.ɵɵrestoreView(_r32); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAgSortChanged($event)); })("selectionChanged", function EntityDataGridComponent_ag_grid_angular_9_Template_ag_grid_angular_selectionChanged_0_listener($event) { i0.ɵɵrestoreView(_r32); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAgSelectionChanged($event)); })("columnResized", function EntityDataGridComponent_ag_grid_angular_9_Template_ag_grid_angular_columnResized_0_listener($event) { i0.ɵɵrestoreView(_r32); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAgColumnResized($event)); })("columnMoved", function EntityDataGridComponent_ag_grid_angular_9_Template_ag_grid_angular_columnMoved_0_listener($event) { i0.ɵɵrestoreView(_r32); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onAgColumnMoved($event)); });
|
|
479
525
|
i0.ɵɵelementEnd();
|
|
480
526
|
} if (rf & 2) {
|
|
481
527
|
const ctx_r2 = i0.ɵɵnextContext();
|
|
482
528
|
i0.ɵɵproperty("theme", ctx_r2.agGridTheme)("columnDefs", ctx_r2.agColumnDefs)("defaultColDef", ctx_r2.defaultColDef)("rowSelection", ctx_r2.agRowSelection)("getRowId", ctx_r2.getRowId)("suppressCellFocus", true)("rowHeight", ctx_r2.RowHeight)("headerHeight", ctx_r2.ShowHeader ? undefined : 0)("rowModelType", "infinite")("cacheBlockSize", ctx_r2.CacheBlockSize)("maxBlocksInCache", ctx_r2.MaxBlocksInCache)("infiniteInitialRowCount", 1)("cacheOverflowSize", 2);
|
|
483
529
|
} }
|
|
530
|
+
function EntityDataGridComponent_div_10_ng_container_2_i_2_Template(rf, ctx) { if (rf & 1) {
|
|
531
|
+
i0.ɵɵelement(0, "i");
|
|
532
|
+
} if (rf & 2) {
|
|
533
|
+
const agg_r33 = i0.ɵɵnextContext().$implicit;
|
|
534
|
+
i0.ɵɵclassMap(agg_r33.icon);
|
|
535
|
+
} }
|
|
536
|
+
function EntityDataGridComponent_div_10_ng_container_2_Template(rf, ctx) { if (rf & 1) {
|
|
537
|
+
i0.ɵɵelementContainerStart(0);
|
|
538
|
+
i0.ɵɵelementStart(1, "div", 107);
|
|
539
|
+
i0.ɵɵtemplate(2, EntityDataGridComponent_div_10_ng_container_2_i_2_Template, 1, 2, "i", 42);
|
|
540
|
+
i0.ɵɵelementStart(3, "span", 108);
|
|
541
|
+
i0.ɵɵtext(4);
|
|
542
|
+
i0.ɵɵelementEnd();
|
|
543
|
+
i0.ɵɵelementStart(5, "span", 109);
|
|
544
|
+
i0.ɵɵtext(6);
|
|
545
|
+
i0.ɵɵelementEnd()();
|
|
546
|
+
i0.ɵɵelementContainerEnd();
|
|
547
|
+
} if (rf & 2) {
|
|
548
|
+
const agg_r33 = ctx.$implicit;
|
|
549
|
+
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
550
|
+
i0.ɵɵadvance(2);
|
|
551
|
+
i0.ɵɵproperty("ngIf", agg_r33.icon);
|
|
552
|
+
i0.ɵɵadvance(2);
|
|
553
|
+
i0.ɵɵtextInterpolate1("", agg_r33.label, ":");
|
|
554
|
+
i0.ɵɵadvance(2);
|
|
555
|
+
i0.ɵɵtextInterpolate(ctx_r2.getAggregateValue(agg_r33));
|
|
556
|
+
} }
|
|
557
|
+
function EntityDataGridComponent_div_10_div_3_Template(rf, ctx) { if (rf & 1) {
|
|
558
|
+
i0.ɵɵelementStart(0, "div", 110);
|
|
559
|
+
i0.ɵɵelement(1, "i", 94);
|
|
560
|
+
i0.ɵɵelementEnd();
|
|
561
|
+
} }
|
|
562
|
+
function EntityDataGridComponent_div_10_Template(rf, ctx) { if (rf & 1) {
|
|
563
|
+
i0.ɵɵelementStart(0, "div", 104)(1, "div", 105);
|
|
564
|
+
i0.ɵɵtemplate(2, EntityDataGridComponent_div_10_ng_container_2_Template, 7, 3, "ng-container", 14);
|
|
565
|
+
i0.ɵɵelementEnd();
|
|
566
|
+
i0.ɵɵtemplate(3, EntityDataGridComponent_div_10_div_3_Template, 2, 0, "div", 106);
|
|
567
|
+
i0.ɵɵelementEnd();
|
|
568
|
+
} if (rf & 2) {
|
|
569
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
570
|
+
i0.ɵɵadvance(2);
|
|
571
|
+
i0.ɵɵproperty("ngForOf", ctx_r2.ColumnAggregates);
|
|
572
|
+
i0.ɵɵadvance();
|
|
573
|
+
i0.ɵɵproperty("ngIf", ctx_r2.AggregatesLoading);
|
|
574
|
+
} }
|
|
484
575
|
// Register AG Grid modules (required for v34+)
|
|
485
576
|
ModuleRegistry.registerModules([AllCommunityModule]);
|
|
486
577
|
/**
|
|
@@ -496,7 +587,7 @@ ModuleRegistry.registerModules([AllCommunityModule]);
|
|
|
496
587
|
* - Inline cell and row editing
|
|
497
588
|
* - Column reordering, resizing, and visibility toggle
|
|
498
589
|
* - State persistence to User Settings
|
|
499
|
-
* - Compatible with
|
|
590
|
+
* - Compatible with ViewGridState from User Views
|
|
500
591
|
*
|
|
501
592
|
* @example
|
|
502
593
|
* ```html
|
|
@@ -823,6 +914,21 @@ export class EntityDataGridComponent {
|
|
|
823
914
|
get RowHeight() {
|
|
824
915
|
return this._rowHeight;
|
|
825
916
|
}
|
|
917
|
+
/**
|
|
918
|
+
* Enable text wrapping in grid cells
|
|
919
|
+
* When true, long text will wrap to multiple lines and rows will auto-size
|
|
920
|
+
* Note: This disables fixed row height and may impact performance with large datasets
|
|
921
|
+
*/
|
|
922
|
+
_wrapText = false;
|
|
923
|
+
set WrapText(value) {
|
|
924
|
+
if (this._wrapText !== value) {
|
|
925
|
+
this._wrapText = value;
|
|
926
|
+
this.updateDefaultColDefForWrapping();
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
get WrapText() {
|
|
930
|
+
return this._wrapText;
|
|
931
|
+
}
|
|
826
932
|
_virtualScroll = true;
|
|
827
933
|
set VirtualScroll(value) {
|
|
828
934
|
this._virtualScroll = value;
|
|
@@ -1127,8 +1233,111 @@ export class EntityDataGridComponent {
|
|
|
1127
1233
|
return this._entityActions;
|
|
1128
1234
|
}
|
|
1129
1235
|
// ========================================
|
|
1236
|
+
// Aggregate Inputs
|
|
1237
|
+
// ========================================
|
|
1238
|
+
/**
|
|
1239
|
+
* Aggregate configuration for the grid.
|
|
1240
|
+
* When provided, aggregate expressions are calculated alongside data and displayed:
|
|
1241
|
+
* - Column-bound aggregates appear in a pinned bottom row
|
|
1242
|
+
* - Card-bound aggregates are exposed via AggregateValues for use with AggregatePanelComponent
|
|
1243
|
+
*/
|
|
1244
|
+
set AggregatesConfig(value) {
|
|
1245
|
+
this._aggregatesConfig = value;
|
|
1246
|
+
}
|
|
1247
|
+
get AggregatesConfig() {
|
|
1248
|
+
return this._aggregatesConfig;
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* Returns the aggregate values map, keyed by expression or id.
|
|
1252
|
+
* Use this to pass values to AggregatePanelComponent.
|
|
1253
|
+
*/
|
|
1254
|
+
get AggregateValuesMap() {
|
|
1255
|
+
return this._aggregateValues;
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Returns the raw aggregate results from the last RunView call.
|
|
1259
|
+
*/
|
|
1260
|
+
get AggregateResultsList() {
|
|
1261
|
+
return this._aggregateResults;
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Whether aggregates are currently loading.
|
|
1265
|
+
*/
|
|
1266
|
+
get AggregatesLoading() {
|
|
1267
|
+
return this._aggregatesLoading;
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Returns the effective aggregates config, preferring _aggregatesConfig but falling back to _gridState.aggregates.
|
|
1271
|
+
* This ensures aggregates work regardless of whether they came from explicit config or from view's GridState.
|
|
1272
|
+
*/
|
|
1273
|
+
get EffectiveAggregatesConfig() {
|
|
1274
|
+
return this._aggregatesConfig || this._gridState?.aggregates;
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* Returns enabled aggregates configured for card display.
|
|
1278
|
+
*/
|
|
1279
|
+
get CardAggregates() {
|
|
1280
|
+
const config = this.EffectiveAggregatesConfig;
|
|
1281
|
+
if (!config?.expressions)
|
|
1282
|
+
return [];
|
|
1283
|
+
return config.expressions
|
|
1284
|
+
.filter(a => a.enabled !== false && a.displayType === 'card')
|
|
1285
|
+
.sort((a, b) => (a.order || 0) - (b.order || 0));
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Returns enabled aggregates configured for column footer display.
|
|
1289
|
+
*/
|
|
1290
|
+
get ColumnAggregates() {
|
|
1291
|
+
const config = this.EffectiveAggregatesConfig;
|
|
1292
|
+
if (!config?.expressions)
|
|
1293
|
+
return [];
|
|
1294
|
+
return config.expressions
|
|
1295
|
+
.filter(a => a.enabled !== false && a.displayType === 'column')
|
|
1296
|
+
.sort((a, b) => (a.order || 0) - (b.order || 0));
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* Whether to show aggregate summary row (has column-type aggregates with values).
|
|
1300
|
+
*/
|
|
1301
|
+
get ShowAggregateSummary() {
|
|
1302
|
+
const hasColumnAggs = this.ColumnAggregates.length > 0;
|
|
1303
|
+
const hasValues = this._aggregateValues.size > 0;
|
|
1304
|
+
return hasColumnAggs && hasValues;
|
|
1305
|
+
}
|
|
1306
|
+
/**
|
|
1307
|
+
* Whether to show aggregate panel (has card-type aggregates with values).
|
|
1308
|
+
*/
|
|
1309
|
+
get ShowAggregatePanel() {
|
|
1310
|
+
return this.CardAggregates.length > 0 && this._aggregateValues.size > 0;
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* Get formatted aggregate value for display
|
|
1314
|
+
*/
|
|
1315
|
+
getAggregateValue(agg) {
|
|
1316
|
+
const key = agg.id || agg.expression;
|
|
1317
|
+
const value = this._aggregateValues.get(key);
|
|
1318
|
+
if (value == null)
|
|
1319
|
+
return '—';
|
|
1320
|
+
// Format based on value type
|
|
1321
|
+
if (typeof value === 'number') {
|
|
1322
|
+
return value.toLocaleString('en-US', {
|
|
1323
|
+
maximumFractionDigits: 2,
|
|
1324
|
+
minimumFractionDigits: 0
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
if (value instanceof Date) {
|
|
1328
|
+
return value.toLocaleDateString();
|
|
1329
|
+
}
|
|
1330
|
+
return String(value);
|
|
1331
|
+
}
|
|
1332
|
+
// ========================================
|
|
1130
1333
|
// Event Outputs
|
|
1131
1334
|
// ========================================
|
|
1335
|
+
// Aggregate Results
|
|
1336
|
+
/**
|
|
1337
|
+
* Emitted when aggregate results are loaded.
|
|
1338
|
+
* Contains the array of AggregateResult objects and a values map for easy lookup.
|
|
1339
|
+
*/
|
|
1340
|
+
AggregatesLoaded = new EventEmitter();
|
|
1132
1341
|
// Row Selection
|
|
1133
1342
|
BeforeRowSelect = new EventEmitter();
|
|
1134
1343
|
AfterRowSelect = new EventEmitter();
|
|
@@ -1140,6 +1349,12 @@ export class EntityDataGridComponent {
|
|
|
1140
1349
|
AfterRowClick = new EventEmitter();
|
|
1141
1350
|
BeforeRowDoubleClick = new EventEmitter();
|
|
1142
1351
|
AfterRowDoubleClick = new EventEmitter();
|
|
1352
|
+
// Foreign Key Link Click
|
|
1353
|
+
/**
|
|
1354
|
+
* Emitted when a foreign key link is clicked in the grid.
|
|
1355
|
+
* Parent components should handle this to navigate to the related record.
|
|
1356
|
+
*/
|
|
1357
|
+
ForeignKeyClick = new EventEmitter();
|
|
1143
1358
|
// Editing
|
|
1144
1359
|
BeforeCellEdit = new EventEmitter();
|
|
1145
1360
|
AfterCellEditBegin = new EventEmitter();
|
|
@@ -1262,6 +1477,30 @@ export class EntityDataGridComponent {
|
|
|
1262
1477
|
resizable: true,
|
|
1263
1478
|
minWidth: 80
|
|
1264
1479
|
};
|
|
1480
|
+
/**
|
|
1481
|
+
* Update defaultColDef when text wrapping setting changes
|
|
1482
|
+
* Enables/disables auto row height and cell text wrapping
|
|
1483
|
+
*/
|
|
1484
|
+
updateDefaultColDefForWrapping() {
|
|
1485
|
+
if (this._wrapText) {
|
|
1486
|
+
this.defaultColDef = {
|
|
1487
|
+
...this.defaultColDef,
|
|
1488
|
+
wrapText: true,
|
|
1489
|
+
autoHeight: true,
|
|
1490
|
+
cellClass: 'cell-wrap-text'
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
else {
|
|
1494
|
+
// Remove wrapping properties
|
|
1495
|
+
const { wrapText, autoHeight, cellClass, ...rest } = this.defaultColDef;
|
|
1496
|
+
this.defaultColDef = rest;
|
|
1497
|
+
}
|
|
1498
|
+
// Refresh the grid to apply changes
|
|
1499
|
+
if (this.gridApi) {
|
|
1500
|
+
this.gridApi.setGridOption('defaultColDef', this.defaultColDef);
|
|
1501
|
+
this.gridApi.refreshCells({ force: true });
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1265
1504
|
/** Get row ID function for AG Grid */
|
|
1266
1505
|
getRowId = (params) => params.data['__pk'];
|
|
1267
1506
|
/** Suppress sort changed events during programmatic updates */
|
|
@@ -1284,6 +1523,11 @@ export class EntityDataGridComponent {
|
|
|
1284
1523
|
_editingRowKey = null;
|
|
1285
1524
|
_editingField = null;
|
|
1286
1525
|
_pendingChanges = [];
|
|
1526
|
+
// Aggregate state
|
|
1527
|
+
_aggregatesConfig = null;
|
|
1528
|
+
_aggregateResults = [];
|
|
1529
|
+
_aggregateValues = new Map();
|
|
1530
|
+
_aggregatesLoading = false;
|
|
1287
1531
|
// ========================================
|
|
1288
1532
|
// Public Read-Only Properties
|
|
1289
1533
|
// ========================================
|
|
@@ -1439,6 +1683,13 @@ export class EntityDataGridComponent {
|
|
|
1439
1683
|
// carry over column/sort settings from a previous view when switching views
|
|
1440
1684
|
this._gridState = null;
|
|
1441
1685
|
this._sortState = [];
|
|
1686
|
+
// Reset aggregates when switching views - don't carry over from previous view
|
|
1687
|
+
this._aggregatesConfig = null;
|
|
1688
|
+
this._aggregateResults = [];
|
|
1689
|
+
this._aggregateValues.clear();
|
|
1690
|
+
// Reset allowLoad to true when params change - this ensures the new view gets to load
|
|
1691
|
+
// (The parent may have set allowLoad=false for the previous view, which shouldn't prevent loading the new view)
|
|
1692
|
+
this._allowLoad = true;
|
|
1442
1693
|
try {
|
|
1443
1694
|
// If using a stored view, load the view entity first
|
|
1444
1695
|
if (this._params.ViewEntity) {
|
|
@@ -1578,7 +1829,8 @@ export class EntityDataGridComponent {
|
|
|
1578
1829
|
if (!this._gridState && gridState.columnSettings?.length) {
|
|
1579
1830
|
this._gridState = {
|
|
1580
1831
|
columnSettings: gridState.columnSettings,
|
|
1581
|
-
sortSettings: gridState.sortSettings || []
|
|
1832
|
+
sortSettings: gridState.sortSettings || [],
|
|
1833
|
+
aggregates: gridState.aggregates
|
|
1582
1834
|
};
|
|
1583
1835
|
}
|
|
1584
1836
|
// Apply sort state if not already set
|
|
@@ -1589,6 +1841,10 @@ export class EntityDataGridComponent {
|
|
|
1589
1841
|
index
|
|
1590
1842
|
}));
|
|
1591
1843
|
}
|
|
1844
|
+
// Apply aggregates from user defaults if present and not already set
|
|
1845
|
+
if (gridState.aggregates && !this._aggregatesConfig) {
|
|
1846
|
+
this._aggregatesConfig = gridState.aggregates;
|
|
1847
|
+
}
|
|
1592
1848
|
}
|
|
1593
1849
|
}
|
|
1594
1850
|
catch (error) {
|
|
@@ -1608,7 +1864,8 @@ export class EntityDataGridComponent {
|
|
|
1608
1864
|
const settingKey = `default-view-setting/${this._entityInfo.Name}`;
|
|
1609
1865
|
const gridStateJson = {
|
|
1610
1866
|
columnSettings: state.columnSettings,
|
|
1611
|
-
sortSettings: state.sortSettings
|
|
1867
|
+
sortSettings: state.sortSettings,
|
|
1868
|
+
aggregates: state.aggregates
|
|
1612
1869
|
};
|
|
1613
1870
|
await UserInfoEngine.Instance.SetSetting(settingKey, JSON.stringify(gridStateJson));
|
|
1614
1871
|
// Clear pending state and reset dirty flag after successful save
|
|
@@ -1639,9 +1896,14 @@ export class EntityDataGridComponent {
|
|
|
1639
1896
|
if (gridState.columnSettings?.length) {
|
|
1640
1897
|
this._gridState = {
|
|
1641
1898
|
columnSettings: gridState.columnSettings,
|
|
1642
|
-
sortSettings: gridState.sortSettings || []
|
|
1899
|
+
sortSettings: gridState.sortSettings || [],
|
|
1900
|
+
aggregates: gridState.aggregates
|
|
1643
1901
|
};
|
|
1644
1902
|
}
|
|
1903
|
+
// Apply aggregates from view's GridState if present
|
|
1904
|
+
if (gridState.aggregates && !this._aggregatesConfig) {
|
|
1905
|
+
this._aggregatesConfig = gridState.aggregates;
|
|
1906
|
+
}
|
|
1645
1907
|
}
|
|
1646
1908
|
catch (e) {
|
|
1647
1909
|
console.warn('Failed to parse view GridState:', e);
|
|
@@ -1681,6 +1943,76 @@ export class EntityDataGridComponent {
|
|
|
1681
1943
|
}));
|
|
1682
1944
|
this.applySortStateToGrid();
|
|
1683
1945
|
}
|
|
1946
|
+
// Apply aggregates from GridState if present and fetch their values
|
|
1947
|
+
if (this._gridState.aggregates) {
|
|
1948
|
+
this._aggregatesConfig = this._gridState.aggregates;
|
|
1949
|
+
// Fetch aggregate values when gridState aggregates change
|
|
1950
|
+
this.refreshAggregates();
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
/**
|
|
1955
|
+
* Fetch aggregate values without reloading data.
|
|
1956
|
+
* Used when gridState changes and includes new aggregate config.
|
|
1957
|
+
* This runs a RunView with MaxRows=0 to get only aggregate results.
|
|
1958
|
+
*/
|
|
1959
|
+
async refreshAggregates() {
|
|
1960
|
+
const effectiveAggConfig = this.EffectiveAggregatesConfig;
|
|
1961
|
+
if (!effectiveAggConfig?.expressions?.length) {
|
|
1962
|
+
this._aggregateResults = [];
|
|
1963
|
+
this._aggregateValues.clear();
|
|
1964
|
+
this._aggregatesLoading = false;
|
|
1965
|
+
this.cdr.detectChanges();
|
|
1966
|
+
return;
|
|
1967
|
+
}
|
|
1968
|
+
// Need entity info to run the query
|
|
1969
|
+
if (!this._entityInfo) {
|
|
1970
|
+
return;
|
|
1971
|
+
}
|
|
1972
|
+
this._aggregatesLoading = true;
|
|
1973
|
+
this.cdr.detectChanges();
|
|
1974
|
+
try {
|
|
1975
|
+
// Build aggregate expressions
|
|
1976
|
+
const aggregateExpressions = effectiveAggConfig.expressions
|
|
1977
|
+
.filter(agg => agg.enabled !== false && agg.expression)
|
|
1978
|
+
.map(agg => ({
|
|
1979
|
+
expression: agg.expression,
|
|
1980
|
+
alias: agg.id || agg.label || agg.expression
|
|
1981
|
+
}));
|
|
1982
|
+
if (aggregateExpressions.length === 0) {
|
|
1983
|
+
this._aggregateResults = [];
|
|
1984
|
+
this._aggregateValues.clear();
|
|
1985
|
+
this._aggregatesLoading = false;
|
|
1986
|
+
this.cdr.detectChanges();
|
|
1987
|
+
return;
|
|
1988
|
+
}
|
|
1989
|
+
const rv = new RunView();
|
|
1990
|
+
// Build the ExtraFilter from params or view entity
|
|
1991
|
+
let extraFilter;
|
|
1992
|
+
if (this._params?.ExtraFilter) {
|
|
1993
|
+
extraFilter = this._params.ExtraFilter;
|
|
1994
|
+
}
|
|
1995
|
+
else if (this._viewEntity?.WhereClause) {
|
|
1996
|
+
extraFilter = this._viewEntity.WhereClause;
|
|
1997
|
+
}
|
|
1998
|
+
const result = await rv.RunView({
|
|
1999
|
+
EntityName: this._entityInfo.Name,
|
|
2000
|
+
MaxRows: 0, // Only get aggregates, no row data
|
|
2001
|
+
ExtraFilter: extraFilter,
|
|
2002
|
+
Aggregates: aggregateExpressions
|
|
2003
|
+
});
|
|
2004
|
+
if (result.Success) {
|
|
2005
|
+
this.processAggregateResults(result.AggregateResults, result.AggregateExecutionTime);
|
|
2006
|
+
}
|
|
2007
|
+
else {
|
|
2008
|
+
this._aggregatesLoading = false;
|
|
2009
|
+
this.cdr.detectChanges();
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
catch (error) {
|
|
2013
|
+
console.error('[EntityDataGrid] Error fetching aggregates:', error);
|
|
2014
|
+
this._aggregatesLoading = false;
|
|
2015
|
+
this.cdr.detectChanges();
|
|
1684
2016
|
}
|
|
1685
2017
|
}
|
|
1686
2018
|
onFilterTextChanged() {
|
|
@@ -1711,77 +2043,122 @@ export class EntityDataGridComponent {
|
|
|
1711
2043
|
this.initializeColumnStates();
|
|
1712
2044
|
this.buildAgColumnDefs();
|
|
1713
2045
|
}
|
|
2046
|
+
/**
|
|
2047
|
+
* Determines if a field should be shown by default when no saved view exists.
|
|
2048
|
+
* This logic is aligned with UserViewEntity.SetDefaultsFromEntity() to ensure
|
|
2049
|
+
* consistent column visibility between initial load and saved views.
|
|
2050
|
+
*/
|
|
1714
2051
|
shouldShowField(field) {
|
|
2052
|
+
// Always exclude system fields
|
|
1715
2053
|
if (field.Name.startsWith('__mj_'))
|
|
1716
2054
|
return false;
|
|
2055
|
+
// Always exclude UUID primary keys (they're not useful to display)
|
|
1717
2056
|
if (field.IsPrimaryKey && field.SQLFullType?.toLowerCase() === 'uniqueidentifier') {
|
|
1718
2057
|
return false;
|
|
1719
2058
|
}
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
return true;
|
|
2059
|
+
// Only show fields explicitly marked as DefaultInView
|
|
2060
|
+
// This aligns with UserViewEntity.SetDefaultsFromEntity() behavior
|
|
2061
|
+
// ensuring users see the same columns before and after saving a view
|
|
2062
|
+
return field.DefaultInView === true;
|
|
1725
2063
|
}
|
|
2064
|
+
/**
|
|
2065
|
+
* Estimate an appropriate column width based on field metadata.
|
|
2066
|
+
* Takes into account: metadata DefaultColumnWidth, header text length, data type, and field patterns.
|
|
2067
|
+
*/
|
|
1726
2068
|
estimateColumnWidth(field) {
|
|
2069
|
+
// Priority 1: Use metadata-defined width if set
|
|
2070
|
+
if (field.DefaultColumnWidth && field.DefaultColumnWidth > 0) {
|
|
2071
|
+
return field.DefaultColumnWidth;
|
|
2072
|
+
}
|
|
2073
|
+
// Calculate minimum width needed for header text
|
|
2074
|
+
const headerText = field.DisplayName || field.Name;
|
|
2075
|
+
const headerWidth = this.calculateHeaderWidth(headerText);
|
|
2076
|
+
// Calculate estimated data width based on field type
|
|
2077
|
+
const dataWidth = this.calculateDataWidth(field);
|
|
2078
|
+
// Use the larger of header or data width, with bounds
|
|
2079
|
+
const estimatedWidth = Math.max(headerWidth, dataWidth);
|
|
2080
|
+
// Apply min/max bounds: minimum 80px, maximum 350px
|
|
2081
|
+
return Math.max(Math.min(estimatedWidth, 350), 80);
|
|
2082
|
+
}
|
|
2083
|
+
/**
|
|
2084
|
+
* Calculate minimum width needed to display header text without truncation.
|
|
2085
|
+
*/
|
|
2086
|
+
calculateHeaderWidth(text) {
|
|
2087
|
+
// Average char width ~7.5px for typical grid font
|
|
2088
|
+
const charWidth = 7.5;
|
|
2089
|
+
// Padding for sort icon and cell padding
|
|
2090
|
+
const sortIconPadding = 24;
|
|
2091
|
+
const cellPadding = 16;
|
|
2092
|
+
return Math.ceil(text.length * charWidth + sortIconPadding + cellPadding);
|
|
2093
|
+
}
|
|
2094
|
+
/**
|
|
2095
|
+
* Calculate estimated data width based on field type and patterns.
|
|
2096
|
+
*/
|
|
2097
|
+
calculateDataWidth(field) {
|
|
1727
2098
|
const fieldNameLower = field.Name.toLowerCase();
|
|
1728
|
-
const
|
|
2099
|
+
const tsType = field.TSType;
|
|
1729
2100
|
// Fixed-width types
|
|
1730
|
-
if (
|
|
1731
|
-
return
|
|
1732
|
-
if (
|
|
1733
|
-
return
|
|
2101
|
+
if (tsType === 'boolean')
|
|
2102
|
+
return 90;
|
|
2103
|
+
if (tsType === 'Date')
|
|
2104
|
+
return 130;
|
|
1734
2105
|
// Numeric fields - compact
|
|
1735
|
-
if (
|
|
2106
|
+
if (tsType === 'number') {
|
|
1736
2107
|
if (fieldNameLower.includes('year') || fieldNameLower.includes('age'))
|
|
1737
2108
|
return 80;
|
|
1738
|
-
if (fieldNameLower.includes('amount') || fieldNameLower.includes('price') ||
|
|
1739
|
-
|
|
2109
|
+
if (fieldNameLower.includes('amount') || fieldNameLower.includes('price') ||
|
|
2110
|
+
fieldNameLower.includes('cost') || fieldNameLower.includes('total'))
|
|
2111
|
+
return 130;
|
|
1740
2112
|
return 100;
|
|
1741
2113
|
}
|
|
1742
|
-
// ID fields
|
|
2114
|
+
// ID fields (typically UUIDs shown truncated or as links)
|
|
1743
2115
|
if (fieldNameLower.endsWith('id') && field.Length <= 50)
|
|
1744
|
-
return
|
|
2116
|
+
return 100;
|
|
1745
2117
|
// Email - needs more space
|
|
1746
2118
|
if (fieldNameLower.includes('email'))
|
|
1747
2119
|
return 220;
|
|
1748
2120
|
// Phone numbers
|
|
1749
2121
|
if (fieldNameLower.includes('phone') || fieldNameLower.includes('mobile') || fieldNameLower.includes('fax'))
|
|
1750
|
-
return
|
|
2122
|
+
return 140;
|
|
2123
|
+
// Description fields - give them adequate room (increased from 150)
|
|
2124
|
+
if (fieldNameLower.includes('description'))
|
|
2125
|
+
return 250;
|
|
1751
2126
|
// Name fields - medium width
|
|
1752
2127
|
if (fieldNameLower.includes('name') || fieldNameLower.includes('title')) {
|
|
1753
|
-
if (fieldNameLower === 'firstname' || fieldNameLower === 'lastname' ||
|
|
1754
|
-
|
|
1755
|
-
|
|
2128
|
+
if (fieldNameLower === 'firstname' || fieldNameLower === 'lastname' ||
|
|
2129
|
+
fieldNameLower === 'first name' || fieldNameLower === 'last name')
|
|
2130
|
+
return 130;
|
|
2131
|
+
return 180;
|
|
1756
2132
|
}
|
|
1757
2133
|
// Location fields
|
|
1758
2134
|
if (fieldNameLower.includes('city'))
|
|
1759
|
-
return
|
|
2135
|
+
return 130;
|
|
1760
2136
|
if (fieldNameLower.includes('state') || fieldNameLower.includes('country'))
|
|
1761
|
-
return
|
|
2137
|
+
return 110;
|
|
1762
2138
|
if (fieldNameLower.includes('zip') || fieldNameLower.includes('postal'))
|
|
1763
|
-
return
|
|
2139
|
+
return 100;
|
|
1764
2140
|
if (fieldNameLower.includes('address'))
|
|
1765
|
-
return
|
|
2141
|
+
return 220;
|
|
1766
2142
|
// Date-like strings
|
|
1767
2143
|
if (fieldNameLower.includes('date') || fieldNameLower.includes('time'))
|
|
1768
|
-
return
|
|
1769
|
-
// Status/Type fields
|
|
1770
|
-
if (fieldNameLower.includes('status') || fieldNameLower.includes('type') ||
|
|
1771
|
-
|
|
2144
|
+
return 130;
|
|
2145
|
+
// Status/Type/Category fields
|
|
2146
|
+
if (fieldNameLower.includes('status') || fieldNameLower.includes('type') ||
|
|
2147
|
+
fieldNameLower.includes('category') || fieldNameLower.includes('mode'))
|
|
2148
|
+
return 130;
|
|
1772
2149
|
// Code/abbreviation fields
|
|
1773
2150
|
if (fieldNameLower.includes('code') || fieldNameLower.includes('abbr'))
|
|
1774
|
-
return
|
|
1775
|
-
// Long text fields -
|
|
2151
|
+
return 110;
|
|
2152
|
+
// Long text fields - give them more room (nvarchar(max) has Length < 0)
|
|
2153
|
+
if (field.Length < 0)
|
|
2154
|
+
return 250; // nvarchar(max)
|
|
1776
2155
|
if (field.Length > 500)
|
|
1777
|
-
return
|
|
2156
|
+
return 220;
|
|
1778
2157
|
if (field.Length > 200)
|
|
1779
|
-
return
|
|
1780
|
-
// Default: estimate based on field length
|
|
1781
|
-
const estimatedChars = Math.min(field.Length,
|
|
1782
|
-
|
|
1783
|
-
const padding = 24;
|
|
1784
|
-
return Math.min(Math.max(estimatedChars * charWidth / 2 + padding, 80), 200);
|
|
2158
|
+
return 200;
|
|
2159
|
+
// Default: estimate based on field length with reasonable bounds
|
|
2160
|
+
const estimatedChars = Math.min(field.Length || 50, 40);
|
|
2161
|
+
return Math.max(estimatedChars * 6 + 24, 100);
|
|
1785
2162
|
}
|
|
1786
2163
|
mapFieldTypeToGridType(fieldType) {
|
|
1787
2164
|
switch (fieldType.toLowerCase()) {
|
|
@@ -1899,20 +2276,108 @@ export class EntityDataGridComponent {
|
|
|
1899
2276
|
}
|
|
1900
2277
|
generateAgColumnDefs(entity) {
|
|
1901
2278
|
const cols = [];
|
|
1902
|
-
|
|
2279
|
+
let visibleFields = entity.Fields.filter(f => this.shouldShowField(f));
|
|
2280
|
+
// Fallback: if no DefaultInView fields are defined, show first 10 non-system fields
|
|
2281
|
+
// sorted by importance to give users a reasonable starting point
|
|
2282
|
+
if (visibleFields.length === 0) {
|
|
2283
|
+
visibleFields = this.getDefaultFieldsFallback(entity);
|
|
2284
|
+
}
|
|
2285
|
+
// Sort fields by importance for better default ordering
|
|
2286
|
+
visibleFields = this.sortFieldsByImportance(visibleFields);
|
|
1903
2287
|
for (const field of visibleFields) {
|
|
1904
2288
|
const colDef = {
|
|
1905
2289
|
field: field.Name,
|
|
1906
2290
|
headerName: field.DisplayNameOrName,
|
|
1907
2291
|
width: this.estimateColumnWidth(field),
|
|
1908
2292
|
sortable: this._allowSorting,
|
|
1909
|
-
resizable: this._allowColumnResize
|
|
2293
|
+
resizable: this._allowColumnResize,
|
|
2294
|
+
headerTooltip: this.buildHeaderTooltip(field)
|
|
1910
2295
|
};
|
|
1911
2296
|
this.applyFieldFormatter(colDef, field);
|
|
1912
2297
|
cols.push(colDef);
|
|
1913
2298
|
}
|
|
1914
2299
|
return cols;
|
|
1915
2300
|
}
|
|
2301
|
+
/**
|
|
2302
|
+
* When no DefaultInView fields are set, fall back to showing the first 10
|
|
2303
|
+
* non-system, non-PK fields to give users something reasonable to start with.
|
|
2304
|
+
*/
|
|
2305
|
+
getDefaultFieldsFallback(entity) {
|
|
2306
|
+
return entity.Fields
|
|
2307
|
+
.filter(f => !f.Name.startsWith('__mj_') &&
|
|
2308
|
+
!(f.IsPrimaryKey && f.SQLFullType?.toLowerCase() === 'uniqueidentifier') &&
|
|
2309
|
+
(f.Length <= 500 || f.Length < 0) // Exclude very long text unless nvarchar(max)
|
|
2310
|
+
)
|
|
2311
|
+
.slice(0, 10);
|
|
2312
|
+
}
|
|
2313
|
+
/**
|
|
2314
|
+
* Sort fields by importance for better default column ordering.
|
|
2315
|
+
* Name fields first, then status/type, then other fields, with system fields last.
|
|
2316
|
+
*/
|
|
2317
|
+
sortFieldsByImportance(fields) {
|
|
2318
|
+
return [...fields].sort((a, b) => {
|
|
2319
|
+
const priorityA = this.getFieldPriority(a);
|
|
2320
|
+
const priorityB = this.getFieldPriority(b);
|
|
2321
|
+
return priorityA - priorityB;
|
|
2322
|
+
});
|
|
2323
|
+
}
|
|
2324
|
+
getFieldPriority(field) {
|
|
2325
|
+
const nameLower = field.Name.toLowerCase();
|
|
2326
|
+
// Name fields first
|
|
2327
|
+
if (field.IsNameField)
|
|
2328
|
+
return 0;
|
|
2329
|
+
if (nameLower === 'name' || nameLower === 'title')
|
|
2330
|
+
return 1;
|
|
2331
|
+
// Status and type fields are important
|
|
2332
|
+
if (nameLower === 'status')
|
|
2333
|
+
return 2;
|
|
2334
|
+
if (nameLower === 'type' || nameLower === 'category')
|
|
2335
|
+
return 3;
|
|
2336
|
+
// Other name-like fields
|
|
2337
|
+
if (nameLower.endsWith('name') && !nameLower.endsWith('typename'))
|
|
2338
|
+
return 4;
|
|
2339
|
+
// Description fields
|
|
2340
|
+
if (nameLower.includes('description'))
|
|
2341
|
+
return 5;
|
|
2342
|
+
// Date fields
|
|
2343
|
+
if (field.TSType === 'Date')
|
|
2344
|
+
return 50;
|
|
2345
|
+
// Foreign keys (usually IDs pointing to other entities)
|
|
2346
|
+
if (field.RelatedEntityID)
|
|
2347
|
+
return 60;
|
|
2348
|
+
// Numbers
|
|
2349
|
+
if (field.TSType === 'number')
|
|
2350
|
+
return 70;
|
|
2351
|
+
// Booleans toward the end
|
|
2352
|
+
if (field.TSType === 'boolean')
|
|
2353
|
+
return 80;
|
|
2354
|
+
// Long text fields at the end
|
|
2355
|
+
if ((field.Length > 500 || field.Length < 0))
|
|
2356
|
+
return 90;
|
|
2357
|
+
// System fields last (though they should be filtered out already)
|
|
2358
|
+
if (field.Name.startsWith('__mj_'))
|
|
2359
|
+
return 100;
|
|
2360
|
+
// Default priority
|
|
2361
|
+
return 40;
|
|
2362
|
+
}
|
|
2363
|
+
/**
|
|
2364
|
+
* Build a tooltip for the column header showing field details.
|
|
2365
|
+
*/
|
|
2366
|
+
buildHeaderTooltip(field) {
|
|
2367
|
+
const parts = [];
|
|
2368
|
+
// Show internal field name if different from display name
|
|
2369
|
+
if (field.DisplayName && field.DisplayName !== field.Name) {
|
|
2370
|
+
parts.push(`Field: ${field.Name}`);
|
|
2371
|
+
}
|
|
2372
|
+
// Show description if available
|
|
2373
|
+
if (field.Description) {
|
|
2374
|
+
parts.push(field.Description);
|
|
2375
|
+
}
|
|
2376
|
+
// Show type info
|
|
2377
|
+
const typeInfo = field.Type + (field.Length && field.Length > 0 ? `(${field.Length})` : '');
|
|
2378
|
+
parts.push(`Type: ${typeInfo}`);
|
|
2379
|
+
return parts.join('\n');
|
|
2380
|
+
}
|
|
1916
2381
|
applyFieldFormatter(colDef, field, customFormat) {
|
|
1917
2382
|
// Store type info for use in cell renderer
|
|
1918
2383
|
const fieldType = field.TSType;
|
|
@@ -1938,6 +2403,22 @@ export class EntityDataGridComponent {
|
|
|
1938
2403
|
(!extendedType && (fieldNameLower.includes('phone') ||
|
|
1939
2404
|
fieldNameLower.includes('mobile') ||
|
|
1940
2405
|
fieldNameLower.includes('fax')));
|
|
2406
|
+
// Check if this is a foreign key field (has a related entity)
|
|
2407
|
+
// Also check if this is a virtual display field that corresponds to an FK field
|
|
2408
|
+
let isForeignKey = !!field.RelatedEntityID;
|
|
2409
|
+
let fkField = field;
|
|
2410
|
+
let relatedEntityName = isForeignKey ? field.RelatedEntity : undefined;
|
|
2411
|
+
// If this field doesn't have RelatedEntityID but is virtual, check for a corresponding FK field
|
|
2412
|
+
// Pattern: for a field named "Category", look for "CategoryID" with RelatedEntityID
|
|
2413
|
+
if (!isForeignKey && field.IsVirtual && this._entityInfo) {
|
|
2414
|
+
const potentialFkFieldName = field.Name + 'ID';
|
|
2415
|
+
const correspondingFkField = this._entityInfo.Fields.find(f => f.Name.toLowerCase() === potentialFkFieldName.toLowerCase() && f.RelatedEntityID);
|
|
2416
|
+
if (correspondingFkField) {
|
|
2417
|
+
isForeignKey = true;
|
|
2418
|
+
fkField = correspondingFkField;
|
|
2419
|
+
relatedEntityName = correspondingFkField.RelatedEntity;
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
1941
2422
|
// Apply alignment - use custom format alignment if provided, otherwise default to right for numbers
|
|
1942
2423
|
const customAlign = customFormat?.align;
|
|
1943
2424
|
if (customAlign) {
|
|
@@ -1962,6 +2443,40 @@ export class EntityDataGridComponent {
|
|
|
1962
2443
|
if (params.value === null || params.value === undefined) {
|
|
1963
2444
|
return '<span class="cell-empty">—</span>';
|
|
1964
2445
|
}
|
|
2446
|
+
// Handle foreign key fields - render as clickable links
|
|
2447
|
+
// Only apply FK rendering if no custom format is specified
|
|
2448
|
+
if (isForeignKey && !customFormat && fkField?.RelatedEntityID) {
|
|
2449
|
+
// For virtual display fields, we show the display value but link to the FK value
|
|
2450
|
+
// For direct FK fields, the value IS the FK value
|
|
2451
|
+
const isVirtualDisplay = field.IsVirtual && fkField !== field;
|
|
2452
|
+
const displayValue = String(params.value);
|
|
2453
|
+
// Get the actual FK value - for virtual fields, look it up from row data
|
|
2454
|
+
let fkValue;
|
|
2455
|
+
if (isVirtualDisplay && params.data) {
|
|
2456
|
+
// Get the FK value from the corresponding FK field in the row data
|
|
2457
|
+
fkValue = String(params.data[fkField.Name] ?? '');
|
|
2458
|
+
}
|
|
2459
|
+
else {
|
|
2460
|
+
fkValue = displayValue;
|
|
2461
|
+
}
|
|
2462
|
+
// Skip if we don't have a valid FK value
|
|
2463
|
+
if (!fkValue) {
|
|
2464
|
+
return `<span>${HighlightUtil.escapeHtml(displayValue)}</span>`;
|
|
2465
|
+
}
|
|
2466
|
+
const escapedDisplayValue = HighlightUtil.escapeHtml(displayValue);
|
|
2467
|
+
const escapedFieldName = HighlightUtil.escapeHtml(fkField.Name);
|
|
2468
|
+
const escapedRelatedEntityId = HighlightUtil.escapeHtml(fkField.RelatedEntityID);
|
|
2469
|
+
const escapedRelatedEntityName = relatedEntityName ? HighlightUtil.escapeHtml(relatedEntityName) : '';
|
|
2470
|
+
// Build data attributes for the click handler
|
|
2471
|
+
const dataAttrs = `data-related-entity-id="${escapedRelatedEntityId}" data-record-id="${fkValue}" data-field-name="${escapedFieldName}"${relatedEntityName ? ` data-related-entity-name="${escapedRelatedEntityName}"` : ''}`;
|
|
2472
|
+
// Apply highlighting if filter text is set
|
|
2473
|
+
const displayText = this._filterText
|
|
2474
|
+
? HighlightUtil.highlight(displayValue, this._filterText, true)
|
|
2475
|
+
: escapedDisplayValue;
|
|
2476
|
+
// NOTE: Do NOT add onclick="event.stopPropagation()" here - it prevents AG Grid's cellClicked from firing
|
|
2477
|
+
const linkHtml = `<a href="javascript:void(0)" class="cell-link cell-fk-link" ${dataAttrs}>${displayText}</a>`;
|
|
2478
|
+
return linkHtml;
|
|
2479
|
+
}
|
|
1965
2480
|
let displayValue = '';
|
|
1966
2481
|
let extraClass = '';
|
|
1967
2482
|
let inlineStyle = '';
|
|
@@ -2348,20 +2863,36 @@ export class EntityDataGridComponent {
|
|
|
2348
2863
|
}
|
|
2349
2864
|
}
|
|
2350
2865
|
this.loading = true;
|
|
2866
|
+
this._aggregatesLoading = true;
|
|
2351
2867
|
this.errorMessage = '';
|
|
2352
2868
|
this.cdr.detectChanges();
|
|
2353
2869
|
const startTime = performance.now();
|
|
2354
2870
|
try {
|
|
2871
|
+
// Build aggregate expressions from config if present
|
|
2872
|
+
// Use EffectiveAggregatesConfig to check both _aggregatesConfig and _gridState.aggregates
|
|
2873
|
+
const effectiveAggConfig = this.EffectiveAggregatesConfig;
|
|
2874
|
+
let aggregateExpressions;
|
|
2875
|
+
if (effectiveAggConfig?.expressions?.length) {
|
|
2876
|
+
aggregateExpressions = effectiveAggConfig.expressions
|
|
2877
|
+
.filter(agg => agg.enabled !== false && agg.expression)
|
|
2878
|
+
.map(agg => ({
|
|
2879
|
+
expression: agg.expression,
|
|
2880
|
+
alias: agg.id || agg.label || agg.expression
|
|
2881
|
+
}));
|
|
2882
|
+
}
|
|
2355
2883
|
const rv = new RunView();
|
|
2356
2884
|
const result = await rv.RunView({
|
|
2357
2885
|
...runViewParams,
|
|
2358
|
-
ResultType: 'entity_object'
|
|
2886
|
+
ResultType: 'entity_object',
|
|
2887
|
+
Aggregates: aggregateExpressions
|
|
2359
2888
|
});
|
|
2360
2889
|
const loadTimeMs = performance.now() - startTime;
|
|
2361
2890
|
if (result.Success) {
|
|
2362
2891
|
this._allData = result.Results || [];
|
|
2363
2892
|
this.totalRowCount = result.TotalRowCount || this._allData.length;
|
|
2364
2893
|
this.processData();
|
|
2894
|
+
// Process aggregate results
|
|
2895
|
+
this.processAggregateResults(result.AggregateResults, result.AggregateExecutionTime);
|
|
2365
2896
|
// Reapply sort state to grid after data load to maintain visual indicators
|
|
2366
2897
|
// Use Promise.resolve() to defer until after Angular's change detection cycle
|
|
2367
2898
|
// has completed and AG Grid has processed the new row data
|
|
@@ -2557,6 +3088,39 @@ export class EntityDataGridComponent {
|
|
|
2557
3088
|
});
|
|
2558
3089
|
this.cdr.detectChanges();
|
|
2559
3090
|
}
|
|
3091
|
+
/**
|
|
3092
|
+
* Process aggregate results from RunView and emit the AggregatesLoaded event.
|
|
3093
|
+
* Builds the value map for easy lookup by expression or id.
|
|
3094
|
+
*/
|
|
3095
|
+
processAggregateResults(results, executionTime) {
|
|
3096
|
+
this._aggregatesLoading = false;
|
|
3097
|
+
if (!results || results.length === 0) {
|
|
3098
|
+
this._aggregateResults = [];
|
|
3099
|
+
this._aggregateValues.clear();
|
|
3100
|
+
return;
|
|
3101
|
+
}
|
|
3102
|
+
this._aggregateResults = results;
|
|
3103
|
+
this._aggregateValues.clear();
|
|
3104
|
+
// Build the values map, keyed by alias (which is set to id or expression)
|
|
3105
|
+
for (const result of results) {
|
|
3106
|
+
if (!result.error) {
|
|
3107
|
+
this._aggregateValues.set(result.alias, result.value);
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
// Also map by expression for easy lookup
|
|
3111
|
+
for (const result of results) {
|
|
3112
|
+
if (!result.error && result.expression !== result.alias) {
|
|
3113
|
+
this._aggregateValues.set(result.expression, result.value);
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
// Emit the aggregates loaded event
|
|
3117
|
+
this.AggregatesLoaded.emit({
|
|
3118
|
+
results: this._aggregateResults,
|
|
3119
|
+
values: this._aggregateValues,
|
|
3120
|
+
executionTime
|
|
3121
|
+
});
|
|
3122
|
+
this.cdr.detectChanges();
|
|
3123
|
+
}
|
|
2560
3124
|
getRowKey(entity) {
|
|
2561
3125
|
// Use composite key if available
|
|
2562
3126
|
if (entity.PrimaryKey) {
|
|
@@ -2658,6 +3222,41 @@ export class EntityDataGridComponent {
|
|
|
2658
3222
|
this.emitNavigationRequest(rowData.entity, pkString);
|
|
2659
3223
|
}
|
|
2660
3224
|
}
|
|
3225
|
+
/**
|
|
3226
|
+
* Handles cell click events to detect FK link clicks.
|
|
3227
|
+
* When a user clicks on a foreign key link, emits ForeignKeyClick event
|
|
3228
|
+
* for the parent component to handle navigation.
|
|
3229
|
+
*/
|
|
3230
|
+
onAgCellClicked(event) {
|
|
3231
|
+
// Check if the click was on an FK link
|
|
3232
|
+
const target = event.event?.target;
|
|
3233
|
+
if (!target) {
|
|
3234
|
+
return;
|
|
3235
|
+
}
|
|
3236
|
+
// Look for the FK link element (may be the target or a parent)
|
|
3237
|
+
const fkLink = target.closest('.cell-fk-link');
|
|
3238
|
+
if (!fkLink) {
|
|
3239
|
+
return;
|
|
3240
|
+
}
|
|
3241
|
+
// Prevent the row click handler from firing
|
|
3242
|
+
event.event?.stopPropagation();
|
|
3243
|
+
// Extract FK data from data attributes
|
|
3244
|
+
const relatedEntityId = fkLink.dataset['relatedEntityId'];
|
|
3245
|
+
const recordId = fkLink.dataset['recordId'];
|
|
3246
|
+
const fieldName = fkLink.dataset['fieldName'];
|
|
3247
|
+
const relatedEntityName = fkLink.dataset['relatedEntityName'];
|
|
3248
|
+
if (relatedEntityId && recordId && fieldName) {
|
|
3249
|
+
this.ForeignKeyClick.emit({
|
|
3250
|
+
relatedEntityId,
|
|
3251
|
+
recordId,
|
|
3252
|
+
fieldName,
|
|
3253
|
+
relatedEntityName
|
|
3254
|
+
});
|
|
3255
|
+
}
|
|
3256
|
+
else {
|
|
3257
|
+
//console.log('[FK Debug] Missing required data attributes, not emitting');
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
2661
3260
|
/**
|
|
2662
3261
|
* Emits a navigation request for the given entity record.
|
|
2663
3262
|
*/
|
|
@@ -2829,7 +3428,8 @@ export class EntityDataGridComponent {
|
|
|
2829
3428
|
// Build the grid state JSON matching ViewGridState format
|
|
2830
3429
|
const gridStateJson = {
|
|
2831
3430
|
columnSettings: state.columnSettings,
|
|
2832
|
-
sortSettings: state.sortSettings
|
|
3431
|
+
sortSettings: state.sortSettings,
|
|
3432
|
+
aggregates: state.aggregates
|
|
2833
3433
|
};
|
|
2834
3434
|
// Update the view entity's GridState
|
|
2835
3435
|
this._viewEntity.GridState = JSON.stringify(gridStateJson);
|
|
@@ -2927,7 +3527,12 @@ export class EntityDataGridComponent {
|
|
|
2927
3527
|
field: s.field,
|
|
2928
3528
|
dir: s.dir
|
|
2929
3529
|
}));
|
|
2930
|
-
|
|
3530
|
+
// Include current aggregates config in the state
|
|
3531
|
+
return {
|
|
3532
|
+
columnSettings,
|
|
3533
|
+
sortSettings,
|
|
3534
|
+
aggregates: this._aggregatesConfig || this._gridState?.aggregates
|
|
3535
|
+
};
|
|
2931
3536
|
}
|
|
2932
3537
|
applySortStateToGrid() {
|
|
2933
3538
|
if (!this.gridApi || this._sortState.length === 0) {
|
|
@@ -3554,21 +4159,25 @@ export class EntityDataGridComponent {
|
|
|
3554
4159
|
} if (rf & 2) {
|
|
3555
4160
|
let _t;
|
|
3556
4161
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.gridContainer = _t.first);
|
|
3557
|
-
} }, inputs: { Params: "Params", AllowLoad: "AllowLoad", AutoRefreshOnParamsChange: "AutoRefreshOnParamsChange", PaginationMode: "PaginationMode", PageSize: "PageSize", CacheBlockSize: "CacheBlockSize", MaxBlocksInCache: "MaxBlocksInCache", Data: "Data", Columns: "Columns", GridState: "GridState", AllowColumnReorder: "AllowColumnReorder", AllowColumnResize: "AllowColumnResize", AllowColumnToggle: "AllowColumnToggle", ShowHeader: "ShowHeader", AllowSorting: "AllowSorting", AllowMultiSort: "AllowMultiSort", ServerSideSorting: "ServerSideSorting", AllowColumnFilters: "AllowColumnFilters", ShowSearch: "ShowSearch", SelectionMode: "SelectionMode", SelectedKeys: "SelectedKeys", KeyField: "KeyField", EditMode: "EditMode", AllowAdd: "AllowAdd", AllowDelete: "AllowDelete", Height: "Height", RowHeight: "RowHeight", VirtualScroll: "VirtualScroll", ShowRowNumbers: "ShowRowNumbers", Striped: "Striped", GridLines: "GridLines", VisualConfig: "VisualConfig", ShowToolbar: "ShowToolbar", ToolbarConfig: "ToolbarConfig", StateKey: "StateKey", AutoPersistState: "AutoPersistState", StatePersistDebounce: "StatePersistDebounce", RefreshDebounce: "RefreshDebounce", FilterText: "FilterText", ShowNewButton: "ShowNewButton", ShowRefreshButton: "ShowRefreshButton", ShowExportButton: "ShowExportButton", ShowDeleteButton: "ShowDeleteButton", ShowCompareButton: "ShowCompareButton", ShowMergeButton: "ShowMergeButton", ShowAddToListButton: "ShowAddToListButton", ShowDuplicateSearchButton: "ShowDuplicateSearchButton", ShowCommunicationButton: "ShowCommunicationButton", AutoNavigate: "AutoNavigate", NavigateOnDoubleClick: "NavigateOnDoubleClick", CreateRecordMode: "CreateRecordMode", NewRecordValues: "NewRecordValues", ShowEntityActionButtons: "ShowEntityActionButtons", EntityActions: "EntityActions" }, outputs: { BeforeRowSelect: "BeforeRowSelect", AfterRowSelect: "AfterRowSelect", BeforeRowDeselect: "BeforeRowDeselect", AfterRowDeselect: "AfterRowDeselect", SelectionChange: "SelectionChange", BeforeRowClick: "BeforeRowClick", AfterRowClick: "AfterRowClick", BeforeRowDoubleClick: "BeforeRowDoubleClick", AfterRowDoubleClick: "AfterRowDoubleClick", BeforeCellEdit: "BeforeCellEdit", AfterCellEditBegin: "AfterCellEditBegin", BeforeCellEditCommit: "BeforeCellEditCommit", AfterCellEditCommit: "AfterCellEditCommit", BeforeCellEditCancel: "BeforeCellEditCancel", AfterCellEditCancel: "AfterCellEditCancel", BeforeRowSave: "BeforeRowSave", AfterRowSave: "AfterRowSave", BeforeRowDelete: "BeforeRowDelete", AfterRowDelete: "AfterRowDelete", BeforeDataLoad: "BeforeDataLoad", AfterDataLoad: "AfterDataLoad", BeforeDataRefresh: "BeforeDataRefresh", AfterDataRefresh: "AfterDataRefresh", BeforeSort: "BeforeSort", AfterSort: "AfterSort", BeforeColumnReorder: "BeforeColumnReorder", AfterColumnReorder: "AfterColumnReorder", BeforeColumnResize: "BeforeColumnResize", AfterColumnResize: "AfterColumnResize", BeforeColumnVisibilityChange: "BeforeColumnVisibilityChange", AfterColumnVisibilityChange: "AfterColumnVisibilityChange", GridStateChanged: "GridStateChanged", AddRequested: "AddRequested", DeleteRequested: "DeleteRequested", ExportRequested: "ExportRequested", NewButtonClick: "NewButtonClick", RefreshButtonClick: "RefreshButtonClick", ExportButtonClick: "ExportButtonClick", DeleteButtonClick: "DeleteButtonClick", CompareButtonClick: "CompareButtonClick", MergeButtonClick: "MergeButtonClick", AddToListButtonClick: "AddToListButtonClick", DuplicateSearchButtonClick: "DuplicateSearchButtonClick", CommunicationButtonClick: "CommunicationButtonClick", NavigationRequested: "NavigationRequested", NewRecordDialogRequested: "NewRecordDialogRequested", NewRecordTabRequested: "NewRecordTabRequested", CompareRecordsRequested: "CompareRecordsRequested", MergeRecordsRequested: "MergeRecordsRequested", CommunicationRequested: "CommunicationRequested", DuplicateSearchRequested: "DuplicateSearchRequested", AddToListRequested: "AddToListRequested", LoadEntityActionsRequested: "LoadEntityActionsRequested", EntityActionRequested: "EntityActionRequested" }, decls: 10, vars: 12, consts: [["gridContainer", ""], ["class", "mj-grid-toolbar", 4, "ngIf"], [1, "mj-grid-content"], ["class", "mj-grid-loading-overlay", 4, "ngIf"], ["class", "mj-grid-error", 4, "ngIf"], ["class", "mj-grid-empty", 4, "ngIf"], ["class", "mj-ag-grid ag-theme-alpine", 3, "theme", "columnDefs", "rowData", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight", "gridReady", "rowClicked", "rowDoubleClicked", "sortChanged", "selectionChanged", "columnResized", "columnMoved", 4, "ngIf"], ["class", "mj-ag-grid ag-theme-alpine", 3, "theme", "columnDefs", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight", "rowModelType", "cacheBlockSize", "maxBlocksInCache", "infiniteInitialRowCount", "cacheOverflowSize", "gridReady", "rowClicked", "rowDoubleClicked", "sortChanged", "selectionChanged", "columnResized", "columnMoved", 4, "ngIf"], [3, "closed", "visible", "config"], [1, "mj-grid-toolbar"], [1, "toolbar-left"], ["class", "toolbar-search", 4, "ngIf"], [4, "ngFor", "ngForOf"], [1, "toolbar-center"], ["class", "row-count", 4, "ngIf"], ["class", "selection-count", 4, "ngIf"], [1, "toolbar-right"], ["class", "toolbar-button", "title", "Create new record", 3, "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Refresh data", 3, "disabled", "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Export to Excel", 3, "click", 4, "ngIf"], ["class", "toolbar-button toolbar-button-danger", "title", "Delete selected records", 3, "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Compare selected records", 3, "disabled", "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Merge selected records", 3, "disabled", "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Add selected records to a list", 3, "disabled", "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Search for duplicate records", 3, "disabled", "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Send message to selected records", 3, "disabled", "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Add New", 3, "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Refresh", 3, "disabled", "click", 4, "ngIf"], ["class", "toolbar-button toolbar-button-danger", "title", "Delete Selected", 3, "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Export", 3, "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Column Chooser", 3, "click", 4, "ngIf"], ["class", "toolbar-overflow", 3, "click", 4, "ngIf"], [1, "toolbar-search"], [1, "fa-solid", "fa-search", "search-icon"], ["type", "text", 1, "search-input", 3, "input", "placeholder", "value"], ["class", "search-clear", 3, "click", 4, "ngIf"], [1, "search-clear", 3, "click"], [1, "fa-solid", "fa-times"], ["class", "toolbar-button", 3, "class", "disabled", "title", "click", 4, "ngIf"], [1, "toolbar-button", 3, "click", "disabled", "title"], [3, "class", 4, "ngIf"], [4, "ngIf"], [1, "row-count"], [1, "selection-count"], ["title", "Create new record", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "button-text"], ["title", "Refresh data", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-arrows-rotate"], ["title", "Export to Excel", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-file-excel"], ["title", "Delete selected records", 1, "toolbar-button", "toolbar-button-danger", 3, "click"], [1, "fa-solid", "fa-trash"], ["title", "Compare selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-code-compare"], ["title", "Merge selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-code-merge"], ["title", "Add selected records to a list", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-list-check"], ["title", "Search for duplicate records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-magnifying-glass-plus"], ["title", "Send message to selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-envelope"], ["title", "Add New", 1, "toolbar-button", 3, "click"], ["title", "Refresh", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-refresh"], ["title", "Delete Selected", 1, "toolbar-button", "toolbar-button-danger", 3, "click"], ["title", "Export", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-download"], ["title", "Column Chooser", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-columns"], [1, "toolbar-overflow", 3, "click"], ["title", "More actions", 1, "toolbar-button", "overflow-trigger", 3, "click"], [1, "fa-solid", "fa-ellipsis-vertical"], ["class", "overflow-menu", 4, "ngIf"], [1, "overflow-menu"], ["class", "overflow-item", 3, "click", 4, "ngIf"], ["class", "overflow-divider", 4, "ngIf"], [1, "overflow-item", 3, "click"], [1, "overflow-divider"], [1, "overflow-section-label"], ["class", "overflow-item", 3, "disabled", "click", 4, "ngFor", "ngForOf"], [1, "overflow-item", 3, "click", "disabled"], [1, "mj-grid-loading-overlay"], ["text", "Loading..."], [1, "mj-grid-error"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "error-retry", 3, "click"], [1, "mj-grid-empty"], [1, "fa-solid", "fa-inbox"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "gridReady", "rowClicked", "rowDoubleClicked", "sortChanged", "selectionChanged", "columnResized", "columnMoved", "theme", "columnDefs", "rowData", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "gridReady", "rowClicked", "rowDoubleClicked", "sortChanged", "selectionChanged", "columnResized", "columnMoved", "theme", "columnDefs", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight", "rowModelType", "cacheBlockSize", "maxBlocksInCache", "infiniteInitialRowCount", "cacheOverflowSize"]], template: function EntityDataGridComponent_Template(rf, ctx) { if (rf & 1) {
|
|
4162
|
+
} }, inputs: { Params: "Params", AllowLoad: "AllowLoad", AutoRefreshOnParamsChange: "AutoRefreshOnParamsChange", PaginationMode: "PaginationMode", PageSize: "PageSize", CacheBlockSize: "CacheBlockSize", MaxBlocksInCache: "MaxBlocksInCache", Data: "Data", Columns: "Columns", GridState: "GridState", AllowColumnReorder: "AllowColumnReorder", AllowColumnResize: "AllowColumnResize", AllowColumnToggle: "AllowColumnToggle", ShowHeader: "ShowHeader", AllowSorting: "AllowSorting", AllowMultiSort: "AllowMultiSort", ServerSideSorting: "ServerSideSorting", AllowColumnFilters: "AllowColumnFilters", ShowSearch: "ShowSearch", SelectionMode: "SelectionMode", SelectedKeys: "SelectedKeys", KeyField: "KeyField", EditMode: "EditMode", AllowAdd: "AllowAdd", AllowDelete: "AllowDelete", Height: "Height", RowHeight: "RowHeight", WrapText: "WrapText", VirtualScroll: "VirtualScroll", ShowRowNumbers: "ShowRowNumbers", Striped: "Striped", GridLines: "GridLines", VisualConfig: "VisualConfig", ShowToolbar: "ShowToolbar", ToolbarConfig: "ToolbarConfig", StateKey: "StateKey", AutoPersistState: "AutoPersistState", StatePersistDebounce: "StatePersistDebounce", RefreshDebounce: "RefreshDebounce", FilterText: "FilterText", ShowNewButton: "ShowNewButton", ShowRefreshButton: "ShowRefreshButton", ShowExportButton: "ShowExportButton", ShowDeleteButton: "ShowDeleteButton", ShowCompareButton: "ShowCompareButton", ShowMergeButton: "ShowMergeButton", ShowAddToListButton: "ShowAddToListButton", ShowDuplicateSearchButton: "ShowDuplicateSearchButton", ShowCommunicationButton: "ShowCommunicationButton", AutoNavigate: "AutoNavigate", NavigateOnDoubleClick: "NavigateOnDoubleClick", CreateRecordMode: "CreateRecordMode", NewRecordValues: "NewRecordValues", ShowEntityActionButtons: "ShowEntityActionButtons", EntityActions: "EntityActions", AggregatesConfig: "AggregatesConfig" }, outputs: { AggregatesLoaded: "AggregatesLoaded", BeforeRowSelect: "BeforeRowSelect", AfterRowSelect: "AfterRowSelect", BeforeRowDeselect: "BeforeRowDeselect", AfterRowDeselect: "AfterRowDeselect", SelectionChange: "SelectionChange", BeforeRowClick: "BeforeRowClick", AfterRowClick: "AfterRowClick", BeforeRowDoubleClick: "BeforeRowDoubleClick", AfterRowDoubleClick: "AfterRowDoubleClick", ForeignKeyClick: "ForeignKeyClick", BeforeCellEdit: "BeforeCellEdit", AfterCellEditBegin: "AfterCellEditBegin", BeforeCellEditCommit: "BeforeCellEditCommit", AfterCellEditCommit: "AfterCellEditCommit", BeforeCellEditCancel: "BeforeCellEditCancel", AfterCellEditCancel: "AfterCellEditCancel", BeforeRowSave: "BeforeRowSave", AfterRowSave: "AfterRowSave", BeforeRowDelete: "BeforeRowDelete", AfterRowDelete: "AfterRowDelete", BeforeDataLoad: "BeforeDataLoad", AfterDataLoad: "AfterDataLoad", BeforeDataRefresh: "BeforeDataRefresh", AfterDataRefresh: "AfterDataRefresh", BeforeSort: "BeforeSort", AfterSort: "AfterSort", BeforeColumnReorder: "BeforeColumnReorder", AfterColumnReorder: "AfterColumnReorder", BeforeColumnResize: "BeforeColumnResize", AfterColumnResize: "AfterColumnResize", BeforeColumnVisibilityChange: "BeforeColumnVisibilityChange", AfterColumnVisibilityChange: "AfterColumnVisibilityChange", GridStateChanged: "GridStateChanged", AddRequested: "AddRequested", DeleteRequested: "DeleteRequested", ExportRequested: "ExportRequested", NewButtonClick: "NewButtonClick", RefreshButtonClick: "RefreshButtonClick", ExportButtonClick: "ExportButtonClick", DeleteButtonClick: "DeleteButtonClick", CompareButtonClick: "CompareButtonClick", MergeButtonClick: "MergeButtonClick", AddToListButtonClick: "AddToListButtonClick", DuplicateSearchButtonClick: "DuplicateSearchButtonClick", CommunicationButtonClick: "CommunicationButtonClick", NavigationRequested: "NavigationRequested", NewRecordDialogRequested: "NewRecordDialogRequested", NewRecordTabRequested: "NewRecordTabRequested", CompareRecordsRequested: "CompareRecordsRequested", MergeRecordsRequested: "MergeRecordsRequested", CommunicationRequested: "CommunicationRequested", DuplicateSearchRequested: "DuplicateSearchRequested", AddToListRequested: "AddToListRequested", LoadEntityActionsRequested: "LoadEntityActionsRequested", EntityActionRequested: "EntityActionRequested" }, decls: 12, vars: 14, consts: [["gridContainer", ""], ["class", "mj-grid-toolbar", 4, "ngIf"], ["class", "mj-aggregate-cards", 4, "ngIf"], [1, "mj-grid-content"], ["class", "mj-grid-loading-overlay", 4, "ngIf"], ["class", "mj-grid-error", 4, "ngIf"], ["class", "mj-grid-empty", 4, "ngIf"], ["class", "mj-ag-grid ag-theme-alpine", 3, "theme", "columnDefs", "rowData", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight", "gridReady", "cellClicked", "rowClicked", "rowDoubleClicked", "sortChanged", "selectionChanged", "columnResized", "columnMoved", 4, "ngIf"], ["class", "mj-ag-grid ag-theme-alpine", 3, "theme", "columnDefs", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight", "rowModelType", "cacheBlockSize", "maxBlocksInCache", "infiniteInitialRowCount", "cacheOverflowSize", "gridReady", "cellClicked", "rowClicked", "rowDoubleClicked", "sortChanged", "selectionChanged", "columnResized", "columnMoved", 4, "ngIf"], ["class", "mj-aggregate-summary", 4, "ngIf"], [3, "closed", "visible", "config"], [1, "mj-grid-toolbar"], [1, "toolbar-left"], ["class", "toolbar-search", 4, "ngIf"], [4, "ngFor", "ngForOf"], [1, "toolbar-center"], ["class", "row-count", 4, "ngIf"], ["class", "selection-count", 4, "ngIf"], [1, "toolbar-right"], ["class", "toolbar-button", "title", "Create new record", 3, "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Refresh data", 3, "disabled", "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Export to Excel", 3, "click", 4, "ngIf"], ["class", "toolbar-button toolbar-button-danger", "title", "Delete selected records", 3, "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Compare selected records", 3, "disabled", "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Merge selected records", 3, "disabled", "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Add selected records to a list", 3, "disabled", "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Search for duplicate records", 3, "disabled", "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Send message to selected records", 3, "disabled", "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Add New", 3, "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Refresh", 3, "disabled", "click", 4, "ngIf"], ["class", "toolbar-button toolbar-button-danger", "title", "Delete Selected", 3, "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Export", 3, "click", 4, "ngIf"], ["class", "toolbar-button", "title", "Column Chooser", 3, "click", 4, "ngIf"], ["class", "toolbar-overflow", 3, "click", 4, "ngIf"], [1, "toolbar-search"], [1, "fa-solid", "fa-search", "search-icon"], ["type", "text", 1, "search-input", 3, "input", "placeholder", "value"], ["class", "search-clear", 3, "click", 4, "ngIf"], [1, "search-clear", 3, "click"], [1, "fa-solid", "fa-times"], ["class", "toolbar-button", 3, "class", "disabled", "title", "click", 4, "ngIf"], [1, "toolbar-button", 3, "click", "disabled", "title"], [3, "class", 4, "ngIf"], [4, "ngIf"], [1, "row-count"], [1, "selection-count"], ["title", "Create new record", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "button-text"], ["title", "Refresh data", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-arrows-rotate"], ["title", "Export to Excel", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-file-excel"], ["title", "Delete selected records", 1, "toolbar-button", "toolbar-button-danger", 3, "click"], [1, "fa-solid", "fa-trash"], ["title", "Compare selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-code-compare"], ["title", "Merge selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-code-merge"], ["title", "Add selected records to a list", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-list-check"], ["title", "Search for duplicate records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-magnifying-glass-plus"], ["title", "Send message to selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-envelope"], ["title", "Add New", 1, "toolbar-button", 3, "click"], ["title", "Refresh", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-refresh"], ["title", "Delete Selected", 1, "toolbar-button", "toolbar-button-danger", 3, "click"], ["title", "Export", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-download"], ["title", "Column Chooser", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-columns"], [1, "toolbar-overflow", 3, "click"], ["title", "More actions", 1, "toolbar-button", "overflow-trigger", 3, "click"], [1, "fa-solid", "fa-ellipsis-vertical"], ["class", "overflow-menu", 4, "ngIf"], [1, "overflow-menu"], ["class", "overflow-item", 3, "click", 4, "ngIf"], ["class", "overflow-divider", 4, "ngIf"], [1, "overflow-item", 3, "click"], [1, "overflow-divider"], [1, "overflow-section-label"], ["class", "overflow-item", 3, "disabled", "click", 4, "ngFor", "ngForOf"], [1, "overflow-item", 3, "click", "disabled"], [1, "mj-aggregate-cards"], ["class", "aggregate-card-loading", 4, "ngIf"], [1, "aggregate-card"], ["class", "aggregate-card-icon", 4, "ngIf"], [1, "aggregate-card-content"], [1, "aggregate-card-label"], [1, "aggregate-card-value"], [1, "aggregate-card-icon"], [1, "aggregate-card-loading"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "mj-grid-loading-overlay"], ["text", "Loading..."], [1, "mj-grid-error"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "error-retry", 3, "click"], [1, "mj-grid-empty"], [1, "fa-solid", "fa-inbox"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "gridReady", "cellClicked", "rowClicked", "rowDoubleClicked", "sortChanged", "selectionChanged", "columnResized", "columnMoved", "theme", "columnDefs", "rowData", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "gridReady", "cellClicked", "rowClicked", "rowDoubleClicked", "sortChanged", "selectionChanged", "columnResized", "columnMoved", "theme", "columnDefs", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight", "rowModelType", "cacheBlockSize", "maxBlocksInCache", "infiniteInitialRowCount", "cacheOverflowSize"], [1, "mj-aggregate-summary"], [1, "aggregate-summary-content"], ["class", "aggregate-loading", 4, "ngIf"], [1, "aggregate-summary-item"], [1, "agg-summary-label"], [1, "agg-summary-value"], [1, "aggregate-loading"]], template: function EntityDataGridComponent_Template(rf, ctx) { if (rf & 1) {
|
|
3558
4163
|
const _r1 = i0.ɵɵgetCurrentView();
|
|
3559
4164
|
i0.ɵɵelementStart(0, "div", null, 0);
|
|
3560
|
-
i0.ɵɵtemplate(2, EntityDataGridComponent_div_2_Template, 24, 20, "div", 1);
|
|
3561
|
-
i0.ɵɵelementStart(
|
|
3562
|
-
i0.ɵɵtemplate(
|
|
3563
|
-
i0.ɵɵelementEnd()
|
|
3564
|
-
i0.ɵɵ
|
|
3565
|
-
i0.ɵɵ
|
|
4165
|
+
i0.ɵɵtemplate(2, EntityDataGridComponent_div_2_Template, 24, 20, "div", 1)(3, EntityDataGridComponent_div_3_Template, 3, 2, "div", 2);
|
|
4166
|
+
i0.ɵɵelementStart(4, "div", 3);
|
|
4167
|
+
i0.ɵɵtemplate(5, EntityDataGridComponent_div_5_Template, 2, 0, "div", 4)(6, EntityDataGridComponent_div_6_Template, 6, 1, "div", 5)(7, EntityDataGridComponent_div_7_Template, 4, 0, "div", 6)(8, EntityDataGridComponent_ag_grid_angular_8_Template, 1, 9, "ag-grid-angular", 7)(9, EntityDataGridComponent_ag_grid_angular_9_Template, 1, 13, "ag-grid-angular", 8);
|
|
4168
|
+
i0.ɵɵelementEnd();
|
|
4169
|
+
i0.ɵɵtemplate(10, EntityDataGridComponent_div_10_Template, 4, 2, "div", 9);
|
|
4170
|
+
i0.ɵɵelementEnd();
|
|
4171
|
+
i0.ɵɵelementStart(11, "mj-export-dialog", 10);
|
|
4172
|
+
i0.ɵɵlistener("closed", function EntityDataGridComponent_Template_mj_export_dialog_closed_11_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onExportDialogClosed($event)); });
|
|
3566
4173
|
i0.ɵɵelementEnd();
|
|
3567
4174
|
} if (rf & 2) {
|
|
3568
4175
|
i0.ɵɵclassMap(ctx.gridContainerClasses.join(" "));
|
|
3569
4176
|
i0.ɵɵstyleProp("height", ctx.gridHeightStyle);
|
|
3570
4177
|
i0.ɵɵadvance(2);
|
|
3571
4178
|
i0.ɵɵproperty("ngIf", ctx.ShowToolbar);
|
|
4179
|
+
i0.ɵɵadvance();
|
|
4180
|
+
i0.ɵɵproperty("ngIf", ctx.ShowAggregatePanel);
|
|
3572
4181
|
i0.ɵɵadvance(2);
|
|
3573
4182
|
i0.ɵɵproperty("ngIf", ctx.loading && ctx.rowData.length === 0);
|
|
3574
4183
|
i0.ɵɵadvance();
|
|
@@ -3580,8 +4189,10 @@ export class EntityDataGridComponent {
|
|
|
3580
4189
|
i0.ɵɵadvance();
|
|
3581
4190
|
i0.ɵɵproperty("ngIf", !ctx.errorMessage && ctx.PaginationMode === "infinite");
|
|
3582
4191
|
i0.ɵɵadvance();
|
|
4192
|
+
i0.ɵɵproperty("ngIf", ctx.ShowAggregateSummary);
|
|
4193
|
+
i0.ɵɵadvance();
|
|
3583
4194
|
i0.ɵɵproperty("visible", ctx.showExportDialog)("config", ctx.exportDialogConfig);
|
|
3584
|
-
} }, dependencies: [i2.NgForOf, i2.NgIf, i3.AgGridAngular, i4.LoadingComponent, i1.ExportDialogComponent], styles: ["\n\n\n\n\n[_nghost-%COMP%] {\n \n\n --grid-border-color: #e0e0e0;\n --grid-border-radius: 0px;\n --grid-background: #ffffff;\n\n \n\n --grid-header-bg: #fafafa;\n --grid-header-text: #333333;\n --grid-header-font-weight: 600;\n --grid-header-height: 40px;\n --grid-header-border-color: #e0e0e0;\n\n \n\n --grid-row-height: 40px;\n --grid-row-bg: #ffffff;\n --grid-row-bg-alt: #fafafa;\n --grid-row-hover-bg: #f5f5f5;\n --grid-row-selected-bg: #fff9e6;\n --grid-row-selected-hover-bg: #fff3cc;\n\n \n\n --grid-cell-padding: 8px 12px;\n --grid-cell-text: #333333;\n --grid-cell-border-color: #f0f0f0;\n\n \n\n --grid-checkbox-color: #2196F3;\n --grid-selection-indicator-color: #f9a825;\n\n \n\n --grid-edit-cell-bg: #ffffff;\n --grid-edit-cell-border: #2196F3;\n --grid-edit-cell-shadow: 0 0 0 3px rgba(33, 150, 243, 0.1);\n\n \n\n --grid-sort-indicator-color: #2196F3;\n\n \n\n --grid-toolbar-bg: #ffffff;\n --grid-toolbar-height: 48px;\n --grid-toolbar-border-color: #e0e0e0;\n\n \n\n --grid-loading-overlay-bg: rgba(255, 255, 255, 0.8);\n\n \n\n --grid-empty-text-color: #999999;\n --grid-empty-icon-color: #cccccc;\n\n display: block;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n}\n\n\n\n\n\n\n.mj-grid-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--grid-border-color);\n border-radius: var(--grid-border-radius);\n background: var(--grid-background);\n overflow: hidden;\n}\n\n\n\n\n\n\n.mj-grid-toolbar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n min-height: var(--grid-toolbar-height);\n padding: 0 12px;\n background: var(--grid-toolbar-bg);\n border-bottom: 1px solid var(--grid-toolbar-border-color);\n gap: 12px;\n}\n\n.toolbar-left[_ngcontent-%COMP%], \n.toolbar-right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-center[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n color: #666;\n font-size: 13px;\n}\n\n.toolbar-search[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n position: relative;\n}\n\n.search-icon[_ngcontent-%COMP%] {\n position: absolute;\n left: 10px;\n color: #999;\n font-size: 13px;\n}\n\n.search-input[_ngcontent-%COMP%] {\n padding: 6px 30px 6px 32px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 13px;\n width: 200px;\n transition: border-color 0.2s, box-shadow 0.2s;\n}\n\n.search-input[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: var(--grid-selection-indicator-color);\n box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.1);\n}\n\n.search-clear[_ngcontent-%COMP%] {\n position: absolute;\n right: 6px;\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px;\n color: #999;\n font-size: 12px;\n}\n\n.search-clear[_ngcontent-%COMP%]:hover {\n color: #666;\n}\n\n.toolbar-button[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: #f5f5f5;\n border: 1px solid #ddd;\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n color: #333;\n transition: background-color 0.2s, border-color 0.2s;\n}\n\n.toolbar-button[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #eee;\n border-color: #ccc;\n}\n\n.toolbar-button[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.toolbar-button[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.toolbar-button-danger[_ngcontent-%COMP%] {\n color: #d32f2f;\n}\n\n.toolbar-button-danger[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #ffebee;\n border-color: #ffcdd2;\n}\n\n.row-count[_ngcontent-%COMP%], \n.selection-count[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n\n\n\n\n\n.mj-grid-content[_ngcontent-%COMP%] {\n flex: 1;\n position: relative;\n overflow: hidden;\n}\n\n.mj-grid-scroll-container[_ngcontent-%COMP%] {\n height: 100%;\n overflow: auto;\n}\n\n\n\n\n\n\n.mj-grid-loading-overlay[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--grid-loading-overlay-bg);\n z-index: 10;\n}\n\n\n\n\n\n\n.mj-grid-error[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n color: #d32f2f;\n gap: 12px;\n}\n\n.mj-grid-error[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 32px;\n}\n\n.error-retry[_ngcontent-%COMP%] {\n padding: 8px 16px;\n background: #d32f2f;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n}\n\n.error-retry[_ngcontent-%COMP%]:hover {\n background: #c62828;\n}\n\n\n\n\n\n\n.mj-grid-empty[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n color: var(--grid-empty-text-color);\n gap: 12px;\n}\n\n.mj-grid-empty[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n color: var(--grid-empty-icon-color);\n}\n\n\n\n\n\n\n.mj-grid-header[_ngcontent-%COMP%] {\n display: flex;\n min-height: var(--grid-header-height);\n background: var(--grid-header-bg);\n border-bottom: 2px solid var(--grid-header-border-color);\n position: sticky;\n top: 0;\n z-index: 5;\n}\n\n.mj-grid-header-cell[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: var(--grid-cell-padding);\n font-weight: var(--grid-header-font-weight);\n color: var(--grid-header-text);\n font-size: 13px;\n user-select: none;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right: 1px solid var(--grid-cell-border-color);\n flex-shrink: 0;\n}\n\n.mj-grid-header-cell[_ngcontent-%COMP%]:last-child {\n border-right: none;\n}\n\n.mj-grid-header-cell.sortable[_ngcontent-%COMP%] {\n cursor: pointer;\n}\n\n.mj-grid-header-cell.sortable[_ngcontent-%COMP%]:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n\n.header-text[_ngcontent-%COMP%] {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.sort-indicator[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n margin-left: 6px;\n color: var(--grid-sort-indicator-color);\n}\n\n.sort-indicator[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n\n.sort-index[_ngcontent-%COMP%] {\n font-size: 10px;\n margin-left: 2px;\n font-weight: normal;\n}\n\n\n\n\n\n\n.mj-grid-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: stretch;\n background: var(--grid-row-bg);\n transition: background-color 0.15s;\n cursor: default;\n}\n\n.mj-grid-row[_ngcontent-%COMP%]:hover {\n background: var(--grid-row-hover-bg);\n}\n\n.mj-grid-row.grid-row-alt[_ngcontent-%COMP%] {\n background: var(--grid-row-bg-alt);\n}\n\n.mj-grid-row.grid-row-alt[_ngcontent-%COMP%]:hover {\n background: var(--grid-row-hover-bg);\n}\n\n.mj-grid-row.grid-row-selected[_ngcontent-%COMP%] {\n background: var(--grid-row-selected-bg);\n}\n\n.mj-grid-row.grid-row-selected[_ngcontent-%COMP%]:hover {\n background: var(--grid-row-selected-hover-bg);\n}\n\n.mj-grid-row.grid-row-editing[_ngcontent-%COMP%] {\n background: #fffde7;\n}\n\n.mj-grid-row.grid-row-dirty[_ngcontent-%COMP%] {\n border-left: 3px solid #ff9800;\n}\n\n\n\n\n\n\n.mj-grid-cell[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: var(--grid-cell-padding);\n color: var(--grid-cell-text);\n font-size: 13px;\n overflow: hidden;\n border-right: 1px solid transparent;\n flex-shrink: 0;\n}\n\n.mj-grid-cell[_ngcontent-%COMP%]:last-child {\n border-right: none;\n}\n\n\n\n.grid-lines-horizontal[_ngcontent-%COMP%] .mj-grid-row[_ngcontent-%COMP%] {\n border-bottom: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-vertical[_ngcontent-%COMP%] .mj-grid-cell[_ngcontent-%COMP%] {\n border-right: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-both[_ngcontent-%COMP%] .mj-grid-row[_ngcontent-%COMP%] {\n border-bottom: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-both[_ngcontent-%COMP%] .mj-grid-cell[_ngcontent-%COMP%] {\n border-right: 1px solid var(--grid-cell-border-color);\n}\n\n\n\n.mj-grid-cell.align-left[_ngcontent-%COMP%] {\n justify-content: flex-start;\n}\n\n.mj-grid-cell.align-center[_ngcontent-%COMP%] {\n justify-content: center;\n}\n\n.mj-grid-cell.align-right[_ngcontent-%COMP%] {\n justify-content: flex-end;\n}\n\n.cell-content[_ngcontent-%COMP%] {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n\n\n.row-number-cell[_ngcontent-%COMP%] {\n width: 50px;\n min-width: 50px;\n max-width: 50px;\n justify-content: center;\n color: #999;\n font-size: 12px;\n background: var(--grid-header-bg);\n}\n\n.checkbox-cell[_ngcontent-%COMP%] {\n width: 40px;\n min-width: 40px;\n max-width: 40px;\n justify-content: center;\n}\n\n.checkbox-cell[_ngcontent-%COMP%] input[type=\"checkbox\"][_ngcontent-%COMP%] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n accent-color: var(--grid-checkbox-color);\n}\n\n\n\n\n\n\n.mj-grid-virtual-spacer[_ngcontent-%COMP%] {\n flex-shrink: 0;\n}\n\n\n\n\n\n\n@media (max-width: 768px) {\n .mj-grid-toolbar[_ngcontent-%COMP%] {\n flex-wrap: wrap;\n padding: 8px;\n }\n\n .toolbar-search[_ngcontent-%COMP%] {\n order: 3;\n width: 100%;\n margin-top: 8px;\n }\n\n .search-input[_ngcontent-%COMP%] {\n width: 100%;\n }\n\n .toolbar-center[_ngcontent-%COMP%] {\n order: 2;\n }\n\n \n\n .toolbar-button[_ngcontent-%COMP%] .button-text[_ngcontent-%COMP%] {\n display: none;\n }\n}\n\n\n\n\n\n\n.toolbar-button[_ngcontent-%COMP%] .button-text[_ngcontent-%COMP%] {\n font-size: 13px;\n}\n\n\n\n\n\n\n.toolbar-overflow[_ngcontent-%COMP%] {\n position: relative;\n}\n\n.overflow-trigger[_ngcontent-%COMP%] {\n padding: 6px 8px !important;\n}\n\n.overflow-trigger[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 16px;\n}\n\n.overflow-menu[_ngcontent-%COMP%] {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n min-width: 220px;\n background: #ffffff;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.08);\n z-index: 1000;\n overflow: hidden;\n}\n\n.overflow-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n width: 100%;\n padding: 10px 16px;\n border: none;\n background: none;\n cursor: pointer;\n font-size: 14px;\n color: #333;\n text-align: left;\n gap: 12px;\n transition: background-color 0.15s;\n}\n\n.overflow-item[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #f5f5f5;\n}\n\n.overflow-item[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.overflow-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 18px;\n font-size: 14px;\n color: #666;\n text-align: center;\n}\n\n.overflow-item[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.overflow-divider[_ngcontent-%COMP%] {\n height: 1px;\n background: #e0e0e0;\n margin: 4px 0;\n}\n\n.overflow-section-label[_ngcontent-%COMP%] {\n padding: 8px 16px 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n color: #999;\n letter-spacing: 0.5px;\n}\n\n\n\n.overflow-item.action-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #2196F3;\n}\n\n\n\n\n\n\n .highlight-match {\n background-color: #fff176;\n border-radius: 2px;\n padding: 0 1px;\n}\n\n\n\n\n\n\n.mj-ag-grid[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n}\n\n .ag-theme-alpine {\n \n\n --ag-row-hover-color: var(--grid-row-hover-bg);\n --ag-selected-row-background-color: var(--grid-row-selected-bg);\n --ag-header-background-color: var(--grid-header-bg);\n\n \n\n --ag-range-selection-background-color: rgba(249, 168, 37, 0.15);\n --ag-range-selection-border-color: #f9a825;\n\n \n\n --ag-borders: none;\n --ag-row-border-color: var(--grid-cell-border-color);\n}\n\n\n\n .ag-row-selected {\n box-shadow: inset 4px 0 0 0 #f9a825;\n}\n\n\n\n .ag-theme-alpine .ag-checkbox-input-wrapper {\n width: 18px;\n height: 18px;\n}\n\n .ag-theme-alpine .ag-checkbox-input-wrapper.ag-checked::after {\n color: var(--grid-checkbox-color, #2196F3);\n}\n\n\n\n .ag-theme-alpine .ag-row:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, #f0f7ff);\n}\n\n\n\n\n\n\n\n\n.header-style-flat[_ngcontent-%COMP%] .ag-header {\n background: var(--grid-header-bg, #fafafa);\n border-bottom: 1px solid var(--grid-header-border-color, #e0e0e0);\n}\n\n\n\n.header-style-elevated[_ngcontent-%COMP%] .ag-header {\n background: linear-gradient(180deg, #ffffff 0%, #f8f9fa 100%);\n border-bottom: 1px solid var(--grid-header-border-color, #e0e0e0);\n}\n\n.header-style-elevated.header-shadow[_ngcontent-%COMP%] .ag-header {\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06);\n}\n\n\n\n.header-style-gradient[_ngcontent-%COMP%] .ag-header {\n background: linear-gradient(180deg, #f8f9fa 0%, #e9ecef 100%);\n border-bottom: none;\n}\n\n.header-style-gradient.header-shadow[_ngcontent-%COMP%] .ag-header {\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n}\n\n\n\n.header-style-bold[_ngcontent-%COMP%] .ag-header {\n background: linear-gradient(180deg, #37474f 0%, #263238 100%);\n border-bottom: none;\n}\n\n.header-style-bold[_ngcontent-%COMP%] .ag-header-cell-text {\n color: #ffffff;\n font-weight: 600;\n}\n\n.header-style-bold[_ngcontent-%COMP%] .ag-header-icon {\n color: rgba(255, 255, 255, 0.7);\n}\n\n.header-style-bold[_ngcontent-%COMP%] .ag-header-cell:hover {\n background: rgba(255, 255, 255, 0.1);\n}\n\n\n\n .ag-header-cell-sorted-asc .ag-icon-asc, \n .ag-header-cell-sorted-desc .ag-icon-desc {\n color: var(--grid-accent-color, var(--grid-sort-indicator-color, #2196F3));\n}\n\n\n\n\n\n\n\n\n.alternate-rows-subtle[_ngcontent-%COMP%] .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.015);\n}\n\n.alternate-rows-subtle[_ngcontent-%COMP%] .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, #f5f5f5);\n}\n\n\n\n.alternate-rows-medium[_ngcontent-%COMP%] .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.025);\n}\n\n.alternate-rows-medium[_ngcontent-%COMP%] .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, #f5f5f5);\n}\n\n\n\n.alternate-rows-strong[_ngcontent-%COMP%] .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n.alternate-rows-strong[_ngcontent-%COMP%] .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, #f5f5f5);\n}\n\n\n\n\n\n\n.hover-transitions[_ngcontent-%COMP%] .ag-row {\n transition: background-color var(--grid-hover-transition, 150ms) ease;\n}\n\n.hover-transitions[_ngcontent-%COMP%] .ag-cell {\n transition: background-color var(--grid-hover-transition, 150ms) ease;\n}\n\n\n\n\n\n\n.cell-padding-compact[_ngcontent-%COMP%] .ag-cell {\n padding-left: 8px;\n padding-right: 8px;\n}\n\n.cell-padding-compact[_ngcontent-%COMP%] .ag-header-cell {\n padding-left: 8px;\n padding-right: 8px;\n}\n\n.cell-padding-normal[_ngcontent-%COMP%] .ag-cell {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.cell-padding-normal[_ngcontent-%COMP%] .ag-header-cell {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.cell-padding-comfortable[_ngcontent-%COMP%] .ag-cell {\n padding-left: 16px;\n padding-right: 16px;\n}\n\n.cell-padding-comfortable[_ngcontent-%COMP%] .ag-header-cell {\n padding-left: 16px;\n padding-right: 16px;\n}\n\n\n\n\n\n\n\n\n.checkbox-style-rounded[_ngcontent-%COMP%] .ag-checkbox-input-wrapper {\n border-radius: 4px;\n}\n\n.checkbox-style-rounded[_ngcontent-%COMP%] .ag-checkbox-input-wrapper::after {\n border-radius: 3px;\n}\n\n\n\n.checkbox-style-filled[_ngcontent-%COMP%] .ag-checkbox-input-wrapper.ag-checked {\n background-color: var(--grid-checkbox-color, #2196F3);\n border-color: var(--grid-checkbox-color, #2196F3);\n}\n\n.checkbox-style-filled[_ngcontent-%COMP%] .ag-checkbox-input-wrapper.ag-checked::after {\n color: #ffffff;\n}\n\n\n\n\n\n\n\n\n .cell-align-right {\n text-align: right;\n justify-content: flex-end;\n}\n\n .header-align-right .ag-header-cell-label {\n justify-content: flex-end;\n}\n\n\n\n .cell-empty {\n color: #bdbdbd;\n font-style: normal;\n}\n\n\n\n .cell-boolean-true {\n color: #43a047;\n font-size: 14px;\n}\n\n .cell-boolean-false {\n color: #bdbdbd;\n font-size: 14px;\n}\n\n\n\n .cell-link {\n color: var(--grid-accent-color, #2196F3);\n text-decoration: none;\n transition: color 0.15s;\n font-size: inherit;\n font-family: inherit;\n line-height: inherit;\n}\n\n .cell-link:hover {\n color: #1976D2;\n text-decoration: underline;\n}\n\n\n\n .cell-email {\n font-family: inherit;\n font-size: 13px;\n}\n\n\n\n .cell-url {\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n font-size: 13px;\n}\n\n\n\n .cell-phone {\n font-variant-numeric: tabular-nums;\n letter-spacing: 0.3px;\n}\n\n\n\n\n\n\n@keyframes _ngcontent-%COMP%_skeleton-shimmer {\n 0% {\n background-position: -200px 0;\n }\n 100% {\n background-position: calc(200px + 100%) 0;\n }\n}\n\n.skeleton-row[_ngcontent-%COMP%] {\n display: flex;\n height: 40px;\n align-items: center;\n padding: 0 12px;\n border-bottom: 1px solid var(--grid-cell-border-color, #f0f0f0);\n}\n\n.skeleton-cell[_ngcontent-%COMP%] {\n height: 16px;\n border-radius: 4px;\n background: linear-gradient(\n 90deg,\n #f0f0f0 0px,\n #e8e8e8 40px,\n #f0f0f0 80px\n );\n background-size: 200px 100%;\n animation: _ngcontent-%COMP%_skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-cell-short[_ngcontent-%COMP%] {\n width: 60px;\n}\n\n.skeleton-cell-medium[_ngcontent-%COMP%] {\n width: 120px;\n}\n\n.skeleton-cell-long[_ngcontent-%COMP%] {\n width: 180px;\n}\n\n\n\n .ag-header-select-all {\n margin-right: 0;\n}\n\n\n\n\n\n\n .ag-theme-alpine .ag-row {\n border-bottom: 1px solid var(--grid-cell-border-color, #f0f0f0);\n}\n\n .ag-theme-alpine .ag-row:last-child {\n border-bottom: none;\n}\n\n\n\n\n\n\n .ag-theme-alpine .ag-cell-focus {\n border: none !important;\n outline: none !important;\n}\n\n .ag-theme-alpine .ag-header-cell:focus {\n outline: 2px solid var(--grid-accent-color, #2196F3);\n outline-offset: -2px;\n}\n\n\n\n\n\n\n .ag-body-viewport::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n .ag-body-viewport::-webkit-scrollbar-track {\n background: #f5f5f5;\n border-radius: 4px;\n}\n\n .ag-body-viewport::-webkit-scrollbar-thumb {\n background: #c0c0c0;\n border-radius: 4px;\n}\n\n .ag-body-viewport::-webkit-scrollbar-thumb:hover {\n background: #a0a0a0;\n}\n\n\n\n\n\n\n .ag-theme-alpine .ag-pinned-left-cols-container {\n border-right: 2px solid var(--grid-border-color, #e0e0e0);\n}\n\n .ag-theme-alpine .ag-pinned-right-cols-container {\n border-left: 2px solid var(--grid-border-color, #e0e0e0);\n}\n\n\n\n\n\n\n .ag-theme-alpine .ag-header-cell {\n font-weight: 600;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n color: #546e7a;\n}\n\n .ag-theme-alpine .ag-header-cell:hover {\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n .ag-theme-alpine .ag-header-cell-sortable:hover .ag-header-cell-label {\n color: var(--grid-accent-color, #2196F3);\n}\n\n\n\n .ag-theme-alpine .ag-sort-indicator-icon {\n transition: transform 0.2s ease;\n}\n\n .ag-theme-alpine .ag-header-cell:hover .ag-sort-indicator-icon {\n transform: scale(1.1);\n}\n\n\n\n\n\n\n\n\n\n\n\n\n[_nghost-%COMP%] .ag-header-cell-sorted-asc {\n background: linear-gradient(180deg, rgba(25, 118, 210, 0.15) 0%, rgba(25, 118, 210, 0.08) 100%) !important;\n position: relative;\n}\n\n\n\n[_nghost-%COMP%] .ag-header-cell-sorted-desc {\n background: linear-gradient(180deg, rgba(216, 27, 96, 0.15) 0%, rgba(216, 27, 96, 0.08) 100%) !important;\n position: relative;\n}\n\n\n\n[_nghost-%COMP%] .ag-header-cell-sorted-asc::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: #1976d2;\n}\n\n\n\n[_nghost-%COMP%] .ag-header-cell-sorted-desc::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: #d81b60;\n}\n\n\n\n[_nghost-%COMP%] .ag-header-cell-sorted-asc .ag-header-cell-text {\n color: #1976d2 !important;\n font-weight: 700 !important;\n}\n\n\n\n[_nghost-%COMP%] .ag-header-cell-sorted-desc .ag-header-cell-text {\n color: #d81b60 !important;\n font-weight: 700 !important;\n}\n\n\n\n\n\n[_nghost-%COMP%] .ag-sort-ascending-icon {\n color: #1976d2 !important;\n}\n\n\n\n[_nghost-%COMP%] .ag-sort-descending-icon {\n color: #d81b60 !important;\n}\n\n[_nghost-%COMP%] .ag-sort-ascending-icon .ag-icon, \n[_nghost-%COMP%] .ag-sort-descending-icon .ag-icon {\n font-size: 14px !important;\n}\n\n\n\n\n\n\n\n\n\n[_nghost-%COMP%] .ag-sort-order {\n margin-left: 4px;\n font-size: 11px !important;\n font-weight: 700 !important;\n color: #1976d2 !important;\n}\n\n\n\n[_nghost-%COMP%] .ag-header-cell-sorted-desc .ag-sort-order {\n color: #d81b60 !important;\n}\n\n\n\n[_nghost-%COMP%] .ag-header-cell-sortable:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n\n[_nghost-%COMP%] .ag-header-cell-sortable:hover .ag-header-cell-text {\n color: #1976d2;\n}"], data: { animation: [
|
|
4195
|
+
} }, dependencies: [i2.NgForOf, i2.NgIf, i3.AgGridAngular, i4.LoadingComponent, i1.ExportDialogComponent], styles: ["\n\n\n\n\n[_nghost-%COMP%] {\n \n\n --grid-border-color: #e0e0e0;\n --grid-border-radius: 0px;\n --grid-background: #ffffff;\n\n \n\n --grid-header-bg: #fafafa;\n --grid-header-text: #333333;\n --grid-header-font-weight: 600;\n --grid-header-height: 40px;\n --grid-header-border-color: #e0e0e0;\n\n \n\n --grid-row-height: 40px;\n --grid-row-bg: #ffffff;\n --grid-row-bg-alt: #fafafa;\n --grid-row-hover-bg: #f5f5f5;\n --grid-row-selected-bg: #fff9e6;\n --grid-row-selected-hover-bg: #fff3cc;\n\n \n\n --grid-cell-padding: 8px 12px;\n --grid-cell-text: #333333;\n --grid-cell-border-color: #f0f0f0;\n\n \n\n --grid-checkbox-color: #2196F3;\n --grid-selection-indicator-color: #f9a825;\n\n \n\n --grid-edit-cell-bg: #ffffff;\n --grid-edit-cell-border: #2196F3;\n --grid-edit-cell-shadow: 0 0 0 3px rgba(33, 150, 243, 0.1);\n\n \n\n --grid-sort-indicator-color: #2196F3;\n\n \n\n --grid-toolbar-bg: #ffffff;\n --grid-toolbar-height: 48px;\n --grid-toolbar-border-color: #e0e0e0;\n\n \n\n --grid-loading-overlay-bg: rgba(255, 255, 255, 0.8);\n\n \n\n --grid-empty-text-color: #999999;\n --grid-empty-icon-color: #cccccc;\n\n display: block;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n}\n\n\n\n\n\n\n.mj-grid-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--grid-border-color);\n border-radius: var(--grid-border-radius);\n background: var(--grid-background);\n overflow: hidden;\n}\n\n\n\n\n\n\n.mj-grid-toolbar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n min-height: var(--grid-toolbar-height);\n padding: 0 12px;\n background: var(--grid-toolbar-bg);\n border-bottom: 1px solid var(--grid-toolbar-border-color);\n gap: 12px;\n}\n\n.toolbar-left[_ngcontent-%COMP%], \n.toolbar-right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-center[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n color: #666;\n font-size: 13px;\n}\n\n.toolbar-search[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n position: relative;\n}\n\n.search-icon[_ngcontent-%COMP%] {\n position: absolute;\n left: 10px;\n color: #999;\n font-size: 13px;\n}\n\n.search-input[_ngcontent-%COMP%] {\n padding: 6px 30px 6px 32px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 13px;\n width: 200px;\n transition: border-color 0.2s, box-shadow 0.2s;\n}\n\n.search-input[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: var(--grid-selection-indicator-color);\n box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.1);\n}\n\n.search-clear[_ngcontent-%COMP%] {\n position: absolute;\n right: 6px;\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px;\n color: #999;\n font-size: 12px;\n}\n\n.search-clear[_ngcontent-%COMP%]:hover {\n color: #666;\n}\n\n.toolbar-button[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: #f5f5f5;\n border: 1px solid #ddd;\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n color: #333;\n transition: background-color 0.2s, border-color 0.2s;\n}\n\n.toolbar-button[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #eee;\n border-color: #ccc;\n}\n\n.toolbar-button[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.toolbar-button[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.toolbar-button-danger[_ngcontent-%COMP%] {\n color: #d32f2f;\n}\n\n.toolbar-button-danger[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #ffebee;\n border-color: #ffcdd2;\n}\n\n.row-count[_ngcontent-%COMP%], \n.selection-count[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n\n\n\n\n\n.mj-grid-content[_ngcontent-%COMP%] {\n flex: 1;\n position: relative;\n overflow: hidden;\n}\n\n.mj-grid-scroll-container[_ngcontent-%COMP%] {\n height: 100%;\n overflow: auto;\n}\n\n\n\n\n\n\n.mj-grid-loading-overlay[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--grid-loading-overlay-bg);\n z-index: 10;\n}\n\n\n\n\n\n\n.mj-grid-error[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n color: #d32f2f;\n gap: 12px;\n}\n\n.mj-grid-error[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 32px;\n}\n\n.error-retry[_ngcontent-%COMP%] {\n padding: 8px 16px;\n background: #d32f2f;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n}\n\n.error-retry[_ngcontent-%COMP%]:hover {\n background: #c62828;\n}\n\n\n\n\n\n\n.mj-grid-empty[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n color: var(--grid-empty-text-color);\n gap: 12px;\n}\n\n.mj-grid-empty[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n color: var(--grid-empty-icon-color);\n}\n\n\n\n\n\n\n.mj-grid-header[_ngcontent-%COMP%] {\n display: flex;\n min-height: var(--grid-header-height);\n background: var(--grid-header-bg);\n border-bottom: 2px solid var(--grid-header-border-color);\n position: sticky;\n top: 0;\n z-index: 5;\n}\n\n.mj-grid-header-cell[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: var(--grid-cell-padding);\n font-weight: var(--grid-header-font-weight);\n color: var(--grid-header-text);\n font-size: 13px;\n user-select: none;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right: 1px solid var(--grid-cell-border-color);\n flex-shrink: 0;\n}\n\n.mj-grid-header-cell[_ngcontent-%COMP%]:last-child {\n border-right: none;\n}\n\n.mj-grid-header-cell.sortable[_ngcontent-%COMP%] {\n cursor: pointer;\n}\n\n.mj-grid-header-cell.sortable[_ngcontent-%COMP%]:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n\n.header-text[_ngcontent-%COMP%] {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.sort-indicator[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n margin-left: 6px;\n color: var(--grid-sort-indicator-color);\n}\n\n.sort-indicator[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n\n.sort-index[_ngcontent-%COMP%] {\n font-size: 10px;\n margin-left: 2px;\n font-weight: normal;\n}\n\n\n\n\n\n\n.mj-grid-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: stretch;\n background: var(--grid-row-bg);\n transition: background-color 0.15s;\n cursor: default;\n}\n\n.mj-grid-row[_ngcontent-%COMP%]:hover {\n background: var(--grid-row-hover-bg);\n}\n\n.mj-grid-row.grid-row-alt[_ngcontent-%COMP%] {\n background: var(--grid-row-bg-alt);\n}\n\n.mj-grid-row.grid-row-alt[_ngcontent-%COMP%]:hover {\n background: var(--grid-row-hover-bg);\n}\n\n.mj-grid-row.grid-row-selected[_ngcontent-%COMP%] {\n background: var(--grid-row-selected-bg);\n}\n\n.mj-grid-row.grid-row-selected[_ngcontent-%COMP%]:hover {\n background: var(--grid-row-selected-hover-bg);\n}\n\n.mj-grid-row.grid-row-editing[_ngcontent-%COMP%] {\n background: #fffde7;\n}\n\n.mj-grid-row.grid-row-dirty[_ngcontent-%COMP%] {\n border-left: 3px solid #ff9800;\n}\n\n\n\n\n\n\n.mj-grid-cell[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: var(--grid-cell-padding);\n color: var(--grid-cell-text);\n font-size: 13px;\n overflow: hidden;\n border-right: 1px solid transparent;\n flex-shrink: 0;\n}\n\n.mj-grid-cell[_ngcontent-%COMP%]:last-child {\n border-right: none;\n}\n\n\n\n.grid-lines-horizontal[_ngcontent-%COMP%] .mj-grid-row[_ngcontent-%COMP%] {\n border-bottom: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-vertical[_ngcontent-%COMP%] .mj-grid-cell[_ngcontent-%COMP%] {\n border-right: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-both[_ngcontent-%COMP%] .mj-grid-row[_ngcontent-%COMP%] {\n border-bottom: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-both[_ngcontent-%COMP%] .mj-grid-cell[_ngcontent-%COMP%] {\n border-right: 1px solid var(--grid-cell-border-color);\n}\n\n\n\n.mj-grid-cell.align-left[_ngcontent-%COMP%] {\n justify-content: flex-start;\n}\n\n.mj-grid-cell.align-center[_ngcontent-%COMP%] {\n justify-content: center;\n}\n\n.mj-grid-cell.align-right[_ngcontent-%COMP%] {\n justify-content: flex-end;\n}\n\n.cell-content[_ngcontent-%COMP%] {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n\n\n.row-number-cell[_ngcontent-%COMP%] {\n width: 50px;\n min-width: 50px;\n max-width: 50px;\n justify-content: center;\n color: #999;\n font-size: 12px;\n background: var(--grid-header-bg);\n}\n\n.checkbox-cell[_ngcontent-%COMP%] {\n width: 40px;\n min-width: 40px;\n max-width: 40px;\n justify-content: center;\n}\n\n.checkbox-cell[_ngcontent-%COMP%] input[type=\"checkbox\"][_ngcontent-%COMP%] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n accent-color: var(--grid-checkbox-color);\n}\n\n\n\n\n\n\n.mj-grid-virtual-spacer[_ngcontent-%COMP%] {\n flex-shrink: 0;\n}\n\n\n\n\n\n\n@media (max-width: 768px) {\n .mj-grid-toolbar[_ngcontent-%COMP%] {\n flex-wrap: wrap;\n padding: 8px;\n }\n\n .toolbar-search[_ngcontent-%COMP%] {\n order: 3;\n width: 100%;\n margin-top: 8px;\n }\n\n .search-input[_ngcontent-%COMP%] {\n width: 100%;\n }\n\n .toolbar-center[_ngcontent-%COMP%] {\n order: 2;\n }\n\n \n\n .toolbar-button[_ngcontent-%COMP%] .button-text[_ngcontent-%COMP%] {\n display: none;\n }\n}\n\n\n\n\n\n\n.toolbar-button[_ngcontent-%COMP%] .button-text[_ngcontent-%COMP%] {\n font-size: 13px;\n}\n\n\n\n\n\n\n.toolbar-overflow[_ngcontent-%COMP%] {\n position: relative;\n}\n\n.overflow-trigger[_ngcontent-%COMP%] {\n padding: 6px 8px !important;\n}\n\n.overflow-trigger[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 16px;\n}\n\n.overflow-menu[_ngcontent-%COMP%] {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n min-width: 220px;\n background: #ffffff;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.08);\n z-index: 1000;\n overflow: hidden;\n}\n\n.overflow-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n width: 100%;\n padding: 10px 16px;\n border: none;\n background: none;\n cursor: pointer;\n font-size: 14px;\n color: #333;\n text-align: left;\n gap: 12px;\n transition: background-color 0.15s;\n}\n\n.overflow-item[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #f5f5f5;\n}\n\n.overflow-item[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.overflow-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 18px;\n font-size: 14px;\n color: #666;\n text-align: center;\n}\n\n.overflow-item[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.overflow-divider[_ngcontent-%COMP%] {\n height: 1px;\n background: #e0e0e0;\n margin: 4px 0;\n}\n\n.overflow-section-label[_ngcontent-%COMP%] {\n padding: 8px 16px 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n color: #999;\n letter-spacing: 0.5px;\n}\n\n\n\n.overflow-item.action-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #2196F3;\n}\n\n\n\n\n\n\n .highlight-match {\n background-color: #fff176;\n border-radius: 2px;\n padding: 0 1px;\n}\n\n\n\n\n\n\n.mj-ag-grid[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n}\n\n .ag-theme-alpine {\n \n\n --ag-row-hover-color: var(--grid-row-hover-bg);\n --ag-selected-row-background-color: var(--grid-row-selected-bg);\n --ag-header-background-color: var(--grid-header-bg);\n\n \n\n --ag-range-selection-background-color: rgba(249, 168, 37, 0.15);\n --ag-range-selection-border-color: #f9a825;\n\n \n\n --ag-borders: none;\n --ag-row-border-color: var(--grid-cell-border-color);\n}\n\n\n\n .ag-row-selected {\n box-shadow: inset 4px 0 0 0 #f9a825;\n}\n\n\n\n .ag-theme-alpine .ag-checkbox-input-wrapper {\n width: 18px;\n height: 18px;\n}\n\n .ag-theme-alpine .ag-checkbox-input-wrapper.ag-checked::after {\n color: var(--grid-checkbox-color, #2196F3);\n}\n\n\n\n .ag-theme-alpine .ag-row:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, #f0f7ff);\n}\n\n\n\n\n\n\n\n\n.header-style-flat[_ngcontent-%COMP%] .ag-header {\n background: var(--grid-header-bg, #fafafa);\n border-bottom: 1px solid var(--grid-header-border-color, #e0e0e0);\n}\n\n\n\n.header-style-elevated[_ngcontent-%COMP%] .ag-header {\n background: linear-gradient(180deg, #ffffff 0%, #f8f9fa 100%);\n border-bottom: 1px solid var(--grid-header-border-color, #e0e0e0);\n}\n\n.header-style-elevated.header-shadow[_ngcontent-%COMP%] .ag-header {\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06);\n}\n\n\n\n.header-style-gradient[_ngcontent-%COMP%] .ag-header {\n background: linear-gradient(180deg, #f8f9fa 0%, #e9ecef 100%);\n border-bottom: none;\n}\n\n.header-style-gradient.header-shadow[_ngcontent-%COMP%] .ag-header {\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n}\n\n\n\n.header-style-bold[_ngcontent-%COMP%] .ag-header {\n background: linear-gradient(180deg, #37474f 0%, #263238 100%);\n border-bottom: none;\n}\n\n.header-style-bold[_ngcontent-%COMP%] .ag-header-cell-text {\n color: #ffffff;\n font-weight: 600;\n}\n\n.header-style-bold[_ngcontent-%COMP%] .ag-header-icon {\n color: rgba(255, 255, 255, 0.7);\n}\n\n.header-style-bold[_ngcontent-%COMP%] .ag-header-cell:hover {\n background: rgba(255, 255, 255, 0.1);\n}\n\n\n\n .ag-header-cell-sorted-asc .ag-icon-asc, \n .ag-header-cell-sorted-desc .ag-icon-desc {\n color: var(--grid-accent-color, var(--grid-sort-indicator-color, #2196F3));\n}\n\n\n\n\n\n\n\n\n.alternate-rows-subtle[_ngcontent-%COMP%] .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.015);\n}\n\n.alternate-rows-subtle[_ngcontent-%COMP%] .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, #f5f5f5);\n}\n\n\n\n.alternate-rows-medium[_ngcontent-%COMP%] .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.025);\n}\n\n.alternate-rows-medium[_ngcontent-%COMP%] .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, #f5f5f5);\n}\n\n\n\n.alternate-rows-strong[_ngcontent-%COMP%] .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n.alternate-rows-strong[_ngcontent-%COMP%] .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, #f5f5f5);\n}\n\n\n\n\n\n\n.hover-transitions[_ngcontent-%COMP%] .ag-row {\n transition: background-color var(--grid-hover-transition, 150ms) ease;\n}\n\n.hover-transitions[_ngcontent-%COMP%] .ag-cell {\n transition: background-color var(--grid-hover-transition, 150ms) ease;\n}\n\n\n\n\n\n\n.cell-padding-compact[_ngcontent-%COMP%] .ag-cell {\n padding-left: 8px;\n padding-right: 8px;\n}\n\n.cell-padding-compact[_ngcontent-%COMP%] .ag-header-cell {\n padding-left: 8px;\n padding-right: 8px;\n}\n\n.cell-padding-normal[_ngcontent-%COMP%] .ag-cell {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.cell-padding-normal[_ngcontent-%COMP%] .ag-header-cell {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.cell-padding-comfortable[_ngcontent-%COMP%] .ag-cell {\n padding-left: 16px;\n padding-right: 16px;\n}\n\n.cell-padding-comfortable[_ngcontent-%COMP%] .ag-header-cell {\n padding-left: 16px;\n padding-right: 16px;\n}\n\n\n\n\n\n\n\n\n.checkbox-style-rounded[_ngcontent-%COMP%] .ag-checkbox-input-wrapper {\n border-radius: 4px;\n}\n\n.checkbox-style-rounded[_ngcontent-%COMP%] .ag-checkbox-input-wrapper::after {\n border-radius: 3px;\n}\n\n\n\n.checkbox-style-filled[_ngcontent-%COMP%] .ag-checkbox-input-wrapper.ag-checked {\n background-color: var(--grid-checkbox-color, #2196F3);\n border-color: var(--grid-checkbox-color, #2196F3);\n}\n\n.checkbox-style-filled[_ngcontent-%COMP%] .ag-checkbox-input-wrapper.ag-checked::after {\n color: #ffffff;\n}\n\n\n\n\n\n\n\n\n .cell-align-right {\n text-align: right;\n justify-content: flex-end;\n}\n\n .header-align-right .ag-header-cell-label {\n justify-content: flex-end;\n}\n\n\n\n .cell-empty {\n color: #bdbdbd;\n font-style: normal;\n}\n\n\n\n .cell-boolean-true {\n color: #43a047;\n font-size: 14px;\n}\n\n .cell-boolean-false {\n color: #bdbdbd;\n font-size: 14px;\n}\n\n\n\n .cell-link {\n color: var(--grid-accent-color, #2196F3);\n text-decoration: none;\n transition: color 0.15s;\n font-size: inherit;\n font-family: inherit;\n line-height: inherit;\n}\n\n .cell-link:hover {\n color: #1976D2;\n text-decoration: underline;\n}\n\n\n\n .cell-email {\n font-family: inherit;\n font-size: 13px;\n}\n\n\n\n .cell-fk-link {\n color: var(--grid-accent-color, #1976D2);\n text-decoration: none;\n cursor: pointer;\n transition: color 0.15s, text-decoration 0.15s;\n}\n\n .cell-fk-link:hover {\n color: #1565C0;\n text-decoration: underline;\n}\n\n .cell-fk-link:active {\n color: #0D47A1;\n}\n\n\n\n .cell-url {\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n font-size: 13px;\n}\n\n\n\n .cell-phone {\n font-variant-numeric: tabular-nums;\n letter-spacing: 0.3px;\n}\n\n\n\n\n\n\n@keyframes _ngcontent-%COMP%_skeleton-shimmer {\n 0% {\n background-position: -200px 0;\n }\n 100% {\n background-position: calc(200px + 100%) 0;\n }\n}\n\n.skeleton-row[_ngcontent-%COMP%] {\n display: flex;\n height: 40px;\n align-items: center;\n padding: 0 12px;\n border-bottom: 1px solid var(--grid-cell-border-color, #f0f0f0);\n}\n\n.skeleton-cell[_ngcontent-%COMP%] {\n height: 16px;\n border-radius: 4px;\n background: linear-gradient(\n 90deg,\n #f0f0f0 0px,\n #e8e8e8 40px,\n #f0f0f0 80px\n );\n background-size: 200px 100%;\n animation: _ngcontent-%COMP%_skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-cell-short[_ngcontent-%COMP%] {\n width: 60px;\n}\n\n.skeleton-cell-medium[_ngcontent-%COMP%] {\n width: 120px;\n}\n\n.skeleton-cell-long[_ngcontent-%COMP%] {\n width: 180px;\n}\n\n\n\n .ag-header-select-all {\n margin-right: 0;\n}\n\n\n\n\n\n\n .ag-theme-alpine .ag-row {\n border-bottom: 1px solid var(--grid-cell-border-color, #f0f0f0);\n}\n\n .ag-theme-alpine .ag-row:last-child {\n border-bottom: none;\n}\n\n\n\n\n\n\n .ag-theme-alpine .ag-cell-focus {\n border: none !important;\n outline: none !important;\n}\n\n .ag-theme-alpine .ag-header-cell:focus {\n outline: 2px solid var(--grid-accent-color, #2196F3);\n outline-offset: -2px;\n}\n\n\n\n\n\n\n .ag-body-viewport::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n .ag-body-viewport::-webkit-scrollbar-track {\n background: #f5f5f5;\n border-radius: 4px;\n}\n\n .ag-body-viewport::-webkit-scrollbar-thumb {\n background: #c0c0c0;\n border-radius: 4px;\n}\n\n .ag-body-viewport::-webkit-scrollbar-thumb:hover {\n background: #a0a0a0;\n}\n\n\n\n\n\n\n .ag-theme-alpine .ag-pinned-left-cols-container {\n border-right: 2px solid var(--grid-border-color, #e0e0e0);\n}\n\n .ag-theme-alpine .ag-pinned-right-cols-container {\n border-left: 2px solid var(--grid-border-color, #e0e0e0);\n}\n\n\n\n\n\n\n .ag-theme-alpine .ag-header-cell {\n font-weight: 600;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n color: #546e7a;\n}\n\n .ag-theme-alpine .ag-header-cell:hover {\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n .ag-theme-alpine .ag-header-cell-sortable:hover .ag-header-cell-label {\n color: var(--grid-accent-color, #2196F3);\n}\n\n\n\n .ag-theme-alpine .ag-sort-indicator-icon {\n transition: transform 0.2s ease;\n}\n\n .ag-theme-alpine .ag-header-cell:hover .ag-sort-indicator-icon {\n transform: scale(1.1);\n}\n\n\n\n\n\n\n\n\n\n\n\n\n[_nghost-%COMP%] .ag-header-cell-sorted-asc {\n background: linear-gradient(180deg, rgba(25, 118, 210, 0.15) 0%, rgba(25, 118, 210, 0.08) 100%) !important;\n position: relative;\n}\n\n\n\n[_nghost-%COMP%] .ag-header-cell-sorted-desc {\n background: linear-gradient(180deg, rgba(216, 27, 96, 0.15) 0%, rgba(216, 27, 96, 0.08) 100%) !important;\n position: relative;\n}\n\n\n\n[_nghost-%COMP%] .ag-header-cell-sorted-asc::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: #1976d2;\n}\n\n\n\n[_nghost-%COMP%] .ag-header-cell-sorted-desc::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: #d81b60;\n}\n\n\n\n[_nghost-%COMP%] .ag-header-cell-sorted-asc .ag-header-cell-text {\n color: #1976d2 !important;\n font-weight: 700 !important;\n}\n\n\n\n[_nghost-%COMP%] .ag-header-cell-sorted-desc .ag-header-cell-text {\n color: #d81b60 !important;\n font-weight: 700 !important;\n}\n\n\n\n\n\n[_nghost-%COMP%] .ag-sort-ascending-icon {\n color: #1976d2 !important;\n}\n\n\n\n[_nghost-%COMP%] .ag-sort-descending-icon {\n color: #d81b60 !important;\n}\n\n[_nghost-%COMP%] .ag-sort-ascending-icon .ag-icon, \n[_nghost-%COMP%] .ag-sort-descending-icon .ag-icon {\n font-size: 14px !important;\n}\n\n\n\n\n\n\n\n\n\n[_nghost-%COMP%] .ag-sort-order {\n margin-left: 4px;\n font-size: 11px !important;\n font-weight: 700 !important;\n color: #1976d2 !important;\n}\n\n\n\n[_nghost-%COMP%] .ag-header-cell-sorted-desc .ag-sort-order {\n color: #d81b60 !important;\n}\n\n\n\n[_nghost-%COMP%] .ag-header-cell-sortable:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n\n[_nghost-%COMP%] .ag-header-cell-sortable:hover .ag-header-cell-text {\n color: #1976d2;\n}\n\n\n\n\n\n\n.mj-aggregate-summary[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px;\n background: linear-gradient(to bottom, #f8fafc, #f1f5f9);\n border-top: 1px solid #e2e8f0;\n font-size: 13px;\n min-height: 44px;\n}\n\n.aggregate-summary-content[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 20px;\n align-items: center;\n}\n\n.aggregate-summary-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: white;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n}\n\n.aggregate-summary-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #64748b;\n}\n\n.agg-summary-label[_ngcontent-%COMP%] {\n color: #64748b;\n font-weight: 500;\n}\n\n.agg-summary-value[_ngcontent-%COMP%] {\n color: #1e293b;\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n}\n\n.aggregate-loading[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n color: #94a3b8;\n}\n\n.aggregate-loading[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n\n\n\n\n\n.mj-aggregate-cards[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n padding: 12px 16px;\n background: linear-gradient(to bottom, #f8fafc, #f1f5f9);\n border-bottom: 1px solid #e2e8f0;\n flex-shrink: 0;\n}\n\n.aggregate-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n background: white;\n border: 1px solid #e2e8f0;\n border-radius: 10px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n min-width: 140px;\n transition: all 0.15s ease;\n}\n\n.aggregate-card[_ngcontent-%COMP%]:hover {\n border-color: #cbd5e1;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);\n}\n\n.aggregate-card-icon[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n background: linear-gradient(135deg, #e0f2fe 0%, #bae6fd 100%);\n border-radius: 10px;\n color: #0284c7;\n font-size: 16px;\n flex-shrink: 0;\n}\n\n.aggregate-card-content[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.aggregate-card-label[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 500;\n color: #64748b;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.aggregate-card-value[_ngcontent-%COMP%] {\n font-size: 18px;\n font-weight: 700;\n color: #0f172a;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.aggregate-card-loading[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 12px;\n color: #94a3b8;\n}\n\n.aggregate-card-loading[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 16px;\n}\n\n\n\n\n\n\n\n\n .cell-wrap-text {\n white-space: normal !important;\n word-wrap: break-word;\n line-height: 1.4;\n padding-top: 8px !important;\n padding-bottom: 8px !important;\n}\n\n\n\n .ag-cell.cell-wrap-text {\n overflow: visible;\n text-overflow: clip;\n}\n\n\n\n .ag-row[style*=\"auto-height\"] {\n min-height: 40px;\n}"], data: { animation: [
|
|
3585
4196
|
trigger('fadeIn', [
|
|
3586
4197
|
transition(':enter', [
|
|
3587
4198
|
style({ opacity: 0, transform: 'translateY(-8px)' }),
|
|
@@ -3605,7 +4216,7 @@ export class EntityDataGridComponent {
|
|
|
3605
4216
|
animate('100ms ease-in', style({ opacity: 0, transform: 'translateY(-8px)' }))
|
|
3606
4217
|
])
|
|
3607
4218
|
])
|
|
3608
|
-
], template: "<!-- Grid Container -->\n<div\n #gridContainer\n [class]=\"gridContainerClasses.join(' ')\"\n [style.height]=\"gridHeightStyle\">\n\n <!-- Toolbar -->\n <div *ngIf=\"ShowToolbar\" class=\"mj-grid-toolbar\">\n <div class=\"toolbar-left\">\n <!-- Search -->\n <div *ngIf=\"ShowSearch\" class=\"toolbar-search\">\n <i class=\"fa-solid fa-search search-icon\"></i>\n <input\n type=\"text\"\n class=\"search-input\"\n [placeholder]=\"ToolbarConfig.searchPlaceholder || 'Search...'\"\n [value]=\"FilterText\"\n (input)=\"FilterText = $any($event.target).value\" />\n <button\n *ngIf=\"FilterText\"\n class=\"search-clear\"\n (click)=\"FilterText = ''\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Custom Left Buttons -->\n <ng-container *ngFor=\"let button of ToolbarConfig.customButtons\">\n <button\n *ngIf=\"button.position !== 'right' && isButtonVisible(button)\"\n class=\"toolbar-button\"\n [class]=\"button.cssClass\"\n [disabled]=\"isButtonDisabled(button)\"\n [title]=\"button.tooltip || ''\"\n (click)=\"onToolbarButtonClick(button)\">\n <i *ngIf=\"button.icon\" [class]=\"button.icon\"></i>\n <span *ngIf=\"button.text\">{{ button.text }}</span>\n </button>\n </ng-container>\n </div>\n\n <div class=\"toolbar-center\">\n <!-- Row Count -->\n <span *ngIf=\"ToolbarConfig.showRowCount !== false\" class=\"row-count\">\n {{ totalRowCount }} {{ totalRowCount === 1 ? 'row' : 'rows' }}\n </span>\n\n <!-- Selection Count -->\n <span *ngIf=\"ToolbarConfig.showSelectionCount && SelectedKeys.length > 0\" class=\"selection-count\">\n ({{ SelectedKeys.length }} selected)\n </span>\n </div>\n\n <div class=\"toolbar-right\">\n <!-- New/Add Button (predefined) -->\n <button\n *ngIf=\"ShowNewButton\"\n class=\"toolbar-button\"\n title=\"Create new record\"\n (click)=\"onAddClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n <span class=\"button-text\">New</span>\n </button>\n\n <!-- Refresh Button (predefined) -->\n <button\n *ngIf=\"ShowRefreshButton\"\n class=\"toolbar-button\"\n title=\"Refresh data\"\n [disabled]=\"loading\"\n (click)=\"onRefreshClick()\">\n <i class=\"fa-solid fa-arrows-rotate\" [class.fa-spin]=\"loading\"></i>\n <span class=\"button-text\">Refresh</span>\n </button>\n\n <!-- Export Button (predefined) -->\n <button\n *ngIf=\"ShowExportButton\"\n class=\"toolbar-button\"\n title=\"Export to Excel\"\n (click)=\"onExportClick()\">\n <i class=\"fa-solid fa-file-excel\"></i>\n <span class=\"button-text\">Export</span>\n </button>\n\n <!-- Delete Button (predefined) -->\n <button\n *ngIf=\"ShowDeleteButton && HasSelection\"\n class=\"toolbar-button toolbar-button-danger\"\n title=\"Delete selected records\"\n (click)=\"onDeleteClick()\">\n <i class=\"fa-solid fa-trash\"></i>\n <span class=\"button-text\">Delete</span>\n </button>\n\n <!-- Compare Button (predefined) -->\n <button\n *ngIf=\"ShowCompareButton\"\n class=\"toolbar-button\"\n title=\"Compare selected records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onCompareClick()\">\n <i class=\"fa-solid fa-code-compare\"></i>\n <span class=\"button-text\">Compare</span>\n </button>\n\n <!-- Merge Button (predefined) -->\n <button\n *ngIf=\"ShowMergeButton\"\n class=\"toolbar-button\"\n title=\"Merge selected records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onMergeClick()\">\n <i class=\"fa-solid fa-code-merge\"></i>\n <span class=\"button-text\">Merge</span>\n </button>\n\n <!-- Add to List Button (predefined) -->\n <button\n *ngIf=\"ShowAddToListButton\"\n class=\"toolbar-button\"\n title=\"Add selected records to a list\"\n [disabled]=\"!HasSelection\"\n (click)=\"onAddToListClick()\">\n <i class=\"fa-solid fa-list-check\"></i>\n <span class=\"button-text\">Add to List</span>\n </button>\n\n <!-- Search for Duplicates Button (predefined) -->\n <button\n *ngIf=\"ShowDuplicateSearchButton\"\n class=\"toolbar-button\"\n title=\"Search for duplicate records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onDuplicateSearchClick()\">\n <i class=\"fa-solid fa-magnifying-glass-plus\"></i>\n <span class=\"button-text\">Find Duplicates</span>\n </button>\n\n <!-- Communication Button (predefined) -->\n <button\n *ngIf=\"ShowCommunicationButton\"\n class=\"toolbar-button\"\n title=\"Send message to selected records\"\n [disabled]=\"!HasSelection\"\n (click)=\"onCommunicationClick()\">\n <i class=\"fa-solid fa-envelope\"></i>\n <span class=\"button-text\">Send Message</span>\n </button>\n\n <!-- Legacy ToolbarConfig buttons -->\n <!-- Add Button (legacy) -->\n <button\n *ngIf=\"ToolbarConfig.showAdd && AllowAdd && !ShowNewButton\"\n class=\"toolbar-button\"\n title=\"Add New\"\n (click)=\"onAddClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n\n <!-- Refresh Button (legacy) -->\n <button\n *ngIf=\"ToolbarConfig.showRefresh !== false && !ShowRefreshButton\"\n class=\"toolbar-button\"\n title=\"Refresh\"\n [disabled]=\"loading\"\n (click)=\"onRefreshClick()\">\n <i class=\"fa-solid fa-refresh\" [class.fa-spin]=\"loading\"></i>\n </button>\n\n <!-- Delete Button (legacy) -->\n <button\n *ngIf=\"ToolbarConfig.showDelete && AllowDelete && HasSelection && !ShowDeleteButton\"\n class=\"toolbar-button toolbar-button-danger\"\n title=\"Delete Selected\"\n (click)=\"onDeleteClick()\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n\n <!-- Export Button (legacy) -->\n <button\n *ngIf=\"ToolbarConfig.showExport && !ShowExportButton\"\n class=\"toolbar-button\"\n title=\"Export\"\n (click)=\"onExportClick()\">\n <i class=\"fa-solid fa-download\"></i>\n </button>\n\n <!-- Column Chooser Button -->\n <button\n *ngIf=\"ToolbarConfig.showColumnChooser && AllowColumnToggle\"\n class=\"toolbar-button\"\n title=\"Column Chooser\"\n (click)=\"onColumnChooserClick()\">\n <i class=\"fa-solid fa-columns\"></i>\n </button>\n\n <!-- Custom Right Buttons -->\n <ng-container *ngFor=\"let button of ToolbarConfig.customButtons\">\n <button\n *ngIf=\"button.position === 'right' && isButtonVisible(button)\"\n class=\"toolbar-button\"\n [class]=\"button.cssClass\"\n [disabled]=\"isButtonDisabled(button)\"\n [title]=\"button.tooltip || ''\"\n (click)=\"onToolbarButtonClick(button)\">\n <i *ngIf=\"button.icon\" [class]=\"button.icon\"></i>\n <span *ngIf=\"button.text\">{{ button.text }}</span>\n </button>\n </ng-container>\n\n <!-- Overflow Menu -->\n <div *ngIf=\"hasOverflowMenuItems\" class=\"toolbar-overflow\" (click)=\"$event.stopPropagation()\">\n <button\n class=\"toolbar-button overflow-trigger\"\n title=\"More actions\"\n (click)=\"toggleOverflowMenu()\">\n <i class=\"fa-solid fa-ellipsis-vertical\"></i>\n </button>\n\n <div *ngIf=\"showOverflowMenu\" class=\"overflow-menu\" [@fadeIn]>\n <!-- Export (if in overflow) -->\n <button *ngIf=\"showExportInOverflow\" class=\"overflow-item\" (click)=\"onExportClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-file-excel\"></i>\n <span>Export to Excel</span>\n </button>\n\n <!-- Entity Actions -->\n <ng-container *ngIf=\"ShowEntityActionButtons && EntityActions.length > 0\">\n <div class=\"overflow-divider\"></div>\n <div class=\"overflow-section-label\">Actions</div>\n <button\n *ngFor=\"let action of EntityActions\"\n class=\"overflow-item\"\n [disabled]=\"!isEntityActionEnabled(action)\"\n (click)=\"onEntityActionClick(action); closeOverflowMenu()\">\n <i [class]=\"action.icon || 'fa-solid fa-bolt'\"></i>\n <span>{{ action.name }}</span>\n </button>\n </ng-container>\n\n <!-- Column Chooser (if in overflow) -->\n <div *ngIf=\"showColumnChooserInOverflow\" class=\"overflow-divider\"></div>\n <button *ngIf=\"showColumnChooserInOverflow\" class=\"overflow-item\" (click)=\"onColumnChooserClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-columns\"></i>\n <span>Manage Columns</span>\n </button>\n\n <!-- Selection-dependent actions in overflow -->\n <ng-container *ngIf=\"HasSelection && hasSelectionDependentOverflowActions\">\n <div class=\"overflow-divider\"></div>\n <button *ngIf=\"showCommunicationInOverflow\" class=\"overflow-item\" (click)=\"onCommunicationClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-envelope\"></i>\n <span>Send Message</span>\n </button>\n </ng-container>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Grid Content -->\n <div class=\"mj-grid-content\">\n <!-- Loading Overlay -->\n <div *ngIf=\"loading && rowData.length === 0\" class=\"mj-grid-loading-overlay\">\n <mj-loading text=\"Loading...\"></mj-loading>\n </div>\n\n <!-- Error State -->\n <div *ngIf=\"errorMessage && !loading\" class=\"mj-grid-error\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ errorMessage }}</span>\n <button class=\"error-retry\" (click)=\"Refresh()\">Retry</button>\n </div>\n\n <!-- Empty State -->\n <div *ngIf=\"!loading && !errorMessage && rowData.length === 0\" class=\"mj-grid-empty\">\n <i class=\"fa-solid fa-inbox\"></i>\n <span>No data to display</span>\n </div>\n\n <!-- AG Grid (Client-side mode) -->\n <ag-grid-angular\n *ngIf=\"!errorMessage && PaginationMode === 'client' && (rowData.length > 0 || loading)\"\n class=\"mj-ag-grid ag-theme-alpine\"\n [theme]=\"agGridTheme\"\n [columnDefs]=\"agColumnDefs\"\n [rowData]=\"rowData\"\n [defaultColDef]=\"defaultColDef\"\n [rowSelection]=\"agRowSelection\"\n [getRowId]=\"getRowId\"\n [suppressCellFocus]=\"true\"\n [rowHeight]=\"RowHeight\"\n [headerHeight]=\"ShowHeader ? undefined : 0\"\n (gridReady)=\"onGridReady($event)\"\n (rowClicked)=\"onAgRowClicked($event)\"\n (rowDoubleClicked)=\"onAgRowDoubleClicked($event)\"\n (sortChanged)=\"onAgSortChanged($event)\"\n (selectionChanged)=\"onAgSelectionChanged($event)\"\n (columnResized)=\"onAgColumnResized($event)\"\n (columnMoved)=\"onAgColumnMoved($event)\">\n </ag-grid-angular>\n\n <!-- AG Grid (Infinite Scroll mode) -->\n <ag-grid-angular\n *ngIf=\"!errorMessage && PaginationMode === 'infinite'\"\n class=\"mj-ag-grid ag-theme-alpine\"\n [theme]=\"agGridTheme\"\n [columnDefs]=\"agColumnDefs\"\n [defaultColDef]=\"defaultColDef\"\n [rowSelection]=\"agRowSelection\"\n [getRowId]=\"getRowId\"\n [suppressCellFocus]=\"true\"\n [rowHeight]=\"RowHeight\"\n [headerHeight]=\"ShowHeader ? undefined : 0\"\n [rowModelType]=\"'infinite'\"\n [cacheBlockSize]=\"CacheBlockSize\"\n [maxBlocksInCache]=\"MaxBlocksInCache\"\n [infiniteInitialRowCount]=\"1\"\n [cacheOverflowSize]=\"2\"\n (gridReady)=\"onGridReady($event)\"\n (rowClicked)=\"onAgRowClicked($event)\"\n (rowDoubleClicked)=\"onAgRowDoubleClicked($event)\"\n (sortChanged)=\"onAgSortChanged($event)\"\n (selectionChanged)=\"onAgSelectionChanged($event)\"\n (columnResized)=\"onAgColumnResized($event)\"\n (columnMoved)=\"onAgColumnMoved($event)\">\n </ag-grid-angular>\n </div>\n</div>\n\n<!-- Export Dialog -->\n<mj-export-dialog\n [visible]=\"showExportDialog\"\n [config]=\"exportDialogConfig\"\n (closed)=\"onExportDialogClosed($event)\">\n</mj-export-dialog>\n", styles: ["/* ========================================\n CSS Custom Properties (Theme Variables)\n ======================================== */\n\n:host {\n /* Grid container */\n --grid-border-color: #e0e0e0;\n --grid-border-radius: 0px;\n --grid-background: #ffffff;\n\n /* Header */\n --grid-header-bg: #fafafa;\n --grid-header-text: #333333;\n --grid-header-font-weight: 600;\n --grid-header-height: 40px;\n --grid-header-border-color: #e0e0e0;\n\n /* Rows */\n --grid-row-height: 40px;\n --grid-row-bg: #ffffff;\n --grid-row-bg-alt: #fafafa;\n --grid-row-hover-bg: #f5f5f5;\n --grid-row-selected-bg: #fff9e6;\n --grid-row-selected-hover-bg: #fff3cc;\n\n /* Cells */\n --grid-cell-padding: 8px 12px;\n --grid-cell-text: #333333;\n --grid-cell-border-color: #f0f0f0;\n\n /* Selection - mellow yellow to avoid conflict with blue hyperlinks */\n --grid-checkbox-color: #2196F3;\n --grid-selection-indicator-color: #f9a825;\n\n /* Editing */\n --grid-edit-cell-bg: #ffffff;\n --grid-edit-cell-border: #2196F3;\n --grid-edit-cell-shadow: 0 0 0 3px rgba(33, 150, 243, 0.1);\n\n /* Sorting */\n --grid-sort-indicator-color: #2196F3;\n\n /* Toolbar */\n --grid-toolbar-bg: #ffffff;\n --grid-toolbar-height: 48px;\n --grid-toolbar-border-color: #e0e0e0;\n\n /* Loading */\n --grid-loading-overlay-bg: rgba(255, 255, 255, 0.8);\n\n /* Empty state */\n --grid-empty-text-color: #999999;\n --grid-empty-icon-color: #cccccc;\n\n display: block;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n}\n\n/* ========================================\n Grid Container\n ======================================== */\n\n.mj-grid-container {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--grid-border-color);\n border-radius: var(--grid-border-radius);\n background: var(--grid-background);\n overflow: hidden;\n}\n\n/* ========================================\n Toolbar\n ======================================== */\n\n.mj-grid-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n min-height: var(--grid-toolbar-height);\n padding: 0 12px;\n background: var(--grid-toolbar-bg);\n border-bottom: 1px solid var(--grid-toolbar-border-color);\n gap: 12px;\n}\n\n.toolbar-left,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-center {\n display: flex;\n align-items: center;\n gap: 8px;\n color: #666;\n font-size: 13px;\n}\n\n.toolbar-search {\n display: flex;\n align-items: center;\n position: relative;\n}\n\n.search-icon {\n position: absolute;\n left: 10px;\n color: #999;\n font-size: 13px;\n}\n\n.search-input {\n padding: 6px 30px 6px 32px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 13px;\n width: 200px;\n transition: border-color 0.2s, box-shadow 0.2s;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--grid-selection-indicator-color);\n box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.1);\n}\n\n.search-clear {\n position: absolute;\n right: 6px;\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px;\n color: #999;\n font-size: 12px;\n}\n\n.search-clear:hover {\n color: #666;\n}\n\n.toolbar-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: #f5f5f5;\n border: 1px solid #ddd;\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n color: #333;\n transition: background-color 0.2s, border-color 0.2s;\n}\n\n.toolbar-button:hover:not(:disabled) {\n background: #eee;\n border-color: #ccc;\n}\n\n.toolbar-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.toolbar-button i {\n font-size: 14px;\n}\n\n.toolbar-button-danger {\n color: #d32f2f;\n}\n\n.toolbar-button-danger:hover:not(:disabled) {\n background: #ffebee;\n border-color: #ffcdd2;\n}\n\n.row-count,\n.selection-count {\n font-weight: 500;\n}\n\n/* ========================================\n Grid Content\n ======================================== */\n\n.mj-grid-content {\n flex: 1;\n position: relative;\n overflow: hidden;\n}\n\n.mj-grid-scroll-container {\n height: 100%;\n overflow: auto;\n}\n\n/* ========================================\n Loading Overlay\n ======================================== */\n\n.mj-grid-loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--grid-loading-overlay-bg);\n z-index: 10;\n}\n\n/* ========================================\n Error State\n ======================================== */\n\n.mj-grid-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n color: #d32f2f;\n gap: 12px;\n}\n\n.mj-grid-error i {\n font-size: 32px;\n}\n\n.error-retry {\n padding: 8px 16px;\n background: #d32f2f;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n}\n\n.error-retry:hover {\n background: #c62828;\n}\n\n/* ========================================\n Empty State\n ======================================== */\n\n.mj-grid-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n color: var(--grid-empty-text-color);\n gap: 12px;\n}\n\n.mj-grid-empty i {\n font-size: 48px;\n color: var(--grid-empty-icon-color);\n}\n\n/* ========================================\n Header Row\n ======================================== */\n\n.mj-grid-header {\n display: flex;\n min-height: var(--grid-header-height);\n background: var(--grid-header-bg);\n border-bottom: 2px solid var(--grid-header-border-color);\n position: sticky;\n top: 0;\n z-index: 5;\n}\n\n.mj-grid-header-cell {\n display: flex;\n align-items: center;\n padding: var(--grid-cell-padding);\n font-weight: var(--grid-header-font-weight);\n color: var(--grid-header-text);\n font-size: 13px;\n user-select: none;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right: 1px solid var(--grid-cell-border-color);\n flex-shrink: 0;\n}\n\n.mj-grid-header-cell:last-child {\n border-right: none;\n}\n\n.mj-grid-header-cell.sortable {\n cursor: pointer;\n}\n\n.mj-grid-header-cell.sortable:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n\n.header-text {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.sort-indicator {\n display: flex;\n align-items: center;\n margin-left: 6px;\n color: var(--grid-sort-indicator-color);\n}\n\n.sort-indicator i {\n font-size: 12px;\n}\n\n.sort-index {\n font-size: 10px;\n margin-left: 2px;\n font-weight: normal;\n}\n\n/* ========================================\n Data Rows\n ======================================== */\n\n.mj-grid-row {\n display: flex;\n align-items: stretch;\n background: var(--grid-row-bg);\n transition: background-color 0.15s;\n cursor: default;\n}\n\n.mj-grid-row:hover {\n background: var(--grid-row-hover-bg);\n}\n\n.mj-grid-row.grid-row-alt {\n background: var(--grid-row-bg-alt);\n}\n\n.mj-grid-row.grid-row-alt:hover {\n background: var(--grid-row-hover-bg);\n}\n\n.mj-grid-row.grid-row-selected {\n background: var(--grid-row-selected-bg);\n}\n\n.mj-grid-row.grid-row-selected:hover {\n background: var(--grid-row-selected-hover-bg);\n}\n\n.mj-grid-row.grid-row-editing {\n background: #fffde7;\n}\n\n.mj-grid-row.grid-row-dirty {\n border-left: 3px solid #ff9800;\n}\n\n/* ========================================\n Data Cells\n ======================================== */\n\n.mj-grid-cell {\n display: flex;\n align-items: center;\n padding: var(--grid-cell-padding);\n color: var(--grid-cell-text);\n font-size: 13px;\n overflow: hidden;\n border-right: 1px solid transparent;\n flex-shrink: 0;\n}\n\n.mj-grid-cell:last-child {\n border-right: none;\n}\n\n/* Grid lines modes */\n.grid-lines-horizontal .mj-grid-row {\n border-bottom: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-vertical .mj-grid-cell {\n border-right: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-both .mj-grid-row {\n border-bottom: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-both .mj-grid-cell {\n border-right: 1px solid var(--grid-cell-border-color);\n}\n\n/* Cell alignment */\n.mj-grid-cell.align-left {\n justify-content: flex-start;\n}\n\n.mj-grid-cell.align-center {\n justify-content: center;\n}\n\n.mj-grid-cell.align-right {\n justify-content: flex-end;\n}\n\n.cell-content {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Special cells */\n.row-number-cell {\n width: 50px;\n min-width: 50px;\n max-width: 50px;\n justify-content: center;\n color: #999;\n font-size: 12px;\n background: var(--grid-header-bg);\n}\n\n.checkbox-cell {\n width: 40px;\n min-width: 40px;\n max-width: 40px;\n justify-content: center;\n}\n\n.checkbox-cell input[type=\"checkbox\"] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n accent-color: var(--grid-checkbox-color);\n}\n\n/* ========================================\n Virtual Scrolling\n ======================================== */\n\n.mj-grid-virtual-spacer {\n flex-shrink: 0;\n}\n\n/* ========================================\n Responsive Adjustments\n ======================================== */\n\n@media (max-width: 768px) {\n .mj-grid-toolbar {\n flex-wrap: wrap;\n padding: 8px;\n }\n\n .toolbar-search {\n order: 3;\n width: 100%;\n margin-top: 8px;\n }\n\n .search-input {\n width: 100%;\n }\n\n .toolbar-center {\n order: 2;\n }\n\n /* Hide button text on mobile */\n .toolbar-button .button-text {\n display: none;\n }\n}\n\n/* ========================================\n Toolbar Button Text\n ======================================== */\n\n.toolbar-button .button-text {\n font-size: 13px;\n}\n\n/* ========================================\n Overflow Menu\n ======================================== */\n\n.toolbar-overflow {\n position: relative;\n}\n\n.overflow-trigger {\n padding: 6px 8px !important;\n}\n\n.overflow-trigger i {\n font-size: 16px;\n}\n\n.overflow-menu {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n min-width: 220px;\n background: #ffffff;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.08);\n z-index: 1000;\n overflow: hidden;\n}\n\n.overflow-item {\n display: flex;\n align-items: center;\n width: 100%;\n padding: 10px 16px;\n border: none;\n background: none;\n cursor: pointer;\n font-size: 14px;\n color: #333;\n text-align: left;\n gap: 12px;\n transition: background-color 0.15s;\n}\n\n.overflow-item:hover:not(:disabled) {\n background: #f5f5f5;\n}\n\n.overflow-item:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.overflow-item i {\n width: 18px;\n font-size: 14px;\n color: #666;\n text-align: center;\n}\n\n.overflow-item span {\n flex: 1;\n}\n\n.overflow-divider {\n height: 1px;\n background: #e0e0e0;\n margin: 4px 0;\n}\n\n.overflow-section-label {\n padding: 8px 16px 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n color: #999;\n letter-spacing: 0.5px;\n}\n\n/* Entity Actions submenu styling */\n.overflow-item.action-item i {\n color: #2196F3;\n}\n\n/* ========================================\n Highlight Matches\n ======================================== */\n\n::ng-deep .highlight-match {\n background-color: #fff176;\n border-radius: 2px;\n padding: 0 1px;\n}\n\n/* ========================================\n AG Grid Customizations\n ======================================== */\n\n.mj-ag-grid {\n width: 100%;\n height: 100%;\n}\n\n::ng-deep .ag-theme-alpine {\n /* Row colors */\n --ag-row-hover-color: var(--grid-row-hover-bg);\n --ag-selected-row-background-color: var(--grid-row-selected-bg);\n --ag-header-background-color: var(--grid-header-bg);\n\n /* Selection accent colors - mellow yellow */\n --ag-range-selection-background-color: rgba(249, 168, 37, 0.15);\n --ag-range-selection-border-color: #f9a825;\n\n /* Ensure borders are visible */\n --ag-borders: none;\n --ag-row-border-color: var(--grid-cell-border-color);\n}\n\n/* Selected row styling - left indicator bar only, background handled by AG Grid theme */\n::ng-deep .ag-row-selected {\n box-shadow: inset 4px 0 0 0 #f9a825;\n}\n\n/* Selection checkbox styling */\n::ng-deep .ag-theme-alpine .ag-checkbox-input-wrapper {\n width: 18px;\n height: 18px;\n}\n\n::ng-deep .ag-theme-alpine .ag-checkbox-input-wrapper.ag-checked::after {\n color: var(--grid-checkbox-color, #2196F3);\n}\n\n/* Row hover effect */\n::ng-deep .ag-theme-alpine .ag-row:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, #f0f7ff);\n}\n\n/* ========================================\n Visual Config: Header Styles\n ======================================== */\n\n/* Flat header (minimal) */\n.header-style-flat ::ng-deep .ag-header {\n background: var(--grid-header-bg, #fafafa);\n border-bottom: 1px solid var(--grid-header-border-color, #e0e0e0);\n}\n\n/* Elevated header (default - subtle shadow) */\n.header-style-elevated ::ng-deep .ag-header {\n background: linear-gradient(180deg, #ffffff 0%, #f8f9fa 100%);\n border-bottom: 1px solid var(--grid-header-border-color, #e0e0e0);\n}\n\n.header-style-elevated.header-shadow ::ng-deep .ag-header {\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06);\n}\n\n/* Gradient header (more prominent) */\n.header-style-gradient ::ng-deep .ag-header {\n background: linear-gradient(180deg, #f8f9fa 0%, #e9ecef 100%);\n border-bottom: none;\n}\n\n.header-style-gradient.header-shadow ::ng-deep .ag-header {\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n}\n\n/* Bold header (high contrast) */\n.header-style-bold ::ng-deep .ag-header {\n background: linear-gradient(180deg, #37474f 0%, #263238 100%);\n border-bottom: none;\n}\n\n.header-style-bold ::ng-deep .ag-header-cell-text {\n color: #ffffff;\n font-weight: 600;\n}\n\n.header-style-bold ::ng-deep .ag-header-icon {\n color: rgba(255, 255, 255, 0.7);\n}\n\n.header-style-bold ::ng-deep .ag-header-cell:hover {\n background: rgba(255, 255, 255, 0.1);\n}\n\n/* Header sort icons enhancement */\n::ng-deep .ag-header-cell-sorted-asc .ag-icon-asc,\n::ng-deep .ag-header-cell-sorted-desc .ag-icon-desc {\n color: var(--grid-accent-color, var(--grid-sort-indicator-color, #2196F3));\n}\n\n/* ========================================\n Visual Config: Zebra Striping\n ======================================== */\n\n/* Subtle contrast */\n.alternate-rows-subtle ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.015);\n}\n\n.alternate-rows-subtle ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, #f5f5f5);\n}\n\n/* Medium contrast (default) */\n.alternate-rows-medium ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.025);\n}\n\n.alternate-rows-medium ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, #f5f5f5);\n}\n\n/* Strong contrast */\n.alternate-rows-strong ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n.alternate-rows-strong ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, #f5f5f5);\n}\n\n/* ========================================\n Visual Config: Hover Transitions\n ======================================== */\n\n.hover-transitions ::ng-deep .ag-row {\n transition: background-color var(--grid-hover-transition, 150ms) ease;\n}\n\n.hover-transitions ::ng-deep .ag-cell {\n transition: background-color var(--grid-hover-transition, 150ms) ease;\n}\n\n/* ========================================\n Visual Config: Cell Padding\n ======================================== */\n\n.cell-padding-compact ::ng-deep .ag-cell {\n padding-left: 8px;\n padding-right: 8px;\n}\n\n.cell-padding-compact ::ng-deep .ag-header-cell {\n padding-left: 8px;\n padding-right: 8px;\n}\n\n.cell-padding-normal ::ng-deep .ag-cell {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.cell-padding-normal ::ng-deep .ag-header-cell {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.cell-padding-comfortable ::ng-deep .ag-cell {\n padding-left: 16px;\n padding-right: 16px;\n}\n\n.cell-padding-comfortable ::ng-deep .ag-header-cell {\n padding-left: 16px;\n padding-right: 16px;\n}\n\n/* ========================================\n Visual Config: Checkbox Styles\n ======================================== */\n\n/* Rounded checkbox */\n.checkbox-style-rounded ::ng-deep .ag-checkbox-input-wrapper {\n border-radius: 4px;\n}\n\n.checkbox-style-rounded ::ng-deep .ag-checkbox-input-wrapper::after {\n border-radius: 3px;\n}\n\n/* Filled checkbox */\n.checkbox-style-filled ::ng-deep .ag-checkbox-input-wrapper.ag-checked {\n background-color: var(--grid-checkbox-color, #2196F3);\n border-color: var(--grid-checkbox-color, #2196F3);\n}\n\n.checkbox-style-filled ::ng-deep .ag-checkbox-input-wrapper.ag-checked::after {\n color: #ffffff;\n}\n\n/* ========================================\n Cell Content Formatting\n ======================================== */\n\n/* Right-aligned cells (numbers) */\n::ng-deep .cell-align-right {\n text-align: right;\n justify-content: flex-end;\n}\n\n::ng-deep .header-align-right .ag-header-cell-label {\n justify-content: flex-end;\n}\n\n/* Empty cell placeholder */\n::ng-deep .cell-empty {\n color: #bdbdbd;\n font-style: normal;\n}\n\n/* Boolean icons */\n::ng-deep .cell-boolean-true {\n color: #43a047;\n font-size: 14px;\n}\n\n::ng-deep .cell-boolean-false {\n color: #bdbdbd;\n font-size: 14px;\n}\n\n/* Clickable links */\n::ng-deep .cell-link {\n color: var(--grid-accent-color, #2196F3);\n text-decoration: none;\n transition: color 0.15s;\n font-size: inherit;\n font-family: inherit;\n line-height: inherit;\n}\n\n::ng-deep .cell-link:hover {\n color: #1976D2;\n text-decoration: underline;\n}\n\n/* Email cells */\n::ng-deep .cell-email {\n font-family: inherit;\n font-size: 13px;\n}\n\n/* URL cells */\n::ng-deep .cell-url {\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n font-size: 13px;\n}\n\n/* Phone cells */\n::ng-deep .cell-phone {\n font-variant-numeric: tabular-nums;\n letter-spacing: 0.3px;\n}\n\n/* ========================================\n Skeleton Loading Animation\n ======================================== */\n\n@keyframes skeleton-shimmer {\n 0% {\n background-position: -200px 0;\n }\n 100% {\n background-position: calc(200px + 100%) 0;\n }\n}\n\n.skeleton-row {\n display: flex;\n height: 40px;\n align-items: center;\n padding: 0 12px;\n border-bottom: 1px solid var(--grid-cell-border-color, #f0f0f0);\n}\n\n.skeleton-cell {\n height: 16px;\n border-radius: 4px;\n background: linear-gradient(\n 90deg,\n #f0f0f0 0px,\n #e8e8e8 40px,\n #f0f0f0 80px\n );\n background-size: 200px 100%;\n animation: skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-cell-short {\n width: 60px;\n}\n\n.skeleton-cell-medium {\n width: 120px;\n}\n\n.skeleton-cell-long {\n width: 180px;\n}\n\n/* Selection checkbox column header */\n::ng-deep .ag-header-select-all {\n margin-right: 0;\n}\n\n/* ========================================\n Row Border Enhancement\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-row {\n border-bottom: 1px solid var(--grid-cell-border-color, #f0f0f0);\n}\n\n::ng-deep .ag-theme-alpine .ag-row:last-child {\n border-bottom: none;\n}\n\n/* ========================================\n Focus States\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-cell-focus {\n border: none !important;\n outline: none !important;\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:focus {\n outline: 2px solid var(--grid-accent-color, #2196F3);\n outline-offset: -2px;\n}\n\n/* ========================================\n Scrollbar Styling\n ======================================== */\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-track {\n background: #f5f5f5;\n border-radius: 4px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-thumb {\n background: #c0c0c0;\n border-radius: 4px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-thumb:hover {\n background: #a0a0a0;\n}\n\n/* ========================================\n Pinned Column Styling\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-pinned-left-cols-container {\n border-right: 2px solid var(--grid-border-color, #e0e0e0);\n}\n\n::ng-deep .ag-theme-alpine .ag-pinned-right-cols-container {\n border-left: 2px solid var(--grid-border-color, #e0e0e0);\n}\n\n/* ========================================\n Header Cell Enhancements\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-header-cell {\n font-weight: 600;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n color: #546e7a;\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:hover {\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell-sortable:hover .ag-header-cell-label {\n color: var(--grid-accent-color, #2196F3);\n}\n\n/* Sort icon animation */\n::ng-deep .ag-theme-alpine .ag-sort-indicator-icon {\n transition: transform 0.2s ease;\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:hover .ag-sort-indicator-icon {\n transform: scale(1.1);\n}\n\n/* ========================================\n Enhanced Multi-Sort Indicators\n These styles use :host to ensure they're scoped to this component\n and !important to override AG Grid's built-in styles\n ======================================== */\n\n/* Highlight sorted column headers with eye-catching background */\n/* Ascending = blue tint */\n:host ::ng-deep .ag-header-cell-sorted-asc {\n background: linear-gradient(180deg, rgba(25, 118, 210, 0.15) 0%, rgba(25, 118, 210, 0.08) 100%) !important;\n position: relative;\n}\n\n/* Descending = pink tint */\n:host ::ng-deep .ag-header-cell-sorted-desc {\n background: linear-gradient(180deg, rgba(216, 27, 96, 0.15) 0%, rgba(216, 27, 96, 0.08) 100%) !important;\n position: relative;\n}\n\n/* Bottom border accent for sorted columns - ascending = blue */\n:host ::ng-deep .ag-header-cell-sorted-asc::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: #1976d2;\n}\n\n/* Bottom border accent for sorted columns - descending = pink */\n:host ::ng-deep .ag-header-cell-sorted-desc::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: #d81b60;\n}\n\n/* Sorted column header text - ascending = blue, bold */\n:host ::ng-deep .ag-header-cell-sorted-asc .ag-header-cell-text {\n color: #1976d2 !important;\n font-weight: 700 !important;\n}\n\n/* Sorted column header text - descending = pink, bold */\n:host ::ng-deep .ag-header-cell-sorted-desc .ag-header-cell-text {\n color: #d81b60 !important;\n font-weight: 700 !important;\n}\n\n/* Sort icons - larger and more prominent */\n/* Ascending = blue */\n:host ::ng-deep .ag-sort-ascending-icon {\n color: #1976d2 !important;\n}\n\n/* Descending = pink/magenta */\n:host ::ng-deep .ag-sort-descending-icon {\n color: #d81b60 !important;\n}\n\n:host ::ng-deep .ag-sort-ascending-icon .ag-icon,\n:host ::ng-deep .ag-sort-descending-icon .ag-icon {\n font-size: 14px !important;\n}\n\n/* Sort order number (1, 2, 3) - show as plain number, no bubble */\n/* This avoids AG Grid bug where .ag-sort-order is always present but empty when unsorted */\n/* The number is only visible when AG Grid populates it (i.e., when column is sorted) */\n/* Default blue for ascending */\n:host ::ng-deep .ag-sort-order {\n margin-left: 4px;\n font-size: 11px !important;\n font-weight: 700 !important;\n color: #1976d2 !important;\n}\n\n/* Sort order number - pink for descending */\n:host ::ng-deep .ag-header-cell-sorted-desc .ag-sort-order {\n color: #d81b60 !important;\n}\n\n/* Hover state for sortable headers */\n:host ::ng-deep .ag-header-cell-sortable:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n\n:host ::ng-deep .ag-header-cell-sortable:hover .ag-header-cell-text {\n color: #1976d2;\n}\n"] }]
|
|
4219
|
+
], template: "<!-- Grid Container -->\n<div\n #gridContainer\n [class]=\"gridContainerClasses.join(' ')\"\n [style.height]=\"gridHeightStyle\">\n\n <!-- Toolbar -->\n <div *ngIf=\"ShowToolbar\" class=\"mj-grid-toolbar\">\n <div class=\"toolbar-left\">\n <!-- Search -->\n <div *ngIf=\"ShowSearch\" class=\"toolbar-search\">\n <i class=\"fa-solid fa-search search-icon\"></i>\n <input\n type=\"text\"\n class=\"search-input\"\n [placeholder]=\"ToolbarConfig.searchPlaceholder || 'Search...'\"\n [value]=\"FilterText\"\n (input)=\"FilterText = $any($event.target).value\" />\n <button\n *ngIf=\"FilterText\"\n class=\"search-clear\"\n (click)=\"FilterText = ''\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Custom Left Buttons -->\n <ng-container *ngFor=\"let button of ToolbarConfig.customButtons\">\n <button\n *ngIf=\"button.position !== 'right' && isButtonVisible(button)\"\n class=\"toolbar-button\"\n [class]=\"button.cssClass\"\n [disabled]=\"isButtonDisabled(button)\"\n [title]=\"button.tooltip || ''\"\n (click)=\"onToolbarButtonClick(button)\">\n <i *ngIf=\"button.icon\" [class]=\"button.icon\"></i>\n <span *ngIf=\"button.text\">{{ button.text }}</span>\n </button>\n </ng-container>\n </div>\n\n <div class=\"toolbar-center\">\n <!-- Row Count -->\n <span *ngIf=\"ToolbarConfig.showRowCount !== false\" class=\"row-count\">\n {{ totalRowCount }} {{ totalRowCount === 1 ? 'row' : 'rows' }}\n </span>\n\n <!-- Selection Count -->\n <span *ngIf=\"ToolbarConfig.showSelectionCount && SelectedKeys.length > 0\" class=\"selection-count\">\n ({{ SelectedKeys.length }} selected)\n </span>\n </div>\n\n <div class=\"toolbar-right\">\n <!-- New/Add Button (predefined) -->\n <button\n *ngIf=\"ShowNewButton\"\n class=\"toolbar-button\"\n title=\"Create new record\"\n (click)=\"onAddClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n <span class=\"button-text\">New</span>\n </button>\n\n <!-- Refresh Button (predefined) -->\n <button\n *ngIf=\"ShowRefreshButton\"\n class=\"toolbar-button\"\n title=\"Refresh data\"\n [disabled]=\"loading\"\n (click)=\"onRefreshClick()\">\n <i class=\"fa-solid fa-arrows-rotate\" [class.fa-spin]=\"loading\"></i>\n <span class=\"button-text\">Refresh</span>\n </button>\n\n <!-- Export Button (predefined) -->\n <button\n *ngIf=\"ShowExportButton\"\n class=\"toolbar-button\"\n title=\"Export to Excel\"\n (click)=\"onExportClick()\">\n <i class=\"fa-solid fa-file-excel\"></i>\n <span class=\"button-text\">Export</span>\n </button>\n\n <!-- Delete Button (predefined) -->\n <button\n *ngIf=\"ShowDeleteButton && HasSelection\"\n class=\"toolbar-button toolbar-button-danger\"\n title=\"Delete selected records\"\n (click)=\"onDeleteClick()\">\n <i class=\"fa-solid fa-trash\"></i>\n <span class=\"button-text\">Delete</span>\n </button>\n\n <!-- Compare Button (predefined) -->\n <button\n *ngIf=\"ShowCompareButton\"\n class=\"toolbar-button\"\n title=\"Compare selected records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onCompareClick()\">\n <i class=\"fa-solid fa-code-compare\"></i>\n <span class=\"button-text\">Compare</span>\n </button>\n\n <!-- Merge Button (predefined) -->\n <button\n *ngIf=\"ShowMergeButton\"\n class=\"toolbar-button\"\n title=\"Merge selected records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onMergeClick()\">\n <i class=\"fa-solid fa-code-merge\"></i>\n <span class=\"button-text\">Merge</span>\n </button>\n\n <!-- Add to List Button (predefined) -->\n <button\n *ngIf=\"ShowAddToListButton\"\n class=\"toolbar-button\"\n title=\"Add selected records to a list\"\n [disabled]=\"!HasSelection\"\n (click)=\"onAddToListClick()\">\n <i class=\"fa-solid fa-list-check\"></i>\n <span class=\"button-text\">Add to List</span>\n </button>\n\n <!-- Search for Duplicates Button (predefined) -->\n <button\n *ngIf=\"ShowDuplicateSearchButton\"\n class=\"toolbar-button\"\n title=\"Search for duplicate records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onDuplicateSearchClick()\">\n <i class=\"fa-solid fa-magnifying-glass-plus\"></i>\n <span class=\"button-text\">Find Duplicates</span>\n </button>\n\n <!-- Communication Button (predefined) -->\n <button\n *ngIf=\"ShowCommunicationButton\"\n class=\"toolbar-button\"\n title=\"Send message to selected records\"\n [disabled]=\"!HasSelection\"\n (click)=\"onCommunicationClick()\">\n <i class=\"fa-solid fa-envelope\"></i>\n <span class=\"button-text\">Send Message</span>\n </button>\n\n <!-- Legacy ToolbarConfig buttons -->\n <!-- Add Button (legacy) -->\n <button\n *ngIf=\"ToolbarConfig.showAdd && AllowAdd && !ShowNewButton\"\n class=\"toolbar-button\"\n title=\"Add New\"\n (click)=\"onAddClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n\n <!-- Refresh Button (legacy) -->\n <button\n *ngIf=\"ToolbarConfig.showRefresh !== false && !ShowRefreshButton\"\n class=\"toolbar-button\"\n title=\"Refresh\"\n [disabled]=\"loading\"\n (click)=\"onRefreshClick()\">\n <i class=\"fa-solid fa-refresh\" [class.fa-spin]=\"loading\"></i>\n </button>\n\n <!-- Delete Button (legacy) -->\n <button\n *ngIf=\"ToolbarConfig.showDelete && AllowDelete && HasSelection && !ShowDeleteButton\"\n class=\"toolbar-button toolbar-button-danger\"\n title=\"Delete Selected\"\n (click)=\"onDeleteClick()\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n\n <!-- Export Button (legacy) -->\n <button\n *ngIf=\"ToolbarConfig.showExport && !ShowExportButton\"\n class=\"toolbar-button\"\n title=\"Export\"\n (click)=\"onExportClick()\">\n <i class=\"fa-solid fa-download\"></i>\n </button>\n\n <!-- Column Chooser Button -->\n <button\n *ngIf=\"ToolbarConfig.showColumnChooser && AllowColumnToggle\"\n class=\"toolbar-button\"\n title=\"Column Chooser\"\n (click)=\"onColumnChooserClick()\">\n <i class=\"fa-solid fa-columns\"></i>\n </button>\n\n <!-- Custom Right Buttons -->\n <ng-container *ngFor=\"let button of ToolbarConfig.customButtons\">\n <button\n *ngIf=\"button.position === 'right' && isButtonVisible(button)\"\n class=\"toolbar-button\"\n [class]=\"button.cssClass\"\n [disabled]=\"isButtonDisabled(button)\"\n [title]=\"button.tooltip || ''\"\n (click)=\"onToolbarButtonClick(button)\">\n <i *ngIf=\"button.icon\" [class]=\"button.icon\"></i>\n <span *ngIf=\"button.text\">{{ button.text }}</span>\n </button>\n </ng-container>\n\n <!-- Overflow Menu -->\n <div *ngIf=\"hasOverflowMenuItems\" class=\"toolbar-overflow\" (click)=\"$event.stopPropagation()\">\n <button\n class=\"toolbar-button overflow-trigger\"\n title=\"More actions\"\n (click)=\"toggleOverflowMenu()\">\n <i class=\"fa-solid fa-ellipsis-vertical\"></i>\n </button>\n\n <div *ngIf=\"showOverflowMenu\" class=\"overflow-menu\" [@fadeIn]>\n <!-- Export (if in overflow) -->\n <button *ngIf=\"showExportInOverflow\" class=\"overflow-item\" (click)=\"onExportClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-file-excel\"></i>\n <span>Export to Excel</span>\n </button>\n\n <!-- Entity Actions -->\n <ng-container *ngIf=\"ShowEntityActionButtons && EntityActions.length > 0\">\n <div class=\"overflow-divider\"></div>\n <div class=\"overflow-section-label\">Actions</div>\n <button\n *ngFor=\"let action of EntityActions\"\n class=\"overflow-item\"\n [disabled]=\"!isEntityActionEnabled(action)\"\n (click)=\"onEntityActionClick(action); closeOverflowMenu()\">\n <i [class]=\"action.icon || 'fa-solid fa-bolt'\"></i>\n <span>{{ action.name }}</span>\n </button>\n </ng-container>\n\n <!-- Column Chooser (if in overflow) -->\n <div *ngIf=\"showColumnChooserInOverflow\" class=\"overflow-divider\"></div>\n <button *ngIf=\"showColumnChooserInOverflow\" class=\"overflow-item\" (click)=\"onColumnChooserClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-columns\"></i>\n <span>Manage Columns</span>\n </button>\n\n <!-- Selection-dependent actions in overflow -->\n <ng-container *ngIf=\"HasSelection && hasSelectionDependentOverflowActions\">\n <div class=\"overflow-divider\"></div>\n <button *ngIf=\"showCommunicationInOverflow\" class=\"overflow-item\" (click)=\"onCommunicationClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-envelope\"></i>\n <span>Send Message</span>\n </button>\n </ng-container>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Aggregate Cards (displayed as summary cards above the grid) -->\n <div *ngIf=\"ShowAggregatePanel\" class=\"mj-aggregate-cards\">\n <ng-container *ngFor=\"let agg of CardAggregates\">\n <div class=\"aggregate-card\">\n <div class=\"aggregate-card-icon\" *ngIf=\"agg.icon\">\n <i [class]=\"agg.icon\"></i>\n </div>\n <div class=\"aggregate-card-content\">\n <span class=\"aggregate-card-label\">{{ agg.label }}</span>\n <span class=\"aggregate-card-value\">{{ getAggregateValue(agg) }}</span>\n </div>\n </div>\n </ng-container>\n <div *ngIf=\"AggregatesLoading\" class=\"aggregate-card-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </div>\n </div>\n\n <!-- Grid Content -->\n <div class=\"mj-grid-content\">\n <!-- Loading Overlay -->\n <div *ngIf=\"loading && rowData.length === 0\" class=\"mj-grid-loading-overlay\">\n <mj-loading text=\"Loading...\"></mj-loading>\n </div>\n\n <!-- Error State -->\n <div *ngIf=\"errorMessage && !loading\" class=\"mj-grid-error\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ errorMessage }}</span>\n <button class=\"error-retry\" (click)=\"Refresh()\">Retry</button>\n </div>\n\n <!-- Empty State -->\n <div *ngIf=\"!loading && !errorMessage && rowData.length === 0\" class=\"mj-grid-empty\">\n <i class=\"fa-solid fa-inbox\"></i>\n <span>No data to display</span>\n </div>\n\n <!-- AG Grid (Client-side mode) -->\n <ag-grid-angular\n *ngIf=\"!errorMessage && PaginationMode === 'client' && (rowData.length > 0 || loading)\"\n class=\"mj-ag-grid ag-theme-alpine\"\n [theme]=\"agGridTheme\"\n [columnDefs]=\"agColumnDefs\"\n [rowData]=\"rowData\"\n [defaultColDef]=\"defaultColDef\"\n [rowSelection]=\"agRowSelection\"\n [getRowId]=\"getRowId\"\n [suppressCellFocus]=\"true\"\n [rowHeight]=\"RowHeight\"\n [headerHeight]=\"ShowHeader ? undefined : 0\"\n (gridReady)=\"onGridReady($event)\"\n (cellClicked)=\"onAgCellClicked($event)\"\n (rowClicked)=\"onAgRowClicked($event)\"\n (rowDoubleClicked)=\"onAgRowDoubleClicked($event)\"\n (sortChanged)=\"onAgSortChanged($event)\"\n (selectionChanged)=\"onAgSelectionChanged($event)\"\n (columnResized)=\"onAgColumnResized($event)\"\n (columnMoved)=\"onAgColumnMoved($event)\">\n </ag-grid-angular>\n\n <!-- AG Grid (Infinite Scroll mode) -->\n <ag-grid-angular\n *ngIf=\"!errorMessage && PaginationMode === 'infinite'\"\n class=\"mj-ag-grid ag-theme-alpine\"\n [theme]=\"agGridTheme\"\n [columnDefs]=\"agColumnDefs\"\n [defaultColDef]=\"defaultColDef\"\n [rowSelection]=\"agRowSelection\"\n [getRowId]=\"getRowId\"\n [suppressCellFocus]=\"true\"\n [rowHeight]=\"RowHeight\"\n [headerHeight]=\"ShowHeader ? undefined : 0\"\n [rowModelType]=\"'infinite'\"\n [cacheBlockSize]=\"CacheBlockSize\"\n [maxBlocksInCache]=\"MaxBlocksInCache\"\n [infiniteInitialRowCount]=\"1\"\n [cacheOverflowSize]=\"2\"\n (gridReady)=\"onGridReady($event)\"\n (cellClicked)=\"onAgCellClicked($event)\"\n (rowClicked)=\"onAgRowClicked($event)\"\n (rowDoubleClicked)=\"onAgRowDoubleClicked($event)\"\n (sortChanged)=\"onAgSortChanged($event)\"\n (selectionChanged)=\"onAgSelectionChanged($event)\"\n (columnResized)=\"onAgColumnResized($event)\"\n (columnMoved)=\"onAgColumnMoved($event)\">\n </ag-grid-angular>\n\n </div>\n\n <!-- Aggregate Summary Row (outside mj-grid-content so it's always visible) -->\n <div *ngIf=\"ShowAggregateSummary\" class=\"mj-aggregate-summary\">\n <div class=\"aggregate-summary-content\">\n <ng-container *ngFor=\"let agg of ColumnAggregates\">\n <div class=\"aggregate-summary-item\">\n <i *ngIf=\"agg.icon\" [class]=\"agg.icon\"></i>\n <span class=\"agg-summary-label\">{{ agg.label }}:</span>\n <span class=\"agg-summary-value\">{{ getAggregateValue(agg) }}</span>\n </div>\n </ng-container>\n </div>\n <div *ngIf=\"AggregatesLoading\" class=\"aggregate-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </div>\n </div>\n</div>\n\n<!-- Export Dialog -->\n<mj-export-dialog\n [visible]=\"showExportDialog\"\n [config]=\"exportDialogConfig\"\n (closed)=\"onExportDialogClosed($event)\">\n</mj-export-dialog>\n", styles: ["/* ========================================\n CSS Custom Properties (Theme Variables)\n ======================================== */\n\n:host {\n /* Grid container */\n --grid-border-color: #e0e0e0;\n --grid-border-radius: 0px;\n --grid-background: #ffffff;\n\n /* Header */\n --grid-header-bg: #fafafa;\n --grid-header-text: #333333;\n --grid-header-font-weight: 600;\n --grid-header-height: 40px;\n --grid-header-border-color: #e0e0e0;\n\n /* Rows */\n --grid-row-height: 40px;\n --grid-row-bg: #ffffff;\n --grid-row-bg-alt: #fafafa;\n --grid-row-hover-bg: #f5f5f5;\n --grid-row-selected-bg: #fff9e6;\n --grid-row-selected-hover-bg: #fff3cc;\n\n /* Cells */\n --grid-cell-padding: 8px 12px;\n --grid-cell-text: #333333;\n --grid-cell-border-color: #f0f0f0;\n\n /* Selection - mellow yellow to avoid conflict with blue hyperlinks */\n --grid-checkbox-color: #2196F3;\n --grid-selection-indicator-color: #f9a825;\n\n /* Editing */\n --grid-edit-cell-bg: #ffffff;\n --grid-edit-cell-border: #2196F3;\n --grid-edit-cell-shadow: 0 0 0 3px rgba(33, 150, 243, 0.1);\n\n /* Sorting */\n --grid-sort-indicator-color: #2196F3;\n\n /* Toolbar */\n --grid-toolbar-bg: #ffffff;\n --grid-toolbar-height: 48px;\n --grid-toolbar-border-color: #e0e0e0;\n\n /* Loading */\n --grid-loading-overlay-bg: rgba(255, 255, 255, 0.8);\n\n /* Empty state */\n --grid-empty-text-color: #999999;\n --grid-empty-icon-color: #cccccc;\n\n display: block;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n}\n\n/* ========================================\n Grid Container\n ======================================== */\n\n.mj-grid-container {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--grid-border-color);\n border-radius: var(--grid-border-radius);\n background: var(--grid-background);\n overflow: hidden;\n}\n\n/* ========================================\n Toolbar\n ======================================== */\n\n.mj-grid-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n min-height: var(--grid-toolbar-height);\n padding: 0 12px;\n background: var(--grid-toolbar-bg);\n border-bottom: 1px solid var(--grid-toolbar-border-color);\n gap: 12px;\n}\n\n.toolbar-left,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-center {\n display: flex;\n align-items: center;\n gap: 8px;\n color: #666;\n font-size: 13px;\n}\n\n.toolbar-search {\n display: flex;\n align-items: center;\n position: relative;\n}\n\n.search-icon {\n position: absolute;\n left: 10px;\n color: #999;\n font-size: 13px;\n}\n\n.search-input {\n padding: 6px 30px 6px 32px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 13px;\n width: 200px;\n transition: border-color 0.2s, box-shadow 0.2s;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--grid-selection-indicator-color);\n box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.1);\n}\n\n.search-clear {\n position: absolute;\n right: 6px;\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px;\n color: #999;\n font-size: 12px;\n}\n\n.search-clear:hover {\n color: #666;\n}\n\n.toolbar-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: #f5f5f5;\n border: 1px solid #ddd;\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n color: #333;\n transition: background-color 0.2s, border-color 0.2s;\n}\n\n.toolbar-button:hover:not(:disabled) {\n background: #eee;\n border-color: #ccc;\n}\n\n.toolbar-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.toolbar-button i {\n font-size: 14px;\n}\n\n.toolbar-button-danger {\n color: #d32f2f;\n}\n\n.toolbar-button-danger:hover:not(:disabled) {\n background: #ffebee;\n border-color: #ffcdd2;\n}\n\n.row-count,\n.selection-count {\n font-weight: 500;\n}\n\n/* ========================================\n Grid Content\n ======================================== */\n\n.mj-grid-content {\n flex: 1;\n position: relative;\n overflow: hidden;\n}\n\n.mj-grid-scroll-container {\n height: 100%;\n overflow: auto;\n}\n\n/* ========================================\n Loading Overlay\n ======================================== */\n\n.mj-grid-loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--grid-loading-overlay-bg);\n z-index: 10;\n}\n\n/* ========================================\n Error State\n ======================================== */\n\n.mj-grid-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n color: #d32f2f;\n gap: 12px;\n}\n\n.mj-grid-error i {\n font-size: 32px;\n}\n\n.error-retry {\n padding: 8px 16px;\n background: #d32f2f;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n}\n\n.error-retry:hover {\n background: #c62828;\n}\n\n/* ========================================\n Empty State\n ======================================== */\n\n.mj-grid-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n color: var(--grid-empty-text-color);\n gap: 12px;\n}\n\n.mj-grid-empty i {\n font-size: 48px;\n color: var(--grid-empty-icon-color);\n}\n\n/* ========================================\n Header Row\n ======================================== */\n\n.mj-grid-header {\n display: flex;\n min-height: var(--grid-header-height);\n background: var(--grid-header-bg);\n border-bottom: 2px solid var(--grid-header-border-color);\n position: sticky;\n top: 0;\n z-index: 5;\n}\n\n.mj-grid-header-cell {\n display: flex;\n align-items: center;\n padding: var(--grid-cell-padding);\n font-weight: var(--grid-header-font-weight);\n color: var(--grid-header-text);\n font-size: 13px;\n user-select: none;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right: 1px solid var(--grid-cell-border-color);\n flex-shrink: 0;\n}\n\n.mj-grid-header-cell:last-child {\n border-right: none;\n}\n\n.mj-grid-header-cell.sortable {\n cursor: pointer;\n}\n\n.mj-grid-header-cell.sortable:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n\n.header-text {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.sort-indicator {\n display: flex;\n align-items: center;\n margin-left: 6px;\n color: var(--grid-sort-indicator-color);\n}\n\n.sort-indicator i {\n font-size: 12px;\n}\n\n.sort-index {\n font-size: 10px;\n margin-left: 2px;\n font-weight: normal;\n}\n\n/* ========================================\n Data Rows\n ======================================== */\n\n.mj-grid-row {\n display: flex;\n align-items: stretch;\n background: var(--grid-row-bg);\n transition: background-color 0.15s;\n cursor: default;\n}\n\n.mj-grid-row:hover {\n background: var(--grid-row-hover-bg);\n}\n\n.mj-grid-row.grid-row-alt {\n background: var(--grid-row-bg-alt);\n}\n\n.mj-grid-row.grid-row-alt:hover {\n background: var(--grid-row-hover-bg);\n}\n\n.mj-grid-row.grid-row-selected {\n background: var(--grid-row-selected-bg);\n}\n\n.mj-grid-row.grid-row-selected:hover {\n background: var(--grid-row-selected-hover-bg);\n}\n\n.mj-grid-row.grid-row-editing {\n background: #fffde7;\n}\n\n.mj-grid-row.grid-row-dirty {\n border-left: 3px solid #ff9800;\n}\n\n/* ========================================\n Data Cells\n ======================================== */\n\n.mj-grid-cell {\n display: flex;\n align-items: center;\n padding: var(--grid-cell-padding);\n color: var(--grid-cell-text);\n font-size: 13px;\n overflow: hidden;\n border-right: 1px solid transparent;\n flex-shrink: 0;\n}\n\n.mj-grid-cell:last-child {\n border-right: none;\n}\n\n/* Grid lines modes */\n.grid-lines-horizontal .mj-grid-row {\n border-bottom: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-vertical .mj-grid-cell {\n border-right: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-both .mj-grid-row {\n border-bottom: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-both .mj-grid-cell {\n border-right: 1px solid var(--grid-cell-border-color);\n}\n\n/* Cell alignment */\n.mj-grid-cell.align-left {\n justify-content: flex-start;\n}\n\n.mj-grid-cell.align-center {\n justify-content: center;\n}\n\n.mj-grid-cell.align-right {\n justify-content: flex-end;\n}\n\n.cell-content {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Special cells */\n.row-number-cell {\n width: 50px;\n min-width: 50px;\n max-width: 50px;\n justify-content: center;\n color: #999;\n font-size: 12px;\n background: var(--grid-header-bg);\n}\n\n.checkbox-cell {\n width: 40px;\n min-width: 40px;\n max-width: 40px;\n justify-content: center;\n}\n\n.checkbox-cell input[type=\"checkbox\"] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n accent-color: var(--grid-checkbox-color);\n}\n\n/* ========================================\n Virtual Scrolling\n ======================================== */\n\n.mj-grid-virtual-spacer {\n flex-shrink: 0;\n}\n\n/* ========================================\n Responsive Adjustments\n ======================================== */\n\n@media (max-width: 768px) {\n .mj-grid-toolbar {\n flex-wrap: wrap;\n padding: 8px;\n }\n\n .toolbar-search {\n order: 3;\n width: 100%;\n margin-top: 8px;\n }\n\n .search-input {\n width: 100%;\n }\n\n .toolbar-center {\n order: 2;\n }\n\n /* Hide button text on mobile */\n .toolbar-button .button-text {\n display: none;\n }\n}\n\n/* ========================================\n Toolbar Button Text\n ======================================== */\n\n.toolbar-button .button-text {\n font-size: 13px;\n}\n\n/* ========================================\n Overflow Menu\n ======================================== */\n\n.toolbar-overflow {\n position: relative;\n}\n\n.overflow-trigger {\n padding: 6px 8px !important;\n}\n\n.overflow-trigger i {\n font-size: 16px;\n}\n\n.overflow-menu {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n min-width: 220px;\n background: #ffffff;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.08);\n z-index: 1000;\n overflow: hidden;\n}\n\n.overflow-item {\n display: flex;\n align-items: center;\n width: 100%;\n padding: 10px 16px;\n border: none;\n background: none;\n cursor: pointer;\n font-size: 14px;\n color: #333;\n text-align: left;\n gap: 12px;\n transition: background-color 0.15s;\n}\n\n.overflow-item:hover:not(:disabled) {\n background: #f5f5f5;\n}\n\n.overflow-item:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.overflow-item i {\n width: 18px;\n font-size: 14px;\n color: #666;\n text-align: center;\n}\n\n.overflow-item span {\n flex: 1;\n}\n\n.overflow-divider {\n height: 1px;\n background: #e0e0e0;\n margin: 4px 0;\n}\n\n.overflow-section-label {\n padding: 8px 16px 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n color: #999;\n letter-spacing: 0.5px;\n}\n\n/* Entity Actions submenu styling */\n.overflow-item.action-item i {\n color: #2196F3;\n}\n\n/* ========================================\n Highlight Matches\n ======================================== */\n\n::ng-deep .highlight-match {\n background-color: #fff176;\n border-radius: 2px;\n padding: 0 1px;\n}\n\n/* ========================================\n AG Grid Customizations\n ======================================== */\n\n.mj-ag-grid {\n width: 100%;\n height: 100%;\n}\n\n::ng-deep .ag-theme-alpine {\n /* Row colors */\n --ag-row-hover-color: var(--grid-row-hover-bg);\n --ag-selected-row-background-color: var(--grid-row-selected-bg);\n --ag-header-background-color: var(--grid-header-bg);\n\n /* Selection accent colors - mellow yellow */\n --ag-range-selection-background-color: rgba(249, 168, 37, 0.15);\n --ag-range-selection-border-color: #f9a825;\n\n /* Ensure borders are visible */\n --ag-borders: none;\n --ag-row-border-color: var(--grid-cell-border-color);\n}\n\n/* Selected row styling - left indicator bar only, background handled by AG Grid theme */\n::ng-deep .ag-row-selected {\n box-shadow: inset 4px 0 0 0 #f9a825;\n}\n\n/* Selection checkbox styling */\n::ng-deep .ag-theme-alpine .ag-checkbox-input-wrapper {\n width: 18px;\n height: 18px;\n}\n\n::ng-deep .ag-theme-alpine .ag-checkbox-input-wrapper.ag-checked::after {\n color: var(--grid-checkbox-color, #2196F3);\n}\n\n/* Row hover effect */\n::ng-deep .ag-theme-alpine .ag-row:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, #f0f7ff);\n}\n\n/* ========================================\n Visual Config: Header Styles\n ======================================== */\n\n/* Flat header (minimal) */\n.header-style-flat ::ng-deep .ag-header {\n background: var(--grid-header-bg, #fafafa);\n border-bottom: 1px solid var(--grid-header-border-color, #e0e0e0);\n}\n\n/* Elevated header (default - subtle shadow) */\n.header-style-elevated ::ng-deep .ag-header {\n background: linear-gradient(180deg, #ffffff 0%, #f8f9fa 100%);\n border-bottom: 1px solid var(--grid-header-border-color, #e0e0e0);\n}\n\n.header-style-elevated.header-shadow ::ng-deep .ag-header {\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06);\n}\n\n/* Gradient header (more prominent) */\n.header-style-gradient ::ng-deep .ag-header {\n background: linear-gradient(180deg, #f8f9fa 0%, #e9ecef 100%);\n border-bottom: none;\n}\n\n.header-style-gradient.header-shadow ::ng-deep .ag-header {\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n}\n\n/* Bold header (high contrast) */\n.header-style-bold ::ng-deep .ag-header {\n background: linear-gradient(180deg, #37474f 0%, #263238 100%);\n border-bottom: none;\n}\n\n.header-style-bold ::ng-deep .ag-header-cell-text {\n color: #ffffff;\n font-weight: 600;\n}\n\n.header-style-bold ::ng-deep .ag-header-icon {\n color: rgba(255, 255, 255, 0.7);\n}\n\n.header-style-bold ::ng-deep .ag-header-cell:hover {\n background: rgba(255, 255, 255, 0.1);\n}\n\n/* Header sort icons enhancement */\n::ng-deep .ag-header-cell-sorted-asc .ag-icon-asc,\n::ng-deep .ag-header-cell-sorted-desc .ag-icon-desc {\n color: var(--grid-accent-color, var(--grid-sort-indicator-color, #2196F3));\n}\n\n/* ========================================\n Visual Config: Zebra Striping\n ======================================== */\n\n/* Subtle contrast */\n.alternate-rows-subtle ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.015);\n}\n\n.alternate-rows-subtle ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, #f5f5f5);\n}\n\n/* Medium contrast (default) */\n.alternate-rows-medium ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.025);\n}\n\n.alternate-rows-medium ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, #f5f5f5);\n}\n\n/* Strong contrast */\n.alternate-rows-strong ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n.alternate-rows-strong ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, #f5f5f5);\n}\n\n/* ========================================\n Visual Config: Hover Transitions\n ======================================== */\n\n.hover-transitions ::ng-deep .ag-row {\n transition: background-color var(--grid-hover-transition, 150ms) ease;\n}\n\n.hover-transitions ::ng-deep .ag-cell {\n transition: background-color var(--grid-hover-transition, 150ms) ease;\n}\n\n/* ========================================\n Visual Config: Cell Padding\n ======================================== */\n\n.cell-padding-compact ::ng-deep .ag-cell {\n padding-left: 8px;\n padding-right: 8px;\n}\n\n.cell-padding-compact ::ng-deep .ag-header-cell {\n padding-left: 8px;\n padding-right: 8px;\n}\n\n.cell-padding-normal ::ng-deep .ag-cell {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.cell-padding-normal ::ng-deep .ag-header-cell {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.cell-padding-comfortable ::ng-deep .ag-cell {\n padding-left: 16px;\n padding-right: 16px;\n}\n\n.cell-padding-comfortable ::ng-deep .ag-header-cell {\n padding-left: 16px;\n padding-right: 16px;\n}\n\n/* ========================================\n Visual Config: Checkbox Styles\n ======================================== */\n\n/* Rounded checkbox */\n.checkbox-style-rounded ::ng-deep .ag-checkbox-input-wrapper {\n border-radius: 4px;\n}\n\n.checkbox-style-rounded ::ng-deep .ag-checkbox-input-wrapper::after {\n border-radius: 3px;\n}\n\n/* Filled checkbox */\n.checkbox-style-filled ::ng-deep .ag-checkbox-input-wrapper.ag-checked {\n background-color: var(--grid-checkbox-color, #2196F3);\n border-color: var(--grid-checkbox-color, #2196F3);\n}\n\n.checkbox-style-filled ::ng-deep .ag-checkbox-input-wrapper.ag-checked::after {\n color: #ffffff;\n}\n\n/* ========================================\n Cell Content Formatting\n ======================================== */\n\n/* Right-aligned cells (numbers) */\n::ng-deep .cell-align-right {\n text-align: right;\n justify-content: flex-end;\n}\n\n::ng-deep .header-align-right .ag-header-cell-label {\n justify-content: flex-end;\n}\n\n/* Empty cell placeholder */\n::ng-deep .cell-empty {\n color: #bdbdbd;\n font-style: normal;\n}\n\n/* Boolean icons */\n::ng-deep .cell-boolean-true {\n color: #43a047;\n font-size: 14px;\n}\n\n::ng-deep .cell-boolean-false {\n color: #bdbdbd;\n font-size: 14px;\n}\n\n/* Clickable links */\n::ng-deep .cell-link {\n color: var(--grid-accent-color, #2196F3);\n text-decoration: none;\n transition: color 0.15s;\n font-size: inherit;\n font-family: inherit;\n line-height: inherit;\n}\n\n::ng-deep .cell-link:hover {\n color: #1976D2;\n text-decoration: underline;\n}\n\n/* Email cells */\n::ng-deep .cell-email {\n font-family: inherit;\n font-size: 13px;\n}\n\n/* Foreign key link cells */\n::ng-deep .cell-fk-link {\n color: var(--grid-accent-color, #1976D2);\n text-decoration: none;\n cursor: pointer;\n transition: color 0.15s, text-decoration 0.15s;\n}\n\n::ng-deep .cell-fk-link:hover {\n color: #1565C0;\n text-decoration: underline;\n}\n\n::ng-deep .cell-fk-link:active {\n color: #0D47A1;\n}\n\n/* URL cells */\n::ng-deep .cell-url {\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n font-size: 13px;\n}\n\n/* Phone cells */\n::ng-deep .cell-phone {\n font-variant-numeric: tabular-nums;\n letter-spacing: 0.3px;\n}\n\n/* ========================================\n Skeleton Loading Animation\n ======================================== */\n\n@keyframes skeleton-shimmer {\n 0% {\n background-position: -200px 0;\n }\n 100% {\n background-position: calc(200px + 100%) 0;\n }\n}\n\n.skeleton-row {\n display: flex;\n height: 40px;\n align-items: center;\n padding: 0 12px;\n border-bottom: 1px solid var(--grid-cell-border-color, #f0f0f0);\n}\n\n.skeleton-cell {\n height: 16px;\n border-radius: 4px;\n background: linear-gradient(\n 90deg,\n #f0f0f0 0px,\n #e8e8e8 40px,\n #f0f0f0 80px\n );\n background-size: 200px 100%;\n animation: skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-cell-short {\n width: 60px;\n}\n\n.skeleton-cell-medium {\n width: 120px;\n}\n\n.skeleton-cell-long {\n width: 180px;\n}\n\n/* Selection checkbox column header */\n::ng-deep .ag-header-select-all {\n margin-right: 0;\n}\n\n/* ========================================\n Row Border Enhancement\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-row {\n border-bottom: 1px solid var(--grid-cell-border-color, #f0f0f0);\n}\n\n::ng-deep .ag-theme-alpine .ag-row:last-child {\n border-bottom: none;\n}\n\n/* ========================================\n Focus States\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-cell-focus {\n border: none !important;\n outline: none !important;\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:focus {\n outline: 2px solid var(--grid-accent-color, #2196F3);\n outline-offset: -2px;\n}\n\n/* ========================================\n Scrollbar Styling\n ======================================== */\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-track {\n background: #f5f5f5;\n border-radius: 4px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-thumb {\n background: #c0c0c0;\n border-radius: 4px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-thumb:hover {\n background: #a0a0a0;\n}\n\n/* ========================================\n Pinned Column Styling\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-pinned-left-cols-container {\n border-right: 2px solid var(--grid-border-color, #e0e0e0);\n}\n\n::ng-deep .ag-theme-alpine .ag-pinned-right-cols-container {\n border-left: 2px solid var(--grid-border-color, #e0e0e0);\n}\n\n/* ========================================\n Header Cell Enhancements\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-header-cell {\n font-weight: 600;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n color: #546e7a;\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:hover {\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell-sortable:hover .ag-header-cell-label {\n color: var(--grid-accent-color, #2196F3);\n}\n\n/* Sort icon animation */\n::ng-deep .ag-theme-alpine .ag-sort-indicator-icon {\n transition: transform 0.2s ease;\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:hover .ag-sort-indicator-icon {\n transform: scale(1.1);\n}\n\n/* ========================================\n Enhanced Multi-Sort Indicators\n These styles use :host to ensure they're scoped to this component\n and !important to override AG Grid's built-in styles\n ======================================== */\n\n/* Highlight sorted column headers with eye-catching background */\n/* Ascending = blue tint */\n:host ::ng-deep .ag-header-cell-sorted-asc {\n background: linear-gradient(180deg, rgba(25, 118, 210, 0.15) 0%, rgba(25, 118, 210, 0.08) 100%) !important;\n position: relative;\n}\n\n/* Descending = pink tint */\n:host ::ng-deep .ag-header-cell-sorted-desc {\n background: linear-gradient(180deg, rgba(216, 27, 96, 0.15) 0%, rgba(216, 27, 96, 0.08) 100%) !important;\n position: relative;\n}\n\n/* Bottom border accent for sorted columns - ascending = blue */\n:host ::ng-deep .ag-header-cell-sorted-asc::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: #1976d2;\n}\n\n/* Bottom border accent for sorted columns - descending = pink */\n:host ::ng-deep .ag-header-cell-sorted-desc::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: #d81b60;\n}\n\n/* Sorted column header text - ascending = blue, bold */\n:host ::ng-deep .ag-header-cell-sorted-asc .ag-header-cell-text {\n color: #1976d2 !important;\n font-weight: 700 !important;\n}\n\n/* Sorted column header text - descending = pink, bold */\n:host ::ng-deep .ag-header-cell-sorted-desc .ag-header-cell-text {\n color: #d81b60 !important;\n font-weight: 700 !important;\n}\n\n/* Sort icons - larger and more prominent */\n/* Ascending = blue */\n:host ::ng-deep .ag-sort-ascending-icon {\n color: #1976d2 !important;\n}\n\n/* Descending = pink/magenta */\n:host ::ng-deep .ag-sort-descending-icon {\n color: #d81b60 !important;\n}\n\n:host ::ng-deep .ag-sort-ascending-icon .ag-icon,\n:host ::ng-deep .ag-sort-descending-icon .ag-icon {\n font-size: 14px !important;\n}\n\n/* Sort order number (1, 2, 3) - show as plain number, no bubble */\n/* This avoids AG Grid bug where .ag-sort-order is always present but empty when unsorted */\n/* The number is only visible when AG Grid populates it (i.e., when column is sorted) */\n/* Default blue for ascending */\n:host ::ng-deep .ag-sort-order {\n margin-left: 4px;\n font-size: 11px !important;\n font-weight: 700 !important;\n color: #1976d2 !important;\n}\n\n/* Sort order number - pink for descending */\n:host ::ng-deep .ag-header-cell-sorted-desc .ag-sort-order {\n color: #d81b60 !important;\n}\n\n/* Hover state for sortable headers */\n:host ::ng-deep .ag-header-cell-sortable:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n\n:host ::ng-deep .ag-header-cell-sortable:hover .ag-header-cell-text {\n color: #1976d2;\n}\n\n/* ========================================\n Aggregate Summary Row\n ======================================== */\n\n.mj-aggregate-summary {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px;\n background: linear-gradient(to bottom, #f8fafc, #f1f5f9);\n border-top: 1px solid #e2e8f0;\n font-size: 13px;\n min-height: 44px;\n}\n\n.aggregate-summary-content {\n display: flex;\n flex-wrap: wrap;\n gap: 20px;\n align-items: center;\n}\n\n.aggregate-summary-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: white;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n}\n\n.aggregate-summary-item i {\n font-size: 12px;\n color: #64748b;\n}\n\n.agg-summary-label {\n color: #64748b;\n font-weight: 500;\n}\n\n.agg-summary-value {\n color: #1e293b;\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n}\n\n.aggregate-loading {\n display: flex;\n align-items: center;\n color: #94a3b8;\n}\n\n.aggregate-loading i {\n font-size: 14px;\n}\n\n/* ========================================\n AGGREGATE CARDS (displayed above grid)\n ======================================== */\n\n.mj-aggregate-cards {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n padding: 12px 16px;\n background: linear-gradient(to bottom, #f8fafc, #f1f5f9);\n border-bottom: 1px solid #e2e8f0;\n flex-shrink: 0;\n}\n\n.aggregate-card {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n background: white;\n border: 1px solid #e2e8f0;\n border-radius: 10px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n min-width: 140px;\n transition: all 0.15s ease;\n}\n\n.aggregate-card:hover {\n border-color: #cbd5e1;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);\n}\n\n.aggregate-card-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n background: linear-gradient(135deg, #e0f2fe 0%, #bae6fd 100%);\n border-radius: 10px;\n color: #0284c7;\n font-size: 16px;\n flex-shrink: 0;\n}\n\n.aggregate-card-content {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.aggregate-card-label {\n font-size: 12px;\n font-weight: 500;\n color: #64748b;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.aggregate-card-value {\n font-size: 18px;\n font-weight: 700;\n color: #0f172a;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.aggregate-card-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 12px;\n color: #94a3b8;\n}\n\n.aggregate-card-loading i {\n font-size: 16px;\n}\n\n/* ========================================\n Text Wrapping Styles\n ======================================== */\n\n/* Cell class for wrapped text */\n::ng-deep .cell-wrap-text {\n white-space: normal !important;\n word-wrap: break-word;\n line-height: 1.4;\n padding-top: 8px !important;\n padding-bottom: 8px !important;\n}\n\n/* Ensure AG Grid cells allow wrapping */\n::ng-deep .ag-cell.cell-wrap-text {\n overflow: visible;\n text-overflow: clip;\n}\n\n/* Maintain minimum row height when wrapping */\n::ng-deep .ag-row[style*=\"auto-height\"] {\n min-height: 40px;\n}\n"] }]
|
|
3609
4220
|
}], () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i1.ExportService }], { Params: [{
|
|
3610
4221
|
type: Input
|
|
3611
4222
|
}], AllowLoad: [{
|
|
@@ -3660,6 +4271,8 @@ export class EntityDataGridComponent {
|
|
|
3660
4271
|
type: Input
|
|
3661
4272
|
}], RowHeight: [{
|
|
3662
4273
|
type: Input
|
|
4274
|
+
}], WrapText: [{
|
|
4275
|
+
type: Input
|
|
3663
4276
|
}], VirtualScroll: [{
|
|
3664
4277
|
type: Input
|
|
3665
4278
|
}], ShowRowNumbers: [{
|
|
@@ -3714,6 +4327,10 @@ export class EntityDataGridComponent {
|
|
|
3714
4327
|
type: Input
|
|
3715
4328
|
}], EntityActions: [{
|
|
3716
4329
|
type: Input
|
|
4330
|
+
}], AggregatesConfig: [{
|
|
4331
|
+
type: Input
|
|
4332
|
+
}], AggregatesLoaded: [{
|
|
4333
|
+
type: Output
|
|
3717
4334
|
}], BeforeRowSelect: [{
|
|
3718
4335
|
type: Output
|
|
3719
4336
|
}], AfterRowSelect: [{
|
|
@@ -3732,6 +4349,8 @@ export class EntityDataGridComponent {
|
|
|
3732
4349
|
type: Output
|
|
3733
4350
|
}], AfterRowDoubleClick: [{
|
|
3734
4351
|
type: Output
|
|
4352
|
+
}], ForeignKeyClick: [{
|
|
4353
|
+
type: Output
|
|
3735
4354
|
}], BeforeCellEdit: [{
|
|
3736
4355
|
type: Output
|
|
3737
4356
|
}], AfterCellEditBegin: [{
|
|
@@ -3826,5 +4445,5 @@ export class EntityDataGridComponent {
|
|
|
3826
4445
|
type: ViewChild,
|
|
3827
4446
|
args: ['gridContainer']
|
|
3828
4447
|
}] }); })();
|
|
3829
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityDataGridComponent, { className: "EntityDataGridComponent", filePath: "src/lib/entity-data-grid/entity-data-grid.component.ts", lineNumber:
|
|
4448
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityDataGridComponent, { className: "EntityDataGridComponent", filePath: "src/lib/entity-data-grid/entity-data-grid.component.ts", lineNumber: 151 }); })();
|
|
3830
4449
|
//# sourceMappingURL=entity-data-grid.component.js.map
|