@flusys/ng-shared 3.0.0 → 3.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.
- package/README.md +282 -344
- package/fesm2022/flusys-ng-shared.mjs +113 -232
- package/fesm2022/flusys-ng-shared.mjs.map +1 -1
- package/package.json +2 -2
- package/types/flusys-ng-shared.d.ts +57 -182
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
## Package Information
|
|
10
10
|
|
|
11
11
|
- **Package:** `@flusys/ng-shared`
|
|
12
|
-
- **Version:** 3.0.
|
|
12
|
+
- **Version:** 3.0.1
|
|
13
13
|
- **Dependencies:** ng-core
|
|
14
14
|
- **Dependents:** ng-layout, ng-auth, ng-iam, ng-storage, flusysng
|
|
15
15
|
- **Build Command:** `npm run build:ng-shared`
|
|
@@ -38,13 +38,13 @@ interface IBaseEntity {
|
|
|
38
38
|
|
|
39
39
|
**Entity Mixins:**
|
|
40
40
|
|
|
41
|
-
| Interface
|
|
42
|
-
|
|
|
43
|
-
| `ISoftDeletable
|
|
44
|
-
| `ITimestampable
|
|
45
|
-
| `IActivatable`
|
|
46
|
-
| `IOrderable`
|
|
47
|
-
| `IMetadata`
|
|
41
|
+
| Interface | Fields | Purpose |
|
|
42
|
+
| ---------------- | ------------------------------------ | -------------------- |
|
|
43
|
+
| `ISoftDeletable` | `deletedAt?: Date \| null` | Soft delete support |
|
|
44
|
+
| `ITimestampable` | `createdAt, updatedAt` | Timestamp tracking |
|
|
45
|
+
| `IActivatable` | `isActive: boolean` | Active status toggle |
|
|
46
|
+
| `IOrderable` | `serial?: number \| null` | Ordering/sorting |
|
|
47
|
+
| `IMetadata` | `metadata?: Record<string, unknown>` | JSON metadata field |
|
|
48
48
|
|
|
49
49
|
#### ILoggedUserInfo
|
|
50
50
|
|
|
@@ -74,7 +74,9 @@ interface IPagination {
|
|
|
74
74
|
currentPage: number;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
interface ISort {
|
|
77
|
+
interface ISort {
|
|
78
|
+
[key: string]: "ASC" | "DESC";
|
|
79
|
+
}
|
|
78
80
|
|
|
79
81
|
// Filter supports primitives and arrays for multi-value filtering
|
|
80
82
|
interface IFilter {
|
|
@@ -98,10 +100,10 @@ interface IFilterData {
|
|
|
98
100
|
Delete request payload matching backend DeleteDto.
|
|
99
101
|
|
|
100
102
|
```typescript
|
|
101
|
-
type DeleteType =
|
|
103
|
+
type DeleteType = "delete" | "restore" | "permanent";
|
|
102
104
|
|
|
103
105
|
interface IDeleteData {
|
|
104
|
-
id: string | string[];
|
|
106
|
+
id: string | string[]; // Single or batch delete
|
|
105
107
|
type: DeleteType;
|
|
106
108
|
}
|
|
107
109
|
```
|
|
@@ -124,7 +126,6 @@ interface IUserSelectFilter {
|
|
|
124
126
|
page: number;
|
|
125
127
|
pageSize: number;
|
|
126
128
|
search: string;
|
|
127
|
-
[key: string]: unknown;
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
type LoadUsersFn = (filter: IUserSelectFilter) => Observable<IListResponse<IUserBasicInfo>>;
|
|
@@ -151,7 +152,7 @@ interface IFileUploadOptions {
|
|
|
151
152
|
}
|
|
152
153
|
|
|
153
154
|
interface IUploadedFile {
|
|
154
|
-
id?: string;
|
|
155
|
+
id?: string; // File manager ID (UUID) - available when registered
|
|
155
156
|
name: string;
|
|
156
157
|
key: string;
|
|
157
158
|
size: number;
|
|
@@ -164,7 +165,6 @@ interface IFileSelectFilter {
|
|
|
164
165
|
search: string;
|
|
165
166
|
contentTypes?: string[];
|
|
166
167
|
folderId?: string;
|
|
167
|
-
[key: string]: unknown;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
type LoadFilesFn = (filter: IFileSelectFilter) => Observable<IListResponse<IFileBasicInfo>>;
|
|
@@ -175,18 +175,18 @@ type GetFileUrlsFn = (fileIds: string[]) => Observable<ISingleResponse<IFileBasi
|
|
|
175
175
|
**File Type Filters (Constants):**
|
|
176
176
|
|
|
177
177
|
```typescript
|
|
178
|
-
import { FILE_TYPE_FILTERS, getAcceptString, isFileTypeAllowed, getFileIconClass, formatFileSize } from
|
|
179
|
-
|
|
180
|
-
FILE_TYPE_FILTERS.IMAGES
|
|
181
|
-
FILE_TYPE_FILTERS.DOCUMENTS
|
|
182
|
-
FILE_TYPE_FILTERS.VIDEOS
|
|
183
|
-
FILE_TYPE_FILTERS.AUDIO
|
|
184
|
-
FILE_TYPE_FILTERS.ALL
|
|
185
|
-
|
|
186
|
-
getAcceptString([
|
|
187
|
-
isFileTypeAllowed(file, [
|
|
188
|
-
getFileIconClass(
|
|
189
|
-
formatFileSize(1024)
|
|
178
|
+
import { FILE_TYPE_FILTERS, getAcceptString, isFileTypeAllowed, getFileIconClass, formatFileSize } from "@flusys/ng-shared";
|
|
179
|
+
|
|
180
|
+
FILE_TYPE_FILTERS.IMAGES; // ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml']
|
|
181
|
+
FILE_TYPE_FILTERS.DOCUMENTS; // ['application/pdf', 'application/msword', ...]
|
|
182
|
+
FILE_TYPE_FILTERS.VIDEOS; // ['video/mp4', 'video/webm', ...]
|
|
183
|
+
FILE_TYPE_FILTERS.AUDIO; // ['audio/mpeg', 'audio/wav', ...]
|
|
184
|
+
FILE_TYPE_FILTERS.ALL; // [] (allows all)
|
|
185
|
+
|
|
186
|
+
getAcceptString(["image/*"]); // 'image/*'
|
|
187
|
+
isFileTypeAllowed(file, ["image/*"]); // true/false
|
|
188
|
+
getFileIconClass("image/png"); // 'pi pi-image'
|
|
189
|
+
formatFileSize(1024); // '1 KB'
|
|
190
190
|
```
|
|
191
191
|
|
|
192
192
|
### Response Interfaces
|
|
@@ -242,12 +242,12 @@ type ApiResponse<T> = ISingleResponse<T> | IListResponse<T> | IBulkResponse<T> |
|
|
|
242
242
|
|
|
243
243
|
**Metadata types:**
|
|
244
244
|
|
|
245
|
-
| Interface | Fields
|
|
246
|
-
| ------------------ |
|
|
247
|
-
| `IRequestMeta` | `requestId?, timestamp?, responseTime?`
|
|
245
|
+
| Interface | Fields |
|
|
246
|
+
| ------------------ | ----------------------------------------------------- |
|
|
247
|
+
| `IRequestMeta` | `requestId?, timestamp?, responseTime?` |
|
|
248
248
|
| `IPaginationMeta` | `total, page, pageSize, count, hasMore?, totalPages?` |
|
|
249
|
-
| `IBulkMeta` | `count, failed?, total?`
|
|
250
|
-
| `IValidationError` | `field, message, constraint?`
|
|
249
|
+
| `IBulkMeta` | `count, failed?, total?` |
|
|
250
|
+
| `IValidationError` | `field, message, constraint?` |
|
|
251
251
|
|
|
252
252
|
### Auth & Storage Response Types
|
|
253
253
|
|
|
@@ -292,14 +292,14 @@ Discriminated union for building complex permission logic trees.
|
|
|
292
292
|
```typescript
|
|
293
293
|
// Single permission check
|
|
294
294
|
interface IActionNode {
|
|
295
|
-
type:
|
|
295
|
+
type: "action";
|
|
296
296
|
actionId: string;
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
// Group with AND/OR logic
|
|
300
300
|
interface IGroupNode {
|
|
301
|
-
type:
|
|
302
|
-
operator:
|
|
301
|
+
type: "group";
|
|
302
|
+
operator: "AND" | "OR";
|
|
303
303
|
children: ILogicNode[];
|
|
304
304
|
}
|
|
305
305
|
|
|
@@ -374,25 +374,25 @@ Signal-based CRUD service using Angular 21 `resource()` API with **lazy initiali
|
|
|
374
374
|
**ServiceName Type:**
|
|
375
375
|
|
|
376
376
|
```typescript
|
|
377
|
-
type ServiceName =
|
|
377
|
+
type ServiceName = "auth" | "administration" | "iam" | "storage" | "formBuilder" | "email";
|
|
378
378
|
```
|
|
379
379
|
|
|
380
380
|
**Define a service:**
|
|
381
381
|
|
|
382
382
|
```typescript
|
|
383
|
-
import { Injectable } from
|
|
384
|
-
import { HttpClient } from
|
|
385
|
-
import { ApiResourceService } from
|
|
383
|
+
import { Injectable } from "@angular/core";
|
|
384
|
+
import { HttpClient } from "@angular/common/http";
|
|
385
|
+
import { ApiResourceService } from "@flusys/ng-shared";
|
|
386
386
|
|
|
387
|
-
@Injectable({ providedIn:
|
|
387
|
+
@Injectable({ providedIn: "root" })
|
|
388
388
|
export class UserService extends ApiResourceService<UserDto, IUser> {
|
|
389
389
|
constructor(http: HttpClient) {
|
|
390
390
|
// Option 1: Use global apiBaseUrl (default)
|
|
391
|
-
super(
|
|
391
|
+
super("users", http);
|
|
392
392
|
// Base URL: APP_CONFIG.apiBaseUrl + '/users'
|
|
393
393
|
|
|
394
394
|
// Option 2: Use feature-specific service URL (recommended)
|
|
395
|
-
super(
|
|
395
|
+
super("users", http, "administration");
|
|
396
396
|
// Base URL: APP_CONFIG.services.administration.baseUrl + '/users'
|
|
397
397
|
}
|
|
398
398
|
}
|
|
@@ -400,13 +400,14 @@ export class UserService extends ApiResourceService<UserDto, IUser> {
|
|
|
400
400
|
|
|
401
401
|
**Constructor Parameters:**
|
|
402
402
|
|
|
403
|
-
| Parameter
|
|
404
|
-
|
|
405
|
-
| `moduleApiName` | `string`
|
|
406
|
-
| `http`
|
|
407
|
-
| `serviceName`
|
|
403
|
+
| Parameter | Type | Required | Description |
|
|
404
|
+
| --------------- | ------------- | -------- | ------------------------------------------------ |
|
|
405
|
+
| `moduleApiName` | `string` | Yes | API path segment (e.g., 'users', 'file-manager') |
|
|
406
|
+
| `http` | `HttpClient` | Yes | Angular HttpClient instance |
|
|
407
|
+
| `serviceName` | `ServiceName` | No | Feature service name for URL resolution |
|
|
408
408
|
|
|
409
409
|
**Service URL Resolution:**
|
|
410
|
+
|
|
410
411
|
- If `serviceName` is provided, uses `getServiceUrl(config, serviceName)` to resolve the base URL
|
|
411
412
|
- Falls back to `APP_CONFIG.apiBaseUrl` if service not found or `serviceName` not provided
|
|
412
413
|
|
|
@@ -416,7 +417,7 @@ The list resource is **lazy-initialized** to avoid unnecessary HTTP requests on
|
|
|
416
417
|
|
|
417
418
|
```typescript
|
|
418
419
|
// Resource is NOT created until one of these is called:
|
|
419
|
-
userService.fetchList(
|
|
420
|
+
userService.fetchList("", { pagination: { currentPage: 0, pageSize: 10 } });
|
|
420
421
|
// OR
|
|
421
422
|
userService.initListResource(); // Explicit initialization
|
|
422
423
|
```
|
|
@@ -439,35 +440,35 @@ initListResource(): void {
|
|
|
439
440
|
|
|
440
441
|
**Endpoint mapping:**
|
|
441
442
|
|
|
442
|
-
| Method
|
|
443
|
-
|
|
|
444
|
-
| `insert(dto)`
|
|
445
|
-
| `insertMany(dtos)`
|
|
446
|
-
| `findById(id, select?)`
|
|
447
|
-
| `getAll(search, filter)` | POST | `/{resource}/get-all?q=`
|
|
448
|
-
| `update(dto)`
|
|
449
|
-
| `updateMany(dtos)`
|
|
450
|
-
| `delete(deleteDto)`
|
|
443
|
+
| Method | HTTP | Endpoint | Response |
|
|
444
|
+
| ------------------------ | ---- | ------------------------- | -------------------- |
|
|
445
|
+
| `insert(dto)` | POST | `/{resource}/insert` | `ISingleResponse<T>` |
|
|
446
|
+
| `insertMany(dtos)` | POST | `/{resource}/insert-many` | `IBulkResponse<T>` |
|
|
447
|
+
| `findById(id, select?)` | POST | `/{resource}/get/:id` | `ISingleResponse<T>` |
|
|
448
|
+
| `getAll(search, filter)` | POST | `/{resource}/get-all?q=` | `IListResponse<T>` |
|
|
449
|
+
| `update(dto)` | POST | `/{resource}/update` | `ISingleResponse<T>` |
|
|
450
|
+
| `updateMany(dtos)` | POST | `/{resource}/update-many` | `IBulkResponse<T>` |
|
|
451
|
+
| `delete(deleteDto)` | POST | `/{resource}/delete` | `IMessageResponse` |
|
|
451
452
|
|
|
452
453
|
All methods above return `Observable`. Async (Promise) variants are also available: `insertAsync`, `insertManyAsync`, `findByIdAsync`, `updateAsync`, `updateManyAsync`, `deleteAsync`.
|
|
453
454
|
|
|
454
455
|
**Reactive signals:**
|
|
455
456
|
|
|
456
|
-
| Signal | Type
|
|
457
|
-
| ------------ |
|
|
458
|
-
| `isLoading` | `Signal<boolean>`
|
|
459
|
-
| `data` | `Signal<T[]>`
|
|
460
|
-
| `total` | `Signal<number>`
|
|
461
|
-
| `pageInfo` | `Signal<IPaginationMeta>`
|
|
462
|
-
| `hasMore` | `Signal<boolean>`
|
|
463
|
-
| `searchTerm` | `WritableSignal<string>`
|
|
457
|
+
| Signal | Type | Description |
|
|
458
|
+
| ------------ | ----------------------------- | ----------------------- |
|
|
459
|
+
| `isLoading` | `Signal<boolean>` | Whether data is loading |
|
|
460
|
+
| `data` | `Signal<T[]>` | Current list data |
|
|
461
|
+
| `total` | `Signal<number>` | Total item count |
|
|
462
|
+
| `pageInfo` | `Signal<IPaginationMeta>` | Pagination metadata |
|
|
463
|
+
| `hasMore` | `Signal<boolean>` | More pages available |
|
|
464
|
+
| `searchTerm` | `WritableSignal<string>` | Current search term |
|
|
464
465
|
| `filterData` | `WritableSignal<IFilterData>` | Filter/pagination state |
|
|
465
466
|
|
|
466
467
|
**List management:**
|
|
467
468
|
|
|
468
469
|
```typescript
|
|
469
470
|
// Trigger data fetch (updates searchTerm and filterData signals, resource auto-reloads)
|
|
470
|
-
userService.fetchList(
|
|
471
|
+
userService.fetchList("search term", { pagination: { currentPage: 0, pageSize: 10 } });
|
|
471
472
|
|
|
472
473
|
// Pagination helpers
|
|
473
474
|
userService.setPagination({ currentPage: 1, pageSize: 20 });
|
|
@@ -530,14 +531,14 @@ export class ProductComponent {
|
|
|
530
531
|
|
|
531
532
|
**Methods:**
|
|
532
533
|
|
|
533
|
-
| Method
|
|
534
|
-
|
|
|
535
|
-
| `getFileUrl(fileId)`
|
|
536
|
-
| `fileUrlSignal(fileId)`
|
|
537
|
-
| `fetchFileUrls(fileIds[])`
|
|
538
|
-
| `fetchSingleFileUrl(fileId)`
|
|
539
|
-
| `clearCache()`
|
|
540
|
-
| `removeFromCache(fileId)`
|
|
534
|
+
| Method | Returns | Description |
|
|
535
|
+
| ---------------------------- | -------------------------------------- | --------------------------------- |
|
|
536
|
+
| `getFileUrl(fileId)` | `string \| null` | Get cached URL (synchronous) |
|
|
537
|
+
| `fileUrlSignal(fileId)` | `Signal<string \| null>` | Computed signal from cache |
|
|
538
|
+
| `fetchFileUrls(fileIds[])` | `Observable<FilesResponseDto[]>` | Fetch from backend, updates cache |
|
|
539
|
+
| `fetchSingleFileUrl(fileId)` | `Observable<FilesResponseDto \| null>` | Fetch single file |
|
|
540
|
+
| `clearCache()` | `void` | Clear all cached URLs |
|
|
541
|
+
| `removeFromCache(fileId)` | `void` | Remove specific entry from cache |
|
|
541
542
|
|
|
542
543
|
### PermissionValidatorService
|
|
543
544
|
|
|
@@ -569,18 +570,18 @@ export class MyComponent {
|
|
|
569
570
|
|
|
570
571
|
**Methods:**
|
|
571
572
|
|
|
572
|
-
| Method
|
|
573
|
-
|
|
|
574
|
-
| `setPermissions(codes[])`
|
|
575
|
-
| `clearPermissions()`
|
|
576
|
-
| `hasPermission(code)`
|
|
577
|
-
| `isPermissionsLoaded()`
|
|
573
|
+
| Method | Returns | Description |
|
|
574
|
+
| ------------------------- | --------- | -------------------------------------------- |
|
|
575
|
+
| `setPermissions(codes[])` | `void` | Replace all permissions |
|
|
576
|
+
| `clearPermissions()` | `void` | Clear all permissions |
|
|
577
|
+
| `hasPermission(code)` | `boolean` | Check single permission (supports wildcards) |
|
|
578
|
+
| `isPermissionsLoaded()` | `boolean` | **Deprecated** - Use `isLoaded()` signal |
|
|
578
579
|
|
|
579
580
|
**Signals:**
|
|
580
581
|
|
|
581
|
-
| Signal | Type | Description
|
|
582
|
-
| ------------- | ------------------ |
|
|
583
|
-
| `permissions` | `Signal<string[]>` | Readonly current permissions
|
|
582
|
+
| Signal | Type | Description |
|
|
583
|
+
| ------------- | ------------------ | --------------------------------- |
|
|
584
|
+
| `permissions` | `Signal<string[]>` | Readonly current permissions |
|
|
584
585
|
| `isLoaded` | `Signal<boolean>` | Whether permissions have been set |
|
|
585
586
|
|
|
586
587
|
**Wildcard Support:**
|
|
@@ -589,13 +590,13 @@ The `hasPermission()` method uses the permission evaluator utility which support
|
|
|
589
590
|
|
|
590
591
|
```typescript
|
|
591
592
|
// Exact match
|
|
592
|
-
hasPermission(
|
|
593
|
+
hasPermission("user.read"); // true if permissions include 'user.read'
|
|
593
594
|
|
|
594
595
|
// Global wildcard
|
|
595
|
-
hasPermission(
|
|
596
|
+
hasPermission("user.read"); // true if permissions include '*'
|
|
596
597
|
|
|
597
598
|
// Module wildcard
|
|
598
|
-
hasPermission(
|
|
599
|
+
hasPermission("user.read"); // true if permissions include 'user.*'
|
|
599
600
|
```
|
|
600
601
|
|
|
601
602
|
### CookieService
|
|
@@ -603,7 +604,7 @@ hasPermission('user.read') // true if permissions include 'user.*'
|
|
|
603
604
|
SSR-aware cookie reading service.
|
|
604
605
|
|
|
605
606
|
```typescript
|
|
606
|
-
import { CookieService } from
|
|
607
|
+
import { CookieService } from "@flusys/ng-shared";
|
|
607
608
|
|
|
608
609
|
const cookies = inject(CookieService).get(); // Returns document.cookie (browser) or request header cookie (server)
|
|
609
610
|
```
|
|
@@ -613,7 +614,7 @@ const cookies = inject(CookieService).get(); // Returns document.cookie (browser
|
|
|
613
614
|
SSR environment detection service.
|
|
614
615
|
|
|
615
616
|
```typescript
|
|
616
|
-
import { PlatformService } from
|
|
617
|
+
import { PlatformService } from "@flusys/ng-shared";
|
|
617
618
|
|
|
618
619
|
const platform = inject(PlatformService);
|
|
619
620
|
if (!platform.isServer) {
|
|
@@ -654,21 +655,7 @@ Single-select dropdown with lazy loading, search, and scroll pagination.
|
|
|
654
655
|
```typescript
|
|
655
656
|
@Component({
|
|
656
657
|
imports: [LazySelectComponent],
|
|
657
|
-
template: `
|
|
658
|
-
<lib-lazy-select
|
|
659
|
-
[(value)]="selectedId"
|
|
660
|
-
[selectDataList]="items()"
|
|
661
|
-
[optionLabel]="'label'"
|
|
662
|
-
[optionValue]="'value'"
|
|
663
|
-
[isEditMode]="true"
|
|
664
|
-
[isLoading]="loading()"
|
|
665
|
-
[total]="total()"
|
|
666
|
-
[pagination]="pagination()"
|
|
667
|
-
[placeHolder]="'Select item'"
|
|
668
|
-
(onSearch)="handleSearch($event)"
|
|
669
|
-
(onPagination)="handlePagination($event)"
|
|
670
|
-
/>
|
|
671
|
-
`,
|
|
658
|
+
template: ` <lib-lazy-select [(value)]="selectedId" [selectDataList]="items()" [optionLabel]="'label'" [optionValue]="'value'" [isEditMode]="true" [isLoading]="loading()" [total]="total()" [pagination]="pagination()" [placeHolder]="'Select item'" (onSearch)="handleSearch($event)" (onPagination)="handlePagination($event)" /> `,
|
|
672
659
|
})
|
|
673
660
|
export class MyComponent {
|
|
674
661
|
readonly selectedId = signal<string | null>(null);
|
|
@@ -681,16 +668,16 @@ export class MyComponent {
|
|
|
681
668
|
|
|
682
669
|
**Inputs:**
|
|
683
670
|
|
|
684
|
-
| Input | Type
|
|
685
|
-
| ---------------- |
|
|
686
|
-
| `selectDataList` | `Array<IDropDown>`
|
|
687
|
-
| `optionLabel` | `string`
|
|
688
|
-
| `optionValue` | `string`
|
|
689
|
-
| `isEditMode` | `boolean`
|
|
690
|
-
| `isLoading` | `boolean`
|
|
691
|
-
| `total` | `number \| undefined` | Total items for pagination
|
|
692
|
-
| `pagination` | `IPagination`
|
|
693
|
-
| `placeHolder` | `string`
|
|
671
|
+
| Input | Type | Description |
|
|
672
|
+
| ---------------- | --------------------- | --------------------------------------------- |
|
|
673
|
+
| `selectDataList` | `Array<IDropDown>` | Dropdown options (required) |
|
|
674
|
+
| `optionLabel` | `string` | Label field name (required) |
|
|
675
|
+
| `optionValue` | `string` | Value field name (required) |
|
|
676
|
+
| `isEditMode` | `boolean` | Enable/disable editing (required) |
|
|
677
|
+
| `isLoading` | `boolean` | Loading state (required) |
|
|
678
|
+
| `total` | `number \| undefined` | Total items for pagination |
|
|
679
|
+
| `pagination` | `IPagination` | Current pagination state |
|
|
680
|
+
| `placeHolder` | `string` | Placeholder text (default: `'Select Option'`) |
|
|
694
681
|
|
|
695
682
|
**Model:** `value` - Two-way bound selected value (`string | null`)
|
|
696
683
|
|
|
@@ -707,19 +694,7 @@ Multi-select dropdown with lazy loading, search, select-all, and scroll paginati
|
|
|
707
694
|
```typescript
|
|
708
695
|
@Component({
|
|
709
696
|
imports: [LazyMultiSelectComponent],
|
|
710
|
-
template: `
|
|
711
|
-
<lib-lazy-multi-select
|
|
712
|
-
[(value)]="selectedIds"
|
|
713
|
-
[selectDataList]="items()"
|
|
714
|
-
[isEditMode]="true"
|
|
715
|
-
[isLoading]="loading()"
|
|
716
|
-
[total]="total()"
|
|
717
|
-
[pagination]="pagination()"
|
|
718
|
-
[placeHolder]="'Select items'"
|
|
719
|
-
(onSearch)="handleSearch($event)"
|
|
720
|
-
(onPagination)="handlePagination($event)"
|
|
721
|
-
/>
|
|
722
|
-
`,
|
|
697
|
+
template: ` <lib-lazy-multi-select [(value)]="selectedIds" [selectDataList]="items()" [isEditMode]="true" [isLoading]="loading()" [total]="total()" [pagination]="pagination()" [placeHolder]="'Select items'" (onSearch)="handleSearch($event)" (onPagination)="handlePagination($event)" /> `,
|
|
723
698
|
})
|
|
724
699
|
export class MyComponent {
|
|
725
700
|
readonly selectedIds = signal<string[] | null>(null);
|
|
@@ -744,17 +719,10 @@ Single user selection with lazy loading. Uses `USER_PROVIDER` internally or acce
|
|
|
744
719
|
imports: [UserSelectComponent],
|
|
745
720
|
template: `
|
|
746
721
|
<!-- Simple usage - uses USER_PROVIDER internally -->
|
|
747
|
-
<lib-user-select
|
|
748
|
-
[(value)]="selectedUserId"
|
|
749
|
-
[isEditMode]="true"
|
|
750
|
-
/>
|
|
722
|
+
<lib-user-select [(value)]="selectedUserId" [isEditMode]="true" />
|
|
751
723
|
|
|
752
724
|
<!-- With custom loadUsers function -->
|
|
753
|
-
<lib-user-select
|
|
754
|
-
[(value)]="selectedUserId"
|
|
755
|
-
[isEditMode]="true"
|
|
756
|
-
[loadUsers]="customLoadUsers"
|
|
757
|
-
/>
|
|
725
|
+
<lib-user-select [(value)]="selectedUserId" [isEditMode]="true" [loadUsers]="customLoadUsers" />
|
|
758
726
|
`,
|
|
759
727
|
})
|
|
760
728
|
export class MyComponent {
|
|
@@ -764,14 +732,14 @@ export class MyComponent {
|
|
|
764
732
|
|
|
765
733
|
**Inputs:**
|
|
766
734
|
|
|
767
|
-
| Input
|
|
768
|
-
|
|
|
769
|
-
| `isEditMode`
|
|
770
|
-
| `placeHolder`
|
|
771
|
-
| `filterActive`
|
|
772
|
-
| `additionalFilters` | `Record<string, unknown>` | `{}`
|
|
773
|
-
| `pageSize`
|
|
774
|
-
| `loadUsers`
|
|
735
|
+
| Input | Type | Default | Description |
|
|
736
|
+
| ------------------- | ------------------------- | --------------- | ---------------------------- |
|
|
737
|
+
| `isEditMode` | `boolean` | required | Enable/disable editing |
|
|
738
|
+
| `placeHolder` | `string` | `'Select User'` | Placeholder text |
|
|
739
|
+
| `filterActive` | `boolean` | `true` | Filter active users only |
|
|
740
|
+
| `additionalFilters` | `Record<string, unknown>` | `{}` | Extra filter params |
|
|
741
|
+
| `pageSize` | `number` | `20` | Page size for pagination |
|
|
742
|
+
| `loadUsers` | `LoadUsersFn` | - | Custom user loading function |
|
|
775
743
|
|
|
776
744
|
**Model:** `value` - Two-way bound selected user ID (`string | null`)
|
|
777
745
|
|
|
@@ -787,12 +755,7 @@ Multiple user selection with lazy loading. Uses `USER_PROVIDER` internally or ac
|
|
|
787
755
|
```typescript
|
|
788
756
|
@Component({
|
|
789
757
|
imports: [UserMultiSelectComponent],
|
|
790
|
-
template: `
|
|
791
|
-
<lib-user-multi-select
|
|
792
|
-
[(value)]="selectedUserIds"
|
|
793
|
-
[isEditMode]="true"
|
|
794
|
-
/>
|
|
795
|
-
`,
|
|
758
|
+
template: ` <lib-user-multi-select [(value)]="selectedUserIds" [isEditMode]="true" /> `,
|
|
796
759
|
})
|
|
797
760
|
export class MyComponent {
|
|
798
761
|
readonly selectedUserIds = signal<string[] | null>(null);
|
|
@@ -816,44 +779,32 @@ Drag & drop file upload with type filtering. Pass your own `uploadFile` function
|
|
|
816
779
|
imports: [FileUploaderComponent],
|
|
817
780
|
template: `
|
|
818
781
|
<!-- Single image upload -->
|
|
819
|
-
<lib-file-uploader
|
|
820
|
-
[uploadFile]="uploadFile"
|
|
821
|
-
[acceptTypes]="['image/*']"
|
|
822
|
-
[multiple]="false"
|
|
823
|
-
(fileUploaded)="onFileUploaded($event)"
|
|
824
|
-
/>
|
|
782
|
+
<lib-file-uploader [uploadFile]="uploadFile" [acceptTypes]="['image/*']" [multiple]="false" (fileUploaded)="onFileUploaded($event)" />
|
|
825
783
|
|
|
826
784
|
<!-- Multiple document upload -->
|
|
827
|
-
<lib-file-uploader
|
|
828
|
-
[uploadFile]="uploadFile"
|
|
829
|
-
[acceptTypes]="FILE_TYPE_FILTERS.DOCUMENTS"
|
|
830
|
-
[multiple]="true"
|
|
831
|
-
[maxFiles]="5"
|
|
832
|
-
(filesUploaded)="onFilesUploaded($event)"
|
|
833
|
-
/>
|
|
785
|
+
<lib-file-uploader [uploadFile]="uploadFile" [acceptTypes]="FILE_TYPE_FILTERS.DOCUMENTS" [multiple]="true" [maxFiles]="5" (filesUploaded)="onFilesUploaded($event)" />
|
|
834
786
|
`,
|
|
835
787
|
})
|
|
836
788
|
export class MyComponent {
|
|
837
789
|
readonly uploadService = inject(UploadService);
|
|
838
790
|
|
|
839
|
-
readonly uploadFile: UploadFileFn = (file, options) =>
|
|
840
|
-
this.uploadService.uploadSingleFile(file, options);
|
|
791
|
+
readonly uploadFile: UploadFileFn = (file, options) => this.uploadService.uploadSingleFile(file, options);
|
|
841
792
|
}
|
|
842
793
|
```
|
|
843
794
|
|
|
844
795
|
**Inputs:**
|
|
845
796
|
|
|
846
|
-
| Input
|
|
847
|
-
|
|
|
848
|
-
| `uploadFile`
|
|
849
|
-
| `acceptTypes`
|
|
850
|
-
| `multiple`
|
|
851
|
-
| `maxFiles`
|
|
852
|
-
| `maxSizeMb`
|
|
853
|
-
| `uploadOptions` | `IFileUploadOptions` | `{}`
|
|
854
|
-
| `disabled`
|
|
855
|
-
| `showPreview`
|
|
856
|
-
| `autoUpload`
|
|
797
|
+
| Input | Type | Default | Description |
|
|
798
|
+
| --------------- | -------------------- | -------- | ------------------------------- |
|
|
799
|
+
| `uploadFile` | `UploadFileFn` | required | Upload function |
|
|
800
|
+
| `acceptTypes` | `string[]` | `[]` | Allowed MIME types |
|
|
801
|
+
| `multiple` | `boolean` | `false` | Allow multiple files |
|
|
802
|
+
| `maxFiles` | `number` | `10` | Max files for multiple |
|
|
803
|
+
| `maxSizeMb` | `number` | `10` | Max file size in MB |
|
|
804
|
+
| `uploadOptions` | `IFileUploadOptions` | `{}` | Upload options |
|
|
805
|
+
| `disabled` | `boolean` | `false` | Disable uploader |
|
|
806
|
+
| `showPreview` | `boolean` | `true` | Show selected files preview |
|
|
807
|
+
| `autoUpload` | `boolean` | `true` | Upload immediately on selection |
|
|
857
808
|
|
|
858
809
|
**Outputs:** `fileUploaded` (IUploadedFile), `filesUploaded` (IUploadedFile[]), `onError` (Error), `fileSelected` (File[])
|
|
859
810
|
|
|
@@ -866,15 +817,7 @@ Dialog to browse and select existing files with filtering.
|
|
|
866
817
|
```typescript
|
|
867
818
|
@Component({
|
|
868
819
|
imports: [FileSelectorDialogComponent],
|
|
869
|
-
template: `
|
|
870
|
-
<lib-file-selector-dialog
|
|
871
|
-
[(visible)]="showFileSelector"
|
|
872
|
-
[loadFiles]="loadFiles"
|
|
873
|
-
[acceptTypes]="['image/*']"
|
|
874
|
-
[multiple]="false"
|
|
875
|
-
(fileSelected)="onFileSelected($event)"
|
|
876
|
-
/>
|
|
877
|
-
`,
|
|
820
|
+
template: ` <lib-file-selector-dialog [(visible)]="showFileSelector" [loadFiles]="loadFiles" [acceptTypes]="['image/*']" [multiple]="false" (fileSelected)="onFileSelected($event)" /> `,
|
|
878
821
|
})
|
|
879
822
|
export class MyComponent {
|
|
880
823
|
readonly showFileSelector = signal(false);
|
|
@@ -889,14 +832,14 @@ export class MyComponent {
|
|
|
889
832
|
|
|
890
833
|
**Inputs:**
|
|
891
834
|
|
|
892
|
-
| Input
|
|
893
|
-
|
|
|
894
|
-
| `loadFiles`
|
|
895
|
-
| `header`
|
|
896
|
-
| `acceptTypes`
|
|
897
|
-
| `multiple`
|
|
898
|
-
| `maxSelection` | `number`
|
|
899
|
-
| `pageSize`
|
|
835
|
+
| Input | Type | Default | Description |
|
|
836
|
+
| -------------- | ------------- | --------------- | ------------------------ |
|
|
837
|
+
| `loadFiles` | `LoadFilesFn` | required | File loading function |
|
|
838
|
+
| `header` | `string` | `'Select File'` | Dialog header |
|
|
839
|
+
| `acceptTypes` | `string[]` | `[]` | Allowed MIME types |
|
|
840
|
+
| `multiple` | `boolean` | `false` | Allow multiple selection |
|
|
841
|
+
| `maxSelection` | `number` | `10` | Max files for multiple |
|
|
842
|
+
| `pageSize` | `number` | `20` | Page size for pagination |
|
|
900
843
|
|
|
901
844
|
**Model:** `visible` - Two-way bound dialog visibility (`boolean`)
|
|
902
845
|
|
|
@@ -914,7 +857,7 @@ Structural directive for permission-based rendering. Fail-closed: hides content
|
|
|
914
857
|
- **Input:** `hasPermission` - `string | ILogicNode | null`
|
|
915
858
|
|
|
916
859
|
```typescript
|
|
917
|
-
import { HasPermissionDirective, ILogicNode } from
|
|
860
|
+
import { HasPermissionDirective, ILogicNode } from "@flusys/ng-shared";
|
|
918
861
|
|
|
919
862
|
@Component({
|
|
920
863
|
imports: [HasPermissionDirective],
|
|
@@ -928,11 +871,11 @@ import { HasPermissionDirective, ILogicNode } from '@flusys/ng-shared';
|
|
|
928
871
|
})
|
|
929
872
|
export class MyComponent {
|
|
930
873
|
readonly editLogic: ILogicNode = {
|
|
931
|
-
type:
|
|
932
|
-
operator:
|
|
874
|
+
type: "group",
|
|
875
|
+
operator: "AND",
|
|
933
876
|
children: [
|
|
934
|
-
{ type:
|
|
935
|
-
{ type:
|
|
877
|
+
{ type: "action", actionId: "user.view" },
|
|
878
|
+
{ type: "action", actionId: "user.update" },
|
|
936
879
|
],
|
|
937
880
|
};
|
|
938
881
|
}
|
|
@@ -946,8 +889,7 @@ Toggles readonly/disabled state for form controls based on edit mode. Supports `
|
|
|
946
889
|
- **Input:** `isEditMode` (required boolean)
|
|
947
890
|
|
|
948
891
|
```html
|
|
949
|
-
<input [appEditModeElementChanger] [isEditMode]="isEditing()" />
|
|
950
|
-
<p-select [appEditModeElementChanger] [isEditMode]="isEditing()" />
|
|
892
|
+
<input [appEditModeElementChanger] [isEditMode]="isEditing()" /> <p-select [appEditModeElementChanger] [isEditMode]="isEditing()" />
|
|
951
893
|
```
|
|
952
894
|
|
|
953
895
|
### IsEmptyImageDirective
|
|
@@ -972,8 +914,7 @@ Prevents default browser behavior on specified events and emits the event.
|
|
|
972
914
|
- **Output:** `action` - Emits the prevented event
|
|
973
915
|
|
|
974
916
|
```html
|
|
975
|
-
<a href="#" appPreventDefault (action)="handleClick($event)">Click me</a>
|
|
976
|
-
<input appPreventDefault eventType="keydown" preventKey="Enter" (action)="onEnter($event)" />
|
|
917
|
+
<a href="#" appPreventDefault (action)="handleClick($event)">Click me</a> <input appPreventDefault eventType="keydown" preventKey="Enter" (action)="onEnter($event)" />
|
|
977
918
|
```
|
|
978
919
|
|
|
979
920
|
---
|
|
@@ -987,24 +928,29 @@ Route-level guards for permission-based access control. All guards deny access w
|
|
|
987
928
|
Single permission or complex logic check.
|
|
988
929
|
|
|
989
930
|
```typescript
|
|
990
|
-
import { permissionGuard } from
|
|
931
|
+
import { permissionGuard } from "@flusys/ng-shared";
|
|
991
932
|
|
|
992
933
|
const routes: Routes = [
|
|
993
934
|
// Simple permission
|
|
994
|
-
{ path:
|
|
935
|
+
{ path: "users", canActivate: [permissionGuard("user.view")] },
|
|
995
936
|
|
|
996
937
|
// Complex logic (ILogicNode)
|
|
997
|
-
{
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
938
|
+
{
|
|
939
|
+
path: "admin",
|
|
940
|
+
canActivate: [
|
|
941
|
+
permissionGuard({
|
|
942
|
+
type: "group",
|
|
943
|
+
operator: "AND",
|
|
944
|
+
children: [
|
|
945
|
+
{ type: "action", actionId: "admin.view" },
|
|
946
|
+
{ type: "action", actionId: "admin.manage" },
|
|
947
|
+
],
|
|
948
|
+
}),
|
|
1003
949
|
],
|
|
1004
|
-
}
|
|
950
|
+
},
|
|
1005
951
|
|
|
1006
952
|
// Custom redirect on deny
|
|
1007
|
-
{ path:
|
|
953
|
+
{ path: "settings", canActivate: [permissionGuard("settings.view", "/access-denied")] },
|
|
1008
954
|
];
|
|
1009
955
|
```
|
|
1010
956
|
|
|
@@ -1033,34 +979,34 @@ AND logic - allows access only if user has ALL specified permissions.
|
|
|
1033
979
|
Pure functions for permission logic evaluation. Used internally by `HasPermissionDirective` and guards. **Supports wildcard permissions.**
|
|
1034
980
|
|
|
1035
981
|
```typescript
|
|
1036
|
-
import { evaluatePermission, evaluateLogicNode, hasAnyPermission, hasAllPermissions, hasPermission } from
|
|
982
|
+
import { evaluatePermission, evaluateLogicNode, hasAnyPermission, hasAllPermissions, hasPermission } from "@flusys/ng-shared";
|
|
1037
983
|
|
|
1038
|
-
const userPermissions = [
|
|
984
|
+
const userPermissions = ["user.view", "user.create", "admin.*"];
|
|
1039
985
|
|
|
1040
986
|
// Low-level hasPermission check with wildcard support
|
|
1041
|
-
hasPermission(
|
|
1042
|
-
hasPermission(
|
|
1043
|
-
hasPermission(
|
|
987
|
+
hasPermission("user.view", userPermissions); // true (exact match)
|
|
988
|
+
hasPermission("admin.manage", userPermissions); // true (matches 'admin.*')
|
|
989
|
+
hasPermission("settings.view", ["*"]); // true (global wildcard)
|
|
1044
990
|
|
|
1045
991
|
// Evaluate string or ILogicNode
|
|
1046
|
-
evaluatePermission(
|
|
1047
|
-
evaluatePermission(null, userPermissions);
|
|
992
|
+
evaluatePermission("user.view", userPermissions); // true
|
|
993
|
+
evaluatePermission(null, userPermissions); // false
|
|
1048
994
|
|
|
1049
995
|
// Evaluate ILogicNode tree recursively
|
|
1050
996
|
evaluateLogicNode(logicNode, userPermissions);
|
|
1051
997
|
|
|
1052
998
|
// Simple OR/AND checks (also support wildcards)
|
|
1053
|
-
hasAnyPermission([
|
|
1054
|
-
hasAllPermissions([
|
|
999
|
+
hasAnyPermission(["user.view", "user.delete"], userPermissions); // true (has user.view)
|
|
1000
|
+
hasAllPermissions(["user.view", "user.delete"], userPermissions); // false (missing user.delete)
|
|
1055
1001
|
```
|
|
1056
1002
|
|
|
1057
1003
|
**Wildcard Rules:**
|
|
1058
1004
|
|
|
1059
|
-
| Pattern
|
|
1060
|
-
|
|
1061
|
-
| `*`
|
|
1062
|
-
| `module.*`
|
|
1063
|
-
| `user.read` | Exact match only
|
|
1005
|
+
| Pattern | Matches |
|
|
1006
|
+
| ----------- | --------------------------------------- |
|
|
1007
|
+
| `*` | All permissions (global wildcard) |
|
|
1008
|
+
| `module.*` | All permissions starting with `module.` |
|
|
1009
|
+
| `user.read` | Exact match only |
|
|
1064
1010
|
|
|
1065
1011
|
**Implementation Details:**
|
|
1066
1012
|
|
|
@@ -1072,10 +1018,10 @@ export function hasPermission(requiredPermission: string, userPermissions: strin
|
|
|
1072
1018
|
// Wildcard matching
|
|
1073
1019
|
for (const permission of userPermissions) {
|
|
1074
1020
|
// Global wildcard
|
|
1075
|
-
if (permission ===
|
|
1021
|
+
if (permission === "*") return true;
|
|
1076
1022
|
|
|
1077
1023
|
// Module wildcard (e.g., 'user.*' matches 'user.read')
|
|
1078
|
-
if (permission.endsWith(
|
|
1024
|
+
if (permission.endsWith(".*")) {
|
|
1079
1025
|
const prefix = permission.slice(0, -1); // 'user.'
|
|
1080
1026
|
if (requiredPermission.startsWith(prefix)) return true;
|
|
1081
1027
|
}
|
|
@@ -1119,10 +1065,10 @@ onScroll(event: Event): void {
|
|
|
1119
1065
|
Abstract base class for custom form controls. Implements both `ControlValueAccessor` (reactive forms) and `FormValueControl` (signal forms).
|
|
1120
1066
|
|
|
1121
1067
|
```typescript
|
|
1122
|
-
import { BaseFormControl, provideValueAccessor } from
|
|
1068
|
+
import { BaseFormControl, provideValueAccessor } from "@flusys/ng-shared";
|
|
1123
1069
|
|
|
1124
1070
|
@Component({
|
|
1125
|
-
selector:
|
|
1071
|
+
selector: "my-select",
|
|
1126
1072
|
providers: [provideValueAccessor(MySelectComponent)],
|
|
1127
1073
|
})
|
|
1128
1074
|
export class MySelectComponent extends BaseFormControl<string | null> {
|
|
@@ -1226,7 +1172,7 @@ export class UserListComponent extends BaseListPage<IUser> {
|
|
|
1226
1172
|
Re-exports common Angular modules for convenience.
|
|
1227
1173
|
|
|
1228
1174
|
```typescript
|
|
1229
|
-
import { AngularModule } from
|
|
1175
|
+
import { AngularModule } from "@flusys/ng-shared";
|
|
1230
1176
|
// Includes: CommonModule, FormsModule, ReactiveFormsModule, RouterLink, RouterOutlet,
|
|
1231
1177
|
// RouterLinkActive, NgOptimizedImage, NgComponentOutlet, + directives (IsEmptyImageDirective, PreventDefaultDirective)
|
|
1232
1178
|
// Providers: DatePipe
|
|
@@ -1237,7 +1183,7 @@ import { AngularModule } from '@flusys/ng-shared';
|
|
|
1237
1183
|
Re-exports PrimeNG component modules for convenience.
|
|
1238
1184
|
|
|
1239
1185
|
```typescript
|
|
1240
|
-
import { PrimeModule } from
|
|
1186
|
+
import { PrimeModule } from "@flusys/ng-shared";
|
|
1241
1187
|
// Includes 30 modules:
|
|
1242
1188
|
// - Layout: AccordionModule, CardModule, DividerModule, PanelModule, SplitterModule, TabsModule, ToolbarModule
|
|
1243
1189
|
// - Form: AutoCompleteModule, CheckboxModule, DatePickerModule, InputTextModule, InputTextareaModule,
|
|
@@ -1273,13 +1219,14 @@ ng-iam/ng-storage (consume interfaces via DI)
|
|
|
1273
1219
|
User list access for IAM user selection.
|
|
1274
1220
|
|
|
1275
1221
|
```typescript
|
|
1276
|
-
interface IUserBasicInfo {
|
|
1222
|
+
interface IUserBasicInfo {
|
|
1223
|
+
id: string;
|
|
1224
|
+
name: string;
|
|
1225
|
+
email: string;
|
|
1226
|
+
}
|
|
1277
1227
|
|
|
1278
1228
|
interface IUserProvider {
|
|
1279
|
-
getUsers(filter?: {
|
|
1280
|
-
page?: number; pageSize?: number; search?: string;
|
|
1281
|
-
companyId?: string; branchId?: string;
|
|
1282
|
-
}): Observable<IListResponse<IUserBasicInfo>>;
|
|
1229
|
+
getUsers(filter?: { page?: number; pageSize?: number; search?: string; companyId?: string; branchId?: string }): Observable<IListResponse<IUserBasicInfo>>;
|
|
1283
1230
|
}
|
|
1284
1231
|
```
|
|
1285
1232
|
|
|
@@ -1290,12 +1237,14 @@ interface IUserProvider {
|
|
|
1290
1237
|
Company list access for IAM company selection.
|
|
1291
1238
|
|
|
1292
1239
|
```typescript
|
|
1293
|
-
interface ICompanyBasicInfo {
|
|
1240
|
+
interface ICompanyBasicInfo {
|
|
1241
|
+
id: string;
|
|
1242
|
+
name: string;
|
|
1243
|
+
slug?: string;
|
|
1244
|
+
}
|
|
1294
1245
|
|
|
1295
1246
|
interface ICompanyApiProvider {
|
|
1296
|
-
getCompanies(filter?: {
|
|
1297
|
-
page?: number; pageSize?: number; search?: string;
|
|
1298
|
-
}): Observable<IListResponse<ICompanyBasicInfo>>;
|
|
1247
|
+
getCompanies(filter?: { page?: number; pageSize?: number; search?: string }): Observable<IListResponse<ICompanyBasicInfo>>;
|
|
1299
1248
|
}
|
|
1300
1249
|
```
|
|
1301
1250
|
|
|
@@ -1325,10 +1274,10 @@ Profile picture upload for ng-auth profile page. Implemented by ng-storage. **Op
|
|
|
1325
1274
|
|
|
1326
1275
|
```typescript
|
|
1327
1276
|
interface IProfileUploadResult {
|
|
1328
|
-
id: string;
|
|
1329
|
-
name: string;
|
|
1330
|
-
key: string;
|
|
1331
|
-
size: number;
|
|
1277
|
+
id: string; // File manager ID (UUID)
|
|
1278
|
+
name: string; // Original file name
|
|
1279
|
+
key: string; // Storage key/path
|
|
1280
|
+
size: number; // File size in bytes
|
|
1332
1281
|
contentType: string; // MIME type
|
|
1333
1282
|
}
|
|
1334
1283
|
|
|
@@ -1340,10 +1289,7 @@ interface IProfileUploadOptions {
|
|
|
1340
1289
|
}
|
|
1341
1290
|
|
|
1342
1291
|
interface IProfileUploadProvider {
|
|
1343
|
-
uploadProfilePicture(
|
|
1344
|
-
file: File,
|
|
1345
|
-
options?: IProfileUploadOptions
|
|
1346
|
-
): Observable<ISingleResponse<IProfileUploadResult>>;
|
|
1292
|
+
uploadProfilePicture(file: File, options?: IProfileUploadOptions): Observable<ISingleResponse<IProfileUploadResult>>;
|
|
1347
1293
|
}
|
|
1348
1294
|
```
|
|
1349
1295
|
|
|
@@ -1417,14 +1363,13 @@ interface IUserListItem {
|
|
|
1417
1363
|
email: string;
|
|
1418
1364
|
phone?: string;
|
|
1419
1365
|
isActive?: boolean;
|
|
1420
|
-
[key: string]: unknown;
|
|
1421
1366
|
}
|
|
1422
1367
|
|
|
1423
1368
|
interface IUserListAction<T = IUserListItem> {
|
|
1424
1369
|
id: string;
|
|
1425
1370
|
label: string;
|
|
1426
1371
|
icon?: string;
|
|
1427
|
-
severity?:
|
|
1372
|
+
severity?: "primary" | "secondary" | "success" | "info" | "warn" | "danger";
|
|
1428
1373
|
permission?: string;
|
|
1429
1374
|
tooltip?: string;
|
|
1430
1375
|
disabled?: boolean | ((user: T) => boolean);
|
|
@@ -1436,7 +1381,7 @@ interface IUserListColumn {
|
|
|
1436
1381
|
header: string;
|
|
1437
1382
|
width?: string;
|
|
1438
1383
|
sortable?: boolean;
|
|
1439
|
-
templateType?:
|
|
1384
|
+
templateType?: "text" | "badge" | "date" | "boolean" | "custom";
|
|
1440
1385
|
}
|
|
1441
1386
|
|
|
1442
1387
|
interface IUserListFilter {
|
|
@@ -1446,7 +1391,6 @@ interface IUserListFilter {
|
|
|
1446
1391
|
isActive?: boolean;
|
|
1447
1392
|
companyId?: string;
|
|
1448
1393
|
branchId?: string;
|
|
1449
|
-
[key: string]: unknown;
|
|
1450
1394
|
}
|
|
1451
1395
|
|
|
1452
1396
|
interface IUserListProvider<T extends IUserListItem = IUserListItem> {
|
|
@@ -1463,17 +1407,13 @@ interface IUserListProvider<T extends IUserListItem = IUserListItem> {
|
|
|
1463
1407
|
|
|
1464
1408
|
```typescript
|
|
1465
1409
|
// In app.config.ts
|
|
1466
|
-
providers: [
|
|
1467
|
-
{ provide: USER_LIST_PROVIDER, useClass: MyUserListProvider },
|
|
1468
|
-
]
|
|
1410
|
+
providers: [{ provide: USER_LIST_PROVIDER, useClass: MyUserListProvider }];
|
|
1469
1411
|
|
|
1470
1412
|
// Implementation
|
|
1471
|
-
@Injectable({ providedIn:
|
|
1413
|
+
@Injectable({ providedIn: "root" })
|
|
1472
1414
|
export class MyUserListProvider implements IUserListProvider {
|
|
1473
1415
|
getExtraRowActions() {
|
|
1474
|
-
return [
|
|
1475
|
-
{ id: 'assign-role', label: 'Assign Role', icon: 'pi pi-users' },
|
|
1476
|
-
];
|
|
1416
|
+
return [{ id: "assign-role", label: "Assign Role", icon: "pi pi-users" }];
|
|
1477
1417
|
}
|
|
1478
1418
|
}
|
|
1479
1419
|
```
|
|
@@ -1481,13 +1421,13 @@ export class MyUserListProvider implements IUserListProvider {
|
|
|
1481
1421
|
### Usage in Consuming Packages
|
|
1482
1422
|
|
|
1483
1423
|
```typescript
|
|
1484
|
-
import { USER_PROVIDER } from
|
|
1424
|
+
import { USER_PROVIDER } from "@flusys/ng-shared";
|
|
1485
1425
|
|
|
1486
1426
|
export class UserSelectorComponent {
|
|
1487
1427
|
private readonly userProvider = inject(USER_PROVIDER);
|
|
1488
1428
|
|
|
1489
1429
|
loadUsers() {
|
|
1490
|
-
this.userProvider.getUsers({ page: 0, pageSize: 50 }).subscribe(response => {
|
|
1430
|
+
this.userProvider.getUsers({ page: 0, pageSize: 50 }).subscribe((response) => {
|
|
1491
1431
|
this.users.set(response.data ?? []);
|
|
1492
1432
|
});
|
|
1493
1433
|
}
|
|
@@ -1498,7 +1438,7 @@ export class UserSelectorComponent {
|
|
|
1498
1438
|
|
|
1499
1439
|
```typescript
|
|
1500
1440
|
// app.config.ts
|
|
1501
|
-
import { provideAuthProviders } from
|
|
1441
|
+
import { provideAuthProviders } from "@flusys/ng-auth";
|
|
1502
1442
|
|
|
1503
1443
|
export const appConfig: ApplicationConfig = {
|
|
1504
1444
|
providers: [
|
|
@@ -1598,12 +1538,10 @@ providers: [
|
|
|
1598
1538
|
{ provide: COMPANY_API_PROVIDER, useClass: AuthCompanyApiProvider },
|
|
1599
1539
|
{ provide: USER_PERMISSION_PROVIDER, useClass: AuthUserPermissionProvider },
|
|
1600
1540
|
{ provide: AUTH_STATE_PROVIDER, useClass: AuthStateProviderAdapter },
|
|
1601
|
-
]
|
|
1541
|
+
];
|
|
1602
1542
|
|
|
1603
1543
|
// OR use the convenience function
|
|
1604
|
-
providers: [
|
|
1605
|
-
...provideAuthProviders(),
|
|
1606
|
-
]
|
|
1544
|
+
providers: [...provideAuthProviders()];
|
|
1607
1545
|
```
|
|
1608
1546
|
|
|
1609
1547
|
### Permissions Not Working
|
|
@@ -1622,107 +1560,107 @@ providers: [
|
|
|
1622
1560
|
|
|
1623
1561
|
### Services
|
|
1624
1562
|
|
|
1625
|
-
| Service
|
|
1626
|
-
|
|
|
1627
|
-
| `ApiResourceService<DTO, T>`
|
|
1628
|
-
| `FileUrlService`
|
|
1629
|
-
| `PermissionValidatorService`
|
|
1630
|
-
| `CookieService`
|
|
1631
|
-
| `PlatformService`
|
|
1563
|
+
| Service | Description |
|
|
1564
|
+
| ---------------------------- | ---------------------------------------------------------------------------------------- |
|
|
1565
|
+
| `ApiResourceService<DTO, T>` | Signal-based CRUD with resource() API (lazy-initialized, accepts optional `serviceName`) |
|
|
1566
|
+
| `FileUrlService` | Cloud storage URL fetching |
|
|
1567
|
+
| `PermissionValidatorService` | Permission state management with wildcards |
|
|
1568
|
+
| `CookieService` | SSR-aware cookie reading |
|
|
1569
|
+
| `PlatformService` | SSR environment detection |
|
|
1632
1570
|
|
|
1633
1571
|
### Classes
|
|
1634
1572
|
|
|
1635
|
-
| Class
|
|
1636
|
-
|
|
|
1637
|
-
| `ApiResourceService`
|
|
1638
|
-
| `BaseFormControl`
|
|
1639
|
-
| `BaseFormPage`
|
|
1640
|
-
| `BaseListPage`
|
|
1573
|
+
| Class | Description |
|
|
1574
|
+
| -------------------- | -------------------------------------------------- |
|
|
1575
|
+
| `ApiResourceService` | Signal-based CRUD base class (alias: `ApiService`) |
|
|
1576
|
+
| `BaseFormControl` | Abstract base for custom form controls |
|
|
1577
|
+
| `BaseFormPage` | Abstract directive for create/edit pages |
|
|
1578
|
+
| `BaseListPage` | Abstract directive for list pages |
|
|
1641
1579
|
|
|
1642
1580
|
### Constants
|
|
1643
1581
|
|
|
1644
|
-
| Constant
|
|
1645
|
-
|
|
|
1646
|
-
| `PERMISSIONS`
|
|
1647
|
-
| `USER_PERMISSIONS`
|
|
1648
|
-
| `ROLE_PERMISSIONS`
|
|
1649
|
-
| `FILE_PERMISSIONS`
|
|
1582
|
+
| Constant | Description |
|
|
1583
|
+
| ------------------- | ----------------------------------------------------- |
|
|
1584
|
+
| `PERMISSIONS` | Aggregated permission codes by module |
|
|
1585
|
+
| `USER_PERMISSIONS` | `{ CREATE, READ, UPDATE, DELETE }` for users |
|
|
1586
|
+
| `ROLE_PERMISSIONS` | `{ CREATE, READ, UPDATE, DELETE }` for roles |
|
|
1587
|
+
| `FILE_PERMISSIONS` | `{ CREATE, READ, UPDATE, DELETE }` for files |
|
|
1650
1588
|
| `FILE_TYPE_FILTERS` | Predefined MIME type arrays (IMAGES, DOCUMENTS, etc.) |
|
|
1651
1589
|
|
|
1652
1590
|
### Components
|
|
1653
1591
|
|
|
1654
|
-
| Component
|
|
1655
|
-
|
|
|
1656
|
-
| `IconComponent`
|
|
1657
|
-
| `LazySelectComponent`
|
|
1658
|
-
| `LazyMultiSelectComponent`
|
|
1659
|
-
| `UserSelectComponent`
|
|
1660
|
-
| `UserMultiSelectComponent`
|
|
1661
|
-
| `FileUploaderComponent`
|
|
1662
|
-
| `FileSelectorDialogComponent` | `lib-file-selector-dialog` | File browser dialog
|
|
1592
|
+
| Component | Selector | Description |
|
|
1593
|
+
| ----------------------------- | -------------------------- | ------------------------- |
|
|
1594
|
+
| `IconComponent` | `lib-icon` | Flexible icon renderer |
|
|
1595
|
+
| `LazySelectComponent` | `lib-lazy-select` | Lazy-loading dropdown |
|
|
1596
|
+
| `LazyMultiSelectComponent` | `lib-lazy-multi-select` | Lazy-loading multi-select |
|
|
1597
|
+
| `UserSelectComponent` | `lib-user-select` | Single user selector |
|
|
1598
|
+
| `UserMultiSelectComponent` | `lib-user-multi-select` | Multiple user selector |
|
|
1599
|
+
| `FileUploaderComponent` | `lib-file-uploader` | Drag & drop file upload |
|
|
1600
|
+
| `FileSelectorDialogComponent` | `lib-file-selector-dialog` | File browser dialog |
|
|
1663
1601
|
|
|
1664
1602
|
### Directives
|
|
1665
1603
|
|
|
1666
|
-
| Directive | Selector
|
|
1667
|
-
| --------------------------------- |
|
|
1668
|
-
| `HasPermissionDirective` | `[hasPermission]`
|
|
1604
|
+
| Directive | Selector | Description |
|
|
1605
|
+
| --------------------------------- | ----------------------------- | --------------------------------- |
|
|
1606
|
+
| `HasPermissionDirective` | `[hasPermission]` | Permission-based rendering |
|
|
1669
1607
|
| `EditModeElementChangerDirective` | `[appEditModeElementChanger]` | Toggle edit mode on form controls |
|
|
1670
|
-
| `IsEmptyImageDirective` | `img`
|
|
1671
|
-
| `PreventDefaultDirective` | `[appPreventDefault]`
|
|
1608
|
+
| `IsEmptyImageDirective` | `img` | Image fallback on error/empty |
|
|
1609
|
+
| `PreventDefaultDirective` | `[appPreventDefault]` | Prevent default event behavior |
|
|
1672
1610
|
|
|
1673
1611
|
### Guards
|
|
1674
1612
|
|
|
1675
|
-
| Guard
|
|
1676
|
-
|
|
|
1677
|
-
| `permissionGuard`
|
|
1678
|
-
| `anyPermissionGuard`
|
|
1679
|
-
| `allPermissionsGuard`
|
|
1613
|
+
| Guard | Description |
|
|
1614
|
+
| --------------------- | ------------------------------------- |
|
|
1615
|
+
| `permissionGuard` | Single permission or ILogicNode check |
|
|
1616
|
+
| `anyPermissionGuard` | OR logic (any of listed permissions) |
|
|
1617
|
+
| `allPermissionsGuard` | AND logic (all of listed permissions) |
|
|
1680
1618
|
|
|
1681
1619
|
### Interfaces
|
|
1682
1620
|
|
|
1683
|
-
| Interface
|
|
1684
|
-
|
|
|
1685
|
-
| `IBaseEntity`
|
|
1686
|
-
| `ILoggedUserInfo`
|
|
1687
|
-
| `IFilterData`
|
|
1688
|
-
| `IFilter`
|
|
1689
|
-
| `IDeleteData`
|
|
1690
|
-
| `IDropDown`
|
|
1691
|
-
| `ISingleResponse<T>`
|
|
1692
|
-
| `IListResponse<T>`
|
|
1693
|
-
| `IBulkResponse<T>`
|
|
1694
|
-
| `IMessageResponse`
|
|
1695
|
-
| `IErrorResponse`
|
|
1696
|
-
| `ILogicNode`
|
|
1697
|
-
| `IUserSelectFilter`
|
|
1698
|
-
| `LoadUsersFn`
|
|
1699
|
-
| `IFileBasicInfo`
|
|
1700
|
-
| `IFileUploadOptions
|
|
1701
|
-
| `IUploadedFile`
|
|
1702
|
-
| `IFileSelectFilter`
|
|
1703
|
-
| `LoadFilesFn`
|
|
1704
|
-
| `UploadFileFn`
|
|
1705
|
-
| `FilesResponseDto`
|
|
1706
|
-
| `IAuthStateProvider
|
|
1707
|
-
| `IUserListProvider`
|
|
1708
|
-
| `IUserListItem`
|
|
1709
|
-
| `IUserListAction`
|
|
1710
|
-
| `IUserListColumn`
|
|
1711
|
-
| `IUserListFilter`
|
|
1712
|
-
| `IUserBranchPermission` | User permissions per branch
|
|
1713
|
-
| `ServiceName`
|
|
1621
|
+
| Interface | Description |
|
|
1622
|
+
| ----------------------- | ------------------------------------------------------------------------------ |
|
|
1623
|
+
| `IBaseEntity` | Base entity with ID and timestamps |
|
|
1624
|
+
| `ILoggedUserInfo` | Current user info with company ctx |
|
|
1625
|
+
| `IFilterData` | Filter, pagination, sort payload |
|
|
1626
|
+
| `IFilter` | Filter object (supports arrays) |
|
|
1627
|
+
| `IDeleteData` | Delete request payload |
|
|
1628
|
+
| `IDropDown` | Simple label/value pair |
|
|
1629
|
+
| `ISingleResponse<T>` | Single item response |
|
|
1630
|
+
| `IListResponse<T>` | List with pagination |
|
|
1631
|
+
| `IBulkResponse<T>` | Bulk operation response |
|
|
1632
|
+
| `IMessageResponse` | Message-only response |
|
|
1633
|
+
| `IErrorResponse` | Error with validation details |
|
|
1634
|
+
| `ILogicNode` | Permission logic tree (AND/OR nodes) |
|
|
1635
|
+
| `IUserSelectFilter` | User select filter params |
|
|
1636
|
+
| `LoadUsersFn` | User loading function type |
|
|
1637
|
+
| `IFileBasicInfo` | Basic file info for selectors |
|
|
1638
|
+
| `IFileUploadOptions` | Upload options (compression, etc.) |
|
|
1639
|
+
| `IUploadedFile` | Uploaded file response |
|
|
1640
|
+
| `IFileSelectFilter` | File select filter params |
|
|
1641
|
+
| `LoadFilesFn` | File loading function type |
|
|
1642
|
+
| `UploadFileFn` | File upload function type |
|
|
1643
|
+
| `FilesResponseDto` | File URL service response |
|
|
1644
|
+
| `IAuthStateProvider` | Auth state provider interface |
|
|
1645
|
+
| `IUserListProvider` | User list extensions provider |
|
|
1646
|
+
| `IUserListItem` | Base user for list operations |
|
|
1647
|
+
| `IUserListAction` | User list action definition |
|
|
1648
|
+
| `IUserListColumn` | Extra column for user list |
|
|
1649
|
+
| `IUserListFilter` | User list filter parameters |
|
|
1650
|
+
| `IUserBranchPermission` | User permissions per branch |
|
|
1651
|
+
| `ServiceName` | `'auth' \| 'administration' \| 'iam' \| 'storage' \| 'formBuilder' \| 'email'` |
|
|
1714
1652
|
|
|
1715
1653
|
### Injection Tokens
|
|
1716
1654
|
|
|
1717
|
-
| Token
|
|
1718
|
-
|
|
|
1719
|
-
| `USER_PROVIDER`
|
|
1720
|
-
| `COMPANY_API_PROVIDER`
|
|
1721
|
-
| `USER_PERMISSION_PROVIDER`
|
|
1722
|
-
| `AUTH_STATE_PROVIDER`
|
|
1723
|
-
| `PROFILE_UPLOAD_PROVIDER`
|
|
1724
|
-
| `PROFILE_PERMISSION_PROVIDER` | `IProfilePermissionProvider` | Yes
|
|
1725
|
-
| `USER_LIST_PROVIDER`
|
|
1655
|
+
| Token | Interface | Optional | Description |
|
|
1656
|
+
| ----------------------------- | ---------------------------- | -------- | ------------------------------------- |
|
|
1657
|
+
| `USER_PROVIDER` | `IUserProvider` | No | User list for IAM |
|
|
1658
|
+
| `COMPANY_API_PROVIDER` | `ICompanyApiProvider` | No | Company list for IAM |
|
|
1659
|
+
| `USER_PERMISSION_PROVIDER` | `IUserPermissionProvider` | No | User permission queries |
|
|
1660
|
+
| `AUTH_STATE_PROVIDER` | `IAuthStateProvider` | No | Auth state for feature packages |
|
|
1661
|
+
| `PROFILE_UPLOAD_PROVIDER` | `IProfileUploadProvider` | Yes | Profile picture upload (ng-storage) |
|
|
1662
|
+
| `PROFILE_PERMISSION_PROVIDER` | `IProfilePermissionProvider` | Yes | User permissions for profile (ng-iam) |
|
|
1663
|
+
| `USER_LIST_PROVIDER` | `IUserListProvider` | Yes | User list extensions |
|
|
1726
1664
|
|
|
1727
1665
|
## See Also
|
|
1728
1666
|
|
|
@@ -1734,5 +1672,5 @@ providers: [
|
|
|
1734
1672
|
---
|
|
1735
1673
|
|
|
1736
1674
|
**Last Updated:** 2026-02-25
|
|
1737
|
-
**Version:** 3.0.
|
|
1675
|
+
**Version:** 3.0.1
|
|
1738
1676
|
**Angular Version:** 21
|