@flusys/ng-storage 1.0.0-rc → 1.0.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.
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-CXVB9G-f.mjs} +4 -5
  3. package/fesm2022/flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs.map +1 -0
  4. package/fesm2022/{flusys-ng-storage-file-manager-list.component-DDOFAPRD.mjs → flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs} +4 -5
  5. package/fesm2022/flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs.map +1 -0
  6. package/fesm2022/{flusys-ng-storage-folder-form.component-ClYATZkh.mjs → flusys-ng-storage-folder-form.component-BTQGEqNu.mjs} +5 -7
  7. package/fesm2022/flusys-ng-storage-folder-form.component-BTQGEqNu.mjs.map +1 -0
  8. package/fesm2022/{flusys-ng-storage-folder-list.component-CzONANHt.mjs → flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs} +5 -11
  9. package/fesm2022/flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs.map +1 -0
  10. package/fesm2022/{flusys-ng-storage-storage-config-form.component-CODLLCtg.mjs → flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs} +70 -93
  11. package/fesm2022/flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs.map +1 -0
  12. package/fesm2022/{flusys-ng-storage-storage-config-list.component-BbBx0iSL.mjs → flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs} +14 -40
  13. package/fesm2022/flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs.map +1 -0
  14. package/fesm2022/flusys-ng-storage.mjs +122 -248
  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
@@ -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,219 +118,135 @@ 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
  }
150
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseStoragePage, deps: [], target: i0.ɵɵFactoryTarget.Directive });
151
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.3", type: BaseStoragePage, isStandalone: true, ngImport: i0 });
138
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseStoragePage, deps: [], target: i0.ɵɵFactoryTarget.Directive });
139
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.5", type: BaseStoragePage, isStandalone: true, ngImport: i0 });
152
140
  }
