@flusys/ng-storage 1.1.0 → 1.2.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/README.md +36 -762
  2. package/fesm2022/flusys-ng-storage-file-manager-form.component-DhqZmpuX.mjs +671 -0
  3. package/fesm2022/flusys-ng-storage-file-manager-form.component-DhqZmpuX.mjs.map +1 -0
  4. package/fesm2022/flusys-ng-storage-file-manager-list.component-nZDj1UfS.mjs +464 -0
  5. package/fesm2022/flusys-ng-storage-file-manager-list.component-nZDj1UfS.mjs.map +1 -0
  6. package/fesm2022/flusys-ng-storage-folder-form.component-Bru9NQyr.mjs +218 -0
  7. package/fesm2022/flusys-ng-storage-folder-form.component-Bru9NQyr.mjs.map +1 -0
  8. package/fesm2022/flusys-ng-storage-folder-list.component-fuJMWYWZ.mjs +263 -0
  9. package/fesm2022/flusys-ng-storage-folder-list.component-fuJMWYWZ.mjs.map +1 -0
  10. package/fesm2022/flusys-ng-storage-storage-config-form.component-Da-pWR7d.mjs +891 -0
  11. package/fesm2022/flusys-ng-storage-storage-config-form.component-Da-pWR7d.mjs.map +1 -0
  12. package/fesm2022/flusys-ng-storage-storage-config-list.component-D59X4sae.mjs +288 -0
  13. package/fesm2022/flusys-ng-storage-storage-config-list.component-D59X4sae.mjs.map +1 -0
  14. package/fesm2022/flusys-ng-storage.mjs +533 -216
  15. package/fesm2022/flusys-ng-storage.mjs.map +1 -1
  16. package/package.json +11 -10
  17. package/types/flusys-ng-storage.d.ts +99 -48
  18. package/fesm2022/flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs +0 -484
  19. package/fesm2022/flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs.map +0 -1
  20. package/fesm2022/flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs +0 -304
  21. package/fesm2022/flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs.map +0 -1
  22. package/fesm2022/flusys-ng-storage-folder-form.component-BTQGEqNu.mjs +0 -155
  23. package/fesm2022/flusys-ng-storage-folder-form.component-BTQGEqNu.mjs.map +0 -1
  24. package/fesm2022/flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs +0 -194
  25. package/fesm2022/flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs.map +0 -1
  26. package/fesm2022/flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs +0 -463
  27. package/fesm2022/flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs.map +0 -1
  28. package/fesm2022/flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs +0 -187
  29. package/fesm2022/flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs.map +0 -1
