@memberjunction/ng-shared-generic 6.1.0-edge.5 → 6.1.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.
|
@@ -174,7 +174,7 @@ export class LoadingComponent {
|
|
|
174
174
|
i0.ɵɵstyleProp("fill", ctx.logoFill);
|
|
175
175
|
i0.ɵɵadvance();
|
|
176
176
|
i0.ɵɵconditional(ctx.showText && ctx.text ? 6 : -1);
|
|
177
|
-
} }, styles: ["[_nghost-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n}\n\n.mj-loading-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n width: 100%;\n height: 100%;\n}\n\n\n
|
|
177
|
+
} }, styles: ["[_nghost-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n}\n\n.mj-loading-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n width: 100%;\n height: 100%;\n}\n\n\n.mj-loading-logo[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n\n.mj-logo-svg[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n}\n\n.mj-logo-svg[_ngcontent-%COMP%] path[_ngcontent-%COMP%] {\n fill: var(--mj-logo-color);\n}\n\n\n.mj-loading-logo.size-small[_ngcontent-%COMP%] {\n width: 40px;\n height: 22px;\n}\n\n.mj-loading-logo.size-medium[_ngcontent-%COMP%] {\n width: 80px;\n height: 45px;\n}\n\n.mj-loading-logo.size-large[_ngcontent-%COMP%] {\n width: 120px;\n height: 67px;\n}\n\n.mj-loading-logo.size-auto[_ngcontent-%COMP%] {\n \n width: 100%;\n height: 100%;\n max-width: 200px;\n max-height: 112px;\n min-width: 40px;\n min-height: 22px;\n}\n\n\n.mj-loading-text[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-muted);\n text-align: center;\n transition: color 0.3s ease;\n}\n\n\n.mj-loading-logo.animation-pulse[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_mj-loading-pulse 1.5s ease-in-out infinite;\n}\n\n@keyframes _ngcontent-%COMP%_mj-loading-pulse {\n 0%, 100% {\n opacity: 0.4;\n transform: scale(0.95);\n }\n 50% {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n\n.mj-loading-logo.animation-spin[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_mj-loading-spin 3s linear infinite;\n}\n\n@keyframes _ngcontent-%COMP%_mj-loading-spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n\n\n.mj-loading-logo.animation-bounce[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_mj-loading-bounce 1s ease-in-out infinite;\n}\n\n@keyframes _ngcontent-%COMP%_mj-loading-bounce {\n 0%, 100% {\n transform: translateY(0);\n }\n 50% {\n transform: translateY(-15px);\n }\n}\n\n\n.mj-loading-logo.animation-pulse-spin[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_mj-loading-pulse-spin 3s ease-in-out infinite;\n}\n\n@keyframes _ngcontent-%COMP%_mj-loading-pulse-spin {\n 0% {\n opacity: 0.4;\n transform: scale(0.95) rotate(0deg);\n }\n 50% {\n opacity: 1;\n transform: scale(1) rotate(180deg);\n }\n 100% {\n opacity: 0.4;\n transform: scale(0.95) rotate(360deg);\n }\n}"] });
|
|
178
178
|
}
|
|
179
179
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(LoadingComponent, [{
|
|
180
180
|
type: Component,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recent-access.service.d.ts","sourceRoot":"","sources":["../../src/lib/recent-access.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAqB,YAAY,EAAmC,MAAM,sBAAsB,CAAC;AAI3H,OAAO,EAAmB,UAAU,EAAE,MAAM,MAAM,CAAC;;AAEnD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,IAAI,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,YAAY,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAC;CACvE;AAED;;;GAGG;AACH;;;GAGG;AACH,qBAGa,mBAAmB;IAC9B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAuC;IAC9E,OAAO,CAAC,aAAa,CAA+C;IACpE,OAAO,CAAC,WAAW,CAAuC;IAC1D,OAAO,CAAC,SAAS,CAAS;IAE1B,OAAO,CAAC,SAAS,CAAkC;IACnD,IAAW,QAAQ,IAAI,iBAAiB,CAEvC;IACD,IAAW,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,EAElD;;IAUD,WAAkB,QAAQ,IAAI,mBAAmB,CAEhD;IAED;;OAEG;IACH,IAAW,WAAW,IAAI,UAAU,CAAC,gBAAgB,EAAE,CAAC,CAEvD;IAED;;OAEG;IACH,IAAW,gBAAgB,IAAI,gBAAgB,EAAE,CAEhD;IAED;;OAEG;IACH,IAAW,SAAS,IAAI,UAAU,CAAC,OAAO,CAAC,CAE1C;IAED;;;;;;;OAOG;IACU,SAAS,CACpB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GAAG,YAAY,EAC/B,YAAY,GAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,QAAmB,GAC/E,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"recent-access.service.d.ts","sourceRoot":"","sources":["../../src/lib/recent-access.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAqB,YAAY,EAAmC,MAAM,sBAAsB,CAAC;AAI3H,OAAO,EAAmB,UAAU,EAAE,MAAM,MAAM,CAAC;;AAEnD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,IAAI,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,YAAY,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAC;CACvE;AAED;;;GAGG;AACH;;;GAGG;AACH,qBAGa,mBAAmB;IAC9B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAuC;IAC9E,OAAO,CAAC,aAAa,CAA+C;IACpE,OAAO,CAAC,WAAW,CAAuC;IAC1D,OAAO,CAAC,SAAS,CAAS;IAE1B,OAAO,CAAC,SAAS,CAAkC;IACnD,IAAW,QAAQ,IAAI,iBAAiB,CAEvC;IACD,IAAW,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,EAElD;;IAUD,WAAkB,QAAQ,IAAI,mBAAmB,CAEhD;IAED;;OAEG;IACH,IAAW,WAAW,IAAI,UAAU,CAAC,gBAAgB,EAAE,CAAC,CAEvD;IAED;;OAEG;IACH,IAAW,gBAAgB,IAAI,gBAAgB,EAAE,CAEhD;IAED;;OAEG;IACH,IAAW,SAAS,IAAI,UAAU,CAAC,OAAO,CAAC,CAE1C;IAED;;;;;;;OAOG;IACU,SAAS,CACpB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GAAG,YAAY,EAC/B,YAAY,GAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,QAAmB,GAC/E,OAAO,CAAC,IAAI,CAAC;IAiEhB;;;;OAIG;IACU,eAAe,CAAC,QAAQ,GAAE,MAAW,EAAE,YAAY,GAAE,OAAe,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAgD/G;;OAEG;IACU,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIhD;;;OAGG;YACW,kBAAkB;IA+BhC;;;;;OAKG;IACH,OAAO,CAAC,0BAA0B;IAsBlC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAmB7B;;OAEG;IACI,UAAU,IAAI,IAAI;yCAhRd,mBAAmB;6CAAnB,mBAAmB;CAoR/B"}
|
|
@@ -69,9 +69,12 @@ export class RecentAccessService {
|
|
|
69
69
|
console.warn(`RecentAccessService: Entity "${entityName}" not found in metadata`);
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
72
|
-
//
|
|
72
|
+
// Persist the key in the compact CompositeKey segment form — the bare value for a single-column
|
|
73
|
+
// primary key (so existing rows and lookups are unchanged), "F1|v1||F2|v2" for a composite one.
|
|
74
|
+
// That is the form CompositeKey.FromURLSegment reads back; the previous Values(',') dropped
|
|
75
|
+
// the field names, so a composite key written here could never be re-opened.
|
|
73
76
|
const recordIdString = recordId instanceof CompositeKey
|
|
74
|
-
? recordId.
|
|
77
|
+
? recordId.ToCompactURLSegment()
|
|
75
78
|
: recordId;
|
|
76
79
|
// Check if we already have a log entry for this user/entity/record combination
|
|
77
80
|
const rv = RunView.FromMetadataProvider(this.Provider);
|
|
@@ -222,16 +225,12 @@ export class RecentAccessService {
|
|
|
222
225
|
// Fall through to entity-based lookup
|
|
223
226
|
}
|
|
224
227
|
}
|
|
225
|
-
// Plain value —
|
|
228
|
+
// Plain value — map it onto the entity's real primary key column (whatever it is called).
|
|
229
|
+
// FromURLSegment also re-parses a delimited segment, covering the case where the try above failed.
|
|
226
230
|
const entityInfo = md.Entities.find(e => e.Name === entityName);
|
|
227
|
-
if (!entityInfo)
|
|
231
|
+
if (!entityInfo || entityInfo.PrimaryKeys.length === 0)
|
|
228
232
|
return null;
|
|
229
|
-
|
|
230
|
-
if (!pkField)
|
|
231
|
-
return null;
|
|
232
|
-
const compositeKey = new CompositeKey();
|
|
233
|
-
compositeKey.KeyValuePairs = [{ FieldName: pkField.Name, Value: recordId }];
|
|
234
|
-
return compositeKey;
|
|
233
|
+
return CompositeKey.FromURLSegment(entityInfo, recordId);
|
|
235
234
|
}
|
|
236
235
|
/**
|
|
237
236
|
* Determines the resource type based on entity name
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recent-access.service.js","sourceRoot":"","sources":["../../src/lib/recent-access.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { IMetadataProvider, Metadata, RunView, CompositeKey, LogError, EntityRecordNameInput } from '@memberjunction/core';\nimport { GetGlobalObjectStore } from '@memberjunction/global';\nimport { MJUserRecordLogEntity, UserInfoEngine } from '@memberjunction/core-entities';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { BehaviorSubject, Observable } from 'rxjs';\n\n/**\n * Represents a recently accessed resource\n */\nexport interface RecentAccessItem {\n id: string;\n entityId: string;\n entityName: string;\n recordId: string;\n recordName?: string;\n latestAt: Date;\n totalCount: number;\n /** Resource type for special handling (record, view, dashboard, artifact) */\n resourceType: 'record' | 'view' | 'dashboard' | 'artifact' | 'report';\n}\n\n/**\n * Service for tracking and retrieving recently accessed resources.\n * Uses the User Record Logs entity to persist access history.\n */\n/**\n * Multi-provider note: callers under a non-default provider should set\n * `service.Provider = component.ProviderToUse` before invoking any methods.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class RecentAccessService {\n private static readonly _globalStoreKey = '___SINGLETON__RecentAccessService';\n private _recentItems$ = new BehaviorSubject<RecentAccessItem[]>([]);\n private _isLoading$ = new BehaviorSubject<boolean>(false);\n private _isLoaded = false;\n\n private _provider: IMetadataProvider | null = null;\n public get Provider(): IMetadataProvider {\n return this._provider ?? Metadata.Provider;\n }\n public set Provider(value: IMetadataProvider | null) {\n this._provider = value;\n }\n\n constructor() {\n const g = GetGlobalObjectStore()!;\n if (g[RecentAccessService._globalStoreKey]) {\n return g[RecentAccessService._globalStoreKey] as RecentAccessService;\n }\n g[RecentAccessService._globalStoreKey] = this;\n }\n\n public static get Instance(): RecentAccessService {\n return GetGlobalObjectStore()![RecentAccessService._globalStoreKey] as RecentAccessService;\n }\n\n /**\n * Observable of recent access items, sorted by most recent first\n */\n public get RecentItems(): Observable<RecentAccessItem[]> {\n return this._recentItems$.asObservable();\n }\n\n /**\n * Current value of recent items\n */\n public get RecentItemsValue(): RecentAccessItem[] {\n return this._recentItems$.value;\n }\n\n /**\n * Observable loading state\n */\n public get IsLoading(): Observable<boolean> {\n return this._isLoading$.asObservable();\n }\n\n /**\n * Logs access to a record. Creates or updates the User Record Log entry.\n * This is a fire-and-forget operation - errors are logged but don't interrupt the user.\n *\n * @param entityName - The name of the entity being accessed\n * @param recordId - The record ID (single value or CompositeKey string)\n * @param resourceType - The type of resource being accessed\n */\n public async logAccess(\n entityName: string,\n recordId: string | CompositeKey,\n resourceType: 'record' | 'view' | 'dashboard' | 'artifact' | 'report' = 'record'\n ): Promise<void> {\n try {\n const md = this.Provider;\n const entityInfo = md.Entities.find(e => e.Name === entityName);\n if (!entityInfo) {\n console.warn(`RecentAccessService: Entity \"${entityName}\" not found in metadata`);\n return;\n }\n\n // Convert CompositeKey to string if needed\n const recordIdString = recordId instanceof CompositeKey\n ? recordId.Values(',') // Values() returns joined string with specified delimiter\n : recordId;\n\n // Check if we already have a log entry for this user/entity/record combination\n const rv = RunView.FromMetadataProvider(this.Provider);\n const existingResult = await rv.RunView<MJUserRecordLogEntity>({\n EntityName: 'MJ: User Record Logs',\n ExtraFilter: `UserID='${md.CurrentUser.ID}' AND EntityID='${entityInfo.ID}' AND RecordID='${recordIdString}'`,\n ResultType: 'entity_object',\n MaxRows: 1\n });\n\n if (!existingResult.Success) {\n console.error('RecentAccessService: Failed to check existing log', existingResult.ErrorMessage);\n return;\n }\n\n if (existingResult.Results && existingResult.Results.length > 0) {\n // Update existing entry\n const existing = existingResult.Results[0];\n existing.LatestAt = new Date();\n existing.TotalCount = (existing.TotalCount || 0) + 1;\n\n const saveResult = await existing.Save();\n if (!saveResult) {\n console.error('RecentAccessService: Failed to update log entry');\n }\n } else {\n // Create new entry\n const newLog = await md.GetEntityObject<MJUserRecordLogEntity>('MJ: User Record Logs', md.CurrentUser);\n newLog.UserID = md.CurrentUser.ID;\n newLog.EntityID = entityInfo.ID;\n newLog.RecordID = recordIdString;\n // EarliestAt and LatestAt have default values of getdate() in the DB\n // TotalCount defaults to 0, so we set it to 1\n newLog.TotalCount = 1;\n\n const saveResult = await newLog.Save();\n if (!saveResult) {\n console.error('RecentAccessService: Failed to create log entry');\n }\n }\n\n // Refresh the recent items list in background\n this.refreshRecentItems();\n } catch (error) {\n // Don't throw - this is non-critical functionality\n console.error('RecentAccessService: Error logging access', error);\n }\n }\n\n /**\n * Loads recent access items for the current user using UserInfoEngine (cached).\n * @param maxItems - Maximum number of items to return (default 15)\n * @param forceRefresh - Force refresh even if already loaded\n */\n public async loadRecentItems(maxItems: number = 15, forceRefresh: boolean = false): Promise<RecentAccessItem[]> {\n if (this._isLoaded && !forceRefresh) {\n return this._recentItems$.value;\n }\n\n try {\n this._isLoading$.next(true);\n\n const md = this.Provider;\n\n // Get recent records, limited to maxItems (already ordered by LatestAt DESC in engine)\n const userRecordLogs = UserInfoEngine.Instance.UserRecordLogs.slice(0, maxItems);\n\n const items: RecentAccessItem[] = [];\n\n for (const log of userRecordLogs) {\n const entityInfo = md.Entities.find(e => UUIDsEqual(e.ID, log.EntityID));\n if (!entityInfo) continue;\n\n // Determine resource type based on entity name\n const resourceType = this.determineResourceType(entityInfo.Name);\n\n items.push({\n id: log.ID,\n entityId: log.EntityID,\n entityName: entityInfo.Name,\n recordId: log.RecordID,\n latestAt: log.LatestAt,\n totalCount: log.TotalCount,\n resourceType\n });\n }\n\n // Batch-resolve record names for all items\n await this.resolveRecordNames(items, md);\n\n this._recentItems$.next(items);\n this._isLoaded = true;\n\n return items;\n } catch (error) {\n LogError('RecentAccessService: Error loading recent items', undefined, error);\n return [];\n } finally {\n this._isLoading$.next(false);\n }\n }\n\n /**\n * Refresh recent items in background\n */\n public async refreshRecentItems(): Promise<void> {\n await this.loadRecentItems(15, true);\n }\n\n /**\n * Batch-resolve record names for a list of recent access items using GetEntityRecordNames().\n * Mutates the items in-place to set recordName where resolved.\n */\n private async resolveRecordNames(items: RecentAccessItem[], md: IMetadataProvider): Promise<void> {\n const nameInputs: EntityRecordNameInput[] = [];\n const indexMap = new Map<string, number>();\n\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n const compositeKey = this.buildCompositeKeyForRecord(item.entityName, item.recordId, md);\n if (!compositeKey) continue;\n\n nameInputs.push({ EntityName: item.entityName, CompositeKey: compositeKey });\n indexMap.set(`${item.entityName}||${compositeKey.ToConcatenatedString()}`, i);\n }\n\n if (nameInputs.length === 0) return;\n\n try {\n const nameResults = await md.GetEntityRecordNames(nameInputs);\n for (const result of nameResults) {\n if (result.Success && result.RecordName) {\n const key = `${result.EntityName}||${result.CompositeKey.ToConcatenatedString()}`;\n const index = indexMap.get(key);\n if (index !== undefined) {\n items[index].recordName = result.RecordName;\n }\n }\n }\n } catch (error) {\n console.warn('RecentAccessService: Failed to resolve record names', error);\n }\n }\n\n /**\n * Build a CompositeKey for a record. RecordID may be stored as either:\n * - Concatenated format: \"FieldName|Value\" or \"Field1|Val1||Field2|Val2\"\n * - Plain value: just the raw value (e.g. a GUID)\n * Detects the format and constructs the key accordingly.\n */\n private buildCompositeKeyForRecord(entityName: string, recordId: string, md: IMetadataProvider): CompositeKey | null {\n if (!recordId) return null;\n\n // If recordId contains '|', it's in concatenated format\n if (recordId.includes('|')) {\n try {\n const compositeKey = new CompositeKey();\n compositeKey.LoadFromConcatenatedString(recordId);\n if (compositeKey.KeyValuePairs.length > 0) return compositeKey;\n } catch {\n // Fall through to entity-based lookup\n }\n }\n\n // Plain value — look up entity primary key field(s) to construct the key\n const entityInfo = md.Entities.find(e => e.Name === entityName);\n if (!entityInfo) return null;\n\n const pkField = entityInfo.FirstPrimaryKey;\n if (!pkField) return null;\n\n const compositeKey = new CompositeKey();\n compositeKey.KeyValuePairs = [{ FieldName: pkField.Name, Value: recordId }];\n return compositeKey;\n }\n\n /**\n * Determines the resource type based on entity name\n */\n private determineResourceType(entityName: string): 'record' | 'view' | 'dashboard' | 'artifact' | 'report' {\n const normalizedName = entityName.toLowerCase();\n\n if (normalizedName === 'user views') {\n return 'view';\n }\n if (normalizedName === 'dashboards') {\n return 'dashboard';\n }\n if (normalizedName === 'mj: conversation artifacts' || normalizedName === 'conversation artifacts') {\n return 'artifact';\n }\n if (normalizedName === 'reports') {\n return 'report';\n }\n\n return 'record';\n }\n\n /**\n * Clears the cached recent items (useful for logout)\n */\n public clearCache(): void {\n this._recentItems$.next([]);\n this._isLoaded = false;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"recent-access.service.js","sourceRoot":"","sources":["../../src/lib/recent-access.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { IMetadataProvider, Metadata, RunView, CompositeKey, LogError, EntityRecordNameInput } from '@memberjunction/core';\nimport { GetGlobalObjectStore } from '@memberjunction/global';\nimport { MJUserRecordLogEntity, UserInfoEngine } from '@memberjunction/core-entities';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { BehaviorSubject, Observable } from 'rxjs';\n\n/**\n * Represents a recently accessed resource\n */\nexport interface RecentAccessItem {\n id: string;\n entityId: string;\n entityName: string;\n recordId: string;\n recordName?: string;\n latestAt: Date;\n totalCount: number;\n /** Resource type for special handling (record, view, dashboard, artifact) */\n resourceType: 'record' | 'view' | 'dashboard' | 'artifact' | 'report';\n}\n\n/**\n * Service for tracking and retrieving recently accessed resources.\n * Uses the User Record Logs entity to persist access history.\n */\n/**\n * Multi-provider note: callers under a non-default provider should set\n * `service.Provider = component.ProviderToUse` before invoking any methods.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class RecentAccessService {\n private static readonly _globalStoreKey = '___SINGLETON__RecentAccessService';\n private _recentItems$ = new BehaviorSubject<RecentAccessItem[]>([]);\n private _isLoading$ = new BehaviorSubject<boolean>(false);\n private _isLoaded = false;\n\n private _provider: IMetadataProvider | null = null;\n public get Provider(): IMetadataProvider {\n return this._provider ?? Metadata.Provider;\n }\n public set Provider(value: IMetadataProvider | null) {\n this._provider = value;\n }\n\n constructor() {\n const g = GetGlobalObjectStore()!;\n if (g[RecentAccessService._globalStoreKey]) {\n return g[RecentAccessService._globalStoreKey] as RecentAccessService;\n }\n g[RecentAccessService._globalStoreKey] = this;\n }\n\n public static get Instance(): RecentAccessService {\n return GetGlobalObjectStore()![RecentAccessService._globalStoreKey] as RecentAccessService;\n }\n\n /**\n * Observable of recent access items, sorted by most recent first\n */\n public get RecentItems(): Observable<RecentAccessItem[]> {\n return this._recentItems$.asObservable();\n }\n\n /**\n * Current value of recent items\n */\n public get RecentItemsValue(): RecentAccessItem[] {\n return this._recentItems$.value;\n }\n\n /**\n * Observable loading state\n */\n public get IsLoading(): Observable<boolean> {\n return this._isLoading$.asObservable();\n }\n\n /**\n * Logs access to a record. Creates or updates the User Record Log entry.\n * This is a fire-and-forget operation - errors are logged but don't interrupt the user.\n *\n * @param entityName - The name of the entity being accessed\n * @param recordId - The record ID (single value or CompositeKey string)\n * @param resourceType - The type of resource being accessed\n */\n public async logAccess(\n entityName: string,\n recordId: string | CompositeKey,\n resourceType: 'record' | 'view' | 'dashboard' | 'artifact' | 'report' = 'record'\n ): Promise<void> {\n try {\n const md = this.Provider;\n const entityInfo = md.Entities.find(e => e.Name === entityName);\n if (!entityInfo) {\n console.warn(`RecentAccessService: Entity \"${entityName}\" not found in metadata`);\n return;\n }\n\n // Persist the key in the compact CompositeKey segment form — the bare value for a single-column\n // primary key (so existing rows and lookups are unchanged), \"F1|v1||F2|v2\" for a composite one.\n // That is the form CompositeKey.FromURLSegment reads back; the previous Values(',') dropped\n // the field names, so a composite key written here could never be re-opened.\n const recordIdString = recordId instanceof CompositeKey\n ? recordId.ToCompactURLSegment()\n : recordId;\n\n // Check if we already have a log entry for this user/entity/record combination\n const rv = RunView.FromMetadataProvider(this.Provider);\n const existingResult = await rv.RunView<MJUserRecordLogEntity>({\n EntityName: 'MJ: User Record Logs',\n ExtraFilter: `UserID='${md.CurrentUser.ID}' AND EntityID='${entityInfo.ID}' AND RecordID='${recordIdString}'`,\n ResultType: 'entity_object',\n MaxRows: 1\n });\n\n if (!existingResult.Success) {\n console.error('RecentAccessService: Failed to check existing log', existingResult.ErrorMessage);\n return;\n }\n\n if (existingResult.Results && existingResult.Results.length > 0) {\n // Update existing entry\n const existing = existingResult.Results[0];\n existing.LatestAt = new Date();\n existing.TotalCount = (existing.TotalCount || 0) + 1;\n\n const saveResult = await existing.Save();\n if (!saveResult) {\n console.error('RecentAccessService: Failed to update log entry');\n }\n } else {\n // Create new entry\n const newLog = await md.GetEntityObject<MJUserRecordLogEntity>('MJ: User Record Logs', md.CurrentUser);\n newLog.UserID = md.CurrentUser.ID;\n newLog.EntityID = entityInfo.ID;\n newLog.RecordID = recordIdString;\n // EarliestAt and LatestAt have default values of getdate() in the DB\n // TotalCount defaults to 0, so we set it to 1\n newLog.TotalCount = 1;\n\n const saveResult = await newLog.Save();\n if (!saveResult) {\n console.error('RecentAccessService: Failed to create log entry');\n }\n }\n\n // Refresh the recent items list in background\n this.refreshRecentItems();\n } catch (error) {\n // Don't throw - this is non-critical functionality\n console.error('RecentAccessService: Error logging access', error);\n }\n }\n\n /**\n * Loads recent access items for the current user using UserInfoEngine (cached).\n * @param maxItems - Maximum number of items to return (default 15)\n * @param forceRefresh - Force refresh even if already loaded\n */\n public async loadRecentItems(maxItems: number = 15, forceRefresh: boolean = false): Promise<RecentAccessItem[]> {\n if (this._isLoaded && !forceRefresh) {\n return this._recentItems$.value;\n }\n\n try {\n this._isLoading$.next(true);\n\n const md = this.Provider;\n\n // Get recent records, limited to maxItems (already ordered by LatestAt DESC in engine)\n const userRecordLogs = UserInfoEngine.Instance.UserRecordLogs.slice(0, maxItems);\n\n const items: RecentAccessItem[] = [];\n\n for (const log of userRecordLogs) {\n const entityInfo = md.Entities.find(e => UUIDsEqual(e.ID, log.EntityID));\n if (!entityInfo) continue;\n\n // Determine resource type based on entity name\n const resourceType = this.determineResourceType(entityInfo.Name);\n\n items.push({\n id: log.ID,\n entityId: log.EntityID,\n entityName: entityInfo.Name,\n recordId: log.RecordID,\n latestAt: log.LatestAt,\n totalCount: log.TotalCount,\n resourceType\n });\n }\n\n // Batch-resolve record names for all items\n await this.resolveRecordNames(items, md);\n\n this._recentItems$.next(items);\n this._isLoaded = true;\n\n return items;\n } catch (error) {\n LogError('RecentAccessService: Error loading recent items', undefined, error);\n return [];\n } finally {\n this._isLoading$.next(false);\n }\n }\n\n /**\n * Refresh recent items in background\n */\n public async refreshRecentItems(): Promise<void> {\n await this.loadRecentItems(15, true);\n }\n\n /**\n * Batch-resolve record names for a list of recent access items using GetEntityRecordNames().\n * Mutates the items in-place to set recordName where resolved.\n */\n private async resolveRecordNames(items: RecentAccessItem[], md: IMetadataProvider): Promise<void> {\n const nameInputs: EntityRecordNameInput[] = [];\n const indexMap = new Map<string, number>();\n\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n const compositeKey = this.buildCompositeKeyForRecord(item.entityName, item.recordId, md);\n if (!compositeKey) continue;\n\n nameInputs.push({ EntityName: item.entityName, CompositeKey: compositeKey });\n indexMap.set(`${item.entityName}||${compositeKey.ToConcatenatedString()}`, i);\n }\n\n if (nameInputs.length === 0) return;\n\n try {\n const nameResults = await md.GetEntityRecordNames(nameInputs);\n for (const result of nameResults) {\n if (result.Success && result.RecordName) {\n const key = `${result.EntityName}||${result.CompositeKey.ToConcatenatedString()}`;\n const index = indexMap.get(key);\n if (index !== undefined) {\n items[index].recordName = result.RecordName;\n }\n }\n }\n } catch (error) {\n console.warn('RecentAccessService: Failed to resolve record names', error);\n }\n }\n\n /**\n * Build a CompositeKey for a record. RecordID may be stored as either:\n * - Concatenated format: \"FieldName|Value\" or \"Field1|Val1||Field2|Val2\"\n * - Plain value: just the raw value (e.g. a GUID)\n * Detects the format and constructs the key accordingly.\n */\n private buildCompositeKeyForRecord(entityName: string, recordId: string, md: IMetadataProvider): CompositeKey | null {\n if (!recordId) return null;\n\n // If recordId contains '|', it's in concatenated format\n if (recordId.includes('|')) {\n try {\n const compositeKey = new CompositeKey();\n compositeKey.LoadFromConcatenatedString(recordId);\n if (compositeKey.KeyValuePairs.length > 0) return compositeKey;\n } catch {\n // Fall through to entity-based lookup\n }\n }\n\n // Plain value — map it onto the entity's real primary key column (whatever it is called).\n // FromURLSegment also re-parses a delimited segment, covering the case where the try above failed.\n const entityInfo = md.Entities.find(e => e.Name === entityName);\n if (!entityInfo || entityInfo.PrimaryKeys.length === 0) return null;\n\n return CompositeKey.FromURLSegment(entityInfo, recordId);\n }\n\n /**\n * Determines the resource type based on entity name\n */\n private determineResourceType(entityName: string): 'record' | 'view' | 'dashboard' | 'artifact' | 'report' {\n const normalizedName = entityName.toLowerCase();\n\n if (normalizedName === 'user views') {\n return 'view';\n }\n if (normalizedName === 'dashboards') {\n return 'dashboard';\n }\n if (normalizedName === 'mj: conversation artifacts' || normalizedName === 'conversation artifacts') {\n return 'artifact';\n }\n if (normalizedName === 'reports') {\n return 'report';\n }\n\n return 'record';\n }\n\n /**\n * Clears the cached recent items (useful for logout)\n */\n public clearCache(): void {\n this._recentItems$.next([]);\n this._isLoaded = false;\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-shared-generic",
|
|
3
|
-
"version": "6.1.0-edge.
|
|
3
|
+
"version": "6.1.0-edge.6",
|
|
4
4
|
"description": "MemberJunction: Generic Angular Shared Package - utility services and reusable elements used in any Angular application.",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -14,23 +14,23 @@
|
|
|
14
14
|
"author": "",
|
|
15
15
|
"license": "BUSL-1.1",
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@angular/compiler": "21.
|
|
18
|
-
"@angular/compiler-cli": "21.
|
|
19
|
-
"@memberjunction/ng-test-utils": "6.1.0-edge.
|
|
17
|
+
"@angular/compiler": "21.2.22",
|
|
18
|
+
"@angular/compiler-cli": "21.2.22",
|
|
19
|
+
"@memberjunction/ng-test-utils": "6.1.0-edge.6",
|
|
20
20
|
"vitest": "^4.0.18"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"@angular/common": "^21.
|
|
24
|
-
"@angular/core": "^21.
|
|
25
|
-
"@angular/platform-browser": "^21.
|
|
23
|
+
"@angular/common": "^21.2.22",
|
|
24
|
+
"@angular/core": "^21.2.22",
|
|
25
|
+
"@angular/platform-browser": "^21.2.22",
|
|
26
26
|
"rxjs": "^7.8.2"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@memberjunction/core": "6.1.0-edge.
|
|
30
|
-
"@memberjunction/ng-base-types": "6.1.0-edge.
|
|
31
|
-
"@memberjunction/core-entities": "6.1.0-edge.
|
|
32
|
-
"@memberjunction/global": "6.1.0-edge.
|
|
33
|
-
"@memberjunction/theme-engine": "6.1.0-edge.
|
|
29
|
+
"@memberjunction/core": "6.1.0-edge.6",
|
|
30
|
+
"@memberjunction/ng-base-types": "6.1.0-edge.6",
|
|
31
|
+
"@memberjunction/core-entities": "6.1.0-edge.6",
|
|
32
|
+
"@memberjunction/global": "6.1.0-edge.6",
|
|
33
|
+
"@memberjunction/theme-engine": "6.1.0-edge.6",
|
|
34
34
|
"dompurify": "^3.3.0",
|
|
35
35
|
"tslib": "^2.8.1"
|
|
36
36
|
},
|