@memberjunction/ng-list-management 6.2.0-edge.6 → 6.2.0-edge.8
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/dist/lib/components/list-stats/list-stats.component.d.ts.map +1 -1
- package/dist/lib/components/list-stats/list-stats.component.js +1 -0
- package/dist/lib/components/list-stats/list-stats.component.js.map +1 -1
- package/dist/lib/services/list-management.service.js +2 -2
- package/dist/lib/services/list-management.service.js.map +1 -1
- package/package.json +11 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-stats.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/list-stats/list-stats.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgE,MAAM,EAAU,MAAM,eAAe,CAAC;AAE7G,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;AAGrE;;;;;;;;;;;;;GAaG;AAEH,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,IAAI,CAAC;CACZ;AAOD,qBAOa,kBAAmB,SAAQ,oBAAqB,YAAW,MAAM;IAC5E,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA6B;IAEjD,IACI,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAK9B;IACD,IAAI,MAAM,IAAI,MAAM,GAAG,IAAI,CAE1B;IACD,OAAO,CAAC,OAAO,CAAuB;IAEtC;;;;OAIG;IACH,IACI,cAAc,CAAC,KAAK,EAAE,MAAM,EAK/B;IACD,IAAI,cAAc,IAAI,MAAM,CAE3B;IACD,OAAO,CAAC,eAAe,CAAK;IAErB,WAAW,SAAK;IAEvB,2CAA2C;IAC3C,IAAW,WAAW,WAErB;IACD,2CAA2C;IAC3C,IAAW,WAAW,CAAC,KAAK,QAAA,EAE3B;IACM,gBAAgB,SAAK;IAE5B,gDAAgD;IAChD,IAAW,gBAAgB,WAE1B;IACD,gDAAgD;IAChD,IAAW,gBAAgB,CAAC,KAAK,QAAA,EAEhC;IACM,eAAe,SAAK;IAE3B,+CAA+C;IAC/C,IAAW,eAAe,WAEzB;IACD,+CAA+C;IAC/C,IAAW,eAAe,CAAC,KAAK,QAAA,EAE/B;IACM,cAAc,EAAE,IAAI,GAAG,IAAI,CAAQ;IAE1C,8CAA8C;IAC9C,IAAW,cAAc,IAAI,IAAI,GAAG,IAAI,CAEvC;IACD,8CAA8C;IAC9C,IAAW,cAAc,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,EAE3C;IACM,MAAM,EAAE,cAAc,EAAE,CAAM;IAErC,sCAAsC;IACtC,IAAW,MAAM,IAAI,cAAc,EAAE,CAEpC;IACD,sCAAsC;IACtC,IAAW,MAAM,CAAC,KAAK,EAAE,cAAc,EAAE,EAExC;IACM,OAAO,UAAS;IAEvB,uCAAuC;IACvC,IAAW,OAAO,YAEjB;IACD,uCAAuC;IACvC,IAAW,OAAO,CAAC,KAAK,SAAA,EAEvB;IAED,OAAO,CAAC,WAAW,CAAS;IAEtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAKxB,cAAc,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM;IAgB7C,8CAA8C;IACvC,cAAc,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM;YAI/B,IAAI;yCAzHP,kBAAkB;2CAAlB,kBAAkB;
|
|
1
|
+
{"version":3,"file":"list-stats.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/list-stats/list-stats.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgE,MAAM,EAAU,MAAM,eAAe,CAAC;AAE7G,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;AAGrE;;;;;;;;;;;;;GAaG;AAEH,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,IAAI,CAAC;CACZ;AAOD,qBAOa,kBAAmB,SAAQ,oBAAqB,YAAW,MAAM;IAC5E,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA6B;IAEjD,IACI,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAK9B;IACD,IAAI,MAAM,IAAI,MAAM,GAAG,IAAI,CAE1B;IACD,OAAO,CAAC,OAAO,CAAuB;IAEtC;;;;OAIG;IACH,IACI,cAAc,CAAC,KAAK,EAAE,MAAM,EAK/B;IACD,IAAI,cAAc,IAAI,MAAM,CAE3B;IACD,OAAO,CAAC,eAAe,CAAK;IAErB,WAAW,SAAK;IAEvB,2CAA2C;IAC3C,IAAW,WAAW,WAErB;IACD,2CAA2C;IAC3C,IAAW,WAAW,CAAC,KAAK,QAAA,EAE3B;IACM,gBAAgB,SAAK;IAE5B,gDAAgD;IAChD,IAAW,gBAAgB,WAE1B;IACD,gDAAgD;IAChD,IAAW,gBAAgB,CAAC,KAAK,QAAA,EAEhC;IACM,eAAe,SAAK;IAE3B,+CAA+C;IAC/C,IAAW,eAAe,WAEzB;IACD,+CAA+C;IAC/C,IAAW,eAAe,CAAC,KAAK,QAAA,EAE/B;IACM,cAAc,EAAE,IAAI,GAAG,IAAI,CAAQ;IAE1C,8CAA8C;IAC9C,IAAW,cAAc,IAAI,IAAI,GAAG,IAAI,CAEvC;IACD,8CAA8C;IAC9C,IAAW,cAAc,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,EAE3C;IACM,MAAM,EAAE,cAAc,EAAE,CAAM;IAErC,sCAAsC;IACtC,IAAW,MAAM,IAAI,cAAc,EAAE,CAEpC;IACD,sCAAsC;IACtC,IAAW,MAAM,CAAC,KAAK,EAAE,cAAc,EAAE,EAExC;IACM,OAAO,UAAS;IAEvB,uCAAuC;IACvC,IAAW,OAAO,YAEjB;IACD,uCAAuC;IACvC,IAAW,OAAO,CAAC,KAAK,SAAA,EAEvB;IAED,OAAO,CAAC,WAAW,CAAS;IAEtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAKxB,cAAc,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM;IAgB7C,8CAA8C;IACvC,cAAc,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM;YAI/B,IAAI;yCAzHP,kBAAkB;2CAAlB,kBAAkB;CAoP9B"}
|
|
@@ -260,6 +260,7 @@ export class ListStatsComponent extends BaseAngularComponent {
|
|
|
260
260
|
ExtraFilter: `ListID='${escapedId}'${cbPredicate}`,
|
|
261
261
|
OrderBy: '__mj_UpdatedAt DESC',
|
|
262
262
|
MaxRows: 1,
|
|
263
|
+
SkipTotalRowCount: true,
|
|
263
264
|
Fields: ['__mj_UpdatedAt'],
|
|
264
265
|
ResultType: 'simple',
|
|
265
266
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-stats.component.js","sourceRoot":"","sources":["../../../../src/lib/components/list-stats/list-stats.component.ts","../../../../src/lib/components/list-stats/list-stats.component.html"],"names":[],"mappings":"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","sourcesContent":["import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, inject } from '@angular/core';\n\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { RunView } from '@memberjunction/core';\n\n/**\n * Usage-stats side panel for a single list (mockup 25). Surfaces four\n * cheap-to-compute counters + a recent-activity feed pulled from the\n * generic `MJ: Audit Logs` table (same one Phase 2's sharing emits to).\n *\n * Stats:\n * - Member count — `count(MJ: List Details)` for this list\n * - Active shares — non-revoked `MJ: Resource Permissions`\n * - Growth this month — `count(MJ: List Details where __mj_CreatedAt >= start-of-month)`\n * - Time since last activity — newest audit-log timestamp\n *\n * Recent activity = the 5 most-recent audit events (uses the same join\n * pattern as `mj-list-audit-log` — type + user lookup in a single batch).\n */\n\ninterface RecentActivity {\n ID: string;\n EventType: string;\n Description: string;\n Who: string;\n When: Date;\n}\n\nfunction newestOf(...dates: (Date | null)[]): Date | null {\n const ms = dates.filter((d): d is Date => d != null).map((d) => d.getTime());\n return ms.length === 0 ? null : new Date(Math.max(...ms));\n}\n\n@Component({\n standalone: false,\n selector: 'mj-list-stats',\n templateUrl: './list-stats.component.html',\n styleUrls: ['./list-stats.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ListStatsComponent extends BaseAngularComponent implements OnInit {\n private readonly cdr = inject(ChangeDetectorRef);\n\n @Input()\n set ListID(value: string | null) {\n if (this._listId !== value) {\n this._listId = value;\n if (this.initialized && value) void this.load();\n }\n }\n get ListID(): string | null {\n return this._listId;\n }\n private _listId: string | null = null;\n\n /**\n * Bump this number from the parent after any mutation (add / remove /\n * status update / share / unshare) to force the stats panel to re-query.\n * Without this, the Members count drifts from reality until full reload.\n */\n @Input()\n set RefreshTrigger(value: number) {\n if (value !== this._refreshTrigger) {\n this._refreshTrigger = value;\n if (this.initialized && this._listId) void this.load();\n }\n }\n get RefreshTrigger(): number {\n return this._refreshTrigger;\n }\n private _refreshTrigger = 0;\n\n public MemberCount = 0;\n\n /** @deprecated Use {@link MemberCount}. */\n public get memberCount() {\n return this.MemberCount;\n }\n /** @deprecated Use {@link MemberCount}. */\n public set memberCount(value) {\n this.MemberCount = value;\n }\n public ActiveShareCount = 0;\n\n /** @deprecated Use {@link ActiveShareCount}. */\n public get activeShareCount() {\n return this.ActiveShareCount;\n }\n /** @deprecated Use {@link ActiveShareCount}. */\n public set activeShareCount(value) {\n this.ActiveShareCount = value;\n }\n public GrowthThisMonth = 0;\n\n /** @deprecated Use {@link GrowthThisMonth}. */\n public get growthThisMonth() {\n return this.GrowthThisMonth;\n }\n /** @deprecated Use {@link GrowthThisMonth}. */\n public set growthThisMonth(value) {\n this.GrowthThisMonth = value;\n }\n public LastActivityAt: Date | null = null;\n\n /** @deprecated Use {@link LastActivityAt}. */\n public get lastActivityAt(): Date | null {\n return this.LastActivityAt;\n }\n /** @deprecated Use {@link LastActivityAt}. */\n public set lastActivityAt(value: Date | null) {\n this.LastActivityAt = value;\n }\n public Recent: RecentActivity[] = [];\n\n /** @deprecated Use {@link Recent}. */\n public get recent(): RecentActivity[] {\n return this.Recent;\n }\n /** @deprecated Use {@link Recent}. */\n public set recent(value: RecentActivity[]) {\n this.Recent = value;\n }\n public Loading = false;\n\n /** @deprecated Use {@link Loading}. */\n public get loading() {\n return this.Loading;\n }\n /** @deprecated Use {@link Loading}. */\n public set loading(value) {\n this.Loading = value;\n }\n\n private initialized = false;\n\n async ngOnInit(): Promise<void> {\n this.initialized = true;\n if (this._listId) await this.load();\n }\n\n public FormatRelative(d: Date | null): string {\n if (!d || isNaN(d.getTime())) return 'never';\n const diff = Date.now() - d.getTime();\n if (diff < 60_000) return 'just now';\n const minutes = Math.floor(diff / 60_000);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n if (days < 7) return `${days}d ago`;\n const weeks = Math.floor(days / 7);\n if (weeks < 5) return `${weeks}w ago`;\n const months = Math.floor(days / 30);\n return `${months}mo ago`;\n }\n\n /** @deprecated Use {@link FormatRelative}. */\n public formatRelative(d: Date | null): string {\n return this.FormatRelative(d);\n }\n\n private async load(): Promise<void> {\n if (!this._listId) return;\n this.Loading = true;\n this.cdr.markForCheck();\n try {\n const md = this.ProviderToUse;\n const listsEntity = md.Entities.find((e) => e.Name === 'MJ: Lists');\n if (!listsEntity) return;\n const startOfMonth = new Date(new Date().getFullYear(), new Date().getMonth(), 1).toISOString();\n const escapedId = this._listId.replace(/'/g, \"''\");\n\n const rv = RunView.FromMetadataProvider(md);\n // Cache-buster: every RunView gets an always-true predicate that\n // varies on each load() call. The server keys its RunView cache off\n // the literal filter string, so we force a miss on every refresh.\n // Without this, Members / Growth / Active shares / Last activity\n // would all read stale until a full server restart, even after the\n // RefreshTrigger input bumps. SQL comments (`/* */`) are rejected by\n // the ExtraFilter injection validator, so we use a numeric equality\n // instead.\n const cb = Date.now();\n const cbPredicate = ` AND ${cb} = ${cb}`;\n const [members, growth, shares, audits, latestMember] = await Promise.all([\n rv.RunView<{ ID: string }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID='${escapedId}'${cbPredicate}`,\n Fields: ['ID'],\n ResultType: 'simple',\n }),\n rv.RunView<{ ID: string }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID='${escapedId}' AND __mj_CreatedAt >= '${startOfMonth}'${cbPredicate}`,\n Fields: ['ID'],\n ResultType: 'simple',\n }),\n rv.RunView<{ ID: string }>({\n EntityName: 'MJ: Resource Permissions',\n ExtraFilter:\n `ResourceTypeID='E64D433E-F36B-1410-8560-0041FA62858A' AND ResourceRecordID='${escapedId}' AND Status<>'Revoked'${cbPredicate}`,\n Fields: ['ID'],\n ResultType: 'simple',\n }),\n rv.RunView<{\n ID: string;\n UserID: string;\n AuditLogTypeID: string;\n Description: string | null;\n __mj_CreatedAt: Date;\n }>({\n EntityName: 'MJ: Audit Logs',\n ExtraFilter: `EntityID='${listsEntity.ID}' AND RecordID='${escapedId}'${cbPredicate}`,\n OrderBy: '__mj_CreatedAt DESC',\n MaxRows: 5,\n Fields: ['ID', 'UserID', 'AuditLogTypeID', 'Description', '__mj_CreatedAt'],\n ResultType: 'simple',\n }),\n // Newest list-detail timestamp. Audit Logs only fire for sharing\n // events, so without this the \"Last activity\" widget reads \"never\"\n // even after the user has been adding/editing members all day.\n rv.RunView<{ __mj_UpdatedAt: Date }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID='${escapedId}'${cbPredicate}`,\n OrderBy: '__mj_UpdatedAt DESC',\n MaxRows: 1,\n Fields: ['__mj_UpdatedAt'],\n ResultType: 'simple',\n }),\n ]);\n\n this.MemberCount = members.Results?.length ?? 0;\n this.GrowthThisMonth = growth.Results?.length ?? 0;\n this.ActiveShareCount = shares.Results?.length ?? 0;\n\n const auditRows = audits.Results ?? [];\n const latestAuditAt =\n auditRows.length > 0 ? new Date(auditRows[0].__mj_CreatedAt) : null;\n const rawLatestMember = latestMember.Results?.[0]?.__mj_UpdatedAt;\n const parsedLatestMember = rawLatestMember ? new Date(rawLatestMember as Date | string) : null;\n const latestMemberAt =\n parsedLatestMember && !isNaN(parsedLatestMember.getTime()) ? parsedLatestMember : null;\n this.LastActivityAt = newestOf(latestAuditAt, latestMemberAt);\n\n if (auditRows.length === 0) {\n this.Recent = [];\n return;\n }\n\n // Resolve user + audit-log type names in two cheap lookups.\n const userIds = Array.from(new Set(auditRows.map((r) => String(r.UserID))));\n const typeIds = Array.from(new Set(auditRows.map((r) => String(r.AuditLogTypeID))));\n const [users, types] = await Promise.all([\n rv.RunView<{ ID: string; Name: string }>({\n EntityName: 'MJ: Users',\n ExtraFilter: `ID IN (${userIds.map((id) => `'${id}'`).join(',')})`,\n Fields: ['ID', 'Name'],\n ResultType: 'simple',\n }),\n rv.RunView<{ ID: string; Name: string }>({\n EntityName: 'MJ: Audit Log Types',\n ExtraFilter: `ID IN (${typeIds.map((id) => `'${id}'`).join(',')})`,\n Fields: ['ID', 'Name'],\n ResultType: 'simple',\n }),\n ]);\n\n const userById = new Map<string, string>();\n for (const u of users.Results ?? []) userById.set(String(u.ID), String(u.Name));\n const typeById = new Map<string, string>();\n for (const t of types.Results ?? []) typeById.set(String(t.ID), String(t.Name));\n\n this.Recent = auditRows.map((r) => ({\n ID: String(r.ID),\n EventType: typeById.get(String(r.AuditLogTypeID)) ?? '(unknown event)',\n Description: r.Description ?? '',\n Who: userById.get(String(r.UserID)) ?? '(unknown user)',\n When: new Date(r.__mj_CreatedAt),\n }));\n } finally {\n this.Loading = false;\n this.cdr.markForCheck();\n }\n }\n}\n","<div class=\"list-stats\">\n @if (loading) {\n <div class=\"list-stats__loading\">\n <mj-loading text=\"Loading stats...\" size=\"small\"></mj-loading>\n </div>\n } @else {\n <div class=\"list-stats__heading\">\n <i class=\"fa-solid fa-chart-simple\"></i>\n Usage\n </div>\n\n <div class=\"list-stats__counters\">\n <div class=\"list-stats__counter\">\n <div class=\"list-stats__value\">{{ memberCount }}</div>\n <div class=\"list-stats__label\">Members</div>\n </div>\n <div class=\"list-stats__counter\">\n <div class=\"list-stats__value\">+{{ growthThisMonth }}</div>\n <div class=\"list-stats__label\">This month</div>\n </div>\n <div class=\"list-stats__counter\">\n <div class=\"list-stats__value\">{{ activeShareCount }}</div>\n <div class=\"list-stats__label\">Active shares</div>\n </div>\n <div class=\"list-stats__counter\">\n <div class=\"list-stats__value-text\">{{ formatRelative(lastActivityAt) }}</div>\n <div class=\"list-stats__label\">Last activity</div>\n </div>\n </div>\n\n @if (recent.length > 0) {\n <div class=\"list-stats__heading list-stats__heading--secondary\">\n <i class=\"fa-solid fa-clock-rotate-left\"></i>\n Recent activity\n </div>\n <ul class=\"list-stats__feed\">\n @for (item of recent; track item.ID) {\n <li class=\"list-stats__entry\">\n <span class=\"list-stats__entry-type\">{{ item.EventType }}</span>\n <span class=\"list-stats__entry-detail\">\n {{ item.Description }}\n <span class=\"list-stats__entry-meta\">— {{ item.Who }} · {{ formatRelative(item.When) }}</span>\n </span>\n </li>\n }\n </ul>\n }\n }\n</div>\n"]}
|
|
1
|
+
{"version":3,"file":"list-stats.component.js","sourceRoot":"","sources":["../../../../src/lib/components/list-stats/list-stats.component.ts","../../../../src/lib/components/list-stats/list-stats.component.html"],"names":[],"mappings":"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","sourcesContent":["import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, inject } from '@angular/core';\n\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { RunView } from '@memberjunction/core';\n\n/**\n * Usage-stats side panel for a single list (mockup 25). Surfaces four\n * cheap-to-compute counters + a recent-activity feed pulled from the\n * generic `MJ: Audit Logs` table (same one Phase 2's sharing emits to).\n *\n * Stats:\n * - Member count — `count(MJ: List Details)` for this list\n * - Active shares — non-revoked `MJ: Resource Permissions`\n * - Growth this month — `count(MJ: List Details where __mj_CreatedAt >= start-of-month)`\n * - Time since last activity — newest audit-log timestamp\n *\n * Recent activity = the 5 most-recent audit events (uses the same join\n * pattern as `mj-list-audit-log` — type + user lookup in a single batch).\n */\n\ninterface RecentActivity {\n ID: string;\n EventType: string;\n Description: string;\n Who: string;\n When: Date;\n}\n\nfunction newestOf(...dates: (Date | null)[]): Date | null {\n const ms = dates.filter((d): d is Date => d != null).map((d) => d.getTime());\n return ms.length === 0 ? null : new Date(Math.max(...ms));\n}\n\n@Component({\n standalone: false,\n selector: 'mj-list-stats',\n templateUrl: './list-stats.component.html',\n styleUrls: ['./list-stats.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ListStatsComponent extends BaseAngularComponent implements OnInit {\n private readonly cdr = inject(ChangeDetectorRef);\n\n @Input()\n set ListID(value: string | null) {\n if (this._listId !== value) {\n this._listId = value;\n if (this.initialized && value) void this.load();\n }\n }\n get ListID(): string | null {\n return this._listId;\n }\n private _listId: string | null = null;\n\n /**\n * Bump this number from the parent after any mutation (add / remove /\n * status update / share / unshare) to force the stats panel to re-query.\n * Without this, the Members count drifts from reality until full reload.\n */\n @Input()\n set RefreshTrigger(value: number) {\n if (value !== this._refreshTrigger) {\n this._refreshTrigger = value;\n if (this.initialized && this._listId) void this.load();\n }\n }\n get RefreshTrigger(): number {\n return this._refreshTrigger;\n }\n private _refreshTrigger = 0;\n\n public MemberCount = 0;\n\n /** @deprecated Use {@link MemberCount}. */\n public get memberCount() {\n return this.MemberCount;\n }\n /** @deprecated Use {@link MemberCount}. */\n public set memberCount(value) {\n this.MemberCount = value;\n }\n public ActiveShareCount = 0;\n\n /** @deprecated Use {@link ActiveShareCount}. */\n public get activeShareCount() {\n return this.ActiveShareCount;\n }\n /** @deprecated Use {@link ActiveShareCount}. */\n public set activeShareCount(value) {\n this.ActiveShareCount = value;\n }\n public GrowthThisMonth = 0;\n\n /** @deprecated Use {@link GrowthThisMonth}. */\n public get growthThisMonth() {\n return this.GrowthThisMonth;\n }\n /** @deprecated Use {@link GrowthThisMonth}. */\n public set growthThisMonth(value) {\n this.GrowthThisMonth = value;\n }\n public LastActivityAt: Date | null = null;\n\n /** @deprecated Use {@link LastActivityAt}. */\n public get lastActivityAt(): Date | null {\n return this.LastActivityAt;\n }\n /** @deprecated Use {@link LastActivityAt}. */\n public set lastActivityAt(value: Date | null) {\n this.LastActivityAt = value;\n }\n public Recent: RecentActivity[] = [];\n\n /** @deprecated Use {@link Recent}. */\n public get recent(): RecentActivity[] {\n return this.Recent;\n }\n /** @deprecated Use {@link Recent}. */\n public set recent(value: RecentActivity[]) {\n this.Recent = value;\n }\n public Loading = false;\n\n /** @deprecated Use {@link Loading}. */\n public get loading() {\n return this.Loading;\n }\n /** @deprecated Use {@link Loading}. */\n public set loading(value) {\n this.Loading = value;\n }\n\n private initialized = false;\n\n async ngOnInit(): Promise<void> {\n this.initialized = true;\n if (this._listId) await this.load();\n }\n\n public FormatRelative(d: Date | null): string {\n if (!d || isNaN(d.getTime())) return 'never';\n const diff = Date.now() - d.getTime();\n if (diff < 60_000) return 'just now';\n const minutes = Math.floor(diff / 60_000);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n if (days < 7) return `${days}d ago`;\n const weeks = Math.floor(days / 7);\n if (weeks < 5) return `${weeks}w ago`;\n const months = Math.floor(days / 30);\n return `${months}mo ago`;\n }\n\n /** @deprecated Use {@link FormatRelative}. */\n public formatRelative(d: Date | null): string {\n return this.FormatRelative(d);\n }\n\n private async load(): Promise<void> {\n if (!this._listId) return;\n this.Loading = true;\n this.cdr.markForCheck();\n try {\n const md = this.ProviderToUse;\n const listsEntity = md.Entities.find((e) => e.Name === 'MJ: Lists');\n if (!listsEntity) return;\n const startOfMonth = new Date(new Date().getFullYear(), new Date().getMonth(), 1).toISOString();\n const escapedId = this._listId.replace(/'/g, \"''\");\n\n const rv = RunView.FromMetadataProvider(md);\n // Cache-buster: every RunView gets an always-true predicate that\n // varies on each load() call. The server keys its RunView cache off\n // the literal filter string, so we force a miss on every refresh.\n // Without this, Members / Growth / Active shares / Last activity\n // would all read stale until a full server restart, even after the\n // RefreshTrigger input bumps. SQL comments (`/* */`) are rejected by\n // the ExtraFilter injection validator, so we use a numeric equality\n // instead.\n const cb = Date.now();\n const cbPredicate = ` AND ${cb} = ${cb}`;\n const [members, growth, shares, audits, latestMember] = await Promise.all([\n rv.RunView<{ ID: string }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID='${escapedId}'${cbPredicate}`,\n Fields: ['ID'],\n ResultType: 'simple',\n }),\n rv.RunView<{ ID: string }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID='${escapedId}' AND __mj_CreatedAt >= '${startOfMonth}'${cbPredicate}`,\n Fields: ['ID'],\n ResultType: 'simple',\n }),\n rv.RunView<{ ID: string }>({\n EntityName: 'MJ: Resource Permissions',\n ExtraFilter:\n `ResourceTypeID='E64D433E-F36B-1410-8560-0041FA62858A' AND ResourceRecordID='${escapedId}' AND Status<>'Revoked'${cbPredicate}`,\n Fields: ['ID'],\n ResultType: 'simple',\n }),\n rv.RunView<{\n ID: string;\n UserID: string;\n AuditLogTypeID: string;\n Description: string | null;\n __mj_CreatedAt: Date;\n }>({\n EntityName: 'MJ: Audit Logs',\n ExtraFilter: `EntityID='${listsEntity.ID}' AND RecordID='${escapedId}'${cbPredicate}`,\n OrderBy: '__mj_CreatedAt DESC',\n MaxRows: 5,\n Fields: ['ID', 'UserID', 'AuditLogTypeID', 'Description', '__mj_CreatedAt'],\n ResultType: 'simple',\n }),\n // Newest list-detail timestamp. Audit Logs only fire for sharing\n // events, so without this the \"Last activity\" widget reads \"never\"\n // even after the user has been adding/editing members all day.\n rv.RunView<{ __mj_UpdatedAt: Date }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID='${escapedId}'${cbPredicate}`,\n OrderBy: '__mj_UpdatedAt DESC',\n MaxRows: 1,\n SkipTotalRowCount: true,\n Fields: ['__mj_UpdatedAt'],\n ResultType: 'simple',\n }),\n ]);\n\n this.MemberCount = members.Results?.length ?? 0;\n this.GrowthThisMonth = growth.Results?.length ?? 0;\n this.ActiveShareCount = shares.Results?.length ?? 0;\n\n const auditRows = audits.Results ?? [];\n const latestAuditAt =\n auditRows.length > 0 ? new Date(auditRows[0].__mj_CreatedAt) : null;\n const rawLatestMember = latestMember.Results?.[0]?.__mj_UpdatedAt;\n const parsedLatestMember = rawLatestMember ? new Date(rawLatestMember as Date | string) : null;\n const latestMemberAt =\n parsedLatestMember && !isNaN(parsedLatestMember.getTime()) ? parsedLatestMember : null;\n this.LastActivityAt = newestOf(latestAuditAt, latestMemberAt);\n\n if (auditRows.length === 0) {\n this.Recent = [];\n return;\n }\n\n // Resolve user + audit-log type names in two cheap lookups.\n const userIds = Array.from(new Set(auditRows.map((r) => String(r.UserID))));\n const typeIds = Array.from(new Set(auditRows.map((r) => String(r.AuditLogTypeID))));\n const [users, types] = await Promise.all([\n rv.RunView<{ ID: string; Name: string }>({\n EntityName: 'MJ: Users',\n ExtraFilter: `ID IN (${userIds.map((id) => `'${id}'`).join(',')})`,\n Fields: ['ID', 'Name'],\n ResultType: 'simple',\n }),\n rv.RunView<{ ID: string; Name: string }>({\n EntityName: 'MJ: Audit Log Types',\n ExtraFilter: `ID IN (${typeIds.map((id) => `'${id}'`).join(',')})`,\n Fields: ['ID', 'Name'],\n ResultType: 'simple',\n }),\n ]);\n\n const userById = new Map<string, string>();\n for (const u of users.Results ?? []) userById.set(String(u.ID), String(u.Name));\n const typeById = new Map<string, string>();\n for (const t of types.Results ?? []) typeById.set(String(t.ID), String(t.Name));\n\n this.Recent = auditRows.map((r) => ({\n ID: String(r.ID),\n EventType: typeById.get(String(r.AuditLogTypeID)) ?? '(unknown event)',\n Description: r.Description ?? '',\n Who: userById.get(String(r.UserID)) ?? '(unknown user)',\n When: new Date(r.__mj_CreatedAt),\n }));\n } finally {\n this.Loading = false;\n this.cdr.markForCheck();\n }\n }\n}\n","<div class=\"list-stats\">\n @if (loading) {\n <div class=\"list-stats__loading\">\n <mj-loading text=\"Loading stats...\" size=\"small\"></mj-loading>\n </div>\n } @else {\n <div class=\"list-stats__heading\">\n <i class=\"fa-solid fa-chart-simple\"></i>\n Usage\n </div>\n\n <div class=\"list-stats__counters\">\n <div class=\"list-stats__counter\">\n <div class=\"list-stats__value\">{{ memberCount }}</div>\n <div class=\"list-stats__label\">Members</div>\n </div>\n <div class=\"list-stats__counter\">\n <div class=\"list-stats__value\">+{{ growthThisMonth }}</div>\n <div class=\"list-stats__label\">This month</div>\n </div>\n <div class=\"list-stats__counter\">\n <div class=\"list-stats__value\">{{ activeShareCount }}</div>\n <div class=\"list-stats__label\">Active shares</div>\n </div>\n <div class=\"list-stats__counter\">\n <div class=\"list-stats__value-text\">{{ formatRelative(lastActivityAt) }}</div>\n <div class=\"list-stats__label\">Last activity</div>\n </div>\n </div>\n\n @if (recent.length > 0) {\n <div class=\"list-stats__heading list-stats__heading--secondary\">\n <i class=\"fa-solid fa-clock-rotate-left\"></i>\n Recent activity\n </div>\n <ul class=\"list-stats__feed\">\n @for (item of recent; track item.ID) {\n <li class=\"list-stats__entry\">\n <span class=\"list-stats__entry-type\">{{ item.EventType }}</span>\n <span class=\"list-stats__entry-detail\">\n {{ item.Description }}\n <span class=\"list-stats__entry-meta\">— {{ item.Who }} · {{ formatRelative(item.When) }}</span>\n </span>\n </li>\n }\n </ul>\n }\n }\n</div>\n"]}
|
|
@@ -195,7 +195,7 @@ export class ListManagementService {
|
|
|
195
195
|
ExtraFilter: `ListID = '${listId}'`,
|
|
196
196
|
ResultType: 'count_only'
|
|
197
197
|
});
|
|
198
|
-
return result.Success ? result.TotalRowCount : 0;
|
|
198
|
+
return result.Success ? result.TotalRowCount ?? 0 : 0;
|
|
199
199
|
}
|
|
200
200
|
/** @deprecated Use {@link GetListItemCount}. */
|
|
201
201
|
async getListItemCount(listId) {
|
|
@@ -218,7 +218,7 @@ export class ListManagementService {
|
|
|
218
218
|
ResultType: 'count_only'
|
|
219
219
|
})));
|
|
220
220
|
countResults.forEach((res, idx) => {
|
|
221
|
-
countMap.set(lists[idx].ID, res.Success ? res.TotalRowCount : 0);
|
|
221
|
+
countMap.set(lists[idx].ID, res.Success ? res.TotalRowCount ?? 0 : 0);
|
|
222
222
|
});
|
|
223
223
|
}
|
|
224
224
|
for (const list of lists) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-management.service.js","sourceRoot":"","sources":["../../../src/lib/services/list-management.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { IMetadataProvider, Metadata, RunView, UserInfo } from '@memberjunction/core';\nimport { MJListEntity, MJListDetailEntity, MJListCategoryEntity } from '@memberjunction/core-entities';\nimport { BehaviorSubject, Observable } from 'rxjs';\nimport {\n ListItemViewModel,\n BatchOperationResult,\n RecordMembershipInfo,\n CreateListConfig\n} from '../models/list-management.models';\n\ninterface CacheEntry<T> {\n data: T;\n timestamp: Date;\n}\n\n/**\n * Service for managing list operations including data loading, caching,\n * and batch add/remove operations.\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 ListManagementService {\n private readonly CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\n // Cache storage\n private listCache = new Map<string, CacheEntry<MJListEntity[]>>();\n private categoryCache: CacheEntry<MJListCategoryEntity[]> | null = null;\n private membershipCache = new Map<string, CacheEntry<Map<string, string[]>>>();\n\n // Loading state subjects\n private loadingSubject = new BehaviorSubject<boolean>(false);\n public Loading$: Observable<boolean> = this.loadingSubject.asObservable();\n\n /** @deprecated Use {@link Loading$}. */\n public get loading$(): Observable<boolean> {\n return this.Loading$;\n }\n /** @deprecated Use {@link Loading$}. */\n public set loading$(value: Observable<boolean>) {\n this.Loading$ = value;\n }\n\n private _provider: IMetadataProvider | null = null;\n\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\n /**\n * Get lists for a specific entity, with optional caching\n */\n async GetListsForEntity(\n entityId: string,\n userId?: string,\n forceRefresh: boolean = false\n ): Promise<MJListEntity[]> {\n const cacheKey = `${entityId}_${userId || 'all'}`;\n\n // Check cache first\n if (!forceRefresh) {\n const cached = this.listCache.get(cacheKey);\n if (cached && this.isCacheValid(cached)) {\n return cached.data;\n }\n }\n\n this.loadingSubject.next(true);\n\n try {\n const rv = RunView.FromMetadataProvider(this.Provider);\n let filter = `EntityID = '${entityId}'`;\n\n if (userId) {\n filter += ` AND UserID = '${userId}'`;\n }\n\n const result = await rv.RunView<MJListEntity>({\n EntityName: 'MJ: Lists',\n ExtraFilter: filter,\n OrderBy: 'Name',\n ResultType: 'entity_object'\n });\n\n if (result.Success) {\n const lists = result.Results || [];\n this.listCache.set(cacheKey, {\n data: lists,\n timestamp: new Date()\n });\n return lists;\n } else {\n console.error('Failed to load lists:', result.ErrorMessage);\n return [];\n }\n } finally {\n this.loadingSubject.next(false);\n }\n }\n\n /** @deprecated Use {@link GetListsForEntity}. */\n async getListsForEntity(\n entityId: string,\n userId?: string,\n forceRefresh: boolean = false\n ): Promise<MJListEntity[]> {\n return this.GetListsForEntity(entityId, userId, forceRefresh);\n }\n\n /**\n * Get all list categories\n */\n async GetListCategories(forceRefresh: boolean = false): Promise<MJListCategoryEntity[]> {\n if (!forceRefresh && this.categoryCache && this.isCacheValid(this.categoryCache)) {\n return this.categoryCache.data;\n }\n\n const rv = RunView.FromMetadataProvider(this.Provider);\n const result = await rv.RunView<MJListCategoryEntity>({\n EntityName: 'MJ: List Categories',\n OrderBy: 'Name',\n ResultType: 'entity_object'\n });\n\n if (result.Success) {\n this.categoryCache = {\n data: result.Results || [],\n timestamp: new Date()\n };\n return this.categoryCache.data;\n }\n\n return [];\n }\n\n /** @deprecated Use {@link GetListCategories}. */\n async getListCategories(forceRefresh: boolean = false): Promise<MJListCategoryEntity[]> {\n return this.GetListCategories(forceRefresh);\n }\n\n /**\n * Get membership information for a set of records\n * Returns a Map where key is listId and value is array of recordIds that are members\n */\n async GetRecordMembership(\n entityId: string,\n recordIds: string[]\n ): Promise<Map<string, string[]>> {\n if (recordIds.length === 0) {\n return new Map();\n }\n\n const cacheKey = `${entityId}_${recordIds.sort().join(',')}`;\n\n // Check cache\n const cached = this.membershipCache.get(cacheKey);\n if (cached && this.isCacheValid(cached)) {\n return cached.data;\n }\n\n this.loadingSubject.next(true);\n\n try {\n const rv = RunView.FromMetadataProvider(this.Provider);\n\n // Get all list details for these records — read-only lookup, so\n // 'simple' with narrow Fields (no BaseEntity instantiation overhead)\n const recordIdFilter = recordIds.map(id => `'${id}'`).join(',');\n const result = await rv.RunView<{ ListID: string; RecordID: string }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `RecordID IN (${recordIdFilter})`,\n Fields: ['ListID', 'RecordID'],\n ResultType: 'simple'\n });\n\n const membership = new Map<string, string[]>();\n\n if (result.Success && result.Results) {\n for (const detail of result.Results) {\n const existingRecords = membership.get(detail.ListID) || [];\n if (!existingRecords.includes(detail.RecordID)) {\n existingRecords.push(detail.RecordID);\n }\n membership.set(detail.ListID, existingRecords);\n }\n }\n\n this.membershipCache.set(cacheKey, {\n data: membership,\n timestamp: new Date()\n });\n\n return membership;\n } finally {\n this.loadingSubject.next(false);\n }\n }\n\n /** @deprecated Use {@link GetRecordMembership}. */\n async getRecordMembership(\n entityId: string,\n recordIds: string[]\n ): Promise<Map<string, string[]>> {\n return this.GetRecordMembership(entityId, recordIds);\n }\n\n /**\n * Get lists that contain a specific record\n */\n async GetListsForRecord(\n entityId: string,\n recordId: string\n ): Promise<MJListEntity[]> {\n const rv = RunView.FromMetadataProvider(this.Provider);\n\n // Get list details for this record — read-only lookup\n const detailsResult = await rv.RunView<{ ListID: string }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `RecordID = '${recordId}'`,\n Fields: ['ListID'],\n ResultType: 'simple'\n });\n\n if (!detailsResult.Success || !detailsResult.Results || detailsResult.Results.length === 0) {\n return [];\n }\n\n const listIds = [...new Set(detailsResult.Results.map(d => d.ListID))];\n\n // Get the lists filtered by entity\n const listIdFilter = listIds.map(id => `'${id}'`).join(',');\n const listsResult = await rv.RunView<MJListEntity>({\n EntityName: 'MJ: Lists',\n ExtraFilter: `ID IN (${listIdFilter}) AND EntityID = '${entityId}'`,\n ResultType: 'entity_object'\n });\n\n return listsResult.Success ? (listsResult.Results || []) : [];\n }\n\n /** @deprecated Use {@link GetListsForRecord}. */\n async getListsForRecord(\n entityId: string,\n recordId: string\n ): Promise<MJListEntity[]> {\n return this.GetListsForRecord(entityId, recordId);\n }\n\n /**\n * Get item count for a list\n */\n async GetListItemCount(listId: string): Promise<number> {\n const rv = RunView.FromMetadataProvider(this.Provider);\n const result = await rv.RunView({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID = '${listId}'`,\n ResultType: 'count_only'\n });\n\n return result.Success ? result.TotalRowCount : 0;\n }\n\n /** @deprecated Use {@link GetListItemCount}. */\n async getListItemCount(listId: string): Promise<number> {\n return this.GetListItemCount(listId);\n }\n\n /**\n * Build view models for lists with membership information\n */\n async BuildListViewModels(\n lists: MJListEntity[],\n recordIds: string[],\n membership: Map<string, string[]>\n ): Promise<ListItemViewModel[]> {\n const viewModels: ListItemViewModel[] = [];\n const rv = RunView.FromMetadataProvider(this.Provider);\n\n // Get item counts for all lists in one batched round trip of count_only\n // queries — each an index-seek COUNT server-side. Never download the\n // membership rows just to count them.\n const countMap = new Map<string, number>();\n if (lists.length > 0) {\n const countResults = await rv.RunViews(lists.map(list => ({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID = '${list.ID}'`,\n ResultType: 'count_only' as const\n })));\n countResults.forEach((res, idx) => {\n countMap.set(lists[idx].ID, res.Success ? res.TotalRowCount : 0);\n });\n }\n\n for (const list of lists) {\n const memberRecordIds = membership.get(list.ID) || [];\n const membershipCount = memberRecordIds.length;\n const totalSelected = recordIds.length;\n\n viewModels.push({\n list,\n itemCount: countMap.get(list.ID) || 0,\n membershipCount,\n totalSelectedRecords: totalSelected,\n isFullMember: membershipCount === totalSelected && totalSelected > 0,\n isPartialMember: membershipCount > 0 && membershipCount < totalSelected,\n isNotMember: membershipCount === 0,\n lastUpdated: list.Get('__mj_UpdatedAt') as Date || new Date(),\n category: undefined, // Can be populated separately if needed\n isSelectedForAdd: false,\n isSelectedForRemove: false\n });\n }\n\n return viewModels;\n }\n\n /** @deprecated Use {@link BuildListViewModels}. */\n async buildListViewModels(\n lists: MJListEntity[],\n recordIds: string[],\n membership: Map<string, string[]>\n ): Promise<ListItemViewModel[]> {\n return this.BuildListViewModels(lists, recordIds, membership);\n }\n\n /**\n * Add records to one or more lists\n */\n async AddRecordsToLists(\n listIds: string[],\n recordIds: string[],\n skipDuplicates: boolean = true\n ): Promise<BatchOperationResult> {\n const result: BatchOperationResult = {\n success: 0,\n failed: 0,\n skipped: 0,\n errors: []\n };\n\n const md = this.Provider;\n\n // Get existing membership to skip duplicates\n let existingMembership = new Map<string, Set<string>>();\n if (skipDuplicates) {\n const rv = RunView.FromMetadataProvider(this.Provider);\n const listIdFilter = listIds.map(id => `'${id}'`).join(',');\n const recordIdFilter = recordIds.map(id => `'${id}'`).join(',');\n\n const existing = await rv.RunView<{ ListID: string; RecordID: string }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID IN (${listIdFilter}) AND RecordID IN (${recordIdFilter})`,\n Fields: ['ListID', 'RecordID'],\n ResultType: 'simple'\n }, md.CurrentUser);\n\n if (existing.Success && existing.Results) {\n for (const detail of existing.Results) {\n const recordSet = existingMembership.get(detail.ListID) || new Set();\n recordSet.add(detail.RecordID);\n existingMembership.set(detail.ListID, recordSet);\n }\n }\n }\n\n // Build list of records to add (excluding duplicates)\n const recordsToAdd: Array<{listId: string, recordId: string}> = [];\n for (const listId of listIds) {\n const existingRecords = existingMembership.get(listId) || new Set();\n for (const recordId of recordIds) {\n if (skipDuplicates && existingRecords.has(recordId)) {\n result.skipped++;\n } else {\n recordsToAdd.push({ listId, recordId });\n }\n }\n }\n\n if (recordsToAdd.length === 0) {\n return result;\n }\n\n // Use transaction group for bulk insert\n const tg = await md.CreateTransactionGroup();\n\n for (const { listId, recordId } of recordsToAdd) {\n try {\n const listDetail = await md.GetEntityObject<MJListDetailEntity>('MJ: List Details', md.CurrentUser);\n listDetail.ListID = listId;\n listDetail.RecordID = recordId;\n listDetail.TransactionGroup = tg;\n const saveResult = await listDetail.Save();\n if (!saveResult) {\n console.error(`[ListManagementService] Failed to queue record ${recordId} for list ${listId}:`, listDetail.LatestResult?.Message);\n result.errors.push(`Failed to add record ${recordId} to list ${listId}: ${listDetail.LatestResult?.Message || 'Unknown error'}`);\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[ListManagementService] Exception adding record:`, error);\n result.errors.push(`Error adding record ${recordId} to list ${listId}: ${errorMessage}`);\n }\n }\n\n // Submit the transaction\n const success = await tg.Submit();\n\n if (success) {\n result.success = recordsToAdd.length - result.errors.length;\n result.failed = result.errors.length;\n } else {\n result.failed = recordsToAdd.length;\n result.success = 0;\n result.errors.push('Transaction failed to submit');\n }\n\n // Invalidate membership cache\n this.membershipCache.clear();\n\n return result;\n }\n\n /** @deprecated Use {@link AddRecordsToLists}. */\n async addRecordsToLists(\n listIds: string[],\n recordIds: string[],\n skipDuplicates: boolean = true\n ): Promise<BatchOperationResult> {\n return this.AddRecordsToLists(listIds, recordIds, skipDuplicates);\n }\n\n /**\n * Remove records from one or more lists\n */\n async RemoveRecordsFromLists(\n listIds: string[],\n recordIds: string[]\n ): Promise<BatchOperationResult> {\n const result: BatchOperationResult = {\n success: 0,\n failed: 0,\n skipped: 0,\n errors: []\n };\n\n const md = this.Provider;\n const rv = RunView.FromMetadataProvider(this.Provider);\n\n // Find existing list details to delete\n const listIdFilter = listIds.map(id => `'${id}'`).join(',');\n const recordIdFilter = recordIds.map(id => `'${id}'`).join(',');\n\n const existingResult = await rv.RunView<MJListDetailEntity>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID IN (${listIdFilter}) AND RecordID IN (${recordIdFilter})`,\n ResultType: 'entity_object'\n });\n\n if (!existingResult.Success) {\n result.errors.push('Failed to query existing list details');\n return result;\n }\n\n const toDelete = existingResult.Results || [];\n if (toDelete.length === 0) {\n return result;\n }\n\n // Queue all deletes in one TransactionGroup — a single round trip\n // instead of one per removed membership row. Deliberately atomic (unlike\n // the server-side bulk paths, which trade atomicity for per-record error\n // isolation): if Submit() fails, the transaction rolled back and no rows\n // were removed.\n const tg = await md.CreateTransactionGroup();\n let queued = 0;\n for (const detail of toDelete) {\n try {\n detail.TransactionGroup = tg;\n // With a TransactionGroup set, Delete() returns true once enqueued;\n // false means a pre-enqueue failure (validation/permission) and the\n // row never joined the transaction.\n if (await detail.Delete()) {\n queued++;\n } else {\n result.failed++;\n result.errors.push(`Failed to queue removal of record ${detail.RecordID} from list ${detail.ListID}: ${detail.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n } catch (error) {\n result.failed++;\n const errorMessage = error instanceof Error ? error.message : String(error);\n result.errors.push(`Error removing record: ${errorMessage}`);\n }\n }\n\n const submitted = queued === 0 || await tg.Submit();\n if (submitted) {\n result.success = queued;\n } else {\n result.failed += queued;\n result.errors.push('Transaction failed to submit');\n }\n\n // Invalidate caches\n this.membershipCache.clear();\n\n return result;\n }\n\n /** @deprecated Use {@link RemoveRecordsFromLists}. */\n async removeRecordsFromLists(\n listIds: string[],\n recordIds: string[]\n ): Promise<BatchOperationResult> {\n return this.RemoveRecordsFromLists(listIds, recordIds);\n }\n\n /**\n * Create a new list\n */\n async CreateList(config: CreateListConfig): Promise<MJListEntity | null> {\n const md = this.Provider;\n\n try {\n const list = await md.GetEntityObject<MJListEntity>('MJ: Lists', md.CurrentUser);\n list.NewRecord();\n list.Name = config.name;\n list.Description = config.description || '';\n list.EntityID = config.entityId;\n list.UserID = md.CurrentUser.ID;\n\n if (config.categoryId) {\n list.CategoryID = config.categoryId;\n }\n\n const saveResult = await list.Save();\n if (saveResult) {\n // Invalidate list cache\n this.invalidateListCache(config.entityId);\n return list;\n }\n\n return null;\n } catch (error) {\n console.error('Error creating list:', error);\n return null;\n }\n }\n\n /** @deprecated Use {@link CreateList}. */\n async createList(config: CreateListConfig): Promise<MJListEntity | null> {\n return this.CreateList(config);\n }\n\n /**\n * Check if a cache entry is still valid\n */\n private isCacheValid<T>(entry: CacheEntry<T>): boolean {\n const now = new Date().getTime();\n const entryTime = entry.timestamp.getTime();\n return (now - entryTime) < this.CACHE_TTL_MS;\n }\n\n /**\n * Invalidate cache for a specific entity or all caches\n */\n InvalidateCache(entityId?: string): void {\n if (entityId) {\n this.invalidateListCache(entityId);\n } else {\n this.listCache.clear();\n this.categoryCache = null;\n this.membershipCache.clear();\n }\n }\n\n /** @deprecated Use {@link InvalidateCache}. */\n invalidateCache(entityId?: string): void {\n return this.InvalidateCache(entityId);\n }\n\n /**\n * Invalidate list cache for a specific entity\n */\n private invalidateListCache(entityId: string): void {\n // Remove all cache entries that start with this entityId\n for (const key of this.listCache.keys()) {\n if (key.startsWith(entityId)) {\n this.listCache.delete(key);\n }\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"list-management.service.js","sourceRoot":"","sources":["../../../src/lib/services/list-management.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { IMetadataProvider, Metadata, RunView, UserInfo } from '@memberjunction/core';\nimport { MJListEntity, MJListDetailEntity, MJListCategoryEntity } from '@memberjunction/core-entities';\nimport { BehaviorSubject, Observable } from 'rxjs';\nimport {\n ListItemViewModel,\n BatchOperationResult,\n RecordMembershipInfo,\n CreateListConfig\n} from '../models/list-management.models';\n\ninterface CacheEntry<T> {\n data: T;\n timestamp: Date;\n}\n\n/**\n * Service for managing list operations including data loading, caching,\n * and batch add/remove operations.\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 ListManagementService {\n private readonly CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\n // Cache storage\n private listCache = new Map<string, CacheEntry<MJListEntity[]>>();\n private categoryCache: CacheEntry<MJListCategoryEntity[]> | null = null;\n private membershipCache = new Map<string, CacheEntry<Map<string, string[]>>>();\n\n // Loading state subjects\n private loadingSubject = new BehaviorSubject<boolean>(false);\n public Loading$: Observable<boolean> = this.loadingSubject.asObservable();\n\n /** @deprecated Use {@link Loading$}. */\n public get loading$(): Observable<boolean> {\n return this.Loading$;\n }\n /** @deprecated Use {@link Loading$}. */\n public set loading$(value: Observable<boolean>) {\n this.Loading$ = value;\n }\n\n private _provider: IMetadataProvider | null = null;\n\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\n /**\n * Get lists for a specific entity, with optional caching\n */\n async GetListsForEntity(\n entityId: string,\n userId?: string,\n forceRefresh: boolean = false\n ): Promise<MJListEntity[]> {\n const cacheKey = `${entityId}_${userId || 'all'}`;\n\n // Check cache first\n if (!forceRefresh) {\n const cached = this.listCache.get(cacheKey);\n if (cached && this.isCacheValid(cached)) {\n return cached.data;\n }\n }\n\n this.loadingSubject.next(true);\n\n try {\n const rv = RunView.FromMetadataProvider(this.Provider);\n let filter = `EntityID = '${entityId}'`;\n\n if (userId) {\n filter += ` AND UserID = '${userId}'`;\n }\n\n const result = await rv.RunView<MJListEntity>({\n EntityName: 'MJ: Lists',\n ExtraFilter: filter,\n OrderBy: 'Name',\n ResultType: 'entity_object'\n });\n\n if (result.Success) {\n const lists = result.Results || [];\n this.listCache.set(cacheKey, {\n data: lists,\n timestamp: new Date()\n });\n return lists;\n } else {\n console.error('Failed to load lists:', result.ErrorMessage);\n return [];\n }\n } finally {\n this.loadingSubject.next(false);\n }\n }\n\n /** @deprecated Use {@link GetListsForEntity}. */\n async getListsForEntity(\n entityId: string,\n userId?: string,\n forceRefresh: boolean = false\n ): Promise<MJListEntity[]> {\n return this.GetListsForEntity(entityId, userId, forceRefresh);\n }\n\n /**\n * Get all list categories\n */\n async GetListCategories(forceRefresh: boolean = false): Promise<MJListCategoryEntity[]> {\n if (!forceRefresh && this.categoryCache && this.isCacheValid(this.categoryCache)) {\n return this.categoryCache.data;\n }\n\n const rv = RunView.FromMetadataProvider(this.Provider);\n const result = await rv.RunView<MJListCategoryEntity>({\n EntityName: 'MJ: List Categories',\n OrderBy: 'Name',\n ResultType: 'entity_object'\n });\n\n if (result.Success) {\n this.categoryCache = {\n data: result.Results || [],\n timestamp: new Date()\n };\n return this.categoryCache.data;\n }\n\n return [];\n }\n\n /** @deprecated Use {@link GetListCategories}. */\n async getListCategories(forceRefresh: boolean = false): Promise<MJListCategoryEntity[]> {\n return this.GetListCategories(forceRefresh);\n }\n\n /**\n * Get membership information for a set of records\n * Returns a Map where key is listId and value is array of recordIds that are members\n */\n async GetRecordMembership(\n entityId: string,\n recordIds: string[]\n ): Promise<Map<string, string[]>> {\n if (recordIds.length === 0) {\n return new Map();\n }\n\n const cacheKey = `${entityId}_${recordIds.sort().join(',')}`;\n\n // Check cache\n const cached = this.membershipCache.get(cacheKey);\n if (cached && this.isCacheValid(cached)) {\n return cached.data;\n }\n\n this.loadingSubject.next(true);\n\n try {\n const rv = RunView.FromMetadataProvider(this.Provider);\n\n // Get all list details for these records — read-only lookup, so\n // 'simple' with narrow Fields (no BaseEntity instantiation overhead)\n const recordIdFilter = recordIds.map(id => `'${id}'`).join(',');\n const result = await rv.RunView<{ ListID: string; RecordID: string }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `RecordID IN (${recordIdFilter})`,\n Fields: ['ListID', 'RecordID'],\n ResultType: 'simple'\n });\n\n const membership = new Map<string, string[]>();\n\n if (result.Success && result.Results) {\n for (const detail of result.Results) {\n const existingRecords = membership.get(detail.ListID) || [];\n if (!existingRecords.includes(detail.RecordID)) {\n existingRecords.push(detail.RecordID);\n }\n membership.set(detail.ListID, existingRecords);\n }\n }\n\n this.membershipCache.set(cacheKey, {\n data: membership,\n timestamp: new Date()\n });\n\n return membership;\n } finally {\n this.loadingSubject.next(false);\n }\n }\n\n /** @deprecated Use {@link GetRecordMembership}. */\n async getRecordMembership(\n entityId: string,\n recordIds: string[]\n ): Promise<Map<string, string[]>> {\n return this.GetRecordMembership(entityId, recordIds);\n }\n\n /**\n * Get lists that contain a specific record\n */\n async GetListsForRecord(\n entityId: string,\n recordId: string\n ): Promise<MJListEntity[]> {\n const rv = RunView.FromMetadataProvider(this.Provider);\n\n // Get list details for this record — read-only lookup\n const detailsResult = await rv.RunView<{ ListID: string }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `RecordID = '${recordId}'`,\n Fields: ['ListID'],\n ResultType: 'simple'\n });\n\n if (!detailsResult.Success || !detailsResult.Results || detailsResult.Results.length === 0) {\n return [];\n }\n\n const listIds = [...new Set(detailsResult.Results.map(d => d.ListID))];\n\n // Get the lists filtered by entity\n const listIdFilter = listIds.map(id => `'${id}'`).join(',');\n const listsResult = await rv.RunView<MJListEntity>({\n EntityName: 'MJ: Lists',\n ExtraFilter: `ID IN (${listIdFilter}) AND EntityID = '${entityId}'`,\n ResultType: 'entity_object'\n });\n\n return listsResult.Success ? (listsResult.Results || []) : [];\n }\n\n /** @deprecated Use {@link GetListsForRecord}. */\n async getListsForRecord(\n entityId: string,\n recordId: string\n ): Promise<MJListEntity[]> {\n return this.GetListsForRecord(entityId, recordId);\n }\n\n /**\n * Get item count for a list\n */\n async GetListItemCount(listId: string): Promise<number> {\n const rv = RunView.FromMetadataProvider(this.Provider);\n const result = await rv.RunView({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID = '${listId}'`,\n ResultType: 'count_only'\n });\n\n return result.Success ? result.TotalRowCount ?? 0 : 0;\n }\n\n /** @deprecated Use {@link GetListItemCount}. */\n async getListItemCount(listId: string): Promise<number> {\n return this.GetListItemCount(listId);\n }\n\n /**\n * Build view models for lists with membership information\n */\n async BuildListViewModels(\n lists: MJListEntity[],\n recordIds: string[],\n membership: Map<string, string[]>\n ): Promise<ListItemViewModel[]> {\n const viewModels: ListItemViewModel[] = [];\n const rv = RunView.FromMetadataProvider(this.Provider);\n\n // Get item counts for all lists in one batched round trip of count_only\n // queries — each an index-seek COUNT server-side. Never download the\n // membership rows just to count them.\n const countMap = new Map<string, number>();\n if (lists.length > 0) {\n const countResults = await rv.RunViews(lists.map(list => ({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID = '${list.ID}'`,\n ResultType: 'count_only' as const\n })));\n countResults.forEach((res, idx) => {\n countMap.set(lists[idx].ID, res.Success ? res.TotalRowCount ?? 0 : 0);\n });\n }\n\n for (const list of lists) {\n const memberRecordIds = membership.get(list.ID) || [];\n const membershipCount = memberRecordIds.length;\n const totalSelected = recordIds.length;\n\n viewModels.push({\n list,\n itemCount: countMap.get(list.ID) || 0,\n membershipCount,\n totalSelectedRecords: totalSelected,\n isFullMember: membershipCount === totalSelected && totalSelected > 0,\n isPartialMember: membershipCount > 0 && membershipCount < totalSelected,\n isNotMember: membershipCount === 0,\n lastUpdated: list.Get('__mj_UpdatedAt') as Date || new Date(),\n category: undefined, // Can be populated separately if needed\n isSelectedForAdd: false,\n isSelectedForRemove: false\n });\n }\n\n return viewModels;\n }\n\n /** @deprecated Use {@link BuildListViewModels}. */\n async buildListViewModels(\n lists: MJListEntity[],\n recordIds: string[],\n membership: Map<string, string[]>\n ): Promise<ListItemViewModel[]> {\n return this.BuildListViewModels(lists, recordIds, membership);\n }\n\n /**\n * Add records to one or more lists\n */\n async AddRecordsToLists(\n listIds: string[],\n recordIds: string[],\n skipDuplicates: boolean = true\n ): Promise<BatchOperationResult> {\n const result: BatchOperationResult = {\n success: 0,\n failed: 0,\n skipped: 0,\n errors: []\n };\n\n const md = this.Provider;\n\n // Get existing membership to skip duplicates\n let existingMembership = new Map<string, Set<string>>();\n if (skipDuplicates) {\n const rv = RunView.FromMetadataProvider(this.Provider);\n const listIdFilter = listIds.map(id => `'${id}'`).join(',');\n const recordIdFilter = recordIds.map(id => `'${id}'`).join(',');\n\n const existing = await rv.RunView<{ ListID: string; RecordID: string }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID IN (${listIdFilter}) AND RecordID IN (${recordIdFilter})`,\n Fields: ['ListID', 'RecordID'],\n ResultType: 'simple'\n }, md.CurrentUser);\n\n if (existing.Success && existing.Results) {\n for (const detail of existing.Results) {\n const recordSet = existingMembership.get(detail.ListID) || new Set();\n recordSet.add(detail.RecordID);\n existingMembership.set(detail.ListID, recordSet);\n }\n }\n }\n\n // Build list of records to add (excluding duplicates)\n const recordsToAdd: Array<{listId: string, recordId: string}> = [];\n for (const listId of listIds) {\n const existingRecords = existingMembership.get(listId) || new Set();\n for (const recordId of recordIds) {\n if (skipDuplicates && existingRecords.has(recordId)) {\n result.skipped++;\n } else {\n recordsToAdd.push({ listId, recordId });\n }\n }\n }\n\n if (recordsToAdd.length === 0) {\n return result;\n }\n\n // Use transaction group for bulk insert\n const tg = await md.CreateTransactionGroup();\n\n for (const { listId, recordId } of recordsToAdd) {\n try {\n const listDetail = await md.GetEntityObject<MJListDetailEntity>('MJ: List Details', md.CurrentUser);\n listDetail.ListID = listId;\n listDetail.RecordID = recordId;\n listDetail.TransactionGroup = tg;\n const saveResult = await listDetail.Save();\n if (!saveResult) {\n console.error(`[ListManagementService] Failed to queue record ${recordId} for list ${listId}:`, listDetail.LatestResult?.Message);\n result.errors.push(`Failed to add record ${recordId} to list ${listId}: ${listDetail.LatestResult?.Message || 'Unknown error'}`);\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[ListManagementService] Exception adding record:`, error);\n result.errors.push(`Error adding record ${recordId} to list ${listId}: ${errorMessage}`);\n }\n }\n\n // Submit the transaction\n const success = await tg.Submit();\n\n if (success) {\n result.success = recordsToAdd.length - result.errors.length;\n result.failed = result.errors.length;\n } else {\n result.failed = recordsToAdd.length;\n result.success = 0;\n result.errors.push('Transaction failed to submit');\n }\n\n // Invalidate membership cache\n this.membershipCache.clear();\n\n return result;\n }\n\n /** @deprecated Use {@link AddRecordsToLists}. */\n async addRecordsToLists(\n listIds: string[],\n recordIds: string[],\n skipDuplicates: boolean = true\n ): Promise<BatchOperationResult> {\n return this.AddRecordsToLists(listIds, recordIds, skipDuplicates);\n }\n\n /**\n * Remove records from one or more lists\n */\n async RemoveRecordsFromLists(\n listIds: string[],\n recordIds: string[]\n ): Promise<BatchOperationResult> {\n const result: BatchOperationResult = {\n success: 0,\n failed: 0,\n skipped: 0,\n errors: []\n };\n\n const md = this.Provider;\n const rv = RunView.FromMetadataProvider(this.Provider);\n\n // Find existing list details to delete\n const listIdFilter = listIds.map(id => `'${id}'`).join(',');\n const recordIdFilter = recordIds.map(id => `'${id}'`).join(',');\n\n const existingResult = await rv.RunView<MJListDetailEntity>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID IN (${listIdFilter}) AND RecordID IN (${recordIdFilter})`,\n ResultType: 'entity_object'\n });\n\n if (!existingResult.Success) {\n result.errors.push('Failed to query existing list details');\n return result;\n }\n\n const toDelete = existingResult.Results || [];\n if (toDelete.length === 0) {\n return result;\n }\n\n // Queue all deletes in one TransactionGroup — a single round trip\n // instead of one per removed membership row. Deliberately atomic (unlike\n // the server-side bulk paths, which trade atomicity for per-record error\n // isolation): if Submit() fails, the transaction rolled back and no rows\n // were removed.\n const tg = await md.CreateTransactionGroup();\n let queued = 0;\n for (const detail of toDelete) {\n try {\n detail.TransactionGroup = tg;\n // With a TransactionGroup set, Delete() returns true once enqueued;\n // false means a pre-enqueue failure (validation/permission) and the\n // row never joined the transaction.\n if (await detail.Delete()) {\n queued++;\n } else {\n result.failed++;\n result.errors.push(`Failed to queue removal of record ${detail.RecordID} from list ${detail.ListID}: ${detail.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n } catch (error) {\n result.failed++;\n const errorMessage = error instanceof Error ? error.message : String(error);\n result.errors.push(`Error removing record: ${errorMessage}`);\n }\n }\n\n const submitted = queued === 0 || await tg.Submit();\n if (submitted) {\n result.success = queued;\n } else {\n result.failed += queued;\n result.errors.push('Transaction failed to submit');\n }\n\n // Invalidate caches\n this.membershipCache.clear();\n\n return result;\n }\n\n /** @deprecated Use {@link RemoveRecordsFromLists}. */\n async removeRecordsFromLists(\n listIds: string[],\n recordIds: string[]\n ): Promise<BatchOperationResult> {\n return this.RemoveRecordsFromLists(listIds, recordIds);\n }\n\n /**\n * Create a new list\n */\n async CreateList(config: CreateListConfig): Promise<MJListEntity | null> {\n const md = this.Provider;\n\n try {\n const list = await md.GetEntityObject<MJListEntity>('MJ: Lists', md.CurrentUser);\n list.NewRecord();\n list.Name = config.name;\n list.Description = config.description || '';\n list.EntityID = config.entityId;\n list.UserID = md.CurrentUser.ID;\n\n if (config.categoryId) {\n list.CategoryID = config.categoryId;\n }\n\n const saveResult = await list.Save();\n if (saveResult) {\n // Invalidate list cache\n this.invalidateListCache(config.entityId);\n return list;\n }\n\n return null;\n } catch (error) {\n console.error('Error creating list:', error);\n return null;\n }\n }\n\n /** @deprecated Use {@link CreateList}. */\n async createList(config: CreateListConfig): Promise<MJListEntity | null> {\n return this.CreateList(config);\n }\n\n /**\n * Check if a cache entry is still valid\n */\n private isCacheValid<T>(entry: CacheEntry<T>): boolean {\n const now = new Date().getTime();\n const entryTime = entry.timestamp.getTime();\n return (now - entryTime) < this.CACHE_TTL_MS;\n }\n\n /**\n * Invalidate cache for a specific entity or all caches\n */\n InvalidateCache(entityId?: string): void {\n if (entityId) {\n this.invalidateListCache(entityId);\n } else {\n this.listCache.clear();\n this.categoryCache = null;\n this.membershipCache.clear();\n }\n }\n\n /** @deprecated Use {@link InvalidateCache}. */\n invalidateCache(entityId?: string): void {\n return this.InvalidateCache(entityId);\n }\n\n /**\n * Invalidate list cache for a specific entity\n */\n private invalidateListCache(entityId: string): void {\n // Remove all cache entries that start with this entityId\n for (const key of this.listCache.keys()) {\n if (key.startsWith(entityId)) {\n this.listCache.delete(key);\n }\n }\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-list-management",
|
|
3
|
-
"version": "6.2.0-edge.
|
|
3
|
+
"version": "6.2.0-edge.8",
|
|
4
4
|
"description": "MemberJunction: Angular Components for managing entity list membership with beautiful, responsive UI",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@angular/compiler": "21.2.22",
|
|
20
20
|
"@angular/compiler-cli": "21.2.22",
|
|
21
|
-
"@memberjunction/ng-test-utils": "6.2.0-edge.
|
|
21
|
+
"@memberjunction/ng-test-utils": "6.2.0-edge.8",
|
|
22
22
|
"vitest": "^4.0.18"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
@@ -28,15 +28,15 @@
|
|
|
28
28
|
"rxjs": "^7.8.2"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@memberjunction/core": "6.2.0-edge.
|
|
32
|
-
"@memberjunction/core-entities": "6.2.0-edge.
|
|
33
|
-
"@memberjunction/global": "6.2.0-edge.
|
|
34
|
-
"@memberjunction/graphql-dataprovider": "6.2.0-edge.
|
|
35
|
-
"@memberjunction/lists-base": "6.2.0-edge.
|
|
36
|
-
"@memberjunction/ng-base-types": "6.2.0-edge.
|
|
37
|
-
"@memberjunction/ng-container-directives": "6.2.0-edge.
|
|
38
|
-
"@memberjunction/ng-shared-generic": "6.2.0-edge.
|
|
39
|
-
"@memberjunction/ng-ui-components": "6.2.0-edge.
|
|
31
|
+
"@memberjunction/core": "6.2.0-edge.8",
|
|
32
|
+
"@memberjunction/core-entities": "6.2.0-edge.8",
|
|
33
|
+
"@memberjunction/global": "6.2.0-edge.8",
|
|
34
|
+
"@memberjunction/graphql-dataprovider": "6.2.0-edge.8",
|
|
35
|
+
"@memberjunction/lists-base": "6.2.0-edge.8",
|
|
36
|
+
"@memberjunction/ng-base-types": "6.2.0-edge.8",
|
|
37
|
+
"@memberjunction/ng-container-directives": "6.2.0-edge.8",
|
|
38
|
+
"@memberjunction/ng-shared-generic": "6.2.0-edge.8",
|
|
39
|
+
"@memberjunction/ng-ui-components": "6.2.0-edge.8",
|
|
40
40
|
"tslib": "^2.8.1"
|
|
41
41
|
},
|
|
42
42
|
"sideEffects": false,
|