@@ -1,14 +1,166 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, signal, computed, Directive, Injectable, ChangeDetectionStrategy, Component } from '@angular/core';
3
- import { Router, RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
2
+ import { inject, signal, computed, Directive, DestroyRef, Injectable, Component } from '@angular/core';
3
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
+ import { Router, RouterOutlet } from '@angular/router';
4
5
  import { APP_CONFIG, DEFAULT_APP_NAME, BaseApiService, getDefaultStorageConfigId } from '@flusys/ng-core';
5
6
  import { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';
6
- import { BaseListPage, ApiResourceService, PROFILE_UPLOAD_PROVIDER, permissionGuard, FILE_PERMISSIONS, FOLDER_PERMISSIONS, STORAGE_CONFIG_PERMISSIONS, PermissionValidatorService } from '@flusys/ng-shared';
7
- import { MessageService } from 'primeng/api';
8
- import { map } from 'rxjs/operators';
9
- import { firstValueFrom, switchMap } from 'rxjs';
10
- import { HttpClient } from '@angular/common/http';
11
- import { CommonModule } from '@angular/common';
7
+ import { TranslateService, ApiResourceService, FILE_PROVIDER, permissionGuard, FILE_PERMISSIONS, FOLDER_PERMISSIONS, STORAGE_CONFIG_PERMISSIONS, resolveTranslationModule, SHARED_MESSAGES, PermissionValidatorService, RouterTabsComponent, TranslatePipe } from '@flusys/ng-shared';
8
+ import { MessageService, ConfirmationService } from '@flusys/ng-ui';
9
+ import { from, switchMap, map } from 'rxjs';
10
+
11
+ const STORAGE_MESSAGES = {
12
+ // File entity CRUD (API keys)
13
+ 'file.create.success': 'File created successfully',
14
+ 'file.create.many.success': '{{count}} files created successfully',
15
+ 'file.get.success': 'File retrieved successfully',
16
+ 'file.get.by.ids.success': 'Files retrieved successfully by Ids',
17
+ 'file.get.by.filter.success': 'Files retrieved successfully by filter',
18
+ 'file.get.all.success': 'Files retrieved successfully',
19
+ 'file.update.success': 'File updated successfully',
20
+ 'file.update.many.success': '{{count}} files updated successfully',
21
+ 'file.delete.success': 'File deleted successfully',
22
+ 'file.restore.success': 'File restored successfully',
23
+ 'file.not.found': 'File not found',
24
+ // Folder entity CRUD (API keys)
25
+ 'folder.create.success': 'Folder created successfully',
26
+ 'folder.create.many.success': '{{count}} folders created successfully',
27
+ 'folder.get.success': 'Folder retrieved successfully',
28
+ 'folder.get.by.ids.success': 'Folders retrieved successfully by Ids',
29
+ 'folder.get.by.filter.success': 'Folders retrieved successfully by filter',
30
+ 'folder.get.all.success': 'Folders retrieved successfully',
31
+ 'folder.update.success': 'Folder updated successfully',
32
+ 'folder.update.many.success': '{{count}} folders updated successfully',
33
+ 'folder.delete.success': 'Folder deleted successfully',
34
+ 'folder.restore.success': 'Folder restored successfully',
35
+ 'folder.not.found': 'Folder not found',
36
+ // Storage config CRUD (API keys)
37
+ 'storage.config.create.success': 'Storage configuration created successfully',
38
+ 'storage.config.create.many.success': '{{count}} storage configurations created successfully',
39
+ 'storage.config.get.success': 'Storage configuration retrieved successfully',
40
+ 'storage.config.get.by.ids.success': 'Storage configurations retrieved successfully by Ids',
41
+ 'storage.config.get.by.filter.success': 'Storage configurations retrieved successfully by filter',
42
+ 'storage.config.get.all.success': 'Storage configurations retrieved successfully',
43
+ 'storage.config.update.success': 'Storage configuration updated successfully',
44
+ 'storage.config.update.many.success': '{{count}} storage configurations updated successfully',
45
+ 'storage.config.delete.success': 'Storage configuration deleted successfully',
46
+ 'storage.config.restore.success': 'Storage configuration restored successfully',
47
+ // Upload (API keys)
48
+ 'upload.success': 'File uploaded successfully',
49
+ 'upload.many.success': 'Files uploaded successfully',
50
+ 'upload.file.too.large': 'File size ({{fileSize}}MB) exceeds maximum {{maxSize}}MB',
51
+ 'upload.invalid.type': 'File type "{{mimeType}}" not allowed. Allowed: {{allowedTypes}}',
52
+ 'upload.no.files.provided': 'No files provided',
53
+ 'upload.no.file.path': 'File path is required',
54
+ 'upload.invalid.file.path': 'Invalid file path',
55
+ 'upload.config.not.found': 'Upload configuration not found',
56
+ // pages/storage-container/storage-container.component.ts
57
+ 'storage.title': 'Storage',
58
+ 'storage.subtitle': 'Manage files, folders, and storage configurations',
59
+ 'storage.file.title': 'Files',
60
+ 'storage.folder.title': 'Folder Setup',
61
+ 'storage.config.title': 'Storage Configurations',
62
+ // pages/storage-config/storage-config-list.component.ts
63
+ 'storage.button.new.configuration': 'New Configuration',
64
+ 'storage.table.provider': 'Provider',
65
+ 'storage.table.created': 'Created',
66
+ 'storage.empty.configs.in.company': 'No storage configurations found in current company',
67
+ 'storage.empty.configs': 'No storage configurations found',
68
+ 'storage.config.load.failed': 'Failed to load storage configurations',
69
+ 'storage.config.delete.title': 'Delete Configuration',
70
+ // pages/storage-config/storage-config-form.component.ts
71
+ 'storage.config.edit.config': 'Edit Storage Configuration',
72
+ 'storage.config.new.config': 'New Configuration',
73
+ 'storage.config.config.name.required': 'Configuration Name *',
74
+ 'storage.config.config.name.placeholder': 'e.g., Production AWS S3',
75
+ 'storage.config.provider.required': 'Storage Provider *',
76
+ 'storage.set.as.default': 'Set as Default',
77
+ 'storage.aws.title': 'AWS S3 Configuration',
78
+ 'storage.config.region.required': 'AWS Region *',
79
+ 'storage.aws.region.placeholder': 'us-east-1',
80
+ 'storage.config.bucket.required': 'Bucket Name *',
81
+ 'storage.aws.bucket.placeholder': 'my-bucket',
82
+ 'storage.config.access.key.required': 'Access Key ID *',
83
+ 'storage.config.secret.key.required': 'Secret Access Key *',
84
+ 'storage.config.endpoint.optional': 'Custom Endpoint (Optional)',
85
+ 'storage.config.endpoint.placeholder': 'https://s3.custom-endpoint.com',
86
+ 'storage.azure.title': 'Azure Blob Configuration',
87
+ 'storage.azure.account.name.required': 'Account Name *',
88
+ 'storage.azure.container.name.required': 'Container Name *',
89
+ 'storage.azure.account.key.required': 'Account Key *',
90
+ 'storage.sftp.title': 'SFTP Configuration',
91
+ 'storage.sftp.host.required': 'Host *',
92
+ 'storage.sftp.host.placeholder': 'sftp.example.com',
93
+ 'storage.sftp.port.required': 'Port *',
94
+ 'storage.sftp.port.placeholder': '22',
95
+ 'storage.sftp.username.required': 'Username *',
96
+ 'storage.sftp.password.required': 'Password *',
97
+ 'storage.sftp.base.path.required': 'Base Path *',
98
+ 'storage.sftp.base.path.placeholder': '/uploads',
99
+ 'storage.local.title': 'Local Storage Configuration',
100
+ 'storage.local.base.path.required': 'Base Path *',
101
+ 'storage.local.base.path.placeholder': '/var/www/uploads',
102
+ 'storage.config.endpoint': 'Endpoint URL',
103
+ 'storage.validation.config.name.required': 'Configuration name is required',
104
+ // pages/file-manager/file-manager-list.component.ts
105
+ 'storage.file.manager.title': 'File Manager',
106
+ 'storage.button.upload.file': 'Upload File',
107
+ 'storage.table.size': 'Size',
108
+ 'storage.table.location': 'Location',
109
+ 'storage.table.config': 'Config',
110
+ 'storage.table.private': 'Private',
111
+ 'storage.private': 'Private',
112
+ 'storage.public': 'Public',
113
+ 'storage.tooltip.trash': 'Trash',
114
+ 'storage.empty.files.in.company': 'No files found in current company',
115
+ 'storage.empty.files': 'No files found',
116
+ 'storage.file.url.not.available': 'File URL is not available',
117
+ 'storage.delete.move.to.trash.confirm': 'Move "{{name}}" to trash? You can recover it later.',
118
+ 'storage.delete.move.to.trash': 'Move to Trash',
119
+ 'storage.delete.permanent.delete.confirm': 'Permanently delete "{{name}}"? This action CANNOT be undone and will delete the file from storage.',
120
+ 'storage.delete.permanent.delete': 'Permanent Delete',
121
+ 'storage.delete.permanent.delete.button': 'Delete Permanently',
122
+ // pages/file-manager/file-manager-form.component.ts
123
+ 'storage.file.manager.edit.file': 'Edit File',
124
+ 'storage.file.manager.upload.file': 'Upload File',
125
+ 'storage.config.select.config': 'Select storage configuration...',
126
+ 'storage.config.no.configs': 'No storage configurations found. Please create one first.',
127
+ 'storage.folder.optional': 'Folder (Optional)',
128
+ 'storage.folder.no.folder': 'No folder (root level)',
129
+ 'storage.folder.no.folders.available': 'No folders available. File will be uploaded to root level.',
130
+ 'storage.folder.no.folders.edit': 'No folders available.',
131
+ 'storage.file.select.required': 'Select File *',
132
+ 'storage.button.choose.file': 'Choose File',
133
+ 'storage.upload.drag.drop': 'Drag and drop file here or click to browse',
134
+ 'storage.upload.max.size': 'Maximum file size: {{size}}',
135
+ 'storage.upload.please.select': 'Please select a file to upload',
136
+ 'storage.file.name.required': 'File Name *',
137
+ 'storage.file.placeholder.edit': 'Enter file name',
138
+ 'storage.file.placeholder': 'Will be auto-filled from selected file',
139
+ 'storage.file.private': 'Private File',
140
+ 'storage.file.private.hint': 'Only users with permission can access',
141
+ 'storage.file.public.hint': 'File is publicly accessible',
142
+ 'storage.validation.file.name.required': 'File name is required',
143
+ 'storage.error.no.file': 'Please select a file',
144
+ 'storage.error.no.config': 'Please select a storage configuration',
145
+ 'storage.upload.failed': 'Failed to upload file',
146
+ // pages/folder/folder-list.component.ts
147
+ 'storage.button.new.folder': 'New Folder',
148
+ 'storage.table.slug': 'Slug',
149
+ 'storage.empty.folders.in.company': 'No folders found in current company',
150
+ 'storage.empty.folders': 'No folders found',
151
+ 'storage.folder.delete.title': 'Delete Folder',
152
+ // pages/folder/folder-form.component.ts
153
+ 'storage.folder.edit.folder': 'Edit Folder',
154
+ 'storage.folder.new.folder': 'New Folder',
155
+ 'storage.folder.name.required': 'Folder Name *',
156
+ 'storage.folder.name.placeholder': 'Enter folder name',
157
+ 'storage.validation.folder.name.required': 'Folder name is required',
158
+ // utils/base-storage-page.ts
159
+ 'storage.delete.this.item': 'this item',
160
+ // utils/storage-utils.ts
161
+ 'storage.size.bytes': 'Bytes',
162
+ 'storage.size.gb': 'GB',
163
+ };
12
164
 
13
165
  /**
14
166
  * File Location Enum
@@ -33,47 +185,56 @@ var ImageFormat;
33
185
  ImageFormat["WEBP"] = "webp";
34
186
  })(ImageFormat || (ImageFormat = {}));
35
187
 
188
+ /**
189
+ * Provider label translation keys
190
+ */
191
+ const PROVIDER_LABEL_KEYS = {
192
+ [FileLocationEnum.LOCAL]: 'storage.local.title',
193
+ [FileLocationEnum.AWS]: 'storage.aws.title',
194
+ [FileLocationEnum.AZURE]: 'storage.azure.title',
195
+ [FileLocationEnum.SFTP]: 'storage.sftp.title',
196
+ };
36
197
  /**
37
198
  * Provider configurations for all storage types
38
199
  */
39
200
  const PROVIDER_CONFIGS = {
40
201
  [FileLocationEnum.LOCAL]: {
41
- label: 'Local',
42
- icon: 'pi pi-folder',
202
+ labelKey: 'storage.local.title',
203
+ icon: 'folder',
43
204
  severity: 'secondary',
44
205
  },
45
206
  [FileLocationEnum.AWS]: {
46
- label: 'AWS S3',
47
- icon: 'pi pi-cloud',
207
+ labelKey: 'storage.aws.title',
208
+ icon: 'cloud',
48
209
  severity: 'warn',
49
210
  },
50
211
  [FileLocationEnum.AZURE]: {
51
- label: 'Azure Blob',
52
- icon: 'pi pi-cloud',
212
+ labelKey: 'storage.azure.title',
213
+ icon: 'cloud',
53
214
  severity: 'info',
54
215
  },
55
216
  [FileLocationEnum.SFTP]: {
56
- label: 'SFTP',
57
- icon: 'pi pi-server',
217
+ labelKey: 'storage.sftp.title',
218
+ icon: 'server',
58
219
  severity: 'contrast',
59
220
  },
60
221
  };
61
222
  /**
62
- * Get provider label for display
223
+ * Get provider label translation key
63
224
  */
64
- function getProviderLabel(storage) {
225
+ function getProviderLabelKey(storage) {
65
226
  const enumValue = storage;
66
- return PROVIDER_CONFIGS[enumValue]?.label ?? storage;
227
+ return PROVIDER_CONFIGS[enumValue]?.labelKey ?? storage;
67
228
  }
68
229
  /**
69
230
  * Get provider icon class
70
231
  */
71
232
  function getProviderIcon(storage) {
72
233
  const enumValue = storage;
73
- return PROVIDER_CONFIGS[enumValue]?.icon ?? 'pi pi-database';
234
+ return PROVIDER_CONFIGS[enumValue]?.icon ?? 'database';
74
235
  }
75
236
  /**
76
- * Get provider severity for p-tag
237
+ * Get provider severity for f-tag
77
238
  */
78
239
  function getProviderSeverity(storage) {
79
240
  const enumValue = storage;
@@ -84,38 +245,62 @@ function getProviderSeverity(storage) {
84
245
  */
85
246
  function getFileIcon(contentType) {
86
247
  if (contentType.startsWith('image/'))
87
- return 'pi pi-image';
248
+ return 'image';
88
249
  if (contentType.startsWith('video/'))
89
- return 'pi pi-video';
250
+ return 'video';
90
251
  if (contentType.startsWith('audio/'))
91
- return 'pi pi-volume-up';
252
+ return 'volume-up';
92
253
  if (contentType.includes('pdf'))
93
- return 'pi pi-file-pdf';
254
+ return 'file-pdf';
94
255
  if (contentType.includes('word') || contentType.includes('document'))
95
- return 'pi pi-file-word';
256
+ return 'file-word';
96
257
  if (contentType.includes('excel') || contentType.includes('spreadsheet'))
97
- return 'pi pi-file-excel';
98
- return 'pi pi-file';
258
+ return 'file-excel';
259
+ return 'file';
99
260
  }
261
+ /** File size unit keys for translation */
262
+ const FILE_SIZE_UNIT_KEYS = [
263
+ 'storage.size.bytes',
264
+ 'shared.size.kb',
265
+ 'shared.size.mb',
266
+ 'storage.size.gb',
267
+ ];
100
268
  /**
101
- * Format file size from bytes to human readable string
269
+ * Format file size from bytes to human readable format
270
+ * Returns an object with value and unitKey for translation
102
271
  */
103
- function formatFileSize(bytes) {
272
+ function formatFileSizeWithKey(bytes) {
104
273
  if (bytes <= 0)
105
- return '0 Bytes';
274
+ return { value: '0', unitKey: 'storage.size.bytes' };
106
275
  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];
276
+ const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), FILE_SIZE_UNIT_KEYS.length - 1);
277
+ const value = (Math.round((bytes / Math.pow(k, i)) * 100) / 100).toString();
278
+ return { value, unitKey: FILE_SIZE_UNIT_KEYS[i] };
279
+ }
280
+ /**
281
+ * Format file size from bytes to human readable string
282
+ * @deprecated Use formatFileSizeWithKey for localization support
283
+ */
284
+ function formatFileSize(bytes) {
285
+ const { value, unitKey } = formatFileSizeWithKey(bytes);
286
+ // Fallback to non-translated units for backward compatibility
287
+ const unitMap = {
288
+ 'storage.size.bytes': 'Bytes',
289
+ 'shared.size.kb': 'KB',
290
+ 'shared.size.mb': 'MB',
291
+ 'storage.size.gb': 'GB',
292
+ };
293
+ return `${value} ${unitMap[unitKey]}`;
110
294
  }
111
295
  /**
112
296
  * Storage provider options for dropdown selects
297
+ * Labels are translation keys - components should translate them
113
298
  */
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 },
299
+ const STORAGE_PROVIDER_OPTIONS = [
300
+ { labelKey: 'storage.aws.title', value: FileLocationEnum.AWS },
301
+ { labelKey: 'storage.azure.title', value: FileLocationEnum.AZURE },
302
+ { labelKey: 'storage.sftp.title', value: FileLocationEnum.SFTP },
303
+ { labelKey: 'storage.local.title', value: FileLocationEnum.LOCAL },
119
304
  ];
120
305
 
121
306
  class BaseStoragePage {
@@ -123,43 +308,151 @@ class BaseStoragePage {
123
308
  messageService = inject(MessageService);
124
309
  appConfig = inject(APP_CONFIG);
125
310
  companyContext = inject(LAYOUT_AUTH_STATE);
126
- isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
127
- showCompanyInfo = computed(() => this.appConfig.enableCompanyFeature, ...(ngDevMode ? [{ debugName: "showCompanyInfo" }] : []));
128
- currentCompanyName = computed(() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
129
- showSuccess(detail, summary = 'Success') {
311
+ translateAdapter = inject(TranslateService);
312
+ isLoading = signal(false, /* @ts-ignore */
313
+ ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
314
+ showCompanyInfo = computed(() => this.appConfig.enableCompanyFeature, /* @ts-ignore */
315
+ ...(ngDevMode ? [{ debugName: "showCompanyInfo" }] : /* istanbul ignore next */ []));
316
+ currentCompanyName = computed(() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, /* @ts-ignore */
317
+ ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : /* istanbul ignore next */ []));
318
+ showSuccess(detail, summaryKey = 'shared.success') {
319
+ const summary = this.translateAdapter.translate(summaryKey);
130
320
  this.messageService.add({ severity: 'success', summary, detail });
131
321
  }
132
- showError(detail, summary = 'Error') {
322
+ showError(detail, summaryKey = 'shared.error') {
323
+ const summary = this.translateAdapter.translate(summaryKey);
133
324
  this.messageService.add({ severity: 'error', summary, detail });
134
325
  }
135
326
  navigateTo(path) {
136
327
  this.router.navigate(path);
137
328
  }
138
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseStoragePage, deps: [], target: i0.ɵɵFactoryTarget.Directive });
139
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.5", type: BaseStoragePage, isStandalone: true, ngImport: i0 });
329
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: BaseStoragePage, deps: [], target: i0.ɵɵFactoryTarget.Directive });
330
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.6", type: BaseStoragePage, isStandalone: true, ngImport: i0 });
140
331
  }
141
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseStoragePage, decorators: [{
332
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: BaseStoragePage, decorators: [{
142
333
  type: Directive
143
334
  }] });
144
- class BaseStorageListPage extends BaseListPage {
335
+ class BaseStorageListPage {
336
+ router = inject(Router);
337
+ messageService = inject(MessageService);
338
+ appConfig = inject(APP_CONFIG);
339
+ confirmationService = inject(ConfirmationService);
340
+ destroyRef = inject(DestroyRef);
341
+ translateAdapter = inject(TranslateService);
145
342
  companyContext = inject(LAYOUT_AUTH_STATE);
146
- pageSize = signal(50, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
343
+ items = signal([], /* @ts-ignore */
344
+ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
345
+ isLoading = signal(false, /* @ts-ignore */
346
+ ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
347
+ total = signal(0, /* @ts-ignore */
348
+ ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
349
+ pageSize = signal(50, /* @ts-ignore */
350
+ ...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
351
+ first = signal(0, /* @ts-ignore */
352
+ ...(ngDevMode ? [{ debugName: "first" }] : /* istanbul ignore next */ []));
353
+ currentPage = computed(() => Math.floor(this.first() / this.pageSize()), /* @ts-ignore */
354
+ ...(ngDevMode ? [{ debugName: "currentPage" }] : /* istanbul ignore next */ []));
355
+ showCompanyInfo = computed(() => this.appConfig.enableCompanyFeature, /* @ts-ignore */
356
+ ...(ngDevMode ? [{ debugName: "showCompanyInfo" }] : /* istanbul ignore next */ []));
147
357
  totalRecords = this.total;
148
- currentCompanyName = computed(() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
358
+ currentCompanyName = computed(() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, /* @ts-ignore */
359
+ ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : /* istanbul ignore next */ []));
149
360
  getDeleteConfirmMessage(item) {
150
- const name = item.name ?? 'this item';
151
- return `Are you sure you want to delete "${name}"?`;
361
+ const itemName = item.name;
362
+ const name = itemName ?? this.translateAdapter.translate('storage.delete.this.item');
363
+ return this.translateAdapter.translate('shared.confirm.delete.item', { name });
364
+ }
365
+ onCreate() {
366
+ this.router.navigate([this.getResourceRoute(), 'new']);
367
+ }
368
+ onEdit(id) {
369
+ this.router.navigate([this.getResourceRoute(), id]);
370
+ }
371
+ onPageChange(event) {
372
+ this.first.set(event.first ?? 0);
373
+ this.pageSize.set(event.rows ?? 10);
374
+ this.loadData();
375
+ }
376
+ getPaginationParams() {
377
+ return { currentPage: this.currentPage(), pageSize: this.pageSize() };
378
+ }
379
+ showSuccess(detail, summary) {
380
+ this.messageService.add({
381
+ severity: 'success',
382
+ summary: summary ?? this.t('shared.success'),
383
+ detail,
384
+ });
385
+ }
386
+ showError(detail, summary) {
387
+ this.messageService.add({
388
+ severity: 'error',
389
+ summary: summary ?? this.t('shared.error'),
390
+ detail,
391
+ });
392
+ }
393
+ showInfo(detail, summary) {
394
+ this.messageService.add({
395
+ severity: 'info',
396
+ summary: summary ?? this.t('shared.info'),
397
+ detail,
398
+ });
399
+ }
400
+ showWarn(detail, summary) {
401
+ this.messageService.add({
402
+ severity: 'warn',
403
+ summary: summary ?? this.t('shared.warning'),
404
+ detail,
405
+ });
406
+ }
407
+ onDelete(item, idGetter, deleteApiCall, options) {
408
+ this.confirmationService.confirm({
409
+ message: this.getDeleteConfirmMessage(item),
410
+ header: options?.header ?? this.t('shared.confirm.delete.header'),
411
+ icon: 'exclamation-triangle',
412
+ acceptButtonStyleClass: 'p-button-danger',
413
+ accept: () => {
414
+ from(deleteApiCall(idGetter(item)))
415
+ .pipe(takeUntilDestroyed(this.destroyRef))
416
+ .subscribe({
417
+ next: (res) => {
418
+ const r = res;
419
+ this.showSuccess(r?.messageKey ? this.t(r.messageKey, r.messageVariables) : (r?.message ?? ''));
420
+ this.loadData();
421
+ },
422
+ });
423
+ },
424
+ });
425
+ }
426
+ async onDeleteAsync(item, idGetter, deleteApiCall, options) {
427
+ this.confirmationService.confirm({
428
+ message: this.getDeleteConfirmMessage(item),
429
+ header: options?.header ?? this.t('shared.confirm.delete.header'),
430
+ icon: 'exclamation-triangle',
431
+ acceptButtonStyleClass: 'p-button-danger',
432
+ accept: async () => {
433
+ const res = await deleteApiCall(idGetter(item));
434
+ const r = res;
435
+ this.showSuccess(r?.messageKey ? this.t(r.messageKey, r.messageVariables) : (r?.message ?? ''));
436
+ await this.loadData();
437
+ },
438
+ });
439
+ }
440
+ navigateTo(path) {
441
+ this.router.navigate(path);
152
442
  }
153
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseStorageListPage, deps: null, target: i0.ɵɵFactoryTarget.Directive });
154
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.5", type: BaseStorageListPage, isStandalone: true, usesInheritance: true, ngImport: i0 });
443
+ t(key, variables) {
444
+ return this.translateAdapter?.translate(key, variables) ?? key;
445
+ }
446
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: BaseStorageListPage, deps: [], target: i0.ɵɵFactoryTarget.Directive });
447
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.6", type: BaseStorageListPage, isStandalone: true, ngImport: i0 });
155
448
  }
156
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseStorageListPage, decorators: [{
449
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: BaseStorageListPage, decorators: [{
157
450
  type: Directive
158
451
  }] });
159
452
 
160
453
  class FileManagerApiService extends ApiResourceService {
161
454
  constructor() {
162
- super('file-manager', inject(HttpClient), 'storage');
455
+ super('file-manager', 'storage');
163
456
  }
164
457
  getFiles(fileIds) {
165
458
  const requests = fileIds.map((id) => ({ id }));
@@ -168,17 +461,59 @@ class FileManagerApiService extends ApiResourceService {
168
461
  createFile(dto) {
169
462
  return this.http.post(`${this.baseUrl}/insert`, dto);
170
463
  }
171
- async createFileAsync(dto) {
172
- return firstValueFrom(this.createFile(dto));
464
+ createFiles(dtos) {
465
+ return this.http.post(`${this.baseUrl}/insert-many`, dtos);
173
466
  }
174
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileManagerApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
175
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileManagerApiService, providedIn: 'root' });
467
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: FileManagerApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
468
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: FileManagerApiService, providedIn: 'root' });
176
469
  }
177
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileManagerApiService, decorators: [{
470
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: FileManagerApiService, decorators: [{
178
471
  type: Injectable,
179
472
  args: [{ providedIn: 'root' }]
180
473
  }], ctorParameters: () => [] });
181
474
 
475
+ /**
476
+ * Folder API Service
477
+ * Handles folder CRUD operations
478
+ * Endpoint: POST /storage/folder/*
479
+ *
480
+ * Note: Company feature enabled - all queries automatically scoped to user's company
481
+ */
482
+ class FolderApiService extends ApiResourceService {
483
+ constructor() {
484
+ super('folder', 'storage');
485
+ }
486
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: FolderApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
487
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: FolderApiService, providedIn: 'root' });
488
+ }
489
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: FolderApiService, decorators: [{
490
+ type: Injectable,
491
+ args: [{
492
+ providedIn: 'root',
493
+ }]
494
+ }], ctorParameters: () => [] });
495
+
496
+ /**
497
+ * Storage Config API Service
498
+ * Handles storage configuration CRUD operations
499
+ * Endpoint: POST /storage/storage-config/*
500
+ *
501
+ * Note: Company feature enabled - all queries automatically scoped to user's company
502
+ */
503
+ class StorageConfigApiService extends ApiResourceService {
504
+ constructor() {
505
+ super('storage-config', 'storage');
506
+ }
507
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: StorageConfigApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
508
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: StorageConfigApiService, providedIn: 'root' });
509
+ }
510
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: StorageConfigApiService, decorators: [{
511
+ type: Injectable,
512
+ args: [{
513
+ providedIn: 'root',
514
+ }]
515
+ }], ctorParameters: () => [] });
516
+
182
517
  /**
183
518
  * Upload Service - File upload/delete operations
184
519
  * Endpoint: POST /storage/upload/*
@@ -220,23 +555,40 @@ class UploadService extends BaseApiService {
220
555
  uploadAndRegister(file, options) {
221
556
  return this.uploadSingleFile(file, options).pipe(switchMap((response) => {
222
557
  if (!response.success || !response.data) {
223
- throw new Error(response.message || 'Upload failed');
558
+ throw new Error(response.message || 'upload.failed');
559
+ }
560
+ return this.fileManagerApi.createFile(this.toCreateFileDto(response.data, options));
561
+ }));
562
+ }
563
+ /** Upload multiple files and register in file manager - batch upload (2 API calls total) */
564
+ uploadAndRegisterMultiple(files, options) {
565
+ return this.uploadMultipleFiles(files, options).pipe(switchMap((response) => {
566
+ if (!response.success || !response.data) {
567
+ throw new Error(response.message || 'upload.failed');
224
568
  }
225
- return this.fileManagerApi.createFile({
226
- name: response.data.name,
227
- key: response.data.key,
228
- size: String(response.data.size),
229
- contentType: response.data.contentType,
230
- isPrivate: options?.isPrivate ?? false,
231
- folderId: options?.folderId,
232
- storageConfigId: this.resolveConfigId(options?.storageConfigId),
233
- });
569
+ const dtos = response.data.map((fileInfo) => this.toCreateFileDto(fileInfo, options));
570
+ return this.fileManagerApi.createFiles(dtos).pipe(map((bulkResponse) => ({
571
+ success: bulkResponse.success,
572
+ data: bulkResponse.data ?? [],
573
+ message: bulkResponse.message,
574
+ })));
234
575
  }));
235
576
  }