153
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseStoragePage, decorators: [{
141
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", 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}"?`;
188
152
  }
189
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseStorageListPage, deps: null, target: i0.ɵɵFactoryTarget.Directive });
190
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.3", type: BaseStorageListPage, isStandalone: true, usesInheritance: true, ngImport: i0 });
153
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseStorageListPage, deps: null, target: i0.ɵɵFactoryTarget.Directive });
154
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.5", type: BaseStorageListPage, isStandalone: true, usesInheritance: true, ngImport: i0 });
191
155
  }
192
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseStorageListPage, decorators: [{
156
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseStorageListPage, decorators: [{
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('file-manager', inject(HttpClient), 'storage');
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
  }
258
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileManagerApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
259
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileManagerApiService, providedIn: 'root' });
174
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileManagerApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
175
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileManagerApiService, providedIn: 'root' });
260
176
  }
261
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileManagerApiService, decorators: [{
177
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", 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 }) });
264
+ buildDeleteBody(data, storageConfigId) {
265
+ const configId = this.resolveConfigId(storageConfigId);
266
+ return { ...data, ...(configId && { storageConfigId: configId }) };
369
267
  }
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
- }));
406
- }
407
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
408
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, providedIn: 'root' });
268
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UploadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
269
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UploadService, providedIn: 'root' });
409
270
  }
410
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UploadService, decorators: [{
271
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", 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
  /**
@@ -454,10 +313,10 @@ class ProfileUploadProviderAdapter {
454
313
  : undefined,
455
314
  })));
456
315
  }
457
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ProfileUploadProviderAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
458
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ProfileUploadProviderAdapter });
316
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ProfileUploadProviderAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
317
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ProfileUploadProviderAdapter });
459
318
  }
460
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ProfileUploadProviderAdapter, decorators: [{
319
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ProfileUploadProviderAdapter, decorators: [{
461
320
  type: Injectable
462
321
  }] });
463
322
 
@@ -495,13 +354,12 @@ function provideStorageProviders() {
495
354
  */
496
355
  class FolderApiService extends ApiResourceService {
497
356
  constructor() {
498
- const http = inject(HttpClient);
499
- super('storage/folder', http);
357
+ super('folder', inject(HttpClient), 'storage');
500
358
  }
501
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FolderApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
502
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FolderApiService, providedIn: 'root' });
359
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FolderApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
360
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FolderApiService, providedIn: 'root' });
503
361
  }
504
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FolderApiService, decorators: [{
362
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FolderApiService, decorators: [{
505
363
  type: Injectable,
506
364
  args: [{
507
365
  providedIn: 'root',
@@ -517,13 +375,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
517
375
  */
518
376
  class StorageConfigApiService extends ApiResourceService {
519
377
  constructor() {
520
- const http = inject(HttpClient);
521
- super('storage/storage-config', http);
378
+ super('storage-config', inject(HttpClient), 'storage');
522
379
  }
523
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageConfigApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
524
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageConfigApiService, providedIn: 'root' });
380
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageConfigApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
381
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageConfigApiService, providedIn: 'root' });
525
382
  }
526
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageConfigApiService, decorators: [{
383
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageConfigApiService, decorators: [{
527
384
  type: Injectable,
528
385
  args: [{
529
386
  providedIn: 'root',
@@ -532,9 +389,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
532
389
 
533
390
  /**
534
391
  * 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.
392
+ * All routes protected by permission guards
538
393
  */
539
394
  const STORAGE_ROUTES = [
540
395
  {
@@ -544,63 +399,68 @@ const STORAGE_ROUTES = [
544
399
  // File Manager Routes
545
400
  {
546
401
  path: 'files',
402
+ canActivate: [permissionGuard(FILE_PERMISSIONS.READ)],
547
403
  children: [
548
404
  {
549
405
  path: '',
550
- loadComponent: () => import('./flusys-ng-storage-file-manager-list.component-DDOFAPRD.mjs').then((m) => m.FileManagerListComponent),
406
+ loadComponent: () => import('./flusys-ng-storage-file-manager-list.component-0O_b4EjN.mjs').then((m) => m.FileManagerListComponent),
551
407
  },
552
408
  {
553
409
  path: 'new',
554
- loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-CMS_ddbs.mjs').then((m) => m.FileManagerFormComponent),
410
+ canActivate: [permissionGuard(FILE_PERMISSIONS.CREATE)],
411
+ loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs').then((m) => m.FileManagerFormComponent),
555
412
  },
556
413
  {
557
414
  path: ':id',
558
- loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-CMS_ddbs.mjs').then((m) => m.FileManagerFormComponent),
415
+ canActivate: [permissionGuard(FILE_PERMISSIONS.UPDATE)],
416
+ loadComponent: () => import('./flusys-ng-storage-file-manager-form.component-CXVB9G-f.mjs').then((m) => m.FileManagerFormComponent),
559
417
  },
560
418
  ],
561
419
  },
562
420
  // Folder Routes
563
421
  {
564
422
  path: 'folders',
423
+ canActivate: [permissionGuard(FOLDER_PERMISSIONS.READ)],
565
424
  children: [
566
425
  {
567
426
  path: '',
568
- loadComponent: () => import('./flusys-ng-storage-folder-list.component-CzONANHt.mjs').then((m) => m.FolderListComponent),
427
+ loadComponent: () => import('./flusys-ng-storage-folder-list.component-B8GoIh6Z.mjs').then((m) => m.FolderListComponent),
569
428
  },
570
429
  {
571
430
  path: 'new',
572
- loadComponent: () => import('./flusys-ng-storage-folder-form.component-ClYATZkh.mjs').then((m) => m.FolderFormComponent),
431
+ canActivate: [permissionGuard(FOLDER_PERMISSIONS.CREATE)],
432
+ loadComponent: () => import('./flusys-ng-storage-folder-form.component-BTQGEqNu.mjs').then((m) => m.FolderFormComponent),
573
433
  },
574
434
  {
575
435
  path: ':id',
576
- loadComponent: () => import('./flusys-ng-storage-folder-form.component-ClYATZkh.mjs').then((m) => m.FolderFormComponent),
436
+ canActivate: [permissionGuard(FOLDER_PERMISSIONS.UPDATE)],
437
+ loadComponent: () => import('./flusys-ng-storage-folder-form.component-BTQGEqNu.mjs').then((m) => m.FolderFormComponent),
577
438
  },
578
439
  ],
579
440
  },
580
441
  // Storage Config Routes
581
442
  {
582
443
  path: 'configs',
444
+ canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.READ)],
583
445
  children: [
584
446
  {
585
447
  path: '',
586
- loadComponent: () => import('./flusys-ng-storage-storage-config-list.component-BbBx0iSL.mjs').then((m) => m.StorageConfigListComponent),
448
+ loadComponent: () => import('./flusys-ng-storage-storage-config-list.component-YXzz3Tvo.mjs').then((m) => m.StorageConfigListComponent),
587
449
  },
588
450
  {
589
451
  path: 'new',
590
- loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-CODLLCtg.mjs').then((m) => m.StorageConfigFormComponent),
452
+ canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.CREATE)],
453
+ loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs').then((m) => m.StorageConfigFormComponent),
591
454
  },
592
455
  {
593
456
  path: ':id',
594
- loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-CODLLCtg.mjs').then((m) => m.StorageConfigFormComponent),
457
+ canActivate: [permissionGuard(STORAGE_CONFIG_PERMISSIONS.UPDATE)],
458
+ loadComponent: () => import('./flusys-ng-storage-storage-config-form.component-CorNWrmF.mjs').then((m) => m.StorageConfigFormComponent),
595
459
  },
596
460
  ],
597
461
  },
598
- // Default redirect to files
599
- {
600
- path: '',
601
- redirectTo: 'files',
602
- pathMatch: 'full',
603
- },
462
+ // Default redirect
463
+ { path: '', redirectTo: 'files', pathMatch: 'full' },
604
464
  ],
605
465
  },
606
466
  ];
@@ -610,39 +470,53 @@ const STORAGE_ROUTES = [
610
470
  *
611
471
  * Main container for Storage module with tab navigation
612
472
  * Routes: Files | Folders | Configurations
473
+ * Tabs are filtered based on user permissions
613
474
  */
614
475
  class StorageContainerComponent {
615
- /** All available tabs */
616
- tabs = [
476
+ permissionValidator = inject(PermissionValidatorService);
477
+ /** All available tabs with required permissions */
478
+ allTabs = [
617
479
  {
618
480
  id: 'files',
619
481
  label: 'Files',
620
482
  icon: 'pi pi-file',
621
483
  route: 'files',
484
+ permission: FILE_PERMISSIONS.READ,
622
485
  },
623
486
  {
624
487
  id: 'folders',
625
488
  label: 'Folders',
626
489
  icon: 'pi pi-folder',
627
490
  route: 'folders',
491
+ permission: FOLDER_PERMISSIONS.READ,
628
492
  },
629
493
  {
630
494
  id: 'configs',
631
495
  label: 'Configurations',
632
496
  icon: 'pi pi-cog',
633
497
  route: 'configs',
498
+ permission: STORAGE_CONFIG_PERMISSIONS.READ,
634
499
  },
635
500
  ];
636
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
637
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: StorageContainerComponent, isStandalone: true, selector: "lib-storage-container", ngImport: i0, template: `
501
+ /** Visible tabs based on user permissions */
502
+ visibleTabs = computed(() => this.allTabs.filter((tab) => this.permissionValidator.hasPermission(tab.permission)), ...(ngDevMode ? [{ debugName: "visibleTabs" }] : []));
503
+ /** Dynamic subtitle based on visible tabs */
504
+ subtitle = computed(() => {
505
+ const tabs = this.visibleTabs();
506
+ if (tabs.length === 0)
507
+ return 'No permissions available';
508
+ return `Manage ${tabs.map((t) => t.label.toLowerCase()).join(', ')}`;
509
+ }, ...(ngDevMode ? [{ debugName: "subtitle" }] : []));
510
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
511
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: StorageContainerComponent, isStandalone: true, selector: "lib-storage-container", ngImport: i0, template: `
638
512
  <div class="p-2 sm:p-4">
639
513
  <div class="mb-4">
640
514
  <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>
515
+ <p class="text-muted-color mt-1 text-sm sm:text-base">{{ subtitle() }}</p>
642
516
  </div>
643
517
 
644
518
  <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) {
519
+ @for (tab of visibleTabs(); track tab.id) {
646
520
  <a
647
521
  [routerLink]="tab.route"
648
522
  routerLinkActive="tab-active"
@@ -658,17 +532,17 @@ class StorageContainerComponent {
658
532
  </div>
659
533
  `, isInline: true, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
660
534
  }
661
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StorageContainerComponent, decorators: [{
535
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: StorageContainerComponent, decorators: [{
662
536
  type: Component,
663
- args: [{ selector: 'lib-storage-container', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive], template: `
537
+ args: [{ selector: 'lib-storage-container', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive], template: `
664
538
  <div class="p-2 sm:p-4">
665
539
  <div class="mb-4">
666
540
  <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>
541
+ <p class="text-muted-color mt-1 text-sm sm:text-base">{{ subtitle() }}</p>
668
542
  </div>
669
543
 
670
544
  <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) {
545
+ @for (tab of visibleTabs(); track tab.id) {
672
546
  <a
673
547
  [routerLink]="tab.route"
674
548
  routerLinkActive="tab-active"