@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.
- package/fesm2022/flusys-ng-storage-file-manager-form.component-BI0hgXeI.mjs +716 -0
- package/fesm2022/flusys-ng-storage-file-manager-form.component-BI0hgXeI.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-file-manager-list.component-C3C7Y8-n.mjs +445 -0
- package/fesm2022/flusys-ng-storage-file-manager-list.component-C3C7Y8-n.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-folder-form.component-Cw2aLZkb.mjs +253 -0
- package/fesm2022/flusys-ng-storage-folder-form.component-Cw2aLZkb.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-folder-list.component-CN_mTtS5.mjs +267 -0
- package/fesm2022/flusys-ng-storage-folder-list.component-CN_mTtS5.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-storage-config-form.component-DHzsQ2Mc.mjs +513 -0
- package/fesm2022/flusys-ng-storage-storage-config-form.component-DHzsQ2Mc.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-storage-config-list.component-tmiE2CpM.mjs +328 -0
- package/fesm2022/flusys-ng-storage-storage-config-list.component-tmiE2CpM.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage.mjs +415 -0
- package/fesm2022/flusys-ng-storage.mjs.map +1 -0
- package/package.json +28 -0
- package/types/flusys-ng-storage.d.ts +343 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flusys-ng-storage-file-manager-form.component-BI0hgXeI.mjs","sources":["../../../projects/ng-storage/pages/file-manager/file-manager-form.component.ts"],"sourcesContent":["import { Component, computed, inject, OnInit, signal } from '@angular/core';\nimport { form, FormField, required } from '@angular/forms/signals';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { APP_CONFIG, DEFAULT_APP_NAME } from '@flusys/ng-core';\nimport { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';\nimport { AngularModule, PrimeModule } from '@flusys/ng-shared';\nimport { MessageService } from 'primeng/api';\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';\n\n/** File manager form model interface */\ninterface IFileManagerFormModel {\n id: string;\n name: string;\n isPrivate: boolean;\n folderId: string;\n storageConfigId: string;\n}\n\n/**\n * File Manager Form Component\n * Upload and edit file metadata\n */\n@Component({\n selector: 'lib-file-manager-form',\n standalone: true,\n imports: [AngularModule, PrimeModule, FormField],\n template: `\n <div class=\"card\">\n <div class=\"flex justify-between items-center mb-4\">\n <div>\n <h3 class=\"text-xl font-semibold\">\n {{ isEditMode() ? 'Edit File' : 'Upload File' }}\n </h3>\n @if (showCompanyInfo()) {\n <p class=\"text-sm text-gray-600 mt-1\">\n Company: {{ currentCompanyName() }}\n </p>\n }\n </div>\n <p-button\n label=\"Back\"\n icon=\"pi pi-arrow-left\"\n severity=\"secondary\"\n [text]=\"true\"\n (onClick)=\"onCancel()\"\n />\n </div>\n\n <form (ngSubmit)=\"onSubmit()\">\n <!-- Storage Configuration & Folder Selection (only for new files) -->\n @if (!isEditMode()) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4 mb-4\">\n <!-- Storage Configuration -->\n <div class=\"field\">\n <label for=\"storageConfig\" class=\"block font-medium mb-2\"\n >Storage Configuration *</label\n >\n <select\n id=\"storageConfig\"\n class=\"p-select-native\"\n [formField]=\"fileForm.storageConfigId\"\n >\n <option value=\"\">Select storage configuration...</option>\n @for (config of availableConfigs(); track config.id) {\n <option [value]=\"config.id\">\n {{ config.name }} ({{ getProviderLabel(config.storage) }})\n </option>\n }\n </select>\n @if (availableConfigs().length === 0 && !isLoadingConfigs()) {\n <small class=\"text-orange-600 block mt-2\">\n <i class=\"pi pi-exclamation-triangle mr-1\"></i>\n No storage configurations found. Please create one first.\n </small>\n }\n </div>\n\n <!-- Folder Selection -->\n <div class=\"field\">\n <label for=\"folder\" class=\"block font-medium mb-2\"\n >Folder (Optional)</label\n >\n <select\n id=\"folder\"\n class=\"p-select-native\"\n [formField]=\"fileForm.folderId\"\n >\n <option value=\"\">No folder (root level)</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-gray-600 block mt-2\">\n <i class=\"pi pi-info-circle mr-1\"></i>\n No folders available. File will be uploaded to root level.\n </small>\n }\n </div>\n </div>\n } @else {\n <!-- Folder Selection for Edit Mode -->\n <div class=\"field mb-4\">\n <label for=\"folder\" class=\"block font-medium mb-2\"\n >Folder (Optional)</label\n >\n <select\n id=\"folder\"\n class=\"p-select-native\"\n [formField]=\"fileForm.folderId\"\n >\n <option value=\"\">No folder (root level)</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-gray-600 block mt-2\">\n <i class=\"pi pi-info-circle mr-1\"></i>\n No folders available.\n </small>\n }\n </div>\n }\n\n <!-- File Upload (only for new files) -->\n @if (!isEditMode()) {\n <div class=\"field mb-4\">\n <label class=\"block font-medium mb-2\">Select File *</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=\"Choose File\"\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 rounded\">\n <i class=\"pi pi-file text-2xl\"></i>\n <div class=\"flex-1\">\n <div class=\"font-medium\">{{ file.name }}</div>\n <div class=\"text-sm text-gray-600\">\n {{ formatFileSize(file.size) }}\n </div>\n </div>\n <p-button\n icon=\"pi pi-times\"\n [text]=\"true\"\n severity=\"danger\"\n size=\"small\"\n (onClick)=\"fileUpload.clear()\"\n />\n </div>\n }\n </div>\n } @else {\n <div\n class=\"flex items-center justify-center flex-col p-8 border-2 border-dashed border-gray-300 rounded\"\n >\n <i\n class=\"pi pi-cloud-upload text-4xl text-gray-400 mb-3\"\n ></i>\n <p class=\"text-gray-600\">\n Drag and drop file here or click to browse\n </p>\n <p class=\"text-sm text-gray-500 mt-2\">\n Maximum file size: 50MB\n </p>\n </div>\n }\n </ng-template>\n </p-fileupload>\n @if (!selectedFile() && !isEditMode()) {\n <small class=\"text-gray-600\"\n >Please select a file to upload</small\n >\n }\n </div>\n }\n\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <!-- File Name (read-only for uploads, editable for existing) -->\n <div class=\"field md:col-span-2\">\n <label for=\"name\" class=\"block font-medium mb-2\">File Name *</label>\n <input\n pInputText\n id=\"name\"\n [formField]=\"fileForm.name\"\n [placeholder]=\"\n isEditMode()\n ? 'Enter file name'\n : 'Will be auto-filled from selected file'\n \"\n class=\"w-full\"\n />\n </div>\n\n <!-- Privacy Toggle -->\n <div class=\"field\">\n <label class=\"p-checkbox-native\">\n <input type=\"checkbox\" [formField]=\"fileForm.isPrivate\" />\n <span>Private File</span>\n </label>\n <small class=\"text-gray-600 block mt-2\">\n {{\n formModel().isPrivate\n ? 'Only users with permission can access'\n : 'File is publicly accessible'\n }}\n </small>\n </div>\n </div>\n\n <div class=\"flex gap-2 mt-4\">\n <p-button\n type=\"submit\"\n [label]=\"isEditMode() ? 'Update' : 'Upload'\"\n [icon]=\"isEditMode() ? 'pi pi-check' : 'pi pi-cloud-upload'\"\n [loading]=\"isLoading()\"\n [disabled]=\"\n !isFormValid() ||\n (!isEditMode() && !selectedFile()) ||\n isLoading()\n \"\n />\n <p-button\n type=\"button\"\n label=\"Cancel\"\n severity=\"secondary\"\n icon=\"pi pi-times\"\n [text]=\"true\"\n (onClick)=\"onCancel()\"\n />\n </div>\n </form>\n </div>\n `,\n})\nexport class FileManagerFormComponent implements OnInit {\n private readonly router = inject(Router);\n private readonly route = inject(ActivatedRoute);\n private readonly messageService = inject(MessageService);\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 appConfig = inject(APP_CONFIG);\n private readonly companyContext = inject(LAYOUT_AUTH_STATE);\n\n readonly isLoading = signal(false);\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 showCompanyInfo = computed(\n () => this.appConfig.enableCompanyFeature,\n );\n readonly currentCompanyName = computed(\n () => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME,\n );\n\n // ============================================\n // Form (Signal Forms)\n // ============================================\n\n /** Form model */\n readonly formModel = signal<IFileManagerFormModel>({\n id: '',\n name: '',\n isPrivate: false,\n folderId: '',\n storageConfigId: '',\n });\n\n /** Form with validation schema */\n readonly fileForm = form(this.formModel, (f) => {\n required(f.name, { message: 'File name is required' });\n // storageConfigId validation will be done manually in onSubmit\n // as it's only required for new uploads, not edits\n });\n\n /** Check if form is valid */\n readonly isFormValid = computed(() => {\n const model = this.formModel();\n return model.name.trim().length > 0;\n });\n\n ngOnInit(): void {\n const fileId = this.route.snapshot.paramMap.get('id');\n if (fileId) {\n this.loadFile(fileId);\n } else {\n // Load storage configurations for new upload\n this.loadStorageConfigs();\n }\n // Load folders for both create and edit modes\n this.loadFolders();\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 // Auto-select first config if only one available\n if (response.data.length === 1) {\n this.formModel.update((m) => ({\n ...m,\n storageConfigId: response.data![0].id,\n }));\n }\n }\n } catch (error) {\n this.messageService.add({\n severity: 'error',\n summary: 'Error',\n detail: 'Failed to load storage configurations.',\n });\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 (error) {\n // Silently fail - folders are optional\n console.error('Failed to load folders:', error);\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 (error) {\n this.messageService.add({\n severity: 'error',\n summary: 'Error',\n detail: 'Failed to load file.',\n });\n this.router.navigate(['/storage/files']);\n } finally {\n this.isLoading.set(false);\n }\n }\n\n onFileSelect(event: any): void {\n const file = event.files?.[0] || event.currentFiles?.[0];\n if (file) {\n this.selectedFile.set(file);\n // Auto-fill file name\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 // Update existing file metadata only\n await this.fileService.updateAsync({\n id: formValue.id,\n name: formValue.name,\n isPrivate: formValue.isPrivate,\n folderId: formValue.folderId || null,\n } as any);\n this.messageService.add({\n severity: 'success',\n summary: 'Success',\n detail: 'File updated successfully.',\n });\n } else {\n // Upload new file\n const file = this.selectedFile();\n if (!file) {\n this.messageService.add({\n severity: 'error',\n summary: 'Error',\n detail: 'Please select a file to upload.',\n });\n return;\n }\n\n // Validate storage config selection\n if (!formValue.storageConfigId) {\n this.messageService.add({\n severity: 'error',\n summary: 'Error',\n detail: 'Please select a storage configuration.',\n });\n return;\n }\n\n // Upload file first\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\n // Save file metadata\n await this.fileService.insertAsync({\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, // ✅ Include storage config ID\n } as any);\n\n this.messageService.add({\n severity: 'success',\n summary: 'Success',\n detail: 'File uploaded successfully.',\n });\n } else {\n throw new Error('Upload failed');\n }\n }\n\n this.router.navigate(['/storage/files']);\n } catch (error: any) {\n this.messageService.add({\n severity: 'error',\n summary: 'Error',\n detail:\n error?.message ||\n `Failed to ${this.isEditMode() ? 'update' : 'upload'} file.`,\n });\n } finally {\n this.isLoading.set(false);\n }\n }\n\n onCancel(): void {\n this.router.navigate(['/storage/files']);\n }\n\n formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 Bytes';\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];\n }\n\n getProviderLabel(storage: string): string {\n const labels: Record<string, string> = {\n AWS: 'AWS S3',\n AZURE: 'Azure Blob',\n SFTP: 'SFTP',\n LOCAL: 'Local',\n };\n return labels[storage] || storage;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, signal, computed, Component } from '@angular/core';
|
|
3
|
+
import { Router } from '@angular/router';
|
|
4
|
+
import { APP_CONFIG, DEFAULT_APP_NAME } from '@flusys/ng-core';
|
|
5
|
+
import { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';
|
|
6
|
+
import { AngularModule, PrimeModule } from '@flusys/ng-shared';
|
|
7
|
+
import { MessageService, ConfirmationService } from 'primeng/api';
|
|
8
|
+
import { firstValueFrom } from 'rxjs';
|
|
9
|
+
import { FileManagerApiService } from './flusys-ng-storage.mjs';
|
|
10
|
+
import * as i1 from 'primeng/tag';
|
|
11
|
+
import * as i3 from 'primeng/button';
|
|
12
|
+
import * as i3$1 from 'primeng/tooltip';
|
|
13
|
+
import * as i4 from 'primeng/table';
|
|
14
|
+
import * as i5 from '@angular/common';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* File Manager List Component
|
|
18
|
+
* Displays all files with company filter (if enabled)
|
|
19
|
+
* Shows current company name in header
|
|
20
|
+
*/
|
|
21
|
+
class FileManagerListComponent {
|
|
22
|
+
router = inject(Router);
|
|
23
|
+
messageService = inject(MessageService);
|
|
24
|
+
confirmationService = inject(ConfirmationService);
|
|
25
|
+
fileService = inject(FileManagerApiService);
|
|
26
|
+
appConfig = inject(APP_CONFIG);
|
|
27
|
+
companyContext = inject(LAYOUT_AUTH_STATE);
|
|
28
|
+
/** Loading state */
|
|
29
|
+
isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
30
|
+
/** Loading state for file viewing */
|
|
31
|
+
isViewLoading = signal(null, ...(ngDevMode ? [{ debugName: "isViewLoading" }] : []));
|
|
32
|
+
/** Files list */
|
|
33
|
+
files = signal([], ...(ngDevMode ? [{ debugName: "files" }] : []));
|
|
34
|
+
/** Total records for pagination */
|
|
35
|
+
totalRecords = signal(0, ...(ngDevMode ? [{ debugName: "totalRecords" }] : []));
|
|
36
|
+
/** Page size */
|
|
37
|
+
pageSize = signal(50, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
|
|
38
|
+
/** Current page */
|
|
39
|
+
currentPage = signal(1, ...(ngDevMode ? [{ debugName: "currentPage" }] : []));
|
|
40
|
+
/** Show company info if company feature enabled */
|
|
41
|
+
showCompanyInfo = computed(() => this.appConfig.enableCompanyFeature, ...(ngDevMode ? [{ debugName: "showCompanyInfo" }] : []));
|
|
42
|
+
/** Current company name */
|
|
43
|
+
currentCompanyName = computed(() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
|
|
44
|
+
ngOnInit() {
|
|
45
|
+
this.loadFiles();
|
|
46
|
+
}
|
|
47
|
+
async loadFiles() {
|
|
48
|
+
this.isLoading.set(true);
|
|
49
|
+
try {
|
|
50
|
+
const response = await firstValueFrom(this.fileService.getAll('', {
|
|
51
|
+
pagination: {
|
|
52
|
+
currentPage: this.currentPage() - 1, // ApiResourceService uses 0-based pages
|
|
53
|
+
pageSize: this.pageSize(),
|
|
54
|
+
},
|
|
55
|
+
filter: {},
|
|
56
|
+
select: [],
|
|
57
|
+
sort: {},
|
|
58
|
+
}));
|
|
59
|
+
if (response.success) {
|
|
60
|
+
this.files.set(response.data ?? []);
|
|
61
|
+
this.totalRecords.set(response.meta?.total ?? 0);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
this.messageService.add({
|
|
66
|
+
severity: 'error',
|
|
67
|
+
summary: 'Error',
|
|
68
|
+
detail: 'Failed to load files.',
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
this.isLoading.set(false);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
onPageChange(event) {
|
|
76
|
+
this.currentPage.set(event.first / event.rows + 1);
|
|
77
|
+
this.pageSize.set(event.rows);
|
|
78
|
+
this.loadFiles();
|
|
79
|
+
}
|
|
80
|
+
onUpload() {
|
|
81
|
+
this.router.navigate(['/storage/files/new']);
|
|
82
|
+
}
|
|
83
|
+
onEdit(fileId) {
|
|
84
|
+
this.router.navigate(['/storage/files', fileId]);
|
|
85
|
+
}
|
|
86
|
+
async onView(file) {
|
|
87
|
+
this.isViewLoading.set(file.id);
|
|
88
|
+
try {
|
|
89
|
+
const response = await firstValueFrom(this.fileService.getFiles([file.id]));
|
|
90
|
+
let files = [];
|
|
91
|
+
if (response && typeof response === 'object') {
|
|
92
|
+
if ('success' in response &&
|
|
93
|
+
response.success &&
|
|
94
|
+
Array.isArray(response.data)) {
|
|
95
|
+
files = response.data;
|
|
96
|
+
}
|
|
97
|
+
else if (Array.isArray(response)) {
|
|
98
|
+
files = response;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (files.length > 0) {
|
|
102
|
+
const fileUrl = files[0].url;
|
|
103
|
+
if (!fileUrl) {
|
|
104
|
+
this.messageService.add({
|
|
105
|
+
severity: 'error',
|
|
106
|
+
summary: 'Error',
|
|
107
|
+
detail: 'File URL is not available.',
|
|
108
|
+
});
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
window.open(fileUrl, '_blank');
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
this.messageService.add({
|
|
115
|
+
severity: 'error',
|
|
116
|
+
summary: 'Error',
|
|
117
|
+
detail: 'Failed to retrieve file URL.',
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
this.messageService.add({
|
|
123
|
+
severity: 'error',
|
|
124
|
+
summary: 'Error',
|
|
125
|
+
detail: 'Failed to get file URL.',
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
this.isViewLoading.set(null);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Soft delete - Move file to trash (recommended)
|
|
134
|
+
* File can be recovered later
|
|
135
|
+
*/
|
|
136
|
+
onSoftDelete(file) {
|
|
137
|
+
this.confirmationService.confirm({
|
|
138
|
+
message: `Move "${file.name}" to trash? You can recover it later.`,
|
|
139
|
+
header: 'Move to Trash',
|
|
140
|
+
icon: 'pi pi-trash',
|
|
141
|
+
acceptLabel: 'Move to Trash',
|
|
142
|
+
acceptButtonStyleClass: 'p-button-warning',
|
|
143
|
+
accept: async () => {
|
|
144
|
+
try {
|
|
145
|
+
await this.fileService.deleteAsync({ id: file.id, type: 'delete' });
|
|
146
|
+
this.messageService.add({
|
|
147
|
+
severity: 'success',
|
|
148
|
+
summary: 'Success',
|
|
149
|
+
detail: 'File moved to trash successfully.',
|
|
150
|
+
});
|
|
151
|
+
this.loadFiles();
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
this.messageService.add({
|
|
155
|
+
severity: 'error',
|
|
156
|
+
summary: 'Error',
|
|
157
|
+
detail: 'Failed to move file to trash.',
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Permanent delete - Cannot be recovered
|
|
165
|
+
* Also deletes the actual file from storage
|
|
166
|
+
*/
|
|
167
|
+
onPermanentDelete(file) {
|
|
168
|
+
this.confirmationService.confirm({
|
|
169
|
+
message: `Permanently delete "${file.name}"? This action CANNOT be undone and will delete the file from storage.`,
|
|
170
|
+
header: 'Permanent Delete',
|
|
171
|
+
icon: 'pi pi-exclamation-triangle',
|
|
172
|
+
acceptLabel: 'Delete Permanently',
|
|
173
|
+
rejectLabel: 'Cancel',
|
|
174
|
+
acceptButtonStyleClass: 'p-button-danger',
|
|
175
|
+
accept: async () => {
|
|
176
|
+
try {
|
|
177
|
+
await this.fileService.deleteAsync({
|
|
178
|
+
id: file.id,
|
|
179
|
+
type: 'permanent',
|
|
180
|
+
});
|
|
181
|
+
this.messageService.add({
|
|
182
|
+
severity: 'success',
|
|
183
|
+
summary: 'Success',
|
|
184
|
+
detail: 'File permanently deleted.',
|
|
185
|
+
});
|
|
186
|
+
this.loadFiles();
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
this.messageService.add({
|
|
190
|
+
severity: 'error',
|
|
191
|
+
summary: 'Error',
|
|
192
|
+
detail: 'Failed to permanently delete file.',
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
getFileIcon(contentType) {
|
|
199
|
+
if (contentType.startsWith('image/'))
|
|
200
|
+
return 'pi pi-image';
|
|
201
|
+
if (contentType.startsWith('video/'))
|
|
202
|
+
return 'pi pi-video';
|
|
203
|
+
if (contentType.startsWith('audio/'))
|
|
204
|
+
return 'pi pi-volume-up';
|
|
205
|
+
if (contentType.includes('pdf'))
|
|
206
|
+
return 'pi pi-file-pdf';
|
|
207
|
+
if (contentType.includes('word') || contentType.includes('document'))
|
|
208
|
+
return 'pi pi-file-word';
|
|
209
|
+
if (contentType.includes('excel') || contentType.includes('spreadsheet'))
|
|
210
|
+
return 'pi pi-file-excel';
|
|
211
|
+
return 'pi pi-file';
|
|
212
|
+
}
|
|
213
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FileManagerListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
214
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: FileManagerListComponent, isStandalone: true, selector: "lib-file-manager-list", ngImport: i0, template: `
|
|
215
|
+
<div class="card">
|
|
216
|
+
<div class="flex justify-between items-center mb-4">
|
|
217
|
+
<div>
|
|
218
|
+
<h3 class="text-xl font-semibold">File Manager</h3>
|
|
219
|
+
@if (showCompanyInfo()) {
|
|
220
|
+
<p class="text-sm text-gray-600 mt-1">
|
|
221
|
+
Company: {{ currentCompanyName() }}
|
|
222
|
+
</p>
|
|
223
|
+
}
|
|
224
|
+
</div>
|
|
225
|
+
<p-button
|
|
226
|
+
label="Upload File"
|
|
227
|
+
icon="pi pi-upload"
|
|
228
|
+
(onClick)="onUpload()"
|
|
229
|
+
/>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
<p-table
|
|
233
|
+
[value]="files()"
|
|
234
|
+
[loading]="isLoading()"
|
|
235
|
+
[paginator]="true"
|
|
236
|
+
[rows]="pageSize()"
|
|
237
|
+
[totalRecords]="totalRecords()"
|
|
238
|
+
[lazy]="true"
|
|
239
|
+
(onLazyLoad)="onPageChange($event)"
|
|
240
|
+
styleClass="p-datatable-sm"
|
|
241
|
+
>
|
|
242
|
+
<ng-template #header>
|
|
243
|
+
<tr>
|
|
244
|
+
<th>Name</th>
|
|
245
|
+
<th>Type</th>
|
|
246
|
+
<th>Size</th>
|
|
247
|
+
<th>Private</th>
|
|
248
|
+
<th>Created</th>
|
|
249
|
+
<th>Actions</th>
|
|
250
|
+
</tr>
|
|
251
|
+
</ng-template>
|
|
252
|
+
<ng-template #body let-file>
|
|
253
|
+
<tr>
|
|
254
|
+
<td>
|
|
255
|
+
<i [class]="getFileIcon(file.contentType)" class="mr-2"></i>
|
|
256
|
+
{{ file.name }}
|
|
257
|
+
</td>
|
|
258
|
+
<td>{{ file.contentType }}</td>
|
|
259
|
+
<td>{{ file.size }} KB</td>
|
|
260
|
+
<td>
|
|
261
|
+
@if (file.isPrivate) {
|
|
262
|
+
<p-tag value="Private" severity="warn" />
|
|
263
|
+
} @else {
|
|
264
|
+
<p-tag value="Public" severity="success" />
|
|
265
|
+
}
|
|
266
|
+
</td>
|
|
267
|
+
<td>{{ file.createdAt | date: 'short' }}</td>
|
|
268
|
+
<td>
|
|
269
|
+
<p-button
|
|
270
|
+
icon="pi pi-eye"
|
|
271
|
+
[text]="true"
|
|
272
|
+
severity="secondary"
|
|
273
|
+
size="small"
|
|
274
|
+
[loading]="isViewLoading() === file.id"
|
|
275
|
+
[disabled]="isViewLoading() === file.id"
|
|
276
|
+
(onClick)="onView(file)"
|
|
277
|
+
pTooltip="View file"
|
|
278
|
+
tooltipPosition="top"
|
|
279
|
+
/>
|
|
280
|
+
<p-button
|
|
281
|
+
icon="pi pi-pencil"
|
|
282
|
+
[text]="true"
|
|
283
|
+
severity="secondary"
|
|
284
|
+
size="small"
|
|
285
|
+
(onClick)="onEdit(file.id)"
|
|
286
|
+
pTooltip="Edit file metadata"
|
|
287
|
+
tooltipPosition="top"
|
|
288
|
+
/>
|
|
289
|
+
<p-button
|
|
290
|
+
icon="pi pi-trash"
|
|
291
|
+
[text]="true"
|
|
292
|
+
severity="warn"
|
|
293
|
+
size="small"
|
|
294
|
+
(onClick)="onSoftDelete(file)"
|
|
295
|
+
pTooltip="Move to trash (recoverable)"
|
|
296
|
+
tooltipPosition="top"
|
|
297
|
+
/>
|
|
298
|
+
<p-button
|
|
299
|
+
icon="pi pi-times-circle"
|
|
300
|
+
[text]="true"
|
|
301
|
+
severity="danger"
|
|
302
|
+
size="small"
|
|
303
|
+
(onClick)="onPermanentDelete(file)"
|
|
304
|
+
pTooltip="Delete permanently (cannot be recovered)"
|
|
305
|
+
tooltipPosition="top"
|
|
306
|
+
/>
|
|
307
|
+
</td>
|
|
308
|
+
</tr>
|
|
309
|
+
</ng-template>
|
|
310
|
+
<ng-template #emptymessage>
|
|
311
|
+
<tr>
|
|
312
|
+
<td colspan="6" class="text-center py-4">
|
|
313
|
+
@if (showCompanyInfo()) {
|
|
314
|
+
No files found in current company
|
|
315
|
+
} @else {
|
|
316
|
+
No files found
|
|
317
|
+
}
|
|
318
|
+
</td>
|
|
319
|
+
</tr>
|
|
320
|
+
</ng-template>
|
|
321
|
+
</p-table>
|
|
322
|
+
</div>
|
|
323
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i1.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { 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: "directive", type: i3$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: i4.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "pipe", type: i5.DatePipe, name: "date" }] });
|
|
324
|
+
}
|
|
325
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FileManagerListComponent, decorators: [{
|
|
326
|
+
type: Component,
|
|
327
|
+
args: [{
|
|
328
|
+
selector: 'lib-file-manager-list',
|
|
329
|
+
standalone: true,
|
|
330
|
+
imports: [AngularModule, PrimeModule],
|
|
331
|
+
template: `
|
|
332
|
+
<div class="card">
|
|
333
|
+
<div class="flex justify-between items-center mb-4">
|
|
334
|
+
<div>
|
|
335
|
+
<h3 class="text-xl font-semibold">File Manager</h3>
|
|
336
|
+
@if (showCompanyInfo()) {
|
|
337
|
+
<p class="text-sm text-gray-600 mt-1">
|
|
338
|
+
Company: {{ currentCompanyName() }}
|
|
339
|
+
</p>
|
|
340
|
+
}
|
|
341
|
+
</div>
|
|
342
|
+
<p-button
|
|
343
|
+
label="Upload File"
|
|
344
|
+
icon="pi pi-upload"
|
|
345
|
+
(onClick)="onUpload()"
|
|
346
|
+
/>
|
|
347
|
+
</div>
|
|
348
|
+
|
|
349
|
+
<p-table
|
|
350
|
+
[value]="files()"
|
|
351
|
+
[loading]="isLoading()"
|
|
352
|
+
[paginator]="true"
|
|
353
|
+
[rows]="pageSize()"
|
|
354
|
+
[totalRecords]="totalRecords()"
|
|
355
|
+
[lazy]="true"
|
|
356
|
+
(onLazyLoad)="onPageChange($event)"
|
|
357
|
+
styleClass="p-datatable-sm"
|
|
358
|
+
>
|
|
359
|
+
<ng-template #header>
|
|
360
|
+
<tr>
|
|
361
|
+
<th>Name</th>
|
|
362
|
+
<th>Type</th>
|
|
363
|
+
<th>Size</th>
|
|
364
|
+
<th>Private</th>
|
|
365
|
+
<th>Created</th>
|
|
366
|
+
<th>Actions</th>
|
|
367
|
+
</tr>
|
|
368
|
+
</ng-template>
|
|
369
|
+
<ng-template #body let-file>
|
|
370
|
+
<tr>
|
|
371
|
+
<td>
|
|
372
|
+
<i [class]="getFileIcon(file.contentType)" class="mr-2"></i>
|
|
373
|
+
{{ file.name }}
|
|
374
|
+
</td>
|
|
375
|
+
<td>{{ file.contentType }}</td>
|
|
376
|
+
<td>{{ file.size }} KB</td>
|
|
377
|
+
<td>
|
|
378
|
+
@if (file.isPrivate) {
|
|
379
|
+
<p-tag value="Private" severity="warn" />
|
|
380
|
+
} @else {
|
|
381
|
+
<p-tag value="Public" severity="success" />
|
|
382
|
+
}
|
|
383
|
+
</td>
|
|
384
|
+
<td>{{ file.createdAt | date: 'short' }}</td>
|
|
385
|
+
<td>
|
|
386
|
+
<p-button
|
|
387
|
+
icon="pi pi-eye"
|
|
388
|
+
[text]="true"
|
|
389
|
+
severity="secondary"
|
|
390
|
+
size="small"
|
|
391
|
+
[loading]="isViewLoading() === file.id"
|
|
392
|
+
[disabled]="isViewLoading() === file.id"
|
|
393
|
+
(onClick)="onView(file)"
|
|
394
|
+
pTooltip="View file"
|
|
395
|
+
tooltipPosition="top"
|
|
396
|
+
/>
|
|
397
|
+
<p-button
|
|
398
|
+
icon="pi pi-pencil"
|
|
399
|
+
[text]="true"
|
|
400
|
+
severity="secondary"
|
|
401
|
+
size="small"
|
|
402
|
+
(onClick)="onEdit(file.id)"
|
|
403
|
+
pTooltip="Edit file metadata"
|
|
404
|
+
tooltipPosition="top"
|
|
405
|
+
/>
|
|
406
|
+
<p-button
|
|
407
|
+
icon="pi pi-trash"
|
|
408
|
+
[text]="true"
|
|
409
|
+
severity="warn"
|
|
410
|
+
size="small"
|
|
411
|
+
(onClick)="onSoftDelete(file)"
|
|
412
|
+
pTooltip="Move to trash (recoverable)"
|
|
413
|
+
tooltipPosition="top"
|
|
414
|
+
/>
|
|
415
|
+
<p-button
|
|
416
|
+
icon="pi pi-times-circle"
|
|
417
|
+
[text]="true"
|
|
418
|
+
severity="danger"
|
|
419
|
+
size="small"
|
|
420
|
+
(onClick)="onPermanentDelete(file)"
|
|
421
|
+
pTooltip="Delete permanently (cannot be recovered)"
|
|
422
|
+
tooltipPosition="top"
|
|
423
|
+
/>
|
|
424
|
+
</td>
|
|
425
|
+
</tr>
|
|
426
|
+
</ng-template>
|
|
427
|
+
<ng-template #emptymessage>
|
|
428
|
+
<tr>
|
|
429
|
+
<td colspan="6" class="text-center py-4">
|
|
430
|
+
@if (showCompanyInfo()) {
|
|
431
|
+
No files found in current company
|
|
432
|
+
} @else {
|
|
433
|
+
No files found
|
|
434
|
+
}
|
|
435
|
+
</td>
|
|
436
|
+
</tr>
|
|
437
|
+
</ng-template>
|
|
438
|
+
</p-table>
|
|
439
|
+
</div>
|
|
440
|
+
`,
|
|
441
|
+
}]
|
|
442
|
+
}] });
|
|
443
|
+
|
|
444
|
+
export { FileManagerListComponent };
|
|
445
|
+
//# sourceMappingURL=flusys-ng-storage-file-manager-list.component-C3C7Y8-n.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flusys-ng-storage-file-manager-list.component-C3C7Y8-n.mjs","sources":["../../../projects/ng-storage/pages/file-manager/file-manager-list.component.ts"],"sourcesContent":["import { Component, computed, inject, OnInit, signal } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { APP_CONFIG, DEFAULT_APP_NAME } from '@flusys/ng-core';\nimport { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';\nimport { AngularModule, PrimeModule } from '@flusys/ng-shared';\nimport { ConfirmationService, MessageService } from 'primeng/api';\nimport { firstValueFrom } from 'rxjs';\nimport { IFileManager } from '../../interfaces/file-manager.interface';\nimport { FileManagerApiService } from '../../services/file-manager-api.service';\n\n/**\n * File Manager List Component\n * Displays all files with company filter (if enabled)\n * Shows current company name in header\n */\n@Component({\n selector: 'lib-file-manager-list',\n standalone: true,\n imports: [AngularModule, PrimeModule],\n template: `\n <div class=\"card\">\n <div class=\"flex justify-between items-center mb-4\">\n <div>\n <h3 class=\"text-xl font-semibold\">File Manager</h3>\n @if (showCompanyInfo()) {\n <p class=\"text-sm text-gray-600 mt-1\">\n Company: {{ currentCompanyName() }}\n </p>\n }\n </div>\n <p-button\n label=\"Upload File\"\n icon=\"pi pi-upload\"\n (onClick)=\"onUpload()\"\n />\n </div>\n\n <p-table\n [value]=\"files()\"\n [loading]=\"isLoading()\"\n [paginator]=\"true\"\n [rows]=\"pageSize()\"\n [totalRecords]=\"totalRecords()\"\n [lazy]=\"true\"\n (onLazyLoad)=\"onPageChange($event)\"\n styleClass=\"p-datatable-sm\"\n >\n <ng-template #header>\n <tr>\n <th>Name</th>\n <th>Type</th>\n <th>Size</th>\n <th>Private</th>\n <th>Created</th>\n <th>Actions</th>\n </tr>\n </ng-template>\n <ng-template #body let-file>\n <tr>\n <td>\n <i [class]=\"getFileIcon(file.contentType)\" class=\"mr-2\"></i>\n {{ file.name }}\n </td>\n <td>{{ file.contentType }}</td>\n <td>{{ file.size }} KB</td>\n <td>\n @if (file.isPrivate) {\n <p-tag value=\"Private\" severity=\"warn\" />\n } @else {\n <p-tag value=\"Public\" severity=\"success\" />\n }\n </td>\n <td>{{ file.createdAt | date: 'short' }}</td>\n <td>\n <p-button\n icon=\"pi pi-eye\"\n [text]=\"true\"\n severity=\"secondary\"\n size=\"small\"\n [loading]=\"isViewLoading() === file.id\"\n [disabled]=\"isViewLoading() === file.id\"\n (onClick)=\"onView(file)\"\n pTooltip=\"View file\"\n tooltipPosition=\"top\"\n />\n <p-button\n icon=\"pi pi-pencil\"\n [text]=\"true\"\n severity=\"secondary\"\n size=\"small\"\n (onClick)=\"onEdit(file.id)\"\n pTooltip=\"Edit file metadata\"\n tooltipPosition=\"top\"\n />\n <p-button\n icon=\"pi pi-trash\"\n [text]=\"true\"\n severity=\"warn\"\n size=\"small\"\n (onClick)=\"onSoftDelete(file)\"\n pTooltip=\"Move to trash (recoverable)\"\n tooltipPosition=\"top\"\n />\n <p-button\n icon=\"pi pi-times-circle\"\n [text]=\"true\"\n severity=\"danger\"\n size=\"small\"\n (onClick)=\"onPermanentDelete(file)\"\n pTooltip=\"Delete permanently (cannot be recovered)\"\n tooltipPosition=\"top\"\n />\n </td>\n </tr>\n </ng-template>\n <ng-template #emptymessage>\n <tr>\n <td colspan=\"6\" class=\"text-center py-4\">\n @if (showCompanyInfo()) {\n No files found in current company\n } @else {\n No files found\n }\n </td>\n </tr>\n </ng-template>\n </p-table>\n </div>\n `,\n})\nexport class FileManagerListComponent implements OnInit {\n private readonly router = inject(Router);\n private readonly messageService = inject(MessageService);\n private readonly confirmationService = inject(ConfirmationService);\n private readonly fileService = inject(FileManagerApiService);\n private readonly appConfig = inject(APP_CONFIG);\n private readonly companyContext = inject(LAYOUT_AUTH_STATE);\n\n /** Loading state */\n readonly isLoading = signal(false);\n\n /** Loading state for file viewing */\n readonly isViewLoading = signal<string | null>(null);\n\n /** Files list */\n readonly files = signal<IFileManager[]>([]);\n\n /** Total records for pagination */\n readonly totalRecords = signal(0);\n\n /** Page size */\n readonly pageSize = signal(50);\n\n /** Current page */\n readonly currentPage = signal(1);\n\n /** Show company info if company feature enabled */\n readonly showCompanyInfo = computed(\n () => this.appConfig.enableCompanyFeature,\n );\n\n /** Current company name */\n readonly currentCompanyName = computed(\n () => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME,\n );\n\n ngOnInit(): void {\n this.loadFiles();\n }\n\n async loadFiles(): Promise<void> {\n this.isLoading.set(true);\n try {\n const response = await firstValueFrom(\n this.fileService.getAll('', {\n pagination: {\n currentPage: this.currentPage() - 1, // ApiResourceService uses 0-based pages\n pageSize: this.pageSize(),\n },\n filter: {},\n select: [],\n sort: {},\n }),\n );\n\n if (response.success) {\n this.files.set(response.data ?? []);\n this.totalRecords.set(response.meta?.total ?? 0);\n }\n } catch (error) {\n this.messageService.add({\n severity: 'error',\n summary: 'Error',\n detail: 'Failed to load files.',\n });\n } finally {\n this.isLoading.set(false);\n }\n }\n\n onPageChange(event: any): void {\n this.currentPage.set(event.first / event.rows + 1);\n this.pageSize.set(event.rows);\n this.loadFiles();\n }\n\n onUpload(): void {\n this.router.navigate(['/storage/files/new']);\n }\n\n onEdit(fileId: string): void {\n this.router.navigate(['/storage/files', fileId]);\n }\n\n async onView(file: IFileManager): Promise<void> {\n this.isViewLoading.set(file.id);\n try {\n const response = await firstValueFrom(\n this.fileService.getFiles([file.id]),\n );\n\n let files: any[] = [];\n\n if (response && typeof response === 'object') {\n if (\n 'success' in response &&\n response.success &&\n Array.isArray(response.data)\n ) {\n files = response.data;\n } else if (Array.isArray(response)) {\n files = response as any;\n }\n }\n\n if (files.length > 0) {\n const fileUrl = files[0].url;\n\n if (!fileUrl) {\n this.messageService.add({\n severity: 'error',\n summary: 'Error',\n detail: 'File URL is not available.',\n });\n return;\n }\n\n window.open(fileUrl, '_blank');\n } else {\n this.messageService.add({\n severity: 'error',\n summary: 'Error',\n detail: 'Failed to retrieve file URL.',\n });\n }\n } catch {\n this.messageService.add({\n severity: 'error',\n summary: 'Error',\n detail: 'Failed to get file URL.',\n });\n } finally {\n this.isViewLoading.set(null);\n }\n }\n\n /**\n * Soft delete - Move file to trash (recommended)\n * File can be recovered later\n */\n onSoftDelete(file: IFileManager): void {\n this.confirmationService.confirm({\n message: `Move \"${file.name}\" to trash? You can recover it later.`,\n header: 'Move to Trash',\n icon: 'pi pi-trash',\n acceptLabel: 'Move to Trash',\n acceptButtonStyleClass: 'p-button-warning',\n accept: async () => {\n try {\n await this.fileService.deleteAsync({ id: file.id, type: 'delete' });\n this.messageService.add({\n severity: 'success',\n summary: 'Success',\n detail: 'File moved to trash successfully.',\n });\n this.loadFiles();\n } catch (error) {\n this.messageService.add({\n severity: 'error',\n summary: 'Error',\n detail: 'Failed to move file to trash.',\n });\n }\n },\n });\n }\n\n /**\n * Permanent delete - Cannot be recovered\n * Also deletes the actual file from storage\n */\n onPermanentDelete(file: IFileManager): void {\n this.confirmationService.confirm({\n message: `Permanently delete \"${file.name}\"? This action CANNOT be undone and will delete the file from storage.`,\n header: 'Permanent Delete',\n icon: 'pi pi-exclamation-triangle',\n acceptLabel: 'Delete Permanently',\n rejectLabel: 'Cancel',\n acceptButtonStyleClass: 'p-button-danger',\n accept: async () => {\n try {\n await this.fileService.deleteAsync({\n id: file.id,\n type: 'permanent',\n });\n this.messageService.add({\n severity: 'success',\n summary: 'Success',\n detail: 'File permanently deleted.',\n });\n this.loadFiles();\n } catch (error) {\n this.messageService.add({\n severity: 'error',\n summary: 'Error',\n detail: 'Failed to permanently delete file.',\n });\n }\n },\n });\n }\n\n getFileIcon(contentType: string): string {\n if (contentType.startsWith('image/')) return 'pi pi-image';\n if (contentType.startsWith('video/')) return 'pi pi-video';\n if (contentType.startsWith('audio/')) return 'pi pi-volume-up';\n if (contentType.includes('pdf')) return 'pi pi-file-pdf';\n if (contentType.includes('word') || contentType.includes('document'))\n return 'pi pi-file-word';\n if (contentType.includes('excel') || contentType.includes('spreadsheet'))\n return 'pi pi-file-excel';\n return 'pi pi-file';\n }\n}\n"],"names":["i2","i3"],"mappings":"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"}
|