Skip to content
Snippets Groups Projects
Commit 18bcad83 authored by Fadil's avatar Fadil Committed by Delphine
Browse files

[FIX TRTL-237] Fix application service for identity apps access

[US TRTL-235] Search apps on change in menu component


[US TRTL-235] Code review
parent c7513173
No related branches found
No related tags found
1 merge request!1Feature/design/1
......@@ -110,4 +110,12 @@ describe('ApplicationService', () => {
fail
);
});
it('should return a map', () => {
const appMap = appService.getAppsGroupByCategories();
appService.applications = [];
expect(appMap).toBeTruthy();
expect(appMap.keys.length).toBeDefined();
expect(appMap.values).toBeDefined();
});
});
......@@ -37,13 +37,11 @@
import { HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { BehaviorSubject, Observable, of } from 'rxjs';
import { Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { ApplicationApiService } from './api/application-api.service';
import { Application } from './models/application/application.interface';
import { Category } from './models/application/category.interface';
import { StartupService } from './startup.service';
@Injectable({
providedIn: 'root'
......@@ -65,19 +63,13 @@ export class ApplicationService {
*/
private appMap: Map<Category, Application[]>;
private appMap$ = new BehaviorSubject(this.appMap);
private unsub: Subscription;
private categories = [
{ position: 0, identifier: 'users', name: 'Utilisateur' },
{ position: 1, identifier: 'administrators', name: 'Management' },
{ position: 2, identifier: 'settings', name: 'Paramétrage' },
] as Category[];
constructor(private applicationApi: ApplicationApiService, private router: Router, private startupService: StartupService) {
this.unsub = this.list().subscribe();
}
constructor(private applicationApi: ApplicationApiService) { }
/**
* Get Applications list for an user and save it in a property.
......@@ -97,29 +89,18 @@ export class ApplicationService {
/**
* Get Applications list grouped by categories in a hashMap.
*/
public getAppsGroupByCategories(): Observable<Map<Category, Application[]>> {
public getAppsGroupByCategories(): Map<Category, Application[]> {
if (!this.appMap) {
this.appMap = new Map<Category, Application[]>();
if (!this.applications) {
this.unsub.unsubscribe();
this.list().subscribe((apps) => {
this.fillCategoriesWithApps(this.categories, apps);
this.appMap$.next(this.appMap);
});
} else {
this.fillCategoriesWithApps(this.categories, this.applications);
this.appMap$.next(this.appMap);
}
}
return this.appMap$;
return this.appMap;
}
public openApplication(app: Application): void {
const uiUrl = this.startupService.getConfigStringValue('UI_URL');
public openApplication(app: Application, router: Router, uiUrl: string): void {
// If called app is in the same server...
if (app.url.includes(uiUrl)) {
this.router.navigate([app.url.replace(uiUrl, '')]);
router.navigate([app.url.replace(uiUrl, '')]);
} else {
window.location.href = app.url;
}
......@@ -132,8 +113,10 @@ export class ApplicationService {
}
private getSortedAppsOfCategory(category: Category, applications: Application[]): Application[] {
const apps = applications.filter(application => application.category === category.identifier) as Application[];
return this.sortApplications(apps);
if (applications) {
const apps = applications.filter(application => application.category === category.identifier) as Application[];
return this.sortApplications(apps);
}
}
private sortApplications(applications: Application[]): Application[] {
......
......@@ -6,7 +6,8 @@
<vitamui-common-search-bar
class="col-6 p-0 mr-auto"
name="category-search"
(search)="onSearch($event)"
(searchChanged)="onSearch($event)"
(clear)="resetSearch()"
placeholder="Saisir le nom de l'application"
i18n-placeholder="@@categorySearchPlaceholder"
></vitamui-common-search-bar>
......@@ -18,7 +19,7 @@
<div class="pt-4">
<ng-container *ngIf="filteredApplications; else displayMats">
<span class="px-3" i18n="menuComponent@@results">Résultat : </span>
<span class="px-3" i18n="menuComponent@@results">Résultats : </span>
<hr class="mb-0">
<mat-selection-list id="searchResults"
class="py-3 overflow"
......
import { animate, keyframes, query, stagger, state, style, transition, trigger } from '@angular/animations';
import { AfterViewInit, ChangeDetectorRef, Component, HostListener, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core';
import { AfterViewInit, ChangeDetectorRef, Component, HostListener, OnInit, QueryList, ViewChildren } from '@angular/core';
import { MatSelectionList, MatTabChangeEvent } from '@angular/material';
import { Subscription } from 'rxjs';
import { Router } from '@angular/router';
import { ApplicationService } from '../../../application.service';
import { Category } from '../../../models';
import { Application } from '../../../models/application/application.interface';
import { StartupService } from './../../../startup.service';
import { MenuOverlayRef } from './menu-overlay-ref';
@Component({
......@@ -48,7 +49,7 @@ import { MenuOverlayRef } from './menu-overlay-ref';
])
]
})
export class MenuComponent implements OnInit, AfterViewInit, OnDestroy {
export class MenuComponent implements OnInit, AfterViewInit {
public state = '';
......@@ -62,7 +63,9 @@ export class MenuComponent implements OnInit, AfterViewInit, OnDestroy {
public selectedCategory: Category;
private unSub: Subscription;
private firstResult: any;
private firstResultFocused = false;
@ViewChildren(MatSelectionList) selectedList: QueryList<MatSelectionList>;
@HostListener('document:keydown', ['$event'])
......@@ -75,52 +78,56 @@ export class MenuComponent implements OnInit, AfterViewInit, OnDestroy {
if (this.tabSelectedIndex > 0) {
this.tabSelectedIndex--;
}
} else if (event.key === 'ArrowDown') {
if (this.firstResult && !this.firstResultFocused) {
this.firstResult.focus();
this.firstResultFocused = true;
}
}
}
constructor(
private dialogRef: MenuOverlayRef,
private applicationService: ApplicationService,
private cdrRef: ChangeDetectorRef) { }
private cdrRef: ChangeDetectorRef,
private router: Router,
private startupService: StartupService) { }
ngAfterViewInit(): void {
this.changeTabFocus();
}
ngOnInit() {
this.unSub = this.applicationService.getAppsGroupByCategories()
.subscribe((map: Map<Category, Application[]>) => {
this.appMap = map;
});
this.appMap = this.applicationService.getAppsGroupByCategories();
this.dialogRef.overlay.backdropClick().subscribe(() => this.onClose());
}
ngOnDestroy() {
this.unSub.unsubscribe();
}
public onSearch(value: string): void {
if (value) {
this.criteria = value;
this.filteredApplications = this.appMap.get(Array.from(this.appMap.keys())[this.tabSelectedIndex])
.filter((application: Application) =>
application.name.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase()
.includes(value.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase()));
this.firstResultFocused = false;
const flattenApps = [].concat.apply([], Array.from(this.appMap.values()));
this.filteredApplications = flattenApps.filter((application: Application) => {
return application.name.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase()
.includes(value.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase());
});
this.cdrRef.detectChanges();
if (this.filteredApplications.length > 0) {
const firstChildElem = document.getElementById('searchResults').firstElementChild as any;
if (firstChildElem) {
firstChildElem.focus();
}
this.firstResult = document.getElementById('searchResults').firstElementChild as any;
}
} else {
this.filteredApplications = null;
this.criteria = '';
this.changeTabFocus();
this.resetSearch();
}
}
public resetSearch(): void {
this.filteredApplications = null;
this.criteria = '';
this.changeTabFocus();
}
public onClose(): void {
this.state = 'close';
setTimeout(() => this.dialogRef.close(), 500);
......@@ -141,6 +148,6 @@ export class MenuComponent implements OnInit, AfterViewInit, OnDestroy {
public openApplication(app: Application) {
this.onClose();
this.applicationService.openApplication(app);
this.applicationService.openApplication(app, this.router, this.startupService.getConfigStringValue('UI_URL'));
}
}
<div class="search">
<input [(ngModel)]="searchValue" [name]="name" type="text" [placeholder]="placeholder">
<input [(ngModel)]="searchValue" (ngModelChange)="searchChanged.emit(searchValue)" [name]="name" type="text" [placeholder]="placeholder">
<button *ngIf="searchValue" type="button" class="btn-reset" (click)="reset()"><i class="material-icons">clear</i></button>
<button type="button" class="btn-circle primary btn-search" (click)="onSearch()"><i class="material-icons">search</i></button>
</div>
......@@ -44,16 +44,20 @@ import { Component, EventEmitter, HostListener, Input, OnInit, Output } from '@a
export class SearchBarComponent implements OnInit {
@Input() placeholder: string;
@Input() name: string;
@Output() search = new EventEmitter<string>();
@Output() searchChanged = new EventEmitter<string>();
@Output() clear = new EventEmitter<string>();
searchValue: string;
constructor() { }
ngOnInit() {
}
ngOnInit() { }
@HostListener('keydown.enter')
onSearch() {
......@@ -63,6 +67,7 @@ export class SearchBarComponent implements OnInit {
@HostListener('keydown.escape')
reset() {
this.searchValue = null;
this.clear.emit();
this.search.emit(this.searchValue);
}
......
......@@ -36,6 +36,7 @@
*/
import { Observable, Subject } from 'rxjs';
import { tap } from 'rxjs/operators';
import { ApplicationService } from './application.service';
import { Inject, Injectable } from '@angular/core';
......@@ -72,6 +73,7 @@ export class StartupService {
private securityApi: SecurityApiService,
private applicationApi: ApplicationApiService,
private themeService: ThemeService,
private applicationService: ApplicationService,
@Inject(WINDOW_LOCATION) private location: any
) { }
......@@ -100,7 +102,8 @@ export class StartupService {
}
this.themeService.overrideTheme(customerColorMap);
});
})
.then(() => this.applicationService.list().toPromise());
}
setTenantIdentifier(tenantIdentifier?: string) {
......
......@@ -34,9 +34,9 @@
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C license and that you accept its terms.
*/
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { Subscription } from 'rxjs';
import { Router } from '@angular/router';
import { StartupService } from 'ui-frontend-common';
import { Application, ApplicationService, Category } from 'ui-frontend-common';
......@@ -45,7 +45,7 @@ import { Application, ApplicationService, Category } from 'ui-frontend-common';
templateUrl: './portal.component.html',
styleUrls: ['./portal.component.scss']
})
export class PortalComponent implements OnInit, OnDestroy {
export class PortalComponent implements OnInit {
public applications: Map<Category, Application[]>;
......@@ -55,20 +55,14 @@ export class PortalComponent implements OnInit, OnDestroy {
public customerLogoUrl: string;
private sub: Subscription;
constructor(
private applicationService: ApplicationService,
private startupService: StartupService,
private domSanitizer: DomSanitizer) { }
private domSanitizer: DomSanitizer,
private router: Router) { }
ngOnInit() {
this.sub = this.applicationService.getAppsGroupByCategories()
.subscribe((map: Map<Category, Application[]>) => {
this.applications = map;
});
this.applications = this.applicationService.getAppsGroupByCategories();
this.welcomeTitle = this.startupService.getWelcomeTitle();
this.welcomeMessage = this.startupService.getWelcomeMessage();
......@@ -85,11 +79,7 @@ export class PortalComponent implements OnInit, OnDestroy {
}
}
ngOnDestroy() {
this.sub.unsubscribe();
}
public openApplication(app: Application): void {
this.applicationService.openApplication(app);
this.applicationService.openApplication(app, this.router, this.startupService.getConfigStringValue('UI_URL'));
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment