@flusys/ng-storage 3.0.1 → 4.0.0-lts

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/README.md +12 -6
  2. package/fesm2022/{flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs → flusys-ng-storage-file-manager-form.component-AM7JE-s5.mjs} +67 -74
  3. package/fesm2022/flusys-ng-storage-file-manager-form.component-AM7JE-s5.mjs.map +1 -0
  4. package/fesm2022/{flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs → flusys-ng-storage-file-manager-list.component-bj6oOzOL.mjs} +66 -74
  5. package/fesm2022/flusys-ng-storage-file-manager-list.component-bj6oOzOL.mjs.map +1 -0
  6. package/fesm2022/{flusys-ng-storage-folder-form.component-BTQGEqNu.mjs → flusys-ng-storage-folder-form.component-Cc2ybTwu.mjs} +25 -34
  7. package/fesm2022/flusys-ng-storage-folder-form.component-Cc2ybTwu.mjs.map +1 -0
  8. package/fesm2022/{flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs → flusys-ng-storage-folder-list.component-B2Q41I1g.mjs} +34 -39
  9. package/fesm2022/flusys-ng-storage-folder-list.component-B2Q41I1g.mjs.map +1 -0
  10. package/fesm2022/{flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs → flusys-ng-storage-storage-config-form.component-a88XhuPH.mjs} +87 -85
  11. package/fesm2022/flusys-ng-storage-storage-config-form.component-a88XhuPH.mjs.map +1 -0
  12. package/fesm2022/{flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs → flusys-ng-storage-storage-config-list.component-DBH_qf9o.mjs} +36 -35
  13. package/fesm2022/flusys-ng-storage-storage-config-list.component-DBH_qf9o.mjs.map +1 -0
  14. package/fesm2022/flusys-ng-storage.mjs +490 -151
  15. package/fesm2022/flusys-ng-storage.mjs.map +1 -1
  16. package/package.json +7 -5
  17. package/types/flusys-ng-storage.d.ts +56 -36
  18. package/fesm2022/flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs.map +0 -1
  19. package/fesm2022/flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs.map +0 -1
  20. package/fesm2022/flusys-ng-storage-folder-form.component-BTQGEqNu.mjs.map +0 -1
  21. package/fesm2022/flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs.map +0 -1
  22. package/fesm2022/flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs.map +0 -1
  23. package/fesm2022/flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs.map +0 -1
@@ -1,10 +1,11 @@
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';
7
- import { FileLocationEnum, BaseStoragePage, StorageConfigApiService, STORAGE_PROVIDERS } from './flusys-ng-storage.mjs';
6
+ import { TranslateService } from '@flusys/ng-localization';
7
+ import { AngularModule, PrimeModule, TranslatePipe } from '@flusys/ng-shared';
8
+ import { FileLocationEnum, BaseStoragePage, StorageConfigApiService, STORAGE_PROVIDER_OPTIONS } from './flusys-ng-storage.mjs';
8
9
  import * as i1 from '@angular/forms';
9
10
  import * as i1$1 from 'primeng/button';
10
11
  import * as i3 from 'primeng/inputtext';
