@memberjunction/ng-shared 5.21.0 → 5.22.0

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.
@@ -18,8 +18,16 @@ export declare abstract class BaseResourceComponent extends BaseNavigationCompon
18
18
  private _resourceRecordSavedEvent;
19
19
  get ResourceRecordSavedEvent(): any;
20
20
  set ResourceRecordSavedEvent(value: any);
21
+ private _displayNameChangedEvent;
22
+ get DisplayNameChangedEvent(): ((newName: string) => void) | null;
23
+ set DisplayNameChangedEvent(value: ((newName: string) => void) | null);
21
24
  protected NotifyLoadComplete(): void;
22
25
  protected NotifyLoadStarted(): void;
26
+ /**
27
+ * Call this to notify the tab system that the resource's display name has changed.
28
+ * The tab container will update the tab title and browser title accordingly.
29
+ */
30
+ protected NotifyDisplayNameChanged(newName: string): void;
23
31
  protected ResourceRecordSaved(resourceRecordEntity: BaseEntity): void;
24
32
  abstract GetResourceDisplayName(data: ResourceData): Promise<string>;
25
33
  abstract GetResourceIconClass(data: ResourceData): Promise<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"base-resource-component.d.ts","sourceRoot":"","sources":["../../src/lib/base-resource-component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,8BAAsB,qBAAsB,SAAQ,uBAAuB;IACvE,OAAO,CAAC,KAAK,CAAoC;IAEjD,IAAW,IAAI,IAAI,YAAY,CAE9B;IACD,IAAW,IAAI,CAAC,KAAK,EAAE,YAAY,EAElC;IAED,OAAO,CAAC,aAAa,CAAkB;IACvC,IAAW,YAAY,IAAI,OAAO,CAEjC;IAED,OAAO,CAAC,YAAY,CAAkB;IACtC,IAAW,WAAW,IAAI,OAAO,CAEhC;IAGD,OAAO,CAAC,kBAAkB,CAAa;IACvC,IAAW,iBAAiB,IAAI,GAAG,CAElC;IACD,IAAW,iBAAiB,CAAC,KAAK,EAAE,GAAG,EAEtC;IAED,OAAO,CAAC,iBAAiB,CAAa;IACtC,IAAW,gBAAgB,IAAI,GAAG,CAEjC;IACD,IAAW,gBAAgB,CAAC,KAAK,EAAE,GAAG,EAErC;IAED,OAAO,CAAC,yBAAyB,CAAa;IAC9C,IAAW,wBAAwB,IAAI,GAAG,CAEzC;IACD,IAAW,wBAAwB,CAAC,KAAK,EAAE,GAAG,EAE7C;IAED,SAAS,CAAC,kBAAkB;IAO5B,SAAS,CAAC,iBAAiB;IAS3B,SAAS,CAAC,mBAAmB,CAAC,oBAAoB,EAAE,UAAU;IAO9D,QAAQ,CAAC,sBAAsB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAEpE,QAAQ,CAAC,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;CACrE"}
