@flusys/ng-storage 3.0.1 → 4.0.0-lts

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 (23) hide show
  1. package/README.md +12 -6
  2. package/fesm2022/{flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs → flusys-ng-storage-file-manager-form.component-AM7JE-s5.mjs} +67 -74
  3. package/fesm2022/flusys-ng-storage-file-manager-form.component-AM7JE-s5.mjs.map +1 -0
  4. package/fesm2022/{flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs → flusys-ng-storage-file-manager-list.component-bj6oOzOL.mjs} +66 -74
  5. package/fesm2022/flusys-ng-storage-file-manager-list.component-bj6oOzOL.mjs.map +1 -0
  6. package/fesm2022/{flusys-ng-storage-folder-form.component-BTQGEqNu.mjs → flusys-ng-storage-folder-form.component-Cc2ybTwu.mjs} +25 -34
  7. package/fesm2022/flusys-ng-storage-folder-form.component-Cc2ybTwu.mjs.map +1 -0
  8. package/fesm2022/{flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs → flusys-ng-storage-folder-list.component-B2Q41I1g.mjs} +34 -39
  9. package/fesm2022/flusys-ng-storage-folder-list.component-B2Q41I1g.mjs.map +1 -0
  10. package/fesm2022/{flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs → flusys-ng-storage-storage-config-form.component-a88XhuPH.mjs} +87 -85
  11. package/fesm2022/flusys-ng-storage-storage-config-form.component-a88XhuPH.mjs.map +1 -0
  12. package/fesm2022/{flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs → flusys-ng-storage-storage-config-list.component-DBH_qf9o.mjs} +36 -35
  13. package/fesm2022/flusys-ng-storage-storage-config-list.component-DBH_qf9o.mjs.map +1 -0
  14. package/fesm2022/flusys-ng-storage.mjs +490 -151
  15. package/fesm2022/flusys-ng-storage.mjs.map +1 -1
  16. package/package.json +7 -5
  17. package/types/flusys-ng-storage.d.ts +56 -36
  18. package/fesm2022/flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs.map +0 -1
  19. package/fesm2022/flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs.map +0 -1
  20. package/fesm2022/flusys-ng-storage-folder-form.component-BTQGEqNu.mjs.map +0 -1
  21. package/fesm2022/flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs.map +0 -1
  22. package/fesm2022/flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs.map +0 -1
  23. package/fesm2022/flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs.map +0 -1
