Merge branch 'DEVELOP' of ssh://git.eclipse.org:29418/openk-usermodules/org.eclipse.openk-usermodules.gridFailureInformation.frontend into SI-541-Rolle-Gast-Lesezugriff
Signed-off-by: Ina Curdt <Ina.Curdt@pta.de>
diff --git a/i18n/grid-failure.de.json b/i18n/grid-failure.de.json
index 6c9a44f..5f7646a 100644
--- a/i18n/grid-failure.de.json
+++ b/i18n/grid-failure.de.json
@@ -44,7 +44,8 @@
"StatusIntern": "Status (intern)",
"Street": "Straße (betroffene Straße)",
"VoltageLevel": "Spannungsebene",
- "PublicationStatus": "Veröffentlichungsstatus"
+ "PublicationStatus": "Veröffentlichungsstatus",
+ "MapView": "Kartenansicht"
},
"GridFailureTemp": {
"FailureTypeId": "(TypId)",
diff --git a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.html b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.html
index 2887813..ac0378a 100644
--- a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.html
+++ b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.html
@@ -487,7 +487,6 @@
type="text"
class="form-control"
id="housenumber"
- (change)="gridFailureDetailsSandbox.setLatLong()"
[ngrxFormControlState]="((gridFailureDetailsSandbox.gridFailureDetailsFormState$ | async)?.controls)['housenumber']"
>
<option [value]="null" selected disabled>{{ 'SelectOption' | translate }}</option>
@@ -507,13 +506,14 @@
<input
type="text"
maxlength="255"
+ id="stationDescription"
class="form-control"
+ autocomplete="off"
+ (input)="resetCoords($event.target.value)"
+ (selectItem)="gridFailureDetailsSandbox.latLonMapping($event.item); gridFailureDetailsSandbox.setStationId($event.item.stationId)"
[ngbTypeahead]="gridFailureDetailsSandbox.searchForStation"
[ngrxValueConverter]="gridFailureDetailsSandbox.stationValueConverter"
- (selectItem)="gridFailureDetailsSandbox.latLonMapping($event.item); gridFailureDetailsSandbox.setStationId($event.item.stationId)"
- id="stationDescription"
[ngrxFormControlState]="((gridFailureDetailsSandbox.gridFailureDetailsFormState$ | async)?.controls)['stationDescription']"
- autocomplete="off"
[resultFormatter]="gridFailureDetailsSandbox.formatter"
[inputFormatter]="gridFailureDetailsSandbox.formatter"
/>
@@ -536,6 +536,17 @@
</div>
</div>
</app-expandable>
+
+ <app-expandable [showBodyInitially]="true">
+ <label header>{{ 'GridFailure.MapView' | translate }}</label>
+ <div class="expandable-content map-container" body>
+ <div class="map-detail-view">
+ <openk-grid-failure-information-map
+ [mapDetailData]="gridFailureDetailsSandbox.currentGridFailureDetailsCoords"
+ ></openk-grid-failure-information-map>
+ </div>
+ </div>
+ </app-expandable>
</div>
</form>
</div>
diff --git a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.scss b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.scss
index d2fa2b6..8ac0d62 100644
--- a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.scss
+++ b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.scss
@@ -62,7 +62,11 @@
margin-left: 0px;
}
.expandable-content {
- padding: 20px 0px 20px 35px;
+ padding: 20px 35px 20px 35px;
+}
+.map-container {
+ height: 600px;
+ width: 100%;
}
.header-container {
display: flex;
@@ -76,3 +80,7 @@
.card {
height: calc(100vh - 180px);
}
+.map-detail-view {
+ position: relative;
+ height: -webkit-fill-available;
+}
diff --git a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.spec.ts b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.spec.ts
index 29c5373..03e6c00 100644
--- a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.spec.ts
+++ b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.spec.ts
@@ -14,11 +14,9 @@
import { of } from 'rxjs';
import { Store } from '@ngrx/store';
import { State } from '@grid-failure-information-app/shared/store';
-import { TestBed, ComponentFixture } from '@angular/core/testing';
describe('GridFailureDetailsComponent', () => {
let component: GridFailureDetailsComponent;
- let fixture: ComponentFixture<GridFailureDetailsComponent>;
let gridFailureSandbox: any;
let gridFailureDetailsFormResponse: any = {
value: {
@@ -36,6 +34,9 @@
setGridFailureDateTime(dateISOString: string, actionType: string) {},
registerEvents() {},
endSubscriptions() {},
+ resetCoords() {},
+ resetStationId() {},
+ resetPostCode() {},
gridFailureDetailsFormState$: of(gridFailureDetailsFormResponse),
} as any;
component = new GridFailureDetailsComponent(gridFailureSandbox, appState);
@@ -85,10 +86,16 @@
expect(expectedResult.toISOString).toBe(result.toISOString);
});
- it('should call the "FailureDetailsSandbox.setState(..)" event 1 times', ()=>{
- //fixture = TestBed.createComponent(GridFailureDetailsComponent);
- // let comp = fixture.componentInstance;
- // fixture.detectChanges();
- // let onClickSpy = spyOn(comp, '')
+ it('checks if resetCoords(value: string) works fine', () => {
+ let spy1 = spyOn(gridFailureSandbox, 'resetCoords');
+ let spy2 = spyOn(gridFailureSandbox, 'resetStationId');
+
+ component.resetCoords('1');
+ expect(spy1).not.toHaveBeenCalled();
+ expect(spy2).not.toHaveBeenCalled();
+
+ component.resetCoords(null);
+ expect(spy1).toHaveBeenCalled();
+ expect(spy2).toHaveBeenCalled();
});
});
diff --git a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.ts b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.ts
index 7ea4328..a0e9cfd 100644
--- a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.ts
+++ b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.ts
@@ -29,6 +29,7 @@
public RolesEnum = RolesEnum;
public StateEnum = StateEnum;
public gridFailureVersions$: Observable<GridFailure[]> = this.appState$.select(store.getGridFailureVersionsData);
+
constructor(public gridFailureDetailsSandbox: GridFailureDetailsSandbox, protected appState$: Store<store.State>) {}
ngOnInit() {
@@ -44,6 +45,13 @@
this.gridFailureDetailsSandbox.clearGridFailureData(actionType);
}
+ public resetCoords(value: string): void {
+ if (!value) {
+ this.gridFailureDetailsSandbox.resetCoords();
+ this.gridFailureDetailsSandbox.resetStationId();
+ }
+ }
+
private _createDateTime(dateTime: any): Date {
let date: Date = new Date();
date.setDate(dateTime.day);
diff --git a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.sandbox.spec.ts b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.sandbox.spec.ts
index 402ff30..b1631cd 100644
--- a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.sandbox.spec.ts
+++ b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.sandbox.spec.ts
@@ -1,4 +1,3 @@
-import { FailureHousenumber } from './../../../shared/models/failure-housenumber.model';
/********************************************************************************
* Copyright (c) 2020 Contributors to the Eclipse Foundation
*
@@ -22,6 +21,7 @@
import { Router } from '@angular/router';
import { StateEnum } from '@grid-failure-information-app/shared/constants/enums';
import { FailureStation } from '@grid-failure-information-app/shared/models';
+import { FailureHousenumber } from '@grid-failure-information-app/shared/models';
describe('GridFailureDetailsSandbox', () => {
let service: GridFailureDetailsSandbox;
@@ -41,6 +41,8 @@
dispatchSpy = spyOn(appState, 'dispatch').and.callFake(() => {});
service = new GridFailureDetailsSandbox(appState, actionSubject, router, utilService, modalService);
+ const gridFailureDetailsFormState = { value: { lontitude: 1.24, latitude: 1.24 } };
+ service.gridFailureDetailsFormState$ = of(gridFailureDetailsFormState as any);
});
it('should create GridFailureDetailsSandbox service', () => {
@@ -211,7 +213,6 @@
});
it('should dispatch action in response to controlId = gridFailureDetailsForm', () => {
- service.gridFailureInternalStates$ = { pipe: () => of({}), map: () => of({}) } as any;
let formState: any = {
id: 'gridFailureDetailsForm',
value: {
@@ -229,7 +230,6 @@
});
it('should dispatch action in response to controlId = gridFailureDetailsForm.postcode', () => {
- service.gridFailureInternalStates$ = { pipe: () => of({}), map: () => of({}) } as any;
let formState: any = {
value: {
statusIntern: StateEnum.CREATED,
@@ -246,7 +246,6 @@
});
it('should dispatch action in response to controlId = gridFailureDetailsForm.city', () => {
- service.gridFailureInternalStates$ = { pipe: () => of({}), map: () => of({}) } as any;
let formState: any = {
value: {
statusIntern: StateEnum.CREATED,
@@ -265,7 +264,6 @@
});
it('should dispatch action in response to controlId = gridFailureDetailsForm.district', () => {
- service.gridFailureInternalStates$ = { pipe: () => of({}), map: () => of({}) } as any;
let formState: any = {
value: {
statusIntern: StateEnum.CREATED,
@@ -286,7 +284,6 @@
});
it('should dispatch action in response to controlId = gridFailureDetailsForm.street', () => {
- service.gridFailureInternalStates$ = { pipe: () => of({}), map: () => of({}) } as any;
let formState: any = {
value: {
statusIntern: StateEnum.CREATED,
@@ -403,6 +400,9 @@
value: gfdetail,
};
+ service.latLonMapping(null);
+ expect(dispatchSpy).not.toHaveBeenCalled();
+
const event = new FailureStation();
event.latitude = 1.24;
event.longitude = 1.23;
@@ -412,39 +412,130 @@
expect(dispatchSpy).toHaveBeenCalled();
});
- it('should format Object to String', () => {
- const station: FailureStation = {
- g3efid: 0,
- id: '',
- longitude: 0,
- latitude: 0,
- sdoxl: 0,
- sdoyl: 0,
- stationId: '',
- stationName: 'test',
- };
-
- const result = service.formatter(station);
-
- expect(result).toBe('test');
- });
-
it('should dispatch loadAddressPostalcodes Action via loadAddressPostalcodes()', () => {
service.loadAddressPostalcodes();
expect(appState.dispatch).toHaveBeenCalledWith(gridFailureActions.loadAddressPostalcodes());
});
- it('should dispatch loadGridFailureAddress action with uuid from housenumber', () => {
- service.addressHouseNumbers = [new FailureHousenumber({ uuid: 'x', housenumber: '1' })];
- let gridFailureDetailsFormResponse: any = {
- controls: {
- housenumber: {
- value: '1',
- } as any,
- } as any,
+ it('should map stationId to gridFailure object', () => {
+ const gfdetail = new GridFailure();
+ gfdetail.id = 'id';
+ gfdetail.stationId = '1';
+
+ service.currentFormState = {
+ ...service.currentFormState,
+ isValid: true,
+ value: gfdetail,
};
- service.gridFailureDetailsFormState$ = of(gridFailureDetailsFormResponse);
- service.setLatLong();
- expect(appState.dispatch).toHaveBeenCalledWith(gridFailureActions.loadGridFailureAddress({ payload: 'x' }));
+
+ const stationId = '2';
+
+ service.setStationId(stationId);
+
+ expect(dispatchSpy).toHaveBeenCalled();
+ });
+
+ it('should reset Coords', () => {
+ const gfdetail = new GridFailure();
+ gfdetail.id = 'id';
+ gfdetail.longitude = 1.0;
+ gfdetail.latitude = 1.0;
+
+ service.currentFormState = {
+ ...service.currentFormState,
+ isValid: true,
+ value: gfdetail,
+ };
+
+ service.resetCoords();
+
+ expect(dispatchSpy).toHaveBeenCalled();
+ });
+
+ it('should reset stationId', () => {
+ const gfdetail = new GridFailure();
+ gfdetail.id = 'id';
+ gfdetail.stationId = 'test';
+
+ service.currentFormState = {
+ ...service.currentFormState,
+ isValid: true,
+ value: gfdetail,
+ };
+
+ service.resetStationId();
+
+ expect(dispatchSpy).toHaveBeenCalled();
+ });
+
+ it('should trigger formatter and check if the right value was returned', () => {
+ const failureStation: FailureStation = new FailureStation();
+ failureStation.stationName = 'test';
+ failureStation.stationId = 'test';
+ const result1 = service.formatter(failureStation);
+ const result2 = service.formatter('test');
+
+ expect(result1).toBe('test (test)');
+ expect(result2).toBe('test');
+ });
+
+ it('should dispatch Action after loadPostalCodes', () => {
+ service.loadAddressPostalcodes();
+ expect(appState.dispatch).toHaveBeenCalledWith(gridFailureActions.loadAddressPostalcodes());
+ });
+
+ it('should trigger searchForAddressPostalcodes and check if the right value was returned', () => {
+ (service as any)._addressPostalcodes = ['test', 'test1', 'test2', 'hello'];
+ const text$ = of('hello');
+ const result = service.searchForAddressPostalcodes(text$);
+
+ result.subscribe(item => {
+ expect(item).toEqual(['hello']);
+ });
+ });
+
+ it('should trigger searchForAddressPostalcodes and check if the right value was returned if length is under 2', () => {
+ (service as any)._addressPostalcodes = ['test', 'test1', 'test2', 'hello'];
+ const text$ = of('h');
+ const result = service.searchForAddressPostalcodes(text$);
+
+ result.subscribe(item => {
+ expect(item).toEqual([]);
+ });
+ });
+
+ it('should trigger searchForStation and check if the right value was returned', () => {
+ const failureStation: FailureStation = new FailureStation();
+ failureStation.stationName = 'hello';
+ (service as any)._gridFailureStations = [new FailureStation(), new FailureStation(), failureStation];
+ const text$ = of('hello');
+ const result = service.searchForStation(text$);
+
+ result.subscribe(item => {
+ expect(item).toEqual([failureStation]);
+ });
+ });
+
+ it('should trigger searchForStation and check if the right value was returned if length is under 2', () => {
+ const failureStation: FailureStation = new FailureStation();
+ failureStation.stationName = 'hello';
+ (service as any)._gridFailureStations = [new FailureStation(), new FailureStation(), failureStation];
+ const text$ = of('h');
+ const result = service.searchForStation(text$);
+
+ result.subscribe(item => {
+ expect(item).toEqual([]);
+ });
+ });
+
+ it('should call setLatLong and dispatch loadGridFailureAddress()', () => {
+ const failureHousenumber = new FailureHousenumber();
+ failureHousenumber.housenumber = 'test';
+ failureHousenumber.uuid = 'hello';
+ service.addressHouseNumbers = [failureHousenumber];
+ const hsnr = 'test';
+ service.setLatLong(hsnr);
+
+ expect(appState.dispatch).toHaveBeenCalledWith(gridFailureActions.loadGridFailureAddress({ payload: failureHousenumber.uuid }));
});
});
diff --git a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.sandbox.ts b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.sandbox.ts
index ed8179d..55c2217 100644
--- a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.sandbox.ts
+++ b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.sandbox.ts
@@ -1,3 +1,4 @@
+import { switchMap, withLatestFrom, mergeMap, takeWhile } from 'rxjs/operators';
/********************************************************************************
* Copyright (c) 2020 Contributors to the Eclipse Foundation
*
@@ -31,20 +32,25 @@
FailureStation,
FailureHousenumber,
FailureAddress,
+ FailureCoords,
} from '@grid-failure-information-app/shared/models';
import { BaseFormSandbox } from '@grid-failure-information-app/shared/sandbox/base-form.sandbox';
import * as store from '@grid-failure-information-app/shared/store';
import * as gridFailureActions from '@grid-failure-information-app/shared/store/actions/grid-failures.action';
import * as fromGridFailuresDetailFormReducer from '@grid-failure-information-app/shared/store/reducers/grid-failures/grid-failure-details-form.reducer';
import * as gridFailuresDetailFormReducer from '@grid-failure-information-app/shared/store/reducers/grid-failures/grid-failure-details-form.reducer';
-import { dateTimeValueConverter, navigateHome, stationToStationNameConverter } from '@grid-failure-information-app/shared/utility';
+import {
+ dateTimeValueConverter,
+ navigateHome,
+ stationToStationNameConverter as stationToStationDescriptionConverter,
+} from '@grid-failure-information-app/shared/utility';
import { UtilService } from '@grid-failure-information-app/shared/utility/utility.service';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { ofType } from '@ngrx/effects';
import { ActionsSubject, Store } from '@ngrx/store';
import { Moment } from 'moment';
import { DisableAction, EnableAction, FormGroupState, NgrxValueConverter, ResetAction, SetValueAction } from 'ngrx-forms';
-import { Observable } from 'rxjs';
+import { Observable, combineLatest, of } from 'rxjs';
import { take, takeUntil, map, debounceTime, distinctUntilChanged, tap, skip } from 'rxjs/operators';
import { Globals } from '@grid-failure-information-app/shared/constants/globals';
import { StateEnum } from '@grid-failure-information-app/shared/constants/enums';
@@ -75,6 +81,7 @@
public internExternEnum = InternExternEnum;
public saveEnabled: boolean = true;
public maxVersionNumber: number;
+ public currentGridFailureDetailsCoords: FailureCoords = new FailureCoords();
public addressCommunities$: Observable<Array<string>> = this.actionsSubject.pipe(
ofType(gridFailureActions.loadAddressCommunitiesSuccess),
@@ -97,6 +104,7 @@
map(payload => payload.map(adrs => adrs.housenumber)),
takeUntil(this._endSubscriptions$)
);
+
public addressHouseNumbers: Array<FailureHousenumber>;
public showQualifyButton: boolean = false;
public showStornoButton: boolean = false;
@@ -259,10 +267,24 @@
}
}
- public setLatLong(): void {
- this.gridFailureDetailsFormState$.pipe(take(1), takeUntil(this._endSubscriptions$)).subscribe((currentFormState: FormGroupState<GridFailure>) => {
+ public setLatLong(hsnr: string): void {
+ const test$ = this.actionsSubject.pipe(
+ ofType(gridFailureActions.loadAddressHouseNumbersSuccess),
+ map(action => action.payload),
+ takeUntil(this._endSubscriptions$)
+ );
+ if (this.addressHouseNumbers.length > 0) {
const failureHousenumber: FailureHousenumber = this.addressHouseNumbers.find((housenumber: FailureHousenumber) => {
- return housenumber.housenumber === currentFormState.controls.housenumber.value;
+ return housenumber.housenumber === hsnr;
+ });
+ if (!!failureHousenumber) {
+ this.appState$.dispatch(gridFailureActions.loadGridFailureAddress({ payload: failureHousenumber.uuid }));
+ }
+ return;
+ }
+ test$.pipe(take(1)).subscribe(addresses => {
+ const failureHousenumber: FailureHousenumber = addresses.find((housenumber: FailureHousenumber) => {
+ return housenumber.housenumber === hsnr;
});
if (!!failureHousenumber) {
this.appState$.dispatch(gridFailureActions.loadGridFailureAddress({ payload: failureHousenumber.uuid }));
@@ -320,6 +342,9 @@
})
);
break;
+ case formState.controls.housenumber.id:
+ this.setLatLong(formState.controls.housenumber.value);
+ break;
default:
break;
@@ -358,18 +383,30 @@
const event = { longitude: address.longitude, latitude: address.latitude };
this.latLonMapping(event);
});
+
+ this.gridFailureDetailsFormState$.subscribe(gridFailureDetails => {
+ if (
+ this.currentGridFailureDetailsCoords.latitude !== gridFailureDetails.value.latitude ||
+ this.currentGridFailureDetailsCoords.longitude !== gridFailureDetails.value.longitude
+ ) {
+ this.currentGridFailureDetailsCoords = new FailureCoords(gridFailureDetails.value);
+ }
+ });
}
public searchForStation = (text$: Observable<string>) =>
text$.pipe(
debounceTime(200),
distinctUntilChanged(),
- map(term => (term.length < 2 ? [] : this._gridFailureStations.filter(s => s.stationName.toLowerCase().indexOf(term.toLowerCase()) > -1)))
+ map(term => (term.length < 2 ? [] : this._gridFailureStations.filter(s => s.failureStationSearchString.toLowerCase().indexOf(term.toLowerCase()) > -1)))
);
- public formatter = (s: FailureStation) => s.stationName;
+ public formatter = (s: FailureStation | string) => {
+ if (s instanceof FailureStation) return s.failureStationSearchString;
+ else return s;
+ };
- public stationValueConverter: NgrxValueConverter<any | null, string | null> = stationToStationNameConverter;
+ public stationValueConverter: NgrxValueConverter<any | null, string | null> = stationToStationDescriptionConverter;
public latLonMapping(data: { longitude: number; latitude: number }): void {
!!data &&
@@ -392,6 +429,25 @@
);
}
+ public resetCoords(): void {
+ this.appState$.dispatch(
+ new SetValueAction(this.currentFormState.id, {
+ ...this.currentFormState.value,
+ longitude: null,
+ latitude: null,
+ })
+ );
+ }
+
+ public resetStationId() {
+ this.appState$.dispatch(
+ new SetValueAction(this.currentFormState.id, {
+ ...this.currentFormState.value,
+ stationId: null,
+ })
+ );
+ }
+
public searchForAddressPostalcodes = (text$: Observable<string>) =>
text$.pipe(
debounceTime(300),
@@ -445,21 +501,15 @@
switch (state) {
case StateEnum.NEW:
+ case StateEnum.PLANNED:
this.showCreatedButton = true;
+ break;
case StateEnum.CREATED:
case StateEnum.UPDATED:
this.showQualifyButton = true;
this.showStornoButton = true;
break;
- case StateEnum.PLANNED:
- this.showQualifyButton = true;
- this.showStornoButton = true;
- this.showCreatedButton = true;
- break;
default:
- this.showQualifyButton = false;
- this.showStornoButton = false;
- this.showCreatedButton = false;
break;
}
}
diff --git a/projects/grid-failure-information-app/src/app/shared/directives/form-disable.directive.spec.ts b/projects/grid-failure-information-app/src/app/shared/directives/form-disable.directive.spec.ts
new file mode 100644
index 0000000..bb0771b
--- /dev/null
+++ b/projects/grid-failure-information-app/src/app/shared/directives/form-disable.directive.spec.ts
@@ -0,0 +1,49 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+// import { FormDisableDirective } from '@shared/directives/form-disable.directive';
+import { FormDisableDirective } from '@grid-failure-information-app/shared/directives/form-disable.directive';
+import { async } from '@angular/core/testing';
+import { of } from 'rxjs/observable/of';
+import { PermissionsModel } from '../models/permissions.model';
+
+describe('FormDisableDirective', () => {
+ let viewContainerRef: any;
+ let appState: any;
+ beforeEach(async(() => {
+ viewContainerRef = {
+ createEmbeddedView: () => {},
+ clear: () => {},
+ element: { nativeElement: { elements: [{ classList: {}, disabled: undefined, childNodes: [] }] } },
+ };
+
+ appState = {
+ pipe: () => of(),
+ dispatch: () => {},
+ select: () => of({ roles: ['grid-failure-reader'] }),
+ map: () => of({ reader: true }),
+ };
+ }));
+
+ it('should create an instance', () => {
+ const directive = new FormDisableDirective(viewContainerRef as any, appState as any);
+ expect(directive).toBeTruthy();
+ });
+
+ it('should traverse a DOM', () => {
+ const directive = new FormDisableDirective(viewContainerRef as any, appState as any);
+ const spy = spyOn(directive, '_traverseDOM' as any);
+ directive.ngAfterViewInit();
+ expect(spy).toHaveBeenCalled();
+ });
+
+});
diff --git a/projects/grid-failure-information-app/src/app/shared/directives/form-disable.directive.ts b/projects/grid-failure-information-app/src/app/shared/directives/form-disable.directive.ts
new file mode 100644
index 0000000..9bfb851
--- /dev/null
+++ b/projects/grid-failure-information-app/src/app/shared/directives/form-disable.directive.ts
@@ -0,0 +1,56 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+import { Directive, ViewContainerRef, AfterViewInit, OnDestroy } from '@angular/core';
+import { Store } from '@ngrx/store';
+// import * as store from '@shared/store';
+import * as store from '@grid-failure-information-app/shared/store';
+import { Observable, Subject } from 'rxjs';
+// import { User } from '@shared/models/user';
+import { User } from '@grid-failure-information-app/shared/models/user';
+// import { PermissionsModel } from '@shared/models/permissions.model';
+import { PermissionsModel } from '@grid-failure-information-app/shared/models/permissions.model';
+import { takeUntil } from 'rxjs/operators';
+
+@Directive({
+ selector: 'form',
+})
+export class FormDisableDirective implements AfterViewInit, OnDestroy {
+ private _endSubscriptions$: Subject<boolean> = new Subject();
+ private _permissions$: Observable<PermissionsModel> = this._appState$
+ .select(store.getUser)
+ .pipe(takeUntil(this._endSubscriptions$))
+ .map((user: User) => {
+ return new PermissionsModel(user.roles);
+ });
+
+ constructor(private _viewContainer: ViewContainerRef, private _appState$: Store<store.State>) {}
+
+ ngAfterViewInit() {
+ this._permissions$.subscribe(permissions => {
+ this._traverseDOM(this._viewContainer.element.nativeElement.elements, permissions);
+ });
+ }
+ ngOnDestroy() {
+ this._endSubscriptions$.next(true);
+ }
+ private _traverseDOM(elements: any[], permissions: PermissionsModel): void {
+ for (const element of elements) {
+ const isCancelButton = !!Object.keys(element.classList || {}).find(
+ item => element.classList[item] === 'cancel-button' || element.classList[item] === 'btn-sm'
+ );
+ element.disabled = element.disabled || (!isCancelButton && permissions.reader);
+
+ this._traverseDOM(element.childNodes, permissions);
+ }
+ }
+}
diff --git a/projects/grid-failure-information-app/src/app/shared/directives/visible-by-right.spec.ts b/projects/grid-failure-information-app/src/app/shared/directives/visible-by-right.spec.ts
index c7d6e12..dc83e66 100644
--- a/projects/grid-failure-information-app/src/app/shared/directives/visible-by-right.spec.ts
+++ b/projects/grid-failure-information-app/src/app/shared/directives/visible-by-right.spec.ts
@@ -74,6 +74,22 @@
expect(spy).toHaveBeenCalled();
});
+ it('should clear view for invalidRole', () => {
+ const directive = new VisibleByRightDirective(templateRef as any, viewContainerRef as any, appState as any);
+
+ directive.visibleByRight = ['_InvalidRole_'];
+ const spy = spyOn(directive['_viewContainer'], 'clear' as any);
+ directive.ngOnInit();
+ expect(spy).toHaveBeenCalled();
+ });
+ it('should create embedded view for role creator', () => {
+ const directive = new VisibleByRightDirective(templateRef as any, viewContainerRef as any, appState as any);
+ directive.visibleByRight = [RolesEnum.CREATOR];
+ const spy = spyOn(directive['_viewContainer'], 'createEmbeddedView' as any);
+ directive.ngOnInit();
+ expect(spy).toHaveBeenCalled();
+ });
+
it('should clear view for another role than admin or qualifier', () => {
appState = {
pipe: () => of(),
@@ -82,6 +98,7 @@
map: () => of({}),
};
const directive = new VisibleByRightDirective(templateRef as any, viewContainerRef as any, appState as any);
+
directive.visibleByRight = [RolesEnum.QUALIFIER];
const spy = spyOn(directive['_viewContainer'], 'clear' as any);
directive.ngOnInit();
@@ -89,14 +106,10 @@
});
it('should create embedded view for role creator', () => {
- // Arrange
- //appState.map = () => of({creator: false, publisher: true, admin: false, qualifier: false});
const directive = new VisibleByRightDirective(templateRef as any, viewContainerRef as any, appState as any);
directive.visibleByRight = [RolesEnum.CREATOR];
const spy = spyOn(directive['_viewContainer'], 'createEmbeddedView' as any);
- // Act
directive.ngOnInit();
- // Assert
expect(spy).toHaveBeenCalled();
});
diff --git a/projects/grid-failure-information-app/src/app/shared/guards/admin.guard.ts b/projects/grid-failure-information-app/src/app/shared/guards/admin.guard.ts
index 8a5e441..2b6312b 100644
--- a/projects/grid-failure-information-app/src/app/shared/guards/admin.guard.ts
+++ b/projects/grid-failure-information-app/src/app/shared/guards/admin.guard.ts
@@ -33,7 +33,7 @@
const isAdmin$: Observable<boolean> = this._appState$
.select(store.getUser)
.pipe(takeUntil(this._endSubscriptions$), take(1))
- .map((user: User) => !!(new PermissionsModel(user.roles).admin));
+ .map((user: User) => !!new PermissionsModel(user.roles).admin);
isAdmin$.subscribe(isAdmin => !isAdmin && this._router.navigate(['/grid-failures']));
return isAdmin$.pipe(take(1));
}
diff --git a/projects/grid-failure-information-app/src/app/shared/models/failure-coords.model.ts b/projects/grid-failure-information-app/src/app/shared/models/failure-coords.model.ts
new file mode 100644
index 0000000..de6290b
--- /dev/null
+++ b/projects/grid-failure-information-app/src/app/shared/models/failure-coords.model.ts
@@ -0,0 +1,31 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+import { Boxed, box } from 'ngrx-forms';
+import { Globals } from '@grid-failure-information-app/shared/constants/globals';
+
+export class FailureCoords {
+ public longitude: number = null;
+ public latitude: number = null;
+
+ public constructor(data: any = null) {
+ Object.keys(data || {})
+ .filter(property => this.hasOwnProperty(property))
+ .forEach(property => {
+ if (Globals.PROPERTIES_TO_BOX.includes(property)) {
+ this[property] = box(data[property]);
+ } else {
+ this[property] = data[property];
+ }
+ });
+ }
+}
diff --git a/projects/grid-failure-information-app/src/app/shared/models/failure.station.model.ts b/projects/grid-failure-information-app/src/app/shared/models/failure.station.model.ts
index e46f42b..3c6a46d 100644
--- a/projects/grid-failure-information-app/src/app/shared/models/failure.station.model.ts
+++ b/projects/grid-failure-information-app/src/app/shared/models/failure.station.model.ts
@@ -25,4 +25,8 @@
.filter(property => this.hasOwnProperty(property))
.forEach(property => (this[property] = data[property]));
}
+
+ get failureStationSearchString(): string {
+ return this.stationName + ' (' + this.stationId + ')';
+ }
}
diff --git a/projects/grid-failure-information-app/src/app/shared/models/index.ts b/projects/grid-failure-information-app/src/app/shared/models/index.ts
index 01ce0d0..ed6d7fe 100644
--- a/projects/grid-failure-information-app/src/app/shared/models/index.ts
+++ b/projects/grid-failure-information-app/src/app/shared/models/index.ts
@@ -21,3 +21,4 @@
export * from './failure.station.model';
export * from './failure-housenumber.model';
export * from './failure-address.model';
+export * from './failure-coords.model';
diff --git a/projects/grid-failure-information-app/src/app/shared/utility/utilityHelpers.ts b/projects/grid-failure-information-app/src/app/shared/utility/utilityHelpers.ts
index 987fd74..989edfa 100644
--- a/projects/grid-failure-information-app/src/app/shared/utility/utilityHelpers.ts
+++ b/projects/grid-failure-information-app/src/app/shared/utility/utilityHelpers.ts
@@ -71,7 +71,7 @@
}
/**
- * Convert a station object to station name
+ * Convert a station object to station description
*/
export const stationToStationNameConverter: NgrxValueConverter<any | null, string | null> = {
convertViewToStateValue(value) {
@@ -79,13 +79,11 @@
return null;
}
- if (value.stationName) return value.stationName;
+ if (value.stationName) return value.stationName + ' (' + value.stationId + ')';
else return value;
},
convertStateToViewValue(value) {
- const failureStation: FailureStation = new FailureStation();
- failureStation.stationName = value;
- return failureStation;
+ return value;
},
};
diff --git a/projects/grid-failure-information-app/src/styles.scss b/projects/grid-failure-information-app/src/styles.scss
index 7148a44..131ecfb 100644
--- a/projects/grid-failure-information-app/src/styles.scss
+++ b/projects/grid-failure-information-app/src/styles.scss
@@ -2338,6 +2338,8 @@
}
.dropdown-menu.show {
display: block;
+ max-height: 200px;
+ overflow: auto;
}
.dropdown-header {
display: block;
diff --git a/projects/openk/grid-failure-information-map/src/constants/globals.ts b/projects/openk/grid-failure-information-map/src/constants/globals.ts
index 4839654..b012a93 100644
--- a/projects/openk/grid-failure-information-map/src/constants/globals.ts
+++ b/projects/openk/grid-failure-information-map/src/constants/globals.ts
@@ -11,9 +11,10 @@
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
export class Globals {
- static INITIAL_LATITUDE = 52.640256;
- static INITIAL_LONGITUDE = 11.495788;
- static INITIAL_ZOOM = 6;
+ static OVERVIEW_MAP_INITIAL_LATITUDE = 51.640256;
+ static OVERVIEW_MAP_INITIAL_LONGITUDE = 10.495788;
+ static OVERVIEW_MAP_INITIAL_ZOOM = 6;
+ static DETAIL_MAP_INITIAL_ZOOM = 13;
static MAX_ZOOM = 19;
static ICON_SIZE_X_COORDINATE = 41;
static ICON_SIZE_Y_COORDINATE = 51;
@@ -25,4 +26,6 @@
static RADIUS_BORDER_COLOR = '#204d74';
static RADIUS_FILL_COLOR = '#337ab7';
static RADIUS_FILL_OPACITY = 0.3;
+
+ public static PROPERTIES_TO_BOX: string[] = ['addressPolygonPoints'];
}
diff --git a/projects/openk/grid-failure-information-map/src/lib/grid-failure-information-map.component.spec.ts b/projects/openk/grid-failure-information-map/src/lib/grid-failure-information-map.component.spec.ts
index b4c3465..369e311 100644
--- a/projects/openk/grid-failure-information-map/src/lib/grid-failure-information-map.component.spec.ts
+++ b/projects/openk/grid-failure-information-map/src/lib/grid-failure-information-map.component.spec.ts
@@ -36,6 +36,8 @@
let spyCircle: any = spyOn(L, 'circle').and.returnValue({ addTo() {} });
component.ngAfterViewInit();
component.mapData = [{ latitude: latitude, longitude: longitude, radius: radius }];
+ component.mapDetailData = { latitude: latitude, longitude: longitude };
+ component.mapDetailData = {};
expect(spyMap).toHaveBeenCalled();
expect(spyTileLayer).toHaveBeenCalled();
expect(spyMarker).toHaveBeenCalledWith([latitude, longitude], { icon: (component as any)._icon });
diff --git a/projects/openk/grid-failure-information-map/src/lib/grid-failure-information-map.component.ts b/projects/openk/grid-failure-information-map/src/lib/grid-failure-information-map.component.ts
index 5bf2c59..995fa84 100644
--- a/projects/openk/grid-failure-information-map/src/lib/grid-failure-information-map.component.ts
+++ b/projects/openk/grid-failure-information-map/src/lib/grid-failure-information-map.component.ts
@@ -14,6 +14,7 @@
import * as L from 'leaflet';
import { Globals } from '@openk-libs/grid-failure-information-map/constants/globals';
import { GridFailureCoordinates } from '@openk-libs/grid-failure-information-map/shared/models/grid-failure-coordinates.model';
+import { unbox } from 'ngrx-forms';
@Component({
selector: 'openk-grid-failure-information-map',
@@ -22,6 +23,7 @@
})
export class GridFailureInformationMapComponent implements AfterViewInit {
private _mapData: Array<GridFailureCoordinates> = [];
+ private _mapDetailData: GridFailureCoordinates;
@Input()
public set mapData(data: Array<any>) {
@@ -30,9 +32,23 @@
});
if (!!this._map) {
this._map.remove();
- this._initMap();
+ this._initMap(Globals.OVERVIEW_MAP_INITIAL_LATITUDE, Globals.OVERVIEW_MAP_INITIAL_LONGITUDE, Globals.OVERVIEW_MAP_INITIAL_ZOOM);
}
- this._setMarker();
+ this._setMultipleMarkers();
+ }
+
+ @Input()
+ public set mapDetailData(gridFailureDetail: any) {
+ this._mapDetailData = new GridFailureCoordinates(gridFailureDetail);
+ if (!!this._map) {
+ this._map.remove();
+ if (this._mapDetailData.latitude && this._mapDetailData.longitude) {
+ this._initMap(this._mapDetailData.latitude, this._mapDetailData.longitude, Globals.DETAIL_MAP_INITIAL_ZOOM);
+ } else {
+ this._initMap(Globals.OVERVIEW_MAP_INITIAL_LATITUDE, Globals.OVERVIEW_MAP_INITIAL_LONGITUDE, Globals.OVERVIEW_MAP_INITIAL_ZOOM);
+ }
+ }
+ this._setDetailMarker();
}
private _map: any;
@@ -45,14 +61,13 @@
constructor() {}
ngAfterViewInit(): void {
- this._initMap();
- this._setMarker();
+ this._initMap(Globals.OVERVIEW_MAP_INITIAL_LATITUDE, Globals.OVERVIEW_MAP_INITIAL_LONGITUDE, Globals.OVERVIEW_MAP_INITIAL_ZOOM);
}
- private _initMap(): void {
+ private _initMap(latitude: number, longitude: number, zoom: number): void {
this._map = L.map(Globals.MAP_ID, {
- center: [Globals.INITIAL_LATITUDE, Globals.INITIAL_LONGITUDE],
- zoom: Globals.INITIAL_ZOOM,
+ center: [latitude, longitude],
+ zoom: zoom,
});
const tiles = L.tileLayer(Globals.TITLE_LAYER, {
maxZoom: Globals.MAX_ZOOM,
@@ -62,15 +77,23 @@
tiles.addTo(this._map);
}
- private _setMarker(): void {
- !!this._map &&
- !!this._mapData &&
+ private _setMultipleMarkers(): void {
+ if (!!this._map && !!this._mapData) {
this._mapData.forEach(gridFailure => {
if (gridFailure.latitude && gridFailure.longitude) {
L.marker([gridFailure.latitude, gridFailure.longitude], { icon: this._icon }).addTo(this._map);
this._drawPolygonOrCircle(gridFailure);
}
});
+ }
+ }
+
+ private _setDetailMarker(): void {
+ if (!!this._map && !!this._mapDetailData) {
+ if (this._mapDetailData.latitude && this._mapDetailData.longitude) {
+ L.marker([this._mapDetailData.latitude, this._mapDetailData.longitude], { icon: this._icon }).addTo(this._map);
+ }
+ }
}
// Draw whether polygon, circle or nothing; graded according to priority
diff --git a/projects/openk/grid-failure-information-map/src/shared/models/grid-failure-coordinates.model.ts b/projects/openk/grid-failure-information-map/src/shared/models/grid-failure-coordinates.model.ts
index 9578542..fb606bb 100644
--- a/projects/openk/grid-failure-information-map/src/shared/models/grid-failure-coordinates.model.ts
+++ b/projects/openk/grid-failure-information-map/src/shared/models/grid-failure-coordinates.model.ts
@@ -10,6 +10,9 @@
*
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
+import { unbox } from 'ngrx-forms';
+import { Globals } from '@openk-libs/grid-failure-information-map/constants/globals';
+
export class GridFailureCoordinates {
public id: string = null;
public radius: number = null;
@@ -20,6 +23,12 @@
public constructor(data: any = null) {
Object.keys(data || {})
.filter(property => this.hasOwnProperty(property))
- .forEach(property => (this[property] = data[property]));
+ .forEach(property => {
+ if (Globals.PROPERTIES_TO_BOX.includes(property)) {
+ this[property] = unbox(data[property]);
+ } else {
+ this[property] = data[property];
+ }
+ });
}
}