Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into feature/angularPag…
Browse files Browse the repository at this point in the history
…eBrowserConnectivityResultSelection

# Conflicts:
#	grails-app/i18n/messages.properties
#	grails-app/i18n/messages_de.properties
  • Loading branch information
finnmartens committed May 6, 2019
2 parents a7f24bd + cbfcd64 commit 95eeeda
Show file tree
Hide file tree
Showing 27 changed files with 423 additions and 216 deletions.
1 change: 1 addition & 0 deletions frontend/src/app/models/perfomance-aspect.model.ts
Expand Up @@ -5,6 +5,7 @@ export interface PerformanceAspect {
id: number
pageId: number
jobGroupId: number
browserId: number
measurand: SelectableMeasurand
performanceAspectType: string
}
@@ -1,47 +1,21 @@
<div class="edit-container">
<div class="aspect-type">
<blockquote>
<span class="performance-aspect-type">
{{"frontend.de.iteratec.osm.performance-aspect." + performanceAspectWrapped?.data?.performanceAspectType + ".user-centric-question" | translate}}
</blockquote>
</span>
<ng-container *ngTemplateOutlet="aspectMetricLabel"></ng-container>
</div>
<div class="measurand">
<div *ngIf="!editMode; else measurandSelect">
<ng-container [ngTemplateOutlet]="aspectMetricLabel"></ng-container>
<div
class="form-control">{{"frontend.de.iteratec.isr.measurand." + (selectedMetric$ | async)?.name | translate}}</div>
</div>
</div>
<div class="buttons">
<div *ngIf="!performanceAspectWrapped?.isLoading">
<div *ngIf="!editMode; else saveOrCancel">
<button class="btn btn-primary edit-button" (click)="edit()">
{{"frontend.default.button.edit.label" | translate}}
</button>
</div>
</div>

<osm-measurand-select [selectedMeasurand]="metric$ | async"
(onSelect)="selectMeasurandForAspect($event)"></osm-measurand-select>
</div>
</div>

<ng-template #measurandSelect>
<ng-container *ngTemplateOutlet="aspectMetricLabel"></ng-container>
<osm-measurand-select [selectedMeasurand]="selectedMetric$ | async"
(onSelect)="selectMeasurandForAspect($event)"></osm-measurand-select>
</ng-template>

<ng-template #saveOrCancel>
<button class="btn btn-warning" (click)="cancel()">
{{"frontend.default.button.cancel.label" | translate}}
</button>
<button class="btn btn-success" (click)="save()">
{{"frontend.default.button.save.label" | translate}}
</button>
</ng-template>

<ng-template #aspectMetricLabel>
<div class="aspect-label">
{{"frontend.de.iteratec.osm.performance-aspect-metric" | translate}}
<span
class="text-info">{{"frontend.de.iteratec.osm.performance-aspect." + performanceAspectWrapped?.data?.performanceAspectType | translate}}</span>
class="text-info">{{"frontend.de.iteratec.osm.performance-aspect." + performanceAspectWrapped?.data?.performanceAspectType + ".description" | translate}}</span>
</div>
</ng-template>

@@ -1,26 +1,32 @@
.edit-container {
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-template-columns: 1fr 3fr;
grid-auto-columns: max-content;
grid-template-rows: 1fr;
grid-gap: 0.8em;
}