@@ -1,15 +1,281 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, signal, computed, Directive, Injectable, ChangeDetectionStrategy, Component } from '@angular/core';
2
+ import { inject, signal, computed, Directive, Injectable, Component } from '@angular/core';
3
3
  import { Router, RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
4
- import { APP_CONFIG, DEFAULT_APP_NAME, BaseApiService, getDefaultStorageConfigId } from '@flusys/ng-core';
4
+ import { APP_CONFIG, TRANSLATE_ADAPTER, DEFAULT_APP_NAME, BaseApiService, getDefaultStorageConfigId } from '@flusys/ng-core';
5
5
  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';
6
+ import { BaseListPage, ApiResourceService, FILE_PROVIDER, permissionGuard, FILE_PERMISSIONS, FOLDER_PERMISSIONS, STORAGE_CONFIG_PERMISSIONS, resolveTranslationModule, SHARED_MESSAGES, PermissionValidatorService, TranslatePipe } from '@flusys/ng-shared';
7
7
  import { MessageService } from 'primeng/api';
8
- import { map } from 'rxjs/operators';
9
- import { firstValueFrom, switchMap } from 'rxjs';
8
+ import { switchMap, map } from 'rxjs';
10
9
  import { HttpClient } from '@angular/common/http';
11
10
  import { CommonModule } from '@angular/common';
12
11
 
12
+ const STORAGE_MESSAGES = {
13
+ // Container page
14
+ 'storage.title': 'Storage',
15
+ 'storage.subtitle': 'Manage files, folders, and storage configurations',
16
+ // Tabs
17
+ 'storage.tabs.file.manager': 'File Manager',
18
+ 'storage.tabs.folders': 'Folders',
19
+ 'storage.tabs.configs': 'Configurations',
20
+ // Page titles
21
+ 'storage.file.title': 'Files',
22
+ 'storage.file.manager.title': 'File Manager',
23
+ 'storage.file.manager.upload.file': 'Upload File',
24
+ 'storage.file.manager.edit.file': 'Edit File',
25
+ 'storage.folder.title': 'Folder Setup',
26
+ 'storage.folder.new.folder': 'New Folder',
27
+ 'storage.folder.edit.folder': 'Edit Folder',
28
+ 'storage.config.title': 'Storage Configurations',
29
+ 'storage.config.new.config': 'New Configuration',
30
+ 'storage.config.edit.config': 'Edit Storage Configuration',
31
+ // Common labels
32
+ 'storage.company': 'Company',
33
+ 'storage.name': 'Name',
34
+ 'storage.type': 'Type',
35
+ 'storage.size': 'Size',
36
+ 'storage.location': 'Location',
37
+ 'storage.config': 'Config',
38
+ 'storage.private': 'Private',
39
+ 'storage.public': 'Public',
40
+ 'storage.created': 'Created',
41
+ 'storage.actions': 'Actions',
42
+ 'storage.slug': 'Slug',
43
+ 'storage.provider': 'Provider',
44
+ 'storage.active': 'Active',
45
+ 'storage.set.as.default': 'Set as Default',
46
+ // Table headers
47
+ 'storage.table.name': 'Name',
48
+ 'storage.table.type': 'Type',
49
+ 'storage.table.size': 'Size',
50
+ 'storage.table.location': 'Location',
51
+ 'storage.table.config': 'Config',
52
+ 'storage.table.private': 'Private',
53
+ 'storage.table.created': 'Created',
54
+ 'storage.table.actions': 'Actions',
55
+ 'storage.table.slug': 'Slug',
56
+ 'storage.table.provider': 'Provider',
57
+ // Buttons
58
+ 'storage.button.upload.file': 'Upload File',
59
+ 'storage.button.new.folder': 'New Folder',
60
+ 'storage.button.new.configuration': 'New Configuration',
61
+ 'storage.button.choose.file': 'Choose File',
62
+ 'storage.button.cancel': 'Cancel',
63
+ 'storage.button.create': 'Create',
64
+ 'storage.button.update': 'Update',
65
+ 'storage.button.upload': 'Upload',
66
+ // Tooltips
67
+ 'storage.tooltip.view': 'View',
68
+ 'storage.tooltip.edit': 'Edit',
69
+ 'storage.tooltip.trash': 'Trash',
70
+ 'storage.tooltip.delete': 'Delete',
71
+ // Empty states
72
+ 'storage.empty.files': 'No files found',
73
+ 'storage.empty.files.in.company': 'No files found in current company',
74
+ 'storage.empty.folders': 'No folders found',
75
+ 'storage.empty.folders.in.company': 'No folders found in current company',
76
+ 'storage.empty.configs': 'No storage configurations found',
77
+ 'storage.empty.configs.in.company': 'No storage configurations found in current company',
78
+ // File operations
79
+ 'storage.upload.title': 'Upload File',
80
+ 'storage.upload.success': 'File uploaded successfully',
81
+ 'storage.upload.failed': 'Failed to upload file',
82
+ 'storage.upload.progress': 'Uploading... {{percent}}%',
83
+ 'storage.upload.drag.drop': 'Drag and drop file here or click to browse',
84
+ 'storage.upload.select.file': 'Select File',
85
+ 'storage.upload.select.files': 'Select Files',
86
+ 'storage.upload.max.size': 'Maximum file size: {{size}}',
87
+ 'storage.upload.please.select': 'Please select a file to upload',
88
+ 'storage.download.success': 'File downloaded successfully',
89
+ 'storage.download.failed': 'Failed to download file',
90
+ 'storage.download.progress': 'Downloading...',
91
+ // Delete operations
92
+ 'storage.delete.success': 'File deleted successfully',
93
+ 'storage.delete.failed': 'Failed to delete file',
94
+ 'storage.delete.confirm': 'Are you sure you want to delete this file?',
95
+ 'storage.delete.this.item': 'this item',
96
+ 'storage.delete.confirm.named': 'Are you sure you want to delete "{{name}}"?',
97
+ 'storage.delete.move.to.trash': 'Move to Trash',
98
+ 'storage.delete.move.to.trash.confirm': 'Move "{{name}}" to trash? You can recover it later.',
99
+ 'storage.delete.permanent.delete': 'Permanent Delete',
100
+ 'storage.delete.permanent.delete.confirm': 'Permanently delete "{{name}}"? This action CANNOT be undone and will delete the file from storage.',
101
+ 'storage.delete.permanent.delete.button': 'Delete Permanently',
102
+ 'storage.delete.trash.success': 'File moved to trash successfully',
103
+ 'storage.delete.trash.failed': 'Failed to move file to trash',
104
+ 'storage.delete.permanent.success': 'File permanently deleted',
105
+ 'storage.delete.permanent.failed': 'Failed to permanently delete file',
106
+ // File info
107
+ 'storage.file.name': 'File Name',
108
+ 'storage.file.name.required': 'File Name *',
109
+ 'storage.file.size': 'File Size',
110
+ 'storage.file.type': 'File Type',
111
+ 'storage.file.extension': 'Extension',
112
+ 'storage.file.uploaded.at': 'Uploaded At',
113
+ 'storage.file.uploaded.by': 'Uploaded By',
114
+ 'storage.file.path': 'File Path',
115
+ 'storage.file.url': 'File URL',
116
+ 'storage.file.url.not.available': 'File URL is not available',
117
+ 'storage.file.get.url.failed': 'Failed to get file URL',
118
+ 'storage.file.private': 'Private File',
119
+ 'storage.file.private.hint': 'Only users with permission can access',
120
+ 'storage.file.public.hint': 'File is publicly accessible',
121
+ 'storage.file.placeholder': 'Will be auto-filled from selected file',
122
+ 'storage.file.placeholder.edit': 'Enter file name',
123
+ 'storage.file.select.required': 'Select File *',
124
+ // Validation
125
+ 'storage.error.file.too.big': 'File size exceeds the maximum limit of {{maxSize}}',
126
+ 'storage.error.invalid.type': 'Invalid file type. Allowed types: {{types}}',
127
+ 'storage.error.too.many.files': 'Maximum {{max}} files allowed',
128
+ 'storage.error.no.file': 'Please select a file',
129
+ 'storage.error.no.config': 'Please select a storage configuration',
130
+ 'storage.error.load.failed': 'Failed to load files',
131
+ 'storage.error.load.file.failed': 'Failed to load file',
132
+ // Storage config
133
+ 'storage.config.config.name': 'Configuration Name',
134
+ 'storage.config.config.name.required': 'Configuration Name *',
135
+ 'storage.config.config.name.placeholder': 'e.g., Production AWS S3',
136
+ 'storage.config.provider': 'Storage Provider',
137
+ 'storage.config.provider.required': 'Storage Provider *',
138
+ 'storage.config.bucket': 'Bucket Name',
139
+ 'storage.config.bucket.required': 'Bucket Name *',
140
+ 'storage.config.region': 'Region',
141
+ 'storage.config.region.required': 'AWS Region *',
142
+ 'storage.config.access.key': 'Access Key',
143
+ 'storage.config.access.key.required': 'Access Key ID *',
144
+ 'storage.config.secret.key': 'Secret Key',
145
+ 'storage.config.secret.key.required': 'Secret Access Key *',
146
+ 'storage.config.endpoint': 'Endpoint URL',
147
+ 'storage.config.endpoint.optional': 'Custom Endpoint (Optional)',
148
+ 'storage.config.endpoint.placeholder': 'https://s3.custom-endpoint.com',
149
+ 'storage.config.public': 'Public Access',
150
+ 'storage.config.no.configs': 'No storage configurations found. Please create one first.',
151
+ 'storage.config.select.config': 'Select storage configuration...',
152
+ 'storage.config.load.failed': 'Failed to load storage configurations',
153
+ 'storage.config.load.config.failed': 'Failed to load storage configuration',
154
+ 'storage.config.delete.title': 'Delete Configuration',
155
+ 'storage.config.delete.success': 'Storage configuration deleted successfully',
156
+ 'storage.config.delete.failed': 'Failed to delete storage configuration',
157
+ // AWS specific
158
+ 'storage.aws.title': 'AWS S3 Configuration',
159
+ 'storage.aws.region': 'AWS Region',
160
+ 'storage.aws.region.placeholder': 'us-east-1',
161
+ 'storage.aws.bucket': 'Bucket Name',
162
+ 'storage.aws.bucket.placeholder': 'my-bucket',
163
+ 'storage.aws.access.key.id': 'Access Key ID',
164
+ 'storage.aws.secret.access.key': 'Secret Access Key',
165
+ // Azure specific
166
+ 'storage.azure.title': 'Azure Blob Configuration',
167
+ 'storage.azure.account.name': 'Account Name',
168
+ 'storage.azure.account.name.required': 'Account Name *',
169
+ 'storage.azure.account.key': 'Account Key',
170
+ 'storage.azure.account.key.required': 'Account Key *',
171
+ 'storage.azure.container.name': 'Container Name',
172
+ 'storage.azure.container.name.required': 'Container Name *',
173
+ // SFTP specific
174
+ 'storage.sftp.title': 'SFTP Configuration',
175
+ 'storage.sftp.host': 'Host',
176
+ 'storage.sftp.host.required': 'Host *',
177
+ 'storage.sftp.host.placeholder': 'sftp.example.com',
178
+ 'storage.sftp.port': 'Port',
179
+ 'storage.sftp.port.required': 'Port *',
180
+ 'storage.sftp.username': 'Username',
181
+ 'storage.sftp.username.required': 'Username *',
182
+ 'storage.sftp.password': 'Password',
183
+ 'storage.sftp.password.required': 'Password *',
184
+ 'storage.sftp.base.path': 'Base Path',
185
+ 'storage.sftp.base.path.required': 'Base Path *',
186
+ 'storage.sftp.base.path.placeholder': '/uploads',
187
+ // Local specific
188
+ 'storage.local.title': 'Local Storage Configuration',
189
+ 'storage.local.base.path': 'Base Path',
190
+ 'storage.local.base.path.required': 'Base Path *',
191
+ 'storage.local.base.path.placeholder': '/var/www/uploads',
192
+ // Folder operations
193
+ 'storage.folder.create': 'Create Folder',
194
+ 'storage.folder.name': 'Folder Name',
195
+ 'storage.folder.name.required': 'Folder Name *',
196
+ 'storage.folder.name.placeholder': 'Enter folder name',
197
+ 'storage.folder.create.success': 'Folder created successfully',
198
+ 'storage.folder.create.failed': 'Failed to create folder',
199
+ 'storage.folder.update.success': 'Folder updated successfully',
200
+ 'storage.folder.update.failed': 'Failed to update folder',
201
+ 'storage.folder.delete': 'Delete Folder',
202
+ 'storage.folder.delete.title': 'Delete Folder',
203
+ 'storage.folder.delete.confirm': 'Are you sure you want to delete this folder and all its contents?',
204
+ 'storage.folder.delete.success': 'Folder deleted successfully',
205
+ 'storage.folder.delete.failed': 'Failed to delete folder',
206
+ 'storage.folder.optional': 'Folder (Optional)',
207
+ 'storage.folder.no.folder': 'No folder (root level)',
208
+ 'storage.folder.no.folders.available': 'No folders available. File will be uploaded to root level.',
209
+ 'storage.folder.no.folders.edit': 'No folders available.',
210
+ 'storage.folder.load.failed': 'Failed to load folder',
211
+ // File entity CRUD (API keys)
212
+ 'file.create.success': 'File created successfully',
213
+ 'file.create.many.success': '{{count}} files created successfully',
214
+ 'file.get.success': 'File retrieved successfully',
215
+ 'file.get.all.success': 'Files retrieved successfully',
216
+ 'file.update.success': 'File updated successfully',
217
+ 'file.update.many.success': '{{count}} files updated successfully',
218
+ 'file.delete.success': 'File deleted successfully',
219
+ 'file.restore.success': 'File restored successfully',
220
+ 'file.not.found': 'File not found: {{path}}',
221
+ 'file.upload.success': 'File uploaded successfully',
222
+ 'file.upload.many.success': 'Files uploaded successfully',
223
+ 'file.download.success': 'File downloaded successfully',
224
+ 'file.move.success': 'File moved successfully',
225
+ 'file.copy.success': 'File copied successfully',
226
+ // Folder entity CRUD (API keys)
227
+ 'folder.create.success': 'Folder created successfully',
228
+ 'folder.create.many.success': '{{count}} folders created successfully',
229
+ 'folder.get.success': 'Folder retrieved successfully',
230
+ 'folder.get.all.success': 'Folders retrieved successfully',
231
+ 'folder.update.success': 'Folder updated successfully',
232
+ 'folder.update.many.success': '{{count}} folders updated successfully',
233
+ 'folder.delete.success': 'Folder deleted successfully',
234
+ 'folder.restore.success': 'Folder restored successfully',
235
+ 'folder.not.found': 'Folder "{{folderId}}" not found or access denied',
236
+ 'folder.tree.success': 'Folder tree retrieved successfully',
237
+ 'folder.contents.success': 'Folder contents retrieved successfully',
238
+ // Storage config CRUD (API keys)
239
+ 'storage.config.update.success': 'Storage configuration updated successfully',
240
+ 'storage.config.create.success': 'Storage configuration created successfully',
241
+ 'storage.config.update.failed': 'Failed to update storage configuration',
242
+ 'storage.config.create.failed': 'Failed to create storage configuration',
243
+ 'storage.config.create.many.success': '{{count}} storage configurations created successfully',
244
+ 'storage.config.get.success': 'Storage configuration retrieved successfully',
245
+ 'storage.config.get.all.success': 'Storage configurations retrieved successfully',
246
+ 'storage.config.update.many.success': '{{count}} storage configurations updated successfully',
247
+ 'storage.config.restore.success': 'Storage configuration restored successfully',
248
+ 'storage.config.not.found': 'Storage configuration not found',
249
+ 'storage.config.active.success': 'Active storage configuration retrieved successfully',
250
+ 'storage.config.test.success': 'Storage configuration test successful',
251
+ 'storage.config.test.failed': 'Storage configuration test failed',
252
+ // Upload messages (API keys)
253
+ 'upload.success': 'File uploaded successfully',
254
+ 'upload.many.success': 'Files uploaded successfully',
255
+ 'upload.failed': 'Failed to upload file',
256
+ 'upload.file.too.large': 'File size ({{fileSize}}MB) exceeds maximum {{maxSize}}MB',
257
+ 'upload.invalid.type': 'File type "{{mimeType}}" not allowed. Allowed: {{allowedTypes}}',
258
+ 'upload.chunk.success': 'Chunk uploaded successfully',
259
+ 'upload.complete.success': 'Upload completed successfully',
260
+ 'upload.no.files.provided': 'No files provided',
261
+ 'upload.no.file.path': 'File path is required',
262
+ 'upload.invalid.file.path': 'Invalid file path',
263
+ 'upload.config.not.found': 'Upload configuration not found',
264
+ // Validation messages
265
+ 'storage.validation.file.name.required': 'File name is required',
266
+ 'storage.validation.folder.name.required': 'Folder name is required',
267
+ 'storage.validation.config.name.required': 'Configuration name is required',
268
+ // Additional missing keys
269
+ 'storage.unit.kb': 'KB',
270
+ 'storage.unit.mb': 'MB',
271
+ 'storage.unit.gb': 'GB',
272
+ 'storage.sftp.port.placeholder': '22',
273
+ 'storage.file.update.success': 'File updated successfully',
274
+ 'storage.pagination.files.template': 'Showing {first} to {last} of {totalRecords} files',
275
+ 'storage.pagination.folders.template': 'Showing {first} to {last} of {totalRecords} folders',
276
+ 'storage.pagination.configs.template': 'Showing {first} to {last} of {totalRecords} configurations',
277
+ };
278
+
13
279
  /**
14
280
  * File Location Enum
15
281
  * Storage provider types (matches backend FileLocationEnum)
@@ -33,37 +299,46 @@ var ImageFormat;
33
299
  ImageFormat["WEBP"] = "webp";
34
300
  })(ImageFormat || (ImageFormat = {}));
35
301
 
302
+ /**
303
+ * Provider label translation keys
304
+ */
305
+ const PROVIDER_LABEL_KEYS = {
306
+ [FileLocationEnum.LOCAL]: 'storage.local.title',
307
+ [FileLocationEnum.AWS]: 'storage.aws.title',
308
+ [FileLocationEnum.AZURE]: 'storage.azure.title',
309
+ [FileLocationEnum.SFTP]: 'storage.sftp.title',
310
+ };
36
311
  /**
37
312
  * Provider configurations for all storage types
38
313
  */
39
314
  const PROVIDER_CONFIGS = {
40
315
  [FileLocationEnum.LOCAL]: {
41
- label: 'Local',
316
+ labelKey: 'storage.local.title',
42
317
  icon: 'pi pi-folder',
43
318
  severity: 'secondary',
44
319
  },
45
320
  [FileLocationEnum.AWS]: {
46
- label: 'AWS S3',
321
+ labelKey: 'storage.aws.title',
47
322
  icon: 'pi pi-cloud',
48
323
  severity: 'warn',
49
324
  },
50
325
  [FileLocationEnum.AZURE]: {
51
- label: 'Azure Blob',
326
+ labelKey: 'storage.azure.title',
52
327
  icon: 'pi pi-cloud',
53
328
  severity: 'info',
54
329
  },
55
330
  [FileLocationEnum.SFTP]: {
56
- label: 'SFTP',
331
+ labelKey: 'storage.sftp.title',
57
332
  icon: 'pi pi-server',
58
333
  severity: 'contrast',
59
334
  },
60
335
  };
61
336
  /**
62
- * Get provider label for display
337
+ * Get provider label translation key
63
338
  */
64
- function getProviderLabel(storage) {
339
+ function getProviderLabelKey(storage) {
65
340
  const enumValue = storage;
66
- return PROVIDER_CONFIGS[enumValue]?.label ?? storage;
341
+ return PROVIDER_CONFIGS[enumValue]?.labelKey ?? storage;
67
342
  }
68
343
  /**
69
344
  * Get provider icon class
@@ -97,25 +372,44 @@ function getFileIcon(contentType) {
97
372
  return 'pi pi-file-excel';
98
373
  return 'pi pi-file';
99
374
  }
375
+ /** File size unit keys for translation */
376
+ const FILE_SIZE_UNIT_KEYS = ['storage.size.bytes', 'storage.size.kb', 'storage.size.mb', 'storage.size.gb'];
100
377
  /**
101
- * Format file size from bytes to human readable string
378
+ * Format file size from bytes to human readable format
379
+ * Returns an object with value and unitKey for translation
102
380
  */
103
- function formatFileSize(bytes) {
381
+ function formatFileSizeWithKey(bytes) {
104
382
  if (bytes <= 0)
105
- return '0 Bytes';
383
+ return { value: '0', unitKey: 'storage.size.bytes' };
106
384
  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];
385
+ const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), FILE_SIZE_UNIT_KEYS.length - 1);
386
+ const value = (Math.round((bytes / Math.pow(k, i)) * 100) / 100).toString();
387
+ return { value, unitKey: FILE_SIZE_UNIT_KEYS[i] };
388
+ }
389
+ /**
390
+ * Format file size from bytes to human readable string
391
+ * @deprecated Use formatFileSizeWithKey for localization support
392
+ */
393
+ function formatFileSize(bytes) {
394
+ const { value, unitKey } = formatFileSizeWithKey(bytes);
395
+ // Fallback to non-translated units for backward compatibility
396
+ const unitMap = {
397
+ 'storage.size.bytes': 'Bytes',
398
+ 'storage.size.kb': 'KB',
399
+ 'storage.size.mb': 'MB',
400
+ 'storage.size.gb': 'GB',
401
+ };
402
+ return `${value} ${unitMap[unitKey]}`;
110
403
  }
