@acontplus/ng-auth 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # @acontplus/ng-auth
2
2
 
3
- Acontplus Angular Authentication Module providing comprehensive authentication and authorization features for Angular applications.
3
+ Acontplus Angular Authentication Module providing comprehensive authentication
4
+ and authorization features for Angular applications.
4
5
 
5
6
  ## Installation
6
7
 
@@ -12,26 +13,36 @@ npm install @acontplus/ng-auth
12
13
 
13
14
  - **Auth Guard**: Route protection with automatic redirect to login page
14
15
  - **Auth Token Service**: JWT token management and authentication state handling
15
- - **URL Redirection Strategy**: Automatically redirects users back to their intended destination after login
16
- - **Session Expiry Handling**: Manages session expiry during HTTP requests with automatic redirection
16
+ - **URL Redirection Strategy**: Automatically redirects users back to their
17
+ intended destination after login
18
+ - **Session Expiry Handling**: Manages session expiry during HTTP requests with
19
+ automatic redirection
17
20
  - **CSRF Protection**: Built-in CSRF token management for secure API requests
18
- - **Token Repository**: Secure token storage and retrieval with local storage support
19
- - **Authentication Use Cases**: Login, register, refresh token, and logout functionality
21
+ - **Token Repository**: Secure token storage and retrieval with local storage
22
+ support
23
+ - **Authentication Use Cases**: Login, register, refresh token, and logout
24
+ functionality
20
25
  - **Domain Models**: User domain models and value objects
21
- - **Clean Architecture**: Organized in domain, application, data, and presentation layers
22
- - **Angular Integration**: Seamless integration with Angular Router and HTTP client
26
+ - **Clean Architecture**: Organized in domain, application, data, and
27
+ presentation layers
28
+ - **Angular Integration**: Seamless integration with Angular Router and HTTP
29
+ client
23
30
  - **Type Safety**: Full TypeScript support with comprehensive type definitions
24
31
 
25
32
  ## URL Redirection Strategy
26
33
 
27
- The module includes a comprehensive URL redirection strategy that ensures users are returned to their intended destination after authentication, even when sessions expire.
34
+ The module includes a comprehensive URL redirection strategy that ensures users
35
+ are returned to their intended destination after authentication, even when
36
+ sessions expire.
28
37
 
29
38
  ### Components
30
39
 
31
40
  1. **UrlRedirectService** - Manages URL storage and redirection logic
32
41
  2. **Enhanced Auth Guard** - Captures URLs before redirecting to login
33
- 3. **Enhanced Login Use Case** - Redirects to stored URLs after successful authentication
34
- 4. **Auth Redirect Interceptor** - Handles session expiry during HTTP requests (optional)
42
+ 3. **Enhanced Login Use Case** - Redirects to stored URLs after successful
43
+ authentication
44
+ 4. **Auth Redirect Interceptor** - Handles session expiry during HTTP requests
45
+ (optional)
35
46
 
36
47
  ### Basic Setup
37
48
 
@@ -43,8 +54,8 @@ const routes: Routes = [
43
54
  {
44
55
  path: 'dashboard',
45
56
  component: DashboardComponent,
46
- canActivate: [authGuard] // Automatically captures URL if not authenticated
47
- }
57
+ canActivate: [authGuard], // Automatically captures URL if not authenticated
58
+ },
48
59
  ];
49
60
  ```
50
61
 
@@ -59,7 +70,7 @@ export const appConfig: ApplicationConfig = {
59
70
  provideHttpClient(
60
71
  withInterceptors([
61
72
  authRedirectInterceptor, // Handles session expiry during API calls
62
- ])
73
+ ]),
63
74
  ),
64
75
  ],
65
76
  };
@@ -68,11 +79,13 @@ export const appConfig: ApplicationConfig = {
68
79
  ### How It Works
69
80
 
70
81
  **Scenario 1 - Route Protection:**
82
+
71
83
  ```
72
84
  User → /dashboard → Auth Guard → Store URL → Login → Success → Redirect to /dashboard
73
85
  ```
74
86
 
75
87
  **Scenario 2 - Session Expiry (with interceptor):**
88
+
76
89
  ```
77
90
  User on /reports → API Call → 401 Error → Store URL → Login → Success → Redirect to /reports
78
91
  ```
@@ -137,21 +150,19 @@ export class AuthComponent {
137
150
 
138
151
  ## Login UI Component
139
152
 
140
- The library includes a comprehensive login UI component with Material Design styling.
153
+ The library includes a comprehensive login UI component with Material Design
154
+ styling.
141
155
 
142
156
  ### Basic Usage
143
157
 
144
158
  ```html
145
- <acp-login
146
- title="Welcome Back"
147
- [showRegisterButton]="true">
148
- </acp-login>
159
+ <acp-login title="Welcome Back" [showRegisterButton]="true"> </acp-login>
149
160
  ```
150
161
 
151
162
  ### Component Features
152
163
 
153
164
  - **Dual Mode Support**: Toggle between login and signup modes
154
- - **Material Design**: Built with Angular Material components
165
+ - **Material Design**: Built with Angular Material components
155
166
  - **Responsive Layout**: Works on desktop and mobile devices
156
167
  - **Theme Integration**: Automatically inherits your app's theme colors
157
168
  - **Validation**: Built-in form validation with error messaging
@@ -162,14 +173,15 @@ The library includes a comprehensive login UI component with Material Design sty
162
173
  ### Core Services
163
174
 
164
175
  - **AuthTokenService**: JWT token management and authentication state
165
- - **UrlRedirectService**: URL storage and redirection management
176
+ - **UrlRedirectService**: URL storage and redirection management
166
177
  - **CsrfService**: CSRF token management for secure API requests
167
178
  - **TokenRepository**: Secure token storage and retrieval
168
179
 
169
180
  ### Guards and Interceptors
170
181
 
171
182
  - **authGuard**: Route protection with automatic URL capture
172
- - **authRedirectInterceptor**: Session expiry handling during HTTP requests (optional)
183
+ - **authRedirectInterceptor**: Session expiry handling during HTTP requests
184
+ (optional)
173
185
 
174
186
  ### Use Cases
175
187
 
@@ -180,7 +192,8 @@ The library includes a comprehensive login UI component with Material Design sty
180
192
 
181
193
  ## Architecture
182
194
 
183
- This library follows Clean Architecture principles with clear separation of concerns organized in domain, application, data, and presentation layers.
195
+ This library follows Clean Architecture principles with clear separation of
196
+ concerns organized in domain, application, data, and presentation layers.
184
197
 
185
198
  ### Authentication Service
186
199
 
@@ -212,7 +225,7 @@ export class LoginComponent {
212
225
 
213
226
  async login(credentials: LoginCredentials) {
214
227
  const csrfToken = await this.csrfService.getCsrfToken();
215
-
228
+
216
229
  // Include CSRF token in your login request
217
230
  const response = await this.http.post('/api/login', {
218
231
  ...credentials,
@@ -257,11 +270,9 @@ import { NgModule } from '@angular/core';
257
270
  import { authProviders } from '@acontplus/ng-auth';
258
271
 
259
272
  @NgModule({
260
- providers: [
261
- ...authProviders
262
- ]
273
+ providers: [...authProviders],
263
274
  })
264
- export class AppModule { }
275
+ export class AppModule {}
265
276
  ```
266
277
 
267
278
  ## Running unit tests
@@ -270,7 +281,8 @@ Run `nx test ng-auth` to execute the unit tests.
270
281
 
271
282
  ## Login Component
272
283
 
273
- The `LoginComponent` provides a flexible, themeable authentication UI component with support for custom fields and dynamic content.
284
+ The `LoginComponent` provides a flexible, themeable authentication UI component
285
+ with support for custom fields and dynamic content.
274
286
 
275
287
  ### Basic Usage
276
288
 
@@ -279,12 +291,9 @@ import { LoginComponent } from '@acontplus/ng-auth/ui/login';
279
291
 
280
292
  @Component({
281
293
  template: `
282
- <acp-login
283
- title="Welcome Back"
284
- [showRegisterButton]="true">
285
- </acp-login>
294
+ <acp-login title="Welcome Back" [showRegisterButton]="true"> </acp-login>
286
295
  `,
287
- imports: [LoginComponent]
296
+ imports: [LoginComponent],
288
297
  })
289
298
  export class AuthPageComponent {}
290
299
  ```
@@ -318,7 +327,8 @@ export class AuthPageComponent {
318
327
  ```html
319
328
  <acp-login
320
329
  [additionalSigninControls]="signinExtras"
321
- [additionalSignupControls]="signupExtras">
330
+ [additionalSignupControls]="signupExtras"
331
+ >
322
332
  </acp-login>
