@acontplus/ng-auth 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +149 -8
- package/fesm2022/acontplus-ng-auth.mjs +255 -81
- package/fesm2022/acontplus-ng-auth.mjs.map +1 -1
- package/index.d.ts +78 -25
- package/package.json +1 -1
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { UserRepository } from '@acontplus/ng-infrastructure';
|
|
1
|
+
import { UserRepository, BaseUseCase } from '@acontplus/ng-infrastructure';
|
|
2
2
|
export { TOKEN_PROVIDER } from '@acontplus/ng-infrastructure';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
4
|
import { inject, PLATFORM_ID, Injectable, signal, input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
5
5
|
import { Router } from '@angular/router';
|
|
6
6
|
import * as i1 from '@angular/common';
|
|
7
|
-
import { isPlatformBrowser, CommonModule } from '@angular/common';
|
|
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
|
+
import { catchError, switchMap } from 'rxjs/operators';
|
|
11
|
+
import { throwError, from, map, of, tap, catchError as catchError$1 } from 'rxjs';
|
|
10
12
|
import { HttpClient } from '@angular/common/http';
|
|
11
|
-
import { from, map, of, tap, catchError, throwError } from 'rxjs';
|
|
12
|
-
import { switchMap } from 'rxjs/operators';
|
|
13
13
|
import { AuthTokens } from '@acontplus/core';
|
|
14
14
|
import * as i2 from '@angular/forms';
|
|
15
15
|
import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
@@ -18,6 +18,7 @@ import { MatLabel, MatFormField } from '@angular/material/form-field';
|
|
|
18
18
|
import { MatInput } from '@angular/material/input';
|
|
19
19
|
import { MatIcon } from '@angular/material/icon';
|
|
20
20
|
import { MatButton, MatAnchor } from '@angular/material/button';
|
|
21
|
+
import { MatCheckbox } from '@angular/material/checkbox';
|
|
21
22
|
|
|
22
23
|
class TokenRepository {
|
|
23
24
|
environment = inject(ENVIRONMENT);
|
|
@@ -36,10 +37,11 @@ class TokenRepository {
|
|
|
36
37
|
sessionStorage.getItem(this.environment.tokenKey));
|
|
37
38
|
}
|
|
38
39
|
getRefreshToken() {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return
|
|
40
|
+
if (!isPlatformBrowser(this.platformId)) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return (localStorage.getItem(this.environment.refreshTokenKey) ||
|
|
44
|
+
sessionStorage.getItem(this.environment.refreshTokenKey));
|
|
43
45
|
}
|
|
44
46
|
setToken(token, rememberMe = false) {
|
|
45
47
|
if (!isPlatformBrowser(this.platformId)) {
|
|
@@ -52,10 +54,16 @@ class TokenRepository {
|
|
|
52
54
|
sessionStorage.setItem(this.environment.tokenKey, token);
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
|
-
setRefreshToken(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
setRefreshToken(refreshToken, rememberMe = false) {
|
|
58
|
+
if (!isPlatformBrowser(this.platformId)) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (rememberMe) {
|
|
62
|
+
localStorage.setItem(this.environment.refreshTokenKey, refreshToken);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
sessionStorage.setItem(this.environment.refreshTokenKey, refreshToken);
|
|
66
|
+
}
|
|
59
67
|
}
|
|
60
68
|
clearTokens() {
|
|
61
69
|
if (!isPlatformBrowser(this.platformId)) {
|
|
@@ -108,6 +116,18 @@ class TokenRepository {
|
|
|
108
116
|
return null;
|
|
109
117
|
}
|
|
110
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Determines if tokens are stored persistently (localStorage) vs session (sessionStorage)
|
|
121
|
+
*/
|
|
122
|
+
isRememberMeEnabled() {
|
|
123
|
+
if (!isPlatformBrowser(this.platformId)) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
// Check if tokens exist in localStorage (persistent storage)
|
|
127
|
+
const tokenInLocalStorage = localStorage.getItem(this.environment.tokenKey);
|
|
128
|
+
const refreshTokenInLocalStorage = localStorage.getItem(this.environment.refreshTokenKey);
|
|
129
|
+
return !!(tokenInLocalStorage || refreshTokenInLocalStorage);
|
|
130
|
+
}
|
|
111
131
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: TokenRepository, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
112
132
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: TokenRepository, providedIn: 'root' });
|
|
113
133
|
}
|
|
@@ -137,59 +157,160 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
|
|
|
137
157
|
args: [{ providedIn: 'root' }]
|
|
138
158
|
}], ctorParameters: () => [{ type: TokenRepository }] });
|
|
139
159
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
160
|
+
/**
|
|
161
|
+
* Service to manage URL redirection after authentication
|
|
162
|
+
* Stores the intended URL when session is lost and redirects to it after successful login
|
|
163
|
+
* SSR-compatible by checking platform before accessing sessionStorage
|
|
164
|
+
*/
|
|
165
|
+
class UrlRedirectService {
|
|
166
|
+
REDIRECT_URL_KEY = 'acp_redirect_url';
|
|
167
|
+
EXCLUDED_ROUTES = ['/login', '/register', '/forgot-password', '/reset-password'];
|
|
168
|
+
router = inject(Router);
|
|
169
|
+
platformId = inject(PLATFORM_ID);
|
|
170
|
+
document = inject(DOCUMENT);
|
|
171
|
+
/**
|
|
172
|
+
* Stores the current URL for later redirection
|
|
173
|
+
* @param url - The URL to store (defaults to current URL)
|
|
174
|
+
*/
|
|
175
|
+
storeIntendedUrl(url) {
|
|
176
|
+
// Only store in browser environment
|
|
177
|
+
if (!this.isBrowser()) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const urlToStore = url || this.router.url;
|
|
181
|
+
// Don't store authentication-related routes
|
|
182
|
+
if (this.isExcludedRoute(urlToStore)) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
// Don't store URLs with query parameters that might contain sensitive data
|
|
186
|
+
const urlWithoutParams = urlToStore.split('?')[0];
|
|
187
|
+
this.getSessionStorage()?.setItem(this.REDIRECT_URL_KEY, urlWithoutParams);
|
|
146
188
|
}
|
|
147
|
-
// Redirect to login page (configurable via environment)
|
|
148
|
-
router.navigate([environment.loginRoute]);
|
|
149
|
-
return false;
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
// src/lib/services/csrf.service.ts
|
|
153
|
-
class CsrfService {
|
|
154
|
-
http = inject(HttpClient);
|
|
155
|
-
environment = inject(ENVIRONMENT);
|
|
156
|
-
csrfToken = null;
|
|
157
189
|
/**
|
|
158
|
-
*
|
|
190
|
+
* Gets the stored intended URL
|
|
191
|
+
* @returns The stored URL or null if none exists
|
|
159
192
|
*/
|
|
160
|
-
|
|
161
|
-
if (this.
|
|
162
|
-
return
|
|
193
|
+
getIntendedUrl() {
|
|
194
|
+
if (!this.isBrowser()) {
|
|
195
|
+
return null;
|
|
163
196
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
197
|
+
return this.getSessionStorage()?.getItem(this.REDIRECT_URL_KEY) || null;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Redirects to the stored URL and clears it from storage
|
|
201
|
+
* @param defaultRoute - The default route to navigate to if no URL is stored
|
|
202
|
+
*/
|
|
203
|
+
redirectToIntendedUrl(defaultRoute = '/') {
|
|
204
|
+
const intendedUrl = this.getIntendedUrl();
|
|
205
|
+
if (intendedUrl && !this.isExcludedRoute(intendedUrl)) {
|
|
206
|
+
this.clearIntendedUrl();
|
|
207
|
+
this.router.navigateByUrl(intendedUrl);
|
|
170
208
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// Server should handle missing CSRF tokens appropriately
|
|
174
|
-
return '';
|
|
209
|
+
else {
|
|
210
|
+
this.router.navigate([defaultRoute]);
|
|
175
211
|
}
|
|
176
212
|
}
|
|
177
213
|
/**
|
|
178
|
-
*
|
|
214
|
+
* Clears the stored intended URL
|
|
179
215
|
*/
|
|
180
|
-
|
|
181
|
-
this.
|
|
216
|
+
clearIntendedUrl() {
|
|
217
|
+
if (!this.isBrowser()) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
this.getSessionStorage()?.removeItem(this.REDIRECT_URL_KEY);
|
|
182
221
|
}
|
|
183
|
-
|
|
184
|
-
|
|
222
|
+
/**
|
|
223
|
+
* Checks if a URL should be excluded from redirection
|
|
224
|
+
* @param url - The URL to check
|
|
225
|
+
* @returns True if the URL should be excluded
|
|
226
|
+
*/
|
|
227
|
+
isExcludedRoute(url) {
|
|
228
|
+
return this.EXCLUDED_ROUTES.some(route => url.includes(route));
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Stores the current URL if it's not an excluded route
|
|
232
|
+
* Useful for guards and interceptors
|
|
233
|
+
*/
|
|
234
|
+
storeCurrentUrlIfAllowed() {
|
|
235
|
+
const currentUrl = this.router.url;
|
|
236
|
+
if (!this.isExcludedRoute(currentUrl)) {
|
|
237
|
+
this.storeIntendedUrl(currentUrl);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Checks if we're running in a browser environment
|
|
242
|
+
* @returns True if running in browser, false if SSR
|
|
243
|
+
*/
|
|
244
|
+
isBrowser() {
|
|
245
|
+
return isPlatformBrowser(this.platformId);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Safely gets sessionStorage reference
|
|
249
|
+
* @returns sessionStorage object or null if not available
|
|
250
|
+
*/
|
|
251
|
+
getSessionStorage() {
|
|
252
|
+
if (!this.isBrowser()) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
return this.document.defaultView?.sessionStorage || null;
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
// Handle cases where sessionStorage might be disabled
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: UrlRedirectService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
264
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: UrlRedirectService, providedIn: 'root' });
|
|
185
265
|
}
|
|
186
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type:
|
|
266
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: UrlRedirectService, decorators: [{
|
|
187
267
|
type: Injectable,
|
|
188
268
|
args: [{
|
|
189
269
|
providedIn: 'root',
|
|
190
270
|
}]
|
|
191
271
|
}] });
|
|
192
272
|
|
|
273
|
+
const authGuard = (_route, state) => {
|
|
274
|
+
const authService = inject(AuthTokenService);
|
|
275
|
+
const router = inject(Router);
|
|
276
|
+
const urlRedirectService = inject(UrlRedirectService);
|
|
277
|
+
const environment = inject(ENVIRONMENT);
|
|
278
|
+
if (authService.isAuthenticated()) {
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
// Store the current URL for redirection after login
|
|
282
|
+
urlRedirectService.storeIntendedUrl(state.url);
|
|
283
|
+
// Redirect to login page (configurable via environment)
|
|
284
|
+
router.navigate([environment.loginRoute]);
|
|
285
|
+
return false;
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Interceptor that handles authentication errors and manages URL redirection
|
|
290
|
+
* Captures the current URL when a 401 error occurs and redirects to login
|
|
291
|
+
*/
|
|
292
|
+
const authRedirectInterceptor = (req, next) => {
|
|
293
|
+
const router = inject(Router);
|
|
294
|
+
const urlRedirectService = inject(UrlRedirectService);
|
|
295
|
+
const authTokenService = inject(AuthTokenService);
|
|
296
|
+
const environment = inject(ENVIRONMENT);
|
|
297
|
+
return next(req).pipe(catchError((error) => {
|
|
298
|
+
// Handle 401 Unauthorized errors
|
|
299
|
+
if (error.status === 401) {
|
|
300
|
+
// Only store and redirect if user was previously authenticated
|
|
301
|
+
// This prevents redirect loops and handles session expiry scenarios
|
|
302
|
+
if (authTokenService.isAuthenticated()) {
|
|
303
|
+
// Store the current URL for redirection after re-authentication
|
|
304
|
+
urlRedirectService.storeCurrentUrlIfAllowed();
|
|
305
|
+
// Navigate to login page
|
|
306
|
+
router.navigate([environment.loginRoute]);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Re-throw the error so other error handlers can process it
|
|
310
|
+
return throwError(() => error);
|
|
311
|
+
}));
|
|
312
|
+
};
|
|
313
|
+
|
|
193
314
|
class User {
|
|
194
315
|
id;
|
|
195
316
|
email;
|
|
@@ -225,6 +346,47 @@ class AuthRepository {
|
|
|
225
346
|
|
|
226
347
|
// src/lib/domain/index.ts
|
|
227
348
|
|
|
349
|
+
// src/lib/services/csrf.service.ts
|
|
350
|
+
class CsrfService {
|
|
351
|
+
http = inject(HttpClient);
|
|
352
|
+
environment = inject(ENVIRONMENT);
|
|
353
|
+
csrfToken = null;
|
|
354
|
+
/**
|
|
355
|
+
* Get CSRF token, fetching it if not available
|
|
356
|
+
*/
|
|
357
|
+
async getCsrfToken() {
|
|
358
|
+
if (this.csrfToken) {
|
|
359
|
+
return this.csrfToken;
|
|
360
|
+
}
|
|
361
|
+
try {
|
|
362
|
+
const response = await this.http
|
|
363
|
+
.get(`${this.environment.apiBaseUrl}/csrf-token`)
|
|
364
|
+
.toPromise();
|
|
365
|
+
this.csrfToken = response?.csrfToken || null;
|
|
366
|
+
return this.csrfToken || '';
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
// If CSRF endpoint fails, return empty token
|
|
370
|
+
// Server should handle missing CSRF tokens appropriately
|
|
371
|
+
return '';
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Clear stored CSRF token (useful on logout)
|
|
376
|
+
*/
|
|
377
|
+
clearCsrfToken() {
|
|
378
|
+
this.csrfToken = null;
|
|
379
|
+
}
|
|
380
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: CsrfService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
381
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: CsrfService, providedIn: 'root' });
|
|
382
|
+
}
|
|
383
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: CsrfService, decorators: [{
|
|
384
|
+
type: Injectable,
|
|
385
|
+
args: [{
|
|
386
|
+
providedIn: 'root',
|
|
387
|
+
}]
|
|
388
|
+
}] });
|
|
389
|
+
|
|
228
390
|
// src/lib/data/repositories/auth-http.repository.ts
|
|
229
391
|
function getDeviceInfo() {
|
|
230
392
|
return `${navigator.platform ?? 'Unknown'} - ${navigator.userAgent}`;
|
|
@@ -251,16 +413,7 @@ class AuthHttpRepository extends AuthRepository {
|
|
|
251
413
|
},
|
|
252
414
|
withCredentials: true,
|
|
253
415
|
})
|
|
254
|
-
.pipe(map(response =>
|
|
255
|
-
// Convert string ID to number - if response.id is a string, hash it to get a number
|
|
256
|
-
const numericId = response.id !== ''
|
|
257
|
-
? Math.abs(response.id.split('').reduce((a, b) => {
|
|
258
|
-
a = (a << 5) - a + b.charCodeAt(0);
|
|
259
|
-
return a & a;
|
|
260
|
-
}, 0))
|
|
261
|
-
: Date.now();
|
|
262
|
-
return new User(numericId, request.email, request.displayName);
|
|
263
|
-
}))));
|
|
416
|
+
.pipe(map(response => new AuthTokens(response.token, response.refreshToken)))));
|
|
264
417
|
}
|
|
265
418
|
refreshToken(request) {
|
|
266
419
|
return from(this.csrfService.getCsrfToken()).pipe(switchMap(csrfToken => this.http
|
|
@@ -393,7 +546,7 @@ class AuthStore {
|
|
|
393
546
|
})
|
|
394
547
|
.pipe(tap(tokens => {
|
|
395
548
|
this.setAuthenticated(tokens);
|
|
396
|
-
}), catchError(() => {
|
|
549
|
+
}), catchError$1(() => {
|
|
397
550
|
this.logout();
|
|
398
551
|
return of(null);
|
|
399
552
|
}), tap({
|
|
@@ -409,8 +562,8 @@ class AuthStore {
|
|
|
409
562
|
/**
|
|
410
563
|
* Set authentication state after successful login
|
|
411
564
|
*/
|
|
412
|
-
setAuthenticated(tokens) {
|
|
413
|
-
this.tokenRepository.saveTokens(tokens);
|
|
565
|
+
setAuthenticated(tokens, rememberMe = false) {
|
|
566
|
+
this.tokenRepository.saveTokens(tokens, rememberMe);
|
|
414
567
|
this._isAuthenticated.set(true);
|
|
415
568
|
const userData = this.userRepository.getCurrentUser();
|
|
416
569
|
this._user.set(userData);
|
|
@@ -497,19 +650,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
|
|
|
497
650
|
}], ctorParameters: () => [] });
|
|
498
651
|
|
|
499
652
|
// src/lib/application/use-cases/login.use-case.ts
|
|
500
|
-
class LoginUseCase {
|
|
653
|
+
class LoginUseCase extends BaseUseCase {
|
|
501
654
|
authRepository = inject(AuthRepository);
|
|
502
655
|
authStore = inject(AuthStore);
|
|
503
656
|
router = inject(Router);
|
|
657
|
+
urlRedirectService = inject(UrlRedirectService);
|
|
504
658
|
execute(request) {
|
|
505
659
|
return this.authRepository.login(request).pipe(tap(tokens => {
|
|
506
|
-
// Set authentication state
|
|
507
|
-
this.authStore.setAuthenticated(tokens);
|
|
508
|
-
//
|
|
509
|
-
this.
|
|
660
|
+
// Set authentication state with rememberMe preference
|
|
661
|
+
this.authStore.setAuthenticated(tokens, request.rememberMe ?? false);
|
|
662
|
+
// Redirect to intended URL or default route
|
|
663
|
+
this.urlRedirectService.redirectToIntendedUrl('/');
|
|
510
664
|
}));
|
|
511
665
|
}
|
|
512
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LoginUseCase, deps:
|
|
666
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LoginUseCase, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
513
667
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LoginUseCase, providedIn: 'root' });
|
|
514
668
|
}
|
|
515
669
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LoginUseCase, decorators: [{
|
|
@@ -520,15 +674,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
|
|
|
520
674
|
}] });
|
|
521
675
|
|
|
522
676
|
// src/lib/application/use-cases/register.use-case.ts
|
|
523
|
-
class RegisterUseCase {
|
|
677
|
+
class RegisterUseCase extends BaseUseCase {
|
|
524
678
|
authRepository = inject(AuthRepository);
|
|
679
|
+
authStore = inject(AuthStore);
|
|
525
680
|
router = inject(Router);
|
|
526
681
|
execute(request) {
|
|
527
|
-
return this.authRepository.register(request).pipe(tap(
|
|
528
|
-
|
|
682
|
+
return this.authRepository.register(request).pipe(tap(tokens => {
|
|
683
|
+
// Set authentication state
|
|
684
|
+
this.authStore.setAuthenticated(tokens);
|
|
685
|
+
// Navigate to main page
|
|
686
|
+
this.router.navigate(['/']);
|
|
529
687
|
}));
|
|
530
688
|
}
|
|
531
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: RegisterUseCase, deps:
|
|
689
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: RegisterUseCase, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
532
690
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: RegisterUseCase, providedIn: 'root' });
|
|
533
691
|
}
|
|
534
692
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: RegisterUseCase, decorators: [{
|
|
@@ -539,7 +697,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
|
|
|
539
697
|
}] });
|
|
540
698
|
|
|
541
699
|
// src/lib/application/use-cases/refresh-token.use-case.ts
|
|
542
|
-
class RefreshTokenUseCase {
|
|
700
|
+
class RefreshTokenUseCase extends BaseUseCase {
|
|
543
701
|
authRepository = inject(AuthRepository);
|
|
544
702
|
userRepository = inject(UserRepository);
|
|
545
703
|
tokenRepository = inject(TokenRepository);
|
|
@@ -557,14 +715,16 @@ class RefreshTokenUseCase {
|
|
|
557
715
|
refreshToken,
|
|
558
716
|
})
|
|
559
717
|
.pipe(tap(tokens => {
|
|
718
|
+
// Preserve the rememberMe preference from the current token storage
|
|
719
|
+
const rememberMe = this.tokenRepository.isRememberMeEnabled();
|
|
560
720
|
// Update authentication state
|
|
561
|
-
this.authStore.setAuthenticated(tokens);
|
|
562
|
-
}), catchError(error => {
|
|
721
|
+
this.authStore.setAuthenticated(tokens, rememberMe);
|
|
722
|
+
}), catchError$1(error => {
|
|
563
723
|
// Don't logout here, let the interceptor handle it
|
|
564
724
|
return throwError(() => error);
|
|
565
725
|
}));
|
|
566
726
|
}
|
|
567
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: RefreshTokenUseCase, deps:
|
|
727
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: RefreshTokenUseCase, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
568
728
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: RefreshTokenUseCase, providedIn: 'root' });
|
|
569
729
|
}
|
|
570
730
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: RefreshTokenUseCase, decorators: [{
|
|
@@ -575,7 +735,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
|
|
|
575
735
|
}] });
|
|
576
736
|
|
|
577
737
|
// src/lib/application/use-cases/logout.use-case.ts
|
|
578
|
-
class LogoutUseCase {
|
|
738
|
+
class LogoutUseCase extends BaseUseCase {
|
|
579
739
|
authRepository = inject(AuthRepository);
|
|
580
740
|
userRepository = inject(UserRepository);
|
|
581
741
|
tokenRepository = inject(TokenRepository);
|
|
@@ -593,7 +753,7 @@ class LogoutUseCase {
|
|
|
593
753
|
// Use auth store for centralized logout
|
|
594
754
|
this.authStore.logout();
|
|
595
755
|
}
|
|
596
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LogoutUseCase, deps:
|
|
756
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LogoutUseCase, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
597
757
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LogoutUseCase, providedIn: 'root' });
|
|
598
758
|
}
|
|
599
759
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LogoutUseCase, decorators: [{
|
|
@@ -613,6 +773,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
|
|
|
613
773
|
class LoginComponent {
|
|
614
774
|
title = input('Login', ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
615
775
|
showRegisterButton = input(true, ...(ngDevMode ? [{ debugName: "showRegisterButton" }] : []));
|
|
776
|
+
showRememberMe = input(true, ...(ngDevMode ? [{ debugName: "showRememberMe" }] : []));
|
|
616
777
|
// Additional form controls that can be passed from parent components
|
|
617
778
|
additionalSigninControls = input({}, ...(ngDevMode ? [{ debugName: "additionalSigninControls" }] : []));
|
|
618
779
|
additionalSignupControls = input({}, ...(ngDevMode ? [{ debugName: "additionalSignupControls" }] : []));
|
|
@@ -636,6 +797,7 @@ class LoginComponent {
|
|
|
636
797
|
this.signinForm = this.fb.group({
|
|
637
798
|
email: ['', [Validators.required, Validators.email]],
|
|
638
799
|
password: ['', Validators.required],
|
|
800
|
+
rememberMe: [false], // Default to false (unchecked)
|
|
639
801
|
});
|
|
640
802
|
this.signupForm = this.fb.group({
|
|
641
803
|
displayName: ['', Validators.required],
|
|
@@ -644,6 +806,11 @@ class LoginComponent {
|
|
|
644
806
|
});
|
|
645
807
|
}
|
|
646
808
|
ngOnInit() {
|
|
809
|
+
// Handle rememberMe control based on showRememberMe input
|
|
810
|
+
if (!this.showRememberMe()) {
|
|
811
|
+
// Remove rememberMe control if not needed
|
|
812
|
+
this.signinForm.removeControl('rememberMe');
|
|
813
|
+
}
|
|
647
814
|
// Add additional controls to signin form
|
|
648
815
|
Object.entries(this.additionalSigninControls()).forEach(([key, control]) => {
|
|
649
816
|
this.signinForm.addControl(key, control);
|
|
@@ -661,7 +828,13 @@ class LoginComponent {
|
|
|
661
828
|
if (this.signinForm.valid) {
|
|
662
829
|
this.isLoading.set(true);
|
|
663
830
|
this.errorMessage.set(null);
|
|
664
|
-
|
|
831
|
+
// Prepare login request with rememberMe handling
|
|
832
|
+
const loginRequest = {
|
|
833
|
+
...this.signinForm.value,
|
|
834
|
+
// If showRememberMe is false, default rememberMe to false
|
|
835
|
+
rememberMe: this.showRememberMe() ? (this.signinForm.value.rememberMe ?? false) : false
|
|
836
|
+
};
|
|
837
|
+
this.loginUseCase.execute(loginRequest).subscribe({
|
|
665
838
|
next: () => {
|
|
666
839
|
this.isLoading.set(false);
|
|
667
840
|
},
|
|
@@ -692,7 +865,7 @@ class LoginComponent {
|
|
|
692
865
|
}
|
|
693
866
|
}
|
|
694
867
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LoginComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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 });
|
|
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 });
|
|
696
869
|
}
|
|
697
870
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LoginComponent, decorators: [{
|
|
698
871
|
type: Component,
|
|
@@ -710,7 +883,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
|
|
|
710
883
|
MatButton,
|
|
711
884
|
MatCardFooter,
|
|
712
885
|
MatAnchor,
|
|
713
|
-
|
|
886
|
+
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"] }]
|
|
714
888
|
}], ctorParameters: () => [] });
|
|
715
889
|
|
|
716
890
|
// src/lib/presentation/components/index.ts
|
|
@@ -730,5 +904,5 @@ const authProviders = [
|
|
|
730
904
|
* Generated bundle index. Do not edit.
|
|
731
905
|
*/
|
|
732
906
|
|
|
733
|
-
export { AuthHttpRepository, AuthRepository, AuthStore, AuthTokenService,
|
|
907
|
+
export { AuthHttpRepository, AuthRepository, AuthStore, AuthTokenService, LoginComponent, LoginUseCase, LogoutUseCase, RefreshTokenUseCase, RegisterUseCase, TokenRepository, UrlRedirectService, User, authGuard, authProviders, authRedirectInterceptor };
|
|
734
908
|
//# sourceMappingURL=acontplus-ng-auth.mjs.map
|