111
404
  /**
112
405
  * Storage provider options for dropdown selects
406
+ * Labels are translation keys - components should translate them
113
407
  */
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 },
408
+ const STORAGE_PROVIDER_OPTIONS = [
409
+ { labelKey: 'storage.aws.title', value: FileLocationEnum.AWS },
410
+ { labelKey: 'storage.azure.title', value: FileLocationEnum.AZURE },
411
+ { labelKey: 'storage.sftp.title', value: FileLocationEnum.SFTP },
412
+ { labelKey: 'storage.local.title', value: FileLocationEnum.LOCAL },
119
413
  ];
120
414
 
121
415
  class BaseStoragePage {
@@ -123,13 +417,16 @@ class BaseStoragePage {
123
417
  messageService = inject(MessageService);
124
418
  appConfig = inject(APP_CONFIG);
125
419
  companyContext = inject(LAYOUT_AUTH_STATE);
420
+ translateAdapter = inject(TRANSLATE_ADAPTER);
126
421
  isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
127
422
  showCompanyInfo = computed(() => this.appConfig.enableCompanyFeature, ...(ngDevMode ? [{ debugName: "showCompanyInfo" }] : []));
128
423
  currentCompanyName = computed(() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
129
- showSuccess(detail, summary = 'Success') {
424
+ showSuccess(detail, summaryKey = 'shared.success') {
425
+ const summary = this.translateAdapter.translate(summaryKey);
130
426
  this.messageService.add({ severity: 'success', summary, detail });
131
427
  }
132
- showError(detail, summary = 'Error') {
428
+ showError(detail, summaryKey = 'shared.error') {
429
+ const summary = this.translateAdapter.translate(summaryKey);
133
430
  this.messageService.add({ severity: 'error', summary, detail });
134
431
  }
135
432
  navigateTo(path) {
@@ -143,12 +440,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
143
440
  }] });
144
441
  class BaseStorageListPage extends BaseListPage {
145
442
  companyContext = inject(LAYOUT_AUTH_STATE);
443
+ translateAdapter = inject(TRANSLATE_ADAPTER);
146
444
  pageSize = signal(50, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
147
445
  totalRecords = this.total;
148
446
  currentCompanyName = computed(() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
149
447
  getDeleteConfirmMessage(item) {
150
- const name = item.name ?? 'this item';
151
- return `Are you sure you want to delete "${name}"?`;
448
+ const itemName = item.name;
449
+ const name = itemName ?? this.translateAdapter.translate('storage.delete.this.item');
450
+ return this.translateAdapter.translate('storage.delete.confirm.named', { name });
152
451
  }
153
452
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseStorageListPage, deps: null, target: i0.ɵɵFactoryTarget.Directive });
154
453
  static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.5", type: BaseStorageListPage, isStandalone: true, usesInheritance: true, ngImport: i0 });
@@ -168,8 +467,8 @@ class FileManagerApiService extends ApiResourceService {
168
467
  createFile(dto) {
169
468
  return this.http.post(`${this.baseUrl}/insert`, dto);
170
469
  }
171
- async createFileAsync(dto) {
172
- return firstValueFrom(this.createFile(dto));
470
+ createFiles(dtos) {
471
+ return this.http.post(`${this.baseUrl}/insert-many`, dtos);
173
472
  }
174
473
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileManagerApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
175
474
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileManagerApiService, providedIn: 'root' });
@@ -179,6 +478,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
179
478
  args: [{ providedIn: 'root' }]
180
479
  }], ctorParameters: () => [] });
