Skip to content
This repository has been archived by the owner on Sep 15, 2023. It is now read-only.

user profiles table is now shoing on each tree, not only the last #224

Merged
merged 1 commit into from
Jul 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<ng-container
*ngIf="treeControl.isExpanded(node) && getProfiles$(node) | async as profilesDataSource; else profilesLoading">
<ng-container *ngIf="profilesDataSource.data?.length">
<div class="table-container">
<table class="ob-table ob-table-hover ob-table-sm" mat-table [dataSource]="profilesDataSource">
<ng-container matColumnDef="select">
<th mat-header-cell *matHeaderCellDef>
<mat-checkbox #checkAllCb
(change)="$event ? selectedProfilesService.toggleAll(profilesDataSource.data) : null"
[checked]="selectedProfilesService.isAllSelected(profilesDataSource.data)"
[indeterminate]="!checkAllCb.checked && selectedProfilesService.isSomeSelected(profilesDataSource.data)">
</mat-checkbox>
</th>
<td mat-cell *matCellDef="let profile">
<mat-checkbox (click)="$event.stopPropagation()"
(change)="$event ? selectedProfilesService.toggle(profile) : null"
[checked]="selectedProfilesService.isSelected(profile)">
</mat-checkbox>
</td>
</ng-container>
<ng-container matColumnDef="firstname">
<th mat-header-cell
*matHeaderCellDef>{{"reports.a4a6.firstName" | translate}}</th>
<td mat-cell *matCellDef="let element"> {{element.firstname}} </td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell
*matHeaderCellDef>{{"reports.a4a6.surName" | translate}}</th>
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
</ng-container>
<ng-container matColumnDef="userExtId">
<th mat-header-cell *matHeaderCellDef>Ext_ID</th>
<td mat-cell *matCellDef="let element"> {{element.userExtId}} </td>
</ng-container>
<ng-container matColumnDef="email">
<th mat-header-cell
*matHeaderCellDef>{{"reports.a4a6.email" | translate}}</th>
<td mat-cell *matCellDef="let element"> {{element.email}} </td>
</ng-container>

<tr mat-header-row *matHeaderRowDef="TABLE_ROWS; sticky: true"></tr>
<tr mat-row *matRowDef="let profile; columns: TABLE_ROWS;"
(click)="selectedProfilesService.toggle(profile)">
</tr>
</table>
</div>
</ng-container>
</ng-container>
<ng-template #profilesLoading>
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.table-container {
padding-bottom: 0.5em;
max-height: 30em;
overflow: auto;
margin-top: 0.5em;
margin-bottom: 2em;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {ComponentFixture, TestBed} from '@angular/core/testing';

import {ProfilesTableComponent} from './profiles-table.component';
import {ObliqueTestingModule} from "@oblique/oblique";
import {TranslateModule} from "@ngx-translate/core";
import {HttpClientTestingModule} from "@angular/common/http/testing";
import {MatTableModule} from "@angular/material/table";
import {SelectedProfilesService} from "../../selected-profiles.service";
import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA} from "@angular/core";
import {UnitTree} from "../unit-search.component";
import {NestedTreeControl} from "@angular/cdk/tree";

describe('ProfilesTableComponent', () => {
let component: ProfilesTableComponent;
let fixture: ComponentFixture<ProfilesTableComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ObliqueTestingModule, HttpClientTestingModule, TranslateModule.forRoot(), MatTableModule],
declarations: [ProfilesTableComponent],
providers: [
{provide: 'REPORT_HOST', useValue: 'REPORT_HOST'},
{provide: SelectedProfilesService, useValue: new SelectedProfilesService()},
],
schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA],
})
.compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(ProfilesTableComponent);
component = fixture.componentInstance;

component.authority = 'BUV'
component.node = {} as any
component.treeControl = new NestedTreeControl<UnitTree>(_ => [])


fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {Component, Inject, Input} from '@angular/core';
import {NestedTreeControl} from "@angular/cdk/tree";
import {UnitTree} from "../unit-search.component";
import {EiamProfile, SelectedProfilesService} from "../../selected-profiles.service";
import {Observable} from "rxjs";
import {MatTableDataSource} from "@angular/material/table";
import {map} from "rxjs/operators";
import {HttpClient} from "@angular/common/http";

type ProfilesObservableStore = {
[unitId: string]: Observable<MatTableDataSource<EiamProfile>>
}

