@memberjunction/ng-explorer-core 6.2.0-edge.5 → 6.2.0-edge.6
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 +7 -2
- package/dist/app-routing.module.d.ts.map +1 -1
- package/dist/app-routing.module.js +12 -4
- package/dist/app-routing.module.js.map +1 -1
- package/dist/generated/lazy-feature-config.d.ts +1 -1
- package/dist/generated/lazy-feature-config.d.ts.map +1 -1
- package/dist/generated/lazy-feature-config.js +4 -2
- package/dist/generated/lazy-feature-config.js.map +1 -1
- package/dist/lib/resource-wrappers/record-form-mode.d.ts +44 -0
- package/dist/lib/resource-wrappers/record-form-mode.d.ts.map +1 -0
- package/dist/lib/resource-wrappers/record-form-mode.js +47 -0
- package/dist/lib/resource-wrappers/record-form-mode.js.map +1 -0
- package/dist/lib/resource-wrappers/record-resource.component.d.ts +56 -1
- package/dist/lib/resource-wrappers/record-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/record-resource.component.js +124 -9
- package/dist/lib/resource-wrappers/record-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/standard-form-create.d.ts +4 -0
- package/dist/lib/resource-wrappers/standard-form-create.d.ts.map +1 -0
- package/dist/lib/resource-wrappers/standard-form-create.js +6 -0
- package/dist/lib/resource-wrappers/standard-form-create.js.map +1 -0
- package/dist/lib/resource-wrappers/view-resource.component.d.ts +18 -1
- package/dist/lib/resource-wrappers/view-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/view-resource.component.js +105 -39
- package/dist/lib/resource-wrappers/view-resource.component.js.map +1 -1
- package/dist/lib/shell/components/record-open/record-origin-crumb.component.d.ts.map +1 -1
- package/dist/lib/shell/components/record-open/record-origin-crumb.component.js +12 -8
- package/dist/lib/shell/components/record-open/record-origin-crumb.component.js.map +1 -1
- package/dist/lib/shell/components/tabs/component-cache-manager.d.ts +6 -0
- package/dist/lib/shell/components/tabs/component-cache-manager.d.ts.map +1 -1
- package/dist/lib/shell/components/tabs/component-cache-manager.js +18 -3
- package/dist/lib/shell/components/tabs/component-cache-manager.js.map +1 -1
- package/dist/lib/shell/components/tabs/tab-container.component.d.ts +2 -0
- package/dist/lib/shell/components/tabs/tab-container.component.d.ts.map +1 -1
- package/dist/lib/shell/components/tabs/tab-container.component.js +38 -10
- package/dist/lib/shell/components/tabs/tab-container.component.js.map +1 -1
- package/dist/lib/shell/shell-chrome-policy.d.ts +6 -0
- package/dist/lib/shell/shell-chrome-policy.d.ts.map +1 -1
- package/dist/lib/shell/shell-chrome-policy.js +1 -0
- package/dist/lib/shell/shell-chrome-policy.js.map +1 -1
- package/dist/lib/shell/shell.component.d.ts +23 -0
- package/dist/lib/shell/shell.component.d.ts.map +1 -1
- package/dist/lib/shell/shell.component.js +283 -71
- package/dist/lib/shell/shell.component.js.map +1 -1
- package/dist/lib/single-record/single-record.component.d.ts +27 -2
- package/dist/lib/single-record/single-record.component.d.ts.map +1 -1
- package/dist/lib/single-record/single-record.component.js +54 -8
- package/dist/lib/single-record/single-record.component.js.map +1 -1
- package/dist/lib/user-notifications/user-notifications.component.d.ts +10 -2
- package/dist/lib/user-notifications/user-notifications.component.d.ts.map +1 -1
- package/dist/lib/user-notifications/user-notifications.component.js +77 -22
- package/dist/lib/user-notifications/user-notifications.component.js.map +1 -1
- package/package.json +57 -57
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter, OnDestroy } from '@angular/core';
|
|
2
2
|
import { CompositeKey, BaseEntity } from '@memberjunction/core';
|
|
3
|
-
import { FormNavigationEvent, FormNotificationEvent } from '@memberjunction/ng-base-forms';
|
|
3
|
+
import { EntityFormMode, FormNavigationEvent, FormNotificationEvent } from '@memberjunction/ng-base-forms';
|
|
4
4
|
import type { BaseFormComponent, FormCompositionSnapshot } from '@memberjunction/ng-base-forms';
|
|
5
5
|
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
6
6
|
import * as i0 from "@angular/core";
|
|
@@ -22,6 +22,18 @@ export declare class SingleRecordComponent extends BaseAngularComponent implemen
|
|
|
22
22
|
PrimaryKey: CompositeKey;
|
|
23
23
|
entityName: string | null;
|
|
24
24
|
NewRecordValues: string | Record<string, unknown> | null;
|
|
25
|
+
/**
|
|
26
|
+
* `'standard'` asks the host for the CodeGen form even when a custom form is
|
|
27
|
+
* registered (MJ#4755). The mount-time mode; later changes go through
|
|
28
|
+
* {@link SwitchFormMode} so the host's unsaved-work guard applies.
|
|
29
|
+
*/
|
|
30
|
+
FormMode: EntityFormMode;
|
|
31
|
+
/**
|
|
32
|
+
* Re-emitted host `FormModeChange`: the mode changed inside the host — the
|
|
33
|
+
* strip, a {@link SwitchFormMode} call, or an interactive-variant pick
|
|
34
|
+
* (which returns to `'default'`).
|
|
35
|
+
*/
|
|
36
|
+
FormModeChange: EventEmitter<EntityFormMode>;
|
|
25
37
|
/** @deprecated Use {@link NewRecordValues}. */
|
|
26
38
|
set newRecordValues(value: string | Record<string, unknown> | null);
|
|
27
39
|
/** @deprecated Use {@link NewRecordValues}. */
|
|
@@ -68,7 +80,18 @@ export declare class SingleRecordComponent extends BaseAngularComponent implemen
|
|
|
68
80
|
* only route the snapshot has out to Explorer.
|
|
69
81
|
*/
|
|
70
82
|
CompositionChanged: EventEmitter<FormCompositionSnapshot>;
|
|
83
|
+
/**
|
|
84
|
+
* Emitted when the user clicks "Ask AI" in the record form toolbar.
|
|
85
|
+
* Also requests expansion of the floating chat overlay via ConversationsRuntime.
|
|
86
|
+
*/
|
|
87
|
+
AskAIRequested: EventEmitter<void>;
|
|
71
88
|
private formHost?;
|
|
89
|
+
/**
|
|
90
|
+
* Switch the mounted form between custom and standard (MJ#4755) through the
|
|
91
|
+
* host's guarded switch. False when refused (unsaved work — the host has
|
|
92
|
+
* already raised a warning) or when no host is mounted yet.
|
|
93
|
+
*/
|
|
94
|
+
SwitchFormMode(mode: EntityFormMode): boolean;
|
|
72
95
|
/**
|
|
73
96
|
* True while the hosted form is in edit mode. The host already exposes the
|
|
74
97
|
* live form instance, so this is a read, not a new event pipeline.
|
|
@@ -83,6 +106,7 @@ export declare class SingleRecordComponent extends BaseAngularComponent implemen
|
|
|
83
106
|
private formPresenter;
|
|
84
107
|
private recentAccessService;
|
|
85
108
|
private cdr;
|
|
109
|
+
constructor();
|
|
86
110
|
/**
|
|
87
111
|
* True once another user (another session) has deleted the record shown here.
|
|
88
112
|
* The tab is NOT closed under the user — they may be reading it — so a banner
|
|
@@ -116,6 +140,7 @@ export declare class SingleRecordComponent extends BaseAngularComponent implemen
|
|
|
116
140
|
/** @deprecated Use {@link OnSaved}. */
|
|
117
141
|
onSaved(record: BaseEntity): void;
|
|
118
142
|
OnEditModeChanged(editing: boolean): void;
|
|
143
|
+
OnAskAIRequested(): void;
|
|
119
144
|
OnNotification(event: FormNotificationEvent): void;
|
|
120
145
|
/** @deprecated Use {@link OnNotification}. */
|
|
121
146
|
onNotification(event: FormNotificationEvent): void;
|
|
@@ -124,6 +149,6 @@ export declare class SingleRecordComponent extends BaseAngularComponent implemen
|
|
|
124
149
|
/** @deprecated Use {@link HandleNavigation}. */
|
|
125
150
|
handleNavigation(event: FormNavigationEvent): void;
|
|
126
151
|
static ɵfac: i0.ɵɵFactoryDeclaration<SingleRecordComponent, never>;
|
|
127
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<SingleRecordComponent, "mj-single-record", never, { "PrimaryKey": { "alias": "PrimaryKey"; "required": false; }; "entityName": { "alias": "entityName"; "required": false; }; "NewRecordValues": { "alias": "NewRecordValues"; "required": false; }; "newRecordValues": { "alias": "newRecordValues"; "required": false; }; }, { "LoadComplete": "LoadComplete"; "loadComplete": "loadComplete"; "RecordSaved": "RecordSaved"; "recordSaved": "recordSaved"; "RecordDismissed": "RecordDismissed"; "recordDismissed": "recordDismissed"; "EditModeChanged": "EditModeChanged"; "CompositionChanged": "CompositionChanged"; }, never, never, false, never>;
|
|
152
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SingleRecordComponent, "mj-single-record", never, { "PrimaryKey": { "alias": "PrimaryKey"; "required": false; }; "entityName": { "alias": "entityName"; "required": false; }; "NewRecordValues": { "alias": "NewRecordValues"; "required": false; }; "FormMode": { "alias": "FormMode"; "required": false; }; "newRecordValues": { "alias": "newRecordValues"; "required": false; }; }, { "FormModeChange": "FormModeChange"; "LoadComplete": "LoadComplete"; "loadComplete": "loadComplete"; "RecordSaved": "RecordSaved"; "recordSaved": "recordSaved"; "RecordDismissed": "RecordDismissed"; "recordDismissed": "recordDismissed"; "EditModeChanged": "EditModeChanged"; "CompositionChanged": "CompositionChanged"; "AskAIRequested": "AskAIRequested"; }, never, never, false, never>;
|
|
128
153
|
}
|
|
129
154
|
//# sourceMappingURL=single-record.component.d.ts.map
|
|
@@ -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,EAAgC,YAAY,EAAiB,SAAS,EAAqB,MAAM,eAAe,CAAC;AACxH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,
|
|
1
|
+
{"version":3,"file":"single-record.component.d.ts","sourceRoot":"","sources":["../../../src/lib/single-record/single-record.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,YAAY,EAAiB,SAAS,EAAqB,MAAM,eAAe,CAAC;AACxH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,qBAAqB,EAAoF,MAAM,+BAA+B,CAAC;AAC7L,OAAO,KAAK,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAIhG,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;AAGrE;;;;;;;;;;;;;GAaG;AACH,qBAMa,qBAAsB,SAAQ,oBAAqB,YAAW,SAAS;IAClE,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;IAC9E;;;;OAIG;IACa,QAAQ,EAAE,cAAc,CAAa;IAErD;;;;OAIG;IACc,cAAc,EAAE,YAAY,CAAC,cAAc,CAAC,CAAsC;IAEnG,+CAA+C;IAC/C,IAAoB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EAEjF;IACD,+CAA+C;IAC/C,IAAW,eAAe,IAAI,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAEpE;IAEgB,YAAY,EAAE,YAAY,CAAC,IAAI,CAAC,CAA4B;IAE7E;;;;;;OAMG;IACc,YAAY,qBAAqB;IACjC,WAAW,EAAE,YAAY,CAAC,UAAU,CAAC,CAAkC;IAExF;;;;;;OAMG;IACc,WAAW,oCAAoB;IAChD,wFAAwF;IACvE,eAAe,EAAE,YAAY,CAAC,IAAI,CAAC,CAA4B;IAEhF;;;;;;OAMG;IACc,eAAe,qBAAwB;IAExD;;;;OAIG;IACc,eAAe,EAAE,YAAY,CAAC,OAAO,CAAC,CAA+B;IAEtF;;;;;;OAMG;IACc,kBAAkB,EAAE,YAAY,CAAC,uBAAuB,CAAC,CAA+C;IAEzH;;;OAGG;IACc,cAAc,EAAE,YAAY,CAAC,IAAI,CAAC,CAA4B;IAEzC,OAAO,CAAC,QAAQ,CAAC,CAA4B;IAEnF;;;;OAIG;IACI,cAAc,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO;IAIpD;;;;;;;OAOG;IACI,SAAS,IAAI,OAAO;IAI3B,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,aAAa,CAAkC;IACvD,OAAO,CAAC,mBAAmB,CAA6B;IACxD,OAAO,CAAC,GAAG,CAA6B;;IAaxC;;;;;OAKG;IACI,eAAe,UAAS;IAE/B,OAAO,CAAC,eAAe,CACqC;IAE5D,yEAAyE;IACzE,oBAAoB,IAAI,IAAI;IAI5B,OAAO,CAAC,aAAa;IAcrB,uEAAuE;IACvE,cAAc,IAAI,IAAI;IAItB,8CAA8C;IAC9C,cAAc,IAAI,IAAI;IAItB,OAAO,CAAC,cAAc,CAA6B;IAEnD;;;;;;OAMG;IACH,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI;IAU5C,WAAW,IAAI,IAAI;IAOnB,uEAAuE;IACvE,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAMvC,6CAA6C;IAC7C,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAIvC,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAIjC,uCAAuC;IACvC,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAIjC,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIzC,gBAAgB,IAAI,IAAI;IAKxB,cAAc,CAAC,KAAK,EAAE,qBAAqB,GAAG,IAAI;IAIlD,8CAA8C;IAC9C,cAAc,CAAC,KAAK,EAAE,qBAAqB,GAAG,IAAI;IAIlD,4EAA4E;IAC5E,gBAAgB,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI;IAyClD,gDAAgD;IAChD,gBAAgB,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI;yCA7QvC,qBAAqB;2CAArB,qBAAqB;CAgRjC"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { ChangeDetectorRef, Component, EventEmitter, inject, Input, Output, ViewChild } from '@angular/core';
|
|
2
2
|
import { CompositeKey } from '@memberjunction/core';
|
|
3
|
-
import { MJFormPresenterService, MjEntityFormHostComponent } from '@memberjunction/ng-base-forms';
|
|
3
|
+
import { MJFormPresenterService, MjEntityFormHostComponent, MjRecordPreviewPopoverService } from '@memberjunction/ng-base-forms';
|
|
4
4
|
import { GetDeletedRecordFromEvent, NavigationService, RecentAccessService, RecordIdentityMatches, SafeDetectChanges, SharedService } from '@memberjunction/ng-shared';
|
|
5
5
|
import { MJGlobal } from '@memberjunction/global';
|
|
6
6
|
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
7
|
+
import { ConversationsRuntime } from '@memberjunction/conversations-runtime';
|
|
7
8
|
import * as i0 from "@angular/core";
|
|
8
9
|
import * as i1 from "@memberjunction/ng-base-forms";
|
|
9
10
|
import * as i2 from "@memberjunction/ng-ui-components";
|
|
@@ -32,6 +33,18 @@ export class SingleRecordComponent extends BaseAngularComponent {
|
|
|
32
33
|
PrimaryKey = new CompositeKey();
|
|
33
34
|
entityName = '';
|
|
34
35
|
NewRecordValues = '';
|
|
36
|
+
/**
|
|
37
|
+
* `'standard'` asks the host for the CodeGen form even when a custom form is
|
|
38
|
+
* registered (MJ#4755). The mount-time mode; later changes go through
|
|
39
|
+
* {@link SwitchFormMode} so the host's unsaved-work guard applies.
|
|
40
|
+
*/
|
|
41
|
+
FormMode = 'default';
|
|
42
|
+
/**
|
|
43
|
+
* Re-emitted host `FormModeChange`: the mode changed inside the host — the
|
|
44
|
+
* strip, a {@link SwitchFormMode} call, or an interactive-variant pick
|
|
45
|
+
* (which returns to `'default'`).
|
|
46
|
+
*/
|
|
47
|
+
FormModeChange = new EventEmitter();
|
|
35
48
|
/** @deprecated Use {@link NewRecordValues}. */
|
|
36
49
|
set newRecordValues(value) {
|
|
37
50
|
this.NewRecordValues = value;
|
|
@@ -82,7 +95,20 @@ export class SingleRecordComponent extends BaseAngularComponent {
|
|
|
82
95
|
* only route the snapshot has out to Explorer.
|
|
83
96
|
*/
|
|
84
97
|
CompositionChanged = new EventEmitter();
|
|
98
|
+
/**
|
|
99
|
+
* Emitted when the user clicks "Ask AI" in the record form toolbar.
|
|
100
|
+
* Also requests expansion of the floating chat overlay via ConversationsRuntime.
|
|
101
|
+
*/
|
|
102
|
+
AskAIRequested = new EventEmitter();
|
|
85
103
|
formHost;
|
|
104
|
+
/**
|
|
105
|
+
* Switch the mounted form between custom and standard (MJ#4755) through the
|
|
106
|
+
* host's guarded switch. False when refused (unsaved work — the host has
|
|
107
|
+
* already raised a warning) or when no host is mounted yet.
|
|
108
|
+
*/
|
|
109
|
+
SwitchFormMode(mode) {
|
|
110
|
+
return this.formHost?.SwitchFormMode(mode) ?? false;
|
|
111
|
+
}
|
|
86
112
|
/**
|
|
87
113
|
* True while the hosted form is in edit mode. The host already exposes the
|
|
88
114
|
* live form instance, so this is a read, not a new event pipeline.
|
|
@@ -99,6 +125,16 @@ export class SingleRecordComponent extends BaseAngularComponent {
|
|
|
99
125
|
formPresenter = inject(MJFormPresenterService);
|
|
100
126
|
recentAccessService = new RecentAccessService();
|
|
101
127
|
cdr = inject(ChangeDetectorRef);
|
|
128
|
+
constructor() {
|
|
129
|
+
super();
|
|
130
|
+
if (!MjRecordPreviewPopoverService.FallbackNavigationHandler) {
|
|
131
|
+
MjRecordPreviewPopoverService.FallbackNavigationHandler = (event) => {
|
|
132
|
+
if (event.Kind === 'record') {
|
|
133
|
+
this.navigationService.OpenEntityRecord(event.EntityName, event.PrimaryKey, { forceNewTab: event.OpenInNewTab });
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
102
138
|
/**
|
|
103
139
|
* True once another user (another session) has deleted the record shown here.
|
|
104
140
|
* The tab is NOT closed under the user — they may be reading it — so a banner
|
|
@@ -174,6 +210,10 @@ export class SingleRecordComponent extends BaseAngularComponent {
|
|
|
174
210
|
OnEditModeChanged(editing) {
|
|
175
211
|
this.EditModeChanged.emit(editing);
|
|
176
212
|
}
|
|
213
|
+
OnAskAIRequested() {
|
|
214
|
+
this.AskAIRequested.emit();
|
|
215
|
+
ConversationsRuntime.Instance.Bridge.RequestExpandOverlay();
|
|
216
|
+
}
|
|
177
217
|
OnNotification(event) {
|
|
178
218
|
this.sharedService.CreateSimpleNotification(event.Message, event.Type, event.Duration);
|
|
179
219
|
}
|
|
@@ -226,32 +266,36 @@ export class SingleRecordComponent extends BaseAngularComponent {
|
|
|
226
266
|
handleNavigation(event) {
|
|
227
267
|
return this.HandleNavigation(event);
|
|
228
268
|
}
|
|
229
|
-
static ɵfac =
|
|
269
|
+
static ɵfac = function SingleRecordComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SingleRecordComponent)(); };
|
|
230
270
|
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SingleRecordComponent, selectors: [["mj-single-record"]], viewQuery: function SingleRecordComponent_Query(rf, ctx) { if (rf & 1) {
|
|
231
271
|
i0.ɵɵviewQuery(MjEntityFormHostComponent, 5);
|
|
232
272
|
} if (rf & 2) {
|
|
233
273
|
let _t;
|
|
234
274
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.formHost = _t.first);
|
|
235
|
-
} }, inputs: { PrimaryKey: "PrimaryKey", entityName: "entityName", NewRecordValues: "NewRecordValues", newRecordValues: "newRecordValues" }, outputs: { LoadComplete: "LoadComplete", loadComplete: "loadComplete", RecordSaved: "RecordSaved", recordSaved: "recordSaved", RecordDismissed: "RecordDismissed", recordDismissed: "recordDismissed", EditModeChanged: "EditModeChanged", CompositionChanged: "CompositionChanged" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 2, vars:
|
|
275
|
+
} }, inputs: { PrimaryKey: "PrimaryKey", entityName: "entityName", NewRecordValues: "NewRecordValues", FormMode: "FormMode", newRecordValues: "newRecordValues" }, outputs: { FormModeChange: "FormModeChange", LoadComplete: "LoadComplete", loadComplete: "loadComplete", RecordSaved: "RecordSaved", recordSaved: "recordSaved", RecordDismissed: "RecordDismissed", recordDismissed: "recordDismissed", EditModeChanged: "EditModeChanged", CompositionChanged: "CompositionChanged", AskAIRequested: "AskAIRequested" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 2, vars: 6, consts: [["Variant", "warning", "Title", "This record was deleted by another user", "Message", "It no longer exists. What you see here is the last copy that was loaded.", 1, "single-record-deleted-banner"], [3, "FormModeChange", "LoadComplete", "LoadError", "RecordReady", "Saved", "Navigate", "Notification", "EditModeChanged", "AskAIRequested", "Dismissed", "FormCreated", "EntityName", "PrimaryKey", "NewRecordValues", "FormMode", "Provider"], ["actions", "", "mjButton", "", "Variant", "secondary", "Size", "sm", 3, "click"]], template: function SingleRecordComponent_Template(rf, ctx) { if (rf & 1) {
|
|
236
276
|
i0.ɵɵconditionalCreate(0, SingleRecordComponent_Conditional_0_Template, 3, 0, "mj-alert", 0);
|
|
237
277
|
i0.ɵɵelementStart(1, "mj-entity-form-host", 1);
|
|
238
|
-
i0.ɵɵlistener("LoadComplete", function SingleRecordComponent_Template_mj_entity_form_host_LoadComplete_1_listener() { return ctx.onLoadComplete(); })("LoadError", function SingleRecordComponent_Template_mj_entity_form_host_LoadError_1_listener() { return ctx.onLoadComplete(); })("RecordReady", function SingleRecordComponent_Template_mj_entity_form_host_RecordReady_1_listener($event) { return ctx.onRecordReady($event); })("Saved", function SingleRecordComponent_Template_mj_entity_form_host_Saved_1_listener($event) { return ctx.onSaved($event); })("Navigate", function SingleRecordComponent_Template_mj_entity_form_host_Navigate_1_listener($event) { return ctx.handleNavigation($event); })("Notification", function SingleRecordComponent_Template_mj_entity_form_host_Notification_1_listener($event) { return ctx.onNotification($event); })("EditModeChanged", function SingleRecordComponent_Template_mj_entity_form_host_EditModeChanged_1_listener($event) { return ctx.OnEditModeChanged($event); })("Dismissed", function SingleRecordComponent_Template_mj_entity_form_host_Dismissed_1_listener() { return ctx.recordDismissed.emit(); })("FormCreated", function SingleRecordComponent_Template_mj_entity_form_host_FormCreated_1_listener($event) { return ctx.onFormCreated($event); });
|
|
278
|
+
i0.ɵɵlistener("FormModeChange", function SingleRecordComponent_Template_mj_entity_form_host_FormModeChange_1_listener($event) { return ctx.FormModeChange.emit($event); })("LoadComplete", function SingleRecordComponent_Template_mj_entity_form_host_LoadComplete_1_listener() { return ctx.onLoadComplete(); })("LoadError", function SingleRecordComponent_Template_mj_entity_form_host_LoadError_1_listener() { return ctx.onLoadComplete(); })("RecordReady", function SingleRecordComponent_Template_mj_entity_form_host_RecordReady_1_listener($event) { return ctx.onRecordReady($event); })("Saved", function SingleRecordComponent_Template_mj_entity_form_host_Saved_1_listener($event) { return ctx.onSaved($event); })("Navigate", function SingleRecordComponent_Template_mj_entity_form_host_Navigate_1_listener($event) { return ctx.handleNavigation($event); })("Notification", function SingleRecordComponent_Template_mj_entity_form_host_Notification_1_listener($event) { return ctx.onNotification($event); })("EditModeChanged", function SingleRecordComponent_Template_mj_entity_form_host_EditModeChanged_1_listener($event) { return ctx.OnEditModeChanged($event); })("AskAIRequested", function SingleRecordComponent_Template_mj_entity_form_host_AskAIRequested_1_listener() { return ctx.OnAskAIRequested(); })("Dismissed", function SingleRecordComponent_Template_mj_entity_form_host_Dismissed_1_listener() { return ctx.recordDismissed.emit(); })("FormCreated", function SingleRecordComponent_Template_mj_entity_form_host_FormCreated_1_listener($event) { return ctx.onFormCreated($event); });
|
|
239
279
|
i0.ɵɵelementEnd();
|
|
240
280
|
} if (rf & 2) {
|
|
241
281
|
i0.ɵɵconditional(ctx.DeletedRemotely ? 0 : -1);
|
|
242
282
|
i0.ɵɵadvance();
|
|
243
|
-
i0.ɵɵproperty("EntityName", ctx.entityName)("PrimaryKey", ctx.PrimaryKey)("NewRecordValues", ctx.newRecordValues)("Provider", ctx.Provider);
|
|
283
|
+
i0.ɵɵproperty("EntityName", ctx.entityName)("PrimaryKey", ctx.PrimaryKey)("NewRecordValues", ctx.newRecordValues)("FormMode", ctx.FormMode)("Provider", ctx.Provider);
|
|
244
284
|
} }, dependencies: [i1.MjEntityFormHostComponent, i2.MJButtonDirective, i2.MJAlertComponent], styles: ["[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n}\n\n\nmj-entity-form-host[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-height: 0;\n}\n\n.single-record-deleted-banner[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n margin: var(--mj-space-3) var(--mj-space-4) 0;\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}"] });
|
|
245
285
|
}
|
|
246
286
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SingleRecordComponent, [{
|
|
247
287
|
type: Component,
|
|
248
|
-
args: [{ standalone: false, selector: 'mj-single-record', template: "@if (DeletedRemotely) {\n <mj-alert\n class=\"single-record-deleted-banner\"\n Variant=\"warning\"\n Title=\"This record was deleted by another user\"\n Message=\"It no longer exists. What you see here is the last copy that was loaded.\">\n <button actions mjButton Variant=\"secondary\" Size=\"sm\" (click)=\"OnCloseDeletedRecord()\">Close</button>\n </mj-alert>\n}\n<mj-entity-form-host\n [EntityName]=\"entityName\"\n [PrimaryKey]=\"PrimaryKey\"\n [NewRecordValues]=\"newRecordValues\"\n [Provider]=\"Provider\"\n (LoadComplete)=\"onLoadComplete()\"\n (LoadError)=\"onLoadComplete()\"\n (RecordReady)=\"onRecordReady($event)\"\n (Saved)=\"onSaved($event)\"\n (Navigate)=\"handleNavigation($event)\"\n (Notification)=\"onNotification($event)\"\n (EditModeChanged)=\"OnEditModeChanged($event)\"\n (Dismissed)=\"recordDismissed.emit()\"\n (FormCreated)=\"onFormCreated($event)\">\n</mj-entity-form-host>\n", styles: [":host {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n}\n\n/* The form fills what the remote-delete banner leaves. */\nmj-entity-form-host {\n flex: 1 1 auto;\n min-height: 0;\n}\n\n.single-record-deleted-banner {\n flex: 0 0 auto;\n margin: var(--mj-space-3) var(--mj-space-4) 0;\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"] }]
|
|
249
|
-
}],
|
|
288
|
+
args: [{ standalone: false, selector: 'mj-single-record', template: "@if (DeletedRemotely) {\n <mj-alert\n class=\"single-record-deleted-banner\"\n Variant=\"warning\"\n Title=\"This record was deleted by another user\"\n Message=\"It no longer exists. What you see here is the last copy that was loaded.\">\n <button actions mjButton Variant=\"secondary\" Size=\"sm\" (click)=\"OnCloseDeletedRecord()\">Close</button>\n </mj-alert>\n}\n<mj-entity-form-host\n [EntityName]=\"entityName\"\n [PrimaryKey]=\"PrimaryKey\"\n [NewRecordValues]=\"newRecordValues\"\n [FormMode]=\"FormMode\"\n (FormModeChange)=\"FormModeChange.emit($event)\"\n [Provider]=\"Provider\"\n (LoadComplete)=\"onLoadComplete()\"\n (LoadError)=\"onLoadComplete()\"\n (RecordReady)=\"onRecordReady($event)\"\n (Saved)=\"onSaved($event)\"\n (Navigate)=\"handleNavigation($event)\"\n (Notification)=\"onNotification($event)\"\n (EditModeChanged)=\"OnEditModeChanged($event)\"\n (AskAIRequested)=\"OnAskAIRequested()\"\n (Dismissed)=\"recordDismissed.emit()\"\n (FormCreated)=\"onFormCreated($event)\">\n</mj-entity-form-host>\n", styles: [":host {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n}\n\n/* The form fills what the remote-delete banner leaves. */\nmj-entity-form-host {\n flex: 1 1 auto;\n min-height: 0;\n}\n\n.single-record-deleted-banner {\n flex: 0 0 auto;\n margin: var(--mj-space-3) var(--mj-space-4) 0;\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"] }]
|
|
289
|
+
}], () => [], { PrimaryKey: [{
|
|
250
290
|
type: Input
|
|
251
291
|
}], entityName: [{
|
|
252
292
|
type: Input
|
|
253
293
|
}], NewRecordValues: [{
|
|
254
294
|
type: Input
|
|
295
|
+
}], FormMode: [{
|
|
296
|
+
type: Input
|
|
297
|
+
}], FormModeChange: [{
|
|
298
|
+
type: Output
|
|
255
299
|
}], newRecordValues: [{
|
|
256
300
|
type: Input
|
|
257
301
|
}], LoadComplete: [{
|
|
@@ -270,9 +314,11 @@ export class SingleRecordComponent extends BaseAngularComponent {
|
|
|
270
314
|
type: Output
|
|
271
315
|
}], CompositionChanged: [{
|
|
272
316
|
type: Output
|
|
317
|
+
}], AskAIRequested: [{
|
|
318
|
+
type: Output
|
|
273
319
|
}], formHost: [{
|
|
274
320
|
type: ViewChild,
|
|
275
321
|
args: [MjEntityFormHostComponent]
|
|
276
322
|
}] }); })();
|
|
277
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SingleRecordComponent, { className: "SingleRecordComponent", filePath: "src/lib/single-record/single-record.component.ts", lineNumber:
|
|
323
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SingleRecordComponent, { className: "SingleRecordComponent", filePath: "src/lib/single-record/single-record.component.ts", lineNumber: 31 }); })();
|
|
278
324
|
//# sourceMappingURL=single-record.component.js.map
|
|
@@ -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 { ChangeDetectorRef, Component, EventEmitter, inject, Input, OnDestroy, Output, ViewChild } from '@angular/core';\nimport { CompositeKey, BaseEntity } from '@memberjunction/core';\nimport { FormNavigationEvent, FormNotificationEvent, MJFormPresenterService, MjEntityFormHostComponent } from '@memberjunction/ng-base-forms';\nimport type { BaseFormComponent, FormCompositionSnapshot } from '@memberjunction/ng-base-forms';\nimport { Subscription } from 'rxjs';\nimport { GetDeletedRecordFromEvent, NavigationService, RecentAccessService, RecordIdentityMatches, SafeDetectChanges, SharedService } from '@memberjunction/ng-shared';\nimport { MJEvent, MJGlobal } from '@memberjunction/global';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\n\n/**\n * Explorer-side host for a single entity record in the main tab area.\n *\n * This is now a **thin wrapper** around the Generic `<mj-entity-form-host>`\n * (in `@memberjunction/ng-base-forms`), which owns all the mechanics: resolving\n * the form (class / custom / interactive override + variants), loading the\n * record, dynamically creating the form, binding it, and tearing it down.\n *\n * SingleRecordComponent's only remaining job is the **Explorer mapping**:\n * translating the host's framework-agnostic events into Explorer services —\n * `Navigate` → {@link NavigationService}, `Notification` → {@link SharedService},\n * record loads → {@link RecentAccessService} — none of which belong in a Generic\n * component.\n */\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 OnDestroy {\n @Input() public PrimaryKey: CompositeKey = new CompositeKey();\n @Input() public entityName: string | null = '';\n @Input() public NewRecordValues: string | Record<string, unknown> | null = '';\n\n /** @deprecated Use {@link NewRecordValues}. */\n @Input() public set newRecordValues(value: string | Record<string, unknown> | null) {\n this.NewRecordValues = value;\n }\n /** @deprecated Use {@link NewRecordValues}. */\n public get newRecordValues(): string | Record<string, unknown> | null {\n return this.NewRecordValues;\n }\n\n @Output() public LoadComplete: EventEmitter<void> = new EventEmitter<void>();\n\n /**\n * @deprecated Use {@link LoadComplete}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (loadComplete) keeps working. Must stay AFTER LoadComplete: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() public loadComplete = this.LoadComplete;\n @Output() public RecordSaved: EventEmitter<BaseEntity> = new EventEmitter<BaseEntity>();\n\n /**\n * @deprecated Use {@link RecordSaved}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (recordSaved) keeps working. Must stay AFTER RecordSaved: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() public recordSaved = this.RecordSaved;\n /** Emitted when the hosted form asks to be dismissed (e.g. Discard on a new record). */\n @Output() public RecordDismissed: EventEmitter<void> = new EventEmitter<void>();\n\n /**\n * @deprecated Use {@link RecordDismissed}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (recordDismissed) keeps working. Must stay AFTER RecordDismissed: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() public recordDismissed = this.RecordDismissed;\n\n /**\n * Edit mode of the hosted form started (true) or ended (false). Relayed from\n * `<mj-entity-form-host>` so the resource wrapper can tell the shell to\n * promote this tab. {@link IsEditing} remains as the synchronous fallback.\n */\n @Output() public EditModeChanged: EventEmitter<boolean> = new EventEmitter<boolean>();\n\n /**\n * The live form's composition — sections, related grids, contributions, and the slots\n * the form actually emits. Re-emitted on every chrome resolve.\n *\n * The form is created dynamically inside `<mj-entity-form-host>`, so this relay is the\n * only route the snapshot has out to Explorer.\n */\n @Output() public CompositionChanged: EventEmitter<FormCompositionSnapshot> = new EventEmitter<FormCompositionSnapshot>();\n\n @ViewChild(MjEntityFormHostComponent) private formHost?: MjEntityFormHostComponent;\n\n /**\n * True while the hosted form is in edit mode. The host already exposes the\n * live form instance, so this is a read, not a new event pipeline.\n *\n * MJ forms are read-only until the user explicitly clicks Edit, so edit mode\n * is a deliberate gesture and a sound proxy for \"there is work in here worth\n * protecting\" — which is all the preview-tab replacement guard needs.\n */\n public IsEditing(): boolean {\n return this.formHost?.Form?.EditMode === true;\n }\n\n private navigationService = inject(NavigationService);\n private sharedService = inject(SharedService);\n private formPresenter = inject(MJFormPresenterService);\n private recentAccessService = new RecentAccessService();\n private cdr = inject(ChangeDetectorRef);\n\n /**\n * True once another user (another session) has deleted the record shown here.\n * The tab is NOT closed under the user — they may be reading it — so a banner\n * says so and offers to close it. Deletes in this session are handled by the\n * shell, which closes the tab outright.\n */\n public DeletedRemotely = false;\n\n private remoteDeleteSub: Subscription | null = MJGlobal.Instance.GetEventListener()\n .subscribe((event: MJEvent) => this.onGlobalEvent(event));\n\n /** Close this record's tab (the remote-delete banner's Close button). */\n OnCloseDeletedRecord(): void {\n this.RecordDismissed.emit();\n }\n\n private onGlobalEvent(event: MJEvent): void {\n if (this.DeletedRemotely) {\n return;\n }\n const deleted = GetDeletedRecordFromEvent(event);\n if (!deleted?.Remote || !this.PrimaryKey?.HasValue) {\n return;\n }\n if (RecordIdentityMatches(this.entityName, this.PrimaryKey.ToURLSegment(), deleted)) {\n this.DeletedRemotely = true;\n SafeDetectChanges(this.cdr);\n }\n }\n\n /** Unblock the shell's first-resource-load gate (success or error). */\n OnLoadComplete(): void {\n this.LoadComplete.emit();\n }\n\n /** @deprecated Use {@link OnLoadComplete}. */\n onLoadComplete(): void {\n return this.OnLoadComplete();\n }\n\n private compositionSub: Subscription | null = null;\n\n /**\n * Bind to the newly created form's composition stream.\n *\n * The previous subscription is dropped first: the host re-creates the form when the\n * user switches variants, and a retained subscription would let the replaced form\n * keep publishing over the live one.\n */\n onFormCreated(form: BaseFormComponent): void {\n this.compositionSub?.unsubscribe();\n this.compositionSub = form.CompositionChanged.subscribe((snapshot: FormCompositionSnapshot) =>\n this.CompositionChanged.emit(snapshot),\n );\n if (form.CompositionSnapshot) {\n this.CompositionChanged.emit(form.CompositionSnapshot);\n }\n }\n\n ngOnDestroy(): void {\n this.compositionSub?.unsubscribe();\n this.compositionSub = null;\n this.remoteDeleteSub?.unsubscribe();\n this.remoteDeleteSub = null;\n }\n\n /** Log access for existing records once the form's record is ready. */\n OnRecordReady(record: BaseEntity): void {\n if (record?.IsSaved) {\n this.recentAccessService.logAccess(record.EntityInfo.Name, record.PrimaryKey, 'record');\n }\n }\n\n /** @deprecated Use {@link OnRecordReady}. */\n onRecordReady(record: BaseEntity): void {\n return this.OnRecordReady(record);\n }\n\n OnSaved(record: BaseEntity): void {\n this.RecordSaved.emit(record);\n }\n\n /** @deprecated Use {@link OnSaved}. */\n onSaved(record: BaseEntity): void {\n return this.OnSaved(record);\n }\n\n OnEditModeChanged(editing: boolean): void {\n this.EditModeChanged.emit(editing);\n }\n\n OnNotification(event: FormNotificationEvent): void {\n this.sharedService.CreateSimpleNotification(event.Message, event.Type, event.Duration);\n }\n\n /** @deprecated Use {@link OnNotification}. */\n onNotification(event: FormNotificationEvent): void {\n return this.OnNotification(event);\n }\n\n /** Map the form's navigation requests onto Explorer's NavigationService. */\n 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 related record from inside an open form: force a new tab so the\n // parent record stays intact in single-resource mode.\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 case 'dismiss':\n this.RecordDismissed.emit();\n break;\n case 'create-related': {\n // A FK field wants a new related record created. Open the related entity's form\n // as a dialog/slide-in (prefilled), then hand the saved record back so the field\n // can select it.\n const ref = this.formPresenter.Open({\n EntityName: event.EntityName,\n Presentation: event.Presentation ?? 'dialog',\n NewRecordValues: event.NewRecordValues,\n Provider: event.Provider,\n });\n ref.AfterSaved().then(created => event.Complete(created));\n break;\n }\n }\n }\n\n /** @deprecated Use {@link HandleNavigation}. */\n handleNavigation(event: FormNavigationEvent): void {\n return this.HandleNavigation(event);\n }\n}\n","@if (DeletedRemotely) {\n <mj-alert\n class=\"single-record-deleted-banner\"\n Variant=\"warning\"\n Title=\"This record was deleted by another user\"\n Message=\"It no longer exists. What you see here is the last copy that was loaded.\">\n <button actions mjButton Variant=\"secondary\" Size=\"sm\" (click)=\"OnCloseDeletedRecord()\">Close</button>\n </mj-alert>\n}\n<mj-entity-form-host\n [EntityName]=\"entityName\"\n [PrimaryKey]=\"PrimaryKey\"\n [NewRecordValues]=\"newRecordValues\"\n [Provider]=\"Provider\"\n (LoadComplete)=\"onLoadComplete()\"\n (LoadError)=\"onLoadComplete()\"\n (RecordReady)=\"onRecordReady($event)\"\n (Saved)=\"onSaved($event)\"\n (Navigate)=\"handleNavigation($event)\"\n (Notification)=\"onNotification($event)\"\n (EditModeChanged)=\"OnEditModeChanged($event)\"\n (Dismissed)=\"recordDismissed.emit()\"\n (FormCreated)=\"onFormCreated($event)\">\n</mj-entity-form-host>\n"]}
|
|
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 { ChangeDetectorRef, Component, EventEmitter, inject, Input, OnDestroy, Output, ViewChild } from '@angular/core';\nimport { CompositeKey, BaseEntity } from '@memberjunction/core';\nimport { EntityFormMode, FormNavigationEvent, FormNotificationEvent, MJFormPresenterService, MjEntityFormHostComponent, MjRecordPreviewPopoverService } from '@memberjunction/ng-base-forms';\nimport type { BaseFormComponent, FormCompositionSnapshot } from '@memberjunction/ng-base-forms';\nimport { Subscription } from 'rxjs';\nimport { GetDeletedRecordFromEvent, NavigationService, RecentAccessService, RecordIdentityMatches, SafeDetectChanges, SharedService } from '@memberjunction/ng-shared';\nimport { MJEvent, MJGlobal } from '@memberjunction/global';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { ConversationsRuntime } from '@memberjunction/conversations-runtime';\n\n/**\n * Explorer-side host for a single entity record in the main tab area.\n *\n * This is now a **thin wrapper** around the Generic `<mj-entity-form-host>`\n * (in `@memberjunction/ng-base-forms`), which owns all the mechanics: resolving\n * the form (class / custom / interactive override + variants), loading the\n * record, dynamically creating the form, binding it, and tearing it down.\n *\n * SingleRecordComponent's only remaining job is the **Explorer mapping**:\n * translating the host's framework-agnostic events into Explorer services —\n * `Navigate` → {@link NavigationService}, `Notification` → {@link SharedService},\n * record loads → {@link RecentAccessService} — none of which belong in a Generic\n * component.\n */\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 OnDestroy {\n @Input() public PrimaryKey: CompositeKey = new CompositeKey();\n @Input() public entityName: string | null = '';\n @Input() public NewRecordValues: string | Record<string, unknown> | null = '';\n /**\n * `'standard'` asks the host for the CodeGen form even when a custom form is\n * registered (MJ#4755). The mount-time mode; later changes go through\n * {@link SwitchFormMode} so the host's unsaved-work guard applies.\n */\n @Input() public FormMode: EntityFormMode = 'default';\n\n /**\n * Re-emitted host `FormModeChange`: the mode changed inside the host — the\n * strip, a {@link SwitchFormMode} call, or an interactive-variant pick\n * (which returns to `'default'`).\n */\n @Output() public FormModeChange: EventEmitter<EntityFormMode> = new EventEmitter<EntityFormMode>();\n\n /** @deprecated Use {@link NewRecordValues}. */\n @Input() public set newRecordValues(value: string | Record<string, unknown> | null) {\n this.NewRecordValues = value;\n }\n /** @deprecated Use {@link NewRecordValues}. */\n public get newRecordValues(): string | Record<string, unknown> | null {\n return this.NewRecordValues;\n }\n\n @Output() public LoadComplete: EventEmitter<void> = new EventEmitter<void>();\n\n /**\n * @deprecated Use {@link LoadComplete}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (loadComplete) keeps working. Must stay AFTER LoadComplete: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() public loadComplete = this.LoadComplete;\n @Output() public RecordSaved: EventEmitter<BaseEntity> = new EventEmitter<BaseEntity>();\n\n /**\n * @deprecated Use {@link RecordSaved}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (recordSaved) keeps working. Must stay AFTER RecordSaved: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() public recordSaved = this.RecordSaved;\n /** Emitted when the hosted form asks to be dismissed (e.g. Discard on a new record). */\n @Output() public RecordDismissed: EventEmitter<void> = new EventEmitter<void>();\n\n /**\n * @deprecated Use {@link RecordDismissed}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (recordDismissed) keeps working. Must stay AFTER RecordDismissed: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() public recordDismissed = this.RecordDismissed;\n\n /**\n * Edit mode of the hosted form started (true) or ended (false). Relayed from\n * `<mj-entity-form-host>` so the resource wrapper can tell the shell to\n * promote this tab. {@link IsEditing} remains as the synchronous fallback.\n */\n @Output() public EditModeChanged: EventEmitter<boolean> = new EventEmitter<boolean>();\n\n /**\n * The live form's composition — sections, related grids, contributions, and the slots\n * the form actually emits. Re-emitted on every chrome resolve.\n *\n * The form is created dynamically inside `<mj-entity-form-host>`, so this relay is the\n * only route the snapshot has out to Explorer.\n */\n @Output() public CompositionChanged: EventEmitter<FormCompositionSnapshot> = new EventEmitter<FormCompositionSnapshot>();\n\n /**\n * Emitted when the user clicks \"Ask AI\" in the record form toolbar.\n * Also requests expansion of the floating chat overlay via ConversationsRuntime.\n */\n @Output() public AskAIRequested: EventEmitter<void> = new EventEmitter<void>();\n\n @ViewChild(MjEntityFormHostComponent) private formHost?: MjEntityFormHostComponent;\n\n /**\n * Switch the mounted form between custom and standard (MJ#4755) through the\n * host's guarded switch. False when refused (unsaved work — the host has\n * already raised a warning) or when no host is mounted yet.\n */\n public SwitchFormMode(mode: EntityFormMode): boolean {\n return this.formHost?.SwitchFormMode(mode) ?? false;\n }\n\n /**\n * True while the hosted form is in edit mode. The host already exposes the\n * live form instance, so this is a read, not a new event pipeline.\n *\n * MJ forms are read-only until the user explicitly clicks Edit, so edit mode\n * is a deliberate gesture and a sound proxy for \"there is work in here worth\n * protecting\" — which is all the preview-tab replacement guard needs.\n */\n public IsEditing(): boolean {\n return this.formHost?.Form?.EditMode === true;\n }\n\n private navigationService = inject(NavigationService);\n private sharedService = inject(SharedService);\n private formPresenter = inject(MJFormPresenterService);\n private recentAccessService = new RecentAccessService();\n private cdr = inject(ChangeDetectorRef);\n\n constructor() {\n super();\n if (!MjRecordPreviewPopoverService.FallbackNavigationHandler) {\n MjRecordPreviewPopoverService.FallbackNavigationHandler = (event: FormNavigationEvent) => {\n if (event.Kind === 'record') {\n this.navigationService.OpenEntityRecord(event.EntityName, event.PrimaryKey, { forceNewTab: event.OpenInNewTab });\n }\n };\n }\n }\n\n /**\n * True once another user (another session) has deleted the record shown here.\n * The tab is NOT closed under the user — they may be reading it — so a banner\n * says so and offers to close it. Deletes in this session are handled by the\n * shell, which closes the tab outright.\n */\n public DeletedRemotely = false;\n\n private remoteDeleteSub: Subscription | null = MJGlobal.Instance.GetEventListener()\n .subscribe((event: MJEvent) => this.onGlobalEvent(event));\n\n /** Close this record's tab (the remote-delete banner's Close button). */\n OnCloseDeletedRecord(): void {\n this.RecordDismissed.emit();\n }\n\n private onGlobalEvent(event: MJEvent): void {\n if (this.DeletedRemotely) {\n return;\n }\n const deleted = GetDeletedRecordFromEvent(event);\n if (!deleted?.Remote || !this.PrimaryKey?.HasValue) {\n return;\n }\n if (RecordIdentityMatches(this.entityName, this.PrimaryKey.ToURLSegment(), deleted)) {\n this.DeletedRemotely = true;\n SafeDetectChanges(this.cdr);\n }\n }\n\n /** Unblock the shell's first-resource-load gate (success or error). */\n OnLoadComplete(): void {\n this.LoadComplete.emit();\n }\n\n /** @deprecated Use {@link OnLoadComplete}. */\n onLoadComplete(): void {\n return this.OnLoadComplete();\n }\n\n private compositionSub: Subscription | null = null;\n\n /**\n * Bind to the newly created form's composition stream.\n *\n * The previous subscription is dropped first: the host re-creates the form when the\n * user switches variants, and a retained subscription would let the replaced form\n * keep publishing over the live one.\n */\n onFormCreated(form: BaseFormComponent): void {\n this.compositionSub?.unsubscribe();\n this.compositionSub = form.CompositionChanged.subscribe((snapshot: FormCompositionSnapshot) =>\n this.CompositionChanged.emit(snapshot),\n );\n if (form.CompositionSnapshot) {\n this.CompositionChanged.emit(form.CompositionSnapshot);\n }\n }\n\n ngOnDestroy(): void {\n this.compositionSub?.unsubscribe();\n this.compositionSub = null;\n this.remoteDeleteSub?.unsubscribe();\n this.remoteDeleteSub = null;\n }\n\n /** Log access for existing records once the form's record is ready. */\n OnRecordReady(record: BaseEntity): void {\n if (record?.IsSaved) {\n this.recentAccessService.logAccess(record.EntityInfo.Name, record.PrimaryKey, 'record');\n }\n }\n\n /** @deprecated Use {@link OnRecordReady}. */\n onRecordReady(record: BaseEntity): void {\n return this.OnRecordReady(record);\n }\n\n OnSaved(record: BaseEntity): void {\n this.RecordSaved.emit(record);\n }\n\n /** @deprecated Use {@link OnSaved}. */\n onSaved(record: BaseEntity): void {\n return this.OnSaved(record);\n }\n\n OnEditModeChanged(editing: boolean): void {\n this.EditModeChanged.emit(editing);\n }\n\n OnAskAIRequested(): void {\n this.AskAIRequested.emit();\n ConversationsRuntime.Instance.Bridge.RequestExpandOverlay();\n }\n\n OnNotification(event: FormNotificationEvent): void {\n this.sharedService.CreateSimpleNotification(event.Message, event.Type, event.Duration);\n }\n\n /** @deprecated Use {@link OnNotification}. */\n onNotification(event: FormNotificationEvent): void {\n return this.OnNotification(event);\n }\n\n /** Map the form's navigation requests onto Explorer's NavigationService. */\n 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 related record from inside an open form: force a new tab so the\n // parent record stays intact in single-resource mode.\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 case 'dismiss':\n this.RecordDismissed.emit();\n break;\n case 'create-related': {\n // A FK field wants a new related record created. Open the related entity's form\n // as a dialog/slide-in (prefilled), then hand the saved record back so the field\n // can select it.\n const ref = this.formPresenter.Open({\n EntityName: event.EntityName,\n Presentation: event.Presentation ?? 'dialog',\n NewRecordValues: event.NewRecordValues,\n Provider: event.Provider,\n });\n ref.AfterSaved().then(created => event.Complete(created));\n break;\n }\n }\n }\n\n /** @deprecated Use {@link HandleNavigation}. */\n handleNavigation(event: FormNavigationEvent): void {\n return this.HandleNavigation(event);\n }\n}\n","@if (DeletedRemotely) {\n <mj-alert\n class=\"single-record-deleted-banner\"\n Variant=\"warning\"\n Title=\"This record was deleted by another user\"\n Message=\"It no longer exists. What you see here is the last copy that was loaded.\">\n <button actions mjButton Variant=\"secondary\" Size=\"sm\" (click)=\"OnCloseDeletedRecord()\">Close</button>\n </mj-alert>\n}\n<mj-entity-form-host\n [EntityName]=\"entityName\"\n [PrimaryKey]=\"PrimaryKey\"\n [NewRecordValues]=\"newRecordValues\"\n [FormMode]=\"FormMode\"\n (FormModeChange)=\"FormModeChange.emit($event)\"\n [Provider]=\"Provider\"\n (LoadComplete)=\"onLoadComplete()\"\n (LoadError)=\"onLoadComplete()\"\n (RecordReady)=\"onRecordReady($event)\"\n (Saved)=\"onSaved($event)\"\n (Navigate)=\"handleNavigation($event)\"\n (Notification)=\"onNotification($event)\"\n (EditModeChanged)=\"OnEditModeChanged($event)\"\n (AskAIRequested)=\"OnAskAIRequested()\"\n (Dismissed)=\"recordDismissed.emit()\"\n (FormCreated)=\"onFormCreated($event)\">\n</mj-entity-form-host>\n"]}
|
|
@@ -49,6 +49,7 @@ export declare class UserNotificationsComponent extends BaseAngularComponent imp
|
|
|
49
49
|
private navigationService;
|
|
50
50
|
private appManager;
|
|
51
51
|
private cdr;
|
|
52
|
+
private ngZone;
|
|
52
53
|
RadioSelected: ReadFilterOption;
|
|
53
54
|
/** @deprecated Use {@link RadioSelected}. */
|
|
54
55
|
get radioSelected(): ReadFilterOption;
|
|
@@ -89,11 +90,18 @@ export declare class UserNotificationsComponent extends BaseAngularComponent imp
|
|
|
89
90
|
ngAfterViewInit(): void;
|
|
90
91
|
ngOnDestroy(): void;
|
|
91
92
|
private loadNotificationTypes;
|
|
93
|
+
private notificationsToShowCache;
|
|
94
|
+
private notificationsToShowKey;
|
|
95
|
+
private unreadNotificationsCache;
|
|
96
|
+
private unreadNotificationsKey;
|
|
97
|
+
private readNotificationsCache;
|
|
98
|
+
private readNotificationsKey;
|
|
99
|
+
private computeAllNotificationsSignature;
|
|
92
100
|
get NotificationsToShow(): MJUserNotificationEntity[];
|
|
93
101
|
/**
|
|
94
102
|
* The filtered notifications as render-ready day groups. Memoized on a composite
|
|
95
|
-
* key of the inputs so the getter stays
|
|
96
|
-
* the (time-sensitive) VM snapshots never rebuild mid-pass.
|
|
103
|
+
* key of the inputs BEFORE filtering `AllNotifications`, so the getter stays $O(1)$
|
|
104
|
+
* across change-detection passes and the (time-sensitive) VM snapshots never rebuild mid-pass.
|
|
97
105
|
*/
|
|
98
106
|
get Groups(): NotificationGroup[];
|
|
99
107
|
private buildGroups;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-notifications.component.d.ts","sourceRoot":"","sources":["../../../src/lib/user-notifications/user-notifications.component.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"user-notifications.component.d.ts","sourceRoot":"","sources":["../../../src/lib/user-notifications/user-notifications.component.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component, ChangeDetectorRef, inject } from '@angular/core';
|
|
1
|
+
import { Component, ChangeDetectorRef, NgZone, inject } from '@angular/core';
|
|
2
2
|
import { SharedService } from '@memberjunction/ng-shared';
|
|
3
3
|
import { MJUserNotificationEntity, UserInfoEngine } from '@memberjunction/core-entities';
|
|
4
4
|
import { CompositeKey } from '@memberjunction/core';
|
|
@@ -368,6 +368,7 @@ export class UserNotificationsComponent extends BaseAngularComponent {
|
|
|
368
368
|
navigationService;
|
|
369
369
|
appManager;
|
|
370
370
|
cdr = inject(ChangeDetectorRef);
|
|
371
|
+
ngZone = inject(NgZone, { optional: true });
|
|
371
372
|
RadioSelected = 'All';
|
|
372
373
|
/** @deprecated Use {@link RadioSelected}. */
|
|
373
374
|
get radioSelected() {
|
|
@@ -436,13 +437,34 @@ export class UserNotificationsComponent extends BaseAngularComponent {
|
|
|
436
437
|
async ngOnInit() {
|
|
437
438
|
this.loadNotificationTypes();
|
|
438
439
|
// Refresh the snapshot relative-time labels between change-detection passes.
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
440
|
+
// Run outside NgZone so the 60s timer only triggers Angular CD when a label actually changes.
|
|
441
|
+
const startTimer = () => {
|
|
442
|
+
this.relativeTimer = setInterval(() => {
|
|
443
|
+
let anyChanged = false;
|
|
444
|
+
for (const vm of this.vmCache.values()) {
|
|
445
|
+
const next = this.relativeTime(vm.N.__mj_CreatedAt);
|
|
446
|
+
if (vm.Relative !== next) {
|
|
447
|
+
vm.Relative = next;
|
|
448
|
+
anyChanged = true;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
this.groupsKey = ''; // day buckets can shift at midnight
|
|
452
|
+
if (anyChanged) {
|
|
453
|
+
if (this.ngZone) {
|
|
454
|
+
this.ngZone.run(() => this.cdr.markForCheck());
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
this.cdr.markForCheck();
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}, RELATIVE_REFRESH_MS);
|
|
461
|
+
};
|
|
462
|
+
if (this.ngZone) {
|
|
463
|
+
this.ngZone.runOutsideAngular(startTimer);
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
startTimer();
|
|
467
|
+
}
|
|
446
468
|
}
|
|
447
469
|
ngAfterViewInit() {
|
|
448
470
|
this.SharedService.InvokeManualResize(); // make sure the notifications component is sized correctly
|
|
@@ -468,17 +490,38 @@ export class UserNotificationsComponent extends BaseAngularComponent {
|
|
|
468
490
|
// ========================================================================
|
|
469
491
|
// Data shaping (filter → view models → day groups)
|
|
470
492
|
// ========================================================================
|
|
493
|
+
notificationsToShowCache = [];
|
|
494
|
+
notificationsToShowKey = '';
|
|
495
|
+
unreadNotificationsCache = [];
|
|
496
|
+
unreadNotificationsKey = '';
|
|
497
|
+
readNotificationsCache = [];
|
|
498
|
+
readNotificationsKey = '';
|
|
499
|
+
computeAllNotificationsSignature() {
|
|
500
|
+
const all = this.AllNotifications;
|
|
501
|
+
let unreadCount = 0;
|
|
502
|
+
for (let i = 0; i < all.length; i++) {
|
|
503
|
+
if (all[i].Unread)
|
|
504
|
+
unreadCount++;
|
|
505
|
+
}
|
|
506
|
+
const baseKey = `${all.length}|${unreadCount}|${all[0]?.ID ?? ''}|${all[all.length - 1]?.ID ?? ''}`;
|
|
507
|
+
return { all, unreadCount, baseKey };
|
|
508
|
+
}
|
|
471
509
|
get NotificationsToShow() {
|
|
510
|
+
const { all, baseKey } = this.computeAllNotificationsSignature();
|
|
511
|
+
const key = `${baseKey}|${this.RadioSelected}|${this.SelectedTypeFilter ?? ''}|${this.CurrentFilter}`;
|
|
512
|
+
if (key === this.notificationsToShowKey) {
|
|
513
|
+
return this.notificationsToShowCache;
|
|
514
|
+
}
|
|
472
515
|
let temp = [];
|
|
473
516
|
switch (this.RadioSelected) {
|
|
474
517
|
case 'All':
|
|
475
|
-
temp =
|
|
518
|
+
temp = all;
|
|
476
519
|
break;
|
|
477
520
|
case 'Unread':
|
|
478
|
-
temp =
|
|
521
|
+
temp = all.filter(n => n.Unread);
|
|
479
522
|
break;
|
|
480
523
|
case 'Read':
|
|
481
|
-
temp =
|
|
524
|
+
temp = all.filter(n => !n.Unread);
|
|
482
525
|
break;
|
|
483
526
|
}
|
|
484
527
|
// Apply type filter if selected
|
|
@@ -486,24 +529,26 @@ export class UserNotificationsComponent extends BaseAngularComponent {
|
|
|
486
529
|
temp = temp.filter(n => UUIDsEqual(n.NotificationTypeID, this.SelectedTypeFilter));
|
|
487
530
|
}
|
|
488
531
|
// Apply text filter if it is not empty
|
|
489
|
-
|
|
532
|
+
const filterLower = this.CurrentFilter.trim().toLowerCase();
|
|
533
|
+
if (filterLower.length > 0) {
|
|
490
534
|
// check for inclusion of filter value in title or message
|
|
491
|
-
temp = temp.filter(n => n.Title?.toLowerCase().includes(
|
|
492
|
-
n.Message?.toLowerCase().includes(
|
|
535
|
+
temp = temp.filter(n => n.Title?.toLowerCase().includes(filterLower) ||
|
|
536
|
+
n.Message?.toLowerCase().includes(filterLower));
|
|
493
537
|
}
|
|
538
|
+
this.notificationsToShowKey = key;
|
|
539
|
+
this.notificationsToShowCache = temp;
|
|
494
540
|
return temp;
|
|
495
541
|
}
|
|
496
542
|
/**
|
|
497
543
|
* The filtered notifications as render-ready day groups. Memoized on a composite
|
|
498
|
-
* key of the inputs so the getter stays
|
|
499
|
-
* the (time-sensitive) VM snapshots never rebuild mid-pass.
|
|
544
|
+
* key of the inputs BEFORE filtering `AllNotifications`, so the getter stays $O(1)$
|
|
545
|
+
* across change-detection passes and the (time-sensitive) VM snapshots never rebuild mid-pass.
|
|
500
546
|
*/
|
|
501
547
|
get Groups() {
|
|
502
|
-
const
|
|
503
|
-
const
|
|
504
|
-
const key = `${list.length}|${unread}|${this.RadioSelected}|${this.SelectedTypeFilter}|${this.CurrentFilter}|${list[0]?.ID ?? ''}|${list[list.length - 1]?.ID ?? ''}`;
|
|
548
|
+
const { baseKey } = this.computeAllNotificationsSignature();
|
|
549
|
+
const key = `${baseKey}|${this.RadioSelected}|${this.SelectedTypeFilter ?? ''}|${this.CurrentFilter}`;
|
|
505
550
|
if (key !== this.groupsKey) {
|
|
506
|
-
this.groups = this.buildGroups(
|
|
551
|
+
this.groups = this.buildGroups(this.NotificationsToShow);
|
|
507
552
|
this.groupsKey = key;
|
|
508
553
|
}
|
|
509
554
|
return this.groups;
|
|
@@ -718,10 +763,20 @@ export class UserNotificationsComponent extends BaseAngularComponent {
|
|
|
718
763
|
return SharedService.UserNotifications;
|
|
719
764
|
}
|
|
720
765
|
get UnreadNotifications() {
|
|
721
|
-
|
|
766
|
+
const { all, baseKey } = this.computeAllNotificationsSignature();
|
|
767
|
+
if (baseKey !== this.unreadNotificationsKey) {
|
|
768
|
+
this.unreadNotificationsKey = baseKey;
|
|
769
|
+
this.unreadNotificationsCache = all.filter(n => n.Unread);
|
|
770
|
+
}
|
|
771
|
+
return this.unreadNotificationsCache;
|
|
722
772
|
}
|
|
723
773
|
get ReadNotifications() {
|
|
724
|
-
|
|
774
|
+
const { all, baseKey } = this.computeAllNotificationsSignature();
|
|
775
|
+
if (baseKey !== this.readNotificationsKey) {
|
|
776
|
+
this.readNotificationsKey = baseKey;
|
|
777
|
+
this.readNotificationsCache = all.filter(n => !n.Unread);
|
|
778
|
+
}
|
|
779
|
+
return this.readNotificationsCache;
|
|
725
780
|
}
|
|
726
781
|
SelectReadOption(option) {
|
|
727
782
|
this.RadioSelected = option;
|