@flusys/ng-storage 0.1.0-beta.3 → 1.0.0-rc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +209 -5
  2. package/fesm2022/flusys-ng-storage-file-manager-form.component-CMS_ddbs.mjs +485 -0
  3. package/fesm2022/flusys-ng-storage-file-manager-form.component-CMS_ddbs.mjs.map +1 -0
  4. package/fesm2022/flusys-ng-storage-file-manager-list.component-DDOFAPRD.mjs +305 -0
  5. package/fesm2022/flusys-ng-storage-file-manager-list.component-DDOFAPRD.mjs.map +1 -0
  6. package/fesm2022/flusys-ng-storage-folder-form.component-ClYATZkh.mjs +157 -0
  7. package/fesm2022/flusys-ng-storage-folder-form.component-ClYATZkh.mjs.map +1 -0
  8. package/fesm2022/flusys-ng-storage-folder-list.component-CzONANHt.mjs +200 -0
  9. package/fesm2022/flusys-ng-storage-folder-list.component-CzONANHt.mjs.map +1 -0
  10. package/fesm2022/{flusys-ng-storage-storage-config-form.component-DHzsQ2Mc.mjs → flusys-ng-storage-storage-config-form.component-CODLLCtg.mjs} +211 -238
  11. package/fesm2022/flusys-ng-storage-storage-config-form.component-CODLLCtg.mjs.map +1 -0
  12. package/fesm2022/flusys-ng-storage-storage-config-list.component-BbBx0iSL.mjs +213 -0
  13. package/fesm2022/flusys-ng-storage-storage-config-list.component-BbBx0iSL.mjs.map +1 -0
  14. package/fesm2022/flusys-ng-storage.mjs +391 -103
  15. package/fesm2022/flusys-ng-storage.mjs.map +1 -1
  16. package/package.json +5 -5
  17. package/types/flusys-ng-storage.d.ts +187 -8
  18. package/fesm2022/flusys-ng-storage-file-manager-form.component-BI0hgXeI.mjs +0 -716
  19. package/fesm2022/flusys-ng-storage-file-manager-form.component-BI0hgXeI.mjs.map +0 -1
  20. package/fesm2022/flusys-ng-storage-file-manager-list.component-5gqfPzR3.mjs +0 -475
  21. package/fesm2022/flusys-ng-storage-file-manager-list.component-5gqfPzR3.mjs.map +0 -1
  22. package/fesm2022/flusys-ng-storage-folder-form.component-Cw2aLZkb.mjs +0 -253
  23. package/fesm2022/flusys-ng-storage-folder-form.component-Cw2aLZkb.mjs.map +0 -1
  24. package/fesm2022/flusys-ng-storage-folder-list.component-CN_mTtS5.mjs +0 -267
  25. package/fesm2022/flusys-ng-storage-folder-list.component-CN_mTtS5.mjs.map +0 -1
  26. package/fesm2022/flusys-ng-storage-storage-config-form.component-DHzsQ2Mc.mjs.map +0 -1
  27. package/fesm2022/flusys-ng-storage-storage-config-list.component-tmiE2CpM.mjs +0 -328
  28. package/fesm2022/flusys-ng-storage-storage-config-list.component-tmiE2CpM.mjs.map +0 -1