236
577
  // ─── Private Helpers ───────────────────────────────────────────────────────
237
578
  resolveConfigId(storageConfigId) {
238
579
  return storageConfigId ?? this.getDefaultConfigId();
239
580
  }
581
+ toCreateFileDto(fileInfo, options) {
582
+ return {
583
+ name: fileInfo.name,
584
+ key: fileInfo.key,
585
+ size: String(fileInfo.size),
586
+ contentType: fileInfo.contentType,
587
+ isPrivate: options?.isPrivate ?? false,
588
+ folderId: options?.folderId,
589
+ storageConfigId: this.resolveConfigId(options?.storageConfigId),
590
+ };
591
+ }
240
592
  buildUploadUrl(endpoint, options) {
241
593
  const params = this.buildQueryParams(options);
242
594
  const baseUrl = this.getUrl(`upload/${endpoint}`);
@@ -265,58 +617,80 @@ class UploadService extends BaseApiService {
265
617
  const configId = this.resolveConfigId(storageConfigId);
266
618
  return { ...data, ...(configId && { storageConfigId: configId }) };
267
619
  }
268
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UploadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
269
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UploadService, providedIn: 'root' });
620
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: UploadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
621
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: UploadService, providedIn: 'root' });
270
622
  }
271
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UploadService, decorators: [{
623
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: UploadService, decorators: [{
272
624
  type: Injectable,
273
625
  args: [{ providedIn: 'root' }]
274
626
  }], ctorParameters: () => [] });
