@acontplus/ng-auth 1.0.1 → 1.0.2

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
@@ -126,3 +126,163 @@ export class AppModule { }
126
126
  ## Running unit tests
127
127
 
128
128
  Run `nx test ng-auth` to execute the unit tests.
129
+
130
+ ## Login Component
131
+
132
+ The `LoginComponent` provides a flexible, themeable authentication UI component with support for custom fields and dynamic content.
133
+
134
+ ### Basic Usage
135
+
136
+ ```typescript
137
+ import { LoginComponent } from '@acontplus/ng-auth/ui/login';
138
+
139
+ @Component({
140
+ template: `
141
+ <acp-login
142
+ title="Welcome Back"
143
+ [showRegisterButton]="true">
144
+ </acp-login>
145
+ `,
146
+ imports: [LoginComponent]
147
+ })
148
+ export class AuthPageComponent {}
149
+ ```
150
+
151
+ ### Flexible Form Fields
152
+
153
+ The component supports additional form fields through two approaches:
154
+
155
+ #### 1. Additional Form Controls via Inputs
156
+
157
+ Pass additional form controls programmatically:
158
+
159
+ ```typescript
160
+ import { FormControl, Validators } from '@angular/forms';
161
+
162
+ @Component({...})
163
+ export class AuthPageComponent {
164
+ signinExtras = {
165
+ companyId: new FormControl('', Validators.required),
166
+ rememberMe: new FormControl(false)
167
+ };
168
+
169
+ signupExtras = {
170
+ companyId: new FormControl('', Validators.required),
171
+ role: new FormControl('', Validators.required),
172
+ validationPin: new FormControl('', [Validators.required, Validators.pattern(/^\d{6}$/)])
173
+ };
174
+ }
175
+ ```
176
+
177
+ ```html
178
+ <acp-login
179
+ [additionalSigninControls]="signinExtras"
180
+ [additionalSignupControls]="signupExtras">
181
+ </acp-login>
182
+ ```
183
+
184
+ #### 2. Content Projection for Custom Templates
185
+
186
+ Use content projection slots for custom field templates:
187
+
188
+ ```html
189
+ <acp-login>
190
+ <!-- Additional fields for signin form -->
191
+ <div signin-fields>
192
+ <mat-form-field class="w-100">
193
+ <mat-label>Company</mat-label>
194
+ <mat-select formControlName="companyId">
195
+ @for (company of companies; track company.id) {
196
+ <mat-option [value]="company.id">
197
+ {{ company.name }}
198
+ </mat-option>
199
+ }
200
+ </mat-select>
201
+ </mat-form-field>
202
+ </div>
203
+
204
+ <!-- Additional fields for signup form -->
205
+ <div signup-fields>
206
+ <mat-form-field class="w-100">
207
+ <mat-label>Company</mat-label>
208
+ <mat-select formControlName="companyId" required>
209
+ @for (company of companies; track company.id) {
210
+ <mat-option [value]="company.id">
211
+ {{ company.name }}
212
+ </mat-option>
213
+ }
214
+ </mat-select>
215
+ </mat-form-field>
216
+
217
+ <mat-form-field class="w-100">
218
+ <mat-label>Role</mat-label>
219
+ <mat-select formControlName="role" required>
220
+ <mat-option value="admin">Administrator</mat-option>
221
+ <mat-option value="user">User</mat-option>
222
+ <mat-option value="manager">Manager</mat-option>
223
+ </mat-select>
224
+ </mat-form-field>
225
+
226
+ <mat-form-field class="w-100">
227
+ <mat-label>Validation PIN</mat-label>
228
+ <input matInput type="text" placeholder="Enter PIN" formControlName="validationPin" required />
229
+ </mat-form-field>
230
+ </div>
231
+ </acp-login>
232
+ ```
233
+
234
+ ### Dynamic Footer Content
235
+
236
+ Customize the footer with dynamic content:
237
+
238
+ ```html
239
+ <ng-template #customFooter>
240
+ <div class="row mt-3">
241
+ <div class="col-12 text-center">
242
+ <a mat-button color="primary" href="/terms">Terms of Service</a>
243
+ <a mat-button color="primary" href="/privacy">Privacy Policy</a>
244
+ </div>
245
+ </div>
246
+ </ng-template>
247
+
248
+ <acp-login [footerContent]="customFooter">
249
+ <!-- form content -->
250
+ </acp-login>
251
+ ```
252
+
253
+ ### Theme Color Inheritance
254
+
255
+ The component uses CSS custom properties to inherit colors from the parent app's theme:
256
+
257
+ - Background gradient: Uses `--mdc-theme-primary` and `--mdc-theme-secondary`
258
+ - Title color: Uses `--mdc-theme-on-surface`
259
+ - Error alerts: Uses `--mdc-theme-error`, `--mdc-theme-error-container`, `--mdc-theme-on-error-container`
260
+
261
+ Fallback colors use Angular Material's Material Design 3 (M3) neutral color tokens:
262
+ - Primary: #5c5f5c (M3 neutral primary)
263
+ - Secondary: #79747e (M3 neutral secondary)
264
+ - On surface: #1c1b1f (M3 on-surface)
265
+ - Error: #ba1a1a (M3 error)
266
+ - Error container: #ffdad6 (M3 error-container)
267
+ - On error container: #410002 (M3 on-error-container)
268
+
269
+ To customize colors, define these CSS variables in your app's global styles:
270
+
271
+ ```css
272
+ :root {
273
+ --mdc-theme-primary: #your-primary-color;
274
+ --mdc-theme-secondary: #your-secondary-color;
275
+ --mdc-theme-on-surface: #your-text-color;
276
+ --mdc-theme-error: #your-error-color;
277
+ --mdc-theme-error-container: #your-error-bg;
278
+ --mdc-theme-on-error-container: #your-error-text;
279
+ }
280
+ ```
281
+
282
+ ### Component Inputs
283
+
284
+ - `title: string` - The title displayed in the card header (default: 'Login')
285
+ - `showRegisterButton: boolean` - Whether to show the register button (default: true)
286
+ - `additionalSigninControls: Record<string, AbstractControl>` - Additional controls for signin form
287
+ - `additionalSignupControls: Record<string, AbstractControl>` - Additional controls for signup form
288
+ - `footerContent: TemplateRef<any> | null` - Custom footer template
@@ -1,23 +1,23 @@
1
1
  import { UserRepository } 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, ChangeDetectionStrategy, Component } from '@angular/core';
4
+ import { inject, PLATFORM_ID, Injectable, signal, input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
5
5
  import { Router } from '@angular/router';
6
- import { isPlatformBrowser } from '@angular/common';
6
+ import * as i1 from '@angular/common';
7
+ import { isPlatformBrowser, CommonModule } from '@angular/common';
7
8
  import { jwtDecode } from 'jwt-decode';
8
9
  import { ENVIRONMENT, AUTH_API } from '@acontplus/ng-config';
9
10
  import { HttpClient } from '@angular/common/http';
10
11
  import { from, map, of, tap, catchError, throwError } from 'rxjs';
11
12
  import { switchMap } from 'rxjs/operators';
12
13
  import { AuthTokens } from '@acontplus/core';
13
- import * as i1 from '@angular/forms';
14
+ import * as i2 from '@angular/forms';
14
15
  import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
15
16
  import { MatCard, MatCardHeader, MatCardTitle, MatCardContent, MatCardFooter } from '@angular/material/card';
16
17
  import { MatLabel, MatFormField } from '@angular/material/form-field';
17
18
  import { MatInput } from '@angular/material/input';
18
19
  import { MatIcon } from '@angular/material/icon';
19
20
  import { MatButton, MatAnchor } from '@angular/material/button';
20
- import { MatTooltip } from '@angular/material/tooltip';
21
21
 
22
22
  class TokenRepository {
23
23
  environment = inject(ENVIRONMENT);
@@ -612,6 +612,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
612
612
  // src/lib/presentation/components/login/login.component.ts
613
613
  class LoginComponent {
614
614
  title = input('Login', ...(ngDevMode ? [{ debugName: "title" }] : []));
615
+ showRegisterButton = input(true, ...(ngDevMode ? [{ debugName: "showRegisterButton" }] : []));
616
+ // Additional form controls that can be passed from parent components
617
+ additionalSigninControls = input({}, ...(ngDevMode ? [{ debugName: "additionalSigninControls" }] : []));
618
+ additionalSignupControls = input({}, ...(ngDevMode ? [{ debugName: "additionalSignupControls" }] : []));
619
+ // Additional field templates
620
+ additionalSigninFields = input(null, ...(ngDevMode ? [{ debugName: "additionalSigninFields" }] : []));
621
+ additionalSignupFields = input(null, ...(ngDevMode ? [{ debugName: "additionalSignupFields" }] : []));
622
+ // Footer content template
623
+ footerContent = input(null, ...(ngDevMode ? [{ debugName: "footerContent" }] : []));
624
+ // Computed signal to check if footer content exists
625
+ hasFooterContent = computed(() => this.footerContent() !== null, ...(ngDevMode ? [{ debugName: "hasFooterContent" }] : []));
615
626
  fb = inject(FormBuilder);
616
627
  loginUseCase = inject(LoginUseCase);
617
628
  registerUseCase = inject(RegisterUseCase);
@@ -632,7 +643,17 @@ class LoginComponent {
632
643
  password: ['', [Validators.required, Validators.minLength(6)]],
633
644
  });
634
645
  }
635
- toggleMode() {
646
+ ngOnInit() {
647
+ // Add additional controls to signin form
648
+ Object.entries(this.additionalSigninControls()).forEach(([key, control]) => {
649
+ this.signinForm.addControl(key, control);
650
+ });
651
+ // Add additional controls to signup form
652
+ Object.entries(this.additionalSignupControls()).forEach(([key, control]) => {
653
+ this.signupForm.addControl(key, control);
654
+ });
655
+ }
656
+ switchMode() {
636
657
  this.isLoginMode.set(!this.isLoginMode());
637
658
  this.errorMessage.set(null);
638
659
  }
@@ -671,122 +692,12 @@ class LoginComponent {
671
692
  }
672
693
  }
673
694
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LoginComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
674
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: LoginComponent, isStandalone: true, selector: "app-login", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
675
- <section id="wrapper" class="d-flex justify-content-center align-items-center">
676
- <mat-card class="mat-elevation-z8 p-4 rounded">
677
- <mat-card-header>
678
- <mat-card-title class="text-center">{{ title() }}</mat-card-title>
679
- </mat-card-header>
680
- <mat-card-content>
681
- @if (isLoginMode()) {
682
- <form [formGroup]="signinForm" (ngSubmit)="signIn()" class="d-flex flex-column gap-3">
683
- <mat-form-field class="w-100">
684
- <mat-label>Usuario</mat-label>
685
- <input matInput type="text" placeholder="Ingrese su usuario" formControlName="email" />
686
- </mat-form-field>
687
-
688
- <mat-form-field class="w-100">
689
- <mat-label>Contraseña</mat-label>
690
- <input matInput type="password" placeholder="Ingrese su contraseña" formControlName="password" />
691
- </mat-form-field>
692
-
693
- <div class="d-flex justify-content-center mt-3">
694
- <button
695
- mat-raised-button
696
- color="primary"
697
- [disabled]="!signinForm.valid || isLoading()"
698
- type="submit"
699
- class="w-100"
700
- >
701
- @if (isLoading()) {
702
- Ingresando...
703
- } @else {
704
- <ng-container>Ingresar <mat-icon>login</mat-icon></ng-container>
705
- }
706
- </button>
707
- </div>
708
-
709
- <!-- <div class="text-center mt-2">
710
- <button
711
- mat-button
712
- type="button"
713
- (click)="toggleMode()"
714
- >
715
- ¿No tienes cuenta? Regístrate
716
- </button>
717
- </div> -->
718
- </form>
719
- } @else {
720
- <form [formGroup]="signupForm" (ngSubmit)="registerUser()" class="d-flex flex-column gap-3">
721
- <mat-form-field class="w-100">
722
- <mat-label>Nombre</mat-label>
723
- <input matInput type="text" placeholder="Ingrese su nombre" formControlName="displayName" />
724
- </mat-form-field>
725
-
726
- <mat-form-field class="w-100">
727
- <mat-label>Email</mat-label>
728
- <input matInput type="email" placeholder="Ingrese su email" formControlName="email" />
729
- </mat-form-field>
730
-
731
- <mat-form-field class="w-100">
732
- <mat-label>Contraseña</mat-label>
733
- <input matInput type="password" placeholder="Ingrese su contraseña" formControlName="password" />
734
- </mat-form-field>
735
-
736
- <div class="d-flex justify-content-center mt-3">
737
- <button
738
- mat-raised-button
739
- color="primary"
740
- [disabled]="!signupForm.valid || isLoading()"
741
- type="submit"
742
- class="w-100"
743
- >
744
- @if (isLoading()) {
745
- Registrando...
746
- } @else {
747
- <ng-container>Registrarse <mat-icon>person_add</mat-icon></ng-container>
748
- }
749
- </button>
750
- </div>
751
-
752
- <div class="text-center mt-2">
753
- <button mat-button type="button" (click)="toggleMode()">¿Ya tienes cuenta? Inicia sesión</button>
754
- </div>
755
- </form>
756
- }
757
- @if (errorMessage()) {
758
- <div class="alert alert-danger mt-3" role="alert">
759
- {{ errorMessage() }}
760
- </div>
761
- }
762
- </mat-card-content>
763
- <mat-card-footer>
764
- <div class="row mt-3">
765
- <div class="col-xs-12 col-sm-12 col-md-12 m-t-10 text-center">
766
- <div class="social">
767
- <a
768
- mat-button
769
- color="primary"
770
- matTooltip="Ir a Acontplus Web"
771
- aria-label="Acontplus"
772
- href="https://app.acontplus.com"
773
- target="_blank"
774
- rel="noopener noreferrer"
775
- >
776
- <mat-icon>arrow_outward</mat-icon>
777
- Acontplus Web
778
- </a>
779
- </div>
780
- </div>
781
- </div>
782
- </mat-card-footer>
783
- </mat-card>
784
- </section>
785
- `, isInline: true, styles: ["#wrapper{min-height:100vh;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#667eea,#764ba2);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:#333}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:#f8d7da;border-color:#f5c6cb;color:#721c24;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: ReactiveFormsModule }, { 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],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: 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: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
695
+ 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 }, 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 <!-- 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" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
786
696
  }
787
697
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LoginComponent, decorators: [{
788
698
  type: Component,
789
- args: [{ selector: 'app-login', imports: [
699
+ args: [{ selector: 'acp-login', imports: [
700
+ CommonModule,
790
701
  ReactiveFormsModule,
791
702
  MatCard,
792
703
  MatCardHeader,
@@ -798,120 +709,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
798
709
  MatIcon,
799
710
  MatButton,
800
711
  MatCardFooter,
801
- MatTooltip,
802
712
  MatAnchor,
803
- ], template: `
804
- <section id="wrapper" class="d-flex justify-content-center align-items-center">
805
- <mat-card class="mat-elevation-z8 p-4 rounded">
806
- <mat-card-header>
807
- <mat-card-title class="text-center">{{ title() }}</mat-card-title>
808
- </mat-card-header>
809
- <mat-card-content>
810
- @if (isLoginMode()) {
811
- <form [formGroup]="signinForm" (ngSubmit)="signIn()" class="d-flex flex-column gap-3">
812
- <mat-form-field class="w-100">
813
- <mat-label>Usuario</mat-label>
814
- <input matInput type="text" placeholder="Ingrese su usuario" formControlName="email" />
815
- </mat-form-field>
816
-
817
- <mat-form-field class="w-100">
818
- <mat-label>Contraseña</mat-label>
819
- <input matInput type="password" placeholder="Ingrese su contraseña" formControlName="password" />
820
- </mat-form-field>
821
-
822
- <div class="d-flex justify-content-center mt-3">
823
- <button
824
- mat-raised-button
825
- color="primary"
826
- [disabled]="!signinForm.valid || isLoading()"
827
- type="submit"
828
- class="w-100"
829
- >
830
- @if (isLoading()) {
831
- Ingresando...
832
- } @else {
833
- <ng-container>Ingresar <mat-icon>login</mat-icon></ng-container>
834
- }
835
- </button>
836
- </div>
837
-
838
- <!-- <div class="text-center mt-2">
839
- <button
840
- mat-button
841
- type="button"
842
- (click)="toggleMode()"
843
- >
844
- ¿No tienes cuenta? Regístrate
845
- </button>
846
- </div> -->
847
- </form>
848
- } @else {
849
- <form [formGroup]="signupForm" (ngSubmit)="registerUser()" class="d-flex flex-column gap-3">
850
- <mat-form-field class="w-100">
851
- <mat-label>Nombre</mat-label>
852
- <input matInput type="text" placeholder="Ingrese su nombre" formControlName="displayName" />
853
- </mat-form-field>
854
-
855
- <mat-form-field class="w-100">
856
- <mat-label>Email</mat-label>
857
- <input matInput type="email" placeholder="Ingrese su email" formControlName="email" />
858
- </mat-form-field>
859
-
860
- <mat-form-field class="w-100">
861
- <mat-label>Contraseña</mat-label>
862
- <input matInput type="password" placeholder="Ingrese su contraseña" formControlName="password" />
863
- </mat-form-field>
864
-
865
- <div class="d-flex justify-content-center mt-3">
866
- <button
867
- mat-raised-button
868
- color="primary"
869
- [disabled]="!signupForm.valid || isLoading()"
870
- type="submit"
871
- class="w-100"
872
- >
873
- @if (isLoading()) {
874
- Registrando...
875
- } @else {
876
- <ng-container>Registrarse <mat-icon>person_add</mat-icon></ng-container>
877
- }
878
- </button>
879
- </div>
880
-
881
- <div class="text-center mt-2">
882
- <button mat-button type="button" (click)="toggleMode()">¿Ya tienes cuenta? Inicia sesión</button>
883
- </div>
884
- </form>
885
- }
886
- @if (errorMessage()) {
887
- <div class="alert alert-danger mt-3" role="alert">
888
- {{ errorMessage() }}
889
- </div>
890
- }
891
- </mat-card-content>
892
- <mat-card-footer>
893
- <div class="row mt-3">
894
- <div class="col-xs-12 col-sm-12 col-md-12 m-t-10 text-center">
895
- <div class="social">
896
- <a
897
- mat-button
898
- color="primary"
899
- matTooltip="Ir a Acontplus Web"
900
- aria-label="Acontplus"
901
- href="https://app.acontplus.com"
902
- target="_blank"
903
- rel="noopener noreferrer"
904
- >
905
- <mat-icon>arrow_outward</mat-icon>
906
- Acontplus Web
907
- </a>
908
- </div>
909
- </div>
910
- </div>
911
- </mat-card-footer>
912
- </mat-card>
913
- </section>
914
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["#wrapper{min-height:100vh;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#667eea,#764ba2);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:#333}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:#f8d7da;border-color:#f5c6cb;color:#721c24;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"] }]
713
+ ], 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 <!-- 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"] }]
915
714
  }], ctorParameters: () => [] });
916
715
 
917
716
  // src/lib/presentation/components/index.ts