@@ -1,10 +1,14 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injectable, Component } from '@angular/core';
2
+ import { inject, signal, computed, Directive, Injectable, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { Router, RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
4
+ import { APP_CONFIG, DEFAULT_APP_NAME, BaseApiService, getDefaultStorageConfigId } from '@flusys/ng-core';
5
+ import { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';
6
+ import { BaseListPage, ApiResourceService, PROFILE_UPLOAD_PROVIDER } from '@flusys/ng-shared';
7
+ import { MessageService } from 'primeng/api';
8
+ import { map } from 'rxjs/operators';
9
+ import { firstValueFrom, switchMap } from 'rxjs';
3
10
  import { HttpClient } from '@angular/common/http';
4
- import { ApiResourceService } from '@flusys/ng-shared';
5
- import { BaseApiService } from '@flusys/ng-core';
6
11
  import { CommonModule } from '@angular/common';
7
- import { RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
8
12
 
9
13
  /**
10
14
  * File Location Enum
@@ -29,6 +33,166 @@ var ImageFormat;
29
33
  ImageFormat["WEBP"] = "webp";
30
34
  })(ImageFormat || (ImageFormat = {}));
31
35
 
36
+ /**
37
+ * Provider configurations for all storage types
38
+ */
39
+ const PROVIDER_CONFIGS = {
40
+ [FileLocationEnum.LOCAL]: {
41
+ label: 'Local',
42
+ icon: 'pi pi-folder',
43
+ severity: 'secondary',
44
+ },
45
+ [FileLocationEnum.AWS]: {
46
+ label: 'AWS S3',
47
+ icon: 'pi pi-cloud',
48
+ severity: 'warn',
49
+ },
50
+ [FileLocationEnum.AZURE]: {
51
+ label: 'Azure Blob',
52
+ icon: 'pi pi-cloud',
53
+ severity: 'info',
54
+ },
55
+ [FileLocationEnum.SFTP]: {
56
+ label: 'SFTP',
57
+ icon: 'pi pi-server',
58
+ severity: 'contrast',
59
+ },
60
+ };
61
+ /**
62
+ * Get provider label for display
63
+ */
64
+ function getProviderLabel(storage) {
65
+ const enumValue = storage;
66
+ return PROVIDER_CONFIGS[enumValue]?.label ?? storage;
67
+ }
68
+ /**
69
+ * Get provider icon class
70
+ */
71
+ function getProviderIcon(storage) {
72
+ const enumValue = storage;
73
+ return PROVIDER_CONFIGS[enumValue]?.icon ?? 'pi pi-database';
74
+ }
75
+ /**
76
+ * Get provider severity for p-tag
77
+ */
78
+ function getProviderSeverity(storage) {
79
+ const enumValue = storage;
80
+ return PROVIDER_CONFIGS[enumValue]?.severity ?? 'secondary';
81
+ }
82
+ /**
83
+ * Get file icon based on content type
84
+ */
85
+ function getFileIcon(contentType) {
86
+ if (contentType.startsWith('image/'))
87
+ return 'pi pi-image';
88
+ if (contentType.startsWith('video/'))
89
+ return 'pi pi-video';
90
+ if (contentType.startsWith('audio/'))
91
+ return 'pi pi-volume-up';
92
+ if (contentType.includes('pdf'))
93
+ return 'pi pi-file-pdf';
94
+ if (contentType.includes('word') || contentType.includes('document'))
95
+ return 'pi pi-file-word';
96
+ if (contentType.includes('excel') || contentType.includes('spreadsheet'))
97
+ return 'pi pi-file-excel';
98
+ return 'pi pi-file';
99
+ }
100
+ /**
101
+ * Format file size from bytes to human readable string
102
+ */
103
+ function formatFileSize(bytes) {
104
+ if (bytes <= 0)
105
+ return '0 Bytes';
106
+ const k = 1024;
107
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
108
+ const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
109
+ return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
110
+ }
111
+ /**
112
+ * Storage provider options for dropdown selects
113
+ */
114
+ const STORAGE_PROVIDERS = [
115
+ { label: 'AWS S3', value: FileLocationEnum.AWS },
116
+ { label: 'Azure Blob Storage', value: FileLocationEnum.AZURE },
117
+ { label: 'SFTP', value: FileLocationEnum.SFTP },
118
+ { label: 'Local Storage', value: FileLocationEnum.LOCAL },
119
+ ];
120
+
121
+ /**
122
+ * Base Storage Page
123
+ *
124
+ * Provides common signals, computed values, and utilities for storage pages.
125
+ * Used by form components that need company context and message utilities.
126
+ */
127
+ class BaseStoragePage {
128
+ router = inject(Router);
129
+ messageService = inject(MessageService);
130
+ appConfig = inject(APP_CONFIG);
131
+ companyContext = inject(LAYOUT_AUTH_STATE);
132
+ /** Loading state */
133
+ isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
134
+ /** Show company info if company feature enabled */
135
+ showCompanyInfo = computed(() => this.appConfig.enableCompanyFeature, ...(ngDevMode ? [{ debugName: "showCompanyInfo" }] : []));
136
+ /** Current company name */
137
+ currentCompanyName = computed(() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
138
+ /** Show success message */
139
+ showSuccess(detail, summary = 'Success') {
140
+ this.messageService.add({ severity: 'success', summary, detail });
141
+ }
142
+ /** Show error message */
143
+ showError(detail, summary = 'Error') {
144
+ this.messageService.add({ severity: 'error', summary, detail });
145
+ }
146
+ /** Navigate to a route */
147
+ navigateTo(path) {
148
+ this.router.navigate(path);
149
+ }
150
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseStoragePage, deps: [], target: i0.ɵɵFactoryTarget.Directive });
151
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.3", type: BaseStoragePage, isStandalone: true, ngImport: i0 });
152
+ }
153
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseStoragePage, decorators: [{
154
+ type: Directive
155
+ }] });
156
+ /**
157
+ * Base Storage List Page
158
+ *
159
+ * Extends the shared BaseListPage with storage-specific functionality:
160
+ * - Company context integration via LAYOUT_AUTH_STATE
161
+ * - Current company name computed signal
162
+ * - Storage-specific defaults (pageSize: 50)
163
+ *
164
+ * Usage:
165
+ * ```typescript
166
+ * @Component({ ... })
167
+ * export class FolderListComponent extends BaseStorageListPage<IFolder> {
168
+ * getResourceRoute(): string { return '/storage/folders'; }
169
+ * getDeleteConfirmMessage(folder: IFolder): string { return `Delete "${folder.name}"?`; }
170
+ * async loadData(): Promise<void> { ... }
171
+ * }
172
+ * ```
173
+ */
174
+ class BaseStorageListPage extends BaseListPage {
175
+ companyContext = inject(LAYOUT_AUTH_STATE);
176
+ /** Override default page size for storage pages */
177
+ pageSize = signal(50, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
178
+ /** Total records alias for backward compatibility */
179
+ totalRecords = this.total;
180
+ /** Current company name */
181
+ currentCompanyName = computed(() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
182
+ /**
183
+ * Default implementation - subclasses should override if needed
184
+ */
185
+ getDeleteConfirmMessage(item) {
186
+ const name = item.name ?? 'this item';
187
+ return `Are you sure you want to delete "${name}"?`;
188
+ }
189
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseStorageListPage, deps: null, target: i0.ɵɵFactoryTarget.Directive });
190
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.3", type: BaseStorageListPage, isStandalone: true, usesInheritance: true, ngImport: i0 });
191
+ }
192
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseStorageListPage, decorators: [{
193
+ type: Directive
194
+ }] });
195
+
32
196
  /**
33
197
  * File Manager API Service
34
198
  * Handles file metadata CRUD operations
@@ -53,7 +217,7 @@ class FileManagerApiService extends ApiResourceService {
53
217
  /**
54
218
  * Get files by folder
55
219
  * POST /storage/file-manager/get-all
56
- * Filtered by folder ID and company (if enabled)
220
+ * Filtered by folder ID (UUID) and company (if enabled)
57
221
  */