1
+ {"version":3,"file":"base-resource-component.d.ts","sourceRoot":"","sources":["../../src/lib/base-resource-component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,8BAAsB,qBAAsB,SAAQ,uBAAuB;IACvE,OAAO,CAAC,KAAK,CAAoC;IAEjD,IAAW,IAAI,IAAI,YAAY,CAE9B;IACD,IAAW,IAAI,CAAC,KAAK,EAAE,YAAY,EAElC;IAED,OAAO,CAAC,aAAa,CAAkB;IACvC,IAAW,YAAY,IAAI,OAAO,CAEjC;IAED,OAAO,CAAC,YAAY,CAAkB;IACtC,IAAW,WAAW,IAAI,OAAO,CAEhC;IAGD,OAAO,CAAC,kBAAkB,CAAa;IACvC,IAAW,iBAAiB,IAAI,GAAG,CAElC;IACD,IAAW,iBAAiB,CAAC,KAAK,EAAE,GAAG,EAEtC;IAED,OAAO,CAAC,iBAAiB,CAAa;IACtC,IAAW,gBAAgB,IAAI,GAAG,CAEjC;IACD,IAAW,gBAAgB,CAAC,KAAK,EAAE,GAAG,EAErC;IAED,OAAO,CAAC,yBAAyB,CAAa;IAC9C,IAAW,wBAAwB,IAAI,GAAG,CAEzC;IACD,IAAW,wBAAwB,CAAC,KAAK,EAAE,GAAG,EAE7C;IAED,OAAO,CAAC,wBAAwB,CAA4C;IAC5E,IAAW,uBAAuB,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAEvE;IACD,IAAW,uBAAuB,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,EAE3E;IAED,SAAS,CAAC,kBAAkB;IAO5B,SAAS,CAAC,iBAAiB;IAS3B;;;OAGG;IACH,SAAS,CAAC,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAMzD,SAAS,CAAC,mBAAmB,CAAC,oBAAoB,EAAE,UAAU;IAO9D,QAAQ,CAAC,sBAAsB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAEpE,QAAQ,CAAC,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;CACrE"}
@@ -37,6 +37,13 @@ export class BaseResourceComponent extends BaseNavigationComponent {
37
37
  set ResourceRecordSavedEvent(value) {
38
38
  this._resourceRecordSavedEvent = value;
39
39
  }
40
+ _displayNameChangedEvent = null;
41
+ get DisplayNameChangedEvent() {
42
+ return this._displayNameChangedEvent;
43
+ }
44
+ set DisplayNameChangedEvent(value) {
45
+ this._displayNameChangedEvent = value;
46
+ }
40
47
  NotifyLoadComplete() {
41
48
  this._loadComplete = true;
42
49
  if (this._loadCompleteEvent) {
@@ -49,6 +56,15 @@ export class BaseResourceComponent extends BaseNavigationComponent {
49
56
  this._loadStartedEvent();
50
57
  }
51
58
  }
59
+ /**
60
+ * Call this to notify the tab system that the resource's display name has changed.
61
+ * The tab container will update the tab title and browser title accordingly.
62
+ */
63
+ NotifyDisplayNameChanged(newName) {
64
+ if (this._displayNameChangedEvent) {
65
+ this._displayNameChangedEvent(newName);
66
+ }
67
+ }
52
68
  ResourceRecordSaved(resourceRecordEntity) {
53
69
  this.Data.ResourceRecordID = resourceRecordEntity.PrimaryKey.ToString();
54
70
  if (this._resourceRecordSavedEvent) {
@@ -1 +1 @@
1
- {"version":3,"file":"base-resource-component.js","sourceRoot":"","sources":["../../src/lib/base-resource-component.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,MAAM,OAAgB,qBAAsB,SAAQ,uBAAuB;IAC/D,KAAK,GAAiB,IAAI,YAAY,EAAE,CAAC;IAEjD,IAAW,IAAI;QACX,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IACD,IAAW,IAAI,CAAC,KAAmB;QAC/B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAEO,aAAa,GAAY,KAAK,CAAC;IACvC,IAAW,YAAY;QACnB,OAAO,IAAI,CAAC,aAAa,CAAC;IAC9B,CAAC;IAEO,YAAY,GAAY,KAAK,CAAC;IACtC,IAAW,WAAW;QAClB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B,CAAC;IAGO,kBAAkB,GAAQ,IAAI,CAAC;IACvC,IAAW,iBAAiB;QACxB,OAAO,IAAI,CAAC,kBAAkB,CAAA;IAClC,CAAC;IACD,IAAW,iBAAiB,CAAC,KAAU;QACnC,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;IACpC,CAAC;IAEO,iBAAiB,GAAQ,IAAI,CAAC;IACtC,IAAW,gBAAgB;QACvB,OAAO,IAAI,CAAC,iBAAiB,CAAA;IACjC,CAAC;IACD,IAAW,gBAAgB,CAAC,KAAU;QAClC,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;IACnC,CAAC;IAEO,yBAAyB,GAAQ,IAAI,CAAC;IAC9C,IAAW,wBAAwB;QAC/B,OAAO,IAAI,CAAC,yBAAyB,CAAA;IACzC,CAAC;IACD,IAAW,wBAAwB,CAAC,KAAU;QAC1C,IAAI,CAAC,yBAAyB,GAAG,KAAK,CAAC;IAC3C,CAAC;IAES,kBAAkB;QACxB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1B,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC9B,CAAC;IACL,CAAC;IAES,iBAAiB;QACvB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACzB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC7B,CAAC;IACL,CAAC;IAIS,mBAAmB,CAAC,oBAAgC;QAC1D,IAAI,CAAC,IAAI,CAAC,gBAAgB,GAAG,oBAAoB,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;QACxE,IAAI,IAAI,CAAC,yBAAyB,EAAE,CAAC;YACjC,IAAI,CAAC,yBAAyB,CAAC,oBAAoB,CAAC,CAAC;QACzD,CAAC;IACL,CAAC;CAKJ","sourcesContent":["import { BaseEntity } from \"@memberjunction/core\";\nimport { SharedService } from \"./shared.service\";\nimport { BaseNavigationComponent } from \"./base-navigation-component\";\nimport { ResourceData } from \"@memberjunction/core-entities\";\n\nexport abstract class BaseResourceComponent extends BaseNavigationComponent {\n private _data: ResourceData = new ResourceData();\n\n public get Data(): ResourceData {\n return this._data;\n }\n public set Data(value: ResourceData) {\n this._data = value;\n }\n\n private _loadComplete: boolean = false;\n public get LoadComplete(): boolean {\n return this._loadComplete;\n }\n\n private _loadStarted: boolean = false;\n public get LoadStarted(): boolean {\n return this._loadStarted;\n }\n\n \n private _loadCompleteEvent: any = null;\n public get LoadCompleteEvent(): any {\n return this._loadCompleteEvent\n }\n public set LoadCompleteEvent(value: any) {\n this._loadCompleteEvent = value;\n }\n\n private _loadStartedEvent: any = null;\n public get LoadStartedEvent(): any {\n return this._loadStartedEvent\n }\n public set LoadStartedEvent(value: any) {\n this._loadStartedEvent = value;\n }\n\n private _resourceRecordSavedEvent: any = null;\n public get ResourceRecordSavedEvent(): any {\n return this._resourceRecordSavedEvent\n }\n public set ResourceRecordSavedEvent(value: any) {\n this._resourceRecordSavedEvent = value;\n }\n\n protected NotifyLoadComplete() {\n this._loadComplete = true;\n if (this._loadCompleteEvent) {\n this._loadCompleteEvent();\n }\n }\n\n protected NotifyLoadStarted() {\n this._loadStarted = true;\n if (this._loadStartedEvent) {\n this._loadStartedEvent();\n }\n }\n \n \n\n protected ResourceRecordSaved(resourceRecordEntity: BaseEntity) {\n this.Data.ResourceRecordID = resourceRecordEntity.PrimaryKey.ToString();\n if (this._resourceRecordSavedEvent) {\n this._resourceRecordSavedEvent(resourceRecordEntity);\n }\n }\n\n abstract GetResourceDisplayName(data: ResourceData): Promise<string>\n\n abstract GetResourceIconClass(data: ResourceData): Promise<string>\n}\n"]}
1
+ {"version":3,"file":"base-resource-component.js","sourceRoot":"","sources":["../../src/lib/base-resource-component.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,MAAM,OAAgB,qBAAsB,SAAQ,uBAAuB;IAC/D,KAAK,GAAiB,IAAI,YAAY,EAAE,CAAC;IAEjD,IAAW,IAAI;QACX,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IACD,IAAW,IAAI,CAAC,KAAmB;QAC/B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAEO,aAAa,GAAY,KAAK,CAAC;IACvC,IAAW,YAAY;QACnB,OAAO,IAAI,CAAC,aAAa,CAAC;IAC9B,CAAC;IAEO,YAAY,GAAY,KAAK,CAAC;IACtC,IAAW,WAAW;QAClB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B,CAAC;IAGO,kBAAkB,GAAQ,IAAI,CAAC;IACvC,IAAW,iBAAiB;QACxB,OAAO,IAAI,CAAC,kBAAkB,CAAA;IAClC,CAAC;IACD,IAAW,iBAAiB,CAAC,KAAU;QACnC,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;IACpC,CAAC;IAEO,iBAAiB,GAAQ,IAAI,CAAC;IACtC,IAAW,gBAAgB;QACvB,OAAO,IAAI,CAAC,iBAAiB,CAAA;IACjC,CAAC;IACD,IAAW,gBAAgB,CAAC,KAAU;QAClC,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;IACnC,CAAC;IAEO,yBAAyB,GAAQ,IAAI,CAAC;IAC9C,IAAW,wBAAwB;QAC/B,OAAO,IAAI,CAAC,yBAAyB,CAAA;IACzC,CAAC;IACD,IAAW,wBAAwB,CAAC,KAAU;QAC1C,IAAI,CAAC,yBAAyB,GAAG,KAAK,CAAC;IAC3C,CAAC;IAEO,wBAAwB,GAAuC,IAAI,CAAC;IAC5E,IAAW,uBAAuB;QAC9B,OAAO,IAAI,CAAC,wBAAwB,CAAC;IACzC,CAAC;IACD,IAAW,uBAAuB,CAAC,KAAyC;QACxE,IAAI,CAAC,wBAAwB,GAAG,KAAK,CAAC;IAC1C,CAAC;IAES,kBAAkB;QACxB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1B,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC9B,CAAC;IACL,CAAC;IAES,iBAAiB;QACvB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACzB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC7B,CAAC;IACL,CAAC;IAID;;;OAGG;IACO,wBAAwB,CAAC,OAAe;QAC9C,IAAI,IAAI,CAAC,wBAAwB,EAAE,CAAC;YAChC,IAAI,CAAC,wBAAwB,CAAC,OAAO,CAAC,CAAC;QAC3C,CAAC;IACL,CAAC;IAES,mBAAmB,CAAC,oBAAgC;QAC1D,IAAI,CAAC,IAAI,CAAC,gBAAgB,GAAG,oBAAoB,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;QACxE,IAAI,IAAI,CAAC,yBAAyB,EAAE,CAAC;YACjC,IAAI,CAAC,yBAAyB,CAAC,oBAAoB,CAAC,CAAC;QACzD,CAAC;IACL,CAAC;CAKJ","sourcesContent":["import { BaseEntity } from \"@memberjunction/core\";\nimport { SharedService } from \"./shared.service\";\nimport { BaseNavigationComponent } from \"./base-navigation-component\";\nimport { ResourceData } from \"@memberjunction/core-entities\";\n\nexport abstract class BaseResourceComponent extends BaseNavigationComponent {\n private _data: ResourceData = new ResourceData();\n\n public get Data(): ResourceData {\n return this._data;\n }\n public set Data(value: ResourceData) {\n this._data = value;\n }\n\n private _loadComplete: boolean = false;\n public get LoadComplete(): boolean {\n return this._loadComplete;\n }\n\n private _loadStarted: boolean = false;\n public get LoadStarted(): boolean {\n return this._loadStarted;\n }\n\n \n private _loadCompleteEvent: any = null;\n public get LoadCompleteEvent(): any {\n return this._loadCompleteEvent\n }\n public set LoadCompleteEvent(value: any) {\n this._loadCompleteEvent = value;\n }\n\n private _loadStartedEvent: any = null;\n public get LoadStartedEvent(): any {\n return this._loadStartedEvent\n }\n public set LoadStartedEvent(value: any) {\n this._loadStartedEvent = value;\n }\n\n private _resourceRecordSavedEvent: any = null;\n public get ResourceRecordSavedEvent(): any {\n return this._resourceRecordSavedEvent\n }\n public set ResourceRecordSavedEvent(value: any) {\n this._resourceRecordSavedEvent = value;\n }\n\n private _displayNameChangedEvent: ((newName: string) => void) | null = null;\n public get DisplayNameChangedEvent(): ((newName: string) => void) | null {\n return this._displayNameChangedEvent;\n }\n public set DisplayNameChangedEvent(value: ((newName: string) => void) | null) {\n this._displayNameChangedEvent = value;\n }\n\n protected NotifyLoadComplete() {\n this._loadComplete = true;\n if (this._loadCompleteEvent) {\n this._loadCompleteEvent();\n }\n }\n\n protected NotifyLoadStarted() {\n this._loadStarted = true;\n if (this._loadStartedEvent) {\n this._loadStartedEvent();\n }\n }\n \n \n\n /**\n * Call this to notify the tab system that the resource's display name has changed.\n * The tab container will update the tab title and browser title accordingly.\n */\n protected NotifyDisplayNameChanged(newName: string): void {\n if (this._displayNameChangedEvent) {\n this._displayNameChangedEvent(newName);\n }\n }\n\n protected ResourceRecordSaved(resourceRecordEntity: BaseEntity) {\n this.Data.ResourceRecordID = resourceRecordEntity.PrimaryKey.ToString();\n if (this._resourceRecordSavedEvent) {\n this._resourceRecordSavedEvent(resourceRecordEntity);\n }\n }\n\n abstract GetResourceDisplayName(data: ResourceData): Promise<string>\n\n abstract GetResourceIconClass(data: ResourceData): Promise<string>\n}\n"]}
@@ -0,0 +1,73 @@
1
+ import { BehaviorSubject } from 'rxjs';
2
+ import { HomeAppPinnedItem, HomeAppPinInput, HomeAppPinUpdate } from './home-pin.types';
3
+ import * as i0 from "@angular/core";
4
+ /**
5
+ * Service for managing pinned items on the Home dashboard.
6
+ *
7
+ * All pin state is stored as a JSON blob in UserSettings via UserInfoEngine.
8
+ * Uses debounced saving to avoid excessive DB writes during edit mode operations.
9
+ */
10
+ export declare class HomeAppPinService {
11
+ /** Observable of current pins (for reactive UI) */
12
+ readonly Pins$: BehaviorSubject<HomeAppPinnedItem[]>;
13
+ private loaded;
14
+ /**
15
+ * Load pins from UserSettings. Safe to call multiple times (loads only once).
16
+ */
17
+ LoadPins(): Promise<void>;
18
+ /**
19
+ * Force reload from UserSettings (e.g. after external changes)
20
+ */
21
+ ReloadPins(): Promise<void>;
22
+ /**
23
+ * Add a new pin. Returns false if a duplicate already exists.
24
+ */
25
+ AddPin(input: HomeAppPinInput): boolean;
26
+ /**
27
+ * Remove a pin by ID
28
+ */
29
+ RemovePin(pinId: string): void;
30
+ /**
31
+ * Update pin properties (name, description, icon, group, thumbnail)
32
+ */
33
+ UpdatePin(pinId: string, updates: HomeAppPinUpdate): void;
34
+ /**
35
+ * Reorder pins. Accepts full reordered array with updated Sequence values.
36
+ */
37
+ ReorderPins(pins: HomeAppPinnedItem[]): void;
38
+ /**
39
+ * Check if a resource is already pinned
40
+ */
41
+ IsPinned(resourceType: string, config: Record<string, unknown>): boolean;
42
+ /**
43
+ * Find the pin for a given resource, if it exists
44
+ */
45
+ FindPin(resourceType: string, config: Record<string, unknown>): HomeAppPinnedItem | undefined;
46
+ /**
47
+ * Get ungrouped pins (Group is null/undefined/empty)
48
+ */
49
+ GetUngroupedPins(): HomeAppPinnedItem[];
50
+ /**
51
+ * Get unique group names in display order (ordered by lowest Sequence in group)
52
+ */
53
+ GetGroups(): string[];
54
+ /**
55
+ * Get pins in a specific group
56
+ */
57
+ GetPinsInGroup(groupName: string): HomeAppPinnedItem[];
58
+ /**
59
+ * Capture a thumbnail of a DOM element using html-to-image.
60
+ * Uses SVG foreignObject so the browser's own CSS engine handles
61
+ * modern features like color-mix() that html2canvas can't parse.
62
+ * Returns a base64 JPEG data URL (~5-15KB) or undefined on failure.
63
+ */
64
+ CaptureThumbnail(element: HTMLElement): Promise<string | undefined>;
65
+ private matchesResource;
66
+ private updateAndSave;
67
+ private sortPins;
68
+ private resequence;
69
+ private generateId;
70
+ static ɵfac: i0.ɵɵFactoryDeclaration<HomeAppPinService, never>;
71
+ static ɵprov: i0.ɵɵInjectableDeclaration<HomeAppPinService>;
72
+ }
73
+ //# sourceMappingURL=home-pin.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"home-pin.service.d.ts","sourceRoot":"","sources":["../../src/lib/home-pin.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAGvC,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;;AAKxF;;;;;GAKG;AACH,qBACa,iBAAiB;IAC5B,mDAAmD;IACnD,SAAgB,KAAK,uCAAgD;IAErE,OAAO,CAAC,MAAM,CAAS;IAMvB;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB/B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IASjC;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO;IAkBvC;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAM9B;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAQzD;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,iBAAiB,EAAE,GAAG,IAAI;IAS5C;;OAEG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO;IAIxE;;OAEG;IACH,OAAO,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,iBAAiB,GAAG,SAAS;IAQ7F;;OAEG;IACH,gBAAgB,IAAI,iBAAiB,EAAE;IAIvC;;OAEG;IACH,SAAS,IAAI,MAAM,EAAE;IAcrB;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAUtD;;;;;OAKG;IACG,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAmBzE,OAAO,CAAC,eAAe;IAiCvB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,UAAU;yCAhOP,iBAAiB;6CAAjB,iBAAiB;CA4O7B"}
@@ -0,0 +1,233 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { BehaviorSubject } from 'rxjs';
3
+ import { UserInfoEngine } from '@memberjunction/core-entities';
4
+ import { toJpeg } from 'html-to-image';
5
+ import * as i0 from "@angular/core";
6
+ /** UserSettings key for persisting pins */
7
+ const PINS_SETTING_KEY = 'HomeApp.PinnedItems';
8
+ /**
9
+ * Service for managing pinned items on the Home dashboard.
10
+ *
11
+ * All pin state is stored as a JSON blob in UserSettings via UserInfoEngine.
12
+ * Uses debounced saving to avoid excessive DB writes during edit mode operations.
13
+ */
14
+ export class HomeAppPinService {
15
+ /** Observable of current pins (for reactive UI) */
16
+ Pins$ = new BehaviorSubject([]);
17
+ loaded = false;
18
+ // =============================================
19
+ // LOADING
20
+ // =============================================
21
+ /**
22
+ * Load pins from UserSettings. Safe to call multiple times (loads only once).
23
+ */
24
+ async LoadPins() {
25
+ if (this.loaded)
26
+ return;
27
+ const raw = UserInfoEngine.Instance.GetSetting(PINS_SETTING_KEY);
28
+ if (raw) {
29
+ try {
30
+ const pins = JSON.parse(raw);
31
+ this.Pins$.next(this.sortPins(pins));
32
+ }
33
+ catch (err) {
34
+ console.warn('HomeAppPinService: Failed to parse saved pins, starting fresh', err);
35
+ this.Pins$.next([]);
36
+ }
37
+ }
38
+ this.loaded = true;
39
+ }
40
+ /**
41
+ * Force reload from UserSettings (e.g. after external changes)
42
+ */
43
+ async ReloadPins() {
44
+ this.loaded = false;
45
+ await this.LoadPins();
46
+ }
47
+ // =============================================
48
+ // CRUD OPERATIONS
49
+ // =============================================
50
+ /**
51
+ * Add a new pin. Returns false if a duplicate already exists.
52
+ */
53
+ AddPin(input) {
54
+ if (this.IsPinned(input.ResourceType, input.Configuration)) {
55
+ return false;
56
+ }
57
+ const pins = [...this.Pins$.value];
58
+ const newPin = {
59
+ ...input,
60
+ Id: this.generateId(),
61
+ Sequence: pins.length,
62
+ PinnedAt: new Date().toISOString()
63
+ };
64
+ pins.push(newPin);
65
+ this.updateAndSave(pins);
66
+ return true;
67
+ }
68
+ /**
69
+ * Remove a pin by ID
70
+ */
71
+ RemovePin(pinId) {
72
+ const pins = this.Pins$.value.filter(p => p.Id !== pinId);
73
+ this.resequence(pins);
74
+ this.updateAndSave(pins);
75
+ }
76
+ /**
77
+ * Update pin properties (name, description, icon, group, thumbnail)
78
+ */
79
+ UpdatePin(pinId, updates) {
80
+ const pins = this.Pins$.value.map(p => {
81
+ if (p.Id !== pinId)
82
+ return p;
83
+ return { ...p, ...updates };
84
+ });
85
+ this.updateAndSave(pins);
86
+ }
87
+ /**
88
+ * Reorder pins. Accepts full reordered array with updated Sequence values.
89
+ */
90
+ ReorderPins(pins) {
91
+ this.resequence(pins);
92
+ this.updateAndSave(pins);
93
+ }
94
+ // =============================================
95
+ // DUPLICATE DETECTION
96
+ // =============================================
97
+ /**
98
+ * Check if a resource is already pinned
99
+ */
100
+ IsPinned(resourceType, config) {
101
+ return this.Pins$.value.some(pin => this.matchesResource(pin, resourceType, config));
102
+ }
103
+ /**
104
+ * Find the pin for a given resource, if it exists
105
+ */
106
+ FindPin(resourceType, config) {
107
+ return this.Pins$.value.find(pin => this.matchesResource(pin, resourceType, config));
108
+ }
109
+ // =============================================
110
+ // HELPERS
111
+ // =============================================
112
+ /**
113
+ * Get ungrouped pins (Group is null/undefined/empty)
114
+ */
115
+ GetUngroupedPins() {
116
+ return this.Pins$.value.filter(p => !p.Group);
117
+ }
118
+ /**
119
+ * Get unique group names in display order (ordered by lowest Sequence in group)
120
+ */
121
+ GetGroups() {
122
+ const groupMinSeq = new Map();
123
+ for (const pin of this.Pins$.value) {
124
+ if (!pin.Group)
125
+ continue;
126
+ const current = groupMinSeq.get(pin.Group);
127
+ if (current === undefined || pin.Sequence < current) {
128
+ groupMinSeq.set(pin.Group, pin.Sequence);
129
+ }
130
+ }
131
+ return [...groupMinSeq.entries()]
132
+ .sort((a, b) => a[1] - b[1])
133
+ .map(([name]) => name);
134
+ }
135
+ /**
136
+ * Get pins in a specific group
137
+ */
138
+ GetPinsInGroup(groupName) {
139
+ return this.Pins$.value
140
+ .filter(p => p.Group === groupName)
141
+ .sort((a, b) => a.Sequence - b.Sequence);
142
+ }
143
+ // =============================================
144
+ // THUMBNAIL CAPTURE
145
+ // =============================================
146
+ /**
147
+ * Capture a thumbnail of a DOM element using html-to-image.
148
+ * Uses SVG foreignObject so the browser's own CSS engine handles
149
+ * modern features like color-mix() that html2canvas can't parse.
150
+ * Returns a base64 JPEG data URL (~5-15KB) or undefined on failure.
151
+ */
152
+ async CaptureThumbnail(element) {
153
+ try {
154
+ if (element.clientWidth === 0 || element.clientHeight === 0) {
155
+ return undefined;
156
+ }
157
+ return await toJpeg(element, {
158
+ quality: 0.6,
159
+ pixelRatio: 0.2,
160
+ cacheBust: true,
161
+ });
162
+ }
163
+ catch {
164
+ return undefined;
165
+ }
166
+ }
167
+ // =============================================
168
+ // PRIVATE
169
+ // =============================================
170
+ matchesResource(pin, resourceType, config) {
171
+ if (pin.ResourceType !== resourceType)
172
+ return false;
173
+ switch (resourceType) {
174
+ case 'Dashboards':
175
+ return pin.Configuration['dashboardId'] === config['dashboardId'];
176
+ case 'User Views':
177
+ return pin.Configuration['viewId'] === config['viewId'];
178
+ case 'Queries':
179
+ return pin.Configuration['queryId'] === config['queryId'];
180
+ case 'Reports':
181
+ return pin.Configuration['reportId'] === config['reportId'];
182
+ case 'Records':
183
+ return pin.Configuration['Entity'] === config['Entity']
184
+ && pin.Configuration['recordId'] === config['recordId'];
185
+ case 'Custom': {
186
+ // Match by app name + nav item name + route — never use appId or driverClass
187
+ // Include route to differentiate same nav item with different URL paths
188
+ const sameApp = pin.Configuration['appName'] === config['appName'];
189
+ const sameNav = pin.Configuration['navItemName'] === config['navItemName'];
190
+ const pinRoute = pin.Configuration['route'] || '';
191
+ const configRoute = config['route'] || '';
192
+ const sameRoute = pinRoute === configRoute;
193
+ // Also compare queryParams if present
194
+ const pinQP = JSON.stringify(pin.Configuration['queryParams'] || {});
195
+ const configQP = JSON.stringify(config['queryParams'] || {});
196
+ const sameQP = pinQP === configQP;
197
+ return sameApp && sameNav && sameRoute && sameQP;
198
+ }
199
+ default:
200
+ return false;
201
+ }
202
+ }
203
+ updateAndSave(pins) {
204
+ const sorted = this.sortPins(pins);
205
+ this.Pins$.next(sorted);
206
+ UserInfoEngine.Instance.SetSettingDebounced(PINS_SETTING_KEY, JSON.stringify(sorted));
207
+ }
208
+ sortPins(pins) {
209
+ return [...pins].sort((a, b) => a.Sequence - b.Sequence);
210
+ }
211
+ resequence(pins) {
212
+ pins.forEach((pin, i) => { pin.Sequence = i; });
213
+ }
214
+ generateId() {
215
+ // Crypto.randomUUID is available in modern browsers
216
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) {
217
+ return crypto.randomUUID();
218
+ }
219
+ // Fallback for older browsers
220
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
221
+ const r = (Math.random() * 16) | 0;
222
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
223
+ return v.toString(16);
224
+ });
225
+ }
226
+ static ɵfac = function HomeAppPinService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || HomeAppPinService)(); };
227
+ static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: HomeAppPinService, factory: HomeAppPinService.ɵfac, providedIn: 'root' });
228
+ }
229
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(HomeAppPinService, [{
230
+ type: Injectable,
231
+ args: [{ providedIn: 'root' }]
232
+ }], null, null); })();
233
+ //# sourceMappingURL=home-pin.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"home-pin.service.js","sourceRoot":"","sources":["../../src/lib/home-pin.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { BehaviorSubject } from 'rxjs';\nimport { UserInfoEngine } from '@memberjunction/core-entities';\nimport { toJpeg } from 'html-to-image';\nimport { HomeAppPinnedItem, HomeAppPinInput, HomeAppPinUpdate } from './home-pin.types';\n\n/** UserSettings key for persisting pins */\nconst PINS_SETTING_KEY = 'HomeApp.PinnedItems';\n\n/**\n * Service for managing pinned items on the Home dashboard.\n *\n * All pin state is stored as a JSON blob in UserSettings via UserInfoEngine.\n * Uses debounced saving to avoid excessive DB writes during edit mode operations.\n */\n@Injectable({ providedIn: 'root' })\nexport class HomeAppPinService {\n /** Observable of current pins (for reactive UI) */\n public readonly Pins$ = new BehaviorSubject<HomeAppPinnedItem[]>([]);\n\n private loaded = false;\n\n // =============================================\n // LOADING\n // =============================================\n\n /**\n * Load pins from UserSettings. Safe to call multiple times (loads only once).\n */\n async LoadPins(): Promise<void> {\n if (this.loaded) return;\n\n const raw = UserInfoEngine.Instance.GetSetting(PINS_SETTING_KEY);\n if (raw) {\n try {\n const pins = JSON.parse(raw) as HomeAppPinnedItem[];\n this.Pins$.next(this.sortPins(pins));\n } catch (err) {\n console.warn('HomeAppPinService: Failed to parse saved pins, starting fresh', err);\n this.Pins$.next([]);\n }\n }\n this.loaded = true;\n }\n\n /**\n * Force reload from UserSettings (e.g. after external changes)\n */\n async ReloadPins(): Promise<void> {\n this.loaded = false;\n await this.LoadPins();\n }\n\n // =============================================\n // CRUD OPERATIONS\n // =============================================\n\n /**\n * Add a new pin. Returns false if a duplicate already exists.\n */\n AddPin(input: HomeAppPinInput): boolean {\n if (this.IsPinned(input.ResourceType, input.Configuration)) {\n return false;\n }\n\n const pins = [...this.Pins$.value];\n const newPin: HomeAppPinnedItem = {\n ...input,\n Id: this.generateId(),\n Sequence: pins.length,\n PinnedAt: new Date().toISOString()\n };\n\n pins.push(newPin);\n this.updateAndSave(pins);\n return true;\n }\n\n /**\n * Remove a pin by ID\n */\n RemovePin(pinId: string): void {\n const pins = this.Pins$.value.filter(p => p.Id !== pinId);\n this.resequence(pins);\n this.updateAndSave(pins);\n }\n\n /**\n * Update pin properties (name, description, icon, group, thumbnail)\n */\n UpdatePin(pinId: string, updates: HomeAppPinUpdate): void {\n const pins = this.Pins$.value.map(p => {\n if (p.Id !== pinId) return p;\n return { ...p, ...updates };\n });\n this.updateAndSave(pins);\n }\n\n /**\n * Reorder pins. Accepts full reordered array with updated Sequence values.\n */\n ReorderPins(pins: HomeAppPinnedItem[]): void {\n this.resequence(pins);\n this.updateAndSave(pins);\n }\n\n // =============================================\n // DUPLICATE DETECTION\n // =============================================\n\n /**\n * Check if a resource is already pinned\n */\n IsPinned(resourceType: string, config: Record<string, unknown>): boolean {\n return this.Pins$.value.some(pin => this.matchesResource(pin, resourceType, config));\n }\n\n /**\n * Find the pin for a given resource, if it exists\n */\n FindPin(resourceType: string, config: Record<string, unknown>): HomeAppPinnedItem | undefined {\n return this.Pins$.value.find(pin => this.matchesResource(pin, resourceType, config));\n }\n\n // =============================================\n // HELPERS\n // =============================================\n\n /**\n * Get ungrouped pins (Group is null/undefined/empty)\n */\n GetUngroupedPins(): HomeAppPinnedItem[] {\n return this.Pins$.value.filter(p => !p.Group);\n }\n\n /**\n * Get unique group names in display order (ordered by lowest Sequence in group)\n */\n GetGroups(): string[] {\n const groupMinSeq = new Map<string, number>();\n for (const pin of this.Pins$.value) {\n if (!pin.Group) continue;\n const current = groupMinSeq.get(pin.Group);\n if (current === undefined || pin.Sequence < current) {\n groupMinSeq.set(pin.Group, pin.Sequence);\n }\n }\n return [...groupMinSeq.entries()]\n .sort((a, b) => a[1] - b[1])\n .map(([name]) => name);\n }\n\n /**\n * Get pins in a specific group\n */\n GetPinsInGroup(groupName: string): HomeAppPinnedItem[] {\n return this.Pins$.value\n .filter(p => p.Group === groupName)\n .sort((a, b) => a.Sequence - b.Sequence);\n }\n\n // =============================================\n // THUMBNAIL CAPTURE\n // =============================================\n\n /**\n * Capture a thumbnail of a DOM element using html-to-image.\n * Uses SVG foreignObject so the browser's own CSS engine handles\n * modern features like color-mix() that html2canvas can't parse.\n * Returns a base64 JPEG data URL (~5-15KB) or undefined on failure.\n */\n async CaptureThumbnail(element: HTMLElement): Promise<string | undefined> {\n try {\n if (element.clientWidth === 0 || element.clientHeight === 0) {\n return undefined;\n }\n return await toJpeg(element, {\n quality: 0.6,\n pixelRatio: 0.2,\n cacheBust: true,\n });\n } catch {\n return undefined;\n }\n }\n\n // =============================================\n // PRIVATE\n // =============================================\n\n private matchesResource(pin: HomeAppPinnedItem, resourceType: string, config: Record<string, unknown>): boolean {\n if (pin.ResourceType !== resourceType) return false;\n switch (resourceType) {\n case 'Dashboards':\n return pin.Configuration['dashboardId'] === config['dashboardId'];\n case 'User Views':\n return pin.Configuration['viewId'] === config['viewId'];\n case 'Queries':\n return pin.Configuration['queryId'] === config['queryId'];\n case 'Reports':\n return pin.Configuration['reportId'] === config['reportId'];\n case 'Records':\n return pin.Configuration['Entity'] === config['Entity']\n && pin.Configuration['recordId'] === config['recordId'];\n case 'Custom': {\n // Match by app name + nav item name + route — never use appId or driverClass\n // Include route to differentiate same nav item with different URL paths\n const sameApp = pin.Configuration['appName'] === config['appName'];\n const sameNav = pin.Configuration['navItemName'] === config['navItemName'];\n const pinRoute = pin.Configuration['route'] as string || '';\n const configRoute = config['route'] as string || '';\n const sameRoute = pinRoute === configRoute;\n // Also compare queryParams if present\n const pinQP = JSON.stringify(pin.Configuration['queryParams'] || {});\n const configQP = JSON.stringify(config['queryParams'] || {});\n const sameQP = pinQP === configQP;\n return sameApp && sameNav && sameRoute && sameQP;\n }\n default:\n return false;\n }\n }\n\n private updateAndSave(pins: HomeAppPinnedItem[]): void {\n const sorted = this.sortPins(pins);\n this.Pins$.next(sorted);\n UserInfoEngine.Instance.SetSettingDebounced(\n PINS_SETTING_KEY,\n JSON.stringify(sorted)\n );\n }\n\n private sortPins(pins: HomeAppPinnedItem[]): HomeAppPinnedItem[] {\n return [...pins].sort((a, b) => a.Sequence - b.Sequence);\n }\n\n private resequence(pins: HomeAppPinnedItem[]): void {\n pins.forEach((pin, i) => { pin.Sequence = i; });\n }\n\n private generateId(): string {\n // Crypto.randomUUID is available in modern browsers\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n}\n"]}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * A pinned item on the Home dashboard.
3
+ * Stored as JSON array in UserSettings key 'HomeApp.PinnedItems'.
4
+ */
5
+ export interface HomeAppPinnedItem {
6
+ /** Client-generated UUID */
7
+ Id: string;
8
+ /** User-editable display name (defaults to tab title at pin time) */
9
+ DisplayName: string;
10
+ /** Optional user-editable subtitle */
11
+ Description?: string;
12
+ /** FA icon class override. Null = auto-resolve from entity/app metadata */
13
+ Icon?: string;
14
+ /** Hex color override. Null = derive from source app's Color */
15
+ Color?: string;
16
+ /** MJ resource type: "Dashboards", "User Views", "Reports", "Queries", "Records", "Custom" */
17
+ ResourceType: string;
18
+ /** Source app ID (null = orphan / Home context) */
19
+ ApplicationID?: string;
20
+ /** Source app name for display badge ("AI", "CRM", etc.) */
21
+ ApplicationName?: string;
22
+ /** Full TabConfiguration blob - everything needed to re-open the resource */
23
+ Configuration: Record<string, unknown>;
24
+ /** Base64 JPEG thumbnail screenshot (~5-15KB at 0.2x scale). Null = use icon fallback */
25
+ Thumbnail?: string;
26
+ /** Display order within group (0-based) */
27
+ Sequence: number;
28
+ /** Optional group name for organizing pins. Null = ungrouped (shown first) */
29
+ Group?: string;
30
+ /** ISO timestamp of when this was pinned */
31
+ PinnedAt: string;
32
+ }
33
+ /**
34
+ * Input for creating a new pin (ID, Sequence, and PinnedAt are auto-generated)
35
+ */
36
+ export type HomeAppPinInput = Omit<HomeAppPinnedItem, 'Id' | 'Sequence' | 'PinnedAt'>;
37
+ /**
38
+ * Fields that can be updated on an existing pin
39
+ */
40
+ export type HomeAppPinUpdate = Partial<Pick<HomeAppPinnedItem, 'DisplayName' | 'Description' | 'Icon' | 'Color' | 'Group' | 'Thumbnail'>>;
41
+ //# sourceMappingURL=home-pin.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"home-pin.types.d.ts","sourceRoot":"","sources":["../../src/lib/home-pin.types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,4BAA4B;IAC5B,EAAE,EAAE,MAAM,CAAC;IAEX,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IAEpB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,8FAA8F;IAC9F,YAAY,EAAE,MAAM,CAAC;IAErB,mDAAmD;IACnD,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,4DAA4D;IAC5D,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEvC,yFAAyF;IACzF,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IAEjB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,iBAAiB,EAAE,IAAI,GAAG,UAAU,GAAG,UAAU,CAAC,CAAC;AAEtF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,aAAa,GAAG,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=home-pin.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"home-pin.types.js","sourceRoot":"","sources":["../../src/lib/home-pin.types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * A pinned item on the Home dashboard.\n * Stored as JSON array in UserSettings key 'HomeApp.PinnedItems'.\n */\nexport interface HomeAppPinnedItem {\n /** Client-generated UUID */\n Id: string;\n\n /** User-editable display name (defaults to tab title at pin time) */\n DisplayName: string;\n\n /** Optional user-editable subtitle */\n Description?: string;\n\n /** FA icon class override. Null = auto-resolve from entity/app metadata */\n Icon?: string;\n\n /** Hex color override. Null = derive from source app's Color */\n Color?: string;\n\n /** MJ resource type: \"Dashboards\", \"User Views\", \"Reports\", \"Queries\", \"Records\", \"Custom\" */\n ResourceType: string;\n\n /** Source app ID (null = orphan / Home context) */\n ApplicationID?: string;\n\n /** Source app name for display badge (\"AI\", \"CRM\", etc.) */\n ApplicationName?: string;\n\n /** Full TabConfiguration blob - everything needed to re-open the resource */\n Configuration: Record<string, unknown>;\n\n /** Base64 JPEG thumbnail screenshot (~5-15KB at 0.2x scale). Null = use icon fallback */\n Thumbnail?: string;\n\n /** Display order within group (0-based) */\n Sequence: number;\n\n /** Optional group name for organizing pins. Null = ungrouped (shown first) */\n Group?: string;\n\n /** ISO timestamp of when this was pinned */\n PinnedAt: string;\n}\n\n/**\n * Input for creating a new pin (ID, Sequence, and PinnedAt are auto-generated)\n */\nexport type HomeAppPinInput = Omit<HomeAppPinnedItem, 'Id' | 'Sequence' | 'PinnedAt'>;\n\n/**\n * Fields that can be updated on an existing pin\n */\nexport type HomeAppPinUpdate = Partial<Pick<HomeAppPinnedItem, 'DisplayName' | 'Description' | 'Icon' | 'Color' | 'Group' | 'Thumbnail'>>;\n"]}
@@ -8,6 +8,8 @@ export * from './lib/navigation.service';
8
8
  export * from './lib/navigation.interfaces';