@@ -34,15 +35,16 @@ const DEFAULT_FORM_MODEL = {
34
35
  class StorageConfigFormComponent extends BaseStoragePage {
35
36
  route = inject(ActivatedRoute);
36
37
  configService = inject(StorageConfigApiService);
38
+ translateService = inject(TranslateService);
37
39
  routeParams = toSignal(this.route.paramMap);
38
40
  initialized = false;
39
41
  fileLocationEnum = FileLocationEnum;
40
- storageProviders = STORAGE_PROVIDERS;
42
+ storageProviderOptions = STORAGE_PROVIDER_OPTIONS;
41
43
  existingConfig = signal(null, ...(ngDevMode ? [{ debugName: "existingConfig" }] : []));
42
44
  isEditMode = computed(() => !!this.existingConfig(), ...(ngDevMode ? [{ debugName: "isEditMode" }] : []));
43
45
  formModel = signal({ ...DEFAULT_FORM_MODEL }, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
44
46
  configForm = form(this.formModel, (f) => {
45
- required(f.name, { message: 'Configuration name is required' });
47
+ required(f.name, { message: this.translateService.translate('storage.validation.config.name.required') });
46
48
  });
47
49
  selectedProvider = computed(() => this.formModel().storage, ...(ngDevMode ? [{ debugName: "selectedProvider" }] : []));
48
50
  isFormValid = computed(() => this.formModel().name.trim().length > 0, ...(ngDevMode ? [{ debugName: "isFormValid" }] : []));
@@ -78,7 +80,7 @@ class StorageConfigFormComponent extends BaseStoragePage {
78
80
  }
79
81
  }
80
82
  catch {
81
- this.showError('Failed to load storage configuration.');
83
+ this.showError(this.translateService.translate('storage.config.load.config.failed'));
82
84
  this.navigateTo(['/storage/configs']);
83
85
  }
84
86
  finally {
@@ -101,7 +103,7 @@ class StorageConfigFormComponent extends BaseStoragePage {
101
103
  isActive: formValue.isActive,
102
104
  isDefault: formValue.isDefault,
103
105
  });
104
- this.showSuccess('Storage configuration updated successfully.');
106
+ this.showSuccess(this.translateService.translate('storage.config.update.success'));
105
107
  }
106
108
  else {
107
109
  await this.configService.insertAsync({
@@ -111,12 +113,13 @@ class StorageConfigFormComponent extends BaseStoragePage {
111
113
  isActive: formValue.isActive,
112
114
  isDefault: formValue.isDefault,
113
115
  });
114
- this.showSuccess('Storage configuration created successfully.');
116
+ this.showSuccess(this.translateService.translate('storage.config.create.success'));
115
117
  }
116
118
  this.navigateTo(['/storage/configs']);
117
119
  }
118
120
  catch {
119
- this.showError(`Failed to ${this.isEditMode() ? 'update' : 'create'} storage configuration.`);
121
+ const action = this.isEditMode() ? 'update' : 'create';
122
+ this.showError(this.translateService.translate(`storage.config.${action}.failed`));
120
123
  }
121
124
  finally {
122
125
  this.isLoading.set(false);
@@ -190,63 +193,63 @@ class StorageConfigFormComponent extends BaseStoragePage {
190
193
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: StorageConfigFormComponent, isStandalone: true, selector: "lib-storage-config-form", usesInheritance: true, ngImport: i0, template: `
191
194
  <div class="card">
192
195
  <h3 class="text-lg sm:text-xl font-semibold mb-4">
193
- {{ isEditMode() ? 'Edit Storage Configuration' : 'New Storage Configuration' }}
196
+ {{ (isEditMode() ? 'storage.config.edit.config' : 'storage.config.new.config') | translate }}
194
197
  </h3>
195
198
  @if (showCompanyInfo()) {
196
- <p class="text-sm text-muted-color mb-4">Company: {{ currentCompanyName() }}</p>
199
+ <p class="text-sm text-muted-color mb-4">{{ 'storage.company' | translate }}: {{ currentCompanyName() }}</p>
197
200
  }
198
201
 
199
202
  <form (ngSubmit)="onSubmit()">
200
203
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
201
204
  <div class="field">
202
- <label for="name" class="block font-medium mb-2 text-sm sm:text-base">Configuration Name *</label>
203
- <input pInputText id="name" [formField]="configForm.name" placeholder="e.g., Production AWS S3" class="w-full" />
205
+ <label for="name" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.config.config.name.required' | translate }}</label>
206
+ <input pInputText id="name" [formField]="configForm.name" [placeholder]="'storage.config.config.name.placeholder' | translate" class="w-full" />
204
207
  </div>
205
208
 
206
209
  <div class="field">
207
- <label for="storage" class="block font-medium mb-2 text-sm sm:text-base">Storage Provider *</label>
210
+ <label for="storage" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.config.provider.required' | translate }}</label>
208
211
  <select id="storage" class="p-select-native w-full" [formField]="configForm.storage">
209
- @for (provider of storageProviders; track provider.value) {
210
- <option [value]="provider.value">{{ provider.label }}</option>
212
+ @for (provider of storageProviderOptions; track provider.value) {
213
+ <option [value]="provider.value">{{ provider.labelKey | translate }}</option>
211
214
  }
212
215
  </select>
213
216
  </div>
214
217
 
215
218
  <div class="field flex items-center gap-2">
216
219
  <p-toggleswitch [formField]="configForm.isActive" />
217
- <label class="text-sm sm:text-base">Active</label>
220
+ <label class="text-sm sm:text-base">{{ 'storage.active' | translate }}</label>
218
221
  </div>
219
222
 
220
223
  <div class="field flex items-center gap-2">
221
224
  <p-toggleswitch [formField]="configForm.isDefault" />
222
- <label class="text-sm sm:text-base">Set as Default</label>
225
+ <label class="text-sm sm:text-base">{{ 'storage.set.as.default' | translate }}</label>
223
226
  </div>
224
227
  </div>
225
228
 
226
229
  @if (selectedProvider() === fileLocationEnum.AWS) {
227
230
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4 p-4 bg-surface-ground rounded-lg border border-surface">
228
231
  <div class="md:col-span-2 mb-2">
229
- <span class="text-sm font-semibold text-muted-color uppercase tracking-wide">AWS S3 Configuration</span>
232
+ <span class="text-sm font-semibold text-muted-color uppercase tracking-wide">{{ 'storage.aws.title' | translate }}</span>
230
233
  </div>
231
234
  <div class="field">
232
- <label for="awsRegion" class="block font-medium mb-2 text-sm sm:text-base">AWS Region *</label>
233
- <input pInputText id="awsRegion" [formField]="configForm.awsRegion" placeholder="us-east-1" class="w-full" />
235
+ <label for="awsRegion" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.config.region.required' | translate }}</label>
236
+ <input pInputText id="awsRegion" [formField]="configForm.awsRegion" [placeholder]="'storage.aws.region.placeholder' | translate" class="w-full" />
234
237
  </div>
235
238
  <div class="field">
236
- <label for="awsBucket" class="block font-medium mb-2 text-sm sm:text-base">Bucket Name *</label>
237
- <input pInputText id="awsBucket" [formField]="configForm.awsBucket" placeholder="my-bucket" class="w-full" />
239
+ <label for="awsBucket" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.config.bucket.required' | translate }}</label>
240
+ <input pInputText id="awsBucket" [formField]="configForm.awsBucket" [placeholder]="'storage.aws.bucket.placeholder' | translate" class="w-full" />
238
241
  </div>
239
242
  <div class="field">
240
- <label for="awsAccessKeyId" class="block font-medium mb-2 text-sm sm:text-base">Access Key ID *</label>
243
+ <label for="awsAccessKeyId" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.config.access.key.required' | translate }}</label>
241
244
  <input pInputText id="awsAccessKeyId" [formField]="configForm.awsAccessKeyId" class="w-full" />
242
245
  </div>
243
246
  <div class="field">
244
- <label for="awsSecretAccessKey" class="block font-medium mb-2 text-sm sm:text-base">Secret Access Key *</label>
247
+ <label for="awsSecretAccessKey" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.config.secret.key.required' | translate }}</label>
245
248
  <input type="password" pInputText id="awsSecretAccessKey" [formField]="configForm.awsSecretAccessKey" class="w-full" />
246
249
  </div>
247
250
  <div class="field md:col-span-2">
248
- <label for="awsEndpoint" class="block font-medium mb-2 text-sm sm:text-base">Custom Endpoint (Optional)</label>
249
- <input pInputText id="awsEndpoint" [formField]="configForm.awsEndpoint" placeholder="https://s3.custom-endpoint.com" class="w-full" />
251
+ <label for="awsEndpoint" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.config.endpoint.optional' | translate }}</label>
252
+ <input pInputText id="awsEndpoint" [formField]="configForm.awsEndpoint" [placeholder]="'storage.config.endpoint.placeholder' | translate" class="w-full" />
250
253
  </div>
251
254
  </div>
252
255
  }
@@ -254,18 +257,18 @@ class StorageConfigFormComponent extends BaseStoragePage {
254
257
  @if (selectedProvider() === fileLocationEnum.AZURE) {
255
258
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4 p-4 bg-surface-ground rounded-lg border border-surface">
256
259
  <div class="md:col-span-2 mb-2">
257
- <span class="text-sm font-semibold text-muted-color uppercase tracking-wide">Azure Blob Configuration</span>
260
+ <span class="text-sm font-semibold text-muted-color uppercase tracking-wide">{{ 'storage.azure.title' | translate }}</span>
258
261
  </div>
259
262
  <div class="field">
260
- <label for="azureAccountName" class="block font-medium mb-2 text-sm sm:text-base">Account Name *</label>
263
+ <label for="azureAccountName" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.azure.account.name.required' | translate }}</label>
261
264
  <input pInputText id="azureAccountName" [formField]="configForm.azureAccountName" class="w-full" />
262
265
  </div>
263
266
  <div class="field">
264
- <label for="azureContainerName" class="block font-medium mb-2 text-sm sm:text-base">Container Name *</label>
267
+ <label for="azureContainerName" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.azure.container.name.required' | translate }}</label>
265
268
  <input pInputText id="azureContainerName" [formField]="configForm.azureContainerName" class="w-full" />
266
269
  </div>
267
270
  <div class="field md:col-span-2">
268
- <label for="azureAccountKey" class="block font-medium mb-2 text-sm sm:text-base">Account Key *</label>
271
+ <label for="azureAccountKey" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.azure.account.key.required' | translate }}</label>
269
272
  <input type="password" pInputText id="azureAccountKey" [formField]="configForm.azureAccountKey" class="w-full" />
270
273
  </div>
271
274
  </div>
@@ -274,27 +277,27 @@ class StorageConfigFormComponent extends BaseStoragePage {
274
277
  @if (selectedProvider() === fileLocationEnum.SFTP) {
275
278
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4 p-4 bg-surface-ground rounded-lg border border-surface">
276
279
  <div class="md:col-span-2 mb-2">
277
- <span class="text-sm font-semibold text-muted-color uppercase tracking-wide">SFTP Configuration</span>
280
+ <span class="text-sm font-semibold text-muted-color uppercase tracking-wide">{{ 'storage.sftp.title' | translate }}</span>
278
281
  </div>
279
282
  <div class="field">
280
- <label for="sftpHost" class="block font-medium mb-2 text-sm sm:text-base">Host *</label>
281
- <input pInputText id="sftpHost" [formField]="configForm.sftpHost" placeholder="sftp.example.com" class="w-full" />
283
+ <label for="sftpHost" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.sftp.host.required' | translate }}</label>
284
+ <input pInputText id="sftpHost" [formField]="configForm.sftpHost" [placeholder]="'storage.sftp.host.placeholder' | translate" class="w-full" />
282
285
  </div>
283
286
  <div class="field">
284
- <label for="sftpPort" class="block font-medium mb-2 text-sm sm:text-base">Port *</label>
285
- <input type="number" pInputText id="sftpPort" [formField]="configForm.sftpPort" placeholder="22" class="w-full" />
287
+ <label for="sftpPort" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.sftp.port.required' | translate }}</label>
288
+ <input type="number" pInputText id="sftpPort" [formField]="configForm.sftpPort" [placeholder]="'storage.sftp.port.placeholder' | translate" class="w-full" />
286
289
  </div>
287
290
  <div class="field">
288
- <label for="sftpUsername" class="block font-medium mb-2 text-sm sm:text-base">Username *</label>
291
+ <label for="sftpUsername" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.sftp.username.required' | translate }}</label>
289
292
  <input pInputText id="sftpUsername" [formField]="configForm.sftpUsername" class="w-full" />
290
293
  </div>
291
294
  <div class="field">
292
- <label for="sftpPassword" class="block font-medium mb-2 text-sm sm:text-base">Password *</label>
295
+ <label for="sftpPassword" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.sftp.password.required' | translate }}</label>
293
296
  <input type="password" pInputText id="sftpPassword" [formField]="configForm.sftpPassword" class="w-full" />
294
297
  </div>
295
298
  <div class="field md:col-span-2">
296
- <label for="sftpBasePath" class="block font-medium mb-2 text-sm sm:text-base">Base Path *</label>
297
- <input pInputText id="sftpBasePath" [formField]="configForm.sftpBasePath" placeholder="/uploads" class="w-full" />
299
+ <label for="sftpBasePath" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.sftp.base.path.required' | translate }}</label>
300
+ <input pInputText id="sftpBasePath" [formField]="configForm.sftpBasePath" [placeholder]="'storage.sftp.base.path.placeholder' | translate" class="w-full" />
298
301
  </div>
299
302
  </div>
300
303
  }
@@ -302,89 +305,88 @@ class StorageConfigFormComponent extends BaseStoragePage {
302
305
  @if (selectedProvider() === fileLocationEnum.LOCAL) {
303
306
  <div class="grid grid-cols-1 gap-4 mt-4 p-4 bg-surface-ground rounded-lg border border-surface">
304
307
  <div class="mb-2">
305
- <span class="text-sm font-semibold text-muted-color uppercase tracking-wide">Local Storage Configuration</span>
308
+ <span class="text-sm font-semibold text-muted-color uppercase tracking-wide">{{ 'storage.local.title' | translate }}</span>
306
309
  </div>
307
310
  <div class="field">
308
- <label for="localBasePath" class="block font-medium mb-2 text-sm sm:text-base">Base Path *</label>
309
- <input pInputText id="localBasePath" [formField]="configForm.localBasePath" placeholder="/var/www/uploads" class="w-full" />
311
+ <label for="localBasePath" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.local.base.path.required' | translate }}</label>
312
+ <input pInputText id="localBasePath" [formField]="configForm.localBasePath" [placeholder]="'storage.local.base.path.placeholder' | translate" class="w-full" />
310
313
  </div>
311
314
  </div>
312
315
  }
313
316
 
314
317
  <div class="flex justify-end gap-2 mt-4">
315
- <p-button label="Cancel" severity="secondary" [outlined]="true" (onClick)="onCancel()" />
316
- <p-button type="submit" [label]="isEditMode() ? 'Update' : 'Create'" [loading]="isLoading()" [disabled]="!isFormValid() || isLoading()" />
318
+ <p-button [label]="'shared.cancel' | translate" severity="secondary" [outlined]="true" (onClick)="onCancel()" />
319
+ <p-button type="submit" [label]="(isEditMode() ? 'shared.update' : 'shared.create') | translate" [loading]="isLoading()" [disabled]="!isFormValid() || isLoading()" />
317
320
  </div>
318
321
  </form>
319
322
  </div>
320
- `, 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: "directive", type: i3.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i4.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["styleClass", "tabindex", "inputId", "readonly", "trueValue", "falseValue", "ariaLabel", "size", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
323
+ `, 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: "directive", type: i3.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i4.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["styleClass", "tabindex", "inputId", "readonly", "trueValue", "falseValue", "ariaLabel", "size", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
321
324
  }
322
325
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageConfigFormComponent, decorators: [{
323
326
  type: Component,
324
327
  args: [{
325
328
  selector: 'lib-storage-config-form',
326
- changeDetection: ChangeDetectionStrategy.OnPush,
327
- imports: [AngularModule, PrimeModule, FormField],
329
+ imports: [AngularModule, PrimeModule, FormField, TranslatePipe],
328
330
  template: `
329
331
  <div class="card">
330
332
  <h3 class="text-lg sm:text-xl font-semibold mb-4">
331
- {{ isEditMode() ? 'Edit Storage Configuration' : 'New Storage Configuration' }}
333
+ {{ (isEditMode() ? 'storage.config.edit.config' : 'storage.config.new.config') | translate }}
332
334
  </h3>
333
335
  @if (showCompanyInfo()) {
334
- <p class="text-sm text-muted-color mb-4">Company: {{ currentCompanyName() }}</p>
336
+ <p class="text-sm text-muted-color mb-4">{{ 'storage.company' | translate }}: {{ currentCompanyName() }}</p>
335
337
  }
336
338
 
337
339
  <form (ngSubmit)="onSubmit()">
338
340
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
339
341
  <div class="field">
340
- <label for="name" class="block font-medium mb-2 text-sm sm:text-base">Configuration Name *</label>
341
- <input pInputText id="name" [formField]="configForm.name" placeholder="e.g., Production AWS S3" class="w-full" />
342
+ <label for="name" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.config.config.name.required' | translate }}</label>
343
+ <input pInputText id="name" [formField]="configForm.name" [placeholder]="'storage.config.config.name.placeholder' | translate" class="w-full" />
342
344
  </div>
343
345
 
344
346
  <div class="field">
345
- <label for="storage" class="block font-medium mb-2 text-sm sm:text-base">Storage Provider *</label>
347
+ <label for="storage" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.config.provider.required' | translate }}</label>
346
348
  <select id="storage" class="p-select-native w-full" [formField]="configForm.storage">
347
- @for (provider of storageProviders; track provider.value) {
348
- <option [value]="provider.value">{{ provider.label }}</option>
349
+ @for (provider of storageProviderOptions; track provider.value) {
350
+ <option [value]="provider.value">{{ provider.labelKey | translate }}</option>
349
351
  }
350
352
  </select>
351
353
  </div>
352
354
 
353
355
  <div class="field flex items-center gap-2">
354
356
  <p-toggleswitch [formField]="configForm.isActive" />
355
- <label class="text-sm sm:text-base">Active</label>
357
+ <label class="text-sm sm:text-base">{{ 'storage.active' | translate }}</label>
356
358
  </div>
357
359
 
358
360
  <div class="field flex items-center gap-2">
359
361
  <p-toggleswitch [formField]="configForm.isDefault" />
360
- <label class="text-sm sm:text-base">Set as Default</label>
362
+ <label class="text-sm sm:text-base">{{ 'storage.set.as.default' | translate }}</label>
361
363
  </div>
362
364
  </div>
363
365
 
364
366
  @if (selectedProvider() === fileLocationEnum.AWS) {
365
367
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4 p-4 bg-surface-ground rounded-lg border border-surface">
366
368
  <div class="md:col-span-2 mb-2">
367
- <span class="text-sm font-semibold text-muted-color uppercase tracking-wide">AWS S3 Configuration</span>
369
+ <span class="text-sm font-semibold text-muted-color uppercase tracking-wide">{{ 'storage.aws.title' | translate }}</span>
368
370
  </div>
369
371
  <div class="field">
370
- <label for="awsRegion" class="block font-medium mb-2 text-sm sm:text-base">AWS Region *</label>
371
- <input pInputText id="awsRegion" [formField]="configForm.awsRegion" placeholder="us-east-1" class="w-full" />
372
+ <label for="awsRegion" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.config.region.required' | translate }}</label>
373
+ <input pInputText id="awsRegion" [formField]="configForm.awsRegion" [placeholder]="'storage.aws.region.placeholder' | translate" class="w-full" />
372
374
  </div>
373
375
  <div class="field">
374
- <label for="awsBucket" class="block font-medium mb-2 text-sm sm:text-base">Bucket Name *</label>
375
- <input pInputText id="awsBucket" [formField]="configForm.awsBucket" placeholder="my-bucket" class="w-full" />
376
+ <label for="awsBucket" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.config.bucket.required' | translate }}</label>
377
+ <input pInputText id="awsBucket" [formField]="configForm.awsBucket" [placeholder]="'storage.aws.bucket.placeholder' | translate" class="w-full" />
376
378
  </div>
377
379
  <div class="field">
378
- <label for="awsAccessKeyId" class="block font-medium mb-2 text-sm sm:text-base">Access Key ID *</label>
380
+ <label for="awsAccessKeyId" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.config.access.key.required' | translate }}</label>
379
381
  <input pInputText id="awsAccessKeyId" [formField]="configForm.awsAccessKeyId" class="w-full" />
380
382
  </div>
381
383
  <div class="field">
382
- <label for="awsSecretAccessKey" class="block font-medium mb-2 text-sm sm:text-base">Secret Access Key *</label>
384
+ <label for="awsSecretAccessKey" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.config.secret.key.required' | translate }}</label>
383
385
  <input type="password" pInputText id="awsSecretAccessKey" [formField]="configForm.awsSecretAccessKey" class="w-full" />
384
386
  </div>
385
387
  <div class="field md:col-span-2">
386
- <label for="awsEndpoint" class="block font-medium mb-2 text-sm sm:text-base">Custom Endpoint (Optional)</label>
387
- <input pInputText id="awsEndpoint" [formField]="configForm.awsEndpoint" placeholder="https://s3.custom-endpoint.com" class="w-full" />
388
+ <label for="awsEndpoint" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.config.endpoint.optional' | translate }}</label>
389
+ <input pInputText id="awsEndpoint" [formField]="configForm.awsEndpoint" [placeholder]="'storage.config.endpoint.placeholder' | translate" class="w-full" />
388
390
  </div>
389
391
  </div>
390
392
  }
@@ -392,18 +394,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
392
394
  @if (selectedProvider() === fileLocationEnum.AZURE) {
393
395
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4 p-4 bg-surface-ground rounded-lg border border-surface">
394
396
  <div class="md:col-span-2 mb-2">
395
- <span class="text-sm font-semibold text-muted-color uppercase tracking-wide">Azure Blob Configuration</span>
397
+ <span class="text-sm font-semibold text-muted-color uppercase tracking-wide">{{ 'storage.azure.title' | translate }}</span>
396
398
  </div>
397
399
  <div class="field">
398
- <label for="azureAccountName" class="block font-medium mb-2 text-sm sm:text-base">Account Name *</label>
400
+ <label for="azureAccountName" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.azure.account.name.required' | translate }}</label>
399
401
  <input pInputText id="azureAccountName" [formField]="configForm.azureAccountName" class="w-full" />
400
402
  </div>
401
403
  <div class="field">
402
- <label for="azureContainerName" class="block font-medium mb-2 text-sm sm:text-base">Container Name *</label>
404
+ <label for="azureContainerName" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.azure.container.name.required' | translate }}</label>
403
405
  <input pInputText id="azureContainerName" [formField]="configForm.azureContainerName" class="w-full" />
404
406
  </div>
405
407
  <div class="field md:col-span-2">
406
- <label for="azureAccountKey" class="block font-medium mb-2 text-sm sm:text-base">Account Key *</label>
408
+ <label for="azureAccountKey" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.azure.account.key.required' | translate }}</label>
407
409
  <input type="password" pInputText id="azureAccountKey" [formField]="configForm.azureAccountKey" class="w-full" />
408
410
  </div>
409
411
  </div>
@@ -412,27 +414,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
412
414
  @if (selectedProvider() === fileLocationEnum.SFTP) {
413
415
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4 p-4 bg-surface-ground rounded-lg border border-surface">
414
416
  <div class="md:col-span-2 mb-2">
415
- <span class="text-sm font-semibold text-muted-color uppercase tracking-wide">SFTP Configuration</span>
417
+ <span class="text-sm font-semibold text-muted-color uppercase tracking-wide">{{ 'storage.sftp.title' | translate }}</span>
416
418
  </div>
417
419
  <div class="field">
418
- <label for="sftpHost" class="block font-medium mb-2 text-sm sm:text-base">Host *</label>
419
- <input pInputText id="sftpHost" [formField]="configForm.sftpHost" placeholder="sftp.example.com" class="w-full" />
420
+ <label for="sftpHost" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.sftp.host.required' | translate }}</label>
421
+ <input pInputText id="sftpHost" [formField]="configForm.sftpHost" [placeholder]="'storage.sftp.host.placeholder' | translate" class="w-full" />
420
422
  </div>
421
423
  <div class="field">
422
- <label for="sftpPort" class="block font-medium mb-2 text-sm sm:text-base">Port *</label>
423
- <input type="number" pInputText id="sftpPort" [formField]="configForm.sftpPort" placeholder="22" class="w-full" />
424
+ <label for="sftpPort" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.sftp.port.required' | translate }}</label>
425
+ <input type="number" pInputText id="sftpPort" [formField]="configForm.sftpPort" [placeholder]="'storage.sftp.port.placeholder' | translate" class="w-full" />
424
426
  </div>
425
427
  <div class="field">
426
- <label for="sftpUsername" class="block font-medium mb-2 text-sm sm:text-base">Username *</label>
428
+ <label for="sftpUsername" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.sftp.username.required' | translate }}</label>
427
429
  <input pInputText id="sftpUsername" [formField]="configForm.sftpUsername" class="w-full" />
428
430
  </div>
429
431
  <div class="field">
430
- <label for="sftpPassword" class="block font-medium mb-2 text-sm sm:text-base">Password *</label>
432
+ <label for="sftpPassword" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.sftp.password.required' | translate }}</label>
431
433
  <input type="password" pInputText id="sftpPassword" [formField]="configForm.sftpPassword" class="w-full" />
432
434
  </div>
433
435
  <div class="field md:col-span-2">
434
- <label for="sftpBasePath" class="block font-medium mb-2 text-sm sm:text-base">Base Path *</label>
435
- <input pInputText id="sftpBasePath" [formField]="configForm.sftpBasePath" placeholder="/uploads" class="w-full" />
436
+ <label for="sftpBasePath" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.sftp.base.path.required' | translate }}</label>
437
+ <input pInputText id="sftpBasePath" [formField]="configForm.sftpBasePath" [placeholder]="'storage.sftp.base.path.placeholder' | translate" class="w-full" />
436
438
  </div>
437
439
  </div>
438
440
  }
@@ -440,18 +442,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
440
442
  @if (selectedProvider() === fileLocationEnum.LOCAL) {
441
443
  <div class="grid grid-cols-1 gap-4 mt-4 p-4 bg-surface-ground rounded-lg border border-surface">
442
444
  <div class="mb-2">
443
- <span class="text-sm font-semibold text-muted-color uppercase tracking-wide">Local Storage Configuration</span>
445
+ <span class="text-sm font-semibold text-muted-color uppercase tracking-wide">{{ 'storage.local.title' | translate }}</span>
444
446
  </div>
445
447
  <div class="field">
446
- <label for="localBasePath" class="block font-medium mb-2 text-sm sm:text-base">Base Path *</label>
447
- <input pInputText id="localBasePath" [formField]="configForm.localBasePath" placeholder="/var/www/uploads" class="w-full" />
448
+ <label for="localBasePath" class="block font-medium mb-2 text-sm sm:text-base">{{ 'storage.local.base.path.required' | translate }}</label>
449
+ <input pInputText id="localBasePath" [formField]="configForm.localBasePath" [placeholder]="'storage.local.base.path.placeholder' | translate" class="w-full" />
448
450
  </div>
449
451
  </div>
450
452
  }
451
453
 
452
454
  <div class="flex justify-end gap-2 mt-4">
453
- <p-button label="Cancel" severity="secondary" [outlined]="true" (onClick)="onCancel()" />
454
- <p-button type="submit" [label]="isEditMode() ? 'Update' : 'Create'" [loading]="isLoading()" [disabled]="!isFormValid() || isLoading()" />
455
+ <p-button [label]="'shared.cancel' | translate" severity="secondary" [outlined]="true" (onClick)="onCancel()" />
456
+ <p-button type="submit" [label]="(isEditMode() ? 'shared.update' : 'shared.create') | translate" [loading]="isLoading()" [disabled]="!isFormValid() || isLoading()" />
455
457
  </div>
456
458
  </form>
457
459
  </div>
@@ -460,4 +462,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
460
462
  }], ctorParameters: () => [] });
461
463
 
462
464
  export { StorageConfigFormComponent };
463
- //# sourceMappingURL=flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs.map
465
+ //# sourceMappingURL=flusys-ng-storage-storage-config-form.component-a88XhuPH.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flusys-ng-storage-storage-config-form.component-a88XhuPH.mjs","sources":["../../../projects/ng-storage/pages/storage-config/storage-config-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 { FileLocationEnum } from '../../enums/file-location.enum';\nimport { IStorageConfig } from '../../interfaces/storage-config.interface';\nimport { StorageConfigApiService } from '../../services/storage-config-api.service';\nimport { BaseStoragePage } from '../../utils/base-storage-page';\nimport { STORAGE_PROVIDER_OPTIONS } from '../../utils/storage-utils';\n\ninterface IStorageConfigFormModel {\n id: string;\n name: string;\n storage: FileLocationEnum;\n isActive: boolean;\n isDefault: boolean;\n awsRegion: string;\n awsBucket: string;\n awsAccessKeyId: string;\n awsSecretAccessKey: string;\n awsEndpoint: string;\n azureAccountName: string;\n azureAccountKey: string;\n azureContainerName: string;\n sftpHost: string;\n sftpPort: string;\n sftpUsername: string;\n sftpPassword: string;\n sftpBasePath: string;\n localBasePath: string;\n}\n\nconst DEFAULT_FORM_MODEL: IStorageConfigFormModel = {\n id: '',\n name: '',\n storage: FileLocationEnum.AWS,\n isActive: true,\n isDefault: false,\n awsRegion: '',\n awsBucket: '',\n awsAccessKeyId: '',\n awsSecretAccessKey: '',\n awsEndpoint: '',\n azureAccountName: '',\n azureAccountKey: '',\n azureContainerName: '',\n sftpHost: '',\n sftpPort: '22',\n sftpUsername: '',\n sftpPassword: '',\n sftpBasePath: '',\n localBasePath: '',\n};\n\n@Component({\n selector: 'lib-storage-config-form',\n imports: [AngularModule, PrimeModule, FormField, TranslatePipe],\n template: `\n <div class=\"card\">\n <h3 class=\"text-lg sm:text-xl font-semibold mb-4\">\n {{ (isEditMode() ? 'storage.config.edit.config' : 'storage.config.new.config') | translate }}\n </h3>\n @if (showCompanyInfo()) {\n <p class=\"text-sm text-muted-color mb-4\">{{ 'storage.company' | translate }}: {{ currentCompanyName() }}</p>\n }\n\n <form (ngSubmit)=\"onSubmit()\">\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <div class=\"field\">\n <label for=\"name\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.config.config.name.required' | translate }}</label>\n <input pInputText id=\"name\" [formField]=\"configForm.name\" [placeholder]=\"'storage.config.config.name.placeholder' | translate\" class=\"w-full\" />\n </div>\n\n <div class=\"field\">\n <label for=\"storage\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.config.provider.required' | translate }}</label>\n <select id=\"storage\" class=\"p-select-native w-full\" [formField]=\"configForm.storage\">\n @for (provider of storageProviderOptions; track provider.value) {\n <option [value]=\"provider.value\">{{ provider.labelKey | translate }}</option>\n }\n </select>\n </div>\n\n <div class=\"field flex items-center gap-2\">\n <p-toggleswitch [formField]=\"configForm.isActive\" />\n <label class=\"text-sm sm:text-base\">{{ 'storage.active' | translate }}</label>\n </div>\n\n <div class=\"field flex items-center gap-2\">\n <p-toggleswitch [formField]=\"configForm.isDefault\" />\n <label class=\"text-sm sm:text-base\">{{ 'storage.set.as.default' | translate }}</label>\n </div>\n </div>\n\n @if (selectedProvider() === fileLocationEnum.AWS) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4 mt-4 p-4 bg-surface-ground rounded-lg border border-surface\">\n <div class=\"md:col-span-2 mb-2\">\n <span class=\"text-sm font-semibold text-muted-color uppercase tracking-wide\">{{ 'storage.aws.title' | translate }}</span>\n </div>\n <div class=\"field\">\n <label for=\"awsRegion\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.config.region.required' | translate }}</label>\n <input pInputText id=\"awsRegion\" [formField]=\"configForm.awsRegion\" [placeholder]=\"'storage.aws.region.placeholder' | translate\" class=\"w-full\" />\n </div>\n <div class=\"field\">\n <label for=\"awsBucket\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.config.bucket.required' | translate }}</label>\n <input pInputText id=\"awsBucket\" [formField]=\"configForm.awsBucket\" [placeholder]=\"'storage.aws.bucket.placeholder' | translate\" class=\"w-full\" />\n </div>\n <div class=\"field\">\n <label for=\"awsAccessKeyId\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.config.access.key.required' | translate }}</label>\n <input pInputText id=\"awsAccessKeyId\" [formField]=\"configForm.awsAccessKeyId\" class=\"w-full\" />\n </div>\n <div class=\"field\">\n <label for=\"awsSecretAccessKey\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.config.secret.key.required' | translate }}</label>\n <input type=\"password\" pInputText id=\"awsSecretAccessKey\" [formField]=\"configForm.awsSecretAccessKey\" class=\"w-full\" />\n </div>\n <div class=\"field md:col-span-2\">\n <label for=\"awsEndpoint\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.config.endpoint.optional' | translate }}</label>\n <input pInputText id=\"awsEndpoint\" [formField]=\"configForm.awsEndpoint\" [placeholder]=\"'storage.config.endpoint.placeholder' | translate\" class=\"w-full\" />\n </div>\n </div>\n }\n\n @if (selectedProvider() === fileLocationEnum.AZURE) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4 mt-4 p-4 bg-surface-ground rounded-lg border border-surface\">\n <div class=\"md:col-span-2 mb-2\">\n <span class=\"text-sm font-semibold text-muted-color uppercase tracking-wide\">{{ 'storage.azure.title' | translate }}</span>\n </div>\n <div class=\"field\">\n <label for=\"azureAccountName\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.azure.account.name.required' | translate }}</label>\n <input pInputText id=\"azureAccountName\" [formField]=\"configForm.azureAccountName\" class=\"w-full\" />\n </div>\n <div class=\"field\">\n <label for=\"azureContainerName\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.azure.container.name.required' | translate }}</label>\n <input pInputText id=\"azureContainerName\" [formField]=\"configForm.azureContainerName\" class=\"w-full\" />\n </div>\n <div class=\"field md:col-span-2\">\n <label for=\"azureAccountKey\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.azure.account.key.required' | translate }}</label>\n <input type=\"password\" pInputText id=\"azureAccountKey\" [formField]=\"configForm.azureAccountKey\" class=\"w-full\" />\n </div>\n </div>\n }\n\n @if (selectedProvider() === fileLocationEnum.SFTP) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4 mt-4 p-4 bg-surface-ground rounded-lg border border-surface\">\n <div class=\"md:col-span-2 mb-2\">\n <span class=\"text-sm font-semibold text-muted-color uppercase tracking-wide\">{{ 'storage.sftp.title' | translate }}</span>\n </div>\n <div class=\"field\">\n <label for=\"sftpHost\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.sftp.host.required' | translate }}</label>\n <input pInputText id=\"sftpHost\" [formField]=\"configForm.sftpHost\" [placeholder]=\"'storage.sftp.host.placeholder' | translate\" class=\"w-full\" />\n </div>\n <div class=\"field\">\n <label for=\"sftpPort\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.sftp.port.required' | translate }}</label>\n <input type=\"number\" pInputText id=\"sftpPort\" [formField]=\"configForm.sftpPort\" [placeholder]=\"'storage.sftp.port.placeholder' | translate\" class=\"w-full\" />\n </div>\n <div class=\"field\">\n <label for=\"sftpUsername\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.sftp.username.required' | translate }}</label>\n <input pInputText id=\"sftpUsername\" [formField]=\"configForm.sftpUsername\" class=\"w-full\" />\n </div>\n <div class=\"field\">\n <label for=\"sftpPassword\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.sftp.password.required' | translate }}</label>\n <input type=\"password\" pInputText id=\"sftpPassword\" [formField]=\"configForm.sftpPassword\" class=\"w-full\" />\n </div>\n <div class=\"field md:col-span-2\">\n <label for=\"sftpBasePath\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.sftp.base.path.required' | translate }}</label>\n <input pInputText id=\"sftpBasePath\" [formField]=\"configForm.sftpBasePath\" [placeholder]=\"'storage.sftp.base.path.placeholder' | translate\" class=\"w-full\" />\n </div>\n </div>\n }\n\n @if (selectedProvider() === fileLocationEnum.LOCAL) {\n <div class=\"grid grid-cols-1 gap-4 mt-4 p-4 bg-surface-ground rounded-lg border border-surface\">\n <div class=\"mb-2\">\n <span class=\"text-sm font-semibold text-muted-color uppercase tracking-wide\">{{ 'storage.local.title' | translate }}</span>\n </div>\n <div class=\"field\">\n <label for=\"localBasePath\" class=\"block font-medium mb-2 text-sm sm:text-base\">{{ 'storage.local.base.path.required' | translate }}</label>\n <input pInputText id=\"localBasePath\" [formField]=\"configForm.localBasePath\" [placeholder]=\"'storage.local.base.path.placeholder' | translate\" class=\"w-full\" />\n </div>\n </div>\n }\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 type=\"submit\" [label]=\"(isEditMode() ? 'shared.update' : 'shared.create') | translate\" [loading]=\"isLoading()\" [disabled]=\"!isFormValid() || isLoading()\" />\n </div>\n </form>\n </div>\n `,\n})\nexport class StorageConfigFormComponent extends BaseStoragePage {\n private readonly route = inject(ActivatedRoute);\n private readonly configService = inject(StorageConfigApiService);\n private readonly translateService = inject(TranslateService);\n private readonly routeParams = toSignal(this.route.paramMap);\n private initialized = false;\n\n readonly fileLocationEnum = FileLocationEnum;\n readonly storageProviderOptions = STORAGE_PROVIDER_OPTIONS;\n\n readonly existingConfig = signal<IStorageConfig | null>(null);\n readonly isEditMode = computed(() => !!this.existingConfig());\n readonly formModel = signal<IStorageConfigFormModel>({ ...DEFAULT_FORM_MODEL });\n\n readonly configForm = form(this.formModel, (f) => {\n required(f.name, { message: this.translateService.translate('storage.validation.config.name.required') });\n });\n\n readonly selectedProvider = computed(() => this.formModel().storage);\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 configId = params.get('id');\n if (configId && configId !== 'new') {\n this.loadConfig(configId);\n }\n });\n }\n\n async loadConfig(id: string): Promise<void> {\n this.isLoading.set(true);\n try {\n const response = await this.configService.findByIdAsync(id);\n if (response.success && response.data) {\n const config = response.data;\n this.existingConfig.set(config);\n this.formModel.update((m) => ({\n ...m,\n id: config.id,\n name: config.name,\n storage: config.storage,\n isActive: config.isActive ?? true,\n isDefault: config.isDefault ?? false,\n ...this.extractProviderConfig(config),\n }));\n }\n } catch {\n this.showError(this.translateService.translate('storage.config.load.config.failed'));\n this.navigateTo(['/storage/configs']);\n } finally {\n this.isLoading.set(false);\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 const config = this.buildProviderConfig(formValue);\n\n if (this.isEditMode()) {\n await this.configService.updateAsync({\n id: formValue.id,\n name: formValue.name,\n storage: formValue.storage,\n config,\n isActive: formValue.isActive,\n isDefault: formValue.isDefault,\n });\n this.showSuccess(this.translateService.translate('storage.config.update.success'));\n } else {\n await this.configService.insertAsync({\n name: formValue.name,\n storage: formValue.storage,\n config,\n isActive: formValue.isActive,\n isDefault: formValue.isDefault,\n });\n this.showSuccess(this.translateService.translate('storage.config.create.success'));\n }\n this.navigateTo(['/storage/configs']);\n } catch {\n const action = this.isEditMode() ? 'update' : 'create';\n this.showError(this.translateService.translate(`storage.config.${action}.failed`));\n } finally {\n this.isLoading.set(false);\n }\n }\n\n onCancel(): void {\n this.navigateTo(['/storage/configs']);\n }\n\n private extractProviderConfig(config: IStorageConfig): Partial<IStorageConfigFormModel> {\n const c = config.config ?? {};\n switch (config.storage) {\n case FileLocationEnum.AWS:\n return {\n awsRegion: (c['region'] as string) || '',\n awsBucket: (c['bucket'] as string) || '',\n awsAccessKeyId: (c['accessKeyId'] as string) || '',\n awsSecretAccessKey: (c['secretAccessKey'] as string) || '',\n awsEndpoint: (c['endpoint'] as string) || '',\n };\n case FileLocationEnum.AZURE:\n return {\n azureAccountName: (c['accountName'] as string) || '',\n azureAccountKey: (c['accountKey'] as string) || '',\n azureContainerName: (c['containerName'] as string) || '',\n };\n case FileLocationEnum.SFTP:\n return {\n sftpHost: (c['host'] as string) || '',\n sftpPort: ((c['port'] as number) || 22).toString(),\n sftpUsername: (c['username'] as string) || '',\n sftpPassword: (c['password'] as string) || '',\n sftpBasePath: (c['basePath'] as string) || '',\n };\n case FileLocationEnum.LOCAL:\n return { localBasePath: (c['basePath'] as string) || '' };\n default:\n return {};\n }\n }\n\n private buildProviderConfig(formValue: IStorageConfigFormModel): Record<string, unknown> {\n switch (formValue.storage) {\n case FileLocationEnum.AWS:\n return {\n region: formValue.awsRegion,\n bucket: formValue.awsBucket,\n accessKeyId: formValue.awsAccessKeyId,\n secretAccessKey: formValue.awsSecretAccessKey,\n ...(formValue.awsEndpoint && { endpoint: formValue.awsEndpoint }),\n };\n case FileLocationEnum.AZURE:\n return {\n accountName: formValue.azureAccountName,\n accountKey: formValue.azureAccountKey,\n containerName: formValue.azureContainerName,\n };\n case FileLocationEnum.SFTP:\n return {\n host: formValue.sftpHost,\n port: formValue.sftpPort ? parseInt(formValue.sftpPort, 10) : 22,\n username: formValue.sftpUsername,\n password: formValue.sftpPassword,\n basePath: formValue.sftpBasePath,\n };\n case FileLocationEnum.LOCAL:\n return { basePath: formValue.localBasePath };\n default:\n return {};\n }\n }\n}\n"],"names":["i2"],"mappings":"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"}