58
222
  getFilesByFolder(folderId, page = 1, limit = 50) {
59
223
  return this.getAll('', {
@@ -76,54 +240,25 @@ class FileManagerApiService extends ApiResourceService {
76
240
  sort: {},
77
241
  });
78
242
  }
79
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FileManagerApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
80
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FileManagerApiService, providedIn: 'root' });
81
- }
82
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FileManagerApiService, decorators: [{
83
- type: Injectable,
84
- args: [{
85
- providedIn: 'root',
86
- }]
87
- }], ctorParameters: () => [] });
88
-
89
- /**
90
- * Folder API Service
91
- * Handles folder CRUD operations
92
- * Endpoint: POST /storage/folder/*
93
- *
94
- * Note: Company feature enabled - all queries automatically scoped to user's company
95
- */
96
- class FolderApiService extends ApiResourceService {
97
- constructor() {
98
- const http = inject(HttpClient);
99
- super('storage/folder', http);
243
+ /**
244
+ * Create a new file record
245
+ * POST /storage/file-manager/insert
246
+ * Uses ICreateFileManagerDto which has different required fields than update
247
+ */
248
+ createFile(dto) {
249
+ return this.http.post(`${this.baseUrl}/insert`, dto);
100
250
  }
101
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FolderApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
102
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FolderApiService, providedIn: 'root' });
103
- }
104
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FolderApiService, decorators: [{
105
- type: Injectable,
106
- args: [{
107
- providedIn: 'root',
108
- }]
109
- }], ctorParameters: () => [] });
110
-
111
- /**
112
- * Storage Config API Service
113
- * Handles storage configuration CRUD operations
114
- * Endpoint: POST /storage/storage-config/*
115
- *
116
- * Note: Company feature enabled - all queries automatically scoped to user's company
117
- */
118
- class StorageConfigApiService extends ApiResourceService {
119
- constructor() {
120
- const http = inject(HttpClient);
121
- super('storage/storage-config', http);
251
+ /**
252
+ * Create a new file record (async/await version)
253
+ * POST /storage/file-manager/insert
254
+ */
255
+ async createFileAsync(dto) {
256
+ return firstValueFrom(this.createFile(dto));
122
257
  }
123
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: StorageConfigApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
124
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: StorageConfigApiService, providedIn: 'root' });
258
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileManagerApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
259
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileManagerApiService, providedIn: 'root' });
125
260
  }
