@memberjunction/ng-explorer-core 5.33.0 → 5.34.1

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.
@@ -20,6 +20,8 @@ export declare class SingleRecordComponent extends BaseAngularComponent implemen
20
20
  appDescription: string;
21
21
  useGenericForm: boolean;
22
22
  loading: boolean;
23
+ errorTitle: string | null;
24
+ errorDetail: string | null;
23
25
  private _formComponentRef;
24
26
  private _currentRecord;
25
27
  private _eventHandlerSubscription;
@@ -27,6 +29,12 @@ export declare class SingleRecordComponent extends BaseAngularComponent implemen
27
29
  ngOnInit(): void;
28
30
  ngAfterViewInit(): void;
29
31
  LoadForm(primaryKey: CompositeKey, entityName: string): Promise<void>;
32
+ /**
33
+ * Render a user-visible error state inside the record pane AND log a structured
34
+ * console.error for developers. Always emits `loadComplete` so the Explorer shell
35
+ * does not hang on its first-resource-load gate.
36
+ */
37
+ private failWithUserError;
30
38
  protected SetNewRecordValues(record: BaseEntity): void;
31
39
  /**
32
40
  * Subscribe to BaseFormComponent @Output events and map them to Explorer services.
@@ -1 +1 @@
1
- {"version":3,"file":"single-record.component.d.ts","sourceRoot":"","sources":["../../../src/lib/single-record/single-record.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA8C,YAAY,EAAiB,SAAS,EAAE,MAAM,EAAqB,MAAM,eAAe,CAAC;AAC7J,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAA0B,YAAY,EAAE,UAAU,EAA4D,MAAM,sBAAsB,CAAC;AAGlJ,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;AACrE,qBAMa,qBAAsB,SAAQ,oBAAqB,YAAW,MAAM,EAAE,aAAa,EAAE,SAAS;IAc5F,OAAO,CAAC,KAAK;IAbY,aAAa,EAAG,SAAS,CAAC;IAChD,UAAU,EAAE,YAAY,CAAsB;IAC9C,UAAU,EAAE,MAAM,GAAG,IAAI,CAAM;IAC/B,eAAe,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAM;IAE7D,YAAY,EAAE,YAAY,CAAC,GAAG,CAAC,CAA2B;IAC1D,WAAW,EAAE,YAAY,CAAC,UAAU,CAAC,CAAkC;IAExF,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,GAAG,CAA6B;gBAEnB,KAAK,EAAE,cAAc;IAKnC,cAAc,EAAE,MAAM,CAAK;IAC3B,cAAc,EAAE,OAAO,CAAS;IAChC,OAAO,EAAE,OAAO,CAAQ;IAG/B,OAAO,CAAC,iBAAiB,CAAgD;IACzE,OAAO,CAAC,cAAc,CAA2B;IACjD,OAAO,CAAC,yBAAyB,CAA6B;IAC9D,OAAO,CAAC,uBAAuB,CAAsB;IAErD,QAAQ,IAAI,IAAI;IAGhB,eAAe;IAIF,QAAQ,CAAC,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM;IAuElE,SAAS,CAAC,kBAAkB,CAAC,MAAM,EAAE,UAAU;IA8E/C;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,gBAAgB;IAoBxB,OAAO,CAAC,wBAAwB;IAOhC,WAAW,IAAI,IAAI;yCAlOR,qBAAqB;2CAArB,qBAAqB;CAgQjC"}
1
+ {"version":3,"file":"single-record.component.d.ts","sourceRoot":"","sources":["../../../src/lib/single-record/single-record.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA8C,YAAY,EAAiB,SAAS,EAAE,MAAM,EAAqB,MAAM,eAAe,CAAC;AAC7J,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAA0B,YAAY,EAAE,UAAU,EAA4D,MAAM,sBAAsB,CAAC;AAGlJ,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;AACrE,qBAMa,qBAAsB,SAAQ,oBAAqB,YAAW,MAAM,EAAE,aAAa,EAAE,SAAS;IAc5F,OAAO,CAAC,KAAK;IAbY,aAAa,EAAG,SAAS,CAAC;IAChD,UAAU,EAAE,YAAY,CAAsB;IAC9C,UAAU,EAAE,MAAM,GAAG,IAAI,CAAM;IAC/B,eAAe,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAM;IAE7D,YAAY,EAAE,YAAY,CAAC,GAAG,CAAC,CAA2B;IAC1D,WAAW,EAAE,YAAY,CAAC,UAAU,CAAC,CAAkC;IAExF,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,GAAG,CAA6B;gBAEnB,KAAK,EAAE,cAAc;IAKnC,cAAc,EAAE,MAAM,CAAK;IAC3B,cAAc,EAAE,OAAO,CAAS;IAChC,OAAO,EAAE,OAAO,CAAQ;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IACjC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAQ;IAGzC,OAAO,CAAC,iBAAiB,CAAgD;IACzE,OAAO,CAAC,cAAc,CAA2B;IACjD,OAAO,CAAC,yBAAyB,CAA6B;IAC9D,OAAO,CAAC,uBAAuB,CAAsB;IAErD,QAAQ,IAAI,IAAI;IAGhB,eAAe;IAIF,QAAQ,CAAC,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM;IA2GlE;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAyBzB,SAAS,CAAC,kBAAkB,CAAC,MAAM,EAAE,UAAU;IA8E/C;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,gBAAgB;IA6BxB,OAAO,CAAC,wBAAwB;IAOhC,WAAW,IAAI,IAAI;yCA/SR,qBAAqB;2CAArB,qBAAqB;CA6UjC"}
@@ -14,7 +14,34 @@ function SingleRecordComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
14
14
  } if (rf & 2) {
15
15
  i0.ɵɵproperty("showText", false);
16
16
  } }
17
- function SingleRecordComponent_ng_template_1_Template(rf, ctx) { }
17
+ function SingleRecordComponent_Conditional_1_Conditional_5_Template(rf, ctx) { if (rf & 1) {
18
+ i0.ɵɵelementStart(0, "p", 6);
19
+ i0.ɵɵtext(1);
20
+ i0.ɵɵelementEnd();
21
+ } if (rf & 2) {
22
+ const ctx_r0 = i0.ɵɵnextContext(2);
23
+ i0.ɵɵadvance();
24
+ i0.ɵɵtextInterpolate(ctx_r0.errorDetail);
25
+ } }
26
+ function SingleRecordComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
27
+ i0.ɵɵelementStart(0, "div", 1)(1, "div", 3);
28
+ i0.ɵɵelement(2, "i", 4);
29
+ i0.ɵɵelementEnd();
30
+ i0.ɵɵelementStart(3, "h2", 5);
31
+ i0.ɵɵtext(4);
32
+ i0.ɵɵelementEnd();
33
+ i0.ɵɵconditionalCreate(5, SingleRecordComponent_Conditional_1_Conditional_5_Template, 2, 1, "p", 6);
34
+ i0.ɵɵelementStart(6, "p", 7);
35
+ i0.ɵɵtext(7, "See browser console for technical details.");
36
+ i0.ɵɵelementEnd()();
37
+ } if (rf & 2) {
38
+ const ctx_r0 = i0.ɵɵnextContext();
39
+ i0.ɵɵadvance(4);
40
+ i0.ɵɵtextInterpolate(ctx_r0.errorTitle);
41
+ i0.ɵɵadvance();
42
+ i0.ɵɵconditional(ctx_r0.errorDetail ? 5 : -1);
43
+ } }
44
+ function SingleRecordComponent_ng_template_2_Template(rf, ctx) { }
18
45
  export class SingleRecordComponent extends BaseAngularComponent {
19
46
  route;
20
47
  formContainer;
@@ -35,6 +62,8 @@ export class SingleRecordComponent extends BaseAngularComponent {
35
62
  appDescription = '';
36
63
  useGenericForm = false;
37
64
  loading = true;
65
+ errorTitle = null;
66
+ errorDetail = null;
38
67
  // Track dynamically created components and entities for cleanup
39
68
  _formComponentRef = null;
40
69
  _currentRecord = null;
@@ -48,7 +77,10 @@ export class SingleRecordComponent extends BaseAngularComponent {
48
77
  async LoadForm(primaryKey, entityName) {
49
78
  // Perform any necessary actions with the ViewID, such as fetching data
50
79
  if (!entityName || entityName.trim().length === 0) {
51
- return; // not ready to load
80
+ // No entity yet — caller will re-invoke once it has one. Don't emit loadComplete;
81
+ // it would race the real load. The shell's recovery timer will surface the
82
+ // "taking longer than expected" reset if this is the terminal state.
83
+ return;
52
84
  }
53
85
  this.entityName = entityName;
54
86
  if (primaryKey.HasValue) {
@@ -61,47 +93,91 @@ export class SingleRecordComponent extends BaseAngularComponent {
61
93
  }
62
94
  const formReg = MJGlobal.Instance.ClassFactory.GetRegistration(BaseFormComponent, entityName);
63
95
  const md = this.ProviderToUse;
64
- const entity = md.Entities.find(e => {
65
- return e.Name === entityName;
66
- });
96
+ const entity = md.EntityByName(entityName);
67
97
  const permissions = entity?.GetUserPermisions(md.CurrentUser);
68
- if (formReg) {
98
+ try {
99
+ if (!entity) {
100
+ this.failWithUserError(`Entity not found: "${entityName}"`, `This MemberJunction instance has no metadata for entity "${entityName}". Check that the entity name in the URL matches a row in __mj.Entity, and that the metadata cache is current.`);
101
+ return;
102
+ }
103
+ if (!formReg) {
104
+ this.failWithUserError(`No form is registered for "${entityName}".`, `MemberJunction could not find an Angular form component registered against BaseFormComponent for entity "${entityName}". This usually means CodeGen has not generated a form for this entity in the running build (forms live under packages/MJExplorer or your app's entity-form package). Run CodeGen, ensure the generated module is imported, or register a custom form via @RegisterClass(BaseFormComponent, '${entityName}').`, { entityId: entity.ID, recordKey: primaryKey?.ToString?.() ?? '(none)' });
105
+ return;
106
+ }
69
107
  const record = await md.GetEntityObject(entityName);
70
- if (record) {
71
- if (primaryKey.HasValue) {
72
- await record.InnerLoad(primaryKey);
73
- // Log access to existing record (fire-and-forget, don't await)
74
- this.recentAccessService.logAccess(entityName, primaryKey, 'record');
75
- }
76
- else {
77
- record.NewRecord();
78
- this.SetNewRecordValues(record);
108
+ if (!record) {
109
+ throw new Error(`Unable to instantiate entity ${entityName} with primary key values: ${primaryKey.ToString()}`);
110
+ }
111
+ if (primaryKey.HasValue) {
112
+ const loadOk = await record.InnerLoad(primaryKey);
113
+ if (!loadOk) {
114
+ this.failWithUserError(`Could not load ${entityName} record.`, record.LatestResult?.Message
115
+ ? `Server error: ${record.LatestResult.Message}`
116
+ : `InnerLoad returned false for entity "${entityName}" with key ${primaryKey.ToString()}. The record may not exist, you may lack permission to view it, or the load may have been blocked server-side.`, { recordKey: primaryKey.ToString() });
117
+ return;
79
118
  }
80
- // CRITICAL: Track the event handler subscription for cleanup
81
- this._eventHandlerSubscription = record.RegisterEventHandler((eventType) => {
82
- if (eventType.type === 'save')
83
- this.recordSaved.emit(record);
84
- });
85
- const viewContainerRef = this.formContainer.viewContainerRef;
86
- viewContainerRef.clear();
87
- const componentRef = viewContainerRef.createComponent(formReg.SubClass);
88
- // Track component and record for cleanup
89
- this._formComponentRef = componentRef;
90
- this._currentRecord = record;
91
- componentRef.instance.record = record;
92
- componentRef.instance.userPermissions = permissions;
93
- componentRef.instance.EditMode = !primaryKey.HasValue; // for new records go direct into edit mode
94
- // Subscribe to form @Output events and map them to Explorer services
95
- this.subscribeToFormEvents(componentRef.instance);
96
- this.useGenericForm = false;
97
- this.loadComplete.emit();
119
+ // Log access to existing record (fire-and-forget, don't await)
120
+ this.recentAccessService.logAccess(entityName, primaryKey, 'record');
121
+ }
122
+ else {
123
+ record.NewRecord();
124
+ this.SetNewRecordValues(record);
98
125
  }
99
- else
100
- throw new Error(`Unable to load entity ${entityName} with primary key values: ${primaryKey.ToString()}`);
126
+ // CRITICAL: Track the event handler subscription for cleanup
127
+ this._eventHandlerSubscription = record.RegisterEventHandler((eventType) => {
128
+ if (eventType.type === 'save')
129
+ this.recordSaved.emit(record);
130
+ });
131
+ const viewContainerRef = this.formContainer.viewContainerRef;
132
+ viewContainerRef.clear();
133
+ const componentRef = viewContainerRef.createComponent(formReg.SubClass);
134
+ // Track component and record for cleanup
135
+ this._formComponentRef = componentRef;
136
+ this._currentRecord = record;
137
+ componentRef.instance.record = record;
138
+ componentRef.instance.userPermissions = permissions;
139
+ componentRef.instance.EditMode = !primaryKey.HasValue; // for new records go direct into edit mode
140
+ // Subscribe to form @Output events and map them to Explorer services
141
+ this.subscribeToFormEvents(componentRef.instance);
142
+ this.useGenericForm = false;
143
+ this.errorTitle = null;
144
+ this.errorDetail = null;
145
+ this.loadComplete.emit();
146
+ }
147
+ catch (err) {
148
+ const msg = err instanceof Error ? err.message : String(err);
149
+ this.failWithUserError(`Failed to load ${entityName} record.`, `An unexpected error occurred while loading this record: ${msg}`, { error: err });
101
150
  }
102
151
  this.loading = false;
103
152
  this.cdr.detectChanges();
104
153
  }
154
+ /**
155
+ * Render a user-visible error state inside the record pane AND log a structured
156
+ * console.error for developers. Always emits `loadComplete` so the Explorer shell
157
+ * does not hang on its first-resource-load gate.
158
+ */
159
+ failWithUserError(title, detail, context) {
160
+ this.errorTitle = title;
161
+ this.errorDetail = detail;
162
+ this.loading = false;
163
+ // Single structured console.error for devs — easy to grep, easy to read.
164
+ console.error(`[SingleRecord] ${title}\n${detail}` +
165
+ (context ? `\nContext: ${JSON.stringify(context, null, 2)}` : ''));
166
+ // Clear any prior form/component so the error UI is what shows.
167
+ if (this._formComponentRef) {
168
+ try {
169
+ this._formComponentRef.destroy();
170
+ }
171
+ catch { /* noop */ }
172
+ this._formComponentRef = null;
173
+ }
174
+ if (this.formContainer?.viewContainerRef) {
175
+ this.formContainer.viewContainerRef.clear();
176
+ }
177
+ // Always unblock the shell.
178
+ this.loadComplete.emit();
179
+ this.cdr.detectChanges();
180
+ }
105
181
  SetNewRecordValues(record) {
106
182
  if (!this.newRecordValues) {
107
183
  return;
@@ -193,7 +269,16 @@ export class SingleRecordComponent extends BaseAngularComponent {
193
269
  this.navigationService.OpenEntityRecord(event.EntityName, event.PrimaryKey, { forceNewTab: event.OpenInNewTab });
194
270
  break;
195
271
  case 'new-record':
196
- this.navigationService.OpenNewEntityRecord(event.EntityName, { newRecordValues: event.DefaultValues });
272
+ // Creating a new related record from inside an open record form (e.g. + New
273
+ // on a related-entity grid). Force a new tab so the parent record stays
274
+ // intact — otherwise the new-record form silently replaces the parent in
275
+ // single-resource mode and the user loses their context. This is the
276
+ // original intent of dea32401ff, now stated explicitly at the call site
277
+ // instead of as a global navigation heuristic.
278
+ this.navigationService.OpenNewEntityRecord(event.EntityName, {
279
+ newRecordValues: event.DefaultValues,
280
+ forceNewTab: true,
281
+ });
197
282
  break;
198
283
  case 'entity-hierarchy':
199
284
  this.navigationService.OpenEntityRecord(event.EntityName, event.PrimaryKey);
@@ -243,16 +328,19 @@ export class SingleRecordComponent extends BaseAngularComponent {
243
328
  } if (rf & 2) {
244
329
  let _t;
245
330
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.formContainer = _t.first);
246
- } }, inputs: { PrimaryKey: "PrimaryKey", entityName: "entityName", newRecordValues: "newRecordValues" }, outputs: { loadComplete: "loadComplete", recordSaved: "recordSaved" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 2, vars: 1, consts: [["size", "large", 3, "showText"], ["mjContainer", ""]], template: function SingleRecordComponent_Template(rf, ctx) { if (rf & 1) {
331
+ } }, inputs: { PrimaryKey: "PrimaryKey", entityName: "entityName", newRecordValues: "newRecordValues" }, outputs: { loadComplete: "loadComplete", recordSaved: "recordSaved" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 3, vars: 2, consts: [["size", "large", 3, "showText"], ["role", "alert", 1, "single-record-error"], ["mjContainer", ""], [1, "single-record-error__icon"], [1, "fa-solid", "fa-triangle-exclamation"], [1, "single-record-error__title"], [1, "single-record-error__detail"], [1, "single-record-error__hint"]], template: function SingleRecordComponent_Template(rf, ctx) { if (rf & 1) {
247
332
  i0.ɵɵconditionalCreate(0, SingleRecordComponent_Conditional_0_Template, 1, 1, "mj-loading", 0);
248
- i0.ɵɵtemplate(1, SingleRecordComponent_ng_template_1_Template, 0, 0, "ng-template", 1);
333
+ i0.ɵɵconditionalCreate(1, SingleRecordComponent_Conditional_1_Template, 8, 2, "div", 1);
334
+ i0.ɵɵtemplate(2, SingleRecordComponent_ng_template_2_Template, 0, 0, "ng-template", 2);
249
335
  } if (rf & 2) {
250
336
  i0.ɵɵconditional(ctx.loading ? 0 : -1);
251
- } }, dependencies: [i2.Container, i3.LoadingComponent], styles: ["[_nghost-%COMP%] {\n display: block;\n height: 100%;\n width: 100%;\n}"] });
337
+ i0.ɵɵadvance();
338
+ i0.ɵɵconditional(ctx.errorTitle ? 1 : -1);
339
+ } }, dependencies: [i2.Container, i3.LoadingComponent], styles: ["[_nghost-%COMP%] {\n display: block;\n height: 100%;\n width: 100%;\n}\n\n.single-record-error[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n text-align: center;\n padding: 48px 32px;\n max-width: 720px;\n margin: 64px auto;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-status-error-border);\n border-radius: 8px;\n color: var(--mj-text-primary);\n}\n\n.single-record-error__icon[_ngcontent-%COMP%] {\n font-size: 36px;\n color: var(--mj-status-error);\n margin-bottom: 16px;\n}\n\n.single-record-error__title[_ngcontent-%COMP%] {\n font-size: 18px;\n font-weight: 600;\n margin: 0 0 12px 0;\n color: var(--mj-status-error-text);\n}\n\n.single-record-error__detail[_ngcontent-%COMP%] {\n font-size: 14px;\n line-height: 1.5;\n color: var(--mj-text-secondary);\n margin: 0 0 16px 0;\n white-space: pre-wrap;\n}\n\n.single-record-error__hint[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin: 0;\n font-style: italic;\n}"] });
252
340
  }