323
333
  ```
324
334
 
@@ -334,9 +344,7 @@ Use content projection slots for custom field templates:
334
344
  <mat-label>Company</mat-label>
335
345
  <mat-select formControlName="companyId">
336
346
  @for (company of companies; track company.id) {
337
- <mat-option [value]="company.id">
338
- {{ company.name }}
339
- </mat-option>
347
+ <mat-option [value]="company.id"> {{ company.name }} </mat-option>
340
348
  }
341
349
  </mat-select>
342
350
  </mat-form-field>
@@ -348,9 +356,7 @@ Use content projection slots for custom field templates:
348
356
  <mat-label>Company</mat-label>
349
357
  <mat-select formControlName="companyId" required>
350
358
  @for (company of companies; track company.id) {
351
- <mat-option [value]="company.id">
352
- {{ company.name }}
353
- </mat-option>
359
+ <mat-option [value]="company.id"> {{ company.name }} </mat-option>
354
360
  }
355
361
  </mat-select>
356
362
  </mat-form-field>
@@ -366,7 +372,13 @@ Use content projection slots for custom field templates:
366
372
 
367
373
  <mat-form-field class="w-100">
368
374
  <mat-label>Validation PIN</mat-label>
369
- <input matInput type="text" placeholder="Enter PIN" formControlName="validationPin" required />
375
+ <input
376
+ matInput
377
+ type="text"
378
+ placeholder="Enter PIN"
379
+ formControlName="validationPin"
380
+ required
381
+ />
370
382
  </mat-form-field>
371
383
  </div>
372
384
  </acp-login>
@@ -393,13 +405,17 @@ Customize the footer with dynamic content:
393
405
 
394
406
  ### Theme Color Inheritance
395
407
 
396
- The component uses CSS custom properties to inherit colors from the parent app's theme:
408
+ The component uses CSS custom properties to inherit colors from the parent app's
409
+ theme:
397
410
 
398
411
  - Background gradient: Uses `--mdc-theme-primary` and `--mdc-theme-secondary`
399
412
  - Title color: Uses `--mdc-theme-on-surface`
400
- - Error alerts: Uses `--mdc-theme-error`, `--mdc-theme-error-container`, `--mdc-theme-on-error-container`
413
+ - Error alerts: Uses `--mdc-theme-error`, `--mdc-theme-error-container`,
414
+ `--mdc-theme-on-error-container`
415
+
416
+ Fallback colors use Angular Material's Material Design 3 (M3) neutral color
417
+ tokens:
401
418
 
402
- Fallback colors use Angular Material's Material Design 3 (M3) neutral color tokens:
403
419
  - Primary: #5c5f5c (M3 neutral primary)
404
420
  - Secondary: #79747e (M3 neutral secondary)
405
421
  - On surface: #1c1b1f (M3 on-surface)
@@ -423,7 +439,10 @@ To customize colors, define these CSS variables in your app's global styles:
423
439
  ### Component Inputs
424
440
 
425
441
  - `title: string` - The title displayed in the card header (default: 'Login')
426
- - `showRegisterButton: boolean` - Whether to show the register button (default: true)
427
- - `additionalSigninControls: Record<string, AbstractControl>` - Additional controls for signin form
428
- - `additionalSignupControls: Record<string, AbstractControl>` - Additional controls for signup form
442
+ - `showRegisterButton: boolean` - Whether to show the register button (default:
443
+ true)
444
+ - `additionalSigninControls: Record<string, AbstractControl>` - Additional
445
+ controls for signin form
446
+ - `additionalSignupControls: Record<string, AbstractControl>` - Additional
447
+ controls for signup form
429
448
  - `footerContent: TemplateRef<any> | null` - Custom footer template
@@ -1,16 +1,15 @@
1
- import { UserRepository, BaseUseCase } from '@acontplus/ng-infrastructure';
1
+ import { UserRepository, BaseUseCase, LoggingService } from '@acontplus/ng-infrastructure';
2
2
  export { TOKEN_PROVIDER } from '@acontplus/ng-infrastructure';
3
3
  import * as i0 from '@angular/core';
4
- import { inject, PLATFORM_ID, Injectable, signal, input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
4
+ import { inject, PLATFORM_ID, Injectable, NgZone, signal, input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
5
5
  import { Router } from '@angular/router';
6
6
  import * as i1 from '@angular/common';
7
7
  import { isPlatformBrowser, DOCUMENT, CommonModule } from '@angular/common';
8
8
  import { jwtDecode } from 'jwt-decode';
9
9
  import { ENVIRONMENT, AUTH_API } from '@acontplus/ng-config';
10
10
  import { catchError, switchMap } from 'rxjs/operators';
11
- import { throwError, from, map, of, tap, catchError as catchError$1 } from 'rxjs';
11
+ import { throwError, from, of, tap, catchError as catchError$1 } from 'rxjs';
12
12
  import { HttpClient } from '@angular/common/http';
13
- import { AuthTokens } from '@acontplus/core';
14
13
  import * as i2 from '@angular/forms';
15
14
  import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
16
15
  import { MatCard, MatCardHeader, MatCardTitle, MatCardContent, MatCardFooter } from '@angular/material/card';
@@ -112,7 +111,7 @@ class TokenRepository {
112
111
  try {
113
112
  return jwtDecode(token);
114
113
  }
115
- catch (error) {
114
+ catch {
116
115
  return null;
117
116
  }
118
117
  }
@@ -139,23 +138,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
139
138
  }] });
140
139
 
141
140
  class AuthTokenService {
142
- tokenRepository;
143
- constructor(tokenRepository) {
144
- this.tokenRepository = tokenRepository;
145
- }
141
+ tokenRepository = inject(TokenRepository);
146
142
  getToken() {
147
143
  return this.tokenRepository.getAccessToken();
148
144
  }
149
145
  isAuthenticated() {
150
146
  return this.tokenRepository.isAuthenticated();
151
147
  }
152
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AuthTokenService, deps: [{ token: TokenRepository }], target: i0.ɵɵFactoryTarget.Injectable });
148
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AuthTokenService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
153
149
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AuthTokenService, providedIn: 'root' });
154
150
  }
155
151
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AuthTokenService, decorators: [{
156
152
  type: Injectable,
157
153
  args: [{ providedIn: 'root' }]
158
- }], ctorParameters: () => [{ type: TokenRepository }] });
154
+ }] });
159
155
 
160
156
  /**
161
157
  * Service to manage URL redirection after authentication
@@ -164,7 +160,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
164
160
  */
165
161
  class UrlRedirectService {
166
162
  REDIRECT_URL_KEY = 'acp_redirect_url';
167
- EXCLUDED_ROUTES = ['/login', '/register', '/forgot-password', '/reset-password'];
163
+ EXCLUDED_ROUTES = [
164
+ '/login',
165
+ '/auth',
166
+ '/register',
167
+ '/forgot-password',
168
+ '/reset-password',
169
+ ];
168
170
  router = inject(Router);
169
171
  platformId = inject(PLATFORM_ID);
170
172
  document = inject(DOCUMENT);
@@ -311,31 +313,7 @@ const authRedirectInterceptor = (req, next) => {
311
313
  }));
312
314
  };
313
315
 
314
- class User {
315
- id;
316
- email;
317
- displayName;
318
- _refreshToken;
319
- constructor(id, email, displayName, _refreshToken) {
320
- this.id = id;
321
- this.email = email;
322
- this.displayName = displayName;
323
- this._refreshToken = _refreshToken;
324
- }
325
- get refreshToken() {
326
- return this._refreshToken;
327
- }
328
- updateRefreshToken(token) {
329
- this._refreshToken = token;
330
- }
331
- clearRefreshToken() {
332
- this._refreshToken = undefined;
333
- }
334
- static create(email, displayName, _password) {
335
- const id = Date.now(); // Generate a numeric ID
336
- return new User(id, email, displayName);
337
- }
338
- }
316
+ // src/lib/domain/models/auth.ts
339
317
 
340
318
  // src/lib/domain/models/index.ts
341
319
 
@@ -405,26 +383,22 @@ class AuthHttpRepository extends AuthRepository {
405
383
  })));
406
384
  }
407
385
  register(request) {
408
- return from(this.csrfService.getCsrfToken()).pipe(switchMap(csrfToken => this.http
409
- .post(`${this.URL}register`, request, {
386
+ return from(this.csrfService.getCsrfToken()).pipe(switchMap(csrfToken => this.http.post(`${this.URL}register`, request, {
410
387
  headers: {
411
388
  'Device-Info': getDeviceInfo(),
412
389
  'X-CSRF-Token': csrfToken,
413
390
  },
414
391
  withCredentials: true,
415
- })
416
- .pipe(map(response => new AuthTokens(response.token, response.refreshToken)))));
392
+ })));
417
393
  }
418
394
  refreshToken(request) {
419
- return from(this.csrfService.getCsrfToken()).pipe(switchMap(csrfToken => this.http
420
- .post(`${this.URL}refresh`, request, {
395
+ return from(this.csrfService.getCsrfToken()).pipe(switchMap(csrfToken => this.http.post(`${this.URL}refresh`, request, {
421
396
  headers: {
422
397
  'Device-Info': getDeviceInfo(),
423
398
  'X-CSRF-Token': csrfToken,
424
399
  },
425
400
  withCredentials: true,
426
- })
427
- .pipe(map(response => new AuthTokens(response.token, response.refreshToken)))));
401
+ })));
428
402
  }
429
403
  logout(email, refreshToken) {
430
404
  return from(this.csrfService.getCsrfToken()).pipe(switchMap(csrfToken => this.http.post(`${this.URL}logout`, { email, refreshToken: refreshToken || undefined }, {
@@ -452,6 +426,7 @@ class AuthStore {
452
426
  tokenRepository = inject(TokenRepository);
453
427
  userRepository = inject(UserRepository);
454
428
  router = inject(Router);
429
+ ngZone = inject(NgZone);
455
430
  // Authentication state signals
456
431
  _isAuthenticated = signal(false, ...(ngDevMode ? [{ debugName: "_isAuthenticated" }] : []));
457
432
  _isLoading = signal(false, ...(ngDevMode ? [{ debugName: "_isLoading" }] : []));
@@ -505,12 +480,16 @@ class AuthStore {
505
480
  if (this.refreshTokenTimeout) {
506
481
  clearTimeout(this.refreshTokenTimeout);
507
482
  }
508
- this.refreshTokenTimeout = window.setTimeout(() => {
509
- // Check if refresh is still needed before executing
510
- if (this.tokenRepository.needsRefresh()) {
511
- this.refreshToken().subscribe();
512
- }
513
- }, refreshTime);
483
+ this.ngZone.runOutsideAngular(() => {
484
+ this.refreshTokenTimeout = window.setTimeout(() => {
485
+ this.ngZone.run(() => {
486
+ // Check if refresh is still needed before executing
487
+ if (this.tokenRepository.needsRefresh()) {
488
+ this.refreshToken().subscribe();
489
+ }
490
+ });
491
+ }, refreshTime);
492
+ });
514
493
  }
515
494
  catch {
516
495
  // Silent fail - token might be invalid
@@ -555,7 +534,7 @@ class AuthStore {
555
534
  },
556
535
  error: () => {
557
536
  this.refreshInProgress$ = undefined;
558
- }
537
+ },
559
538
  }));
560
539
  return this.refreshInProgress$;
561
540
  }
@@ -584,7 +563,7 @@ class AuthStore {
584
563
  error: () => {
585
564
  // Server logout failed, still clear client-side data for security
586
565
  this.performClientLogout();
587
- }
566
+ },
588
567
  });
589
568
  }
590
569
  else {
@@ -744,7 +723,9 @@ class LogoutUseCase extends BaseUseCase {
744
723
  const userData = this.userRepository.getCurrentUser();
745
724
  const refreshToken = this.tokenRepository.getRefreshToken();
746
725
  if (userData?.email && refreshToken && refreshToken.length > 0) {
747
- return this.authRepository.logout(userData.email, refreshToken).pipe(tap(() => this.cleanup()));
726
+ return this.authRepository
727
+ .logout(userData.email, refreshToken)
728
+ .pipe(tap(() => this.cleanup()));
748
729
  }
749
730
  this.cleanup();
750
731
  return of(void 0);
@@ -785,8 +766,10 @@ class LoginComponent {
785
766
  // Computed signal to check if footer content exists
786
767
  hasFooterContent = computed(() => this.footerContent() !== null, ...(ngDevMode ? [{ debugName: "hasFooterContent" }] : []));
787
768
  fb = inject(FormBuilder);
769
+ authStore = inject(AuthStore);
788
770
  loginUseCase = inject(LoginUseCase);
789
771
  registerUseCase = inject(RegisterUseCase);
772
+ loggingService = inject(LoggingService);
790
773
  // Angular 20+ signals
791
774
  isLoginMode = signal(true, ...(ngDevMode ? [{ debugName: "isLoginMode" }] : []));
792
775
  isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
@@ -832,7 +815,7 @@ class LoginComponent {
832
815
  const loginRequest = {
833
816
  ...this.signinForm.value,
834
817
  // If showRememberMe is false, default rememberMe to false
835
- rememberMe: this.showRememberMe() ? (this.signinForm.value.rememberMe ?? false) : false
818
+ rememberMe: this.showRememberMe() ? (this.signinForm.value.rememberMe ?? false) : false,
836
819
  };
837
820
  this.loginUseCase.execute(loginRequest).subscribe({
838
821
  next: () => {
@@ -841,7 +824,7 @@ class LoginComponent {
841
824
  error: error => {
842
825
  this.isLoading.set(false);
843
826
  this.errorMessage.set('Error al iniciar sesión. Verifique sus credenciales.');
844
- console.error('Login error:', error);
827
+ this.loggingService.error('Login failed', { error });
845
828
  },
846
829
  });
847
830
  }
@@ -859,13 +842,13 @@ class LoginComponent {
859
842
  error: error => {
860
843
  this.isLoading.set(false);
861
844
  this.errorMessage.set('Error al registrar usuario. Intente nuevamente.');
862
- console.error('Register error:', error);
845
+ this.loggingService.error('Register error', { error });
863
846
  },
864
847
  });
865
848
  }
866
849
  }
867
850
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LoginComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
868
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: LoginComponent, isStandalone: true, selector: "acp-login", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, showRegisterButton: { classPropertyName: "showRegisterButton", publicName: "showRegisterButton", isSignal: true, isRequired: false, transformFunction: null }, showRememberMe: { classPropertyName: "showRememberMe", publicName: "showRememberMe", isSignal: true, isRequired: false, transformFunction: null }, additionalSigninControls: { classPropertyName: "additionalSigninControls", publicName: "additionalSigninControls", isSignal: true, isRequired: false, transformFunction: null }, additionalSignupControls: { classPropertyName: "additionalSignupControls", publicName: "additionalSignupControls", isSignal: true, isRequired: false, transformFunction: null }, additionalSigninFields: { classPropertyName: "additionalSigninFields", publicName: "additionalSigninFields", isSignal: true, isRequired: false, transformFunction: null }, additionalSignupFields: { classPropertyName: "additionalSignupFields", publicName: "additionalSignupFields", isSignal: true, isRequired: false, transformFunction: null }, footerContent: { classPropertyName: "footerContent", publicName: "footerContent", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<section id=\"wrapper\" class=\"d-flex justify-content-center align-items-center\">\n <mat-card class=\"mat-elevation-z8 p-4 rounded\">\n <mat-card-header>\n <mat-card-title class=\"text-center\">{{ title() }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n @if (isLoginMode()) {\n <form [formGroup]=\"signinForm\" (ngSubmit)=\"signIn()\" class=\"d-flex flex-column gap-3\">\n <mat-form-field class=\"w-100\">\n <mat-label>Usuario</mat-label>\n <input matInput type=\"text\" placeholder=\"Ingrese su usuario\" formControlName=\"email\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Contrase\u00F1a</mat-label>\n <input matInput type=\"password\" placeholder=\"Ingrese su contrase\u00F1a\" formControlName=\"password\" />\n </mat-form-field>\n\n <!-- Remember Me checkbox - conditional -->\n @if (showRememberMe()) {\n <div class=\"d-flex align-items-center mt-2\">\n <mat-checkbox formControlName=\"rememberMe\">\n Recordarme\n </mat-checkbox>\n </div>\n }\n\n <!-- Additional signin fields -->\n <ng-container *ngTemplateOutlet=\"additionalSigninFields()\"></ng-container>\n\n <div class=\"d-flex justify-content-center mt-3\">\n <button mat-raised-button color=\"primary\" [disabled]=\"!signinForm.valid || isLoading()\" type=\"submit\"\n class=\"w-100\">\n @if (isLoading()) {\n Ingresando...\n } @else {\n <ng-container>Ingresar <mat-icon>login</mat-icon></ng-container>\n }\n </button>\n </div>\n\n <div class=\"text-center mt-2\">\n @if (showRegisterButton()) {\n <button mat-button type=\"button\" (click)=\"switchMode()\">\n \u00BFNo tienes cuenta? Reg\u00EDstrate\n </button>\n }\n </div>\n </form>\n } @else {\n <form [formGroup]=\"signupForm\" (ngSubmit)=\"registerUser()\" class=\"d-flex flex-column gap-3\">\n <mat-form-field class=\"w-100\">\n <mat-label>Nombre</mat-label>\n <input matInput type=\"text\" placeholder=\"Ingrese su nombre\" formControlName=\"displayName\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Email</mat-label>\n <input matInput type=\"email\" placeholder=\"Ingrese su email\" formControlName=\"email\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Contrase\u00F1a</mat-label>\n <input matInput type=\"password\" placeholder=\"Ingrese su contrase\u00F1a\" formControlName=\"password\" />\n </mat-form-field>\n\n <!-- Additional signup fields -->\n <ng-container *ngTemplateOutlet=\"additionalSignupFields()\"></ng-container>\n\n <div class=\"d-flex justify-content-center mt-3\">\n <button mat-raised-button color=\"primary\" [disabled]=\"!signupForm.valid || isLoading()\" type=\"submit\"\n class=\"w-100\">\n @if (isLoading()) {\n Registrando...\n } @else {\n <ng-container>Registrarse <mat-icon>person_add</mat-icon></ng-container>\n }\n </button>\n </div>\n\n <div class=\"text-center mt-2\">\n <button mat-button type=\"button\" (click)=\"switchMode()\">\u00BFYa tienes cuenta? Inicia sesi\u00F3n</button>\n </div>\n </form>\n }\n @if (errorMessage()) {\n <div class=\"alert alert-danger mt-3\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n }\n </mat-card-content>\n @if (hasFooterContent()) {\n <mat-card-footer>\n <ng-container *ngTemplateOutlet=\"footerContent()\"></ng-container>\n </mat-card-footer>\n }\n </mat-card>\n</section>\n", styles: ["#wrapper{min-height:100vh;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,var(--mdc-theme-primary, #5c5f5c) 0%,var(--mdc-theme-secondary, #79747e) 100%);padding:20px}mat-card{width:100%;max-width:400px;border-radius:12px;box-shadow:0 8px 32px #0000001a}mat-card-header{text-align:center;margin-bottom:20px}mat-card-title{font-size:24px;font-weight:600;color:var(--mdc-theme-on-surface, #1c1b1f)}mat-form-field{width:100%}.w-100{width:100%}.d-flex{display:flex}.flex-column{flex-direction:column}.gap-3{gap:12px}.justify-content-center{justify-content:center}.align-items-center{align-items:center}.mt-3{margin-top:12px}.mt-2{margin-top:8px}.text-center{text-align:center}.p-4{padding:16px}.rounded{border-radius:12px}.alert-danger{background-color:var(--mdc-theme-error-container, #ffdad6);border-color:var(--mdc-theme-error, #ba1a1a);color:var(--mdc-theme-on-error-container, #410002);padding:12px;border-radius:4px;border:1px solid transparent}.row{display:flex;flex-wrap:wrap;margin:0 -15px}.col-xs-12,.col-sm-12,.col-md-12{flex:0 0 100%;max-width:100%;padding:0 15px}.m-t-10{margin-top:10px}.social{display:flex;justify-content:center}mat-button{border-radius:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "component", type: MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { kind: "directive", type: MatCardContent, selector: "mat-card-content" }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: MatCardFooter, selector: "mat-card-footer" }, { kind: "component", type: MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
851
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: LoginComponent, isStandalone: true, selector: "acp-login", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, showRegisterButton: { classPropertyName: "showRegisterButton", publicName: "showRegisterButton", isSignal: true, isRequired: false, transformFunction: null }, showRememberMe: { classPropertyName: "showRememberMe", publicName: "showRememberMe", isSignal: true, isRequired: false, transformFunction: null }, additionalSigninControls: { classPropertyName: "additionalSigninControls", publicName: "additionalSigninControls", isSignal: true, isRequired: false, transformFunction: null }, additionalSignupControls: { classPropertyName: "additionalSignupControls", publicName: "additionalSignupControls", isSignal: true, isRequired: false, transformFunction: null }, additionalSigninFields: { classPropertyName: "additionalSigninFields", publicName: "additionalSigninFields", isSignal: true, isRequired: false, transformFunction: null }, additionalSignupFields: { classPropertyName: "additionalSignupFields", publicName: "additionalSignupFields", isSignal: true, isRequired: false, transformFunction: null }, footerContent: { classPropertyName: "footerContent", publicName: "footerContent", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<section id=\"wrapper\" class=\"d-flex justify-content-center align-items-center\">\n <mat-card class=\"mat-elevation-z8 p-4 rounded\">\n <mat-card-header>\n <mat-card-title class=\"text-center\">{{ title() }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n @if (isLoginMode()) {\n <form [formGroup]=\"signinForm\" (ngSubmit)=\"signIn()\" class=\"d-flex flex-column gap-3\">\n <mat-form-field class=\"w-100\">\n <mat-label>Usuario</mat-label>\n <input matInput type=\"text\" placeholder=\"Ingrese su usuario\" formControlName=\"email\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Contrase\u00F1a</mat-label>\n <input\n matInput\n type=\"password\"\n placeholder=\"Ingrese su contrase\u00F1a\"\n formControlName=\"password\"\n autocomplete=\"current-password\"\n />\n </mat-form-field>\n\n <!-- Remember Me checkbox - conditional -->\n @if (showRememberMe()) {\n <div class=\"d-flex align-items-center mt-2\">\n <mat-checkbox formControlName=\"rememberMe\"> Recordarme </mat-checkbox>\n </div>\n }\n\n <!-- Additional signin fields -->\n <ng-container *ngTemplateOutlet=\"additionalSigninFields()\"></ng-container>\n\n <div class=\"d-flex justify-content-center mt-3\">\n <button\n mat-raised-button\n color=\"primary\"\n [disabled]=\"!signinForm.valid || isLoading()\"\n type=\"submit\"\n class=\"w-100\"\n >\n @if (isLoading()) {\n Ingresando...\n } @else {\n <ng-container>Ingresar <mat-icon>login</mat-icon></ng-container>\n }\n </button>\n </div>\n\n <div class=\"text-center mt-2\">\n @if (showRegisterButton()) {\n <button mat-button type=\"button\" (click)=\"switchMode()\">\n \u00BFNo tienes cuenta? Reg\u00EDstrate\n </button>\n }\n </div>\n </form>\n } @else {\n <form [formGroup]=\"signupForm\" (ngSubmit)=\"registerUser()\" class=\"d-flex flex-column gap-3\">\n <mat-form-field class=\"w-100\">\n <mat-label>Nombre</mat-label>\n <input\n matInput\n type=\"text\"\n placeholder=\"Ingrese su nombre\"\n formControlName=\"displayName\"\n />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Email</mat-label>\n <input matInput type=\"email\" placeholder=\"Ingrese su email\" formControlName=\"email\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Contrase\u00F1a</mat-label>\n <input\n matInput\n type=\"password\"\n placeholder=\"Ingrese su contrase\u00F1a\"\n formControlName=\"password\"\n autocomplete=\"new-password\"\n />\n </mat-form-field>\n\n <!-- Additional signup fields -->\n <ng-container *ngTemplateOutlet=\"additionalSignupFields()\"></ng-container>\n\n <div class=\"d-flex justify-content-center mt-3\">\n <button\n mat-raised-button\n color=\"primary\"\n [disabled]=\"!signupForm.valid || isLoading()\"\n type=\"submit\"\n class=\"w-100\"\n >\n @if (isLoading()) {\n Registrando...\n } @else {\n <ng-container>Registrarse <mat-icon>person_add</mat-icon></ng-container>\n }\n </button>\n </div>\n\n <div class=\"text-center mt-2\">\n <button mat-button type=\"button\" (click)=\"switchMode()\">\n \u00BFYa tienes cuenta? Inicia sesi\u00F3n\n </button>\n </div>\n </form>\n }\n @if (errorMessage()) {\n <div class=\"alert alert-danger mt-3\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n }\n </mat-card-content>\n @if (hasFooterContent()) {\n <mat-card-footer>\n <ng-container *ngTemplateOutlet=\"footerContent()\"></ng-container>\n </mat-card-footer>\n }\n </mat-card>\n</section>\n", styles: ["#wrapper{min-height:100vh;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,var(--mdc-theme-primary, #5c5f5c) 0%,var(--mdc-theme-secondary, #79747e) 100%);padding:20px}mat-card{width:100%;max-width:400px;border-radius:12px;box-shadow:0 8px 32px #0000001a}mat-card-header{text-align:center;margin-bottom:20px}mat-card-title{font-size:24px;font-weight:600;color:var(--mdc-theme-on-surface, #1c1b1f)}mat-form-field{width:100%}.w-100{width:100%}.d-flex{display:flex}.flex-column{flex-direction:column}.gap-3{gap:12px}.justify-content-center{justify-content:center}.align-items-center{align-items:center}.mt-3{margin-top:12px}.mt-2{margin-top:8px}.text-center{text-align:center}.p-4{padding:16px}.rounded{border-radius:12px}.alert-danger{background-color:var(--mdc-theme-error-container, #ffdad6);border-color:var(--mdc-theme-error, #ba1a1a);color:var(--mdc-theme-on-error-container, #410002);padding:12px;border-radius:4px;border:1px solid transparent}.row{display:flex;flex-wrap:wrap;margin:0 -15px}.col-xs-12,.col-sm-12,.col-md-12{flex:0 0 100%;max-width:100%;padding:0 15px}.m-t-10{margin-top:10px}.social{display:flex;justify-content:center}mat-button{border-radius:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "component", type: MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { kind: "directive", type: MatCardContent, selector: "mat-card-content" }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: MatCardFooter, selector: "mat-card-footer" }, { kind: "component", type: MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
869
852
  }
870
853
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LoginComponent, decorators: [{
871
854
  type: Component,
@@ -884,7 +867,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
884
867
  MatCardFooter,
885
868
  MatAnchor,
886
869
  MatCheckbox,
887
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section id=\"wrapper\" class=\"d-flex justify-content-center align-items-center\">\n <mat-card class=\"mat-elevation-z8 p-4 rounded\">\n <mat-card-header>\n <mat-card-title class=\"text-center\">{{ title() }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n @if (isLoginMode()) {\n <form [formGroup]=\"signinForm\" (ngSubmit)=\"signIn()\" class=\"d-flex flex-column gap-3\">\n <mat-form-field class=\"w-100\">\n <mat-label>Usuario</mat-label>\n <input matInput type=\"text\" placeholder=\"Ingrese su usuario\" formControlName=\"email\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Contrase\u00F1a</mat-label>\n <input matInput type=\"password\" placeholder=\"Ingrese su contrase\u00F1a\" formControlName=\"password\" />\n </mat-form-field>\n\n <!-- Remember Me checkbox - conditional -->\n @if (showRememberMe()) {\n <div class=\"d-flex align-items-center mt-2\">\n <mat-checkbox formControlName=\"rememberMe\">\n Recordarme\n </mat-checkbox>\n </div>\n }\n\n <!-- Additional signin fields -->\n <ng-container *ngTemplateOutlet=\"additionalSigninFields()\"></ng-container>\n\n <div class=\"d-flex justify-content-center mt-3\">\n <button mat-raised-button color=\"primary\" [disabled]=\"!signinForm.valid || isLoading()\" type=\"submit\"\n class=\"w-100\">\n @if (isLoading()) {\n Ingresando...\n } @else {\n <ng-container>Ingresar <mat-icon>login</mat-icon></ng-container>\n }\n </button>\n </div>\n\n <div class=\"text-center mt-2\">\n @if (showRegisterButton()) {\n <button mat-button type=\"button\" (click)=\"switchMode()\">\n \u00BFNo tienes cuenta? Reg\u00EDstrate\n </button>\n }\n </div>\n </form>\n } @else {\n <form [formGroup]=\"signupForm\" (ngSubmit)=\"registerUser()\" class=\"d-flex flex-column gap-3\">\n <mat-form-field class=\"w-100\">\n <mat-label>Nombre</mat-label>\n <input matInput type=\"text\" placeholder=\"Ingrese su nombre\" formControlName=\"displayName\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Email</mat-label>\n <input matInput type=\"email\" placeholder=\"Ingrese su email\" formControlName=\"email\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Contrase\u00F1a</mat-label>\n <input matInput type=\"password\" placeholder=\"Ingrese su contrase\u00F1a\" formControlName=\"password\" />\n </mat-form-field>\n\n <!-- Additional signup fields -->\n <ng-container *ngTemplateOutlet=\"additionalSignupFields()\"></ng-container>\n\n <div class=\"d-flex justify-content-center mt-3\">\n <button mat-raised-button color=\"primary\" [disabled]=\"!signupForm.valid || isLoading()\" type=\"submit\"\n class=\"w-100\">\n @if (isLoading()) {\n Registrando...\n } @else {\n <ng-container>Registrarse <mat-icon>person_add</mat-icon></ng-container>\n }\n </button>\n </div>\n\n <div class=\"text-center mt-2\">\n <button mat-button type=\"button\" (click)=\"switchMode()\">\u00BFYa tienes cuenta? Inicia sesi\u00F3n</button>\n </div>\n </form>\n }\n @if (errorMessage()) {\n <div class=\"alert alert-danger mt-3\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n }\n </mat-card-content>\n @if (hasFooterContent()) {\n <mat-card-footer>\n <ng-container *ngTemplateOutlet=\"footerContent()\"></ng-container>\n </mat-card-footer>\n }\n </mat-card>\n</section>\n", styles: ["#wrapper{min-height:100vh;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,var(--mdc-theme-primary, #5c5f5c) 0%,var(--mdc-theme-secondary, #79747e) 100%);padding:20px}mat-card{width:100%;max-width:400px;border-radius:12px;box-shadow:0 8px 32px #0000001a}mat-card-header{text-align:center;margin-bottom:20px}mat-card-title{font-size:24px;font-weight:600;color:var(--mdc-theme-on-surface, #1c1b1f)}mat-form-field{width:100%}.w-100{width:100%}.d-flex{display:flex}.flex-column{flex-direction:column}.gap-3{gap:12px}.justify-content-center{justify-content:center}.align-items-center{align-items:center}.mt-3{margin-top:12px}.mt-2{margin-top:8px}.text-center{text-align:center}.p-4{padding:16px}.rounded{border-radius:12px}.alert-danger{background-color:var(--mdc-theme-error-container, #ffdad6);border-color:var(--mdc-theme-error, #ba1a1a);color:var(--mdc-theme-on-error-container, #410002);padding:12px;border-radius:4px;border:1px solid transparent}.row{display:flex;flex-wrap:wrap;margin:0 -15px}.col-xs-12,.col-sm-12,.col-md-12{flex:0 0 100%;max-width:100%;padding:0 15px}.m-t-10{margin-top:10px}.social{display:flex;justify-content:center}mat-button{border-radius:8px}\n"] }]
870
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section id=\"wrapper\" class=\"d-flex justify-content-center align-items-center\">\n <mat-card class=\"mat-elevation-z8 p-4 rounded\">\n <mat-card-header>\n <mat-card-title class=\"text-center\">{{ title() }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n @if (isLoginMode()) {\n <form [formGroup]=\"signinForm\" (ngSubmit)=\"signIn()\" class=\"d-flex flex-column gap-3\">\n <mat-form-field class=\"w-100\">\n <mat-label>Usuario</mat-label>\n <input matInput type=\"text\" placeholder=\"Ingrese su usuario\" formControlName=\"email\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Contrase\u00F1a</mat-label>\n <input\n matInput\n type=\"password\"\n placeholder=\"Ingrese su contrase\u00F1a\"\n formControlName=\"password\"\n autocomplete=\"current-password\"\n />\n </mat-form-field>\n\n <!-- Remember Me checkbox - conditional -->\n @if (showRememberMe()) {\n <div class=\"d-flex align-items-center mt-2\">\n <mat-checkbox formControlName=\"rememberMe\"> Recordarme </mat-checkbox>\n </div>\n }\n\n <!-- Additional signin fields -->\n <ng-container *ngTemplateOutlet=\"additionalSigninFields()\"></ng-container>\n\n <div class=\"d-flex justify-content-center mt-3\">\n <button\n mat-raised-button\n color=\"primary\"\n [disabled]=\"!signinForm.valid || isLoading()\"\n type=\"submit\"\n class=\"w-100\"\n >\n @if (isLoading()) {\n Ingresando...\n } @else {\n <ng-container>Ingresar <mat-icon>login</mat-icon></ng-container>\n }\n </button>\n </div>\n\n <div class=\"text-center mt-2\">\n @if (showRegisterButton()) {\n <button mat-button type=\"button\" (click)=\"switchMode()\">\n \u00BFNo tienes cuenta? Reg\u00EDstrate\n </button>\n }\n </div>\n </form>\n } @else {\n <form [formGroup]=\"signupForm\" (ngSubmit)=\"registerUser()\" class=\"d-flex flex-column gap-3\">\n <mat-form-field class=\"w-100\">\n <mat-label>Nombre</mat-label>\n <input\n matInput\n type=\"text\"\n placeholder=\"Ingrese su nombre\"\n formControlName=\"displayName\"\n />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Email</mat-label>\n <input matInput type=\"email\" placeholder=\"Ingrese su email\" formControlName=\"email\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Contrase\u00F1a</mat-label>\n <input\n matInput\n type=\"password\"\n placeholder=\"Ingrese su contrase\u00F1a\"\n formControlName=\"password\"\n autocomplete=\"new-password\"\n />\n </mat-form-field>\n\n <!-- Additional signup fields -->\n <ng-container *ngTemplateOutlet=\"additionalSignupFields()\"></ng-container>\n\n <div class=\"d-flex justify-content-center mt-3\">\n <button\n mat-raised-button\n color=\"primary\"\n [disabled]=\"!signupForm.valid || isLoading()\"\n type=\"submit\"\n class=\"w-100\"\n >\n @if (isLoading()) {\n Registrando...\n } @else {\n <ng-container>Registrarse <mat-icon>person_add</mat-icon></ng-container>\n }\n </button>\n </div>\n\n <div class=\"text-center mt-2\">\n <button mat-button type=\"button\" (click)=\"switchMode()\">\n \u00BFYa tienes cuenta? Inicia sesi\u00F3n\n </button>\n </div>\n </form>\n }\n @if (errorMessage()) {\n <div class=\"alert alert-danger mt-3\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n }\n </mat-card-content>\n @if (hasFooterContent()) {\n <mat-card-footer>\n <ng-container *ngTemplateOutlet=\"footerContent()\"></ng-container>\n </mat-card-footer>\n }\n </mat-card>\n</section>\n", styles: ["#wrapper{min-height:100vh;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,var(--mdc-theme-primary, #5c5f5c) 0%,var(--mdc-theme-secondary, #79747e) 100%);padding:20px}mat-card{width:100%;max-width:400px;border-radius:12px;box-shadow:0 8px 32px #0000001a}mat-card-header{text-align:center;margin-bottom:20px}mat-card-title{font-size:24px;font-weight:600;color:var(--mdc-theme-on-surface, #1c1b1f)}mat-form-field{width:100%}.w-100{width:100%}.d-flex{display:flex}.flex-column{flex-direction:column}.gap-3{gap:12px}.justify-content-center{justify-content:center}.align-items-center{align-items:center}.mt-3{margin-top:12px}.mt-2{margin-top:8px}.text-center{text-align:center}.p-4{padding:16px}.rounded{border-radius:12px}.alert-danger{background-color:var(--mdc-theme-error-container, #ffdad6);border-color:var(--mdc-theme-error, #ba1a1a);color:var(--mdc-theme-on-error-container, #410002);padding:12px;border-radius:4px;border:1px solid transparent}.row{display:flex;flex-wrap:wrap;margin:0 -15px}.col-xs-12,.col-sm-12,.col-md-12{flex:0 0 100%;max-width:100%;padding:0 15px}.m-t-10{margin-top:10px}.social{display:flex;justify-content:center}mat-button{border-radius:8px}\n"] }]
888
871
  }], ctorParameters: () => [] });
889
872
 
890
873
  // src/lib/presentation/components/index.ts
@@ -904,5 +887,5 @@ const authProviders = [
904
887
  * Generated bundle index. Do not edit.
905
888
  */
906
889
 
907
- export { AuthHttpRepository, AuthRepository, AuthStore, AuthTokenService, LoginComponent, LoginUseCase, LogoutUseCase, RefreshTokenUseCase, RegisterUseCase, TokenRepository, UrlRedirectService, User, authGuard, authProviders, authRedirectInterceptor };
890
+ export { AuthHttpRepository, AuthRepository, AuthStore, AuthTokenService, LoginComponent, LoginUseCase, LogoutUseCase, RefreshTokenUseCase, RegisterUseCase, TokenRepository, UrlRedirectService, authGuard, authProviders, authRedirectInterceptor };
908
891
  //# sourceMappingURL=acontplus-ng-auth.mjs.map