126
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: StorageConfigApiService, decorators: [{
261
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileManagerApiService, decorators: [{
127
262
  type: Injectable,
128
263
  args: [{
129
264
  providedIn: 'root',
@@ -140,11 +275,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
140
275
  * - Image compression
141
276
  * - Multiple storage providers
142
277
  * - File validation (size, type)
278
+ * - Default storage config from environment (services.storage.defaultStorageConfigId)
143
279
  */
144
280
  class UploadService extends BaseApiService {
281
+ fileManagerApi = inject(FileManagerApiService);
145
282
  constructor() {
146
283
  super('storage');
147
284
  }
285
+ /**
286
+ * Get default storage config ID from app config
287
+ * Used when no storageConfigId is provided in upload options
288
+ */
289
+ getDefaultConfigId() {
290
+ return getDefaultStorageConfigId(this.appConfig);
291
+ }
148
292
  /**
149
293
  * Upload single file
150
294
  * POST /storage/upload/single-file?storageConfigId=x&folderPath=y
@@ -179,24 +323,25 @@ class UploadService extends BaseApiService {
179
323
  }
180
324
  /**
181
325
  * Build query string from upload options
326
+ * Uses default storageConfigId from app config if not provided in options
182
327
  */
183
328
  buildQueryParams(options) {
184
- if (!options)
185
- return '';
186
329
  const params = new URLSearchParams();
187
- if (options.storageConfigId)
188
- params.append('storageConfigId', options.storageConfigId);
189
- if (options.folderPath)
330
+ // Use provided storageConfigId or fall back to default from config
331
+ const storageConfigId = options?.storageConfigId ?? this.getDefaultConfigId();
332
+ if (storageConfigId)
333
+ params.append('storageConfigId', storageConfigId);
334
+ if (options?.folderPath)
190
335
  params.append('folderPath', options.folderPath);
191
- if (options.maxWidth)
336
+ if (options?.maxWidth)
192
337
  params.append('maxWidth', options.maxWidth.toString());
193
- if (options.maxHeight)
338
+ if (options?.maxHeight)
194
339
  params.append('maxHeight', options.maxHeight.toString());
195
- if (options.quality)
340
+ if (options?.quality)
196
341
  params.append('quality', options.quality.toString());
197
- if (options.format)
342
+ if (options?.format)
198
343
  params.append('format', options.format);
199
- if (options.compress !== undefined)
344
+ if (options?.compress !== undefined)
200
345
  params.append('compress', options.compress.toString());
201
346
  return params.toString();
202
347
  }
@@ -205,20 +350,22 @@ class UploadService extends BaseApiService {
205
350
  * POST /storage/upload/delete-single-file
206
351
  *
207
352
  * @param key - File key/path to delete
208
- * @param storageConfigId - Optional storage config ID (uses default if not provided)
353
+ * @param storageConfigId - Optional storage config ID (uses default from config if not provided)
209
354
  */
210
355
  deleteSingleFile(key, storageConfigId) {
211
- return this.http.post(this.getUrl('upload/delete-single-file'), { key, ...(storageConfigId && { storageConfigId }) });
356
+ const configId = storageConfigId ?? this.getDefaultConfigId();
357
+ return this.http.post(this.getUrl('upload/delete-single-file'), { key, ...(configId && { storageConfigId: configId }) });
212
358
  }
213
359
  /**
214
360
  * Delete multiple files
215
361
  * POST /storage/upload/delete-multiple-file
216
362
  *
217
363
  * @param keys - Array of file keys/paths to delete
218
- * @param storageConfigId - Optional storage config ID (uses default if not provided)
364
+ * @param storageConfigId - Optional storage config ID (uses default from config if not provided)
219
365
  */
220
366
  deleteMultipleFiles(keys, storageConfigId) {
221
- return this.http.post(this.getUrl('upload/delete-multiple-file'), { keys, ...(storageConfigId && { storageConfigId }) });
367
+ const configId = storageConfigId ?? this.getDefaultConfigId();
368
+ return this.http.post(this.getUrl('upload/delete-multiple-file'), { keys, ...(configId && { storageConfigId: configId }) });
222
369
  }
223
370
  /**
224
371
  * Get file download URL
@@ -230,10 +377,153 @@ class UploadService extends BaseApiService {
230
377
  getFileUrl(key) {
231
378
  return this.getUrl(`upload/file/${key}`);
232
379
  }
233
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: UploadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
234
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: UploadService, providedIn: 'root' });
380
+ /**
381
+ * Upload file and register in file manager
382
+ * Returns file manager ID (UUID) for storing in references like profilePictureId
383
+ *
384
+ * @param file - File to upload
385
+ * @param options - Upload options (storage config, compression, etc.)
386
+ * @returns Observable with file manager record (id is the UUID to store)
387
+ */
388
+ uploadAndRegister(file, options) {
389
+ return this.uploadSingleFile(file, options).pipe(switchMap((uploadResponse) => {
390
+ if (!uploadResponse.success || !uploadResponse.data) {
391
+ throw new Error(uploadResponse.message || 'Upload failed');
392
+ }
393
+ const uploadedFile = uploadResponse.data;
394
+ const storageConfigId = options?.storageConfigId ?? this.getDefaultConfigId();
395
+ // Create file manager record using proper DTO
396
+ return this.fileManagerApi.createFile({
397
+ name: uploadedFile.name,
398
+ key: uploadedFile.key,
399
+ size: String(uploadedFile.size),
400
+ contentType: uploadedFile.contentType,
401
+ isPrivate: options?.isPrivate ?? false,
402
+ folderId: options?.folderId,
403
+ storageConfigId,
404
+ });
405
+ }));
406
+ }
407
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
408
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, providedIn: 'root' });
409
+ }
410
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, decorators: [{
411
+ type: Injectable,
412
+ args: [{
413
+ providedIn: 'root',
414
+ }]
415
+ }], ctorParameters: () => [] });
416
+
417
+ /**
418
+ * Profile Upload Provider Adapter
419
+ *
420
+ * Implements IProfileUploadProvider interface for ng-auth profile page.
421
+ * Uses UploadService.uploadAndRegister() to upload files and register in file manager.
422
+ *
423
+ * @example
424
+ * // In app.config.ts
425
+ * providers: [
426
+ * ...provideStorageProviders(),
427
+ * ]
428
+ */
429
+ class ProfileUploadProviderAdapter {
430
+ uploadService = inject(UploadService);
431
+ /**
432
+ * Upload profile picture and register in file manager
433
+ * Returns file manager ID (UUID) for storing in user.profilePictureId
434
+ */
435
+ uploadProfilePicture(file, options) {
436
+ return this.uploadService
437
+ .uploadAndRegister(file, {
438
+ folderPath: options?.folderPath ?? 'profile-pictures',
439
+ compress: options?.compress ?? true,
440
+ maxWidth: options?.maxWidth ?? 400,
441
+ maxHeight: options?.maxHeight ?? 400,
442
+ })
443
+ .pipe(map((response) => ({
444
+ success: response.success,
445
+ message: response.message,
446
+ data: response.data
447
+ ? {
448
+ id: response.data.id,
449
+ name: response.data.name,
450
+ key: response.data.key,
451
+ size: parseFloat(response.data.size) * 1024, // Convert KB to bytes
452
+ contentType: response.data.contentType,
453
+ }
454
+ : undefined,
455
+ })));
456
+ }
457
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ProfileUploadProviderAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
458
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ProfileUploadProviderAdapter });
459
+ }
460
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ProfileUploadProviderAdapter, decorators: [{
461
+ type: Injectable
462
+ }] });
463
+
464
+ /**
465
+ * Provide Storage Providers
466
+ *
467
+ * Registers storage-related provider implementations.
468
+ * Call this in app.config.ts to enable storage features in other packages.
469
+ *
470
+ * @example
471
+ * // In app.config.ts
472
+ * export const appConfig: ApplicationConfig = {
473
+ * providers: [
474
+ * ...provideStorageProviders(),
475
+ * ],
476
+ * };
477
+ *
478
+ * @returns Array of providers for storage functionality
479
+ */
480
+ function provideStorageProviders() {
481
+ return [
482
+ {
483
+ provide: PROFILE_UPLOAD_PROVIDER,
484
+ useClass: ProfileUploadProviderAdapter,
485
+ },
486
+ ];
487
+ }
488
+
489
+ /**
490
+ * Folder API Service
491
+ * Handles folder CRUD operations
492
+ * Endpoint: POST /storage/folder/*
493
+ *
494
+ * Note: Company feature enabled - all queries automatically scoped to user's company
495
+ */
496
+ class FolderApiService extends ApiResourceService {
497
+ constructor() {
498
+ const http = inject(HttpClient);
499
+ super('storage/folder', http);
500
+ }
501
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FolderApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
502
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FolderApiService, providedIn: 'root' });
503
+ }
504
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FolderApiService, decorators: [{
505
+ type: Injectable,
506
+ args: [{
507
+ providedIn: 'root',
508
+ }]
509
+ }], ctorParameters: () => [] });
510
+
511
+ /**
512
+ * Storage Config API Service
513
+ * Handles storage configuration CRUD operations
514
+ * Endpoint: POST /storage/storage-config/*
515
+ *
516
+ * Note: Company feature enabled - all queries automatically scoped to user's company
517
+ */
518
+ class StorageConfigApiService extends ApiResourceService {
519
+ constructor() {
520
+ const http = inject(HttpClient);
521
+ super('storage/storage-config', http);
522
+ }
523
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageConfigApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
524
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageConfigApiService, providedIn: 'root' });
235
525
  }
