@memberjunction/ng-list-management 5.35.0 → 5.37.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.
Files changed (63) hide show
  1. package/README.md +179 -85
  2. package/dist/lib/components/audience-source-picker/audience-source-picker.component.d.ts +74 -0
  3. package/dist/lib/components/audience-source-picker/audience-source-picker.component.d.ts.map +1 -0
  4. package/dist/lib/components/audience-source-picker/audience-source-picker.component.js +396 -0
  5. package/dist/lib/components/audience-source-picker/audience-source-picker.component.js.map +1 -0
  6. package/dist/lib/components/audience-source-summary/audience-source-summary.component.d.ts +34 -0
  7. package/dist/lib/components/audience-source-summary/audience-source-summary.component.d.ts.map +1 -0
  8. package/dist/lib/components/audience-source-summary/audience-source-summary.component.js +136 -0
  9. package/dist/lib/components/audience-source-summary/audience-source-summary.component.js.map +1 -0
  10. package/dist/lib/components/list-audit-log/list-audit-log.component.d.ts +55 -0
  11. package/dist/lib/components/list-audit-log/list-audit-log.component.d.ts.map +1 -0
  12. package/dist/lib/components/list-audit-log/list-audit-log.component.js +280 -0
  13. package/dist/lib/components/list-audit-log/list-audit-log.component.js.map +1 -0
  14. package/dist/lib/components/list-delta-confirm/list-delta-confirm.component.d.ts +64 -0
  15. package/dist/lib/components/list-delta-confirm/list-delta-confirm.component.d.ts.map +1 -0
  16. package/dist/lib/components/list-delta-confirm/list-delta-confirm.component.js +323 -0
  17. package/dist/lib/components/list-delta-confirm/list-delta-confirm.component.js.map +1 -0
  18. package/dist/lib/components/list-invitations/list-invitations.component.d.ts +60 -0
  19. package/dist/lib/components/list-invitations/list-invitations.component.d.ts.map +1 -0
  20. package/dist/lib/components/list-invitations/list-invitations.component.js +367 -0
  21. package/dist/lib/components/list-invitations/list-invitations.component.js.map +1 -0
  22. package/dist/lib/components/list-management-dialog/list-management-dialog.component.d.ts.map +1 -1
  23. package/dist/lib/components/list-management-dialog/list-management-dialog.component.js +0 -4
  24. package/dist/lib/components/list-management-dialog/list-management-dialog.component.js.map +1 -1
  25. package/dist/lib/components/list-share-dialog/list-share-dialog.component.d.ts +18 -1
  26. package/dist/lib/components/list-share-dialog/list-share-dialog.component.d.ts.map +1 -1
  27. package/dist/lib/components/list-share-dialog/list-share-dialog.component.js +115 -66
  28. package/dist/lib/components/list-share-dialog/list-share-dialog.component.js.map +1 -1
  29. package/dist/lib/components/list-stats/list-stats.component.d.ts +52 -0
  30. package/dist/lib/components/list-stats/list-stats.component.d.ts.map +1 -0
  31. package/dist/lib/components/list-stats/list-stats.component.js +283 -0
  32. package/dist/lib/components/list-stats/list-stats.component.js.map +1 -0
  33. package/dist/lib/components/save-view-as-list-dialog/save-view-as-list-dialog.component.d.ts +68 -0
  34. package/dist/lib/components/save-view-as-list-dialog/save-view-as-list-dialog.component.d.ts.map +1 -0
  35. package/dist/lib/components/save-view-as-list-dialog/save-view-as-list-dialog.component.js +316 -0
  36. package/dist/lib/components/save-view-as-list-dialog/save-view-as-list-dialog.component.js.map +1 -0
  37. package/dist/lib/components/shared-with-me/shared-with-me.component.d.ts +47 -0
  38. package/dist/lib/components/shared-with-me/shared-with-me.component.d.ts.map +1 -0
  39. package/dist/lib/components/shared-with-me/shared-with-me.component.js +231 -0
  40. package/dist/lib/components/shared-with-me/shared-with-me.component.js.map +1 -0
  41. package/dist/lib/components/tag-chips/tag-chips.component.d.ts +85 -0
  42. package/dist/lib/components/tag-chips/tag-chips.component.d.ts.map +1 -0
  43. package/dist/lib/components/tag-chips/tag-chips.component.js +392 -0
  44. package/dist/lib/components/tag-chips/tag-chips.component.js.map +1 -0
  45. package/dist/lib/models/list-sharing.models.d.ts +5 -0
  46. package/dist/lib/models/list-sharing.models.d.ts.map +1 -1
  47. package/dist/lib/models/list-sharing.models.js.map +1 -1
  48. package/dist/lib/module.d.ts +17 -8
  49. package/dist/lib/module.d.ts.map +1 -1
  50. package/dist/lib/module.js +51 -6
  51. package/dist/lib/module.js.map +1 -1
  52. package/dist/lib/services/list-management.service.d.ts.map +1 -1
  53. package/dist/lib/services/list-management.service.js +0 -9
  54. package/dist/lib/services/list-management.service.js.map +1 -1
  55. package/dist/lib/services/list-sharing.service.d.ts +8 -3
  56. package/dist/lib/services/list-sharing.service.d.ts.map +1 -1
  57. package/dist/lib/services/list-sharing.service.js +33 -15
  58. package/dist/lib/services/list-sharing.service.js.map +1 -1
  59. package/dist/public-api.d.ts +9 -0
  60. package/dist/public-api.d.ts.map +1 -1
  61. package/dist/public-api.js +9 -0
  62. package/dist/public-api.js.map +1 -1
  63. package/package.json +13 -11
