@flusys/ng-auth 1.1.0-beta → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +364 -21
- package/fesm2022/flusys-ng-auth.mjs +1381 -1827
- package/fesm2022/flusys-ng-auth.mjs.map +1 -1
- package/package.json +11 -11
- package/types/flusys-ng-auth.d.ts +180 -651
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
`@flusys/ng-auth` provides complete authentication functionality including login, registration, token management, company/branch selection, user administration, and permission handling.
|
|
6
6
|
|
|
7
7
|
**Package:** `@flusys/ng-auth`
|
|
8
|
+
**Version:** 1.1.0
|
|
8
9
|
**Dependencies:** ng-core, ng-shared, ng-layout
|
|
9
10
|
**Build:** `npm run build:ng-auth`
|
|
10
11
|
|
|
@@ -27,7 +28,9 @@
|
|
|
27
28
|
│ Guards │
|
|
28
29
|
│ ├── authGuard (requires authentication) │
|
|
29
30
|
│ ├── guestGuard (allows only unauthenticated) │
|
|
30
|
-
│
|
|
31
|
+
│ ├── companyFeatureGuard (requires company feature enabled) │
|
|
32
|
+
│ ├── emailFeatureGuard (requires email service enabled) │
|
|
33
|
+
│ └── signUpFeatureGuard (requires sign-up enabled) │
|
|
31
34
|
├─────────────────────────────────────────────────────────────┤
|
|
32
35
|
│ Interceptors │
|
|
33
36
|
│ ├── authInterceptor (adds JWT + tenant headers) │
|
|
@@ -273,7 +276,39 @@ import { companyFeatureGuard } from '@flusys/ng-auth';
|
|
|
273
276
|
}
|
|
274
277
|
```
|
|
275
278
|
|
|
276
|
-
**Checks:** `
|
|
279
|
+
**Checks:** `enableCompanyFeature` in APP_CONFIG. Redirects to `/` if disabled.
|
|
280
|
+
|
|
281
|
+
### emailFeatureGuard
|
|
282
|
+
|
|
283
|
+
Protects email-related routes (forgot-password, reset-password, verify-email).
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
import { emailFeatureGuard } from '@flusys/ng-auth';
|
|
287
|
+
|
|
288
|
+
{
|
|
289
|
+
path: 'auth/forgot-password',
|
|
290
|
+
canActivate: [emailFeatureGuard, guestGuard],
|
|
291
|
+
loadComponent: () => import('./forgot-password.component')
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**Checks:** `services.email.enabled` in APP_CONFIG. Redirects to `/auth/login` if disabled.
|
|
296
|
+
|
|
297
|
+
### signUpFeatureGuard
|
|
298
|
+
|
|
299
|
+
Protects registration route when sign-up is disabled.
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
import { signUpFeatureGuard } from '@flusys/ng-auth';
|
|
303
|
+
|
|
304
|
+
{
|
|
305
|
+
path: 'auth/register',
|
|
306
|
+
canActivate: [signUpFeatureGuard, guestGuard],
|
|
307
|
+
loadComponent: () => import('./register.component')
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Checks:** `enableSignUp` in APP_CONFIG (defaults to `true`). Redirects to `/auth/login` if `false`.
|
|
277
312
|
|
|
278
313
|
---
|
|
279
314
|
|
|
@@ -324,7 +359,7 @@ provideHttpClient(
|
|
|
324
359
|
Centralized endpoint definitions used by interceptors.
|
|
325
360
|
|
|
326
361
|
```typescript
|
|
327
|
-
import { AUTH_ENDPOINTS,
|
|
362
|
+
import { AUTH_ENDPOINTS, isEndpoint } from '@flusys/ng-auth';
|
|
328
363
|
|
|
329
364
|
// Endpoint constants
|
|
330
365
|
AUTH_ENDPOINTS.LOGIN // '/auth/login'
|
|
@@ -334,8 +369,7 @@ AUTH_ENDPOINTS.REGISTER // '/auth/register'
|
|
|
334
369
|
AUTH_ENDPOINTS.SELECT // '/auth/select'
|
|
335
370
|
AUTH_ENDPOINTS.SWITCH_COMPANY // '/auth/switch-company'
|
|
336
371
|
|
|
337
|
-
// Helper
|
|
338
|
-
isAuthEndpoint(url); // Check if URL is any auth endpoint
|
|
372
|
+
// Helper function
|
|
339
373
|
isEndpoint(url, endpoint); // Check if URL matches specific endpoint
|
|
340
374
|
```
|
|
341
375
|
|
|
@@ -393,27 +427,61 @@ getCompanyBranches(companyId); // User's permitted branches
|
|
|
393
427
|
|
|
394
428
|
## Pages
|
|
395
429
|
|
|
430
|
+
All page components follow Angular 21 best practices:
|
|
431
|
+
- `ChangeDetectionStrategy.OnPush` for optimal performance
|
|
432
|
+
- Signal Forms (`@angular/forms/signals`) with validation
|
|
433
|
+
- 100% responsive design (mobile-first with Tailwind)
|
|
434
|
+
- Dark mode compatible (PrimeNG + Tailwind CSS variables)
|
|
435
|
+
- `inject()` for dependency injection
|
|
436
|
+
|
|
396
437
|
### Authentication Pages
|
|
397
438
|
|
|
398
439
|
**LoginPageComponent** (`/auth/login`)
|
|
399
440
|
- Multi-step login: Step 1 = credentials, Step 2 = company/branch selection (if needed)
|
|
400
|
-
- Visual step indicator
|
|
441
|
+
- Visual step indicator (when company feature enabled)
|
|
442
|
+
- Email verification step (when email not verified)
|
|
401
443
|
- Auto-selects if only one company/branch available
|
|
402
444
|
- Stores returnUrl for post-login redirect
|
|
445
|
+
- Responsive form layout with mobile padding
|
|
446
|
+
- **Conditional UI based on config:**
|
|
447
|
+
- "Forgot password?" link hidden when `services.email.enabled: false`
|
|
448
|
+
- "Sign Up" link hidden when `enableSignUp: false`
|
|
403
449
|
|
|
404
450
|
**RegisterPageComponent** (`/auth/register`)
|
|
405
451
|
- User registration with optional company creation
|
|
406
|
-
-
|
|
407
|
-
-
|
|
452
|
+
- Two-column responsive grid layout
|
|
453
|
+
- Email/password validation with signal forms
|
|
454
|
+
- Company details section (when company feature enabled)
|
|
455
|
+
- **Default company/branch support:**
|
|
456
|
+
- When `defaultCompanySlug` is set: pre-fills company code field and makes it readonly
|
|
457
|
+
- When `defaultBranchSlug` is set: pre-fills branch code field and makes it readonly
|
|
458
|
+
- "Create new company" option hidden when defaults are configured
|
|
459
|
+
|
|
460
|
+
**ForgotPasswordPageComponent** (`/auth/forgot-password`)
|
|
461
|
+
- Email input with validation
|
|
462
|
+
- Success state showing email sent confirmation
|
|
463
|
+
|
|
464
|
+
**ResetPasswordPageComponent** (`/auth/reset-password`)
|
|
465
|
+
- Password reset with token validation
|
|
466
|
+
- Password confirmation matching
|
|
467
|
+
- Success/invalid token states
|
|
468
|
+
|
|
469
|
+
**VerifyEmailPageComponent** (`/auth/verify-email`)
|
|
470
|
+
- Token verification with SSR support (TransferState)
|
|
471
|
+
- Resend verification option
|
|
472
|
+
- Success/failure states
|
|
408
473
|
|
|
409
474
|
### Profile Page
|
|
410
475
|
|
|
411
476
|
**ProfilePageComponent** (`/profile`)
|
|
477
|
+
- Page title with description
|
|
478
|
+
- Avatar section with initials fallback
|
|
412
479
|
- Update name and phone
|
|
413
480
|
- Upload profile picture (requires `PROFILE_UPLOAD_PROVIDER` from ng-storage)
|
|
414
|
-
- Change password
|
|
481
|
+
- Change password section with strength indicator
|
|
415
482
|
- View company/branch info (if company feature enabled)
|
|
416
483
|
- View roles/actions (requires `PROFILE_PERMISSION_PROVIDER` from ng-iam)
|
|
484
|
+
- Responsive two-column layout on desktop
|
|
417
485
|
|
|
418
486
|
**Optional Provider Dependencies:**
|
|
419
487
|
|
|
@@ -430,25 +498,42 @@ getCompanyBranches(companyId); // User's permitted branches
|
|
|
430
498
|
### Administration Pages
|
|
431
499
|
|
|
432
500
|
**AdministrationPageComponent** (`/administration`)
|
|
433
|
-
-
|
|
434
|
-
-
|
|
501
|
+
- Page title "Administration" with dynamic subtitle
|
|
502
|
+
- Horizontal scrolling tabs (hidden scrollbar on mobile)
|
|
503
|
+
- Tabs: Users, Companies, Branches
|
|
504
|
+
- Company/Branch tabs only visible when company feature enabled
|
|
435
505
|
|
|
436
506
|
**User Management:**
|
|
437
507
|
- `UserListComponent` - LazyLoad datatable with Edit, Delete, Permission actions
|
|
438
|
-
- `UserFormComponent` - Create/edit user
|
|
508
|
+
- `UserFormComponent` - Create/edit user with auto company/branch assignment
|
|
509
|
+
- Permission dialogs for company/branch assignments
|
|
439
510
|
|
|
440
511
|
**Company Management:**
|
|
441
|
-
- `CompanyListComponent` - Company CRUD
|
|
442
|
-
- `CompanyFormComponent` - Create/edit company
|
|
512
|
+
- `CompanyListComponent` - Company CRUD with View Branches action
|
|
513
|
+
- `CompanyFormComponent` - Create/edit company with auto slug generation
|
|
443
514
|
|
|
444
515
|
**Branch Management:**
|
|
445
|
-
- `BranchListComponent` - Branch CRUD filtered by company
|
|
446
|
-
- `BranchFormComponent` - Create/edit branch
|
|
516
|
+
- `BranchListComponent` - Branch CRUD filtered by company dropdown
|
|
517
|
+
- `BranchFormComponent` - Create/edit branch with company selection
|
|
518
|
+
|
|
519
|
+
**Table Features (all list components):**
|
|
520
|
+
- Responsive header (stacked on mobile)
|
|
521
|
+
- Horizontal scroll wrapper (`overflow-x-auto`)
|
|
522
|
+
- Conditional pagination (`[paginator]="total() > 0"`)
|
|
523
|
+
- Striped rows (`p-datatable-striped`)
|
|
524
|
+
- Lazy loading for performance
|
|
447
525
|
|
|
448
526
|
---
|
|
449
527
|
|
|
450
528
|
## Dialog Components
|
|
451
529
|
|
|
530
|
+
Both dialog components use Angular 21 patterns:
|
|
531
|
+
- `ChangeDetectionStrategy.OnPush`
|
|
532
|
+
- `input.required<T>()` for required inputs
|
|
533
|
+
- `output<void>()` for events
|
|
534
|
+
- `effect()` for reactive data loading
|
|
535
|
+
- Responsive breakpoints (`{ '960px': '90vw', '640px': '95vw' }`)
|
|
536
|
+
|
|
452
537
|
### UserCompanyPermissionDialogComponent
|
|
453
538
|
|
|
454
539
|
Manages user-to-company assignments with checkboxes.
|
|
@@ -468,11 +553,21 @@ import { UserCompanyPermissionDialogComponent } from '@flusys/ng-auth';
|
|
|
468
553
|
})
|
|
469
554
|
```
|
|
470
555
|
|
|
556
|
+
**Inputs/Outputs:**
|
|
557
|
+
```typescript
|
|
558
|
+
readonly visible = input.required<boolean>();
|
|
559
|
+
readonly user = input.required<IUser | null>();
|
|
560
|
+
readonly closed = output<void>();
|
|
561
|
+
readonly permissionsChanged = output<void>();
|
|
562
|
+
```
|
|
563
|
+
|
|
471
564
|
**Features:**
|
|
472
565
|
- Shows all companies with checkboxes
|
|
473
566
|
- Pre-checks assigned companies
|
|
474
567
|
- Immediate save on toggle (no Save button)
|
|
475
568
|
- Toast notifications for success/error
|
|
569
|
+
- Loading spinner during data fetch
|
|
570
|
+
- Disabled checkboxes while saving
|
|
476
571
|
|
|
477
572
|
### UserBranchPermissionDialogComponent
|
|
478
573
|
|
|
@@ -494,13 +589,14 @@ import { UserBranchPermissionDialogComponent } from '@flusys/ng-auth';
|
|
|
494
589
|
```
|
|
495
590
|
|
|
496
591
|
**Features:**
|
|
497
|
-
-
|
|
498
|
-
-
|
|
592
|
+
- Company dropdown shows only user's permitted companies
|
|
593
|
+
- Branch list loads on company selection
|
|
499
594
|
- Immediate save on toggle
|
|
595
|
+
- Contextual empty messages (no permissions vs no branches)
|
|
500
596
|
- Scoped to user's permitted companies
|
|
501
597
|
|
|
502
598
|
**Workflow:**
|
|
503
|
-
1. Dialog opens → Loads user's permitted companies
|
|
599
|
+
1. Dialog opens → Loads user's permitted companies via effect()
|
|
504
600
|
2. User selects company → Loads branches for that company
|
|
505
601
|
3. User toggles branch checkboxes → Immediately assigns/revokes
|
|
506
602
|
|
|
@@ -706,6 +802,67 @@ interface IUserBranchPermission { id: string; userId: string; branchId: string;
|
|
|
706
802
|
|
|
707
803
|
## Best Practices
|
|
708
804
|
|
|
805
|
+
### Angular 21 Component Patterns
|
|
806
|
+
|
|
807
|
+
All components in ng-auth follow these patterns:
|
|
808
|
+
|
|
809
|
+
```typescript
|
|
810
|
+
@Component({
|
|
811
|
+
selector: 'lib-example',
|
|
812
|
+
standalone: true,
|
|
813
|
+
changeDetection: ChangeDetectionStrategy.OnPush, // Always OnPush
|
|
814
|
+
imports: [AngularModule, PrimeModule, FormField],
|
|
815
|
+
template: `...`
|
|
816
|
+
})
|
|
817
|
+
export class ExampleComponent {
|
|
818
|
+
// Dependency injection via inject()
|
|
819
|
+
private readonly service = inject(MyService);
|
|
820
|
+
|
|
821
|
+
// Signal-based state
|
|
822
|
+
readonly isLoading = signal(false);
|
|
823
|
+
readonly data = signal<IData | null>(null);
|
|
824
|
+
|
|
825
|
+
// Computed signals
|
|
826
|
+
readonly isValid = computed(() => this.data() !== null);
|
|
827
|
+
|
|
828
|
+
// Signal Forms
|
|
829
|
+
readonly formModel = signal<IFormModel>({ name: '', email: '' });
|
|
830
|
+
readonly formTree = form(this.formModel, (f) => {
|
|
831
|
+
required(f.name, { message: 'Name is required' });
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
// Error handling with proper typing
|
|
835
|
+
async onSubmit(): Promise<void> {
|
|
836
|
+
try {
|
|
837
|
+
// ...
|
|
838
|
+
} catch (error: unknown) {
|
|
839
|
+
const err = error as { error?: { message?: string } };
|
|
840
|
+
// Handle error
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
### Responsive Design Patterns
|
|
847
|
+
|
|
848
|
+
```html
|
|
849
|
+
<!-- Responsive container -->
|
|
850
|
+
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 p-4">
|
|
851
|
+
|
|
852
|
+
<!-- Two-column form grid -->
|
|
853
|
+
<form class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
854
|
+
|
|
855
|
+
<!-- Right-aligned buttons -->
|
|
856
|
+
<div class="flex justify-end gap-4 md:col-span-2 pt-4">
|
|
857
|
+
|
|
858
|
+
<!-- Horizontally scrollable tabs -->
|
|
859
|
+
<div class="scrollbar-hide flex gap-1 overflow-x-auto flex-nowrap">
|
|
860
|
+
|
|
861
|
+
<!-- Responsive table wrapper -->
|
|
862
|
+
<div class="overflow-x-auto">
|
|
863
|
+
<p-table [scrollable]="true" [paginator]="total() > 0">
|
|
864
|
+
```
|
|
865
|
+
|
|
709
866
|
### Component Organization
|
|
710
867
|
|
|
711
868
|
```
|
|
@@ -716,7 +873,14 @@ projects/ng-auth/
|
|
|
716
873
|
├── pages/ ← Route-specific pages
|
|
717
874
|
│ ├── login/
|
|
718
875
|
│ ├── register/
|
|
719
|
-
│
|
|
876
|
+
│ ├── forgot-password/
|
|
877
|
+
│ ├── reset-password/
|
|
878
|
+
│ ├── verify-email/
|
|
879
|
+
│ ├── profile/
|
|
880
|
+
│ ├── administration/
|
|
881
|
+
│ ├── users/
|
|
882
|
+
│ ├── company/
|
|
883
|
+
│ └── branch/
|
|
720
884
|
├── services/
|
|
721
885
|
├── guards/
|
|
722
886
|
├── interceptors/
|
|
@@ -745,6 +909,27 @@ const allCompanies = await companyApi.fetchList(); // Security risk
|
|
|
745
909
|
- TokenRefreshInterceptor handles automatic refresh
|
|
746
910
|
- Auto-persistence for company/branch selection
|
|
747
911
|
|
|
912
|
+
### Type Safety
|
|
913
|
+
|
|
914
|
+
Avoid `any` types - use explicit interfaces:
|
|
915
|
+
|
|
916
|
+
```typescript
|
|
917
|
+
// ✅ CORRECT - Explicit response type
|
|
918
|
+
.subscribe({
|
|
919
|
+
next: (result: { message?: string }) => {
|
|
920
|
+
this.messageService.add({
|
|
921
|
+
severity: 'success',
|
|
922
|
+
detail: result.message,
|
|
923
|
+
});
|
|
924
|
+
},
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
// ❌ WRONG - Using any
|
|
928
|
+
.subscribe({
|
|
929
|
+
next: (result: any) => { ... },
|
|
930
|
+
});
|
|
931
|
+
```
|
|
932
|
+
|
|
748
933
|
### Multi-Company Flow
|
|
749
934
|
|
|
750
935
|
1. Login → Check if `requiresSelection: true`
|
|
@@ -801,6 +986,143 @@ providers: [
|
|
|
801
986
|
|
|
802
987
|
---
|
|
803
988
|
|
|
989
|
+
## No-IAM Mode (Bypassing Permission Guards)
|
|
990
|
+
|
|
991
|
+
When you don't use the `@flusys/ng-iam` module but your routes use `permissionGuard`, the guard will fail because no permissions are loaded.
|
|
992
|
+
|
|
993
|
+
**Solution:** Set wildcard permissions in `PermissionValidatorService` after login.
|
|
994
|
+
|
|
995
|
+
### How Permission Guard Works
|
|
996
|
+
|
|
997
|
+
```
|
|
998
|
+
Route with permissionGuard('user.view')
|
|
999
|
+
↓
|
|
1000
|
+
permissionGuard() → inject(PermissionValidatorService)
|
|
1001
|
+
↓
|
|
1002
|
+
permissionValidator.isPermissionsLoaded() → false
|
|
1003
|
+
↓
|
|
1004
|
+
Redirect to '/' ❌
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
### Fix: Set Wildcard Permissions After Login
|
|
1008
|
+
|
|
1009
|
+
#### Option 1: In Login Flow (Recommended)
|
|
1010
|
+
|
|
1011
|
+
Update your login handling to set permissions after successful authentication:
|
|
1012
|
+
|
|
1013
|
+
```typescript
|
|
1014
|
+
// login.component.ts or auth.service.ts
|
|
1015
|
+
import { PermissionValidatorService } from '@flusys/ng-shared';
|
|
1016
|
+
|
|
1017
|
+
@Component({ ... })
|
|
1018
|
+
export class LoginComponent {
|
|
1019
|
+
private readonly permissionValidator = inject(PermissionValidatorService);
|
|
1020
|
+
private readonly authApi = inject(AuthApiService);
|
|
1021
|
+
private readonly authState = inject(AuthStateService);
|
|
1022
|
+
|
|
1023
|
+
async onLogin(): Promise<void> {
|
|
1024
|
+
const response = await firstValueFrom(this.authApi.login(credentials));
|
|
1025
|
+
this.authState.setLoginState(response);
|
|
1026
|
+
|
|
1027
|
+
// When IAM is NOT used, grant all permissions via wildcard
|
|
1028
|
+
this.permissionValidator.setPermissions(['*']);
|
|
1029
|
+
|
|
1030
|
+
// Navigate to dashboard
|
|
1031
|
+
this.router.navigate(['/dashboard']);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
```
|
|
1035
|
+
|
|
1036
|
+
#### Option 2: In APP_INITIALIZER
|
|
1037
|
+
|
|
1038
|
+
Set permissions at app startup:
|
|
1039
|
+
|
|
1040
|
+
```typescript
|
|
1041
|
+
// app.config.ts
|
|
1042
|
+
import { APP_INITIALIZER } from '@angular/core';
|
|
1043
|
+
import { PermissionValidatorService } from '@flusys/ng-shared';
|
|
1044
|
+
|
|
1045
|
+
export const appConfig: ApplicationConfig = {
|
|
1046
|
+
providers: [
|
|
1047
|
+
{
|
|
1048
|
+
provide: APP_INITIALIZER,
|
|
1049
|
+
useFactory: (permissionValidator: PermissionValidatorService) => {
|
|
1050
|
+
return () => {
|
|
1051
|
+
// Set wildcard permissions when IAM is disabled
|
|
1052
|
+
permissionValidator.setPermissions(['*']);
|
|
1053
|
+
};
|
|
1054
|
+
},
|
|
1055
|
+
deps: [PermissionValidatorService],
|
|
1056
|
+
multi: true,
|
|
1057
|
+
},
|
|
1058
|
+
// ... other providers
|
|
1059
|
+
],
|
|
1060
|
+
};
|
|
1061
|
+
```
|
|
1062
|
+
|
|
1063
|
+
#### Option 3: In AuthInitService (After Session Restore)
|
|
1064
|
+
|
|
1065
|
+
If you restore sessions on page refresh, set permissions there:
|
|
1066
|
+
|
|
1067
|
+
```typescript
|
|
1068
|
+
// After successful /auth/me response
|
|
1069
|
+
authInit.initialize().subscribe({
|
|
1070
|
+
next: (user) => {
|
|
1071
|
+
// Session restored, set wildcard permissions
|
|
1072
|
+
permissionValidator.setPermissions(['*']);
|
|
1073
|
+
},
|
|
1074
|
+
});
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
### Wildcard Permission Syntax
|
|
1078
|
+
|
|
1079
|
+
The `permissionGuard` utilities support these patterns:
|
|
1080
|
+
|
|
1081
|
+
| Pattern | Meaning |
|
|
1082
|
+
|---------|---------|
|
|
1083
|
+
| `*` | Full access to everything |
|
|
1084
|
+
| `user.*` | All user permissions |
|
|
1085
|
+
| `branch.*` | All branch permissions |
|
|
1086
|
+
|
|
1087
|
+
### For Jest Tests
|
|
1088
|
+
|
|
1089
|
+
Mock `PermissionValidatorService` in your test setup:
|
|
1090
|
+
|
|
1091
|
+
```typescript
|
|
1092
|
+
import { PermissionValidatorService } from '@flusys/ng-shared';
|
|
1093
|
+
|
|
1094
|
+
describe('MyComponent', () => {
|
|
1095
|
+
beforeEach(async () => {
|
|
1096
|
+
await TestBed.configureTestingModule({
|
|
1097
|
+
imports: [MyComponent],
|
|
1098
|
+
providers: [
|
|
1099
|
+
{
|
|
1100
|
+
provide: PermissionValidatorService,
|
|
1101
|
+
useValue: {
|
|
1102
|
+
permissions: () => ['*'],
|
|
1103
|
+
isPermissionsLoaded: () => true,
|
|
1104
|
+
hasPermission: () => true,
|
|
1105
|
+
setPermissions: jest.fn(),
|
|
1106
|
+
clearPermissions: jest.fn(),
|
|
1107
|
+
},
|
|
1108
|
+
},
|
|
1109
|
+
],
|
|
1110
|
+
}).compileComponents();
|
|
1111
|
+
});
|
|
1112
|
+
});
|
|
1113
|
+
```
|
|
1114
|
+
|
|
1115
|
+
### For Template Permission Checks
|
|
1116
|
+
|
|
1117
|
+
If using `*hasPermission` directive, it will also respect wildcard:
|
|
1118
|
+
|
|
1119
|
+
```html
|
|
1120
|
+
<!-- This will show when permissions include '*' -->
|
|
1121
|
+
<button *hasPermission="'user.delete'">Delete User</button>
|
|
1122
|
+
```
|
|
1123
|
+
|
|
1124
|
+
---
|
|
1125
|
+
|
|
804
1126
|
## See Also
|
|
805
1127
|
|
|
806
1128
|
- [CORE-GUIDE.md](./CORE-GUIDE.md) - APP_CONFIG configuration
|
|
@@ -811,5 +1133,26 @@ providers: [
|
|
|
811
1133
|
|
|
812
1134
|
---
|
|
813
1135
|
|
|
814
|
-
**Last Updated:** 2026-02-
|
|
1136
|
+
**Last Updated:** 2026-02-23
|
|
1137
|
+
**Version:** 1.1.0
|
|
815
1138
|
**Angular Version:** 21
|
|
1139
|
+
|
|
1140
|
+
## Component Checklist
|
|
1141
|
+
|
|
1142
|
+
| Component | OnPush | Signal Forms | Responsive | Dark Mode |
|
|
1143
|
+
|-----------|--------|--------------|------------|-----------|
|
|
1144
|
+
| LoginPageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1145
|
+
| RegisterPageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1146
|
+
| ForgotPasswordPageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1147
|
+
| ResetPasswordPageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1148
|
+
| VerifyEmailPageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1149
|
+
| ProfilePageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1150
|
+
| AdministrationPageComponent | ✅ | N/A | ✅ | ✅ |
|
|
1151
|
+
| UserListComponent | ✅ | N/A | ✅ | ✅ |
|
|
1152
|
+
| UserFormComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1153
|
+
| CompanyListComponent | ✅ | N/A | ✅ | ✅ |
|
|
1154
|
+
| CompanyFormComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1155
|
+
| BranchListComponent | ✅ | N/A | ✅ | ✅ |
|
|
1156
|
+
| BranchFormComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1157
|
+
| UserCompanyPermissionDialogComponent | ✅ | N/A | ✅ | ✅ |
|
|
1158
|
+
| UserBranchPermissionDialogComponent | ✅ | N/A | ✅ | ✅ |
|