@Component({
selector: 'ec-profiles-table',
templateUrl: './profiles-table.component.html',
styleUrls: ['./profiles-table.component.scss']
})
export class ProfilesTableComponent {

@Input()
node: UnitTree

@Input()
treeControl: NestedTreeControl<UnitTree>

@Input()
authority: string

profileObservablesStore: ProfilesObservableStore = {}

readonly TABLE_ROWS = [
'select',
'firstname',
'name',
'userExtId',
'email',
]

private readonly PROFILES_URL: string

constructor(public readonly selectedProfilesService: SelectedProfilesService,
private readonly http: HttpClient,
@Inject('REPORT_HOST') private readonly REPORT_HOST: string) {
this.PROFILES_URL = REPORT_HOST + '/api/v2/report/unit/profiles'

}

/** Stores and returns the Observables for fetching Eiam profiles. We store them in order to avoid creating
* new Observables, causing infinite loop of the change detection because of how the async pipe works. */
getProfiles$(unit: UnitTree): Observable<MatTableDataSource<EiamProfile>> {
if (!this.profileObservablesStore[unit.id]) {
this.profileObservablesStore[unit.id] = this.http.post<EiamProfile[]>(this.PROFILES_URL, {
id: unit.id,
authority: this.authority
}).pipe(map(profiles => new MatTableDataSource<EiamProfile>(profiles)))
}
return this.profileObservablesStore[unit.id]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,55 +52,9 @@
</div>
<div class="profiles-list-container">
<ng-container *ngIf="treeControl.isExpanded(node)">
<ng-container
*ngIf="getProfiles$(node) | async as profilesDataSource; else profilesLoading">
<div class="table-container">
<table class="ob-table ob-table-hover ob-table-sm" mat-table [dataSource]="profilesDataSource">
<ng-container matColumnDef="select">
<th mat-header-cell *matHeaderCellDef>
<mat-checkbox #checkAllCb
(change)="$event ? selectedProfilesService.toggleAll(profilesDataSource.data) : null"
[checked]="selectedProfilesService.isAllSelected(profilesDataSource.data)"
[indeterminate]="!checkAllCb.checked && selectedProfilesService.isSomeSelected(profilesDataSource.data)">
</mat-checkbox>
</th>
<td mat-cell *matCellDef="let profile">
<mat-checkbox (click)="$event.stopPropagation()"
(change)="$event ? selectedProfilesService.toggle(profile) : null"
[checked]="selectedProfilesService.isSelected(profile)">
</mat-checkbox>
</td>
</ng-container>
<ng-container matColumnDef="firstname">
<th mat-header-cell
*matHeaderCellDef>{{"reports.a4a6.firstName" | translate}}</th>
<td mat-cell *matCellDef="let element"> {{element.firstname}} </td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell
*matHeaderCellDef>{{"reports.a4a6.surName" | translate}}</th>
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
</ng-container>
<ng-container matColumnDef="userExtId">
<th mat-header-cell *matHeaderCellDef>Ext_ID</th>
<td mat-cell *matCellDef="let element"> {{element.userExtId}} </td>
</ng-container>
<ng-container matColumnDef="email">
<th mat-header-cell
*matHeaderCellDef>{{"reports.a4a6.email" | translate}}</th>
<td mat-cell *matCellDef="let element"> {{element.email}} </td>
</ng-container>

<tr mat-header-row *matHeaderRowDef="TABLE_ROWS; sticky: true"></tr>
<tr mat-row *matRowDef="let profile; columns: TABLE_ROWS;"
(click)="selectedProfilesService.toggle(profile)">
</tr>
</table>
</div>
</ng-container>
<ng-template #profilesLoading>
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
</ng-template>
<ec-profiles-table [authority]="authority" [node]="node"
[treeControl]="treeControl">
</ec-profiles-table>
</ng-container>
</div>
</div>
Expand All @@ -117,6 +71,8 @@
<div [class.unit-tree-invisible]="node.hidden || !treeControl.isExpanded(node)"
role="group">
<ng-container matTreeNodeOutlet></ng-container>
<ec-profiles-table [authority]="authority" [node]="node"
[treeControl]="treeControl"></ec-profiles-table>
</div>
</mat-nested-tree-node>
</mat-tree>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,6 @@
padding-left: 2em;
}

.table-container {
max-height: 30em;
overflow: auto;
margin-top: 0.5em;
margin-bottom: 2em;
}

.profiles-container {
width: 100%;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ describe('UnitSearchComponent', () => {
it('should perform post request if its the first change', () => {
component.ngOnChanges({authority: new SimpleChange('de', 'de', true)})

expect(post).toHaveBeenCalledWith("REPORT_HOST/api/v2/unit/tree", {authority: 'buv', language: 'de'})
expect(post).toHaveBeenCalledWith("REPORT_HOST/api/v2/report/unit/tree", {authority: 'buv', language: 'de'})
});

it('should perform post request if authority has changed', () => {
component.ngOnChanges({authority: new SimpleChange('be', 'buv', false)})

expect(post).toHaveBeenCalledWith("REPORT_HOST/api/v2/unit/tree", {authority: 'buv', language: 'de'})
expect(post).toHaveBeenCalledWith("REPORT_HOST/api/v2/report/unit/tree", {authority: 'buv', language: 'de'})
});

it('should not perform post request if authority is null', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ import {NestedTreeControl} from "@angular/cdk/tree";
import {MatTreeNestedDataSource} from "@angular/material/tree";
import {TranslateService} from "@ngx-translate/core";
import {HttpClient} from "@angular/common/http";
import {Observable} from "rxjs";
import {MatTableDataSource} from "@angular/material/table";
import {map} from "rxjs/operators";
import {EiamProfile, SelectedProfilesService} from "../selected-profiles.service";
import {SelectedProfilesService} from "../selected-profiles.service";
import {FormArray} from "@angular/forms";

export interface UnitTree {
Expand All @@ -17,10 +14,6 @@ export interface UnitTree {
parent?: UnitTree
}

type ProfilesObservableStore = {
[unitId: string]: Observable<MatTableDataSource<EiamProfile>>
}

@Component({
selector: 'ec-unit-search',
templateUrl: './unit-search.component.html',
Expand All @@ -38,25 +31,15 @@ export class UnitSearchComponent implements OnChanges {
treeControl = new NestedTreeControl<UnitTree>(node => node.children);
treeDataSource = new MatTreeNestedDataSource<UnitTree>();
organisationSearchValue = ''
profileObservablesStore: ProfilesObservableStore = {}

readonly TABLE_ROWS = [
'select',
'firstname',
'name',
'userExtId',
'email',
]

private readonly UNIT_TREE_URL: string
private readonly PROFILES_URL: string

constructor(
private readonly translate: TranslateService,
private readonly http: HttpClient,
public readonly selectedProfilesService: SelectedProfilesService,
@Inject('REPORT_HOST') private readonly REPORT_HOST: string) {
this.UNIT_TREE_URL = REPORT_HOST + '/api/v2/report/unit/tree'
this.PROFILES_URL = REPORT_HOST + '/api/v2/report/unit/profiles'
}

ngOnChanges(changes: SimpleChanges) {
Expand All @@ -80,18 +63,6 @@ export class UnitSearchComponent implements OnChanges {

isVisible = (_: number, node: UnitTree) => !!node.children && node.children.length > 0;

/** Stores and returns the Observables for fetching Eiam profiles. We store them in order to avoid creating
* new Observables, causing infinite loop of the change detection because of how the async pipe works. */
getProfiles$(unit: UnitTree): Observable<MatTableDataSource<EiamProfile>> {
if (!this.profileObservablesStore[unit.id]) {
this.profileObservablesStore[unit.id] = this.http.post<EiamProfile[]>(this.PROFILES_URL, {
id: unit.id,
authority: this.authority
}).pipe(map(profiles => new MatTableDataSource<EiamProfile>(profiles)))
}
return this.profileObservablesStore[unit.id]
}

/** Sets `hidden` property on the unit trees based on the search value. */
setHiddenBySearchValue(unitTrees: UnitTree[], first = true): void {
if (!unitTrees) {
Expand Down
47 changes: 25 additions & 22 deletions src/app/report/report.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,30 +55,33 @@ import {IssuerSearchComponent} from './report-parameter/report-a4-a6/issuer-sear
import {MatPaginatorModule} from "@angular/material/paginator";
import {MatSortModule} from "@angular/material/sort";
import {REPORT_ERROR_STATE_MATCHER} from "./errorStateMatcher";
import { ProfilesTableComponent } from './report-parameter/report-a4-a6/unit-search/profiles-table/profiles-table.component';

@NgModule({
declarations: [
ReportComponent,
SelectReportTypeComponent,
ReportParameterComponent,
ReportGenerationComponent,
ReportA2Component,
ReportA7Component,
ReportEndComponent,
DataRoomSelectionFieldsetComponent,
DateFromToFieldsetComponent,
CertTypeSelectionFieldsetComponent,
ReportEndComponent,
UnitSearchComponent,
DataRoomSelectionFieldsetComponent,
DateFromToFieldsetComponent,
CertTypeSelectionFieldsetComponent,
ReportA4A6Component,
UnitSearchComponent,
SelectedProfilesComponent,
FieldWrapperComponent,
IssuerSearchComponent
],
declarations: [
ReportComponent,
SelectReportTypeComponent,
ReportParameterComponent,
ReportGenerationComponent,
ReportA2Component,
ReportA7Component,
ReportEndComponent,
DataRoomSelectionFieldsetComponent,
DateFromToFieldsetComponent,
CertTypeSelectionFieldsetComponent,
ReportEndComponent,
UnitSearchComponent,
DataRoomSelectionFieldsetComponent,
DateFromToFieldsetComponent,
CertTypeSelectionFieldsetComponent,
ReportA4A6Component,
UnitSearchComponent,
SelectedProfilesComponent,
FieldWrapperComponent,
IssuerSearchComponent,
ProfilesTableComponent,
ProfilesTableComponent
],
imports: [
SharedModule,
ObButtonModule,
Expand Down