275
627
 
276
- /**
277
- * Profile Upload Provider Adapter
278
- *
279
- * Implements IProfileUploadProvider interface for ng-auth profile page.
280
- * Uses UploadService.uploadAndRegister() to upload files and register in file manager.
281
- *
282
- * @example
283
- * // In app.config.ts
284
- * providers: [
285
- * ...provideStorageProviders(),
286
- * ]
287
- */
288
- class ProfileUploadProviderAdapter {
628
+ class FileProviderAdapter {
629
+ fileManagerApi = inject(FileManagerApiService);
289
630
  uploadService = inject(UploadService);
290
- /**
291
- * Upload profile picture and register in file manager
292
- * Returns file manager ID (UUID) for storing in user.profilePictureId
293
- */
294
- uploadProfilePicture(file, options) {
631
+ folderApi = inject(FolderApiService);
632
+ storageConfigApi = inject(StorageConfigApiService);
633
+ loadFiles(filter) {
634
+ const filterObj = this.buildFileFilter(filter);
635
+ return from(this.fileManagerApi
636
+ .getAll({ pagination: { currentPage: filter.page, pageSize: filter.pageSize }, filter: Object.keys(filterObj).length > 0 ? filterObj : undefined }, filter.search || ''))
637
+ .pipe(map((res) => ({ ...res, data: res.data?.map(this.mapToFileBasicInfo) })));
638
+ }
639
+ uploadFile(file, options) {
640
+ return this.uploadService
641
+ .uploadAndRegister(file, options)
642
+ .pipe(map((res) => ({ ...res, data: res.data ? this.mapToUploadedFile(res.data) : undefined })));
643
+ }
644
+ uploadMultipleFiles(files, options) {
295
645
  return this.uploadService
296
- .uploadAndRegister(file, {
297
- folderPath: options?.folderPath ?? 'profile-pictures',
298
- compress: options?.compress ?? true,
299
- maxWidth: options?.maxWidth ?? 400,
300
- maxHeight: options?.maxHeight ?? 400,
301
- })
302
- .pipe(map((response) => ({
303
- success: response.success,
304
- message: response.message,
305
- data: response.data
306
- ? {
307
- id: response.data.id,
308
- name: response.data.name,
309
- key: response.data.key,
310
- size: parseFloat(response.data.size) * 1024, // Convert KB to bytes
311
- contentType: response.data.contentType,
312
- }
313
- : undefined,
646
+ .uploadAndRegisterMultiple(files, options)
647
+ .pipe(map((res) => ({ ...res, data: res.data?.map(this.mapToUploadedFile) })));
648
+ }
649
+ getFileUrls(fileIds) {
650
+ return this.fileManagerApi.getFiles(fileIds).pipe(map((res) => ({
651
+ ...res,
652
+ data: res.data?.map((f) => ({ id: f.id, name: f.name, contentType: f.contentType, size: '0', url: f.url })),
314
653
  })));
315
654
  }
316
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ProfileUploadProviderAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
317
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ProfileUploadProviderAdapter });
655
+ loadFolders(filter) {
656
+ return from(this.folderApi
657
+ .getAll({ pagination: { currentPage: filter.page, pageSize: filter.pageSize } }, filter.search || ''))
658
+ .pipe(map((res) => ({ ...res, data: res.data?.map((f) => ({ id: f.id, name: f.name, slug: f.slug })) })));
659
+ }
660
+ loadStorageConfigs(filter) {
661
+ return from(this.storageConfigApi
662
+ .getAll({ pagination: { currentPage: filter.page, pageSize: filter.pageSize } }, filter.search || ''))
663
+ .pipe(map((res) => ({ ...res, data: res.data?.map((c) => ({ id: c.id, name: c.name, isDefault: c.isDefault })) })));
664
+ }
665
+ // ─── Private Helpers ────────────────────────────────────────────────────────
666
+ buildFileFilter(filter) {
667
+ const filterObj = {};
668
+ if (filter.folderId)
669
+ filterObj['folderId'] = filter.folderId;
670
+ if (filter.storageConfigId)
671
+ filterObj['storageConfigId'] = filter.storageConfigId;
672
+ if (filter.contentTypes?.length)
673
+ filterObj['contentType'] = filter.contentTypes.join(',');
674
+ return filterObj;
675
+ }
676
+ mapToFileBasicInfo = (f) => ({
677
+ id: f.id,
678
+ name: f.name,
679
+ contentType: f.contentType,
680
+ size: f.size,
681
+ url: f.url,
682
+ });
683
+ mapToUploadedFile = (f) => ({
684
+ id: f.id,
685
+ name: f.name,
686
+ key: f.key,
687
+ size: parseFloat(f.size) * 1024,
688
+ contentType: f.contentType,
689
+ });
690
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: FileProviderAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
691
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: FileProviderAdapter });
318
692
  }
319
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ProfileUploadProviderAdapter, decorators: [{
693
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: FileProviderAdapter, decorators: [{
320
694
  type: Injectable
321
695
  }] });
322
696
 
@@ -339,54 +713,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
339
713
  function provideStorageProviders() {
340
714
  return [
341
715
  {
342
- provide: PROFILE_UPLOAD_PROVIDER,
343
- useClass: ProfileUploadProviderAdapter,
716
+ provide: FILE_PROVIDER,
717
+ useClass: FileProviderAdapter,
344
718
  },
345
719
  ];
346
720
  }
347
721
 
348
- /**
349
- * Folder API Service
350
- * Handles folder CRUD operations
351
- * Endpoint: POST /storage/folder/*
352
- *
353
- * Note: Company feature enabled - all queries automatically scoped to user's company
354
- */
355
- class FolderApiService extends ApiResourceService {
356
- constructor() {
357
- super('folder', inject(HttpClient), 'storage');
358
- }
359
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FolderApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
360
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FolderApiService, providedIn: 'root' });
361
- }
362
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FolderApiService, decorators: [{
363
- type: Injectable,
364
- args: [{
365
- providedIn: 'root',
366
- }]
367
- }], ctorParameters: () => [] });
368
-
369
- /**
370
- * Storage Config API Service
371
- * Handles storage configuration CRUD operations
372
- * Endpoint: POST /storage/storage-config/*
373
- *
374
- * Note: Company feature enabled - all queries automatically scoped to user's company
375
- */
376
- class StorageConfigApiService extends ApiResourceService {
377
- constructor() {
378
- super('storage-config', inject(HttpClient), 'storage');
379
- }
380
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageConfigApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
381
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageConfigApiService, providedIn: 'root' });
382
- }
383
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageConfigApiService, decorators: [{
384
- type: Injectable,
385
- args: [{
386
- providedIn: 'root',
387
- }]
388
- }], ctorParameters: () => [] });
389
-
390
722
  /**
391
723
  * Storage Module Routes
392
724
  * All routes protected by permission guards
@@ -394,6 +726,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
394
726
  const STORAGE_ROUTES = [
395
727
  {
396
728
  path: '',
729
+ resolve: { translations: resolveTranslationModule({ modules: ['storage'], fallbackMessages: { ...STORAGE_MESSAGES, ...SHARED_MESSAGES } }) },
397
730
  loadComponent: () => Promise.resolve().then(function () { return storageContainer_component; }).then((m) => m.StorageContainerComponent),
398
731
  children: [
399
732
  // File Manager Routes
@@ -403,17 +736,17 @@ const STORAGE_ROUTES = [
403
736
  children: [
404
737
  {
405
738
  path: '',
406
- loadComponent: () => import('./flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs').then((m) => m.FileManagerListComponent),
739
+ loadComponent: () => import('./flusys-ng-storage-file-manager-list.component-nZDj1UfS.mjs').then((m) => m.FileManagerListComponent),
407
740
  },
408
741
  {
409
742
  path: 'new',
410
743
  canActivate: [permissionGuard(FILE_PERMISSIONS.CREATE)],
411
- loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs').then((m) => m.FileManagerFormComponent),
744
+ loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-DhqZmpuX.mjs').then((m) => m.FileManagerFormComponent),
412
745
  },
413
746
  {
414
747
  path: ':id',
415
748
  canActivate: [permissionGuard(FILE_PERMISSIONS.UPDATE)],
416
- loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs').then((m) => m.FileManagerFormComponent),
749
+ loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-DhqZmpuX.mjs').then((m) => m.FileManagerFormComponent),
417
750
  },
418
751
  ],
419
752
  },
@@ -424,17 +757,17 @@ const STORAGE_ROUTES = [
424
757
  children: [
425
758
  {
426
759
  path: '',
427
- loadComponent: () => import('./flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs').then((m) => m.FolderListComponent),
760
+ loadComponent: () => import('./flusys-ng-storage-folder-list.component-fuJMWYWZ.mjs').then((m) => m.FolderListComponent),
428
761
  },
429
762
  {
430
763
  path: 'new',
431
764
  canActivate: [permissionGuard(FOLDER_PERMISSIONS.CREATE)],
432
- loadComponent: () => import('./flusys-ng-storage-folder-form.component-BTQGEqNu.mjs').then((m) => m.FolderFormComponent),
765
+ loadComponent: () => import('./flusys-ng-storage-folder-form.component-Bru9NQyr.mjs').then((m) => m.FolderFormComponent),
433
766
  },
434
767
  {
435
768
  path: ':id',
436
769
  canActivate: [permissionGuard(FOLDER_PERMISSIONS.UPDATE)],
437
- loadComponent: () => import('./flusys-ng-storage-folder-form.component-BTQGEqNu.mjs').then((m) => m.FolderFormComponent),
770
+ loadComponent: () => import('./flusys-ng-storage-folder-form.component-Bru9NQyr.mjs').then((m) => m.FolderFormComponent),
438
771
  },
439
772
  ],
440
773
  },
@@ -445,17 +778,17 @@ const STORAGE_ROUTES = [
445
778
  children: [
446
779
  {
447
780
  path: '',
448
- loadComponent: () => import('./flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs').then((m) => m.StorageConfigListComponent),
781
+ loadComponent: () => import('./flusys-ng-storage-storage-config-list.component-D59X4sae.mjs').then((m) => m.StorageConfigListComponent),
449
782
  },
450
783
  {
451
784
  path: 'new',
452
785
  canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.CREATE)],
453
- loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs').then((m) => m.StorageConfigFormComponent),
786
+ loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-Da-pWR7d.mjs').then((m) => m.StorageConfigFormComponent),
454
787
  },
455
788
  {
456
789
  path: ':id',
457
790
  canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.UPDATE)],
458
- loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs').then((m) => m.StorageConfigFormComponent),
791
+ loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-Da-pWR7d.mjs').then((m) => m.StorageConfigFormComponent),
459
792
  },
460
793
  ],
461
794
  },
@@ -474,89 +807,73 @@ const STORAGE_ROUTES = [
474
807
  */