181
480
 
481
+ /**
482
+ * Folder API Service
483
+ * Handles folder CRUD operations
484
+ * Endpoint: POST /storage/folder/*
485
+ *
486
+ * Note: Company feature enabled - all queries automatically scoped to user's company
487
+ */
488
+ class FolderApiService extends ApiResourceService {
489
+ constructor() {
490
+ super('folder', inject(HttpClient), 'storage');
491
+ }
492
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FolderApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
493
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FolderApiService, providedIn: 'root' });
494
+ }
495
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FolderApiService, decorators: [{
496
+ type: Injectable,
497
+ args: [{
498
+ providedIn: 'root',
499
+ }]
500
+ }], ctorParameters: () => [] });
501
+
502
+ /**
503
+ * Storage Config API Service
504
+ * Handles storage configuration CRUD operations
505
+ * Endpoint: POST /storage/storage-config/*
506
+ *
507
+ * Note: Company feature enabled - all queries automatically scoped to user's company
508
+ */
509
+ class StorageConfigApiService extends ApiResourceService {
510
+ constructor() {
511
+ super('storage-config', inject(HttpClient), 'storage');
512
+ }
513
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageConfigApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
514
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageConfigApiService, providedIn: 'root' });
515
+ }
516
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageConfigApiService, decorators: [{
517
+ type: Injectable,
518
+ args: [{
519
+ providedIn: 'root',
520
+ }]
521
+ }], ctorParameters: () => [] });
522
+
182
523
  /**
183
524
  * Upload Service - File upload/delete operations
184
525
  * Endpoint: POST /storage/upload/*
@@ -220,23 +561,40 @@ class UploadService extends BaseApiService {
220
561
  uploadAndRegister(file, options) {
221
562
  return this.uploadSingleFile(file, options).pipe(switchMap((response) => {
222
563
  if (!response.success || !response.data) {
223
- throw new Error(response.message || 'Upload failed');
564
+ throw new Error(response.message || 'upload.failed');
224
565
  }
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
- });
566
+ return this.fileManagerApi.createFile(this.toCreateFileDto(response.data, options));
567
+ }));
568
+ }
569
+ /** Upload multiple files and register in file manager - batch upload (2 API calls total) */
570
+ uploadAndRegisterMultiple(files, options) {
571
+ return this.uploadMultipleFiles(files, options).pipe(switchMap((response) => {
572
+ if (!response.success || !response.data) {
573
+ throw new Error(response.message || 'upload.failed');
574
+ }
575
+ const dtos = response.data.map((fileInfo) => this.toCreateFileDto(fileInfo, options));
576
+ return this.fileManagerApi.createFiles(dtos).pipe(map((bulkResponse) => ({
577
+ success: bulkResponse.success,
578
+ data: bulkResponse.data ?? [],
579
+ message: bulkResponse.message,
580
+ })));
234
581
  }));
235
582
  }
