@memberjunction/ng-explorer-core 6.2.0-edge.4 → 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 +20 -10
- 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 +6 -3
- package/dist/generated/lazy-feature-config.js.map +1 -1
- package/dist/lib/oauth/oauth-callback.component.d.ts +2 -0
- package/dist/lib/oauth/oauth-callback.component.d.ts.map +1 -1
- package/dist/lib/oauth/oauth-callback.component.js +10 -10
- package/dist/lib/oauth/oauth-callback.component.js.map +1 -1
- package/dist/lib/resource-wrappers/chat-conversations-resource.component.d.ts +17 -3
- package/dist/lib/resource-wrappers/chat-conversations-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/chat-conversations-resource.component.js +211 -13
- package/dist/lib/resource-wrappers/chat-conversations-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/chat-tasks-resource.component.d.ts +12 -3
- package/dist/lib/resource-wrappers/chat-tasks-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/chat-tasks-resource.component.js +18 -3
- package/dist/lib/resource-wrappers/chat-tasks-resource.component.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/search-results-resource.component.d.ts +3 -3
- package/dist/lib/resource-wrappers/search-results-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/search-results-resource.component.js +5 -10
- package/dist/lib/resource-wrappers/search-results-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/server-connectivity/server-connectivity-banner.component.d.ts +16 -3
- package/dist/lib/server-connectivity/server-connectivity-banner.component.d.ts.map +1 -1
- package/dist/lib/server-connectivity/server-connectivity-banner.component.js +17 -3
- package/dist/lib/server-connectivity/server-connectivity-banner.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 +36 -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 +188 -47
- package/dist/lib/shell/components/tabs/tab-container.component.js.map +1 -1
- package/dist/lib/shell/deep-link-workspace.d.ts +25 -0
- package/dist/lib/shell/deep-link-workspace.d.ts.map +1 -0
- package/dist/lib/shell/deep-link-workspace.js +49 -0
- package/dist/lib/shell/deep-link-workspace.js.map +1 -0
- 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 +35 -0
- package/dist/lib/shell/shell.component.d.ts.map +1 -1
- package/dist/lib/shell/shell.component.js +348 -78
- package/dist/lib/shell/shell.component.js.map +1 -1
- package/dist/lib/single-record/single-record.component.d.ts +39 -2
- package/dist/lib/single-record/single-record.component.d.ts.map +1 -1
- package/dist/lib/single-record/single-record.component.js +99 -12
- 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,10 +1,11 @@
|
|
|
1
|
-
import { OnDestroy } from '@angular/core';
|
|
1
|
+
import { OnDestroy, Signal } from '@angular/core';
|
|
2
2
|
import { AnimationEvent } from '@angular/animations';
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
/**
|
|
5
5
|
* When visible, this banner sets --mj-connectivity-banner-height on <html>
|
|
6
6
|
* so that any position:fixed element referencing the shell header offset
|
|
7
|
-
* (e.g. app-switcher mobile dropdown) can account for the banner.
|
|
7
|
+
* (e.g. app-switcher mobile dropdown) can account for the banner. Template
|
|
8
|
+
* bindings read the same height from `HeightPx` instead (MJ #4377).
|
|
8
9
|
*/
|
|
9
10
|
export declare class ServerConnectivityBannerComponent implements OnDestroy {
|
|
10
11
|
private static readonly CSS_VAR;
|
|
@@ -25,7 +26,19 @@ export declare class ServerConnectivityBannerComponent implements OnDestroy {
|
|
|
25
26
|
* `toSignal` also owns the subscription lifetime via `DestroyRef`, so there is no manual
|
|
26
27
|
* `Subscription` to unsubscribe.
|
|
27
28
|
*/
|
|
28
|
-
readonly IsConnected:
|
|
29
|
+
readonly IsConnected: Signal<boolean>;
|
|
30
|
+
private readonly heightPx;
|
|
31
|
+
/**
|
|
32
|
+
* Rendered height (px) of the banner, 0 while it is hidden. Published together with the
|
|
33
|
+
* `--mj-connectivity-banner-height` custom property on `<html>`.
|
|
34
|
+
*
|
|
35
|
+
* CSS should keep using the custom property; a template binding must use this signal. A
|
|
36
|
+
* binding that reads the custom property is invisible to change detection: the height is
|
|
37
|
+
* published from the animation-done listener, which can fire while views are refreshing,
|
|
38
|
+
* so the reading view is never re-checked and dev mode reports NG0100 on every later pass
|
|
39
|
+
* (MJ #4377). Reading a signal makes the view a consumer, so the write re-refreshes it.
|
|
40
|
+
*/
|
|
41
|
+
readonly HeightPx: Signal<number>;
|
|
29
42
|
ngOnDestroy(): void;
|
|
30
43
|
/** After the enter/leave animation finishes, publish the banner height as a CSS custom property */
|
|
31
44
|
OnAnimationDone(event: AnimationEvent): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-connectivity-banner.component.d.ts","sourceRoot":"","sources":["../../../src/lib/server-connectivity/server-connectivity-banner.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,SAAS,
|
|
1
|
+
{"version":3,"file":"server-connectivity-banner.component.d.ts","sourceRoot":"","sources":["../../../src/lib/server-connectivity/server-connectivity-banner.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,SAAS,EAAE,MAAM,EAAkB,MAAM,eAAe,CAAC;AAE7E,OAAO,EAAuC,cAAc,EAAE,MAAM,qBAAqB,CAAC;;AAG1F;;;;;GAKG;AACH,qBA0Da,iCAAkC,YAAW,SAAS;IACjE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAEpE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqC;IAEzE;;;;;;;;;;;;;;;OAeG;IACH,SAAgB,WAAW,kBAA2E;IAEtG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAa;IACtC;;;;;;;;;OASG;IACH,SAAgB,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAA8B;IAEtE,WAAW,IAAI,IAAI;IAInB,mGAAmG;IACnG,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAS5C,OAAO,CAAC,eAAe;yCAlDZ,iCAAiC;2CAAjC,iCAAiC;CAyD7C"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component, inject } from '@angular/core';
|
|
1
|
+
import { Component, inject, signal } from '@angular/core';
|
|
2
2
|
import { toSignal } from '@angular/core/rxjs-interop';
|
|
3
3
|
import { trigger, transition, style, animate } from '@angular/animations';
|
|
4
4
|
import { ServerConnectivityService } from '../services/server-connectivity.service';
|
|
@@ -17,7 +17,8 @@ function ServerConnectivityBannerComponent_Conditional_0_Template(rf, ctx) { if
|
|
|
17
17
|
/**
|
|
18
18
|
* When visible, this banner sets --mj-connectivity-banner-height on <html>
|
|
19
19
|
* so that any position:fixed element referencing the shell header offset
|
|
20
|
-
* (e.g. app-switcher mobile dropdown) can account for the banner.
|
|
20
|
+
* (e.g. app-switcher mobile dropdown) can account for the banner. Template
|
|
21
|
+
* bindings read the same height from `HeightPx` instead (MJ #4377).
|
|
21
22
|
*/
|
|
22
23
|
export class ServerConnectivityBannerComponent {
|
|
23
24
|
static CSS_VAR = '--mj-connectivity-banner-height';
|
|
@@ -39,6 +40,18 @@ export class ServerConnectivityBannerComponent {
|
|
|
39
40
|
* `Subscription` to unsubscribe.
|
|
40
41
|
*/
|
|
41
42
|
IsConnected = toSignal(this.connectivityService.IsConnected$, { initialValue: true });
|
|
43
|
+
heightPx = signal(0, ...(ngDevMode ? [{ debugName: "heightPx" }] : /* istanbul ignore next */ []));
|
|
44
|
+
/**
|
|
45
|
+
* Rendered height (px) of the banner, 0 while it is hidden. Published together with the
|
|
46
|
+
* `--mj-connectivity-banner-height` custom property on `<html>`.
|
|
47
|
+
*
|
|
48
|
+
* CSS should keep using the custom property; a template binding must use this signal. A
|
|
49
|
+
* binding that reads the custom property is invisible to change detection: the height is
|
|
50
|
+
* published from the animation-done listener, which can fire while views are refreshing,
|
|
51
|
+
* so the reading view is never re-checked and dev mode reports NG0100 on every later pass
|
|
52
|
+
* (MJ #4377). Reading a signal makes the view a consumer, so the write re-refreshes it.
|
|
53
|
+
*/
|
|
54
|
+
HeightPx = this.heightPx.asReadonly();
|
|
42
55
|
ngOnDestroy() {
|
|
43
56
|
this.setBannerHeight(0);
|
|
44
57
|
}
|
|
@@ -54,6 +67,7 @@ export class ServerConnectivityBannerComponent {
|
|
|
54
67
|
}
|
|
55
68
|
setBannerHeight(px) {
|
|
56
69
|
document.documentElement.style.setProperty(ServerConnectivityBannerComponent.CSS_VAR, `${px}px`);
|
|
70
|
+
this.heightPx.set(px);
|
|
57
71
|
}
|
|
58
72
|
static ɵfac = function ServerConnectivityBannerComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ServerConnectivityBannerComponent)(); };
|
|
59
73
|
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ServerConnectivityBannerComponent, selectors: [["mj-server-connectivity-banner"]], decls: 1, vars: 1, consts: [[1, "connectivity-banner"], [1, "fa-solid", "fa-triangle-exclamation"]], template: function ServerConnectivityBannerComponent_Template(rf, ctx) { if (rf & 1) {
|
|
@@ -93,5 +107,5 @@ export class ServerConnectivityBannerComponent {
|
|
|
93
107
|
}
|
|
94
108
|
`, styles: ["\n :host {\n flex-shrink: 0;\n width: 100%;\n }\n\n .connectivity-banner {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border-bottom: 2px solid var(--mj-status-warning-border);\n padding: 8px 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n font-size: 14px;\n font-weight: 500;\n }\n\n .connectivity-banner i {\n font-size: 16px;\n flex-shrink: 0;\n }\n\n .connectivity-banner span {\n text-align: center;\n }\n\n @media (max-width: 480px) {\n .connectivity-banner {\n padding: 6px 12px;\n font-size: 13px;\n }\n }\n "] }]
|
|
95
109
|
}], null, null); })();
|
|
96
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ServerConnectivityBannerComponent, { className: "ServerConnectivityBannerComponent", filePath: "src/lib/server-connectivity/server-connectivity-banner.component.ts", lineNumber:
|
|
110
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ServerConnectivityBannerComponent, { className: "ServerConnectivityBannerComponent", filePath: "src/lib/server-connectivity/server-connectivity-banner.component.ts", lineNumber: 70 }); })();
|
|
97
111
|
//# sourceMappingURL=server-connectivity-banner.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-connectivity-banner.component.js","sourceRoot":"","sources":["../../../src/lib/server-connectivity/server-connectivity-banner.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"server-connectivity-banner.component.js","sourceRoot":"","sources":["../../../src/lib/server-connectivity/server-connectivity-banner.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, OnDestroy, Signal, inject, signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { trigger, transition, style, animate, AnimationEvent } from '@angular/animations';\nimport { ServerConnectivityService } from '../services/server-connectivity.service';\n\n/**\n * When visible, this banner sets --mj-connectivity-banner-height on <html>\n * so that any position:fixed element referencing the shell header offset\n * (e.g. app-switcher mobile dropdown) can account for the banner. Template\n * bindings read the same height from `HeightPx` instead (MJ #4377).\n */\n@Component({\n selector: 'mj-server-connectivity-banner',\n standalone: true,\n animations: [\n trigger('slideDown', [\n transition(':enter', [\n style({ height: 0, opacity: 0, overflow: 'hidden' }),\n animate('300ms ease-out', style({ height: '*', opacity: 1 }))\n ]),\n transition(':leave', [\n animate('200ms ease-in', style({ height: 0, opacity: 0, overflow: 'hidden' }))\n ])\n ])\n ],\n template: `\n @if (!IsConnected()) {\n <div class=\"connectivity-banner\" @slideDown (@slideDown.done)=\"OnAnimationDone($event)\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <span>Server unavailable — viewing cached data. Some features may not work.</span>\n </div>\n }\n `,\n styles: [`\n :host {\n flex-shrink: 0;\n width: 100%;\n }\n\n .connectivity-banner {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border-bottom: 2px solid var(--mj-status-warning-border);\n padding: 8px 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n font-size: 14px;\n font-weight: 500;\n }\n\n .connectivity-banner i {\n font-size: 16px;\n flex-shrink: 0;\n }\n\n .connectivity-banner span {\n text-align: center;\n }\n\n @media (max-width: 480px) {\n .connectivity-banner {\n padding: 6px 12px;\n font-size: 13px;\n }\n }\n `]\n})\nexport class ServerConnectivityBannerComponent implements OnDestroy {\n private static readonly CSS_VAR = '--mj-connectivity-banner-height';\n\n private readonly connectivityService = inject(ServerConnectivityService);\n\n /**\n * Connectivity state as a **signal**, not a plain property — and that distinction is the\n * whole point of this shape.\n *\n * The socket state that feeds `IsConnected$` is reported by graphql-ws, which can announce\n * a drop from a callback that lands *inside* an in-flight change-detection pass (during app\n * startup this reliably happens after the banner's own view has been checked). Assigning a\n * plain property there mutates state the `@if` was already evaluated against, so dev-mode\n * check-no-changes reports `NG0100 ... Previous value: '-1'` — `-1` being the `@if` branch\n * index for \"no branch rendered\". Reading a signal from the template instead registers the\n * view as its consumer, so a write marks the view dirty and Angular re-refreshes it within\n * the same pass; the DOM and the expression can no longer disagree.\n *\n * `toSignal` also owns the subscription lifetime via `DestroyRef`, so there is no manual\n * `Subscription` to unsubscribe.\n */\n public readonly IsConnected = toSignal(this.connectivityService.IsConnected$, { initialValue: true });\n\n private readonly heightPx = signal(0);\n /**\n * Rendered height (px) of the banner, 0 while it is hidden. Published together with the\n * `--mj-connectivity-banner-height` custom property on `<html>`.\n *\n * CSS should keep using the custom property; a template binding must use this signal. A\n * binding that reads the custom property is invisible to change detection: the height is\n * published from the animation-done listener, which can fire while views are refreshing,\n * so the reading view is never re-checked and dev mode reports NG0100 on every later pass\n * (MJ #4377). Reading a signal makes the view a consumer, so the write re-refreshes it.\n */\n public readonly HeightPx: Signal<number> = this.heightPx.asReadonly();\n\n ngOnDestroy(): void {\n this.setBannerHeight(0);\n }\n\n /** After the enter/leave animation finishes, publish the banner height as a CSS custom property */\n OnAnimationDone(event: AnimationEvent): void {\n if (event.toState === 'void') {\n this.setBannerHeight(0);\n } else {\n const height = (event.element as HTMLElement).offsetHeight;\n this.setBannerHeight(height);\n }\n }\n\n private setBannerHeight(px: number): void {\n document.documentElement.style.setProperty(\n ServerConnectivityBannerComponent.CSS_VAR,\n `${px}px`\n );\n this.heightPx.set(px);\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-origin-crumb.component.d.ts","sourceRoot":"","sources":["../../../../../src/lib/shell/components/record-open/record-origin-crumb.component.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,mBAAmB,
|
|
1
|
+
{"version":3,"file":"record-origin-crumb.component.d.ts","sourceRoot":"","sources":["../../../../../src/lib/shell/components/record-open/record-origin-crumb.component.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,mBAAmB,EAAgF,MAAM,2BAA2B,CAAC;;AAGjK;;;;;;;;;;;GAWG;AACH,qBA8Fa,0BAA0B;IACrC,OAAO,CAAC,OAAO,CAAoC;IAC5C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAEnC,IACI,MAAM,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,EAI3C;IACD,IAAI,MAAM,IAAI,mBAAmB,GAAG,IAAI,CAEvC;IAED,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,GAAG,CAA6B;IAExC,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,YAAY,IAAI,MAAM,GAAG,SAAS,CAmBrC;IAED,OAAO,CAAC,iBAAiB;IA4BzB,6EAA6E;IACvE,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAMlC,uEAAuE;IACjE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;yCA9EtB,0BAA0B;2CAA1B,0BAA0B;CAmFtC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Component, Input, inject, ChangeDetectorRef } from '@angular/core';
|
|
2
|
-
import { NavigationService, RecordSourceHasReturnTarget } from '@memberjunction/ng-shared';
|
|
2
|
+
import { NavigationService, RecordSourceHasReturnTarget, FormatEntityRecordDisplayTitle, IsRawUUIDString } from '@memberjunction/ng-shared';
|
|
3
3
|
import { CompositeKey, Metadata } from '@memberjunction/core';
|
|
4
4
|
import * as i0 from "@angular/core";
|
|
5
5
|
function RecordOriginCrumbComponent_Conditional_0_Conditional_0_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
@@ -103,13 +103,13 @@ export class RecordOriginCrumbComponent {
|
|
|
103
103
|
const key = CompositeKey.FromURLSegment(entity, this.Origin.sourceRecordId);
|
|
104
104
|
const cached = md ? md.GetCachedRecordNameOnlyIfCached(this.Origin.sourceRecordEntity, key) : undefined;
|
|
105
105
|
if (cached) {
|
|
106
|
-
return cached;
|
|
106
|
+
return FormatEntityRecordDisplayTitle(entity, this.Origin.sourceRecordEntity, key.KeyValuePairs?.length ? key : this.Origin.sourceRecordId, cached);
|
|
107
107
|
}
|
|
108
108
|
const fallbackEntityLabel = entity?.DisplayName || entity?.Name || this.Origin.sourceRecordEntity;
|
|
109
|
-
if (this.Origin.sourceLabel && !this.Origin.sourceLabel.includes(this.Origin.sourceRecordId)) {
|
|
109
|
+
if (this.Origin.sourceLabel && !this.Origin.sourceLabel.includes(this.Origin.sourceRecordId) && !IsRawUUIDString(this.Origin.sourceLabel)) {
|
|
110
110
|
return this.Origin.sourceLabel;
|
|
111
111
|
}
|
|
112
|
-
return
|
|
112
|
+
return FormatEntityRecordDisplayTitle(entity, this.Origin.sourceRecordEntity, key.KeyValuePairs?.length ? key : this.Origin.sourceRecordId, null);
|
|
113
113
|
}
|
|
114
114
|
return this.Origin?.sourceLabel;
|
|
115
115
|
}
|
|
@@ -118,17 +118,21 @@ export class RecordOriginCrumbComponent {
|
|
|
118
118
|
if (this.Origin?.sourceRecordEntity && this.Origin?.sourceRecordId && md) {
|
|
119
119
|
const entity = md.EntityByName(this.Origin.sourceRecordEntity);
|
|
120
120
|
const key = CompositeKey.FromURLSegment(entity, this.Origin.sourceRecordId);
|
|
121
|
+
const keySource = key.KeyValuePairs?.length ? key : this.Origin.sourceRecordId;
|
|
121
122
|
const cached = md.GetCachedRecordNameOnlyIfCached(this.Origin.sourceRecordEntity, key);
|
|
122
123
|
if (cached) {
|
|
123
|
-
this.asyncResolvedLabel = cached;
|
|
124
|
+
this.asyncResolvedLabel = FormatEntityRecordDisplayTitle(entity, this.Origin.sourceRecordEntity, keySource, cached);
|
|
124
125
|
return;
|
|
125
126
|
}
|
|
126
127
|
const entityName = this.Origin.sourceRecordEntity;
|
|
127
128
|
if (typeof md.GetEntityRecordName === 'function') {
|
|
128
129
|
md.GetEntityRecordName(entityName, key).then(name => {
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
if (this.Origin?.sourceRecordEntity === entityName) {
|
|
131
|
+
const formatted = FormatEntityRecordDisplayTitle(entity, entityName, keySource, name);
|
|
132
|
+
if (formatted) {
|
|
133
|
+
this.asyncResolvedLabel = formatted;
|
|
134
|
+
this.cdr.markForCheck();
|
|
135
|
+
}
|
|
132
136
|
}
|
|
133
137
|
}).catch(() => {
|
|
134
138
|
// Silently ignore async name fetch error, fallback will remain active
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-origin-crumb.component.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/record-open/record-origin-crumb.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, inject, ChangeDetectorRef } from '@angular/core';\nimport { NavigationService, RecordSourceContext, RecordSourceHasReturnTarget } from '@memberjunction/ng-shared';\nimport { CompositeKey, Metadata } from '@memberjunction/core';\n\n/**\n * Pane-level origin crumb — the FIRST element inside a record's golden-layout\n * pane (Matt's placement call, 2026-07-30: no record-chrome refactor; the\n * crumb rides the pane). One instance per record pane, which makes it correct\n * in every layout context the region-level bar could not be:\n * - split views: each pane carries its OWN origin\n * - docked records (main layout, bar or single-resource): provenance intact\n * - edit mode / custom form headers: unaffected — the crumb sits above the\n * form entirely, in shell-owned territory\n * Rendered/positioned by the tab-container wherever record content attaches\n * (fresh loads, cache reattaches after promote/demote, single-resource).\n */\n@Component({\n selector: 'mj-record-origin-crumb',\n standalone: true,\n // The crumb sits INSIDE a GL pane: without these stoppers, clicks bubble\n // to Golden Layout's pane-focus handlers, which re-activate the pane's\n // own tab and STOMP the navigation the click just started (the app\n // segment created the destination tab and then lost the activation race).\n host: {\n '(mousedown)': '$event.stopPropagation()',\n '(click)': '$event.stopPropagation()'\n },\n template: `\n @if (Origin) {\n @if (Clickable) {\n <i class=\"fa-solid fa-arrow-left crumb-lead\" aria-hidden=\"true\"></i>\n @if (DisplayLabel) {\n <button type=\"button\" class=\"crumb-seg\" (click)=\"OnPageClick()\" title=\"Back to where you opened this record\">\n {{ DisplayLabel }}\n </button>\n } @else {\n <button type=\"button\" class=\"crumb-seg\" (click)=\"OnAppClick()\" [title]=\"'Go to ' + Origin.sourceAppName\">\n {{ Origin.sourceAppName }}\n </button>\n @if (Origin.sourceNavLabel) {\n <span class=\"crumb-sep\" aria-hidden=\"true\">›</span>\n <button type=\"button\" class=\"crumb-seg crumb-page\" (click)=\"OnPageClick()\" title=\"Back to where you opened this record\">\n {{ Origin.sourceNavLabel }}\n </button>\n }\n }\n } @else {\n <!-- Provenance only — no return target (e.g. opened by an agent) -->\n <span class=\"origin-static\">From {{ DisplayLabel || Origin.sourceLabel }}</span>\n }\n }\n `,\n styles: [`\n :host {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-shrink: 0;\n height: 36px;\n /* Left indent deliberately DEEPER than the first tab's edge (~16px)\n so the crumb reads as a level BENEATH the tab — hierarchy, not a\n sibling bar. */\n padding: 0 12px 0 24px;\n /* Match the ACTIVE tab's surface so the tab flows into its pane\n (the sunken tone read as a disconnected band between them). */\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n }\n :host:not(:has(*)) { display: none; }\n .crumb-lead {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-right: 6px;\n }\n /* Each breadcrumb level is its OWN link with its own hover */\n .crumb-seg {\n display: inline-flex;\n align-items: center;\n max-width: 100%;\n padding: 5px 8px;\n border: none;\n border-radius: 4px;\n background: transparent;\n font-size: 12px;\n line-height: 1;\n font-family: inherit;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .crumb-seg:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n }\n .crumb-seg:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: -2px;\n }\n .crumb-page { font-weight: 500; }\n .crumb-sep { color: var(--mj-text-muted); font-size: 12px; }\n .origin-static {\n padding: 5px 8px;\n font-size: 12px;\n color: var(--mj-text-muted);\n }\n `]\n})\nexport class RecordOriginCrumbComponent {\n private _origin: RecordSourceContext | null = null;\n public asyncResolvedLabel?: string; // case-violation-ok-legacy-back-compat: an accessor cannot be optional, so a stub would turn this into a required member\n\n @Input()\n set Origin(value: RecordSourceContext | null) {\n this._origin = value;\n this.asyncResolvedLabel = undefined;\n this.resolveAsyncLabel();\n }\n get Origin(): RecordSourceContext | null {\n return this._origin;\n }\n\n private navigationService = inject(NavigationService);\n private cdr = inject(ChangeDetectorRef);\n\n get Clickable(): boolean {\n return RecordSourceHasReturnTarget(this.Origin);\n }\n\n get DisplayLabel(): string | undefined {\n if (this.Origin?.sourceRecordEntity && this.Origin?.sourceRecordId) {\n if (this.asyncResolvedLabel) {\n return this.asyncResolvedLabel;\n }\n const md = Metadata.Provider; // global-provider-ok: shell breadcrumb component displays navigation history using global metadata cache\n const entity = md?.EntityByName(this.Origin.sourceRecordEntity);\n const key = CompositeKey.FromURLSegment(entity, this.Origin.sourceRecordId);\n const cached = md ? md.GetCachedRecordNameOnlyIfCached(this.Origin.sourceRecordEntity, key) : undefined;\n if (cached) {\n return cached;\n }\n const fallbackEntityLabel = entity?.DisplayName || entity?.Name || this.Origin.sourceRecordEntity;\n if (this.Origin.sourceLabel && !this.Origin.sourceLabel.includes(this.Origin.sourceRecordId)) {\n return this.Origin.sourceLabel;\n }\n return fallbackEntityLabel;\n }\n return this.Origin?.sourceLabel;\n }\n\n private resolveAsyncLabel(): void {\n const md = Metadata.Provider; // global-provider-ok: shell breadcrumb component asynchronously resolves record name using global metadata provider\n if (this.Origin?.sourceRecordEntity && this.Origin?.sourceRecordId && md) {\n const entity = md.EntityByName(this.Origin.sourceRecordEntity);\n const key = CompositeKey.FromURLSegment(entity, this.Origin.sourceRecordId);\n const cached = md.GetCachedRecordNameOnlyIfCached(this.Origin.sourceRecordEntity, key);\n if (cached) {\n this.asyncResolvedLabel = cached;\n return;\n }\n const entityName = this.Origin.sourceRecordEntity;\n if (typeof md.GetEntityRecordName === 'function') {\n md.GetEntityRecordName(entityName, key).then(name => {\n if (name && this.Origin?.sourceRecordEntity === entityName) {\n this.asyncResolvedLabel = name;\n this.cdr.markForCheck();\n }\n }).catch(() => {\n // Silently ignore async name fetch error, fallback will remain active\n });\n }\n }\n }\n\n /** Page segment: full restore — the captured page, section state included */\n async OnPageClick(): Promise<void> {\n if (this.Origin) {\n await this.navigationService.ReturnToRecordSource(this.Origin);\n }\n }\n\n /** App segment: the app's LANDING page (breadcrumb level semantics) */\n async OnAppClick(): Promise<void> {\n if (this.Origin?.sourceAppId) {\n await this.navigationService.SwitchToAppHome(this.Origin.sourceAppId);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"record-origin-crumb.component.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/record-open/record-origin-crumb.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, inject, ChangeDetectorRef } from '@angular/core';\nimport { NavigationService, RecordSourceContext, RecordSourceHasReturnTarget, FormatEntityRecordDisplayTitle, IsRawUUIDString } from '@memberjunction/ng-shared';\nimport { CompositeKey, Metadata } from '@memberjunction/core';\n\n/**\n * Pane-level origin crumb — the FIRST element inside a record's golden-layout\n * pane (Matt's placement call, 2026-07-30: no record-chrome refactor; the\n * crumb rides the pane). One instance per record pane, which makes it correct\n * in every layout context the region-level bar could not be:\n * - split views: each pane carries its OWN origin\n * - docked records (main layout, bar or single-resource): provenance intact\n * - edit mode / custom form headers: unaffected — the crumb sits above the\n * form entirely, in shell-owned territory\n * Rendered/positioned by the tab-container wherever record content attaches\n * (fresh loads, cache reattaches after promote/demote, single-resource).\n */\n@Component({\n selector: 'mj-record-origin-crumb',\n standalone: true,\n // The crumb sits INSIDE a GL pane: without these stoppers, clicks bubble\n // to Golden Layout's pane-focus handlers, which re-activate the pane's\n // own tab and STOMP the navigation the click just started (the app\n // segment created the destination tab and then lost the activation race).\n host: {\n '(mousedown)': '$event.stopPropagation()',\n '(click)': '$event.stopPropagation()'\n },\n template: `\n @if (Origin) {\n @if (Clickable) {\n <i class=\"fa-solid fa-arrow-left crumb-lead\" aria-hidden=\"true\"></i>\n @if (DisplayLabel) {\n <button type=\"button\" class=\"crumb-seg\" (click)=\"OnPageClick()\" title=\"Back to where you opened this record\">\n {{ DisplayLabel }}\n </button>\n } @else {\n <button type=\"button\" class=\"crumb-seg\" (click)=\"OnAppClick()\" [title]=\"'Go to ' + Origin.sourceAppName\">\n {{ Origin.sourceAppName }}\n </button>\n @if (Origin.sourceNavLabel) {\n <span class=\"crumb-sep\" aria-hidden=\"true\">›</span>\n <button type=\"button\" class=\"crumb-seg crumb-page\" (click)=\"OnPageClick()\" title=\"Back to where you opened this record\">\n {{ Origin.sourceNavLabel }}\n </button>\n }\n }\n } @else {\n <!-- Provenance only — no return target (e.g. opened by an agent) -->\n <span class=\"origin-static\">From {{ DisplayLabel || Origin.sourceLabel }}</span>\n }\n }\n `,\n styles: [`\n :host {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-shrink: 0;\n height: 36px;\n /* Left indent deliberately DEEPER than the first tab's edge (~16px)\n so the crumb reads as a level BENEATH the tab — hierarchy, not a\n sibling bar. */\n padding: 0 12px 0 24px;\n /* Match the ACTIVE tab's surface so the tab flows into its pane\n (the sunken tone read as a disconnected band between them). */\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n }\n :host:not(:has(*)) { display: none; }\n .crumb-lead {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-right: 6px;\n }\n /* Each breadcrumb level is its OWN link with its own hover */\n .crumb-seg {\n display: inline-flex;\n align-items: center;\n max-width: 100%;\n padding: 5px 8px;\n border: none;\n border-radius: 4px;\n background: transparent;\n font-size: 12px;\n line-height: 1;\n font-family: inherit;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .crumb-seg:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n }\n .crumb-seg:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: -2px;\n }\n .crumb-page { font-weight: 500; }\n .crumb-sep { color: var(--mj-text-muted); font-size: 12px; }\n .origin-static {\n padding: 5px 8px;\n font-size: 12px;\n color: var(--mj-text-muted);\n }\n `]\n})\nexport class RecordOriginCrumbComponent {\n private _origin: RecordSourceContext | null = null;\n public asyncResolvedLabel?: string; // case-violation-ok-legacy-back-compat: an accessor cannot be optional, so a stub would turn this into a required member\n\n @Input()\n set Origin(value: RecordSourceContext | null) {\n this._origin = value;\n this.asyncResolvedLabel = undefined;\n this.resolveAsyncLabel();\n }\n get Origin(): RecordSourceContext | null {\n return this._origin;\n }\n\n private navigationService = inject(NavigationService);\n private cdr = inject(ChangeDetectorRef);\n\n get Clickable(): boolean {\n return RecordSourceHasReturnTarget(this.Origin);\n }\n\n get DisplayLabel(): string | undefined {\n if (this.Origin?.sourceRecordEntity && this.Origin?.sourceRecordId) {\n if (this.asyncResolvedLabel) {\n return this.asyncResolvedLabel;\n }\n const md = Metadata.Provider; // global-provider-ok: shell breadcrumb component displays navigation history using global metadata cache\n const entity = md?.EntityByName(this.Origin.sourceRecordEntity);\n const key = CompositeKey.FromURLSegment(entity, this.Origin.sourceRecordId);\n const cached = md ? md.GetCachedRecordNameOnlyIfCached(this.Origin.sourceRecordEntity, key) : undefined;\n if (cached) {\n return FormatEntityRecordDisplayTitle(entity, this.Origin.sourceRecordEntity, key.KeyValuePairs?.length ? key : this.Origin.sourceRecordId, cached);\n }\n const fallbackEntityLabel = entity?.DisplayName || entity?.Name || this.Origin.sourceRecordEntity;\n if (this.Origin.sourceLabel && !this.Origin.sourceLabel.includes(this.Origin.sourceRecordId) && !IsRawUUIDString(this.Origin.sourceLabel)) {\n return this.Origin.sourceLabel;\n }\n return FormatEntityRecordDisplayTitle(entity, this.Origin.sourceRecordEntity, key.KeyValuePairs?.length ? key : this.Origin.sourceRecordId, null);\n }\n return this.Origin?.sourceLabel;\n }\n\n private resolveAsyncLabel(): void {\n const md = Metadata.Provider; // global-provider-ok: shell breadcrumb component asynchronously resolves record name using global metadata provider\n if (this.Origin?.sourceRecordEntity && this.Origin?.sourceRecordId && md) {\n const entity = md.EntityByName(this.Origin.sourceRecordEntity);\n const key = CompositeKey.FromURLSegment(entity, this.Origin.sourceRecordId);\n const keySource = key.KeyValuePairs?.length ? key : this.Origin.sourceRecordId;\n const cached = md.GetCachedRecordNameOnlyIfCached(this.Origin.sourceRecordEntity, key);\n if (cached) {\n this.asyncResolvedLabel = FormatEntityRecordDisplayTitle(entity, this.Origin.sourceRecordEntity, keySource, cached);\n return;\n }\n const entityName = this.Origin.sourceRecordEntity;\n if (typeof md.GetEntityRecordName === 'function') {\n md.GetEntityRecordName(entityName, key).then(name => {\n if (this.Origin?.sourceRecordEntity === entityName) {\n const formatted = FormatEntityRecordDisplayTitle(entity, entityName, keySource, name);\n if (formatted) {\n this.asyncResolvedLabel = formatted;\n this.cdr.markForCheck();\n }\n }\n }).catch(() => {\n // Silently ignore async name fetch error, fallback will remain active\n });\n }\n }\n }\n\n /** Page segment: full restore — the captured page, section state included */\n async OnPageClick(): Promise<void> {\n if (this.Origin) {\n await this.navigationService.ReturnToRecordSource(this.Origin);\n }\n }\n\n /** App segment: the app's LANDING page (breadcrumb level semantics) */\n async OnAppClick(): Promise<void> {\n if (this.Origin?.sourceAppId) {\n await this.navigationService.SwitchToAppHome(this.Origin.sourceAppId);\n }\n }\n}\n"]}
|
|
@@ -80,6 +80,12 @@ export declare class ComponentCacheManager {
|
|
|
80
80
|
* it just detached.
|
|
81
81
|
*/
|
|
82
82
|
private getCacheKey;
|
|
83
|
+
/**
|
|
84
|
+
* Computes the cache discriminator for a resource configuration, combining `Entity` and any
|
|
85
|
+
* `AlternateBaseView` override so dynamic or saved views opened with different `?abv=` values
|
|
86
|
+
* do not collide in the component cache.
|
|
87
|
+
*/
|
|
88
|
+
static GetCacheDiscriminator(configuration?: Record<string, unknown> | null): string | undefined;
|
|
83
89
|
/**
|
|
84
90
|
* Whether a recordId is the `'dynamic'` marker `NavigationService.OpenDynamicView` stamps on a
|
|
85
91
|
* dynamic view, which is not a saved-view ID. Matched the way `ViewResourceComponent` matches it
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-cache-manager.d.ts","sourceRoot":"","sources":["../../../../../src/lib/shell/components/tabs/component-cache-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAElC,YAAY,EAAE,YAAY,CAAC,qBAAqB,CAAC,CAAC;IAGlD,cAAc,EAAE,WAAW,CAAC;IAG5B,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IAMtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAG1B,UAAU,EAAE,OAAO,CAAC;IACpB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAG/B,QAAQ,EAAE,IAAI,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;IAGhB,YAAY,EAAE,YAAY,CAAC;IAK3B,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAK1C,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAGvC,gBAAgB,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,EAAE,CAAC;CACtK;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,qBAAqB;IAiBpB,OAAO,CAAC,MAAM;IAAkB,OAAO,CAAC,iBAAiB,CAAC;IAhBtE,OAAO,CAAC,KAAK,CAA0C;IAEvD;;;;OAIG;IACH,OAAc,qBAAqB,EAAE,MAAM,CAAM;IAEjD;;;;;;OAMG;gBACiB,MAAM,EAAE,cAAc,EAAU,iBAAiB,CAAC,EAAE,iBAAiB,YAAA;IAEzF;;;;;;;;;;;;;;;;;;OAkBG;IACH,OAAO,CAAC,WAAW;
|
|
1
|
+
{"version":3,"file":"component-cache-manager.d.ts","sourceRoot":"","sources":["../../../../../src/lib/shell/components/tabs/component-cache-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -63,11 +63,27 @@ export class ComponentCacheManager {
|
|
|
63
63
|
else if (discriminator && ComponentCacheManager.IsDynamicViewMarker(recordId)) {
|
|
64
64
|
normalizedRecordId = `dynamic::${discriminator}`;
|
|
65
65
|
}
|
|
66
|
+
else if (discriminator && discriminator.includes('abv:')) {
|
|
67
|
+
normalizedRecordId = `${recordId}::${discriminator}`;
|
|
68
|
+
}
|
|
66
69
|
else {
|
|
67
70
|
normalizedRecordId = recordId;
|
|
68
71
|
}
|
|
69
72
|
return `${appId}::${resourceType}::${normalizedRecordId}`;
|
|
70
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Computes the cache discriminator for a resource configuration, combining `Entity` and any
|
|
76
|
+
* `AlternateBaseView` override so dynamic or saved views opened with different `?abv=` values
|
|
77
|
+
* do not collide in the component cache.
|
|
78
|
+
*/
|
|
79
|
+
static GetCacheDiscriminator(configuration) {
|
|
80
|
+
const entity = configuration?.Entity;
|
|
81
|
+
const abv = (configuration?.AlternateBaseView ?? configuration?.alternateBaseView);
|
|
82
|
+
if (entity && abv) {
|
|
83
|
+
return `${entity}|abv:${abv}`;
|
|
84
|
+
}
|
|
85
|
+
return entity || (abv ? `abv:${abv}` : undefined);
|
|
86
|
+
}
|
|
71
87
|
/**
|
|
72
88
|
* Whether a recordId is the `'dynamic'` marker `NavigationService.OpenDynamicView` stamps on a
|
|
73
89
|
* dynamic view, which is not a saved-view ID. Matched the way `ViewResourceComponent` matches it
|
|
@@ -118,9 +134,8 @@ export class ComponentCacheManager {
|
|
|
118
134
|
const resolvedResourceType = resourceData.Configuration?.resourceTypeDriverClass
|
|
119
135
|
|| resourceData.Configuration?.driverClass
|
|
120
136
|
|| resourceData.ResourceType;
|
|
121
|
-
// Entity name discriminates between distinct "new record"
|
|
122
|
-
|
|
123
|
-
const discriminator = resourceData.Configuration?.Entity;
|
|
137
|
+
// Entity name (plus any AlternateBaseView) discriminates between distinct "new record" or dynamic view tabs.
|
|
138
|
+
const discriminator = ComponentCacheManager.GetCacheDiscriminator(resourceData.Configuration);
|
|
124
139
|
const key = this.getCacheKey(resolvedResourceType, resourceData.ResourceRecordID || '', resourceData.Configuration?.applicationId || '', discriminator);
|
|
125
140
|
const info = {
|
|
126
141
|
componentRef,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-cache-manager.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/tabs/component-cache-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { ComponentRef, ApplicationRef } from '@angular/core';\nimport { BaseResourceComponent, NavigationService } from '@memberjunction/ng-shared';\nimport { ResourceData } from '@memberjunction/core-entities';\n\n/**\n * Metadata about a cached component\n */\nexport interface CachedComponentInfo {\n // The Angular component reference\n componentRef: ComponentRef<BaseResourceComponent>; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // The wrapper DOM element (for detaching/reattaching)\n wrapperElement: HTMLElement; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Resource identity (the ONLY key used for cache operations)\n resourceType: string; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n resourceRecordId: string; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n applicationId: string; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Optional tiebreaker used when recordId is empty (e.g., \"new record\" tabs for\n // different entities all have recordId='' but must NOT share a cache entry).\n // For entity-record resources this is the entity name; null/undefined for\n // resource types that don't need disambiguation.\n keyDiscriminator?: string; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Usage tracking\n isAttached: boolean; // Currently attached to a tab/container? — case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n attachedToTabId: string | null; // Which tab is it attached to? (metadata only, NOT used for lookup) — case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Lifecycle tracking\n lastUsed: Date; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n createdAt: Date; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Resource data snapshot (for comparison)\n resourceData: ResourceData; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Saved query params from the tab config at detach time.\n // Restored to the tab config when the component is reattached,\n // so the URL reflects the component's preserved state.\n savedQueryParams?: Record<string, string>; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Not written or read by the cache manager. Tools are replayed on reattach by\n // NavigationService.NotifyResourceReattached; a component that publishes agent context keeps\n // it itself and publishes it again on reattach (NavigationService.ResourceReattached$).\n AgentContext?: Record<string, unknown>;\n\n // Not written or read by the cache manager; see AgentContext.\n AgentClientTools?: { Name: string; Description: string; ParameterSchema: Record<string, unknown>; Handler: (params: Record<string, unknown>) => Promise<unknown> }[];\n}\n\n/**\n * Smart component cache manager that preserves component state across tab switches.\n *\n * ALL cache operations use a consistent identity key: `appId::resourceType::recordId`.\n * This key is the same regardless of whether the component is in Golden Layout (tabbed)\n * mode or Single Resource mode, ensuring components are reusable across both modes.\n *\n * The `attachedToTabId` field is metadata for debugging/display — it is NEVER used\n * as a lookup key. This prevents bugs where multiple resources sharing the same tab ID\n * (e.g., nav items within a single-resource app) interfere with each other's cache state.\n *\n * Features:\n * - Caches components by resource identity (appId + resourceType + recordId)\n * - Tracks component usage to prevent double-attachment\n * - Detaches/reattaches DOM elements without destroying Angular components\n * - LRU eviction when detached component count exceeds MaxDetachedComponents\n */\nexport class ComponentCacheManager {\n private cache = new Map<string, CachedComponentInfo>();\n\n /**\n * Maximum number of detached (not currently visible) components to keep\n * cached. When exceeded, least-recently-used detached components are\n * evicted. Set to 0 to disable eviction (legacy behavior). Default: 20.\n */\n public static MaxDetachedComponents: number = 20;\n\n /**\n * @param navigationService Optional — when provided, the cache manager keeps the AI agent's live\n * client-tool set in sync with the attached surface: it replays a reattached component's tools\n * ({@link NavigationService.NotifyResourceReattached}) and clears them on detach\n * ({@link NavigationService.NotifyResourceDetached}), since cached components don't re-run\n * `ngAfterViewInit`. Centralizing it here covers BOTH single-resource and Golden Layout paths.\n */\n constructor(private appRef: ApplicationRef, private navigationService?: NavigationService) {}\n\n /**\n * Generate a unique cache key from resource identity.\n * This is the ONE canonical key format used by ALL cache operations.\n *\n * `discriminator` is appended into the recordId slot when the recordId alone does not\n * identify the resource:\n * - **Empty recordId** — distinct \"new record\" tabs, e.g. a new MJ:Companies form and a new\n * MJ:Employees form, would otherwise both cache at `appId::RecordResource::__no_record__`\n * and clobber each other.\n * - **The `'dynamic'` view marker** — `NavigationService.OpenDynamicView` stamps every\n * dynamic view with that same recordId, so without the entity every dynamic view in an app\n * shares one key. A reload from `#Accounts` to `#Contacts` would then get a cache hit on the\n * Accounts component, which never has its `Data` rebound, so the tab keeps showing Accounts\n * and the tab-container's entity check fires again on every emission.\n *\n * Passing the entity name as discriminator keeps them separate. Whatever the tab-container's\n * reload check compares has to be part of this key, or a reload is handed back the component\n * it just detached.\n */\n private getCacheKey(resourceType: string, recordId: string, appId: string, discriminator?: string): string {\n let normalizedRecordId: string;\n if (!recordId) {\n normalizedRecordId = discriminator ? `__new__::${discriminator}` : '__no_record__';\n } else if (discriminator && ComponentCacheManager.IsDynamicViewMarker(recordId)) {\n normalizedRecordId = `dynamic::${discriminator}`;\n } else {\n normalizedRecordId = recordId;\n }\n return `${appId}::${resourceType}::${normalizedRecordId}`;\n }\n\n /**\n * Whether a recordId is the `'dynamic'` marker `NavigationService.OpenDynamicView` stamps on a\n * dynamic view, which is not a saved-view ID. Matched the way `ViewResourceComponent` matches it\n * (trimmed, case-insensitive), so the cache and the view agree on what counts as dynamic.\n */\n public static IsDynamicViewMarker(recordId: string | null | undefined): boolean {\n return typeof recordId === 'string' && recordId.trim().toLowerCase() === 'dynamic';\n }\n\n /**\n * Check if a component exists in cache and is available for reuse.\n */\n HasAvailableComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): boolean {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n return info !== undefined && !info.isAttached;\n }\n\n /** @deprecated Use {@link HasAvailableComponent}. */\n hasAvailableComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): boolean {\n return this.HasAvailableComponent(resourceType, recordId, appId, discriminator);\n }\n\n /**\n * Get a cached component if available (not currently attached).\n * Lookup is by resource identity, not tab ID.\n */\n GetCachedComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n\n if (!info) {\n return null;\n }\n\n // Can only reuse if not currently attached elsewhere\n if (info.isAttached) {\n return null;\n }\n\n return info;\n }\n\n /** @deprecated Use {@link GetCachedComponent}. */\n getCachedComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null {\n return this.GetCachedComponent(resourceType, recordId, appId, discriminator);\n }\n\n /**\n * Store a component in the cache and mark as attached.\n */\n CacheComponent(\n componentRef: ComponentRef<BaseResourceComponent>,\n wrapperElement: HTMLElement,\n resourceData: ResourceData,\n tabId: string\n ): void {\n // Use driverClass (the actual component class name) as the resourceType for the cache key,\n // NOT resourceData.ResourceType (which is often just \"Custom\" for dashboard resources).\n // This must match the lookup key used in getCachedComponent/markAsAttached/markAsDetached.\n const resolvedResourceType = resourceData.Configuration?.resourceTypeDriverClass\n || resourceData.Configuration?.driverClass\n || resourceData.ResourceType;\n // Entity name discriminates between distinct \"new record\" tabs (empty recordId).\n // For non-Records resource types this is undefined, leaving the key unchanged.\n const discriminator = resourceData.Configuration?.Entity as string | undefined;\n const key = this.getCacheKey(\n resolvedResourceType,\n resourceData.ResourceRecordID || '',\n resourceData.Configuration?.applicationId || '',\n discriminator\n );\n\n const info: CachedComponentInfo = {\n componentRef,\n wrapperElement,\n resourceType: resolvedResourceType,\n resourceRecordId: resourceData.ResourceRecordID || '',\n applicationId: resourceData.Configuration?.applicationId || '',\n keyDiscriminator: discriminator,\n isAttached: true,\n attachedToTabId: tabId,\n lastUsed: new Date(),\n createdAt: new Date(),\n resourceData\n };\n\n this.cache.set(key, info);\n }\n\n /** @deprecated Use {@link CacheComponent}. */\n cacheComponent(\n componentRef: ComponentRef<BaseResourceComponent>,\n wrapperElement: HTMLElement,\n resourceData: ResourceData,\n tabId: string\n ): void {\n return this.CacheComponent(componentRef, wrapperElement, resourceData, tabId);\n }\n\n /**\n * Mark a component as attached. Lookup by resource identity.\n */\n MarkAsAttached(resourceType: string, recordId: string, appId: string, tabId: string, discriminator?: string): void {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n\n if (info) {\n info.isAttached = true;\n info.attachedToTabId = tabId;\n info.lastUsed = new Date();\n // Replay this surface's agent client tools to the AI agent on reattach (cached components keep\n // their instance but never re-run ngAfterViewInit, so they don't re-register on their own).\n this.navigationService?.NotifyResourceReattached(info.componentRef.instance);\n }\n }\n\n /** @deprecated Use {@link MarkAsAttached}. */\n markAsAttached(resourceType: string, recordId: string, appId: string, tabId: string, discriminator?: string): void {\n return this.MarkAsAttached(resourceType, recordId, appId, tabId, discriminator);\n }\n\n /**\n * Mark a component as detached (available for reuse). Lookup by resource identity.\n *\n * This is the ONLY way to detach a component. Both single-resource mode and\n * Golden Layout mode use this same method to ensure consistent cache behavior.\n */\n MarkAsDetached(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n if (!info) return null;\n\n info.isAttached = false;\n info.attachedToTabId = null;\n info.lastUsed = new Date();\n // Clear this surface's agent client tools on detach so the previous app's tools aren't offered to\n // the AI agent on the next surface. NotifyResourceReattached replays them if the user returns.\n this.navigationService?.NotifyResourceDetached(info.componentRef.instance);\n this.evictIfNeeded();\n return info;\n }\n\n /** @deprecated Use {@link MarkAsDetached}. */\n markAsDetached(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null {\n return this.MarkAsDetached(resourceType, recordId, appId, discriminator);\n }\n\n /**\n * Re-key a cached component when its underlying record identity changes.\n *\n * Used when a \"new record\" component (cached at `recordId = ''`) becomes a saved record\n * (now identified by its new PK). Without re-keying, the next \"new record\" request would\n * still resolve to this same cache entry, surfacing the previously-saved record on a form\n * the user expects to be blank.\n *\n * Preserves the live component instance — only the cache key + stored identity change.\n * Returns true if a matching entry was found and re-keyed, false otherwise.\n */\n RekeyComponent(\n resourceType: string,\n oldRecordId: string,\n newRecordId: string,\n appId: string,\n oldDiscriminator?: string,\n newDiscriminator?: string\n ): boolean {\n const oldKey = this.getCacheKey(resourceType, oldRecordId, appId, oldDiscriminator);\n const info = this.cache.get(oldKey);\n if (!info) {\n return false;\n }\n\n // Once a record has a real id, the discriminator no longer matters (the id is unique).\n // Default the new discriminator to undefined unless explicitly provided.\n const newKey = this.getCacheKey(resourceType, newRecordId, appId, newDiscriminator);\n if (oldKey === newKey) {\n return false;\n }\n\n info.resourceRecordId = newRecordId;\n info.keyDiscriminator = newDiscriminator;\n info.lastUsed = new Date();\n if (info.resourceData) {\n info.resourceData.ResourceRecordID = newRecordId;\n }\n\n this.cache.delete(oldKey);\n this.cache.set(newKey, info);\n return true;\n }\n\n /** @deprecated Use {@link RekeyComponent}. */\n rekeyComponent(\n resourceType: string,\n oldRecordId: string,\n newRecordId: string,\n appId: string,\n oldDiscriminator?: string,\n newDiscriminator?: string\n ): boolean {\n return this.RekeyComponent(resourceType, oldRecordId, newRecordId, appId, oldDiscriminator, newDiscriminator);\n }\n\n /**\n * Find a cached component by tab ID and detach it.\n * This is a convenience wrapper for callers that only know the tab ID\n * (e.g., Golden Layout tab close events). It resolves the tab ID to\n * resource identity, then delegates to the identity-based markAsDetached.\n */\n FindAndDetachByTabId(tabId: string): CachedComponentInfo | null {\n const entry = Array.from(this.cache.entries())\n .find(([_, info]) => info.attachedToTabId === tabId);\n\n if (!entry) return null;\n\n const [_, info] = entry;\n return this.MarkAsDetached(info.resourceType, info.resourceRecordId, info.applicationId, info.keyDiscriminator);\n }\n\n /** @deprecated Use {@link FindAndDetachByTabId}. */\n findAndDetachByTabId(tabId: string): CachedComponentInfo | null {\n return this.FindAndDetachByTabId(tabId);\n }\n\n /**\n * Evict least-recently-used detached components when over the limit.\n * Only evicts components that are not currently attached.\n */\n private evictIfNeeded(): void {\n if (ComponentCacheManager.MaxDetachedComponents <= 0) return;\n\n const detached = Array.from(this.cache.entries())\n .filter(([_, info]) => !info.isAttached)\n .sort((a, b) => a[1].lastUsed.getTime() - b[1].lastUsed.getTime());\n\n while (detached.length > ComponentCacheManager.MaxDetachedComponents) {\n const [key, info] = detached.shift()!;\n // Drop the destroyed component's cached agent tools so the registry doesn't retain a dead ref.\n this.navigationService?.ForgetResource(info.componentRef.instance);\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n this.cache.delete(key);\n }\n }\n\n /**\n * Get component info by tab ID (for finding what's attached to a tab).\n * Uses linear scan since tabId is metadata, not a key.\n */\n GetComponentByTabId(tabId: string): CachedComponentInfo | null {\n const entry = Array.from(this.cache.entries())\n .find(([_, info]) => info.attachedToTabId === tabId);\n\n return entry ? entry[1] : null;\n }\n\n /** @deprecated Use {@link GetComponentByTabId}. */\n getComponentByTabId(tabId: string): CachedComponentInfo | null {\n return this.GetComponentByTabId(tabId);\n }\n\n /**\n * Remove and destroy a specific component from cache by resource identity.\n */\n DestroyComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): void {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n\n if (!info) return;\n\n this.navigationService?.ForgetResource(info.componentRef.instance);\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n this.cache.delete(key);\n }\n\n /** @deprecated Use {@link DestroyComponent}. */\n destroyComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): void {\n return this.DestroyComponent(resourceType, recordId, appId, discriminator);\n }\n\n /**\n * Remove and destroy component by tab ID (convenience for Golden Layout tab close).\n */\n DestroyComponentByTabId(tabId: string): void {\n const entry = Array.from(this.cache.entries())\n .find(([_, info]) => info.attachedToTabId === tabId);\n\n if (!entry) return;\n\n const [key, info] = entry;\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n this.cache.delete(key);\n }\n\n /** @deprecated Use {@link DestroyComponentByTabId}. */\n destroyComponentByTabId(tabId: string): void {\n return this.DestroyComponentByTabId(tabId);\n }\n\n /**\n * Clear the entire cache, destroying all components.\n * Call this on user logout or app shutdown.\n */\n ClearCache(): void {\n this.cache.forEach(info => {\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n });\n this.cache.clear();\n }\n\n /** @deprecated Use {@link ClearCache}. */\n clearCache(): void {\n return this.ClearCache();\n }\n\n /**\n * Selectively clear cached components matching a predicate.\n * Components that match are destroyed; those that don't are kept.\n *\n * Use this for tenant switching: clear org-scoped components while\n * keeping system/global components alive.\n *\n * @param predicate Return true for components that should be destroyed.\n * @returns Number of components destroyed.\n */\n ClearCacheByPredicate(predicate: (info: CachedComponentInfo) => boolean): number {\n let destroyed = 0;\n const toRemove: string[] = [];\n\n this.cache.forEach((info, key) => {\n if (predicate(info)) {\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n toRemove.push(key);\n destroyed++;\n }\n });\n\n for (const key of toRemove) {\n this.cache.delete(key);\n }\n\n return destroyed;\n }\n\n /**\n * Get cache statistics for debugging.\n */\n GetCacheStats(): {\n total: number;\n attached: number;\n detached: number;\n byResourceType: Map<string, number>;\n } {\n const stats = {\n total: this.cache.size,\n attached: 0,\n detached: 0,\n byResourceType: new Map<string, number>()\n };\n\n this.cache.forEach(info => {\n if (info.isAttached) {\n stats.attached++;\n } else {\n stats.detached++;\n }\n\n const count = stats.byResourceType.get(info.resourceType) || 0;\n stats.byResourceType.set(info.resourceType, count + 1);\n });\n\n return stats;\n }\n\n /** @deprecated Use {@link GetCacheStats}. */\n getCacheStats(): {\n total: number;\n attached: number;\n detached: number;\n byResourceType: Map<string, number>;\n } {\n return this.GetCacheStats();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"component-cache-manager.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/tabs/component-cache-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { ComponentRef, ApplicationRef } from '@angular/core';\nimport { BaseResourceComponent, NavigationService } from '@memberjunction/ng-shared';\nimport { ResourceData } from '@memberjunction/core-entities';\n\n/**\n * Metadata about a cached component\n */\nexport interface CachedComponentInfo {\n // The Angular component reference\n componentRef: ComponentRef<BaseResourceComponent>; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // The wrapper DOM element (for detaching/reattaching)\n wrapperElement: HTMLElement; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Resource identity (the ONLY key used for cache operations)\n resourceType: string; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n resourceRecordId: string; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n applicationId: string; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Optional tiebreaker used when recordId is empty (e.g., \"new record\" tabs for\n // different entities all have recordId='' but must NOT share a cache entry).\n // For entity-record resources this is the entity name; null/undefined for\n // resource types that don't need disambiguation.\n keyDiscriminator?: string; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Usage tracking\n isAttached: boolean; // Currently attached to a tab/container? — case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n attachedToTabId: string | null; // Which tab is it attached to? (metadata only, NOT used for lookup) — case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Lifecycle tracking\n lastUsed: Date; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n createdAt: Date; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Resource data snapshot (for comparison)\n resourceData: ResourceData; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Saved query params from the tab config at detach time.\n // Restored to the tab config when the component is reattached,\n // so the URL reflects the component's preserved state.\n savedQueryParams?: Record<string, string>; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n // Not written or read by the cache manager. Tools are replayed on reattach by\n // NavigationService.NotifyResourceReattached; a component that publishes agent context keeps\n // it itself and publishes it again on reattach (NavigationService.ResourceReattached$).\n AgentContext?: Record<string, unknown>;\n\n // Not written or read by the cache manager; see AgentContext.\n AgentClientTools?: { Name: string; Description: string; ParameterSchema: Record<string, unknown>; Handler: (params: Record<string, unknown>) => Promise<unknown> }[];\n}\n\n/**\n * Smart component cache manager that preserves component state across tab switches.\n *\n * ALL cache operations use a consistent identity key: `appId::resourceType::recordId`.\n * This key is the same regardless of whether the component is in Golden Layout (tabbed)\n * mode or Single Resource mode, ensuring components are reusable across both modes.\n *\n * The `attachedToTabId` field is metadata for debugging/display — it is NEVER used\n * as a lookup key. This prevents bugs where multiple resources sharing the same tab ID\n * (e.g., nav items within a single-resource app) interfere with each other's cache state.\n *\n * Features:\n * - Caches components by resource identity (appId + resourceType + recordId)\n * - Tracks component usage to prevent double-attachment\n * - Detaches/reattaches DOM elements without destroying Angular components\n * - LRU eviction when detached component count exceeds MaxDetachedComponents\n */\nexport class ComponentCacheManager {\n private cache = new Map<string, CachedComponentInfo>();\n\n /**\n * Maximum number of detached (not currently visible) components to keep\n * cached. When exceeded, least-recently-used detached components are\n * evicted. Set to 0 to disable eviction (legacy behavior). Default: 20.\n */\n public static MaxDetachedComponents: number = 20;\n\n /**\n * @param navigationService Optional — when provided, the cache manager keeps the AI agent's live\n * client-tool set in sync with the attached surface: it replays a reattached component's tools\n * ({@link NavigationService.NotifyResourceReattached}) and clears them on detach\n * ({@link NavigationService.NotifyResourceDetached}), since cached components don't re-run\n * `ngAfterViewInit`. Centralizing it here covers BOTH single-resource and Golden Layout paths.\n */\n constructor(private appRef: ApplicationRef, private navigationService?: NavigationService) {}\n\n /**\n * Generate a unique cache key from resource identity.\n * This is the ONE canonical key format used by ALL cache operations.\n *\n * `discriminator` is appended into the recordId slot when the recordId alone does not\n * identify the resource:\n * - **Empty recordId** — distinct \"new record\" tabs, e.g. a new MJ:Companies form and a new\n * MJ:Employees form, would otherwise both cache at `appId::RecordResource::__no_record__`\n * and clobber each other.\n * - **The `'dynamic'` view marker** — `NavigationService.OpenDynamicView` stamps every\n * dynamic view with that same recordId, so without the entity every dynamic view in an app\n * shares one key. A reload from `#Accounts` to `#Contacts` would then get a cache hit on the\n * Accounts component, which never has its `Data` rebound, so the tab keeps showing Accounts\n * and the tab-container's entity check fires again on every emission.\n *\n * Passing the entity name as discriminator keeps them separate. Whatever the tab-container's\n * reload check compares has to be part of this key, or a reload is handed back the component\n * it just detached.\n */\n private getCacheKey(resourceType: string, recordId: string, appId: string, discriminator?: string): string {\n let normalizedRecordId: string;\n if (!recordId) {\n normalizedRecordId = discriminator ? `__new__::${discriminator}` : '__no_record__';\n } else if (discriminator && ComponentCacheManager.IsDynamicViewMarker(recordId)) {\n normalizedRecordId = `dynamic::${discriminator}`;\n } else if (discriminator && discriminator.includes('abv:')) {\n normalizedRecordId = `${recordId}::${discriminator}`;\n } else {\n normalizedRecordId = recordId;\n }\n return `${appId}::${resourceType}::${normalizedRecordId}`;\n }\n\n /**\n * Computes the cache discriminator for a resource configuration, combining `Entity` and any\n * `AlternateBaseView` override so dynamic or saved views opened with different `?abv=` values\n * do not collide in the component cache.\n */\n public static GetCacheDiscriminator(configuration?: Record<string, unknown> | null): string | undefined {\n const entity = configuration?.Entity as string | undefined;\n const abv = (configuration?.AlternateBaseView ?? configuration?.alternateBaseView) as string | undefined;\n if (entity && abv) {\n return `${entity}|abv:${abv}`;\n }\n return entity || (abv ? `abv:${abv}` : undefined);\n }\n\n /**\n * Whether a recordId is the `'dynamic'` marker `NavigationService.OpenDynamicView` stamps on a\n * dynamic view, which is not a saved-view ID. Matched the way `ViewResourceComponent` matches it\n * (trimmed, case-insensitive), so the cache and the view agree on what counts as dynamic.\n */\n public static IsDynamicViewMarker(recordId: string | null | undefined): boolean {\n return typeof recordId === 'string' && recordId.trim().toLowerCase() === 'dynamic';\n }\n\n /**\n * Check if a component exists in cache and is available for reuse.\n */\n HasAvailableComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): boolean {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n return info !== undefined && !info.isAttached;\n }\n\n /** @deprecated Use {@link HasAvailableComponent}. */\n hasAvailableComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): boolean {\n return this.HasAvailableComponent(resourceType, recordId, appId, discriminator);\n }\n\n /**\n * Get a cached component if available (not currently attached).\n * Lookup is by resource identity, not tab ID.\n */\n GetCachedComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n\n if (!info) {\n return null;\n }\n\n // Can only reuse if not currently attached elsewhere\n if (info.isAttached) {\n return null;\n }\n\n return info;\n }\n\n /** @deprecated Use {@link GetCachedComponent}. */\n getCachedComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null {\n return this.GetCachedComponent(resourceType, recordId, appId, discriminator);\n }\n\n /**\n * Store a component in the cache and mark as attached.\n */\n CacheComponent(\n componentRef: ComponentRef<BaseResourceComponent>,\n wrapperElement: HTMLElement,\n resourceData: ResourceData,\n tabId: string\n ): void {\n // Use driverClass (the actual component class name) as the resourceType for the cache key,\n // NOT resourceData.ResourceType (which is often just \"Custom\" for dashboard resources).\n // This must match the lookup key used in getCachedComponent/markAsAttached/markAsDetached.\n const resolvedResourceType = resourceData.Configuration?.resourceTypeDriverClass\n || resourceData.Configuration?.driverClass\n || resourceData.ResourceType;\n // Entity name (plus any AlternateBaseView) discriminates between distinct \"new record\" or dynamic view tabs.\n const discriminator = ComponentCacheManager.GetCacheDiscriminator(resourceData.Configuration);\n const key = this.getCacheKey(\n resolvedResourceType,\n resourceData.ResourceRecordID || '',\n resourceData.Configuration?.applicationId || '',\n discriminator\n );\n\n const info: CachedComponentInfo = {\n componentRef,\n wrapperElement,\n resourceType: resolvedResourceType,\n resourceRecordId: resourceData.ResourceRecordID || '',\n applicationId: resourceData.Configuration?.applicationId || '',\n keyDiscriminator: discriminator,\n isAttached: true,\n attachedToTabId: tabId,\n lastUsed: new Date(),\n createdAt: new Date(),\n resourceData\n };\n\n this.cache.set(key, info);\n }\n\n /** @deprecated Use {@link CacheComponent}. */\n cacheComponent(\n componentRef: ComponentRef<BaseResourceComponent>,\n wrapperElement: HTMLElement,\n resourceData: ResourceData,\n tabId: string\n ): void {\n return this.CacheComponent(componentRef, wrapperElement, resourceData, tabId);\n }\n\n /**\n * Mark a component as attached. Lookup by resource identity.\n */\n MarkAsAttached(resourceType: string, recordId: string, appId: string, tabId: string, discriminator?: string): void {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n\n if (info) {\n info.isAttached = true;\n info.attachedToTabId = tabId;\n info.lastUsed = new Date();\n // Replay this surface's agent client tools to the AI agent on reattach (cached components keep\n // their instance but never re-run ngAfterViewInit, so they don't re-register on their own).\n this.navigationService?.NotifyResourceReattached(info.componentRef.instance);\n }\n }\n\n /** @deprecated Use {@link MarkAsAttached}. */\n markAsAttached(resourceType: string, recordId: string, appId: string, tabId: string, discriminator?: string): void {\n return this.MarkAsAttached(resourceType, recordId, appId, tabId, discriminator);\n }\n\n /**\n * Mark a component as detached (available for reuse). Lookup by resource identity.\n *\n * This is the ONLY way to detach a component. Both single-resource mode and\n * Golden Layout mode use this same method to ensure consistent cache behavior.\n */\n MarkAsDetached(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n if (!info) return null;\n\n info.isAttached = false;\n info.attachedToTabId = null;\n info.lastUsed = new Date();\n // Clear this surface's agent client tools on detach so the previous app's tools aren't offered to\n // the AI agent on the next surface. NotifyResourceReattached replays them if the user returns.\n this.navigationService?.NotifyResourceDetached(info.componentRef.instance);\n this.evictIfNeeded();\n return info;\n }\n\n /** @deprecated Use {@link MarkAsDetached}. */\n markAsDetached(resourceType: string, recordId: string, appId: string, discriminator?: string): CachedComponentInfo | null {\n return this.MarkAsDetached(resourceType, recordId, appId, discriminator);\n }\n\n /**\n * Re-key a cached component when its underlying record identity changes.\n *\n * Used when a \"new record\" component (cached at `recordId = ''`) becomes a saved record\n * (now identified by its new PK). Without re-keying, the next \"new record\" request would\n * still resolve to this same cache entry, surfacing the previously-saved record on a form\n * the user expects to be blank.\n *\n * Preserves the live component instance — only the cache key + stored identity change.\n * Returns true if a matching entry was found and re-keyed, false otherwise.\n */\n RekeyComponent(\n resourceType: string,\n oldRecordId: string,\n newRecordId: string,\n appId: string,\n oldDiscriminator?: string,\n newDiscriminator?: string\n ): boolean {\n const oldKey = this.getCacheKey(resourceType, oldRecordId, appId, oldDiscriminator);\n const info = this.cache.get(oldKey);\n if (!info) {\n return false;\n }\n\n // Once a record has a real id, the discriminator no longer matters (the id is unique).\n // Default the new discriminator to undefined unless explicitly provided.\n const newKey = this.getCacheKey(resourceType, newRecordId, appId, newDiscriminator);\n if (oldKey === newKey) {\n return false;\n }\n\n info.resourceRecordId = newRecordId;\n info.keyDiscriminator = newDiscriminator;\n info.lastUsed = new Date();\n if (info.resourceData) {\n info.resourceData.ResourceRecordID = newRecordId;\n }\n\n this.cache.delete(oldKey);\n this.cache.set(newKey, info);\n return true;\n }\n\n /** @deprecated Use {@link RekeyComponent}. */\n rekeyComponent(\n resourceType: string,\n oldRecordId: string,\n newRecordId: string,\n appId: string,\n oldDiscriminator?: string,\n newDiscriminator?: string\n ): boolean {\n return this.RekeyComponent(resourceType, oldRecordId, newRecordId, appId, oldDiscriminator, newDiscriminator);\n }\n\n /**\n * Find a cached component by tab ID and detach it.\n * This is a convenience wrapper for callers that only know the tab ID\n * (e.g., Golden Layout tab close events). It resolves the tab ID to\n * resource identity, then delegates to the identity-based markAsDetached.\n */\n FindAndDetachByTabId(tabId: string): CachedComponentInfo | null {\n const entry = Array.from(this.cache.entries())\n .find(([_, info]) => info.attachedToTabId === tabId);\n\n if (!entry) return null;\n\n const [_, info] = entry;\n return this.MarkAsDetached(info.resourceType, info.resourceRecordId, info.applicationId, info.keyDiscriminator);\n }\n\n /** @deprecated Use {@link FindAndDetachByTabId}. */\n findAndDetachByTabId(tabId: string): CachedComponentInfo | null {\n return this.FindAndDetachByTabId(tabId);\n }\n\n /**\n * Evict least-recently-used detached components when over the limit.\n * Only evicts components that are not currently attached.\n */\n private evictIfNeeded(): void {\n if (ComponentCacheManager.MaxDetachedComponents <= 0) return;\n\n const detached = Array.from(this.cache.entries())\n .filter(([_, info]) => !info.isAttached)\n .sort((a, b) => a[1].lastUsed.getTime() - b[1].lastUsed.getTime());\n\n while (detached.length > ComponentCacheManager.MaxDetachedComponents) {\n const [key, info] = detached.shift()!;\n // Drop the destroyed component's cached agent tools so the registry doesn't retain a dead ref.\n this.navigationService?.ForgetResource(info.componentRef.instance);\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n this.cache.delete(key);\n }\n }\n\n /**\n * Get component info by tab ID (for finding what's attached to a tab).\n * Uses linear scan since tabId is metadata, not a key.\n */\n GetComponentByTabId(tabId: string): CachedComponentInfo | null {\n const entry = Array.from(this.cache.entries())\n .find(([_, info]) => info.attachedToTabId === tabId);\n\n return entry ? entry[1] : null;\n }\n\n /** @deprecated Use {@link GetComponentByTabId}. */\n getComponentByTabId(tabId: string): CachedComponentInfo | null {\n return this.GetComponentByTabId(tabId);\n }\n\n /**\n * Remove and destroy a specific component from cache by resource identity.\n */\n DestroyComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): void {\n const key = this.getCacheKey(resourceType, recordId, appId, discriminator);\n const info = this.cache.get(key);\n\n if (!info) return;\n\n this.navigationService?.ForgetResource(info.componentRef.instance);\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n this.cache.delete(key);\n }\n\n /** @deprecated Use {@link DestroyComponent}. */\n destroyComponent(resourceType: string, recordId: string, appId: string, discriminator?: string): void {\n return this.DestroyComponent(resourceType, recordId, appId, discriminator);\n }\n\n /**\n * Remove and destroy component by tab ID (convenience for Golden Layout tab close).\n */\n DestroyComponentByTabId(tabId: string): void {\n const entry = Array.from(this.cache.entries())\n .find(([_, info]) => info.attachedToTabId === tabId);\n\n if (!entry) return;\n\n const [key, info] = entry;\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n this.cache.delete(key);\n }\n\n /** @deprecated Use {@link DestroyComponentByTabId}. */\n destroyComponentByTabId(tabId: string): void {\n return this.DestroyComponentByTabId(tabId);\n }\n\n /**\n * Clear the entire cache, destroying all components.\n * Call this on user logout or app shutdown.\n */\n ClearCache(): void {\n this.cache.forEach(info => {\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n });\n this.cache.clear();\n }\n\n /** @deprecated Use {@link ClearCache}. */\n clearCache(): void {\n return this.ClearCache();\n }\n\n /**\n * Selectively clear cached components matching a predicate.\n * Components that match are destroyed; those that don't are kept.\n *\n * Use this for tenant switching: clear org-scoped components while\n * keeping system/global components alive.\n *\n * @param predicate Return true for components that should be destroyed.\n * @returns Number of components destroyed.\n */\n ClearCacheByPredicate(predicate: (info: CachedComponentInfo) => boolean): number {\n let destroyed = 0;\n const toRemove: string[] = [];\n\n this.cache.forEach((info, key) => {\n if (predicate(info)) {\n this.appRef.detachView(info.componentRef.hostView);\n info.componentRef.destroy();\n toRemove.push(key);\n destroyed++;\n }\n });\n\n for (const key of toRemove) {\n this.cache.delete(key);\n }\n\n return destroyed;\n }\n\n /**\n * Get cache statistics for debugging.\n */\n GetCacheStats(): {\n total: number;\n attached: number;\n detached: number;\n byResourceType: Map<string, number>;\n } {\n const stats = {\n total: this.cache.size,\n attached: 0,\n detached: 0,\n byResourceType: new Map<string, number>()\n };\n\n this.cache.forEach(info => {\n if (info.isAttached) {\n stats.attached++;\n } else {\n stats.detached++;\n }\n\n const count = stats.byResourceType.get(info.resourceType) || 0;\n stats.byResourceType.set(info.resourceType, count + 1);\n });\n\n return stats;\n }\n\n /** @deprecated Use {@link GetCacheStats}. */\n getCacheStats(): {\n total: number;\n attached: number;\n detached: number;\n byResourceType: Map<string, number>;\n } {\n return this.GetCacheStats();\n }\n}\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { OnInit, OnDestroy, AfterViewInit, ElementRef, ApplicationRef, EnvironmentInjector, ChangeDetectorRef, EventEmitter } from '@angular/core';
|
|
2
2
|
import { GoldenLayoutManager, WorkspaceStateManager, ApplicationManager } from '@memberjunction/ng-base-application';
|
|
3
|
+
import type { DeletedRecordRef } from '@memberjunction/ng-shared';
|
|
3
4
|
import { RecordSwitcherEntry } from '../record-open/record-switcher-sheet.component';
|
|
4
5
|
import { CachedComponentInfo } from './component-cache-manager';
|
|
5
6
|
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
@@ -65,6 +66,7 @@ export declare class TabContainerComponent extends BaseAngularComponent implemen
|
|
|
65
66
|
layoutInitError: EventEmitter<void>;
|
|
66
67
|
private pinService;
|
|
67
68
|
private navigationService;
|
|
69
|
+
private tabService;
|
|
68
70
|
private recordSwitcher;
|
|
69
71
|
private subscriptions;
|
|
70
72
|
private layoutInitRetryCount;
|
|
@@ -186,6 +188,7 @@ export declare class TabContainerComponent extends BaseAngularComponent implemen
|
|
|
186
188
|
private updateRecordSurfaceState;
|
|
187
189
|
/** One switcher sheet row: identity + origin subtitle (crumb semantics) */
|
|
188
190
|
private buildSwitcherEntry;
|
|
191
|
+
private resolveTabTitle;
|
|
189
192
|
/** True while the RECORDS region is the visible surface (active tab is a record) */
|
|
190
193
|
ShowRecordsRegion: boolean;
|
|
191
194
|
/**
|
|
@@ -203,6 +206,7 @@ export declare class TabContainerComponent extends BaseAngularComponent implemen
|
|
|
203
206
|
* first so even the re-entrant case gets picked up by the in-flight pass.
|
|
204
207
|
*/
|
|
205
208
|
private flushRegionCd;
|
|
209
|
+
private recordsLayoutInitRetryCount;
|
|
206
210
|
/**
|
|
207
211
|
* Keep the RECORDS region in lockstep with the workspace configuration:
|
|
208
212
|
* resolve region visibility (active tab is a record), lazily initialize the
|
|
@@ -446,6 +450,37 @@ export declare class TabContainerComponent extends BaseAngularComponent implemen
|
|
|
446
450
|
* replaces the now-temp tab. End result: user lands on the app's home/default view.
|
|
447
451
|
*/
|
|
448
452
|
private handleResourceCloseRequested;
|
|
453
|
+
/**
|
|
454
|
+
* Close a tab and DESTROY (not just detach) its cached component.
|
|
455
|
+
*
|
|
456
|
+
* The default tab-close path detaches and keeps components alive for reuse —
|
|
457
|
+
* but a discarded or deleted record's form is exactly what we DON'T want to
|
|
458
|
+
* keep: it holds a stale BaseEntity in view mode. The next "Create New Record"
|
|
459
|
+
* click for the same entity would otherwise hit the cache discriminator
|
|
460
|
+
* lookup, find this stale component, and reattach it — surfacing a blank form
|
|
461
|
+
* in view mode instead of a fresh edit-mode form. Destroy here forces a cache
|
|
462
|
+
* miss on the next open.
|
|
463
|
+
*/
|
|
464
|
+
private closeTabDestroyingComponent;
|
|
465
|
+
/** Open an app's default tab (its home / landing surface), if it has one. */
|
|
466
|
+
private openAppDefaultTab;
|
|
467
|
+
/**
|
|
468
|
+
* A record was deleted in this session — from its own form, a custom form, a
|
|
469
|
+
* grid row, or any other `BaseEntity.Delete()`. Nothing may keep showing it:
|
|
470
|
+
*
|
|
471
|
+
* 1. Other record tabs whose origin chain names it get the record spliced out,
|
|
472
|
+
* so their crumb returns to where the user was before it.
|
|
473
|
+
* 2. Every record tab showing it closes, its cached form destroyed.
|
|
474
|
+
* 3. If one of those was the ACTIVE tab, the user is taken back to where it
|
|
475
|
+
* was opened from; with no origin (e.g. loaded straight from a URL), to the
|
|
476
|
+
* Home dashboard, or the tab's own app landing page when there is no Home
|
|
477
|
+
* app. Its URL's history entry is replaced so browser Back can't reopen it.
|
|
478
|
+
*
|
|
479
|
+
* Inactive tabs showing it just close: the user wasn't looking at them.
|
|
480
|
+
*/
|
|
481
|
+
HandleRecordDeleted(record: DeletedRecordRef): Promise<void>;
|
|
482
|
+
/** Where the user goes after the record they were looking at is deleted. */
|
|
483
|
+
private leaveDeletedRecord;
|
|
449
484
|
/**
|
|
450
485
|
* Generate a signature for tab content to detect when content changes
|
|
451
486
|
* This is needed because in single-resource mode, the same tab ID can have different content
|
|
@@ -691,6 +726,7 @@ export declare class TabContainerComponent extends BaseAngularComponent implemen
|
|
|
691
726
|
* shell's first-resource-load gate is satisfied so the loading overlay clears.
|
|
692
727
|
*/
|
|
693
728
|
private renderMissingResourceError;
|
|
729
|
+
private resolveCacheDiscriminator;
|
|
694
730
|
static ɵfac: i0.ɵɵFactoryDeclaration<TabContainerComponent, never>;
|
|
695
731
|
static ɵcmp: i0.ɵɵComponentDeclaration<TabContainerComponent, "mj-tab-container", never, {}, { "FirstResourceLoadComplete": "FirstResourceLoadComplete"; "firstResourceLoadComplete": "firstResourceLoadComplete"; "LayoutInitError": "LayoutInitError"; "layoutInitError": "layoutInitError"; }, never, never, false, never>;
|
|
696
732
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-container.component.d.ts","sourceRoot":"","sources":["../../../../../src/lib/shell/components/tabs/tab-container.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,MAAM,EACN,SAAS,EACT,aAAa,EAEb,UAAU,EACV,cAAc,EACd,mBAAmB,EAKnB,iBAAiB,EAGjB,YAAY,EAEb,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"tab-container.component.d.ts","sourceRoot":"","sources":["../../../../../src/lib/shell/components/tabs/tab-container.component.ts"],"names":[],"mappings":"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"}
|