@flusys/ng-shared 1.0.0-beta → 1.0.0-rc

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 CHANGED
@@ -110,6 +110,77 @@ interface IDropDown {
110
110
  }
111
111
  ```
112
112
 
113
+ #### User Select Interfaces
114
+
115
+ ```typescript
116
+ interface IUserSelectFilter {
117
+ page: number;
118
+ pageSize: number;
119
+ search: string;
120
+ [key: string]: unknown;
121
+ }
122
+
123
+ type LoadUsersFn = (filter: IUserSelectFilter) => Observable<IListResponse<IUserBasicInfo>>;
124
+ ```
125
+
126
+ #### File Select Interfaces
127
+
128
+ ```typescript
129
+ interface IFileBasicInfo {
130
+ id: string;
131
+ name: string;
132
+ contentType: string;
133
+ size: string;
134
+ url: string | null;
135
+ }
136
+
137
+ interface IFileUploadOptions {
138
+ storageConfigId?: string;
139
+ folderPath?: string;
140
+ maxWidth?: number;
141
+ maxHeight?: number;
142
+ quality?: number;
143
+ compress?: boolean;
144
+ }
145
+
146
+ interface IUploadedFile {
147
+ name: string;
148
+ key: string;
149
+ size: number;
150
+ contentType: string;
151
+ }
152
+
153
+ interface IFileSelectFilter {
154
+ page: number;
155
+ pageSize: number;
156
+ search: string;
157
+ contentTypes?: string[];
158
+ folderId?: string;
159
+ [key: string]: unknown;
160
+ }
161
+
162
+ type LoadFilesFn = (filter: IFileSelectFilter) => Observable<IListResponse<IFileBasicInfo>>;
163
+ type UploadFileFn = (file: File, options?: IFileUploadOptions) => Observable<ISingleResponse<IUploadedFile>>;
164
+ type GetFileUrlsFn = (fileIds: string[]) => Observable<ISingleResponse<IFileBasicInfo[]>>;
165
+ ```
166
+
167
+ **File Type Filters (Constants):**
168
+
169
+ ```typescript
170
+ import { FILE_TYPE_FILTERS, getAcceptString, isFileTypeAllowed, getFileIconClass, formatFileSize } from '@flusys/ng-shared';
171
+
172
+ FILE_TYPE_FILTERS.IMAGES // ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml']
173
+ FILE_TYPE_FILTERS.DOCUMENTS // ['application/pdf', 'application/msword', ...]
174
+ FILE_TYPE_FILTERS.VIDEOS // ['video/mp4', 'video/webm', ...]
175
+ FILE_TYPE_FILTERS.AUDIO // ['audio/mpeg', 'audio/wav', ...]
176
+ FILE_TYPE_FILTERS.ALL // [] (allows all)
177
+
178
+ getAcceptString(['image/*']) // 'image/*'
179
+ isFileTypeAllowed(file, ['image/*']) // true/false
180
+ getFileIconClass('image/png') // 'pi pi-image'
181
+ formatFileSize(1024) // '1 KB'
182
+ ```
183
+
113
184
  ### Response Interfaces
114
185
 
115
186
  Type-safe interfaces matching FLUSYS_NEST backend response DTOs.
@@ -446,7 +517,7 @@ Flexible icon renderer supporting PrimeNG icons, image files, and SVG.
446
517
  <!-- Image file -->
447
518
  <lib-icon icon="/assets/logo.png" [iconType]="IconTypeEnum.IMAGE_FILE_LINK" />
448
519
 
449
- <!-- SVG tag -->
520
+ <!-- SVG tag (shows fallback icon - full SVG rendering coming soon) -->
450
521
  <lib-icon icon="<svg>...</svg>" [iconType]="IconTypeEnum.DIRECT_TAG_SVG" />
451
522
  ```
452
523
 
