@flusys/ng-storage 1.1.1-beta → 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.
- package/README.md +36 -623
- package/fesm2022/flusys-ng-storage-file-manager-form.component-DhqZmpuX.mjs +671 -0
- package/fesm2022/flusys-ng-storage-file-manager-form.component-DhqZmpuX.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-file-manager-list.component-nZDj1UfS.mjs +464 -0
- package/fesm2022/flusys-ng-storage-file-manager-list.component-nZDj1UfS.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-folder-form.component-Bru9NQyr.mjs +218 -0
- package/fesm2022/flusys-ng-storage-folder-form.component-Bru9NQyr.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-folder-list.component-fuJMWYWZ.mjs +263 -0
- package/fesm2022/flusys-ng-storage-folder-list.component-fuJMWYWZ.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-storage-config-form.component-Da-pWR7d.mjs +891 -0
- package/fesm2022/flusys-ng-storage-storage-config-form.component-Da-pWR7d.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-storage-config-list.component-D59X4sae.mjs +288 -0
- package/fesm2022/flusys-ng-storage-storage-config-list.component-D59X4sae.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage.mjs +679 -321
- package/fesm2022/flusys-ng-storage.mjs.map +1 -1
- package/package.json +11 -10
- package/types/flusys-ng-storage.d.ts +179 -178
- package/fesm2022/flusys-ng-storage-file-manager-form.component-H3OZS0SW.mjs +0 -715
- package/fesm2022/flusys-ng-storage-file-manager-form.component-H3OZS0SW.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-file-manager-list.component-4pb6TafD.mjs +0 -475
- package/fesm2022/flusys-ng-storage-file-manager-list.component-4pb6TafD.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-folder-form.component-DQ0QY7P0.mjs +0 -253
- package/fesm2022/flusys-ng-storage-folder-form.component-DQ0QY7P0.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-folder-list.component-D-oMaEgl.mjs +0 -267
- package/fesm2022/flusys-ng-storage-folder-list.component-D-oMaEgl.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-storage-config-form.component-CjIAxfHC.mjs +0 -546
- package/fesm2022/flusys-ng-storage-storage-config-form.component-CjIAxfHC.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-storage-config-list.component-jHk8O9Mx.mjs +0 -328
- package/fesm2022/flusys-ng-storage-storage-config-list.component-jHk8O9Mx.mjs.map +0 -1
|
@@ -1,12 +1,166 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Injectable, Component } from '@angular/core';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { BaseApiService, getDefaultStorageConfigId } from '@flusys/ng-core';
|
|
6
|
-
import {
|
|
7
|
-
import { ApiResourceService,
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
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';
|
|
5
|
+
import { APP_CONFIG, DEFAULT_APP_NAME, BaseApiService, getDefaultStorageConfigId } from '@flusys/ng-core';
|
|
6
|
+
import { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';
|
|
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
|
+
};
|
|
10
164
|
|
|
11
165
|
/**
|
|
12
166
|
* File Location Enum
|
|
@@ -32,63 +186,307 @@ var ImageFormat;
|
|
|
32
186
|
})(ImageFormat || (ImageFormat = {}));
|
|
33
187
|
|
|
34
188
|
/**
|
|
35
|
-
*
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
+
};
|
|
197
|
+
/**
|
|
198
|
+
* Provider configurations for all storage types
|
|
199
|
+
*/
|
|
200
|
+
const PROVIDER_CONFIGS = {
|
|
201
|
+
[FileLocationEnum.LOCAL]: {
|
|
202
|
+
labelKey: 'storage.local.title',
|
|
203
|
+
icon: 'folder',
|
|
204
|
+
severity: 'secondary',
|
|
205
|
+
},
|
|
206
|
+
[FileLocationEnum.AWS]: {
|
|
207
|
+
labelKey: 'storage.aws.title',
|
|
208
|
+
icon: 'cloud',
|
|
209
|
+
severity: 'warn',
|
|
210
|
+
},
|
|
211
|
+
[FileLocationEnum.AZURE]: {
|
|
212
|
+
labelKey: 'storage.azure.title',
|
|
213
|
+
icon: 'cloud',
|
|
214
|
+
severity: 'info',
|
|
215
|
+
},
|
|
216
|
+
[FileLocationEnum.SFTP]: {
|
|
217
|
+
labelKey: 'storage.sftp.title',
|
|
218
|
+
icon: 'server',
|
|
219
|
+
severity: 'contrast',
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
/**
|
|
223
|
+
* Get provider label translation key
|
|
224
|
+
*/
|
|
225
|
+
function getProviderLabelKey(storage) {
|
|
226
|
+
const enumValue = storage;
|
|
227
|
+
return PROVIDER_CONFIGS[enumValue]?.labelKey ?? storage;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get provider icon class
|
|
231
|
+
*/
|
|
232
|
+
function getProviderIcon(storage) {
|
|
233
|
+
const enumValue = storage;
|
|
234
|
+
return PROVIDER_CONFIGS[enumValue]?.icon ?? 'database';
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get provider severity for f-tag
|
|
238
|
+
*/
|
|
239
|
+
function getProviderSeverity(storage) {
|
|
240
|
+
const enumValue = storage;
|
|
241
|
+
return PROVIDER_CONFIGS[enumValue]?.severity ?? 'secondary';
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Get file icon based on content type
|
|
245
|
+
*/
|
|
246
|
+
function getFileIcon(contentType) {
|
|
247
|
+
if (contentType.startsWith('image/'))
|
|
248
|
+
return 'image';
|
|
249
|
+
if (contentType.startsWith('video/'))
|
|
250
|
+
return 'video';
|
|
251
|
+
if (contentType.startsWith('audio/'))
|
|
252
|
+
return 'volume-up';
|
|
253
|
+
if (contentType.includes('pdf'))
|
|
254
|
+
return 'file-pdf';
|
|
255
|
+
if (contentType.includes('word') || contentType.includes('document'))
|
|
256
|
+
return 'file-word';
|
|
257
|
+
if (contentType.includes('excel') || contentType.includes('spreadsheet'))
|
|
258
|
+
return 'file-excel';
|
|
259
|
+
return 'file';
|
|
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
|
+
];
|
|
268
|
+
/**
|
|
269
|
+
* Format file size from bytes to human readable format
|
|
270
|
+
* Returns an object with value and unitKey for translation
|
|
40
271
|
*/
|
|
272
|
+
function formatFileSizeWithKey(bytes) {
|
|
273
|
+
if (bytes <= 0)
|
|
274
|
+
return { value: '0', unitKey: 'storage.size.bytes' };
|
|
275
|
+
const k = 1024;
|
|
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]}`;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Storage provider options for dropdown selects
|
|
297
|
+
* Labels are translation keys - components should translate them
|
|
298
|
+
*/
|
|
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 },
|
|
304
|
+
];
|
|
305
|
+
|
|
306
|
+
class BaseStoragePage {
|
|
307
|
+
router = inject(Router);
|
|
308
|
+
messageService = inject(MessageService);
|
|
309
|
+
appConfig = inject(APP_CONFIG);
|
|
310
|
+
companyContext = inject(LAYOUT_AUTH_STATE);
|
|
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);
|
|
320
|
+
this.messageService.add({ severity: 'success', summary, detail });
|
|
321
|
+
}
|
|
322
|
+
showError(detail, summaryKey = 'shared.error') {
|
|
323
|
+
const summary = this.translateAdapter.translate(summaryKey);
|
|
324
|
+
this.messageService.add({ severity: 'error', summary, detail });
|
|
325
|
+
}
|
|
326
|
+
navigateTo(path) {
|
|
327
|
+
this.router.navigate(path);
|
|
328
|
+
}
|
|
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 });
|
|
331
|
+
}
|
|
332
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: BaseStoragePage, decorators: [{
|
|
333
|
+
type: Directive
|
|
334
|
+
}] });
|
|
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);
|
|
342
|
+
companyContext = inject(LAYOUT_AUTH_STATE);
|
|
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 */ []));
|
|
357
|
+
totalRecords = this.total;
|
|
358
|
+
currentCompanyName = computed(() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, /* @ts-ignore */
|
|
359
|
+
...(ngDevMode ? [{ debugName: "currentCompanyName" }] : /* istanbul ignore next */ []));
|
|
360
|
+
getDeleteConfirmMessage(item) {
|
|
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);
|
|
442
|
+
}
|
|
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 });
|
|
448
|
+
}
|
|
449
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: BaseStorageListPage, decorators: [{
|
|
450
|
+
type: Directive
|
|
451
|
+
}] });
|
|
452
|
+
|
|
41
453
|
class FileManagerApiService extends ApiResourceService {
|
|
42
454
|
constructor() {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Get multiple files with valid URLs
|
|
48
|
-
* POST /storage/file-manager/get-files
|
|
49
|
-
* Regenerates presigned URLs if expired
|
|
50
|
-
*/
|
|
455
|
+
super('file-manager', 'storage');
|
|
456
|
+
}
|
|
51
457
|
getFiles(fileIds) {
|
|
52
458
|
const requests = fileIds.map((id) => ({ id }));
|
|
53
459
|
return this.http.post(`${this.baseUrl}/get-files`, requests);
|
|
54
460
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
* POST /storage/file-manager/get-all
|
|
58
|
-
* Filtered by folder ID and company (if enabled)
|
|
59
|
-
*/
|
|
60
|
-
getFilesByFolder(folderId, page = 1, limit = 50) {
|
|
61
|
-
return this.getAll('', {
|
|
62
|
-
filter: folderId ? { folderId } : {},
|
|
63
|
-
pagination: { currentPage: page - 1, pageSize: limit },
|
|
64
|
-
select: [],
|
|
65
|
-
sort: {},
|
|
66
|
-
});
|
|
461
|
+
createFile(dto) {
|
|
462
|
+
return this.http.post(`${this.baseUrl}/insert`, dto);
|
|
67
463
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
* POST /storage/file-manager/get-all
|
|
71
|
-
* Filtered by isPrivate and user permission 'storage.file.viewPrivate'
|
|
72
|
-
*/
|
|
73
|
-
getPrivateFiles(page = 1, limit = 50) {
|
|
74
|
-
return this.getAll('', {
|
|
75
|
-
filter: { isPrivate: true },
|
|
76
|
-
pagination: { currentPage: page - 1, pageSize: limit },
|
|
77
|
-
select: [],
|
|
78
|
-
sort: {},
|
|
79
|
-
});
|
|
464
|
+
createFiles(dtos) {
|
|
465
|
+
return this.http.post(`${this.baseUrl}/insert-many`, dtos);
|
|
80
466
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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' });
|
|
469
|
+
}
|
|
470
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: FileManagerApiService, decorators: [{
|
|
471
|
+
type: Injectable,
|
|
472
|
+
args: [{ providedIn: 'root' }]
|
|
473
|
+
}], ctorParameters: () => [] });
|
|
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');
|
|
87
485
|
}
|
|
88
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
89
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
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' });
|
|
90
488
|
}
|
|
91
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
489
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: FolderApiService, decorators: [{
|
|
92
490
|
type: Injectable,
|
|
93
491
|
args: [{
|
|
94
492
|
providedIn: 'root',
|
|
@@ -96,71 +494,111 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
96
494
|
}], ctorParameters: () => [] });
|
|
97
495
|
|
|
98
496
|
/**
|
|
99
|
-
*
|
|
100
|
-
* Handles
|
|
101
|
-
* Endpoint: POST /storage/
|
|
497
|
+
* Storage Config API Service
|
|
498
|
+
* Handles storage configuration CRUD operations
|
|
499
|
+
* Endpoint: POST /storage/storage-config/*
|
|
102
500
|
*
|
|
103
|
-
*
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Upload Service - File upload/delete operations
|
|
519
|
+
* Endpoint: POST /storage/upload/*
|
|
109
520
|
*/
|
|
110
521
|
class UploadService extends BaseApiService {
|
|
111
522
|
fileManagerApi = inject(FileManagerApiService);
|
|
112
523
|
constructor() {
|
|
113
524
|
super('storage');
|
|
114
525
|
}
|
|
115
|
-
/**
|
|
116
|
-
* Get default storage config ID from app config
|
|
117
|
-
* Used when no storageConfigId is provided in upload options
|
|
118
|
-
*/
|
|
526
|
+
/** Get default storage config ID from app config */
|
|
119
527
|
getDefaultConfigId() {
|
|
120
528
|
return getDefaultStorageConfigId(this.appConfig);
|
|
121
529
|
}
|
|
122
|
-
/**
|
|
123
|
-
* Upload single file
|
|
124
|
-
* POST /storage/upload/single-file?storageConfigId=x&folderPath=y
|
|
125
|
-
*
|
|
126
|
-
* @param file - File to upload
|
|
127
|
-
* @param options - Upload options (storage config, compression, etc.)
|
|
128
|
-
*/
|
|
530
|
+
/** Upload single file - POST /storage/upload/single-file */
|
|
129
531
|
uploadSingleFile(file, options) {
|
|
130
532
|
const formData = new FormData();
|
|
131
533
|
formData.append('file', file);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
: this.getUrl('upload/single-file');
|
|
136
|
-
return this.http.post(url, formData);
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Upload multiple files (max 50)
|
|
140
|
-
* POST /storage/upload/multiple-file?storageConfigId=x&folderPath=y
|
|
141
|
-
*
|
|
142
|
-
* @param files - Array of files to upload
|
|
143
|
-
* @param options - Upload options
|
|
144
|
-
*/
|
|
534
|
+
return this.http.post(this.buildUploadUrl('single-file', options), formData);
|
|
535
|
+
}
|
|
536
|
+
/** Upload multiple files (max 50) - POST /storage/upload/multiple-file */
|
|
145
537
|
uploadMultipleFiles(files, options) {
|
|
146
538
|
const formData = new FormData();
|
|
147
539
|
files.forEach((file) => formData.append('files', file));
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return this.http.post(
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
540
|
+
return this.http.post(this.buildUploadUrl('multiple-file', options), formData);
|
|
541
|
+
}
|
|
542
|
+
/** Delete single file - POST /storage/upload/delete-single-file */
|
|
543
|
+
deleteSingleFile(key, storageConfigId) {
|
|
544
|
+
return this.http.post(this.getUrl('upload/delete-single-file'), this.buildDeleteBody({ key }, storageConfigId));
|
|
545
|
+
}
|
|
546
|
+
/** Delete multiple files - POST /storage/upload/delete-multiple-file */
|
|
547
|
+
deleteMultipleFiles(keys, storageConfigId) {
|
|
548
|
+
return this.http.post(this.getUrl('upload/delete-multiple-file'), this.buildDeleteBody({ keys }, storageConfigId));
|
|
549
|
+
}
|
|
550
|
+
/** Get file download URL - GET /storage/upload/file/:filePath */
|
|
551
|
+
getFileUrl(key) {
|
|
552
|
+
return this.getUrl(`upload/file/${key}`);
|
|
553
|
+
}
|
|
554
|
+
/** Upload file and register in file manager - returns file manager ID (UUID) */
|
|
555
|
+
uploadAndRegister(file, options) {
|
|
556
|
+
return this.uploadSingleFile(file, options).pipe(switchMap((response) => {
|
|
557
|
+
if (!response.success || !response.data) {
|
|
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');
|
|
568
|
+
}
|
|
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
|
+
})));
|
|
575
|
+
}));
|
|
576
|
+
}
|
|
577
|
+
// ─── Private Helpers ───────────────────────────────────────────────────────
|
|
578
|
+
resolveConfigId(storageConfigId) {
|
|
579
|
+
return storageConfigId ?? this.getDefaultConfigId();
|
|
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
|
+
}
|
|
592
|
+
buildUploadUrl(endpoint, options) {
|
|
593
|
+
const params = this.buildQueryParams(options);
|
|
594
|
+
const baseUrl = this.getUrl(`upload/${endpoint}`);
|
|
595
|
+
return params ? `${baseUrl}?${params}` : baseUrl;
|
|
596
|
+
}
|
|
158
597
|
buildQueryParams(options) {
|
|
159
598
|
const params = new URLSearchParams();
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
params.append('storageConfigId', storageConfigId);
|
|
599
|
+
const configId = this.resolveConfigId(options?.storageConfigId);
|
|
600
|
+
if (configId)
|
|
601
|
+
params.append('storageConfigId', configId);
|
|
164
602
|
if (options?.folderPath)
|
|
165
603
|
params.append('folderPath', options.folderPath);
|
|
166
604
|
if (options?.maxWidth)
|
|
@@ -175,120 +613,84 @@ class UploadService extends BaseApiService {
|
|
|
175
613
|
params.append('compress', options.compress.toString());
|
|
176
614
|
return params.toString();
|
|
177
615
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
*
|
|
182
|
-
* @param key - File key/path to delete
|
|
183
|
-
* @param storageConfigId - Optional storage config ID (uses default from config if not provided)
|
|
184
|
-
*/
|
|
185
|
-
deleteSingleFile(key, storageConfigId) {
|
|
186
|
-
const configId = storageConfigId ?? this.getDefaultConfigId();
|
|
187
|
-
return this.http.post(this.getUrl('upload/delete-single-file'), { key, ...(configId && { storageConfigId: configId }) });
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Delete multiple files
|
|
191
|
-
* POST /storage/upload/delete-multiple-file
|
|
192
|
-
*
|
|
193
|
-
* @param keys - Array of file keys/paths to delete
|
|
194
|
-
* @param storageConfigId - Optional storage config ID (uses default from config if not provided)
|
|
195
|
-
*/
|
|
196
|
-
deleteMultipleFiles(keys, storageConfigId) {
|
|
197
|
-
const configId = storageConfigId ?? this.getDefaultConfigId();
|
|
198
|
-
return this.http.post(this.getUrl('upload/delete-multiple-file'), { keys, ...(configId && { storageConfigId: configId }) });
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Get file download URL
|
|
202
|
-
* GET /storage/upload/file/:filePath
|
|
203
|
-
*
|
|
204
|
-
* @param key - File key/path
|
|
205
|
-
* @returns URL for direct file access
|
|
206
|
-
*/
|
|
207
|
-
getFileUrl(key) {
|
|
208
|
-
return this.getUrl(`upload/file/${key}`);
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Upload file and register in file manager
|
|
212
|
-
* Returns file manager ID (UUID) for storing in references like profilePictureId
|
|
213
|
-
*
|
|
214
|
-
* @param file - File to upload
|
|
215
|
-
* @param options - Upload options (storage config, compression, etc.)
|
|
216
|
-
* @returns Observable with file manager record (id is the UUID to store)
|
|
217
|
-
*/
|
|
218
|
-
uploadAndRegister(file, options) {
|
|
219
|
-
return this.uploadSingleFile(file, options).pipe(switchMap((uploadResponse) => {
|
|
220
|
-
if (!uploadResponse.success || !uploadResponse.data) {
|
|
221
|
-
throw new Error(uploadResponse.message || 'Upload failed');
|
|
222
|
-
}
|
|
223
|
-
const uploadedFile = uploadResponse.data;
|
|
224
|
-
const storageConfigId = options?.storageConfigId ?? this.getDefaultConfigId();
|
|
225
|
-
// Create file manager record
|
|
226
|
-
return this.fileManagerApi.create({
|
|
227
|
-
name: uploadedFile.name,
|
|
228
|
-
key: uploadedFile.key,
|
|
229
|
-
size: String(uploadedFile.size),
|
|
230
|
-
contentType: uploadedFile.contentType,
|
|
231
|
-
isPrivate: options?.isPrivate ?? false,
|
|
232
|
-
folderId: options?.folderId,
|
|
233
|
-
storageConfigId,
|
|
234
|
-
location: FileLocationEnum.LOCAL,
|
|
235
|
-
});
|
|
236
|
-
}));
|
|
616
|
+
buildDeleteBody(data, storageConfigId) {
|
|
617
|
+
const configId = this.resolveConfigId(storageConfigId);
|
|
618
|
+
return { ...data, ...(configId && { storageConfigId: configId }) };
|
|
237
619
|
}
|
|
238
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
239
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
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' });
|
|
240
622
|
}
|
|
241
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
623
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: UploadService, decorators: [{
|
|
242
624
|
type: Injectable,
|
|
243
|
-
args: [{
|
|
244
|
-
providedIn: 'root',
|
|
245
|
-
}]
|
|
625
|
+
args: [{ providedIn: 'root' }]
|
|
246
626
|
}], ctorParameters: () => [] });
|
|
247
627
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
*
|
|
251
|
-
* Implements IProfileUploadProvider interface for ng-auth profile page.
|
|
252
|
-
* Uses UploadService.uploadAndRegister() to upload files and register in file manager.
|
|
253
|
-
*
|
|
254
|
-
* @example
|
|
255
|
-
* // In app.config.ts
|
|
256
|
-
* providers: [
|
|
257
|
-
* ...provideStorageProviders(),
|
|
258
|
-
* ]
|
|
259
|
-
*/
|
|
260
|
-
class ProfileUploadProviderAdapter {
|
|
628
|
+
class FileProviderAdapter {
|
|
629
|
+
fileManagerApi = inject(FileManagerApiService);
|
|
261
630
|
uploadService = inject(UploadService);
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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) {
|
|
267
640
|
return this.uploadService
|
|
268
|
-
.uploadAndRegister(file,
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
.pipe(map((
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
name: response.data.name,
|
|
281
|
-
key: response.data.key,
|
|
282
|
-
size: parseFloat(response.data.size) * 1024, // Convert KB to bytes
|
|
283
|
-
contentType: response.data.contentType,
|
|
284
|
-
}
|
|
285
|
-
: undefined,
|
|
641
|
+
.uploadAndRegister(file, options)
|
|
642
|
+
.pipe(map((res) => ({ ...res, data: res.data ? this.mapToUploadedFile(res.data) : undefined })));
|
|
643
|
+
}
|
|
644
|
+
uploadMultipleFiles(files, options) {
|
|
645
|
+
return this.uploadService
|
|
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 })),
|
|
286
653
|
})));
|
|
287
654
|
}
|
|
288
|
-
|
|
289
|
-
|
|
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 });
|
|
290
692
|
}
|
|
291
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
693
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: FileProviderAdapter, decorators: [{
|
|
292
694
|
type: Injectable
|
|
293
695
|
}] });
|
|
294
696
|
|
|
@@ -311,127 +713,87 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
311
713
|
function provideStorageProviders() {
|
|
312
714
|
return [
|
|
313
715
|
{
|
|
314
|
-
provide:
|
|
315
|
-
useClass:
|
|
716
|
+
provide: FILE_PROVIDER,
|
|
717
|
+
useClass: FileProviderAdapter,
|
|
316
718
|
},
|
|
317
719
|
];
|
|
318
720
|
}
|
|
319
721
|
|
|
320
|
-
/**
|
|
321
|
-
* Folder API Service
|
|
322
|
-
* Handles folder CRUD operations
|
|
323
|
-
* Endpoint: POST /storage/folder/*
|
|
324
|
-
*
|
|
325
|
-
* Note: Company feature enabled - all queries automatically scoped to user's company
|
|
326
|
-
*/
|
|
327
|
-
class FolderApiService extends ApiResourceService {
|
|
328
|
-
constructor() {
|
|
329
|
-
const http = inject(HttpClient);
|
|
330
|
-
super('storage/folder', http);
|
|
331
|
-
}
|
|
332
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FolderApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
333
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FolderApiService, providedIn: 'root' });
|
|
334
|
-
}
|
|
335
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FolderApiService, decorators: [{
|
|
336
|
-
type: Injectable,
|
|
337
|
-
args: [{
|
|
338
|
-
providedIn: 'root',
|
|
339
|
-
}]
|
|
340
|
-
}], ctorParameters: () => [] });
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* Storage Config API Service
|
|
344
|
-
* Handles storage configuration CRUD operations
|
|
345
|
-
* Endpoint: POST /storage/storage-config/*
|
|
346
|
-
*
|
|
347
|
-
* Note: Company feature enabled - all queries automatically scoped to user's company
|
|
348
|
-
*/
|
|
349
|
-
class StorageConfigApiService extends ApiResourceService {
|
|
350
|
-
constructor() {
|
|
351
|
-
const http = inject(HttpClient);
|
|
352
|
-
super('storage/storage-config', http);
|
|
353
|
-
}
|
|
354
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageConfigApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
355
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageConfigApiService, providedIn: 'root' });
|
|
356
|
-
}
|
|
357
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageConfigApiService, decorators: [{
|
|
358
|
-
type: Injectable,
|
|
359
|
-
args: [{
|
|
360
|
-
providedIn: 'root',
|
|
361
|
-
}]
|
|
362
|
-
}], ctorParameters: () => [] });
|
|
363
|
-
|
|
364
722
|
/**
|
|
365
723
|
* Storage Module Routes
|
|
366
|
-
* All
|
|
367
|
-
*
|
|
368
|
-
* All routes are protected by permission guards to prevent direct URL access.
|
|
724
|
+
* All routes protected by permission guards
|
|
369
725
|
*/
|
|
370
726
|
const STORAGE_ROUTES = [
|
|
371
727
|
{
|
|
372
728
|
path: '',
|
|
729
|
+
resolve: { translations: resolveTranslationModule({ modules: ['storage'], fallbackMessages: { ...STORAGE_MESSAGES, ...SHARED_MESSAGES } }) },
|
|
373
730
|
loadComponent: () => Promise.resolve().then(function () { return storageContainer_component; }).then((m) => m.StorageContainerComponent),
|
|
374
731
|
children: [
|
|
375
732
|
// File Manager Routes
|
|
376
733
|
{
|
|
377
734
|
path: 'files',
|
|
735
|
+
canActivate: [permissionGuard(FILE_PERMISSIONS.READ)],
|
|
378
736
|
children: [
|
|
379
737
|
{
|
|
380
738
|
path: '',
|
|
381
|
-
loadComponent: () => import('./flusys-ng-storage-file-manager-list.component-
|
|
739
|
+
loadComponent: () => import('./flusys-ng-storage-file-manager-list.component-nZDj1UfS.mjs').then((m) => m.FileManagerListComponent),
|
|
382
740
|
},
|
|
383
741
|
{
|
|
384
742
|
path: 'new',
|
|
385
|
-
|
|
743
|
+
canActivate: [permissionGuard(FILE_PERMISSIONS.CREATE)],
|
|
744
|
+
loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-DhqZmpuX.mjs').then((m) => m.FileManagerFormComponent),
|
|
386
745
|
},
|
|
387
746
|
{
|
|
388
747
|
path: ':id',
|
|
389
|
-
|
|
748
|
+
canActivate: [permissionGuard(FILE_PERMISSIONS.UPDATE)],
|
|
749
|
+
loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-DhqZmpuX.mjs').then((m) => m.FileManagerFormComponent),
|
|
390
750
|
},
|
|
391
751
|
],
|
|
392
752
|
},
|
|
393
753
|
// Folder Routes
|
|
394
754
|
{
|
|
395
755
|
path: 'folders',
|
|
756
|
+
canActivate: [permissionGuard(FOLDER_PERMISSIONS.READ)],
|
|
396
757
|
children: [
|
|
397
758
|
{
|
|
398
759
|
path: '',
|
|
399
|
-
loadComponent: () => import('./flusys-ng-storage-folder-list.component-
|
|
760
|
+
loadComponent: () => import('./flusys-ng-storage-folder-list.component-fuJMWYWZ.mjs').then((m) => m.FolderListComponent),
|
|
400
761
|
},
|
|
401
762
|
{
|
|
402
763
|
path: 'new',
|
|
403
|
-
|
|
764
|
+
canActivate: [permissionGuard(FOLDER_PERMISSIONS.CREATE)],
|
|
765
|
+
loadComponent: () => import('./flusys-ng-storage-folder-form.component-Bru9NQyr.mjs').then((m) => m.FolderFormComponent),
|
|
404
766
|
},
|
|
405
767
|
{
|
|
406
768
|
path: ':id',
|
|
407
|
-
|
|
769
|
+
canActivate: [permissionGuard(FOLDER_PERMISSIONS.UPDATE)],
|
|
770
|
+
loadComponent: () => import('./flusys-ng-storage-folder-form.component-Bru9NQyr.mjs').then((m) => m.FolderFormComponent),
|
|
408
771
|
},
|
|
409
772
|
],
|
|
410
773
|
},
|
|
411
774
|
// Storage Config Routes
|
|
412
775
|
{
|
|
413
776
|
path: 'configs',
|
|
777
|
+
canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.READ)],
|
|
414
778
|
children: [
|
|
415
779
|
{
|
|
416
780
|
path: '',
|
|
417
|
-
loadComponent: () => import('./flusys-ng-storage-storage-config-list.component-
|
|
781
|
+
loadComponent: () => import('./flusys-ng-storage-storage-config-list.component-D59X4sae.mjs').then((m) => m.StorageConfigListComponent),
|
|
418
782
|
},
|
|
419
783
|
{
|
|
420
784
|
path: 'new',
|
|
421
|
-
|
|
785
|
+
canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.CREATE)],
|
|
786
|
+
loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-Da-pWR7d.mjs').then((m) => m.StorageConfigFormComponent),
|
|
422
787
|
},
|
|
423
788
|
{
|
|
424
789
|
path: ':id',
|
|
425
|
-
|
|
790
|
+
canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.UPDATE)],
|
|
791
|
+
loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-Da-pWR7d.mjs').then((m) => m.StorageConfigFormComponent),
|
|
426
792
|
},
|
|
427
793
|
],
|
|
428
794
|
},
|
|
429
|
-
// Default redirect
|
|
430
|
-
{
|
|
431
|
-
path: '',
|
|
432
|
-
redirectTo: 'files',
|
|
433
|
-
pathMatch: 'full',
|
|
434
|
-
},
|
|
795
|
+
// Default redirect
|
|
796
|
+
{ path: '', redirectTo: 'files', pathMatch: 'full' },
|
|
435
797
|
],
|
|
436
798
|
},
|
|
437
799
|
];
|
|
@@ -441,81 +803,77 @@ const STORAGE_ROUTES = [
|
|
|
441
803
|
*
|
|
442
804
|
* Main container for Storage module with tab navigation
|
|
443
805
|
* Routes: Files | Folders | Configurations
|
|
806
|
+
* Tabs are filtered based on user permissions
|
|
444
807
|
*/
|
|
445
808
|
class StorageContainerComponent {
|
|
446
|
-
|
|
447
|
-
|
|
809
|
+
permissionValidator = inject(PermissionValidatorService);
|
|
810
|
+
translateService = inject(TranslateService);
|
|
811
|
+
/** All available tabs with required permissions */
|
|
812
|
+
allTabs = [
|
|
448
813
|
{
|
|
449
814
|
id: 'files',
|
|
450
|
-
|
|
451
|
-
icon: '
|
|
815
|
+
labelKey: 'storage.file.title',
|
|
816
|
+
icon: 'file',
|
|
452
817
|
route: 'files',
|
|
818
|
+
permission: FILE_PERMISSIONS.READ,
|
|
453
819
|
},
|
|
454
820
|
{
|
|
455
821
|
id: 'folders',
|
|
456
|
-
|
|
457
|
-
icon: '
|
|
822
|
+
labelKey: 'storage.folder.title',
|
|
823
|
+
icon: 'folder',
|
|
458
824
|
route: 'folders',
|
|
825
|
+
permission: FOLDER_PERMISSIONS.READ,
|
|
459
826
|
},
|
|
460
827
|
{
|
|
461
828
|
id: 'configs',
|
|
462
|
-
|
|
463
|
-
icon: '
|
|
829
|
+
labelKey: 'storage.config.title',
|
|
830
|
+
icon: 'cog',
|
|
464
831
|
route: 'configs',
|
|
832
|
+
permission: STORAGE_CONFIG_PERMISSIONS.READ,
|
|
465
833
|
},
|
|
466
834
|
];
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
835
|
+
/** Visible tabs based on user permissions */
|
|
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: `
|
|
847
|
+
<div class="p-2 sm:p-4">
|
|
848
|
+
<div class="mb-4">
|
|
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>
|
|
473
851
|
</div>
|
|
474
852
|
|
|
475
|
-
<
|
|
476
|
-
@for (tab of tabs; track tab.id) {
|
|
477
|
-
<a
|
|
478
|
-
[routerLink]="tab.route"
|
|
479
|
-
routerLinkActive="active"
|
|
480
|
-
class="tab-link flex items-center gap-2 px-4 py-2 rounded-t-lg cursor-pointer transition-colors">
|
|
481
|
-
<i [class]="tab.icon"></i>
|
|
482
|
-
<span>{{ tab.label }}</span>
|
|
483
|
-
</a>
|
|
484
|
-
}
|
|
485
|
-
</div>
|
|
853
|
+
<lib-router-tabs [tabs]="visibleTabs()" class="mb-4" />
|
|
486
854
|
|
|
487
|
-
<
|
|
488
|
-
<router-outlet />
|
|
489
|
-
</div>
|
|
855
|
+
<router-outlet />
|
|
490
856
|
</div>
|
|
491
|
-
`, isInline: true,
|
|
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" }] });
|
|
492
858
|
}
|
|
493
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
859
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: StorageContainerComponent, decorators: [{
|
|
494
860
|
type: Component,
|
|
495
|
-
args: [{
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
861
|
+
args: [{
|
|
862
|
+
selector: 'lib-storage-container',
|
|
863
|
+
imports: [RouterOutlet, TranslatePipe, RouterTabsComponent],
|
|
864
|
+
template: `
|
|
865
|
+
<div class="p-2 sm:p-4">
|
|
866
|
+
<div class="mb-4">
|
|
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>
|
|
500
869
|
</div>
|
|
501
870
|
|
|
502
|
-
<
|
|
503
|
-
@for (tab of tabs; track tab.id) {
|
|
504
|
-
<a
|
|
505
|
-
[routerLink]="tab.route"
|
|
506
|
-
routerLinkActive="active"
|
|
507
|
-
class="tab-link flex items-center gap-2 px-4 py-2 rounded-t-lg cursor-pointer transition-colors">
|
|
508
|
-
<i [class]="tab.icon"></i>
|
|
509
|
-
<span>{{ tab.label }}</span>
|
|
510
|
-
</a>
|
|
511
|
-
}
|
|
512
|
-
</div>
|
|
871
|
+
<lib-router-tabs [tabs]="visibleTabs()" class="mb-4" />
|
|
513
872
|
|
|
514
|
-
<
|
|
515
|
-
<router-outlet />
|
|
516
|
-
</div>
|
|
873
|
+
<router-outlet />
|
|
517
874
|
</div>
|
|
518
|
-
`,
|
|
875
|
+
`,
|
|
876
|
+
}]
|
|
519
877
|
}] });
|
|
520
878
|
|
|
521
879
|
var storageContainer_component = /*#__PURE__*/Object.freeze({
|
|
@@ -526,11 +884,11 @@ var storageContainer_component = /*#__PURE__*/Object.freeze({
|
|
|
526
884
|
// =============================================================================
|
|
527
885
|
// @flusys/ng-storage - Storage Package
|
|
528
886
|
// =============================================================================
|
|
529
|
-
//
|
|
887
|
+
// Constants
|
|
530
888
|
|
|
531
889
|
/**
|
|
532
890
|
* Generated bundle index. Do not edit.
|
|
533
891
|
*/
|
|
534
892
|
|
|
535
|
-
export { FileLocationEnum, FileManagerApiService, FolderApiService, ImageFormat,
|
|
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 };
|
|
536
894
|
//# sourceMappingURL=flusys-ng-storage.mjs.map
|