@memberjunction/ng-file-storage 5.51.0 → 6.1.0-edge.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"folder-tree.component.d.ts","sourceRoot":"","sources":["../../../src/lib/file-browser/folder-tree.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAA4C,MAAM,eAAe,CAAC;AAElG,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;;AAG3E;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,qBAMa,mBAAmB;IAC9B;;OAEG;IACH,OAAO,CAAC,aAAa,CAA2B;IAEhD;;OAEG;IACH,OAAO,CAAC,QAAQ,CAA2C;;IAM3D,IACI,OAAO,CAAC,KAAK,EAAE,0BAA0B,GAAG,IAAI,EAQnD;IACD,IAAI,OAAO,IAAI,0BAA0B,GAAG,IAAI,CAE/C;IAED;;OAEG;IACO,cAAc,uBAA8B;IAEtD;;OAEG;IACI,WAAW,EAAE,MAAM,CAAO;IAEjC;;OAEG;IACI,OAAO,EAAE,MAAM,EAAE,CAAM;IAE9B;;OAEG;IACI,YAAY,EAAE,MAAM,CAAM;IAEjC;;OAEG;IACI,WAAW,EAAE,cAAc,EAAE,CAAM;IAE1C;;OAEG;IACI,OAAO,EAAE,UAAU,EAAE,CAAM;IAElC,OAAO,CAAC,GAAG,CAA6B;IAExC;;OAEG;IACI,SAAS,EAAE,OAAO,CAAS;IAElC;;OAEG;IACI,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE1C;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACI,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAuBzC;;;OAGG;IACI,OAAO,IAAI,IAAI;IAItB;;OAEG;IACI,YAAY,IAAI,IAAI;IAY3B;;OAEG;IACI,eAAe,IAAI,IAAI;IAY9B;;OAEG;IACI,SAAS,IAAI,OAAO;IAI3B;;OAEG;IACI,YAAY,IAAI,OAAO;IAI9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA+BzB;;OAEG;IACI,iBAAiB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IAKpD;;OAEG;YACW,WAAW;IAuCzB;;OAEG;IACI,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;yCAtPnC,mBAAmB;2CAAnB,mBAAmB;CA0P/B"}
1
+ {"version":3,"file":"folder-tree.component.d.ts","sourceRoot":"","sources":["../../../src/lib/file-browser/folder-tree.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAA4C,MAAM,eAAe,CAAC;AAElG,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;;AAG3E;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,qBAMa,mBAAmB;IAC9B;;OAEG;IACH,OAAO,CAAC,aAAa,CAA2B;IAEhD;;OAEG;IACH,OAAO,CAAC,QAAQ,CAA2C;;IAM3D,IACI,OAAO,CAAC,KAAK,EAAE,0BAA0B,GAAG,IAAI,EAQnD;IACD,IAAI,OAAO,IAAI,0BAA0B,GAAG,IAAI,CAE/C;IAED;;OAEG;IACO,cAAc,uBAA8B;IAEtD;;OAEG;IACI,WAAW,EAAE,MAAM,CAAO;IAEjC;;OAEG;IACI,OAAO,EAAE,MAAM,EAAE,CAAM;IAE9B;;OAEG;IACI,YAAY,EAAE,MAAM,CAAM;IAEjC;;OAEG;IACI,WAAW,EAAE,cAAc,EAAE,CAAM;IAE1C;;OAEG;IACI,OAAO,EAAE,UAAU,EAAE,CAAM;IAElC,OAAO,CAAC,GAAG,CAA6B;IAExC;;OAEG;IACI,SAAS,EAAE,OAAO,CAAS;IAElC;;OAEG;IACI,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE1C;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACI,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAuBzC;;;OAGG;IACI,OAAO,IAAI,IAAI;IAItB;;OAEG;IACI,YAAY,IAAI,IAAI;IAY3B;;OAEG;IACI,eAAe,IAAI,IAAI;IAY9B;;OAEG;IACI,SAAS,IAAI,OAAO;IAI3B;;OAEG;IACI,YAAY,IAAI,OAAO;IAI9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA+BzB;;OAEG;IACI,iBAAiB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IAKpD;;OAEG;YACW,WAAW;IA6CzB;;OAEG;IACI,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;yCA5PnC,mBAAmB;2CAAnB,mBAAmB;CAgQ/B"}
@@ -300,7 +300,13 @@ export class FolderTreeComponent {
300
300
  this.errorMessage = null;
301
301
  this.cdr.detectChanges();
302
302
  try {
303
- const listResult = await this.storageClient.ListObjects(this.account.account.ID, this.currentPath === '/' ? '' : this.currentPath, '/');
303
+ // Blob keys (Azure/S3) have NO leading slash and use a trailing slash to denote a
304
+ // directory prefix. currentPath is stored with a leading slash (e.g. "/test"), which
305
+ // never matches real keys — so nested-folder listings came back empty (empty folder
306
+ // names). Normalize to a slash-free, trailing-slash prefix before listing.
307
+ const rawPath = this.currentPath && this.currentPath !== '/' ? this.currentPath : '';
308
+ const listPrefix = rawPath ? rawPath.replace(/^\/+/, '').replace(/\/+$/, '') + '/' : '';
309
+ const listResult = await this.storageClient.ListObjects(this.account.account.ID, listPrefix, '/');
304
310
  // Convert prefixes to FolderItems
305
311
  const prefixes = listResult.prefixes || [];
306
312
  this.folders = prefixes.map((prefix) => {
@@ -1 +1 @@
1
- {"version":3,"file":"folder-tree.component.js","sourceRoot":"","sources":["../../../src/lib/file-browser/folder-tree.component.ts","../../../src/lib/file-browser/folder-tree.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, Output, ChangeDetectorRef, inject } from '@angular/core';\nimport { GraphQLDataProvider, GraphQLFileStorageClient } from '@memberjunction/graphql-dataprovider';\nimport { StorageAccountWithProvider } from '@memberjunction/core-entities';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Represents a breadcrumb item in the path\n */\nexport interface BreadcrumbItem {\n label: string;\n path: string;\n}\n\n/**\n * Represents a folder in the tree\n */\nexport interface FolderItem {\n name: string;\n fullPath: string;\n}\n\n/**\n * Folder tree navigation component with breadcrumbs and history navigation.\n * Provides Mac Finder-style navigation with back/forward buttons and breadcrumb path.\n * Loads actual folder structure from storage accounts via GraphQL.\n */\n@Component({\n standalone: false,\n selector: 'mj-folder-tree',\n templateUrl: './folder-tree.component.html',\n styleUrls: ['./folder-tree.component.css']\n})\nexport class FolderTreeComponent {\n /**\n * GraphQL client for file storage operations\n */\n private storageClient: GraphQLFileStorageClient;\n\n /**\n * Currently selected storage account with provider details\n */\n private _account: StorageAccountWithProvider | null = null;\n\n constructor() {\n this.storageClient = new GraphQLFileStorageClient(GraphQLDataProvider.Instance);\n }\n\n @Input()\n set account(value: StorageAccountWithProvider | null) {\n const previousAccount = this._account;\n this._account = value;\n\n if (value && !UUIDsEqual(value.account.ID, previousAccount?.account.ID)) {\n this.resetNavigation();\n this.loadFolders();\n }\n }\n get account(): StorageAccountWithProvider | null {\n return this._account;\n }\n\n /**\n * Emits when a folder is selected in the tree\n */\n @Output() folderSelected = new EventEmitter<string>();\n\n /**\n * Current folder path\n */\n public currentPath: string = '/';\n\n /**\n * Navigation history (for back button)\n */\n public history: string[] = [];\n\n /**\n * Current position in history\n */\n public historyIndex: number = -1;\n\n /**\n * Breadcrumb items for current path\n */\n public breadcrumbs: BreadcrumbItem[] = [];\n\n /**\n * Folders in current path\n */\n public folders: FolderItem[] = [];\n\n private cdr = inject(ChangeDetectorRef);\n\n /**\n * Loading state\n */\n public isLoading: boolean = false;\n\n /**\n * Error message\n */\n public errorMessage: string | null = null;\n\n /**\n * Resets navigation when provider changes\n */\n private resetNavigation(): void {\n this.currentPath = '/';\n this.history = ['/'];\n this.historyIndex = 0;\n this.updateBreadcrumbs();\n }\n\n /**\n * Navigates to a specific folder path\n */\n public navigateToPath(path: string): void {\n // Don't navigate if already at this path\n if (path === this.currentPath) {\n return;\n }\n\n // Add to history if navigating from user action (not back/forward)\n if (this.historyIndex === this.history.length - 1) {\n this.history.push(path);\n this.historyIndex = this.history.length - 1;\n } else {\n // Navigating from middle of history - truncate forward history\n this.history = this.history.slice(0, this.historyIndex + 1);\n this.history.push(path);\n this.historyIndex = this.history.length - 1;\n }\n\n this.currentPath = path;\n this.updateBreadcrumbs();\n this.loadFolders();\n this.folderSelected.emit(path);\n }\n\n /**\n * Refreshes the current folder view without changing navigation\n * Used when folder structure changes (e.g., folder deleted) but we're staying in the same location\n */\n public refresh(): void {\n this.loadFolders();\n }\n\n /**\n * Navigates back in history\n */\n public navigateBack(): void {\n if (!this.canGoBack()) {\n return;\n }\n\n this.historyIndex--;\n this.currentPath = this.history[this.historyIndex];\n this.updateBreadcrumbs();\n this.folderSelected.emit(this.currentPath);\n this.loadFolders();\n }\n\n /**\n * Navigates forward in history\n */\n public navigateForward(): void {\n if (!this.canGoForward()) {\n return;\n }\n\n this.historyIndex++;\n this.currentPath = this.history[this.historyIndex];\n this.updateBreadcrumbs();\n this.folderSelected.emit(this.currentPath);\n this.loadFolders();\n }\n\n /**\n * Checks if can navigate back\n */\n public canGoBack(): boolean {\n return this.historyIndex > 0;\n }\n\n /**\n * Checks if can navigate forward\n */\n public canGoForward(): boolean {\n return this.historyIndex < this.history.length - 1;\n }\n\n /**\n * Updates breadcrumbs based on current path\n */\n private updateBreadcrumbs(): void {\n if (!this.account) {\n this.breadcrumbs = [];\n return;\n }\n\n // Start with account root (show account name)\n const items: BreadcrumbItem[] = [\n {\n label: this.account.account.Name,\n path: '/'\n }\n ];\n\n // Add path segments if not at root\n if (this.currentPath !== '/') {\n const segments = this.currentPath.split('/').filter(s => s.length > 0);\n let builtPath = '';\n\n for (const segment of segments) {\n builtPath += '/' + segment;\n items.push({\n label: segment,\n path: builtPath\n });\n }\n }\n\n this.breadcrumbs = items;\n }\n\n /**\n * Handles breadcrumb click\n */\n public onBreadcrumbClick(item: BreadcrumbItem): void {\n this.navigateToPath(item.path);\n this.loadFolders();\n }\n\n /**\n * Loads folders from the storage account for the current path\n */\n private async loadFolders(): Promise<void> {\n if (!this.account) {\n this.folders = [];\n return;\n }\n\n this.isLoading = true;\n this.errorMessage = null;\n this.cdr.detectChanges();\n\n try {\n const listResult = await this.storageClient.ListObjects(\n this.account.account.ID,\n this.currentPath === '/' ? '' : this.currentPath,\n '/'\n );\n\n // Convert prefixes to FolderItems\n const prefixes = listResult.prefixes || [];\n this.folders = prefixes.map((prefix: string) => {\n // Remove trailing slash and get just the folder name\n const cleanPath = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;\n const name = cleanPath.split('/').pop() || cleanPath;\n\n return {\n name,\n fullPath: prefix\n };\n });\n } catch (error) {\n console.error('Error loading folders:', error);\n this.errorMessage = error instanceof Error ? error.message : 'Failed to load folders';\n this.folders = [];\n } finally {\n this.isLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Handles folder click for navigation\n */\n public onFolderClick(folder: FolderItem): void {\n this.navigateToPath(folder.fullPath);\n this.loadFolders();\n }\n}\n","<div class=\"folder-tree\">\n <!-- Navigation bar with back/forward buttons and breadcrumbs -->\n @if (account) {\n <div class=\"navigation-bar\">\n <!-- Back/Forward buttons -->\n <div class=\"nav-buttons\">\n <button mjButton\n variant=\"flat\"\n size=\"sm\"\n [disabled]=\"!canGoBack()\"\n (click)=\"navigateBack()\"\n title=\"Go back\"\n class=\"nav-button\">\n <i class=\"fa-solid fa-chevron-left\"></i>\n </button>\n <button mjButton\n variant=\"flat\"\n size=\"sm\"\n [disabled]=\"!canGoForward()\"\n (click)=\"navigateForward()\"\n title=\"Go forward\"\n class=\"nav-button\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n <!-- Breadcrumb path -->\n <div class=\"breadcrumbs\">\n @for (item of breadcrumbs; track item; let last = $last) {\n <button mjButton\n variant=\"flat\"\n size=\"sm\"\n (click)=\"onBreadcrumbClick(item)\"\n [class.current]=\"last\"\n class=\"breadcrumb-item\">\n {{ item.label }}\n </button>\n @if (!last) {\n <i class=\"fa-solid fa-chevron-right breadcrumb-separator\"></i>\n }\n }\n </div>\n </div>\n }\n\n <!-- Folder tree content -->\n <div class=\"tree-content\">\n <!-- Loading state -->\n @if (isLoading) {\n <mj-loading text=\"Loading folders...\"></mj-loading>\n }\n\n <!-- Error state -->\n @if (errorMessage && !isLoading) {\n <div class=\"tree-error\">\n <div class=\"error-icon\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n </div>\n <p class=\"error-text\">{{ errorMessage }}</p>\n </div>\n }\n\n <!-- Folder list -->\n @if (!isLoading && !errorMessage) {\n <div class=\"folder-list\">\n @for (folder of folders; track folder) {\n <div\n class=\"folder-item\"\n (click)=\"onFolderClick(folder)\"\n >\n <i class=\"fa-solid fa-folder folder-icon\"></i>\n <span class=\"folder-name\">{{ folder.name }}</span>\n </div>\n }\n <!-- Empty state -->\n @if (folders.length === 0) {\n <div class=\"tree-placeholder\">\n <div class=\"placeholder-icon\">\n <i class=\"fa-solid fa-folder-open\"></i>\n </div>\n <p class=\"placeholder-text\">No folders in this location</p>\n @if (account) {\n <p class=\"placeholder-hint\">\n Account: {{ account.account.Name }}\n </p>\n }\n <p class=\"placeholder-hint\">\n Path: {{ currentPath }}\n </p>\n </div>\n }\n </div>\n }\n </div>\n</div>\n"]}
1
+ {"version":3,"file":"folder-tree.component.js","sourceRoot":"","sources":["../../../src/lib/file-browser/folder-tree.component.ts","../../../src/lib/file-browser/folder-tree.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAClG,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;AAErG,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;;;;;ICkCxC,wBAA8D;;;;IAThE,kCAK0B;IAFxB,wNAAS,iCAAuB,KAAC;IAGjC,YACF;IAAA,iBAAS;IACT,wGAAa;;;;;IAJX,8DAAsB;IAEtB,cACF;IADE,8CACF;IACA,cAEC;IAFD,iEAEC;;;;IAhCH,AADF,AAFF,8BAA4B,aAED,gBAOF;IAFnB,uLAAS,qBAAc,KAAC;IAGxB,uBAAwC;IAC1C,iBAAS;IACT,iCAMqB;IAFnB,uLAAS,wBAAiB,KAAC;IAG3B,wBAAyC;IAE7C,AADE,iBAAS,EACL;IAEN,+BAAyB;IACvB,wHAYC;IAEL,AADE,iBAAM,EACF;;;IAhCA,eAAyB;IAAzB,8CAAyB;IASzB,eAA4B;IAA5B,iDAA4B;IAS9B,eAYC;IAZD,iCAYC;;;IASH,gCAAmD;;;IAMjD,AADF,8BAAwB,cACE;IACtB,wBAAgD;IAClD,iBAAM;IACN,6BAAsB;IAAA,YAAkB;IAC1C,AAD0C,iBAAI,EACxC;;;IADkB,eAAkB;IAAlB,yCAAkB;;;;IAQtC,+BAGG;IADD,uNAAS,+BAAqB,KAAC;IAE/B,wBAA8C;IAC9C,gCAA0B;IAAA,YAAiB;IAC7C,AAD6C,iBAAO,EAC9C;;;IADsB,eAAiB;IAAjB,oCAAiB;;;IAWzC,6BAA4B;IAC1B,YACF;IAAA,iBAAI;;;IADF,cACF;IADE,qEACF;;;IAPF,AADF,+BAA8B,cACE;IAC5B,wBAAuC;IACzC,iBAAM;IACN,6BAA4B;IAAA,2CAA2B;IAAA,iBAAI;IAC3D,gHAAe;IAKf,6BAA4B;IAC1B,YACF;IACF,AADE,iBAAI,EACA;;;IARJ,eAIC;IAJD,yCAIC;IAEC,eACF;IADE,yDACF;;;IAxBN,8BAAyB;IACvB,uHAQC;IAED,oGAA4B;IAgB9B,iBAAM;;;IA1BJ,cAQC;IARD,6BAQC;IAED,eAeC;IAfD,sDAeC;;ADpET;;;;GAIG;AAOH,MAAM,OAAO,mBAAmB;IAC9B;;OAEG;IACK,aAAa,CAA2B;IAEhD;;OAEG;IACK,QAAQ,GAAsC,IAAI,CAAC;IAE3D;QACE,IAAI,CAAC,aAAa,GAAG,IAAI,wBAAwB,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAClF,CAAC;IAED,IACI,OAAO,CAAC,KAAwC;QAClD,MAAM,eAAe,GAAG,IAAI,CAAC,QAAQ,CAAC;QACtC,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QAEtB,IAAI,KAAK,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;YACxE,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;IACD,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED;;OAEG;IACO,cAAc,GAAG,IAAI,YAAY,EAAU,CAAC;IAEtD;;OAEG;IACI,WAAW,GAAW,GAAG,CAAC;IAEjC;;OAEG;IACI,OAAO,GAAa,EAAE,CAAC;IAE9B;;OAEG;IACI,YAAY,GAAW,CAAC,CAAC,CAAC;IAEjC;;OAEG;IACI,WAAW,GAAqB,EAAE,CAAC;IAE1C;;OAEG;IACI,OAAO,GAAiB,EAAE,CAAC;IAE1B,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAExC;;OAEG;IACI,SAAS,GAAY,KAAK,CAAC;IAElC;;OAEG;IACI,YAAY,GAAkB,IAAI,CAAC;IAE1C;;OAEG;IACK,eAAe;QACrB,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;QACvB,IAAI,CAAC,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAC3B,CAAC;IAED;;OAEG;IACI,cAAc,CAAC,IAAY;QAChC,yCAAyC;QACzC,IAAI,IAAI,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;YAC9B,OAAO;QACT,CAAC;QAED,mEAAmE;QACnE,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACN,+DAA+D;YAC/D,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;YAC5D,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QAC9C,CAAC;QAED,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,IAAI,CAAC,WAAW,EAAE,CAAC;QACnB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED;;;OAGG;IACI,OAAO;QACZ,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAED;;OAEG;IACI,YAAY;QACjB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACnD,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC3C,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAED;;OAEG;IACI,eAAe;QACpB,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;YACzB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACnD,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC3C,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAED;;OAEG;IACI,SAAS;QACd,OAAO,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;IAC/B,CAAC;IAED;;OAEG;IACI,YAAY;QACjB,OAAO,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IACrD,CAAC;IAED;;OAEG;IACK,iBAAiB;QACvB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QAED,8CAA8C;QAC9C,MAAM,KAAK,GAAqB;YAC9B;gBACE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI;gBAChC,IAAI,EAAE,GAAG;aACV;SACF,CAAC;QAEF,mCAAmC;QACnC,IAAI,IAAI,CAAC,WAAW,KAAK,GAAG,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACvE,IAAI,SAAS,GAAG,EAAE,CAAC;YAEnB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,SAAS,IAAI,GAAG,GAAG,OAAO,CAAC;gBAC3B,KAAK,CAAC,IAAI,CAAC;oBACT,KAAK,EAAE,OAAO;oBACd,IAAI,EAAE,SAAS;iBAChB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;IAC3B,CAAC;IAED;;OAEG;IACI,iBAAiB,CAAC,IAAoB;QAC3C,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,WAAW;QACvB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAEzB,IAAI,CAAC;YACH,kFAAkF;YAClF,qFAAqF;YACrF,oFAAoF;YACpF,2EAA2E;YAC3E,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;YACrF,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACxF,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,WAAW,CACrD,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EACvB,UAAU,EACV,GAAG,CACJ,CAAC;YAEF,kCAAkC;YAClC,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,IAAI,EAAE,CAAC;YAC3C,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAc,EAAE,EAAE;gBAC7C,qDAAqD;gBACrD,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;gBACtE,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,SAAS,CAAC;gBAErD,OAAO;oBACL,IAAI;oBACJ,QAAQ,EAAE,MAAM;iBACjB,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;YAC/C,IAAI,CAAC,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB,CAAC;YACtF,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QACpB,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC3B,CAAC;IACH,CAAC;IAED;;OAEG;IACI,aAAa,CAAC,MAAkB;QACrC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;6GA/PU,mBAAmB;6DAAnB,mBAAmB;YChChC,8BAAyB;YAEvB,qFAAe;YA2Cf,8BAA0B;YAExB,4FAAiB;YAKjB,qFAAkC;YAUlC,qFAAmC;YA+BvC,AADE,iBAAM,EACF;;YA3FJ,cAwCC;YAxCD,sCAwCC;YAKC,eAEC;YAFD,wCAEC;YAGD,cAOC;YAPD,6DAOC;YAGD,cA6BC;YA7BD,8DA6BC;;;iFD3DQ,mBAAmB;cAN/B,SAAS;6BACI,KAAK,YACP,gBAAgB;;kBAmBzB,KAAK;;kBAiBL,MAAM;;kFAhCI,mBAAmB","sourcesContent":["import { Component, EventEmitter, Input, Output, ChangeDetectorRef, inject } from '@angular/core';\nimport { GraphQLDataProvider, GraphQLFileStorageClient } from '@memberjunction/graphql-dataprovider';\nimport { StorageAccountWithProvider } from '@memberjunction/core-entities';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Represents a breadcrumb item in the path\n */\nexport interface BreadcrumbItem {\n label: string;\n path: string;\n}\n\n/**\n * Represents a folder in the tree\n */\nexport interface FolderItem {\n name: string;\n fullPath: string;\n}\n\n/**\n * Folder tree navigation component with breadcrumbs and history navigation.\n * Provides Mac Finder-style navigation with back/forward buttons and breadcrumb path.\n * Loads actual folder structure from storage accounts via GraphQL.\n */\n@Component({\n standalone: false,\n selector: 'mj-folder-tree',\n templateUrl: './folder-tree.component.html',\n styleUrls: ['./folder-tree.component.css']\n})\nexport class FolderTreeComponent {\n /**\n * GraphQL client for file storage operations\n */\n private storageClient: GraphQLFileStorageClient;\n\n /**\n * Currently selected storage account with provider details\n */\n private _account: StorageAccountWithProvider | null = null;\n\n constructor() {\n this.storageClient = new GraphQLFileStorageClient(GraphQLDataProvider.Instance);\n }\n\n @Input()\n set account(value: StorageAccountWithProvider | null) {\n const previousAccount = this._account;\n this._account = value;\n\n if (value && !UUIDsEqual(value.account.ID, previousAccount?.account.ID)) {\n this.resetNavigation();\n this.loadFolders();\n }\n }\n get account(): StorageAccountWithProvider | null {\n return this._account;\n }\n\n /**\n * Emits when a folder is selected in the tree\n */\n @Output() folderSelected = new EventEmitter<string>();\n\n /**\n * Current folder path\n */\n public currentPath: string = '/';\n\n /**\n * Navigation history (for back button)\n */\n public history: string[] = [];\n\n /**\n * Current position in history\n */\n public historyIndex: number = -1;\n\n /**\n * Breadcrumb items for current path\n */\n public breadcrumbs: BreadcrumbItem[] = [];\n\n /**\n * Folders in current path\n */\n public folders: FolderItem[] = [];\n\n private cdr = inject(ChangeDetectorRef);\n\n /**\n * Loading state\n */\n public isLoading: boolean = false;\n\n /**\n * Error message\n */\n public errorMessage: string | null = null;\n\n /**\n * Resets navigation when provider changes\n */\n private resetNavigation(): void {\n this.currentPath = '/';\n this.history = ['/'];\n this.historyIndex = 0;\n this.updateBreadcrumbs();\n }\n\n /**\n * Navigates to a specific folder path\n */\n public navigateToPath(path: string): void {\n // Don't navigate if already at this path\n if (path === this.currentPath) {\n return;\n }\n\n // Add to history if navigating from user action (not back/forward)\n if (this.historyIndex === this.history.length - 1) {\n this.history.push(path);\n this.historyIndex = this.history.length - 1;\n } else {\n // Navigating from middle of history - truncate forward history\n this.history = this.history.slice(0, this.historyIndex + 1);\n this.history.push(path);\n this.historyIndex = this.history.length - 1;\n }\n\n this.currentPath = path;\n this.updateBreadcrumbs();\n this.loadFolders();\n this.folderSelected.emit(path);\n }\n\n /**\n * Refreshes the current folder view without changing navigation\n * Used when folder structure changes (e.g., folder deleted) but we're staying in the same location\n */\n public refresh(): void {\n this.loadFolders();\n }\n\n /**\n * Navigates back in history\n */\n public navigateBack(): void {\n if (!this.canGoBack()) {\n return;\n }\n\n this.historyIndex--;\n this.currentPath = this.history[this.historyIndex];\n this.updateBreadcrumbs();\n this.folderSelected.emit(this.currentPath);\n this.loadFolders();\n }\n\n /**\n * Navigates forward in history\n */\n public navigateForward(): void {\n if (!this.canGoForward()) {\n return;\n }\n\n this.historyIndex++;\n this.currentPath = this.history[this.historyIndex];\n this.updateBreadcrumbs();\n this.folderSelected.emit(this.currentPath);\n this.loadFolders();\n }\n\n /**\n * Checks if can navigate back\n */\n public canGoBack(): boolean {\n return this.historyIndex > 0;\n }\n\n /**\n * Checks if can navigate forward\n */\n public canGoForward(): boolean {\n return this.historyIndex < this.history.length - 1;\n }\n\n /**\n * Updates breadcrumbs based on current path\n */\n private updateBreadcrumbs(): void {\n if (!this.account) {\n this.breadcrumbs = [];\n return;\n }\n\n // Start with account root (show account name)\n const items: BreadcrumbItem[] = [\n {\n label: this.account.account.Name,\n path: '/'\n }\n ];\n\n // Add path segments if not at root\n if (this.currentPath !== '/') {\n const segments = this.currentPath.split('/').filter(s => s.length > 0);\n let builtPath = '';\n\n for (const segment of segments) {\n builtPath += '/' + segment;\n items.push({\n label: segment,\n path: builtPath\n });\n }\n }\n\n this.breadcrumbs = items;\n }\n\n /**\n * Handles breadcrumb click\n */\n public onBreadcrumbClick(item: BreadcrumbItem): void {\n this.navigateToPath(item.path);\n this.loadFolders();\n }\n\n /**\n * Loads folders from the storage account for the current path\n */\n private async loadFolders(): Promise<void> {\n if (!this.account) {\n this.folders = [];\n return;\n }\n\n this.isLoading = true;\n this.errorMessage = null;\n this.cdr.detectChanges();\n\n try {\n // Blob keys (Azure/S3) have NO leading slash and use a trailing slash to denote a\n // directory prefix. currentPath is stored with a leading slash (e.g. \"/test\"), which\n // never matches real keys — so nested-folder listings came back empty (empty folder\n // names). Normalize to a slash-free, trailing-slash prefix before listing.\n const rawPath = this.currentPath && this.currentPath !== '/' ? this.currentPath : '';\n const listPrefix = rawPath ? rawPath.replace(/^\\/+/, '').replace(/\\/+$/, '') + '/' : '';\n const listResult = await this.storageClient.ListObjects(\n this.account.account.ID,\n listPrefix,\n '/'\n );\n\n // Convert prefixes to FolderItems\n const prefixes = listResult.prefixes || [];\n this.folders = prefixes.map((prefix: string) => {\n // Remove trailing slash and get just the folder name\n const cleanPath = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;\n const name = cleanPath.split('/').pop() || cleanPath;\n\n return {\n name,\n fullPath: prefix\n };\n });\n } catch (error) {\n console.error('Error loading folders:', error);\n this.errorMessage = error instanceof Error ? error.message : 'Failed to load folders';\n this.folders = [];\n } finally {\n this.isLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Handles folder click for navigation\n */\n public onFolderClick(folder: FolderItem): void {\n this.navigateToPath(folder.fullPath);\n this.loadFolders();\n }\n}\n","<div class=\"folder-tree\">\n <!-- Navigation bar with back/forward buttons and breadcrumbs -->\n @if (account) {\n <div class=\"navigation-bar\">\n <!-- Back/Forward buttons -->\n <div class=\"nav-buttons\">\n <button mjButton\n variant=\"flat\"\n size=\"sm\"\n [disabled]=\"!canGoBack()\"\n (click)=\"navigateBack()\"\n title=\"Go back\"\n class=\"nav-button\">\n <i class=\"fa-solid fa-chevron-left\"></i>\n </button>\n <button mjButton\n variant=\"flat\"\n size=\"sm\"\n [disabled]=\"!canGoForward()\"\n (click)=\"navigateForward()\"\n title=\"Go forward\"\n class=\"nav-button\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n <!-- Breadcrumb path -->\n <div class=\"breadcrumbs\">\n @for (item of breadcrumbs; track item; let last = $last) {\n <button mjButton\n variant=\"flat\"\n size=\"sm\"\n (click)=\"onBreadcrumbClick(item)\"\n [class.current]=\"last\"\n class=\"breadcrumb-item\">\n {{ item.label }}\n </button>\n @if (!last) {\n <i class=\"fa-solid fa-chevron-right breadcrumb-separator\"></i>\n }\n }\n </div>\n </div>\n }\n\n <!-- Folder tree content -->\n <div class=\"tree-content\">\n <!-- Loading state -->\n @if (isLoading) {\n <mj-loading text=\"Loading folders...\"></mj-loading>\n }\n\n <!-- Error state -->\n @if (errorMessage && !isLoading) {\n <div class=\"tree-error\">\n <div class=\"error-icon\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n </div>\n <p class=\"error-text\">{{ errorMessage }}</p>\n </div>\n }\n\n <!-- Folder list -->\n @if (!isLoading && !errorMessage) {\n <div class=\"folder-list\">\n @for (folder of folders; track folder) {\n <div\n class=\"folder-item\"\n (click)=\"onFolderClick(folder)\"\n >\n <i class=\"fa-solid fa-folder folder-icon\"></i>\n <span class=\"folder-name\">{{ folder.name }}</span>\n </div>\n }\n <!-- Empty state -->\n @if (folders.length === 0) {\n <div class=\"tree-placeholder\">\n <div class=\"placeholder-icon\">\n <i class=\"fa-solid fa-folder-open\"></i>\n </div>\n <p class=\"placeholder-text\">No folders in this location</p>\n @if (account) {\n <p class=\"placeholder-hint\">\n Account: {{ account.account.Name }}\n </p>\n }\n <p class=\"placeholder-hint\">\n Path: {{ currentPath }}\n </p>\n </div>\n }\n </div>\n }\n </div>\n</div>\n"]}
@@ -1,11 +1,12 @@
1
1
  import { OnChanges, OnInit, SimpleChanges } from '@angular/core';
2
+ import { MJNotificationService } from '@memberjunction/ng-notifications';
3
+ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
2
4
  import { MJFileEntity } from '@memberjunction/core-entities';
3
- import { SharedService } from '@memberjunction/ng-shared';
4
5
  import { ColDef, GridReadyEvent, type Theme } from 'ag-grid-community';
5
6
  import { FileUploadEvent } from '../file-upload/file-upload';
6
7
  import * as i0 from "@angular/core";
7
- export declare class FilesGridComponent implements OnInit, OnChanges {
8
- private sharedService;
8
+ export declare class FilesGridComponent extends BaseAngularComponent implements OnInit, OnChanges {
9
+ private notifications;
9
10
  files: MJFileEntity[];
10
11
  isLoading: boolean;
11
12
  editFile: MJFileEntity | undefined;
@@ -13,7 +14,7 @@ export declare class FilesGridComponent implements OnInit, OnChanges {
13
14
  ColumnDefs: ColDef[];
14
15
  DefaultColDef: ColDef;
15
16
  private gridApi;
16
- constructor(sharedService: SharedService);
17
+ constructor(notifications: MJNotificationService);
17
18
  CategoryID: string | undefined;
18
19
  ngOnInit(): void;
19
20
  ngOnChanges(changes: SimpleChanges): void;
@@ -29,7 +30,7 @@ export declare class FilesGridComponent implements OnInit, OnChanges {
29
30
  /**
30
31
  * Saves the edited file.
31
32
  *
32
- * This method saves the changes made to the edited file. It first checks if there is an edited file available. If so, it sets the isLoading property to true to indicate that the save operation is in progress. Then, it calls the Save method of the edited file to save the changes. If the save operation is successful, it creates a success notification using the sharedService.CreateSimpleNotification method. The notification message includes the ID and name of the saved file. Finally, it sets the edited file to undefined and sets the isLoading property to false to indicate that the save operation is complete.
33
+ * This method saves the changes made to the edited file. It first checks if there is an edited file available. If so, it sets the isLoading property to true to indicate that the save operation is in progress. Then, it calls the Save method of the edited file to save the changes. If the save operation is successful, it creates a success notification using the MJNotificationService.CreateSimpleNotification method. The notification message includes the ID and name of the saved file. Finally, it sets the edited file to undefined and sets the isLoading property to false to indicate that the save operation is complete.
33
34
  *
34
35
  * @returns Promise<void> - A promise that resolves when the save operation is complete.
35
36
  * @throws Error - If there is an error during the save operation.
@@ -1 +1 @@
1
- {"version":3,"file":"files-grid.d.ts","sourceRoot":"","sources":["../../../src/lib/files-grid/files-grid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnF,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE1D,OAAO,EACL,MAAM,EACN,cAAc,EAKd,KAAK,KAAK,EAEX,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;;AAmD7D,qBAMa,kBAAmB,YAAW,MAAM,EAAE,SAAS;IAiB9C,OAAO,CAAC,aAAa;IAhB1B,KAAK,EAAE,YAAY,EAAE,CAAM;IAC3B,SAAS,EAAE,OAAO,CAAS;IAC3B,QAAQ,EAAE,YAAY,GAAG,SAAS,CAAC;IAKnC,SAAS,EAAE,KAAK,CAAmD;IACnE,UAAU,EAAE,MAAM,EAAE,CAAM;IAC1B,aAAa,EAAE,MAAM,CAI1B;IACF,OAAO,CAAC,OAAO,CAAwB;gBAEnB,aAAa,EAAE,aAAa;IAIvC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAa;IAEpD,QAAQ,IAAI,IAAI;IAIhB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAMlC,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAI/C;;;;;;OAMG;IACI,aAAa;IAKpB;;;;;;;OAOG;IACU,YAAY;IAmBzB;;;;;;OAMG;IACI,YAAY,GAAU,MAAM,YAAY,mBAkB7C;IAEF;;;;;OAKG;IACI,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO;IAMhD;;;;;;OAMG;IACI,UAAU,GAAU,MAAM,YAAY,mBAY3C;IAEF;;;;;OAKG;IACI,gBAAgB,CAAC,CAAC,EAAE,eAAe;IAW1C;;;OAGG;IACG,OAAO;IAiBb;;OAEG;IACH,OAAO,CAAC,eAAe;IAmCvB;;OAEG;IACH,OAAO,CAAC,kBAAkB;yCAvNf,kBAAkB;2CAAlB,kBAAkB;CAgO9B"}
1
+ {"version":3,"file":"files-grid.d.ts","sourceRoot":"","sources":["../../../src/lib/files-grid/files-grid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnF,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAGrE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAG7D,OAAO,EACL,MAAM,EACN,cAAc,EAKd,KAAK,KAAK,EAEX,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;;AAmD7D,qBAMa,kBAAmB,SAAQ,oBAAqB,YAAW,MAAM,EAAE,SAAS;IAiB3E,OAAO,CAAC,aAAa;IAhB1B,KAAK,EAAE,YAAY,EAAE,CAAM;IAC3B,SAAS,EAAE,OAAO,CAAS;IAC3B,QAAQ,EAAE,YAAY,GAAG,SAAS,CAAC;IAKnC,SAAS,EAAE,KAAK,CAAmD;IACnE,UAAU,EAAE,MAAM,EAAE,CAAM;IAC1B,aAAa,EAAE,MAAM,CAI1B;IACF,OAAO,CAAC,OAAO,CAAwB;gBAEnB,aAAa,EAAE,qBAAqB;IAK/C,UAAU,EAAE,MAAM,GAAG,SAAS,CAAa;IAEpD,QAAQ,IAAI,IAAI;IAIhB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAMlC,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAI/C;;;;;;OAMG;IACI,aAAa;IAKpB;;;;;;;OAOG;IACU,YAAY;IAmBzB;;;;;;OAMG;IACI,YAAY,GAAU,MAAM,YAAY,mBAkB7C;IAEF;;;;;OAKG;IACI,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO;IAMhD;;;;;;OAMG;IACI,UAAU,GAAU,MAAM,YAAY,mBAY3C;IAEF;;;;;OAKG;IACI,gBAAgB,CAAC,CAAC,EAAE,eAAe;IAW1C;;;OAGG;IACG,OAAO;IAiBb;;OAEG;IACH,OAAO,CAAC,eAAe;IAmCvB;;OAEG;IACH,OAAO,CAAC,kBAAkB;yCAxNf,kBAAkB;2CAAlB,kBAAkB;CAiO9B"}
@@ -1,11 +1,12 @@
1
1
  import { Component, Input } from '@angular/core';
2
+ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
2
3
  import { RunView } from '@memberjunction/core';
3
4
  import { GraphQLDataProvider, gql } from '@memberjunction/graphql-dataprovider';
4
5
  import { MJ_AG_GRID_THEME_PARAMS } from '@memberjunction/ng-shared-generic';
5
6
  import { ModuleRegistry, AllCommunityModule, themeAlpine } from 'ag-grid-community';
6
7
  import { z } from 'zod';
7
8
  import * as i0 from "@angular/core";
8
- import * as i1 from "@memberjunction/ng-shared";
9
+ import * as i1 from "@memberjunction/ng-notifications";
9
10
  import * as i2 from "@angular/forms";
10
11
  import * as i3 from "ag-grid-angular";
11
12
  import * as i4 from "@memberjunction/ng-shared-generic";
@@ -82,8 +83,8 @@ const FileDownloadQuerySchema = z.object({
82
83
  DownloadUrl: z.string(),
83
84
  }),
84
85
  });
85
- export class FilesGridComponent {
86
- sharedService;
86
+ export class FilesGridComponent extends BaseAngularComponent {
87
+ notifications;
87
88
  files = [];
88
89
  isLoading = false;
89
90
  editFile;
@@ -98,8 +99,9 @@ export class FilesGridComponent {
98
99
  filter: false,
99
100
  };
100
101
  gridApi = null;
101
- constructor(sharedService) {
102
- this.sharedService = sharedService;
102
+ constructor(notifications) {
103
+ super();
104
+ this.notifications = notifications;
103
105
  this.ColumnDefs = this.buildColumnDefs();
104
106
  }
105
107
  CategoryID = undefined;
@@ -128,7 +130,7 @@ export class FilesGridComponent {
128
130
  /**
129
131
  * Saves the edited file.
130
132
  *
131
- * This method saves the changes made to the edited file. It first checks if there is an edited file available. If so, it sets the isLoading property to true to indicate that the save operation is in progress. Then, it calls the Save method of the edited file to save the changes. If the save operation is successful, it creates a success notification using the sharedService.CreateSimpleNotification method. The notification message includes the ID and name of the saved file. Finally, it sets the edited file to undefined and sets the isLoading property to false to indicate that the save operation is complete.
133
+ * This method saves the changes made to the edited file. It first checks if there is an edited file available. If so, it sets the isLoading property to true to indicate that the save operation is in progress. Then, it calls the Save method of the edited file to save the changes. If the save operation is successful, it creates a success notification using the MJNotificationService.CreateSimpleNotification method. The notification message includes the ID and name of the saved file. Finally, it sets the edited file to undefined and sets the isLoading property to false to indicate that the save operation is complete.
132
134
  *
133
135
  * @returns Promise<void> - A promise that resolves when the save operation is complete.
134
136
  * @throws Error - If there is an error during the save operation.
@@ -139,7 +141,7 @@ export class FilesGridComponent {
139
141
  //
140
142
  const success = await this.editFile.Save();
141
143
  if (success) {
142
- this.sharedService.CreateSimpleNotification(`Successfully saved file ${this.editFile.ID} ${this.editFile.Name}`, 'success');
144
+ this.notifications.CreateSimpleNotification(`Successfully saved file ${this.editFile.ID} ${this.editFile.Name}`, 'success');
143
145
  this.editFile = undefined;
144
146
  // Refresh the grid to show the updated data
145
147
  if (this.gridApi) {
@@ -147,7 +149,7 @@ export class FilesGridComponent {
147
149
  }
148
150
  }
149
151
  else {
150
- this.sharedService.CreateSimpleNotification(`Unable to save file ${this.editFile.ID} ${this.editFile.Name}`, 'error');
152
+ this.notifications.CreateSimpleNotification(`Unable to save file ${this.editFile.ID} ${this.editFile.Name}`, 'error');
151
153
  }
152
154
  this.isLoading = false;
153
155
  }
@@ -169,12 +171,12 @@ export class FilesGridComponent {
169
171
  const downloadUrl = parsedResult.data.MJFile.DownloadUrl;
170
172
  const success = downloadFromUrl(downloadUrl, file.Name, file.ContentType);
171
173
  if (!success) {
172
- this.sharedService.CreateSimpleNotification(`Unable to download file ${file.ID} ${file.Name}`, 'error');
174
+ this.notifications.CreateSimpleNotification(`Unable to download file ${file.ID} ${file.Name}`, 'error');
173
175
  }
174
176
  }
175
177
  else {
176
178
  console.error(parsedResult.error);
177
- this.sharedService.CreateSimpleNotification(`Unable to download file ${file.ID} ${file.Name}`, 'error');
179
+ this.notifications.CreateSimpleNotification(`Unable to download file ${file.ID} ${file.Name}`, 'error');
178
180
  }
179
181
  this.isLoading = false;
180
182
  };
@@ -202,11 +204,11 @@ export class FilesGridComponent {
202
204
  const Name = file.Name;
203
205
  const deleteResult = await file.Delete();
204
206
  if (deleteResult) {
205
- this.sharedService.CreateSimpleNotification(`Successfully deleted file ${ID} ${Name}`, 'info');
207
+ this.notifications.CreateSimpleNotification(`Successfully deleted file ${ID} ${Name}`, 'info');
206
208
  this.files = this.files.filter((f) => typeof f.ID === 'string' && f.ID !== ID);
207
209
  }
208
210
  else {
209
- this.sharedService.CreateSimpleNotification(`Unable to delete file ${ID} ${Name}`, 'error');
211
+ this.notifications.CreateSimpleNotification(`Unable to delete file ${ID} ${Name}`, 'error');
210
212
  }
211
213
  this.isLoading = false;
212
214
  };
@@ -218,7 +220,7 @@ export class FilesGridComponent {
218
220
  */
219
221
  handleFileUpload(e) {
220
222
  if (!e.success) {
221
- this.sharedService.CreateSimpleNotification(`Unable to upload file '${e.file.name}'`, 'error');
223
+ this.notifications.CreateSimpleNotification(`Unable to upload file '${e.file.name}'`, 'error');
222
224
  return;
223
225
  }
224
226
  this.files.push(e.file);
@@ -231,7 +233,7 @@ export class FilesGridComponent {
231
233
  */
232
234
  async Refresh() {
233
235
  this.isLoading = true;
234
- const rv = new RunView();
236
+ const rv = RunView.FromMetadataProvider(this.ProviderToUse);
235
237
  const result = await rv.RunView({
236
238
  EntityName: 'MJ: Files',
237
239
  ResultType: 'entity_object',
@@ -289,8 +291,8 @@ export class FilesGridComponent {
289
291
  btn.appendChild(icon);
290
292
  return btn;
291
293
  }
292
- static ɵfac = function FilesGridComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FilesGridComponent)(i0.ɵɵdirectiveInject(i1.SharedService)); };
293
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FilesGridComponent, selectors: [["mj-files-grid"]], inputs: { CategoryID: "CategoryID" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 11, vars: 14, consts: [["templateForm", "ngForm"], [1, "container"], [1, "grid-loading-overlay"], [1, "files-grid", 3, "gridReady", "theme", "rowData", "columnDefs", "defaultColDef", "loading"], [3, "uploadStarted", "fileUpload", "CategoryID", "disabled"], ["Title", "Edit file", 3, "Close", "Visible", "Width", "MinWidth"], [1, "edit-form-container"], ["mjButton", "", "variant", "primary", 3, "click", "disabled"], ["mjButton", "", 3, "click", "disabled"], ["text", "Loading files...", "size", "medium"], [1, "mj-form-field"], [1, "mj-form-label"], ["name", "name", "required", "", 1, "mj-input", 3, "ngModelChange", "ngModel", "disabled"], ["name", "description", 1, "mj-input", 3, "ngModelChange", "ngModel", "disabled"]], template: function FilesGridComponent_Template(rf, ctx) { if (rf & 1) {
294
+ static ɵfac = function FilesGridComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FilesGridComponent)(i0.ɵɵdirectiveInject(i1.MJNotificationService)); };
295
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FilesGridComponent, selectors: [["mj-files-grid"]], inputs: { CategoryID: "CategoryID" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 11, vars: 14, consts: [["templateForm", "ngForm"], [1, "container"], [1, "grid-loading-overlay"], [1, "files-grid", 3, "gridReady", "theme", "rowData", "columnDefs", "defaultColDef", "loading"], [3, "uploadStarted", "fileUpload", "CategoryID", "disabled"], ["Title", "Edit file", 3, "Close", "Visible", "Width", "MinWidth"], [1, "edit-form-container"], ["mjButton", "", "variant", "primary", 3, "click", "disabled"], ["mjButton", "", 3, "click", "disabled"], ["text", "Loading files...", "size", "medium"], [1, "mj-form-field"], [1, "mj-form-label"], ["name", "name", "required", "", 1, "mj-input", 3, "ngModelChange", "ngModel", "disabled"], ["name", "description", 1, "mj-input", 3, "ngModelChange", "ngModel", "disabled"]], template: function FilesGridComponent_Template(rf, ctx) { if (rf & 1) {
294
296
  i0.ɵɵelementStart(0, "div", 1);
295
297
  i0.ɵɵconditionalCreate(1, FilesGridComponent_Conditional_1_Template, 2, 0, "div", 2);
296
298
  i0.ɵɵelementStart(2, "ag-grid-angular", 3);
@@ -330,8 +332,8 @@ export class FilesGridComponent {
330
332
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FilesGridComponent, [{
331
333
  type: Component,
332
334
  args: [{ standalone: false, selector: 'mj-files-grid', template: "<div class=\"container\">\n @if (isLoading) {\n <div class=\"grid-loading-overlay\">\n <mj-loading text=\"Loading files...\" size=\"medium\"></mj-loading>\n </div>\n }\n <ag-grid-angular\n class=\"files-grid\"\n [theme]=\"GridTheme\"\n [rowData]=\"files\"\n [columnDefs]=\"ColumnDefs\"\n [defaultColDef]=\"DefaultColDef\"\n [loading]=\"isLoading\"\n (gridReady)=\"OnGridReady($event)\">\n </ag-grid-angular>\n\n <mj-files-file-upload\n [CategoryID]=\"CategoryID\"\n [disabled]=\"isLoading\"\n (uploadStarted)=\"isLoading = true\"\n (fileUpload)=\"handleFileUpload($event)\"\n ></mj-files-file-upload>\n\n <mj-dialog\n [Visible]=\"!!editFile\"\n Title=\"Edit file\"\n [Width]=\"450\"\n [MinWidth]=\"250\"\n (Close)=\"resetEditFile()\">\n @if (editFile) {\n <div class=\"edit-form-container\">\n <form #templateForm=\"ngForm\">\n <label class=\"mj-form-field\">\n <span class=\"mj-form-label\">Name</span>\n <input\n class=\"mj-input\"\n name=\"name\"\n [(ngModel)]=\"editFile.Name\"\n required\n [disabled]=\"isLoading\" />\n </label>\n <label class=\"mj-form-field\">\n <span class=\"mj-form-label\">Description</span>\n <input\n class=\"mj-input\"\n name=\"description\"\n [(ngModel)]=\"editFile.Description\"\n [disabled]=\"isLoading\" />\n </label>\n </form>\n </div>\n }\n <mj-dialog-actions>\n <button mjButton variant=\"primary\" (click)=\"saveEditFile()\" [disabled]=\"isLoading || !editFile?.Dirty\">Save</button>\n <button mjButton (click)=\"resetEditFile()\" [disabled]=\"isLoading\">Cancel</button>\n </mj-dialog-actions>\n </mj-dialog>\n</div>\n", styles: [".container {\n display: flex;\n flex-direction: column;\n}\n\n.files-grid {\n width: 100%;\n height: 400px;\n}\n\n.grid-loading-overlay {\n display: flex;\n justify-content: center;\n padding: 16px;\n}\n\n.edit-form-container {\n padding: 8px 0;\n}\n\n.mj-form-field {\n display: flex;\n flex-direction: row;\n align-items: center;\n column-gap: 12px;\n margin-bottom: 8px;\n}\n\n.mj-form-label {\n min-width: 80px;\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.mj-form-field .mj-input {\n width: 100%;\n}\n\n.action-buttons {\n display: flex;\n gap: 4px;\n}\n\n.grid-action-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px 6px;\n border-radius: 4px;\n color: var(--mj-text-secondary);\n transition: color 0.15s, background-color 0.15s;\n}\n\n.grid-action-btn:hover:not(:disabled) {\n color: var(--mj-text-primary);\n background-color: var(--mj-bg-surface-hover);\n}\n\n.grid-action-btn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n"] }]
333
- }], () => [{ type: i1.SharedService }], { CategoryID: [{
335
+ }], () => [{ type: i1.MJNotificationService }], { CategoryID: [{
334
336
  type: Input
335
337
  }] }); })();
336
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FilesGridComponent, { className: "FilesGridComponent", filePath: "src/lib/files-grid/files-grid.ts", lineNumber: 76 }); })();
338
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FilesGridComponent, { className: "FilesGridComponent", filePath: "src/lib/files-grid/files-grid.ts", lineNumber: 77 }); })();
337
339
  //# sourceMappingURL=files-grid.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"files-grid.js","sourceRoot":"","sources":["../../../src/lib/files-grid/files-grid.ts","../../../src/lib/files-grid/files-grid.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';\n\nimport { RunView } from '@memberjunction/core';\nimport { MJFileEntity } from '@memberjunction/core-entities';\nimport { GraphQLDataProvider, gql } from '@memberjunction/graphql-dataprovider';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { MJ_AG_GRID_THEME_PARAMS } from '@memberjunction/ng-shared-generic';\nimport {\n ColDef,\n GridReadyEvent,\n GridApi,\n ModuleRegistry,\n AllCommunityModule,\n themeAlpine,\n type Theme,\n ICellRendererParams\n} from 'ag-grid-community';\nimport { z } from 'zod';\nimport { FileUploadEvent } from '../file-upload/file-upload';\n\n// Register AG Grid modules\nModuleRegistry.registerModules([AllCommunityModule]);\n\n/**\n * Downloads a file from the provided URL.\n *\n * @param url - The URL of the file to be downloaded.\n * @param fileName - The name to be given to the downloaded file.\n * @param contentType - The content type of the file. Defaults to 'application/octet-stream'.\n * @returns Promise<void> - A promise that resolves when the file download is complete.\n */\nconst downloadFromUrl = async (url: string, fileName: string, contentType?: string | null) => {\n // First, fetch the data and create a blob with the correct content type\n const response = await fetch(url);\n if (!response.ok) {\n return false;\n }\n const rawBlob = await response.blob();\n const blob = new Blob([rawBlob], { type: contentType || 'application/octet-stream' });\n\n // Then add a temporary link to the document and click it\n const link = document.createElement('a');\n const blobUrl = window.URL.createObjectURL(blob);\n link.href = blobUrl;\n link.setAttribute('download', fileName);\n document.body.appendChild(link);\n link.click();\n link.parentNode?.removeChild(link);\n window.URL.revokeObjectURL(blobUrl);\n\n return true;\n};\n\nconst FileDownloadQuery = gql`\n query FileDownloadUrl($FileID: String!) {\n MJFile(ID: $FileID) {\n ContentType\n DownloadUrl\n }\n }\n`;\n\nconst FileDownloadQuerySchema = z.object({\n MJFile: z.object({\n ContentType: z.string().optional(),\n DownloadUrl: z.string(),\n }),\n});\n\n@Component({\n standalone: false,\n selector: 'mj-files-grid',\n templateUrl: './files-grid.html',\n styleUrls: ['./files-grid.css'],\n})\nexport class FilesGridComponent implements OnInit, OnChanges {\n public files: MJFileEntity[] = [];\n public isLoading: boolean = false;\n public editFile: MJFileEntity | undefined;\n\n // AG Grid configuration\n // Themed from the shared --mj-* token params so this grid follows light/dark and\n // the org brand overlay, matching the main entity/query grids.\n public GridTheme: Theme = themeAlpine.withParams(MJ_AG_GRID_THEME_PARAMS);\n public ColumnDefs: ColDef[] = [];\n public DefaultColDef: ColDef = {\n sortable: true,\n resizable: true,\n filter: false,\n };\n private gridApi: GridApi | null = null;\n\n constructor(private sharedService: SharedService) {\n this.ColumnDefs = this.buildColumnDefs();\n }\n\n @Input() CategoryID: string | undefined = undefined;\n\n ngOnInit(): void {\n this.Refresh();\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['CategoryID']) {\n this.Refresh();\n }\n }\n\n public OnGridReady(event: GridReadyEvent): void {\n this.gridApi = event.api;\n }\n\n /**\n * Resets the edited file.\n *\n * This method reverts any changes made to the edited file by calling the Revert method of the MJFileEntity class. It then sets the editFile property to undefined, indicating that there is no longer an edited file.\n *\n * @returns void\n */\n public resetEditFile() {\n this.editFile?.Revert();\n this.editFile = undefined;\n }\n\n /**\n * Saves the edited file.\n *\n * This method saves the changes made to the edited file. It first checks if there is an edited file available. If so, it sets the isLoading property to true to indicate that the save operation is in progress. Then, it calls the Save method of the edited file to save the changes. If the save operation is successful, it creates a success notification using the sharedService.CreateSimpleNotification method. The notification message includes the ID and name of the saved file. Finally, it sets the edited file to undefined and sets the isLoading property to false to indicate that the save operation is complete.\n *\n * @returns Promise<void> - A promise that resolves when the save operation is complete.\n * @throws Error - If there is an error during the save operation.\n */\n public async saveEditFile() {\n if (this.editFile) {\n this.isLoading = true;\n //\n const success = await this.editFile.Save();\n if (success) {\n this.sharedService.CreateSimpleNotification(`Successfully saved file ${this.editFile.ID} ${this.editFile.Name}`, 'success');\n this.editFile = undefined;\n // Refresh the grid to show the updated data\n if (this.gridApi) {\n this.gridApi.setGridOption('rowData', this.files);\n }\n } else {\n this.sharedService.CreateSimpleNotification(`Unable to save file ${this.editFile.ID} ${this.editFile.Name}`, 'error');\n }\n this.isLoading = false;\n }\n }\n\n /**\n * Downloads a file using the provided MJFileEntity.\n *\n * @param file - The MJFileEntity representing the file to be downloaded.\n * @returns Promise<void> - A promise that resolves when the file download is complete.\n * @throws Error - If there is an error during the file download process.\n */\n public downloadFile = async (file: MJFileEntity) => {\n this.isLoading = true;\n const result = await GraphQLDataProvider.ExecuteGQL(FileDownloadQuery, {\n FileID: file.ID,\n });\n const parsedResult = FileDownloadQuerySchema.safeParse(result);\n\n if (parsedResult.success) {\n const downloadUrl = parsedResult.data.MJFile.DownloadUrl;\n const success = downloadFromUrl(downloadUrl, file.Name, file.ContentType);\n if (!success) {\n this.sharedService.CreateSimpleNotification(`Unable to download file ${file.ID} ${file.Name}`, 'error');\n }\n } else {\n console.error(parsedResult.error);\n this.sharedService.CreateSimpleNotification(`Unable to download file ${file.ID} ${file.Name}`, 'error');\n }\n this.isLoading = false;\n };\n\n /**\n * Determines whether a file can be deleted based on its status and creation time.\n *\n * @param file - The MJFileEntity representing the file to be checked.\n * @returns boolean - True if the file can be deleted, false otherwise.\n */\n public canBeDeleted(file: MJFileEntity): boolean {\n const status = file.Status;\n const deletable = status === 'Uploaded' || Date.now() - +file.__mj_CreatedAt > 10 * 60 * 60;\n return deletable;\n }\n\n /**\n * Deletes a file using the provided MJFileEntity.\n *\n * @param file - The MJFileEntity representing the file to be deleted.\n * @returns Promise<void> - A promise that resolves when the file deletion is complete.\n * @throws Error - If there is an error during the file deletion process.\n */\n public deleteFile = async (file: MJFileEntity) => {\n this.isLoading = true;\n const ID = file.ID;\n const Name = file.Name;\n const deleteResult = await file.Delete();\n if (deleteResult) {\n this.sharedService.CreateSimpleNotification(`Successfully deleted file ${ID} ${Name}`, 'info');\n this.files = this.files.filter((f) => typeof f.ID === 'string' && f.ID !== ID);\n } else {\n this.sharedService.CreateSimpleNotification(`Unable to delete file ${ID} ${Name}`, 'error');\n }\n this.isLoading = false;\n };\n\n /**\n * Handles the file upload event, sending a notification in case of failure and otherwise adding\n * the newly uploaded files to the files currently displayed.\n *\n * @param e - The file upload event.\n */\n public handleFileUpload(e: FileUploadEvent) {\n if (!e.success) {\n this.sharedService.CreateSimpleNotification(`Unable to upload file '${e.file.name}'`, 'error');\n return;\n }\n\n this.files.push(e.file);\n this.files = [...this.files]; // trigger AG Grid row data change\n this.isLoading = false;\n }\n\n /**\n * Refreshes the data by running a view and loading the files.\n * @returns {Promise<void>} - A promise that resolves when the data is refreshed.\n */\n async Refresh() {\n this.isLoading = true;\n\n const rv = new RunView();\n const result = await rv.RunView({\n EntityName: 'MJ: Files',\n ResultType: 'entity_object',\n ...(this.CategoryID !== undefined && { ExtraFilter: `CategoryID='${this.CategoryID}'` }),\n });\n if (result.Success) {\n this.files = <MJFileEntity[]>result.Results ?? [];\n } else {\n throw new Error('Error loading files: ' + result.ErrorMessage);\n }\n this.isLoading = false;\n }\n\n /**\n * Builds AG Grid column definitions from the entity fields.\n */\n private buildColumnDefs(): ColDef[] {\n return [\n { field: 'ID', headerName: 'ID' },\n { field: 'Category', headerName: 'Category' },\n { field: 'Name', headerName: 'Name' },\n { field: 'Description', headerName: 'Description' },\n { field: 'Status', headerName: 'Status' },\n {\n headerName: 'Actions',\n sortable: false,\n resizable: false,\n filter: false,\n cellRenderer: (params: ICellRendererParams) => {\n const container = document.createElement('div');\n container.className = 'action-buttons';\n\n const downloadBtn = this.createActionButton('fa-download', params.data?.Status !== 'Uploaded');\n downloadBtn.addEventListener('click', () => this.downloadFile(params.data));\n\n const deleteBtn = this.createActionButton('fa-trash-can', !this.canBeDeleted(params.data));\n deleteBtn.addEventListener('click', () => this.deleteFile(params.data));\n\n const editBtn = this.createActionButton('fa-pen-to-square', params.data?.Status !== 'Uploaded');\n editBtn.addEventListener('click', () => { this.editFile = params.data; });\n\n container.appendChild(downloadBtn);\n container.appendChild(deleteBtn);\n container.appendChild(editBtn);\n\n return container;\n }\n }\n ];\n }\n\n /**\n * Creates a small action button element for the AG Grid cell renderer.\n */\n private createActionButton(iconClass: string, disabled: boolean): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.className = 'mj-btn mj-btn-flat mj-btn-sm grid-action-btn';\n btn.disabled = disabled;\n const icon = document.createElement('span');\n icon.className = `fa-solid ${iconClass}`;\n btn.appendChild(icon);\n return btn;\n }\n}\n","<div class=\"container\">\n @if (isLoading) {\n <div class=\"grid-loading-overlay\">\n <mj-loading text=\"Loading files...\" size=\"medium\"></mj-loading>\n </div>\n }\n <ag-grid-angular\n class=\"files-grid\"\n [theme]=\"GridTheme\"\n [rowData]=\"files\"\n [columnDefs]=\"ColumnDefs\"\n [defaultColDef]=\"DefaultColDef\"\n [loading]=\"isLoading\"\n (gridReady)=\"OnGridReady($event)\">\n </ag-grid-angular>\n\n <mj-files-file-upload\n [CategoryID]=\"CategoryID\"\n [disabled]=\"isLoading\"\n (uploadStarted)=\"isLoading = true\"\n (fileUpload)=\"handleFileUpload($event)\"\n ></mj-files-file-upload>\n\n <mj-dialog\n [Visible]=\"!!editFile\"\n Title=\"Edit file\"\n [Width]=\"450\"\n [MinWidth]=\"250\"\n (Close)=\"resetEditFile()\">\n @if (editFile) {\n <div class=\"edit-form-container\">\n <form #templateForm=\"ngForm\">\n <label class=\"mj-form-field\">\n <span class=\"mj-form-label\">Name</span>\n <input\n class=\"mj-input\"\n name=\"name\"\n [(ngModel)]=\"editFile.Name\"\n required\n [disabled]=\"isLoading\" />\n </label>\n <label class=\"mj-form-field\">\n <span class=\"mj-form-label\">Description</span>\n <input\n class=\"mj-input\"\n name=\"description\"\n [(ngModel)]=\"editFile.Description\"\n [disabled]=\"isLoading\" />\n </label>\n </form>\n </div>\n }\n <mj-dialog-actions>\n <button mjButton variant=\"primary\" (click)=\"saveEditFile()\" [disabled]=\"isLoading || !editFile?.Dirty\">Save</button>\n <button mjButton (click)=\"resetEditFile()\" [disabled]=\"isLoading\">Cancel</button>\n </mj-dialog-actions>\n </mj-dialog>\n</div>\n"]}
1
+ {"version":3,"file":"files-grid.js","sourceRoot":"","sources":["../../../src/lib/files-grid/files-grid.ts","../../../src/lib/files-grid/files-grid.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAoC,MAAM,eAAe,CAAC;AAEnF,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAErE,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE/C,OAAO,EAAE,mBAAmB,EAAE,GAAG,EAAE,MAAM,sCAAsC,CAAC;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,EAIL,cAAc,EACd,kBAAkB,EAClB,WAAW,EAGZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;;;;;;;;;IChBpB,8BAAkC;IAChC,gCAA+D;IACjE,iBAAM;;;;IA6BE,AADF,AADF,AADF,8BAAiC,oBACF,gBACE,eACC;IAAA,oBAAI;IAAA,iBAAO;IACvC,iCAK2B;IAFzB,oTAA2B;IAG/B,AANE,iBAK2B,EACrB;IAEN,AADF,iCAA6B,eACC;IAAA,2BAAW;IAAA,iBAAO;IAC9C,kCAI2B;IADzB,mUAAkC;IAI1C,AADE,AADE,AALE,iBAI2B,EACrB,EACH,EACH;;;IAbE,eAA2B;IAA3B,oDAA2B;IAE3B,2CAAsB;IAOtB,eAAkC;IAAlC,2DAAkC;IAClC,2CAAsB;;AD1BpC,2BAA2B;AAC3B,cAAc,CAAC,eAAe,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC;AAErD;;;;;;;GAOG;AACH,MAAM,eAAe,GAAG,KAAK,EAAE,GAAW,EAAE,QAAgB,EAAE,WAA2B,EAAE,EAAE;IAC3F,wEAAwE;IACxE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACtC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,WAAW,IAAI,0BAA0B,EAAE,CAAC,CAAC;IAEtF,yDAAyD;IACzD,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACzC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC;IACpB,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACxC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;IACb,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IAEpC,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,GAAG,CAAA;;;;;;;CAO5B,CAAC;AAEF,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAClC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;KACxB,CAAC;CACH,CAAC,CAAC;AAQH,MAAM,OAAO,kBAAmB,SAAQ,oBAAoB;IAiBtC;IAhBb,KAAK,GAAmB,EAAE,CAAC;IAC3B,SAAS,GAAY,KAAK,CAAC;IAC3B,QAAQ,CAA2B;IAE1C,wBAAwB;IACxB,iFAAiF;IACjF,+DAA+D;IACxD,SAAS,GAAU,WAAW,CAAC,UAAU,CAAC,uBAAuB,CAAC,CAAC;IACnE,UAAU,GAAa,EAAE,CAAC;IAC1B,aAAa,GAAW;QAC7B,QAAQ,EAAE,IAAI;QACd,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,KAAK;KACd,CAAC;IACM,OAAO,GAAmB,IAAI,CAAC;IAEvC,YAAoB,aAAoC;QAClD,KAAK,EAAE,CAAC;QADM,kBAAa,GAAb,aAAa,CAAuB;QAEtD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;IAC3C,CAAC;IAEQ,UAAU,GAAuB,SAAS,CAAC;IAEpD,QAAQ;QACN,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IAED,WAAW,CAAC,OAAsB;QAChC,IAAI,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,CAAC;IACH,CAAC;IAEM,WAAW,CAAC,KAAqB;QACtC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC;IAC3B,CAAC;IAED;;;;;;OAMG;IACI,aAAa;QAClB,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;QACxB,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;IAC5B,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAAC,YAAY;QACvB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,EAAE;YACF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;YAC3C,IAAI,OAAO,EAAE,CAAC;gBACZ,IAAI,CAAC,aAAa,CAAC,wBAAwB,CAAC,2BAA2B,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,SAAS,CAAC,CAAC;gBAC5H,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;gBAC1B,4CAA4C;gBAC5C,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;oBACjB,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;gBACpD,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,aAAa,CAAC,wBAAwB,CAAC,uBAAuB,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;YACxH,CAAC;YACD,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACzB,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACI,YAAY,GAAG,KAAK,EAAE,IAAkB,EAAE,EAAE;QACjD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,UAAU,CAAC,iBAAiB,EAAE;YACrE,MAAM,EAAE,IAAI,CAAC,EAAE;SAChB,CAAC,CAAC;QACH,MAAM,YAAY,GAAG,uBAAuB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAE/D,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;YACzB,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;YACzD,MAAM,OAAO,GAAG,eAAe,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YAC1E,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,IAAI,CAAC,aAAa,CAAC,wBAAwB,CAAC,2BAA2B,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;YAC1G,CAAC;QACH,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YAClC,IAAI,CAAC,aAAa,CAAC,wBAAwB,CAAC,2BAA2B,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;QAC1G,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IACzB,CAAC,CAAC;IAEF;;;;;OAKG;IACI,YAAY,CAAC,IAAkB;QACpC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,MAAM,SAAS,GAAG,MAAM,KAAK,UAAU,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;QAC5F,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;;;;;OAMG;IACI,UAAU,GAAG,KAAK,EAAE,IAAkB,EAAE,EAAE;QAC/C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACzC,IAAI,YAAY,EAAE,CAAC;YACjB,IAAI,CAAC,aAAa,CAAC,wBAAwB,CAAC,6BAA6B,EAAE,IAAI,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC;YAC/F,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACjF,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,aAAa,CAAC,wBAAwB,CAAC,yBAAyB,EAAE,IAAI,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;QAC9F,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IACzB,CAAC,CAAC;IAEF;;;;;OAKG;IACI,gBAAgB,CAAC,CAAkB;QACxC,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;YACf,IAAI,CAAC,aAAa,CAAC,wBAAwB,CAAC,0BAA0B,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,EAAE,OAAO,CAAC,CAAC;YAC/F,OAAO;QACT,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACxB,IAAI,CAAC,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,kCAAkC;QAChE,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IACzB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QAEtB,MAAM,EAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC;YAC9B,UAAU,EAAE,WAAW;YACvB,UAAU,EAAE,eAAe;YAC3B,GAAG,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,eAAe,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;SACzF,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,IAAI,CAAC,KAAK,GAAmB,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,uBAAuB,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;QACjE,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IACzB,CAAC;IAED;;OAEG;IACK,eAAe;QACrB,OAAO;YACL,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE;YACjC,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE;YAC7C,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE;YACrC,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE;YACnD,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE;YACzC;gBACE,UAAU,EAAE,SAAS;gBACrB,QAAQ,EAAE,KAAK;gBACf,SAAS,EAAE,KAAK;gBAChB,MAAM,EAAE,KAAK;gBACb,YAAY,EAAE,CAAC,MAA2B,EAAE,EAAE;oBAC5C,MAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;oBAChD,SAAS,CAAC,SAAS,GAAG,gBAAgB,CAAC;oBAEvC,MAAM,WAAW,GAAG,IAAI,CAAC,kBAAkB,CAAC,aAAa,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,KAAK,UAAU,CAAC,CAAC;oBAC/F,WAAW,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;oBAE5E,MAAM,SAAS,GAAG,IAAI,CAAC,kBAAkB,CAAC,cAAc,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;oBAC3F,SAAS,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;oBAExE,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,CAAC,kBAAkB,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,KAAK,UAAU,CAAC,CAAC;oBAChG,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;oBAE1E,SAAS,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;oBACnC,SAAS,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;oBACjC,SAAS,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;oBAE/B,OAAO,SAAS,CAAC;gBACnB,CAAC;aACF;SACF,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,kBAAkB,CAAC,SAAiB,EAAE,QAAiB;QAC7D,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC7C,GAAG,CAAC,SAAS,GAAG,8CAA8C,CAAC;QAC/D,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACxB,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,SAAS,GAAG,YAAY,SAAS,EAAE,CAAC;QACzC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACtB,OAAO,GAAG,CAAC;IACb,CAAC;4GAhOU,kBAAkB;6DAAlB,kBAAkB;YC5E/B,8BAAuB;YACrB,oFAAiB;YAKjB,0CAOoC;YAAlC,sHAAa,uBAAmB,IAAC;YACnC,iBAAkB;YAElB,+CAKC;YADC,AADA,6IAA6B,IAAI,IAAC,gHACpB,4BAAwB,IAAC;YACxC,iBAAuB;YAExB,oCAK4B;YAA1B,kGAAS,mBAAe,IAAC;YACzB,qFAAgB;YAwBd,AADF,yCAAmB,gBACsF;YAApE,+FAAS,kBAAc,IAAC;YAA4C,oBAAI;YAAA,iBAAS;YACpH,iCAAkE;YAAjD,+FAAS,mBAAe,IAAC;YAAwB,uBAAM;YAG9E,AADE,AADE,AAD0E,iBAAS,EAC/D,EACV,EACR;;YAxDJ,cAIC;YAJD,wCAIC;YAGC,cAAmB;YAInB,AADA,AADA,AADA,AADA,qCAAmB,sBACF,8BACQ,oCACM,0BACV;YAKrB,cAAyB;YACzB,AADA,2CAAyB,2BACH;YAMtB,cAAsB;YAGtB,AADA,AAFA,wCAAsB,cAET,iBACG;YAEhB,cAsBC;YAtBD,uCAsBC;YAE6D,eAA0C;YAA1C,+FAA0C;YAC3D,eAAsB;YAAtB,wCAAsB;;;iFDsB1D,kBAAkB;cAN9B,SAAS;6BACI,KAAK,YACP,eAAe;;kBA0BxB,KAAK;;kFAtBK,kBAAkB","sourcesContent":["import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';\nimport { MJNotificationService } from '@memberjunction/ng-notifications';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\n\nimport { RunView } from '@memberjunction/core';\nimport { MJFileEntity } from '@memberjunction/core-entities';\nimport { GraphQLDataProvider, gql } from '@memberjunction/graphql-dataprovider';\nimport { MJ_AG_GRID_THEME_PARAMS } from '@memberjunction/ng-shared-generic';\nimport {\n ColDef,\n GridReadyEvent,\n GridApi,\n ModuleRegistry,\n AllCommunityModule,\n themeAlpine,\n type Theme,\n ICellRendererParams\n} from 'ag-grid-community';\nimport { z } from 'zod';\nimport { FileUploadEvent } from '../file-upload/file-upload';\n\n// Register AG Grid modules\nModuleRegistry.registerModules([AllCommunityModule]);\n\n/**\n * Downloads a file from the provided URL.\n *\n * @param url - The URL of the file to be downloaded.\n * @param fileName - The name to be given to the downloaded file.\n * @param contentType - The content type of the file. Defaults to 'application/octet-stream'.\n * @returns Promise<void> - A promise that resolves when the file download is complete.\n */\nconst downloadFromUrl = async (url: string, fileName: string, contentType?: string | null) => {\n // First, fetch the data and create a blob with the correct content type\n const response = await fetch(url);\n if (!response.ok) {\n return false;\n }\n const rawBlob = await response.blob();\n const blob = new Blob([rawBlob], { type: contentType || 'application/octet-stream' });\n\n // Then add a temporary link to the document and click it\n const link = document.createElement('a');\n const blobUrl = window.URL.createObjectURL(blob);\n link.href = blobUrl;\n link.setAttribute('download', fileName);\n document.body.appendChild(link);\n link.click();\n link.parentNode?.removeChild(link);\n window.URL.revokeObjectURL(blobUrl);\n\n return true;\n};\n\nconst FileDownloadQuery = gql`\n query FileDownloadUrl($FileID: String!) {\n MJFile(ID: $FileID) {\n ContentType\n DownloadUrl\n }\n }\n`;\n\nconst FileDownloadQuerySchema = z.object({\n MJFile: z.object({\n ContentType: z.string().optional(),\n DownloadUrl: z.string(),\n }),\n});\n\n@Component({\n standalone: false,\n selector: 'mj-files-grid',\n templateUrl: './files-grid.html',\n styleUrls: ['./files-grid.css'],\n})\nexport class FilesGridComponent extends BaseAngularComponent implements OnInit, OnChanges {\n public files: MJFileEntity[] = [];\n public isLoading: boolean = false;\n public editFile: MJFileEntity | undefined;\n\n // AG Grid configuration\n // Themed from the shared --mj-* token params so this grid follows light/dark and\n // the org brand overlay, matching the main entity/query grids.\n public GridTheme: Theme = themeAlpine.withParams(MJ_AG_GRID_THEME_PARAMS);\n public ColumnDefs: ColDef[] = [];\n public DefaultColDef: ColDef = {\n sortable: true,\n resizable: true,\n filter: false,\n };\n private gridApi: GridApi | null = null;\n\n constructor(private notifications: MJNotificationService) {\n super();\n this.ColumnDefs = this.buildColumnDefs();\n }\n\n @Input() CategoryID: string | undefined = undefined;\n\n ngOnInit(): void {\n this.Refresh();\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['CategoryID']) {\n this.Refresh();\n }\n }\n\n public OnGridReady(event: GridReadyEvent): void {\n this.gridApi = event.api;\n }\n\n /**\n * Resets the edited file.\n *\n * This method reverts any changes made to the edited file by calling the Revert method of the MJFileEntity class. It then sets the editFile property to undefined, indicating that there is no longer an edited file.\n *\n * @returns void\n */\n public resetEditFile() {\n this.editFile?.Revert();\n this.editFile = undefined;\n }\n\n /**\n * Saves the edited file.\n *\n * This method saves the changes made to the edited file. It first checks if there is an edited file available. If so, it sets the isLoading property to true to indicate that the save operation is in progress. Then, it calls the Save method of the edited file to save the changes. If the save operation is successful, it creates a success notification using the MJNotificationService.CreateSimpleNotification method. The notification message includes the ID and name of the saved file. Finally, it sets the edited file to undefined and sets the isLoading property to false to indicate that the save operation is complete.\n *\n * @returns Promise<void> - A promise that resolves when the save operation is complete.\n * @throws Error - If there is an error during the save operation.\n */\n public async saveEditFile() {\n if (this.editFile) {\n this.isLoading = true;\n //\n const success = await this.editFile.Save();\n if (success) {\n this.notifications.CreateSimpleNotification(`Successfully saved file ${this.editFile.ID} ${this.editFile.Name}`, 'success');\n this.editFile = undefined;\n // Refresh the grid to show the updated data\n if (this.gridApi) {\n this.gridApi.setGridOption('rowData', this.files);\n }\n } else {\n this.notifications.CreateSimpleNotification(`Unable to save file ${this.editFile.ID} ${this.editFile.Name}`, 'error');\n }\n this.isLoading = false;\n }\n }\n\n /**\n * Downloads a file using the provided MJFileEntity.\n *\n * @param file - The MJFileEntity representing the file to be downloaded.\n * @returns Promise<void> - A promise that resolves when the file download is complete.\n * @throws Error - If there is an error during the file download process.\n */\n public downloadFile = async (file: MJFileEntity) => {\n this.isLoading = true;\n const result = await GraphQLDataProvider.ExecuteGQL(FileDownloadQuery, {\n FileID: file.ID,\n });\n const parsedResult = FileDownloadQuerySchema.safeParse(result);\n\n if (parsedResult.success) {\n const downloadUrl = parsedResult.data.MJFile.DownloadUrl;\n const success = downloadFromUrl(downloadUrl, file.Name, file.ContentType);\n if (!success) {\n this.notifications.CreateSimpleNotification(`Unable to download file ${file.ID} ${file.Name}`, 'error');\n }\n } else {\n console.error(parsedResult.error);\n this.notifications.CreateSimpleNotification(`Unable to download file ${file.ID} ${file.Name}`, 'error');\n }\n this.isLoading = false;\n };\n\n /**\n * Determines whether a file can be deleted based on its status and creation time.\n *\n * @param file - The MJFileEntity representing the file to be checked.\n * @returns boolean - True if the file can be deleted, false otherwise.\n */\n public canBeDeleted(file: MJFileEntity): boolean {\n const status = file.Status;\n const deletable = status === 'Uploaded' || Date.now() - +file.__mj_CreatedAt > 10 * 60 * 60;\n return deletable;\n }\n\n /**\n * Deletes a file using the provided MJFileEntity.\n *\n * @param file - The MJFileEntity representing the file to be deleted.\n * @returns Promise<void> - A promise that resolves when the file deletion is complete.\n * @throws Error - If there is an error during the file deletion process.\n */\n public deleteFile = async (file: MJFileEntity) => {\n this.isLoading = true;\n const ID = file.ID;\n const Name = file.Name;\n const deleteResult = await file.Delete();\n if (deleteResult) {\n this.notifications.CreateSimpleNotification(`Successfully deleted file ${ID} ${Name}`, 'info');\n this.files = this.files.filter((f) => typeof f.ID === 'string' && f.ID !== ID);\n } else {\n this.notifications.CreateSimpleNotification(`Unable to delete file ${ID} ${Name}`, 'error');\n }\n this.isLoading = false;\n };\n\n /**\n * Handles the file upload event, sending a notification in case of failure and otherwise adding\n * the newly uploaded files to the files currently displayed.\n *\n * @param e - The file upload event.\n */\n public handleFileUpload(e: FileUploadEvent) {\n if (!e.success) {\n this.notifications.CreateSimpleNotification(`Unable to upload file '${e.file.name}'`, 'error');\n return;\n }\n\n this.files.push(e.file);\n this.files = [...this.files]; // trigger AG Grid row data change\n this.isLoading = false;\n }\n\n /**\n * Refreshes the data by running a view and loading the files.\n * @returns {Promise<void>} - A promise that resolves when the data is refreshed.\n */\n async Refresh() {\n this.isLoading = true;\n\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const result = await rv.RunView({\n EntityName: 'MJ: Files',\n ResultType: 'entity_object',\n ...(this.CategoryID !== undefined && { ExtraFilter: `CategoryID='${this.CategoryID}'` }),\n });\n if (result.Success) {\n this.files = <MJFileEntity[]>result.Results ?? [];\n } else {\n throw new Error('Error loading files: ' + result.ErrorMessage);\n }\n this.isLoading = false;\n }\n\n /**\n * Builds AG Grid column definitions from the entity fields.\n */\n private buildColumnDefs(): ColDef[] {\n return [\n { field: 'ID', headerName: 'ID' },\n { field: 'Category', headerName: 'Category' },\n { field: 'Name', headerName: 'Name' },\n { field: 'Description', headerName: 'Description' },\n { field: 'Status', headerName: 'Status' },\n {\n headerName: 'Actions',\n sortable: false,\n resizable: false,\n filter: false,\n cellRenderer: (params: ICellRendererParams) => {\n const container = document.createElement('div');\n container.className = 'action-buttons';\n\n const downloadBtn = this.createActionButton('fa-download', params.data?.Status !== 'Uploaded');\n downloadBtn.addEventListener('click', () => this.downloadFile(params.data));\n\n const deleteBtn = this.createActionButton('fa-trash-can', !this.canBeDeleted(params.data));\n deleteBtn.addEventListener('click', () => this.deleteFile(params.data));\n\n const editBtn = this.createActionButton('fa-pen-to-square', params.data?.Status !== 'Uploaded');\n editBtn.addEventListener('click', () => { this.editFile = params.data; });\n\n container.appendChild(downloadBtn);\n container.appendChild(deleteBtn);\n container.appendChild(editBtn);\n\n return container;\n }\n }\n ];\n }\n\n /**\n * Creates a small action button element for the AG Grid cell renderer.\n */\n private createActionButton(iconClass: string, disabled: boolean): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.className = 'mj-btn mj-btn-flat mj-btn-sm grid-action-btn';\n btn.disabled = disabled;\n const icon = document.createElement('span');\n icon.className = `fa-solid ${iconClass}`;\n btn.appendChild(icon);\n return btn;\n }\n}\n","<div class=\"container\">\n @if (isLoading) {\n <div class=\"grid-loading-overlay\">\n <mj-loading text=\"Loading files...\" size=\"medium\"></mj-loading>\n </div>\n }\n <ag-grid-angular\n class=\"files-grid\"\n [theme]=\"GridTheme\"\n [rowData]=\"files\"\n [columnDefs]=\"ColumnDefs\"\n [defaultColDef]=\"DefaultColDef\"\n [loading]=\"isLoading\"\n (gridReady)=\"OnGridReady($event)\">\n </ag-grid-angular>\n\n <mj-files-file-upload\n [CategoryID]=\"CategoryID\"\n [disabled]=\"isLoading\"\n (uploadStarted)=\"isLoading = true\"\n (fileUpload)=\"handleFileUpload($event)\"\n ></mj-files-file-upload>\n\n <mj-dialog\n [Visible]=\"!!editFile\"\n Title=\"Edit file\"\n [Width]=\"450\"\n [MinWidth]=\"250\"\n (Close)=\"resetEditFile()\">\n @if (editFile) {\n <div class=\"edit-form-container\">\n <form #templateForm=\"ngForm\">\n <label class=\"mj-form-field\">\n <span class=\"mj-form-label\">Name</span>\n <input\n class=\"mj-input\"\n name=\"name\"\n [(ngModel)]=\"editFile.Name\"\n required\n [disabled]=\"isLoading\" />\n </label>\n <label class=\"mj-form-field\">\n <span class=\"mj-form-label\">Description</span>\n <input\n class=\"mj-input\"\n name=\"description\"\n [(ngModel)]=\"editFile.Description\"\n [disabled]=\"isLoading\" />\n </label>\n </form>\n </div>\n }\n <mj-dialog-actions>\n <button mjButton variant=\"primary\" (click)=\"saveEditFile()\" [disabled]=\"isLoading || !editFile?.Dirty\">Save</button>\n <button mjButton (click)=\"resetEditFile()\" [disabled]=\"isLoading\">Cancel</button>\n </mj-dialog-actions>\n </mj-dialog>\n</div>\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-file-storage",
3
- "version": "5.51.0",
3
+ "version": "6.1.0-edge.1",
4
4
  "description": "MemberJunction: Angular components for managing files, and related components.",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -17,38 +17,32 @@
17
17
  "files": [
18
18
  "/dist"
19
19
  ],
20
- "scripts": {
21
- "test": "vitest run",
22
- "build": "ngc",
23
- "watch": "ngc -w",
24
- "test:watch": "vitest"
25
- },
26
20
  "keywords": [],
27
21
  "author": "",
28
22
  "license": "ISC",
29
23
  "devDependencies": {
30
24
  "@angular/compiler": "21.1.3",
31
25
  "@angular/compiler-cli": "21.1.3",
32
- "@memberjunction/ng-test-utils": "5.51.0",
26
+ "@memberjunction/ng-test-utils": "6.1.0-edge.1",
33
27
  "vitest": "^4.0.18"
34
28
  },
35
29
  "peerDependencies": {
36
- "@angular/common": "21.1.3",
37
- "@angular/core": "21.1.3",
38
- "@angular/forms": "21.1.3",
39
- "@angular/router": "21.1.3"
30
+ "@angular/common": "^21.1.3",
31
+ "@angular/core": "^21.1.3",
32
+ "@angular/forms": "^21.1.3"
40
33
  },
41
34
  "dependencies": {
42
- "@memberjunction/ng-ui-components": "5.51.0",
43
35
  "@angular/platform-browser": "21.1.3",
44
- "@memberjunction/core": "5.51.0",
45
- "@memberjunction/ng-base-types": "5.51.0",
46
- "@memberjunction/core-entities": "5.51.0",
47
- "@memberjunction/global": "5.51.0",
48
- "@memberjunction/graphql-dataprovider": "5.51.0",
49
- "@memberjunction/ng-container-directives": "5.51.0",
50
- "@memberjunction/ng-shared": "5.51.0",
51
- "@memberjunction/ng-shared-generic": "5.51.0",
36
+ "@memberjunction/core": "6.1.0-edge.1",
37
+ "@memberjunction/core-entities": "6.1.0-edge.1",
38
+ "@memberjunction/global": "6.1.0-edge.1",
39
+ "@memberjunction/graphql-dataprovider": "6.1.0-edge.1",
40
+ "@memberjunction/ng-base-types": "6.1.0-edge.1",
41
+ "@memberjunction/ng-container-directives": "6.1.0-edge.1",
42
+ "@memberjunction/ng-notifications": "6.1.0-edge.1",
43
+ "@memberjunction/ng-shared": "6.1.0-edge.1",
44
+ "@memberjunction/ng-shared-generic": "6.1.0-edge.1",
45
+ "@memberjunction/ng-ui-components": "6.1.0-edge.1",
52
46
  "ag-grid-angular": "^35.0.1",
53
47
  "ag-grid-community": "^35.0.1",
54
48
  "tslib": "^2.8.1",
@@ -58,5 +52,13 @@
58
52
  "repository": {
59
53
  "type": "git",
60
54
  "url": "https://github.com/MemberJunction/MJ"
55
+ },
56
+ "mjUILayer": "surface",
57
+ "scripts": {
58
+ "test": "vitest run",
59
+ "test:types": "tsc --noEmit -p tsconfig.spec.json",
60
+ "build": "ngc",
61
+ "watch": "ngc -w",
62
+ "test:watch": "vitest"
61
63
  }
62
- }
64
+ }