253
341
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SingleRecordComponent, [{
254
342
  type: Component,
255
- args: [{ standalone: false, selector: 'mj-single-record', template: "@if (loading) {\n <mj-loading [showText]=\"false\" size=\"large\"></mj-loading>\n}\n<ng-template mjContainer></ng-template>", styles: [":host {\n display: block;\n height: 100%;\n width: 100%;\n}\n"] }]
343
+ args: [{ standalone: false, selector: 'mj-single-record', template: "@if (loading) {\n <mj-loading [showText]=\"false\" size=\"large\"></mj-loading>\n}\n@if (errorTitle) {\n <div class=\"single-record-error\" role=\"alert\">\n <div class=\"single-record-error__icon\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n </div>\n <h2 class=\"single-record-error__title\">{{ errorTitle }}</h2>\n @if (errorDetail) {\n <p class=\"single-record-error__detail\">{{ errorDetail }}</p>\n }\n <p class=\"single-record-error__hint\">See browser console for technical details.</p>\n </div>\n}\n<ng-template mjContainer></ng-template>\n", styles: [":host {\n display: block;\n height: 100%;\n width: 100%;\n}\n\n.single-record-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n text-align: center;\n padding: 48px 32px;\n max-width: 720px;\n margin: 64px auto;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-status-error-border);\n border-radius: 8px;\n color: var(--mj-text-primary);\n}\n\n.single-record-error__icon {\n font-size: 36px;\n color: var(--mj-status-error);\n margin-bottom: 16px;\n}\n\n.single-record-error__title {\n font-size: 18px;\n font-weight: 600;\n margin: 0 0 12px 0;\n color: var(--mj-status-error-text);\n}\n\n.single-record-error__detail {\n font-size: 14px;\n line-height: 1.5;\n color: var(--mj-text-secondary);\n margin: 0 0 16px 0;\n white-space: pre-wrap;\n}\n\n.single-record-error__hint {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin: 0;\n font-style: italic;\n}\n"] }]
256
344
  }], () => [{ type: i1.ActivatedRoute }], { formContainer: [{
257
345
  type: ViewChild,
258
346
  args: [Container, { static: true }]
@@ -1 +1 @@
1
- {"version":3,"file":"single-record.component.js","sourceRoot":"","sources":["../../../src/lib/single-record/single-record.component.ts","../../../src/lib/single-record/single-record.component.html"],"names":[],"mappings":"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","sourcesContent":["import { AfterViewInit, ChangeDetectorRef, Component, ComponentRef, EventEmitter, inject, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router'\nimport { Metadata, KeyValuePair, CompositeKey, BaseEntity, BaseEntityEvent, FieldValueCollection, EntityFieldTSType } from '@memberjunction/core';\nimport { Subscription } from 'rxjs';\nimport { MJGlobal } from '@memberjunction/global';\nimport { Container } from '@memberjunction/ng-container-directives';\nimport { BaseFormComponent, FormNavigationEvent, FormNotificationEvent } from '@memberjunction/ng-base-forms';\nimport { NavigationService, RecentAccessService, SharedService } from '@memberjunction/ng-shared';\n\n\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\n@Component({\n standalone: false,\n selector: 'mj-single-record',\n templateUrl: './single-record.component.html',\n styleUrls: ['./single-record.component.css']\n})\nexport class SingleRecordComponent extends BaseAngularComponent implements OnInit, AfterViewInit, OnDestroy {\n @ViewChild(Container, {static: true}) formContainer!: Container;\n @Input() public PrimaryKey: CompositeKey = new CompositeKey();\n @Input() public entityName: string | null = '';\n @Input() public newRecordValues: string | Record<string, unknown> | null = '';\n\n @Output() public loadComplete: EventEmitter<any> = new EventEmitter<any>();\n @Output() public recordSaved: EventEmitter<BaseEntity> = new EventEmitter<BaseEntity>();\n\n private recentAccessService: RecentAccessService;\n private navigationService = inject(NavigationService);\n private sharedService = inject(SharedService);\n private cdr = inject(ChangeDetectorRef);\n\n constructor (private route: ActivatedRoute) {\n super();\n this.recentAccessService = new RecentAccessService();\n }\n\n public appDescription: string = ''\n public useGenericForm: boolean = false;\n public loading: boolean = true;\n\n // Track dynamically created components and entities for cleanup\n private _formComponentRef: ComponentRef<BaseFormComponent> | null = null;\n private _currentRecord: BaseEntity | null = null;\n private _eventHandlerSubscription: Subscription | null = null;\n private _formEventSubscriptions: Subscription[] = [];\n\n ngOnInit(): void {\n }\n\n ngAfterViewInit() {\n this.LoadForm(this.PrimaryKey, <string>this.entityName)\n }\n\n public async LoadForm(primaryKey: CompositeKey, entityName: string) {\n \n // Perform any necessary actions with the ViewID, such as fetching data\n if (!entityName || entityName.trim().length === 0) {\n return; // not ready to load\n }\n\n this.entityName = entityName;\n if (primaryKey.HasValue) {\n // we have an existing record to load up\n this.PrimaryKey = primaryKey;\n }\n else {\n // new record, no existing primary key\n this.PrimaryKey = new CompositeKey();\n }\n\n const formReg = MJGlobal.Instance.ClassFactory.GetRegistration(BaseFormComponent, entityName);\n \n const md = this.ProviderToUse;\n const entity = md.Entities.find(e => {\n return e.Name === entityName\n });\n const permissions = entity?.GetUserPermisions(md.CurrentUser);\n\n if (formReg) {\n const record = await md.GetEntityObject<BaseEntity>(entityName);\n if (record) {\n if (primaryKey.HasValue) {\n await record.InnerLoad(primaryKey);\n // Log access to existing record (fire-and-forget, don't await)\n this.recentAccessService.logAccess(entityName, primaryKey, 'record');\n }\n else {\n record.NewRecord();\n this.SetNewRecordValues(record);\n }\n\n // CRITICAL: Track the event handler subscription for cleanup\n this._eventHandlerSubscription = record.RegisterEventHandler((eventType: BaseEntityEvent) => {\n if (eventType.type === 'save')\n this.recordSaved.emit(record);\n });\n \n const viewContainerRef = this.formContainer.viewContainerRef;\n viewContainerRef.clear();\n\n const componentRef = viewContainerRef.createComponent<typeof formReg.SubClass>(formReg.SubClass);\n \n // Track component and record for cleanup\n this._formComponentRef = componentRef;\n this._currentRecord = record;\n \n componentRef.instance.record = record\n componentRef.instance.userPermissions = permissions\n componentRef.instance.EditMode = !primaryKey.HasValue; // for new records go direct into edit mode\n\n // Subscribe to form @Output events and map them to Explorer services\n this.subscribeToFormEvents(componentRef.instance);\n\n this.useGenericForm = false;\n this.loadComplete.emit();\n }\n else\n throw new Error(`Unable to load entity ${entityName} with primary key values: ${primaryKey.ToString()}`);\n }\n\n this.loading = false;\n this.cdr.detectChanges();\n }\n\n protected SetNewRecordValues(record: BaseEntity) {\n if (!this.newRecordValues) {\n return;\n }\n\n // Handle both object and string (URL segment) formats\n if (typeof this.newRecordValues === 'string') {\n if (this.newRecordValues.length === 0) {\n return;\n }\n // we have a URL segment string format: \"field1|value1||field2|value2\"\n const fv = new FieldValueCollection();\n fv.SimpleLoadFromURLSegment(this.newRecordValues);\n // now apply the values to the record\n fv.KeyValuePairs.filter(kvp => kvp.Value !== null && kvp.Value !== undefined).forEach(kvp => {\n const f = record.Fields.find(f => f.Name.trim().toLowerCase() === kvp.FieldName.trim().toLowerCase());\n if (f) {\n // make sure we set the value to the right type based on the f.TSType property\n switch (f.EntityFieldInfo.TSType) {\n case EntityFieldTSType.String:\n record.Set(kvp.FieldName, kvp.Value);\n break;\n case EntityFieldTSType.Number:\n record.Set(kvp.FieldName, parseFloat(kvp.Value));\n break;\n case EntityFieldTSType.Boolean:\n if (kvp.Value === 'false' || kvp.Value === '0' || kvp.Value.toString().trim().length === 0 )\n record.Set(kvp.FieldName, false);\n else\n record.Set(kvp.FieldName, true);\n break;\n case EntityFieldTSType.Date:\n record.Set(kvp.FieldName, new Date(kvp.Value));\n break;\n }\n }\n });\n }\n else {\n // we have a plain object format: { field1: value1, field2: value2 }\n const recordValues = this.newRecordValues as Record<string, unknown>;\n Object.keys(recordValues)\n .filter(key => recordValues[key] !== null && recordValues[key] !== undefined)\n .forEach(key => {\n const f = record.Fields.find(f => f.Name.trim().toLowerCase() === key.trim().toLowerCase());\n if (f) {\n const value = recordValues[key];\n // Set the value with proper type conversion\n switch (f.EntityFieldInfo.TSType) {\n case EntityFieldTSType.String:\n record.Set(key, value?.toString() || '');\n break;\n case EntityFieldTSType.Number:\n record.Set(key, typeof value === 'number' ? value : parseFloat(value?.toString() || '0'));\n break;\n case EntityFieldTSType.Boolean:\n if (typeof value === 'boolean') {\n record.Set(key, value);\n }\n else if (typeof value === 'string') {\n record.Set(key, value !== 'false' && value !== '0' && value.trim().length > 0);\n }\n else {\n record.Set(key, !!value);\n }\n break;\n case EntityFieldTSType.Date:\n record.Set(key, value instanceof Date ? value : new Date(value?.toString() || ''));\n break;\n default:\n record.Set(key, value);\n break;\n }\n }\n });\n }\n }\n\n /**\n * Subscribe to BaseFormComponent @Output events and map them to Explorer services.\n */\n private subscribeToFormEvents(form: BaseFormComponent): void {\n this.cleanupFormSubscriptions();\n\n this._formEventSubscriptions.push(\n form.Navigate.subscribe((event: FormNavigationEvent) => this.handleNavigation(event)),\n form.Notification.subscribe((event: FormNotificationEvent) => {\n this.sharedService.CreateSimpleNotification(event.Message, event.Type, event.Duration);\n })\n );\n }\n\n private handleNavigation(event: FormNavigationEvent): void {\n switch (event.Kind) {\n case 'record':\n this.navigationService.OpenEntityRecord(event.EntityName, event.PrimaryKey, { forceNewTab: event.OpenInNewTab });\n break;\n case 'new-record':\n this.navigationService.OpenNewEntityRecord(event.EntityName, { newRecordValues: event.DefaultValues });\n break;\n case 'entity-hierarchy':\n this.navigationService.OpenEntityRecord(event.EntityName, event.PrimaryKey);\n break;\n case 'external-link':\n window.open(event.Url, '_blank');\n break;\n case 'email':\n window.open(`mailto:${event.EmailAddress}`, '_self');\n break;\n }\n }\n\n private cleanupFormSubscriptions(): void {\n for (const sub of this._formEventSubscriptions) {\n sub.unsubscribe();\n }\n this._formEventSubscriptions = [];\n }\n\n ngOnDestroy(): void {\n // CRITICAL: Clean up form event subscriptions first\n this.cleanupFormSubscriptions();\n\n // CRITICAL: Clean up dynamically created form component to prevent zombie components\n if (this._formComponentRef) {\n this._formComponentRef.destroy();\n this._formComponentRef = null;\n }\n\n // CRITICAL: Unsubscribe from event handler to prevent memory leaks\n if (this._eventHandlerSubscription) {\n this._eventHandlerSubscription.unsubscribe();\n this._eventHandlerSubscription = null;\n }\n \n // Clean up record reference\n if (this._currentRecord) {\n this._currentRecord = null;\n }\n \n // Clear the view container to ensure no lingering references\n if (this.formContainer?.viewContainerRef) {\n this.formContainer.viewContainerRef.clear();\n }\n \n // Reset state\n this.loading = true;\n this.useGenericForm = false;\n }\n}\n","@if (loading) {\n <mj-loading [showText]=\"false\" size=\"large\"></mj-loading>\n}\n<ng-template mjContainer></ng-template>"]}
1
+ {"version":3,"file":"single-record.component.js","sourceRoot":"","sources":["../../../src/lib/single-record/single-record.component.ts","../../../src/lib/single-record/single-record.component.html"],"names":[],"mappings":"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","sourcesContent":["import { AfterViewInit, ChangeDetectorRef, Component, ComponentRef, EventEmitter, inject, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router'\nimport { Metadata, KeyValuePair, CompositeKey, BaseEntity, BaseEntityEvent, FieldValueCollection, EntityFieldTSType } from '@memberjunction/core';\nimport { Subscription } from 'rxjs';\nimport { MJGlobal } from '@memberjunction/global';\nimport { Container } from '@memberjunction/ng-container-directives';\nimport { BaseFormComponent, FormNavigationEvent, FormNotificationEvent } from '@memberjunction/ng-base-forms';\nimport { NavigationService, RecentAccessService, SharedService } from '@memberjunction/ng-shared';\n\n\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\n@Component({\n standalone: false,\n selector: 'mj-single-record',\n templateUrl: './single-record.component.html',\n styleUrls: ['./single-record.component.css']\n})\nexport class SingleRecordComponent extends BaseAngularComponent implements OnInit, AfterViewInit, OnDestroy {\n @ViewChild(Container, {static: true}) formContainer!: Container;\n @Input() public PrimaryKey: CompositeKey = new CompositeKey();\n @Input() public entityName: string | null = '';\n @Input() public newRecordValues: string | Record<string, unknown> | null = '';\n\n @Output() public loadComplete: EventEmitter<any> = new EventEmitter<any>();\n @Output() public recordSaved: EventEmitter<BaseEntity> = new EventEmitter<BaseEntity>();\n\n private recentAccessService: RecentAccessService;\n private navigationService = inject(NavigationService);\n private sharedService = inject(SharedService);\n private cdr = inject(ChangeDetectorRef);\n\n constructor (private route: ActivatedRoute) {\n super();\n this.recentAccessService = new RecentAccessService();\n }\n\n public appDescription: string = ''\n public useGenericForm: boolean = false;\n public loading: boolean = true;\n public errorTitle: string | null = null;\n public errorDetail: string | null = null;\n\n // Track dynamically created components and entities for cleanup\n private _formComponentRef: ComponentRef<BaseFormComponent> | null = null;\n private _currentRecord: BaseEntity | null = null;\n private _eventHandlerSubscription: Subscription | null = null;\n private _formEventSubscriptions: Subscription[] = [];\n\n ngOnInit(): void {\n }\n\n ngAfterViewInit() {\n this.LoadForm(this.PrimaryKey, <string>this.entityName)\n }\n\n public async LoadForm(primaryKey: CompositeKey, entityName: string) {\n // Perform any necessary actions with the ViewID, such as fetching data\n if (!entityName || entityName.trim().length === 0) {\n // No entity yet — caller will re-invoke once it has one. Don't emit loadComplete;\n // it would race the real load. The shell's recovery timer will surface the\n // \"taking longer than expected\" reset if this is the terminal state.\n return;\n }\n\n this.entityName = entityName;\n if (primaryKey.HasValue) {\n // we have an existing record to load up\n this.PrimaryKey = primaryKey;\n }\n else {\n // new record, no existing primary key\n this.PrimaryKey = new CompositeKey();\n }\n\n const formReg = MJGlobal.Instance.ClassFactory.GetRegistration(BaseFormComponent, entityName);\n\n const md = this.ProviderToUse;\n const entity = md.EntityByName(entityName);\n const permissions = entity?.GetUserPermisions(md.CurrentUser);\n\n try {\n if (!entity) {\n this.failWithUserError(\n `Entity not found: \"${entityName}\"`,\n `This MemberJunction instance has no metadata for entity \"${entityName}\". Check that the entity name in the URL matches a row in __mj.Entity, and that the metadata cache is current.`\n );\n return;\n }\n\n if (!formReg) {\n this.failWithUserError(\n `No form is registered for \"${entityName}\".`,\n `MemberJunction could not find an Angular form component registered against BaseFormComponent for entity \"${entityName}\". This usually means CodeGen has not generated a form for this entity in the running build (forms live under packages/MJExplorer or your app's entity-form package). Run CodeGen, ensure the generated module is imported, or register a custom form via @RegisterClass(BaseFormComponent, '${entityName}').`,\n { entityId: entity.ID, recordKey: primaryKey?.ToString?.() ?? '(none)' }\n );\n return;\n }\n\n const record = await md.GetEntityObject<BaseEntity>(entityName);\n if (!record) {\n throw new Error(`Unable to instantiate entity ${entityName} with primary key values: ${primaryKey.ToString()}`);\n }\n\n if (primaryKey.HasValue) {\n const loadOk = await record.InnerLoad(primaryKey);\n if (!loadOk) {\n this.failWithUserError(\n `Could not load ${entityName} record.`,\n record.LatestResult?.Message\n ? `Server error: ${record.LatestResult.Message}`\n : `InnerLoad returned false for entity \"${entityName}\" with key ${primaryKey.ToString()}. The record may not exist, you may lack permission to view it, or the load may have been blocked server-side.`,\n { recordKey: primaryKey.ToString() }\n );\n return;\n }\n // Log access to existing record (fire-and-forget, don't await)\n this.recentAccessService.logAccess(entityName, primaryKey, 'record');\n }\n else {\n record.NewRecord();\n this.SetNewRecordValues(record);\n }\n\n // CRITICAL: Track the event handler subscription for cleanup\n this._eventHandlerSubscription = record.RegisterEventHandler((eventType: BaseEntityEvent) => {\n if (eventType.type === 'save')\n this.recordSaved.emit(record);\n });\n\n const viewContainerRef = this.formContainer.viewContainerRef;\n viewContainerRef.clear();\n\n const componentRef = viewContainerRef.createComponent<typeof formReg.SubClass>(formReg.SubClass);\n\n // Track component and record for cleanup\n this._formComponentRef = componentRef;\n this._currentRecord = record;\n\n componentRef.instance.record = record\n componentRef.instance.userPermissions = permissions\n componentRef.instance.EditMode = !primaryKey.HasValue; // for new records go direct into edit mode\n\n // Subscribe to form @Output events and map them to Explorer services\n this.subscribeToFormEvents(componentRef.instance);\n\n this.useGenericForm = false;\n this.errorTitle = null;\n this.errorDetail = null;\n this.loadComplete.emit();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n this.failWithUserError(\n `Failed to load ${entityName} record.`,\n `An unexpected error occurred while loading this record: ${msg}`,\n { error: err }\n );\n }\n\n this.loading = false;\n this.cdr.detectChanges();\n }\n\n /**\n * Render a user-visible error state inside the record pane AND log a structured\n * console.error for developers. Always emits `loadComplete` so the Explorer shell\n * does not hang on its first-resource-load gate.\n */\n private failWithUserError(title: string, detail: string, context?: Record<string, unknown>): void {\n this.errorTitle = title;\n this.errorDetail = detail;\n this.loading = false;\n\n // Single structured console.error for devs — easy to grep, easy to read.\n console.error(\n `[SingleRecord] ${title}\\n${detail}` +\n (context ? `\\nContext: ${JSON.stringify(context, null, 2)}` : '')\n );\n\n // Clear any prior form/component so the error UI is what shows.\n if (this._formComponentRef) {\n try { this._formComponentRef.destroy(); } catch { /* noop */ }\n this._formComponentRef = null;\n }\n if (this.formContainer?.viewContainerRef) {\n this.formContainer.viewContainerRef.clear();\n }\n\n // Always unblock the shell.\n this.loadComplete.emit();\n this.cdr.detectChanges();\n }\n\n protected SetNewRecordValues(record: BaseEntity) {\n if (!this.newRecordValues) {\n return;\n }\n\n // Handle both object and string (URL segment) formats\n if (typeof this.newRecordValues === 'string') {\n if (this.newRecordValues.length === 0) {\n return;\n }\n // we have a URL segment string format: \"field1|value1||field2|value2\"\n const fv = new FieldValueCollection();\n fv.SimpleLoadFromURLSegment(this.newRecordValues);\n // now apply the values to the record\n fv.KeyValuePairs.filter(kvp => kvp.Value !== null && kvp.Value !== undefined).forEach(kvp => {\n const f = record.Fields.find(f => f.Name.trim().toLowerCase() === kvp.FieldName.trim().toLowerCase());\n if (f) {\n // make sure we set the value to the right type based on the f.TSType property\n switch (f.EntityFieldInfo.TSType) {\n case EntityFieldTSType.String:\n record.Set(kvp.FieldName, kvp.Value);\n break;\n case EntityFieldTSType.Number:\n record.Set(kvp.FieldName, parseFloat(kvp.Value));\n break;\n case EntityFieldTSType.Boolean:\n if (kvp.Value === 'false' || kvp.Value === '0' || kvp.Value.toString().trim().length === 0 )\n record.Set(kvp.FieldName, false);\n else\n record.Set(kvp.FieldName, true);\n break;\n case EntityFieldTSType.Date:\n record.Set(kvp.FieldName, new Date(kvp.Value));\n break;\n }\n }\n });\n }\n else {\n // we have a plain object format: { field1: value1, field2: value2 }\n const recordValues = this.newRecordValues as Record<string, unknown>;\n Object.keys(recordValues)\n .filter(key => recordValues[key] !== null && recordValues[key] !== undefined)\n .forEach(key => {\n const f = record.Fields.find(f => f.Name.trim().toLowerCase() === key.trim().toLowerCase());\n if (f) {\n const value = recordValues[key];\n // Set the value with proper type conversion\n switch (f.EntityFieldInfo.TSType) {\n case EntityFieldTSType.String:\n record.Set(key, value?.toString() || '');\n break;\n case EntityFieldTSType.Number:\n record.Set(key, typeof value === 'number' ? value : parseFloat(value?.toString() || '0'));\n break;\n case EntityFieldTSType.Boolean:\n if (typeof value === 'boolean') {\n record.Set(key, value);\n }\n else if (typeof value === 'string') {\n record.Set(key, value !== 'false' && value !== '0' && value.trim().length > 0);\n }\n else {\n record.Set(key, !!value);\n }\n break;\n case EntityFieldTSType.Date:\n record.Set(key, value instanceof Date ? value : new Date(value?.toString() || ''));\n break;\n default:\n record.Set(key, value);\n break;\n }\n }\n });\n }\n }\n\n /**\n * Subscribe to BaseFormComponent @Output events and map them to Explorer services.\n */\n private subscribeToFormEvents(form: BaseFormComponent): void {\n this.cleanupFormSubscriptions();\n\n this._formEventSubscriptions.push(\n form.Navigate.subscribe((event: FormNavigationEvent) => this.handleNavigation(event)),\n form.Notification.subscribe((event: FormNotificationEvent) => {\n this.sharedService.CreateSimpleNotification(event.Message, event.Type, event.Duration);\n })\n );\n }\n\n private handleNavigation(event: FormNavigationEvent): void {\n switch (event.Kind) {\n case 'record':\n this.navigationService.OpenEntityRecord(event.EntityName, event.PrimaryKey, { forceNewTab: event.OpenInNewTab });\n break;\n case 'new-record':\n // Creating a new related record from inside an open record form (e.g. + New\n // on a related-entity grid). Force a new tab so the parent record stays\n // intact — otherwise the new-record form silently replaces the parent in\n // single-resource mode and the user loses their context. This is the\n // original intent of dea32401ff, now stated explicitly at the call site\n // instead of as a global navigation heuristic.\n this.navigationService.OpenNewEntityRecord(event.EntityName, {\n newRecordValues: event.DefaultValues,\n forceNewTab: true,\n });\n break;\n case 'entity-hierarchy':\n this.navigationService.OpenEntityRecord(event.EntityName, event.PrimaryKey);\n break;\n case 'external-link':\n window.open(event.Url, '_blank');\n break;\n case 'email':\n window.open(`mailto:${event.EmailAddress}`, '_self');\n break;\n }\n }\n\n private cleanupFormSubscriptions(): void {\n for (const sub of this._formEventSubscriptions) {\n sub.unsubscribe();\n }\n this._formEventSubscriptions = [];\n }\n\n ngOnDestroy(): void {\n // CRITICAL: Clean up form event subscriptions first\n this.cleanupFormSubscriptions();\n\n // CRITICAL: Clean up dynamically created form component to prevent zombie components\n if (this._formComponentRef) {\n this._formComponentRef.destroy();\n this._formComponentRef = null;\n }\n\n // CRITICAL: Unsubscribe from event handler to prevent memory leaks\n if (this._eventHandlerSubscription) {\n this._eventHandlerSubscription.unsubscribe();\n this._eventHandlerSubscription = null;\n }\n \n // Clean up record reference\n if (this._currentRecord) {\n this._currentRecord = null;\n }\n \n // Clear the view container to ensure no lingering references\n if (this.formContainer?.viewContainerRef) {\n this.formContainer.viewContainerRef.clear();\n }\n \n // Reset state\n this.loading = true;\n this.useGenericForm = false;\n }\n}\n","@if (loading) {\n <mj-loading [showText]=\"false\" size=\"large\"></mj-loading>\n}\n@if (errorTitle) {\n <div class=\"single-record-error\" role=\"alert\">\n <div class=\"single-record-error__icon\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n </div>\n <h2 class=\"single-record-error__title\">{{ errorTitle }}</h2>\n @if (errorDetail) {\n <p class=\"single-record-error__detail\">{{ errorDetail }}</p>\n }\n <p class=\"single-record-error__hint\">See browser console for technical details.</p>\n </div>\n}\n<ng-template mjContainer></ng-template>\n"]}
@@ -120,7 +120,7 @@ export class SystemValidationBannerComponent {
120
120
  i0.ɵɵconditionalCreate(0, SystemValidationBannerComponent_Conditional_0_Template, 5, 1, "div", 0);
121
121
  } if (rf & 2) {
122
122
  i0.ɵɵconditional(ctx.issues.length > 0 ? 0 : -1);
123
- } }, dependencies: [CommonModule, i2.NgClass], styles: [".system-validation-wrapper[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 10000;\n pointer-events: none;\n }\n \n .system-validation-overlay[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0, 0, 0, 0.5);\n z-index: 10001;\n pointer-events: auto;\n }\n \n .system-validation-container[_ngcontent-%COMP%] {\n position: fixed;\n top: 60px;\n left: 0;\n right: 0;\n z-index: 10002;\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 16px;\n max-height: 80vh;\n overflow-y: auto;\n pointer-events: auto;\n }\n\n .system-validation-banner[_ngcontent-%COMP%] {\n border-radius: 4px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n padding: 16px;\n animation: _ngcontent-%COMP%_slide-down 0.3s ease-out;\n }\n\n .system-validation-error[_ngcontent-%COMP%] {\n background-color: #ffebee;\n border-left: 4px solid #f44336;\n }\n\n .system-validation-warning[_ngcontent-%COMP%] {\n background-color: #fff8e1;\n border-left: 4px solid #ff9800;\n }\n\n .system-validation-info[_ngcontent-%COMP%] {\n background-color: #e3f2fd;\n border-left: 4px solid #2196f3;\n }\n\n .banner-content[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n }\n\n .banner-icon[_ngcontent-%COMP%] {\n margin-right: 16px;\n font-size: 24px;\n }\n\n .severity-icon[_ngcontent-%COMP%] {\n font-size: 18px;\n }\n\n .severity-error[_ngcontent-%COMP%] {\n color: #f44336;\n }\n\n .severity-warning[_ngcontent-%COMP%] {\n color: #ff9800;\n }\n\n .severity-info[_ngcontent-%COMP%] {\n color: #2196f3;\n }\n\n .banner-message[_ngcontent-%COMP%] {\n flex: 1;\n }\n\n .banner-message[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n }\n\n .banner-message[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 4px 0 0 0;\n font-size: 14px;\n }\n\n .help-text[_ngcontent-%COMP%] {\n font-style: italic;\n }\n\n .dismiss-button[_ngcontent-%COMP%] {\n background: transparent;\n border: none;\n cursor: pointer;\n font-size: 16px;\n color: var(--mj-text-secondary);\n padding: 4px;\n }\n\n .dismiss-button[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n }\n\n @keyframes _ngcontent-%COMP%_slide-down {\n 0% {\n transform: translateY(-20px);\n opacity: 0;\n }\n 100% {\n transform: translateY(0);\n opacity: 1;\n }\n }"] });
123
+ } }, dependencies: [CommonModule, i2.NgClass], styles: [".system-validation-wrapper[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 10000;\n pointer-events: none;\n }\n \n .system-validation-overlay[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: var(--mj-bg-overlay);\n z-index: 10001;\n pointer-events: auto;\n }\n \n .system-validation-container[_ngcontent-%COMP%] {\n position: fixed;\n top: 60px;\n left: 0;\n right: 0;\n z-index: 10002;\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 16px;\n max-height: 80vh;\n overflow-y: auto;\n pointer-events: auto;\n }\n\n .system-validation-banner[_ngcontent-%COMP%] {\n border-radius: 4px;\n box-shadow: var(--mj-shadow-md);\n padding: 16px;\n animation: _ngcontent-%COMP%_slide-down 0.3s ease-out;\n }\n\n .system-validation-error[_ngcontent-%COMP%] {\n background-color: var(--mj-status-error-bg);\n border-left: 4px solid var(--mj-status-error);\n }\n\n .system-validation-warning[_ngcontent-%COMP%] {\n background-color: var(--mj-status-warning-bg);\n border-left: 4px solid var(--mj-status-warning);\n }\n\n .system-validation-info[_ngcontent-%COMP%] {\n background-color: var(--mj-status-info-bg);\n border-left: 4px solid var(--mj-status-info);\n }\n\n .banner-content[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n }\n\n .banner-icon[_ngcontent-%COMP%] {\n margin-right: 16px;\n font-size: 24px;\n }\n\n .severity-icon[_ngcontent-%COMP%] {\n font-size: 18px;\n }\n\n .severity-error[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n }\n\n .severity-warning[_ngcontent-%COMP%] {\n color: var(--mj-status-warning);\n }\n\n .severity-info[_ngcontent-%COMP%] {\n color: var(--mj-status-info);\n }\n\n .banner-message[_ngcontent-%COMP%] {\n flex: 1;\n }\n\n .banner-message[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n }\n\n .banner-message[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 4px 0 0 0;\n font-size: 14px;\n }\n\n .help-text[_ngcontent-%COMP%] {\n font-style: italic;\n }\n\n .dismiss-button[_ngcontent-%COMP%] {\n background: transparent;\n border: none;\n cursor: pointer;\n font-size: 16px;\n color: var(--mj-text-secondary);\n padding: 4px;\n }\n\n .dismiss-button[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n }\n\n @keyframes _ngcontent-%COMP%_slide-down {\n 0% {\n transform: translateY(-20px);\n opacity: 0;\n }\n 100% {\n transform: translateY(0);\n opacity: 1;\n }\n }"] });
124
124
  }