9
9
  export * from './lib/title.service';
10
10
  export * from './lib/developer-mode.service';
11
+ export * from './lib/home-pin.types';
12
+ export * from './lib/home-pin.service';
11
13
  export { SYSTEM_APP_ID } from './lib/navigation.service';
12
14
  export * from '@memberjunction/ng-shared-generic';
13
15
  export * from './module';
@@ -1 +1 @@
1
- {"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAIA,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,8BAA8B,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,cAAc,mCAAmC,CAAC;AAClD,cAAc,UAAU,CAAC"}
1
+ {"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAIA,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,cAAc,mCAAmC,CAAC;AAClD,cAAc,UAAU,CAAC"}
@@ -11,6 +11,8 @@ export * from './lib/navigation.service';
11
11
  export * from './lib/navigation.interfaces';
12
12
  export * from './lib/title.service';
13
13
  export * from './lib/developer-mode.service';
14
+ export * from './lib/home-pin.types';
15
+ export * from './lib/home-pin.service';
14
16
  export { SYSTEM_APP_ID } from './lib/navigation.service';
15
17
  // Re-export from ng-shared-generic for backwards compatibility
16
18
  export * from '@memberjunction/ng-shared-generic';
@@ -1 +1 @@
1
- {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,8BAA8B,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,+DAA+D;AAC/D,cAAc,mCAAmC,CAAC;AAClD,cAAc,UAAU,CAAC","sourcesContent":["/*\n * Public API Surface\n */\n\nexport * from './lib/urlPipe';\nexport * from './lib/base-dashboard';\nexport * from './lib/simpleTextFormat';\nexport * from './lib/shared.service';\nexport * from './lib/base-resource-component'\nexport * from './lib/base-navigation-component';\nexport * from './lib/navigation.service';\nexport * from './lib/navigation.interfaces';\nexport * from './lib/title.service';\nexport * from './lib/developer-mode.service';\nexport { SYSTEM_APP_ID } from './lib/navigation.service';\n// Re-export from ng-shared-generic for backwards compatibility\nexport * from '@memberjunction/ng-shared-generic';\nexport * from './module';"]}
1
+ {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,+DAA+D;AAC/D,cAAc,mCAAmC,CAAC;AAClD,cAAc,UAAU,CAAC","sourcesContent":["/*\n * Public API Surface\n */\n\nexport * from './lib/urlPipe';\nexport * from './lib/base-dashboard';\nexport * from './lib/simpleTextFormat';\nexport * from './lib/shared.service';\nexport * from './lib/base-resource-component'\nexport * from './lib/base-navigation-component';\nexport * from './lib/navigation.service';\nexport * from './lib/navigation.interfaces';\nexport * from './lib/title.service';\nexport * from './lib/developer-mode.service';\nexport * from './lib/home-pin.types';\nexport * from './lib/home-pin.service';\nexport { SYSTEM_APP_ID } from './lib/navigation.service';\n// Re-export from ng-shared-generic for backwards compatibility\nexport * from '@memberjunction/ng-shared-generic';\nexport * from './module';"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-shared",
3
- "version": "5.21.0",
3
+ "version": "5.22.0",
4
4
  "description": "MemberJunction: MJ Explorer Angular Shared Package - utility functions and other reusable elements used across other MJ Angular packages within the MJ Explorer App - do not use outside of MJ Explorer.",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -29,17 +29,18 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@angular/platform-browser": "21.1.3",
32
- "@memberjunction/ai-engine-base": "5.21.0",
33
- "@memberjunction/core": "5.21.0",
34
- "@memberjunction/core-entities": "5.21.0",
35
- "@memberjunction/global": "5.21.0",
36
- "@memberjunction/graphql-dataprovider": "5.21.0",
37
- "@memberjunction/entity-communications-base": "5.21.0",
38
- "@memberjunction/ng-base-application": "5.21.0",
39
- "@memberjunction/ng-base-types": "5.21.0",
40
- "@memberjunction/ng-notifications": "5.21.0",
41
- "@memberjunction/ng-shared-generic": "5.21.0",
32
+ "@memberjunction/ai-engine-base": "5.22.0",
33
+ "@memberjunction/core": "5.22.0",
34
+ "@memberjunction/core-entities": "5.22.0",
35
+ "@memberjunction/global": "5.22.0",
36
+ "@memberjunction/graphql-dataprovider": "5.22.0",
37
+ "@memberjunction/entity-communications-base": "5.22.0",
38
+ "@memberjunction/ng-base-application": "5.22.0",
39
+ "@memberjunction/ng-base-types": "5.22.0",
40
+ "@memberjunction/ng-notifications": "5.22.0",
41
+ "@memberjunction/ng-shared-generic": "5.22.0",
42
42
  "@progress/kendo-angular-notification": "22.0.1",
43
+ "html-to-image": "^1.11.11",
43
44
  "tslib": "^2.8.1"
44
45
  },
45
46
  "sideEffects": false,