@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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-tasks-resource.component.js","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/chat-tasks-resource.component.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAa,SAAS,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"chat-tasks-resource.component.js","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/chat-tasks-resource.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, ViewEncapsulation, OnDestroy, ViewChild } from '@angular/core';\nimport { CompositeKey, UserInfo } from '@memberjunction/core';\nimport { RegisterClass } from '@memberjunction/global';\nimport { BaseResourceComponent } from '@memberjunction/ng-shared';\nimport { ResourceData } from '@memberjunction/core-entities';\nimport { MJEnvironmentEntityExtended } from '@memberjunction/core-entities';\nimport { TasksFullViewComponent } from '@memberjunction/ng-conversations';\n/**\n * Chat Tasks Resource - displays the tasks full view for tab-based display\n * Extends BaseResourceComponent to work with the resource type system\n * Shows all tasks associated with conversations and artifacts\n * Supports URL deep-linking via taskId query parameter\n */\n@RegisterClass(BaseResourceComponent, 'ChatTasksResource')\n@Component({\n standalone: false,\n selector: 'mj-chat-tasks-resource',\n template: `\n <div class=\"chat-tasks-container\">\n @if (currentUser) {\n <mj-tasks-full-view\n #tasksView\n [environmentId]=\"environmentId\"\n [currentUser]=\"currentUser\"\n [baseFilter]=\"'1=1'\"\n [activeTaskId]=\"activeTaskId\"\n (taskSelected)=\"onTaskSelected($event)\"\n (openEntityRecord)=\"OnOpenEntityRecord($event)\"\n style=\"height: 100%;\">\n </mj-tasks-full-view>\n }\n </div>\n `,\n styles: [`\n :host {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n }\n\n .chat-tasks-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n flex: 1;\n overflow: hidden;\n }\n `],\n encapsulation: ViewEncapsulation.None\n})\nexport class ChatTasksResource extends BaseResourceComponent implements OnDestroy {\n @ViewChild('tasksView') TasksView?: TasksFullViewComponent;\n\n /** @deprecated Use {@link TasksView}. */\n get tasksView(): TasksFullViewComponent | undefined {\n return this.TasksView;\n }\n /** @deprecated Use {@link TasksView}. */\n set tasksView(value: TasksFullViewComponent | undefined) {\n this.TasksView = value;\n }\n\n public CurrentUser: UserInfo | null = null;\n\n /** @deprecated Use {@link CurrentUser}. */\n public get currentUser(): UserInfo | null {\n return this.CurrentUser;\n }\n /** @deprecated Use {@link CurrentUser}. */\n public set currentUser(value: UserInfo | null) {\n this.CurrentUser = value;\n }\n public activeTaskId?: string; // case-violation-ok-legacy-back-compat: an accessor cannot be optional, so a stub would turn this into a required member\n\n ngOnInit() {\n super.ngOnInit();\n const md = this.ProviderToUse;\n this.CurrentUser = md.CurrentUser;\n\n // Apply initial state from query params or tab config\n const params = this.GetQueryParams();\n const config = this.Data?.Configuration;\n const taskId = params['taskId'] || (config?.taskId as string);\n if (taskId) {\n this.activeTaskId = taskId;\n }\n\n // Push initial state to URL\n this.UpdateQueryParams({ taskId: this.activeTaskId ?? null });\n\n // Notify load complete after user is set\n setTimeout(() => {\n this.NotifyLoadComplete();\n }, 100);\n }\n\n ngOnDestroy() {\n super.ngOnDestroy();\n }\n\n protected override OnQueryParamsChanged(params: Record<string, string>, source: 'popstate' | 'deeplink'): void {\n this.activeTaskId = params['taskId'] || undefined;\n }\n\n /**\n * Handle task selection from the tasks view.\n */\n OnTaskSelected(taskId: string | null): void {\n this.activeTaskId = taskId || undefined;\n this.UpdateQueryParams({ taskId: this.activeTaskId ?? null });\n }\n\n /** @deprecated Use {@link OnTaskSelected}. */\n onTaskSelected(taskId: string | null): void {\n return this.OnTaskSelected(taskId);\n }\n\n /**\n * Handle entity record navigation requests from the AI Agent Workflows & Tasks Command Center\n * (e.g. opening an AI Agent or AI Agent Run record in Explorer).\n */\n OnOpenEntityRecord(event: { entityName: string; recordId: string }): void {\n if (!event?.entityName || !event?.recordId) {\n return;\n }\n // The entity arrives at runtime, so the key is built against its real primary key; a\n // composite key's \"F1|v1||F2|v2\" segment round-trips too.\n const key = CompositeKey.FromURLSegment(this.ProviderToUse.EntityByName(event.entityName), event.recordId);\n this.navigationService.OpenEntityRecord(event.entityName, key);\n }\n\n\n /**\n * Get the environment ID from configuration or use default\n */\n get EnvironmentId(): string {\n return this.Data?.Configuration?.environmentId || MJEnvironmentEntityExtended.DefaultEnvironmentID;\n }\n\n /** @deprecated Use {@link EnvironmentId}. */\n get environmentId(): string {\n return this.EnvironmentId;\n }\n\n /**\n * Get the display name for chat tasks\n */\n async GetResourceDisplayName(data: ResourceData): Promise<string> {\n return 'Tasks';\n }\n\n /**\n * Get the icon class for chat tasks\n */\n async GetResourceIconClass(data: ResourceData): Promise<string> {\n return 'fa-solid fa-tasks';\n }\n}\n"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { EntityFormMode } from '@memberjunction/ng-base-forms';
|
|
2
|
+
/**
|
|
3
|
+
* A record tab's form mode (MJ#4755) lives in its `form` query param, so the
|
|
4
|
+
* URL, back/forward, re-focus and a cached component all see one value.
|
|
5
|
+
* These are the decisions EntityRecordResource makes about it, kept pure so
|
|
6
|
+
* the component stays a thin caller.
|
|
7
|
+
*/
|
|
8
|
+
/** The form mode a tab's query params ask for: `'standard'` only for `form=standard`. */
|
|
9
|
+
export declare function FormModeFromQueryParams(params: Record<string, string>): EntityFormMode;
|
|
10
|
+
/** The query-param patch that records `mode` on the tab (`null` removes the param). */
|
|
11
|
+
export declare function FormModeQueryParams(mode: EntityFormMode): Record<string, string | null>;
|
|
12
|
+
/** The record a tab hosts, or that a record component was opened for. */
|
|
13
|
+
export interface RecordTabIdentity {
|
|
14
|
+
Entity: string | undefined;
|
|
15
|
+
RecordId: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Whether a record component still owns its tab: the live tab hosts the same
|
|
19
|
+
* record (entity compared case- and whitespace-insensitively; record id via
|
|
20
|
+
* UUIDsEqual, like the tab identity guard — a save rewrites the component's id
|
|
21
|
+
* in the server's casing). False for a closed tab. A cached
|
|
22
|
+
* component whose tab id was reused for another record is not the owner, so
|
|
23
|
+
* that tab's `form` param is not addressed to it.
|
|
24
|
+
*/
|
|
25
|
+
export declare function IsRecordTabOwner(liveTab: RecordTabIdentity | null, own: RecordTabIdentity): boolean;
|
|
26
|
+
/** Whatever can switch a mounted form (SingleRecordComponent → the form host). */
|
|
27
|
+
export interface FormModeSwitcher {
|
|
28
|
+
SwitchFormMode(mode: EntityFormMode): boolean;
|
|
29
|
+
}
|
|
30
|
+
/** Outcome of following a query-param change. */
|
|
31
|
+
export interface FormModeReconciliation {
|
|
32
|
+
/** The mode now in effect. */
|
|
33
|
+
Mode: EntityFormMode;
|
|
34
|
+
/** Params to write back when the switch was refused, so the URL matches the screen; else null. */
|
|
35
|
+
WriteBack: Record<string, string | null> | null;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Follow the tab's query params. With a mounted form, switches through the
|
|
39
|
+
* host's guarded `SwitchFormMode`; a refusal (unsaved work) keeps the live
|
|
40
|
+
* mode and asks for a write-back. Without one (`switcher` null), the requested
|
|
41
|
+
* mode is simply adopted — the input binding carries it to the host at mount.
|
|
42
|
+
*/
|
|
43
|
+
export declare function ReconcileFormMode(params: Record<string, string>, current: EntityFormMode, switcher: FormModeSwitcher | null): FormModeReconciliation;
|
|
44
|
+
//# sourceMappingURL=record-form-mode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-form-mode.d.ts","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/record-form-mode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAIpE;;;;;GAKG;AAEH,yFAAyF;AACzF,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,cAAc,CAEtF;AAED,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAEvF;AAED,yEAAyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,EAAE,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAInG;AAED,kFAAkF;AAClF,MAAM,WAAW,gBAAgB;IAC/B,cAAc,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC;CAC/C;AAED,iDAAiD;AACjD,MAAM,WAAW,sBAAsB;IACrC,8BAA8B;IAC9B,IAAI,EAAE,cAAc,CAAC;IACrB,kGAAkG;IAClG,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;CACjD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,gBAAgB,GAAG,IAAI,GAChC,sBAAsB,CAOxB"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { FORM_MODE_QUERY_PARAM, ReadStandardFormQuery } from '@memberjunction/ng-shared';
|
|
2
|
+
import { UUIDsEqual } from '@memberjunction/global';
|
|
3
|
+
/**
|
|
4
|
+
* A record tab's form mode (MJ#4755) lives in its `form` query param, so the
|
|
5
|
+
* URL, back/forward, re-focus and a cached component all see one value.
|
|
6
|
+
* These are the decisions EntityRecordResource makes about it, kept pure so
|
|
7
|
+
* the component stays a thin caller.
|
|
8
|
+
*/
|
|
9
|
+
/** The form mode a tab's query params ask for: `'standard'` only for `form=standard`. */
|
|
10
|
+
export function FormModeFromQueryParams(params) {
|
|
11
|
+
return ReadStandardFormQuery(params) ?? 'default';
|
|
12
|
+
}
|
|
13
|
+
/** The query-param patch that records `mode` on the tab (`null` removes the param). */
|
|
14
|
+
export function FormModeQueryParams(mode) {
|
|
15
|
+
return { [FORM_MODE_QUERY_PARAM]: mode === 'standard' ? 'standard' : null };
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Whether a record component still owns its tab: the live tab hosts the same
|
|
19
|
+
* record (entity compared case- and whitespace-insensitively; record id via
|
|
20
|
+
* UUIDsEqual, like the tab identity guard — a save rewrites the component's id
|
|
21
|
+
* in the server's casing). False for a closed tab. A cached
|
|
22
|
+
* component whose tab id was reused for another record is not the owner, so
|
|
23
|
+
* that tab's `form` param is not addressed to it.
|
|
24
|
+
*/
|
|
25
|
+
export function IsRecordTabOwner(liveTab, own) {
|
|
26
|
+
if (!liveTab)
|
|
27
|
+
return false;
|
|
28
|
+
const entityKey = (entity) => (entity ?? '').trim().toLowerCase();
|
|
29
|
+
return entityKey(liveTab.Entity) === entityKey(own.Entity) && UUIDsEqual(liveTab.RecordId, own.RecordId);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Follow the tab's query params. With a mounted form, switches through the
|
|
33
|
+
* host's guarded `SwitchFormMode`; a refusal (unsaved work) keeps the live
|
|
34
|
+
* mode and asks for a write-back. Without one (`switcher` null), the requested
|
|
35
|
+
* mode is simply adopted — the input binding carries it to the host at mount.
|
|
36
|
+
*/
|
|
37
|
+
export function ReconcileFormMode(params, current, switcher) {
|
|
38
|
+
const desired = FormModeFromQueryParams(params);
|
|
39
|
+
if (desired === current)
|
|
40
|
+
return { Mode: current, WriteBack: null };
|
|
41
|
+
if (!switcher)
|
|
42
|
+
return { Mode: desired, WriteBack: null };
|
|
43
|
+
return switcher.SwitchFormMode(desired)
|
|
44
|
+
? { Mode: desired, WriteBack: null }
|
|
45
|
+
: { Mode: current, WriteBack: FormModeQueryParams(current) };
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=record-form-mode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-form-mode.js","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/record-form-mode.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACzF,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD;;;;;GAKG;AAEH,yFAAyF;AACzF,MAAM,UAAU,uBAAuB,CAAC,MAA8B;IACpE,OAAO,qBAAqB,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC;AACpD,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,mBAAmB,CAAC,IAAoB;IACtD,OAAO,EAAE,CAAC,qBAAqB,CAAC,EAAE,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAC9E,CAAC;AAQD;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAiC,EAAE,GAAsB;IACxF,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAC3B,MAAM,SAAS,GAAG,CAAC,MAA0B,EAAU,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC9F,OAAO,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC3G,CAAC;AAeD;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAA8B,EAC9B,OAAuB,EACvB,QAAiC;IAEjC,MAAM,OAAO,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAChD,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACnE,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACzD,OAAO,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC;QACrC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;QACpC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAC;AACjE,CAAC","sourcesContent":["import type { EntityFormMode } from '@memberjunction/ng-base-forms';\nimport { FORM_MODE_QUERY_PARAM, ReadStandardFormQuery } from '@memberjunction/ng-shared';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * A record tab's form mode (MJ#4755) lives in its `form` query param, so the\n * URL, back/forward, re-focus and a cached component all see one value.\n * These are the decisions EntityRecordResource makes about it, kept pure so\n * the component stays a thin caller.\n */\n\n/** The form mode a tab's query params ask for: `'standard'` only for `form=standard`. */\nexport function FormModeFromQueryParams(params: Record<string, string>): EntityFormMode {\n return ReadStandardFormQuery(params) ?? 'default';\n}\n\n/** The query-param patch that records `mode` on the tab (`null` removes the param). */\nexport function FormModeQueryParams(mode: EntityFormMode): Record<string, string | null> {\n return { [FORM_MODE_QUERY_PARAM]: mode === 'standard' ? 'standard' : null };\n}\n\n/** The record a tab hosts, or that a record component was opened for. */\nexport interface RecordTabIdentity {\n Entity: string | undefined;\n RecordId: string;\n}\n\n/**\n * Whether a record component still owns its tab: the live tab hosts the same\n * record (entity compared case- and whitespace-insensitively; record id via\n * UUIDsEqual, like the tab identity guard — a save rewrites the component's id\n * in the server's casing). False for a closed tab. A cached\n * component whose tab id was reused for another record is not the owner, so\n * that tab's `form` param is not addressed to it.\n */\nexport function IsRecordTabOwner(liveTab: RecordTabIdentity | null, own: RecordTabIdentity): boolean {\n if (!liveTab) return false;\n const entityKey = (entity: string | undefined): string => (entity ?? '').trim().toLowerCase();\n return entityKey(liveTab.Entity) === entityKey(own.Entity) && UUIDsEqual(liveTab.RecordId, own.RecordId);\n}\n\n/** Whatever can switch a mounted form (SingleRecordComponent → the form host). */\nexport interface FormModeSwitcher {\n SwitchFormMode(mode: EntityFormMode): boolean;\n}\n\n/** Outcome of following a query-param change. */\nexport interface FormModeReconciliation {\n /** The mode now in effect. */\n Mode: EntityFormMode;\n /** Params to write back when the switch was refused, so the URL matches the screen; else null. */\n WriteBack: Record<string, string | null> | null;\n}\n\n/**\n * Follow the tab's query params. With a mounted form, switches through the\n * host's guarded `SwitchFormMode`; a refusal (unsaved work) keeps the live\n * mode and asks for a write-back. Without one (`switcher` null), the requested\n * mode is simply adopted — the input binding carries it to the host at mount.\n */\nexport function ReconcileFormMode(\n params: Record<string, string>,\n current: EntityFormMode,\n switcher: FormModeSwitcher | null\n): FormModeReconciliation {\n const desired = FormModeFromQueryParams(params);\n if (desired === current) return { Mode: current, WriteBack: null };\n if (!switcher) return { Mode: desired, WriteBack: null };\n return switcher.SwitchFormMode(desired)\n ? { Mode: desired, WriteBack: null }\n : { Mode: current, WriteBack: FormModeQueryParams(current) };\n}\n"]}
|
|
@@ -2,7 +2,7 @@ import { OnInit } from '@angular/core';
|
|
|
2
2
|
import { BaseResourceComponent } from '@memberjunction/ng-shared';
|
|
3
3
|
import { ResourceData } from '@memberjunction/core-entities';
|
|
4
4
|
import { CompositeKey, IMetadataProvider } from '@memberjunction/core';
|
|
5
|
-
import { type FormCompositionSnapshot } from '@memberjunction/ng-base-forms';
|
|
5
|
+
import { EntityFormMode, type FormCompositionSnapshot } from '@memberjunction/ng-base-forms';
|
|
6
6
|
import * as i0 from "@angular/core";
|
|
7
7
|
export declare class EntityRecordResource extends BaseResourceComponent implements OnInit {
|
|
8
8
|
private singleRecord?;
|
|
@@ -27,11 +27,66 @@ export declare class EntityRecordResource extends BaseResourceComponent implemen
|
|
|
27
27
|
* the surface the user sees, and returning to this tab publishes it again.
|
|
28
28
|
*/
|
|
29
29
|
OnCompositionChanged(snapshot: FormCompositionSnapshot): void;
|
|
30
|
+
/** Ensure the active form's agent context is published immediately when opening AI chat. */
|
|
31
|
+
OnAskAIRequested(): void;
|
|
30
32
|
/** Publishes the kept context while this tab is on screen and the shell's context lacks it. */
|
|
31
33
|
private publishAgentContext;
|
|
32
34
|
/** True while the cache has this tab attached and it is the workspace's active tab. */
|
|
33
35
|
private isOnScreen;
|
|
34
36
|
get PrimaryKey(): CompositeKey;
|
|
37
|
+
/**
|
|
38
|
+
* Custom vs standard form (MJ#4755). The tab's `form` query param is the
|
|
39
|
+
* source of truth — the URL, back/forward, tab re-focus and a cached
|
|
40
|
+
* component all read it — and this tracks the mode actually on screen.
|
|
41
|
+
*/
|
|
42
|
+
private _formMode;
|
|
43
|
+
/** The mode bound to the form host: seeded from the tab's params, so a deep link mounts the standard form directly. */
|
|
44
|
+
get FormMode(): EntityFormMode;
|
|
45
|
+
set Data(value: ResourceData);
|
|
46
|
+
get Data(): ResourceData;
|
|
47
|
+
private destroyed;
|
|
48
|
+
ngOnDestroy(): void;
|
|
49
|
+
/**
|
|
50
|
+
* Follow the tab's `form` param — a standard-form open that dedup routed
|
|
51
|
+
* to this tab, a deep link, back/forward, or a plain URL clearing it.
|
|
52
|
+
*/
|
|
53
|
+
protected OnQueryParamsChanged(params: Record<string, string>, _source: 'popstate' | 'deeplink'): void;
|
|
54
|
+
/**
|
|
55
|
+
* Re-home after a cache reattach. A reattach to a DIFFERENT tab id replays
|
|
56
|
+
* that tab's params in the base class. A same-id reattach does not — and
|
|
57
|
+
* a delivery this component skipped while its tab hosted another record
|
|
58
|
+
* (see {@link applyFormModeParams}) is recorded as delivered, so it would
|
|
59
|
+
* never come again. Re-read the live params in that case.
|
|
60
|
+
*/
|
|
61
|
+
RebindTabId(tabId: string): void;
|
|
62
|
+
/**
|
|
63
|
+
* The host switched forms and told us: the strip, a programmatic
|
|
64
|
+
* {@link SingleRecordComponent.SwitchFormMode} (including our own, from
|
|
65
|
+
* {@link applyFormModeParams}), or an interactive-variant pick (back to
|
|
66
|
+
* default). Record it on the tab, and so the URL. For our own switch this
|
|
67
|
+
* runs inside a query-param delivery, where UpdateQueryParams is suppressed
|
|
68
|
+
* — correct, since the tab already says it, and it cannot loop.
|
|
69
|
+
*/
|
|
70
|
+
OnFormModeChange(mode: EntityFormMode): void;
|
|
71
|
+
/**
|
|
72
|
+
* Reconcile the mounted form with a tab's `form` param. Ignored unless this
|
|
73
|
+
* component still owns its tab: a cached (detached) record stays subscribed
|
|
74
|
+
* to its birth tab id, which may now host another record — whose params
|
|
75
|
+
* would otherwise reload this form in the background or raise a spurious
|
|
76
|
+
* "save or discard" warning.
|
|
77
|
+
*/
|
|
78
|
+
private applyFormModeParams;
|
|
79
|
+
/**
|
|
80
|
+
* The switch was refused (unsaved work; the host warned the user): put the
|
|
81
|
+
* URL back to what is on screen. Deferred a microtask because
|
|
82
|
+
* UpdateQueryParams is suppressed for the whole delivery that called us;
|
|
83
|
+
* re-checked then, since the component may be gone or its tab reused.
|
|
84
|
+
*/
|
|
85
|
+
private writeBackRefusedMode;
|
|
86
|
+
/** Re-read the live tab's params and reconcile (same-id cache reattach). */
|
|
87
|
+
private resyncFormModeFromTab;
|
|
88
|
+
/** Whether the live tab still hosts this component's record. */
|
|
89
|
+
private ownsTab;
|
|
35
90
|
static GetPrimaryKey(data: ResourceData, provider?: IMetadataProvider): CompositeKey;
|
|
36
91
|
GetResourceDisplayName(data: ResourceData): Promise<string>;
|
|
37
92
|
GetResourceIconClass(data: ResourceData): Promise<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-resource.component.d.ts","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/record-resource.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,MAAM,EAAqB,MAAM,eAAe,CAAC;AAGrE,OAAO,EAAE,qBAAqB,
|
|
1
|
+
{"version":3,"file":"record-resource.component.d.ts","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/record-resource.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,MAAM,EAAqB,MAAM,eAAe,CAAC;AAGrE,OAAO,EAAE,qBAAqB,EAAkC,MAAM,2BAA2B,CAAC;AAElG,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,OAAO,EAAY,YAAY,EAAc,iBAAiB,EAAoC,MAAM,sBAAsB,CAAC;AAG/H,OAAO,EAAyB,cAAc,EAAyB,KAAK,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;;AAC3I,qBAOa,oBAAqB,SAAQ,qBAAsB,YAAW,MAAM;IAC3C,OAAO,CAAC,YAAY,CAAC,CAAwB;IAE/E,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;IAE3D,kFAAkF;IAClF,OAAO,CAAC,YAAY,CAA2C;IAE/D,wGAAwG;IACxF,SAAS,IAAI,OAAO;IAIpC,uFAAuF;IAChF,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAItC,QAAQ,IAAI,IAAI;IAahC;;;;;;;;;;;OAWG;IACI,oBAAoB,CAAC,QAAQ,EAAE,uBAAuB,GAAG,IAAI;IAKpE,4FAA4F;IACrF,gBAAgB,IAAI,IAAI;IAI/B,+FAA+F;IAC/F,OAAO,CAAC,mBAAmB;IAO3B,uFAAuF;IACvF,OAAO,CAAC,UAAU;IAMlB,IAAW,UAAU,IAAI,YAAY,CAEpC;IAED;;;;OAIG;IACH,OAAO,CAAC,SAAS,CAA6B;IAE9C,uHAAuH;IACvH,IAAW,QAAQ,IAAI,cAAc,CAEpC;IAED,IAAoB,IAAI,CAAC,KAAK,EAAE,YAAY,EAQ3C;IACD,IAAoB,IAAI,IAAI,YAAY,CAEvC;IAED,OAAO,CAAC,SAAS,CAAS;IAEV,WAAW,IAAI,IAAI;IAKnC;;;OAGG;cACgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI;IAI/G;;;;;;OAMG;IACa,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAQhD;;;;;;;OAOG;IACI,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IAKnD;;;;;;OAMG;IACH,OAAO,CAAC,mBAAmB;IAS3B;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAY5B,4EAA4E;IAC5E,OAAO,CAAC,qBAAqB;IAS7B,gEAAgE;IAChE,OAAO,CAAC,OAAO;WASD,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,YAAY;IA4BrF,sBAAsB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IA2B3D,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;yCAzPtD,oBAAoB;2CAApB,oBAAoB;CAsQhC"}
|
|
@@ -7,11 +7,12 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var EntityRecordResource_1;
|
|
8
8
|
import { Component, ViewChild, inject } from '@angular/core';
|
|
9
9
|
import { asapScheduler, merge } from 'rxjs';
|
|
10
|
-
import { filter, observeOn, takeUntil } from 'rxjs/operators';
|
|
11
|
-
import { BaseResourceComponent } from '@memberjunction/ng-shared';
|
|
10
|
+
import { filter, observeOn, take, takeUntil } from 'rxjs/operators';
|
|
11
|
+
import { BaseResourceComponent, FormatEntityRecordDisplayTitle } from '@memberjunction/ng-shared';
|
|
12
12
|
import { WorkspaceStateManager } from '@memberjunction/ng-base-application';
|
|
13
13
|
import { RegisterClass } from '@memberjunction/global';
|
|
14
|
-
import { Metadata, CompositeKey, IsNewEntityRecordUrlId } from '@memberjunction/core';
|
|
14
|
+
import { Metadata, CompositeKey, IsNewEntityRecordUrlId, LogError } from '@memberjunction/core';
|
|
15
|
+
import { FormModeFromQueryParams, FormModeQueryParams, IsRecordTabOwner, ReconcileFormMode } from './record-form-mode';
|
|
15
16
|
import { SingleRecordComponent } from '../single-record/single-record.component';
|
|
16
17
|
import { BuildFormAgentContext } from '@memberjunction/ng-base-forms';
|
|
17
18
|
import * as i0 from "@angular/core";
|
|
@@ -55,6 +56,10 @@ let EntityRecordResource = class EntityRecordResource extends BaseResourceCompon
|
|
|
55
56
|
this.agentContext = { Form: BuildFormAgentContext(snapshot) };
|
|
56
57
|
this.publishAgentContext();
|
|
57
58
|
}
|
|
59
|
+
/** Ensure the active form's agent context is published immediately when opening AI chat. */
|
|
60
|
+
OnAskAIRequested() {
|
|
61
|
+
this.publishAgentContext();
|
|
62
|
+
}
|
|
58
63
|
/** Publishes the kept context while this tab is on screen and the shell's context lacks it. */
|
|
59
64
|
publishAgentContext() {
|
|
60
65
|
const context = this.agentContext;
|
|
@@ -74,6 +79,116 @@ let EntityRecordResource = class EntityRecordResource extends BaseResourceCompon
|
|
|
74
79
|
get PrimaryKey() {
|
|
75
80
|
return EntityRecordResource_1.GetPrimaryKey(this.Data, this.ProviderToUse);
|
|
76
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Custom vs standard form (MJ#4755). The tab's `form` query param is the
|
|
84
|
+
* source of truth — the URL, back/forward, tab re-focus and a cached
|
|
85
|
+
* component all read it — and this tracks the mode actually on screen.
|
|
86
|
+
*/
|
|
87
|
+
_formMode = 'default';
|
|
88
|
+
/** The mode bound to the form host: seeded from the tab's params, so a deep link mounts the standard form directly. */
|
|
89
|
+
get FormMode() {
|
|
90
|
+
return this._formMode;
|
|
91
|
+
}
|
|
92
|
+
set Data(value) {
|
|
93
|
+
super.Data = value;
|
|
94
|
+
// Seed only before the form mounts: afterwards a changed input would
|
|
95
|
+
// remount the host WITHOUT its unsaved-work guard. Later changes arrive
|
|
96
|
+
// through OnQueryParamsChanged instead.
|
|
97
|
+
if (!this.singleRecord) {
|
|
98
|
+
this._formMode = FormModeFromQueryParams(this.GetQueryParams());
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
get Data() {
|
|
102
|
+
return super.Data;
|
|
103
|
+
}
|
|
104
|
+
destroyed = false;
|
|
105
|
+
ngOnDestroy() {
|
|
106
|
+
this.destroyed = true;
|
|
107
|
+
super.ngOnDestroy();
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Follow the tab's `form` param — a standard-form open that dedup routed
|
|
111
|
+
* to this tab, a deep link, back/forward, or a plain URL clearing it.
|
|
112
|
+
*/
|
|
113
|
+
OnQueryParamsChanged(params, _source) {
|
|
114
|
+
this.applyFormModeParams(params);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Re-home after a cache reattach. A reattach to a DIFFERENT tab id replays
|
|
118
|
+
* that tab's params in the base class. A same-id reattach does not — and
|
|
119
|
+
* a delivery this component skipped while its tab hosted another record
|
|
120
|
+
* (see {@link applyFormModeParams}) is recorded as delivered, so it would
|
|
121
|
+
* never come again. Re-read the live params in that case.
|
|
122
|
+
*/
|
|
123
|
+
RebindTabId(tabId) {
|
|
124
|
+
const sameTab = this.getTabId() === tabId;
|
|
125
|
+
super.RebindTabId(tabId);
|
|
126
|
+
if (sameTab) {
|
|
127
|
+
this.resyncFormModeFromTab();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The host switched forms and told us: the strip, a programmatic
|
|
132
|
+
* {@link SingleRecordComponent.SwitchFormMode} (including our own, from
|
|
133
|
+
* {@link applyFormModeParams}), or an interactive-variant pick (back to
|
|
134
|
+
* default). Record it on the tab, and so the URL. For our own switch this
|
|
135
|
+
* runs inside a query-param delivery, where UpdateQueryParams is suppressed
|
|
136
|
+
* — correct, since the tab already says it, and it cannot loop.
|
|
137
|
+
*/
|
|
138
|
+
OnFormModeChange(mode) {
|
|
139
|
+
this._formMode = mode;
|
|
140
|
+
this.UpdateQueryParams(FormModeQueryParams(mode));
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Reconcile the mounted form with a tab's `form` param. Ignored unless this
|
|
144
|
+
* component still owns its tab: a cached (detached) record stays subscribed
|
|
145
|
+
* to its birth tab id, which may now host another record — whose params
|
|
146
|
+
* would otherwise reload this form in the background or raise a spurious
|
|
147
|
+
* "save or discard" warning.
|
|
148
|
+
*/
|
|
149
|
+
applyFormModeParams(params) {
|
|
150
|
+
if (!this.ownsTab())
|
|
151
|
+
return;
|
|
152
|
+
const result = ReconcileFormMode(params, this._formMode, this.singleRecord ?? null);
|
|
153
|
+
this._formMode = result.Mode;
|
|
154
|
+
if (result.WriteBack) {
|
|
155
|
+
this.writeBackRefusedMode(result.WriteBack);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The switch was refused (unsaved work; the host warned the user): put the
|
|
160
|
+
* URL back to what is on screen. Deferred a microtask because
|
|
161
|
+
* UpdateQueryParams is suppressed for the whole delivery that called us;
|
|
162
|
+
* re-checked then, since the component may be gone or its tab reused.
|
|
163
|
+
*/
|
|
164
|
+
writeBackRefusedMode(writeBack) {
|
|
165
|
+
Promise.resolve()
|
|
166
|
+
.then(() => {
|
|
167
|
+
if (!this.destroyed && this.ownsTab()) {
|
|
168
|
+
this.UpdateQueryParams(writeBack);
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
.catch((err) => {
|
|
172
|
+
LogError(`EntityRecordResource: could not restore the form-mode query param for "${this.Data?.Configuration?.Entity}" (tab ${this.getTabId()}): ${err instanceof Error ? err.message : String(err)}`);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/** Re-read the live tab's params and reconcile (same-id cache reattach). */
|
|
176
|
+
resyncFormModeFromTab() {
|
|
177
|
+
const tabId = this.getTabId();
|
|
178
|
+
if (!tabId)
|
|
179
|
+
return;
|
|
180
|
+
// The workspace stream replays its current value on subscribe, so take(1) is a synchronous read.
|
|
181
|
+
this.navigationService.ObserveTabQueryParams(tabId)
|
|
182
|
+
.pipe(take(1))
|
|
183
|
+
.subscribe(params => this.applyFormModeParams(params));
|
|
184
|
+
}
|
|
185
|
+
/** Whether the live tab still hosts this component's record. */
|
|
186
|
+
ownsTab() {
|
|
187
|
+
const tabId = this.getTabId();
|
|
188
|
+
if (!tabId)
|
|
189
|
+
return false;
|
|
190
|
+
return IsRecordTabOwner(this.navigationService.GetTabRecordIdentity(tabId), { Entity: this.Data?.Configuration?.Entity, RecordId: this.Data?.ResourceRecordID || this.Data?.Configuration?.recordId || '' });
|
|
191
|
+
}
|
|
77
192
|
static GetPrimaryKey(data, provider) {
|
|
78
193
|
// global-provider-ok: static helper has no component instance scope; falls back to default provider
|
|
79
194
|
const md = (provider ?? Metadata.Provider);
|
|
@@ -116,7 +231,7 @@ let EntityRecordResource = class EntityRecordResource extends BaseResourceCompon
|
|
|
116
231
|
const pk = EntityRecordResource_1.GetPrimaryKey(data, this.ProviderToUse);
|
|
117
232
|
if (pk.HasValue) {
|
|
118
233
|
const name = await md.GetEntityRecordName(data.Configuration.Entity, pk);
|
|
119
|
-
return
|
|
234
|
+
return FormatEntityRecordDisplayTitle(e, data.Configuration.Entity, pk, name);
|
|
120
235
|
}
|
|
121
236
|
else {
|
|
122
237
|
return `New ${e.DisplayNameOrName} Record`;
|
|
@@ -141,12 +256,12 @@ let EntityRecordResource = class EntityRecordResource extends BaseResourceCompon
|
|
|
141
256
|
} if (rf & 2) {
|
|
142
257
|
let _t;
|
|
143
258
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.singleRecord = _t.first);
|
|
144
|
-
} }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 1, vars:
|
|
259
|
+
} }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 1, vars: 4, consts: [[3, "FormModeChange", "loadComplete", "recordSaved", "recordDismissed", "EditModeChanged", "CompositionChanged", "AskAIRequested", "PrimaryKey", "entityName", "newRecordValues", "FormMode"]], template: function EntityRecordResource_Template(rf, ctx) { if (rf & 1) {
|
|
145
260
|
i0.ɵɵelementStart(0, "mj-single-record", 0);
|
|
146
|
-
i0.ɵɵlistener("loadComplete", function EntityRecordResource_Template_mj_single_record_loadComplete_0_listener() { return ctx.NotifyLoadComplete(); })("recordSaved", function EntityRecordResource_Template_mj_single_record_recordSaved_0_listener($event) { return ctx.ResourceRecordSaved($event); })("recordDismissed", function EntityRecordResource_Template_mj_single_record_recordDismissed_0_listener() { return ctx.NotifyCloseRequested(); })("EditModeChanged", function EntityRecordResource_Template_mj_single_record_EditModeChanged_0_listener($event) { return ctx.ResourceEditModeChanged($event); })("CompositionChanged", function EntityRecordResource_Template_mj_single_record_CompositionChanged_0_listener($event) { return ctx.OnCompositionChanged($event); });
|
|
261
|
+
i0.ɵɵlistener("FormModeChange", function EntityRecordResource_Template_mj_single_record_FormModeChange_0_listener($event) { return ctx.OnFormModeChange($event); })("loadComplete", function EntityRecordResource_Template_mj_single_record_loadComplete_0_listener() { return ctx.NotifyLoadComplete(); })("recordSaved", function EntityRecordResource_Template_mj_single_record_recordSaved_0_listener($event) { return ctx.ResourceRecordSaved($event); })("recordDismissed", function EntityRecordResource_Template_mj_single_record_recordDismissed_0_listener() { return ctx.NotifyCloseRequested(); })("EditModeChanged", function EntityRecordResource_Template_mj_single_record_EditModeChanged_0_listener($event) { return ctx.ResourceEditModeChanged($event); })("CompositionChanged", function EntityRecordResource_Template_mj_single_record_CompositionChanged_0_listener($event) { return ctx.OnCompositionChanged($event); })("AskAIRequested", function EntityRecordResource_Template_mj_single_record_AskAIRequested_0_listener() { return ctx.OnAskAIRequested(); });
|
|
147
262
|
i0.ɵɵelementEnd();
|
|
148
263
|
} if (rf & 2) {
|
|
149
|
-
i0.ɵɵproperty("PrimaryKey", ctx.PrimaryKey)("entityName", ctx.Data.Configuration.Entity)("newRecordValues", ctx.Data.Configuration.NewRecordValues);
|
|
264
|
+
i0.ɵɵproperty("PrimaryKey", ctx.PrimaryKey)("entityName", ctx.Data.Configuration.Entity)("newRecordValues", ctx.Data.Configuration.NewRecordValues)("FormMode", ctx.FormMode);
|
|
150
265
|
} }, dependencies: [i1.SingleRecordComponent], styles: ["[_nghost-%COMP%] { display: block; height: 100%; width: 100%; }"] });
|
|
151
266
|
};
|
|
152
267
|
EntityRecordResource = EntityRecordResource_1 = __decorate([
|
|
@@ -155,10 +270,10 @@ EntityRecordResource = EntityRecordResource_1 = __decorate([
|
|
|
155
270
|
export { EntityRecordResource };
|
|
156
271
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(EntityRecordResource, [{
|
|
157
272
|
type: Component,
|
|
158
|
-
args: [{ standalone: false, selector: 'mj-record-resource', template: `<mj-single-record [PrimaryKey]="this.PrimaryKey" [entityName]="Data.Configuration.Entity" [newRecordValues]="Data.Configuration.NewRecordValues" (loadComplete)="NotifyLoadComplete()" (recordSaved)="ResourceRecordSaved($event)" (recordDismissed)="NotifyCloseRequested()" (EditModeChanged)="ResourceEditModeChanged($event)" (CompositionChanged)="OnCompositionChanged($event)"></mj-single-record>`, styles: [":host { display: block; height: 100%; width: 100%; }"] }]
|
|
273
|
+
args: [{ standalone: false, selector: 'mj-record-resource', template: `<mj-single-record [PrimaryKey]="this.PrimaryKey" [entityName]="Data.Configuration.Entity" [newRecordValues]="Data.Configuration.NewRecordValues" [FormMode]="FormMode" (FormModeChange)="OnFormModeChange($event)" (loadComplete)="NotifyLoadComplete()" (recordSaved)="ResourceRecordSaved($event)" (recordDismissed)="NotifyCloseRequested()" (EditModeChanged)="ResourceEditModeChanged($event)" (CompositionChanged)="OnCompositionChanged($event)" (AskAIRequested)="OnAskAIRequested()"></mj-single-record>`, styles: [":host { display: block; height: 100%; width: 100%; }"] }]
|
|
159
274
|
}], null, { singleRecord: [{
|
|
160
275
|
type: ViewChild,
|
|
161
276
|
args: [SingleRecordComponent]
|
|
162
277
|
}] }); })();
|
|
163
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityRecordResource, { className: "EntityRecordResource", filePath: "src/lib/resource-wrappers/record-resource.component.ts", lineNumber:
|
|
278
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityRecordResource, { className: "EntityRecordResource", filePath: "src/lib/resource-wrappers/record-resource.component.ts", lineNumber: 19 }); })();
|
|
164
279
|
//# sourceMappingURL=record-resource.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-resource.component.js","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/record-resource.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, OnInit, ViewChild, inject } from '@angular/core';\nimport { asapScheduler, merge } from 'rxjs';\nimport { filter, observeOn, takeUntil } from 'rxjs/operators';\nimport { BaseResourceComponent } from '@memberjunction/ng-shared';\nimport { WorkspaceStateManager } from '@memberjunction/ng-base-application';\nimport { ResourceData } from '@memberjunction/core-entities';\nimport { RegisterClass } from '@memberjunction/global';\nimport { Metadata, CompositeKey, EntityInfo, IMetadataProvider, IsNewEntityRecordUrlId } from '@memberjunction/core';\nimport { SingleRecordComponent } from '../single-record/single-record.component';\nimport { BuildFormAgentContext, type FormAgentContext, type FormCompositionSnapshot } from '@memberjunction/ng-base-forms';\n@RegisterClass(BaseResourceComponent, 'RecordResource')\n@Component({\n standalone: false,\n selector: 'mj-record-resource',\n styles: [`:host { display: block; height: 100%; width: 100%; }`],\n template: `<mj-single-record [PrimaryKey]=\"this.PrimaryKey\" [entityName]=\"Data.Configuration.Entity\" [newRecordValues]=\"Data.Configuration.NewRecordValues\" (loadComplete)=\"NotifyLoadComplete()\" (recordSaved)=\"ResourceRecordSaved($event)\" (recordDismissed)=\"NotifyCloseRequested()\" (EditModeChanged)=\"ResourceEditModeChanged($event)\" (CompositionChanged)=\"OnCompositionChanged($event)\"></mj-single-record>`\n})\nexport class EntityRecordResource extends BaseResourceComponent implements OnInit {\n @ViewChild(SingleRecordComponent) private singleRecord?: SingleRecordComponent;\n\n private readonly workspace = inject(WorkspaceStateManager);\n\n /** The form context this tab last reported, kept so it can be published again. */\n private agentContext: { Form: FormAgentContext } | null = null;\n\n /** Fallback read for the pool predicate; promotion via ResourceEditModeChanged is the primary guard. */\n public override IsEditing(): boolean {\n return this.singleRecord?.IsEditing() === true;\n }\n\n /** The hosted form entered or left edit mode; the shell promotes the tab on `true`. */\n public ResourceEditModeChanged(editing: boolean): void {\n this.NotifyEditModeChanged(editing);\n }\n\n public override ngOnInit(): void {\n super.ngOnInit();\n // Publish the kept context again when the shell's app context lacks it, or when the cache\n // reattaches this tab. Deferred, so a publish never runs inside another subscriber's\n // AppContextSnapshot$ delivery.\n merge(\n this.navigationService.AppContextSnapshot$,\n this.navigationService.ResourceReattached$.pipe(filter((resource) => resource === this)),\n )\n .pipe(observeOn(asapScheduler), takeUntil(this.destroy$))\n .subscribe(() => this.publishAgentContext());\n }\n\n /**\n * Report the form to the agent context, so an agent asked to build a panel for this record\n * knows what the form shows. The agent gets the compact form ({@link BuildFormAgentContext}):\n * entity, record, which form, and the sections. The full snapshot stays in the browser, in\n * the `FormCompositionRegistry` the apply flow reads.\n *\n * The shell folds this into `AppContextSnapshot.AdditionalContext` and assigns that wholesale,\n * so the last publisher wins app-wide, and it rebuilds the app context without it on every tab\n * switch. So the context is kept here and published only while this tab is the one on screen\n * (attached and the active tab): a background tab's late resolve cannot replace the context of\n * the surface the user sees, and returning to this tab publishes it again.\n */\n public OnCompositionChanged(snapshot: FormCompositionSnapshot): void {\n this.agentContext = { Form: BuildFormAgentContext(snapshot) };\n this.publishAgentContext();\n }\n\n /** Publishes the kept context while this tab is on screen and the shell's context lacks it. */\n private publishAgentContext(): void {\n const context = this.agentContext;\n if (!context || !this.isOnScreen()) return;\n if (this.navigationService.AppContextSnapshot$.value?.AdditionalContext === context) return;\n this.navigationService.SetAgentContext(this, context);\n }\n\n /** True while the cache has this tab attached and it is the workspace's active tab. */\n private isOnScreen(): boolean {\n if (this.navigationService.IsResourceDetached(this)) return false;\n const tabId = this.getTabId();\n return !!tabId && this.workspace.GetActiveTabId() === tabId;\n }\n\n public get PrimaryKey(): CompositeKey {\n return EntityRecordResource.GetPrimaryKey(this.Data, this.ProviderToUse);\n }\n\n public static GetPrimaryKey(data: ResourceData, provider?: IMetadataProvider): CompositeKey {\n // global-provider-ok: static helper has no component instance scope; falls back to default provider\n const md = (provider ?? Metadata.Provider) as IMetadataProvider;\n const requested = (data.Configuration.Entity ?? '').trim();\n // Prefer EntityByName (project rule: never use Entities.find for\n // single-entity lookups — case/whitespace tolerant + O(1) map). Fall\n // back to DisplayName match for callers (AI agents, hand-typed URLs,\n // external CTAs) that pass the human-readable display name instead\n // of the canonical Name (e.g. \"Users\" for \"MJ: Users\").\n let e: EntityInfo | undefined = md.EntityByName(requested);\n if (!e) {\n const lowered = requested.toLowerCase();\n e = md.Entities.find((x: EntityInfo) =>\n (x.DisplayName ?? '').trim().toLowerCase() === lowered);\n }\n if (!e){\n throw new Error(`Entity ${data.Configuration.Entity} not found in metadata (tried Name and DisplayName)`);\n }\n\n if (IsNewEntityRecordUrlId(data.ResourceRecordID)) {\n return new CompositeKey();\n }\n\n let compositeKey: CompositeKey = new CompositeKey();\n compositeKey.LoadFromURLSegment(e, data.ResourceRecordID);\n return compositeKey;\n }\n\n async GetResourceDisplayName(data: ResourceData): Promise<string> {\n if (!data.Configuration.Entity) {\n return '';\n }\n\n const md = this.ProviderToUse;\n let e = md.EntityByName(data.Configuration.Entity);\n if (!e) {\n // Same DisplayName fallback as GetPrimaryKey — keeps the\n // breadcrumb/title in sync when a CTA used the display name.\n const lowered = (data.Configuration.Entity ?? '').trim().toLowerCase();\n e = md.Entities.find((x: EntityInfo) =>\n (x.DisplayName ?? '').trim().toLowerCase() === lowered);\n }\n if (!e) {\n return '';\n }\n\n const pk: CompositeKey = EntityRecordResource.GetPrimaryKey(data, this.ProviderToUse);\n if (pk.HasValue) {\n const name = await md.GetEntityRecordName(data.Configuration.Entity, pk);\n return name ? name : e.DisplayNameOrName;\n } else {\n return `New ${e.DisplayNameOrName} Record`;\n }\n }\n\n async GetResourceIconClass(data: ResourceData): Promise<string> {\n if (!data.Configuration.Entity){\n return ''\n }\n else {\n const md = this.ProviderToUse;\n const e = md.Entities.find(e => e.Name.trim().toLowerCase() === data.Configuration.Entity.trim().toLowerCase());\n if (e)\n return e?.Icon;\n else\n return '';\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"record-resource.component.js","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/record-resource.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, OnInit, ViewChild, inject } from '@angular/core';\nimport { asapScheduler, merge } from 'rxjs';\nimport { filter, observeOn, take, takeUntil } from 'rxjs/operators';\nimport { BaseResourceComponent, FormatEntityRecordDisplayTitle } from '@memberjunction/ng-shared';\nimport { WorkspaceStateManager } from '@memberjunction/ng-base-application';\nimport { ResourceData } from '@memberjunction/core-entities';\nimport { RegisterClass } from '@memberjunction/global';\nimport { Metadata, CompositeKey, EntityInfo, IMetadataProvider, IsNewEntityRecordUrlId, LogError } from '@memberjunction/core';\nimport { FormModeFromQueryParams, FormModeQueryParams, IsRecordTabOwner, ReconcileFormMode } from './record-form-mode';\nimport { SingleRecordComponent } from '../single-record/single-record.component';\nimport { BuildFormAgentContext, EntityFormMode, type FormAgentContext, type FormCompositionSnapshot } from '@memberjunction/ng-base-forms';\n@RegisterClass(BaseResourceComponent, 'RecordResource')\n@Component({\n standalone: false,\n selector: 'mj-record-resource',\n styles: [`:host { display: block; height: 100%; width: 100%; }`],\n template: `<mj-single-record [PrimaryKey]=\"this.PrimaryKey\" [entityName]=\"Data.Configuration.Entity\" [newRecordValues]=\"Data.Configuration.NewRecordValues\" [FormMode]=\"FormMode\" (FormModeChange)=\"OnFormModeChange($event)\" (loadComplete)=\"NotifyLoadComplete()\" (recordSaved)=\"ResourceRecordSaved($event)\" (recordDismissed)=\"NotifyCloseRequested()\" (EditModeChanged)=\"ResourceEditModeChanged($event)\" (CompositionChanged)=\"OnCompositionChanged($event)\" (AskAIRequested)=\"OnAskAIRequested()\"></mj-single-record>`\n})\nexport class EntityRecordResource extends BaseResourceComponent implements OnInit {\n @ViewChild(SingleRecordComponent) private singleRecord?: SingleRecordComponent;\n\n private readonly workspace = inject(WorkspaceStateManager);\n\n /** The form context this tab last reported, kept so it can be published again. */\n private agentContext: { Form: FormAgentContext } | null = null;\n\n /** Fallback read for the pool predicate; promotion via ResourceEditModeChanged is the primary guard. */\n public override IsEditing(): boolean {\n return this.singleRecord?.IsEditing() === true;\n }\n\n /** The hosted form entered or left edit mode; the shell promotes the tab on `true`. */\n public ResourceEditModeChanged(editing: boolean): void {\n this.NotifyEditModeChanged(editing);\n }\n\n public override ngOnInit(): void {\n super.ngOnInit();\n // Publish the kept context again when the shell's app context lacks it, or when the cache\n // reattaches this tab. Deferred, so a publish never runs inside another subscriber's\n // AppContextSnapshot$ delivery.\n merge(\n this.navigationService.AppContextSnapshot$,\n this.navigationService.ResourceReattached$.pipe(filter((resource) => resource === this)),\n )\n .pipe(observeOn(asapScheduler), takeUntil(this.destroy$))\n .subscribe(() => this.publishAgentContext());\n }\n\n /**\n * Report the form to the agent context, so an agent asked to build a panel for this record\n * knows what the form shows. The agent gets the compact form ({@link BuildFormAgentContext}):\n * entity, record, which form, and the sections. The full snapshot stays in the browser, in\n * the `FormCompositionRegistry` the apply flow reads.\n *\n * The shell folds this into `AppContextSnapshot.AdditionalContext` and assigns that wholesale,\n * so the last publisher wins app-wide, and it rebuilds the app context without it on every tab\n * switch. So the context is kept here and published only while this tab is the one on screen\n * (attached and the active tab): a background tab's late resolve cannot replace the context of\n * the surface the user sees, and returning to this tab publishes it again.\n */\n public OnCompositionChanged(snapshot: FormCompositionSnapshot): void {\n this.agentContext = { Form: BuildFormAgentContext(snapshot) };\n this.publishAgentContext();\n }\n\n /** Ensure the active form's agent context is published immediately when opening AI chat. */\n public OnAskAIRequested(): void {\n this.publishAgentContext();\n }\n\n /** Publishes the kept context while this tab is on screen and the shell's context lacks it. */\n private publishAgentContext(): void {\n const context = this.agentContext;\n if (!context || !this.isOnScreen()) return;\n if (this.navigationService.AppContextSnapshot$.value?.AdditionalContext === context) return;\n this.navigationService.SetAgentContext(this, context);\n }\n\n /** True while the cache has this tab attached and it is the workspace's active tab. */\n private isOnScreen(): boolean {\n if (this.navigationService.IsResourceDetached(this)) return false;\n const tabId = this.getTabId();\n return !!tabId && this.workspace.GetActiveTabId() === tabId;\n }\n\n public get PrimaryKey(): CompositeKey {\n return EntityRecordResource.GetPrimaryKey(this.Data, this.ProviderToUse);\n }\n\n /**\n * Custom vs standard form (MJ#4755). The tab's `form` query param is the\n * source of truth — the URL, back/forward, tab re-focus and a cached\n * component all read it — and this tracks the mode actually on screen.\n */\n private _formMode: EntityFormMode = 'default';\n\n /** The mode bound to the form host: seeded from the tab's params, so a deep link mounts the standard form directly. */\n public get FormMode(): EntityFormMode {\n return this._formMode;\n }\n\n public override set Data(value: ResourceData) {\n super.Data = value;\n // Seed only before the form mounts: afterwards a changed input would\n // remount the host WITHOUT its unsaved-work guard. Later changes arrive\n // through OnQueryParamsChanged instead.\n if (!this.singleRecord) {\n this._formMode = FormModeFromQueryParams(this.GetQueryParams());\n }\n }\n public override get Data(): ResourceData {\n return super.Data;\n }\n\n private destroyed = false;\n\n public override ngOnDestroy(): void {\n this.destroyed = true;\n super.ngOnDestroy();\n }\n\n /**\n * Follow the tab's `form` param — a standard-form open that dedup routed\n * to this tab, a deep link, back/forward, or a plain URL clearing it.\n */\n protected override OnQueryParamsChanged(params: Record<string, string>, _source: 'popstate' | 'deeplink'): void {\n this.applyFormModeParams(params);\n }\n\n /**\n * Re-home after a cache reattach. A reattach to a DIFFERENT tab id replays\n * that tab's params in the base class. A same-id reattach does not — and\n * a delivery this component skipped while its tab hosted another record\n * (see {@link applyFormModeParams}) is recorded as delivered, so it would\n * never come again. Re-read the live params in that case.\n */\n public override RebindTabId(tabId: string): void {\n const sameTab = this.getTabId() === tabId;\n super.RebindTabId(tabId);\n if (sameTab) {\n this.resyncFormModeFromTab();\n }\n }\n\n /**\n * The host switched forms and told us: the strip, a programmatic\n * {@link SingleRecordComponent.SwitchFormMode} (including our own, from\n * {@link applyFormModeParams}), or an interactive-variant pick (back to\n * default). Record it on the tab, and so the URL. For our own switch this\n * runs inside a query-param delivery, where UpdateQueryParams is suppressed\n * — correct, since the tab already says it, and it cannot loop.\n */\n public OnFormModeChange(mode: EntityFormMode): void {\n this._formMode = mode;\n this.UpdateQueryParams(FormModeQueryParams(mode));\n }\n\n /**\n * Reconcile the mounted form with a tab's `form` param. Ignored unless this\n * component still owns its tab: a cached (detached) record stays subscribed\n * to its birth tab id, which may now host another record — whose params\n * would otherwise reload this form in the background or raise a spurious\n * \"save or discard\" warning.\n */\n private applyFormModeParams(params: Record<string, string>): void {\n if (!this.ownsTab()) return;\n const result = ReconcileFormMode(params, this._formMode, this.singleRecord ?? null);\n this._formMode = result.Mode;\n if (result.WriteBack) {\n this.writeBackRefusedMode(result.WriteBack);\n }\n }\n\n /**\n * The switch was refused (unsaved work; the host warned the user): put the\n * URL back to what is on screen. Deferred a microtask because\n * UpdateQueryParams is suppressed for the whole delivery that called us;\n * re-checked then, since the component may be gone or its tab reused.\n */\n private writeBackRefusedMode(writeBack: Record<string, string | null>): void {\n Promise.resolve()\n .then(() => {\n if (!this.destroyed && this.ownsTab()) {\n this.UpdateQueryParams(writeBack);\n }\n })\n .catch((err: unknown) => {\n LogError(`EntityRecordResource: could not restore the form-mode query param for \"${this.Data?.Configuration?.Entity}\" (tab ${this.getTabId()}): ${err instanceof Error ? err.message : String(err)}`);\n });\n }\n\n /** Re-read the live tab's params and reconcile (same-id cache reattach). */\n private resyncFormModeFromTab(): void {\n const tabId = this.getTabId();\n if (!tabId) return;\n // The workspace stream replays its current value on subscribe, so take(1) is a synchronous read.\n this.navigationService.ObserveTabQueryParams(tabId)\n .pipe(take(1))\n .subscribe(params => this.applyFormModeParams(params));\n }\n\n /** Whether the live tab still hosts this component's record. */\n private ownsTab(): boolean {\n const tabId = this.getTabId();\n if (!tabId) return false;\n return IsRecordTabOwner(\n this.navigationService.GetTabRecordIdentity(tabId),\n { Entity: this.Data?.Configuration?.Entity, RecordId: this.Data?.ResourceRecordID || this.Data?.Configuration?.recordId || '' }\n );\n }\n\n public static GetPrimaryKey(data: ResourceData, provider?: IMetadataProvider): CompositeKey {\n // global-provider-ok: static helper has no component instance scope; falls back to default provider\n const md = (provider ?? Metadata.Provider) as IMetadataProvider;\n const requested = (data.Configuration.Entity ?? '').trim();\n // Prefer EntityByName (project rule: never use Entities.find for\n // single-entity lookups — case/whitespace tolerant + O(1) map). Fall\n // back to DisplayName match for callers (AI agents, hand-typed URLs,\n // external CTAs) that pass the human-readable display name instead\n // of the canonical Name (e.g. \"Users\" for \"MJ: Users\").\n let e: EntityInfo | undefined = md.EntityByName(requested);\n if (!e) {\n const lowered = requested.toLowerCase();\n e = md.Entities.find((x: EntityInfo) =>\n (x.DisplayName ?? '').trim().toLowerCase() === lowered);\n }\n if (!e){\n throw new Error(`Entity ${data.Configuration.Entity} not found in metadata (tried Name and DisplayName)`);\n }\n\n if (IsNewEntityRecordUrlId(data.ResourceRecordID)) {\n return new CompositeKey();\n }\n\n let compositeKey: CompositeKey = new CompositeKey();\n compositeKey.LoadFromURLSegment(e, data.ResourceRecordID);\n return compositeKey;\n }\n\n async GetResourceDisplayName(data: ResourceData): Promise<string> {\n if (!data.Configuration.Entity) {\n return '';\n }\n\n const md = this.ProviderToUse;\n let e = md.EntityByName(data.Configuration.Entity);\n if (!e) {\n // Same DisplayName fallback as GetPrimaryKey — keeps the\n // breadcrumb/title in sync when a CTA used the display name.\n const lowered = (data.Configuration.Entity ?? '').trim().toLowerCase();\n e = md.Entities.find((x: EntityInfo) =>\n (x.DisplayName ?? '').trim().toLowerCase() === lowered);\n }\n if (!e) {\n return '';\n }\n\n const pk: CompositeKey = EntityRecordResource.GetPrimaryKey(data, this.ProviderToUse);\n if (pk.HasValue) {\n const name = await md.GetEntityRecordName(data.Configuration.Entity, pk);\n return FormatEntityRecordDisplayTitle(e, data.Configuration.Entity, pk, name);\n } else {\n return `New ${e.DisplayNameOrName} Record`;\n }\n }\n\n async GetResourceIconClass(data: ResourceData): Promise<string> {\n if (!data.Configuration.Entity){\n return ''\n }\n else {\n const md = this.ProviderToUse;\n const e = md.Entities.find(e => e.Name.trim().toLowerCase() === data.Configuration.Entity.trim().toLowerCase());\n if (e)\n return e?.Icon;\n else\n return '';\n }\n }\n}\n"]}
|
|
@@ -80,9 +80,9 @@ export declare class SearchResultsResource extends BaseResourceComponent {
|
|
|
80
80
|
private navigateToResult;
|
|
81
81
|
private executeSearch;
|
|
82
82
|
/**
|
|
83
|
-
* Phase 2C streaming variant. Subscribes to `streamScopedSearch
|
|
84
|
-
*
|
|
85
|
-
*
|
|
83
|
+
* Phase 2C streaming variant. Subscribes to `streamScopedSearch`, shows each provider's
|
|
84
|
+
* count in the chip strip as it reports back, and renders the result list from the 'final'
|
|
85
|
+
* event. A 'provider' event carries no rows: they arrive before the server's permission pass.
|
|
86
86
|
*
|
|
87
87
|
* Cancellation: a new query starts by tearing down the in-flight stream so events
|
|
88
88
|
* from a stale search do not bleed into the current view.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-results-resource.component.d.ts","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/search-results-resource.component.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"search-results-resource.component.d.ts","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/search-results-resource.component.ts"],"names":[],"mappings":"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"}
|
|
@@ -509,9 +509,9 @@ let SearchResultsResource = class SearchResultsResource extends BaseResourceComp
|
|
|
509
509
|
this.cdr.detectChanges();
|
|
510
510
|
}
|
|
511
511
|
/**
|
|
512
|
-
* Phase 2C streaming variant. Subscribes to `streamScopedSearch
|
|
513
|
-
*
|
|
514
|
-
*
|
|
512
|
+
* Phase 2C streaming variant. Subscribes to `streamScopedSearch`, shows each provider's
|
|
513
|
+
* count in the chip strip as it reports back, and renders the result list from the 'final'
|
|
514
|
+
* event. A 'provider' event carries no rows: they arrive before the server's permission pass.
|
|
515
515
|
*
|
|
516
516
|
* Cancellation: a new query starts by tearing down the in-flight stream so events
|
|
517
517
|
* from a stale search do not bleed into the current view.
|
|
@@ -531,21 +531,16 @@ let SearchResultsResource = class SearchResultsResource extends BaseResourceComp
|
|
|
531
531
|
this.currentStream = this.searchService.StreamSearch(request).subscribe({
|
|
532
532
|
next: (event) => {
|
|
533
533
|
if (event.Phase === 'provider' && event.ProviderName) {
|
|
534
|
-
|
|
534
|
+
// Progress only: the count, never the rows (they precede the permission pass).
|
|
535
535
|
this.StreamingProviders = [
|
|
536
536
|
...this.StreamingProviders,
|
|
537
537
|
{
|
|
538
538
|
Name: event.ProviderName,
|
|
539
|
-
Count:
|
|
539
|
+
Count: event.ResultCount ?? 0,
|
|
540
540
|
ElapsedMs: event.ElapsedMs ?? 0,
|
|
541
541
|
State: 'Completed',
|
|
542
542
|
},
|
|
543
543
|
];
|
|
544
|
-
if (event.Results && event.Results.length > 0) {
|
|
545
|
-
this.allResults = [...this.allResults, ...event.Results].sort((a, b) => b.Score - a.Score);
|
|
546
|
-
this.ServerResultCount = this.allResults.length;
|
|
547
|
-
this.applyClientFilters();
|
|
548
|
-
}
|
|
549
544
|
}
|
|
550
545
|
else if (event.Phase === 'final' && event.Results) {
|
|
551
546
|
this.allResults = [...event.Results].sort((a, b) => b.Score - a.Score);
|