125
125
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SystemValidationBannerComponent, [{
126
126
  type: Component,
@@ -170,7 +170,7 @@ export class SystemValidationBannerComponent {
170
170
  </div>
171
171
  </div>
172
172
  }
173
- `, styles: ["\n .system-validation-wrapper {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 10000;\n pointer-events: none;\n }\n \n .system-validation-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0, 0, 0, 0.5);\n z-index: 10001;\n pointer-events: auto;\n }\n \n .system-validation-container {\n position: fixed;\n top: 60px;\n left: 0;\n right: 0;\n z-index: 10002;\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 16px;\n max-height: 80vh;\n overflow-y: auto;\n pointer-events: auto;\n }\n\n .system-validation-banner {\n border-radius: 4px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n padding: 16px;\n animation: slide-down 0.3s ease-out;\n }\n\n .system-validation-error {\n background-color: #ffebee;\n border-left: 4px solid #f44336;\n }\n\n .system-validation-warning {\n background-color: #fff8e1;\n border-left: 4px solid #ff9800;\n }\n\n .system-validation-info {\n background-color: #e3f2fd;\n border-left: 4px solid #2196f3;\n }\n\n .banner-content {\n display: flex;\n align-items: flex-start;\n }\n\n .banner-icon {\n margin-right: 16px;\n font-size: 24px;\n }\n\n .severity-icon {\n font-size: 18px;\n }\n\n .severity-error {\n color: #f44336;\n }\n\n .severity-warning {\n color: #ff9800;\n }\n\n .severity-info {\n color: #2196f3;\n }\n\n .banner-message {\n flex: 1;\n }\n\n .banner-message h3 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n }\n\n .banner-message p {\n margin: 4px 0 0 0;\n font-size: 14px;\n }\n\n .help-text {\n font-style: italic;\n }\n\n .dismiss-button {\n background: transparent;\n border: none;\n cursor: pointer;\n font-size: 16px;\n color: var(--mj-text-secondary);\n padding: 4px;\n }\n\n .dismiss-button:hover {\n color: var(--mj-text-primary);\n }\n\n @keyframes slide-down {\n 0% {\n transform: translateY(-20px);\n opacity: 0;\n }\n 100% {\n transform: translateY(0);\n opacity: 1;\n }\n }\n "] }]
173
+ `, styles: ["\n .system-validation-wrapper {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 10000;\n pointer-events: none;\n }\n \n .system-validation-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: var(--mj-bg-overlay);\n z-index: 10001;\n pointer-events: auto;\n }\n \n .system-validation-container {\n position: fixed;\n top: 60px;\n left: 0;\n right: 0;\n z-index: 10002;\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 16px;\n max-height: 80vh;\n overflow-y: auto;\n pointer-events: auto;\n }\n\n .system-validation-banner {\n border-radius: 4px;\n box-shadow: var(--mj-shadow-md);\n padding: 16px;\n animation: slide-down 0.3s ease-out;\n }\n\n .system-validation-error {\n background-color: var(--mj-status-error-bg);\n border-left: 4px solid var(--mj-status-error);\n }\n\n .system-validation-warning {\n background-color: var(--mj-status-warning-bg);\n border-left: 4px solid var(--mj-status-warning);\n }\n\n .system-validation-info {\n background-color: var(--mj-status-info-bg);\n border-left: 4px solid var(--mj-status-info);\n }\n\n .banner-content {\n display: flex;\n align-items: flex-start;\n }\n\n .banner-icon {\n margin-right: 16px;\n font-size: 24px;\n }\n\n .severity-icon {\n font-size: 18px;\n }\n\n .severity-error {\n color: var(--mj-status-error);\n }\n\n .severity-warning {\n color: var(--mj-status-warning);\n }\n\n .severity-info {\n color: var(--mj-status-info);\n }\n\n .banner-message {\n flex: 1;\n }\n\n .banner-message h3 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n }\n\n .banner-message p {\n margin: 4px 0 0 0;\n font-size: 14px;\n }\n\n .help-text {\n font-style: italic;\n }\n\n .dismiss-button {\n background: transparent;\n border: none;\n cursor: pointer;\n font-size: 16px;\n color: var(--mj-text-secondary);\n padding: 4px;\n }\n\n .dismiss-button:hover {\n color: var(--mj-text-primary);\n }\n\n @keyframes slide-down {\n 0% {\n transform: translateY(-20px);\n opacity: 0;\n }\n 100% {\n transform: translateY(0);\n opacity: 1;\n }\n }\n "] }]
174
174
  }], () => [{ type: i1.SystemValidationService }], null); })();