.aspect-label{
font-size: 14px;
@media (max-width:768px) {
.edit-container {
grid-template-columns: 2fr 2fr;
}
}

.aspect-label {
font-size: 12px;
color: #555555;
margin: 0 0 2px 2px;
font-weight: lighter;
}

.aspect-type {
grid-column-start: 1;
//grid-column-end: span 2;
grid-column-end: span 1;
}

.measurand {
grid-column-start: 2;
grid-column-end: span 2;
align-self: end;
align-self: start;
}

.buttons {
Expand All @@ -33,3 +39,7 @@
margin: 0 0.5em;
width: 8em;
}

.performance-aspect-type {
font-size: 15px;
}
@@ -1,10 +1,8 @@
import {Component, Input, OnChanges, OnInit} from '@angular/core';
import {Component, EventEmitter, Input, OnChanges, OnInit, Output} from '@angular/core';
import {PerformanceAspect} from "../../../../../models/perfomance-aspect.model";
import {TranslateService} from "@ngx-translate/core";
import {ReplaySubject} from "rxjs";
import {ResponseWithLoadingState} from "../../../../../models/response-with-loading-state.model";
import {SelectableMeasurand} from "../../../../../models/measurand.model";
import {ApplicationService} from "../../../../../services/application.service";

@Component({
selector: 'osm-performance-aspect-inspect',
Expand All @@ -13,51 +11,30 @@ import {ApplicationService} from "../../../../../services/application.service";
})
export class PerformanceAspectInspectComponent implements OnInit, OnChanges {
@Input() performanceAspectWrapped: ResponseWithLoadingState<PerformanceAspect>;
selectedMetric$: ReplaySubject<SelectableMeasurand> = new ReplaySubject<SelectableMeasurand>();
editMode: boolean = false;
performanceAspectInEditing: PerformanceAspect;
@Output() onSelect: EventEmitter<PerformanceAspect> = new EventEmitter<PerformanceAspect>();
metric$: ReplaySubject<SelectableMeasurand> = new ReplaySubject<SelectableMeasurand>();

constructor(private translateService: TranslateService, private applicationService: ApplicationService) {
constructor() {
}

ngOnInit() {
if(this.performanceAspectWrapped){
this.setAspectInEditing();
this.updateSelectedMetric();
}
}

ngOnChanges(){
if(this.performanceAspectWrapped){
this.setAspectInEditing();
this.updateSelectedMetric();
}
}

private updateSelectedMetric() {
this.selectedMetric$.next(this.performanceAspectWrapped.data.measurand);
}

private setAspectInEditing() {
this.performanceAspectInEditing = Object.assign({}, this.performanceAspectWrapped.data);
}

edit() {
this.setAspectInEditing();
this.editMode = true;
}

cancel() {
this.setAspectInEditing();
this.editMode = false;
}

save() {
this.applicationService.createOrUpdatePerformanceAspect(this.performanceAspectInEditing);
this.editMode = false;
this.metric$.next(this.performanceAspectWrapped.data.measurand);
}

selectMeasurandForAspect(measurand: SelectableMeasurand) {
this.performanceAspectInEditing.measurand = measurand;
this.performanceAspectWrapped.data.measurand = measurand;
this.onSelect.emit(this.performanceAspectWrapped.data);
}
}
@@ -1,15 +1,24 @@
<div class="btn">
<div class="btn-init-dialog">
<i class="fas fa-cog" (click)="initDialog()"></i>
</div>

<ngx-smart-modal id="preformanceAspectMgmtModal" identifier="preformanceAspectMgmtModal" [customClass]="'aspect-management-modal'" (onAnyCloseEvent)="cancel()">
<h2>{{"frontend.de.iteratec.osm.performance-aspect.mgmt" | translate}} <span class="text-info">{{pageName}}</span>
<ngx-smart-modal #performanceAspectMgmtModal id="performanceAspectMgmtModal" identifier="performanceAspectMgmtModal"
[customClass]="'aspect-management-modal'" (onAnyCloseEvent)="resetModalData()" force="false">
<h2>
{{"frontend.de.iteratec.osm.performance-aspect.title" | translate}} <span class="text-info">{{ performanceAspectMgmtModal.getData() }}</span>
</h2>
<hr>
<div *ngIf="(performanceAspects$ | async)?.length > 0; else isLoading" class="aspect-management-box">
<osm-performance-aspect-inspect *ngFor="let performanceAspect of (performanceAspects$ | async)"
[performanceAspectWrapped]="performanceAspect"
class="aspect-inspect"></osm-performance-aspect-inspect>
<main>
<div *ngIf="(performanceAspects$ | async)?.length > 0; else isLoading" class="aspect-management-box">
<osm-performance-aspect-inspect *ngFor="let performanceAspect of (performanceAspects$ | async)"
[performanceAspectWrapped]="performanceAspect" (onSelect)="updatePerformanceAspect($event)"
class="aspect-inspect"></osm-performance-aspect-inspect>
</div>
</main>
<hr>
<div class="buttons">
<a (click)="saveAndClose()" class="btn btn-primary">{{ 'frontend.default.button.save' | translate }}</a>
<a (click)="ngxSmartModalService.close('performanceAspectMgmtModal')" class="btn btn-default">{{ 'frontend.default.button.cancel' | translate }}</a>
</div>
</ngx-smart-modal>

Expand Down
@@ -1,17 +1,34 @@
.aspect-management-modal{
max-width: 1200px;
max-width: 910px;
}

.aspect-management-box{
display: grid;
grid-template-rows: 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr;
//grid-gap: 8px;
grid-gap: 0.3em;
grid-template-columns: 1fr 1fr;
grid-gap: 1em;
}

.aspect-inspect{
grid-column-start: 1;
grid-column-end: span 3;
grid-column-end: span 2;
}

.aspect-management-modal > .nsm-content > .nsm-body {
margin: 20px 20px 0 20px;
}

.buttons {
height: 2.5em;
a {
float: right;
width: 100px;
margin: 0 5px 0 10px;
}
}

.btn-init-dialog {
cursor: pointer;
font-size: 14px;
color: rgb(189, 188, 188);
}
Expand Up @@ -16,22 +16,38 @@ export class PerformanceAspectManagementComponent implements OnInit {
@Input() pageId: number;
@Input() pageName: string;
performanceAspects$: Subject<ResponseWithLoadingState<PerformanceAspect>[]>;
changedMetrics: Map<string, PerformanceAspect>;

constructor(private ngxSmartModalService: NgxSmartModalService, private measurandsService: ResultSelectionService, private applicationService: ApplicationService) {
this.performanceAspects$ = this.applicationService.performanceAspectForPage$
constructor(public ngxSmartModalService: NgxSmartModalService, private measurandsService: ResultSelectionService, private applicationService: ApplicationService) {
this.performanceAspects$ = this.applicationService.performanceAspectForPage$;
}

ngOnInit() {
this.changedMetrics = new Map<string, PerformanceAspect>();
}

initDialog(){
this.measurandsService.updatePages([{id: this.pageId, name: "does-not-matter"}]);
this.applicationService.updatePage({id: this.pageId, name: "does-not-matter"});
this.ngxSmartModalService.open('preformanceAspectMgmtModal');
initDialog() {
this.ngxSmartModalService.setModalData(this.pageName, "performanceAspectMgmtModal");
this.measurandsService.updatePages([{id: this.pageId, name: this.pageName}]);
this.applicationService.updatePage({id: this.pageId, name: this.pageName});
this.ngxSmartModalService.open('performanceAspectMgmtModal');
}

cancel(){
resetModalData() {
this.changedMetrics.clear();
this.ngxSmartModalService.resetModalData('performanceAspectMgmtModal');
}

updatePerformanceAspect(performanceAspect: PerformanceAspect) {
let key: string = performanceAspect.jobGroupId + "." + performanceAspect.pageId + "." + performanceAspect.performanceAspectType;
this.changedMetrics.set(key, performanceAspect);
}

saveAndClose() {
this.changedMetrics.forEach((performanceAspect: PerformanceAspect) => {
this.applicationService.createOrUpdatePerformanceAspect(performanceAspect);
});
this.ngxSmartModalService.close('performanceAspectMgmtModal');
}

}
@@ -0,0 +1,9 @@
export interface BrowserDto {
id: number
name: string
}
export interface LocationDto {
id: Number
name: string
parent: BrowserDto
}
38 changes: 26 additions & 12 deletions frontend/src/app/modules/landing/landing.component.spec.ts
Expand Up @@ -8,7 +8,6 @@ import {By} from "@angular/platform-browser";
import {Application} from "../../models/application.model";
import {CsiValueSmallComponent} from "../shared/components/csi-value/csi-value-small/csi-value-small.component";
import {ApplicationCsi} from "../../models/application-csi.model";
import { of } from 'rxjs';

describe('LandingComponent', () => {
let component: LandingComponent;
Expand Down Expand Up @@ -79,40 +78,55 @@ describe('LandingComponent', () => {
expect(fixture.debugElement.query(By.css(".clickable-list"))).toBeTruthy();
const links = fixture.debugElement.queryAll(By.css(".clickable-list a"));
expect(links.length).toBe(2);
expect(links[0].nativeElement.href).toMatch(".*/applicationDashboard/1");
expect(links[0].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestOne");

const linkHrefs = [".*/applicationDashboard/1", ".*/applicationDashboard/2"];
const linkTitles = ["TestOne", "TestTwo"];
expect(links.some(element => element.nativeElement.href.match(linkHrefs[0])));
expect(links.some(element => element.query(By.css(".title")).nativeElement.textContent.trim().match(linkTitles[0])));
expect(links.some(element => element.nativeElement.href.match(linkHrefs[1])));
expect(links.some(element => element.query(By.css(".title")).nativeElement.textContent.trim().match(linkTitles[1])));
expect(links[0].query(By.directive(CsiValueSmallComponent)).componentInstance.showLoading).toBeTruthy();
expect(links[1].nativeElement.href).toMatch(".*/applicationDashboard/2");
expect(links[1].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestTwo");
expect(links[1].query(By.directive(CsiValueSmallComponent)).componentInstance.showLoading).toBeTruthy();
});

it('should show a list of applications if existing, with CSI values', () => {
it('should show a list of applications if existing, with CSI values (in descending order)', () => {
applicationService.applications$.next({
isLoading: false,
data: [
new Application({id: 1, name: "TestOne"}),
new Application({id: 2, name: "TestTwo"})
new Application({id: 2, name: "TestTwo"}),
new Application({id: 3, name: "TestThree"}),
new Application({id: 4, name: "TestFour"})
]
});
applicationService.applicationCsiById$.next({
isLoading: false,
1: new ApplicationCsi({csiValues: [{csiDocComplete: 50}, {csiDocComplete: 60}]}),
2: new ApplicationCsi({csiValues: [{csiDocComplete: 60}, {csiDocComplete: 70}]})
2: new ApplicationCsi({csiValues: [{csiDocComplete: 60}, {csiDocComplete: 70}]}),
3: new ApplicationCsi({csiValues: [{csiDocComplete: 70}, {csiDocComplete: 80}]}),
4: new ApplicationCsi({csiValues: []})
});
fixture.detectChanges();
expect(fixture.debugElement.query(By.css("main")).classes.center).toBeFalsy();
expect(fixture.debugElement.query(By.css(".clickable-list"))).toBeTruthy();
const links = fixture.debugElement.queryAll(By.css(".clickable-list a"));
expect(links.length).toBe(2);
expect(links[0].nativeElement.href).toMatch(".*/applicationDashboard/1");
expect(links[0].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestOne");
expect(links.length).toBe(4);
expect(links[0].nativeElement.href).toMatch(".*/applicationDashboard/3");
expect(links[0].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestThree");
expect(links[0].query(By.directive(CsiValueSmallComponent)).componentInstance.showLoading).toBeFalsy();
expect(links[0].query(By.directive(CsiValueSmallComponent)).componentInstance.csiValue).toEqual(60);
expect(links[0].query(By.directive(CsiValueSmallComponent)).componentInstance.csiValue).toEqual(80);
expect(links[1].nativeElement.href).toMatch(".*/applicationDashboard/2");
expect(links[1].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestTwo");
expect(links[1].query(By.directive(CsiValueSmallComponent)).componentInstance.showLoading).toBeFalsy();
expect(links[1].query(By.directive(CsiValueSmallComponent)).componentInstance.csiValue).toEqual(70);
expect(links[2].nativeElement.href).toMatch(".*/applicationDashboard/1");
expect(links[2].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestOne");
expect(links[2].query(By.directive(CsiValueSmallComponent)).componentInstance.showLoading).toBeFalsy();
expect(links[2].query(By.directive(CsiValueSmallComponent)).componentInstance.csiValue).toEqual(60);
expect(links[3].nativeElement.href).toMatch(".*/applicationDashboard/4");
expect(links[3].query(By.css(".title")).nativeElement.textContent.trim()).toEqual("TestFour");
expect(links[3].query(By.directive(CsiValueSmallComponent)).componentInstance.showLoading).toBeFalsy();
expect(links[3].query(By.directive(CsiValueSmallComponent)).componentInstance.csiValue).toEqual(undefined);
});

it('should show a button to view all jobs', () => {
Expand Down
10 changes: 9 additions & 1 deletion frontend/src/app/modules/landing/landing.component.ts
Expand Up @@ -26,7 +26,15 @@ export class LandingComponent {
);
this.applications$ = combineLatest(this.applicationService.applications$, this.applicationService.applicationCsiById$).pipe(
filter(([applications]) => !applications.isLoading && !!applications.data),
map(([applications, csiById]) => applications.data.map(app => new ApplicationWithCsi(app, csiById[app.id], csiById.isLoading)))
map(([applications, csiById]) => applications.data.map(app => new ApplicationWithCsi(app, csiById[app.id], csiById.isLoading)).sort((a, b) => {
if (!b.recentCsi.csiDocComplete) {
return -1;
} else if (!a.recentCsi.csiDocComplete && !b.recentCsi.csiDocComplete) {
return 0;
} else {
return b.recentCsi.csiDocComplete - a.recentCsi.csiDocComplete;
}
}))
);
this.applicationService.loadApplications();
this.applicationService.loadRecentCsiForApplications();
Expand Down

0 comments on commit 95eeeda

Please sign in to comment.