236
583
  // ─── Private Helpers ───────────────────────────────────────────────────────
237
584
  resolveConfigId(storageConfigId) {
238
585
  return storageConfigId ?? this.getDefaultConfigId();
239
586
  }
587
+ toCreateFileDto(fileInfo, options) {
588
+ return {
589
+ name: fileInfo.name,
590
+ key: fileInfo.key,
591
+ size: String(fileInfo.size),
592
+ contentType: fileInfo.contentType,
593
+ isPrivate: options?.isPrivate ?? false,
594
+ folderId: options?.folderId,
595
+ storageConfigId: this.resolveConfigId(options?.storageConfigId),
596
+ };
597
+ }
240
598
  buildUploadUrl(endpoint, options) {
241
599
  const params = this.buildQueryParams(options);
242
600
  const baseUrl = this.getUrl(`upload/${endpoint}`);
@@ -273,50 +631,79 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
273
631
  args: [{ providedIn: 'root' }]
274
632
  }], ctorParameters: () => [] });
275
633
 
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 {
634
+ class FileProviderAdapter {
635
+ fileManagerApi = inject(FileManagerApiService);
289
636
  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) {
295
- 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,
637
+ folderApi = inject(FolderApiService);
638
+ storageConfigApi = inject(StorageConfigApiService);
639
+ loadFiles(filter) {
640
+ const filterObj = this.buildFileFilter(filter);
641
+ return this.fileManagerApi
642
+ .getAll(filter.search || '', {
643
+ pagination: { currentPage: filter.page, pageSize: filter.pageSize },
644
+ filter: Object.keys(filterObj).length > 0 ? filterObj : undefined,
301
645
  })
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
+ .pipe(map((res) => ({ ...res, data: res.data?.map(this.mapToFileBasicInfo) })));
647
+ }
648
+ uploadFile(file, options) {
649
+ return this.uploadService
650
+ .uploadAndRegister(file, options)
651
+ .pipe(map((res) => ({ ...res, data: res.data ? this.mapToUploadedFile(res.data) : undefined })));
652
+ }
653
+ uploadMultipleFiles(files, options) {
654
+ return this.uploadService
655
+ .uploadAndRegisterMultiple(files, options)
656
+ .pipe(map((res) => ({ ...res, data: res.data?.map(this.mapToUploadedFile) })));
657
+ }
658
+ getFileUrls(fileIds) {
659
+ return this.fileManagerApi.getFiles(fileIds).pipe(map((res) => ({
660
+ ...res,
661
+ data: res.data?.map((f) => ({ id: f.id, name: f.name, contentType: f.contentType, size: '0', url: f.url })),
314
662
  })));
315
663
  }
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 });
664
+ loadFolders(filter) {
665
+ return this.folderApi
666
+ .getAll(filter.search || '', {
667
+ pagination: { currentPage: filter.page, pageSize: filter.pageSize },
668
+ })
669
+ .pipe(map((res) => ({ ...res, data: res.data?.map((f) => ({ id: f.id, name: f.name, slug: f.slug })) })));
670
+ }
671
+ loadStorageConfigs(filter) {
672
+ return this.storageConfigApi
673
+ .getAll(filter.search || '', {
674
+ pagination: { currentPage: filter.page, pageSize: filter.pageSize },
675
+ })
676
+ .pipe(map((res) => ({ ...res, data: res.data?.map((c) => ({ id: c.id, name: c.name, isDefault: c.isDefault })) })));
677
+ }
678
+ // ─── Private Helpers ────────────────────────────────────────────────────────
679
+ buildFileFilter(filter) {
680
+ const filterObj = {};
681
+ if (filter.folderId)
682
+ filterObj['folderId'] = filter.folderId;
683
+ if (filter.storageConfigId)
684
+ filterObj['storageConfigId'] = filter.storageConfigId;
685
+ if (filter.contentTypes?.length)
686
+ filterObj['contentType'] = filter.contentTypes.join(',');
687
+ return filterObj;
688
+ }
689
+ mapToFileBasicInfo = (f) => ({
690
+ id: f.id,
691
+ name: f.name,
692
+ contentType: f.contentType,
693
+ size: f.size,
694
+ url: f.url,
695
+ });
696
+ mapToUploadedFile = (f) => ({
697
+ id: f.id,
698
+ name: f.name,
699
+ key: f.key,
700
+ size: parseFloat(f.size) * 1024,
701
+ contentType: f.contentType,
702
+ });
703
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileProviderAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
704
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileProviderAdapter });
318
705
  }
