diff --git a/src/app/shared/services/institutions/institutions.service.ts b/src/app/shared/services/institutions/institutions.service.ts index a400de1b96..8896067d30 100644 --- a/src/app/shared/services/institutions/institutions.service.ts +++ b/src/app/shared/services/institutions/institutions.service.ts @@ -13,6 +13,10 @@ export class InstitutionsService { return this.http.get('/api/v1/Institution/GetAll'); } + getAllInstitutionHierarchies(): Observable { + return this.http.get ('/api/v1/InstitutionHierarchy/GetAll'); + } + getAllByInstitutionAndLevel(institutionsId: string, hierarchyLevel: number): Observable { let params = new HttpParams(); params = params.set('institutionId', institutionsId); diff --git a/src/app/shared/store/meta-data.actions.ts b/src/app/shared/store/meta-data.actions.ts index 7e2eb0b5d2..5049cbfa12 100644 --- a/src/app/shared/store/meta-data.actions.ts +++ b/src/app/shared/store/meta-data.actions.ts @@ -33,6 +33,11 @@ export class GetAllInstitutions { constructor() {} } +export class GetAllInstitutionsHierarchy { + static readonly type = '[meta-data] Get All Institutions Hierarchy'; + constructor() {} +} + export class GetAchievementsType { static readonly type = '[meta-data] Get All Achievement Types'; constructor() {} diff --git a/src/app/shared/store/meta-data.state.ts b/src/app/shared/store/meta-data.state.ts index f82e431b21..999fdd2b9a 100644 --- a/src/app/shared/store/meta-data.state.ts +++ b/src/app/shared/store/meta-data.state.ts @@ -29,6 +29,7 @@ import { GetAchievementsType, GetAllByInstitutionAndLevel, GetAllInstitutions, + GetAllInstitutionsHierarchy, GetCodeficatorById, GetCodeficatorSearch, GetFeaturesList, @@ -49,6 +50,7 @@ export interface MetaDataStateModel { featuresList: FeaturesList; institutions: Institution[]; institutionFieldDesc: InstitutionFieldDescription[]; + instituitionsHierarchyAll: InstituitionHierarchy[]; instituitionsHierarchy: InstituitionHierarchy[]; editInstituitionsHierarchy: InstituitionHierarchy[]; codeficatorSearch: Codeficator[]; @@ -67,6 +69,7 @@ export interface MetaDataStateModel { featuresList: { release1: true, release2: true, release3: false }, institutions: null, institutionFieldDesc: null, + instituitionsHierarchyAll: null, instituitionsHierarchy: null, editInstituitionsHierarchy: null, codeficatorSearch: [], @@ -111,7 +114,8 @@ export class MetaDataState { } @Selector() - static institutions(state: MetaDataStateModel): Institution[] { + static institutions( + state: MetaDataStateModel): Institution[] { return state.institutions; } @@ -121,7 +125,16 @@ export class MetaDataState { } @Selector() - static instituitionsHierarchy(state: MetaDataStateModel): InstituitionHierarchy[] { + static instituitionsHierarchyAll( + state: MetaDataStateModel + ): InstituitionHierarchy[] { + return state.instituitionsHierarchyAll; + } + + @Selector() + static instituitionsHierarchy( + state: MetaDataStateModel + ): InstituitionHierarchy[] { return state.instituitionsHierarchy; } @@ -209,6 +222,17 @@ export class MetaDataState { .pipe(tap((institutions: Institution[]) => patchState({ institutions: institutions, isLoading: false }))); } + @Action(GetAllInstitutionsHierarchy) + getAllInstitutionsHierarchy( + { patchState }: StateContext, + {}: GetAllInstitutionsHierarchy + ): Observable { + patchState({ isLoading: true }); + return this.institutionsService + .getAllInstitutionHierarchies() + .pipe(tap((instituitionsHierarchyAll: InstituitionHierarchy[]) => patchState({ instituitionsHierarchyAll: instituitionsHierarchyAll, isLoading: false }))); + } + @Action(GetAchievementsType) getAchievementType({ patchState }: StateContext, {}: GetAchievementsType): Observable { patchState({ isLoading: true }); diff --git a/src/app/shell/admin-tools/data/data.module.ts b/src/app/shell/admin-tools/data/data.module.ts index d49f8d91db..e9312e53b6 100644 --- a/src/app/shell/admin-tools/data/data.module.ts +++ b/src/app/shell/admin-tools/data/data.module.ts @@ -15,6 +15,7 @@ import { MaterialModule } from '../../../shared/modules/material.module'; import { DirectionsComponent } from './directions-wrapper/directions/directions.component'; import { CreateDirectionComponent } from './directions-wrapper/directions/create-direction/create-direction.component'; import { DirectionsWrapperComponent } from './directions-wrapper/directions-wrapper.component'; +import { DirectionsInstitutionHierarchiesListComponent } from './directions-wrapper/directions-institution-hierarchies-list/directions-institution-hierarchies-list.component'; @NgModule({ declarations: [ @@ -28,7 +29,8 @@ import { DirectionsWrapperComponent } from './directions-wrapper/directions-wrap HistoryLogFiltersComponent, DirectionsComponent, CreateDirectionComponent, - DirectionsWrapperComponent + DirectionsWrapperComponent, + DirectionsInstitutionHierarchiesListComponent ], imports: [CommonModule, DataRoutingModule, SharedModule, MaterialModule, FlexLayoutModule], exports: [DataComponent] diff --git a/src/app/shell/admin-tools/data/directions-wrapper/directions-institution-hierarchies-list/directions-institution-hierarchies-list.component.html b/src/app/shell/admin-tools/data/directions-wrapper/directions-institution-hierarchies-list/directions-institution-hierarchies-list.component.html new file mode 100644 index 0000000000..9cd7c50068 --- /dev/null +++ b/src/app/shell/admin-tools/data/directions-wrapper/directions-institution-hierarchies-list/directions-institution-hierarchies-list.component.html @@ -0,0 +1,19 @@ +
+
+ + + + + + + + + +
+ {{column}} + +
{{ element[i] }} +
+
+
+
\ No newline at end of file diff --git a/src/app/shell/admin-tools/data/directions-wrapper/directions-institution-hierarchies-list/directions-institution-hierarchies-list.component.scss b/src/app/shell/admin-tools/data/directions-wrapper/directions-institution-hierarchies-list/directions-institution-hierarchies-list.component.scss new file mode 100644 index 0000000000..3eee176ce8 --- /dev/null +++ b/src/app/shell/admin-tools/data/directions-wrapper/directions-institution-hierarchies-list/directions-institution-hierarchies-list.component.scss @@ -0,0 +1 @@ +@import "src/app/shared/styles/tables.scss"; \ No newline at end of file diff --git a/src/app/shell/admin-tools/data/directions-wrapper/directions-institution-hierarchies-list/directions-institution-hierarchies-list.component.spec.ts b/src/app/shell/admin-tools/data/directions-wrapper/directions-institution-hierarchies-list/directions-institution-hierarchies-list.component.spec.ts new file mode 100644 index 0000000000..68851870ab --- /dev/null +++ b/src/app/shell/admin-tools/data/directions-wrapper/directions-institution-hierarchies-list/directions-institution-hierarchies-list.component.spec.ts @@ -0,0 +1,30 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatTableModule } from '@angular/material/table'; +import { NgxsModule } from '@ngxs/store'; +import { DirectionsInstitutionHierarchiesListComponent } from './directions-institution-hierarchies-list.component'; + +describe('DirectionsInstitutionHierarchiesListComponent', () => { + let component: DirectionsInstitutionHierarchiesListComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [NgxsModule.forRoot([]), MatTableModule], + declarations: [DirectionsInstitutionHierarchiesListComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(DirectionsInstitutionHierarchiesListComponent); + component = fixture.componentInstance; + component.institution = { + id: "id", + title: 'title', + numberOfHierarchyLevels: 2 + }; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shell/admin-tools/data/directions-wrapper/directions-institution-hierarchies-list/directions-institution-hierarchies-list.component.ts b/src/app/shell/admin-tools/data/directions-wrapper/directions-institution-hierarchies-list/directions-institution-hierarchies-list.component.ts new file mode 100644 index 0000000000..c76e987fab --- /dev/null +++ b/src/app/shell/admin-tools/data/directions-wrapper/directions-institution-hierarchies-list/directions-institution-hierarchies-list.component.ts @@ -0,0 +1,90 @@ +import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { MatTableDataSource } from '@angular/material/table'; +import { Select, Store } from '@ngxs/store'; +import { distinctUntilChanged, filter, map, Observable, Subject, takeUntil } from 'rxjs'; +import { Institution, InstituitionHierarchy, InstitutionFieldDescription } from '../../../../../shared/models/institution.model'; +import { GetFieldDescriptionByInstitutionId, GetAllInstitutionsHierarchy } from '../../../../../shared/store/meta-data.actions'; +import { MetaDataState } from '../../../../../shared/store/meta-data.state'; + + +@Component({ + selector: 'app-directions-institution-hierarchies-list', + templateUrl: './directions-institution-hierarchies-list.component.html', + styleUrls: ['./directions-institution-hierarchies-list.component.scss'] +}) +export class DirectionsInstitutionHierarchiesListComponent implements OnInit, OnDestroy { + @Input() institution: Institution; + + @Select(MetaDataState.instituitionsHierarchyAll) + institutionsHierarchies$: Observable; + @Select(MetaDataState.institutionFieldDesc) + institutionFieldDesc$: Observable; + + destroy$: Subject = new Subject(); + displayedColumns: string[]; + institutionalHierarchies: InstituitionHierarchy[]; + records: string[][]; + isLoaded: boolean = false; + dataSource: MatTableDataSource = new MatTableDataSource([{}]); + + constructor(private store: Store) { + } + + ngOnInit(): void { + this.loadDirectionInstitutionHierarchiesData(); + } + + private loadDirectionInstitutionHierarchiesData() { + this.store.dispatch([new GetFieldDescriptionByInstitutionId(this.institution.id), + new GetAllInstitutionsHierarchy()]); + + this.institutionFieldDesc$.pipe( + filter((institutionFieldDesc: InstitutionFieldDescription[]) => !!institutionFieldDesc), + distinctUntilChanged(), + takeUntil(this.destroy$) + ).subscribe((institutionFieldDesc: InstitutionFieldDescription[]) => { + this.displayedColumns = institutionFieldDesc.map((ins: InstitutionFieldDescription) => ins.title); + }); + this.institutionsHierarchies$.pipe( + filter((institutiionHierarchies: InstituitionHierarchy[]) => !!institutiionHierarchies), + distinctUntilChanged(), + map((institutionHierarchies: InstituitionHierarchy[]) => + this.createDirectionTableRecords(institutionHierarchies.filter(ins => ins.institution.title === this.institution.title)) + ), + takeUntil(this.destroy$) + ).subscribe(() => { + this.isLoaded = true; + this.dataSource = new MatTableDataSource(this.records) + }); + } + + private createDirectionTableRecords(institutionalHierarchies: InstituitionHierarchy[]) { + if (institutionalHierarchies) { + this.institutionalHierarchies = institutionalHierarchies; + this.records = []; + const firstLevelInstitutions = this.institutionalHierarchies.filter((ins: InstituitionHierarchy) => ins.hierarchyLevel === 1); + firstLevelInstitutions.forEach((ins: InstituitionHierarchy) => { + let records: string[] = []; + this.createDirectionTableRecord(ins, [...records]); + }); + } + } + + private createDirectionTableRecord(parent: InstituitionHierarchy, records: string[]) { + records.push(parent.title); + let children = this.institutionalHierarchies.filter((ins: InstituitionHierarchy) => ins.parentId === parent.id); + if (!children.length) { + this.records.push(records); + } + else { + children.forEach((child: InstituitionHierarchy) => { + this.createDirectionTableRecord(child, [...records]); + }) + } + } + + ngOnDestroy(): void { + this.destroy$.next(true); + this.destroy$.unsubscribe(); + } +} \ No newline at end of file diff --git a/src/app/shell/admin-tools/data/directions-wrapper/directions-wrapper.component.html b/src/app/shell/admin-tools/data/directions-wrapper/directions-wrapper.component.html index f2e0935e90..f8fb4af313 100644 --- a/src/app/shell/admin-tools/data/directions-wrapper/directions-wrapper.component.html +++ b/src/app/shell/admin-tools/data/directions-wrapper/directions-wrapper.component.html @@ -3,9 +3,13 @@ - - - - + + + + + + + + diff --git a/src/app/shell/admin-tools/data/directions-wrapper/directions-wrapper.component.spec.ts b/src/app/shell/admin-tools/data/directions-wrapper/directions-wrapper.component.spec.ts index fb5e860913..d4a9e53b8e 100644 --- a/src/app/shell/admin-tools/data/directions-wrapper/directions-wrapper.component.spec.ts +++ b/src/app/shell/admin-tools/data/directions-wrapper/directions-wrapper.component.spec.ts @@ -1,6 +1,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatTabsModule } from '@angular/material/tabs'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NgxsModule } from '@ngxs/store'; import { DirectionsWrapperComponent } from './directions-wrapper.component'; +import { DirectionsComponent } from './directions/directions.component'; describe('DirectionsWrapperComponent', () => { let component: DirectionsWrapperComponent; @@ -8,8 +12,8 @@ describe('DirectionsWrapperComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [NgxsModule.forRoot([])], - declarations: [DirectionsWrapperComponent] + imports: [NgxsModule.forRoot([]), MatTabsModule, MatDialogModule, BrowserAnimationsModule], + declarations: [DirectionsWrapperComponent, DirectionsComponent], }).compileComponents(); fixture = TestBed.createComponent(DirectionsWrapperComponent); diff --git a/src/app/shell/admin-tools/data/directions-wrapper/directions-wrapper.component.ts b/src/app/shell/admin-tools/data/directions-wrapper/directions-wrapper.component.ts index bd174b2409..486e00837e 100644 --- a/src/app/shell/admin-tools/data/directions-wrapper/directions-wrapper.component.ts +++ b/src/app/shell/admin-tools/data/directions-wrapper/directions-wrapper.component.ts @@ -1,7 +1,11 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { Store } from '@ngxs/store'; +import { Select, Store } from '@ngxs/store'; +import { Observable } from 'rxjs'; import { NavBarName } from '../../../../shared/enum/navigation-bar'; import { PopNavPath, PushNavPath } from '../../../../shared/store/navigation.actions'; +import { MetaDataState } from '../../../../shared/store/meta-data.state'; +import { Institution } from '../../../../shared/models/institution.model'; +import { GetAllInstitutions } from '../../../../shared/store/meta-data.actions'; @Component({ selector: 'app-directions-wrapper', @@ -9,16 +13,19 @@ import { PopNavPath, PushNavPath } from '../../../../shared/store/navigation.act styleUrls: ['./directions-wrapper.component.scss'] }) export class DirectionsWrapperComponent implements OnInit, OnDestroy { + @Select(MetaDataState.institutions) + institutions$: Observable; + constructor(private store: Store) {} ngOnInit(): void { - this.store.dispatch( + this.store.dispatch([new GetAllInstitutions(), new PushNavPath({ name: NavBarName.Directions, isActive: false, disable: true }) - ); + ]); } ngOnDestroy(): void {