@flusys/ng-auth 1.0.0-beta → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +362 -21
- package/fesm2022/flusys-ng-auth.mjs +3011 -1746
- package/fesm2022/flusys-ng-auth.mjs.map +1 -1
- package/package.json +11 -11
- package/types/flusys-ng-auth.d.ts +348 -674
package/README.md
CHANGED
|
@@ -324,7 +324,7 @@ provideHttpClient(
|
|
|
324
324
|
Centralized endpoint definitions used by interceptors.
|
|
325
325
|
|
|
326
326
|
```typescript
|
|
327
|
-
import { AUTH_ENDPOINTS,
|
|
327
|
+
import { AUTH_ENDPOINTS, isEndpoint } from '@flusys/ng-auth';
|
|
328
328
|
|
|
329
329
|
// Endpoint constants
|
|
330
330
|
AUTH_ENDPOINTS.LOGIN // '/auth/login'
|
|
@@ -334,8 +334,7 @@ AUTH_ENDPOINTS.REGISTER // '/auth/register'
|
|
|
334
334
|
AUTH_ENDPOINTS.SELECT // '/auth/select'
|
|
335
335
|
AUTH_ENDPOINTS.SWITCH_COMPANY // '/auth/switch-company'
|
|
336
336
|
|
|
337
|
-
// Helper
|
|
338
|
-
isAuthEndpoint(url); // Check if URL is any auth endpoint
|
|
337
|
+
// Helper function
|
|
339
338
|
isEndpoint(url, endpoint); // Check if URL matches specific endpoint
|
|
340
339
|
```
|
|
341
340
|
|
|
@@ -393,41 +392,106 @@ getCompanyBranches(companyId); // User's permitted branches
|
|
|
393
392
|
|
|
394
393
|
## Pages
|
|
395
394
|
|
|
395
|
+
All page components follow Angular 21 best practices:
|
|
396
|
+
- `ChangeDetectionStrategy.OnPush` for optimal performance
|
|
397
|
+
- Signal Forms (`@angular/forms/signals`) with validation
|
|
398
|
+
- 100% responsive design (mobile-first with Tailwind)
|
|
399
|
+
- Dark mode compatible (PrimeNG + Tailwind CSS variables)
|
|
400
|
+
- `inject()` for dependency injection
|
|
401
|
+
|
|
396
402
|
### Authentication Pages
|
|
397
403
|
|
|
398
404
|
**LoginPageComponent** (`/auth/login`)
|
|
399
405
|
- Multi-step login: Step 1 = credentials, Step 2 = company/branch selection (if needed)
|
|
400
|
-
- Visual step indicator
|
|
406
|
+
- Visual step indicator (when company feature enabled)
|
|
407
|
+
- Email verification step (when email not verified)
|
|
401
408
|
- Auto-selects if only one company/branch available
|
|
402
409
|
- Stores returnUrl for post-login redirect
|
|
410
|
+
- Responsive form layout with mobile padding
|
|
403
411
|
|
|
404
412
|
**RegisterPageComponent** (`/auth/register`)
|
|
405
413
|
- User registration with optional company creation
|
|
406
|
-
-
|
|
407
|
-
-
|
|
414
|
+
- Two-column responsive grid layout
|
|
415
|
+
- Email/password validation with signal forms
|
|
416
|
+
- Company details section (when company feature enabled)
|
|
417
|
+
|
|
418
|
+
**ForgotPasswordPageComponent** (`/auth/forgot-password`)
|
|
419
|
+
- Email input with validation
|
|
420
|
+
- Success state showing email sent confirmation
|
|
421
|
+
|
|
422
|
+
**ResetPasswordPageComponent** (`/auth/reset-password`)
|
|
423
|
+
- Password reset with token validation
|
|
424
|
+
- Password confirmation matching
|
|
425
|
+
- Success/invalid token states
|
|
426
|
+
|
|
427
|
+
**VerifyEmailPageComponent** (`/auth/verify-email`)
|
|
428
|
+
- Token verification with SSR support (TransferState)
|
|
429
|
+
- Resend verification option
|
|
430
|
+
- Success/failure states
|
|
431
|
+
|
|
432
|
+
### Profile Page
|
|
433
|
+
|
|
434
|
+
**ProfilePageComponent** (`/profile`)
|
|
435
|
+
- Page title with description
|
|
436
|
+
- Avatar section with initials fallback
|
|
437
|
+
- Update name and phone
|
|
438
|
+
- Upload profile picture (requires `PROFILE_UPLOAD_PROVIDER` from ng-storage)
|
|
439
|
+
- Change password section with strength indicator
|
|
440
|
+
- View company/branch info (if company feature enabled)
|
|
441
|
+
- View roles/actions (requires `PROFILE_PERMISSION_PROVIDER` from ng-iam)
|
|
442
|
+
- Responsive two-column layout on desktop
|
|
443
|
+
|
|
444
|
+
**Optional Provider Dependencies:**
|
|
445
|
+
|
|
446
|
+
| Provider | Source | Feature Enabled |
|
|
447
|
+
|----------|--------|-----------------|
|
|
448
|
+
| `PROFILE_UPLOAD_PROVIDER` | ng-storage via `provideStorageProviders()` | Profile picture upload |
|
|
449
|
+
| `PROFILE_PERMISSION_PROVIDER` | ng-iam via `provideIamProviders()` | Roles/actions display |
|
|
450
|
+
|
|
451
|
+
**Graceful Degradation:**
|
|
452
|
+
- If provider not registered, feature shows "not enabled" message
|
|
453
|
+
- If service disabled in config, section is hidden
|
|
454
|
+
- No errors thrown for missing optional providers
|
|
408
455
|
|
|
409
456
|
### Administration Pages
|
|
410
457
|
|
|
411
458
|
**AdministrationPageComponent** (`/administration`)
|
|
412
|
-
-
|
|
413
|
-
-
|
|
459
|
+
- Page title "Administration" with dynamic subtitle
|
|
460
|
+
- Horizontal scrolling tabs (hidden scrollbar on mobile)
|
|
461
|
+
- Tabs: Users, Companies, Branches
|
|
462
|
+
- Company/Branch tabs only visible when company feature enabled
|
|
414
463
|
|
|
415
464
|
**User Management:**
|
|
416
465
|
- `UserListComponent` - LazyLoad datatable with Edit, Delete, Permission actions
|
|
417
|
-
- `UserFormComponent` - Create/edit user
|
|
466
|
+
- `UserFormComponent` - Create/edit user with auto company/branch assignment
|
|
467
|
+
- Permission dialogs for company/branch assignments
|
|
418
468
|
|
|
419
469
|
**Company Management:**
|
|
420
|
-
- `CompanyListComponent` - Company CRUD
|
|
421
|
-
- `CompanyFormComponent` - Create/edit company
|
|
470
|
+
- `CompanyListComponent` - Company CRUD with View Branches action
|
|
471
|
+
- `CompanyFormComponent` - Create/edit company with auto slug generation
|
|
422
472
|
|
|
423
473
|
**Branch Management:**
|
|
424
|
-
- `BranchListComponent` - Branch CRUD filtered by company
|
|
425
|
-
- `BranchFormComponent` - Create/edit branch
|
|
474
|
+
- `BranchListComponent` - Branch CRUD filtered by company dropdown
|
|
475
|
+
- `BranchFormComponent` - Create/edit branch with company selection
|
|
476
|
+
|
|
477
|
+
**Table Features (all list components):**
|
|
478
|
+
- Responsive header (stacked on mobile)
|
|
479
|
+
- Horizontal scroll wrapper (`overflow-x-auto`)
|
|
480
|
+
- Conditional pagination (`[paginator]="total() > 0"`)
|
|
481
|
+
- Striped rows (`p-datatable-striped`)
|
|
482
|
+
- Lazy loading for performance
|
|
426
483
|
|
|
427
484
|
---
|
|
428
485
|
|
|
429
486
|
## Dialog Components
|
|
430
487
|
|
|
488
|
+
Both dialog components use Angular 21 patterns:
|
|
489
|
+
- `ChangeDetectionStrategy.OnPush`
|
|
490
|
+
- `input.required<T>()` for required inputs
|
|
491
|
+
- `output<void>()` for events
|
|
492
|
+
- `effect()` for reactive data loading
|
|
493
|
+
- Responsive breakpoints (`{ '960px': '90vw', '640px': '95vw' }`)
|
|
494
|
+
|
|
431
495
|
### UserCompanyPermissionDialogComponent
|
|
432
496
|
|
|
433
497
|
Manages user-to-company assignments with checkboxes.
|
|
@@ -447,11 +511,21 @@ import { UserCompanyPermissionDialogComponent } from '@flusys/ng-auth';
|
|
|
447
511
|
})
|
|
448
512
|
```
|
|
449
513
|
|
|
514
|
+
**Inputs/Outputs:**
|
|
515
|
+
```typescript
|
|
516
|
+
readonly visible = input.required<boolean>();
|
|
517
|
+
readonly user = input.required<IUser | null>();
|
|
518
|
+
readonly closed = output<void>();
|
|
519
|
+
readonly permissionsChanged = output<void>();
|
|
520
|
+
```
|
|
521
|
+
|
|
450
522
|
**Features:**
|
|
451
523
|
- Shows all companies with checkboxes
|
|
452
524
|
- Pre-checks assigned companies
|
|
453
525
|
- Immediate save on toggle (no Save button)
|
|
454
526
|
- Toast notifications for success/error
|
|
527
|
+
- Loading spinner during data fetch
|
|
528
|
+
- Disabled checkboxes while saving
|
|
455
529
|
|
|
456
530
|
### UserBranchPermissionDialogComponent
|
|
457
531
|
|
|
@@ -473,13 +547,14 @@ import { UserBranchPermissionDialogComponent } from '@flusys/ng-auth';
|
|
|
473
547
|
```
|
|
474
548
|
|
|
475
549
|
**Features:**
|
|
476
|
-
-
|
|
477
|
-
-
|
|
550
|
+
- Company dropdown shows only user's permitted companies
|
|
551
|
+
- Branch list loads on company selection
|
|
478
552
|
- Immediate save on toggle
|
|
553
|
+
- Contextual empty messages (no permissions vs no branches)
|
|
479
554
|
- Scoped to user's permitted companies
|
|
480
555
|
|
|
481
556
|
**Workflow:**
|
|
482
|
-
1. Dialog opens → Loads user's permitted companies
|
|
557
|
+
1. Dialog opens → Loads user's permitted companies via effect()
|
|
483
558
|
2. User selects company → Loads branches for that company
|
|
484
559
|
3. User toggles branch checkboxes → Immediately assigns/revokes
|
|
485
560
|
|
|
@@ -513,6 +588,21 @@ import { ADMINISTRATION_ROUTES } from '@flusys/ng-auth';
|
|
|
513
588
|
}
|
|
514
589
|
```
|
|
515
590
|
|
|
591
|
+
### PROFILE_ROUTES
|
|
592
|
+
|
|
593
|
+
User profile page routes.
|
|
594
|
+
|
|
595
|
+
```typescript
|
|
596
|
+
import { PROFILE_ROUTES } from '@flusys/ng-auth';
|
|
597
|
+
|
|
598
|
+
// Route: /profile
|
|
599
|
+
{
|
|
600
|
+
path: 'profile',
|
|
601
|
+
canActivate: [authGuard],
|
|
602
|
+
children: PROFILE_ROUTES
|
|
603
|
+
}
|
|
604
|
+
```
|
|
605
|
+
|
|
516
606
|
---
|
|
517
607
|
|
|
518
608
|
## Setup
|
|
@@ -529,6 +619,8 @@ import {
|
|
|
529
619
|
provideAuthLayoutIntegration,
|
|
530
620
|
provideAuthProviders
|
|
531
621
|
} from '@flusys/ng-auth';
|
|
622
|
+
import { provideIamProviders } from '@flusys/ng-iam';
|
|
623
|
+
import { provideStorageProviders } from '@flusys/ng-storage';
|
|
532
624
|
|
|
533
625
|
export const appConfig: ApplicationConfig = {
|
|
534
626
|
providers: [
|
|
@@ -542,7 +634,9 @@ export const appConfig: ApplicationConfig = {
|
|
|
542
634
|
),
|
|
543
635
|
{ provide: APP_CONFIG, useValue: DEFAULT_APP_CONFIG },
|
|
544
636
|
...provideAuthLayoutIntegration(),
|
|
545
|
-
...provideAuthProviders()
|
|
637
|
+
...provideAuthProviders(),
|
|
638
|
+
...provideIamProviders(), // Enables permissions display on profile page
|
|
639
|
+
...provideStorageProviders(), // Enables profile picture upload
|
|
546
640
|
]
|
|
547
641
|
};
|
|
548
642
|
```
|
|
@@ -551,7 +645,7 @@ export const appConfig: ApplicationConfig = {
|
|
|
551
645
|
|
|
552
646
|
```typescript
|
|
553
647
|
import { Routes } from '@angular/router';
|
|
554
|
-
import { authGuard, guestGuard, AUTH_ROUTES, ADMINISTRATION_ROUTES } from '@flusys/ng-auth';
|
|
648
|
+
import { authGuard, guestGuard, AUTH_ROUTES, ADMINISTRATION_ROUTES, PROFILE_ROUTES } from '@flusys/ng-auth';
|
|
555
649
|
|
|
556
650
|
export const routes: Routes = [
|
|
557
651
|
{ path: 'auth', children: AUTH_ROUTES },
|
|
@@ -560,7 +654,8 @@ export const routes: Routes = [
|
|
|
560
654
|
canActivate: [authGuard],
|
|
561
655
|
children: [
|
|
562
656
|
{ path: 'dashboard', loadComponent: () => import('./dashboard.component') },
|
|
563
|
-
{ path: 'administration', children: ADMINISTRATION_ROUTES }
|
|
657
|
+
{ path: 'administration', children: ADMINISTRATION_ROUTES },
|
|
658
|
+
{ path: 'profile', children: PROFILE_ROUTES }
|
|
564
659
|
]
|
|
565
660
|
}
|
|
566
661
|
];
|
|
@@ -665,6 +760,67 @@ interface IUserBranchPermission { id: string; userId: string; branchId: string;
|
|
|
665
760
|
|
|
666
761
|
## Best Practices
|
|
667
762
|
|
|
763
|
+
### Angular 21 Component Patterns
|
|
764
|
+
|
|
765
|
+
All components in ng-auth follow these patterns:
|
|
766
|
+
|
|
767
|
+
```typescript
|
|
768
|
+
@Component({
|
|
769
|
+
selector: 'lib-example',
|
|
770
|
+
standalone: true,
|
|
771
|
+
changeDetection: ChangeDetectionStrategy.OnPush, // Always OnPush
|
|
772
|
+
imports: [AngularModule, PrimeModule, FormField],
|
|
773
|
+
template: `...`
|
|
774
|
+
})
|
|
775
|
+
export class ExampleComponent {
|
|
776
|
+
// Dependency injection via inject()
|
|
777
|
+
private readonly service = inject(MyService);
|
|
778
|
+
|
|
779
|
+
// Signal-based state
|
|
780
|
+
readonly isLoading = signal(false);
|
|
781
|
+
readonly data = signal<IData | null>(null);
|
|
782
|
+
|
|
783
|
+
// Computed signals
|
|
784
|
+
readonly isValid = computed(() => this.data() !== null);
|
|
785
|
+
|
|
786
|
+
// Signal Forms
|
|
787
|
+
readonly formModel = signal<IFormModel>({ name: '', email: '' });
|
|
788
|
+
readonly formTree = form(this.formModel, (f) => {
|
|
789
|
+
required(f.name, { message: 'Name is required' });
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
// Error handling with proper typing
|
|
793
|
+
async onSubmit(): Promise<void> {
|
|
794
|
+
try {
|
|
795
|
+
// ...
|
|
796
|
+
} catch (error: unknown) {
|
|
797
|
+
const err = error as { error?: { message?: string } };
|
|
798
|
+
// Handle error
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
### Responsive Design Patterns
|
|
805
|
+
|
|
806
|
+
```html
|
|
807
|
+
<!-- Responsive container -->
|
|
808
|
+
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 p-4">
|
|
809
|
+
|
|
810
|
+
<!-- Two-column form grid -->
|
|
811
|
+
<form class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
812
|
+
|
|
813
|
+
<!-- Right-aligned buttons -->
|
|
814
|
+
<div class="flex justify-end gap-4 md:col-span-2 pt-4">
|
|
815
|
+
|
|
816
|
+
<!-- Horizontally scrollable tabs -->
|
|
817
|
+
<div class="scrollbar-hide flex gap-1 overflow-x-auto flex-nowrap">
|
|
818
|
+
|
|
819
|
+
<!-- Responsive table wrapper -->
|
|
820
|
+
<div class="overflow-x-auto">
|
|
821
|
+
<p-table [scrollable]="true" [paginator]="total() > 0">
|
|
822
|
+
```
|
|
823
|
+
|
|
668
824
|
### Component Organization
|
|
669
825
|
|
|
670
826
|
```
|
|
@@ -675,7 +831,14 @@ projects/ng-auth/
|
|
|
675
831
|
├── pages/ ← Route-specific pages
|
|
676
832
|
│ ├── login/
|
|
677
833
|
│ ├── register/
|
|
678
|
-
│
|
|
834
|
+
│ ├── forgot-password/
|
|
835
|
+
│ ├── reset-password/
|
|
836
|
+
│ ├── verify-email/
|
|
837
|
+
│ ├── profile/
|
|
838
|
+
│ ├── administration/
|
|
839
|
+
│ ├── users/
|
|
840
|
+
│ ├── company/
|
|
841
|
+
│ └── branch/
|
|
679
842
|
├── services/
|
|
680
843
|
├── guards/
|
|
681
844
|
├── interceptors/
|
|
@@ -704,6 +867,27 @@ const allCompanies = await companyApi.fetchList(); // Security risk
|
|
|
704
867
|
- TokenRefreshInterceptor handles automatic refresh
|
|
705
868
|
- Auto-persistence for company/branch selection
|
|
706
869
|
|
|
870
|
+
### Type Safety
|
|
871
|
+
|
|
872
|
+
Avoid `any` types - use explicit interfaces:
|
|
873
|
+
|
|
874
|
+
```typescript
|
|
875
|
+
// ✅ CORRECT - Explicit response type
|
|
876
|
+
.subscribe({
|
|
877
|
+
next: (result: { message?: string }) => {
|
|
878
|
+
this.messageService.add({
|
|
879
|
+
severity: 'success',
|
|
880
|
+
detail: result.message,
|
|
881
|
+
});
|
|
882
|
+
},
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
// ❌ WRONG - Using any
|
|
886
|
+
.subscribe({
|
|
887
|
+
next: (result: any) => { ... },
|
|
888
|
+
});
|
|
889
|
+
```
|
|
890
|
+
|
|
707
891
|
### Multi-Company Flow
|
|
708
892
|
|
|
709
893
|
1. Login → Check if `requiresSelection: true`
|
|
@@ -760,6 +944,143 @@ providers: [
|
|
|
760
944
|
|
|
761
945
|
---
|
|
762
946
|
|
|
947
|
+
## No-IAM Mode (Bypassing Permission Guards)
|
|
948
|
+
|
|
949
|
+
When you don't use the `@flusys/ng-iam` module but your routes use `permissionGuard`, the guard will fail because no permissions are loaded.
|
|
950
|
+
|
|
951
|
+
**Solution:** Set wildcard permissions in `PermissionValidatorService` after login.
|
|
952
|
+
|
|
953
|
+
### How Permission Guard Works
|
|
954
|
+
|
|
955
|
+
```
|
|
956
|
+
Route with permissionGuard('user.view')
|
|
957
|
+
↓
|
|
958
|
+
permissionGuard() → inject(PermissionValidatorService)
|
|
959
|
+
↓
|
|
960
|
+
permissionValidator.isPermissionsLoaded() → false
|
|
961
|
+
↓
|
|
962
|
+
Redirect to '/' ❌
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
### Fix: Set Wildcard Permissions After Login
|
|
966
|
+
|
|
967
|
+
#### Option 1: In Login Flow (Recommended)
|
|
968
|
+
|
|
969
|
+
Update your login handling to set permissions after successful authentication:
|
|
970
|
+
|
|
971
|
+
```typescript
|
|
972
|
+
// login.component.ts or auth.service.ts
|
|
973
|
+
import { PermissionValidatorService } from '@flusys/ng-shared';
|
|
974
|
+
|
|
975
|
+
@Component({ ... })
|
|
976
|
+
export class LoginComponent {
|
|
977
|
+
private readonly permissionValidator = inject(PermissionValidatorService);
|
|
978
|
+
private readonly authApi = inject(AuthApiService);
|
|
979
|
+
private readonly authState = inject(AuthStateService);
|
|
980
|
+
|
|
981
|
+
async onLogin(): Promise<void> {
|
|
982
|
+
const response = await firstValueFrom(this.authApi.login(credentials));
|
|
983
|
+
this.authState.setLoginState(response);
|
|
984
|
+
|
|
985
|
+
// When IAM is NOT used, grant all permissions via wildcard
|
|
986
|
+
this.permissionValidator.setPermissions(['*']);
|
|
987
|
+
|
|
988
|
+
// Navigate to dashboard
|
|
989
|
+
this.router.navigate(['/dashboard']);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
#### Option 2: In APP_INITIALIZER
|
|
995
|
+
|
|
996
|
+
Set permissions at app startup:
|
|
997
|
+
|
|
998
|
+
```typescript
|
|
999
|
+
// app.config.ts
|
|
1000
|
+
import { APP_INITIALIZER } from '@angular/core';
|
|
1001
|
+
import { PermissionValidatorService } from '@flusys/ng-shared';
|
|
1002
|
+
|
|
1003
|
+
export const appConfig: ApplicationConfig = {
|
|
1004
|
+
providers: [
|
|
1005
|
+
{
|
|
1006
|
+
provide: APP_INITIALIZER,
|
|
1007
|
+
useFactory: (permissionValidator: PermissionValidatorService) => {
|
|
1008
|
+
return () => {
|
|
1009
|
+
// Set wildcard permissions when IAM is disabled
|
|
1010
|
+
permissionValidator.setPermissions(['*']);
|
|
1011
|
+
};
|
|
1012
|
+
},
|
|
1013
|
+
deps: [PermissionValidatorService],
|
|
1014
|
+
multi: true,
|
|
1015
|
+
},
|
|
1016
|
+
// ... other providers
|
|
1017
|
+
],
|
|
1018
|
+
};
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
#### Option 3: In AuthInitService (After Session Restore)
|
|
1022
|
+
|
|
1023
|
+
If you restore sessions on page refresh, set permissions there:
|
|
1024
|
+
|
|
1025
|
+
```typescript
|
|
1026
|
+
// After successful /auth/me response
|
|
1027
|
+
authInit.initialize().subscribe({
|
|
1028
|
+
next: (user) => {
|
|
1029
|
+
// Session restored, set wildcard permissions
|
|
1030
|
+
permissionValidator.setPermissions(['*']);
|
|
1031
|
+
},
|
|
1032
|
+
});
|
|
1033
|
+
```
|
|
1034
|
+
|
|
1035
|
+
### Wildcard Permission Syntax
|
|
1036
|
+
|
|
1037
|
+
The `permissionGuard` utilities support these patterns:
|
|
1038
|
+
|
|
1039
|
+
| Pattern | Meaning |
|
|
1040
|
+
|---------|---------|
|
|
1041
|
+
| `*` | Full access to everything |
|
|
1042
|
+
| `user.*` | All user permissions |
|
|
1043
|
+
| `branch.*` | All branch permissions |
|
|
1044
|
+
|
|
1045
|
+
### For Jest Tests
|
|
1046
|
+
|
|
1047
|
+
Mock `PermissionValidatorService` in your test setup:
|
|
1048
|
+
|
|
1049
|
+
```typescript
|
|
1050
|
+
import { PermissionValidatorService } from '@flusys/ng-shared';
|
|
1051
|
+
|
|
1052
|
+
describe('MyComponent', () => {
|
|
1053
|
+
beforeEach(async () => {
|
|
1054
|
+
await TestBed.configureTestingModule({
|
|
1055
|
+
imports: [MyComponent],
|
|
1056
|
+
providers: [
|
|
1057
|
+
{
|
|
1058
|
+
provide: PermissionValidatorService,
|
|
1059
|
+
useValue: {
|
|
1060
|
+
permissions: () => ['*'],
|
|
1061
|
+
isPermissionsLoaded: () => true,
|
|
1062
|
+
hasPermission: () => true,
|
|
1063
|
+
setPermissions: jest.fn(),
|
|
1064
|
+
clearPermissions: jest.fn(),
|
|
1065
|
+
},
|
|
1066
|
+
},
|
|
1067
|
+
],
|
|
1068
|
+
}).compileComponents();
|
|
1069
|
+
});
|
|
1070
|
+
});
|
|
1071
|
+
```
|
|
1072
|
+
|
|
1073
|
+
### For Template Permission Checks
|
|
1074
|
+
|
|
1075
|
+
If using `*hasPermission` directive, it will also respect wildcard:
|
|
1076
|
+
|
|
1077
|
+
```html
|
|
1078
|
+
<!-- This will show when permissions include '*' -->
|
|
1079
|
+
<button *hasPermission="'user.delete'">Delete User</button>
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
---
|
|
1083
|
+
|
|
763
1084
|
## See Also
|
|
764
1085
|
|
|
765
1086
|
- [CORE-GUIDE.md](./CORE-GUIDE.md) - APP_CONFIG configuration
|
|
@@ -770,5 +1091,25 @@ providers: [
|
|
|
770
1091
|
|
|
771
1092
|
---
|
|
772
1093
|
|
|
773
|
-
**Last Updated:** 2026-02-
|
|
1094
|
+
**Last Updated:** 2026-02-21
|
|
774
1095
|
**Angular Version:** 21
|
|
1096
|
+
|
|
1097
|
+
## Component Checklist
|
|
1098
|
+
|
|
1099
|
+
| Component | OnPush | Signal Forms | Responsive | Dark Mode |
|
|
1100
|
+
|-----------|--------|--------------|------------|-----------|
|
|
1101
|
+
| LoginPageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1102
|
+
| RegisterPageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1103
|
+
| ForgotPasswordPageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1104
|
+
| ResetPasswordPageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1105
|
+
| VerifyEmailPageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1106
|
+
| ProfilePageComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1107
|
+
| AdministrationPageComponent | ✅ | N/A | ✅ | ✅ |
|
|
1108
|
+
| UserListComponent | ✅ | N/A | ✅ | ✅ |
|
|
1109
|
+
| UserFormComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1110
|
+
| CompanyListComponent | ✅ | N/A | ✅ | ✅ |
|
|
1111
|
+
| CompanyFormComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1112
|
+
| BranchListComponent | ✅ | N/A | ✅ | ✅ |
|
|
1113
|
+
| BranchFormComponent | ✅ | ✅ | ✅ | ✅ |
|
|
1114
|
+
| UserCompanyPermissionDialogComponent | ✅ | N/A | ✅ | ✅ |
|
|
1115
|
+
| UserBranchPermissionDialogComponent | ✅ | N/A | ✅ | ✅ |
|