@flusys/ng-storage 1.0.0-rc → 1.0.0

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 +1 -1
  2. package/fesm2022/{flusys-ng-storage-file-manager-form.component-CMS_ddbs.mjs → flusys-ng-storage-file-manager-form.component-Bv8ER8J0.mjs} +1 -2
  3. package/fesm2022/flusys-ng-storage-file-manager-form.component-Bv8ER8J0.mjs.map +1 -0
  4. package/fesm2022/{flusys-ng-storage-file-manager-list.component-DDOFAPRD.mjs → flusys-ng-storage-file-manager-list.component-McM1zSlE.mjs} +1 -2
  5. package/fesm2022/flusys-ng-storage-file-manager-list.component-McM1zSlE.mjs.map +1 -0
  6. package/fesm2022/{flusys-ng-storage-folder-form.component-ClYATZkh.mjs → flusys-ng-storage-folder-form.component-fRFiMFev.mjs} +2 -4
  7. package/fesm2022/flusys-ng-storage-folder-form.component-fRFiMFev.mjs.map +1 -0
  8. package/fesm2022/{flusys-ng-storage-folder-list.component-CzONANHt.mjs → flusys-ng-storage-folder-list.component-C4xHI8SO.mjs} +2 -8
  9. package/fesm2022/flusys-ng-storage-folder-list.component-C4xHI8SO.mjs.map +1 -0
  10. package/fesm2022/{flusys-ng-storage-storage-config-form.component-CODLLCtg.mjs → flusys-ng-storage-storage-config-form.component-DEMexjmg.mjs} +67 -90
  11. package/fesm2022/flusys-ng-storage-storage-config-form.component-DEMexjmg.mjs.map +1 -0
  12. package/fesm2022/{flusys-ng-storage-storage-config-list.component-BbBx0iSL.mjs → flusys-ng-storage-storage-config-list.component-iJez-rII.mjs} +11 -37
  13. package/fesm2022/flusys-ng-storage-storage-config-list.component-iJez-rII.mjs.map +1 -0
  14. package/fesm2022/flusys-ng-storage.mjs +96 -220
  15. package/fesm2022/flusys-ng-storage.mjs.map +1 -1
  16. package/package.json +10 -10
  17. package/types/flusys-ng-storage.d.ts +32 -188
  18. package/fesm2022/flusys-ng-storage-file-manager-form.component-CMS_ddbs.mjs.map +0 -1
  19. package/fesm2022/flusys-ng-storage-file-manager-list.component-DDOFAPRD.mjs.map +0 -1
  20. package/fesm2022/flusys-ng-storage-folder-form.component-ClYATZkh.mjs.map +0 -1
  21. package/fesm2022/flusys-ng-storage-folder-list.component-CzONANHt.mjs.map +0 -1
  22. package/fesm2022/flusys-ng-storage-storage-config-form.component-CODLLCtg.mjs.map +0 -1
  23. package/fesm2022/flusys-ng-storage-storage-config-list.component-BbBx0iSL.mjs.map +0 -1
@@ -9,20 +9,12 @@ import * as i3 from 'primeng/tag';
9
9
  import * as i4 from 'primeng/tooltip';
10
10
  import * as i5 from '@angular/common';
11
11
 
