diff --git a/frontend/src/app/diagram/diagram.component.html b/frontend/src/app/diagram/diagram.component.html
index e6594ae99e..2491b38045 100644
--- a/frontend/src/app/diagram/diagram.component.html
+++ b/frontend/src/app/diagram/diagram.component.html
@@ -1,6 +1,9 @@
-
+
Kein Alignment vorhanden
diff --git a/frontend/src/app/diagram/diagram.component.spec.ts b/frontend/src/app/diagram/diagram.component.spec.ts
index 2a9d737409..4b6a6b0285 100644
--- a/frontend/src/app/diagram/diagram.component.spec.ts
+++ b/frontend/src/app/diagram/diagram.component.spec.ts
@@ -44,7 +44,7 @@ describe('DiagramComponent', () => {
component.prepareDiagramData(alignmentLists);
expect(component.generateNodes).toHaveBeenCalled();
- expect(component.noAlignmentData).toBeFalsy();
+ expect(component.alignmentDataCache?.alignmentObjectDtoList.length).not.toEqual(0);
});
it('should not call generateElements if alignmentData is empty', () => {
@@ -57,7 +57,6 @@ describe('DiagramComponent', () => {
component.prepareDiagramData(alignmentLists);
expect(component.generateNodes).not.toHaveBeenCalled();
- expect(component.noAlignmentData).toBeTruthy();
});
it('should call prepareDiagramData when Subject receives new data', () => {
diff --git a/frontend/src/app/diagram/diagram.component.ts b/frontend/src/app/diagram/diagram.component.ts
index 1fa6bc43d7..4883cb248b 100644
--- a/frontend/src/app/diagram/diagram.component.ts
+++ b/frontend/src/app/diagram/diagram.component.ts
@@ -1,5 +1,5 @@
import { AfterViewInit, Component, Input, OnDestroy } from '@angular/core';
-import { map, Observable, Subject, zip } from 'rxjs';
+import { map, Observable, of, Subject, zip } from 'rxjs';
import { AlignmentLists } from '../shared/types/model/AlignmentLists';
import cytoscape from 'cytoscape';
import {
@@ -19,6 +19,8 @@ import { KeyResultOrdinal } from '../shared/types/model/KeyResultOrdinal';
import { Router } from '@angular/router';
import { AlignmentObject } from '../shared/types/model/AlignmentObject';
import { AlignmentConnection } from '../shared/types/model/AlignmentConnection';
+import { Zone } from '../shared/types/enums/Zone';
+import { ObjectiveState } from '../shared/types/enums/ObjectiveState';
@Component({
selector: 'app-diagram',
@@ -29,7 +31,6 @@ export class DiagramComponent implements AfterViewInit, OnDestroy {
private alignmentData$: Subject
= new Subject();
cy!: cytoscape.Core;
diagramData: any[] = [];
- noAlignmentData: boolean = false;
alignmentDataCache: AlignmentLists | null = null;
constructor(
@@ -51,7 +52,7 @@ export class DiagramComponent implements AfterViewInit, OnDestroy {
let lastAlignmentItem: AlignmentObject =
alignmentData.alignmentObjectDtoList[alignmentData.alignmentObjectDtoList.length - 1];
- let diagramReloadRequired: boolean =
+ const diagramReloadRequired: boolean =
lastAlignmentItem?.objectTitle === 'reload'
? lastAlignmentItem?.objectType === 'true'
: JSON.stringify(this.alignmentDataCache) !== JSON.stringify(alignmentData);
@@ -70,6 +71,7 @@ export class DiagramComponent implements AfterViewInit, OnDestroy {
ngOnDestroy(): void {
this.cleanUpDiagram();
+ this.alignmentData.unsubscribe();
}
generateDiagram(): void {
@@ -140,10 +142,7 @@ export class DiagramComponent implements AfterViewInit, OnDestroy {
}
prepareDiagramData(alignmentData: AlignmentLists): void {
- if (alignmentData.alignmentObjectDtoList.length == 0) {
- this.noAlignmentData = true;
- } else {
- this.noAlignmentData = false;
+ if (alignmentData.alignmentObjectDtoList.length != 0) {
this.generateNodes(alignmentData);
}
}
@@ -153,24 +152,20 @@ export class DiagramComponent implements AfterViewInit, OnDestroy {
let diagramElements: any[] = [];
alignmentData.alignmentObjectDtoList.forEach((alignmentObject: AlignmentObject) => {
if (alignmentObject.objectType == 'objective') {
- let observable: Observable = new Observable((observer) => {
- let node = {
- data: {
- id: 'Ob' + alignmentObject.objectId,
- },
- style: {
- 'background-image': this.generateObjectiveSVG(
- alignmentObject.objectTitle,
- alignmentObject.objectTeamName,
- alignmentObject.objectState!,
- ),
- },
- };
- diagramElements.push(node);
- observer.next(node);
- observer.complete();
- });
- observableArray.push(observable);
+ let node = {
+ data: {
+ id: 'Ob' + alignmentObject.objectId,
+ },
+ style: {
+ 'background-image': this.generateObjectiveSVG(
+ alignmentObject.objectTitle,
+ alignmentObject.objectTeamName,
+ alignmentObject.objectState!,
+ ),
+ },
+ };
+ diagramElements.push(node);
+ observableArray.push(of(node));
} else {
let observable: Observable = this.keyResultService.getFullKeyResult(alignmentObject.objectId).pipe(
map((keyResult: KeyResult) => {
@@ -248,11 +243,11 @@ export class DiagramComponent implements AfterViewInit, OnDestroy {
generateObjectiveSVG(title: string, teamName: string, state: string): string {
switch (state) {
- case 'ONGOING':
+ case ObjectiveState.ONGOING:
return generateObjectiveSVG(title, teamName, getOnGoingIcon);
- case 'SUCCESSFUL':
+ case ObjectiveState.SUCCESSFUL:
return generateObjectiveSVG(title, teamName, getSuccessfulIcon);
- case 'NOTSUCCESSFUL':
+ case ObjectiveState.NOTSUCCESSFUL:
return generateObjectiveSVG(title, teamName, getNotSuccessfulIcon);
default:
return generateObjectiveSVG(title, teamName, getDraftIcon);
@@ -261,13 +256,13 @@ export class DiagramComponent implements AfterViewInit, OnDestroy {
generateKeyResultSVG(title: string, teamName: string, state: string | undefined): string {
switch (state) {
- case 'FAIL':
+ case Zone.FAIL:
return generateKeyResultSVG(title, teamName, '#BA3838', 'white');
- case 'COMMIT':
+ case Zone.COMMIT:
return generateKeyResultSVG(title, teamName, '#FFD600', 'black');
- case 'TARGET':
+ case Zone.TARGET:
return generateKeyResultSVG(title, teamName, '#1E8A29', 'black');
- case 'STRETCH':
+ case Zone.STRETCH:
return generateKeyResultSVG(title, teamName, '#1E5A96', 'white');
default:
return generateNeutralKeyResultSVG(title, teamName);
diff --git a/frontend/src/app/objective-detail/objective-detail.component.ts b/frontend/src/app/objective-detail/objective-detail.component.ts
index 03bd8735e3..747bd70ea0 100644
--- a/frontend/src/app/objective-detail/objective-detail.component.ts
+++ b/frontend/src/app/objective-detail/objective-detail.component.ts
@@ -65,11 +65,9 @@ export class ObjectiveDetailComponent {
.subscribe((result) => {
if (result?.openNew) {
this.openAddKeyResultDialog();
- } else if (result == '' || result == undefined) {
return;
- } else {
- this.refreshDataService.markDataRefresh();
}
+ this.refreshDataService.markDataRefresh();
});
}
diff --git a/frontend/src/app/overview/overview.component.spec.ts b/frontend/src/app/overview/overview.component.spec.ts
index e38f09d652..c047057d24 100644
--- a/frontend/src/app/overview/overview.component.spec.ts
+++ b/frontend/src/app/overview/overview.component.spec.ts
@@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OverviewComponent } from './overview.component';
import { HttpClientTestingModule } from '@angular/common/http/testing';
-import { overViewEntity1 } from '../shared/testData';
+import { alignmentLists, overViewEntity1 } from '../shared/testData';
import { BehaviorSubject, of, Subject } from 'rxjs';
import { OverviewService } from '../shared/services/overview.service';
import { AppRoutingModule } from '../app-routing.module';
@@ -16,11 +16,16 @@ import { MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
+import { AlignmentService } from '../shared/services/alignment.service';
const overviewService = {
getOverview: jest.fn(),
};
+const alignmentService = {
+ getAlignmentByFilter: jest.fn(),
+};
+
const authGuardMock = () => {
return Promise.resolve(true);
};
@@ -53,6 +58,10 @@ describe('OverviewComponent', () => {
provide: OverviewService,
useValue: overviewService,
},
+ {
+ provide: AlignmentService,
+ useValue: alignmentService,
+ },
{
provide: authGuard,
useValue: authGuardMock,
@@ -132,6 +141,23 @@ describe('OverviewComponent', () => {
expect(component.loadOverview).toHaveBeenLastCalledWith();
});
+ it('should call overviewService on overview', async () => {
+ jest.spyOn(overviewService, 'getOverview');
+ component.isOverview = true;
+
+ component.loadOverview(3, [5, 6], '', null);
+ expect(overviewService.getOverview).toHaveBeenCalled();
+ });
+
+ it('should call alignmentService on diagram', async () => {
+ jest.spyOn(alignmentService, 'getAlignmentByFilter').mockReturnValue(of(alignmentLists));
+ component.isOverview = false;
+ fixture.detectChanges();
+
+ component.loadOverview(3, [5, 6], '', null);
+ expect(alignmentService.getAlignmentByFilter).toHaveBeenCalled();
+ });
+
function markFiltersAsReady() {
refreshDataServiceMock.quarterFilterReady.next(null);
refreshDataServiceMock.teamFilterReady.next(null);
diff --git a/frontend/src/app/overview/overview.component.ts b/frontend/src/app/overview/overview.component.ts
index 25ac21ff80..cc97822235 100644
--- a/frontend/src/app/overview/overview.component.ts
+++ b/frontend/src/app/overview/overview.component.ts
@@ -69,45 +69,53 @@ export class OverviewComponent implements OnInit, OnDestroy {
this.loadOverview(quarterId, teamIds, objectiveQueryString, reload);
}
- loadOverview(quarterId?: number, teamIds?: number[], objectiveQuery?: string, reload?: boolean | null) {
+ loadOverview(quarterId?: number, teamIds?: number[], objectiveQuery?: string, reload?: boolean | null): void {
if (this.isOverview) {
- this.overviewService
- .getOverview(quarterId, teamIds, objectiveQuery)
- .pipe(
- catchError(() => {
- this.loadOverview();
- return EMPTY;
- }),
- )
- .subscribe((dashboard) => {
- this.hasAdminAccess.next(dashboard.adminAccess);
- this.overviewEntities$.next(dashboard.overviews);
- });
+ this.loadOverviewData(quarterId, teamIds, objectiveQuery);
} else {
- this.alignmentService
- .getAlignmentByFilter(quarterId, teamIds, objectiveQuery)
- .pipe(
- catchError(() => {
- this.loadOverview();
- return EMPTY;
- }),
- )
- .subscribe((alignmentLists: AlignmentLists) => {
- if (reload != null) {
- let alignmentObjectReload: AlignmentObject = {
- objectId: 0,
- objectTitle: 'reload',
- objectType: reload.toString(),
- objectTeamName: '',
- objectState: null,
- };
- alignmentLists.alignmentObjectDtoList.push(alignmentObjectReload);
- }
- this.alignmentLists$.next(alignmentLists);
- });
+ this.loadAlignmentData(quarterId, teamIds, objectiveQuery, reload);
}
}
+ loadOverviewData(quarterId?: number, teamIds?: number[], objectiveQuery?: string): void {
+ this.overviewService
+ .getOverview(quarterId, teamIds, objectiveQuery)
+ .pipe(
+ catchError(() => {
+ this.loadOverview();
+ return EMPTY;
+ }),
+ )
+ .subscribe((dashboard) => {
+ this.hasAdminAccess.next(dashboard.adminAccess);
+ this.overviewEntities$.next(dashboard.overviews);
+ });
+ }
+
+ loadAlignmentData(quarterId?: number, teamIds?: number[], objectiveQuery?: string, reload?: boolean | null): void {
+ this.alignmentService
+ .getAlignmentByFilter(quarterId, teamIds, objectiveQuery)
+ .pipe(
+ catchError(() => {
+ this.loadOverview();
+ return EMPTY;
+ }),
+ )
+ .subscribe((alignmentLists: AlignmentLists) => {
+ if (reload != null) {
+ let alignmentObjectReload: AlignmentObject = {
+ objectId: 0,
+ objectTitle: 'reload',
+ objectType: reload.toString(),
+ objectTeamName: '',
+ objectState: null,
+ };
+ alignmentLists.alignmentObjectDtoList.push(alignmentObjectReload);
+ }
+ this.alignmentLists$.next(alignmentLists);
+ });
+ }
+
ngOnDestroy(): void {
this.destroyed$.next(true);
this.destroyed$.complete();
diff --git a/frontend/src/app/shared/types/enums/ObjectiveState.ts b/frontend/src/app/shared/types/enums/ObjectiveState.ts
new file mode 100644
index 0000000000..342fa5a49d
--- /dev/null
+++ b/frontend/src/app/shared/types/enums/ObjectiveState.ts
@@ -0,0 +1,6 @@
+export enum ObjectiveState {
+ DRAFT = 'DRAFT',
+ ONGOING = 'ONGOING',
+ SUCCESSFUL = 'SUCCESSFUL',
+ NOTSUCCESSFUL = 'NOTSUCCESSFUL',
+}