475
808
  class StorageContainerComponent {
476
809
  permissionValidator = inject(PermissionValidatorService);
810
+ translateService = inject(TranslateService);
477
811
  /** All available tabs with required permissions */
478
812
  allTabs = [
479
813
  {
480
814
  id: 'files',
481
- label: 'Files',
482
- icon: 'pi pi-file',
815
+ labelKey: 'storage.file.title',
816
+ icon: 'file',
483
817
  route: 'files',
484
818
  permission: FILE_PERMISSIONS.READ,
485
819
  },
486
820
  {
487
821
  id: 'folders',
488
- label: 'Folders',
489
- icon: 'pi pi-folder',
822
+ labelKey: 'storage.folder.title',
823
+ icon: 'folder',
490
824
  route: 'folders',
491
825
  permission: FOLDER_PERMISSIONS.READ,
492
826
  },
493
827
  {
494
828
  id: 'configs',
495
- label: 'Configurations',
496
- icon: 'pi pi-cog',
829
+ labelKey: 'storage.config.title',
830
+ icon: 'cog',
497
831
  route: 'configs',
498
832
  permission: STORAGE_CONFIG_PERMISSIONS.READ,
499
833
  },
500
834
  ];
501
835
  /** Visible tabs based on user permissions */
502
- visibleTabs = computed(() => this.allTabs.filter((tab) => this.permissionValidator.hasPermission(tab.permission)), ...(ngDevMode ? [{ debugName: "visibleTabs" }] : []));
503
- /** Dynamic subtitle based on visible tabs */
504
- subtitle = computed(() => {
505
- const tabs = this.visibleTabs();
506
- if (tabs.length === 0)
507
- return 'No permissions available';
508
- return `Manage ${tabs.map((t) => t.label.toLowerCase()).join(', ')}`;
509
- }, ...(ngDevMode ? [{ debugName: "subtitle" }] : []));
510
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
511
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: StorageContainerComponent, isStandalone: true, selector: "lib-storage-container", ngImport: i0, template: `
836
+ visibleTabs = computed(() => this.allTabs
837
+ .filter((tab) => this.permissionValidator.hasPermission(tab.permission))
838
+ .map((tab) => ({
839
+ id: tab.id,
840
+ label: this.translateService.translate(tab.labelKey),
841
+ icon: tab.icon,
842
+ route: tab.route,
843
+ })), /* @ts-ignore */
844
+ ...(ngDevMode ? [{ debugName: "visibleTabs" }] : /* istanbul ignore next */ []));
845
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: StorageContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
846
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.6", type: StorageContainerComponent, isStandalone: true, selector: "lib-storage-container", ngImport: i0, template: `
512
847
  <div class="p-2 sm:p-4">
513
848
  <div class="mb-4">
514
- <h1 class="text-xl sm:text-2xl font-bold m-0">Storage</h1>
515
- <p class="text-muted-color mt-1 text-sm sm:text-base">{{ subtitle() }}</p>
849
+ <h1 class="text-xl sm:text-2xl font-bold m-0">{{ 'storage.title' | translate }}</h1>
850
+ <p class="text-muted-color mt-1 text-sm sm:text-base">{{ 'storage.subtitle' | translate }}</p>
516
851
  </div>
517
852
 
518
- <div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
519
- @for (tab of visibleTabs(); track tab.id) {
520
- <a
521
- [routerLink]="tab.route"
522
- routerLinkActive="tab-active"
523
- 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"
524
- >
525
- <i [class]="tab.icon"></i>
526
- <span>{{ tab.label }}</span>
527
- </a>
528
- }
529
- </div>
853
+ <lib-router-tabs [tabs]="visibleTabs()" class="mb-4" />
530
854
 
531
855
  <router-outlet />
532
856
  </div>
533
- `, 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 });
857
+ `, isInline: true, dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: RouterTabsComponent, selector: "lib-router-tabs", inputs: ["tabs"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
534
858
  }
535
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageContainerComponent, decorators: [{
859
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: StorageContainerComponent, decorators: [{
536
860
  type: Component,
537
- args: [{ selector: 'lib-storage-container', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive], template: `
861
+ args: [{
862
+ selector: 'lib-storage-container',
863
+ imports: [RouterOutlet, TranslatePipe, RouterTabsComponent],
864
+ template: `
538
865
  <div class="p-2 sm:p-4">
539
866
  <div class="mb-4">
540
- <h1 class="text-xl sm:text-2xl font-bold m-0">Storage</h1>
541
- <p class="text-muted-color mt-1 text-sm sm:text-base">{{ subtitle() }}</p>
867
+ <h1 class="text-xl sm:text-2xl font-bold m-0">{{ 'storage.title' | translate }}</h1>
868
+ <p class="text-muted-color mt-1 text-sm sm:text-base">{{ 'storage.subtitle' | translate }}</p>
542
869
  </div>
543
870
 
544
- <div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
545
- @for (tab of visibleTabs(); track tab.id) {
546
- <a
547
- [routerLink]="tab.route"
548
- routerLinkActive="tab-active"
549
- 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"
550
- >
551
- <i [class]="tab.icon"></i>
552
- <span>{{ tab.label }}</span>
553
- </a>
554
- }
555
- </div>
871
+ <lib-router-tabs [tabs]="visibleTabs()" class="mb-4" />
556
872
 
557
873
  <router-outlet />
558
874
  </div>
559
- `, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"] }]
875
+ `,
876
+ }]
560
877
  }] });
