@flusys/ng-storage 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,716 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, signal, computed, Component } from '@angular/core';
3
+ import { form, required, FormField } from '@angular/forms/signals';
4
+ import { Router, ActivatedRoute } from '@angular/router';
5
+ import { APP_CONFIG, DEFAULT_APP_NAME } from '@flusys/ng-core';
6
+ import { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';
7
+ import { AngularModule, PrimeModule } from '@flusys/ng-shared';
8
+ import { MessageService } from 'primeng/api';
9
+ import { firstValueFrom } from 'rxjs';
10
+ import { FileManagerApiService, UploadService, StorageConfigApiService, FolderApiService } from './flusys-ng-storage.mjs';
11
+ import * as i1 from '@angular/forms';
12
+ import * as i2 from 'primeng/inputtext';
13
+ import * as i3 from 'primeng/button';
14
+ import * as i4 from 'primeng/fileupload';
15
+
16
+ /**
17
+ * File Manager Form Component
18
+ * Upload and edit file metadata
19
+ */
20
+ class FileManagerFormComponent {
21
+ router = inject(Router);
22
+ route = inject(ActivatedRoute);
23
+ messageService = inject(MessageService);
24
+ fileService = inject(FileManagerApiService);
25
+ uploadService = inject(UploadService);
26
+ configService = inject(StorageConfigApiService);
27
+ folderService = inject(FolderApiService);
28
+ appConfig = inject(APP_CONFIG);
29
+ companyContext = inject(LAYOUT_AUTH_STATE);
30
+ isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
31
+ isLoadingConfigs = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingConfigs" }] : []));
32
+ isLoadingFolders = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingFolders" }] : []));
33
+ existingFile = signal(null, ...(ngDevMode ? [{ debugName: "existingFile" }] : []));
34
+ isEditMode = computed(() => !!this.existingFile(), ...(ngDevMode ? [{ debugName: "isEditMode" }] : []));
35
+ selectedFile = signal(null, ...(ngDevMode ? [{ debugName: "selectedFile" }] : []));
36
+ availableConfigs = signal([], ...(ngDevMode ? [{ debugName: "availableConfigs" }] : []));
37
+ availableFolders = signal([], ...(ngDevMode ? [{ debugName: "availableFolders" }] : []));
38
+ showCompanyInfo = computed(() => this.appConfig.enableCompanyFeature, ...(ngDevMode ? [{ debugName: "showCompanyInfo" }] : []));
39
+ currentCompanyName = computed(() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
40
+ // ============================================
41
+ // Form (Signal Forms)
42
+ // ============================================
43
+ /** Form model */
44
+ formModel = signal({
45
+ id: '',
46
+ name: '',
47
+ isPrivate: false,
48
+ folderId: '',
49
+ storageConfigId: '',
50
+ }, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
51
+ /** Form with validation schema */
52
+ fileForm = form(this.formModel, (f) => {
53
+ required(f.name, { message: 'File name is required' });
54
+ // storageConfigId validation will be done manually in onSubmit
55
+ // as it's only required for new uploads, not edits
56
+ });
57
+ /** Check if form is valid */
58
+ isFormValid = computed(() => {
59
+ const model = this.formModel();
60
+ return model.name.trim().length > 0;
61
+ }, ...(ngDevMode ? [{ debugName: "isFormValid" }] : []));
62
+ ngOnInit() {
63
+ const fileId = this.route.snapshot.paramMap.get('id');
64
+ if (fileId) {
65
+ this.loadFile(fileId);
66
+ }
67
+ else {
68
+ // Load storage configurations for new upload
69
+ this.loadStorageConfigs();
70
+ }
71
+ // Load folders for both create and edit modes
72
+ this.loadFolders();
73
+ }
74
+ async loadStorageConfigs() {
75
+ this.isLoadingConfigs.set(true);
76
+ try {
77
+ const response = await firstValueFrom(this.configService.getAll('', {
78
+ pagination: { currentPage: 0, pageSize: 100 },
79
+ filter: {},
80
+ select: [],
81
+ sort: {},
82
+ }));
83
+ if (response.success && response.data) {
84
+ this.availableConfigs.set(response.data);
85
+ // Auto-select first config if only one available
86
+ if (response.data.length === 1) {
87
+ this.formModel.update((m) => ({
88
+ ...m,
89
+ storageConfigId: response.data[0].id,
90
+ }));
91
+ }
92
+ }
93
+ }
94
+ catch (error) {
95
+ this.messageService.add({
96
+ severity: 'error',
97
+ summary: 'Error',
98
+ detail: 'Failed to load storage configurations.',
99
+ });
100
+ }
101
+ finally {
102
+ this.isLoadingConfigs.set(false);
103
+ }
104
+ }
105
+ async loadFolders() {
106
+ this.isLoadingFolders.set(true);
107
+ try {
108
+ const response = await firstValueFrom(this.folderService.getAll('', {
109
+ pagination: { currentPage: 0, pageSize: 100 },
110
+ filter: {},
111
+ select: [],
112
+ sort: {},
113
+ }));
114
+ if (response.success && response.data) {
115
+ this.availableFolders.set(response.data);
116
+ }
117
+ }
118
+ catch (error) {
119
+ // Silently fail - folders are optional
120
+ console.error('Failed to load folders:', error);
121
+ }
122
+ finally {
123
+ this.isLoadingFolders.set(false);
124
+ }
125
+ }
126
+ async loadFile(id) {
127
+ this.isLoading.set(true);
128
+ try {
129
+ const response = await this.fileService.findByIdAsync(id);
130
+ if (response.success && response.data) {
131
+ const file = response.data;
132
+ this.existingFile.set(file);
133
+ this.formModel.set({
134
+ id: file.id,
135
+ name: file.name,
136
+ isPrivate: file.isPrivate ?? false,
137
+ folderId: file.folderId ?? '',
138
+ storageConfigId: '',
139
+ });
140
+ }
141
+ }
142
+ catch (error) {
143
+ this.messageService.add({
144
+ severity: 'error',
145
+ summary: 'Error',
146
+ detail: 'Failed to load file.',
147
+ });
148
+ this.router.navigate(['/storage/files']);
149
+ }
150
+ finally {
151
+ this.isLoading.set(false);
152
+ }
153
+ }
154
+ onFileSelect(event) {
155
+ const file = event.files?.[0] || event.currentFiles?.[0];
156
+ if (file) {
157
+ this.selectedFile.set(file);
158
+ // Auto-fill file name
159
+ this.formModel.update((m) => ({ ...m, name: file.name }));
160
+ }
161
+ }
162
+ async onSubmit() {
163
+ if (!this.isFormValid())
164
+ return;
165
+ this.isLoading.set(true);
166
+ try {
167
+ const formValue = this.formModel();
168
+ if (this.isEditMode()) {
169
+ // Update existing file metadata only
170
+ await this.fileService.updateAsync({
171
+ id: formValue.id,
172
+ name: formValue.name,
173
+ isPrivate: formValue.isPrivate,
174
+ folderId: formValue.folderId || null,
175
+ });
176
+ this.messageService.add({
177
+ severity: 'success',
178
+ summary: 'Success',
179
+ detail: 'File updated successfully.',
180
+ });
181
+ }
182
+ else {
183
+ // Upload new file
184
+ const file = this.selectedFile();
185
+ if (!file) {
186
+ this.messageService.add({
187
+ severity: 'error',
188
+ summary: 'Error',
189
+ detail: 'Please select a file to upload.',
190
+ });
191
+ return;
192
+ }
193
+ // Validate storage config selection
194
+ if (!formValue.storageConfigId) {
195
+ this.messageService.add({
196
+ severity: 'error',
197
+ summary: 'Error',
198
+ detail: 'Please select a storage configuration.',
199
+ });
200
+ return;
201
+ }
202
+ // Upload file first
203
+ const uploadResponse = await firstValueFrom(this.uploadService.uploadSingleFile(file, {
204
+ compress: true,
205
+ quality: 85,
206
+ storageConfigId: formValue.storageConfigId,
207
+ }));
208
+ if (uploadResponse.success && uploadResponse.data) {
209
+ const uploadedFileInfo = uploadResponse.data;
210
+ // Save file metadata
211
+ await this.fileService.insertAsync({
212
+ name: formValue.name,
213
+ key: uploadedFileInfo.key,
214
+ contentType: file.type || 'application/octet-stream',
215
+ size: Math.round(file.size / 1024).toString(),
216
+ isPrivate: formValue.isPrivate,
217
+ folderId: formValue.folderId || undefined,
218
+ storageConfigId: formValue.storageConfigId, // ✅ Include storage config ID
219
+ });
220
+ this.messageService.add({
221
+ severity: 'success',
222
+ summary: 'Success',
223
+ detail: 'File uploaded successfully.',
224
+ });
225
+ }
226
+ else {
227
+ throw new Error('Upload failed');
228
+ }
229
+ }
230
+ this.router.navigate(['/storage/files']);
231
+ }
232
+ catch (error) {
233
+ this.messageService.add({
234
+ severity: 'error',
235
+ summary: 'Error',
236
+ detail: error?.message ||
237
+ `Failed to ${this.isEditMode() ? 'update' : 'upload'} file.`,
238
+ });
239
+ }
240
+ finally {
241
+ this.isLoading.set(false);
242
+ }
243
+ }
244
+ onCancel() {
245
+ this.router.navigate(['/storage/files']);
246
+ }
247
+ formatFileSize(bytes) {
248
+ if (bytes === 0)
249
+ return '0 Bytes';
250
+ const k = 1024;
251
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
252
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
253
+ return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
254
+ }
255
+ getProviderLabel(storage) {
256
+ const labels = {
257
+ AWS: 'AWS S3',
258
+ AZURE: 'Azure Blob',
259
+ SFTP: 'SFTP',
260
+ LOCAL: 'Local',
261
+ };
262
+ return labels[storage] || storage;
263
+ }
264
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FileManagerFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
265
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: FileManagerFormComponent, isStandalone: true, selector: "lib-file-manager-form", ngImport: i0, template: `
266
+ <div class="card">
267
+ <div class="flex justify-between items-center mb-4">
268
+ <div>
269
+ <h3 class="text-xl font-semibold">
270
+ {{ isEditMode() ? 'Edit File' : 'Upload File' }}
271
+ </h3>
272
+ @if (showCompanyInfo()) {
273
+ <p class="text-sm text-gray-600 mt-1">
274
+ Company: {{ currentCompanyName() }}
275
+ </p>
276
+ }
277
+ </div>
278
+ <p-button
279
+ label="Back"
280
+ icon="pi pi-arrow-left"
281
+ severity="secondary"
282
+ [text]="true"
283
+ (onClick)="onCancel()"
284
+ />
285
+ </div>
286
+
287
+ <form (ngSubmit)="onSubmit()">
288
+ <!-- Storage Configuration & Folder Selection (only for new files) -->
289
+ @if (!isEditMode()) {
290
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
291
+ <!-- Storage Configuration -->
292
+ <div class="field">
293
+ <label for="storageConfig" class="block font-medium mb-2"
294
+ >Storage Configuration *</label
295
+ >
296
+ <select
297
+ id="storageConfig"
298
+ class="p-select-native"
299
+ [formField]="fileForm.storageConfigId"
300
+ >
301
+ <option value="">Select storage configuration...</option>
302
+ @for (config of availableConfigs(); track config.id) {
303
+ <option [value]="config.id">
304
+ {{ config.name }} ({{ getProviderLabel(config.storage) }})
305
+ </option>
306
+ }
307
+ </select>
308
+ @if (availableConfigs().length === 0 && !isLoadingConfigs()) {
309
+ <small class="text-orange-600 block mt-2">
310
+ <i class="pi pi-exclamation-triangle mr-1"></i>
311
+ No storage configurations found. Please create one first.
312
+ </small>
313
+ }
314
+ </div>
315
+
316
+ <!-- Folder Selection -->
317
+ <div class="field">
318
+ <label for="folder" class="block font-medium mb-2"
319
+ >Folder (Optional)</label
320
+ >
321
+ <select
322
+ id="folder"
323
+ class="p-select-native"
324
+ [formField]="fileForm.folderId"
325
+ >
326
+ <option value="">No folder (root level)</option>
327
+ @for (folder of availableFolders(); track folder.id) {
328
+ <option [value]="folder.id">{{ folder.name }}</option>
329
+ }
330
+ </select>
331
+ @if (availableFolders().length === 0 && !isLoadingFolders()) {
332
+ <small class="text-gray-600 block mt-2">
333
+ <i class="pi pi-info-circle mr-1"></i>
334
+ No folders available. File will be uploaded to root level.
335
+ </small>
336
+ }
337
+ </div>
338
+ </div>
339
+ } @else {
340
+ <!-- Folder Selection for Edit Mode -->
341
+ <div class="field mb-4">
342
+ <label for="folder" class="block font-medium mb-2"
343
+ >Folder (Optional)</label
344
+ >
345
+ <select
346
+ id="folder"
347
+ class="p-select-native"
348
+ [formField]="fileForm.folderId"
349
+ >
350
+ <option value="">No folder (root level)</option>
351
+ @for (folder of availableFolders(); track folder.id) {
352
+ <option [value]="folder.id">{{ folder.name }}</option>
353
+ }
354
+ </select>
355
+ @if (availableFolders().length === 0 && !isLoadingFolders()) {
356
+ <small class="text-gray-600 block mt-2">
357
+ <i class="pi pi-info-circle mr-1"></i>
358
+ No folders available.
359
+ </small>
360
+ }
361
+ </div>
362
+ }
363
+
364
+ <!-- File Upload (only for new files) -->
365
+ @if (!isEditMode()) {
366
+ <div class="field mb-4">
367
+ <label class="block font-medium mb-2">Select File *</label>
368
+ <p-fileupload
369
+ #fileUpload
370
+ [customUpload]="true"
371
+ (onSelect)="onFileSelect($event)"
372
+ [multiple]="false"
373
+ [maxFileSize]="50000000"
374
+ [showUploadButton]="false"
375
+ [showCancelButton]="false"
376
+ chooseLabel="Choose File"
377
+ chooseIcon="pi pi-upload"
378
+ accept="*/*"
379
+ class="w-full"
380
+ >
381
+ <ng-template #content let-files>
382
+ @if (files && files.length > 0) {
383
+ <div class="flex flex-col gap-2">
384
+ @for (file of files; track file.name) {
385
+ <div class="flex items-center gap-3 p-3 border rounded">
386
+ <i class="pi pi-file text-2xl"></i>
387
+ <div class="flex-1">
388
+ <div class="font-medium">{{ file.name }}</div>
389
+ <div class="text-sm text-gray-600">
390
+ {{ formatFileSize(file.size) }}
391
+ </div>
392
+ </div>
393
+ <p-button
394
+ icon="pi pi-times"
395
+ [text]="true"
396
+ severity="danger"
397
+ size="small"
398
+ (onClick)="fileUpload.clear()"
399
+ />
400
+ </div>
401
+ }
402
+ </div>
403
+ } @else {
404
+ <div
405
+ class="flex items-center justify-center flex-col p-8 border-2 border-dashed border-gray-300 rounded"
406
+ >
407
+ <i
408
+ class="pi pi-cloud-upload text-4xl text-gray-400 mb-3"
409
+ ></i>
410
+ <p class="text-gray-600">
411
+ Drag and drop file here or click to browse
412
+ </p>
413
+ <p class="text-sm text-gray-500 mt-2">
414
+ Maximum file size: 50MB
415
+ </p>
416
+ </div>
417
+ }
418
+ </ng-template>
419
+ </p-fileupload>
420
+ @if (!selectedFile() && !isEditMode()) {
421
+ <small class="text-gray-600"
422
+ >Please select a file to upload</small
423
+ >
424
+ }
425
+ </div>
426
+ }
427
+
428
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
429
+ <!-- File Name (read-only for uploads, editable for existing) -->
430
+ <div class="field md:col-span-2">
431
+ <label for="name" class="block font-medium mb-2">File Name *</label>
432
+ <input
433
+ pInputText
434
+ id="name"
435
+ [formField]="fileForm.name"
436
+ [placeholder]="
437
+ isEditMode()
438
+ ? 'Enter file name'
439
+ : 'Will be auto-filled from selected file'
440
+ "
441
+ class="w-full"
442
+ />
443
+ </div>
444
+
445
+ <!-- Privacy Toggle -->
446
+ <div class="field">
447
+ <label class="p-checkbox-native">
448
+ <input type="checkbox" [formField]="fileForm.isPrivate" />
449
+ <span>Private File</span>
450
+ </label>
451
+ <small class="text-gray-600 block mt-2">
452
+ {{
453
+ formModel().isPrivate
454
+ ? 'Only users with permission can access'
455
+ : 'File is publicly accessible'
456
+ }}
457
+ </small>
458
+ </div>
459
+ </div>
460
+
461
+ <div class="flex gap-2 mt-4">
462
+ <p-button
463
+ type="submit"
464
+ [label]="isEditMode() ? 'Update' : 'Upload'"
465
+ [icon]="isEditMode() ? 'pi pi-check' : 'pi pi-cloud-upload'"
466
+ [loading]="isLoading()"
467
+ [disabled]="
468
+ !isFormValid() ||
469
+ (!isEditMode() && !selectedFile()) ||
470
+ isLoading()
471
+ "
472
+ />
473
+ <p-button
474
+ type="button"
475
+ label="Cancel"
476
+ severity="secondary"
477
+ icon="pi pi-times"
478
+ [text]="true"
479
+ (onClick)="onCancel()"
480
+ />
481
+ </div>
482
+ </form>
483
+ </div>
484
+ `, 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: "directive", type: i2.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i3.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: i4.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: FormField, selector: "[formField]", inputs: ["formField"] }] });
485
+ }
486
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FileManagerFormComponent, decorators: [{
487
+ type: Component,
488
+ args: [{
489
+ selector: 'lib-file-manager-form',
490
+ standalone: true,
491
+ imports: [AngularModule, PrimeModule, FormField],
492
+ template: `
493
+ <div class="card">
494
+ <div class="flex justify-between items-center mb-4">
495
+ <div>
496
+ <h3 class="text-xl font-semibold">
497
+ {{ isEditMode() ? 'Edit File' : 'Upload File' }}
498
+ </h3>
499
+ @if (showCompanyInfo()) {
500
+ <p class="text-sm text-gray-600 mt-1">
501
+ Company: {{ currentCompanyName() }}
502
+ </p>
503
+ }
504
+ </div>
505
+ <p-button
506
+ label="Back"
507
+ icon="pi pi-arrow-left"
508
+ severity="secondary"
509
+ [text]="true"
510
+ (onClick)="onCancel()"
511
+ />
512
+ </div>
513
+
514
+ <form (ngSubmit)="onSubmit()">
515
+ <!-- Storage Configuration & Folder Selection (only for new files) -->
516
+ @if (!isEditMode()) {
517
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
518
+ <!-- Storage Configuration -->
519
+ <div class="field">
520
+ <label for="storageConfig" class="block font-medium mb-2"
521
+ >Storage Configuration *</label
522
+ >
523
+ <select
524
+ id="storageConfig"
525
+ class="p-select-native"
526
+ [formField]="fileForm.storageConfigId"
527
+ >
528
+ <option value="">Select storage configuration...</option>
529
+ @for (config of availableConfigs(); track config.id) {
530
+ <option [value]="config.id">
531
+ {{ config.name }} ({{ getProviderLabel(config.storage) }})
532
+ </option>
533
+ }
534
+ </select>
535
+ @if (availableConfigs().length === 0 && !isLoadingConfigs()) {
536
+ <small class="text-orange-600 block mt-2">
537
+ <i class="pi pi-exclamation-triangle mr-1"></i>
538
+ No storage configurations found. Please create one first.
539
+ </small>
540
+ }
541
+ </div>
542
+
543
+ <!-- Folder Selection -->
544
+ <div class="field">
545
+ <label for="folder" class="block font-medium mb-2"
546
+ >Folder (Optional)</label
547
+ >
548
+ <select
549
+ id="folder"
550
+ class="p-select-native"
551
+ [formField]="fileForm.folderId"
552
+ >
553
+ <option value="">No folder (root level)</option>
554
+ @for (folder of availableFolders(); track folder.id) {
555
+ <option [value]="folder.id">{{ folder.name }}</option>
556
+ }
557
+ </select>
558
+ @if (availableFolders().length === 0 && !isLoadingFolders()) {
559
+ <small class="text-gray-600 block mt-2">
560
+ <i class="pi pi-info-circle mr-1"></i>
561
+ No folders available. File will be uploaded to root level.
562
+ </small>
563
+ }
564
+ </div>
565
+ </div>
566
+ } @else {
567
+ <!-- Folder Selection for Edit Mode -->
568
+ <div class="field mb-4">
569
+ <label for="folder" class="block font-medium mb-2"
570
+ >Folder (Optional)</label
571
+ >
572
+ <select
573
+ id="folder"
574
+ class="p-select-native"
575
+ [formField]="fileForm.folderId"
576
+ >
577
+ <option value="">No folder (root level)</option>
578
+ @for (folder of availableFolders(); track folder.id) {
579
+ <option [value]="folder.id">{{ folder.name }}</option>
580
+ }
581
+ </select>
582
+ @if (availableFolders().length === 0 && !isLoadingFolders()) {
583
+ <small class="text-gray-600 block mt-2">
584
+ <i class="pi pi-info-circle mr-1"></i>
585
+ No folders available.
586
+ </small>
587
+ }
588
+ </div>
589
+ }
590
+
591
+ <!-- File Upload (only for new files) -->
592
+ @if (!isEditMode()) {
593
+ <div class="field mb-4">
594
+ <label class="block font-medium mb-2">Select File *</label>
595
+ <p-fileupload
596
+ #fileUpload
597
+ [customUpload]="true"
598
+ (onSelect)="onFileSelect($event)"
599
+ [multiple]="false"
600
+ [maxFileSize]="50000000"
601
+ [showUploadButton]="false"
602
+ [showCancelButton]="false"
603
+ chooseLabel="Choose File"
604
+ chooseIcon="pi pi-upload"
605
+ accept="*/*"
606
+ class="w-full"
607
+ >
608
+ <ng-template #content let-files>
609
+ @if (files && files.length > 0) {
610
+ <div class="flex flex-col gap-2">
611
+ @for (file of files; track file.name) {
612
+ <div class="flex items-center gap-3 p-3 border rounded">
613
+ <i class="pi pi-file text-2xl"></i>
614
+ <div class="flex-1">
615
+ <div class="font-medium">{{ file.name }}</div>
616
+ <div class="text-sm text-gray-600">
617
+ {{ formatFileSize(file.size) }}
618
+ </div>
619
+ </div>
620
+ <p-button
621
+ icon="pi pi-times"
622
+ [text]="true"
623
+ severity="danger"
624
+ size="small"
625
+ (onClick)="fileUpload.clear()"
626
+ />
627
+ </div>
628
+ }
629
+ </div>
630
+ } @else {
631
+ <div
632
+ class="flex items-center justify-center flex-col p-8 border-2 border-dashed border-gray-300 rounded"
633
+ >
634
+ <i
635
+ class="pi pi-cloud-upload text-4xl text-gray-400 mb-3"
636
+ ></i>
637
+ <p class="text-gray-600">
638
+ Drag and drop file here or click to browse
639
+ </p>
640
+ <p class="text-sm text-gray-500 mt-2">
641
+ Maximum file size: 50MB
642
+ </p>
643
+ </div>
644
+ }
645
+ </ng-template>
646
+ </p-fileupload>
647
+ @if (!selectedFile() && !isEditMode()) {
648
+ <small class="text-gray-600"
649
+ >Please select a file to upload</small
650
+ >
651
+ }
652
+ </div>
653
+ }
654
+
655
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
656
+ <!-- File Name (read-only for uploads, editable for existing) -->
657
+ <div class="field md:col-span-2">
658
+ <label for="name" class="block font-medium mb-2">File Name *</label>
659
+ <input
660
+ pInputText
661
+ id="name"
662
+ [formField]="fileForm.name"
663
+ [placeholder]="
664
+ isEditMode()
665
+ ? 'Enter file name'
666
+ : 'Will be auto-filled from selected file'
667
+ "
668
+ class="w-full"
669
+ />
670
+ </div>
671
+
672
+ <!-- Privacy Toggle -->
673
+ <div class="field">
674
+ <label class="p-checkbox-native">
675
+ <input type="checkbox" [formField]="fileForm.isPrivate" />
676
+ <span>Private File</span>
677
+ </label>
678
+ <small class="text-gray-600 block mt-2">
679
+ {{
680
+ formModel().isPrivate
681
+ ? 'Only users with permission can access'
682
+ : 'File is publicly accessible'
683
+ }}
684
+ </small>
685
+ </div>
686
+ </div>
687
+
688
+ <div class="flex gap-2 mt-4">
689
+ <p-button
690
+ type="submit"
691
+ [label]="isEditMode() ? 'Update' : 'Upload'"
692
+ [icon]="isEditMode() ? 'pi pi-check' : 'pi pi-cloud-upload'"
693
+ [loading]="isLoading()"
694
+ [disabled]="
695
+ !isFormValid() ||
696
+ (!isEditMode() && !selectedFile()) ||
697
+ isLoading()
698
+ "
699
+ />
700
+ <p-button
701
+ type="button"
702
+ label="Cancel"
703
+ severity="secondary"
704
+ icon="pi pi-times"
705
+ [text]="true"
706
+ (onClick)="onCancel()"
707
+ />
708
+ </div>
709
+ </form>
710
+ </div>
711
+ `,
712
+ }]
713
+ }] });
714
+
715
+ export { FileManagerFormComponent };
716
+ //# sourceMappingURL=flusys-ng-storage-file-manager-form.component-BI0hgXeI.mjs.map