@@ -1 +1 @@
1
- {"version":3,"file":"list-share-dialog.component.js","sourceRoot":"","sources":["../../../../src/lib/components/list-share-dialog/list-share-dialog.component.ts","../../../../src/lib/components/list-share-dialog/list-share-dialog.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n Input,\n Output,\n EventEmitter,\n OnInit,\n OnDestroy,\n ChangeDetectorRef\n} from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { debounceTime, takeUntil } from 'rxjs/operators';\nimport { Metadata } from '@memberjunction/core';\nimport { ListSharingService } from '../../services/list-sharing.service';\nimport {\n ListShareDialogConfig,\n ListShareDialogResult,\n ListShareInfo,\n ListPermissionLevel,\n ShareRecipient\n} from '../../models/list-sharing.models';\n\n/**\n * Dialog component for managing list sharing.\n * Allows users to share lists with other users or roles with different permission levels.\n *\n * Features:\n * - User/role search with autocomplete\n * - Permission level selection (View, Edit, Owner)\n * - Current shares management\n * - Remove access functionality\n */\n@Component({\n standalone: false,\n selector: 'mj-list-share-dialog',\n templateUrl: './list-share-dialog.component.html',\n styleUrls: ['./list-share-dialog.component.css']\n})\nexport class ListShareDialogComponent extends BaseAngularComponent implements OnInit, OnDestroy {\n /**\n * Configuration for the dialog\n */\n @Input() config!: ListShareDialogConfig;\n\n /**\n * Controls dialog visibility\n */\n @Input()\n get visible(): boolean {\n return this._visible;\n }\n set visible(value: boolean) {\n if (value && !this._visible) {\n this.initializeDialog();\n }\n this._visible = value;\n }\n private _visible = false;\n\n /**\n * Emitted when dialog is closed with results\n */\n @Output() complete = new EventEmitter<ListShareDialogResult>();\n\n /**\n * Emitted when dialog is cancelled\n */\n @Output() cancel = new EventEmitter<void>();\n\n // State\n loading = false;\n saving = false;\n searchText = '';\n searchType: 'User' | 'Role' = 'User';\n showSearchResults = false;\n\n // Data\n currentShares: ListShareInfo[] = [];\n searchResults: ShareRecipient[] = [];\n selectedRecipient: ShareRecipient | null = null;\n selectedPermission: ListPermissionLevel = 'View';\n\n // Track changes\n sharesAdded: ListShareInfo[] = [];\n sharesUpdated: ListShareInfo[] = [];\n sharesRemoved: string[] = [];\n\n // Permission options\n permissionOptions: { value: ListPermissionLevel; label: string; description: string }[] = [\n { value: 'View', label: 'Viewer', description: 'Can view list contents' },\n { value: 'Edit', label: 'Editor', description: 'Can add/remove items' },\n { value: 'Owner', label: 'Co-owner', description: 'Full control including sharing' }\n ];\n\n // Cleanup\n private destroy$ = new Subject<void>();\n private searchSubject = new Subject<string>();\n\n constructor(\n private sharingService: ListSharingService,\n private cdr: ChangeDetectorRef\n ) {\n super();}\n\n ngOnInit(): void {\n this.setupSearchDebounce();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * Get dialog title\n */\n get dialogTitle(): string {\n return `Share \"${this.config?.listName || 'List'}\"`;\n }\n\n /**\n * Check if there are pending changes\n */\n get hasChanges(): boolean {\n return this.sharesAdded.length > 0 ||\n this.sharesUpdated.length > 0 ||\n this.sharesRemoved.length > 0;\n }\n\n /**\n * Setup search with debounce\n */\n private setupSearchDebounce(): void {\n this.searchSubject.pipe(\n debounceTime(300),\n takeUntil(this.destroy$)\n ).subscribe(async (searchText: string) => {\n if (searchText.trim().length >= 2) {\n await this.performSearch(searchText);\n } else {\n this.searchResults = [];\n this.showSearchResults = false;\n }\n this.cdr.detectChanges();\n });\n }\n\n /**\n * Initialize dialog when opened\n */\n private async initializeDialog(): Promise<void> {\n this.resetState();\n await this.loadCurrentShares();\n }\n\n /**\n * Reset all state\n */\n private resetState(): void {\n this.searchText = '';\n this.searchType = 'User';\n this.showSearchResults = false;\n this.searchResults = [];\n this.selectedRecipient = null;\n this.selectedPermission = 'View';\n this.sharesAdded = [];\n this.sharesUpdated = [];\n this.sharesRemoved = [];\n }\n\n /**\n * Load current shares for the list\n */\n private async loadCurrentShares(): Promise<void> {\n if (!this.config) return;\n\n this.loading = true;\n this.cdr.detectChanges();\n\n try {\n this.currentShares = await this.sharingService.getListShares(this.config.listId);\n } catch (error) {\n console.error('Error loading shares:', error);\n this.currentShares = [];\n } finally {\n this.loading = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Handle search input\n */\n onSearchInput(event: Event): void {\n const value = (event.target as HTMLInputElement).value;\n this.searchText = value;\n this.searchSubject.next(value);\n }\n\n /**\n * Clear search\n */\n clearSearch(): void {\n this.searchText = '';\n this.searchResults = [];\n this.showSearchResults = false;\n this.selectedRecipient = null;\n }\n\n /**\n * Switch search type\n */\n setSearchType(type: 'User' | 'Role'): void {\n this.searchType = type;\n if (this.searchText.trim().length >= 2) {\n this.searchSubject.next(this.searchText);\n }\n }\n\n /**\n * Perform search\n */\n private async performSearch(searchText: string): Promise<void> {\n try {\n if (this.searchType === 'User') {\n this.searchResults = await this.sharingService.searchUsers(searchText);\n } else {\n this.searchResults = await this.sharingService.searchRoles(searchText);\n }\n\n // Filter out recipients that already have access\n const existingIds = new Set(this.currentShares.map(s => s.recipientId));\n this.searchResults = this.searchResults.filter(r => !existingIds.has(r.id));\n\n // Also filter out the list owner\n this.searchResults = this.searchResults.filter(r => r.id !== this.config?.currentUserId);\n\n this.showSearchResults = this.searchResults.length > 0;\n } catch (error) {\n console.error('Error searching:', error);\n this.searchResults = [];\n }\n }\n\n /**\n * Select a recipient from search results\n */\n selectRecipient(recipient: ShareRecipient): void {\n this.selectedRecipient = recipient;\n this.searchText = recipient.name;\n this.showSearchResults = false;\n this.cdr.detectChanges();\n }\n\n /**\n * Add the selected recipient as a share\n */\n async addShare(): Promise<void> {\n if (!this.selectedRecipient || !this.config) return;\n\n this.saving = true;\n\n try {\n const md = this.ProviderToUse;\n let result;\n\n if (this.selectedRecipient.type === 'User') {\n result = await this.sharingService.shareListWithUser(\n this.config.listId,\n this.selectedRecipient.id,\n this.selectedPermission,\n this.config.currentUserId\n );\n } else {\n result = await this.sharingService.shareListWithRole(\n this.config.listId,\n this.selectedRecipient.id,\n this.selectedPermission,\n this.config.currentUserId\n );\n }\n\n if (result.success && result.shareId) {\n // Create a ListShareInfo for tracking\n const newShare: ListShareInfo = {\n shareId: result.shareId,\n listId: this.config.listId,\n type: this.selectedRecipient.type,\n recipientId: this.selectedRecipient.id,\n recipientName: this.selectedRecipient.name,\n recipientEmail: this.selectedRecipient.email,\n permissionLevel: this.selectedPermission,\n status: 'Approved'\n };\n\n this.currentShares.push(newShare);\n this.sharesAdded.push(newShare);\n\n // Clear selection\n this.clearSearch();\n this.selectedPermission = 'View';\n } else {\n console.error('Failed to add share:', result.message);\n }\n } catch (error) {\n console.error('Error adding share:', error);\n } finally {\n this.saving = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Update permission level for an existing share\n */\n async updateSharePermission(share: ListShareInfo, newLevel: ListPermissionLevel): Promise<void> {\n if (share.permissionLevel === newLevel) return;\n\n this.saving = true;\n\n try {\n const result = await this.sharingService.updateSharePermission(share.shareId, newLevel);\n\n if (result.success) {\n share.permissionLevel = newLevel;\n\n // Track the update if not already tracked\n const existingUpdate = this.sharesUpdated.find(s => s.shareId === share.shareId);\n if (!existingUpdate) {\n this.sharesUpdated.push({ ...share });\n }\n } else {\n console.error('Failed to update permission:', result.message);\n }\n } catch (error) {\n console.error('Error updating permission:', error);\n } finally {\n this.saving = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Remove a share\n */\n async removeShare(share: ListShareInfo): Promise<void> {\n this.saving = true;\n\n try {\n const result = await this.sharingService.removeShare(share.shareId);\n\n if (result.success) {\n // Remove from current shares\n this.currentShares = this.currentShares.filter(s => s.shareId !== share.shareId);\n\n // Track the removal\n this.sharesRemoved.push(share.shareId);\n\n // Remove from added if it was just added in this session\n this.sharesAdded = this.sharesAdded.filter(s => s.shareId !== share.shareId);\n } else {\n console.error('Failed to remove share:', result.message);\n }\n } catch (error) {\n console.error('Error removing share:', error);\n } finally {\n this.saving = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Get icon for share type\n */\n getShareTypeIcon(type: 'User' | 'Role'): string {\n return type === 'User' ? 'fa-solid fa-user' : 'fa-solid fa-users';\n }\n\n /**\n * Get permission label\n */\n getPermissionLabel(level: ListPermissionLevel): string {\n const option = this.permissionOptions.find(o => o.value === level);\n return option?.label || level;\n }\n\n /**\n * Close dialog with results\n */\n onDone(): void {\n const result: ListShareDialogResult = {\n action: 'apply',\n sharesAdded: this.sharesAdded,\n sharesUpdated: this.sharesUpdated,\n sharesRemoved: this.sharesRemoved\n };\n\n this._visible = false;\n this.complete.emit(result);\n }\n\n /**\n * Cancel and close dialog\n */\n onCancel(): void {\n this._visible = false;\n this.cancel.emit();\n }\n\n /**\n * Check if current user can modify shares\n */\n get canModifyShares(): boolean {\n return this.config?.isOwner === true;\n }\n}\n","@if (visible) {\n <mj-dialog\n [Visible]=\"true\"\n [Title]=\"dialogTitle\"\n [Width]=\"500\"\n (Close)=\"onCancel()\">\n <div class=\"share-dialog-content\">\n <!-- Loading state -->\n @if (loading) {\n <div class=\"loading-container\">\n <mj-loading text=\"Loading shares...\"></mj-loading>\n </div>\n }\n @if (!loading) {\n <!-- Add new share section -->\n @if (canModifyShares) {\n <div class=\"add-share-section\">\n <h4 class=\"section-title\">Add people or roles</h4>\n <!-- Search type toggle -->\n <div class=\"search-type-toggle\">\n <button mjButton\n [variant]=\"searchType === 'User' ? 'primary' : 'outline'\"\n (click)=\"setSearchType('User')\">\n <i class=\"fa-solid fa-user\"></i> Users\n </button>\n <button mjButton\n [variant]=\"searchType === 'Role' ? 'primary' : 'outline'\"\n (click)=\"setSearchType('Role')\">\n <i class=\"fa-solid fa-users\"></i> Roles\n </button>\n </div>\n <!-- Search input -->\n <div class=\"search-container\">\n <div class=\"search-input-wrapper\">\n <i class=\"fa-solid fa-search search-icon\"></i>\n <input type=\"text\"\n class=\"mj-input\"\n [value]=\"searchText\"\n (input)=\"onSearchInput($any($event.target).value)\"\n [placeholder]=\"'Search for ' + (searchType === 'User' ? 'users' : 'roles') + '...'\"\n (blur)=\"showSearchResults = false\" />\n </div>\n <!-- Search results dropdown -->\n @if (showSearchResults && searchResults.length > 0) {\n <div class=\"search-results\">\n @for (recipient of searchResults; track recipient) {\n <div class=\"search-result-item\"\n (mousedown)=\"selectRecipient(recipient)\">\n <i [class]=\"getShareTypeIcon(recipient.type)\"></i>\n <div class=\"recipient-info\">\n <span class=\"recipient-name\">{{ recipient.name }}</span>\n @if (recipient.email) {\n <span class=\"recipient-email\">{{ recipient.email }}</span>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n <!-- Selected recipient and permission -->\n @if (selectedRecipient) {\n <div class=\"selected-recipient\">\n <div class=\"recipient-chip\">\n <i [class]=\"getShareTypeIcon(selectedRecipient.type)\"></i>\n <span>{{ selectedRecipient.name }}</span>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"clearSearch()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <mj-dropdown\n [Data]=\"permissionOptions\"\n TextField=\"label\"\n ValueField=\"value\"\n [(ngModel)]=\"selectedPermission\"\n [ValuePrimitive]=\"true\"\n class=\"permission-dropdown\">\n <ng-template #mjDropdownItem let-dataItem>\n <div class=\"permission-option\">\n <span class=\"permission-label\">{{ dataItem.label }}</span>\n <span class=\"permission-desc\">{{ dataItem.description }}</span>\n </div>\n </ng-template>\n </mj-dropdown>\n <button mjButton\n variant=\"primary\"\n [disabled]=\"saving\"\n (click)=\"addShare()\">\n <i class=\"fa-solid fa-plus\"></i> Add\n </button>\n </div>\n }\n </div>\n }\n <!-- Divider -->\n @if (currentShares.length > 0 && canModifyShares) {\n <hr class=\"section-divider\">\n }\n <!-- Current shares section -->\n @if (currentShares.length > 0) {\n <div class=\"current-shares-section\">\n <h4 class=\"section-title\">People with access</h4>\n <div class=\"share-list\">\n @for (share of currentShares; track share) {\n <div class=\"share-item\">\n <div class=\"share-info\">\n <i [class]=\"getShareTypeIcon(share.type)\" class=\"share-type-icon\"></i>\n <div class=\"share-details\">\n <span class=\"share-name\">{{ share.recipientName }}</span>\n @if (share.recipientEmail) {\n <span class=\"share-email\">{{ share.recipientEmail }}</span>\n }\n @if (share.type === 'Role') {\n <span class=\"share-type-label\">Role</span>\n }\n </div>\n </div>\n <div class=\"share-actions\">\n @if (canModifyShares) {\n <mj-dropdown\n [Data]=\"permissionOptions\"\n TextField=\"label\"\n ValueField=\"value\"\n [ngModel]=\"share.permissionLevel\"\n [ValuePrimitive]=\"true\"\n (ValueChange)=\"updateSharePermission(share, $any($event))\"\n [Disabled]=\"saving\"\n class=\"permission-dropdown-small\">\n </mj-dropdown>\n }\n @if (!canModifyShares) {\n <span class=\"permission-badge\">\n {{ getPermissionLabel(share.permissionLevel) }}\n </span>\n }\n @if (canModifyShares) {\n <button mjButton\n variant=\"danger\"\n size=\"sm\"\n title=\"Remove access\"\n [disabled]=\"saving\"\n (click)=\"removeShare(share)\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n }\n </div>\n </div>\n }\n </div>\n </div>\n }\n <!-- Empty state -->\n @if (currentShares.length === 0 && !loading) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-share-nodes empty-icon\"></i>\n <p>This list hasn't been shared with anyone yet.</p>\n @if (canModifyShares) {\n <p class=\"empty-hint\">\n Use the search above to find users or roles to share with.\n </p>\n }\n </div>\n }\n <!-- Read-only notice -->\n @if (!canModifyShares) {\n <div class=\"readonly-notice\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Only the list owner can manage sharing.</span>\n </div>\n }\n }\n </div>\n <mj-dialog-actions>\n <button mjButton variant=\"primary\" (click)=\"onDone()\">Done</button>\n <button mjButton (click)=\"onCancel()\">Cancel</button>\n </mj-dialog-actions>\n </mj-dialog>\n}\n"]}
1
+ {"version":3,"file":"list-share-dialog.component.js","sourceRoot":"","sources":["../../../../src/lib/components/list-share-dialog/list-share-dialog.component.ts","../../../../src/lib/components/list-share-dialog/list-share-dialog.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n Input,\n Output,\n EventEmitter,\n OnInit,\n OnDestroy,\n ChangeDetectorRef\n} from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { debounceTime, takeUntil } from 'rxjs/operators';\nimport { Metadata } from '@memberjunction/core';\nimport { ListSharingService } from '../../services/list-sharing.service';\nimport {\n ListShareDialogConfig,\n ListShareDialogResult,\n ListShareInfo,\n ListPermissionLevel,\n ShareRecipient\n} from '../../models/list-sharing.models';\n\n/**\n * Dialog component for managing list sharing.\n * Allows users to share lists with other users or roles with different permission levels.\n *\n * Features:\n * - User/role search with autocomplete\n * - Permission level selection (View, Edit, Owner)\n * - Current shares management\n * - Remove access functionality\n */\n@Component({\n standalone: false,\n selector: 'mj-list-share-dialog',\n templateUrl: './list-share-dialog.component.html',\n styleUrls: ['./list-share-dialog.component.css']\n})\nexport class ListShareDialogComponent extends BaseAngularComponent implements OnInit, OnDestroy {\n /**\n * Configuration for the dialog\n */\n @Input() config!: ListShareDialogConfig;\n\n /**\n * Controls dialog visibility\n */\n @Input()\n get visible(): boolean {\n return this._visible;\n }\n set visible(value: boolean) {\n if (value && !this._visible) {\n this.initializeDialog();\n }\n this._visible = value;\n }\n private _visible = false;\n\n /**\n * Emitted when dialog is closed with results\n */\n @Output() complete = new EventEmitter<ListShareDialogResult>();\n\n /**\n * Emitted when dialog is cancelled\n */\n @Output() cancel = new EventEmitter<void>();\n\n /**\n * Emitted when the user clicks \"Manage Invitations\" in the footer.\n * Phase 2: opens the invitations management UI for this list.\n */\n @Output() manageInvitations = new EventEmitter<void>();\n\n /**\n * Emitted when the user clicks \"View audit log\" in the footer.\n * Phase 2: opens the per-list audit log view.\n */\n @Output() viewAuditLog = new EventEmitter<void>();\n\n /**\n * When true (the default), newly-added shares fan out as notifications\n * to the recipient (email + in-app, depending on user preferences).\n * The state is captured on the dialog so consumers can read it back\n * via `ListShareDialogResult.notifyByEmail`.\n */\n public notifyByEmail = true;\n\n // State\n loading = false;\n saving = false;\n searchText = '';\n searchType: 'User' | 'Role' = 'User';\n showSearchResults = false;\n\n // Data\n currentShares: ListShareInfo[] = [];\n searchResults: ShareRecipient[] = [];\n selectedRecipient: ShareRecipient | null = null;\n selectedPermission: ListPermissionLevel = 'View';\n\n // Track changes\n sharesAdded: ListShareInfo[] = [];\n sharesUpdated: ListShareInfo[] = [];\n sharesRemoved: string[] = [];\n\n // Permission options\n permissionOptions: { value: ListPermissionLevel; label: string; description: string }[] = [\n { value: 'View', label: 'Viewer', description: 'Can view list contents' },\n { value: 'Edit', label: 'Editor', description: 'Can add/remove items' },\n { value: 'Owner', label: 'Co-owner', description: 'Full control including sharing' }\n ];\n\n // Cleanup\n private destroy$ = new Subject<void>();\n private searchSubject = new Subject<string>();\n\n constructor(\n private sharingService: ListSharingService,\n private cdr: ChangeDetectorRef\n ) {\n super();}\n\n ngOnInit(): void {\n this.setupSearchDebounce();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * Get dialog title\n */\n get dialogTitle(): string {\n return `Share \"${this.config?.listName || 'List'}\"`;\n }\n\n /**\n * Check if there are pending changes\n */\n get hasChanges(): boolean {\n return this.sharesAdded.length > 0 ||\n this.sharesUpdated.length > 0 ||\n this.sharesRemoved.length > 0;\n }\n\n /**\n * Setup search with debounce\n */\n private setupSearchDebounce(): void {\n this.searchSubject.pipe(\n debounceTime(300),\n takeUntil(this.destroy$)\n ).subscribe(async (searchText: string) => {\n if (searchText.trim().length >= 2) {\n await this.performSearch(searchText);\n } else {\n this.searchResults = [];\n this.showSearchResults = false;\n }\n this.cdr.detectChanges();\n });\n }\n\n /**\n * Initialize dialog when opened\n */\n private async initializeDialog(): Promise<void> {\n this.resetState();\n await this.loadCurrentShares();\n }\n\n /**\n * Reset all state\n */\n private resetState(): void {\n this.searchText = '';\n this.searchType = 'User';\n this.showSearchResults = false;\n this.searchResults = [];\n this.selectedRecipient = null;\n this.selectedPermission = 'View';\n this.sharesAdded = [];\n this.sharesUpdated = [];\n this.sharesRemoved = [];\n }\n\n /**\n * Load current shares for the list\n */\n private async loadCurrentShares(): Promise<void> {\n if (!this.config) return;\n\n this.loading = true;\n this.cdr.detectChanges();\n\n try {\n this.currentShares = await this.sharingService.getListShares(this.config.listId);\n } catch (error) {\n console.error('Error loading shares:', error);\n this.currentShares = [];\n } finally {\n this.loading = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Handle search input\n */\n onSearchInput(event: Event): void {\n const value = (event.target as HTMLInputElement).value;\n this.searchText = value;\n this.searchSubject.next(value);\n }\n\n /**\n * Clear search\n */\n clearSearch(): void {\n this.searchText = '';\n this.searchResults = [];\n this.showSearchResults = false;\n this.selectedRecipient = null;\n }\n\n /**\n * Switch search type\n */\n setSearchType(type: 'User' | 'Role'): void {\n this.searchType = type;\n if (this.searchText.trim().length >= 2) {\n this.searchSubject.next(this.searchText);\n }\n }\n\n /**\n * Perform search\n */\n private async performSearch(searchText: string): Promise<void> {\n try {\n if (this.searchType === 'User') {\n this.searchResults = await this.sharingService.searchUsers(searchText);\n } else {\n this.searchResults = await this.sharingService.searchRoles(searchText);\n }\n\n // Filter out recipients that already have access\n const existingIds = new Set(this.currentShares.map(s => s.recipientId));\n this.searchResults = this.searchResults.filter(r => !existingIds.has(r.id));\n\n // Also filter out the list owner\n this.searchResults = this.searchResults.filter(r => r.id !== this.config?.currentUserId);\n\n this.showSearchResults = this.searchResults.length > 0;\n } catch (error) {\n console.error('Error searching:', error);\n this.searchResults = [];\n }\n }\n\n /**\n * Select a recipient from search results\n */\n selectRecipient(recipient: ShareRecipient): void {\n this.selectedRecipient = recipient;\n this.searchText = recipient.name;\n this.showSearchResults = false;\n this.cdr.detectChanges();\n }\n\n /**\n * Add the selected recipient as a share\n */\n async addShare(): Promise<void> {\n if (!this.selectedRecipient || !this.config) return;\n\n this.saving = true;\n\n try {\n const md = this.ProviderToUse;\n let result;\n\n if (this.selectedRecipient.type === 'User') {\n result = await this.sharingService.shareListWithUser(\n this.config.listId,\n this.selectedRecipient.id,\n this.selectedPermission,\n this.config.currentUserId\n );\n } else {\n result = await this.sharingService.shareListWithRole(\n this.config.listId,\n this.selectedRecipient.id,\n this.selectedPermission,\n this.config.currentUserId\n );\n }\n\n if (result.success && result.shareId) {\n // Create a ListShareInfo for tracking\n const newShare: ListShareInfo = {\n shareId: result.shareId,\n listId: this.config.listId,\n type: this.selectedRecipient.type,\n recipientId: this.selectedRecipient.id,\n recipientName: this.selectedRecipient.name,\n recipientEmail: this.selectedRecipient.email,\n permissionLevel: this.selectedPermission,\n status: 'Approved'\n };\n\n this.currentShares.push(newShare);\n this.sharesAdded.push(newShare);\n\n // Clear selection\n this.clearSearch();\n this.selectedPermission = 'View';\n } else {\n console.error('Failed to add share:', result.message);\n }\n } catch (error) {\n console.error('Error adding share:', error);\n } finally {\n this.saving = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Update permission level for an existing share\n */\n async updateSharePermission(share: ListShareInfo, newLevel: ListPermissionLevel): Promise<void> {\n if (share.permissionLevel === newLevel) return;\n\n this.saving = true;\n\n try {\n const result = await this.sharingService.updateSharePermission(share.shareId, newLevel);\n\n if (result.success) {\n share.permissionLevel = newLevel;\n\n // Track the update if not already tracked\n const existingUpdate = this.sharesUpdated.find(s => s.shareId === share.shareId);\n if (!existingUpdate) {\n this.sharesUpdated.push({ ...share });\n }\n } else {\n console.error('Failed to update permission:', result.message);\n }\n } catch (error) {\n console.error('Error updating permission:', error);\n } finally {\n this.saving = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Remove a share\n */\n async removeShare(share: ListShareInfo): Promise<void> {\n this.saving = true;\n\n try {\n const result = await this.sharingService.removeShare(share.shareId);\n\n if (result.success) {\n // Remove from current shares\n this.currentShares = this.currentShares.filter(s => s.shareId !== share.shareId);\n\n // Track the removal\n this.sharesRemoved.push(share.shareId);\n\n // Remove from added if it was just added in this session\n this.sharesAdded = this.sharesAdded.filter(s => s.shareId !== share.shareId);\n } else {\n console.error('Failed to remove share:', result.message);\n }\n } catch (error) {\n console.error('Error removing share:', error);\n } finally {\n this.saving = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Get icon for share type\n */\n getShareTypeIcon(type: 'User' | 'Role'): string {\n return type === 'User' ? 'fa-solid fa-user' : 'fa-solid fa-users';\n }\n\n /**\n * Get permission label\n */\n getPermissionLabel(level: ListPermissionLevel): string {\n const option = this.permissionOptions.find(o => o.value === level);\n return option?.label || level;\n }\n\n /**\n * Close dialog with results\n */\n onDone(): void {\n const result: ListShareDialogResult = {\n action: 'apply',\n sharesAdded: this.sharesAdded,\n sharesUpdated: this.sharesUpdated,\n sharesRemoved: this.sharesRemoved,\n notifyByEmail: this.notifyByEmail\n };\n\n this._visible = false;\n this.complete.emit(result);\n }\n\n /**\n * Cancel and close dialog\n */\n onCancel(): void {\n this._visible = false;\n this.cancel.emit();\n }\n\n /**\n * Check if current user can modify shares\n */\n get canModifyShares(): boolean {\n return this.config?.isOwner === true;\n }\n}\n","@if (visible) {\n <mj-dialog\n [Visible]=\"true\"\n [Title]=\"dialogTitle\"\n [Width]=\"500\"\n (Close)=\"onCancel()\">\n <div class=\"share-dialog-content\">\n <!-- Loading state -->\n @if (loading) {\n <div class=\"loading-container\">\n <mj-loading text=\"Loading shares...\"></mj-loading>\n </div>\n }\n @if (!loading) {\n <!-- Add new share section -->\n @if (canModifyShares) {\n <div class=\"add-share-section\">\n <h4 class=\"section-title\">Add people or roles</h4>\n <!-- Search type toggle -->\n <div class=\"search-type-toggle\">\n <button mjButton\n [variant]=\"searchType === 'User' ? 'primary' : 'outline'\"\n (click)=\"setSearchType('User')\">\n <i class=\"fa-solid fa-user\"></i> Users\n </button>\n <button mjButton\n [variant]=\"searchType === 'Role' ? 'primary' : 'outline'\"\n (click)=\"setSearchType('Role')\">\n <i class=\"fa-solid fa-users\"></i> Roles\n </button>\n </div>\n <!-- Search input -->\n <div class=\"search-container\">\n <div class=\"search-input-wrapper\">\n <i class=\"fa-solid fa-search search-icon\"></i>\n <input type=\"text\"\n class=\"mj-input\"\n [value]=\"searchText\"\n (input)=\"onSearchInput($any($event.target).value)\"\n [placeholder]=\"'Search for ' + (searchType === 'User' ? 'users' : 'roles') + '...'\"\n (blur)=\"showSearchResults = false\" />\n </div>\n <!-- Search results dropdown -->\n @if (showSearchResults && searchResults.length > 0) {\n <div class=\"search-results\">\n @for (recipient of searchResults; track recipient) {\n <div class=\"search-result-item\"\n (mousedown)=\"selectRecipient(recipient)\">\n <i [class]=\"getShareTypeIcon(recipient.type)\"></i>\n <div class=\"recipient-info\">\n <span class=\"recipient-name\">{{ recipient.name }}</span>\n @if (recipient.email) {\n <span class=\"recipient-email\">{{ recipient.email }}</span>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n <!-- Selected recipient and permission -->\n @if (selectedRecipient) {\n <div class=\"selected-recipient\">\n <div class=\"recipient-chip\">\n <i [class]=\"getShareTypeIcon(selectedRecipient.type)\"></i>\n <span>{{ selectedRecipient.name }}</span>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"clearSearch()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <mj-dropdown\n [Data]=\"permissionOptions\"\n TextField=\"label\"\n ValueField=\"value\"\n [(ngModel)]=\"selectedPermission\"\n [ValuePrimitive]=\"true\"\n class=\"permission-dropdown\">\n <ng-template #mjDropdownItem let-dataItem>\n <div class=\"permission-option\">\n <span class=\"permission-label\">{{ dataItem.label }}</span>\n <span class=\"permission-desc\">{{ dataItem.description }}</span>\n </div>\n </ng-template>\n </mj-dropdown>\n <button mjButton\n variant=\"primary\"\n [disabled]=\"saving\"\n (click)=\"addShare()\">\n <i class=\"fa-solid fa-plus\"></i> Add\n </button>\n </div>\n }\n </div>\n }\n <!-- Divider -->\n @if (currentShares.length > 0 && canModifyShares) {\n <hr class=\"section-divider\">\n }\n <!-- Current shares section -->\n @if (currentShares.length > 0) {\n <div class=\"current-shares-section\">\n <h4 class=\"section-title\">People with access</h4>\n <div class=\"share-list\">\n @for (share of currentShares; track share) {\n <div class=\"share-item\">\n <div class=\"share-info\">\n <i [class]=\"getShareTypeIcon(share.type)\" class=\"share-type-icon\"></i>\n <div class=\"share-details\">\n <span class=\"share-name\">{{ share.recipientName }}</span>\n @if (share.recipientEmail) {\n <span class=\"share-email\">{{ share.recipientEmail }}</span>\n }\n @if (share.type === 'Role') {\n <span class=\"share-type-label\">Role</span>\n }\n </div>\n </div>\n <div class=\"share-actions\">\n @if (canModifyShares) {\n <mj-dropdown\n [Data]=\"permissionOptions\"\n TextField=\"label\"\n ValueField=\"value\"\n [ngModel]=\"share.permissionLevel\"\n [ValuePrimitive]=\"true\"\n (ValueChange)=\"updateSharePermission(share, $any($event))\"\n [Disabled]=\"saving\"\n class=\"permission-dropdown-small\">\n </mj-dropdown>\n }\n @if (!canModifyShares) {\n <span class=\"permission-badge\">\n {{ getPermissionLabel(share.permissionLevel) }}\n </span>\n }\n @if (canModifyShares) {\n <button mjButton\n variant=\"danger\"\n size=\"sm\"\n title=\"Remove access\"\n [disabled]=\"saving\"\n (click)=\"removeShare(share)\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n }\n </div>\n </div>\n }\n </div>\n </div>\n }\n <!-- Empty state -->\n @if (currentShares.length === 0 && !loading) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-share-nodes empty-icon\"></i>\n <p>This list hasn't been shared with anyone yet.</p>\n @if (canModifyShares) {\n <p class=\"empty-hint\">\n Use the search above to find users or roles to share with.\n </p>\n }\n </div>\n }\n <!-- Read-only notice -->\n @if (!canModifyShares) {\n <div class=\"readonly-notice\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Only the list owner can manage sharing.</span>\n </div>\n }\n }\n </div>\n <!-- Notify-by-email toggle (mockup 15). Sits above the action footer so\n the user can flip it after adding shares but before clicking Done. -->\n @if (canModifyShares) {\n <div class=\"notify-toggle\">\n <input id=\"share-notify\" type=\"checkbox\" [(ngModel)]=\"notifyByEmail\" />\n <label for=\"share-notify\">\n <i class=\"fa-solid fa-envelope\"></i>\n Send email notification to new shares\n </label>\n </div>\n }\n <mj-dialog-actions>\n <button mjButton variant=\"primary\" (click)=\"onDone()\">Done</button>\n <button mjButton variant=\"outline\" (click)=\"manageInvitations.emit()\">\n <i class=\"fa-solid fa-envelope-open-text\"></i>\n Manage Invitations\n </button>\n <button mjButton (click)=\"onCancel()\">Cancel</button>\n <a class=\"audit-log-link\" (click)=\"viewAuditLog.emit(); $event.preventDefault()\" href=\"#\">\n <i class=\"fa-solid fa-clock-rotate-left\"></i>\n View audit log\n </a>\n </mj-dialog-actions>\n </mj-dialog>\n}\n"]}
@@ -0,0 +1,52 @@
1
+ import { OnInit } from '@angular/core';
2
+ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
3
+ import * as i0 from "@angular/core";
4
+ /**
5
+ * Usage-stats side panel for a single list (mockup 25). Surfaces four
6
+ * cheap-to-compute counters + a recent-activity feed pulled from the
7
+ * generic `MJ: Audit Logs` table (same one Phase 2's sharing emits to).
8
+ *
9
+ * Stats:
10
+ * - Member count — `count(MJ: List Details)` for this list
11
+ * - Active shares — non-revoked `MJ: Resource Permissions`
12
+ * - Growth this month — `count(MJ: List Details where __mj_CreatedAt >= start-of-month)`
13
+ * - Time since last activity — newest audit-log timestamp
14
+ *
15
+ * Recent activity = the 5 most-recent audit events (uses the same join
16
+ * pattern as `mj-list-audit-log` — type + user lookup in a single batch).
17
+ */
18
+ interface RecentActivity {
19
+ ID: string;
20
+ EventType: string;
21
+ Description: string;
22
+ Who: string;
23
+ When: Date;
24
+ }
25
+ export declare class ListStatsComponent extends BaseAngularComponent implements OnInit {
26
+ private readonly cdr;
27
+ set ListID(value: string | null);
28
+ get ListID(): string | null;
29
+ private _listId;
30
+ /**
31
+ * Bump this number from the parent after any mutation (add / remove /
32
+ * status update / share / unshare) to force the stats panel to re-query.
33
+ * Without this, the Members count drifts from reality until full reload.
34
+ */
35
+ set RefreshTrigger(value: number);
36
+ get RefreshTrigger(): number;
37
+ private _refreshTrigger;
38
+ memberCount: number;
39
+ activeShareCount: number;
40
+ growthThisMonth: number;
41
+ lastActivityAt: Date | null;
42
+ recent: RecentActivity[];
43
+ loading: boolean;
44
+ private initialized;
45
+ ngOnInit(): Promise<void>;
46
+ formatRelative(d: Date | null): string;
47
+ private load;
48
+ static ɵfac: i0.ɵɵFactoryDeclaration<ListStatsComponent, never>;
49
+ static ɵcmp: i0.ɵɵComponentDeclaration<ListStatsComponent, "mj-list-stats", never, { "ListID": { "alias": "ListID"; "required": false; }; "RefreshTrigger": { "alias": "RefreshTrigger"; "required": false; }; }, {}, never, never, false, never>;
50
+ }
51
+ export {};
52
+ //# sourceMappingURL=list-stats.component.d.ts.map
@@ -0,0 +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;IAChB,gBAAgB,SAAK;IACrB,eAAe,SAAK;IACpB,cAAc,EAAE,IAAI,GAAG,IAAI,CAAQ;IACnC,MAAM,EAAE,cAAc,EAAE,CAAM;IAC9B,OAAO,UAAS;IAEvB,OAAO,CAAC,WAAW,CAAS;IAEtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAKxB,cAAc,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM;YAgB/B,IAAI;yCA9DP,kBAAkB;2CAAlB,kBAAkB;CAwL9B"}
@@ -0,0 +1,283 @@
1
+ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, inject } from '@angular/core';
2
+ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
3
+ import { RunView } from '@memberjunction/core';
4
+ import * as i0 from "@angular/core";
5
+ import * as i1 from "@memberjunction/ng-shared-generic";
6
+ const _forTrack0 = ($index, $item) => $item.ID;
7
+ function ListStatsComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
8
+ i0.ɵɵelementStart(0, "div", 1);
9
+ i0.ɵɵelement(1, "mj-loading", 2);
10
+ i0.ɵɵelementEnd();
11
+ } }
12
+ function ListStatsComponent_Conditional_2_Conditional_24_For_5_Template(rf, ctx) { if (rf & 1) {
13
+ i0.ɵɵelementStart(0, "li", 13)(1, "span", 14);
14
+ i0.ɵɵtext(2);
15
+ i0.ɵɵelementEnd();
16
+ i0.ɵɵelementStart(3, "span", 15);
17
+ i0.ɵɵtext(4);
18
+ i0.ɵɵelementStart(5, "span", 16);
19
+ i0.ɵɵtext(6);
20
+ i0.ɵɵelementEnd()()();
21
+ } if (rf & 2) {
22
+ const item_r1 = ctx.$implicit;
23
+ const ctx_r1 = i0.ɵɵnextContext(3);
24
+ i0.ɵɵadvance(2);
25
+ i0.ɵɵtextInterpolate(item_r1.EventType);
26
+ i0.ɵɵadvance(2);
27
+ i0.ɵɵtextInterpolate1(" ", item_r1.Description, " ");
28
+ i0.ɵɵadvance(2);
29
+ i0.ɵɵtextInterpolate2("\u2014 ", item_r1.Who, " \u00B7 ", ctx_r1.formatRelative(item_r1.When));
30
+ } }
31
+ function ListStatsComponent_Conditional_2_Conditional_24_Template(rf, ctx) { if (rf & 1) {
32
+ i0.ɵɵelementStart(0, "div", 10);
33
+ i0.ɵɵelement(1, "i", 11);
34
+ i0.ɵɵtext(2, " Recent activity ");
35
+ i0.ɵɵelementEnd();
36
+ i0.ɵɵelementStart(3, "ul", 12);
37
+ i0.ɵɵrepeaterCreate(4, ListStatsComponent_Conditional_2_Conditional_24_For_5_Template, 7, 4, "li", 13, _forTrack0);
38
+ i0.ɵɵelementEnd();
39
+ } if (rf & 2) {
40
+ const ctx_r1 = i0.ɵɵnextContext(2);
41
+ i0.ɵɵadvance(4);
42
+ i0.ɵɵrepeater(ctx_r1.recent);
43
+ } }
44
+ function ListStatsComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
45
+ i0.ɵɵelementStart(0, "div", 3);
46
+ i0.ɵɵelement(1, "i", 4);
47
+ i0.ɵɵtext(2, " Usage ");
48
+ i0.ɵɵelementEnd();
49
+ i0.ɵɵelementStart(3, "div", 5)(4, "div", 6)(5, "div", 7);
50
+ i0.ɵɵtext(6);
51
+ i0.ɵɵelementEnd();
52
+ i0.ɵɵelementStart(7, "div", 8);
53
+ i0.ɵɵtext(8, "Members");
54
+ i0.ɵɵelementEnd()();
55
+ i0.ɵɵelementStart(9, "div", 6)(10, "div", 7);
56
+ i0.ɵɵtext(11);
57
+ i0.ɵɵelementEnd();
58
+ i0.ɵɵelementStart(12, "div", 8);
59
+ i0.ɵɵtext(13, "This month");
60
+ i0.ɵɵelementEnd()();
61
+ i0.ɵɵelementStart(14, "div", 6)(15, "div", 7);
62
+ i0.ɵɵtext(16);
63
+ i0.ɵɵelementEnd();
64
+ i0.ɵɵelementStart(17, "div", 8);
65
+ i0.ɵɵtext(18, "Active shares");
66
+ i0.ɵɵelementEnd()();
67
+ i0.ɵɵelementStart(19, "div", 6)(20, "div", 9);
68
+ i0.ɵɵtext(21);
69
+ i0.ɵɵelementEnd();
70
+ i0.ɵɵelementStart(22, "div", 8);
71
+ i0.ɵɵtext(23, "Last activity");
72
+ i0.ɵɵelementEnd()()();
73
+ i0.ɵɵconditionalCreate(24, ListStatsComponent_Conditional_2_Conditional_24_Template, 6, 0);
74
+ } if (rf & 2) {
75
+ const ctx_r1 = i0.ɵɵnextContext();
76
+ i0.ɵɵadvance(6);
77
+ i0.ɵɵtextInterpolate(ctx_r1.memberCount);
78
+ i0.ɵɵadvance(5);
79
+ i0.ɵɵtextInterpolate1("+", ctx_r1.growthThisMonth);
80
+ i0.ɵɵadvance(5);
81
+ i0.ɵɵtextInterpolate(ctx_r1.activeShareCount);
82
+ i0.ɵɵadvance(5);
83
+ i0.ɵɵtextInterpolate(ctx_r1.formatRelative(ctx_r1.lastActivityAt));
84
+ i0.ɵɵadvance(3);
85
+ i0.ɵɵconditional(ctx_r1.recent.length > 0 ? 24 : -1);
86
+ } }
87
+ function newestOf(...dates) {
88
+ const ms = dates.filter((d) => d != null).map((d) => d.getTime());
89
+ return ms.length === 0 ? null : new Date(Math.max(...ms));
90
+ }
91
+ export class ListStatsComponent extends BaseAngularComponent {
92
+ cdr = inject(ChangeDetectorRef);
93
+ set ListID(value) {
94
+ if (this._listId !== value) {
95
+ this._listId = value;
96
+ if (this.initialized && value)
97
+ void this.load();
98
+ }
99
+ }
100
+ get ListID() {
101
+ return this._listId;
102
+ }
103
+ _listId = null;
104
+ /**
105
+ * Bump this number from the parent after any mutation (add / remove /
106
+ * status update / share / unshare) to force the stats panel to re-query.
107
+ * Without this, the Members count drifts from reality until full reload.
108
+ */
109
+ set RefreshTrigger(value) {
110
+ if (value !== this._refreshTrigger) {
111
+ this._refreshTrigger = value;
112
+ if (this.initialized && this._listId)
113
+ void this.load();
114
+ }
115
+ }
116
+ get RefreshTrigger() {
117
+ return this._refreshTrigger;
118
+ }
119
+ _refreshTrigger = 0;
120
+ memberCount = 0;
121
+ activeShareCount = 0;
122
+ growthThisMonth = 0;
123
+ lastActivityAt = null;
124
+ recent = [];
125
+ loading = false;
126
+ initialized = false;
127
+ async ngOnInit() {
128
+ this.initialized = true;
129
+ if (this._listId)
130
+ await this.load();
131
+ }
132
+ formatRelative(d) {
133
+ if (!d || isNaN(d.getTime()))
134
+ return 'never';
135
+ const diff = Date.now() - d.getTime();
136
+ if (diff < 60_000)
137
+ return 'just now';
138
+ const minutes = Math.floor(diff / 60_000);
139
+ if (minutes < 60)
140
+ return `${minutes}m ago`;
141
+ const hours = Math.floor(minutes / 60);
142
+ if (hours < 24)
143
+ return `${hours}h ago`;
144
+ const days = Math.floor(hours / 24);
145
+ if (days < 7)
146
+ return `${days}d ago`;
147
+ const weeks = Math.floor(days / 7);
148
+ if (weeks < 5)
149
+ return `${weeks}w ago`;
150
+ const months = Math.floor(days / 30);
151
+ return `${months}mo ago`;
152
+ }
153
+ async load() {
154
+ if (!this._listId)
155
+ return;
156
+ this.loading = true;
157
+ this.cdr.markForCheck();
158
+ try {
159
+ const md = this.ProviderToUse;
160
+ const listsEntity = md.Entities.find((e) => e.Name === 'MJ: Lists');
161
+ if (!listsEntity)
162
+ return;
163
+ const startOfMonth = new Date(new Date().getFullYear(), new Date().getMonth(), 1).toISOString();
164
+ const escapedId = this._listId.replace(/'/g, "''");
165
+ const rv = RunView.FromMetadataProvider(md);
166
+ // Cache-buster: every RunView gets an always-true predicate that
167
+ // varies on each load() call. The server keys its RunView cache off
168
+ // the literal filter string, so we force a miss on every refresh.
169
+ // Without this, Members / Growth / Active shares / Last activity
170
+ // would all read stale until a full server restart, even after the
171
+ // RefreshTrigger input bumps. SQL comments (`/* */`) are rejected by
172
+ // the ExtraFilter injection validator, so we use a numeric equality
173
+ // instead.
174
+ const cb = Date.now();
175
+ const cbPredicate = ` AND ${cb} = ${cb}`;
176
+ const [members, growth, shares, audits, latestMember] = await Promise.all([
177
+ rv.RunView({
178
+ EntityName: 'MJ: List Details',
179
+ ExtraFilter: `ListID='${escapedId}'${cbPredicate}`,
180
+ Fields: ['ID'],
181
+ ResultType: 'simple',
182
+ }),
183
+ rv.RunView({
184
+ EntityName: 'MJ: List Details',
185
+ ExtraFilter: `ListID='${escapedId}' AND __mj_CreatedAt >= '${startOfMonth}'${cbPredicate}`,
186
+ Fields: ['ID'],
187
+ ResultType: 'simple',
188
+ }),
189
+ rv.RunView({
190
+ EntityName: 'MJ: Resource Permissions',
191
+ ExtraFilter: `ResourceTypeID='E64D433E-F36B-1410-8560-0041FA62858A' AND ResourceRecordID='${escapedId}' AND Status<>'Revoked'${cbPredicate}`,
192
+ Fields: ['ID'],
193
+ ResultType: 'simple',
194
+ }),
195
+ rv.RunView({
196
+ EntityName: 'MJ: Audit Logs',
197
+ ExtraFilter: `EntityID='${listsEntity.ID}' AND RecordID='${escapedId}'${cbPredicate}`,
198
+ OrderBy: '__mj_CreatedAt DESC',
199
+ MaxRows: 5,
200
+ Fields: ['ID', 'UserID', 'AuditLogTypeID', 'Description', '__mj_CreatedAt'],
201
+ ResultType: 'simple',
202
+ }),
203
+ // Newest list-detail timestamp. Audit Logs only fire for sharing
204
+ // events, so without this the "Last activity" widget reads "never"
205
+ // even after the user has been adding/editing members all day.
206
+ rv.RunView({
207
+ EntityName: 'MJ: List Details',
208
+ ExtraFilter: `ListID='${escapedId}'${cbPredicate}`,
209
+ OrderBy: '__mj_UpdatedAt DESC',
210
+ MaxRows: 1,
211
+ Fields: ['__mj_UpdatedAt'],
212
+ ResultType: 'simple',
213
+ }),
214
+ ]);
215
+ this.memberCount = members.Results?.length ?? 0;
216
+ this.growthThisMonth = growth.Results?.length ?? 0;
217
+ this.activeShareCount = shares.Results?.length ?? 0;
218
+ const auditRows = audits.Results ?? [];
219
+ const latestAuditAt = auditRows.length > 0 ? new Date(auditRows[0].__mj_CreatedAt) : null;
220
+ const rawLatestMember = latestMember.Results?.[0]?.__mj_UpdatedAt;
221
+ const parsedLatestMember = rawLatestMember ? new Date(rawLatestMember) : null;
222
+ const latestMemberAt = parsedLatestMember && !isNaN(parsedLatestMember.getTime()) ? parsedLatestMember : null;
223
+ this.lastActivityAt = newestOf(latestAuditAt, latestMemberAt);
224
+ if (auditRows.length === 0) {
225
+ this.recent = [];
226
+ return;
227
+ }
228
+ // Resolve user + audit-log type names in two cheap lookups.
229
+ const userIds = Array.from(new Set(auditRows.map((r) => String(r.UserID))));
230
+ const typeIds = Array.from(new Set(auditRows.map((r) => String(r.AuditLogTypeID))));
231
+ const [users, types] = await Promise.all([
232
+ rv.RunView({
233
+ EntityName: 'MJ: Users',
234
+ ExtraFilter: `ID IN (${userIds.map((id) => `'${id}'`).join(',')})`,
235
+ Fields: ['ID', 'Name'],
236
+ ResultType: 'simple',
237
+ }),
238
+ rv.RunView({
239
+ EntityName: 'MJ: Audit Log Types',
240
+ ExtraFilter: `ID IN (${typeIds.map((id) => `'${id}'`).join(',')})`,
241
+ Fields: ['ID', 'Name'],
242
+ ResultType: 'simple',
243
+ }),
244
+ ]);
245
+ const userById = new Map();
246
+ for (const u of users.Results ?? [])
247
+ userById.set(String(u.ID), String(u.Name));
248
+ const typeById = new Map();
249
+ for (const t of types.Results ?? [])
250
+ typeById.set(String(t.ID), String(t.Name));
251
+ this.recent = auditRows.map((r) => ({
252
+ ID: String(r.ID),
253
+ EventType: typeById.get(String(r.AuditLogTypeID)) ?? '(unknown event)',
254
+ Description: r.Description ?? '',
255
+ Who: userById.get(String(r.UserID)) ?? '(unknown user)',
256
+ When: new Date(r.__mj_CreatedAt),
257
+ }));
258
+ }
259
+ finally {
260
+ this.loading = false;
261
+ this.cdr.markForCheck();
262
+ }
263
+ }
264
+ static ɵfac = /*@__PURE__*/ (() => { let ɵListStatsComponent_BaseFactory; return function ListStatsComponent_Factory(__ngFactoryType__) { return (ɵListStatsComponent_BaseFactory || (ɵListStatsComponent_BaseFactory = i0.ɵɵgetInheritedFactory(ListStatsComponent)))(__ngFactoryType__ || ListStatsComponent); }; })();
265
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ListStatsComponent, selectors: [["mj-list-stats"]], inputs: { ListID: "ListID", RefreshTrigger: "RefreshTrigger" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 3, vars: 1, consts: [[1, "list-stats"], [1, "list-stats__loading"], ["text", "Loading stats...", "size", "small"], [1, "list-stats__heading"], [1, "fa-solid", "fa-chart-simple"], [1, "list-stats__counters"], [1, "list-stats__counter"], [1, "list-stats__value"], [1, "list-stats__label"], [1, "list-stats__value-text"], [1, "list-stats__heading", "list-stats__heading--secondary"], [1, "fa-solid", "fa-clock-rotate-left"], [1, "list-stats__feed"], [1, "list-stats__entry"], [1, "list-stats__entry-type"], [1, "list-stats__entry-detail"], [1, "list-stats__entry-meta"]], template: function ListStatsComponent_Template(rf, ctx) { if (rf & 1) {
266
+ i0.ɵɵelementStart(0, "div", 0);
267
+ i0.ɵɵconditionalCreate(1, ListStatsComponent_Conditional_1_Template, 2, 0, "div", 1)(2, ListStatsComponent_Conditional_2_Template, 25, 5);
268
+ i0.ɵɵelementEnd();
269
+ } if (rf & 2) {
270
+ i0.ɵɵadvance();
271
+ i0.ɵɵconditional(ctx.loading ? 1 : 2);
272
+ } }, dependencies: [i1.LoadingComponent], styles: [".list-stats[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n font-size: 13px;\n}\n\n.list-stats__loading[_ngcontent-%COMP%] {\n padding: 16px;\n display: flex;\n justify-content: center;\n}\n\n.list-stats__heading[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--mj-text-muted);\n font-weight: 600;\n}\n\n.list-stats__heading--secondary[_ngcontent-%COMP%] {\n margin-top: 4px;\n}\n\n.list-stats__counters[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 10px;\n}\n\n.list-stats__counter[_ngcontent-%COMP%] {\n padding: 10px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.list-stats__value[_ngcontent-%COMP%] {\n font-size: 20px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.1;\n}\n\n.list-stats__value-text[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.list-stats__label[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.list-stats__feed[_ngcontent-%COMP%] {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.list-stats__entry[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n font-size: 12px;\n}\n\n.list-stats__entry-type[_ngcontent-%COMP%] {\n display: inline-block;\n font-size: 10.5px;\n padding: 1px 6px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 600;\n width: fit-content;\n}\n\n.list-stats__entry-detail[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n line-height: 1.4;\n}\n\n.list-stats__entry-meta[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}"], changeDetection: 0 });
273
+ }
274
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ListStatsComponent, [{
275
+ type: Component,
276
+ args: [{ standalone: false, selector: 'mj-list-stats', changeDetection: ChangeDetectionStrategy.OnPush, template: "<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\">\u2014 {{ item.Who }} \u00B7 {{ formatRelative(item.When) }}</span>\n </span>\n </li>\n }\n </ul>\n }\n }\n</div>\n", styles: [".list-stats {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n font-size: 13px;\n}\n\n.list-stats__loading {\n padding: 16px;\n display: flex;\n justify-content: center;\n}\n\n.list-stats__heading {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--mj-text-muted);\n font-weight: 600;\n}\n\n.list-stats__heading--secondary {\n margin-top: 4px;\n}\n\n.list-stats__counters {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 10px;\n}\n\n.list-stats__counter {\n padding: 10px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.list-stats__value {\n font-size: 20px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.1;\n}\n\n.list-stats__value-text {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.list-stats__label {\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.list-stats__feed {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.list-stats__entry {\n display: flex;\n flex-direction: column;\n gap: 2px;\n font-size: 12px;\n}\n\n.list-stats__entry-type {\n display: inline-block;\n font-size: 10.5px;\n padding: 1px 6px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 600;\n width: fit-content;\n}\n\n.list-stats__entry-detail {\n color: var(--mj-text-secondary);\n line-height: 1.4;\n}\n\n.list-stats__entry-meta {\n color: var(--mj-text-muted);\n}\n"] }]
277
+ }], null, { ListID: [{
278
+ type: Input
279
+ }], RefreshTrigger: [{
280
+ type: Input
281
+ }] }); })();
282
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ListStatsComponent, { className: "ListStatsComponent", filePath: "src/lib/components/list-stats/list-stats.component.ts", lineNumber: 41 }); })();
283
+ //# sourceMappingURL=list-stats.component.js.map
@@ -0,0 +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 public activeShareCount = 0;\n public growthThisMonth = 0;\n public lastActivityAt: Date | null = null;\n public recent: RecentActivity[] = [];\n public loading = false;\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 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"]}