561
878
 
562
879
  var storageContainer_component = /*#__PURE__*/Object.freeze({
@@ -567,11 +884,11 @@ var storageContainer_component = /*#__PURE__*/Object.freeze({
567
884
  // =============================================================================
568
885
  // @flusys/ng-storage - Storage Package
569
886
  // =============================================================================
570
- // Enums
887
+ // Constants
571
888
 
572
889
  /**
573
890
  * Generated bundle index. Do not edit.
574
891
  */
575
892
 
576
- export { BaseStorageListPage, BaseStoragePage, FileLocationEnum, FileManagerApiService, FolderApiService, ImageFormat, ProfileUploadProviderAdapter, STORAGE_PROVIDERS, STORAGE_ROUTES, StorageConfigApiService, StorageContainerComponent, UploadService, formatFileSize, getFileIcon, getProviderIcon, getProviderLabel, getProviderSeverity, provideStorageProviders };
893
+ export { BaseStorageListPage, BaseStoragePage, FILE_SIZE_UNIT_KEYS, FileLocationEnum, FileManagerApiService, FileProviderAdapter, FolderApiService, ImageFormat, PROVIDER_LABEL_KEYS, STORAGE_MESSAGES, STORAGE_PROVIDER_OPTIONS, STORAGE_ROUTES, StorageConfigApiService, StorageContainerComponent, UploadService, formatFileSize, formatFileSizeWithKey, getFileIcon, getProviderIcon, getProviderLabelKey, getProviderSeverity, provideStorageProviders };
577
894
  //# sourceMappingURL=flusys-ng-storage.mjs.map