236
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: UploadService, decorators: [{
526
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageConfigApiService, decorators: [{
237
527
  type: Injectable,
238
528
  args: [{
239
529
  providedIn: 'root',
@@ -257,15 +547,15 @@ const STORAGE_ROUTES = [
257
547
  children: [
258
548
  {
259
549
  path: '',
260
- loadComponent: () => import('./flusys-ng-storage-file-manager-list.component-5gqfPzR3.mjs').then((m) => m.FileManagerListComponent),
550
+ loadComponent: () => import('./flusys-ng-storage-file-manager-list.component-DDOFAPRD.mjs').then((m) => m.FileManagerListComponent),
261
551
  },
262
552
  {
263
553
  path: 'new',
264
- loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-BI0hgXeI.mjs').then((m) => m.FileManagerFormComponent),
554
+ loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-CMS_ddbs.mjs').then((m) => m.FileManagerFormComponent),
265
555
  },
266
556
  {
267
557
  path: ':id',
268
- loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-BI0hgXeI.mjs').then((m) => m.FileManagerFormComponent),
558
+ loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-CMS_ddbs.mjs').then((m) => m.FileManagerFormComponent),
269
559
  },
270
560
  ],
271
561
  },
@@ -275,15 +565,15 @@ const STORAGE_ROUTES = [
275
565
  children: [
276
566
  {
277
567
  path: '',
278
- loadComponent: () => import('./flusys-ng-storage-folder-list.component-CN_mTtS5.mjs').then((m) => m.FolderListComponent),
568
+ loadComponent: () => import('./flusys-ng-storage-folder-list.component-CzONANHt.mjs').then((m) => m.FolderListComponent),
279
569
  },
280
570
  {
281
571
  path: 'new',
282
- loadComponent: () => import('./flusys-ng-storage-folder-form.component-Cw2aLZkb.mjs').then((m) => m.FolderFormComponent),
572
+ loadComponent: () => import('./flusys-ng-storage-folder-form.component-ClYATZkh.mjs').then((m) => m.FolderFormComponent),
283
573
  },
284
574
  {
285
575
  path: ':id',
286
- loadComponent: () => import('./flusys-ng-storage-folder-form.component-Cw2aLZkb.mjs').then((m) => m.FolderFormComponent),
576
+ loadComponent: () => import('./flusys-ng-storage-folder-form.component-ClYATZkh.mjs').then((m) => m.FolderFormComponent),
287
577
  },
288
578
  ],
289
579
  },
@@ -293,15 +583,15 @@ const STORAGE_ROUTES = [
293
583
  children: [
294
584
  {
295
585
  path: '',
296
- loadComponent: () => import('./flusys-ng-storage-storage-config-list.component-tmiE2CpM.mjs').then((m) => m.StorageConfigListComponent),
586
+ loadComponent: () => import('./flusys-ng-storage-storage-config-list.component-BbBx0iSL.mjs').then((m) => m.StorageConfigListComponent),
297
587
  },
298
588
  {
299
589
  path: 'new',
300
- loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-DHzsQ2Mc.mjs').then((m) => m.StorageConfigFormComponent),
590
+ loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-CODLLCtg.mjs').then((m) => m.StorageConfigFormComponent),
301
591
  },
302
592
  {
303
593
  path: ':id',
304
- loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-DHzsQ2Mc.mjs').then((m) => m.StorageConfigFormComponent),
594
+ loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-CODLLCtg.mjs').then((m) => m.StorageConfigFormComponent),
305
595
  },
306
596
  ],
307
597
  },
@@ -343,58 +633,56 @@ class StorageContainerComponent {
343
633
  route: 'configs',
344
634
  },
345
635
  ];
346
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: StorageContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
347
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: StorageContainerComponent, isStandalone: true, selector: "lib-storage-container", ngImport: i0, template: `
348
- <div class="storage-page p-4">
349
- <div class="storage-header mb-4">
350
- <h1 class="text-2xl font-bold m-0">Storage</h1>
351
- <p class="text-gray-500 mt-1">Manage files, folders, and storage configurations</p>
636
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
637
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: StorageContainerComponent, isStandalone: true, selector: "lib-storage-container", ngImport: i0, template: `
638
+ <div class="p-2 sm:p-4">
639
+ <div class="mb-4">
640
+ <h1 class="text-xl sm:text-2xl font-bold m-0">Storage</h1>
641
+ <p class="text-muted-color mt-1 text-sm sm:text-base">Manage files, folders, and storage configurations</p>
352
642
  </div>
353
643
 
354
- <div class="storage-tabs flex gap-1 mb-4 border-b border-gray-200">
644
+ <div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
355
645
  @for (tab of tabs; track tab.id) {
356
646
  <a
357
647
  [routerLink]="tab.route"
358
- routerLinkActive="active"
359
- class="tab-link flex items-center gap-2 px-4 py-2 rounded-t-lg cursor-pointer transition-colors">
648
+ routerLinkActive="tab-active"
649
+ class="flex items-center gap-2 px-4 py-2 rounded-t-lg cursor-pointer transition-colors text-muted-color no-underline border-b-2 border-transparent -mb-px hover:bg-surface-hover [&.tab-active]:text-primary [&.tab-active]:border-primary [&.tab-active]:bg-surface-ground whitespace-nowrap shrink-0"
650
+ >
360
651
  <i [class]="tab.icon"></i>
361
652
  <span>{{ tab.label }}</span>
362
653
  </a>
363
654
  }
364
655
  </div>
365
656
 
366
- <div class="storage-content">
367
- <router-outlet />
368
- </div>
657
+ <router-outlet />
369
658
  </div>
370
- `, isInline: true, styles: [".tab-link{color:var(--text-color-secondary, #6b7280);text-decoration:none;border-bottom:2px solid transparent;margin-bottom:-1px}.tab-link.active{color:var(--primary-color, #3b82f6);border-bottom-color:var(--primary-color, #3b82f6);background-color:var(--surface-ground, #f9fafb)}.tab-link:hover:not(.active){background-color:var(--surface-hover, #f3f4f6)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }] });
659
+ `, isInline: true, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
371
660
  }
372
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: StorageContainerComponent, decorators: [{
661
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageContainerComponent, decorators: [{
373
662
  type: Component,
374
- args: [{ selector: 'lib-storage-container', standalone: true, imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive], template: `
375
- <div class="storage-page p-4">
376
- <div class="storage-header mb-4">
377
- <h1 class="text-2xl font-bold m-0">Storage</h1>
378
- <p class="text-gray-500 mt-1">Manage files, folders, and storage configurations</p>
663
+ args: [{ selector: 'lib-storage-container', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive], template: `
664
+ <div class="p-2 sm:p-4">
665
+ <div class="mb-4">
666
+ <h1 class="text-xl sm:text-2xl font-bold m-0">Storage</h1>
667
+ <p class="text-muted-color mt-1 text-sm sm:text-base">Manage files, folders, and storage configurations</p>
379
668
  </div>
380
669
 
381
- <div class="storage-tabs flex gap-1 mb-4 border-b border-gray-200">
670
+ <div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
382
671
  @for (tab of tabs; track tab.id) {
383
672
  <a
384
673
  [routerLink]="tab.route"
385
- routerLinkActive="active"
386
- class="tab-link flex items-center gap-2 px-4 py-2 rounded-t-lg cursor-pointer transition-colors">
674
+ routerLinkActive="tab-active"
675
+ class="flex items-center gap-2 px-4 py-2 rounded-t-lg cursor-pointer transition-colors text-muted-color no-underline border-b-2 border-transparent -mb-px hover:bg-surface-hover [&.tab-active]:text-primary [&.tab-active]:border-primary [&.tab-active]:bg-surface-ground whitespace-nowrap shrink-0"
676
+ >
387
677
  <i [class]="tab.icon"></i>
388
678
  <span>{{ tab.label }}</span>
389
679
  </a>
390
680
  }
391
681
  </div>
392
682
 
393
- <div class="storage-content">
394
- <router-outlet />
395
- </div>
683
+ <router-outlet />
396
684
  </div>
397
- `, styles: [".tab-link{color:var(--text-color-secondary, #6b7280);text-decoration:none;border-bottom:2px solid transparent;margin-bottom:-1px}.tab-link.active{color:var(--primary-color, #3b82f6);border-bottom-color:var(--primary-color, #3b82f6);background-color:var(--surface-ground, #f9fafb)}.tab-link:hover:not(.active){background-color:var(--surface-hover, #f3f4f6)}\n"] }]
685
+ `, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"] }]
398
686
  }] });
399
687
 
400
688
  var storageContainer_component = /*#__PURE__*/Object.freeze({
@@ -411,5 +699,5 @@ var storageContainer_component = /*#__PURE__*/Object.freeze({
411
699
  * Generated bundle index. Do not edit.
412
700
  */
413
701
 
414
- export { FileLocationEnum, FileManagerApiService, FolderApiService, ImageFormat, STORAGE_ROUTES, StorageConfigApiService, StorageContainerComponent, UploadService };
702
+ export { BaseStorageListPage, BaseStoragePage, FileLocationEnum, FileManagerApiService, FolderApiService, ImageFormat, ProfileUploadProviderAdapter, STORAGE_PROVIDERS, STORAGE_ROUTES, StorageConfigApiService, StorageContainerComponent, UploadService, formatFileSize, getFileIcon, getProviderIcon, getProviderLabel, getProviderSeverity, provideStorageProviders };
415
703
  //# sourceMappingURL=flusys-ng-storage.mjs.map