@hichchi/ngx-auth 0.0.1-alpha.4 → 0.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/fesm2022/hichchi-ngx-auth.mjs +122 -162
- package/fesm2022/hichchi-ngx-auth.mjs.map +1 -1
- package/package.json +8 -8
- package/types/hichchi-ngx-auth.d.ts +1332 -0
- package/index.d.ts +0 -1
- package/lib/auth.module.d.ts +0 -92
- package/lib/components/auth-form/auth-form.component.d.ts +0 -163
- package/lib/components/index.d.ts +0 -1
- package/lib/constants.d.ts +0 -96
- package/lib/directives/index.d.ts +0 -1
- package/lib/directives/permission.directive.d.ts +0 -134
- package/lib/enums/auth-guard-condition.enum.d.ts +0 -38
- package/lib/enums/index.d.ts +0 -1
- package/lib/guards/auth.guard.d.ts +0 -71
- package/lib/guards/index.d.ts +0 -2
- package/lib/guards/role.guard.d.ts +0 -71
- package/lib/index.d.ts +0 -9
- package/lib/interceptors/auth.interceptor.d.ts +0 -101
- package/lib/interceptors/index.d.ts +0 -1
- package/lib/interfaces/auth-config.interface.d.ts +0 -5
- package/lib/interfaces/auth-form-data.interface.d.ts +0 -61
- package/lib/interfaces/auth-guard-option.interface.d.ts +0 -72
- package/lib/interfaces/index.d.ts +0 -4
- package/lib/interfaces/role-guard-option.interface.d.ts +0 -6
- package/lib/services/auth.service.d.ts +0 -333
- package/lib/services/index.d.ts +0 -1
- package/lib/state/auth.state.d.ts +0 -156
- package/lib/state/index.d.ts +0 -1
- package/lib/tokens.d.ts +0 -73
- package/lib/utils/index.d.ts +0 -1
- package/lib/utils/route.utils.d.ts +0 -100
|
@@ -1,94 +1,47 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import {
|
|
3
|
-
import * as i1
|
|
4
|
-
import { Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
5
|
-
import { AuthEndpoint,
|
|
6
|
-
import
|
|
7
|
-
import { provideHttpClient } from '@angular/common/http';
|
|
8
|
-
import { take, map, tap, catchError, EMPTY, ReplaySubject, throwError, switchMap, filter } from 'rxjs';
|
|
2
|
+
import { InjectionToken, Inject, Injectable, computed, inject, input, output, signal, effect, Component, TemplateRef, ViewContainerRef, Directive, NgModule } from '@angular/core';
|
|
3
|
+
import * as i1 from '@angular/forms';
|
|
4
|
+
import { FormBuilder, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
5
|
+
import { AuthEndpoint, isRoleObject, AuthField, AuthErrorResponseCode } from '@hichchi/nest-connector/auth';
|
|
6
|
+
import { take, map, tap, catchError, EMPTY, firstValueFrom, ReplaySubject, throwError, switchMap, filter } from 'rxjs';
|
|
9
7
|
import { Endpoint, HttpClientErrorStatus } from '@hichchi/nest-connector';
|
|
10
|
-
import { skipNotifyContext, validatedFormData } from '@hichchi/ngx-utils';
|
|
8
|
+
import { CrudHttpService, skipNotifyContext, validatedFormData } from '@hichchi/ngx-utils';
|
|
11
9
|
import { toFirstCase } from '@hichchi/utils';
|
|
12
10
|
import { signalStore, withState, withComputed, withMethods, patchState } from '@ngrx/signals';
|
|
13
11
|
import { withStorageSync } from '@angular-architects/ngrx-toolkit';
|
|
14
12
|
import { Router } from '@angular/router';
|
|
15
|
-
import * as
|
|
13
|
+
import * as i2 from '@hichchi/ngx-ui';
|
|
16
14
|
import { ButtonComponent, HcCardComponent, HcSeparatorComponent } from '@hichchi/ngx-ui';
|
|
17
15
|
import { CommonModule } from '@angular/common';
|
|
18
16
|
|
|
19
17
|
/**
|
|
20
|
-
* Injection token for
|
|
18
|
+
* Injection token for the ngx-auth runtime configuration.
|
|
21
19
|
*
|
|
22
|
-
* This
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* The token is used internally by the NgxHichchiAuthModule.forRoot() method to
|
|
28
|
-
* register the authentication configuration as a provider, making it available
|
|
29
|
-
* for injection in services like AuthService.
|
|
30
|
-
*
|
|
31
|
-
* This follows Angular's recommended pattern for providing configuration objects
|
|
32
|
-
* to libraries and modules, ensuring type safety and proper dependency injection.
|
|
20
|
+
* This token is bound in `NgxHichchiAuthModule.forRoot(config)` as:
|
|
21
|
+
* `{ provide: AUTH_CONFIG, useValue: config }`.
|
|
22
|
+
* Consumers can inject it to access the same `AuthConfig` object
|
|
23
|
+
* (for example `apiBaseURL` and optional `authField`).
|
|
33
24
|
*
|
|
34
25
|
* @example
|
|
35
26
|
* ```typescript
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* { provide: AUTH_CONFIG, useValue: config },
|
|
40
|
-
* AuthService
|
|
41
|
-
* ]
|
|
42
|
-
* })
|
|
43
|
-
* export class NgxHichchiAuthModule {
|
|
44
|
-
* static forRoot(config: AuthConfig): ModuleWithProviders<NgxHichchiAuthModule> {
|
|
45
|
-
* return {
|
|
46
|
-
* ngModule: NgxHichchiAuthModule,
|
|
47
|
-
* providers: [
|
|
48
|
-
* { provide: AUTH_CONFIG, useValue: config },
|
|
49
|
-
* provideHttpClient(),
|
|
50
|
-
* AuthService
|
|
51
|
-
* ]
|
|
52
|
-
* };
|
|
53
|
-
* }
|
|
54
|
-
* }
|
|
27
|
+
* NgxHichchiAuthModule.forRoot({
|
|
28
|
+
* apiBaseURL: "https://api.example.com",
|
|
29
|
+
* });
|
|
55
30
|
* ```
|
|
56
31
|
*
|
|
57
32
|
* @example
|
|
58
33
|
* ```typescript
|
|
59
|
-
* // Injecting the configuration in a service
|
|
60
34
|
* @Injectable()
|
|
61
|
-
* export class
|
|
62
|
-
* constructor(
|
|
63
|
-
*
|
|
64
|
-
* ) {
|
|
65
|
-
* console.log('API Base URL:', this.config.apiBaseURL);
|
|
66
|
-
* console.log('Auth Field:', this.config.authField);
|
|
67
|
-
* }
|
|
68
|
-
* }
|
|
69
|
-
* ```
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* ```typescript
|
|
73
|
-
* // Using in a component (though typically not recommended)
|
|
74
|
-
* @Component({
|
|
75
|
-
* selector: 'app-auth-info',
|
|
76
|
-
* template: `<p>API URL: {{ apiUrl }}</p>`
|
|
77
|
-
* })
|
|
78
|
-
* export class AuthInfoComponent {
|
|
79
|
-
* apiUrl: string;
|
|
80
|
-
*
|
|
81
|
-
* constructor(@Inject(AUTH_CONFIG) private config: AuthConfig) {
|
|
82
|
-
* this.apiUrl = config.apiBaseURL;
|
|
35
|
+
* export class ExampleService {
|
|
36
|
+
* constructor(@Inject(AUTH_CONFIG) private readonly config: AuthConfig) {
|
|
37
|
+
* console.log(this.config.apiBaseURL);
|
|
83
38
|
* }
|
|
84
39
|
* }
|
|
85
40
|
* ```
|
|
86
41
|
*
|
|
87
|
-
* @see {@link AuthConfig}
|
|
88
|
-
* @see {@link NgxHichchiAuthModule} Module that uses this token to provide configuration
|
|
89
|
-
* @see {@link AuthService} Service that injects this configuration
|
|
42
|
+
* @see {@link AuthConfig} Configuration contract provided through this token
|
|
90
43
|
*/
|
|
91
|
-
const AUTH_CONFIG = "AUTH_CONFIG";
|
|
44
|
+
const AUTH_CONFIG = new InjectionToken("AUTH_CONFIG");
|
|
92
45
|
|
|
93
46
|
/**
|
|
94
47
|
* Width of the Google OAuth authentication popup window in pixels
|
|
@@ -233,20 +186,22 @@ const AUTH_GUARD_OPTIONS_KEY = "authGuardOptions";
|
|
|
233
186
|
* @see {@link AuthState} State management service for authentication
|
|
234
187
|
* @see {@link AuthResponse} Response interface for authentication operations
|
|
235
188
|
*/
|
|
236
|
-
|
|
237
|
-
|
|
189
|
+
/**
|
|
190
|
+
* HTTP client wrapper for authentication and token lifecycle endpoints.
|
|
191
|
+
*/
|
|
192
|
+
class AuthService extends CrudHttpService {
|
|
238
193
|
config;
|
|
239
194
|
/**
|
|
240
195
|
* Creates an instance of AuthService
|
|
241
196
|
*
|
|
242
|
-
* @param http - Http client
|
|
243
197
|
* @param config - The authentication configuration injected from AUTH_CONFIG token
|
|
244
198
|
*
|
|
245
199
|
* @see {@link AUTH_CONFIG} Injection token for authentication configuration
|
|
246
200
|
* @see {@link AuthConfig} Interface defining the configuration structure
|
|
247
201
|
*/
|
|
248
|
-
|
|
249
|
-
|
|
202
|
+
// eslint-disable-next-line @angular-eslint/prefer-inject
|
|
203
|
+
constructor(config) {
|
|
204
|
+
super();
|
|
250
205
|
this.config = config;
|
|
251
206
|
}
|
|
252
207
|
/**
|
|
@@ -257,7 +212,7 @@ class AuthService {
|
|
|
257
212
|
* into JavaScript Date objects for easier handling in the client application.
|
|
258
213
|
*
|
|
259
214
|
* @param dto - The sign-in data containing user credentials
|
|
260
|
-
* @param
|
|
215
|
+
* @param skipNotify - Optional flag to skip error notifications for this request
|
|
261
216
|
* @returns Observable that emits the authentication response with user data and tokens
|
|
262
217
|
*
|
|
263
218
|
* @example
|
|
@@ -281,10 +236,8 @@ class AuthService {
|
|
|
281
236
|
* @see {@link AuthResponse} Interface for authentication response
|
|
282
237
|
* @see {@link AuthEndpoint.SIGN_IN} Backend endpoint for user authentication
|
|
283
238
|
*/
|
|
284
|
-
signIn(dto,
|
|
285
|
-
return this.
|
|
286
|
-
.post(`${Endpoint.AUTH}/${AuthEndpoint.SIGN_IN}`, dto, skipNotifyContext(skipErrorNotify))
|
|
287
|
-
.pipe(take(1), map(res => ({
|
|
239
|
+
signIn(dto, skipNotify) {
|
|
240
|
+
return this.post(`${Endpoint.AUTH}/${AuthEndpoint.SIGN_IN}`, dto, { skipNotify }).pipe(take(1), map(res => ({
|
|
288
241
|
...res,
|
|
289
242
|
accessTokenExpiresOn: new Date(res.accessTokenExpiresOn),
|
|
290
243
|
refreshTokenExpiresOn: new Date(res.refreshTokenExpiresOn),
|
|
@@ -392,7 +345,7 @@ class AuthService {
|
|
|
392
345
|
* Date objects for easier handling in the client application.
|
|
393
346
|
*
|
|
394
347
|
* @param accessToken - The access token to exchange for authentication response
|
|
395
|
-
* @param
|
|
348
|
+
* @param skipNotify - Optional flag to skip error notifications for this request
|
|
396
349
|
* @returns Observable that emits the complete authentication response
|
|
397
350
|
*
|
|
398
351
|
* @example
|
|
@@ -418,11 +371,11 @@ class AuthService {
|
|
|
418
371
|
* @see {@link AuthEndpoint.GET_AUTH_RESPONSE} Backend endpoint for token exchange
|
|
419
372
|
* @see {@link googleSignIn} Method that provides access tokens for this operation
|
|
420
373
|
*/
|
|
421
|
-
getAuthResponse(accessToken,
|
|
374
|
+
getAuthResponse(accessToken, skipNotify) {
|
|
422
375
|
return this.http
|
|
423
376
|
.post(`${Endpoint.AUTH}/${AuthEndpoint.GET_AUTH_RESPONSE}`, {
|
|
424
377
|
accessToken,
|
|
425
|
-
}, skipNotifyContext(
|
|
378
|
+
}, skipNotifyContext(skipNotify))
|
|
426
379
|
.pipe(take(1), map(res => ({
|
|
427
380
|
...res,
|
|
428
381
|
accessTokenExpiresOn: new Date(res.accessTokenExpiresOn),
|
|
@@ -441,7 +394,7 @@ class AuthService {
|
|
|
441
394
|
* or handle email verification depending on your application's configuration.
|
|
442
395
|
*
|
|
443
396
|
* @param dto - The sign-up data containing user registration information
|
|
444
|
-
* @param
|
|
397
|
+
* @param skipNotify - Optional flag to skip error notifications for this request
|
|
445
398
|
* @returns Observable that emits the newly created user data
|
|
446
399
|
*
|
|
447
400
|
* @example
|
|
@@ -470,9 +423,9 @@ class AuthService {
|
|
|
470
423
|
* @see {@link AuthEndpoint.SIGN_UP} Backend endpoint for user registration
|
|
471
424
|
* @see {@link signIn} Method to authenticate user after registration
|
|
472
425
|
*/
|
|
473
|
-
signUp(dto,
|
|
426
|
+
signUp(dto, skipNotify) {
|
|
474
427
|
return this.http
|
|
475
|
-
.post(`${Endpoint.AUTH}/${AuthEndpoint.SIGN_UP}`, dto, skipNotifyContext(
|
|
428
|
+
.post(`${Endpoint.AUTH}/${AuthEndpoint.SIGN_UP}`, dto, skipNotifyContext(skipNotify))
|
|
476
429
|
.pipe(take(1));
|
|
477
430
|
}
|
|
478
431
|
/**
|
|
@@ -486,7 +439,7 @@ class AuthService {
|
|
|
486
439
|
* while keeping access tokens short-lived for better security.
|
|
487
440
|
*
|
|
488
441
|
* @param refreshToken - The refresh token to exchange for new tokens
|
|
489
|
-
* @param
|
|
442
|
+
* @param skipNotify - Optional flag to skip error notifications for this request
|
|
490
443
|
* @returns Observable that emits the new token response
|
|
491
444
|
*
|
|
492
445
|
* @example
|
|
@@ -515,11 +468,11 @@ class AuthService {
|
|
|
515
468
|
* @see {@link AuthEndpoint.REFRESH_TOKEN} Backend endpoint for token refresh
|
|
516
469
|
* @see {@link signIn} Method to get initial tokens through authentication
|
|
517
470
|
*/
|
|
518
|
-
refreshToken(refreshToken,
|
|
471
|
+
refreshToken(refreshToken, skipNotify) {
|
|
519
472
|
return this.http
|
|
520
473
|
.post(`${Endpoint.AUTH}/${AuthEndpoint.REFRESH_TOKEN}`, {
|
|
521
474
|
refreshToken,
|
|
522
|
-
}, skipNotifyContext(
|
|
475
|
+
}, skipNotifyContext(skipNotify))
|
|
523
476
|
.pipe(take(1));
|
|
524
477
|
}
|
|
525
478
|
/**
|
|
@@ -532,7 +485,7 @@ class AuthService {
|
|
|
532
485
|
* After calling this method, you should also clear any client-side authentication
|
|
533
486
|
* data such as tokens stored in localStorage, sessionStorage, or application state.
|
|
534
487
|
*
|
|
535
|
-
* @param
|
|
488
|
+
* @param skipNotify - Optional flag to skip error notifications for this request
|
|
536
489
|
* @returns Observable that emits a success response when sign-out is complete
|
|
537
490
|
*
|
|
538
491
|
* @example
|
|
@@ -578,21 +531,21 @@ class AuthService {
|
|
|
578
531
|
* @see {@link AuthEndpoint.SIGN_OUT} Backend endpoint for user sign-out
|
|
579
532
|
* @see {@link signIn} Method to authenticate user after sign-out
|
|
580
533
|
*/
|
|
581
|
-
signOut(
|
|
534
|
+
signOut(skipNotify) {
|
|
582
535
|
// this.app.startSpinner();
|
|
583
536
|
return this.http
|
|
584
|
-
.post(`${Endpoint.AUTH}/${AuthEndpoint.SIGN_OUT}`, {}, skipNotifyContext(
|
|
537
|
+
.post(`${Endpoint.AUTH}/${AuthEndpoint.SIGN_OUT}`, {}, skipNotifyContext(skipNotify))
|
|
585
538
|
.pipe(take(1));
|
|
586
539
|
}
|
|
587
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
588
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
540
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: AuthService, deps: [{ token: AUTH_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
541
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: AuthService, providedIn: "root" });
|
|
589
542
|
}
|
|
590
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
543
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: AuthService, decorators: [{
|
|
591
544
|
type: Injectable,
|
|
592
545
|
args: [{
|
|
593
546
|
providedIn: "root",
|
|
594
547
|
}]
|
|
595
|
-
}], ctorParameters: () => [{ type:
|
|
548
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
596
549
|
type: Inject,
|
|
597
550
|
args: [AUTH_CONFIG]
|
|
598
551
|
}] }] });
|
|
@@ -607,6 +560,7 @@ const initialState = {
|
|
|
607
560
|
refreshToken: null,
|
|
608
561
|
accessTokenExpiresOn: null,
|
|
609
562
|
refreshTokenExpiresOn: null,
|
|
563
|
+
data: {}
|
|
610
564
|
};
|
|
611
565
|
/**
|
|
612
566
|
* Authentication state management store using NgRx Signals
|
|
@@ -730,6 +684,10 @@ const AuthState = signalStore({ providedIn: "root" }, withState(initialState), w
|
|
|
730
684
|
* ```
|
|
731
685
|
*/
|
|
732
686
|
role: computed(() => user()?.role),
|
|
687
|
+
permissions: computed(() => {
|
|
688
|
+
const role = user()?.role;
|
|
689
|
+
return role && isRoleObject(role) ? role.permissions || [] : [];
|
|
690
|
+
}),
|
|
733
691
|
/**
|
|
734
692
|
* Computed signal that returns the current user's role name as a string
|
|
735
693
|
*
|
|
@@ -879,7 +837,7 @@ const AuthState = signalStore({ providedIn: "root" }, withState(initialState), w
|
|
|
879
837
|
*
|
|
880
838
|
* This method handles user sign-in by calling the AuthService and updating
|
|
881
839
|
* the authentication state with the response. It supports both Observable
|
|
882
|
-
* and Promise return types based on the
|
|
840
|
+
* and Promise return types based on the asPromise parameter, and can
|
|
883
841
|
* automatically redirect users after successful authentication.
|
|
884
842
|
*
|
|
885
843
|
* The method integrates with the error notification system and can
|
|
@@ -887,9 +845,9 @@ const AuthState = signalStore({ providedIn: "root" }, withState(initialState), w
|
|
|
887
845
|
*
|
|
888
846
|
* @param signInBody - The sign-in credentials containing email/username and password
|
|
889
847
|
* @param redirect - Optional redirect path or function that returns a path after successful sign-in
|
|
890
|
-
* @param
|
|
848
|
+
* @param asPromise - Optional flag to return a Promise instead of an Observable (defaults to false)
|
|
891
849
|
* @param showError - Optional flag to control error notification display (defaults to true for Promise mode, false for Observable mode)
|
|
892
|
-
* @returns
|
|
850
|
+
* @returns Promise<AuthResponse> if asPromise is true, otherwise Observable<AuthResponse>
|
|
893
851
|
*
|
|
894
852
|
* @example
|
|
895
853
|
* ```typescript
|
|
@@ -974,19 +932,17 @@ const AuthState = signalStore({ providedIn: "root" }, withState(initialState), w
|
|
|
974
932
|
* @see {@link SignInBody} Interface for sign-in request data
|
|
975
933
|
* @see {@link AuthResponse} Interface for authentication response
|
|
976
934
|
*/
|
|
977
|
-
signIn: (signInBody, redirect,
|
|
978
|
-
const sub = authService.signIn(signInBody, !(showError ||
|
|
935
|
+
signIn: (signInBody, redirect, asPromise, showError) => {
|
|
936
|
+
const sub = authService.signIn(signInBody, !(showError || asPromise && showError === undefined)).pipe(tap((res) => {
|
|
979
937
|
patchState(state, { ...res, signedIn: true });
|
|
980
938
|
if (redirect) {
|
|
981
939
|
void router.navigateByUrl(typeof redirect === "string" ? redirect : redirect(res));
|
|
982
940
|
}
|
|
983
941
|
}));
|
|
984
|
-
if (
|
|
985
|
-
return sub;
|
|
942
|
+
if (asPromise) {
|
|
943
|
+
return firstValueFrom(sub);
|
|
986
944
|
}
|
|
987
|
-
return
|
|
988
|
-
sub.subscribe({ next: () => resolve(), error: () => resolve() });
|
|
989
|
-
});
|
|
945
|
+
return sub;
|
|
990
946
|
},
|
|
991
947
|
/**
|
|
992
948
|
* Authenticates a user using an existing access token
|
|
@@ -1002,9 +958,9 @@ const AuthState = signalStore({ providedIn: "root" }, withState(initialState), w
|
|
|
1002
958
|
*
|
|
1003
959
|
* @param accessToken - The access token to authenticate with
|
|
1004
960
|
* @param redirect - Optional redirect path or function that returns a path after successful authentication
|
|
1005
|
-
* @param
|
|
961
|
+
* @param asPromise - Optional flag to return a Promise instead of an Observable (defaults to false)
|
|
1006
962
|
* @param showError - Optional flag to control error notification display (defaults to true for Promise mode, false for Observable mode)
|
|
1007
|
-
* @returns
|
|
963
|
+
* @returns Promise<AuthResponse> if asPromise is true, otherwise Observable<AuthResponse>
|
|
1008
964
|
*
|
|
1009
965
|
* @example
|
|
1010
966
|
* ```typescript
|
|
@@ -1083,8 +1039,8 @@ const AuthState = signalStore({ providedIn: "root" }, withState(initialState), w
|
|
|
1083
1039
|
* @see {@link AccessToken} Type representing access tokens
|
|
1084
1040
|
* @see {@link AuthResponse} Interface for authentication response
|
|
1085
1041
|
*/
|
|
1086
|
-
authenticateWithToken: (accessToken, redirect,
|
|
1087
|
-
const sub = authService.getAuthResponse(accessToken, !(showError ||
|
|
1042
|
+
authenticateWithToken: (accessToken, redirect, asPromise, showError) => {
|
|
1043
|
+
const sub = authService.getAuthResponse(accessToken, !(showError || asPromise && showError === undefined)).pipe(tap((res) => {
|
|
1088
1044
|
patchState(state, {
|
|
1089
1045
|
...res,
|
|
1090
1046
|
signedIn: Boolean(res.user.role)
|
|
@@ -1093,12 +1049,10 @@ const AuthState = signalStore({ providedIn: "root" }, withState(initialState), w
|
|
|
1093
1049
|
void router.navigateByUrl(typeof redirect === "string" ? redirect : redirect(res));
|
|
1094
1050
|
}
|
|
1095
1051
|
}), catchError(() => EMPTY));
|
|
1096
|
-
if (
|
|
1097
|
-
return sub;
|
|
1052
|
+
if (asPromise) {
|
|
1053
|
+
return firstValueFrom(sub);
|
|
1098
1054
|
}
|
|
1099
|
-
return
|
|
1100
|
-
sub.subscribe({ next: () => resolve(), error: () => resolve() });
|
|
1101
|
-
});
|
|
1055
|
+
return sub;
|
|
1102
1056
|
},
|
|
1103
1057
|
/**
|
|
1104
1058
|
* Signs out the current user and clears authentication state
|
|
@@ -1112,9 +1066,9 @@ const AuthState = signalStore({ providedIn: "root" }, withState(initialState), w
|
|
|
1112
1066
|
* handling that ensures local state is cleared even if server sign-out fails.
|
|
1113
1067
|
*
|
|
1114
1068
|
* @param redirect - Optional redirect path after successful sign-out
|
|
1115
|
-
* @param
|
|
1069
|
+
* @param asPromise - Optional flag to return a Promise instead of an Observable (defaults to false)
|
|
1116
1070
|
* @param showError - Optional flag to control error notification display (defaults to true for Promise mode, false for Observable mode)
|
|
1117
|
-
* @returns
|
|
1071
|
+
* @returns Promise<SuccessResponse | null> if asPromise is true, otherwise Observable<SuccessResponse | null>
|
|
1118
1072
|
*
|
|
1119
1073
|
* @example
|
|
1120
1074
|
* ```typescript
|
|
@@ -1204,8 +1158,8 @@ const AuthState = signalStore({ providedIn: "root" }, withState(initialState), w
|
|
|
1204
1158
|
* @see {@link SuccessResponse} Interface for success response
|
|
1205
1159
|
* @see {@link reset} Method to manually clear authentication state
|
|
1206
1160
|
*/
|
|
1207
|
-
signOut: (redirect,
|
|
1208
|
-
const sub = authService.signOut(!(showError ||
|
|
1161
|
+
signOut: (redirect, asPromise, showError) => {
|
|
1162
|
+
const sub = authService.signOut(!(showError || asPromise && showError === undefined)).pipe(tap({
|
|
1209
1163
|
next: () => {
|
|
1210
1164
|
patchState(state, initialState);
|
|
1211
1165
|
if (redirect) {
|
|
@@ -1213,13 +1167,17 @@ const AuthState = signalStore({ providedIn: "root" }, withState(initialState), w
|
|
|
1213
1167
|
}
|
|
1214
1168
|
},
|
|
1215
1169
|
}), catchError(() => EMPTY));
|
|
1216
|
-
if (
|
|
1217
|
-
return sub;
|
|
1170
|
+
if (asPromise) {
|
|
1171
|
+
return firstValueFrom(sub);
|
|
1218
1172
|
}
|
|
1219
|
-
return
|
|
1220
|
-
sub.subscribe({ next: () => resolve(), error: () => resolve() });
|
|
1221
|
-
});
|
|
1173
|
+
return sub;
|
|
1222
1174
|
},
|
|
1175
|
+
setData(stateData) {
|
|
1176
|
+
patchState(state, data => ({
|
|
1177
|
+
...data,
|
|
1178
|
+
data: { ...data.data, ...stateData },
|
|
1179
|
+
}));
|
|
1180
|
+
}
|
|
1223
1181
|
})));
|
|
1224
1182
|
|
|
1225
1183
|
/* eslint-disable @angular-eslint/no-output-on-prefix */
|
|
@@ -1293,15 +1251,15 @@ const AuthState = signalStore({ providedIn: "root" }, withState(initialState), w
|
|
|
1293
1251
|
* @see {@link HttpError} Interface for HTTP error handling
|
|
1294
1252
|
*/
|
|
1295
1253
|
class AuthFormComponent {
|
|
1296
|
-
config;
|
|
1297
|
-
fb;
|
|
1298
|
-
authService;
|
|
1254
|
+
config = inject(AUTH_CONFIG);
|
|
1255
|
+
fb = inject(FormBuilder);
|
|
1256
|
+
authService = inject(AuthService);
|
|
1299
1257
|
/** Input signal to control whether local authentication (username/email + password) is enabled */
|
|
1300
|
-
local = input(true);
|
|
1258
|
+
local = input(true, ...(ngDevMode ? [{ debugName: "local" }] : []));
|
|
1301
1259
|
/** Input signal to control whether Google OAuth authentication is enabled */
|
|
1302
|
-
google = input(true);
|
|
1260
|
+
google = input(true, ...(ngDevMode ? [{ debugName: "google" }] : []));
|
|
1303
1261
|
/** Input signal to control whether Facebook authentication is enabled */
|
|
1304
|
-
facebook = input(true);
|
|
1262
|
+
facebook = input(true, ...(ngDevMode ? [{ debugName: "facebook" }] : []));
|
|
1305
1263
|
/** Output emitter that fires when an authentication error occurs */
|
|
1306
1264
|
onError = output();
|
|
1307
1265
|
/** Output emitter that fires when a user successfully signs in */
|
|
@@ -1309,26 +1267,23 @@ class AuthFormComponent {
|
|
|
1309
1267
|
/** Output emitter that fires when a user successfully signs up */
|
|
1310
1268
|
onSignUp = output();
|
|
1311
1269
|
/** Writable signal indicating whether an authentication operation is in progress */
|
|
1312
|
-
isLoading = signal(false);
|
|
1270
|
+
isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
1313
1271
|
/** Writable signal indicating whether the form is in sign-up mode (true) or sign-in mode (false) */
|
|
1314
|
-
isSignUp = signal(false);
|
|
1272
|
+
isSignUp = signal(false, ...(ngDevMode ? [{ debugName: "isSignUp" }] : []));
|
|
1315
1273
|
/** Writable signal indicating whether an error state is currently active */
|
|
1316
|
-
isError = signal(false);
|
|
1274
|
+
isError = signal(false, ...(ngDevMode ? [{ debugName: "isError" }] : []));
|
|
1317
1275
|
/** Writable signal containing the current authentication field type (EMAIL or USERNAME) */
|
|
1318
|
-
authField = signal(AuthField.EMAIL);
|
|
1276
|
+
authField = signal(AuthField.EMAIL, ...(ngDevMode ? [{ debugName: "authField" }] : []));
|
|
1319
1277
|
/** Writable signal containing the display label for the authentication field */
|
|
1320
|
-
authFieldLabel = signal(toFirstCase(AuthField.EMAIL));
|
|
1278
|
+
authFieldLabel = signal(toFirstCase(AuthField.EMAIL), ...(ngDevMode ? [{ debugName: "authFieldLabel" }] : []));
|
|
1321
1279
|
/** Writable signal containing the current error object, if any */
|
|
1322
|
-
error = signal(null);
|
|
1280
|
+
error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
1323
1281
|
/** Injected AuthState service for managing authentication state */
|
|
1324
1282
|
authState = inject(AuthState);
|
|
1325
1283
|
/** Reactive form group for handling authentication form data */
|
|
1326
1284
|
authForm;
|
|
1327
|
-
constructor(
|
|
1328
|
-
this.config
|
|
1329
|
-
this.fb = fb;
|
|
1330
|
-
this.authService = authService;
|
|
1331
|
-
this.authField.set(config.authField === AuthField.USERNAME ? AuthField.USERNAME : AuthField.EMAIL);
|
|
1285
|
+
constructor() {
|
|
1286
|
+
this.authField.set(this.config.authField === AuthField.USERNAME ? AuthField.USERNAME : AuthField.EMAIL);
|
|
1332
1287
|
this.authFieldLabel.set(toFirstCase(this.authField()));
|
|
1333
1288
|
this.authForm = this.fb.group({
|
|
1334
1289
|
firstName: ["", Validators.required],
|
|
@@ -1365,7 +1320,7 @@ class AuthFormComponent {
|
|
|
1365
1320
|
async handleGoogleSignIn() {
|
|
1366
1321
|
const accessToken = await this.authService.googleSignIn();
|
|
1367
1322
|
// noinspection ES6MissingAwait
|
|
1368
|
-
this.authState.authenticateWithToken(accessToken, undefined,
|
|
1323
|
+
this.authState.authenticateWithToken(accessToken, undefined, false).subscribe({
|
|
1369
1324
|
next: authResponse => {
|
|
1370
1325
|
this.isLoading.set(false);
|
|
1371
1326
|
this.onSignIn.emit(authResponse);
|
|
@@ -1394,7 +1349,15 @@ class AuthFormComponent {
|
|
|
1394
1349
|
handleLocalAuth(signInBody) {
|
|
1395
1350
|
this.isLoading.set(true);
|
|
1396
1351
|
this.isError.set(false);
|
|
1397
|
-
this.authState.signIn(signInBody,
|
|
1352
|
+
this.authState.signIn(signInBody, "/", false).subscribe({
|
|
1353
|
+
next: () => {
|
|
1354
|
+
this.isLoading.set(false);
|
|
1355
|
+
},
|
|
1356
|
+
error: () => {
|
|
1357
|
+
this.isLoading.set(false);
|
|
1358
|
+
this.isError.set(true);
|
|
1359
|
+
},
|
|
1360
|
+
});
|
|
1398
1361
|
}
|
|
1399
1362
|
/**
|
|
1400
1363
|
* Handles user registration (sign-up) process
|
|
@@ -1490,16 +1453,13 @@ class AuthFormComponent {
|
|
|
1490
1453
|
this.error.set(error);
|
|
1491
1454
|
this.onError.emit(error);
|
|
1492
1455
|
}
|
|
1493
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1494
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.7", type: AuthFormComponent, isStandalone: false, selector: "hc-auth-card", inputs: { local: { classPropertyName: "local", publicName: "local", isSignal: true, isRequired: false, transformFunction: null }, google: { classPropertyName: "google", publicName: "google", isSignal: true, isRequired: false, transformFunction: null }, facebook: { classPropertyName: "facebook", publicName: "facebook", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onError: "onError", onSignIn: "onSignIn", onSignUp: "onSignUp" }, ngImport: i0, template: "<!--<div class=\"auth-container d-flex justify-content-center align-items-center\">-->\r\n<!-- -->\r\n<!--</div>-->\r\n<hc-card>\r\n <!-- @if (isLoading()) {-->\r\n <!-- <div class=\"loading-overlay w-100 h-100\"></div>-->\r\n <!-- }-->\r\n <form\r\n class=\"d-inline-flex flex-column align-items-center w-100\"\r\n [formGroup]=\"authForm\"\r\n (ngSubmit)=\"handleSubmit($event)\"\r\n >\r\n @if (isSignUp()) {\r\n <div class=\"form-group w-100\">\r\n <label for=\"firstName\" class=\"form-label\">First Name</label>\r\n <input\r\n id=\"firstName\"\r\n type=\"text\"\r\n class=\"form-control w-100\"\r\n formControlName=\"firstName\"\r\n placeholder=\"Enter your first name\"\r\n />\r\n </div>\r\n\r\n <div class=\"form-group w-100\">\r\n <label for=\"lastName\" class=\"form-label\">Last Name</label>\r\n <input\r\n id=\"lastName\"\r\n type=\"text\"\r\n class=\"form-control w-100\"\r\n formControlName=\"lastName\"\r\n placeholder=\"Enter your last name\"\r\n />\r\n </div>\r\n }\r\n\r\n <div class=\"form-group w-100\">\r\n <label for=\"authField\" class=\"form-label\">{{ authFieldLabel() }}</label>\r\n <input\r\n id=\"authField\"\r\n type=\"text\"\r\n class=\"form-control w-100\"\r\n formControlName=\"authFieldValue\"\r\n [placeholder]=\"'Enter your ' + authFieldLabel().toLowerCase()\"\r\n />\r\n </div>\r\n\r\n <div class=\"form-group w-100\">\r\n <label for=\"password\" class=\"form-label\">Password</label>\r\n <input\r\n id=\"password\"\r\n type=\"password\"\r\n class=\"form-control w-100\"\r\n formControlName=\"password\"\r\n placeholder=\"Enter your password\"\r\n />\r\n </div>\r\n\r\n <button type=\"submit\" class=\"btn btn-primary mb-3 w-100\" [disabled]=\"isLoading()\">\r\n {{ isSignUp() ? (isLoading() ? \"Signing Up...\" : \"Sign Up\") : isLoading() ? \"Signing In...\" : \"Sign In\" }}\r\n </button>\r\n\r\n <button type=\"button\" class=\"btn btn-link p-0 mb-3\" (click)=\"isSignUp.set(!isSignUp())\">\r\n {{ isSignUp() ? \"Already have an account? Sign In\" : \"Don't have an account? Sign Up\" }}\r\n </button>\r\n\r\n @if (!isSignUp()) {\r\n @if (local() && (google() || facebook())) {\r\n <hc-separator label=\"OR\"></hc-separator>\r\n }\r\n\r\n @if (google()) {\r\n <button type=\"button\" class=\"btn google-btn btn-light mb-3 w-100\" (click)=\"handleGoogleSignIn()\">\r\n <div class=\"icon\"></div>\r\n Sign in with Google\r\n </button>\r\n }\r\n\r\n @if (facebook()) {\r\n <button type=\"button\" class=\"btn facebook-btn btn-light mb-3 w-100\">\r\n <div class=\"icon\"></div>\r\n Sign in with Facebook\r\n </button>\r\n }\r\n\r\n @if (isError()) {\r\n <div class=\"error-message w-100\">\r\n {{ error()?.error?.message || \"Something went wrong!\" }}\r\n </div>\r\n }\r\n }\r\n </form>\r\n</hc-card>\r\n", styles: [".auth-form{background:#fffffff2;-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px);border-radius:16px;margin:8px;padding:2rem;position:relative;z-index:1}.loading-overlay{background:#ffffffe6;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);border-radius:16px;display:flex;align-items:center;justify-content:center;font-weight:600;color:var(--secondary-color);font-size:1.1rem;position:absolute;top:0;left:0}.loading-overlay:after{content:\"\";width:50px;height:50px;border:5px solid var(--secondary-color);border-top:5px solid transparent;border-radius:50%;animation:spin 1s linear infinite;margin-left:10px}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.form-group{position:relative;margin-bottom:1.5rem}.form-label{font-weight:600;color:var(--text-color);margin-bottom:.5rem;font-size:.9rem;text-transform:uppercase;letter-spacing:.5px}.form-control{border:2px solid var(--border-color);border-radius:12px;padding:.75rem 1rem;font-size:1rem;transition:all .3s ease;background:#fffc;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.form-control:focus{border-color:var(--secondary-color);box-shadow:0 0 0 3px #667eea1a;background:#fffffff2}.form-control::placeholder{color:var(--text-light)}.btn{border-radius:12px;padding:.75rem 1.5rem;font-weight:600;font-size:1rem;transition:all .3s ease;border:none;position:relative;overflow:hidden}.btn:before{content:\"\";position:absolute;top:0;left:-100%;width:100%;height:100%;background:linear-gradient(90deg,transparent,rgba(255,255,255,.2),transparent);transition:left .5s}.btn:hover:before{left:100%}.btn-primary{background:var(--secondary-gradient);color:#fff;box-shadow:var(--shadow-md)}.btn-primary:hover{box-shadow:var(--shadow-lg)}.btn-link{color:var(--text-light);text-decoration:none;font-size:.9rem;transition:all .3s ease}.btn-link:hover{color:var(--secondary-color);text-decoration:underline}.google-btn,.facebook-btn{display:flex;align-items:center;justify-content:center;gap:12px;color:var(--text-color);background:#ffffffe6;border:2px solid var(--border-color);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);font-weight:600;transition:all .3s ease}.google-btn:hover,.facebook-btn:hover{background:#fff;border-color:var(--secondary-color);box-shadow:var(--shadow-md)}.google-btn .icon,.facebook-btn .icon{height:24px;width:24px;background-repeat:no-repeat;background-size:contain;background-position:center}.google-btn .icon{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTgiIGhlaWdodD0iMTgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNMTcuNiA5LjJsLS4xLTEuOEg5djMuNGg0LjhDMTMuNiAxMiAxMyAxMyAxMiAxMy42djIuMmgzYTguOCA4LjggMCAwIDAgMi42LTYuNnoiIGZpbGw9IiM0Mjg1RjQiIGZpbGwtcnVsZT0ibm9uemVybyIvPjxwYXRoIGQ9Ik05IDE4YzIuNCAwIDQuNS0uOCA2LTIuMmwtMy0yLjJhNS40IDUuNCAwIDAgMS04LTIuOUgxVjEzYTkgOSAwIDAgMCA4IDV6IiBmaWxsPSIjMzRBODUzIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48cGF0aCBkPSJNNCAxMC43YTUuNCA1LjQgMCAwIDEgMC0zLjRWNUgxYTkgOSAwIDAgMCAwIDhsMy0yLjN6IiBmaWxsPSIjRkJCQzA1IiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48cGF0aCBkPSJNOSAzLjZjMS4zIDAgMi41LjQgMy40IDEuM0wxNSAyLjNBOSA5IDAgMCAwIDEgNWwzIDIuNGE1LjQgNS40IDAgMCAxIDUtMy43eiIgZmlsbD0iI0VBNDMzNSIgZmlsbC1ydWxlPSJub256ZXJvIi8+PHBhdGggZD0iTTAgMGgxOHYxOEgweiIvPjwvZz48L3N2Zz4=)}.facebook-btn .icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAmJJREFUaEPtmj9oFEEUxr9v7nIpzkIRLQQVFLUSBBs7IbfGwkrhtNHbvYiiRTqbNKIRbGwD/iFkd88uISmsEtwErRQsLEVUiGIbsDAK8W6e3IESkuzt3eVucwMz7b5hvt/73rwZluHwtcr+Wrb6BMJhgnkYMASyKlotDGZqt1goT81R1EUDdG+SqDXnWPD8n6ZkfiNB3Qk6XiAmZv+fZguw0+5ZBzp3QITAOw1EiupDrYYVZvFHaZ0DkNfCHCn7ILwAwolbZ0ccEMgCtRqLKu77pAQ45eAOBI/6CID3oqA0DrCl7tdfAIKJKPRGk7K+/nsfAci3Pav5YzMzl9fMBCBHI9+daEd8PbZvHKhmswdfTV79biSACD4tht7xOPHF4nTux65fD7RIEcIDJAZbBU2ljYrm0mLFLcSJKpSD+xTcbVX0+rhUADT19JI/ciUWwAveEDjTtwAAn0eBW4oT6LjBFxBHzAUohctQctgCdJKB1uYklJB1oLU0JkYJMZ7ReLExUFFW5oPycuwm9vyTSli/Rm8aNWKSwKmUbqNyPQrKU4mkbQQ4nv8V4CEjAU7ffDqwey33m2DGSIAhNziqiM/NDOvySdzdEiqMhA61vDQWwPH8GwCfGQtwzg0eCjGWGoCGvFbkxy0WfBv5nh8npCFUYe8WPfQslJxIDSB+IXsSx+amy10otls3v07bu1AbR3tnoXYP2D3QWeX8n2VLyJaQLaGm/4XsQbbNAkmebrtQfBdK56lBbxxoPDUYKoWzSsml5DLYTkRvAADMsv7cpkp5TKXP9+7RR3cBGpkH5weob/8FwaStQs990hUAAAAASUVORK5CYII=)}.separator{position:relative;width:calc(100% - 40px);height:1px;background:linear-gradient(90deg,transparent,var(--border-color),transparent);margin:2rem auto}.separator:after{content:\"OR\";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background:#ffffffe6;padding:0 1rem;font-size:.8rem;font-weight:600;color:var(--text-light);letter-spacing:1px}.error-message{color:var(--danger-color);margin-top:1rem;font-weight:600;text-align:center}@keyframes slideIn{0%{opacity:0}to{opacity:1}}@media (max-width: 480px){.auth-container{margin:1rem;border-radius:16px}.auth-form{padding:1.5rem;margin:4px}.btn{padding:.875rem 1.25rem;font-size:.95rem}}\n"], dependencies: [{ kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3.HcCardComponent, selector: "hc-card" }, { kind: "component", type: i3.HcSeparatorComponent, selector: "hc-separator", inputs: ["label"] }] });
|
|
1456
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: AuthFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1457
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.4", type: AuthFormComponent, isStandalone: false, selector: "hc-auth-card", inputs: { local: { classPropertyName: "local", publicName: "local", isSignal: true, isRequired: false, transformFunction: null }, google: { classPropertyName: "google", publicName: "google", isSignal: true, isRequired: false, transformFunction: null }, facebook: { classPropertyName: "facebook", publicName: "facebook", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onError: "onError", onSignIn: "onSignIn", onSignUp: "onSignUp" }, ngImport: i0, template: "<hc-card>\r\n <section class=\"auth-shell\">\r\n <header class=\"auth-header\">\r\n <div class=\"auth-badge\">{{ isSignUp() ? \"Create Account\" : \"Welcome Back\" }}</div>\r\n <h2 class=\"auth-title\">{{ isSignUp() ? \"Create your account\" : \"Sign in to your account\" }}</h2>\r\n <p class=\"auth-subtitle\">\r\n {{ isSignUp() ? \"Set up your account to get started.\" : \"Use your credentials to continue.\" }}\r\n </p>\r\n </header>\r\n\r\n <form class=\"auth-form\" [formGroup]=\"authForm\" (ngSubmit)=\"handleSubmit($event)\">\r\n @if (isSignUp()) {\r\n <div class=\"auth-grid\">\r\n <div class=\"auth-field\">\r\n <label for=\"firstName\" class=\"auth-label\">First Name</label>\r\n <input\r\n id=\"firstName\"\r\n type=\"text\"\r\n class=\"auth-input\"\r\n formControlName=\"firstName\"\r\n placeholder=\"Enter your first name\"\r\n />\r\n </div>\r\n <div class=\"auth-field\">\r\n <label for=\"lastName\" class=\"auth-label\">Last Name</label>\r\n <input\r\n id=\"lastName\"\r\n type=\"text\"\r\n class=\"auth-input\"\r\n formControlName=\"lastName\"\r\n placeholder=\"Enter your last name\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n\r\n <div class=\"auth-field\">\r\n <label for=\"authField\" class=\"auth-label\">{{ authFieldLabel() }}</label>\r\n <input\r\n id=\"authField\"\r\n type=\"text\"\r\n class=\"auth-input\"\r\n formControlName=\"authFieldValue\"\r\n [placeholder]=\"'Enter your ' + authFieldLabel().toLowerCase()\"\r\n />\r\n </div>\r\n\r\n <div class=\"auth-field\">\r\n <label for=\"password\" class=\"auth-label\">Password</label>\r\n <input\r\n id=\"password\"\r\n type=\"password\"\r\n class=\"auth-input\"\r\n formControlName=\"password\"\r\n placeholder=\"Enter your password\"\r\n />\r\n </div>\r\n\r\n <button type=\"submit\" class=\"auth-btn auth-btn-primary\">\r\n {{ isSignUp() ? (isLoading() ? \"Signing Up...\" : \"Sign Up\") : isLoading() ? \"Signing In...\" : \"Sign In\" }}\r\n </button>\r\n\r\n <button type=\"button\" class=\"auth-btn auth-btn-link\" (click)=\"isSignUp.set(!isSignUp())\">\r\n {{ isSignUp() ? \"Already have an account? Sign In\" : \"Don't have an account? Sign Up\" }}\r\n </button>\r\n\r\n @if (!isSignUp()) {\r\n @if (local() && (google() || facebook())) {\r\n <hc-separator label=\"or\"></hc-separator>\r\n }\r\n\r\n @if (google()) {\r\n <button type=\"button\" class=\"auth-btn social-btn google-btn\" (click)=\"handleGoogleSignIn()\">\r\n <div class=\"icon\"></div>\r\n Continue with Google\r\n </button>\r\n }\r\n\r\n @if (facebook()) {\r\n <button type=\"button\" class=\"auth-btn social-btn facebook-btn\">\r\n <div class=\"icon\"></div>\r\n Continue with Facebook\r\n </button>\r\n }\r\n\r\n @if (isError()) {\r\n <p class=\"error-message\">\r\n {{ error()?.error?.message || \"Something went wrong!\" }}\r\n </p>\r\n }\r\n }\r\n </form>\r\n </section>\r\n</hc-card>\r\n", styles: [".auth-shell{display:grid;gap:1rem}.auth-header{text-align:center}.auth-badge{display:inline-flex;align-items:center;border-radius:999px;padding:.3rem .65rem;background:#2563eb17;color:#1d4ed8;font-size:.68rem;font-weight:700;letter-spacing:.09em;text-transform:uppercase}.auth-title{margin:.55rem 0 .25rem;color:#0f172a;font-size:clamp(1.1rem,2.8vw,1.45rem);font-weight:700;line-height:1.25;letter-spacing:-.02em}.auth-subtitle{margin:0;color:#64748b;font-size:.88rem}.auth-form{display:grid;gap:.85rem}.auth-grid{display:grid;grid-template-columns:1fr 1fr;gap:.75rem}.auth-field{display:grid;gap:.4rem}.auth-label{display:block;font-weight:700;color:#334155;font-size:.69rem;text-transform:uppercase;letter-spacing:.08em}.auth-input{width:100%;border:1px solid #cbd5e1;border-radius:.75rem;padding:.74rem .82rem;font-size:.92rem;color:#0f172a;background:#fff;transition:border-color .2s ease,box-shadow .2s ease}.auth-input:focus{border-color:#2563eb;box-shadow:0 0 0 3px #2563eb26;outline:0}.auth-input::placeholder{color:#94a3b8}.auth-btn{width:100%;min-height:2.7rem;border:0;border-radius:.78rem;font-size:.9rem;font-weight:700;line-height:1.2;cursor:pointer;transition:transform .2s ease,box-shadow .2s ease,background-color .2s ease}.auth-btn:disabled{cursor:not-allowed;opacity:.7}.auth-btn-primary{background:linear-gradient(135deg,#2563eb,#4f46e5);color:#fff;box-shadow:0 16px 26px -18px #2563ebd9}.auth-btn-primary:hover:not(:disabled){transform:translateY(-1px);box-shadow:0 20px 28px -18px #2563ebe6}.auth-btn-link{min-height:1.65rem;margin-top:-.1rem;border-radius:0;background:transparent;color:#64748b;font-size:.82rem;font-weight:600;box-shadow:none}.auth-btn-link:hover{color:#2563eb;text-decoration:underline}.social-btn{display:flex;align-items:center;justify-content:center;gap:.6rem;border:1px solid #cbd5e1;background:#fff;color:#1e293b}.social-btn:hover{background:#f8fafc;border-color:#94a3b8}.social-btn .icon{height:1.1rem;width:1.1rem;background-repeat:no-repeat;background-size:contain;background-position:center}.google-btn .icon{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTgiIGhlaWdodD0iMTgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNMTcuNiA5LjJsLS4xLTEuOEg5djMuNGg0LjhDMTMuNiAxMiAxMyAxMyAxMiAxMy42djIuMmgzYTguOCA4LjggMCAwIDAgMi42LTYuNnoiIGZpbGw9IiM0Mjg1RjQiIGZpbGwtcnVsZT0ibm9uemVybyIvPjxwYXRoIGQ9Ik05IDE4YzIuNCAwIDQuNS0uOCA2LTIuMmwtMy0yLjJhNS40IDUuNCAwIDAgMS04LTIuOUgxVjEzYTkgOSAwIDAgMCA4IDV6IiBmaWxsPSIjMzRBODUzIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48cGF0aCBkPSJNNCAxMC43YTUuNCA1LjQgMCAwIDEgMC0zLjRWNUgxYTkgOSAwIDAgMCAwIDhsMy0yLjN6IiBmaWxsPSIjRkJCQzA1IiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48cGF0aCBkPSJNOSAzLjZjMS4zIDAgMi41LjQgMy40IDEuM0wxNSAyLjNBOSA5IDAgMCAwIDEgNWwzIDIuNGE1LjQgNS40IDAgMCAxIDUtMy43eiIgZmlsbD0iI0VBNDMzNSIgZmlsbC1ydWxlPSJub256ZXJvIi8+PHBhdGggZD0iTTAgMGgxOHYxOEgweiIvPjwvZz48L3N2Zz4=)}.facebook-btn .icon{background:#1877f2;border-radius:999px;position:relative}.facebook-btn .icon:before{content:\"f\";position:absolute;left:50%;top:50%;transform:translate(-44%,-56%);color:#fff;font-weight:700;font-size:.9rem;font-family:Arial,sans-serif}.error-message{margin:0;border-radius:.7rem;padding:.62rem .75rem;background:#fee2e2;border:1px solid #fecaca;color:#b91c1c;text-align:center;font-size:.82rem;font-weight:600}@media(max-width:480px){.auth-grid{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i2.HcCardComponent, selector: "hc-card" }, { kind: "component", type: i2.HcSeparatorComponent, selector: "hc-separator", inputs: ["label"] }] });
|
|
1495
1458
|
}
|
|
1496
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1459
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: AuthFormComponent, decorators: [{
|
|
1497
1460
|
type: Component,
|
|
1498
|
-
args: [{ selector: "hc-auth-card", standalone: false, template: "
|
|
1499
|
-
}], ctorParameters: () => [{ type:
|
|
1500
|
-
type: Inject,
|
|
1501
|
-
args: [AUTH_CONFIG]
|
|
1502
|
-
}] }, { type: i1$1.FormBuilder }, { type: AuthService }] });
|
|
1461
|
+
args: [{ selector: "hc-auth-card", standalone: false, template: "<hc-card>\r\n <section class=\"auth-shell\">\r\n <header class=\"auth-header\">\r\n <div class=\"auth-badge\">{{ isSignUp() ? \"Create Account\" : \"Welcome Back\" }}</div>\r\n <h2 class=\"auth-title\">{{ isSignUp() ? \"Create your account\" : \"Sign in to your account\" }}</h2>\r\n <p class=\"auth-subtitle\">\r\n {{ isSignUp() ? \"Set up your account to get started.\" : \"Use your credentials to continue.\" }}\r\n </p>\r\n </header>\r\n\r\n <form class=\"auth-form\" [formGroup]=\"authForm\" (ngSubmit)=\"handleSubmit($event)\">\r\n @if (isSignUp()) {\r\n <div class=\"auth-grid\">\r\n <div class=\"auth-field\">\r\n <label for=\"firstName\" class=\"auth-label\">First Name</label>\r\n <input\r\n id=\"firstName\"\r\n type=\"text\"\r\n class=\"auth-input\"\r\n formControlName=\"firstName\"\r\n placeholder=\"Enter your first name\"\r\n />\r\n </div>\r\n <div class=\"auth-field\">\r\n <label for=\"lastName\" class=\"auth-label\">Last Name</label>\r\n <input\r\n id=\"lastName\"\r\n type=\"text\"\r\n class=\"auth-input\"\r\n formControlName=\"lastName\"\r\n placeholder=\"Enter your last name\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n\r\n <div class=\"auth-field\">\r\n <label for=\"authField\" class=\"auth-label\">{{ authFieldLabel() }}</label>\r\n <input\r\n id=\"authField\"\r\n type=\"text\"\r\n class=\"auth-input\"\r\n formControlName=\"authFieldValue\"\r\n [placeholder]=\"'Enter your ' + authFieldLabel().toLowerCase()\"\r\n />\r\n </div>\r\n\r\n <div class=\"auth-field\">\r\n <label for=\"password\" class=\"auth-label\">Password</label>\r\n <input\r\n id=\"password\"\r\n type=\"password\"\r\n class=\"auth-input\"\r\n formControlName=\"password\"\r\n placeholder=\"Enter your password\"\r\n />\r\n </div>\r\n\r\n <button type=\"submit\" class=\"auth-btn auth-btn-primary\">\r\n {{ isSignUp() ? (isLoading() ? \"Signing Up...\" : \"Sign Up\") : isLoading() ? \"Signing In...\" : \"Sign In\" }}\r\n </button>\r\n\r\n <button type=\"button\" class=\"auth-btn auth-btn-link\" (click)=\"isSignUp.set(!isSignUp())\">\r\n {{ isSignUp() ? \"Already have an account? Sign In\" : \"Don't have an account? Sign Up\" }}\r\n </button>\r\n\r\n @if (!isSignUp()) {\r\n @if (local() && (google() || facebook())) {\r\n <hc-separator label=\"or\"></hc-separator>\r\n }\r\n\r\n @if (google()) {\r\n <button type=\"button\" class=\"auth-btn social-btn google-btn\" (click)=\"handleGoogleSignIn()\">\r\n <div class=\"icon\"></div>\r\n Continue with Google\r\n </button>\r\n }\r\n\r\n @if (facebook()) {\r\n <button type=\"button\" class=\"auth-btn social-btn facebook-btn\">\r\n <div class=\"icon\"></div>\r\n Continue with Facebook\r\n </button>\r\n }\r\n\r\n @if (isError()) {\r\n <p class=\"error-message\">\r\n {{ error()?.error?.message || \"Something went wrong!\" }}\r\n </p>\r\n }\r\n }\r\n </form>\r\n </section>\r\n</hc-card>\r\n", styles: [".auth-shell{display:grid;gap:1rem}.auth-header{text-align:center}.auth-badge{display:inline-flex;align-items:center;border-radius:999px;padding:.3rem .65rem;background:#2563eb17;color:#1d4ed8;font-size:.68rem;font-weight:700;letter-spacing:.09em;text-transform:uppercase}.auth-title{margin:.55rem 0 .25rem;color:#0f172a;font-size:clamp(1.1rem,2.8vw,1.45rem);font-weight:700;line-height:1.25;letter-spacing:-.02em}.auth-subtitle{margin:0;color:#64748b;font-size:.88rem}.auth-form{display:grid;gap:.85rem}.auth-grid{display:grid;grid-template-columns:1fr 1fr;gap:.75rem}.auth-field{display:grid;gap:.4rem}.auth-label{display:block;font-weight:700;color:#334155;font-size:.69rem;text-transform:uppercase;letter-spacing:.08em}.auth-input{width:100%;border:1px solid #cbd5e1;border-radius:.75rem;padding:.74rem .82rem;font-size:.92rem;color:#0f172a;background:#fff;transition:border-color .2s ease,box-shadow .2s ease}.auth-input:focus{border-color:#2563eb;box-shadow:0 0 0 3px #2563eb26;outline:0}.auth-input::placeholder{color:#94a3b8}.auth-btn{width:100%;min-height:2.7rem;border:0;border-radius:.78rem;font-size:.9rem;font-weight:700;line-height:1.2;cursor:pointer;transition:transform .2s ease,box-shadow .2s ease,background-color .2s ease}.auth-btn:disabled{cursor:not-allowed;opacity:.7}.auth-btn-primary{background:linear-gradient(135deg,#2563eb,#4f46e5);color:#fff;box-shadow:0 16px 26px -18px #2563ebd9}.auth-btn-primary:hover:not(:disabled){transform:translateY(-1px);box-shadow:0 20px 28px -18px #2563ebe6}.auth-btn-link{min-height:1.65rem;margin-top:-.1rem;border-radius:0;background:transparent;color:#64748b;font-size:.82rem;font-weight:600;box-shadow:none}.auth-btn-link:hover{color:#2563eb;text-decoration:underline}.social-btn{display:flex;align-items:center;justify-content:center;gap:.6rem;border:1px solid #cbd5e1;background:#fff;color:#1e293b}.social-btn:hover{background:#f8fafc;border-color:#94a3b8}.social-btn .icon{height:1.1rem;width:1.1rem;background-repeat:no-repeat;background-size:contain;background-position:center}.google-btn .icon{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTgiIGhlaWdodD0iMTgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNMTcuNiA5LjJsLS4xLTEuOEg5djMuNGg0LjhDMTMuNiAxMiAxMyAxMyAxMiAxMy42djIuMmgzYTguOCA4LjggMCAwIDAgMi42LTYuNnoiIGZpbGw9IiM0Mjg1RjQiIGZpbGwtcnVsZT0ibm9uemVybyIvPjxwYXRoIGQ9Ik05IDE4YzIuNCAwIDQuNS0uOCA2LTIuMmwtMy0yLjJhNS40IDUuNCAwIDAgMS04LTIuOUgxVjEzYTkgOSAwIDAgMCA4IDV6IiBmaWxsPSIjMzRBODUzIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48cGF0aCBkPSJNNCAxMC43YTUuNCA1LjQgMCAwIDEgMC0zLjRWNUgxYTkgOSAwIDAgMCAwIDhsMy0yLjN6IiBmaWxsPSIjRkJCQzA1IiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48cGF0aCBkPSJNOSAzLjZjMS4zIDAgMi41LjQgMy40IDEuM0wxNSAyLjNBOSA5IDAgMCAwIDEgNWwzIDIuNGE1LjQgNS40IDAgMCAxIDUtMy43eiIgZmlsbD0iI0VBNDMzNSIgZmlsbC1ydWxlPSJub256ZXJvIi8+PHBhdGggZD0iTTAgMGgxOHYxOEgweiIvPjwvZz48L3N2Zz4=)}.facebook-btn .icon{background:#1877f2;border-radius:999px;position:relative}.facebook-btn .icon:before{content:\"f\";position:absolute;left:50%;top:50%;transform:translate(-44%,-56%);color:#fff;font-weight:700;font-size:.9rem;font-family:Arial,sans-serif}.error-message{margin:0;border-radius:.7rem;padding:.62rem .75rem;background:#fee2e2;border:1px solid #fecaca;color:#b91c1c;text-align:center;font-size:.82rem;font-weight:600}@media(max-width:480px){.auth-grid{grid-template-columns:1fr}}\n"] }]
|
|
1462
|
+
}], ctorParameters: () => [], propDecorators: { local: [{ type: i0.Input, args: [{ isSignal: true, alias: "local", required: false }] }], google: [{ type: i0.Input, args: [{ isSignal: true, alias: "google", required: false }] }], facebook: [{ type: i0.Input, args: [{ isSignal: true, alias: "facebook", required: false }] }], onError: [{ type: i0.Output, args: ["onError"] }], onSignIn: [{ type: i0.Output, args: ["onSignIn"] }], onSignUp: [{ type: i0.Output, args: ["onSignUp"] }] } });
|
|
1503
1463
|
|
|
1504
1464
|
/**
|
|
1505
1465
|
* Angular structural directive for permission-based conditional rendering
|
|
@@ -1595,7 +1555,7 @@ class PermissionDirective {
|
|
|
1595
1555
|
* <div *hcPermission="['users.read', 'users.write']">Content</div>
|
|
1596
1556
|
* ```
|
|
1597
1557
|
*/
|
|
1598
|
-
hcPermission = input.required();
|
|
1558
|
+
hcPermission = input.required(...(ngDevMode ? [{ debugName: "hcPermission" }] : []));
|
|
1599
1559
|
/**
|
|
1600
1560
|
* Constructor that sets up the permission checking effect
|
|
1601
1561
|
*
|
|
@@ -1650,15 +1610,15 @@ class PermissionDirective {
|
|
|
1650
1610
|
: user.role.permissions.includes(requiredPermission)
|
|
1651
1611
|
: false;
|
|
1652
1612
|
}
|
|
1653
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1654
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
1613
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: PermissionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1614
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.4", type: PermissionDirective, isStandalone: true, selector: "[hcPermission]", inputs: { hcPermission: { classPropertyName: "hcPermission", publicName: "hcPermission", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
|
|
1655
1615
|
}
|
|
1656
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1616
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: PermissionDirective, decorators: [{
|
|
1657
1617
|
type: Directive,
|
|
1658
1618
|
args: [{
|
|
1659
1619
|
selector: "[hcPermission]",
|
|
1660
1620
|
}]
|
|
1661
|
-
}], ctorParameters: () => [] });
|
|
1621
|
+
}], ctorParameters: () => [], propDecorators: { hcPermission: [{ type: i0.Input, args: [{ isSignal: true, alias: "hcPermission", required: true }] }] } });
|
|
1662
1622
|
|
|
1663
1623
|
/**
|
|
1664
1624
|
* Enumeration of authentication guard conditions
|
|
@@ -2002,7 +1962,7 @@ function roleGuard(role, options) {
|
|
|
2002
1962
|
return false;
|
|
2003
1963
|
}
|
|
2004
1964
|
}
|
|
2005
|
-
await authState.signOut("/auth");
|
|
1965
|
+
await authState.signOut("/auth", true);
|
|
2006
1966
|
return false;
|
|
2007
1967
|
};
|
|
2008
1968
|
}
|
|
@@ -2274,6 +2234,9 @@ function authInterceptor(redirect, onRedirect) {
|
|
|
2274
2234
|
* @see {@link PermissionDirective} Permission-based conditional rendering directive
|
|
2275
2235
|
* @see {@link AuthService} Authentication service for managing user sessions
|
|
2276
2236
|
*/
|
|
2237
|
+
/**
|
|
2238
|
+
* Root module for reusable authentication UI, directives, and providers.
|
|
2239
|
+
*/
|
|
2277
2240
|
class NgxHichchiAuthModule {
|
|
2278
2241
|
/**
|
|
2279
2242
|
* Configures the NgxHichchiAuthModule with the provided authentication configuration
|
|
@@ -2309,27 +2272,25 @@ class NgxHichchiAuthModule {
|
|
|
2309
2272
|
static forRoot(config) {
|
|
2310
2273
|
return {
|
|
2311
2274
|
ngModule: NgxHichchiAuthModule,
|
|
2312
|
-
providers: [{ provide: AUTH_CONFIG, useValue: config },
|
|
2275
|
+
providers: [{ provide: AUTH_CONFIG, useValue: config }, AuthService],
|
|
2313
2276
|
};
|
|
2314
2277
|
}
|
|
2315
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
2316
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "
|
|
2278
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: NgxHichchiAuthModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
2279
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.4", ngImport: i0, type: NgxHichchiAuthModule, declarations: [AuthFormComponent], imports: [CommonModule,
|
|
2317
2280
|
FormsModule,
|
|
2318
2281
|
ReactiveFormsModule,
|
|
2319
2282
|
ButtonComponent,
|
|
2320
2283
|
HcCardComponent,
|
|
2321
|
-
HcCardComponent,
|
|
2322
2284
|
HcSeparatorComponent,
|
|
2323
2285
|
PermissionDirective], exports: [AuthFormComponent, PermissionDirective] });
|
|
2324
|
-
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "
|
|
2286
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: NgxHichchiAuthModule, imports: [CommonModule,
|
|
2325
2287
|
FormsModule,
|
|
2326
2288
|
ReactiveFormsModule,
|
|
2327
2289
|
ButtonComponent,
|
|
2328
2290
|
HcCardComponent,
|
|
2329
|
-
HcCardComponent,
|
|
2330
2291
|
HcSeparatorComponent] });
|
|
2331
2292
|
}
|
|
2332
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2293
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: NgxHichchiAuthModule, decorators: [{
|
|
2333
2294
|
type: NgModule,
|
|
2334
2295
|
args: [{
|
|
2335
2296
|
declarations: [AuthFormComponent],
|
|
@@ -2339,7 +2300,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.7", ngImpor
|
|
|
2339
2300
|
ReactiveFormsModule,
|
|
2340
2301
|
ButtonComponent,
|
|
2341
2302
|
HcCardComponent,
|
|
2342
|
-
HcCardComponent,
|
|
2343
2303
|
HcSeparatorComponent,
|
|
2344
2304
|
PermissionDirective,
|
|
2345
2305
|
],
|