319
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ProfileUploadProviderAdapter, decorators: [{
706
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileProviderAdapter, decorators: [{
320
707
  type: Injectable
321
708
  }] });
322
709
 
@@ -339,54 +726,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
339
726
  function provideStorageProviders() {
340
727
  return [
341
728
  {
342
- provide: PROFILE_UPLOAD_PROVIDER,
343
- useClass: ProfileUploadProviderAdapter,
729
+ provide: FILE_PROVIDER,
730
+ useClass: FileProviderAdapter,
344
731
  },
345
732
  ];
346
733
  }
347
734
 
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
735
  /**
391
736
  * Storage Module Routes
392
737
  * All routes protected by permission guards
@@ -394,6 +739,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
394
739
  const STORAGE_ROUTES = [
395
740
  {
396
741
  path: '',
742
+ resolve: { translations: resolveTranslationModule({ modules: ['storage'], fallbackMessages: { ...STORAGE_MESSAGES, ...SHARED_MESSAGES } }) },
397
743
  loadComponent: () => Promise.resolve().then(function () { return storageContainer_component; }).then((m) => m.StorageContainerComponent),
398
744
  children: [
399
745
  // File Manager Routes
@@ -403,17 +749,17 @@ const STORAGE_ROUTES = [
403
749
  children: [
404
750
  {
405
751
  path: '',
406
- loadComponent: () => import('./flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs').then((m) => m.FileManagerListComponent),
752
+ loadComponent: () => import('./flusys-ng-storage-file-manager-list.component-bj6oOzOL.mjs').then((m) => m.FileManagerListComponent),
407
753
  },
408
754
  {
409
755
  path: 'new',
410
756
  canActivate: [permissionGuard(FILE_PERMISSIONS.CREATE)],
411
- loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs').then((m) => m.FileManagerFormComponent),
757
+ loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-AM7JE-s5.mjs').then((m) => m.FileManagerFormComponent),
412
758
  },
413
759
  {
414
760
  path: ':id',
415
761
  canActivate: [permissionGuard(FILE_PERMISSIONS.UPDATE)],
416
- loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs').then((m) => m.FileManagerFormComponent),
762
+ loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-AM7JE-s5.mjs').then((m) => m.FileManagerFormComponent),
417
763
  },
418
764
  ],
419
765
  },
@@ -424,17 +770,17 @@ const STORAGE_ROUTES = [
424
770
  children: [
425
771
  {
426
772
  path: '',
427
- loadComponent: () => import('./flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs').then((m) => m.FolderListComponent),
773
+ loadComponent: () => import('./flusys-ng-storage-folder-list.component-B2Q41I1g.mjs').then((m) => m.FolderListComponent),
428
774
  },
429
775
  {
430
776
  path: 'new',
431
777
  canActivate: [permissionGuard(FOLDER_PERMISSIONS.CREATE)],
432
- loadComponent: () => import('./flusys-ng-storage-folder-form.component-BTQGEqNu.mjs').then((m) => m.FolderFormComponent),
778
+ loadComponent: () => import('./flusys-ng-storage-folder-form.component-Cc2ybTwu.mjs').then((m) => m.FolderFormComponent),
433
779
  },
434
780
  {
435
781
  path: ':id',
436
782
  canActivate: [permissionGuard(FOLDER_PERMISSIONS.UPDATE)],
437
- loadComponent: () => import('./flusys-ng-storage-folder-form.component-BTQGEqNu.mjs').then((m) => m.FolderFormComponent),
783
+ loadComponent: () => import('./flusys-ng-storage-folder-form.component-Cc2ybTwu.mjs').then((m) => m.FolderFormComponent),
438
784
  },
439
785
  ],
440
786
  },
@@ -445,17 +791,17 @@ const STORAGE_ROUTES = [
445
791
  children: [
446
792
  {
447
793
  path: '',
448
- loadComponent: () => import('./flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs').then((m) => m.StorageConfigListComponent),
794
+ loadComponent: () => import('./flusys-ng-storage-storage-config-list.component-DBH_qf9o.mjs').then((m) => m.StorageConfigListComponent),
449
795
  },
450
796
  {
451
797
  path: 'new',
452
798
  canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.CREATE)],
453
- loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs').then((m) => m.StorageConfigFormComponent),
799
+ loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-a88XhuPH.mjs').then((m) => m.StorageConfigFormComponent),
454
800
  },
455
801
  {
456
802
  path: ':id',
457
803
  canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.UPDATE)],
458
- loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs').then((m) => m.StorageConfigFormComponent),
804
+ loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-a88XhuPH.mjs').then((m) => m.StorageConfigFormComponent),
459
805
  },
460
806
  ],
461
807
  },
@@ -478,21 +824,21 @@ class StorageContainerComponent {
478
824
  allTabs = [
479
825
  {
480
826
  id: 'files',
481
- label: 'Files',
827
+ labelKey: 'storage.file.title',
482
828
  icon: 'pi pi-file',
483
829
  route: 'files',
484
830
  permission: FILE_PERMISSIONS.READ,
485
831
  },
486
832
  {
487
833
  id: 'folders',
488
- label: 'Folders',
834
+ labelKey: 'storage.folder.title',
489
835
  icon: 'pi pi-folder',
490
836
  route: 'folders',
491
837
  permission: FOLDER_PERMISSIONS.READ,
492
838
  },
493
839
  {
494
840
  id: 'configs',
495
- label: 'Configurations',
841
+ labelKey: 'storage.config.title',
496
842
  icon: 'pi pi-cog',
497
843
  route: 'configs',
498
844
  permission: STORAGE_CONFIG_PERMISSIONS.READ,
@@ -500,19 +846,12 @@ class StorageContainerComponent {
500
846
  ];
501
847
  /** Visible tabs based on user permissions */