@@ -539,6 +610,176 @@ export class MyComponent {
539
610
 
540
611
  **Computed signals:** `selectedValueDisplay` (display text), `isSelectAll` (all items selected)
541
612
 
613
+ ### UserSelectComponent
614
+
615
+ Single user selection with lazy loading. Uses `USER_PROVIDER` internally or accepts custom `loadUsers` function.
616
+
617
+ - **Selector:** `lib-user-select`
618
+ - **Supports:** Template-driven, reactive forms, signal forms
619
+
620
+ ```typescript
621
+ @Component({
622
+ imports: [UserSelectComponent],
623
+ template: `
624
+ <!-- Simple usage - uses USER_PROVIDER internally -->
625
+ <lib-user-select
626
+ [(value)]="selectedUserId"
627
+ [isEditMode]="true"
628
+ />
629
+
630
+ <!-- With custom loadUsers function -->
631
+ <lib-user-select
632
+ [(value)]="selectedUserId"
633
+ [isEditMode]="true"
634
+ [loadUsers]="customLoadUsers"
635
+ />
636
+ `,
637
+ })
638
+ export class MyComponent {
639
+ readonly selectedUserId = signal<string | null>(null);
640
+ }
641
+ ```
642
+
643
+ **Inputs:**
644
+
645
+ | Input | Type | Default | Description |
646
+ | ----- | ---- | ------- | ----------- |
647
+ | `isEditMode` | `boolean` | required | Enable/disable editing |
648
+ | `placeHolder` | `string` | `'Select User'` | Placeholder text |
649
+ | `filterActive` | `boolean` | `true` | Filter active users only |
650
+ | `additionalFilters` | `Record<string, unknown>` | `{}` | Extra filter params |
651
+ | `pageSize` | `number` | `20` | Page size for pagination |
652
+ | `loadUsers` | `LoadUsersFn` | - | Custom user loading function |
653
+
654
+ **Model:** `value` - Two-way bound selected user ID (`string | null`)
655
+
656
+ **Outputs:** `userSelected` (IUserBasicInfo | null), `onError` (Error)
657
+
658
+ ### UserMultiSelectComponent
659
+
660
+ Multiple user selection with lazy loading. Uses `USER_PROVIDER` internally or accepts custom `loadUsers` function.
661
+
662
+ - **Selector:** `lib-user-multi-select`
663
+ - **Supports:** Template-driven, reactive forms, signal forms
664
+
665
+ ```typescript
666
+ @Component({
667
+ imports: [UserMultiSelectComponent],
668
+ template: `
669
+ <lib-user-multi-select
670
+ [(value)]="selectedUserIds"
671
+ [isEditMode]="true"
672
+ />
673
+ `,
674
+ })
675
+ export class MyComponent {
676
+ readonly selectedUserIds = signal<string[] | null>(null);
677
+ }
678
+ ```
679
+
680
+ **Inputs:** Same as `UserSelectComponent`.
681
+
682
+ **Model:** `value` - Two-way bound selected user IDs (`string[] | null`)
683
+
684
+ **Outputs:** `usersSelected` (IUserBasicInfo[]), `onError` (Error)
685
+
686
+ ### FileUploaderComponent
687
+
688
+ Drag & drop file upload with type filtering. Pass your own `uploadFile` function.
689
+
690
+ - **Selector:** `lib-file-uploader`
691
+
692
+ ```typescript
693
+ @Component({
694
+ imports: [FileUploaderComponent],
695
+ template: `
696
+ <!-- Single image upload -->
697
+ <lib-file-uploader
698
+ [uploadFile]="uploadFile"
699
+ [acceptTypes]="['image/*']"
700
+ [multiple]="false"
701
+ (fileUploaded)="onFileUploaded($event)"
702
+ />
703
+
704
+ <!-- Multiple document upload -->
705
+ <lib-file-uploader
706
+ [uploadFile]="uploadFile"
707
+ [acceptTypes]="FILE_TYPE_FILTERS.DOCUMENTS"
708
+ [multiple]="true"
709
+ [maxFiles]="5"
710
+ (filesUploaded)="onFilesUploaded($event)"
711
+ />
712
+ `,
713
+ })
714
+ export class MyComponent {
715
+ readonly uploadService = inject(UploadService);
716
+
717
+ readonly uploadFile: UploadFileFn = (file, options) =>
718
+ this.uploadService.uploadSingleFile(file, options);
719
+ }
720
+ ```
721
+
722
+ **Inputs:**
723
+
724
+ | Input | Type | Default | Description |
725
+ | ----- | ---- | ------- | ----------- |
726
+ | `uploadFile` | `UploadFileFn` | required | Upload function |
727
+ | `acceptTypes` | `string[]` | `[]` | Allowed MIME types |
728
+ | `multiple` | `boolean` | `false` | Allow multiple files |
729
+ | `maxFiles` | `number` | `10` | Max files for multiple |
730
+ | `maxSizeMb` | `number` | `10` | Max file size in MB |
731
+ | `uploadOptions` | `IFileUploadOptions` | `{}` | Upload options |
732
+ | `disabled` | `boolean` | `false` | Disable uploader |
733
+ | `showPreview` | `boolean` | `true` | Show selected files preview |
734
+ | `autoUpload` | `boolean` | `true` | Upload immediately on selection |
735
+
736
+ **Outputs:** `fileUploaded` (IUploadedFile), `filesUploaded` (IUploadedFile[]), `onError` (Error), `fileSelected` (File[])
737
+
738
+ ### FileSelectorDialogComponent
739
+
740
+ Dialog to browse and select existing files with filtering.
741
+
742
+ - **Selector:** `lib-file-selector-dialog`
743
+
744
+ ```typescript
745
+ @Component({
746
+ imports: [FileSelectorDialogComponent],
747
+ template: `
748
+ <lib-file-selector-dialog
749
+ [(visible)]="showFileSelector"
750
+ [loadFiles]="loadFiles"
751
+ [acceptTypes]="['image/*']"
752
+ [multiple]="false"
753
+ (fileSelected)="onFileSelected($event)"
754
+ />
755
+ `,
756
+ })
757
+ export class MyComponent {
758
+ readonly showFileSelector = signal(false);
759
+ readonly fileService = inject(FileManagerApiService);
760
+
761
+ readonly loadFiles: LoadFilesFn = (filter) =>
762
+ this.fileService.getAll(filter.search, {
763
+ pagination: { currentPage: filter.page, pageSize: filter.pageSize },
764
+ });
765
+ }
766
+ ```
767
+
768
+ **Inputs:**
769
+
770
+ | Input | Type | Default | Description |
771
+ | ----- | ---- | ------- | ----------- |
772
+ | `loadFiles` | `LoadFilesFn` | required | File loading function |
773
+ | `header` | `string` | `'Select File'` | Dialog header |
774
+ | `acceptTypes` | `string[]` | `[]` | Allowed MIME types |
775
+ | `multiple` | `boolean` | `false` | Allow multiple selection |
776
+ | `maxSelection` | `number` | `10` | Max files for multiple |
777
+ | `pageSize` | `number` | `20` | Page size for pagination |
778
+
779
+ **Model:** `visible` - Two-way bound dialog visibility (`boolean`)
780
+
781
+ **Outputs:** `fileSelected` (IFileBasicInfo), `filesSelected` (IFileBasicInfo[]), `closed` (void), `onError` (Error)
782
+
542
783
  ---
543
784
 
544
785
  ## 5. Directives
@@ -805,6 +1046,153 @@ interface IUserPermissionProvider {
805
1046
  }
806
1047
  ```
807
1048
 
1049
+ #### IProfileUploadProvider / `PROFILE_UPLOAD_PROVIDER`
1050
+
1051
+ Profile picture upload for ng-auth profile page. Implemented by ng-storage.
1052
+
1053
+ ```typescript
1054
+ interface IProfileUploadResult {
1055
+ id: string; // File manager ID (UUID)
1056
+ name: string; // Original file name
1057
+ key: string; // Storage key/path
1058
+ size: number; // File size in bytes
1059
+ contentType: string; // MIME type
1060
+ }
1061
+
1062
+ interface IProfileUploadOptions {
1063
+ folderPath?: string;
1064
+ compress?: boolean;
1065
+ maxWidth?: number;
1066
+ maxHeight?: number;
1067
+ }
1068
+
1069
+ interface IProfileUploadProvider {
1070
+ uploadProfilePicture(
1071
+ file: File,
1072
+ options?: IProfileUploadOptions
1073
+ ): Observable<ISingleResponse<IProfileUploadResult>>;
1074
+ }
1075
+ ```
1076
+
1077
+ #### IProfilePermissionProvider / `PROFILE_PERMISSION_PROVIDER`
1078
+
1079
+ User permission queries for ng-auth profile page. Implemented by ng-iam.
1080
+
1081
+ ```typescript
1082
+ interface IProfileRoleInfo {
1083
+ id: string;
1084
+ name: string;
1085
+ description?: string | null;
1086
+ }
1087
+
1088
+ interface IProfileActionInfo {
1089
+ id: string;
1090
+ code: string;
1091
+ name: string;
1092
+ description?: string | null;
1093
+ }
1094
+
1095
+ interface IProfilePermissionProvider {
1096
+ getUserRoles(userId: string, branchId?: string): Observable<ISingleResponse<IProfileRoleInfo[]>>;
1097
+ getUserActions(userId: string, branchId?: string): Observable<ISingleResponse<IProfileActionInfo[]>>;
1098
+ }
1099
+ ```
1100
+
1101
+ #### IAuthStateProvider / `AUTH_STATE_PROVIDER`
1102
+
1103
+ Auth state access for feature packages (form-builder, etc.) that need to check authentication without depending on ng-auth directly.
1104
+
1105
+ ```typescript
1106
+ interface IAuthStateProvider {
1107
+ /** Signal indicating if user is currently authenticated */
1108
+ isAuthenticated: Signal<boolean>;
1109
+
1110
+ /** Initialize auth state (restore session from cookies/storage) */
1111
+ initialize(): Observable<void>;
1112
+ }
1113
+ ```
1114
+
1115
+ **Usage:**
1116
+
1117
+ ```typescript
1118
+ import { AUTH_STATE_PROVIDER } from '@flusys/ng-shared';
1119
+
1120
+ @Component({...})
1121
+ export class PublicFormComponent {
1122
+ private readonly authState = inject(AUTH_STATE_PROVIDER);
1123
+
1124
+ ngOnInit() {
1125
+ this.authState.initialize().subscribe(() => {
1126
+ if (this.authState.isAuthenticated()) {
1127
+ // User is logged in
1128
+ }
1129
+ });
1130
+ }
1131
+ }
1132
+ ```
1133
+
1134
+ #### IUserListProvider / `USER_LIST_PROVIDER`
1135
+
1136
+ Extends user list pages with extra columns, actions, and data enrichment. Optional provider.
1137
+
1138
+ ```typescript
1139
+ interface IUserListItem {
1140
+ id: string;
1141
+ name: string;
1142
+ email: string;
1143
+ phone?: string;
1144
+ isActive?: boolean;
1145
+ [key: string]: unknown;
1146
+ }
1147
+
1148
+ interface IUserListAction<T = IUserListItem> {
1149
+ id: string;
1150
+ label: string;
1151
+ icon?: string;
1152
+ severity?: 'primary' | 'secondary' | 'success' | 'info' | 'warn' | 'danger';
1153
+ permission?: string;
1154
+ tooltip?: string;
1155
+ disabled?: boolean | ((user: T) => boolean);
1156
+ visible?: boolean | ((user: T) => boolean);
1157
+ }
1158
+
1159
+ interface IUserListColumn {
1160
+ field: string;
1161
+ header: string;
1162
+ width?: string;
1163
+ sortable?: boolean;
1164
+ templateType?: 'text' | 'badge' | 'date' | 'boolean' | 'custom';
1165
+ }
1166
+
1167
+ interface IUserListProvider<T extends IUserListItem = IUserListItem> {
1168
+ getExtraColumns?(): IUserListColumn[];
1169
+ getExtraRowActions?(): IUserListAction<T>[];
1170
+ getExtraToolbarActions?(): IUserListAction<T>[];
1171
+ onRowAction?(actionId: string, user: T): void;
1172
+ onToolbarAction?(actionId: string, selectedUsers: T[]): void;
1173
+ enrichListData?(users: T[]): Observable<T[]>;
1174
+ }
1175
+ ```
1176
+
1177
+ **Usage:**
1178
+
1179
+ ```typescript
1180
+ // In app.config.ts
1181
+ providers: [
1182
+ { provide: USER_LIST_PROVIDER, useClass: MyUserListProvider },
1183
+ ]
1184
+
1185
+ // Implementation
1186
+ @Injectable({ providedIn: 'root' })
1187
+ export class MyUserListProvider implements IUserListProvider {
1188
+ getExtraRowActions() {
1189
+ return [
1190
+ { id: 'assign-role', label: 'Assign Role', icon: 'pi pi-users' },
1191
+ ];
1192
+ }
1193
+ }
1194
+ ```
1195
+
808
1196
  ### Usage in Consuming Packages
809
1197
 
810
1198
  ```typescript
@@ -931,6 +1319,10 @@ export const appConfig: ApplicationConfig = {
931
1319
  | `IconComponent` | `lib-icon` | Flexible icon renderer |
932
1320
  | `LazySelectComponent` | `lib-lazy-select` | Lazy-loading dropdown |
933
1321
  | `LazyMultiSelectComponent` | `lib-lazy-multi-select`| Lazy-loading multi-select |
1322
+ | `UserSelectComponent` | `lib-user-select` | Single user selector |
1323
+ | `UserMultiSelectComponent` | `lib-user-multi-select`| Multiple user selector |
1324
+ | `FileUploaderComponent` | `lib-file-uploader` | Drag & drop file upload |
1325
+ | `FileSelectorDialogComponent` | `lib-file-selector-dialog` | File browser dialog |
934
1326
 
935
1327
  ### Directives
936
1328
 
@@ -964,6 +1356,19 @@ export const appConfig: ApplicationConfig = {
964
1356
  | `IMessageResponse` | Message-only response |
965
1357
  | `IErrorResponse` | Error with validation details |
966
1358
  | `ILogicNode` | Permission logic tree (AND/OR nodes) |
1359
+ | `IUserSelectFilter` | User select filter params |
1360
+ | `LoadUsersFn` | User loading function type |
1361
+ | `IFileBasicInfo` | Basic file info for selectors |
1362
+ | `IFileUploadOptions`| Upload options (compression, etc.) |
1363
+ | `IUploadedFile` | Uploaded file response |
1364
+ | `IFileSelectFilter` | File select filter params |
1365
+ | `LoadFilesFn` | File loading function type |
1366
+ | `UploadFileFn` | File upload function type |
1367
+ | `IAuthStateProvider`| Auth state provider interface |
1368
+ | `IUserListProvider` | User list extensions provider |
1369
+ | `IUserListItem` | Base user for list operations |
1370
+ | `IUserListAction` | User list action definition |
1371
+ | `IUserListColumn` | Extra column for user list |
967
1372
 
968
1373
  ### Injection Tokens
969
1374
 
@@ -972,6 +1377,10 @@ export const appConfig: ApplicationConfig = {
972
1377
  | `USER_PROVIDER` | `IUserProvider` | User list for IAM |
973
1378
  | `COMPANY_API_PROVIDER` | `ICompanyApiProvider` | Company list for IAM |
974
1379
  | `USER_PERMISSION_PROVIDER` | `IUserPermissionProvider` | User permission queries |
1380
+ | `PROFILE_UPLOAD_PROVIDER` | `IProfileUploadProvider` | Profile picture upload (ng-storage) |
1381
+ | `PROFILE_PERMISSION_PROVIDER` | `IProfilePermissionProvider` | User permissions for profile (ng-iam) |
1382
+ | `AUTH_STATE_PROVIDER` | `IAuthStateProvider` | Auth state for feature packages |
1383
+ | `USER_LIST_PROVIDER` | `IUserListProvider` | User list extensions (optional) |
975
1384
 
976
1385
  ## See Also
977
1386
 
@@ -982,5 +1391,5 @@ export const appConfig: ApplicationConfig = {
982
1391
 
983
1392
  ---
984
1393
 
985
- **Last Updated:** 2026-02-15
1394
+ **Last Updated:** 2026-02-18
986
1395
  **Angular Version:** 21