@rolatech/angular-auth 20.3.0-beta.4 → 20.3.1-beta.3
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.
|
@@ -1,43 +1,27 @@
|
|
|
1
|
+
import { Location, isPlatformBrowser, CommonModule } from '@angular/common';
|
|
1
2
|
import * as i0 from '@angular/core';
|
|
2
|
-
import {
|
|
3
|
+
import { signal, computed, Injectable, inject, Component, input, ChangeDetectionStrategy, output, model, PLATFORM_ID, InjectionToken, APP_INITIALIZER, makeEnvironmentProviders } from '@angular/core';
|
|
4
|
+
import * as i1 from '@angular/material/button';
|
|
5
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
6
|
+
import * as i2 from '@angular/material/progress-spinner';
|
|
7
|
+
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
8
|
+
import { ActivatedRoute, Router, RouterLink, provideRouter, ROUTES } from '@angular/router';
|
|
9
|
+
import { APP_CONFIG, AngularCommonModule } from '@rolatech/angular-common';
|
|
10
|
+
import { of, forkJoin, throwError, catchError as catchError$1, switchMap as switchMap$1, map as map$1, BehaviorSubject, NEVER, firstValueFrom } from 'rxjs';
|
|
11
|
+
import { HttpClient, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
|
|
12
|
+
import { switchMap, catchError, map, finalize, shareReplay, tap } from 'rxjs/operators';
|
|
3
13
|
import { toSignal, toObservable } from '@angular/core/rxjs-interop';
|
|
4
|
-
import * as i2 from '@angular/material/icon';
|
|
14
|
+
import * as i2$1 from '@angular/material/icon';
|
|
5
15
|
import { MatIconModule } from '@angular/material/icon';
|
|
6
|
-
import * as i1 from '@angular/material/menu';
|
|
16
|
+
import * as i1$1 from '@angular/material/menu';
|
|
7
17
|
import { MatMenuModule } from '@angular/material/menu';
|
|
8
|
-
import { RouterLink, Router, provideRouter, ROUTES } from '@angular/router';
|
|
9
18
|
import { OnboardingApplicantService, SnackBarService } from '@rolatech/angular-services';
|
|
10
|
-
import { switchMap, of, catchError, forkJoin, throwError, map as map$1, BehaviorSubject, NEVER, firstValueFrom } from 'rxjs';
|
|
11
19
|
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule, MatDialog } from '@angular/material/dialog';
|
|
12
|
-
import { APP_CONFIG, AngularCommonModule } from '@rolatech/angular-common';
|
|
13
20
|
import QRCode from 'qrcode';
|
|
14
|
-
import { MatButtonModule } from '@angular/material/button';
|
|
15
|
-
import { HttpClient, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
|
|
16
|
-
import { switchMap as switchMap$1, catchError as catchError$1, map, finalize, shareReplay, tap, filter } from 'rxjs/operators';
|
|
17
21
|
import { SpinnerComponent, AngularComponentsModule } from '@rolatech/angular-components';
|
|
18
|
-
import * as i1$
|
|
19
|
-
import * as i2$
|
|
22
|
+
import * as i1$2 from '@angular/forms';
|
|
23
|
+
import * as i2$2 from '@angular/material/form-field';
|
|
20
24
|
import * as i3 from '@angular/material/input';
|
|
21
|
-
import { isPlatformBrowser, CommonModule } from '@angular/common';
|
|
22
|
-
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
23
|
-
|
|
24
|
-
class ForbiddenComponent {
|
|
25
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ForbiddenComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
26
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.1", type: ForbiddenComponent, isStandalone: true, selector: "rolatech-forbidden", ngImport: i0, template: "<div class=\"p-6 max-w-lg h-auto max-h-32\">\n <div class=\"flex flex-col\">\n <b>403.</b>\n <p>Forbidden page.</p>\n </div>\n</div>\n", styles: [""] });
|
|
27
|
-
}
|
|
28
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ForbiddenComponent, decorators: [{
|
|
29
|
-
type: Component,
|
|
30
|
-
args: [{ selector: 'rolatech-forbidden', template: "<div class=\"p-6 max-w-lg h-auto max-h-32\">\n <div class=\"flex flex-col\">\n <b>403.</b>\n <p>Forbidden page.</p>\n </div>\n</div>\n" }]
|
|
31
|
-
}] });
|
|
32
|
-
|
|
33
|
-
class UnauthorizedComponent {
|
|
34
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: UnauthorizedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
35
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.1", type: UnauthorizedComponent, isStandalone: true, selector: "rolatech-unauthorized", ngImport: i0, template: "<div class=\"p-6 max-w-lg h-auto max-h-32\">\n <div class=\"flex flex-col\">\n <b>401.</b>\n <p>Unauthorized page.</p>\n </div>\n</div>\n", styles: [""] });
|
|
36
|
-
}
|
|
37
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: UnauthorizedComponent, decorators: [{
|
|
38
|
-
type: Component,
|
|
39
|
-
args: [{ selector: 'rolatech-unauthorized', template: "<div class=\"p-6 max-w-lg h-auto max-h-32\">\n <div class=\"flex flex-col\">\n <b>401.</b>\n <p>Unauthorized page.</p>\n </div>\n</div>\n" }]
|
|
40
|
-
}] });
|
|
41
25
|
|
|
42
26
|
class AuthContextStore {
|
|
43
27
|
state = signal({
|
|
@@ -220,128 +204,59 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
220
204
|
args: [{ providedIn: 'root' }]
|
|
221
205
|
}] });
|
|
222
206
|
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
applicationCode = input('primecasa', ...(ngDevMode ? [{ debugName: "applicationCode" }] : []));
|
|
233
|
-
applyRouterLink = input('/agents', ...(ngDevMode ? [{ debugName: "applyRouterLink" }] : []));
|
|
234
|
-
dashboardRouterLink = input('/organization', ...(ngDevMode ? [{ debugName: "dashboardRouterLink" }] : []));
|
|
235
|
-
showApplyAction = input(true, ...(ngDevMode ? [{ debugName: "showApplyAction" }] : []));
|
|
236
|
-
dashboardLabel = input('Agent dashboard', ...(ngDevMode ? [{ debugName: "dashboardLabel" }] : []));
|
|
237
|
-
authStore = inject(AuthStore);
|
|
238
|
-
authContextStore = inject(AuthContextStore);
|
|
239
|
-
onboardingApplicantService = inject(OnboardingApplicantService);
|
|
240
|
-
mine = toSignal(toObservable(computed(() => ({
|
|
241
|
-
authenticated: this.authStore.authenticated(),
|
|
242
|
-
loaded: this.authStore.loaded(),
|
|
243
|
-
applicationCode: this.applicationCode(),
|
|
244
|
-
}))).pipe(switchMap(({ authenticated, loaded, applicationCode }) => {
|
|
245
|
-
if (!loaded || !authenticated || !applicationCode) {
|
|
246
|
-
return of(null);
|
|
247
|
-
}
|
|
248
|
-
return this.onboardingApplicantService.listMine(applicationCode).pipe(catchError(() => of([])));
|
|
249
|
-
})), { initialValue: null });
|
|
250
|
-
latestApplication = computed(() => {
|
|
251
|
-
const applications = this.mine();
|
|
252
|
-
if (!applications?.length) {
|
|
253
|
-
return null;
|
|
254
|
-
}
|
|
255
|
-
return [...applications].sort((left, right) => this.toTimestamp(right) - this.toTimestamp(left))[0] ?? null;
|
|
256
|
-
}, ...(ngDevMode ? [{ debugName: "latestApplication" }] : []));
|
|
257
|
-
hasDashboardAccess = computed(() => {
|
|
258
|
-
this.authStore.loaded();
|
|
259
|
-
const appId = this.authContextStore.appId() ?? this.authStore.primaryApplication()?.appId ?? this.authStore.primaryOrganization()?.appId ?? null;
|
|
260
|
-
return this.authStore.hasOrganizationRole(appId, null, ...ORGANIZATION_OWNER_ROLES, ...ORGANIZATION_ADMIN_ROLES, ...ORGANIZATION_MEMBER_ROLES);
|
|
261
|
-
}, ...(ngDevMode ? [{ debugName: "hasDashboardAccess" }] : []));
|
|
262
|
-
entry = computed(() => {
|
|
263
|
-
if (this.hasDashboardAccess()) {
|
|
264
|
-
return {
|
|
265
|
-
icon: 'storefront',
|
|
266
|
-
label: this.dashboardLabel(),
|
|
267
|
-
routerLink: this.dashboardRouterLink(),
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
const application = this.latestApplication();
|
|
271
|
-
if (application) {
|
|
272
|
-
return {
|
|
273
|
-
icon: 'assignment',
|
|
274
|
-
label: this.applicationLabel(application.status),
|
|
275
|
-
routerLink: this.applicationRouterLink(application),
|
|
276
|
-
};
|
|
207
|
+
const LAST_ALLOWED_URL_KEY = 'rolatech.auth.last-allowed-url';
|
|
208
|
+
function normalizeUrl(url) {
|
|
209
|
+
if (!url) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
213
|
+
try {
|
|
214
|
+
const parsed = new URL(url);
|
|
215
|
+
return `${parsed.pathname}${parsed.search}${parsed.hash}` || '/';
|
|
277
216
|
}
|
|
278
|
-
|
|
217
|
+
catch {
|
|
279
218
|
return null;
|
|
280
219
|
}
|
|
281
|
-
return {
|
|
282
|
-
icon: 'badge',
|
|
283
|
-
label: 'Apply as agent',
|
|
284
|
-
routerLink: this.applyRouterLink(),
|
|
285
|
-
};
|
|
286
|
-
}, ...(ngDevMode ? [{ debugName: "entry" }] : []));
|
|
287
|
-
applicationLabel(status) {
|
|
288
|
-
switch (status) {
|
|
289
|
-
case 'APPROVED':
|
|
290
|
-
return 'Agent application approved';
|
|
291
|
-
case 'FAILED':
|
|
292
|
-
return 'Agent application result';
|
|
293
|
-
case 'NEED_MORE_INFO':
|
|
294
|
-
return 'Agent application updates';
|
|
295
|
-
case 'SUBMITTED':
|
|
296
|
-
case 'IN_REVIEW':
|
|
297
|
-
return 'Agent application review';
|
|
298
|
-
default:
|
|
299
|
-
return 'Continue agent application';
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
applicationRouterLink(application) {
|
|
303
|
-
const route = this.canOpenReview(application) ? 'review' : 'form';
|
|
304
|
-
return ['/agents', application.id, route];
|
|
305
220
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
221
|
+
return url.startsWith('/') ? url : `/${url}`;
|
|
222
|
+
}
|
|
223
|
+
function isAccessBoundaryUrl(url) {
|
|
224
|
+
return url.startsWith('/forbidden') || url.startsWith('/unauthorized');
|
|
225
|
+
}
|
|
226
|
+
function rememberAllowedUrl(url) {
|
|
227
|
+
if (typeof window === 'undefined') {
|
|
228
|
+
return;
|
|
312
229
|
}
|
|
313
|
-
|
|
314
|
-
|
|
230
|
+
const normalizedUrl = normalizeUrl(url);
|
|
231
|
+
if (!normalizedUrl || isAccessBoundaryUrl(normalizedUrl)) {
|
|
232
|
+
return;
|
|
315
233
|
}
|
|
316
|
-
|
|
317
|
-
|
|
234
|
+
window.sessionStorage.setItem(LAST_ALLOWED_URL_KEY, normalizedUrl);
|
|
235
|
+
}
|
|
236
|
+
function readLastAllowedUrl() {
|
|
237
|
+
if (typeof window === 'undefined') {
|
|
238
|
+
return null;
|
|
318
239
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
<a mat-menu-item [routerLink]="item.routerLink" class="px-6 flex items-center cursor-pointer">
|
|
323
|
-
<mat-icon>{{ item.icon }}</mat-icon>
|
|
324
|
-
<span class="flex items-center pl-1">{{ item.label }}</span>
|
|
325
|
-
</a>
|
|
240
|
+
const value = normalizeUrl(window.sessionStorage.getItem(LAST_ALLOWED_URL_KEY));
|
|
241
|
+
if (!value || isAccessBoundaryUrl(value)) {
|
|
242
|
+
return null;
|
|
326
243
|
}
|
|
327
|
-
|
|
244
|
+
return value;
|
|
328
245
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
<a mat-menu-item [routerLink]="item.routerLink" class="px-6 flex items-center cursor-pointer">
|
|
337
|
-
<mat-icon>{{ item.icon }}</mat-icon>
|
|
338
|
-
<span class="flex items-center pl-1">{{ item.label }}</span>
|
|
339
|
-
</a>
|
|
246
|
+
function createForbiddenUrlTree(router, deniedUrl, reason = 'forbidden') {
|
|
247
|
+
const queryParams = {
|
|
248
|
+
reason,
|
|
249
|
+
};
|
|
250
|
+
const normalizedDeniedUrl = normalizeUrl(deniedUrl);
|
|
251
|
+
if (normalizedDeniedUrl) {
|
|
252
|
+
queryParams['denied'] = normalizedDeniedUrl;
|
|
340
253
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
254
|
+
const fallbackUrl = readLastAllowedUrl();
|
|
255
|
+
if (fallbackUrl) {
|
|
256
|
+
queryParams['fallback'] = fallbackUrl;
|
|
257
|
+
}
|
|
258
|
+
return router.createUrlTree(['/forbidden'], { queryParams });
|
|
259
|
+
}
|
|
345
260
|
|
|
346
261
|
class AuthService {
|
|
347
262
|
environment = inject(APP_CONFIG);
|
|
@@ -370,15 +285,15 @@ class AuthService {
|
|
|
370
285
|
.get(`${this.environment.baseUrl}/auth/introspect`, {
|
|
371
286
|
withCredentials: true,
|
|
372
287
|
})
|
|
373
|
-
.pipe(switchMap
|
|
288
|
+
.pipe(switchMap((res) => {
|
|
374
289
|
if (!res.authenticated) {
|
|
375
290
|
this.authStore.clear();
|
|
376
291
|
this.authContextStore.clear();
|
|
377
292
|
return of(res);
|
|
378
293
|
}
|
|
379
294
|
return forkJoin({
|
|
380
|
-
user: this.me().pipe(catchError
|
|
381
|
-
context: this.getMeContext().pipe(catchError
|
|
295
|
+
user: this.me().pipe(catchError(() => of(null))),
|
|
296
|
+
context: this.getMeContext().pipe(catchError(() => of(this.emptyContext(res)))),
|
|
382
297
|
}).pipe(map(({ user, context }) => {
|
|
383
298
|
this.authStore.update({
|
|
384
299
|
authenticated: true,
|
|
@@ -395,7 +310,7 @@ class AuthService {
|
|
|
395
310
|
this.syncContext(context);
|
|
396
311
|
return res;
|
|
397
312
|
}));
|
|
398
|
-
}), catchError
|
|
313
|
+
}), catchError((error) => {
|
|
399
314
|
this.authStore.clear();
|
|
400
315
|
this.authContextStore.clear();
|
|
401
316
|
return throwError(() => error);
|
|
@@ -584,6 +499,257 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
584
499
|
args: [{ providedIn: 'root' }]
|
|
585
500
|
}] });
|
|
586
501
|
|
|
502
|
+
class ForbiddenComponent {
|
|
503
|
+
route = inject(ActivatedRoute);
|
|
504
|
+
router = inject(Router);
|
|
505
|
+
location = inject(Location);
|
|
506
|
+
authService = inject(AuthService);
|
|
507
|
+
authStore = inject(AuthStore);
|
|
508
|
+
authContextStore = inject(AuthContextStore);
|
|
509
|
+
environment = inject(APP_CONFIG);
|
|
510
|
+
switchingAccount = signal(false, ...(ngDevMode ? [{ debugName: "switchingAccount" }] : []));
|
|
511
|
+
deniedUrl = this.normalizeRoute(this.route.snapshot.queryParamMap.get('denied'));
|
|
512
|
+
fallbackUrl = this.resolveFallbackUrl();
|
|
513
|
+
authenticated = this.authStore.authenticated;
|
|
514
|
+
accountLabel = computed(() => this.authStore.username() ?? this.authStore.user()?.email ?? 'this account', ...(ngDevMode ? [{ debugName: "accountLabel" }] : []));
|
|
515
|
+
goBackOrFallback() {
|
|
516
|
+
if (this.fallbackUrl) {
|
|
517
|
+
this.router.navigateByUrl(this.fallbackUrl);
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
this.location.back();
|
|
521
|
+
}
|
|
522
|
+
signOutAndSwitchAccount() {
|
|
523
|
+
const signInUrl = this.buildSignInUrl();
|
|
524
|
+
this.switchingAccount.set(true);
|
|
525
|
+
this.authService
|
|
526
|
+
.logout()
|
|
527
|
+
.pipe(catchError$1(() => {
|
|
528
|
+
this.authStore.clear();
|
|
529
|
+
this.authContextStore.clear();
|
|
530
|
+
return of(null);
|
|
531
|
+
}))
|
|
532
|
+
.subscribe({
|
|
533
|
+
next: () => {
|
|
534
|
+
window.location.href = signInUrl;
|
|
535
|
+
},
|
|
536
|
+
error: () => {
|
|
537
|
+
window.location.href = signInUrl;
|
|
538
|
+
},
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
goToSignIn() {
|
|
542
|
+
window.location.href = this.buildSignInUrl();
|
|
543
|
+
}
|
|
544
|
+
buildSignInUrl() {
|
|
545
|
+
const continueUrl = this.buildContinueUrl();
|
|
546
|
+
return `${this.environment.accountsUrl}/signin?continue=${encodeURIComponent(continueUrl)}`;
|
|
547
|
+
}
|
|
548
|
+
buildContinueUrl() {
|
|
549
|
+
if (typeof window === 'undefined') {
|
|
550
|
+
return this.environment.myaccountUrl;
|
|
551
|
+
}
|
|
552
|
+
const target = this.deniedUrl || this.fallbackUrl || '/';
|
|
553
|
+
return new URL(target, window.location.origin).toString();
|
|
554
|
+
}
|
|
555
|
+
resolveFallbackUrl() {
|
|
556
|
+
const queryFallback = this.normalizeRoute(this.route.snapshot.queryParamMap.get('fallback'));
|
|
557
|
+
const storedFallback = this.normalizeRoute(readLastAllowedUrl());
|
|
558
|
+
const fallback = queryFallback || storedFallback;
|
|
559
|
+
if (!fallback || fallback === this.deniedUrl || fallback.startsWith('/forbidden')) {
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
return fallback;
|
|
563
|
+
}
|
|
564
|
+
normalizeRoute(url) {
|
|
565
|
+
if (!url) {
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
return url.startsWith('/') ? url : `/${url}`;
|
|
569
|
+
}
|
|
570
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ForbiddenComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
571
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: ForbiddenComponent, isStandalone: true, selector: "rolatech-forbidden", ngImport: i0, template: "<section class=\"forbidden-page\">\n <article class=\"forbidden-page__card\">\n <span class=\"forbidden-page__eyebrow\">403</span>\n <h1 class=\"forbidden-page__title\">This account cannot open that page</h1>\n\n @if (authenticated()) {\n <p class=\"forbidden-page__description\">\n You are signed in as <strong>{{ accountLabel() }}</strong>, but that account does not have permission to view this route.\n </p>\n } @else {\n <p class=\"forbidden-page__description\">You do not have permission to open this route with the current session.</p>\n }\n\n @if (deniedUrl) {\n <div class=\"forbidden-page__route\">\n <span class=\"forbidden-page__route-label\">Attempted page</span>\n <code class=\"forbidden-page__route-value\">{{ deniedUrl }}</code>\n </div>\n }\n\n <p class=\"forbidden-page__hint\">\n Return to a page that still works for this account, or sign out and continue with the correct account to retry access.\n </p>\n\n <div class=\"forbidden-page__actions\">\n <button mat-stroked-button type=\"button\" (click)=\"goBackOrFallback()\">\n {{ fallbackUrl ? 'Back to allowed page' : 'Go back' }}\n </button>\n\n @if (authenticated()) {\n <button mat-flat-button type=\"button\" (click)=\"signOutAndSwitchAccount()\" [disabled]=\"switchingAccount()\">\n @if (switchingAccount()) {\n <mat-progress-spinner diameter=\"18\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span>{{ switchingAccount() ? 'Switching account...' : 'Sign out and switch account' }}</span>\n </button>\n } @else {\n <button mat-flat-button type=\"button\" (click)=\"goToSignIn()\">Sign in</button>\n }\n </div>\n\n <p class=\"forbidden-page__support\">\n If this should be available for the current account, ask an administrator to review the role or permission assignment.\n </p>\n </article>\n</section>\n", styles: [":host{display:grid;min-height:100dvh;padding:1.5rem;place-items:center;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 42%),var(--rt-base-background, #ffffff)}.forbidden-page{width:min(100%,42rem)}.forbidden-page__card{display:flex;flex-direction:column;gap:1rem;padding:1.5rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 24px 56px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.forbidden-page__eyebrow{display:inline-flex;align-self:flex-start;padding:.32rem .72rem;border-radius:9999px;background:color-mix(in srgb,var(--mat-sys-error, #b91c1c) 12%,transparent);color:var(--mat-sys-error, #b91c1c);font-size:.78rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.forbidden-page__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,4vw,2.4rem);line-height:1.05}.forbidden-page__description,.forbidden-page__hint,.forbidden-page__support{margin:0;color:var(--rt-text-secondary);line-height:1.6}.forbidden-page__route{display:flex;flex-direction:column;gap:.45rem;padding:.95rem 1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent)}.forbidden-page__route-label{font-size:.8rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--rt-text-secondary)}.forbidden-page__route-value{overflow-wrap:anywhere;color:var(--rt-text-primary);font-family:SFMono-Regular,ui-monospace,monospace;font-size:.95rem}.forbidden-page__actions{display:flex;flex-wrap:wrap;gap:.75rem;align-items:center}.forbidden-page__actions button{display:inline-flex;gap:.55rem;align-items:center}@media(max-width:640px){:host{padding:1rem}.forbidden-page__card{padding:1.2rem}.forbidden-page__actions{flex-direction:column;align-items:stretch}.forbidden-page__actions button{justify-content:center}}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.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: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i2.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }] });
|
|
572
|
+
}
|
|
573
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ForbiddenComponent, decorators: [{
|
|
574
|
+
type: Component,
|
|
575
|
+
args: [{ selector: 'rolatech-forbidden', imports: [MatButtonModule, MatProgressSpinnerModule], template: "<section class=\"forbidden-page\">\n <article class=\"forbidden-page__card\">\n <span class=\"forbidden-page__eyebrow\">403</span>\n <h1 class=\"forbidden-page__title\">This account cannot open that page</h1>\n\n @if (authenticated()) {\n <p class=\"forbidden-page__description\">\n You are signed in as <strong>{{ accountLabel() }}</strong>, but that account does not have permission to view this route.\n </p>\n } @else {\n <p class=\"forbidden-page__description\">You do not have permission to open this route with the current session.</p>\n }\n\n @if (deniedUrl) {\n <div class=\"forbidden-page__route\">\n <span class=\"forbidden-page__route-label\">Attempted page</span>\n <code class=\"forbidden-page__route-value\">{{ deniedUrl }}</code>\n </div>\n }\n\n <p class=\"forbidden-page__hint\">\n Return to a page that still works for this account, or sign out and continue with the correct account to retry access.\n </p>\n\n <div class=\"forbidden-page__actions\">\n <button mat-stroked-button type=\"button\" (click)=\"goBackOrFallback()\">\n {{ fallbackUrl ? 'Back to allowed page' : 'Go back' }}\n </button>\n\n @if (authenticated()) {\n <button mat-flat-button type=\"button\" (click)=\"signOutAndSwitchAccount()\" [disabled]=\"switchingAccount()\">\n @if (switchingAccount()) {\n <mat-progress-spinner diameter=\"18\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span>{{ switchingAccount() ? 'Switching account...' : 'Sign out and switch account' }}</span>\n </button>\n } @else {\n <button mat-flat-button type=\"button\" (click)=\"goToSignIn()\">Sign in</button>\n }\n </div>\n\n <p class=\"forbidden-page__support\">\n If this should be available for the current account, ask an administrator to review the role or permission assignment.\n </p>\n </article>\n</section>\n", styles: [":host{display:grid;min-height:100dvh;padding:1.5rem;place-items:center;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 42%),var(--rt-base-background, #ffffff)}.forbidden-page{width:min(100%,42rem)}.forbidden-page__card{display:flex;flex-direction:column;gap:1rem;padding:1.5rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 24px 56px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.forbidden-page__eyebrow{display:inline-flex;align-self:flex-start;padding:.32rem .72rem;border-radius:9999px;background:color-mix(in srgb,var(--mat-sys-error, #b91c1c) 12%,transparent);color:var(--mat-sys-error, #b91c1c);font-size:.78rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.forbidden-page__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,4vw,2.4rem);line-height:1.05}.forbidden-page__description,.forbidden-page__hint,.forbidden-page__support{margin:0;color:var(--rt-text-secondary);line-height:1.6}.forbidden-page__route{display:flex;flex-direction:column;gap:.45rem;padding:.95rem 1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent)}.forbidden-page__route-label{font-size:.8rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--rt-text-secondary)}.forbidden-page__route-value{overflow-wrap:anywhere;color:var(--rt-text-primary);font-family:SFMono-Regular,ui-monospace,monospace;font-size:.95rem}.forbidden-page__actions{display:flex;flex-wrap:wrap;gap:.75rem;align-items:center}.forbidden-page__actions button{display:inline-flex;gap:.55rem;align-items:center}@media(max-width:640px){:host{padding:1rem}.forbidden-page__card{padding:1.2rem}.forbidden-page__actions{flex-direction:column;align-items:stretch}.forbidden-page__actions button{justify-content:center}}\n"] }]
|
|
576
|
+
}] });
|
|
577
|
+
|
|
578
|
+
class UnauthorizedComponent {
|
|
579
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: UnauthorizedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
580
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.1", type: UnauthorizedComponent, isStandalone: true, selector: "rolatech-unauthorized", ngImport: i0, template: "<div class=\"p-6 max-w-lg h-auto max-h-32\">\n <div class=\"flex flex-col\">\n <b>401.</b>\n <p>Unauthorized page.</p>\n </div>\n</div>\n", styles: [""] });
|
|
581
|
+
}
|
|
582
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: UnauthorizedComponent, decorators: [{
|
|
583
|
+
type: Component,
|
|
584
|
+
args: [{ selector: 'rolatech-unauthorized', template: "<div class=\"p-6 max-w-lg h-auto max-h-32\">\n <div class=\"flex flex-col\">\n <b>401.</b>\n <p>Unauthorized page.</p>\n </div>\n</div>\n" }]
|
|
585
|
+
}] });
|
|
586
|
+
|
|
587
|
+
const PLATFORM_ADMIN_ROLES = ['PLATFORM_ADMIN', 'ROLE_PLATFORM_ADMIN'];
|
|
588
|
+
const APPLICATION_OWNER_ROLES = ['APPLICATION_OWNER', 'APP_OWNER', 'ROLE_APPLICATION_OWNER', 'ROLE_APP_OWNER'];
|
|
589
|
+
const APPLICATION_ADMIN_ROLES = ['APPLICATION_ADMIN', 'APP_ADMIN', 'ROLE_APPLICATION_ADMIN', 'ROLE_APP_ADMIN'];
|
|
590
|
+
const ORGANIZATION_OWNER_ROLES = ['ORGANIZATION_OWNER', 'ORG_OWNER', 'ROLE_ORGANIZATION_OWNER', 'ROLE_ORG_OWNER'];
|
|
591
|
+
const ORGANIZATION_ADMIN_ROLES = ['ORGANIZATION_ADMIN', 'ORG_ADMIN', 'ROLE_ORGANIZATION_ADMIN', 'ROLE_ORG_ADMIN'];
|
|
592
|
+
const ORGANIZATION_MEMBER_ROLES = ['ORGANIZATION_MEMBER', 'ORG_MEMBER', 'ROLE_ORGANIZATION_MEMBER', 'ROLE_ORG_MEMBER'];
|
|
593
|
+
const ORGANIZATION_STAFF_ROLES = ['ORGANIZATION_STAFF', 'ORG_STAFF', 'ROLE_ORGANIZATION_STAFF', 'ROLE_ORG_STAFF'];
|
|
594
|
+
|
|
595
|
+
class AgentAccessMenu {
|
|
596
|
+
applicationCode = input('primecasa', ...(ngDevMode ? [{ debugName: "applicationCode" }] : []));
|
|
597
|
+
applyRouterLink = input('/agents', ...(ngDevMode ? [{ debugName: "applyRouterLink" }] : []));
|
|
598
|
+
applicationsRouterLink = input('/applications', ...(ngDevMode ? [{ debugName: "applicationsRouterLink" }] : []));
|
|
599
|
+
dashboardRouterLink = input(null, ...(ngDevMode ? [{ debugName: "dashboardRouterLink" }] : []));
|
|
600
|
+
showApplyAction = input(true, ...(ngDevMode ? [{ debugName: "showApplyAction" }] : []));
|
|
601
|
+
dashboardLabel = input('Agent dashboard', ...(ngDevMode ? [{ debugName: "dashboardLabel" }] : []));
|
|
602
|
+
authStore = inject(AuthStore);
|
|
603
|
+
authContextStore = inject(AuthContextStore);
|
|
604
|
+
onboardingApplicantService = inject(OnboardingApplicantService);
|
|
605
|
+
mine = toSignal(toObservable(computed(() => ({
|
|
606
|
+
authenticated: this.authStore.authenticated(),
|
|
607
|
+
loaded: this.authStore.loaded(),
|
|
608
|
+
applicationCode: this.applicationCode(),
|
|
609
|
+
}))).pipe(switchMap$1(({ authenticated, loaded, applicationCode }) => {
|
|
610
|
+
if (!loaded || !authenticated || !applicationCode) {
|
|
611
|
+
return of(null);
|
|
612
|
+
}
|
|
613
|
+
return this.onboardingApplicantService.listMine().pipe(catchError$1(() => of([])));
|
|
614
|
+
})), { initialValue: null });
|
|
615
|
+
latestApplication = computed(() => {
|
|
616
|
+
const applications = this.mine();
|
|
617
|
+
if (!applications?.length) {
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
return [...applications].sort((left, right) => this.toTimestamp(right) - this.toTimestamp(left))[0] ?? null;
|
|
621
|
+
}, ...(ngDevMode ? [{ debugName: "latestApplication" }] : []));
|
|
622
|
+
hasDashboardAccess = computed(() => {
|
|
623
|
+
this.authStore.loaded();
|
|
624
|
+
const appId = this.authContextStore.appId() ??
|
|
625
|
+
this.authStore.primaryApplication()?.appId ??
|
|
626
|
+
this.authStore.primaryOrganization()?.appId ??
|
|
627
|
+
null;
|
|
628
|
+
return this.authStore.hasOrganizationRole(appId, null, ...ORGANIZATION_OWNER_ROLES, ...ORGANIZATION_ADMIN_ROLES, ...ORGANIZATION_MEMBER_ROLES);
|
|
629
|
+
}, ...(ngDevMode ? [{ debugName: "hasDashboardAccess" }] : []));
|
|
630
|
+
entry = computed(() => {
|
|
631
|
+
const application = this.latestApplication();
|
|
632
|
+
if (application?.status === 'APPROVED') {
|
|
633
|
+
return this.toEntry(this.dashboardLabel(), this.dashboardEntryRouterLink(application));
|
|
634
|
+
}
|
|
635
|
+
if (application) {
|
|
636
|
+
return this.toEntry(this.applicationLabel(application.status), this.applicationRouterLink(application));
|
|
637
|
+
}
|
|
638
|
+
if (this.hasDashboardAccess()) {
|
|
639
|
+
return this.toEntry(this.dashboardLabel(), this.dashboardEntryRouterLink(null));
|
|
640
|
+
}
|
|
641
|
+
if (!this.showApplyAction()) {
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
644
|
+
return this.toEntry('Apply as agent', this.applyRouterLink());
|
|
645
|
+
}, ...(ngDevMode ? [{ debugName: "entry" }] : []));
|
|
646
|
+
applicationLabel(status) {
|
|
647
|
+
switch (status) {
|
|
648
|
+
case 'APPROVED':
|
|
649
|
+
return this.dashboardLabel();
|
|
650
|
+
case 'FAILED':
|
|
651
|
+
return 'Agent application result';
|
|
652
|
+
case 'NEED_MORE_INFO':
|
|
653
|
+
return 'Agent application updates';
|
|
654
|
+
case 'SUBMITTED':
|
|
655
|
+
case 'IN_REVIEW':
|
|
656
|
+
return 'Agent application review';
|
|
657
|
+
default:
|
|
658
|
+
return 'Continue agent application';
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
applicationRouterLink(application) {
|
|
662
|
+
return this.composeRouterLink(this.applicationsRouterLink(), application.id);
|
|
663
|
+
}
|
|
664
|
+
dashboardEntryRouterLink(application) {
|
|
665
|
+
const dashboardRouterLink = this.dashboardRouterLink();
|
|
666
|
+
if (dashboardRouterLink) {
|
|
667
|
+
return dashboardRouterLink;
|
|
668
|
+
}
|
|
669
|
+
if (application) {
|
|
670
|
+
return this.applicationRouterLink(application);
|
|
671
|
+
}
|
|
672
|
+
return this.applicationsRouterLink();
|
|
673
|
+
}
|
|
674
|
+
toEntry(label, link) {
|
|
675
|
+
if (this.isExternalLink(link)) {
|
|
676
|
+
return {
|
|
677
|
+
label,
|
|
678
|
+
href: link,
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
return {
|
|
682
|
+
label,
|
|
683
|
+
routerLink: link,
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
isExternalLink(link) {
|
|
687
|
+
return typeof link === 'string' && /^(https?:)?\/\//.test(link);
|
|
688
|
+
}
|
|
689
|
+
toTimestamp(application) {
|
|
690
|
+
return Date.parse(application.updatedAt ?? application.createdAt ?? '') || 0;
|
|
691
|
+
}
|
|
692
|
+
composeRouterLink(base, ...segments) {
|
|
693
|
+
const prefix = Array.isArray(base) ? [...base] : [base];
|
|
694
|
+
return [...prefix, ...segments];
|
|
695
|
+
}
|
|
696
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: AgentAccessMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
697
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: AgentAccessMenu, isStandalone: true, selector: "rolatech-agent-access-menu", inputs: { applicationCode: { classPropertyName: "applicationCode", publicName: "applicationCode", isSignal: true, isRequired: false, transformFunction: null }, applyRouterLink: { classPropertyName: "applyRouterLink", publicName: "applyRouterLink", isSignal: true, isRequired: false, transformFunction: null }, applicationsRouterLink: { classPropertyName: "applicationsRouterLink", publicName: "applicationsRouterLink", isSignal: true, isRequired: false, transformFunction: null }, dashboardRouterLink: { classPropertyName: "dashboardRouterLink", publicName: "dashboardRouterLink", isSignal: true, isRequired: false, transformFunction: null }, showApplyAction: { classPropertyName: "showApplyAction", publicName: "showApplyAction", isSignal: true, isRequired: false, transformFunction: null }, dashboardLabel: { classPropertyName: "dashboardLabel", publicName: "dashboardLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
698
|
+
@if (entry(); as item) {
|
|
699
|
+
@if (item.href) {
|
|
700
|
+
<a mat-menu-item [href]="item.href" class="px-6 flex items-center cursor-pointer">
|
|
701
|
+
@if (item.icon) {
|
|
702
|
+
<mat-icon> {{ item.icon }} </mat-icon>
|
|
703
|
+
}
|
|
704
|
+
<span class="flex items-center pl-2">
|
|
705
|
+
<span> {{ item.label }} </span>
|
|
706
|
+
</span>
|
|
707
|
+
</a>
|
|
708
|
+
} @else {
|
|
709
|
+
<a mat-menu-item [routerLink]="item.routerLink" class="px-6 flex items-center cursor-pointer">
|
|
710
|
+
@if (item.icon) {
|
|
711
|
+
<mat-icon> {{ item.icon }} </mat-icon>
|
|
712
|
+
}
|
|
713
|
+
<span class="flex items-center pl-2">
|
|
714
|
+
<span> {{ item.label }} </span>
|
|
715
|
+
</span>
|
|
716
|
+
</a>
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
720
|
+
}
|
|
721
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: AgentAccessMenu, decorators: [{
|
|
722
|
+
type: Component,
|
|
723
|
+
args: [{
|
|
724
|
+
selector: 'rolatech-agent-access-menu',
|
|
725
|
+
imports: [MatMenuModule, MatIconModule, RouterLink],
|
|
726
|
+
template: `
|
|
727
|
+
@if (entry(); as item) {
|
|
728
|
+
@if (item.href) {
|
|
729
|
+
<a mat-menu-item [href]="item.href" class="px-6 flex items-center cursor-pointer">
|
|
730
|
+
@if (item.icon) {
|
|
731
|
+
<mat-icon> {{ item.icon }} </mat-icon>
|
|
732
|
+
}
|
|
733
|
+
<span class="flex items-center pl-2">
|
|
734
|
+
<span> {{ item.label }} </span>
|
|
735
|
+
</span>
|
|
736
|
+
</a>
|
|
737
|
+
} @else {
|
|
738
|
+
<a mat-menu-item [routerLink]="item.routerLink" class="px-6 flex items-center cursor-pointer">
|
|
739
|
+
@if (item.icon) {
|
|
740
|
+
<mat-icon> {{ item.icon }} </mat-icon>
|
|
741
|
+
}
|
|
742
|
+
<span class="flex items-center pl-2">
|
|
743
|
+
<span> {{ item.label }} </span>
|
|
744
|
+
</span>
|
|
745
|
+
</a>
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
`,
|
|
749
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
750
|
+
}]
|
|
751
|
+
}], propDecorators: { applicationCode: [{ type: i0.Input, args: [{ isSignal: true, alias: "applicationCode", required: false }] }], applyRouterLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "applyRouterLink", required: false }] }], applicationsRouterLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "applicationsRouterLink", required: false }] }], dashboardRouterLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "dashboardRouterLink", required: false }] }], showApplyAction: [{ type: i0.Input, args: [{ isSignal: true, alias: "showApplyAction", required: false }] }], dashboardLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "dashboardLabel", required: false }] }] } });
|
|
752
|
+
|
|
587
753
|
class FaceidDetectDialogComponent {
|
|
588
754
|
dialogRef = inject(MatDialogRef);
|
|
589
755
|
environment = inject(APP_CONFIG);
|
|
@@ -671,7 +837,7 @@ class AddressComponent {
|
|
|
671
837
|
this.output.emit(this.address());
|
|
672
838
|
}
|
|
673
839
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: AddressComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
674
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: AddressComponent, isStandalone: true, selector: "rolatech-address", inputs: { address: { classPropertyName: "address", publicName: "address", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { address: "addressChange", output: "output" }, ngImport: i0, template: "<div>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Name </mat-label>\n <input matInput [(ngModel)]=\"address().name\" />\n </mat-form-field>\n @if (address().type === addressType.BILLING) {\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Email </mat-label>\n <input matInput [(ngModel)]=\"address().email\" />\n </mat-form-field>\n }\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Phone </mat-label>\n <input matInput [(ngModel)]=\"address().phone\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> province </mat-label>\n <input matInput [(ngModel)]=\"address().province\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> City </mat-label>\n <input matInput [(ngModel)]=\"address().city\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> District </mat-label>\n <input matInput [(ngModel)]=\"address().district\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Street </mat-label>\n <input matInput [(ngModel)]=\"address().street\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Detail </mat-label>\n <input matInput [(ngModel)]=\"address().detail\" />\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "directive", type: i1$
|
|
840
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: AddressComponent, isStandalone: true, selector: "rolatech-address", inputs: { address: { classPropertyName: "address", publicName: "address", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { address: "addressChange", output: "output" }, ngImport: i0, template: "<div>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Name </mat-label>\n <input matInput [(ngModel)]=\"address().name\" />\n </mat-form-field>\n @if (address().type === addressType.BILLING) {\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Email </mat-label>\n <input matInput [(ngModel)]=\"address().email\" />\n </mat-form-field>\n }\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Phone </mat-label>\n <input matInput [(ngModel)]=\"address().phone\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> province </mat-label>\n <input matInput [(ngModel)]=\"address().province\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> City </mat-label>\n <input matInput [(ngModel)]=\"address().city\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> District </mat-label>\n <input matInput [(ngModel)]=\"address().district\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Street </mat-label>\n <input matInput [(ngModel)]=\"address().street\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n> Detail </mat-label>\n <input matInput [(ngModel)]=\"address().detail\" />\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "directive", type: i1$2.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$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.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"] }] });
|
|
675
841
|
}
|
|
676
842
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: AddressComponent, decorators: [{
|
|
677
843
|
type: Component,
|
|
@@ -693,8 +859,7 @@ const AuthGuard = (route, state) => {
|
|
|
693
859
|
const routeRoles = route.data['roles'];
|
|
694
860
|
const passRoled = routeRoles ? roles?.some((r) => routeRoles.indexOf(r) >= 0) : true;
|
|
695
861
|
if (authenticated && !passRoled) {
|
|
696
|
-
router.
|
|
697
|
-
return false;
|
|
862
|
+
return createForbiddenUrlTree(router, state.url, 'role');
|
|
698
863
|
}
|
|
699
864
|
if (authenticated) {
|
|
700
865
|
if (isSignInPage) {
|
|
@@ -712,6 +877,7 @@ const AuthGuard = (route, state) => {
|
|
|
712
877
|
return false;
|
|
713
878
|
}
|
|
714
879
|
// 3. Otherwise, if the user is already where they need to be, allow navigation
|
|
880
|
+
rememberAllowedUrl(state.url);
|
|
715
881
|
return true;
|
|
716
882
|
}
|
|
717
883
|
else {
|
|
@@ -799,21 +965,34 @@ function matchesRouteAccessPolicy(authStore, authContextStore, policy) {
|
|
|
799
965
|
}
|
|
800
966
|
|
|
801
967
|
function accessGuard(policy, fallbackUrl = '/forbidden') {
|
|
802
|
-
return () => {
|
|
968
|
+
return (_route, state) => {
|
|
803
969
|
const router = inject(Router);
|
|
804
970
|
const authService = inject(AuthService);
|
|
805
971
|
const authStore = inject(AuthStore);
|
|
806
972
|
const authContextStore = inject(AuthContextStore);
|
|
807
|
-
return authService.ensureLoaded().pipe(map(() =>
|
|
973
|
+
return authService.ensureLoaded().pipe(map(() => {
|
|
974
|
+
if (matchesRouteAccessPolicy(authStore, authContextStore, policy)) {
|
|
975
|
+
rememberAllowedUrl(state.url);
|
|
976
|
+
return true;
|
|
977
|
+
}
|
|
978
|
+
return fallbackUrl === '/forbidden' ? createForbiddenUrlTree(router, state.url, 'policy') : router.parseUrl(fallbackUrl);
|
|
979
|
+
}));
|
|
808
980
|
};
|
|
809
981
|
}
|
|
810
982
|
function accessMatchGuard(policy, fallbackUrl = '/forbidden') {
|
|
811
|
-
return () => {
|
|
983
|
+
return (_route, segments) => {
|
|
812
984
|
const router = inject(Router);
|
|
813
985
|
const authService = inject(AuthService);
|
|
814
986
|
const authStore = inject(AuthStore);
|
|
815
987
|
const authContextStore = inject(AuthContextStore);
|
|
816
|
-
|
|
988
|
+
const attemptedUrl = segments.length ? `/${segments.map((segment) => segment.path).join('/')}` : router.url;
|
|
989
|
+
return authService.ensureLoaded().pipe(map(() => {
|
|
990
|
+
if (matchesRouteAccessPolicy(authStore, authContextStore, policy)) {
|
|
991
|
+
rememberAllowedUrl(router.url);
|
|
992
|
+
return true;
|
|
993
|
+
}
|
|
994
|
+
return fallbackUrl === '/forbidden' ? createForbiddenUrlTree(router, attemptedUrl, 'policy') : router.parseUrl(fallbackUrl);
|
|
995
|
+
}));
|
|
817
996
|
};
|
|
818
997
|
}
|
|
819
998
|
|
|
@@ -848,25 +1027,19 @@ function landingRedirectGuard(targets = {}) {
|
|
|
848
1027
|
|
|
849
1028
|
const RoleGuard = (route, state) => {
|
|
850
1029
|
const authService = inject(AuthService);
|
|
851
|
-
const environment = inject(APP_CONFIG);
|
|
852
1030
|
const platformId = inject(PLATFORM_ID);
|
|
853
|
-
const snackBar = inject(MatSnackBar);
|
|
854
1031
|
const router = inject(Router);
|
|
855
|
-
const isContinue = state.url.includes('continue');
|
|
856
|
-
const continueUrl = route.queryParams['continue'];
|
|
857
1032
|
if (!isPlatformBrowser(platformId)) {
|
|
858
1033
|
return of(false);
|
|
859
1034
|
}
|
|
860
1035
|
const routeRoles = route.data['roles'];
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
if (!authed) {
|
|
867
|
-
router.navigate(['/forbidden']);
|
|
1036
|
+
return authService.ensureLoaded().pipe(map$1(({ roles }) => {
|
|
1037
|
+
const authed = routeRoles ? (roles ?? []).some((role) => routeRoles.indexOf(role) >= 0) : true;
|
|
1038
|
+
if (authed) {
|
|
1039
|
+
rememberAllowedUrl(state.url);
|
|
1040
|
+
return true;
|
|
868
1041
|
}
|
|
869
|
-
return
|
|
1042
|
+
return createForbiddenUrlTree(router, state.url, 'role');
|
|
870
1043
|
}));
|
|
871
1044
|
};
|
|
872
1045
|
|
|
@@ -935,15 +1108,15 @@ class AuthUserService {
|
|
|
935
1108
|
}
|
|
936
1109
|
verifyEmailByToken(token) {
|
|
937
1110
|
return this.http
|
|
938
|
-
.
|
|
1111
|
+
.get(`${this.environment.baseUrl}/auth/users/verification/email`, {
|
|
939
1112
|
params: { token },
|
|
940
1113
|
withCredentials: true,
|
|
941
1114
|
})
|
|
942
1115
|
.pipe(map$1((response) => {
|
|
943
|
-
if (typeof response === '
|
|
944
|
-
return response;
|
|
1116
|
+
if (response && typeof response === 'object' && 'data' in response) {
|
|
1117
|
+
return response.data;
|
|
945
1118
|
}
|
|
946
|
-
return
|
|
1119
|
+
return response;
|
|
947
1120
|
}));
|
|
948
1121
|
}
|
|
949
1122
|
findByUsername(username) {
|
|
@@ -1150,7 +1323,7 @@ class ErrorInterceptor {
|
|
|
1150
1323
|
isRefreshingToken = false;
|
|
1151
1324
|
authService = inject(AuthService);
|
|
1152
1325
|
intercept(request, next) {
|
|
1153
|
-
return next.handle(request).pipe(catchError((error) => {
|
|
1326
|
+
return next.handle(request).pipe(catchError$1((error) => {
|
|
1154
1327
|
if (error instanceof HttpErrorResponse) {
|
|
1155
1328
|
switch (error.status) {
|
|
1156
1329
|
case 400:
|
|
@@ -1216,7 +1389,7 @@ class AuthInterceptor {
|
|
|
1216
1389
|
// // window.location.href = res.headers.get('Location') as string;
|
|
1217
1390
|
// }
|
|
1218
1391
|
return res;
|
|
1219
|
-
}), catchError
|
|
1392
|
+
}), catchError((error) => {
|
|
1220
1393
|
if (isPlatformBrowser(this.platformId)) {
|
|
1221
1394
|
if (error.url?.includes('auth/introspect')) {
|
|
1222
1395
|
// if (window.location.origin !== `${this.environment.accountsUrl}`) {
|
|
@@ -1284,7 +1457,7 @@ function provideAngularAuth(config) {
|
|
|
1284
1457
|
if (config?.autoLoadSession === false || !isPlatformBrowser(platformId)) {
|
|
1285
1458
|
return Promise.resolve();
|
|
1286
1459
|
}
|
|
1287
|
-
return firstValueFrom(authService.ensureLoaded().pipe(catchError
|
|
1460
|
+
return firstValueFrom(authService.ensureLoaded().pipe(catchError(() => of(null))));
|
|
1288
1461
|
};
|
|
1289
1462
|
},
|
|
1290
1463
|
},
|
|
@@ -1417,5 +1590,5 @@ function provideRoleAwareConsoleShell(config, ...features) {
|
|
|
1417
1590
|
* Generated bundle index. Do not edit.
|
|
1418
1591
|
*/
|
|
1419
1592
|
|
|
1420
|
-
export { APPLICATION_ADMIN_ROLES, APPLICATION_OWNER_ROLES, AUTH_METHODS, AddressComponent, AddressType,
|
|
1593
|
+
export { APPLICATION_ADMIN_ROLES, APPLICATION_OWNER_ROLES, AUTH_METHODS, AddressComponent, AddressType, AgentAccessMenu, AppNavigationService, AuthAgentService, AuthContextStore, AuthDialogGuard, AuthGuard, AuthInterceptor, AuthMethod, AuthService, AuthStore, AuthUserService, ErrorInterceptor, FaceidDetectDialogComponent, ForbiddenComponent, LocalStorageService, ORGANIZATION_ADMIN_ROLES, ORGANIZATION_MEMBER_ROLES, ORGANIZATION_OWNER_ROLES, ORGANIZATION_STAFF_ROLES, PLATFORM_ADMIN_ROLES, PermissionGuard, ROLE_NAVIGATION_LINKS, ROLE_SHELL_CHILD_ROUTES, RoleGuard, UnauthorizedComponent, UserStatus, accessGuard, accessMatchGuard, authRoutes, landingRedirectGuard, provideAngularAuth, provideRoleAwareConsoleShell, provideRoleAwareFeature, provideRoleAwareShell, provideRoleAwareShellRouter, provideRoleNavigation, provideRoleShellRoutes };
|
|
1421
1594
|
//# sourceMappingURL=rolatech-angular-auth.mjs.map
|