@memberjunction/ng-file-storage 5.23.0 → 5.25.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.
- package/dist/lib/file-browser/file-browser-resource.component.d.ts.map +1 -1
- package/dist/lib/file-browser/file-browser-resource.component.js +4 -10
- package/dist/lib/file-browser/file-browser-resource.component.js.map +1 -1
- package/dist/lib/file-browser/file-grid.component.d.ts +1 -0
- package/dist/lib/file-browser/file-grid.component.d.ts.map +1 -1
- package/dist/lib/file-browser/file-grid.component.js +7 -4
- package/dist/lib/file-browser/file-grid.component.js.map +1 -1
- package/dist/lib/file-browser/folder-tree.component.d.ts +1 -0
- package/dist/lib/file-browser/folder-tree.component.d.ts.map +1 -1
- package/dist/lib/file-browser/folder-tree.component.js +4 -7
- package/dist/lib/file-browser/folder-tree.component.js.map +1 -1
- package/dist/lib/file-browser/storage-providers-list.component.d.ts +1 -1
- package/dist/lib/file-browser/storage-providers-list.component.d.ts.map +1 -1
- package/dist/lib/file-browser/storage-providers-list.component.js +6 -5
- package/dist/lib/file-browser/storage-providers-list.component.js.map +1 -1
- package/dist/lib/file-open.service.d.ts +41 -0
- package/dist/lib/file-open.service.d.ts.map +1 -0
- package/dist/lib/file-open.service.js +116 -0
- package/dist/lib/file-open.service.js.map +1 -0
- package/dist/lib/file-upload/file-upload.d.ts.map +1 -1
- package/dist/lib/file-upload/file-upload.js +7 -5
- package/dist/lib/file-upload/file-upload.js.map +1 -1
- package/dist/public-api.d.ts +1 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +1 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +9 -9
|
@@ -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,
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
|
1
|
+
import { Component, EventEmitter, Input, Output, ChangeDetectorRef, inject } from '@angular/core';
|
|
2
2
|
import { GraphQLDataProvider, GraphQLFileStorageClient } from '@memberjunction/graphql-dataprovider';
|
|
3
3
|
import { UUIDsEqual } from '@memberjunction/global';
|
|
4
4
|
import * as i0 from "@angular/core";
|
|
@@ -165,6 +165,7 @@ export class FolderTreeComponent {
|
|
|
165
165
|
* Folders in current path
|
|
166
166
|
*/
|
|
167
167
|
folders = [];
|
|
168
|
+
cdr = inject(ChangeDetectorRef);
|
|
168
169
|
/**
|
|
169
170
|
* Loading state
|
|
170
171
|
*/
|
|
@@ -297,16 +298,11 @@ export class FolderTreeComponent {
|
|
|
297
298
|
}
|
|
298
299
|
this.isLoading = true;
|
|
299
300
|
this.errorMessage = null;
|
|
301
|
+
this.cdr.detectChanges();
|
|
300
302
|
try {
|
|
301
303
|
const listResult = await this.storageClient.ListObjects(this.account.account.ID, this.currentPath === '/' ? '' : this.currentPath, '/');
|
|
302
|
-
console.log('[FolderTree] ListObjects result:', {
|
|
303
|
-
prefixesCount: listResult.prefixes?.length || 0,
|
|
304
|
-
prefixes: listResult.prefixes,
|
|
305
|
-
objectsCount: listResult.objects?.length || 0
|
|
306
|
-
});
|
|
307
304
|
// Convert prefixes to FolderItems
|
|
308
305
|
const prefixes = listResult.prefixes || [];
|
|
309
|
-
console.log('[FolderTree] Processing prefixes:', prefixes);
|
|
310
306
|
this.folders = prefixes.map((prefix) => {
|
|
311
307
|
// Remove trailing slash and get just the folder name
|
|
312
308
|
const cleanPath = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
|
|
@@ -324,6 +320,7 @@ export class FolderTreeComponent {
|
|
|
324
320
|
}
|
|
325
321
|
finally {
|
|
326
322
|
this.isLoading = false;
|
|
323
|
+
this.cdr.detectChanges();
|
|
327
324
|
}
|
|
328
325
|
}
|
|
329
326
|
/**
|
|
@@ -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 } 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 /**\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\n try {\n const listResult = await this.storageClient.ListObjects(\n this.account.account.ID,\n this.currentPath === '/' ? '' : this.currentPath,\n '/'\n );\n\n console.log('[FolderTree] ListObjects result:', {\n prefixesCount: listResult.prefixes?.length || 0,\n prefixes: listResult.prefixes,\n objectsCount: listResult.objects?.length || 0\n });\n\n // Convert prefixes to FolderItems\n const prefixes = listResult.prefixes || [];\n console.log('[FolderTree] Processing prefixes:', 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 }\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,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,WAAW,CACrD,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EACvB,IAAI,CAAC,WAAW,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAChD,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;6GAzPU,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 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"]}
|
|
@@ -34,7 +34,7 @@ export declare class StorageProvidersListComponent implements OnInit {
|
|
|
34
34
|
ngOnInit(): void;
|
|
35
35
|
/**
|
|
36
36
|
* Loads all available file storage accounts with their provider details.
|
|
37
|
-
* Uses
|
|
37
|
+
* Uses FileStorageEngineBase for centralized, cached access.
|
|
38
38
|
*/
|
|
39
39
|
private loadAccounts;
|
|
40
40
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-providers-list.component.d.ts","sourceRoot":"","sources":["../../../src/lib/file-browser/storage-providers-list.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAE,MAAM,EAAU,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,
|
|
1
|
+
{"version":3,"file":"storage-providers-list.component.d.ts","sourceRoot":"","sources":["../../../src/lib/file-browser/storage-providers-list.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAE,MAAM,EAAU,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,EAAyB,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;;AAGlG;;;;;GAKG;AACH,qBAMa,6BAA8B,YAAW,MAAM;IA2B9C,OAAO,CAAC,GAAG;IA1BvB;;;OAGG;IACO,eAAe,kDAAyD;IAElF;;OAEG;IACI,QAAQ,EAAE,0BAA0B,EAAE,CAAM;IAEnD;;OAEG;IACI,eAAe,EAAE,0BAA0B,GAAG,IAAI,CAAQ;IAEjE;;OAEG;IACI,SAAS,EAAE,OAAO,CAAS;IAElC;;OAEG;IACI,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;gBAEtB,GAAG,EAAE,iBAAiB;IAE1C,QAAQ,IAAI,IAAI;IAIhB;;;OAGG;YACW,YAAY;IAiC1B;;OAEG;IACI,aAAa,CAAC,mBAAmB,EAAE,0BAA0B,GAAG,IAAI;IAK3E;;OAEG;IACI,UAAU,CAAC,mBAAmB,EAAE,0BAA0B,GAAG,OAAO;IAI3E;;OAEG;IACI,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAsBpD;;OAEG;IACI,OAAO,IAAI,IAAI;yCAjHX,6BAA6B;2CAA7B,6BAA6B;CAoHzC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Component, EventEmitter, Output } from '@angular/core';
|
|
2
|
-
import {
|
|
2
|
+
import { FileStorageEngineBase } from '@memberjunction/core-entities';
|
|
3
3
|
import { UUIDsEqual } from '@memberjunction/global';
|
|
4
4
|
import * as i0 from "@angular/core";
|
|
5
5
|
import * as i1 from "@memberjunction/ng-shared-generic";
|
|
@@ -129,15 +129,16 @@ export class StorageProvidersListComponent {
|
|
|
129
129
|
}
|
|
130
130
|
/**
|
|
131
131
|
* Loads all available file storage accounts with their provider details.
|
|
132
|
-
* Uses
|
|
132
|
+
* Uses FileStorageEngineBase for centralized, cached access.
|
|
133
133
|
*/
|
|
134
134
|
async loadAccounts() {
|
|
135
135
|
this.isLoading = true;
|
|
136
136
|
this.errorMessage = null;
|
|
137
137
|
try {
|
|
138
|
-
const engine =
|
|
138
|
+
const engine = FileStorageEngineBase.Instance;
|
|
139
139
|
await engine.Config(false); // Use cached data if available
|
|
140
|
-
|
|
140
|
+
// Only show accounts whose provider is active
|
|
141
|
+
this.accounts = engine.AccountsWithProviders.filter(a => a.provider.IsActive);
|
|
141
142
|
console.log('[StorageAccountsList] Loaded accounts:', this.accounts.map(a => ({
|
|
142
143
|
name: a.account.Name,
|
|
143
144
|
provider: a.provider.Name,
|
|
@@ -207,7 +208,7 @@ export class StorageProvidersListComponent {
|
|
|
207
208
|
* Refreshes the accounts list by forcing a reload from the database.
|
|
208
209
|
*/
|
|
209
210
|
refresh() {
|
|
210
|
-
|
|
211
|
+
FileStorageEngineBase.Instance.Config(true).then(() => this.loadAccounts());
|
|
211
212
|
}
|
|
212
213
|
static ɵfac = function StorageProvidersListComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || StorageProvidersListComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
|
|
213
214
|
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: StorageProvidersListComponent, selectors: [["mj-storage-providers-list"]], outputs: { accountSelected: "accountSelected" }, standalone: false, decls: 8, vars: 4, consts: [[1, "providers-list"], [1, "providers-header"], [1, "header-title"], [1, "loading-state"], [1, "error-state"], [1, "empty-state"], [1, "providers-container"], ["text", "Loading accounts..."], [1, "error-icon"], [1, "fa-solid", "fa-triangle-exclamation"], [1, "error-message"], [1, "empty-icon"], [1, "fa-solid", "fa-folder-plus"], [1, "empty-message"], [1, "empty-hint"], [1, "provider-item", 3, "selected", "title"], [1, "provider-item", 3, "click", "title"], [1, "provider-icon"], [1, "provider-info"], [1, "provider-name"], [1, "provider-meta"], [1, "provider-type-badge", 3, "title"], ["title", "Supports search", 1, "search-badge"], ["title", "Credentials configured", 1, "credential-badge"], ["title", "No credentials configured", 1, "unconfigured-badge"], [1, "fa-solid", "fa-magnifying-glass"], [1, "fa-solid", "fa-key"], [1, "fa-solid", "fa-exclamation-triangle"]], template: function StorageProvidersListComponent_Template(rf, ctx) { if (rf & 1) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-providers-list.component.js","sourceRoot":"","sources":["../../../src/lib/file-browser/storage-providers-list.component.ts","../../../src/lib/file-browser/storage-providers-list.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, OnInit, Output, ChangeDetectorRef } from '@angular/core';\nimport { FileStorageEngine, StorageAccountWithProvider } from '@memberjunction/core-entities';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Displays a list of organizational file storage accounts.\n * In the enterprise model, accounts are configured by administrators\n * and available to users based on permissions. Users no longer manage\n * their own OAuth connections - credentials are handled at the org level.\n */\n@Component({\n standalone: false,\n selector: 'mj-storage-providers-list',\n templateUrl: './storage-providers-list.component.html',\n styleUrls: ['./storage-providers-list.component.css']\n})\nexport class StorageProvidersListComponent implements OnInit {\n /**\n * Emits when an account is selected by the user, or null when no accounts are available.\n * Emits the full account-with-provider object for downstream components to use.\n */\n @Output() accountSelected = new EventEmitter<StorageAccountWithProvider | null>();\n\n /**\n * All available storage accounts with their provider details.\n */\n public accounts: StorageAccountWithProvider[] = [];\n\n /**\n * Currently selected account.\n */\n public selectedAccount: StorageAccountWithProvider | null = null;\n\n /**\n * Loading state indicator.\n */\n public isLoading: boolean = false;\n\n /**\n * Error message if loading fails.\n */\n public errorMessage: string | null = null;\n\n constructor(private cdr: ChangeDetectorRef) {}\n\n ngOnInit(): void {\n this.loadAccounts();\n }\n\n /**\n * Loads all available file storage accounts with their provider details.\n * Uses FileStorageEngine for centralized, cached access.\n */\n private async loadAccounts(): Promise<void> {\n this.isLoading = true;\n this.errorMessage = null;\n\n try {\n const engine = FileStorageEngine.Instance;\n await engine.Config(false); // Use cached data if available\n\n this.accounts = engine.AccountsWithProviders;\n\n console.log('[StorageAccountsList] Loaded accounts:', this.accounts.map(a => ({\n name: a.account.Name,\n provider: a.provider.Name,\n hasCredential: !!a.account.CredentialID\n })));\n\n // Auto-select first account if available\n if (this.accounts.length > 0) {\n this.selectAccount(this.accounts[0]);\n } else {\n this.accountSelected.emit(null);\n }\n\n } catch (error) {\n this.errorMessage = error instanceof Error ? error.message : 'Failed to load storage accounts';\n console.error('[StorageProvidersList] Error loading accounts:', error);\n } finally {\n this.isLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Handles account selection by the user.\n */\n public selectAccount(accountWithProvider: StorageAccountWithProvider): void {\n this.selectedAccount = accountWithProvider;\n this.accountSelected.emit(accountWithProvider);\n }\n\n /**\n * Checks if an account is currently selected.\n */\n public isSelected(accountWithProvider: StorageAccountWithProvider): boolean {\n return UUIDsEqual(this.selectedAccount?.account.ID, accountWithProvider.account.ID);\n }\n\n /**\n * Gets the icon class for a provider based on its name.\n */\n public getProviderIcon(providerName: string): string {\n const name = providerName.toLowerCase();\n\n if (name.includes('aws') || name.includes('s3')) {\n return 'fa-brands fa-aws';\n } else if (name.includes('azure')) {\n return 'fa-brands fa-microsoft';\n } else if (name.includes('google drive')) {\n return 'fa-brands fa-google-drive';\n } else if (name.includes('google cloud')) {\n return 'fa-brands fa-google';\n } else if (name.includes('dropbox')) {\n return 'fa-brands fa-dropbox';\n } else if (name.includes('box')) {\n return 'fa-solid fa-box';\n } else if (name.includes('sharepoint') || name.includes('onedrive')) {\n return 'fa-brands fa-microsoft';\n } else {\n return 'fa-solid fa-cloud';\n }\n }\n\n /**\n * Refreshes the accounts list by forcing a reload from the database.\n */\n public refresh(): void {\n FileStorageEngine.Instance.Config(true).then(() => this.loadAccounts());\n }\n}\n","<div class=\"providers-list\">\n <!-- Header -->\n <div class=\"providers-header\">\n <span class=\"header-title\">Storage Accounts</span>\n </div>\n\n <!-- Loading State -->\n @if (isLoading) {\n <div class=\"loading-state\">\n <mj-loading text=\"Loading accounts...\"></mj-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (errorMessage && !isLoading) {\n <div class=\"error-state\">\n <div class=\"error-icon\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n </div>\n <p class=\"error-message\">{{ errorMessage }}</p>\n </div>\n }\n\n <!-- Empty State -->\n @if (!isLoading && !errorMessage && accounts.length === 0) {\n <div class=\"empty-state\">\n <div class=\"empty-icon\">\n <i class=\"fa-solid fa-folder-plus\"></i>\n </div>\n <p class=\"empty-message\">No storage accounts configured</p>\n <p class=\"empty-hint\">Contact your administrator to set up file storage accounts</p>\n </div>\n }\n\n <!-- Account List -->\n @if (!isLoading && !errorMessage && accounts.length > 0) {\n <div class=\"providers-container\">\n @for (item of accounts; track item) {\n <div\n class=\"provider-item\"\n [class.selected]=\"isSelected(item)\"\n (click)=\"selectAccount(item)\"\n [title]=\"item.account.Description || item.account.Name\"\n >\n <div class=\"provider-icon\">\n <i [class]=\"getProviderIcon(item.provider.Name)\"></i>\n </div>\n <div class=\"provider-info\">\n <div class=\"provider-name\">{{ item.account.Name }}</div>\n <div class=\"provider-meta\">\n <!-- Provider type badge -->\n <span class=\"provider-type-badge\" [title]=\"item.provider.Name\">\n {{ item.provider.Name }}\n </span>\n <!-- Search support badge -->\n @if (item.provider.SupportsSearch) {\n <span class=\"search-badge\" title=\"Supports search\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n </span>\n }\n <!-- Credential configured badge -->\n @if (item.account.CredentialID) {\n <span class=\"credential-badge\" title=\"Credentials configured\">\n <i class=\"fa-solid fa-key\"></i>\n </span>\n }\n <!-- No credential warning -->\n @if (!item.account.CredentialID) {\n <span class=\"unconfigured-badge\" title=\"No credentials configured\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n </span>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n</div>\n"]}
|
|
1
|
+
{"version":3,"file":"storage-providers-list.component.js","sourceRoot":"","sources":["../../../src/lib/file-browser/storage-providers-list.component.ts","../../../src/lib/file-browser/storage-providers-list.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, OnInit, Output, ChangeDetectorRef } from '@angular/core';\nimport { FileStorageEngineBase, StorageAccountWithProvider } from '@memberjunction/core-entities';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Displays a list of organizational file storage accounts.\n * In the enterprise model, accounts are configured by administrators\n * and available to users based on permissions. Users no longer manage\n * their own OAuth connections - credentials are handled at the org level.\n */\n@Component({\n standalone: false,\n selector: 'mj-storage-providers-list',\n templateUrl: './storage-providers-list.component.html',\n styleUrls: ['./storage-providers-list.component.css']\n})\nexport class StorageProvidersListComponent implements OnInit {\n /**\n * Emits when an account is selected by the user, or null when no accounts are available.\n * Emits the full account-with-provider object for downstream components to use.\n */\n @Output() accountSelected = new EventEmitter<StorageAccountWithProvider | null>();\n\n /**\n * All available storage accounts with their provider details.\n */\n public accounts: StorageAccountWithProvider[] = [];\n\n /**\n * Currently selected account.\n */\n public selectedAccount: StorageAccountWithProvider | null = null;\n\n /**\n * Loading state indicator.\n */\n public isLoading: boolean = false;\n\n /**\n * Error message if loading fails.\n */\n public errorMessage: string | null = null;\n\n constructor(private cdr: ChangeDetectorRef) {}\n\n ngOnInit(): void {\n this.loadAccounts();\n }\n\n /**\n * Loads all available file storage accounts with their provider details.\n * Uses FileStorageEngineBase for centralized, cached access.\n */\n private async loadAccounts(): Promise<void> {\n this.isLoading = true;\n this.errorMessage = null;\n\n try {\n const engine = FileStorageEngineBase.Instance;\n await engine.Config(false); // Use cached data if available\n\n // Only show accounts whose provider is active\n this.accounts = engine.AccountsWithProviders.filter(a => a.provider.IsActive);\n\n console.log('[StorageAccountsList] Loaded accounts:', this.accounts.map(a => ({\n name: a.account.Name,\n provider: a.provider.Name,\n hasCredential: !!a.account.CredentialID\n })));\n\n // Auto-select first account if available\n if (this.accounts.length > 0) {\n this.selectAccount(this.accounts[0]);\n } else {\n this.accountSelected.emit(null);\n }\n\n } catch (error) {\n this.errorMessage = error instanceof Error ? error.message : 'Failed to load storage accounts';\n console.error('[StorageProvidersList] Error loading accounts:', error);\n } finally {\n this.isLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Handles account selection by the user.\n */\n public selectAccount(accountWithProvider: StorageAccountWithProvider): void {\n this.selectedAccount = accountWithProvider;\n this.accountSelected.emit(accountWithProvider);\n }\n\n /**\n * Checks if an account is currently selected.\n */\n public isSelected(accountWithProvider: StorageAccountWithProvider): boolean {\n return UUIDsEqual(this.selectedAccount?.account.ID, accountWithProvider.account.ID);\n }\n\n /**\n * Gets the icon class for a provider based on its name.\n */\n public getProviderIcon(providerName: string): string {\n const name = providerName.toLowerCase();\n\n if (name.includes('aws') || name.includes('s3')) {\n return 'fa-brands fa-aws';\n } else if (name.includes('azure')) {\n return 'fa-brands fa-microsoft';\n } else if (name.includes('google drive')) {\n return 'fa-brands fa-google-drive';\n } else if (name.includes('google cloud')) {\n return 'fa-brands fa-google';\n } else if (name.includes('dropbox')) {\n return 'fa-brands fa-dropbox';\n } else if (name.includes('box')) {\n return 'fa-solid fa-box';\n } else if (name.includes('sharepoint') || name.includes('onedrive')) {\n return 'fa-brands fa-microsoft';\n } else {\n return 'fa-solid fa-cloud';\n }\n }\n\n /**\n * Refreshes the accounts list by forcing a reload from the database.\n */\n public refresh(): void {\n FileStorageEngineBase.Instance.Config(true).then(() => this.loadAccounts());\n }\n}\n","<div class=\"providers-list\">\n <!-- Header -->\n <div class=\"providers-header\">\n <span class=\"header-title\">Storage Accounts</span>\n </div>\n\n <!-- Loading State -->\n @if (isLoading) {\n <div class=\"loading-state\">\n <mj-loading text=\"Loading accounts...\"></mj-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (errorMessage && !isLoading) {\n <div class=\"error-state\">\n <div class=\"error-icon\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n </div>\n <p class=\"error-message\">{{ errorMessage }}</p>\n </div>\n }\n\n <!-- Empty State -->\n @if (!isLoading && !errorMessage && accounts.length === 0) {\n <div class=\"empty-state\">\n <div class=\"empty-icon\">\n <i class=\"fa-solid fa-folder-plus\"></i>\n </div>\n <p class=\"empty-message\">No storage accounts configured</p>\n <p class=\"empty-hint\">Contact your administrator to set up file storage accounts</p>\n </div>\n }\n\n <!-- Account List -->\n @if (!isLoading && !errorMessage && accounts.length > 0) {\n <div class=\"providers-container\">\n @for (item of accounts; track item) {\n <div\n class=\"provider-item\"\n [class.selected]=\"isSelected(item)\"\n (click)=\"selectAccount(item)\"\n [title]=\"item.account.Description || item.account.Name\"\n >\n <div class=\"provider-icon\">\n <i [class]=\"getProviderIcon(item.provider.Name)\"></i>\n </div>\n <div class=\"provider-info\">\n <div class=\"provider-name\">{{ item.account.Name }}</div>\n <div class=\"provider-meta\">\n <!-- Provider type badge -->\n <span class=\"provider-type-badge\" [title]=\"item.provider.Name\">\n {{ item.provider.Name }}\n </span>\n <!-- Search support badge -->\n @if (item.provider.SupportsSearch) {\n <span class=\"search-badge\" title=\"Supports search\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n </span>\n }\n <!-- Credential configured badge -->\n @if (item.account.CredentialID) {\n <span class=\"credential-badge\" title=\"Credentials configured\">\n <i class=\"fa-solid fa-key\"></i>\n </span>\n }\n <!-- No credential warning -->\n @if (!item.account.CredentialID) {\n <span class=\"unconfigured-badge\" title=\"No credentials configured\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n </span>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n</div>\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
export declare class FileOpenService {
|
|
3
|
+
private client;
|
|
4
|
+
private getClient;
|
|
5
|
+
/**
|
|
6
|
+
* Open a file from a storage provider in a new browser tab.
|
|
7
|
+
* Generates a pre-authenticated download URL and opens it.
|
|
8
|
+
*
|
|
9
|
+
* @param accountId - The FileStorageAccount ID
|
|
10
|
+
* @param path - The file path within the storage account
|
|
11
|
+
* @param objectId - Optional provider-specific object ID (e.g., Box file ID).
|
|
12
|
+
* When provided, uses this instead of path for faster resolution.
|
|
13
|
+
* @returns true if the file was opened successfully
|
|
14
|
+
*/
|
|
15
|
+
OpenFile(accountId: string, path: string, objectId?: string): Promise<boolean>;
|
|
16
|
+
/**
|
|
17
|
+
* Open a file from search result metadata.
|
|
18
|
+
* Extracts accountId and objectId/path from the RawMetadata JSON string.
|
|
19
|
+
*
|
|
20
|
+
* @param rawMetadata - JSON string from SearchResultItem.RawMetadata
|
|
21
|
+
* @returns true if the file was opened successfully, false if metadata was invalid or open failed
|
|
22
|
+
*/
|
|
23
|
+
OpenFileFromSearchResult(rawMetadata: string | undefined): Promise<boolean>;
|
|
24
|
+
/**
|
|
25
|
+
* Open a file for preview in the provider's web viewer (if available).
|
|
26
|
+
* Falls back to download URL if no preview URL can be constructed.
|
|
27
|
+
*
|
|
28
|
+
* Currently supported:
|
|
29
|
+
* - Box.com: https://app.box.com/file/{fileId}
|
|
30
|
+
* - Google Drive: https://drive.google.com/file/d/{fileId}/view
|
|
31
|
+
* - SharePoint: Uses webUrl from provider data
|
|
32
|
+
* - Dropbox: https://www.dropbox.com/preview/{path}
|
|
33
|
+
*
|
|
34
|
+
* @param rawMetadata - JSON string from SearchResultItem.RawMetadata
|
|
35
|
+
* @returns true if a preview was opened
|
|
36
|
+
*/
|
|
37
|
+
OpenPreviewFromSearchResult(rawMetadata: string | undefined): boolean;
|
|
38
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FileOpenService, never>;
|
|
39
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<FileOpenService>;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=file-open.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-open.service.d.ts","sourceRoot":"","sources":["../../src/lib/file-open.service.ts"],"names":[],"mappings":";AAoBA,qBACa,eAAe;IACxB,OAAO,CAAC,MAAM,CAAyC;IAEvD,OAAO,CAAC,SAAS;IAQjB;;;;;;;;;OASG;IACU,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiB3F;;;;;;OAMG;IACU,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;IAwBxF;;;;;;;;;;;;OAYG;IACI,2BAA2B,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO;yCAlFnE,eAAe;6CAAf,eAAe;CAyG3B"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Reusable service for opening files from MJ Storage providers.
|
|
3
|
+
*
|
|
4
|
+
* Generates pre-authenticated download URLs via GraphQL and opens files
|
|
5
|
+
* in new browser tabs. Provider-agnostic — works with Box, GDrive,
|
|
6
|
+
* SharePoint, Dropbox, or any configured storage provider.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const fileOpen = inject(FileOpenService);
|
|
11
|
+
* await fileOpen.OpenFile(accountId, filePath);
|
|
12
|
+
* // or with an objectId for better performance:
|
|
13
|
+
* await fileOpen.OpenFile(accountId, filePath, objectId);
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
import { Injectable } from '@angular/core';
|
|
17
|
+
import { Metadata } from '@memberjunction/core';
|
|
18
|
+
import { GraphQLFileStorageClient } from '@memberjunction/graphql-dataprovider';
|
|
19
|
+
import * as i0 from "@angular/core";
|
|
20
|
+
export class FileOpenService {
|
|
21
|
+
client = null;
|
|
22
|
+
getClient() {
|
|
23
|
+
if (!this.client) {
|
|
24
|
+
const provider = Metadata.Provider;
|
|
25
|
+
this.client = new GraphQLFileStorageClient(provider);
|
|
26
|
+
}
|
|
27
|
+
return this.client;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Open a file from a storage provider in a new browser tab.
|
|
31
|
+
* Generates a pre-authenticated download URL and opens it.
|
|
32
|
+
*
|
|
33
|
+
* @param accountId - The FileStorageAccount ID
|
|
34
|
+
* @param path - The file path within the storage account
|
|
35
|
+
* @param objectId - Optional provider-specific object ID (e.g., Box file ID).
|
|
36
|
+
* When provided, uses this instead of path for faster resolution.
|
|
37
|
+
* @returns true if the file was opened successfully
|
|
38
|
+
*/
|
|
39
|
+
async OpenFile(accountId, path, objectId) {
|
|
40
|
+
try {
|
|
41
|
+
const client = this.getClient();
|
|
42
|
+
// Use path for CreatePreAuthDownloadUrl — drivers resolve paths to provider IDs internally
|
|
43
|
+
const downloadUrl = await client.CreatePreAuthDownloadUrl(accountId, path);
|
|
44
|
+
if (downloadUrl) {
|
|
45
|
+
window.open(downloadUrl, '_blank');
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error('[FileOpenService] Error opening file:', error);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Open a file from search result metadata.
|
|
57
|
+
* Extracts accountId and objectId/path from the RawMetadata JSON string.
|
|
58
|
+
*
|
|
59
|
+
* @param rawMetadata - JSON string from SearchResultItem.RawMetadata
|
|
60
|
+
* @returns true if the file was opened successfully, false if metadata was invalid or open failed
|
|
61
|
+
*/
|
|
62
|
+
async OpenFileFromSearchResult(rawMetadata) {
|
|
63
|
+
if (!rawMetadata)
|
|
64
|
+
return false;
|
|
65
|
+
try {
|
|
66
|
+
const meta = JSON.parse(rawMetadata);
|
|
67
|
+
if (!meta.accountId) {
|
|
68
|
+
console.error('[FileOpenService] Missing accountId in search result metadata');
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
const path = meta.path || meta.objectId || '';
|
|
72
|
+
return this.OpenFile(meta.accountId, path, meta.objectId);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error('[FileOpenService] Error parsing search result metadata:', error);
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Open a file for preview in the provider's web viewer (if available).
|
|
81
|
+
* Falls back to download URL if no preview URL can be constructed.
|
|
82
|
+
*
|
|
83
|
+
* Currently supported:
|
|
84
|
+
* - Box.com: https://app.box.com/file/{fileId}
|
|
85
|
+
* - Google Drive: https://drive.google.com/file/d/{fileId}/view
|
|
86
|
+
* - SharePoint: Uses webUrl from provider data
|
|
87
|
+
* - Dropbox: https://www.dropbox.com/preview/{path}
|
|
88
|
+
*
|
|
89
|
+
* @param rawMetadata - JSON string from SearchResultItem.RawMetadata
|
|
90
|
+
* @returns true if a preview was opened
|
|
91
|
+
*/
|
|
92
|
+
OpenPreviewFromSearchResult(rawMetadata) {
|
|
93
|
+
if (!rawMetadata)
|
|
94
|
+
return false;
|
|
95
|
+
try {
|
|
96
|
+
const meta = JSON.parse(rawMetadata);
|
|
97
|
+
// Try to construct a provider-specific preview URL from the objectId
|
|
98
|
+
if (meta.objectId) {
|
|
99
|
+
// Box.com preview URL
|
|
100
|
+
window.open(`https://app.box.com/file/${meta.objectId}`, '_blank');
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
static ɵfac = function FileOpenService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FileOpenService)(); };
|
|
110
|
+
static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: FileOpenService, factory: FileOpenService.ɵfac, providedIn: 'root' });
|
|
111
|
+
}
|
|
112
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FileOpenService, [{
|
|
113
|
+
type: Injectable,
|
|
114
|
+
args: [{ providedIn: 'root' }]
|
|
115
|
+
}], null, null); })();
|
|
116
|
+
//# sourceMappingURL=file-open.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-open.service.js","sourceRoot":"","sources":["../../src/lib/file-open.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAuB,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;;AAGrG,MAAM,OAAO,eAAe;IAChB,MAAM,GAAoC,IAAI,CAAC;IAE/C,SAAS;QACb,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACf,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAA+B,CAAC;YAC1D,IAAI,CAAC,MAAM,GAAG,IAAI,wBAAwB,CAAC,QAAQ,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;IAED;;;;;;;;;OASG;IACI,KAAK,CAAC,QAAQ,CAAC,SAAiB,EAAE,IAAY,EAAE,QAAiB;QACpE,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;YAChC,2FAA2F;YAC3F,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YAE3E,IAAI,WAAW,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;gBACnC,OAAO,IAAI,CAAC;YAChB,CAAC;YACD,OAAO,KAAK,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,KAAK,CAAC,CAAC;YAC9D,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,wBAAwB,CAAC,WAA+B;QACjE,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAC;QAE/B,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAKlC,CAAC;YAEF,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBAClB,OAAO,CAAC,KAAK,CAAC,+DAA+D,CAAC,CAAC;gBAC/E,OAAO,KAAK,CAAC;YACjB,CAAC;YAED,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;YAC9C,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,yDAAyD,EAAE,KAAK,CAAC,CAAC;YAChF,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;IAED;;;;;;;;;;;;OAYG;IACI,2BAA2B,CAAC,WAA+B;QAC9D,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAC;QAE/B,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAKlC,CAAC;YAEF,qEAAqE;YACrE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAChB,sBAAsB;gBACtB,MAAM,CAAC,IAAI,CAAC,4BAA4B,IAAI,CAAC,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAAC;gBACnE,OAAO,IAAI,CAAC;YAChB,CAAC;YAED,OAAO,KAAK,CAAC;QACjB,CAAC;QAAC,MAAM,CAAC;YACL,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;yGAxGQ,eAAe;gEAAf,eAAe,WAAf,eAAe,mBADF,MAAM;;iFACnB,eAAe;cAD3B,UAAU;eAAC,EAAE,UAAU,EAAE,MAAM,EAAE","sourcesContent":["/**\n * @fileoverview Reusable service for opening files from MJ Storage providers.\n *\n * Generates pre-authenticated download URLs via GraphQL and opens files\n * in new browser tabs. Provider-agnostic — works with Box, GDrive,\n * SharePoint, Dropbox, or any configured storage provider.\n *\n * Usage:\n * ```typescript\n * const fileOpen = inject(FileOpenService);\n * await fileOpen.OpenFile(accountId, filePath);\n * // or with an objectId for better performance:\n * await fileOpen.OpenFile(accountId, filePath, objectId);\n * ```\n */\n\nimport { Injectable } from '@angular/core';\nimport { Metadata } from '@memberjunction/core';\nimport { GraphQLDataProvider, GraphQLFileStorageClient } from '@memberjunction/graphql-dataprovider';\n\n@Injectable({ providedIn: 'root' })\nexport class FileOpenService {\n private client: GraphQLFileStorageClient | null = null;\n\n private getClient(): GraphQLFileStorageClient {\n if (!this.client) {\n const provider = Metadata.Provider as GraphQLDataProvider;\n this.client = new GraphQLFileStorageClient(provider);\n }\n return this.client;\n }\n\n /**\n * Open a file from a storage provider in a new browser tab.\n * Generates a pre-authenticated download URL and opens it.\n *\n * @param accountId - The FileStorageAccount ID\n * @param path - The file path within the storage account\n * @param objectId - Optional provider-specific object ID (e.g., Box file ID).\n * When provided, uses this instead of path for faster resolution.\n * @returns true if the file was opened successfully\n */\n public async OpenFile(accountId: string, path: string, objectId?: string): Promise<boolean> {\n try {\n const client = this.getClient();\n // Use path for CreatePreAuthDownloadUrl — drivers resolve paths to provider IDs internally\n const downloadUrl = await client.CreatePreAuthDownloadUrl(accountId, path);\n\n if (downloadUrl) {\n window.open(downloadUrl, '_blank');\n return true;\n }\n return false;\n } catch (error) {\n console.error('[FileOpenService] Error opening file:', error);\n return false;\n }\n }\n\n /**\n * Open a file from search result metadata.\n * Extracts accountId and objectId/path from the RawMetadata JSON string.\n *\n * @param rawMetadata - JSON string from SearchResultItem.RawMetadata\n * @returns true if the file was opened successfully, false if metadata was invalid or open failed\n */\n public async OpenFileFromSearchResult(rawMetadata: string | undefined): Promise<boolean> {\n if (!rawMetadata) return false;\n\n try {\n const meta = JSON.parse(rawMetadata) as {\n accountId?: string;\n accountName?: string;\n path?: string;\n objectId?: string;\n };\n\n if (!meta.accountId) {\n console.error('[FileOpenService] Missing accountId in search result metadata');\n return false;\n }\n\n const path = meta.path || meta.objectId || '';\n return this.OpenFile(meta.accountId, path, meta.objectId);\n } catch (error) {\n console.error('[FileOpenService] Error parsing search result metadata:', error);\n return false;\n }\n }\n\n /**\n * Open a file for preview in the provider's web viewer (if available).\n * Falls back to download URL if no preview URL can be constructed.\n *\n * Currently supported:\n * - Box.com: https://app.box.com/file/{fileId}\n * - Google Drive: https://drive.google.com/file/d/{fileId}/view\n * - SharePoint: Uses webUrl from provider data\n * - Dropbox: https://www.dropbox.com/preview/{path}\n *\n * @param rawMetadata - JSON string from SearchResultItem.RawMetadata\n * @returns true if a preview was opened\n */\n public OpenPreviewFromSearchResult(rawMetadata: string | undefined): boolean {\n if (!rawMetadata) return false;\n\n try {\n const meta = JSON.parse(rawMetadata) as {\n accountId?: string;\n accountName?: string;\n path?: string;\n objectId?: string;\n };\n\n // Try to construct a provider-specific preview URL from the objectId\n if (meta.objectId) {\n // Box.com preview URL\n window.open(`https://app.box.com/file/${meta.objectId}`, '_blank');\n return true;\n }\n\n return false;\n } catch {\n return false;\n }\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-upload.d.ts","sourceRoot":"","sources":["../../../src/lib/file-upload/file-upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAS,MAAM,EAAU,MAAM,eAAe,CAAC;AAE/E,OAAO,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"file-upload.d.ts","sourceRoot":"","sources":["../../../src/lib/file-upload/file-upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAS,MAAM,EAAU,MAAM,eAAe,CAAC;AAE/E,OAAO,EAAE,YAAY,EAAoE,MAAM,+BAA+B,CAAC;AAG/H,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;;AAExB;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,IAAI,CAAC;CACf;AAED,MAAM,MAAM,eAAe,GAAG;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,cAAc,CAAA;CAAE,CAAC;AAgC/G,QAAA,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAM5B,CAAC;AAEH,KAAK,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAChF,KAAK,WAAW,GAAG,CAAC,cAAc,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAErD,qBAMa,mBAAoB,YAAW,MAAM;IACzC,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,CAAM;IACtC,WAAW,EAAE,KAAK,CAAC,cAAc,CAAC,CAAM;IAC/C,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,EAAE,CAAkB;IAE5B,IAAI,WAAW,IAAI,OAAO,CAEzB;;IAIQ,QAAQ,UAAS;IACjB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAa;IAC1C,aAAa,qBAA4B;IACzC,UAAU,gCAAuC;IAE3D,QAAQ,IAAI,IAAI;IAIV,OAAO;IAWb,OAAO;IAOD,aAAa;IAanB;;OAEG;IACH,cAAc,CAAC,KAAK,EAAE,KAAK;YAyBb,mBAAmB;YAkCnB,WAAW;yCAlHd,mBAAmB;2CAAnB,mBAAmB;CA0I/B"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
|
2
|
-
import { Metadata
|
|
3
|
-
import { MJFileSchema } from '@memberjunction/core-entities';
|
|
2
|
+
import { Metadata } from '@memberjunction/core';
|
|
3
|
+
import { MJFileSchema, FileStorageEngineBase } from '@memberjunction/core-entities';
|
|
4
4
|
import { GraphQLDataProvider, gql } from '@memberjunction/graphql-dataprovider';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import * as i0 from "@angular/core";
|
|
@@ -66,9 +66,11 @@ export class FileUploadComponent {
|
|
|
66
66
|
this.Refresh();
|
|
67
67
|
}
|
|
68
68
|
async Refresh() {
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
69
|
+
await FileStorageEngineBase.Instance.Config(false);
|
|
70
|
+
const activeProviders = FileStorageEngineBase.Instance.Providers
|
|
71
|
+
.filter(p => p.IsActive)
|
|
72
|
+
.sort((a, b) => (b.Priority ?? 0) - (a.Priority ?? 0));
|
|
73
|
+
const provider = activeProviders[0];
|
|
72
74
|
if (typeof provider?.ID === 'string') {
|
|
73
75
|
this.defaultProviderID = provider.ID;
|
|
74
76
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-upload.js","sourceRoot":"","sources":["../../../src/lib/file-upload/file-upload.ts","../../../src/lib/file-upload/file-upload.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';\nimport { Metadata, RunView } from '@memberjunction/core';\nimport { MJFileEntity, MJFileSchema, MJFileStorageProviderEntity } from '@memberjunction/core-entities';\nimport { GraphQLDataProvider, gql } from '@memberjunction/graphql-dataprovider';\n\nimport { z } from 'zod';\n\n/**\n * Minimal file info interface replacing Kendo's FileInfo.\n */\nexport interface FileSelectInfo {\n name: string;\n size: number;\n rawFile: File;\n}\n\nexport type FileUploadEvent = { success: true; file: MJFileEntity } | { success: false; file: FileSelectInfo };\n\nconst FileFieldsFragment = gql`\n fragment FileFields on MJFile_ {\n Category\n CategoryID\n ContentType\n _mj__CreatedAt\n Description\n ID\n Name\n Provider\n ProviderID\n ProviderKey\n Status\n _mj__UpdatedAt\n }\n`;\n\nconst FileUploadMutation = gql`\n ${FileFieldsFragment}\n mutation CreateMJFile($input: CreateMJFileInput!) {\n CreateMJFile(input: $input) {\n NameExists\n UploadUrl\n File {\n ...FileFields\n }\n }\n }\n`;\n\nconst FileUploadMutationSchema = z.object({\n CreateMJFile: z.object({\n NameExists: z.boolean(),\n UploadUrl: z.string(),\n File: MJFileSchema.omit({ __mj_CreatedAt: true, __mj_UpdatedAt: true }).passthrough(),\n }),\n});\n\ntype ApiFile = z.infer<typeof FileUploadMutationSchema>['CreateMJFile']['File'];\ntype UploadTuple = [FileSelectInfo, ApiFile, string];\n\n@Component({\n standalone: false,\n selector: 'mj-files-file-upload',\n templateUrl: './file-upload.html',\n styleUrls: ['./file-upload.css'],\n})\nexport class FileUploadComponent implements OnInit {\n public ConfirmQueue: Array<UploadTuple> = [];\n public UploadQueue: Array<FileSelectInfo> = [];\n private defaultProviderID = '';\n private md = new Metadata();\n\n get IsUploading(): boolean {\n return this.UploadQueue.length + this.ConfirmQueue.length > 0;\n }\n\n constructor() {}\n\n @Input() disabled = false;\n @Input() CategoryID: string | undefined = undefined;\n @Output() uploadStarted = new EventEmitter<void>();\n @Output() fileUpload = new EventEmitter<FileUploadEvent>();\n\n ngOnInit(): void {\n this.Refresh();\n }\n\n async Refresh() {\n const rv = new RunView();\n const viewResults = await rv.RunView({ EntityName: 'MJ: File Storage Providers', ExtraFilter: 'IsActive = 1', OrderBy: 'Priority DESC' });\n const provider: MJFileStorageProviderEntity | undefined = viewResults.Results[0];\n if (typeof provider?.ID === 'string') {\n this.defaultProviderID = provider.ID;\n }\n }\n\n Confirm() {\n const confirmed = this.ConfirmQueue.shift();\n if (confirmed) {\n this._uploadFile(...confirmed);\n }\n }\n\n async CancelConfirm() {\n const cancelled = this.ConfirmQueue.shift();\n if (cancelled) {\n const [file, fileRecord] = cancelled;\n\n const fileEntity: MJFileEntity = await this.md.GetEntityObject('MJ: Files');\n await fileEntity.LoadFromData(fileRecord);\n await fileEntity.Delete();\n\n this.fileUpload.emit({ success: false, file });\n }\n }\n\n /**\n * Handles the native file input change event.\n */\n OnFileSelected(event: Event) {\n const input = event.target as HTMLInputElement;\n if (!input.files || input.files.length === 0) {\n return;\n }\n\n this.uploadStarted.emit();\n\n // Convert native File objects to our FileSelectInfo format\n for (let i = 0; i < input.files.length; i++) {\n const nativeFile = input.files[i];\n const fileInfo: FileSelectInfo = {\n name: nativeFile.name,\n size: nativeFile.size,\n rawFile: nativeFile,\n };\n this.UploadQueue.push(fileInfo);\n }\n\n // Reset input so the same file can be selected again\n input.value = '';\n\n this._processUploadQueue();\n }\n\n private async _processUploadQueue() {\n // for each selected file to upload\n let file = this.UploadQueue.shift();\n while (file) {\n const input = {\n Name: file.name,\n ProviderID: this.defaultProviderID,\n Status: 'Pending',\n CategoryID: this.CategoryID,\n };\n\n // call the gql\n const result = await GraphQLDataProvider.ExecuteGQL(FileUploadMutation, { input });\n\n // make sure the response is correct\n const parsedResult = FileUploadMutationSchema.safeParse(result);\n if (parsedResult.success) {\n const { File, UploadUrl, NameExists } = parsedResult.data.CreateMJFile;\n const uploadTuple: UploadTuple = [file, File, UploadUrl];\n\n // Confirm we want to overwrite\n if (NameExists) {\n this.ConfirmQueue.push(uploadTuple);\n } else {\n await this._uploadFile(...uploadTuple);\n }\n } else {\n console.error('The API returned an unexpected result', parsedResult.error.issues);\n this.fileUpload.emit({ success: false, file });\n }\n file = this.UploadQueue.shift();\n }\n }\n\n private async _uploadFile(file: FileSelectInfo, fileRecord: ApiFile, uploadUrl: string) {\n try {\n // now upload to the url\n await window.fetch(uploadUrl, {\n method: 'PUT',\n headers: { 'x-ms-blob-type': 'BlockBlob' },\n body: file.rawFile,\n });\n\n // now update that file to set status\n const fileEntity: MJFileEntity = await this.md.GetEntityObject('MJ: Files');\n await fileEntity.LoadFromData(fileRecord);\n fileEntity.Status = 'Uploaded';\n await fileEntity.Save();\n\n // emit an event about a new file uploaded, include the file data\n this.fileUpload.emit({ success: true, file: fileEntity });\n // Could also emit a progress event with each iteration\n } catch (e) {\n console.error(e);\n // something failed when actually uploading or when updating the API, what do to about pending file?\n this.fileUpload.emit({ success: false, file });\n }\n }\n}\n","<div>\n <label class=\"mj-file-select\" [class.disabled]=\"IsUploading || disabled\">\n <i class=\"fa-solid fa-upload\"></i>\n <span>Select file to upload</span>\n <input\n type=\"file\"\n class=\"mj-file-input\"\n [disabled]=\"IsUploading || disabled\"\n (change)=\"OnFileSelected($event)\" />\n </label>\n\n <mj-dialog\n [Visible]=\"ConfirmQueue.length > 0\"\n Title=\"Please confirm\"\n [Width]=\"450\"\n [MinWidth]=\"250\"\n (Close)=\"CancelConfirm()\">\n @if (ConfirmQueue.length > 0) {\n <p class=\"confirm-message\">\n This will overwrite an existing file named '{{ ConfirmQueue[0][0].name }}'. Are you sure you want to continue?\n </p>\n }\n <mj-dialog-actions>\n <button mjButton variant=\"primary\" (click)=\"Confirm()\">Overwrite</button>\n <button mjButton (click)=\"CancelConfirm()\">Cancel</button>\n </mj-dialog-actions>\n </mj-dialog>\n</div>\n"]}
|
|
1
|
+
{"version":3,"file":"file-upload.js","sourceRoot":"","sources":["../../../src/lib/file-upload/file-upload.ts","../../../src/lib/file-upload/file-upload.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';\nimport { Metadata } from '@memberjunction/core';\nimport { MJFileEntity, MJFileSchema, MJFileStorageProviderEntity, FileStorageEngineBase } from '@memberjunction/core-entities';\nimport { GraphQLDataProvider, gql } from '@memberjunction/graphql-dataprovider';\n\nimport { z } from 'zod';\n\n/**\n * Minimal file info interface replacing Kendo's FileInfo.\n */\nexport interface FileSelectInfo {\n name: string;\n size: number;\n rawFile: File;\n}\n\nexport type FileUploadEvent = { success: true; file: MJFileEntity } | { success: false; file: FileSelectInfo };\n\nconst FileFieldsFragment = gql`\n fragment FileFields on MJFile_ {\n Category\n CategoryID\n ContentType\n _mj__CreatedAt\n Description\n ID\n Name\n Provider\n ProviderID\n ProviderKey\n Status\n _mj__UpdatedAt\n }\n`;\n\nconst FileUploadMutation = gql`\n ${FileFieldsFragment}\n mutation CreateMJFile($input: CreateMJFileInput!) {\n CreateMJFile(input: $input) {\n NameExists\n UploadUrl\n File {\n ...FileFields\n }\n }\n }\n`;\n\nconst FileUploadMutationSchema = z.object({\n CreateMJFile: z.object({\n NameExists: z.boolean(),\n UploadUrl: z.string(),\n File: MJFileSchema.omit({ __mj_CreatedAt: true, __mj_UpdatedAt: true }).passthrough(),\n }),\n});\n\ntype ApiFile = z.infer<typeof FileUploadMutationSchema>['CreateMJFile']['File'];\ntype UploadTuple = [FileSelectInfo, ApiFile, string];\n\n@Component({\n standalone: false,\n selector: 'mj-files-file-upload',\n templateUrl: './file-upload.html',\n styleUrls: ['./file-upload.css'],\n})\nexport class FileUploadComponent implements OnInit {\n public ConfirmQueue: Array<UploadTuple> = [];\n public UploadQueue: Array<FileSelectInfo> = [];\n private defaultProviderID = '';\n private md = new Metadata();\n\n get IsUploading(): boolean {\n return this.UploadQueue.length + this.ConfirmQueue.length > 0;\n }\n\n constructor() {}\n\n @Input() disabled = false;\n @Input() CategoryID: string | undefined = undefined;\n @Output() uploadStarted = new EventEmitter<void>();\n @Output() fileUpload = new EventEmitter<FileUploadEvent>();\n\n ngOnInit(): void {\n this.Refresh();\n }\n\n async Refresh() {\n await FileStorageEngineBase.Instance.Config(false);\n const activeProviders = FileStorageEngineBase.Instance.Providers\n .filter(p => p.IsActive)\n .sort((a, b) => (b.Priority ?? 0) - (a.Priority ?? 0));\n const provider: MJFileStorageProviderEntity | undefined = activeProviders[0];\n if (typeof provider?.ID === 'string') {\n this.defaultProviderID = provider.ID;\n }\n }\n\n Confirm() {\n const confirmed = this.ConfirmQueue.shift();\n if (confirmed) {\n this._uploadFile(...confirmed);\n }\n }\n\n async CancelConfirm() {\n const cancelled = this.ConfirmQueue.shift();\n if (cancelled) {\n const [file, fileRecord] = cancelled;\n\n const fileEntity: MJFileEntity = await this.md.GetEntityObject('MJ: Files');\n await fileEntity.LoadFromData(fileRecord);\n await fileEntity.Delete();\n\n this.fileUpload.emit({ success: false, file });\n }\n }\n\n /**\n * Handles the native file input change event.\n */\n OnFileSelected(event: Event) {\n const input = event.target as HTMLInputElement;\n if (!input.files || input.files.length === 0) {\n return;\n }\n\n this.uploadStarted.emit();\n\n // Convert native File objects to our FileSelectInfo format\n for (let i = 0; i < input.files.length; i++) {\n const nativeFile = input.files[i];\n const fileInfo: FileSelectInfo = {\n name: nativeFile.name,\n size: nativeFile.size,\n rawFile: nativeFile,\n };\n this.UploadQueue.push(fileInfo);\n }\n\n // Reset input so the same file can be selected again\n input.value = '';\n\n this._processUploadQueue();\n }\n\n private async _processUploadQueue() {\n // for each selected file to upload\n let file = this.UploadQueue.shift();\n while (file) {\n const input = {\n Name: file.name,\n ProviderID: this.defaultProviderID,\n Status: 'Pending',\n CategoryID: this.CategoryID,\n };\n\n // call the gql\n const result = await GraphQLDataProvider.ExecuteGQL(FileUploadMutation, { input });\n\n // make sure the response is correct\n const parsedResult = FileUploadMutationSchema.safeParse(result);\n if (parsedResult.success) {\n const { File, UploadUrl, NameExists } = parsedResult.data.CreateMJFile;\n const uploadTuple: UploadTuple = [file, File, UploadUrl];\n\n // Confirm we want to overwrite\n if (NameExists) {\n this.ConfirmQueue.push(uploadTuple);\n } else {\n await this._uploadFile(...uploadTuple);\n }\n } else {\n console.error('The API returned an unexpected result', parsedResult.error.issues);\n this.fileUpload.emit({ success: false, file });\n }\n file = this.UploadQueue.shift();\n }\n }\n\n private async _uploadFile(file: FileSelectInfo, fileRecord: ApiFile, uploadUrl: string) {\n try {\n // now upload to the url\n await window.fetch(uploadUrl, {\n method: 'PUT',\n headers: { 'x-ms-blob-type': 'BlockBlob' },\n body: file.rawFile,\n });\n\n // now update that file to set status\n const fileEntity: MJFileEntity = await this.md.GetEntityObject('MJ: Files');\n await fileEntity.LoadFromData(fileRecord);\n fileEntity.Status = 'Uploaded';\n await fileEntity.Save();\n\n // emit an event about a new file uploaded, include the file data\n this.fileUpload.emit({ success: true, file: fileEntity });\n // Could also emit a progress event with each iteration\n } catch (e) {\n console.error(e);\n // something failed when actually uploading or when updating the API, what do to about pending file?\n this.fileUpload.emit({ success: false, file });\n }\n }\n}\n","<div>\n <label class=\"mj-file-select\" [class.disabled]=\"IsUploading || disabled\">\n <i class=\"fa-solid fa-upload\"></i>\n <span>Select file to upload</span>\n <input\n type=\"file\"\n class=\"mj-file-input\"\n [disabled]=\"IsUploading || disabled\"\n (change)=\"OnFileSelected($event)\" />\n </label>\n\n <mj-dialog\n [Visible]=\"ConfirmQueue.length > 0\"\n Title=\"Please confirm\"\n [Width]=\"450\"\n [MinWidth]=\"250\"\n (Close)=\"CancelConfirm()\">\n @if (ConfirmQueue.length > 0) {\n <p class=\"confirm-message\">\n This will overwrite an existing file named '{{ ConfirmQueue[0][0].name }}'. Are you sure you want to continue?\n </p>\n }\n <mj-dialog-actions>\n <button mjButton variant=\"primary\" (click)=\"Confirm()\">Overwrite</button>\n <button mjButton (click)=\"CancelConfirm()\">Cancel</button>\n </mj-dialog-actions>\n </mj-dialog>\n</div>\n"]}
|
package/dist/public-api.d.ts
CHANGED
|
@@ -3,5 +3,6 @@ export * from './lib/files-grid/files-grid';
|
|
|
3
3
|
export * from './lib/file-browser/file-browser.component';
|
|
4
4
|
export * from './lib/file-browser/file-browser-demo.component';
|
|
5
5
|
export * from './lib/file-browser/file-browser-resource.component';
|
|
6
|
+
export * from './lib/file-open.service';
|
|
6
7
|
export * from './lib/module';
|
|
7
8
|
//# sourceMappingURL=public-api.d.ts.map
|
package/dist/public-api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAIA,cAAc,mCAAmC,CAAC;AAClD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,gDAAgD,CAAC;AAC/D,cAAc,oDAAoD,CAAC;AACnE,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAIA,cAAc,mCAAmC,CAAC;AAClD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,gDAAgD,CAAC;AAC/D,cAAc,oDAAoD,CAAC;AACnE,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC"}
|
package/dist/public-api.js
CHANGED
|
@@ -6,5 +6,6 @@ export * from './lib/files-grid/files-grid';
|
|
|
6
6
|
export * from './lib/file-browser/file-browser.component';
|
|
7
7
|
export * from './lib/file-browser/file-browser-demo.component';
|
|
8
8
|
export * from './lib/file-browser/file-browser-resource.component';
|
|
9
|
+
export * from './lib/file-open.service';
|
|
9
10
|
export * from './lib/module';
|
|
10
11
|
//# sourceMappingURL=public-api.js.map
|
package/dist/public-api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,mCAAmC,CAAC;AAClD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,gDAAgD,CAAC;AAC/D,cAAc,oDAAoD,CAAC;AACnE,cAAc,cAAc,CAAC","sourcesContent":["/*\n * Public API Surface\n */\n\nexport * from './lib/category-tree/category-tree';\nexport * from './lib/files-grid/files-grid';\nexport * from './lib/file-browser/file-browser.component';\nexport * from './lib/file-browser/file-browser-demo.component';\nexport * from './lib/file-browser/file-browser-resource.component';\nexport * from './lib/module';\n"]}
|
|
1
|
+
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,mCAAmC,CAAC;AAClD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,gDAAgD,CAAC;AAC/D,cAAc,oDAAoD,CAAC;AACnE,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC","sourcesContent":["/*\n * Public API Surface\n */\n\nexport * from './lib/category-tree/category-tree';\nexport * from './lib/files-grid/files-grid';\nexport * from './lib/file-browser/file-browser.component';\nexport * from './lib/file-browser/file-browser-demo.component';\nexport * from './lib/file-browser/file-browser-resource.component';\nexport * from './lib/file-open.service';\nexport * from './lib/module';\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-file-storage",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.25.0",
|
|
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",
|
|
@@ -38,15 +38,15 @@
|
|
|
38
38
|
"@angular/router": "21.1.3"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@memberjunction/ng-ui-components": "5.
|
|
41
|
+
"@memberjunction/ng-ui-components": "5.25.0",
|
|
42
42
|
"@angular/platform-browser": "21.1.3",
|
|
43
|
-
"@memberjunction/core": "5.
|
|
44
|
-
"@memberjunction/core-entities": "5.
|
|
45
|
-
"@memberjunction/global": "5.
|
|
46
|
-
"@memberjunction/graphql-dataprovider": "5.
|
|
47
|
-
"@memberjunction/ng-container-directives": "5.
|
|
48
|
-
"@memberjunction/ng-shared": "5.
|
|
49
|
-
"@memberjunction/ng-shared-generic": "5.
|
|
43
|
+
"@memberjunction/core": "5.25.0",
|
|
44
|
+
"@memberjunction/core-entities": "5.25.0",
|
|
45
|
+
"@memberjunction/global": "5.25.0",
|
|
46
|
+
"@memberjunction/graphql-dataprovider": "5.25.0",
|
|
47
|
+
"@memberjunction/ng-container-directives": "5.25.0",
|
|
48
|
+
"@memberjunction/ng-shared": "5.25.0",
|
|
49
|
+
"@memberjunction/ng-shared-generic": "5.25.0",
|
|
50
50
|
"ag-grid-angular": "^35.0.1",
|
|
51
51
|
"ag-grid-community": "^35.0.1",
|
|
52
52
|
"tslib": "^2.8.1",
|