175
175
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SystemValidationBannerComponent, { className: "SystemValidationBannerComponent", filePath: "src/lib/system-validation/system-validation-banner.component.ts", lineNumber: 186 }); })();
176
176
  //# sourceMappingURL=system-validation-banner.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"system-validation-banner.component.js","sourceRoot":"","sources":["../../../src/lib/system-validation/system-validation-banner.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAqB,MAAM,eAAe,CAAC;AAG7D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;;;;;IAWrC,yBAA6C;;;IAUnC,+BAA2C;IAAA,sBAAC;IAAA,iBAAO;;;IAGnD,+BAA6C;IAAA,4BAAE;IAAA,iBAAO;;;IAGtD,+BAA0C;IAAA,4BAAE;IAAA,iBAAO;;;IAMnD,yBAAG;IAAA,YAAmB;IAAA,iBAAI;;;IAAvB,cAAmB;IAAnB,sCAAmB;;;IAGtB,6BAAqB;IAAA,YAAgB;IAAA,iBAAI;;;IAApB,cAAgB;IAAhB,mCAAgB;;;;IAKrC,kCAAqF;IAA7E,wQAAS,gCAAsB,KAAC;IACtC,wBACF;IAAA,iBAAS;;;IAxBb,AADF,AAHF,8BAEoD,aACtB,aACD;IACvB,sHAAkC;IAGlC,sHAAoC;IAGpC,sHAAiC;IAGnC,iBAAM;IAEJ,AADF,8BAA4B,SACtB;IAAA,YAAmB;IAAA,iBAAK;IAC5B,gHAAqB;IAGrB,sHAAkB;IAGpB,iBAAM;IACN,gCAA0B;IACxB,2HAAkC;IAOxC,AADE,AADE,iBAAM,EACF,EACF;;;IA9BJ,kEAAiD;IAG7C,eAEC;IAFD,wDAEC;IACD,cAEC;IAFD,0DAEC;IACD,cAEC;IAFD,uDAEC;IAGG,eAAmB;IAAnB,sCAAmB;IACvB,cAEC;IAFD,2CAEC;IACD,cAEC;IAFD,yCAEC;IAGD,eAIC;IAJD,yDAIC;;;IApCb,8BAAuC;IAErC,+GAAiB;IAGjB,8BAAyC;IACvC,mIAkCC;IAEL,AADE,iBAAM,EACF;;;IAxCJ,cAEC;IAFD,2CAEC;IAEC,eAkCC;IAlCD,4BAkCC;;AAsIX,MAAM,OAAO,+BAA+B;IAKtB;IAJpB,MAAM,GAA4B,EAAE,CAAC;IACrC,SAAS,GAAG,KAAK,CAAC;IACV,YAAY,CAA2B;IAE/C,YAAoB,iBAA0C;QAA1C,sBAAiB,GAAjB,iBAAiB,CAAyB;IAAI,CAAC;IAEnE,QAAQ;QACN,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE;YAC9E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;YAErB,4CAA4C;YAC5C,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,WAAW;QACT,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;QAClC,CAAC;IACH,CAAC;IAED,YAAY,CAAC,EAAU;QACrB,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;yHAxBU,+BAA+B;6DAA/B,+BAA+B;YA/KxC,iGAAyB;;YAAzB,gDA4CC;4BA9CO,YAAY;;iFAiLX,+BAA+B;cApL3C,SAAS;2BACE,6BAA6B,cAC3B,IAAI,WACP,CAAC,YAAY,CAAC,YACb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA8CP;;kFAkIQ,+BAA+B","sourcesContent":["import { Component, OnInit, OnDestroy } from '@angular/core';\nimport { SystemValidationService, SystemValidationIssue } from '../services/system-validation.service';\nimport { Subscription } from 'rxjs';\nimport { CommonModule } from '@angular/common';\n\n@Component({\n selector: 'mj-system-validation-banner',\n standalone: true,\n imports: [CommonModule],\n template: `\n @if (issues.length > 0) {\n <div class=\"system-validation-wrapper\">\n <!-- Dark overlay for serious errors -->\n @if (hasErrors) {\n <div class=\"system-validation-overlay\"></div>\n }\n <div class=\"system-validation-container\">\n @for (issue of issues; track issue) {\n <div\n class=\"system-validation-banner\"\n [ngClass]=\"'system-validation-' + issue.severity\">\n <div class=\"banner-content\">\n <div class=\"banner-icon\">\n @if (issue.severity === 'error') {\n <span class=\"severity-icon severity-error\">❌</span>\n }\n @if (issue.severity === 'warning') {\n <span class=\"severity-icon severity-warning\">⚠️</span>\n }\n @if (issue.severity === 'info') {\n <span class=\"severity-icon severity-info\">ℹ️</span>\n }\n </div>\n <div class=\"banner-message\">\n <h3>{{ issue.message }}</h3>\n @if (issue.details) {\n <p>{{ issue.details }}</p>\n }\n @if (issue.help) {\n <p class=\"help-text\">{{ issue.help }}</p>\n }\n </div>\n <div class=\"banner-close\">\n @if (issue.severity !== 'error') {\n <button (click)=\"dismissIssue(issue.id)\" aria-label=\"Dismiss\" class=\"dismiss-button\">\n ✕\n </button>\n }\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n }\n `,\n styles: [`\n .system-validation-wrapper {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 10000;\n pointer-events: none;\n }\n \n .system-validation-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0, 0, 0, 0.5);\n z-index: 10001;\n pointer-events: auto;\n }\n \n .system-validation-container {\n position: fixed;\n top: 60px;\n left: 0;\n right: 0;\n z-index: 10002;\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 16px;\n max-height: 80vh;\n overflow-y: auto;\n pointer-events: auto;\n }\n\n .system-validation-banner {\n border-radius: 4px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n padding: 16px;\n animation: slide-down 0.3s ease-out;\n }\n\n .system-validation-error {\n background-color: #ffebee;\n border-left: 4px solid #f44336;\n }\n\n .system-validation-warning {\n background-color: #fff8e1;\n border-left: 4px solid #ff9800;\n }\n\n .system-validation-info {\n background-color: #e3f2fd;\n border-left: 4px solid #2196f3;\n }\n\n .banner-content {\n display: flex;\n align-items: flex-start;\n }\n\n .banner-icon {\n margin-right: 16px;\n font-size: 24px;\n }\n\n .severity-icon {\n font-size: 18px;\n }\n\n .severity-error {\n color: #f44336;\n }\n\n .severity-warning {\n color: #ff9800;\n }\n\n .severity-info {\n color: #2196f3;\n }\n\n .banner-message {\n flex: 1;\n }\n\n .banner-message h3 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n }\n\n .banner-message p {\n margin: 4px 0 0 0;\n font-size: 14px;\n }\n\n .help-text {\n font-style: italic;\n }\n\n .dismiss-button {\n background: transparent;\n border: none;\n cursor: pointer;\n font-size: 16px;\n color: var(--mj-text-secondary);\n padding: 4px;\n }\n\n .dismiss-button:hover {\n color: var(--mj-text-primary);\n }\n\n @keyframes slide-down {\n 0% {\n transform: translateY(-20px);\n opacity: 0;\n }\n 100% {\n transform: translateY(0);\n opacity: 1;\n }\n }\n `]\n})\nexport class SystemValidationBannerComponent implements OnInit, OnDestroy {\n issues: SystemValidationIssue[] = [];\n hasErrors = false;\n private subscription: Subscription | undefined;\n\n constructor(private validationService: SystemValidationService) { }\n\n ngOnInit() {\n this.subscription = this.validationService.validationIssues$.subscribe(issues => {\n this.issues = issues;\n \n // Check if there are any error-level issues\n this.hasErrors = issues.some(issue => issue.severity === 'error');\n });\n }\n\n ngOnDestroy() {\n if (this.subscription) {\n this.subscription.unsubscribe();\n }\n }\n\n dismissIssue(id: string) {\n this.validationService.removeIssue(id);\n }\n}"]}
