Merge branch 'VM/RED-10301' into 'unprotected'
RED-10301 - Use RM/DM UI depending on application type of tenant See merge request fforesight/shared-ui-libraries/common-ui!14
This commit is contained in:
commit
b929f1d136
@ -18,9 +18,7 @@ export interface QueryParam {
|
||||
*/
|
||||
export abstract class GenericService<I> {
|
||||
protected readonly _http = inject(HttpClient);
|
||||
protected readonly _lastCheckedForChanges = new Map<string, string>([
|
||||
[ROOT_CHANGES_KEY, new Date(Date.now()).toISOString()],
|
||||
]);
|
||||
protected readonly _lastCheckedForChanges = new Map<string, string>([[ROOT_CHANGES_KEY, new Date(Date.now()).toISOString()]]);
|
||||
protected abstract readonly _defaultModelPath: string;
|
||||
protected readonly _serviceName: string = 'redaction-gateway-v1';
|
||||
|
||||
@ -40,7 +38,7 @@ export abstract class GenericService<I> {
|
||||
headers: HeadersConfiguration.getHeaders({ contentType: false }),
|
||||
observe: 'body',
|
||||
params: this._queryParams(queryParams),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
getFor<R = I[]>(entityId: string, queryParams?: List<QueryParam>): Observable<R> {
|
||||
|
||||
@ -3,6 +3,8 @@ import { Title } from '@angular/platform-browser';
|
||||
import { CacheApiService } from '../caching/cache-api.service';
|
||||
import { wipeAllCaches } from '../caching/cache-utils';
|
||||
import { IqserAppConfig } from '../utils/iqser-app-config';
|
||||
import { THEME_DIRECTORIES } from '../utils/constants';
|
||||
import { IStoredTenantId } from '../tenants';
|
||||
|
||||
@Injectable()
|
||||
export class IqserConfigService<T extends IqserAppConfig = IqserAppConfig> {
|
||||
@ -23,6 +25,15 @@ export class IqserConfigService<T extends IqserAppConfig = IqserAppConfig> {
|
||||
this._titleService.setTitle(this._values.APP_NAME);
|
||||
}
|
||||
|
||||
updateIsDocumine(tenant: IStoredTenantId): void {
|
||||
const isDocumine = tenant.documine;
|
||||
this._values = {
|
||||
...this._values,
|
||||
IS_DOCUMINE: isDocumine,
|
||||
THEME: !isDocumine ? THEME_DIRECTORIES.REDACT : THEME_DIRECTORIES.SCM,
|
||||
};
|
||||
}
|
||||
|
||||
protected _checkFrontendVersion(): void {
|
||||
this._cacheApiService.getCachedValue('FRONTEND_APP_VERSION').then(async lastVersion => {
|
||||
const version = this._values.FRONTEND_APP_VERSION;
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { NgClass } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'iqser-logo',
|
||||
template: ` <mat-icon [svgIcon]="icon"></mat-icon>`,
|
||||
template: `<mat-icon [svgIcon]="icon()"></mat-icon>`,
|
||||
styles: [
|
||||
`
|
||||
:host {
|
||||
@ -18,8 +19,8 @@ import { MatIconModule } from '@angular/material/icon';
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [MatIconModule],
|
||||
imports: [MatIconModule, NgClass],
|
||||
})
|
||||
export class LogoComponent {
|
||||
@Input({ required: true }) icon!: string;
|
||||
icon = input.required<string>();
|
||||
}
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
import { inject, Injectable, signal } from '@angular/core';
|
||||
import dayjs from 'dayjs';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { Observable } from 'rxjs';
|
||||
import { firstValueFrom, Observable, take } from 'rxjs';
|
||||
import { GenericService } from '../../services';
|
||||
import { List } from '../../utils';
|
||||
import { Tenant, TenantDetails } from '../types';
|
||||
import { APPLICATION_TYPES } from '../../utils/constants';
|
||||
import { toObservable } from '@angular/core/rxjs-interop';
|
||||
import { filter } from 'rxjs/operators';
|
||||
|
||||
export interface IStoredTenantId {
|
||||
readonly tenantId: string;
|
||||
readonly created: string;
|
||||
readonly documine: boolean;
|
||||
}
|
||||
|
||||
export type StoredTenantIds = List<IStoredTenantId>;
|
||||
@ -27,6 +31,8 @@ export class TenantsService extends GenericService<Tenant> {
|
||||
key: localStorage.key.bind(localStorage),
|
||||
};
|
||||
readonly #activeTenantId = signal('');
|
||||
readonly #tenantSet = signal(false);
|
||||
readonly tenantSet$ = toObservable(this.#tenantSet);
|
||||
protected readonly _defaultModelPath = 'tenants';
|
||||
protected override readonly _serviceName: string = 'tenant-user-management';
|
||||
|
||||
@ -34,18 +40,26 @@ export class TenantsService extends GenericService<Tenant> {
|
||||
return this.#activeTenantId();
|
||||
}
|
||||
|
||||
get activeTenant() {
|
||||
return this.getStoredTenants().find(t => t.tenantId === this.activeTenantId);
|
||||
}
|
||||
|
||||
async selectTenant(tenantId: string): Promise<boolean> {
|
||||
this.#mutateStorage(tenantId);
|
||||
this.#setActiveTenantId(tenantId);
|
||||
return true;
|
||||
}
|
||||
|
||||
storeTenant() {
|
||||
async storeTenant() {
|
||||
const storedTenants = this.getStoredTenants();
|
||||
const activeTenantId = this.#activeTenantId();
|
||||
const existing = storedTenants.find(s => s.tenantId === activeTenantId);
|
||||
|
||||
const tenant = await firstValueFrom(this.getActiveTenant());
|
||||
|
||||
if (existing) {
|
||||
this.#logger.info('[TENANTS] Stored tenant exists: ', storedTenants);
|
||||
this.#tenantSet.set(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -54,8 +68,13 @@ export class TenantsService extends GenericService<Tenant> {
|
||||
return;
|
||||
}
|
||||
|
||||
storedTenants.push({ tenantId: activeTenantId, created: new Date().toISOString() });
|
||||
storedTenants.push({
|
||||
tenantId: activeTenantId,
|
||||
created: new Date().toISOString(),
|
||||
documine: tenant.applicationType === APPLICATION_TYPES.DOCUMINE,
|
||||
});
|
||||
this.#storageReference.setItem(STORED_TENANTS_KEY, JSON.stringify(storedTenants));
|
||||
this.#tenantSet.set(true);
|
||||
this.#logger.info('[TENANTS] Stored tenants: ', storedTenants);
|
||||
}
|
||||
|
||||
@ -104,6 +123,13 @@ export class TenantsService extends GenericService<Tenant> {
|
||||
return this._getOne([this.activeTenantId]);
|
||||
}
|
||||
|
||||
waitForSettingTenant(): Observable<boolean> {
|
||||
return this.tenantSet$.pipe(
|
||||
filter(tenantSet => tenantSet),
|
||||
take(1),
|
||||
);
|
||||
}
|
||||
|
||||
#setActiveTenantId(tenantId: string) {
|
||||
this.#logger.info('[TENANTS] Set current tenant id: ', tenantId);
|
||||
this.#activeTenantId.set(tenantId);
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
<div>
|
||||
@for (stored of storedTenants; track stored) {
|
||||
<div (click)="select(stored.tenantId)" class="d-flex pointer mat-elevation-z2 card stored-tenant-card mt-10">
|
||||
<iqser-logo class="card-icon" icon="iqser:logo" mat-card-image></iqser-logo>
|
||||
<iqser-logo class="card-icon" [icon]="tenantIcon(stored)" mat-card-image></iqser-logo>
|
||||
<div class="card-content flex-column">
|
||||
<span class="heading">{{ stored.tenantId }}</span>
|
||||
</div>
|
||||
|
||||
@ -38,6 +38,10 @@ export class TenantSelectComponent {
|
||||
this.#loadStoredTenants();
|
||||
}
|
||||
|
||||
protected tenantIcon(tenant: IStoredTenantId) {
|
||||
return `red:${tenant.documine ? 'documine' : 'redaction'}-logo`;
|
||||
}
|
||||
|
||||
updateTenantSelection() {
|
||||
const tenantId = this.form.controls.tenantId.value;
|
||||
if (!tenantId) {
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { ApplicationType } from '../../utils/constants';
|
||||
|
||||
export type TenantDetails = Record<string, unknown>;
|
||||
|
||||
export interface Tenant<TD extends TenantDetails = TenantDetails> {
|
||||
tenantId: string;
|
||||
displayName: string;
|
||||
applicationType: ApplicationType;
|
||||
details: TD;
|
||||
}
|
||||
|
||||
@ -2,11 +2,13 @@ import { HttpClient } from '@angular/common/http';
|
||||
import { inject } from '@angular/core';
|
||||
import { getConfig } from '../services';
|
||||
import { PruningTranslationLoader } from '../utils';
|
||||
import { TenantsService } from '../tenants';
|
||||
|
||||
export function pruningTranslationLoaderFactory(pathPrefix: string): PruningTranslationLoader {
|
||||
const httpClient = inject(HttpClient);
|
||||
const tenantService = inject(TenantsService);
|
||||
const config = getConfig();
|
||||
const version = config.FRONTEND_APP_VERSION;
|
||||
|
||||
return new PruningTranslationLoader(httpClient, pathPrefix, `.json?version=${version}`);
|
||||
return new PruningTranslationLoader(httpClient, tenantService, pathPrefix, `.json?version=${version}`);
|
||||
}
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { TranslateLoader } from '@ngx-translate/core';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
import { TenantsService } from '../tenants';
|
||||
import { APP_TYPE_PATHS } from '../utils/constants';
|
||||
import { inject } from '@angular/core';
|
||||
import { GET_TENANT_FROM_PATH_FN } from '../utils';
|
||||
|
||||
interface T {
|
||||
[key: string]: string | T;
|
||||
@ -10,12 +14,30 @@ interface T {
|
||||
export class PruningTranslationLoader implements TranslateLoader {
|
||||
constructor(
|
||||
private readonly _http: HttpClient,
|
||||
private readonly _tenantService: TenantsService,
|
||||
private readonly _prefix: string,
|
||||
private readonly _suffix: string,
|
||||
) {}
|
||||
|
||||
getTranslation(lang: string): Observable<T> {
|
||||
return this._http.get(`${this._prefix}${lang}${this._suffix}`).pipe(map(result => this._process(result as T)));
|
||||
const tenant = inject(GET_TENANT_FROM_PATH_FN)();
|
||||
|
||||
if (tenant) {
|
||||
return this._tenantService.waitForSettingTenant().pipe(
|
||||
switchMap(() => {
|
||||
const tenant = this._tenantService.activeTenant;
|
||||
const translationPath = tenant?.documine ? APP_TYPE_PATHS.SCM : APP_TYPE_PATHS.REDACT;
|
||||
|
||||
return this._http
|
||||
.get(`${this._prefix}${translationPath}/${lang}${this._suffix}`)
|
||||
.pipe(map(result => this._process(result as T)));
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return this._http
|
||||
.get(`${this._prefix}${APP_TYPE_PATHS.REDACT}/${lang}${this._suffix}`)
|
||||
.pipe(map(result => this._process(result as T)));
|
||||
}
|
||||
|
||||
private _process(object: T): T {
|
||||
|
||||
@ -51,3 +51,26 @@ export const ICONS = new Set([
|
||||
'visibility',
|
||||
'visibility-off',
|
||||
]);
|
||||
|
||||
export const LANDING_PAGE_THEMES = {
|
||||
REDACT_MANAGER: 'redactmanager',
|
||||
DOCUMINE: 'documine',
|
||||
MIXED: 'mixed',
|
||||
} as const;
|
||||
|
||||
export const THEME_DIRECTORIES = {
|
||||
REDACT: 'redact',
|
||||
SCM: 'scm',
|
||||
} as const;
|
||||
|
||||
export const APP_TYPE_PATHS = {
|
||||
REDACT: 'redact',
|
||||
SCM: 'scm',
|
||||
} as const;
|
||||
|
||||
export const APPLICATION_TYPES = {
|
||||
REDACT_MANAGER: 'RedactManager',
|
||||
DOCUMINE: 'DocuMine',
|
||||
} as const;
|
||||
|
||||
export type ApplicationType = (typeof APPLICATION_TYPES)[keyof typeof APPLICATION_TYPES];
|
||||
|
||||
@ -8,4 +8,5 @@ export interface IqserAppConfig {
|
||||
readonly OAUTH_URL: string;
|
||||
readonly MANUAL_BASE_URL: string;
|
||||
readonly BASE_TRANSLATIONS_DIRECTORY?: string;
|
||||
readonly LANDING_PAGE_THEME: 'redactmanager' | 'documine' | 'mixed';
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user