@flusys/ng-storage 3.0.0 → 4.0.0-rc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/README.md +13 -7
  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 +4 -4
  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
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Storage Package Guide
2
2
 
3
3
  > **Package:** `@flusys/ng-storage`
4
- > **Version:** 3.0.0
4
+ > **Version:** 4.0.0-rc
5
5
  > **Type:** File storage management with upload, folder organization, and multi-provider support
6
6
 
7
7
  ---
@@ -529,11 +529,17 @@ Registers provider adapters that enable other packages (ng-auth) to access stora
529
529
  ...provideStorageProviders()
530
530
  ```
531
531
 
532
- **Registers:** `PROFILE_UPLOAD_PROVIDER` → `ProfileUploadProviderAdapter` (implements `IProfileUploadProvider` from ng-shared)
532
+ **Registers:** `FILE_PROVIDER` → `FileProviderAdapter` (implements `IFileProvider` from ng-shared)
533
533
 
534
- **ProfileUploadProviderAdapter defaults:** `folderPath: 'profile-pictures'`, `compress: true`, `maxWidth: 400`, `maxHeight: 400`
534
+ **FileProviderAdapter features:**
535
+ - `loadFiles(filter)` - Load files with filtering and pagination
536
+ - `uploadFile(file, options)` - Upload a single file and register in file manager
537
+ - `uploadMultipleFiles(files, options)` - Upload multiple files at once
538
+ - `getFileUrls(fileIds)` - Get file URLs by IDs (for refreshing presigned URLs)
539
+ - `loadFolders(filter)` - Load folders for folder selector
540
+ - `loadStorageConfigs(filter)` - Load storage configs for config selector
535
541
 
536
- **Usage:** `inject(PROFILE_UPLOAD_PROVIDER, { optional: true })?.uploadProfilePicture(file)`
542
+ **Usage:** `inject(FILE_PROVIDER, { optional: true })?.uploadFile(file, options)`
537
543
 
538
544
  ---
539
545
 
@@ -544,13 +550,13 @@ Registers provider adapters that enable other packages (ng-auth) to access stora
544
550
  | Enums | `FileLocationEnum`, `ImageFormat` |
545
551
  | Interfaces | `IFileManager`, `ICreateFileManagerDto`, `IUpdateFileManagerDto`, `IGetFilesRequestDto`, `IFilesResponseDto`, `IFolder`, `ICreateFolderDto`, `IUpdateFolderDto`, `IStorageConfig`, `ICreateStorageConfigDto`, `IUpdateStorageConfigDto`, `IAwsS3Config`, `IAzureBlobConfig`, `ISftpConfig`, `ILocalStorageConfig`, `IUploadOptionsDto`, `IUploadedFileInfo` |
546
552
  | Utils | `getProviderLabel`, `getProviderIcon`, `getProviderSeverity`, `getFileIcon`, `formatFileSize`, `STORAGE_PROVIDERS`, `BaseStoragePage`, `BaseStorageListPage` |
547
- | Adapters | `ProfileUploadProviderAdapter` |
553
+ | Adapters | `FileProviderAdapter` |
548
554
  | Services | `FileManagerApiService`, `FolderApiService`, `StorageConfigApiService`, `UploadService` |
549
555
  | Routes | `STORAGE_ROUTES`, `StorageContainerComponent` |
550
556
  | Providers | `provideStorageProviders` |
551
557
 
552
558
  ---
553
559
 
554
- **Last Updated:** 2026-02-25
555
- **Version:** 3.0.0
560
+ **Last Updated:** 2026-03-01
561
+ **Version:** 3.0.1
556
562
  **Angular Version:** 21
@@ -1,26 +1,24 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, signal, computed, effect, ChangeDetectionStrategy, Component } from '@angular/core';
2
+ import { inject, signal, computed, effect, Component } from '@angular/core';
3
3
  import { toSignal } from '@angular/core/rxjs-interop';
4
4
  import { form, required, FormField } from '@angular/forms/signals';
5
5
  import { ActivatedRoute } from '@angular/router';
6
- import { AngularModule, PrimeModule } from '@flusys/ng-shared';
6
+ import { TranslateService } from '@flusys/ng-localization';
7
+ import { AngularModule, PrimeModule, TranslatePipe } from '@flusys/ng-shared';
7
8
  import { firstValueFrom } from 'rxjs';
8
- import { BaseStoragePage, FileManagerApiService, UploadService, StorageConfigApiService, FolderApiService, formatFileSize, getProviderLabel } from './flusys-ng-storage.mjs';
9
+ import { BaseStoragePage, FileManagerApiService, UploadService, StorageConfigApiService, FolderApiService, formatFileSize, getProviderLabelKey } from './flusys-ng-storage.mjs';
9
10
  import * as i1 from '@angular/forms';
10
11
  import * as i1$1 from 'primeng/button';
11
12
  import * as i3 from 'primeng/fileupload';
12
13
  import * as i3$1 from 'primeng/inputtext';
13
14
 
14
- /**
15
- * File Manager Form Component
16
- * Upload and edit file metadata
17
- */
18
15
  class FileManagerFormComponent extends BaseStoragePage {
19
16
  route = inject(ActivatedRoute);
20
17
  fileService = inject(FileManagerApiService);
21
18
  uploadService = inject(UploadService);
22
19
  configService = inject(StorageConfigApiService);
23
20
  folderService = inject(FolderApiService);
21
+ translateService = inject(TranslateService);
24
22
  routeParams = toSignal(this.route.paramMap);
25
23
  initialized = false;
26
24
  isLoadingConfigs = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingConfigs" }] : []));
@@ -30,10 +28,8 @@ class FileManagerFormComponent extends BaseStoragePage {
30
28
  selectedFile = signal(null, ...(ngDevMode ? [{ debugName: "selectedFile" }] : []));
31
29
  availableConfigs = signal([], ...(ngDevMode ? [{ debugName: "availableConfigs" }] : []));
32
30
  availableFolders = signal([], ...(ngDevMode ? [{ debugName: "availableFolders" }] : []));
33
- /** Utility functions exposed to template */
34
31
  formatFileSize = formatFileSize;
35
- getProviderLabel = getProviderLabel;
36
- /** Form model */
32
+ getProviderLabelKey = getProviderLabelKey;
37
33
  formModel = signal({
38
34
  id: '',
39
35
  name: '',
@@ -41,11 +37,9 @@ class FileManagerFormComponent extends BaseStoragePage {
41
37
  folderId: '',
42
38
  storageConfigId: '',
43
39
  }, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
44
- /** Form with validation */
45
40
  fileForm = form(this.formModel, (f) => {
46
- required(f.name, { message: 'File name is required' });
41
+ required(f.name, { message: this.translateService.translate('storage.validation.file.name.required') });
47
42
  });
48
- /** Check if form is valid */
49
43
  isFormValid = computed(() => this.formModel().name.trim().length > 0, ...(ngDevMode ? [{ debugName: "isFormValid" }] : []));
50
44
  constructor() {
51
45
  super();
@@ -81,7 +75,7 @@ class FileManagerFormComponent extends BaseStoragePage {
81
75
  }
82
76
  }
83
77
  catch {
84
- this.showError('Failed to load storage configurations.');
78
+ this.showError(this.translateService.translate('storage.config.load.failed'));
85
79
  }
86
80
  finally {
87
81
  this.isLoadingConfigs.set(false);
@@ -124,7 +118,7 @@ class FileManagerFormComponent extends BaseStoragePage {
124
118
  }
125
119
  }
126
120
  catch {
127
- this.showError('Failed to load file.');
121
+ this.showError(this.translateService.translate('storage.error.load.file.failed'));
128
122
  this.navigateTo(['/storage/files']);
129
123
  }
130
124
  finally {
@@ -151,16 +145,16 @@ class FileManagerFormComponent extends BaseStoragePage {
151
145
  isPrivate: formValue.isPrivate,
152
146
  folderId: formValue.folderId || null,
153
147
  });
154
- this.showSuccess('File updated successfully.');
148
+ this.showSuccess(this.translateService.translate('storage.file.update.success'));
155
149
  }
156
150
  else {
157
151
  const file = this.selectedFile();
158
152
  if (!file) {
159
- this.showError('Please select a file to upload.');
153
+ this.showError(this.translateService.translate('storage.error.no.file'));
160
154
  return;
161
155
  }
162
156
  if (!formValue.storageConfigId) {
163
- this.showError('Please select a storage configuration.');
157
+ this.showError(this.translateService.translate('storage.error.no.config'));
164
158
  return;
165
159
  }
166
160
  const uploadResponse = await firstValueFrom(this.uploadService.uploadSingleFile(file, {
@@ -170,7 +164,7 @@ class FileManagerFormComponent extends BaseStoragePage {
170
164
  }));
171
165
  if (uploadResponse.success && uploadResponse.data) {
172
166
  const uploadedFileInfo = uploadResponse.data;
173
- await this.fileService.createFileAsync({
167
+ await firstValueFrom(this.fileService.createFile({
174
168
  name: formValue.name,
175
169
  key: uploadedFileInfo.key,
176
170
  contentType: file.type || 'application/octet-stream',
@@ -178,17 +172,17 @@ class FileManagerFormComponent extends BaseStoragePage {
178
172
  isPrivate: formValue.isPrivate,
179
173
  folderId: formValue.folderId || undefined,
180
174
  storageConfigId: formValue.storageConfigId,
181
- });
182
- this.showSuccess('File uploaded successfully.');
175
+ }));
176
+ this.showSuccess(this.translateService.translate('storage.upload.success'));
183
177
  }
184
178
  else {
185
- throw new Error('Upload failed');
179
+ throw new Error(this.translateService.translate('storage.upload.failed'));
186
180
  }
187
181
  }
188
182
  this.navigateTo(['/storage/files']);
189
183
  }
190
- catch (error) {
191
- this.showError(error?.message || `Failed to ${this.isEditMode() ? 'update' : 'upload'} file.`);
184
+ catch {
185
+ this.showError(this.translateService.translate('storage.upload.failed'));
192
186
  }
193
187
  finally {
194
188
  this.isLoading.set(false);
@@ -201,9 +195,9 @@ class FileManagerFormComponent extends BaseStoragePage {
201
195
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: FileManagerFormComponent, isStandalone: true, selector: "lib-file-manager-form", usesInheritance: true, ngImport: i0, template: `
202
196
  <div class="card">