1
+ {"version":3,"file":"system-validation-banner.component.js","sourceRoot":"","sources":["../../../src/lib/system-validation/system-validation-banner.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAqB,MAAM,eAAe,CAAC;AAG7D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;;;;;IAWrC,yBAA6C;;;IAUnC,+BAA2C;IAAA,sBAAC;IAAA,iBAAO;;;IAGnD,+BAA6C;IAAA,4BAAE;IAAA,iBAAO;;;IAGtD,+BAA0C;IAAA,4BAAE;IAAA,iBAAO;;;IAMnD,yBAAG;IAAA,YAAmB;IAAA,iBAAI;;;IAAvB,cAAmB;IAAnB,sCAAmB;;;IAGtB,6BAAqB;IAAA,YAAgB;IAAA,iBAAI;;;IAApB,cAAgB;IAAhB,mCAAgB;;;;IAKrC,kCAAqF;IAA7E,wQAAS,gCAAsB,KAAC;IACtC,wBACF;IAAA,iBAAS;;;IAxBb,AADF,AAHF,8BAEoD,aACtB,aACD;IACvB,sHAAkC;IAGlC,sHAAoC;IAGpC,sHAAiC;IAGnC,iBAAM;IAEJ,AADF,8BAA4B,SACtB;IAAA,YAAmB;IAAA,iBAAK;IAC5B,gHAAqB;IAGrB,sHAAkB;IAGpB,iBAAM;IACN,gCAA0B;IACxB,2HAAkC;IAOxC,AADE,AADE,iBAAM,EACF,EACF;;;IA9BJ,kEAAiD;IAG7C,eAEC;IAFD,wDAEC;IACD,cAEC;IAFD,0DAEC;IACD,cAEC;IAFD,uDAEC;IAGG,eAAmB;IAAnB,sCAAmB;IACvB,cAEC;IAFD,2CAEC;IACD,cAEC;IAFD,yCAEC;IAGD,eAIC;IAJD,yDAIC;;;IApCb,8BAAuC;IAErC,+GAAiB;IAGjB,8BAAyC;IACvC,mIAkCC;IAEL,AADE,iBAAM,EACF;;;IAxCJ,cAEC;IAFD,2CAEC;IAEC,eAkCC;IAlCD,4BAkCC;;AAsIX,MAAM,OAAO,+BAA+B;IAKtB;IAJpB,MAAM,GAA4B,EAAE,CAAC;IACrC,SAAS,GAAG,KAAK,CAAC;IACV,YAAY,CAA2B;IAE/C,YAAoB,iBAA0C;QAA1C,sBAAiB,GAAjB,iBAAiB,CAAyB;IAAI,CAAC;IAEnE,QAAQ;QACN,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE;YAC9E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;YAErB,4CAA4C;YAC5C,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,WAAW;QACT,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;QAClC,CAAC;IACH,CAAC;IAED,YAAY,CAAC,EAAU;QACrB,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;yHAxBU,+BAA+B;6DAA/B,+BAA+B;YA/KxC,iGAAyB;;YAAzB,gDA4CC;4BA9CO,YAAY;;iFAiLX,+BAA+B;cApL3C,SAAS;2BACE,6BAA6B,cAC3B,IAAI,WACP,CAAC,YAAY,CAAC,YACb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA8CP;;kFAkIQ,+BAA+B","sourcesContent":["import { Component, OnInit, OnDestroy } from '@angular/core';\nimport { SystemValidationService, SystemValidationIssue } from '../services/system-validation.service';\nimport { Subscription } from 'rxjs';\nimport { CommonModule } from '@angular/common';\n\n@Component({\n selector: 'mj-system-validation-banner',\n standalone: true,\n imports: [CommonModule],\n template: `\n @if (issues.length > 0) {\n <div class=\"system-validation-wrapper\">\n <!-- Dark overlay for serious errors -->\n @if (hasErrors) {\n <div class=\"system-validation-overlay\"></div>\n }\n <div class=\"system-validation-container\">\n @for (issue of issues; track issue) {\n <div\n class=\"system-validation-banner\"\n [ngClass]=\"'system-validation-' + issue.severity\">\n <div class=\"banner-content\">\n <div class=\"banner-icon\">\n @if (issue.severity === 'error') {\n <span class=\"severity-icon severity-error\">❌</span>\n }\n @if (issue.severity === 'warning') {\n <span class=\"severity-icon severity-warning\">⚠️</span>\n }\n @if (issue.severity === 'info') {\n <span class=\"severity-icon severity-info\">ℹ️</span>\n }\n </div>\n <div class=\"banner-message\">\n <h3>{{ issue.message }}</h3>\n @if (issue.details) {\n <p>{{ issue.details }}</p>\n }\n @if (issue.help) {\n <p class=\"help-text\">{{ issue.help }}</p>\n }\n </div>\n <div class=\"banner-close\">\n @if (issue.severity !== 'error') {\n <button (click)=\"dismissIssue(issue.id)\" aria-label=\"Dismiss\" class=\"dismiss-button\">\n ✕\n </button>\n }\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n }\n `,\n styles: [`\n .system-validation-wrapper {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 10000;\n pointer-events: none;\n }\n \n .system-validation-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: var(--mj-bg-overlay);\n z-index: 10001;\n pointer-events: auto;\n }\n \n .system-validation-container {\n position: fixed;\n top: 60px;\n left: 0;\n right: 0;\n z-index: 10002;\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 16px;\n max-height: 80vh;\n overflow-y: auto;\n pointer-events: auto;\n }\n\n .system-validation-banner {\n border-radius: 4px;\n box-shadow: var(--mj-shadow-md);\n padding: 16px;\n animation: slide-down 0.3s ease-out;\n }\n\n .system-validation-error {\n background-color: var(--mj-status-error-bg);\n border-left: 4px solid var(--mj-status-error);\n }\n\n .system-validation-warning {\n background-color: var(--mj-status-warning-bg);\n border-left: 4px solid var(--mj-status-warning);\n }\n\n .system-validation-info {\n background-color: var(--mj-status-info-bg);\n border-left: 4px solid var(--mj-status-info);\n }\n\n .banner-content {\n display: flex;\n align-items: flex-start;\n }\n\n .banner-icon {\n margin-right: 16px;\n font-size: 24px;\n }\n\n .severity-icon {\n font-size: 18px;\n }\n\n .severity-error {\n color: var(--mj-status-error);\n }\n\n .severity-warning {\n color: var(--mj-status-warning);\n }\n\n .severity-info {\n color: var(--mj-status-info);\n }\n\n .banner-message {\n flex: 1;\n }\n\n .banner-message h3 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n }\n\n .banner-message p {\n margin: 4px 0 0 0;\n font-size: 14px;\n }\n\n .help-text {\n font-style: italic;\n }\n\n .dismiss-button {\n background: transparent;\n border: none;\n cursor: pointer;\n font-size: 16px;\n color: var(--mj-text-secondary);\n padding: 4px;\n }\n\n .dismiss-button:hover {\n color: var(--mj-text-primary);\n }\n\n @keyframes slide-down {\n 0% {\n transform: translateY(-20px);\n opacity: 0;\n }\n 100% {\n transform: translateY(0);\n opacity: 1;\n }\n }\n `]\n})\nexport class SystemValidationBannerComponent implements OnInit, OnDestroy {\n issues: SystemValidationIssue[] = [];\n hasErrors = false;\n private subscription: Subscription | undefined;\n\n constructor(private validationService: SystemValidationService) { }\n\n ngOnInit() {\n this.subscription = this.validationService.validationIssues$.subscribe(issues => {\n this.issues = issues;\n \n // Check if there are any error-level issues\n this.hasErrors = issues.some(issue => issue.severity === 'error');\n });\n }\n\n ngOnDestroy() {\n if (this.subscription) {\n this.subscription.unsubscribe();\n }\n }\n\n dismissIssue(id: string) {\n this.validationService.removeIssue(id);\n }\n}"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-explorer-core",
3
- "version": "5.33.0",
3
+ "version": "5.34.1",
4
4
  "description": "MemberJunction Explorer: Core Angular Components",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -32,47 +32,47 @@
32
32
  "@angular/cdk": "21.1.3",
33
33
  "@angular/platform-browser": "21.1.3",
34
34
  "@auth0/auth0-angular": "^2.6.0",
35
- "@memberjunction/ai-core-plus": "5.33.0",
36
- "@memberjunction/ai-engine-base": "5.33.0",
37
- "@memberjunction/communication-types": "5.33.0",
38
- "@memberjunction/core": "5.33.0",
39
- "@memberjunction/core-entities": "5.33.0",
40
- "@memberjunction/entity-communications-client": "5.33.0",
41
- "@memberjunction/export-engine": "5.33.0",
42
- "@memberjunction/global": "5.33.0",
43
- "@memberjunction/graphql-dataprovider": "5.33.0",
44
- "@memberjunction/ng-ai-test-harness": "5.33.0",
45
- "@memberjunction/ng-artifacts": "5.33.0",
46
- "@memberjunction/ng-auth-services": "5.33.0",
47
- "@memberjunction/ng-base-application": "5.33.0",
48
- "@memberjunction/ng-base-forms": "5.33.0",
49
- "@memberjunction/ng-base-types": "5.33.0",
50
- "@memberjunction/ng-container-directives": "5.33.0",
51
- "@memberjunction/ng-conversations": "5.33.0",
52
- "@memberjunction/ng-dashboard-viewer": "5.33.0",
53
- "@memberjunction/ng-dashboards": "5.33.0",
54
- "@memberjunction/ng-entity-form-dialog": "5.33.0",
55
- "@memberjunction/ng-entity-permissions": "5.33.0",
56
- "@memberjunction/ng-entity-viewer": "5.33.0",
57
- "@memberjunction/ng-explorer-settings": "5.33.0",
58
- "@memberjunction/ng-export-service": "5.33.0",
59
- "@memberjunction/ng-feedback": "5.33.0",
60
- "@memberjunction/ng-file-storage": "5.33.0",
61
- "@memberjunction/ng-generic-dialog": "5.33.0",
62
- "@memberjunction/ng-list-detail-grid": "5.33.0",
63
- "@memberjunction/ng-notifications": "5.33.0",
64
- "@memberjunction/ng-query-viewer": "5.33.0",
65
- "@memberjunction/ng-record-changes": "5.33.0",
66
- "@memberjunction/ng-record-selector": "5.33.0",
67
- "@memberjunction/ng-record-tags": "5.33.0",
68
- "@memberjunction/ng-resource-permissions": "5.33.0",
69
- "@memberjunction/ng-search": "5.33.0",
70
- "@memberjunction/ng-shared": "5.33.0",
71
- "@memberjunction/ng-shared-generic": "5.33.0",
72
- "@memberjunction/ng-ui-components": "5.33.0",
73
- "@memberjunction/ng-user-avatar": "5.33.0",
74
- "@memberjunction/ng-word-cloud": "5.33.0",
75
- "@memberjunction/templates-base-types": "5.33.0",
35
+ "@memberjunction/ai-core-plus": "5.34.1",
36
+ "@memberjunction/ai-engine-base": "5.34.1",
37
+ "@memberjunction/communication-types": "5.34.1",
38
+ "@memberjunction/core": "5.34.1",
39
+ "@memberjunction/core-entities": "5.34.1",
40
+ "@memberjunction/entity-communications-client": "5.34.1",
41
+ "@memberjunction/export-engine": "5.34.1",
42
+ "@memberjunction/global": "5.34.1",
43
+ "@memberjunction/graphql-dataprovider": "5.34.1",
44
+ "@memberjunction/ng-ai-test-harness": "5.34.1",
45
+ "@memberjunction/ng-artifacts": "5.34.1",
46
+ "@memberjunction/ng-auth-services": "5.34.1",
47
+ "@memberjunction/ng-base-application": "5.34.1",
48
+ "@memberjunction/ng-base-forms": "5.34.1",
49
+ "@memberjunction/ng-base-types": "5.34.1",
50
+ "@memberjunction/ng-container-directives": "5.34.1",
51
+ "@memberjunction/ng-conversations": "5.34.1",
52
+ "@memberjunction/ng-dashboard-viewer": "5.34.1",
53
+ "@memberjunction/ng-dashboards": "5.34.1",
54
+ "@memberjunction/ng-entity-form-dialog": "5.34.1",
55
+ "@memberjunction/ng-entity-permissions": "5.34.1",
56
+ "@memberjunction/ng-entity-viewer": "5.34.1",
57
+ "@memberjunction/ng-explorer-settings": "5.34.1",
58
+ "@memberjunction/ng-export-service": "5.34.1",
59
+ "@memberjunction/ng-feedback": "5.34.1",
60
+ "@memberjunction/ng-file-storage": "5.34.1",
61
+ "@memberjunction/ng-generic-dialog": "5.34.1",
62
+ "@memberjunction/ng-list-detail-grid": "5.34.1",
63
+ "@memberjunction/ng-notifications": "5.34.1",
64
+ "@memberjunction/ng-query-viewer": "5.34.1",
65
+ "@memberjunction/ng-record-changes": "5.34.1",
66
+ "@memberjunction/ng-record-selector": "5.34.1",
67
+ "@memberjunction/ng-record-tags": "5.34.1",
68
+ "@memberjunction/ng-resource-permissions": "5.34.1",
69
+ "@memberjunction/ng-search": "5.34.1",
70
+ "@memberjunction/ng-shared": "5.34.1",
71
+ "@memberjunction/ng-shared-generic": "5.34.1",
72
+ "@memberjunction/ng-ui-components": "5.34.1",
73
+ "@memberjunction/ng-user-avatar": "5.34.1",
74
+ "@memberjunction/ng-word-cloud": "5.34.1",
75
+ "@memberjunction/templates-base-types": "5.34.1",
76
76
  "golden-layout": "^2.6.0",
77
77
  "rxjs": "^7.8.2",
78
78
  "tslib": "^2.8.1"