RED-6713: show previous signed in domains
This commit is contained in:
parent
721d3e3b1a
commit
223fbe688c
@ -1,74 +1,74 @@
|
||||
@use 'common-mixins';
|
||||
|
||||
.top-bar {
|
||||
height: var(--iqser-top-bar-height);
|
||||
width: 100vw;
|
||||
height: var(--iqser-top-bar-height);
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 24px;
|
||||
border-bottom: 1px solid var(--iqser-separator);
|
||||
box-sizing: border-box;
|
||||
background-color: var(--iqser-background);
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: 50px;
|
||||
}
|
||||
|
||||
.menu {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 24px;
|
||||
border-bottom: 1px solid var(--iqser-separator);
|
||||
box-sizing: border-box;
|
||||
background-color: var(--iqser-background);
|
||||
|
||||
&.right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-placeholder {
|
||||
display: flex;
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
margin-right: 8px;
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: 50px;
|
||||
margin-right: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
@include common-mixins.clear-a;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.logo {
|
||||
@include common-mixins.clear-a;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.app-name {
|
||||
font-family: var(--iqser-app-name-font-family);
|
||||
font-size: var(--iqser-app-name-font-size);
|
||||
color: var(--iqser-app-name-color);
|
||||
font-weight: 800;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.menu {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&.right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-placeholder {
|
||||
display: flex;
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
margin-right: 8px;
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: 14px;
|
||||
}
|
||||
}
|
||||
.app-name {
|
||||
font-family: var(--iqser-app-name-font-family);
|
||||
font-size: var(--iqser-app-name-font-size);
|
||||
color: var(--iqser-app-name-color);
|
||||
font-weight: 800;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dev-mode {
|
||||
background-color: var(--iqser-primary);
|
||||
color: var(--iqser-white);
|
||||
font-size: 22px;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
right: 0;
|
||||
height: var(--iqser-top-bar-height);
|
||||
word-break: break-all;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: monospace;
|
||||
width: 24px;
|
||||
font-weight: bold;
|
||||
background-color: var(--iqser-primary);
|
||||
color: var(--iqser-white);
|
||||
font-size: 22px;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
right: 0;
|
||||
height: var(--iqser-top-bar-height);
|
||||
word-break: break-all;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: monospace;
|
||||
width: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@ -85,10 +85,10 @@ export class IqserPermissionsGuard implements CanActivate, CanMatch, CanActivate
|
||||
}
|
||||
|
||||
if (Array.isArray(_redirectTo)) {
|
||||
return this._router.navigate([this._tenantsService.currentTenant, ..._redirectTo]);
|
||||
return this._router.navigate([this._tenantsService.activeTenantId, ..._redirectTo]);
|
||||
}
|
||||
|
||||
return this._router.navigate([`${this._tenantsService.currentTenant}${_redirectTo}`]);
|
||||
return this._router.navigate([`${this._tenantsService.activeTenantId}${_redirectTo}`]);
|
||||
}
|
||||
|
||||
#checkRedirect(permissions: IqserPermissionsData, route: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) {
|
||||
|
||||
@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
|
||||
@Component({
|
||||
selector: 'iqser-logo [icon]',
|
||||
selector: 'iqser-logo',
|
||||
template: ` <mat-icon [svgIcon]="icon"></mat-icon>`,
|
||||
styles: [
|
||||
`
|
||||
@ -21,5 +21,5 @@ import { MatIconModule } from '@angular/material/icon';
|
||||
imports: [MatIconModule],
|
||||
})
|
||||
export class LogoComponent {
|
||||
@Input() icon!: string;
|
||||
@Input({ required: true }) icon!: string;
|
||||
}
|
||||
|
||||
15
src/lib/shared/spacer/spacer.component.scss
Normal file
15
src/lib/shared/spacer/spacer.component.scss
Normal file
@ -0,0 +1,15 @@
|
||||
:host {
|
||||
--height: 20px;
|
||||
|
||||
div {
|
||||
height: #{var(--height)};
|
||||
|
||||
@media screen and (max-width: 1600px) {
|
||||
height: calc(#{var(--height)} * 0.9);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
height: calc(#{var(--height)} * 0.85);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/lib/shared/spacer/spacer.component.ts
Normal file
16
src/lib/shared/spacer/spacer.component.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { Component, HostBinding, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'iqser-spacer [height]',
|
||||
template: ' <div></div> ',
|
||||
styleUrls: ['./spacer.component.scss'],
|
||||
standalone: true,
|
||||
})
|
||||
export class SpacerComponent {
|
||||
@Input({ required: true }) height!: number;
|
||||
|
||||
@HostBinding('style.--height')
|
||||
get heightStyle() {
|
||||
return this.height + 'px';
|
||||
}
|
||||
}
|
||||
@ -4,5 +4,5 @@ export * from './guards/if-not-logged-in.guard';
|
||||
export * from './services';
|
||||
export * from './tenant.pipe';
|
||||
export * from './tenants.module';
|
||||
export * from './tenant-resolve/tenant-resolve.component';
|
||||
export * from './tenant-select/tenant-select.component';
|
||||
export * from './services/keycloak-status.service';
|
||||
|
||||
@ -30,7 +30,7 @@ export class KeycloakStatusService {
|
||||
}
|
||||
|
||||
createLoginUrl() {
|
||||
const currentTenant = this.#tenantsService.currentTenant;
|
||||
const currentTenant = this.#tenantsService.activeTenantId;
|
||||
if (currentTenant && window.location.href.indexOf('/' + currentTenant) > 0) {
|
||||
return window.location.href;
|
||||
}
|
||||
|
||||
@ -8,9 +8,9 @@ export class TenantIdInterceptor implements HttpInterceptor {
|
||||
protected readonly _tenantsService = inject(TenantsService);
|
||||
|
||||
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||
if (this._tenantsService.currentTenant) {
|
||||
if (this._tenantsService.activeTenantId) {
|
||||
const updatedRequest = req.clone({
|
||||
setHeaders: { 'X-TENANT-ID': this._tenantsService.currentTenant },
|
||||
setHeaders: { 'X-TENANT-ID': this._tenantsService.activeTenantId },
|
||||
});
|
||||
|
||||
return next.handle(updatedRequest);
|
||||
|
||||
@ -2,7 +2,7 @@ import { computed, inject, Injectable, signal } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { BASE_HREF } from '../../utils';
|
||||
import { BASE_HREF, List } from '../../utils';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
export interface IBaseTenant {
|
||||
@ -11,6 +11,22 @@ export interface IBaseTenant {
|
||||
readonly guid: string;
|
||||
}
|
||||
|
||||
export interface IStoredTenantId {
|
||||
readonly tenantId: string;
|
||||
readonly email: string;
|
||||
}
|
||||
|
||||
export type StoredTenantIds = List<IStoredTenantId>;
|
||||
|
||||
export interface IStoredTenant {
|
||||
readonly tenant: IBaseTenant;
|
||||
readonly email: string;
|
||||
}
|
||||
|
||||
export type StoredTenants = IStoredTenant[];
|
||||
|
||||
const STORED_TENANTS_KEY = 'red-stored-tenants';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class TenantsService {
|
||||
readonly tenants = signal<IBaseTenant[] | undefined>(undefined);
|
||||
@ -31,8 +47,11 @@ export class TenantsService {
|
||||
key: localStorage.key.bind(localStorage),
|
||||
};
|
||||
readonly #activeTenantId = signal<string>('');
|
||||
readonly activeTenant = computed(() => {
|
||||
return this.tenants()?.find(t => t.tenantId === this.#activeTenantId());
|
||||
});
|
||||
|
||||
get currentTenant() {
|
||||
get activeTenantId() {
|
||||
return this.#activeTenantId();
|
||||
}
|
||||
|
||||
@ -82,11 +101,61 @@ export class TenantsService {
|
||||
}
|
||||
|
||||
this.#mutateStorage(tenantId);
|
||||
this.#setCurrentTenantId(tenantId);
|
||||
this.#setActiveTenantId(tenantId);
|
||||
return true;
|
||||
}
|
||||
|
||||
#setCurrentTenantId(tenantId: string) {
|
||||
storeTenant(email: string) {
|
||||
if (!email) {
|
||||
this.#logger.warn('[TENANTS] Email is null, skip storing');
|
||||
return;
|
||||
}
|
||||
|
||||
const storedTenants = this.getStoredTenants();
|
||||
const activeTenant = this.activeTenant();
|
||||
const existing = storedTenants.find(s => s.email === email && s.tenant.tenantId === activeTenant?.tenantId);
|
||||
if (existing) {
|
||||
this.#logger.info('[TENANTS] Stored tenant exists: ', storedTenants);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!activeTenant) {
|
||||
this.#logger.error('[TENANTS] Active tenant is null when storing tenants');
|
||||
return;
|
||||
}
|
||||
|
||||
storedTenants.push({ tenant: activeTenant, email: email });
|
||||
const serializableTenants = storedTenants.map(s => ({
|
||||
tenantId: s.tenant.tenantId,
|
||||
email: s.email,
|
||||
}));
|
||||
this.#storageReference.setItem(STORED_TENANTS_KEY, JSON.stringify(serializableTenants));
|
||||
this.#logger.info('[TENANTS] Stored tenants: ', storedTenants);
|
||||
}
|
||||
|
||||
getStoredTenants(): StoredTenants {
|
||||
const rawStoredTenants = this.#storageReference.getItem(STORED_TENANTS_KEY);
|
||||
const stored = JSON.parse(rawStoredTenants ?? '[]') as StoredTenantIds;
|
||||
const tenants = this.tenants();
|
||||
if (!tenants) {
|
||||
this.#logger.error('[TENANTS] Tenants data is null when retrieving stored tenants');
|
||||
return [];
|
||||
}
|
||||
|
||||
const trueStored: StoredTenants = [];
|
||||
for (const s of stored) {
|
||||
const tenant = tenants.find(t => t.tenantId === s.tenantId);
|
||||
if (!tenant) {
|
||||
this.#logger.warn(`[TENANTS] Stored tenant with id ${tenant} not found, skipping`);
|
||||
continue;
|
||||
}
|
||||
trueStored.push({ tenant, email: s.email });
|
||||
}
|
||||
|
||||
return trueStored;
|
||||
}
|
||||
|
||||
#setActiveTenantId(tenantId: string) {
|
||||
this.#logger.info('[TENANTS] Set current tenant id: ', tenantId);
|
||||
this.#activeTenantId.set(tenantId);
|
||||
}
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
<div class="tenant-section">
|
||||
<form (submit)="updateTenantSelection()" [formGroup]="form">
|
||||
<div class="heading-l" translate="tenant-resolve.header"></div>
|
||||
|
||||
<div class="iqser-input-group required w-400">
|
||||
<mat-form-field *ngIf="_tenantsService.tenants() as tenants">
|
||||
<mat-select
|
||||
[placeholder]="'tenant-resolve.form.tenant-placeholder' | translate"
|
||||
class="full-width"
|
||||
formControlName="tenantId"
|
||||
>
|
||||
<mat-option *ngFor="let option of tenants" [value]="option.tenantId">
|
||||
{{ option.displayName || option.tenantId }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<iqser-icon-button
|
||||
[label]="'tenant-resolve.actions.save' | translate"
|
||||
[submit]="true"
|
||||
[type]="iconButtonTypes.primary"
|
||||
></iqser-icon-button>
|
||||
</form>
|
||||
</div>
|
||||
@ -1,13 +0,0 @@
|
||||
.tenant-section {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
margin-top: 32px;
|
||||
|
||||
> form > * {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
55
src/lib/tenants/tenant-select/tenant-select.component.html
Normal file
55
src/lib/tenants/tenant-select/tenant-select.component.html
Normal file
@ -0,0 +1,55 @@
|
||||
<div class="tenant-section">
|
||||
<a class="logo">
|
||||
<div class="actions">
|
||||
<iqser-logo icon="red:logo"></iqser-logo>
|
||||
<div class="app-name">{{ titleService.getTitle() }}</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<iqser-spacer [height]="100"></iqser-spacer>
|
||||
|
||||
<div *ngIf="storedTenants.length" class="pb-30 subheading" translate="tenant-resolve.header.sign-in-previous-domain"></div>
|
||||
|
||||
<div *ngIf="storedTenants.length" style="display: flex; flex-direction: column">
|
||||
<div
|
||||
*ngFor="let stored of storedTenants"
|
||||
[routerLink]="[stored.tenant.tenantId]"
|
||||
class="pointer mat-elevation-z2 card stored-tenant-card mt-10"
|
||||
>
|
||||
<iqser-logo class="card-icon" icon="red:logo" mat-card-image></iqser-logo>
|
||||
|
||||
<div class="card-content">
|
||||
<span class="heading">{{ stored.tenant.displayName }}</span>
|
||||
<span>{{ stored.email }}</span>
|
||||
</div>
|
||||
|
||||
<mat-icon class="card-icon upside-down" svgIcon="iqser:expand"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="storedTenants.length === 0" class="heading pb-30" translate="tenant-resolve.header.first-time"></div>
|
||||
|
||||
<ng-container *ngIf="storedTenants.length">
|
||||
<iqser-spacer [height]="100"></iqser-spacer>
|
||||
<div class="pb-30 subheading" translate="tenant-resolve.header.join-another-domain"></div>
|
||||
</ng-container>
|
||||
|
||||
<div class="mat-elevation-z16 card input-card">
|
||||
<span class="heading url">{{ url }}</span>
|
||||
|
||||
<form (submit)="updateTenantSelection()" [formGroup]="form" class="w-full">
|
||||
<div class="iqser-input-group required w-full">
|
||||
<mat-form-field>
|
||||
<input [placeholder]="'tenant-resolve.input-placeholder' | translate" formControlName="tenantId" matInput />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<button class="card-icon upside-down" mat-icon-button type="submit">
|
||||
<mat-icon svgIcon="iqser:expand"></mat-icon>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<iqser-spacer [height]="50"></iqser-spacer>
|
||||
<div [innerHTML]="'tenant-resolve.contact-administrator' | translate"></div>
|
||||
</div>
|
||||
65
src/lib/tenants/tenant-select/tenant-select.component.scss
Normal file
65
src/lib/tenants/tenant-select/tenant-select.component.scss
Normal file
@ -0,0 +1,65 @@
|
||||
.tenant-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.stored-tenant-card {
|
||||
width: 450px;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
margin-right: 20px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.upside-down {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.pb-30 {
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
.input-card {
|
||||
width: 600px;
|
||||
height: 65px;
|
||||
}
|
||||
|
||||
.url {
|
||||
margin-left: 20px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.subheading {
|
||||
font-size: 17px;
|
||||
}
|
||||
@ -4,26 +4,27 @@ import { FormBuilder, Validators } from '@angular/forms';
|
||||
import { TenantsService } from '../services';
|
||||
import { IconButtonTypes } from '../../buttons';
|
||||
import { LoadingService } from '../../loading';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { getConfig } from '../../services';
|
||||
import { BASE_HREF } from '../../utils';
|
||||
|
||||
@Component({
|
||||
templateUrl: './tenant-resolve.component.html',
|
||||
styleUrls: ['./tenant-resolve.component.scss'],
|
||||
templateUrl: './tenant-select.component.html',
|
||||
styleUrls: ['./tenant-select.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class TenantResolveComponent {
|
||||
export class TenantSelectComponent {
|
||||
readonly loadingService = inject(LoadingService);
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly form;
|
||||
readonly form = inject(FormBuilder).group({
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
tenantId: ['', Validators.required],
|
||||
});
|
||||
readonly url = getConfig().OAUTH_URL.replace('https://', '').replace('/auth', '') + inject(BASE_HREF) + '/';
|
||||
protected readonly _tenantsService = inject(TenantsService);
|
||||
protected readonly storedTenants = this._tenantsService.getStoredTenants();
|
||||
protected readonly titleService = inject(Title);
|
||||
readonly #router = inject(Router);
|
||||
readonly #formBuilder = inject(FormBuilder);
|
||||
|
||||
constructor() {
|
||||
this.form = this.#formBuilder.group({
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
tenantId: ['', Validators.required],
|
||||
});
|
||||
}
|
||||
|
||||
updateTenantSelection() {
|
||||
const tenant = this.form.controls.tenantId.value;
|
||||
@ -14,6 +14,6 @@ export class TenantPipe implements PipeTransform {
|
||||
return undefined;
|
||||
}
|
||||
const _value = Array.isArray(value) ? value.join('/') : value;
|
||||
return '/' + this.#tenantsService.currentTenant + _value;
|
||||
return '/' + this.#tenantsService.activeTenantId + _value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,14 +3,20 @@ import { CommonModule } from '@angular/common';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { CircleButtonComponent, IconButtonComponent } from '../buttons';
|
||||
import { TenantResolveComponent } from './tenant-resolve/tenant-resolve.component';
|
||||
import { TenantSelectComponent } from './tenant-select/tenant-select.component';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { TenantIdInterceptor, TenantIdResponseInterceptor, TenantsService } from './services';
|
||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { LogoComponent } from '../shared/logo/logo.component';
|
||||
import { SpacerComponent } from '../shared/spacer/spacer.component';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { RouterLink } from '@angular/router';
|
||||
|
||||
const components = [TenantResolveComponent];
|
||||
const components = [TenantSelectComponent];
|
||||
|
||||
@NgModule({
|
||||
declarations: [...components],
|
||||
@ -23,6 +29,12 @@ const components = [TenantResolveComponent];
|
||||
MatSelectModule,
|
||||
IconButtonComponent,
|
||||
ReactiveFormsModule,
|
||||
MatCardModule,
|
||||
LogoComponent,
|
||||
SpacerComponent,
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
RouterLink,
|
||||
],
|
||||
exports: [...components],
|
||||
})
|
||||
|
||||
@ -35,7 +35,7 @@ export class IqserAuthGuard extends KeycloakAuthGuard {
|
||||
|
||||
const user = await this._userService.loadCurrentUser();
|
||||
if (user?.hasAnyRole && route.routeConfig?.path === 'auth-error') {
|
||||
await this._router.navigate([`/${this._tenantsService.currentTenant}/main`]);
|
||||
await this._router.navigate([`/${this._tenantsService.activeTenantId}/main`]);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ export class IqserRoleGuard implements CanActivate {
|
||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
const currentUser = this._userService.currentUser;
|
||||
if (!currentUser || !currentUser.hasAnyRole) {
|
||||
await this._router.navigate([`/${this._tenantsService.currentTenant}/auth-error`]);
|
||||
await this._router.navigate([`/${this._tenantsService.activeTenantId}/auth-error`]);
|
||||
this._loadingService.stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user