12
- /**
13
- * Storage Config List Component
14
- * Displays all storage configurations with company filter (if enabled)
15
- */
16
12
  class StorageConfigListComponent extends BaseStorageListPage {
17
- // Permission constants for template
18
13
  STORAGE_CONFIG_PERMISSIONS = STORAGE_CONFIG_PERMISSIONS;
19
14
  configService = inject(StorageConfigApiService);
20
- /** Configs list alias for template compatibility */
21
- configs = this.items;
22
- /** Utility functions exposed to template */
23
- getProviderIcon = (storage) => getProviderIcon(storage);
24
- getProviderLabel = (storage) => getProviderLabel(storage);
25
- getProviderSeverity = (storage) => getProviderSeverity(storage);
15
+ getProviderIcon = getProviderIcon;
16
+ getProviderLabel = getProviderLabel;
17
+ getProviderSeverity = getProviderSeverity;
26
18
  getResourceRoute() {
27
19
  return '/storage/configs';
28
20
  }
@@ -32,12 +24,7 @@ class StorageConfigListComponent extends BaseStorageListPage {
32
24
  async loadData() {
33
25
  this.isLoading.set(true);
34
26
  try {
35
- const response = await firstValueFrom(this.configService.getAll('', {
36
- pagination: this.getPaginationParams(),
37
- filter: {},
38
- select: [],
39
- sort: {},
40
- }));
27
+ const response = await firstValueFrom(this.configService.getAll('', { pagination: this.getPaginationParams() }));
41
28
  if (response.success) {
42
29
  this.items.set(response.data ?? []);
43
30
  this.total.set(response.meta?.total ?? 0);
@@ -64,9 +51,7 @@ class StorageConfigListComponent extends BaseStorageListPage {
64
51
  <div>
65
52
  <h3 class="text-lg sm:text-xl font-semibold">Storage Configurations</h3>
66
53
  @if (showCompanyInfo()) {
67
- <p class="text-sm text-muted-color mt-1">
68
- Company: {{ currentCompanyName() }}
69
- </p>
54
+ <p class="text-sm text-muted-color mt-1">Company: {{ currentCompanyName() }}</p>
70
55
  }
71
56
  </div>
72
57
  <p-button *hasPermission="STORAGE_CONFIG_PERMISSIONS.CREATE" label="New Configuration" icon="pi pi-plus" (onClick)="onCreate()" styleClass="w-full sm:w-auto" />
@@ -74,7 +59,7 @@ class StorageConfigListComponent extends BaseStorageListPage {
74
59
 
75
60
  <div class="overflow-x-auto">
76
61
  <p-table
77
- [value]="configs()"
62
+ [value]="items()"
78
63
  [loading]="isLoading()"
79
64
  [paginator]="totalRecords() > 0"
80
65
  [rows]="pageSize()"
@@ -116,11 +101,7 @@ class StorageConfigListComponent extends BaseStorageListPage {
116
101
  <ng-template #emptymessage>
117
102
  <tr>
118
103
  <td colspan="4" class="text-center py-4 text-muted-color">
119
- @if (showCompanyInfo()) {
120
- No storage configurations found in current company
121
- } @else {
122
- No storage configurations found
123
- }
104
+ {{ showCompanyInfo() ? 'No storage configurations found in current company' : 'No storage configurations found' }}
124
105
  </td>
125
106
  </tr>
126
107
  </ng-template>
@@ -133,7 +114,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
133
114
  type: Component,
134
115
  args: [{
135
116
  selector: 'lib-storage-config-list',
136
- standalone: true,
137
117
  changeDetection: ChangeDetectionStrategy.OnPush,
138
118
  imports: [AngularModule, PrimeModule, HasPermissionDirective],
139
119
  template: `
@@ -142,9 +122,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
142
122
  <div>
143
123
  <h3 class="text-lg sm:text-xl font-semibold">Storage Configurations</h3>
144
124
  @if (showCompanyInfo()) {
145
- <p class="text-sm text-muted-color mt-1">
146
- Company: {{ currentCompanyName() }}
147
- </p>
125
+ <p class="text-sm text-muted-color mt-1">Company: {{ currentCompanyName() }}</p>
148
126
  }
149
127
  </div>
150
128
  <p-button *hasPermission="STORAGE_CONFIG_PERMISSIONS.CREATE" label="New Configuration" icon="pi pi-plus" (onClick)="onCreate()" styleClass="w-full sm:w-auto" />
@@ -152,7 +130,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
152
130
 
153
131
  <div class="overflow-x-auto">
154
132
  <p-table
155
- [value]="configs()"
133
+ [value]="items()"
156
134
  [loading]="isLoading()"
157
135
  [paginator]="totalRecords() > 0"
158
136
  [rows]="pageSize()"
@@ -194,11 +172,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
194
172
  <ng-template #emptymessage>
195
173
  <tr>
196
174
  <td colspan="4" class="text-center py-4 text-muted-color">
197
- @if (showCompanyInfo()) {
198
- No storage configurations found in current company
199
- } @else {
200
- No storage configurations found
201
- }
175
+ {{ showCompanyInfo() ? 'No storage configurations found in current company' : 'No storage configurations found' }}
202
176
  </td>
203
177
  </tr>
204
178
  </ng-template>
@@ -210,4 +184,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
210
184
  }] });
211
185
 
212
186
  export { StorageConfigListComponent };
213
- //# sourceMappingURL=flusys-ng-storage-storage-config-list.component-BbBx0iSL.mjs.map
187
+ //# sourceMappingURL=flusys-ng-storage-storage-config-list.component-iJez-rII.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flusys-ng-storage-storage-config-list.component-iJez-rII.mjs","sources":["../../../projects/ng-storage/pages/storage-config/storage-config-list.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject } from '@angular/core';\nimport { AngularModule, HasPermissionDirective, PrimeModule, STORAGE_CONFIG_PERMISSIONS } from '@flusys/ng-shared';\nimport { firstValueFrom } from 'rxjs';\nimport { IStorageConfig } from '../../interfaces/storage-config.interface';\nimport { StorageConfigApiService } from '../../services/storage-config-api.service';\nimport { BaseStorageListPage } from '../../utils/base-storage-page';\nimport { getProviderIcon, getProviderLabel, getProviderSeverity } from '../../utils/storage-utils';\n\n@Component({\n selector: 'lib-storage-config-list',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [AngularModule, PrimeModule, HasPermissionDirective],\n template: `\n <div class=\"card\">\n <div class=\"flex flex-col sm:flex-row sm:justify-between sm:items-center gap-3 mb-4\">\n <div>\n <h3 class=\"text-lg sm:text-xl font-semibold\">Storage Configurations</h3>\n @if (showCompanyInfo()) {\n <p class=\"text-sm text-muted-color mt-1\">Company: {{ currentCompanyName() }}</p>\n }\n </div>\n <p-button *hasPermission=\"STORAGE_CONFIG_PERMISSIONS.CREATE\" label=\"New Configuration\" icon=\"pi pi-plus\" (onClick)=\"onCreate()\" styleClass=\"w-full sm:w-auto\" />\n </div>\n\n <div class=\"overflow-x-auto\">\n <p-table\n [value]=\"items()\"\n [loading]=\"isLoading()\"\n [paginator]=\"totalRecords() > 0\"\n [rows]=\"pageSize()\"\n [totalRecords]=\"totalRecords()\"\n [lazy]=\"true\"\n (onLazyLoad)=\"onPageChange($event)\"\n [rowsPerPageOptions]=\"[10, 25, 50]\"\n [scrollable]=\"true\"\n styleClass=\"p-datatable-sm\"\n >\n <ng-template #header>\n <tr>\n <th>Name</th>\n <th>Provider</th>\n <th class=\"hidden sm:table-cell\">Created</th>\n <th class=\"w-[120px]\">Actions</th>\n </tr>\n </ng-template>\n <ng-template #body let-config>\n <tr>\n <td>\n <div class=\"flex items-center gap-2\">\n <i [class]=\"getProviderIcon(config.storage)\"></i>\n <span class=\"truncate max-w-32 sm:max-w-none\">{{ config.name }}</span>\n </div>\n </td>\n <td>\n <p-tag [value]=\"getProviderLabel(config.storage)\" [severity]=\"getProviderSeverity(config.storage)\" />\n </td>\n <td class=\"hidden sm:table-cell\">{{ config.createdAt | date: 'short' }}</td>\n <td>\n <div class=\"flex gap-1\">\n <p-button *hasPermission=\"STORAGE_CONFIG_PERMISSIONS.UPDATE\" icon=\"pi pi-pencil\" [text]=\"true\" severity=\"secondary\" size=\"small\" (onClick)=\"onEdit(config.id)\" pTooltip=\"Edit\" tooltipPosition=\"top\" />\n <p-button *hasPermission=\"STORAGE_CONFIG_PERMISSIONS.DELETE\" icon=\"pi pi-trash\" [text]=\"true\" severity=\"danger\" size=\"small\" (onClick)=\"onDeleteConfig(config)\" pTooltip=\"Delete\" tooltipPosition=\"top\" />\n </div>\n </td>\n </tr>\n </ng-template>\n <ng-template #emptymessage>\n <tr>\n <td colspan=\"4\" class=\"text-center py-4 text-muted-color\">\n {{ showCompanyInfo() ? 'No storage configurations found in current company' : 'No storage configurations found' }}\n </td>\n </tr>\n </ng-template>\n </p-table>\n </div>\n </div>\n `,\n})\nexport class StorageConfigListComponent extends BaseStorageListPage<IStorageConfig> {\n readonly STORAGE_CONFIG_PERMISSIONS = STORAGE_CONFIG_PERMISSIONS;\n\n private readonly configService = inject(StorageConfigApiService);\n\n readonly getProviderIcon = getProviderIcon;\n readonly getProviderLabel = getProviderLabel;\n readonly getProviderSeverity = getProviderSeverity;\n\n getResourceRoute(): string {\n return '/storage/configs';\n }\n\n override getDeleteConfirmMessage(config: IStorageConfig): string {\n return `Are you sure you want to delete \"${config.name}\"?`;\n }\n\n async loadData(): Promise<void> {\n this.isLoading.set(true);\n try {\n const response = await firstValueFrom(\n this.configService.getAll('', { pagination: this.getPaginationParams() }),\n );\n if (response.success) {\n this.items.set(response.data ?? []);\n this.total.set(response.meta?.total ?? 0);\n }\n } catch {\n this.showError('Failed to load storage configurations.');\n } finally {\n this.isLoading.set(false);\n }\n }\n\n onDeleteConfig(config: IStorageConfig): void {\n this.onDeleteAsync(\n config,\n (c) => c.id,\n (id) => this.configService.deleteAsync({ id, type: 'delete' }),\n {\n header: 'Delete Configuration',\n successMessage: 'Storage configuration deleted successfully.',\n errorMessage: 'Failed to delete storage configuration.',\n },\n );\n }\n}\n"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import { inject, signal, computed, Directive, Injectable, ChangeDetectionStrateg
3
3
  import { Router, RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
4
4
  import { APP_CONFIG, DEFAULT_APP_NAME, BaseApiService, getDefaultStorageConfigId } from '@flusys/ng-core';
5
5
  import { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';
6
- import { BaseListPage, ApiResourceService, PROFILE_UPLOAD_PROVIDER } from '@flusys/ng-shared';
6
+ import { BaseListPage, ApiResourceService, PROFILE_UPLOAD_PROVIDER, permissionGuard, FILE_PERMISSIONS, FOLDER_PERMISSIONS, STORAGE_CONFIG_PERMISSIONS, PermissionValidatorService } from '@flusys/ng-shared';
7
7
  import { MessageService } from 'primeng/api';
8
8
  import { map } from 'rxjs/operators';
9
9
  import { firstValueFrom, switchMap } from 'rxjs';
@@ -118,32 +118,20 @@ const STORAGE_PROVIDERS = [
118
118
  { label: 'Local Storage', value: FileLocationEnum.LOCAL },
119
119
  ];
120
120
 
121
- /**
122
- * Base Storage Page
123
- *
124
- * Provides common signals, computed values, and utilities for storage pages.
125
- * Used by form components that need company context and message utilities.
126
- */
127
121
  class BaseStoragePage {
128
122
  router = inject(Router);
129
123
  messageService = inject(MessageService);
130
124
  appConfig = inject(APP_CONFIG);
131
125
  companyContext = inject(LAYOUT_AUTH_STATE);
132
- /** Loading state */
133
126
  isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
134
- /** Show company info if company feature enabled */
135
127
  showCompanyInfo = computed(() => this.appConfig.enableCompanyFeature, ...(ngDevMode ? [{ debugName: "showCompanyInfo" }] : []));
136
- /** Current company name */
137
128
  currentCompanyName = computed(() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
138
- /** Show success message */
139
129
  showSuccess(detail, summary = 'Success') {
140
130
  this.messageService.add({ severity: 'success', summary, detail });
141
131
  }
142
- /** Show error message */
143
132
  showError(detail, summary = 'Error') {
144
133
  this.messageService.add({ severity: 'error', summary, detail });
145
134
  }
146
- /** Navigate to a route */
147
135
  navigateTo(path) {
148
136
  this.router.navigate(path);
149
137
  }
@@ -153,35 +141,11 @@ class BaseStoragePage {
153
141
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseStoragePage, decorators: [{
154
142
  type: Directive
155
143
  }] });
156
- /**
157
- * Base Storage List Page
158
- *
159
- * Extends the shared BaseListPage with storage-specific functionality:
160
- * - Company context integration via LAYOUT_AUTH_STATE
161
- * - Current company name computed signal
162
- * - Storage-specific defaults (pageSize: 50)
163
- *
164
- * Usage:
165
- * ```typescript
166
- * @Component({ ... })
167
- * export class FolderListComponent extends BaseStorageListPage<IFolder> {
168
- * getResourceRoute(): string { return '/storage/folders'; }
169
- * getDeleteConfirmMessage(folder: IFolder): string { return `Delete "${folder.name}"?`; }
170
- * async loadData(): Promise<void> { ... }
171
- * }
172
- * ```
173
- */
174
144
  class BaseStorageListPage extends BaseListPage {
175
145
  companyContext = inject(LAYOUT_AUTH_STATE);
176
- /** Override default page size for storage pages */
177
146
  pageSize = signal(50, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
178
- /** Total records alias for backward compatibility */
179
147
  totalRecords = this.total;
180
- /** Current company name */
181
148
  currentCompanyName = computed(() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
182
- /**
183
- * Default implementation - subclasses should override if needed
184
- */
185
149
  getDeleteConfirmMessage(item) {
186
150
  const name = item.name ?? 'this item';
187
151
  return `Are you sure you want to delete "${name}"?`;
@@ -193,65 +157,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
193
157
  type: Directive
194
158
  }] });
195
159
 
196
- /**
197
- * File Manager API Service
198
- * Handles file metadata CRUD operations
199
- * Endpoint: POST /storage/file-manager/*
200
- *
201
- * Note: Company feature enabled - all queries automatically scoped to user's company
202
- */
203
160
  class FileManagerApiService extends ApiResourceService {
204
161
  constructor() {
205
- const http = inject(HttpClient);
206
- super('storage/file-manager', http);
162
+ super('storage/file-manager', inject(HttpClient));
207
163
  }
208
- /**
209
- * Get multiple files with valid URLs
210
- * POST /storage/file-manager/get-files
211
- * Regenerates presigned URLs if expired
212
- */
213
164
  getFiles(fileIds) {
214
165
  const requests = fileIds.map((id) => ({ id }));
215
166
  return this.http.post(`${this.baseUrl}/get-files`, requests);
216
167
  }
217
- /**
218
- * Get files by folder
219
- * POST /storage/file-manager/get-all
220
- * Filtered by folder ID (UUID) and company (if enabled)
221
- */
222
- getFilesByFolder(folderId, page = 1, limit = 50) {
223
- return this.getAll('', {
224
- filter: folderId ? { folderId } : {},
225
- pagination: { currentPage: page - 1, pageSize: limit },
226
- select: [],
227
- sort: {},
228
- });
229
- }
230
- /**
231
- * Get private files (requires permission)
232
- * POST /storage/file-manager/get-all
233
- * Filtered by isPrivate and user permission 'storage.file.viewPrivate'
234
- */
235
- getPrivateFiles(page = 1, limit = 50) {
236
- return this.getAll('', {
237
- filter: { isPrivate: true },
238
- pagination: { currentPage: page - 1, pageSize: limit },
239
- select: [],
240
- sort: {},
241
- });
242
- }
243
- /**
244
- * Create a new file record
245
- * POST /storage/file-manager/insert
246
- * Uses ICreateFileManagerDto which has different required fields than update
247
- */
248
168
  createFile(dto) {
249
169
  return this.http.post(`${this.baseUrl}/insert`, dto);
250
170
  }
251
- /**
252
- * Create a new file record (async/await version)
253
- * POST /storage/file-manager/insert
254
- */
255
171
  async createFileAsync(dto) {
256
172
  return firstValueFrom(this.createFile(dto));
257
173
  }
@@ -260,77 +176,77 @@ class FileManagerApiService extends ApiResourceService {
260
176
  }
261
177
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileManagerApiService, decorators: [{
262
178
  type: Injectable,
263
- args: [{
264
- providedIn: 'root',
265
- }]
179
+ args: [{ providedIn: 'root' }]
266
180
  }], ctorParameters: () => [] });
267
181
 
268
182
  /**
269
- * Upload Service
270
- * Handles file upload/delete operations
183
+ * Upload Service - File upload/delete operations
271
184
  * Endpoint: POST /storage/upload/*
272
- *
273
- * Features:
274
- * - Company-scoped uploads (if company feature enabled)
275
- * - Image compression
276
- * - Multiple storage providers
277
- * - File validation (size, type)
278
- * - Default storage config from environment (services.storage.defaultStorageConfigId)
279
185
  */
280
186
  class UploadService extends BaseApiService {
281
187
  fileManagerApi = inject(FileManagerApiService);
282
188
  constructor() {
283
189
  super('storage');
284
190
  }
285
- /**
286
- * Get default storage config ID from app config
287
- * Used when no storageConfigId is provided in upload options
288
- */
191
+ /** Get default storage config ID from app config */
289
192
  getDefaultConfigId() {
290
193
  return getDefaultStorageConfigId(this.appConfig);
291
194
  }
292
- /**
293
- * Upload single file
294
- * POST /storage/upload/single-file?storageConfigId=x&folderPath=y
295
- *
296
- * @param file - File to upload
297
- * @param options - Upload options (storage config, compression, etc.)
298
- */
195
+ /** Upload single file - POST /storage/upload/single-file */
299
196
  uploadSingleFile(file, options) {
300
197
  const formData = new FormData();
301
198
  formData.append('file', file);
302
- const queryParams = this.buildQueryParams(options);
303
- const url = queryParams
304
- ? `${this.getUrl('upload/single-file')}?${queryParams}`
305
- : this.getUrl('upload/single-file');
306
- return this.http.post(url, formData);
199
+ return this.http.post(this.buildUploadUrl('single-file', options), formData);
307
200
  }
308
- /**
309
- * Upload multiple files (max 50)
310
- * POST /storage/upload/multiple-file?storageConfigId=x&folderPath=y
311
- *
312
- * @param files - Array of files to upload
313
- * @param options - Upload options
314
- */
201
+ /** Upload multiple files (max 50) - POST /storage/upload/multiple-file */
315
202
  uploadMultipleFiles(files, options) {
316
203
  const formData = new FormData();
317
204
  files.forEach((file) => formData.append('files', file));
318
- const queryParams = this.buildQueryParams(options);
319
- const url = queryParams
320
- ? `${this.getUrl('upload/multiple-file')}?${queryParams}`
321
- : this.getUrl('upload/multiple-file');
322
- return this.http.post(url, formData);
205
+ return this.http.post(this.buildUploadUrl('multiple-file', options), formData);
206
+ }
207
+ /** Delete single file - POST /storage/upload/delete-single-file */
208
+ deleteSingleFile(key, storageConfigId) {
209
+ return this.http.post(this.getUrl('upload/delete-single-file'), this.buildDeleteBody({ key }, storageConfigId));
210
+ }
211
+ /** Delete multiple files - POST /storage/upload/delete-multiple-file */
212
+ deleteMultipleFiles(keys, storageConfigId) {
213
+ return this.http.post(this.getUrl('upload/delete-multiple-file'), this.buildDeleteBody({ keys }, storageConfigId));
214
+ }
215
+ /** Get file download URL - GET /storage/upload/file/:filePath */
216
+ getFileUrl(key) {
217
+ return this.getUrl(`upload/file/${key}`);
218
+ }
219
+ /** Upload file and register in file manager - returns file manager ID (UUID) */
220
+ uploadAndRegister(file, options) {
221
+ return this.uploadSingleFile(file, options).pipe(switchMap((response) => {
222
+ if (!response.success || !response.data) {
223
+ throw new Error(response.message || 'Upload failed');
224
+ }
225
+ return this.fileManagerApi.createFile({
226
+ name: response.data.name,
227
+ key: response.data.key,
228
+ size: String(response.data.size),
229
+ contentType: response.data.contentType,
230
+ isPrivate: options?.isPrivate ?? false,
231
+ folderId: options?.folderId,
232
+ storageConfigId: this.resolveConfigId(options?.storageConfigId),
233
+ });
234
+ }));
235
+ }
236
+ // ─── Private Helpers ───────────────────────────────────────────────────────
237
+ resolveConfigId(storageConfigId) {
238
+ return storageConfigId ?? this.getDefaultConfigId();
239
+ }
240
+ buildUploadUrl(endpoint, options) {
241
+ const params = this.buildQueryParams(options);
242
+ const baseUrl = this.getUrl(`upload/${endpoint}`);
243
+ return params ? `${baseUrl}?${params}` : baseUrl;
323
244
  }
324
- /**
325
- * Build query string from upload options
326
- * Uses default storageConfigId from app config if not provided in options
327
- */
328
245
  buildQueryParams(options) {
329
246
  const params = new URLSearchParams();
330
- // Use provided storageConfigId or fall back to default from config
331
- const storageConfigId = options?.storageConfigId ?? this.getDefaultConfigId();
332
- if (storageConfigId)
333
- params.append('storageConfigId', storageConfigId);
247
+ const configId = this.resolveConfigId(options?.storageConfigId);
248
+ if (configId)
249
+ params.append('storageConfigId', configId);
334
250
  if (options?.folderPath)
335
251
  params.append('folderPath', options.folderPath);
336
252
  if (options?.maxWidth)
@@ -345,73 +261,16 @@ class UploadService extends BaseApiService {
345
261
  params.append('compress', options.compress.toString());
346
262
  return params.toString();
347
263
  }
348
- /**
349
- * Delete single file
350
- * POST /storage/upload/delete-single-file
351
- *
352
- * @param key - File key/path to delete
353
- * @param storageConfigId - Optional storage config ID (uses default from config if not provided)
354
- */
355
- deleteSingleFile(key, storageConfigId) {
356
- const configId = storageConfigId ?? this.getDefaultConfigId();
357
- return this.http.post(this.getUrl('upload/delete-single-file'), { key, ...(configId && { storageConfigId: configId }) });
358
- }
359
- /**
360
- * Delete multiple files
361
- * POST /storage/upload/delete-multiple-file
362
- *
363
- * @param keys - Array of file keys/paths to delete
364
- * @param storageConfigId - Optional storage config ID (uses default from config if not provided)
365
- */
366
- deleteMultipleFiles(keys, storageConfigId) {
367
- const configId = storageConfigId ?? this.getDefaultConfigId();
368
- return this.http.post(this.getUrl('upload/delete-multiple-file'), { keys, ...(configId && { storageConfigId: configId }) });
369
- }
370
- /**
371
- * Get file download URL
372
- * GET /storage/upload/file/:filePath
373
- *
374
- * @param key - File key/path
375
- * @returns URL for direct file access
376
- */
377
- getFileUrl(key) {
378
- return this.getUrl(`upload/file/${key}`);
379
- }
380
- /**
381
- * Upload file and register in file manager
382
- * Returns file manager ID (UUID) for storing in references like profilePictureId
383
- *
384
- * @param file - File to upload
385
- * @param options - Upload options (storage config, compression, etc.)
386
- * @returns Observable with file manager record (id is the UUID to store)
387
- */
388
- uploadAndRegister(file, options) {
389
- return this.uploadSingleFile(file, options).pipe(switchMap((uploadResponse) => {
390
- if (!uploadResponse.success || !uploadResponse.data) {
391
- throw new Error(uploadResponse.message || 'Upload failed');
392
- }
393
- const uploadedFile = uploadResponse.data;
394
- const storageConfigId = options?.storageConfigId ?? this.getDefaultConfigId();
395
- // Create file manager record using proper DTO
396
- return this.fileManagerApi.createFile({
397
- name: uploadedFile.name,
398
- key: uploadedFile.key,
399
- size: String(uploadedFile.size),
400
- contentType: uploadedFile.contentType,
401
- isPrivate: options?.isPrivate ?? false,
402
- folderId: options?.folderId,
403
- storageConfigId,
404
- });
405
- }));
264
+ buildDeleteBody(data, storageConfigId) {
265
+ const configId = this.resolveConfigId(storageConfigId);
266
+ return { ...data, ...(configId && { storageConfigId: configId }) };
406
267
  }
407
268
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
408
269
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, providedIn: 'root' });
409
270
  }
410
271
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, decorators: [{
411
272
  type: Injectable,
412
- args: [{
413
- providedIn: 'root',
414
- }]
273
+ args: [{ providedIn: 'root' }]
415
274
  }], ctorParameters: () => [] });
416
275
 
417
276
  /**
@@ -532,9 +391,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
532
391
 
533
392
  /**
534
393
  * Storage Module Routes
535
- * All storage management routes: configs, files, folders
536
- *
537
- * All routes are protected by permission guards to prevent direct URL access.
394
+ * All routes protected by permission guards
538
395
  */
539
396
  const STORAGE_ROUTES = [
540
397
  {
@@ -544,63 +401,68 @@ const STORAGE_ROUTES = [
544
401
  // File Manager Routes
545
402
  {
546
403
  path: 'files',
404
+ canActivate: [permissionGuard(FILE_PERMISSIONS.READ)],
547
405
  children: [
548
406
  {
549
407
  path: '',
550
- loadComponent: () => import('./flusys-ng-storage-file-manager-list.component-DDOFAPRD.mjs').then((m) => m.FileManagerListComponent),
408
+ loadComponent: () => import('./flusys-ng-storage-file-manager-list.component-McM1zSlE.mjs').then((m) => m.FileManagerListComponent),
551
409
  },
552
410
  {
553
411
  path: 'new',
554
- loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-CMS_ddbs.mjs').then((m) => m.FileManagerFormComponent),
412
+ canActivate: [permissionGuard(FILE_PERMISSIONS.CREATE)],
413
+ loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-Bv8ER8J0.mjs').then((m) => m.FileManagerFormComponent),
555
414
  },
556
415
  {
557
416
  path: ':id',
558
- loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-CMS_ddbs.mjs').then((m) => m.FileManagerFormComponent),
417
+ canActivate: [permissionGuard(FILE_PERMISSIONS.UPDATE)],
418
+ loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-Bv8ER8J0.mjs').then((m) => m.FileManagerFormComponent),
559
419
  },
560
420
  ],
561
421
  },
562
422
  // Folder Routes
563
423
  {
564
424
  path: 'folders',
425
+ canActivate: [permissionGuard(FOLDER_PERMISSIONS.READ)],
565
426
  children: [
566
427
  {
567
428
  path: '',
568
- loadComponent: () => import('./flusys-ng-storage-folder-list.component-CzONANHt.mjs').then((m) => m.FolderListComponent),
429
+ loadComponent: () => import('./flusys-ng-storage-folder-list.component-C4xHI8SO.mjs').then((m) => m.FolderListComponent),
569
430
  },
570
431
  {
571
432
  path: 'new',
572
- loadComponent: () => import('./flusys-ng-storage-folder-form.component-ClYATZkh.mjs').then((m) => m.FolderFormComponent),
433
+ canActivate: [permissionGuard(FOLDER_PERMISSIONS.CREATE)],
434
+ loadComponent: () => import('./flusys-ng-storage-folder-form.component-fRFiMFev.mjs').then((m) => m.FolderFormComponent),
573
435
  },
574
436
  {
575
437
  path: ':id',
576
- loadComponent: () => import('./flusys-ng-storage-folder-form.component-ClYATZkh.mjs').then((m) => m.FolderFormComponent),
438
+ canActivate: [permissionGuard(FOLDER_PERMISSIONS.UPDATE)],
439
+ loadComponent: () => import('./flusys-ng-storage-folder-form.component-fRFiMFev.mjs').then((m) => m.FolderFormComponent),
577
440
  },
578
441
  ],
579
442
  },
580
443
  // Storage Config Routes
581
444
  {
582
445
  path: 'configs',
446
+ canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.READ)],
583
447
  children: [
584
448
  {
585
449
  path: '',
586
- loadComponent: () => import('./flusys-ng-storage-storage-config-list.component-BbBx0iSL.mjs').then((m) => m.StorageConfigListComponent),
450
+ loadComponent: () => import('./flusys-ng-storage-storage-config-list.component-iJez-rII.mjs').then((m) => m.StorageConfigListComponent),
587
451
  },
588
452
  {
589
453
  path: 'new',
590
- loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-CODLLCtg.mjs').then((m) => m.StorageConfigFormComponent),
454
+ canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.CREATE)],
455
+ loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-DEMexjmg.mjs').then((m) => m.StorageConfigFormComponent),
591
456
  },
592
457
  {
593
458
  path: ':id',
594
- loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-CODLLCtg.mjs').then((m) => m.StorageConfigFormComponent),
459
+ canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.UPDATE)],
460
+ loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-DEMexjmg.mjs').then((m) => m.StorageConfigFormComponent),
595
461
  },
596
462
  ],
597
463
  },
598
- // Default redirect to files
599
- {
600
- path: '',
601
- redirectTo: 'files',
602
- pathMatch: 'full',
603
- },
464
+ // Default redirect
465
+ { path: '', redirectTo: 'files', pathMatch: 'full' },
604
466
  ],
605
467
  },
606
468
  ];
@@ -610,39 +472,53 @@ const STORAGE_ROUTES = [
610
472
  *
611
473
  * Main container for Storage module with tab navigation
612
474
  * Routes: Files | Folders | Configurations
475
+ * Tabs are filtered based on user permissions
613
476
  */
614
477
  class StorageContainerComponent {
615
- /** All available tabs */
616
- tabs = [
478
+ permissionValidator = inject(PermissionValidatorService);
479
+ /** All available tabs with required permissions */
480
+ allTabs = [
617
481
  {
618
482
  id: 'files',
619
483
  label: 'Files',
620
484
  icon: 'pi pi-file',
621
485
  route: 'files',
486
+ permission: FILE_PERMISSIONS.READ,
622
487
  },
623
488
  {
624
489
  id: 'folders',
625
490
  label: 'Folders',
626
491
  icon: 'pi pi-folder',
627
492
  route: 'folders',
493
+ permission: FOLDER_PERMISSIONS.READ,
628
494
  },
629
495
  {
630
496
  id: 'configs',
631
497
  label: 'Configurations',
632
498
  icon: 'pi pi-cog',
633
499
  route: 'configs',
500
+ permission: STORAGE_CONFIG_PERMISSIONS.READ,
634
501
  },
635
502
  ];
503
+ /** Visible tabs based on user permissions */
504
+ visibleTabs = computed(() => this.allTabs.filter((tab) => this.permissionValidator.hasPermission(tab.permission)), ...(ngDevMode ? [{ debugName: "visibleTabs" }] : []));
505
+ /** Dynamic subtitle based on visible tabs */
506
+ subtitle = computed(() => {
507
+ const tabs = this.visibleTabs();
508
+ if (tabs.length === 0)
509
+ return 'No permissions available';
510
+ return `Manage ${tabs.map((t) => t.label.toLowerCase()).join(', ')}`;
511
+ }, ...(ngDevMode ? [{ debugName: "subtitle" }] : []));
636
512
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
637
513
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: StorageContainerComponent, isStandalone: true, selector: "lib-storage-container", ngImport: i0, template: `
638
514
  <div class="p-2 sm:p-4">
639
515
  <div class="mb-4">
640
516
  <h1 class="text-xl sm:text-2xl font-bold m-0">Storage</h1>
641
- <p class="text-muted-color mt-1 text-sm sm:text-base">Manage files, folders, and storage configurations</p>
517
+ <p class="text-muted-color mt-1 text-sm sm:text-base">{{ subtitle() }}</p>
642
518
  </div>
643
519
 
644
520
  <div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
645
- @for (tab of tabs; track tab.id) {
521
+ @for (tab of visibleTabs(); track tab.id) {
646
522
  <a
647
523
  [routerLink]="tab.route"
648
524
  routerLinkActive="tab-active"
@@ -660,15 +536,15 @@ class StorageContainerComponent {
660
536
  }
661
537
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageContainerComponent, decorators: [{
662
538
  type: Component,
663
- args: [{ selector: 'lib-storage-container', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive], template: `
539
+ args: [{ selector: 'lib-storage-container', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive], template: `
664
540
  <div class="p-2 sm:p-4">
665
541
  <div class="mb-4">
666
542
  <h1 class="text-xl sm:text-2xl font-bold m-0">Storage</h1>
667
- <p class="text-muted-color mt-1 text-sm sm:text-base">Manage files, folders, and storage configurations</p>
543
+ <p class="text-muted-color mt-1 text-sm sm:text-base">{{ subtitle() }}</p>
668
544
  </div>
669
545
 
670
546
  <div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
671
- @for (tab of tabs; track tab.id) {
547
+ @for (tab of visibleTabs(); track tab.id) {
672
548
  <a
673
549
  [routerLink]="tab.route"
674
550
  routerLinkActive="tab-active"