203
197
  <div class="mb-4">
204
- <h3 class="text-lg sm:text-xl font-semibold">{{ isEditMode() ? 'Edit File' : 'Upload File' }}</h3>
198
+ <h3 class="text-lg sm:text-xl font-semibold">{{ (isEditMode() ? 'storage.file.manager.edit.file' : 'storage.file.manager.upload.file') | translate }}</h3>
205
199
  @if (showCompanyInfo()) {
206
- <p class="text-sm text-muted-color mt-1">Company: {{ currentCompanyName() }}</p>
200
+ <p class="text-sm text-muted-color mt-1">{{ 'storage.company' | translate }}: {{ currentCompanyName() }}</p>
207
201
  }
208
202
  </div>
209
203
 
@@ -211,25 +205,25 @@ class FileManagerFormComponent extends BaseStoragePage {
211
205
  @if (!isEditMode()) {
212
206
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
213
207
  <div class="field">
214
- <label for="storageConfig" class="block font-medium mb-2 text-sm sm:text-base">Storage Configuration *</label>
208
+ <label for="storageConfig" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.config.provider.required' | translate }}</label>
215
209
  <select id="storageConfig" class="p-select-native w-full" [formField]="fileForm.storageConfigId">
216
- <option value="">Select storage configuration...</option>
210
+ <option value="">{{ 'storage.config.select.config' | translate }}</option>
217
211
  @for (config of availableConfigs(); track config.id) {
218
- <option [value]="config.id">{{ config.name }} ({{ getProviderLabel(config.storage) }})</option>
212
+ <option [value]="config.id">{{ config.name }} ({{ getProviderLabelKey(config.storage) | translate }})</option>
219
213
  }
220
214
  </select>
221
215
  @if (availableConfigs().length === 0 && !isLoadingConfigs()) {
222
216
  <small class="text-warn block mt-2">
223
217
  <i class="pi pi-exclamation-triangle mr-1"></i>
224
- No storage configurations found. Please create one first.
218
+ {{ 'storage.config.no.configs' | translate }}
225
219
  </small>
226
220
  }
227
221
  </div>
228
222
 
229
223
  <div class="field">
230
- <label for="folder" class="block font-medium mb-2 text-sm sm:text-base">Folder (Optional)</label>
224
+ <label for="folder" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.folder.optional' | translate }}</label>
231
225
  <select id="folder" class="p-select-native w-full" [formField]="fileForm.folderId">
232
- <option value="">No folder (root level)</option>
226
+ <option value="">{{ 'storage.folder.no.folder' | translate }}</option>
233
227
  @for (folder of availableFolders(); track folder.id) {
234
228
  <option [value]="folder.id">{{ folder.name }}</option>
235
229
  }
@@ -237,16 +231,16 @@ class FileManagerFormComponent extends BaseStoragePage {
237
231
  @if (availableFolders().length === 0 && !isLoadingFolders()) {
238
232
  <small class="text-muted-color block mt-2">
239
233
  <i class="pi pi-info-circle mr-1"></i>
240
- No folders available. File will be uploaded to root level.
234
+ {{ 'storage.folder.no.folders.available' | translate }}
241
235
  </small>
242
236
  }
243
237
  </div>
244
238
  </div>
245
239
  } @else {
246
240
  <div class="field mb-4">
247
- <label for="folder" class="block font-medium mb-2 text-sm sm:text-base">Folder (Optional)</label>
241
+ <label for="folder" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.folder.optional' | translate }}</label>
248
242
  <select id="folder" class="p-select-native w-full" [formField]="fileForm.folderId">
249
- <option value="">No folder (root level)</option>
243
+ <option value="">{{ 'storage.folder.no.folder' | translate }}</option>
250
244
  @for (folder of availableFolders(); track folder.id) {
251
245
  <option [value]="folder.id">{{ folder.name }}</option>
252
246
  }
@@ -254,7 +248,7 @@ class FileManagerFormComponent extends BaseStoragePage {
254
248
  @if (availableFolders().length === 0 && !isLoadingFolders()) {
255
249
  <small class="text-muted-color block mt-2">
256
250
  <i class="pi pi-info-circle mr-1"></i>
257
- No folders available.
251
+ {{ 'storage.folder.no.folders.edit' | translate }}
258
252
  </small>
259
253
  }
260
254
  </div>
@@ -262,7 +256,7 @@ class FileManagerFormComponent extends BaseStoragePage {
262
256
 
263
257
  @if (!isEditMode()) {
264
258
  <div class="field mb-4">
265
- <label class="block font-medium mb-2 text-sm sm:text-base">Select File *</label>
259
+ <label class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.file.select.required' | translate }}</label>
266
260
  <p-fileupload
267
261
  #fileUpload
268
262
  [customUpload]="true"
@@ -271,7 +265,7 @@ class FileManagerFormComponent extends BaseStoragePage {
271
265
  [maxFileSize]="50000000"
272
266
  [showUploadButton]="false"
273
267
  [showCancelButton]="false"
274
- chooseLabel="Choose File"
268
+ [chooseLabel]="'storage.button.choose.file' | translate"
275
269
  chooseIcon="pi pi-upload"
276
270
  accept="*/*"
277
271
  class="w-full"
@@ -293,60 +287,59 @@ class FileManagerFormComponent extends BaseStoragePage {
293
287
  } @else {
294
288
  <div class="flex items-center justify-center flex-col p-6 sm:p-8 border-2 border-dashed border-surface rounded-lg">
295
289
  <i class="pi pi-cloud-upload text-3xl sm:text-4xl text-muted-color mb-3"></i>
296
- <p class="text-muted-color text-center text-sm sm:text-base">Drag and drop file here or click to browse</p>
297
- <p class="text-xs sm:text-sm text-muted-color mt-2">Maximum file size: 50MB</p>
290
+ <p class="text-muted-color text-center text-sm sm:text-base">{{ 'storage.upload.drag.drop' | translate }}</p>
291
+ <p class="text-xs sm:text-sm text-muted-color mt-2">{{ 'storage.upload.max.size' | translate: { size: '50MB' } }}</p>
298
292
  </div>
299
293
  }
300
294
  </ng-template>
301
295
  </p-fileupload>
302
296
  @if (!selectedFile() && !isEditMode()) {
303
- <small class="text-muted-color">Please select a file to upload</small>
297
+ <small class="text-muted-color">{{ 'storage.upload.please.select' | translate }}</small>
304
298
  }
305
299
  </div>
306
300
  }
307
301
 
308
302
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
309
303
  <div class="field md:col-span-2">
310
- <label for="name" class="block font-medium mb-2 text-sm sm:text-base">File Name *</label>
311
- <input pInputText id="name" [formField]="fileForm.name" [placeholder]="isEditMode() ? 'Enter file name' : 'Will be auto-filled from selected file'" class="w-full" />
304
+ <label for="name" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.file.name.required' | translate }}</label>
305
+ <input pInputText id="name" [formField]="fileForm.name" [placeholder]="(isEditMode() ? 'storage.file.placeholder.edit' : 'storage.file.placeholder') | translate" class="w-full" />
312
306
  </div>
313
307
 
314
308
  <div class="field">
315
309
  <label class="p-checkbox-native flex items-center gap-2">
316
310
  <input type="checkbox" [formField]="fileForm.isPrivate" />
317
- <span>Private File</span>
311
+ <span>{{ 'storage.file.private' | translate }}</span>
318
312
  </label>
319
313
  <small class="text-muted-color block mt-2">
320
- {{ formModel().isPrivate ? 'Only users with permission can access' : 'File is publicly accessible' }}
314
+ {{ (formModel().isPrivate ? 'storage.file.private.hint' : 'storage.file.public.hint') | translate }}
321
315
  </small>
322
316
  </div>
323
317
  </div>
324
318
 
325
319
  <div class="flex justify-end gap-2 mt-4">
326
- <p-button label="Cancel" severity="secondary" [outlined]="true" (onClick)="onCancel()" />
320
+ <p-button [label]="'shared.cancel' | translate" severity="secondary" [outlined]="true" (onClick)="onCancel()" />
327
321
  <p-button
328
322
  type="submit"
329
- [label]="isEditMode() ? 'Update' : 'Upload'"
323
+ [label]="(isEditMode() ? 'shared.update' : 'storage.button.upload') | translate"
330
324
  [loading]="isLoading()"
331
325
  [disabled]="!isFormValid() || (!isEditMode() && !selectedFile()) || isLoading()"
332
326
  />
333
327
  </div>
334
328
  </form>
335
329
  </div>
336
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.FileUpload, selector: "p-fileupload, p-fileUpload", inputs: ["name", "url", "method", "multiple", "accept", "disabled", "auto", "withCredentials", "maxFileSize", "invalidFileSizeMessageSummary", "invalidFileSizeMessageDetail", "invalidFileTypeMessageSummary", "invalidFileTypeMessageDetail", "invalidFileLimitMessageDetail", "invalidFileLimitMessageSummary", "style", "styleClass", "previewWidth", "chooseLabel", "uploadLabel", "cancelLabel", "chooseIcon", "uploadIcon", "cancelIcon", "showUploadButton", "showCancelButton", "mode", "headers", "customUpload", "fileLimit", "uploadStyleClass", "cancelStyleClass", "removeStyleClass", "chooseStyleClass", "chooseButtonProps", "uploadButtonProps", "cancelButtonProps", "files"], outputs: ["onBeforeUpload", "onSend", "onUpload", "onError", "onClear", "onRemove", "onSelect", "onProgress", "uploadHandler", "onImageError", "onRemoveUploadedFile"] }, { kind: "directive", type: i3$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
330
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.FileUpload, selector: "p-fileupload, p-fileUpload", inputs: ["name", "url", "method", "multiple", "accept", "disabled", "auto", "withCredentials", "maxFileSize", "invalidFileSizeMessageSummary", "invalidFileSizeMessageDetail", "invalidFileTypeMessageSummary", "invalidFileTypeMessageDetail", "invalidFileLimitMessageDetail", "invalidFileLimitMessageSummary", "style", "styleClass", "previewWidth", "chooseLabel", "uploadLabel", "cancelLabel", "chooseIcon", "uploadIcon", "cancelIcon", "showUploadButton", "showCancelButton", "mode", "headers", "customUpload", "fileLimit", "uploadStyleClass", "cancelStyleClass", "removeStyleClass", "chooseStyleClass", "chooseButtonProps", "uploadButtonProps", "cancelButtonProps", "files"], outputs: ["onBeforeUpload", "onSend", "onUpload", "onError", "onClear", "onRemove", "onSelect", "onProgress", "uploadHandler", "onImageError", "onRemoveUploadedFile"] }, { kind: "directive", type: i3$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
337
331
  }
338
332
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileManagerFormComponent, decorators: [{
339
333
  type: Component,
340
334
  args: [{
341
335
  selector: 'lib-file-manager-form',
342
- changeDetection: ChangeDetectionStrategy.OnPush,
343
- imports: [AngularModule, PrimeModule, FormField],
336
+ imports: [AngularModule, PrimeModule, FormField, TranslatePipe],
344
337
  template: `
345
338
  <div class="card">
346
339
  <div class="mb-4">
347
- <h3 class="text-lg sm:text-xl font-semibold">{{ isEditMode() ? 'Edit File' : 'Upload File' }}</h3>
340
+ <h3 class="text-lg sm:text-xl font-semibold">{{ (isEditMode() ? 'storage.file.manager.edit.file' : 'storage.file.manager.upload.file') | translate }}</h3>
348
341
  @if (showCompanyInfo()) {
349
- <p class="text-sm text-muted-color mt-1">Company: {{ currentCompanyName() }}</p>
342
+ <p class="text-sm text-muted-color mt-1">{{ 'storage.company' | translate }}: {{ currentCompanyName() }}</p>
350
343
  }
351
344
  </div>
352
345
 
@@ -354,25 +347,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
354
347
  @if (!isEditMode()) {
355
348
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
356
349
  <div class="field">
357
- <label for="storageConfig" class="block font-medium mb-2 text-sm sm:text-base">Storage Configuration *</label>
350
+ <label for="storageConfig" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.config.provider.required' | translate }}</label>
358
351
  <select id="storageConfig" class="p-select-native w-full" [formField]="fileForm.storageConfigId">
359
- <option value="">Select storage configuration...</option>
352
+ <option value="">{{ 'storage.config.select.config' | translate }}</option>
360
353
  @for (config of availableConfigs(); track config.id) {
361
- <option [value]="config.id">{{ config.name }} ({{ getProviderLabel(config.storage) }})</option>
354
+ <option [value]="config.id">{{ config.name }} ({{ getProviderLabelKey(config.storage) | translate }})</option>
362
355
  }
363
356
  </select>
364
357
  @if (availableConfigs().length === 0 && !isLoadingConfigs()) {
365
358
  <small class="text-warn block mt-2">
366
359
  <i class="pi pi-exclamation-triangle mr-1"></i>
367
- No storage configurations found. Please create one first.
360
+ {{ 'storage.config.no.configs' | translate }}
368
361
  </small>
369
362
  }
370
363
  </div>
371
364
 
372
365
  <div class="field">
373
- <label for="folder" class="block font-medium mb-2 text-sm sm:text-base">Folder (Optional)</label>
366
+ <label for="folder" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.folder.optional' | translate }}</label>
374
367
  <select id="folder" class="p-select-native w-full" [formField]="fileForm.folderId">
375
- <option value="">No folder (root level)</option>
368
+ <option value="">{{ 'storage.folder.no.folder' | translate }}</option>
376
369
  @for (folder of availableFolders(); track folder.id) {
377
370
  <option [value]="folder.id">{{ folder.name }}</option>
378
371
  }
@@ -380,16 +373,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
380
373
  @if (availableFolders().length === 0 && !isLoadingFolders()) {
381
374
  <small class="text-muted-color block mt-2">
382
375
  <i class="pi pi-info-circle mr-1"></i>
383
- No folders available. File will be uploaded to root level.
376
+ {{ 'storage.folder.no.folders.available' | translate }}
384
377
  </small>
385
378
  }
386
379
  </div>
387
380
  </div>
388
381
  } @else {
389
382
  <div class="field mb-4">
390
- <label for="folder" class="block font-medium mb-2 text-sm sm:text-base">Folder (Optional)</label>
383
+ <label for="folder" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.folder.optional' | translate }}</label>
391
384
  <select id="folder" class="p-select-native w-full" [formField]="fileForm.folderId">
392
- <option value="">No folder (root level)</option>
385
+ <option value="">{{ 'storage.folder.no.folder' | translate }}</option>
393
386
  @for (folder of availableFolders(); track folder.id) {
394
387
  <option [value]="folder.id">{{ folder.name }}</option>
395
388
  }
@@ -397,7 +390,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
397
390
  @if (availableFolders().length === 0 && !isLoadingFolders()) {
398
391
  <small class="text-muted-color block mt-2">
399
392
  <i class="pi pi-info-circle mr-1"></i>
400
- No folders available.
393
+ {{ 'storage.folder.no.folders.edit' | translate }}
401
394
  </small>
402
395
  }
403
396
  </div>
@@ -405,7 +398,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
405
398
 
406
399
  @if (!isEditMode()) {
407
400
  <div class="field mb-4">
408
- <label class="block font-medium mb-2 text-sm sm:text-base">Select File *</label>
401
+ <label class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.file.select.required' | translate }}</label>
409
402
  <p-fileupload
410
403
  #fileUpload
411
404
  [customUpload]="true"
@@ -414,7 +407,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
414
407
  [maxFileSize]="50000000"
415
408
  [showUploadButton]="false"
416
409
  [showCancelButton]="false"
417
- chooseLabel="Choose File"
410
+ [chooseLabel]="'storage.button.choose.file' | translate"
418
411
  chooseIcon="pi pi-upload"
419
412
  accept="*/*"
420
413
  class="w-full"
@@ -436,40 +429,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
436
429
  } @else {
437
430
  <div class="flex items-center justify-center flex-col p-6 sm:p-8 border-2 border-dashed border-surface rounded-lg">
438
431
  <i class="pi pi-cloud-upload text-3xl sm:text-4xl text-muted-color mb-3"></i>
439
- <p class="text-muted-color text-center text-sm sm:text-base">Drag and drop file here or click to browse</p>
440
- <p class="text-xs sm:text-sm text-muted-color mt-2">Maximum file size: 50MB</p>
432
+ <p class="text-muted-color text-center text-sm sm:text-base">{{ 'storage.upload.drag.drop' | translate }}</p>
433
+ <p class="text-xs sm:text-sm text-muted-color mt-2">{{ 'storage.upload.max.size' | translate: { size: '50MB' } }}</p>
441
434
  </div>
442
435
  }
443
436
  </ng-template>
444
437
  </p-fileupload>
445
438
  @if (!selectedFile() && !isEditMode()) {
446
- <small class="text-muted-color">Please select a file to upload</small>
439
+ <small class="text-muted-color">{{ 'storage.upload.please.select' | translate }}</small>
447
440
  }
448
441
  </div>
449
442
  }
450
443
 
451
444
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
452
445
  <div class="field md:col-span-2">
453
- <label for="name" class="block font-medium mb-2 text-sm sm:text-base">File Name *</label>
454
- <input pInputText id="name" [formField]="fileForm.name" [placeholder]="isEditMode() ? 'Enter file name' : 'Will be auto-filled from selected file'" class="w-full" />
446
+ <label for="name" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.file.name.required' | translate }}</label>
447
+ <input pInputText id="name" [formField]="fileForm.name" [placeholder]="(isEditMode() ? 'storage.file.placeholder.edit' : 'storage.file.placeholder') | translate" class="w-full" />
455
448
  </div>
456
449
 
457
450
  <div class="field">
458
451
  <label class="p-checkbox-native flex items-center gap-2">
459
452
  <input type="checkbox" [formField]="fileForm.isPrivate" />
460
- <span>Private File</span>
453
+ <span>{{ 'storage.file.private' | translate }}</span>
461
454
  </label>
462
455
  <small class="text-muted-color block mt-2">
463
- {{ formModel().isPrivate ? 'Only users with permission can access' : 'File is publicly accessible' }}
456
+ {{ (formModel().isPrivate ? 'storage.file.private.hint' : 'storage.file.public.hint') | translate }}
464
457
  </small>
465
458
  </div>
466
459
  </div>
467
460
 
468
461
  <div class="flex justify-end gap-2 mt-4">
469
- <p-button label="Cancel" severity="secondary" [outlined]="true" (onClick)="onCancel()" />
462
+ <p-button [label]="'shared.cancel' | translate" severity="secondary" [outlined]="true" (onClick)="onCancel()" />
470
463
  <p-button
471
464
  type="submit"
472
- [label]="isEditMode() ? 'Update' : 'Upload'"
465
+ [label]="(isEditMode() ? 'shared.update' : 'storage.button.upload') | translate"
473
466
  [loading]="isLoading()"
474
467
  [disabled]="!isFormValid() || (!isEditMode() && !selectedFile()) || isLoading()"
475
468
  />
@@ -481,4 +474,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
481
474
  }], ctorParameters: () => [] });
482
475
 
483
476
  export { FileManagerFormComponent };
484
- //# sourceMappingURL=flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs.map
477
+ //# sourceMappingURL=flusys-ng-storage-file-manager-form.component-AM7JE-s5.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flusys-ng-storage-file-manager-form.component-AM7JE-s5.mjs","sources":["../../../projects/ng-storage/pages/file-manager/file-manager-form.component.ts"],"sourcesContent":["import { Component, computed, effect, inject, signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { form, FormField, required } from '@angular/forms/signals';\nimport { ActivatedRoute } from '@angular/router';\nimport { TranslateService } from '@flusys/ng-localization';\nimport { AngularModule, PrimeModule, TranslatePipe } from '@flusys/ng-shared';\nimport { firstValueFrom } from 'rxjs';\nimport { IFileManager } from '../../interfaces/file-manager.interface';\nimport { IFolder } from '../../interfaces/folder.interface';\nimport { IStorageConfig } from '../../interfaces/storage-config.interface';\nimport { FileManagerApiService } from '../../services/file-manager-api.service';\nimport { FolderApiService } from '../../services/folder-api.service';\nimport { StorageConfigApiService } from '../../services/storage-config-api.service';\nimport { UploadService } from '../../services/upload.service';\nimport { BaseStoragePage } from '../../utils/base-storage-page';\nimport { formatFileSize, getProviderLabelKey } from '../../utils/storage-utils';\n\ninterface IFileManagerFormModel {\n id: string;\n name: string;\n isPrivate: boolean;\n folderId: string;\n storageConfigId: string;\n}\n\n@Component({\n selector: 'lib-file-manager-form',\n imports: [AngularModule, PrimeModule, FormField, TranslatePipe],\n template: `\n <div class=\"card\">\n <div class=\"mb-4\">\n <h3 class=\"text-lg sm:text-xl font-semibold\">{{ (isEditMode() ? 'storage.file.manager.edit.file' : 'storage.file.manager.upload.file') | translate }}</h3>\n @if (showCompanyInfo()) {\n <p class=\"text-sm text-muted-color mt-1\">{{ 'storage.company' | translate }}: {{ currentCompanyName() }}</p>\n }\n </div>\n\n <form (ngSubmit)=\"onSubmit()\">\n @if (!isEditMode()) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4 mb-4\">\n <div class=\"field\">\n <label for=\"storageConfig\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.config.provider.required' | translate }}</label>\n <select id=\"storageConfig\" class=\"p-select-native w-full\" [formField]=\"fileForm.storageConfigId\">\n <option value=\"\">{{ 'storage.config.select.config' | translate }}</option>\n @for (config of availableConfigs(); track config.id) {\n <option [value]=\"config.id\">{{ config.name }} ({{ getProviderLabelKey(config.storage) | translate }})</option>\n }\n </select>\n @if (availableConfigs().length === 0 && !isLoadingConfigs()) {\n <small class=\"text-warn block mt-2\">\n <i class=\"pi pi-exclamation-triangle mr-1\"></i>\n {{ 'storage.config.no.configs' | translate }}\n </small>\n }\n </div>\n\n <div class=\"field\">\n <label for=\"folder\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.folder.optional' | translate }}</label>\n <select id=\"folder\" class=\"p-select-native w-full\" [formField]=\"fileForm.folderId\">\n <option value=\"\">{{ 'storage.folder.no.folder' | translate }}</option>\n @for (folder of availableFolders(); track folder.id) {\n <option [value]=\"folder.id\">{{ folder.name }}</option>\n }\n </select>\n @if (availableFolders().length === 0 && !isLoadingFolders()) {\n <small class=\"text-muted-color block mt-2\">\n <i class=\"pi pi-info-circle mr-1\"></i>\n {{ 'storage.folder.no.folders.available' | translate }}\n </small>\n }\n </div>\n </div>\n } @else {\n <div class=\"field mb-4\">\n <label for=\"folder\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.folder.optional' | translate }}</label>\n <select id=\"folder\" class=\"p-select-native w-full\" [formField]=\"fileForm.folderId\">\n <option value=\"\">{{ 'storage.folder.no.folder' | translate }}</option>\n @for (folder of availableFolders(); track folder.id) {\n <option [value]=\"folder.id\">{{ folder.name }}</option>\n }\n </select>\n @if (availableFolders().length === 0 && !isLoadingFolders()) {\n <small class=\"text-muted-color block mt-2\">\n <i class=\"pi pi-info-circle mr-1\"></i>\n {{ 'storage.folder.no.folders.edit' | translate }}\n </small>\n }\n </div>\n }\n\n @if (!isEditMode()) {\n <div class=\"field mb-4\">\n <label class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.file.select.required' | translate }}</label>\n <p-fileupload\n #fileUpload\n [customUpload]=\"true\"\n (onSelect)=\"onFileSelect($event)\"\n [multiple]=\"false\"\n [maxFileSize]=\"50000000\"\n [showUploadButton]=\"false\"\n [showCancelButton]=\"false\"\n [chooseLabel]=\"'storage.button.choose.file' | translate\"\n chooseIcon=\"pi pi-upload\"\n accept=\"*/*\"\n class=\"w-full\"\n >\n <ng-template #content let-files>\n @if (files && files.length > 0) {\n <div class=\"flex flex-col gap-2\">\n @for (file of files; track file.name) {\n <div class=\"flex items-center gap-3 p-3 border border-surface rounded-lg bg-surface-ground\">\n <i class=\"pi pi-file text-xl sm:text-2xl text-primary\"></i>\n <div class=\"flex-1 min-w-0\">\n <div class=\"font-medium truncate\">{{ file.name }}</div>\n <div class=\"text-sm text-muted-color\">{{ formatFileSize(file.size) }}</div>\n </div>\n <p-button icon=\"pi pi-times\" [text]=\"true\" severity=\"danger\" size=\"small\" (onClick)=\"fileUpload.clear()\" />\n </div>\n }\n </div>\n } @else {\n <div class=\"flex items-center justify-center flex-col p-6 sm:p-8 border-2 border-dashed border-surface rounded-lg\">\n <i class=\"pi pi-cloud-upload text-3xl sm:text-4xl text-muted-color mb-3\"></i>\n <p class=\"text-muted-color text-center text-sm sm:text-base\">{{ 'storage.upload.drag.drop' | translate }}</p>\n <p class=\"text-xs sm:text-sm text-muted-color mt-2\">{{ 'storage.upload.max.size' | translate: { size: '50MB' } }}</p>\n </div>\n }\n </ng-template>\n </p-fileupload>\n @if (!selectedFile() && !isEditMode()) {\n <small class=\"text-muted-color\">{{ 'storage.upload.please.select' | translate }}</small>\n }\n </div>\n }\n\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <div class=\"field md:col-span-2\">\n <label for=\"name\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.file.name.required' | translate }}</label>\n <input pInputText id=\"name\" [formField]=\"fileForm.name\" [placeholder]=\"(isEditMode() ? 'storage.file.placeholder.edit' : 'storage.file.placeholder') | translate\" class=\"w-full\" />\n </div>\n\n <div class=\"field\">\n <label class=\"p-checkbox-native flex items-center gap-2\">\n <input type=\"checkbox\" [formField]=\"fileForm.isPrivate\" />\n <span>{{ 'storage.file.private' | translate }}</span>\n </label>\n <small class=\"text-muted-color block mt-2\">\n {{ (formModel().isPrivate ? 'storage.file.private.hint' : 'storage.file.public.hint') | translate }}\n </small>\n </div>\n </div>\n\n <div class=\"flex justify-end gap-2 mt-4\">\n <p-button [label]=\"'shared.cancel' | translate\" severity=\"secondary\" [outlined]=\"true\" (onClick)=\"onCancel()\" />\n <p-button\n type=\"submit\"\n [label]=\"(isEditMode() ? 'shared.update' : 'storage.button.upload') | translate\"\n [loading]=\"isLoading()\"\n [disabled]=\"!isFormValid() || (!isEditMode() && !selectedFile()) || isLoading()\"\n />\n </div>\n </form>\n </div>\n `,\n})\nexport class FileManagerFormComponent extends BaseStoragePage {\n private readonly route = inject(ActivatedRoute);\n private readonly fileService = inject(FileManagerApiService);\n private readonly uploadService = inject(UploadService);\n private readonly configService = inject(StorageConfigApiService);\n private readonly folderService = inject(FolderApiService);\n private readonly translateService = inject(TranslateService);\n\n private readonly routeParams = toSignal(this.route.paramMap);\n private initialized = false;\n\n readonly isLoadingConfigs = signal(false);\n readonly isLoadingFolders = signal(false);\n readonly existingFile = signal<IFileManager | null>(null);\n readonly isEditMode = computed(() => !!this.existingFile());\n readonly selectedFile = signal<File | null>(null);\n readonly availableConfigs = signal<IStorageConfig[]>([]);\n readonly availableFolders = signal<IFolder[]>([]);\n\n readonly formatFileSize = formatFileSize;\n readonly getProviderLabelKey = getProviderLabelKey;\n\n readonly formModel = signal<IFileManagerFormModel>({\n id: '',\n name: '',\n isPrivate: false,\n folderId: '',\n storageConfigId: '',\n });\n\n readonly fileForm = form(this.formModel, (f) => {\n required(f.name, { message: this.translateService.translate('storage.validation.file.name.required') });\n });\n\n readonly isFormValid = computed(() => this.formModel().name.trim().length > 0);\n\n constructor() {\n super();\n effect(() => {\n const params = this.routeParams();\n if (!params || this.initialized) return;\n\n this.initialized = true;\n const fileId = params.get('id');\n if (fileId) {\n this.loadFile(fileId);\n } else {\n this.loadStorageConfigs();\n }\n this.loadFolders();\n });\n }\n\n async loadStorageConfigs(): Promise<void> {\n this.isLoadingConfigs.set(true);\n try {\n const response = await firstValueFrom(\n this.configService.getAll('', {\n pagination: { currentPage: 0, pageSize: 100 },\n filter: {},\n select: [],\n sort: {},\n }),\n );\n if (response.success && response.data) {\n this.availableConfigs.set(response.data);\n if (response.data.length === 1) {\n this.formModel.update((m) => ({ ...m, storageConfigId: response.data![0].id }));\n }\n }\n } catch {\n this.showError(this.translateService.translate('storage.config.load.failed'));\n } finally {\n this.isLoadingConfigs.set(false);\n }\n }\n\n async loadFolders(): Promise<void> {\n this.isLoadingFolders.set(true);\n try {\n const response = await firstValueFrom(\n this.folderService.getAll('', {\n pagination: { currentPage: 0, pageSize: 100 },\n filter: {},\n select: [],\n sort: {},\n }),\n );\n if (response.success && response.data) {\n this.availableFolders.set(response.data);\n }\n } catch {\n // Silently fail - folders are optional\n } finally {\n this.isLoadingFolders.set(false);\n }\n }\n\n async loadFile(id: string): Promise<void> {\n this.isLoading.set(true);\n try {\n const response = await this.fileService.findByIdAsync(id);\n if (response.success && response.data) {\n const file = response.data;\n this.existingFile.set(file);\n this.formModel.set({\n id: file.id,\n name: file.name,\n isPrivate: file.isPrivate ?? false,\n folderId: file.folderId ?? '',\n storageConfigId: '',\n });\n }\n } catch {\n this.showError(this.translateService.translate('storage.error.load.file.failed'));\n this.navigateTo(['/storage/files']);\n } finally {\n this.isLoading.set(false);\n }\n }\n\n onFileSelect(event: { files?: File[]; currentFiles?: File[] }): void {\n const file = event.files?.[0] || event.currentFiles?.[0];\n if (file) {\n this.selectedFile.set(file);\n this.formModel.update((m) => ({ ...m, name: file.name }));\n }\n }\n\n async onSubmit(): Promise<void> {\n if (!this.isFormValid()) return;\n\n this.isLoading.set(true);\n try {\n const formValue = this.formModel();\n\n if (this.isEditMode()) {\n await this.fileService.updateAsync({\n id: formValue.id,\n name: formValue.name,\n isPrivate: formValue.isPrivate,\n folderId: formValue.folderId || null,\n });\n this.showSuccess(this.translateService.translate('storage.file.update.success'));\n } else {\n const file = this.selectedFile();\n if (!file) {\n this.showError(this.translateService.translate('storage.error.no.file'));\n return;\n }\n\n if (!formValue.storageConfigId) {\n this.showError(this.translateService.translate('storage.error.no.config'));\n return;\n }\n\n const uploadResponse = await firstValueFrom(\n this.uploadService.uploadSingleFile(file, {\n compress: true,\n quality: 85,\n storageConfigId: formValue.storageConfigId,\n }),\n );\n\n if (uploadResponse.success && uploadResponse.data) {\n const uploadedFileInfo = uploadResponse.data;\n await firstValueFrom(\n this.fileService.createFile({\n name: formValue.name,\n key: uploadedFileInfo.key,\n contentType: file.type || 'application/octet-stream',\n size: Math.round(file.size / 1024).toString(),\n isPrivate: formValue.isPrivate,\n folderId: formValue.folderId || undefined,\n storageConfigId: formValue.storageConfigId,\n }),\n );\n this.showSuccess(this.translateService.translate('storage.upload.success'));\n } else {\n throw new Error(this.translateService.translate('storage.upload.failed'));\n }\n }\n\n this.navigateTo(['/storage/files']);\n } catch {\n this.showError(this.translateService.translate('storage.upload.failed'));\n } finally {\n this.isLoading.set(false);\n }\n }\n\n onCancel(): void {\n this.navigateTo(['/storage/files']);\n }\n}\n"],"names":["i2","i4"],"mappings":"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"}