502
848
  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
849
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
511
850
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: StorageContainerComponent, isStandalone: true, selector: "lib-storage-container", ngImport: i0, template: `
512
851
  <div class="p-2 sm:p-4">
513
852
  <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>
853
+ <h1 class="text-xl sm:text-2xl font-bold m-0">{{ 'storage.title' | translate }}</h1>
854
+ <p class="text-muted-color mt-1 text-sm sm:text-base">{{ 'storage.subtitle' | translate }}</p>
516
855
  </div>
517
856
 
518
857
  <div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
@@ -523,22 +862,22 @@ class StorageContainerComponent {
523
862
  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
863
  >
525
864
  <i [class]="tab.icon"></i>
526
- <span>{{ tab.label }}</span>
865
+ <span>{{ tab.labelKey | translate }}</span>
527
866
  </a>
528
867
  }
529
868
  </div>
530
869
 
531
870
  <router-outlet />
532
871
  </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 });
872
+ `, 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"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
534
873
  }
535
874
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageContainerComponent, decorators: [{
536
875
  type: Component,
537
- args: [{ selector: 'lib-storage-container', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive], template: `
876
+ args: [{ selector: 'lib-storage-container', imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive, TranslatePipe], template: `
538
877
  <div class="p-2 sm:p-4">
539
878
  <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>
879
+ <h1 class="text-xl sm:text-2xl font-bold m-0">{{ 'storage.title' | translate }}</h1>
880
+ <p class="text-muted-color mt-1 text-sm sm:text-base">{{ 'storage.subtitle' | translate }}</p>
542
881
  </div>
543
882
 
544
883
  <div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
@@ -549,7 +888,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
549
888
  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
889
  >
551
890
  <i [class]="tab.icon"></i>
552
- <span>{{ tab.label }}</span>
891
+ <span>{{ tab.labelKey | translate }}</span>
553
892
  </a>
554
893
  }
555
894
  </div>
@@ -567,11 +906,11 @@ var storageContainer_component = /*#__PURE__*/Object.freeze({
567
906
  // =============================================================================
568
907
  // @flusys/ng-storage - Storage Package
569
908
  // =============================================================================
570
- // Enums
909
+ // Constants
571
910
 
572
911
  /**
573
912
  * Generated bundle index. Do not edit.
574
913
  */
575
914
 
576
- export { BaseStorageListPage, BaseStoragePage, FileLocationEnum, FileManagerApiService, FolderApiService, ImageFormat, ProfileUploadProviderAdapter, STORAGE_PROVIDERS, STORAGE_ROUTES, StorageConfigApiService, StorageContainerComponent, UploadService, formatFileSize, getFileIcon, getProviderIcon, getProviderLabel, getProviderSeverity, provideStorageProviders };
915
+ 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
916
  //# sourceMappingURL=flusys-ng-storage.mjs.map