@memberjunction/ng-entity-viewer 0.0.1 → 2.121.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/README.md +224 -43
- package/dist/lib/entity-cards/entity-cards.component.d.ts +163 -0
- package/dist/lib/entity-cards/entity-cards.component.d.ts.map +1 -0
- package/dist/lib/entity-cards/entity-cards.component.js +797 -0
- package/dist/lib/entity-cards/entity-cards.component.js.map +1 -0
- package/dist/lib/entity-grid/entity-grid.component.d.ts +216 -0
- package/dist/lib/entity-grid/entity-grid.component.d.ts.map +1 -0
- package/dist/lib/entity-grid/entity-grid.component.js +676 -0
- package/dist/lib/entity-grid/entity-grid.component.js.map +1 -0
- package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.d.ts +182 -0
- package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.d.ts.map +1 -0
- package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.js +787 -0
- package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.js.map +1 -0
- package/dist/lib/entity-viewer/entity-viewer.component.d.ts +252 -0
- package/dist/lib/entity-viewer/entity-viewer.component.d.ts.map +1 -0
- package/dist/lib/entity-viewer/entity-viewer.component.js +883 -0
- package/dist/lib/entity-viewer/entity-viewer.component.js.map +1 -0
- package/dist/lib/pagination/pagination.component.d.ts +60 -0
- package/dist/lib/pagination/pagination.component.d.ts.map +1 -0
- package/dist/lib/pagination/pagination.component.js +199 -0
- package/dist/lib/pagination/pagination.component.js.map +1 -0
- package/dist/lib/pill/pill.component.d.ts +58 -0
- package/dist/lib/pill/pill.component.d.ts.map +1 -0
- package/dist/lib/pill/pill.component.js +125 -0
- package/dist/lib/pill/pill.component.js.map +1 -0
- package/dist/lib/types.d.ts +316 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +30 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/utils/highlight.util.d.ts +69 -0
- package/dist/lib/utils/highlight.util.d.ts.map +1 -0
- package/dist/lib/utils/highlight.util.js +214 -0
- package/dist/lib/utils/highlight.util.js.map +1 -0
- package/dist/module.d.ts +38 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +83 -0
- package/dist/module.js.map +1 -0
- package/dist/public-api.d.ts +16 -0
- package/dist/public-api.d.ts.map +1 -0
- package/dist/public-api.js +20 -0
- package/dist/public-api.js.map +1 -0
- package/package.json +45 -6
|
@@ -0,0 +1,787 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
2
|
+
import { RunView, Metadata, EntityFieldValueListType } from '@memberjunction/core';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
import * as i1 from "@memberjunction/ng-shared-generic";
|
|
5
|
+
import * as i2 from "../pill/pill.component";
|
|
6
|
+
const _forTrack0 = ($index, $item) => $item.name;
|
|
7
|
+
const _forTrack1 = ($index, $item) => $item.relatedEntityName;
|
|
8
|
+
const _forTrack2 = ($index, $item) => $item.PrimaryKey.ToString();
|
|
9
|
+
function EntityRecordDetailPanelComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
10
|
+
i0.ɵɵelement(0, "i", 18);
|
|
11
|
+
} if (rf & 2) {
|
|
12
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
13
|
+
i0.ɵɵclassMap(ctx_r0.getEntityIconClass());
|
|
14
|
+
} }
|
|
15
|
+
function EntityRecordDetailPanelComponent_Conditional_15_For_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
16
|
+
const _r2 = i0.ɵɵgetCurrentView();
|
|
17
|
+
i0.ɵɵelementStart(0, "div", 20)(1, "div", 23);
|
|
18
|
+
i0.ɵɵelement(2, "i", 24);
|
|
19
|
+
i0.ɵɵelementStart(3, "span", 25);
|
|
20
|
+
i0.ɵɵtext(4);
|
|
21
|
+
i0.ɵɵelementEnd();
|
|
22
|
+
i0.ɵɵelementStart(5, "button", 26);
|
|
23
|
+
i0.ɵɵlistener("click", function EntityRecordDetailPanelComponent_Conditional_15_For_2_Conditional_0_Template_button_click_5_listener($event) { i0.ɵɵrestoreView(_r2); const field_r3 = i0.ɵɵnextContext().$implicit; const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.copyToClipboard(field_r3.value, $event)); });
|
|
24
|
+
i0.ɵɵelement(6, "i", 27);
|
|
25
|
+
i0.ɵɵelementEnd()()();
|
|
26
|
+
} if (rf & 2) {
|
|
27
|
+
const field_r3 = i0.ɵɵnextContext().$implicit;
|
|
28
|
+
i0.ɵɵadvance(4);
|
|
29
|
+
i0.ɵɵtextInterpolate(field_r3.label);
|
|
30
|
+
} }
|
|
31
|
+
function EntityRecordDetailPanelComponent_Conditional_15_For_2_Conditional_1_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
32
|
+
i0.ɵɵelementStart(0, "span", 29);
|
|
33
|
+
i0.ɵɵtext(1);
|
|
34
|
+
i0.ɵɵelementEnd();
|
|
35
|
+
} if (rf & 2) {
|
|
36
|
+
const field_r3 = i0.ɵɵnextContext(2).$implicit;
|
|
37
|
+
i0.ɵɵadvance();
|
|
38
|
+
i0.ɵɵtextInterpolate(field_r3.displayValue);
|
|
39
|
+
} }
|
|
40
|
+
function EntityRecordDetailPanelComponent_Conditional_15_For_2_Conditional_1_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
41
|
+
i0.ɵɵelementStart(0, "span", 30);
|
|
42
|
+
i0.ɵɵtext(1);
|
|
43
|
+
i0.ɵɵelementEnd();
|
|
44
|
+
} if (rf & 2) {
|
|
45
|
+
const field_r3 = i0.ɵɵnextContext(2).$implicit;
|
|
46
|
+
i0.ɵɵadvance();
|
|
47
|
+
i0.ɵɵtextInterpolate(field_r3.value);
|
|
48
|
+
} }
|
|
49
|
+
function EntityRecordDetailPanelComponent_Conditional_15_For_2_Conditional_1_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
50
|
+
const _r4 = i0.ɵɵgetCurrentView();
|
|
51
|
+
i0.ɵɵelementStart(0, "button", 32);
|
|
52
|
+
i0.ɵɵlistener("click", function EntityRecordDetailPanelComponent_Conditional_15_For_2_Conditional_1_Conditional_6_Template_button_click_0_listener($event) { i0.ɵɵrestoreView(_r4); const field_r3 = i0.ɵɵnextContext(2).$implicit; const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onForeignKeyClick(field_r3, $event)); });
|
|
53
|
+
i0.ɵɵelement(1, "i", 17);
|
|
54
|
+
i0.ɵɵelementEnd();
|
|
55
|
+
} if (rf & 2) {
|
|
56
|
+
const field_r3 = i0.ɵɵnextContext(2).$implicit;
|
|
57
|
+
i0.ɵɵpropertyInterpolate1("title", "Open ", field_r3.relatedEntityName, " record");
|
|
58
|
+
} }
|
|
59
|
+
function EntityRecordDetailPanelComponent_Conditional_15_For_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
60
|
+
i0.ɵɵelementStart(0, "div", 21)(1, "span", 25);
|
|
61
|
+
i0.ɵɵtext(2);
|
|
62
|
+
i0.ɵɵelementEnd();
|
|
63
|
+
i0.ɵɵelementStart(3, "div", 28);
|
|
64
|
+
i0.ɵɵtemplate(4, EntityRecordDetailPanelComponent_Conditional_15_For_2_Conditional_1_Conditional_4_Template, 2, 1, "span", 29)(5, EntityRecordDetailPanelComponent_Conditional_15_For_2_Conditional_1_Conditional_5_Template, 2, 1, "span", 30)(6, EntityRecordDetailPanelComponent_Conditional_15_For_2_Conditional_1_Conditional_6_Template, 2, 2, "button", 31);
|
|
65
|
+
i0.ɵɵelementEnd()();
|
|
66
|
+
} if (rf & 2) {
|
|
67
|
+
const field_r3 = i0.ɵɵnextContext().$implicit;
|
|
68
|
+
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
69
|
+
i0.ɵɵadvance(2);
|
|
70
|
+
i0.ɵɵtextInterpolate(field_r3.label);
|
|
71
|
+
i0.ɵɵadvance();
|
|
72
|
+
i0.ɵɵclassProp("fk-id-only", !ctx_r0.hasFriendlyName(field_r3));
|
|
73
|
+
i0.ɵɵadvance();
|
|
74
|
+
i0.ɵɵconditional(ctx_r0.hasFriendlyName(field_r3) ? 4 : 5);
|
|
75
|
+
i0.ɵɵadvance(2);
|
|
76
|
+
i0.ɵɵconditional(field_r3.relatedEntityName ? 6 : -1);
|
|
77
|
+
} }
|
|
78
|
+
function EntityRecordDetailPanelComponent_Conditional_15_For_2_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
79
|
+
i0.ɵɵelementStart(0, "div", 22)(1, "span", 25);
|
|
80
|
+
i0.ɵɵtext(2);
|
|
81
|
+
i0.ɵɵelementEnd();
|
|
82
|
+
i0.ɵɵelement(3, "mj-pill", 33);
|
|
83
|
+
i0.ɵɵelementEnd();
|
|
84
|
+
} if (rf & 2) {
|
|
85
|
+
const field_r3 = i0.ɵɵnextContext().$implicit;
|
|
86
|
+
i0.ɵɵadvance(2);
|
|
87
|
+
i0.ɵɵtextInterpolate(field_r3.label);
|
|
88
|
+
i0.ɵɵadvance();
|
|
89
|
+
i0.ɵɵproperty("value", field_r3.value);
|
|
90
|
+
} }
|
|
91
|
+
function EntityRecordDetailPanelComponent_Conditional_15_For_2_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
92
|
+
i0.ɵɵelementStart(0, "div", 22)(1, "span", 25);
|
|
93
|
+
i0.ɵɵtext(2);
|
|
94
|
+
i0.ɵɵelementEnd();
|
|
95
|
+
i0.ɵɵelementStart(3, "span", 34);
|
|
96
|
+
i0.ɵɵtext(4);
|
|
97
|
+
i0.ɵɵelementEnd()();
|
|
98
|
+
} if (rf & 2) {
|
|
99
|
+
const field_r3 = i0.ɵɵnextContext().$implicit;
|
|
100
|
+
i0.ɵɵadvance(2);
|
|
101
|
+
i0.ɵɵtextInterpolate(field_r3.label);
|
|
102
|
+
i0.ɵɵadvance(2);
|
|
103
|
+
i0.ɵɵtextInterpolate(field_r3.value);
|
|
104
|
+
} }
|
|
105
|
+
function EntityRecordDetailPanelComponent_Conditional_15_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
106
|
+
i0.ɵɵtemplate(0, EntityRecordDetailPanelComponent_Conditional_15_For_2_Conditional_0_Template, 7, 1, "div", 20)(1, EntityRecordDetailPanelComponent_Conditional_15_For_2_Conditional_1_Template, 7, 5, "div", 21)(2, EntityRecordDetailPanelComponent_Conditional_15_For_2_Conditional_2_Template, 4, 2, "div", 22)(3, EntityRecordDetailPanelComponent_Conditional_15_For_2_Conditional_3_Template, 5, 2, "div", 22);
|
|
107
|
+
} if (rf & 2) {
|
|
108
|
+
const field_r3 = ctx.$implicit;
|
|
109
|
+
i0.ɵɵconditional(field_r3.type === "primary-key" ? 0 : field_r3.type === "foreign-key" ? 1 : field_r3.type === "enum" ? 2 : 3);
|
|
110
|
+
} }
|
|
111
|
+
function EntityRecordDetailPanelComponent_Conditional_15_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
112
|
+
i0.ɵɵelementStart(0, "div", 19);
|
|
113
|
+
i0.ɵɵtext(1, "No details available");
|
|
114
|
+
i0.ɵɵelementEnd();
|
|
115
|
+
} }
|
|
116
|
+
function EntityRecordDetailPanelComponent_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
117
|
+
i0.ɵɵelementStart(0, "div", 13);
|
|
118
|
+
i0.ɵɵrepeaterCreate(1, EntityRecordDetailPanelComponent_Conditional_15_For_2_Template, 4, 1, null, null, _forTrack0);
|
|
119
|
+
i0.ɵɵtemplate(3, EntityRecordDetailPanelComponent_Conditional_15_Conditional_3_Template, 2, 0, "div", 19);
|
|
120
|
+
i0.ɵɵelementEnd();
|
|
121
|
+
} if (rf & 2) {
|
|
122
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
123
|
+
i0.ɵɵadvance();
|
|
124
|
+
i0.ɵɵrepeater(ctx_r0.displayFields);
|
|
125
|
+
i0.ɵɵadvance(2);
|
|
126
|
+
i0.ɵɵconditional(ctx_r0.displayFields.length === 0 ? 3 : -1);
|
|
127
|
+
} }
|
|
128
|
+
function EntityRecordDetailPanelComponent_Conditional_22_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
129
|
+
i0.ɵɵelementStart(0, "div", 35);
|
|
130
|
+
i0.ɵɵelement(1, "mj-loading", 36);
|
|
131
|
+
i0.ɵɵelementEnd();
|
|
132
|
+
} }
|
|
133
|
+
function EntityRecordDetailPanelComponent_Conditional_22_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
134
|
+
i0.ɵɵelementStart(0, "div", 19);
|
|
135
|
+
i0.ɵɵtext(1, "No related records");
|
|
136
|
+
i0.ɵɵelementEnd();
|
|
137
|
+
} }
|
|
138
|
+
function EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Conditional_8_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
139
|
+
i0.ɵɵelementStart(0, "div", 44);
|
|
140
|
+
i0.ɵɵelement(1, "mj-loading", 45);
|
|
141
|
+
i0.ɵɵelementEnd();
|
|
142
|
+
} }
|
|
143
|
+
function EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Conditional_8_Conditional_2_For_1_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
144
|
+
i0.ɵɵelementStart(0, "span", 51);
|
|
145
|
+
i0.ɵɵtext(1);
|
|
146
|
+
i0.ɵɵelementEnd();
|
|
147
|
+
} if (rf & 2) {
|
|
148
|
+
i0.ɵɵadvance();
|
|
149
|
+
i0.ɵɵtextInterpolate(ctx);
|
|
150
|
+
} }
|
|
151
|
+
function EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Conditional_8_Conditional_2_For_1_Template(rf, ctx) { if (rf & 1) {
|
|
152
|
+
const _r7 = i0.ɵɵgetCurrentView();
|
|
153
|
+
i0.ɵɵelementStart(0, "div", 48);
|
|
154
|
+
i0.ɵɵlistener("click", function EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Conditional_8_Conditional_2_For_1_Template_div_click_0_listener($event) { const rec_r8 = i0.ɵɵrestoreView(_r7).$implicit; const relEntity_r6 = i0.ɵɵnextContext(3).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.onRelatedRecordClick(relEntity_r6, rec_r8, $event)); });
|
|
155
|
+
i0.ɵɵelementStart(1, "div", 49)(2, "span", 50);
|
|
156
|
+
i0.ɵɵtext(3);
|
|
157
|
+
i0.ɵɵelementEnd();
|
|
158
|
+
i0.ɵɵtemplate(4, EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Conditional_8_Conditional_2_For_1_Conditional_4_Template, 2, 1, "span", 51);
|
|
159
|
+
i0.ɵɵelementEnd();
|
|
160
|
+
i0.ɵɵelement(5, "i", 52);
|
|
161
|
+
i0.ɵɵelementEnd();
|
|
162
|
+
} if (rf & 2) {
|
|
163
|
+
let tmp_25_0;
|
|
164
|
+
const rec_r8 = ctx.$implicit;
|
|
165
|
+
const relEntity_r6 = i0.ɵɵnextContext(3).$implicit;
|
|
166
|
+
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
167
|
+
i0.ɵɵadvance(3);
|
|
168
|
+
i0.ɵɵtextInterpolate(ctx_r0.getRelatedRecordDisplayName(relEntity_r6, rec_r8));
|
|
169
|
+
i0.ɵɵadvance();
|
|
170
|
+
i0.ɵɵconditional((tmp_25_0 = ctx_r0.getRelatedRecordSubtitle(relEntity_r6, rec_r8)) ? 4 : -1, tmp_25_0);
|
|
171
|
+
} }
|
|
172
|
+
function EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Conditional_8_Conditional_2_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
173
|
+
const _r9 = i0.ɵɵgetCurrentView();
|
|
174
|
+
i0.ɵɵelementStart(0, "div", 53);
|
|
175
|
+
i0.ɵɵlistener("click", function EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Conditional_8_Conditional_2_Conditional_2_Template_div_click_0_listener($event) { i0.ɵɵrestoreView(_r9); const relEntity_r6 = i0.ɵɵnextContext(3).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.onViewAllRelated(relEntity_r6, $event)); });
|
|
176
|
+
i0.ɵɵelementStart(1, "span");
|
|
177
|
+
i0.ɵɵtext(2);
|
|
178
|
+
i0.ɵɵelementEnd();
|
|
179
|
+
i0.ɵɵelement(3, "i", 54);
|
|
180
|
+
i0.ɵɵelementEnd();
|
|
181
|
+
} if (rf & 2) {
|
|
182
|
+
const relEntity_r6 = i0.ɵɵnextContext(3).$implicit;
|
|
183
|
+
i0.ɵɵadvance(2);
|
|
184
|
+
i0.ɵɵtextInterpolate1("View all ", relEntity_r6.count, " records");
|
|
185
|
+
} }
|
|
186
|
+
function EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Conditional_8_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
187
|
+
i0.ɵɵrepeaterCreate(0, EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Conditional_8_Conditional_2_For_1_Template, 6, 2, "div", 46, _forTrack2);
|
|
188
|
+
i0.ɵɵtemplate(2, EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Conditional_8_Conditional_2_Conditional_2_Template, 4, 1, "div", 47);
|
|
189
|
+
} if (rf & 2) {
|
|
190
|
+
const relEntity_r6 = i0.ɵɵnextContext(2).$implicit;
|
|
191
|
+
i0.ɵɵrepeater(relEntity_r6.records);
|
|
192
|
+
i0.ɵɵadvance(2);
|
|
193
|
+
i0.ɵɵconditional(relEntity_r6.count > 10 ? 2 : -1);
|
|
194
|
+
} }
|
|
195
|
+
function EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
196
|
+
i0.ɵɵelementStart(0, "div", 43);
|
|
197
|
+
i0.ɵɵtemplate(1, EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Conditional_8_Conditional_1_Template, 2, 0, "div", 44)(2, EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Conditional_8_Conditional_2_Template, 3, 1);
|
|
198
|
+
i0.ɵɵelementEnd();
|
|
199
|
+
} if (rf & 2) {
|
|
200
|
+
const relEntity_r6 = i0.ɵɵnextContext().$implicit;
|
|
201
|
+
i0.ɵɵadvance();
|
|
202
|
+
i0.ɵɵconditional(relEntity_r6.isLoadingRecords ? 1 : 2);
|
|
203
|
+
} }
|
|
204
|
+
function EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Template(rf, ctx) { if (rf & 1) {
|
|
205
|
+
const _r5 = i0.ɵɵgetCurrentView();
|
|
206
|
+
i0.ɵɵelementStart(0, "div", 38)(1, "div", 39);
|
|
207
|
+
i0.ɵɵlistener("click", function EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Template_div_click_1_listener($event) { const relEntity_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.toggleRelatedEntityExpansion(relEntity_r6, $event)); });
|
|
208
|
+
i0.ɵɵelement(2, "i", 40)(3, "i");
|
|
209
|
+
i0.ɵɵelementStart(4, "span", 41);
|
|
210
|
+
i0.ɵɵtext(5);
|
|
211
|
+
i0.ɵɵelementEnd();
|
|
212
|
+
i0.ɵɵelementStart(6, "span", 42);
|
|
213
|
+
i0.ɵɵtext(7);
|
|
214
|
+
i0.ɵɵelementEnd()();
|
|
215
|
+
i0.ɵɵtemplate(8, EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Conditional_8_Template, 3, 1, "div", 43);
|
|
216
|
+
i0.ɵɵelementEnd();
|
|
217
|
+
} if (rf & 2) {
|
|
218
|
+
const relEntity_r6 = ctx.$implicit;
|
|
219
|
+
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
220
|
+
i0.ɵɵclassProp("expanded", relEntity_r6.isExpanded);
|
|
221
|
+
i0.ɵɵadvance();
|
|
222
|
+
i0.ɵɵclassProp("clickable", relEntity_r6.count > 0);
|
|
223
|
+
i0.ɵɵadvance();
|
|
224
|
+
i0.ɵɵclassProp("fa-chevron-down", relEntity_r6.isExpanded)("fa-chevron-right", !relEntity_r6.isExpanded);
|
|
225
|
+
i0.ɵɵadvance();
|
|
226
|
+
i0.ɵɵclassMap(ctx_r0.getRelatedEntityIcon(relEntity_r6));
|
|
227
|
+
i0.ɵɵadvance(2);
|
|
228
|
+
i0.ɵɵtextInterpolate(relEntity_r6.relatedEntityName);
|
|
229
|
+
i0.ɵɵadvance(2);
|
|
230
|
+
i0.ɵɵtextInterpolate(relEntity_r6.count);
|
|
231
|
+
i0.ɵɵadvance();
|
|
232
|
+
i0.ɵɵconditional(relEntity_r6.isExpanded ? 8 : -1);
|
|
233
|
+
} }
|
|
234
|
+
function EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
235
|
+
i0.ɵɵrepeaterCreate(0, EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_For_1_Template, 9, 13, "div", 37, _forTrack1);
|
|
236
|
+
} if (rf & 2) {
|
|
237
|
+
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
238
|
+
i0.ɵɵrepeater(ctx_r0.relatedEntitiesWithRecords);
|
|
239
|
+
} }
|
|
240
|
+
function EntityRecordDetailPanelComponent_Conditional_22_Template(rf, ctx) { if (rf & 1) {
|
|
241
|
+
i0.ɵɵelementStart(0, "div", 13);
|
|
242
|
+
i0.ɵɵtemplate(1, EntityRecordDetailPanelComponent_Conditional_22_Conditional_1_Template, 2, 0, "div", 35)(2, EntityRecordDetailPanelComponent_Conditional_22_Conditional_2_Template, 2, 0, "div", 19)(3, EntityRecordDetailPanelComponent_Conditional_22_Conditional_3_Template, 2, 0);
|
|
243
|
+
i0.ɵɵelementEnd();
|
|
244
|
+
} if (rf & 2) {
|
|
245
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
246
|
+
i0.ɵɵadvance();
|
|
247
|
+
i0.ɵɵconditional(ctx_r0.isLoadingRelationships ? 1 : ctx_r0.relatedEntitiesWithRecords.length === 0 ? 2 : 3);
|
|
248
|
+
} }
|
|
249
|
+
/**
|
|
250
|
+
* EntityRecordDetailPanelComponent - A reusable panel for displaying entity record details
|
|
251
|
+
*
|
|
252
|
+
* This component provides a detail panel view for entity records with:
|
|
253
|
+
* - Primary key display with copy functionality
|
|
254
|
+
* - Foreign key fields showing friendly names with navigation
|
|
255
|
+
* - Enum fields displayed as pills
|
|
256
|
+
* - Related entities with expandable record lists
|
|
257
|
+
* - Configurable sections for details and relationships
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```html
|
|
261
|
+
* <mj-entity-record-detail-panel
|
|
262
|
+
* [entity]="selectedEntity"
|
|
263
|
+
* [record]="selectedRecord"
|
|
264
|
+
* (close)="onClosePanel()"
|
|
265
|
+
* (openRecord)="onOpenRecord($event)"
|
|
266
|
+
* (navigateToRelated)="onNavigateToRelated($event)">
|
|
267
|
+
* </mj-entity-record-detail-panel>
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
export class EntityRecordDetailPanelComponent {
|
|
271
|
+
cdr;
|
|
272
|
+
entity = null;
|
|
273
|
+
record = null;
|
|
274
|
+
close = new EventEmitter();
|
|
275
|
+
openRecord = new EventEmitter();
|
|
276
|
+
navigateToRelated = new EventEmitter();
|
|
277
|
+
openRelatedRecord = new EventEmitter();
|
|
278
|
+
openForeignKeyRecord = new EventEmitter();
|
|
279
|
+
// Related entity counts
|
|
280
|
+
relatedEntities = [];
|
|
281
|
+
isLoadingRelationships = false;
|
|
282
|
+
metadata = new Metadata();
|
|
283
|
+
// Sections expanded state
|
|
284
|
+
detailsSectionExpanded = true;
|
|
285
|
+
relationshipsSectionExpanded = true;
|
|
286
|
+
constructor(cdr) {
|
|
287
|
+
this.cdr = cdr;
|
|
288
|
+
}
|
|
289
|
+
ngOnChanges(changes) {
|
|
290
|
+
if (changes['record'] && this.record && this.entity) {
|
|
291
|
+
this.loadRelationshipCounts();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Load counts for related entities using batch RunViews call
|
|
296
|
+
*/
|
|
297
|
+
async loadRelationshipCounts() {
|
|
298
|
+
if (!this.entity || !this.record)
|
|
299
|
+
return;
|
|
300
|
+
this.isLoadingRelationships = true;
|
|
301
|
+
this.relatedEntities = [];
|
|
302
|
+
// Get relationships where this entity is the related entity (foreign keys pointing TO this record)
|
|
303
|
+
const relationships = this.entity.RelatedEntities;
|
|
304
|
+
if (relationships.length === 0) {
|
|
305
|
+
this.isLoadingRelationships = false;
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
// Build the filter using all primary key fields
|
|
309
|
+
const pkFilter = this.record.PrimaryKey.ToWhereClause();
|
|
310
|
+
if (!pkFilter) {
|
|
311
|
+
this.isLoadingRelationships = false;
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
// Get the first PK value for the join field filter
|
|
315
|
+
const pkValue = this.record.PrimaryKey.KeyValuePairs[0]?.Value;
|
|
316
|
+
if (!pkValue) {
|
|
317
|
+
this.isLoadingRelationships = false;
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
// Build batch query params for all relationships
|
|
321
|
+
const viewParams = relationships.map(rel => ({
|
|
322
|
+
EntityName: rel.RelatedEntity,
|
|
323
|
+
ExtraFilter: `${rel.RelatedEntityJoinField}='${pkValue}'`,
|
|
324
|
+
ResultType: 'count_only'
|
|
325
|
+
}));
|
|
326
|
+
try {
|
|
327
|
+
const rv = new RunView();
|
|
328
|
+
const results = await rv.RunViews(viewParams);
|
|
329
|
+
// Map results back to relationship data
|
|
330
|
+
this.relatedEntities = relationships.map((rel, index) => {
|
|
331
|
+
const result = results[index];
|
|
332
|
+
return {
|
|
333
|
+
relationship: rel,
|
|
334
|
+
relatedEntityName: rel.RelatedEntity,
|
|
335
|
+
count: result.Success ? result.TotalRowCount : 0,
|
|
336
|
+
isExpanded: false,
|
|
337
|
+
records: [],
|
|
338
|
+
isLoadingRecords: false
|
|
339
|
+
};
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
catch (error) {
|
|
343
|
+
console.warn('Failed to load relationship counts:', error);
|
|
344
|
+
// Initialize with zero counts on error
|
|
345
|
+
this.relatedEntities = relationships.map(rel => ({
|
|
346
|
+
relationship: rel,
|
|
347
|
+
relatedEntityName: rel.RelatedEntity,
|
|
348
|
+
count: 0,
|
|
349
|
+
isExpanded: false,
|
|
350
|
+
records: [],
|
|
351
|
+
isLoadingRecords: false
|
|
352
|
+
}));
|
|
353
|
+
}
|
|
354
|
+
finally {
|
|
355
|
+
this.isLoadingRelationships = false;
|
|
356
|
+
this.cdr.detectChanges();
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Get key fields to display in details section, categorized by type
|
|
361
|
+
*/
|
|
362
|
+
get displayFields() {
|
|
363
|
+
if (!this.entity || !this.record)
|
|
364
|
+
return [];
|
|
365
|
+
const fields = [];
|
|
366
|
+
const excludePatterns = ['__mj_', 'password', 'secret', 'token'];
|
|
367
|
+
for (const field of this.entity.Fields) {
|
|
368
|
+
// Skip system fields and sensitive fields
|
|
369
|
+
if (excludePatterns.some(p => field.Name.toLowerCase().includes(p)))
|
|
370
|
+
continue;
|
|
371
|
+
// Skip very long text fields (but not FK fields which are usually GUIDs)
|
|
372
|
+
if (field.Length && field.Length > 500 && !field.RelatedEntityID)
|
|
373
|
+
continue;
|
|
374
|
+
const value = this.record.Get(field.Name);
|
|
375
|
+
// Handle Primary Key fields specially
|
|
376
|
+
if (field.IsPrimaryKey) {
|
|
377
|
+
fields.push({
|
|
378
|
+
type: 'primary-key',
|
|
379
|
+
name: field.Name,
|
|
380
|
+
label: this.formatFieldLabel(field),
|
|
381
|
+
value: value !== null && value !== undefined ? String(value) : ''
|
|
382
|
+
});
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
// Handle Foreign Key fields - show the related record name instead of ID
|
|
386
|
+
if (field.RelatedEntityID && field.RelatedEntityID.length > 0) {
|
|
387
|
+
const fkDisplay = this.buildForeignKeyDisplay(field, value);
|
|
388
|
+
if (fkDisplay) {
|
|
389
|
+
fields.push(fkDisplay);
|
|
390
|
+
}
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
// Skip empty values for regular fields
|
|
394
|
+
if (value === null || value === undefined || String(value).trim() === '')
|
|
395
|
+
continue;
|
|
396
|
+
// Limit regular fields to reasonable number
|
|
397
|
+
if (fields.filter(f => f.type === 'regular' || f.type === 'enum').length >= 10)
|
|
398
|
+
continue;
|
|
399
|
+
// Check if this field has enumerated values
|
|
400
|
+
const isEnum = field.ValueListTypeEnum !== EntityFieldValueListType.None &&
|
|
401
|
+
field.EntityFieldValues.length > 0;
|
|
402
|
+
fields.push({
|
|
403
|
+
type: isEnum ? 'enum' : 'regular',
|
|
404
|
+
name: field.Name,
|
|
405
|
+
label: this.formatFieldLabel(field),
|
|
406
|
+
value: this.formatFieldValue(value, field.Name)
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
return fields;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Build display info for a foreign key field
|
|
413
|
+
* Uses RelatedEntityNameFieldMap to get the human-readable name
|
|
414
|
+
* Label comes from the virtual field's DisplayNameOrName (e.g., "Template" not "Template ID")
|
|
415
|
+
*/
|
|
416
|
+
buildForeignKeyDisplay(field, value) {
|
|
417
|
+
if (value === null || value === undefined || String(value).trim() === '') {
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
const fkValue = String(value);
|
|
421
|
+
let displayValue = fkValue;
|
|
422
|
+
let label = field.DisplayNameOrName; // Fallback to FK field's label
|
|
423
|
+
// Try to get the display name from the mapped field
|
|
424
|
+
// RelatedEntityNameFieldMap tells us which field contains the name of the related record
|
|
425
|
+
if (field.RelatedEntityNameFieldMap && field.RelatedEntityNameFieldMap.trim().length > 0) {
|
|
426
|
+
const mappedValue = this.record.Get(field.RelatedEntityNameFieldMap);
|
|
427
|
+
if (mappedValue !== null && mappedValue !== undefined && String(mappedValue).trim() !== '') {
|
|
428
|
+
displayValue = String(mappedValue);
|
|
429
|
+
}
|
|
430
|
+
// Use the mapped field's DisplayNameOrName for the label
|
|
431
|
+
const mappedField = this.entity.Fields.find(f => f.Name === field.RelatedEntityNameFieldMap);
|
|
432
|
+
if (mappedField) {
|
|
433
|
+
label = mappedField.DisplayNameOrName;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
// Fallback: try to find a virtual field with the same name minus "ID" suffix
|
|
438
|
+
// e.g., for "TemplateID", look for "Template" field
|
|
439
|
+
const baseName = field.Name.replace(/ID$/i, '');
|
|
440
|
+
if (baseName !== field.Name) {
|
|
441
|
+
const virtualField = this.entity.Fields.find(f => f.Name.toLowerCase() === baseName.toLowerCase() && f.IsVirtual);
|
|
442
|
+
if (virtualField) {
|
|
443
|
+
const virtualValue = this.record.Get(virtualField.Name);
|
|
444
|
+
if (virtualValue !== null && virtualValue !== undefined && String(virtualValue).trim() !== '') {
|
|
445
|
+
displayValue = String(virtualValue);
|
|
446
|
+
}
|
|
447
|
+
// Use the virtual field's DisplayNameOrName for the label
|
|
448
|
+
label = virtualField.DisplayNameOrName;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return {
|
|
453
|
+
type: 'foreign-key',
|
|
454
|
+
name: field.Name,
|
|
455
|
+
label: label,
|
|
456
|
+
value: fkValue,
|
|
457
|
+
displayValue: displayValue,
|
|
458
|
+
relatedEntityName: field.RelatedEntity || undefined,
|
|
459
|
+
relatedRecordId: fkValue
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Format field name to display label using EntityFieldInfo's built-in property
|
|
464
|
+
*/
|
|
465
|
+
formatFieldLabel(field) {
|
|
466
|
+
return field.DisplayNameOrName;
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Format field value for display
|
|
470
|
+
*/
|
|
471
|
+
formatFieldValue(value, fieldName) {
|
|
472
|
+
if (value === null || value === undefined)
|
|
473
|
+
return '-';
|
|
474
|
+
// Handle dates
|
|
475
|
+
if (value instanceof Date) {
|
|
476
|
+
return value.toLocaleDateString();
|
|
477
|
+
}
|
|
478
|
+
// Handle booleans
|
|
479
|
+
if (typeof value === 'boolean') {
|
|
480
|
+
return value ? 'Yes' : 'No';
|
|
481
|
+
}
|
|
482
|
+
// Handle numbers that look like currency
|
|
483
|
+
if (typeof value === 'number') {
|
|
484
|
+
const nameLower = fieldName.toLowerCase();
|
|
485
|
+
if (nameLower.includes('amount') ||
|
|
486
|
+
nameLower.includes('price') ||
|
|
487
|
+
nameLower.includes('cost') ||
|
|
488
|
+
nameLower.includes('total') ||
|
|
489
|
+
nameLower.includes('value')) {
|
|
490
|
+
return `$${value.toLocaleString()}`;
|
|
491
|
+
}
|
|
492
|
+
return value.toLocaleString();
|
|
493
|
+
}
|
|
494
|
+
const strValue = String(value);
|
|
495
|
+
// Truncate long strings
|
|
496
|
+
if (strValue.length > 100) {
|
|
497
|
+
return strValue.substring(0, 100) + '...';
|
|
498
|
+
}
|
|
499
|
+
return strValue;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Get record title
|
|
503
|
+
*/
|
|
504
|
+
get recordTitle() {
|
|
505
|
+
if (!this.entity || !this.record)
|
|
506
|
+
return 'Record';
|
|
507
|
+
if (this.entity.NameField) {
|
|
508
|
+
const name = this.record.Get(this.entity.NameField.Name);
|
|
509
|
+
if (name)
|
|
510
|
+
return String(name);
|
|
511
|
+
}
|
|
512
|
+
return this.record.PrimaryKey.ToString();
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Handle close button click
|
|
516
|
+
*/
|
|
517
|
+
onClose() {
|
|
518
|
+
this.close.emit();
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Handle open record button click
|
|
522
|
+
*/
|
|
523
|
+
onOpenRecord() {
|
|
524
|
+
if (this.record) {
|
|
525
|
+
this.openRecord.emit(this.record);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Copy primary key value to clipboard
|
|
530
|
+
*/
|
|
531
|
+
copyToClipboard(value, event) {
|
|
532
|
+
event.stopPropagation();
|
|
533
|
+
navigator.clipboard.writeText(value).then(() => {
|
|
534
|
+
// Could add a toast notification here
|
|
535
|
+
console.log('Copied to clipboard:', value);
|
|
536
|
+
}).catch(err => {
|
|
537
|
+
console.error('Failed to copy:', err);
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Open a foreign key record (FK link click)
|
|
542
|
+
* Emits openForeignKeyRecord event for parent to handle opening the record
|
|
543
|
+
*/
|
|
544
|
+
onForeignKeyClick(field, event) {
|
|
545
|
+
event.stopPropagation();
|
|
546
|
+
if (field.relatedEntityName && field.relatedRecordId) {
|
|
547
|
+
this.openForeignKeyRecord.emit({
|
|
548
|
+
entityName: field.relatedEntityName,
|
|
549
|
+
recordId: field.relatedRecordId
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Check if a FK display value is different from the raw ID (i.e., we have a name to show)
|
|
555
|
+
*/
|
|
556
|
+
hasFriendlyName(field) {
|
|
557
|
+
return field.type === 'foreign-key' &&
|
|
558
|
+
field.displayValue !== undefined &&
|
|
559
|
+
field.displayValue !== field.value;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Toggle expansion of related entity section and load records if needed
|
|
563
|
+
*/
|
|
564
|
+
async toggleRelatedEntityExpansion(relEntity, event) {
|
|
565
|
+
event.stopPropagation();
|
|
566
|
+
if (relEntity.count === 0)
|
|
567
|
+
return;
|
|
568
|
+
relEntity.isExpanded = !relEntity.isExpanded;
|
|
569
|
+
// Load records on first expansion
|
|
570
|
+
if (relEntity.isExpanded && relEntity.records.length === 0 && !relEntity.isLoadingRecords) {
|
|
571
|
+
await this.loadRelatedRecords(relEntity);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Load actual records for a related entity
|
|
576
|
+
*/
|
|
577
|
+
async loadRelatedRecords(relEntity) {
|
|
578
|
+
if (!this.record)
|
|
579
|
+
return;
|
|
580
|
+
const pkValue = this.record.PrimaryKey.KeyValuePairs[0]?.Value;
|
|
581
|
+
if (!pkValue)
|
|
582
|
+
return;
|
|
583
|
+
relEntity.isLoadingRecords = true;
|
|
584
|
+
try {
|
|
585
|
+
const rv = new RunView();
|
|
586
|
+
const result = await rv.RunView({
|
|
587
|
+
EntityName: relEntity.relationship.RelatedEntity,
|
|
588
|
+
ExtraFilter: `${relEntity.relationship.RelatedEntityJoinField}='${pkValue}'`,
|
|
589
|
+
ResultType: 'entity_object',
|
|
590
|
+
MaxRows: 10 // Limit inline display to 10 records
|
|
591
|
+
});
|
|
592
|
+
if (result.Success) {
|
|
593
|
+
relEntity.records = result.Results;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
catch (error) {
|
|
597
|
+
console.warn(`Failed to load records for ${relEntity.relatedEntityName}:`, error);
|
|
598
|
+
}
|
|
599
|
+
finally {
|
|
600
|
+
relEntity.isLoadingRecords = false;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Handle click on individual related record - opens in new tab
|
|
605
|
+
*/
|
|
606
|
+
onRelatedRecordClick(relEntity, record, event) {
|
|
607
|
+
event.stopPropagation();
|
|
608
|
+
this.openRelatedRecord.emit({
|
|
609
|
+
entityName: relEntity.relatedEntityName,
|
|
610
|
+
record
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Navigate to view all related records (when count > 10)
|
|
615
|
+
*/
|
|
616
|
+
onViewAllRelated(relEntity, event) {
|
|
617
|
+
event.stopPropagation();
|
|
618
|
+
if (!this.record)
|
|
619
|
+
return;
|
|
620
|
+
const pkValue = this.record.PrimaryKey.KeyValuePairs[0]?.Value;
|
|
621
|
+
if (!pkValue)
|
|
622
|
+
return;
|
|
623
|
+
this.navigateToRelated.emit({
|
|
624
|
+
entityName: relEntity.relatedEntityName,
|
|
625
|
+
filter: `${relEntity.relationship.RelatedEntityJoinField}='${pkValue}'`
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Get display name for a related record
|
|
630
|
+
*/
|
|
631
|
+
getRelatedRecordDisplayName(relEntity, record) {
|
|
632
|
+
const entityInfo = this.metadata.Entities.find(e => e.Name === relEntity.relatedEntityName);
|
|
633
|
+
if (entityInfo?.NameField) {
|
|
634
|
+
const name = record.Get(entityInfo.NameField.Name);
|
|
635
|
+
if (name)
|
|
636
|
+
return String(name);
|
|
637
|
+
}
|
|
638
|
+
return record.PrimaryKey.ToString();
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Get subtitle/secondary info for a related record
|
|
642
|
+
*/
|
|
643
|
+
getRelatedRecordSubtitle(relEntity, record) {
|
|
644
|
+
const entityInfo = this.metadata.Entities.find(e => e.Name === relEntity.relatedEntityName);
|
|
645
|
+
if (!entityInfo)
|
|
646
|
+
return '';
|
|
647
|
+
// Look for common subtitle fields
|
|
648
|
+
const subtitleFieldNames = ['Description', 'Status', 'Type', 'Email', 'Date', 'Amount', 'Total'];
|
|
649
|
+
for (const fieldName of subtitleFieldNames) {
|
|
650
|
+
const field = entityInfo.Fields.find(f => f.Name.includes(fieldName) && f.Name !== entityInfo.NameField?.Name);
|
|
651
|
+
if (field) {
|
|
652
|
+
const value = record.Get(field.Name);
|
|
653
|
+
if (value !== null && value !== undefined) {
|
|
654
|
+
return this.formatFieldValue(value, field.Name);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return '';
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Toggle section expansion
|
|
662
|
+
*/
|
|
663
|
+
toggleSection(section) {
|
|
664
|
+
if (section === 'details') {
|
|
665
|
+
this.detailsSectionExpanded = !this.detailsSectionExpanded;
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
this.relationshipsSectionExpanded = !this.relationshipsSectionExpanded;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Get only related entities that have records (count > 0)
|
|
673
|
+
*/
|
|
674
|
+
get relatedEntitiesWithRecords() {
|
|
675
|
+
return this.relatedEntities.filter(r => r.count > 0);
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Get icon for related entity by looking up EntityInfo from Metadata
|
|
679
|
+
*/
|
|
680
|
+
getRelatedEntityIcon(relEntity) {
|
|
681
|
+
const entityInfo = this.metadata.Entities.find(e => e.Name === relEntity.relatedEntityName);
|
|
682
|
+
if (entityInfo?.Icon) {
|
|
683
|
+
return this.formatEntityIcon(entityInfo.Icon);
|
|
684
|
+
}
|
|
685
|
+
return 'fa-solid fa-table';
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Get the icon class for the current entity
|
|
689
|
+
*/
|
|
690
|
+
getEntityIconClass() {
|
|
691
|
+
if (!this.entity?.Icon) {
|
|
692
|
+
return 'fa-solid fa-table';
|
|
693
|
+
}
|
|
694
|
+
return this.formatEntityIcon(this.entity.Icon);
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Format entity icon to ensure proper Font Awesome class format
|
|
698
|
+
*/
|
|
699
|
+
formatEntityIcon(icon) {
|
|
700
|
+
if (!icon) {
|
|
701
|
+
return 'fa-solid fa-table';
|
|
702
|
+
}
|
|
703
|
+
// If icon already has fa- prefix, use it as-is
|
|
704
|
+
if (icon.startsWith('fa-') || icon.startsWith('fa ')) {
|
|
705
|
+
// Ensure it has a style prefix (fa-solid, fa-regular, etc.)
|
|
706
|
+
if (icon.startsWith('fa-solid') || icon.startsWith('fa-regular') ||
|
|
707
|
+
icon.startsWith('fa-light') || icon.startsWith('fa-brands') ||
|
|
708
|
+
icon.startsWith('fa ')) {
|
|
709
|
+
return icon;
|
|
710
|
+
}
|
|
711
|
+
// It's just "fa-something", add fa-solid prefix
|
|
712
|
+
return `fa-solid ${icon}`;
|
|
713
|
+
}
|
|
714
|
+
// Check if it's just an icon name like "table" or "users"
|
|
715
|
+
return `fa-solid fa-${icon}`;
|
|
716
|
+
}
|
|
717
|
+
static ɵfac = function EntityRecordDetailPanelComponent_Factory(t) { return new (t || EntityRecordDetailPanelComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
|
|
718
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: EntityRecordDetailPanelComponent, selectors: [["mj-entity-record-detail-panel"]], inputs: { entity: "entity", record: "record" }, outputs: { close: "close", openRecord: "openRecord", navigateToRelated: "navigateToRelated", openRelatedRecord: "openRelatedRecord", openForeignKeyRecord: "openForeignKeyRecord" }, features: [i0.ɵɵNgOnChangesFeature], decls: 27, vars: 12, consts: [[1, "entity-record-card-container"], [1, "panel-header"], [1, "panel-title-row"], [1, "entity-icon", 3, "class"], [1, "panel-title"], ["title", "Close", 1, "close-btn", 3, "click"], [1, "fa-solid", "fa-times"], [1, "panel-content"], [1, "section"], [1, "section-header", 3, "click"], [1, "fa-solid", "fa-info-circle", "section-icon"], [1, "section-title"], [1, "fa-solid", "expand-icon"], [1, "section-content"], [1, "fa-solid", "fa-project-diagram", "section-icon"], [1, "panel-footer"], [1, "open-record-btn", 3, "click"], [1, "fa-solid", "fa-external-link-alt"], [1, "entity-icon"], [1, "empty-section"], [1, "field-row", "field-row-pk"], [1, "field-row", "field-row-fk"], [1, "field-row"], [1, "pk-row"], [1, "fa-solid", "fa-key", "pk-icon"], [1, "field-label"], ["title", "Copy ID", 1, "copy-btn", 3, "click"], [1, "fa-solid", "fa-copy"], [1, "fk-value-row"], [1, "field-value", "fk-display-value"], [1, "field-value", "fk-id-value"], [1, "fk-link-btn", 3, "title"], [1, "fk-link-btn", 3, "click", "title"], [3, "value"], [1, "field-value"], [1, "relationships-loading"], ["text", "Loading related records...", "size", "small"], [1, "related-entity-group", 3, "expanded"], [1, "related-entity-group"], [1, "related-entity-header", 3, "click"], [1, "fa-solid", "expand-chevron"], [1, "related-entity-name"], [1, "related-entity-count"], [1, "related-records-list"], [1, "records-loading"], ["text", "Loading...", "size", "small"], [1, "related-record-item"], [1, "view-all-link"], [1, "related-record-item", 3, "click"], [1, "record-info"], [1, "record-name"], [1, "record-subtitle"], [1, "fa-solid", "fa-external-link-alt", "record-open-icon"], [1, "view-all-link", 3, "click"], [1, "fa-solid", "fa-arrow-right"]], template: function EntityRecordDetailPanelComponent_Template(rf, ctx) { if (rf & 1) {
|
|
719
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2);
|
|
720
|
+
i0.ɵɵtemplate(3, EntityRecordDetailPanelComponent_Conditional_3_Template, 1, 2, "i", 3);
|
|
721
|
+
i0.ɵɵelementStart(4, "h3", 4);
|
|
722
|
+
i0.ɵɵtext(5);
|
|
723
|
+
i0.ɵɵelementEnd()();
|
|
724
|
+
i0.ɵɵelementStart(6, "button", 5);
|
|
725
|
+
i0.ɵɵlistener("click", function EntityRecordDetailPanelComponent_Template_button_click_6_listener() { return ctx.onClose(); });
|
|
726
|
+
i0.ɵɵelement(7, "i", 6);
|
|
727
|
+
i0.ɵɵelementEnd()();
|
|
728
|
+
i0.ɵɵelementStart(8, "div", 7)(9, "div", 8)(10, "div", 9);
|
|
729
|
+
i0.ɵɵlistener("click", function EntityRecordDetailPanelComponent_Template_div_click_10_listener() { return ctx.toggleSection("details"); });
|
|
730
|
+
i0.ɵɵelement(11, "i", 10);
|
|
731
|
+
i0.ɵɵelementStart(12, "span", 11);
|
|
732
|
+
i0.ɵɵtext(13, "Details");
|
|
733
|
+
i0.ɵɵelementEnd();
|
|
734
|
+
i0.ɵɵelement(14, "i", 12);
|
|
735
|
+
i0.ɵɵelementEnd();
|
|
736
|
+
i0.ɵɵtemplate(15, EntityRecordDetailPanelComponent_Conditional_15_Template, 4, 1, "div", 13);
|
|
737
|
+
i0.ɵɵelementEnd();
|
|
738
|
+
i0.ɵɵelementStart(16, "div", 8)(17, "div", 9);
|
|
739
|
+
i0.ɵɵlistener("click", function EntityRecordDetailPanelComponent_Template_div_click_17_listener() { return ctx.toggleSection("relationships"); });
|
|
740
|
+
i0.ɵɵelement(18, "i", 14);
|
|
741
|
+
i0.ɵɵelementStart(19, "span", 11);
|
|
742
|
+
i0.ɵɵtext(20, "Related Records");
|
|
743
|
+
i0.ɵɵelementEnd();
|
|
744
|
+
i0.ɵɵelement(21, "i", 12);
|
|
745
|
+
i0.ɵɵelementEnd();
|
|
746
|
+
i0.ɵɵtemplate(22, EntityRecordDetailPanelComponent_Conditional_22_Template, 4, 1, "div", 13);
|
|
747
|
+
i0.ɵɵelementEnd()();
|
|
748
|
+
i0.ɵɵelementStart(23, "div", 15)(24, "button", 16);
|
|
749
|
+
i0.ɵɵlistener("click", function EntityRecordDetailPanelComponent_Template_button_click_24_listener() { return ctx.onOpenRecord(); });
|
|
750
|
+
i0.ɵɵelement(25, "i", 17);
|
|
751
|
+
i0.ɵɵtext(26, " Open Full Record ");
|
|
752
|
+
i0.ɵɵelementEnd()()();
|
|
753
|
+
} if (rf & 2) {
|
|
754
|
+
i0.ɵɵadvance(3);
|
|
755
|
+
i0.ɵɵconditional((ctx.entity == null ? null : ctx.entity.Icon) ? 3 : -1);
|
|
756
|
+
i0.ɵɵadvance(2);
|
|
757
|
+
i0.ɵɵtextInterpolate(ctx.recordTitle);
|
|
758
|
+
i0.ɵɵadvance(9);
|
|
759
|
+
i0.ɵɵclassProp("fa-chevron-down", ctx.detailsSectionExpanded)("fa-chevron-right", !ctx.detailsSectionExpanded);
|
|
760
|
+
i0.ɵɵadvance();
|
|
761
|
+
i0.ɵɵconditional(ctx.detailsSectionExpanded ? 15 : -1);
|
|
762
|
+
i0.ɵɵadvance(6);
|
|
763
|
+
i0.ɵɵclassProp("fa-chevron-down", ctx.relationshipsSectionExpanded)("fa-chevron-right", !ctx.relationshipsSectionExpanded);
|
|
764
|
+
i0.ɵɵadvance();
|
|
765
|
+
i0.ɵɵconditional(ctx.relationshipsSectionExpanded ? 22 : -1);
|
|
766
|
+
} }, dependencies: [i1.LoadingComponent, i2.PillComponent], styles: [".entity-record-card-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: white;\n}\n\n.panel-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid #e0e0e0;\n flex-shrink: 0;\n}\n\n.panel-title-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n min-width: 0;\n flex: 1;\n}\n\n.entity-icon[_ngcontent-%COMP%] {\n font-size: 18px;\n color: #757575;\n flex-shrink: 0;\n}\n\n.panel-title[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: #212121;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.close-btn[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #757575;\n transition: all 0.15s ease;\n flex-shrink: 0;\n}\n.close-btn[_ngcontent-%COMP%]:hover {\n background: #f5f5f5;\n color: #424242;\n}\n\n.panel-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 8px 0;\n}\n\n.section[_ngcontent-%COMP%] {\n margin-bottom: 8px;\n}\n\n.section-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 12px 20px;\n cursor: pointer;\n user-select: none;\n transition: background 0.15s ease;\n}\n.section-header[_ngcontent-%COMP%]:hover {\n background: #f8f9fa;\n}\n\n.section-icon[_ngcontent-%COMP%] {\n width: 20px;\n font-size: 14px;\n color: #757575;\n margin-right: 10px;\n}\n\n.section-title[_ngcontent-%COMP%] {\n flex: 1;\n font-size: 13px;\n font-weight: 600;\n color: #424242;\n}\n\n.loading-icon[_ngcontent-%COMP%] {\n margin-right: 8px;\n font-size: 12px;\n color: #9e9e9e;\n}\n\n.expand-icon[_ngcontent-%COMP%] {\n font-size: 10px;\n color: #9e9e9e;\n transition: transform 0.15s ease;\n}\n\n.section-content[_ngcontent-%COMP%] {\n padding: 0 20px 12px 20px;\n}\n\n.empty-section[_ngcontent-%COMP%] {\n padding: 12px 0;\n text-align: center;\n color: #9e9e9e;\n font-size: 13px;\n font-style: italic;\n}\n\n.relationships-loading[_ngcontent-%COMP%] {\n padding: 20px 0;\n display: flex;\n justify-content: center;\n}\n\n.field-row[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n padding: 8px 0;\n border-bottom: 1px solid #f5f5f5;\n}\n.field-row[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n.field-label[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 500;\n color: #9e9e9e;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 2px;\n}\n\n.field-value[_ngcontent-%COMP%] {\n font-size: 14px;\n color: #424242;\n word-break: break-word;\n}\n\n\n\n.field-row-pk[_ngcontent-%COMP%] {\n flex-direction: row;\n padding: 6px 0;\n}\n\n.pk-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n}\n\n.pk-icon[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #9e9e9e;\n}\n\n.pk-row[_ngcontent-%COMP%] .field-label[_ngcontent-%COMP%] {\n margin-bottom: 0;\n flex: 1;\n}\n\n.copy-btn[_ngcontent-%COMP%] {\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #9e9e9e;\n transition: all 0.15s ease;\n flex-shrink: 0;\n}\n.copy-btn[_ngcontent-%COMP%]:hover {\n background: #f0f0f0;\n color: #1976d2;\n}\n.copy-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n\n\n\n.field-row-fk[_ngcontent-%COMP%] {\n padding: 8px 0;\n}\n\n.fk-value-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.fk-display-value[_ngcontent-%COMP%] {\n flex: 1;\n color: #333;\n font-weight: 500;\n}\n\n.fk-link-btn[_ngcontent-%COMP%] {\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #9e9e9e;\n transition: all 0.15s ease;\n flex-shrink: 0;\n}\n.fk-link-btn[_ngcontent-%COMP%]:hover {\n background: #e3f2fd;\n color: #1976d2;\n}\n.fk-link-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 11px;\n}\n\n\n\n.fk-id-only[_ngcontent-%COMP%] {\n background: #f8f9fa;\n border-radius: 4px;\n padding: 4px 8px;\n}\n\n.fk-id-value[_ngcontent-%COMP%] {\n font-size: 12px;\n font-family: monospace;\n color: #757575;\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n\n\n.related-entity-group[_ngcontent-%COMP%] {\n margin-bottom: 4px;\n border-radius: 6px;\n overflow: hidden;\n}\n.related-entity-group.expanded[_ngcontent-%COMP%] {\n background: #f8f9fa;\n margin-bottom: 8px;\n}\n\n\n\n.related-entity-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 10px 12px;\n border-radius: 6px;\n gap: 8px;\n transition: background 0.15s ease;\n}\n.related-entity-header.clickable[_ngcontent-%COMP%] {\n cursor: pointer;\n}\n.related-entity-header.clickable[_ngcontent-%COMP%]:hover {\n background: #f0f0f0;\n}\n.related-entity-header.loading[_ngcontent-%COMP%] {\n opacity: 0.7;\n}\n\n.expand-chevron[_ngcontent-%COMP%] {\n width: 12px;\n font-size: 10px;\n color: #9e9e9e;\n transition: transform 0.15s ease;\n}\n\n.related-entity-header[_ngcontent-%COMP%] i[_ngcontent-%COMP%]:nth-child(2) {\n width: 16px;\n font-size: 13px;\n color: #757575;\n}\n\n.related-entity-name[_ngcontent-%COMP%] {\n flex: 1;\n font-size: 13px;\n color: #424242;\n}\n\n.related-entity-count[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: #757575;\n min-width: 24px;\n text-align: right;\n}\n\n\n\n.related-records-list[_ngcontent-%COMP%] {\n padding: 4px 8px 8px 32px;\n}\n\n.records-loading[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 0;\n color: #9e9e9e;\n font-size: 12px;\n}\n\n.related-record-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 8px 12px;\n margin-bottom: 2px;\n background: white;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid #e8e8e8;\n}\n.related-record-item[_ngcontent-%COMP%]:hover {\n border-color: #1976d2;\n box-shadow: 0 1px 4px rgba(25, 118, 210, 0.15);\n}\n.related-record-item[_ngcontent-%COMP%]:hover .record-open-icon[_ngcontent-%COMP%] {\n color: #1976d2;\n}\n\n.record-info[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.record-name[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: #333;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.record-subtitle[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #757575;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.record-open-icon[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #bdbdbd;\n margin-left: 8px;\n flex-shrink: 0;\n transition: color 0.15s ease;\n}\n\n.view-all-link[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 10px;\n margin-top: 4px;\n font-size: 12px;\n font-weight: 500;\n color: #1976d2;\n cursor: pointer;\n border-radius: 6px;\n transition: background 0.15s ease;\n}\n.view-all-link[_ngcontent-%COMP%]:hover {\n background: #e3f2fd;\n}\n\n.panel-footer[_ngcontent-%COMP%] {\n padding: 16px 20px;\n border-top: 1px solid #e0e0e0;\n flex-shrink: 0;\n}\n\n.open-record-btn[_ngcontent-%COMP%] {\n width: 100%;\n padding: 12px 16px;\n border: none;\n background: #1976d2;\n color: white;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n transition: all 0.15s ease;\n}\n.open-record-btn[_ngcontent-%COMP%]:hover {\n background: #1565c0;\n}\n.open-record-btn[_ngcontent-%COMP%]:active {\n transform: scale(0.98);\n}"] });
|
|
767
|
+
}
|
|
768
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(EntityRecordDetailPanelComponent, [{
|
|
769
|
+
type: Component,
|
|
770
|
+
args: [{ selector: 'mj-entity-record-detail-panel', template: "<div class=\"entity-record-card-container\">\n <!-- Header -->\n <div class=\"panel-header\">\n <div class=\"panel-title-row\">\n @if (entity?.Icon) {\n <i [class]=\"getEntityIconClass()\" class=\"entity-icon\"></i>\n }\n <h3 class=\"panel-title\">{{ recordTitle }}</h3>\n </div>\n <button class=\"close-btn\" (click)=\"onClose()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"panel-content\">\n <!-- Details Section -->\n <div class=\"section\">\n <div class=\"section-header\" (click)=\"toggleSection('details')\">\n <i class=\"fa-solid fa-info-circle section-icon\"></i>\n <span class=\"section-title\">Details</span>\n <i class=\"fa-solid expand-icon\" [class.fa-chevron-down]=\"detailsSectionExpanded\" [class.fa-chevron-right]=\"!detailsSectionExpanded\"></i>\n </div>\n\n @if (detailsSectionExpanded) {\n <div class=\"section-content\">\n @for (field of displayFields; track field.name) {\n <!-- Primary Key Field - Compact with copy button -->\n @if (field.type === 'primary-key') {\n <div class=\"field-row field-row-pk\">\n <div class=\"pk-row\">\n <i class=\"fa-solid fa-key pk-icon\"></i>\n <span class=\"field-label\">{{ field.label }}</span>\n <button class=\"copy-btn\" (click)=\"copyToClipboard(field.value, $event)\" title=\"Copy ID\">\n <i class=\"fa-solid fa-copy\"></i>\n </button>\n </div>\n </div>\n }\n <!-- Foreign Key Field - Show friendly name with open button -->\n @else if (field.type === 'foreign-key') {\n <div class=\"field-row field-row-fk\">\n <span class=\"field-label\">{{ field.label }}</span>\n <div class=\"fk-value-row\" [class.fk-id-only]=\"!hasFriendlyName(field)\">\n @if (hasFriendlyName(field)) {\n <span class=\"field-value fk-display-value\">{{ field.displayValue }}</span>\n } @else {\n <!-- No friendly name available, show ID in muted style -->\n <span class=\"field-value fk-id-value\">{{ field.value }}</span>\n }\n <!-- Always show open button for FK fields that have a related entity -->\n @if (field.relatedEntityName) {\n <button class=\"fk-link-btn\" (click)=\"onForeignKeyClick(field, $event)\" title=\"Open {{ field.relatedEntityName }} record\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n </button>\n }\n </div>\n </div>\n }\n <!-- Enum Field - Show as pill -->\n @else if (field.type === 'enum') {\n <div class=\"field-row\">\n <span class=\"field-label\">{{ field.label }}</span>\n <mj-pill [value]=\"field.value\"></mj-pill>\n </div>\n }\n <!-- Regular Field -->\n @else {\n <div class=\"field-row\">\n <span class=\"field-label\">{{ field.label }}</span>\n <span class=\"field-value\">{{ field.value }}</span>\n </div>\n }\n }\n\n @if (displayFields.length === 0) {\n <div class=\"empty-section\">No details available</div>\n }\n </div>\n }\n </div>\n\n <!-- Relationships Section -->\n <div class=\"section\">\n <div class=\"section-header\" (click)=\"toggleSection('relationships')\">\n <i class=\"fa-solid fa-project-diagram section-icon\"></i>\n <span class=\"section-title\">Related Records</span>\n <i class=\"fa-solid expand-icon\" [class.fa-chevron-down]=\"relationshipsSectionExpanded\" [class.fa-chevron-right]=\"!relationshipsSectionExpanded\"></i>\n </div>\n\n @if (relationshipsSectionExpanded) {\n <div class=\"section-content\">\n @if (isLoadingRelationships) {\n <div class=\"relationships-loading\">\n <mj-loading text=\"Loading related records...\" size=\"small\"></mj-loading>\n </div>\n } @else if (relatedEntitiesWithRecords.length === 0) {\n <div class=\"empty-section\">No related records</div>\n } @else {\n @for (relEntity of relatedEntitiesWithRecords; track relEntity.relatedEntityName) {\n <div class=\"related-entity-group\" [class.expanded]=\"relEntity.isExpanded\">\n <!-- Header row - click to expand -->\n <div\n class=\"related-entity-header\"\n [class.clickable]=\"relEntity.count > 0\"\n (click)=\"toggleRelatedEntityExpansion(relEntity, $event)\">\n <i class=\"fa-solid expand-chevron\"\n [class.fa-chevron-down]=\"relEntity.isExpanded\"\n [class.fa-chevron-right]=\"!relEntity.isExpanded\"></i>\n <i [class]=\"getRelatedEntityIcon(relEntity)\"></i>\n <span class=\"related-entity-name\">{{ relEntity.relatedEntityName }}</span>\n <span class=\"related-entity-count\">{{ relEntity.count }}</span>\n </div>\n\n <!-- Expanded records list -->\n @if (relEntity.isExpanded) {\n <div class=\"related-records-list\">\n @if (relEntity.isLoadingRecords) {\n <div class=\"records-loading\">\n <mj-loading text=\"Loading...\" size=\"small\"></mj-loading>\n </div>\n } @else {\n @for (rec of relEntity.records; track rec.PrimaryKey.ToString()) {\n <div class=\"related-record-item\" (click)=\"onRelatedRecordClick(relEntity, rec, $event)\">\n <div class=\"record-info\">\n <span class=\"record-name\">{{ getRelatedRecordDisplayName(relEntity, rec) }}</span>\n @if (getRelatedRecordSubtitle(relEntity, rec); as subtitle) {\n <span class=\"record-subtitle\">{{ subtitle }}</span>\n }\n </div>\n <i class=\"fa-solid fa-external-link-alt record-open-icon\"></i>\n </div>\n }\n\n <!-- View All link if there are more records -->\n @if (relEntity.count > 10) {\n <div class=\"view-all-link\" (click)=\"onViewAllRelated(relEntity, $event)\">\n <span>View all {{ relEntity.count }} records</span>\n <i class=\"fa-solid fa-arrow-right\"></i>\n </div>\n }\n }\n </div>\n }\n </div>\n }\n }\n </div>\n }\n </div>\n </div>\n\n <!-- Footer -->\n <div class=\"panel-footer\">\n <button class=\"open-record-btn\" (click)=\"onOpenRecord()\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n Open Full Record\n </button>\n </div>\n</div>\n", styles: [".entity-record-card-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: white;\n}\n\n.panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid #e0e0e0;\n flex-shrink: 0;\n}\n\n.panel-title-row {\n display: flex;\n align-items: center;\n gap: 10px;\n min-width: 0;\n flex: 1;\n}\n\n.entity-icon {\n font-size: 18px;\n color: #757575;\n flex-shrink: 0;\n}\n\n.panel-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: #212121;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.close-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #757575;\n transition: all 0.15s ease;\n flex-shrink: 0;\n}\n.close-btn:hover {\n background: #f5f5f5;\n color: #424242;\n}\n\n.panel-content {\n flex: 1;\n overflow-y: auto;\n padding: 8px 0;\n}\n\n.section {\n margin-bottom: 8px;\n}\n\n.section-header {\n display: flex;\n align-items: center;\n padding: 12px 20px;\n cursor: pointer;\n user-select: none;\n transition: background 0.15s ease;\n}\n.section-header:hover {\n background: #f8f9fa;\n}\n\n.section-icon {\n width: 20px;\n font-size: 14px;\n color: #757575;\n margin-right: 10px;\n}\n\n.section-title {\n flex: 1;\n font-size: 13px;\n font-weight: 600;\n color: #424242;\n}\n\n.loading-icon {\n margin-right: 8px;\n font-size: 12px;\n color: #9e9e9e;\n}\n\n.expand-icon {\n font-size: 10px;\n color: #9e9e9e;\n transition: transform 0.15s ease;\n}\n\n.section-content {\n padding: 0 20px 12px 20px;\n}\n\n.empty-section {\n padding: 12px 0;\n text-align: center;\n color: #9e9e9e;\n font-size: 13px;\n font-style: italic;\n}\n\n.relationships-loading {\n padding: 20px 0;\n display: flex;\n justify-content: center;\n}\n\n.field-row {\n display: flex;\n flex-direction: column;\n padding: 8px 0;\n border-bottom: 1px solid #f5f5f5;\n}\n.field-row:last-child {\n border-bottom: none;\n}\n\n.field-label {\n font-size: 11px;\n font-weight: 500;\n color: #9e9e9e;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 2px;\n}\n\n.field-value {\n font-size: 14px;\n color: #424242;\n word-break: break-word;\n}\n\n/* Primary Key Field - Compact row with key icon and copy button */\n.field-row-pk {\n flex-direction: row;\n padding: 6px 0;\n}\n\n.pk-row {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n}\n\n.pk-icon {\n font-size: 12px;\n color: #9e9e9e;\n}\n\n.pk-row .field-label {\n margin-bottom: 0;\n flex: 1;\n}\n\n.copy-btn {\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #9e9e9e;\n transition: all 0.15s ease;\n flex-shrink: 0;\n}\n.copy-btn:hover {\n background: #f0f0f0;\n color: #1976d2;\n}\n.copy-btn i {\n font-size: 12px;\n}\n\n/* Foreign Key Field - Display name with link button */\n.field-row-fk {\n padding: 8px 0;\n}\n\n.fk-value-row {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.fk-display-value {\n flex: 1;\n color: #333;\n font-weight: 500;\n}\n\n.fk-link-btn {\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #9e9e9e;\n transition: all 0.15s ease;\n flex-shrink: 0;\n}\n.fk-link-btn:hover {\n background: #e3f2fd;\n color: #1976d2;\n}\n.fk-link-btn i {\n font-size: 11px;\n}\n\n/* FK without friendly name - show ID in muted style */\n.fk-id-only {\n background: #f8f9fa;\n border-radius: 4px;\n padding: 4px 8px;\n}\n\n.fk-id-value {\n font-size: 12px;\n font-family: monospace;\n color: #757575;\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Related Entity Group - expandable container */\n.related-entity-group {\n margin-bottom: 4px;\n border-radius: 6px;\n overflow: hidden;\n}\n.related-entity-group.expanded {\n background: #f8f9fa;\n margin-bottom: 8px;\n}\n\n/* Header row */\n.related-entity-header {\n display: flex;\n align-items: center;\n padding: 10px 12px;\n border-radius: 6px;\n gap: 8px;\n transition: background 0.15s ease;\n}\n.related-entity-header.clickable {\n cursor: pointer;\n}\n.related-entity-header.clickable:hover {\n background: #f0f0f0;\n}\n.related-entity-header.loading {\n opacity: 0.7;\n}\n\n.expand-chevron {\n width: 12px;\n font-size: 10px;\n color: #9e9e9e;\n transition: transform 0.15s ease;\n}\n\n.related-entity-header i:nth-child(2) {\n width: 16px;\n font-size: 13px;\n color: #757575;\n}\n\n.related-entity-name {\n flex: 1;\n font-size: 13px;\n color: #424242;\n}\n\n.related-entity-count {\n font-size: 13px;\n font-weight: 500;\n color: #757575;\n min-width: 24px;\n text-align: right;\n}\n\n/* Expanded records list */\n.related-records-list {\n padding: 4px 8px 8px 32px;\n}\n\n.records-loading {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 0;\n color: #9e9e9e;\n font-size: 12px;\n}\n\n.related-record-item {\n display: flex;\n align-items: center;\n padding: 8px 12px;\n margin-bottom: 2px;\n background: white;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid #e8e8e8;\n}\n.related-record-item:hover {\n border-color: #1976d2;\n box-shadow: 0 1px 4px rgba(25, 118, 210, 0.15);\n}\n.related-record-item:hover .record-open-icon {\n color: #1976d2;\n}\n\n.record-info {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.record-name {\n font-size: 13px;\n font-weight: 500;\n color: #333;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.record-subtitle {\n font-size: 11px;\n color: #757575;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.record-open-icon {\n font-size: 11px;\n color: #bdbdbd;\n margin-left: 8px;\n flex-shrink: 0;\n transition: color 0.15s ease;\n}\n\n.view-all-link {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 10px;\n margin-top: 4px;\n font-size: 12px;\n font-weight: 500;\n color: #1976d2;\n cursor: pointer;\n border-radius: 6px;\n transition: background 0.15s ease;\n}\n.view-all-link:hover {\n background: #e3f2fd;\n}\n\n.panel-footer {\n padding: 16px 20px;\n border-top: 1px solid #e0e0e0;\n flex-shrink: 0;\n}\n\n.open-record-btn {\n width: 100%;\n padding: 12px 16px;\n border: none;\n background: #1976d2;\n color: white;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n transition: all 0.15s ease;\n}\n.open-record-btn:hover {\n background: #1565c0;\n}\n.open-record-btn:active {\n transform: scale(0.98);\n}\n"] }]
|
|
771
|
+
}], () => [{ type: i0.ChangeDetectorRef }], { entity: [{
|
|
772
|
+
type: Input
|
|
773
|
+
}], record: [{
|
|
774
|
+
type: Input
|
|
775
|
+
}], close: [{
|
|
776
|
+
type: Output
|
|
777
|
+
}], openRecord: [{
|
|
778
|
+
type: Output
|
|
779
|
+
}], navigateToRelated: [{
|
|
780
|
+
type: Output
|
|
781
|
+
}], openRelatedRecord: [{
|
|
782
|
+
type: Output
|
|
783
|
+
}], openForeignKeyRecord: [{
|
|
784
|
+
type: Output
|
|
785
|
+
}] }); })();
|
|
786
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityRecordDetailPanelComponent, { className: "EntityRecordDetailPanelComponent" }); })();
|
|
787
|
+
//# sourceMappingURL=entity-record-detail-panel.component.js.map
|