From 2cbe29b68672ded00538a13e3caac849832f2ff9 Mon Sep 17 00:00:00 2001 From: megli2 Date: Mon, 9 Oct 2023 15:38:15 +0200 Subject: [PATCH 01/63] create sidepanel component and add additional route to routing module --- frontend/src/app/app-routing.module.ts | 33 ++++++++++++++--- frontend/src/app/app.module.ts | 2 + .../custom/sidepanel/sidepanel.component.html | 18 +++++++++ .../custom/sidepanel/sidepanel.component.scss | 23 ++++++++++++ .../sidepanel/sidepanel.component.spec.ts | 21 +++++++++++ .../custom/sidepanel/sidepanel.component.ts | 37 +++++++++++++++++++ 6 files changed, 129 insertions(+), 5 deletions(-) create mode 100644 frontend/src/app/shared/custom/sidepanel/sidepanel.component.html create mode 100644 frontend/src/app/shared/custom/sidepanel/sidepanel.component.scss create mode 100644 frontend/src/app/shared/custom/sidepanel/sidepanel.component.spec.ts create mode 100644 frontend/src/app/shared/custom/sidepanel/sidepanel.component.ts diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index af826b7ce3..cf7915948b 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -1,16 +1,39 @@ import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { ActivatedRouteSnapshot, ResolveFn, RouterModule, Routes } from '@angular/router'; import { OverviewComponent } from './overview/overview.component'; -import { ObjectiveDetailComponent } from './objective-detail/objective-detail.component'; -import { KeyresultDetailComponent } from './keyresult-detail/keyresult-detail.component'; +import { EMPTY, of } from 'rxjs'; +import { SidepanelComponent } from './shared/custom/sidepanel/sidepanel.component'; + +/** + * Resolver for get the id from url like `/objective/42` or `/keyresult/42`. + */ +export const getIdFromPathResolver: ResolveFn = (route: ActivatedRouteSnapshot) => { + try { + let id = Number.parseInt(route.url[1].path); + return of(id); + } catch (error) { + console.error('Can not get id from URL:', error); + return EMPTY; + } +}; const routes: Routes = [ { path: '', component: OverviewComponent, children: [ - { path: 'objective/:id', component: ObjectiveDetailComponent, pathMatch: 'full' }, - { path: 'keyresult/:id', component: KeyresultDetailComponent, pathMatch: 'full' }, + { + path: 'objective/:id', + component: SidepanelComponent, + resolve: { id: getIdFromPathResolver }, + data: { type: 'Objective' }, + }, + { + path: 'keyresult/:id', + component: SidepanelComponent, + resolve: { id: getIdFromPathResolver }, + data: { type: 'KeyResult' }, + }, ], }, { path: '**', redirectTo: '', pathMatch: 'full' }, diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 97185ee803..ea6b1eb960 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -60,6 +60,7 @@ import { UnitLabelTransformationPipe } from './shared/pipes/unit-label-transform import { ParseUnitValuePipe } from './shared/pipes/parse-unit-value/parse-unit-value.pipe'; import { ScoringComponent } from './shared/scoring/scoring/scoring.component'; import { QuarterFilterComponent } from './quarter-filter/quarter-filter.component'; +import { SidepanelComponent } from './shared/custom/sidepanel/sidepanel.component'; function initOauthFactory(configService: ConfigService, oauthService: OAuthService) { return async () => { @@ -118,6 +119,7 @@ export const MY_FORMATS = { ParseUnitValuePipe, ObjectiveFormComponent, QuarterFilterComponent, + SidepanelComponent, ], imports: [ CommonModule, diff --git a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html new file mode 100644 index 0000000000..136ba82ba6 --- /dev/null +++ b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html @@ -0,0 +1,18 @@ + diff --git a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.scss b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.scss new file mode 100644 index 0000000000..f0a31205e5 --- /dev/null +++ b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.scss @@ -0,0 +1,23 @@ + +.sidebar { + display: block; + opacity: 1; + background-color: #f1f1f1; + height: 100vh; + position: fixed; + overflow-y: auto; + z-index: 101; + top: 48px; + right: -600px; + min-width: 300px; + max-width: 600px; +} + +.sidebar-show { + transition: right 200ms; + right: 0; +} + +.sidebar__content { + padding: 8px; +} diff --git a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.spec.ts b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.spec.ts new file mode 100644 index 0000000000..56d9c6cf45 --- /dev/null +++ b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SidepanelComponent } from './sidepanel.component'; + +describe('SidepanelComponent', () => { + let component: SidepanelComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [SidepanelComponent] + }); + fixture = TestBed.createComponent(SidepanelComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.ts b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.ts new file mode 100644 index 0000000000..e06eed84e2 --- /dev/null +++ b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.ts @@ -0,0 +1,37 @@ +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { map, Observable } from 'rxjs'; +import { Objective } from '../../types/model/Objective'; + +@Component({ + selector: 'app-sidepanel', + templateUrl: './sidepanel.component.html', + styleUrls: ['./sidepanel.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class SidepanelComponent implements OnInit, AfterViewInit { + @ViewChild('sidebar') + sidebar!: ElementRef; + + modelType = ''; + id$!: Observable; + + constructor(private activatedRoute: ActivatedRoute) {} + + ngOnInit(): void { + this.activatedRoute.data.subscribe((data) => (this.modelType = data['type'])); + this.id$ = this.activatedRoute.data.pipe(map((data) => data['id'])); + } + + ngAfterViewInit(): void { + setTimeout(this.displaySidebar, 0, this.sidebar); + } + + displaySidebar(sidebar: ElementRef) { + sidebar.nativeElement.classList.add('sidebar-show'); + } + + getId(): Observable { + return this.id$ as Observable; + } +} From 24743664f0343700313e3726994e819435de57e9 Mon Sep 17 00:00:00 2001 From: megli2 Date: Mon, 9 Oct 2023 15:38:39 +0200 Subject: [PATCH 02/63] format new generated component --- .../app/shared/custom/sidepanel/sidepanel.component.html | 6 +++--- .../app/shared/custom/sidepanel/sidepanel.component.scss | 1 - .../app/shared/custom/sidepanel/sidepanel.component.spec.ts | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html index 136ba82ba6..12dcb43ea2 100644 --- a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html +++ b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html @@ -10,9 +10,9 @@

Sidepanel

Kein passender switch case!
- - - + + +
diff --git a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.scss b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.scss index f0a31205e5..35c047b8a2 100644 --- a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.scss +++ b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.scss @@ -1,4 +1,3 @@ - .sidebar { display: block; opacity: 1; diff --git a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.spec.ts b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.spec.ts index 56d9c6cf45..447a656e06 100644 --- a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.spec.ts +++ b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.spec.ts @@ -8,7 +8,7 @@ describe('SidepanelComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ - declarations: [SidepanelComponent] + declarations: [SidepanelComponent], }); fixture = TestBed.createComponent(SidepanelComponent); component = fixture.componentInstance; From 73fb77b624d70b11326d8d4b0ac183efd6d30f1c Mon Sep 17 00:00:00 2001 From: megli2 Date: Mon, 9 Oct 2023 15:44:03 +0200 Subject: [PATCH 03/63] create refresh-data service --- .../shared/services/refresh-data.service.spec.ts | 16 ++++++++++++++++ .../app/shared/services/refresh-data.service.ts | 13 +++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 frontend/src/app/shared/services/refresh-data.service.spec.ts create mode 100644 frontend/src/app/shared/services/refresh-data.service.ts diff --git a/frontend/src/app/shared/services/refresh-data.service.spec.ts b/frontend/src/app/shared/services/refresh-data.service.spec.ts new file mode 100644 index 0000000000..0c95fb8b3c --- /dev/null +++ b/frontend/src/app/shared/services/refresh-data.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { RefreshDataService } from './refresh-data.service'; + +describe('RefreshDataService', () => { + let service: RefreshDataService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(RefreshDataService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/shared/services/refresh-data.service.ts b/frontend/src/app/shared/services/refresh-data.service.ts new file mode 100644 index 0000000000..5a34437143 --- /dev/null +++ b/frontend/src/app/shared/services/refresh-data.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs'; + +@Injectable({ + providedIn: 'root', +}) +export class RefreshDataService { + public reloadOverviewSubject: Subject = new Subject(); + + markDataRefresh() { + this.reloadOverviewSubject.next(); + } +} From bd921188540e0f703ed8a4e6171b0516262dee58 Mon Sep 17 00:00:00 2001 From: megli2 Date: Mon, 9 Oct 2023 16:08:27 +0200 Subject: [PATCH 04/63] implement objective detail in sidepanel and delete old drawer component --- frontend/src/app/app.component.html | 28 +----- frontend/src/app/app.component.ts | 20 ++--- frontend/src/app/app.module.ts | 2 - .../drawer-content.component.html | 8 -- .../drawer-content.component.scss | 7 -- .../drawer-content.component.spec.ts | 39 --------- .../drawer-content.component.ts | 12 --- .../objective-detail.component.html | 29 ++++--- .../objective-detail.component.ts | 85 +++++++++++-------- .../src/app/overview/overview.component.html | 2 + .../custom/sidepanel/sidepanel.component.html | 6 +- 11 files changed, 81 insertions(+), 157 deletions(-) delete mode 100644 frontend/src/app/drawer-content/drawer-content.component.html delete mode 100644 frontend/src/app/drawer-content/drawer-content.component.scss delete mode 100644 frontend/src/app/drawer-content/drawer-content.component.spec.ts delete mode 100644 frontend/src/app/drawer-content/drawer-content.component.ts diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index 4459bc65ab..7667467553 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -1,28 +1,6 @@
- - - - - - - -
- -
-
+ +
+
diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 31dee870a8..e510e5267f 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -49,16 +49,16 @@ export class AppComponent implements OnInit, OnDestroy { map((event) => event as NavigationEnd), ) .subscribe((event) => { - drawerRoutes.forEach((route) => { - if (event.url.startsWith(`/${route}/`)) { - const match = event.url.match(ROUTE_PARAM_REGEX); - if (match) { - const id = parseInt(match[1]); - this.sidenavContentInformation = { id: id, type: route }; - this.openDrawer(); - } - } - }); + // drawerRoutes.forEach((route) => { + // if (event.url.startsWith(`/${route}/`)) { + // const match = event.url.match(ROUTE_PARAM_REGEX); + // if (match) { + // const id = parseInt(match[1]); + // this.sidenavContentInformation = { id: id, type: route }; + // this.openDrawer(); + // } + // } + // }); }); this.notifierService.closeDetailSubject.subscribe(() => { diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index ea6b1eb960..48f380b536 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -39,7 +39,6 @@ import { KeyresultComponent } from './keyresult/keyresult.component'; import { KeyresultDetailComponent } from './keyresult-detail/keyresult-detail.component'; import { ObjectiveDetailComponent } from './objective-detail/objective-detail.component'; import { MatSidenavModule } from '@angular/material/sidenav'; -import { DrawerContentComponent } from './drawer-content/drawer-content.component'; import { ConfidenceComponent } from './confidence/confidence.component'; import { MatSliderModule } from '@angular/material/slider'; import { ErrorInterceptor } from './shared/interceptors/error-interceptor.service'; @@ -103,7 +102,6 @@ export const MY_FORMATS = { ScoringComponent, KeyresultDetailComponent, ObjectiveDetailComponent, - DrawerContentComponent, ApplicationBannerComponent, KeyResultDialogComponent, ConfirmDialogComponent, diff --git a/frontend/src/app/drawer-content/drawer-content.component.html b/frontend/src/app/drawer-content/drawer-content.component.html deleted file mode 100644 index 99304f48a8..0000000000 --- a/frontend/src/app/drawer-content/drawer-content.component.html +++ /dev/null @@ -1,8 +0,0 @@ -
- - - - - - -
diff --git a/frontend/src/app/drawer-content/drawer-content.component.scss b/frontend/src/app/drawer-content/drawer-content.component.scss deleted file mode 100644 index 31a167c818..0000000000 --- a/frontend/src/app/drawer-content/drawer-content.component.scss +++ /dev/null @@ -1,7 +0,0 @@ -.drawer-content { - width: 95%; -} - -.close-icon { - width: 5%; -} diff --git a/frontend/src/app/drawer-content/drawer-content.component.spec.ts b/frontend/src/app/drawer-content/drawer-content.component.spec.ts deleted file mode 100644 index e34b7a24e0..0000000000 --- a/frontend/src/app/drawer-content/drawer-content.component.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { DrawerContentComponent } from './drawer-content.component'; -import { By } from '@angular/platform-browser'; -import { ObjectiveDetailComponent } from '../objective-detail/objective-detail.component'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { MatDialogModule } from '@angular/material/dialog'; - -describe('DrawerContentComponent', () => { - let component: DrawerContentComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, MatDialogModule], - declarations: [DrawerContentComponent, ObjectiveDetailComponent], - }).compileComponents(); - - fixture = TestBed.createComponent(DrawerContentComponent); - component = fixture.componentInstance; - component.drawerContent = { id: 1, type: 'objective' }; - }); - - it('should create', () => { - fixture.detectChanges(); - expect(component).toBeTruthy(); - }); - - it.each([['objective', 'app-objective-detail']])( - 'should display right component', - (type: string, selector: string) => { - component.drawerContent = { id: 1, type: type }; - fixture.detectChanges(); - const contentComponent = fixture.debugElement.query(By.css(selector)); - - expect(contentComponent).toBeTruthy(); - }, - ); -}); diff --git a/frontend/src/app/drawer-content/drawer-content.component.ts b/frontend/src/app/drawer-content/drawer-content.component.ts deleted file mode 100644 index d7c6e6acef..0000000000 --- a/frontend/src/app/drawer-content/drawer-content.component.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { NotifierService } from '../shared/services/notifier.service'; - -@Component({ - selector: 'app-drawer-content', - templateUrl: './drawer-content.component.html', - styleUrls: ['./drawer-content.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class DrawerContentComponent { - @Input() drawerContent!: { id: number; type: string }; -} diff --git a/frontend/src/app/objective-detail/objective-detail.component.html b/frontend/src/app/objective-detail/objective-detail.component.html index dd2d053ba2..c46a22457b 100644 --- a/frontend/src/app/objective-detail/objective-detail.component.html +++ b/frontend/src/app/objective-detail/objective-detail.component.html @@ -1,15 +1,16 @@ -
-
-
{{ objective.title }}
- close + +
+
+
{{ objective.title }}
+ close +
+
Beschrieb
+

{{ objective.description }}

+
+ +
-
Beschrieb
-

{{ objective.description }}

- -
- -
-
+ diff --git a/frontend/src/app/objective-detail/objective-detail.component.ts b/frontend/src/app/objective-detail/objective-detail.component.ts index 14b2c26dbf..1109eb6618 100644 --- a/frontend/src/app/objective-detail/objective-detail.component.ts +++ b/frontend/src/app/objective-detail/objective-detail.component.ts @@ -1,9 +1,8 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, AfterViewInit } from '@angular/core'; -import { Objective } from '../shared/types/model/Objective'; -import { ObjectiveService } from '../shared/services/objective.service'; -import { KeyResultDialogComponent } from '../key-result-dialog/key-result-dialog.component'; -import { MatDialog } from '@angular/material/dialog'; -import { NotifierService } from '../shared/services/notifier.service'; +import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; +import {Objective} from '../shared/types/model/Objective'; +import {ObjectiveService} from '../shared/services/objective.service'; +import {NotifierService} from '../shared/services/notifier.service'; +import {catchError, Observable, Subject} from "rxjs"; @Component({ selector: 'app-objective-detail', @@ -11,47 +10,59 @@ import { NotifierService } from '../shared/services/notifier.service'; styleUrls: ['./objective-detail.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class ObjectiveDetailComponent implements AfterViewInit { - @Input() objectiveId!: number; - objective!: Objective; +export class ObjectiveDetailComponent { + @Input() + objectiveId$!: Observable; + objective$: Subject = new Subject(); constructor( private objectiveService: ObjectiveService, - private changeDetectorRef: ChangeDetectorRef, - private dialog: MatDialog, private notifierService: NotifierService, ) {} - ngAfterViewInit(): void { - this.objectiveService.getFullObjective(this.objectiveId).subscribe((fullObjective) => { - this.objective = fullObjective; - this.changeDetectorRef.markForCheck(); + + ngOnInit(): void { + this.objectiveId$.subscribe((id) => { + this.loadObjective(id); }); } - openAddKeyResultDialog() { - this.dialog - .open(KeyResultDialogComponent, { - width: '45em', - height: 'auto', - data: { - objective: this.objective, - keyResult: null, - }, - }) - .afterClosed() - .subscribe(async (result) => { - await this.notifierService.keyResultsChanges.next({ - keyResult: result.keyResult, - changeId: null, - objective: result.objective, - delete: false, - }); + loadObjective(id: number): void { + this.objectiveService + .getFullObjective(id) + .pipe( + catchError((err, caught) => { + console.error(err); + // TODO: maybe return a EMPTY or NEVER + return caught; + }), + ) + .subscribe((objective) => this.objective$.next(objective)); + } - if (result.openNew) { - this.openAddKeyResultDialog(); - } - }); + openAddKeyResultDialog() { + // this.dialog + // .open(KeyResultDialogComponent, { + // width: '45em', + // height: 'auto', + // data: { + // objective: this.objective, + // keyResult: null, + // }, + // }) + // .afterClosed() + // .subscribe(async (result) => { + // await this.notifierService.keyResultsChanges.next({ + // keyResult: result.keyResult, + // changeId: null, + // objective: result.objective, + // delete: false, + // }); + // + // if (result.openNew) { + // this.openAddKeyResultDialog(); + // } + // }); } closeDrawer() { this.notifierService.closeDetailSubject.next(); diff --git a/frontend/src/app/overview/overview.component.html b/frontend/src/app/overview/overview.component.html index 862ceaf978..c8ba36f13d 100644 --- a/frontend/src/app/overview/overview.component.html +++ b/frontend/src/app/overview/overview.component.html @@ -1,3 +1,5 @@ + +
diff --git a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html index 12dcb43ea2..fb0092ba96 100644 --- a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html +++ b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html @@ -10,9 +10,9 @@

Sidepanel

Kein passender switch case!
- - - + + +
From 3f3c7208380066c6544a4a68317ca7e1a07ab579 Mon Sep 17 00:00:00 2001 From: megli2 Date: Mon, 9 Oct 2023 16:08:59 +0200 Subject: [PATCH 05/63] format files --- .../objective-detail/objective-detail.component.ts | 11 +++++------ .../shared/custom/sidepanel/sidepanel.component.html | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/objective-detail/objective-detail.component.ts b/frontend/src/app/objective-detail/objective-detail.component.ts index 1109eb6618..92ef316504 100644 --- a/frontend/src/app/objective-detail/objective-detail.component.ts +++ b/frontend/src/app/objective-detail/objective-detail.component.ts @@ -1,8 +1,8 @@ -import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; -import {Objective} from '../shared/types/model/Objective'; -import {ObjectiveService} from '../shared/services/objective.service'; -import {NotifierService} from '../shared/services/notifier.service'; -import {catchError, Observable, Subject} from "rxjs"; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { Objective } from '../shared/types/model/Objective'; +import { ObjectiveService } from '../shared/services/objective.service'; +import { NotifierService } from '../shared/services/notifier.service'; +import { catchError, Observable, Subject } from 'rxjs'; @Component({ selector: 'app-objective-detail', @@ -20,7 +20,6 @@ export class ObjectiveDetailComponent { private notifierService: NotifierService, ) {} - ngOnInit(): void { this.objectiveId$.subscribe((id) => { this.loadObjective(id); diff --git a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html index fb0092ba96..91ea612b1d 100644 --- a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html +++ b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html @@ -11,8 +11,8 @@

Sidepanel

Kein passender switch case!
- - + +
From 7baec9e0fbe72043a387ea1f3537a30e427feb84 Mon Sep 17 00:00:00 2001 From: megli2 Date: Tue, 10 Oct 2023 08:07:26 +0200 Subject: [PATCH 06/63] implement key-result-detail in new sidepanel --- .../keyresult-detail.component.html | 151 ++++++++---------- .../keyresult-detail.component.ts | 131 +++++++-------- .../custom/sidepanel/sidepanel.component.html | 3 +- .../src/app/shared/types/enums/CloseState.ts | 6 + 4 files changed, 130 insertions(+), 161 deletions(-) create mode 100644 frontend/src/app/shared/types/enums/CloseState.ts diff --git a/frontend/src/app/keyresult-detail/keyresult-detail.component.html b/frontend/src/app/keyresult-detail/keyresult-detail.component.html index 7b7b36c1cb..10904b0c67 100644 --- a/frontend/src/app/keyresult-detail/keyresult-detail.component.html +++ b/frontend/src/app/keyresult-detail/keyresult-detail.component.html @@ -1,92 +1,73 @@ -
-
-

{{ keyResult.title }}

- close -
-
-
- {{ "KEY_RESULT_Type." + keyResult.keyResultType | translate }} -
-
- person icon -

{{ keyResult.owner.firstname }} {{ keyResult.owner.lastname }}

-
-
-

{{ keyResult.objective.quarter.startDate | date: "dd.MM.yyy" }} -

-

{{ keyResult.objective.quarter.endDate | date: "dd.MM.yyy" }}

-
-
-
- -
-

Confidence

- + +
+
+

{{ keyResult.title }}

-
-
- -

- Einheit: {{ "UNIT." + castToMetric(keyResult).unit | translate }} -

-

- Baseline: {{ castToMetric(keyResult).baseline - }}{{ "UNIT." + castToMetric(keyResult).unit.toString() | translate }} -

-

- Stretch Goal: {{ castToMetric(keyResult).stretchGoal - }}{{ "UNIT." + castToMetric(keyResult).unit.toString() | translate }} -

-
- - -
-

Commit

-
{{ castToOrdinal(keyResult).commitZone || "Keine" }}
+
+
+ {{ "KEY_RESULT_Type." + keyResult.keyResultType | translate }}
-
-

Target

-

{{ castToOrdinal(keyResult).targetZone || "Keine" }}

+
+ person icon +

{{ keyResult.owner.firstname }} {{ keyResult.owner.lastname }}

-
-

Stretch

-

{{ castToOrdinal(keyResult).stretchZone || "Keine" }}

+
+

{{ keyResult.objective.quarter.startDate | date: "dd.MM.yyy" }} -

+

{{ keyResult.objective.quarter.endDate | date: "dd.MM.yyy" }}

- -
-
-

- Letztes Check-in ({{ keyResult.lastCheckIn?.modifiedOn | date: "dd.MM.yyy" }}) -

-

{{ keyResult.lastCheckIn?.changeInfo }}

-
-
-

- Letztes Check-in ({{ keyResult.lastCheckIn?.createdOn | date: "dd.MM.yyy" }}) -

-

{{ keyResult.lastCheckIn?.changeInfo }}

-
- - - Alle Check-ins anzeigen +
+
+
+

Confidence

+ + +
+
+
+ +

+ Einheit: {{ castToMetric(keyResult).unit || "Keine" }} +

+

+ Baseline: {{ castToMetric(keyResult).baseline || "0" }} +

+

+ Stretch Goal: {{ castToMetric(keyResult).stretchGoal || "100" }} +

+
+ + +
+

Commit

+
{{ castToOrdinal(keyResult).commitZone || "Keine" }}
+
+
+

Target

+

{{ castToOrdinal(keyResult).targetZone || "Keine" }}

+
+
+

Stretch

+

{{ castToOrdinal(keyResult).stretchZone || "Keine" }}

+
+
+
+
+

+ Letztes Check-in ({{ keyResult.lastCheckIn?.createdOn | date: "dd.MM.yyy" }}) +

+

{{ keyResult.lastCheckIn?.changeInfo }}

+
+ + - -
-

Beschrieb

-

{{ keyResult.description }}

-
-
- - +
+

Beschrieb

+

{{ keyResult.description }}

+
+
+ + +
-
+ diff --git a/frontend/src/app/keyresult-detail/keyresult-detail.component.ts b/frontend/src/app/keyresult-detail/keyresult-detail.component.ts index 34f2279f06..81f593102e 100644 --- a/frontend/src/app/keyresult-detail/keyresult-detail.component.ts +++ b/frontend/src/app/keyresult-detail/keyresult-detail.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { KeyResult } from '../shared/types/model/KeyResult'; import { KeyresultService } from '../shared/services/keyresult.service'; import { KeyResultMetric } from '../shared/types/model/KeyResultMetric'; @@ -6,9 +6,11 @@ import { KeyResultOrdinal } from '../shared/types/model/KeyResultOrdinal'; import { CheckInHistoryDialogComponent } from '../shared/dialog/check-in-history-dialog/check-in-history-dialog.component'; import { MatDialog } from '@angular/material/dialog'; import { KeyResultDialogComponent } from '../key-result-dialog/key-result-dialog.component'; -import { NotifierService } from '../shared/services/notifier.service'; import { CheckInService } from '../shared/services/check-in.service'; -import { CheckInFormComponent } from '../shared/dialog/checkin/check-in-form/check-in-form.component'; +import { catchError, Observable, Subject } from 'rxjs'; +import { Router } from '@angular/router'; +import { RefreshDataService } from '../shared/services/refresh-data.service'; +import { CloseState } from '../shared/types/enums/CloseState'; @Component({ selector: 'app-keyresult-detail', @@ -16,46 +18,39 @@ import { CheckInFormComponent } from '../shared/dialog/checkin/check-in-form/che styleUrls: ['./keyresult-detail.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class KeyresultDetailComponent implements AfterViewInit { - @Input() keyResultId!: number; - keyResult!: KeyResult; +export class KeyresultDetailComponent implements OnInit { + @Input() + keyResultId$!: Observable; + + keyResult$: Subject = new Subject(); constructor( private keyResultService: KeyresultService, private checkInService: CheckInService, - private notifierService: NotifierService, - private changeDetectorRef: ChangeDetectorRef, private dialog: MatDialog, - ) { - this.notifierService.reopenCheckInHistoryDialog.subscribe((result) => { - /* Update lastCheckIn if it was changed in history dialog */ - if (this.keyResult.lastCheckIn?.id === result?.checkIn?.id) { - this.keyResult = { ...this.keyResult, lastCheckIn: result.checkIn }; - this.changeDetectorRef.detectChanges(); - } - /* Update lastCheckIn to null if it was deleted in history dialog */ - if (result.deleted) { - if (result.checkIn?.id == this.keyResult.lastCheckIn?.id) { - this.keyResultService.getFullKeyResult(this.keyResultId).subscribe((fullKeyResult) => { - this.keyResult = fullKeyResult; - this.changeDetectorRef.markForCheck(); - if (this.keyResult.lastCheckIn != null) { - this.checkInHistory(); - } - }); - } - return; - } - this.checkInHistory(); + private refreshDataService: RefreshDataService, + private router: Router, + ) {} + + ngOnInit(): void { + this.keyResultId$.subscribe((id) => { + this.loadKeyResult(id); }); } - ngAfterViewInit(): void { - this.keyResultService.getFullKeyResult(this.keyResultId).subscribe((fullKeyResult) => { - this.keyResult = fullKeyResult; - this.changeDetectorRef.markForCheck(); - }); + loadKeyResult(id: number): void { + this.keyResultService + .getFullKeyResult(id) + .pipe( + catchError((err, caught) => { + console.error(err); + // TODO: maybe return a EMPTY or NEVER + return caught; + }), + ) + .subscribe((keyResult) => this.keyResult$.next(keyResult)); } + castToMetric(keyResult: KeyResult) { return keyResult as KeyResultMetric; } @@ -64,66 +59,54 @@ export class KeyresultDetailComponent implements AfterViewInit { return keyResult as KeyResultOrdinal; } - checkInHistory() { + checkInHistory(keyResultId: number) { const dialogRef = this.dialog.open(CheckInHistoryDialogComponent, { data: { - keyResultId: this.keyResult.id, - keyResult: this.keyResult, + keyResultId: keyResultId, }, }); dialogRef.afterClosed().subscribe(() => {}); } - openEditKeyResultDialog() { + openEditKeyResultDialog(keyResult: KeyResult) { this.dialog .open(KeyResultDialogComponent, { width: '45em', height: 'auto', data: { objective: null, - keyResult: this.keyResult, + keyResult: keyResult, }, }) .afterClosed() - .subscribe(async (result) => { - await this.notifierService.keyResultsChanges.next({ - keyResult: result.keyResult, - changeId: result.changeId, - objective: result.objective, - delete: result.delete, - }); - if (result.openNew) { - this.openEditKeyResultDialog(); + .subscribe((result) => { + console.log('result.id', result.id); + console.log('result.closeState', result.closeState); + if (result.closeState === CloseState.SAVED) { + this.loadKeyResult(result.id); + this.refreshDataService.markDataRefresh(); + } + if (result.closeState === CloseState.DELETED) { + this.refreshDataService.markDataRefresh(); + this.router.navigate(['/']); } - - this.keyResult = { - ...this.keyResult, - id: result.keyResult.id, - title: result.keyResult.title, - description: result.keyResult.description, - }; - this.changeDetectorRef.markForCheck(); }); } openCheckInForm() { - const dialogRef = this.dialog.open(CheckInFormComponent, { - data: { - keyResult: this.keyResult, - }, - width: '719px', - }); - dialogRef.afterClosed().subscribe((result) => { - if (result != undefined && result != '') { - this.checkInService.createCheckIn(result.data).subscribe((createdCheckIn) => { - this.keyResult = { ...this.keyResult, lastCheckIn: createdCheckIn }; - this.changeDetectorRef.detectChanges(); - }); - } - }); - } - - closeDrawer() { - this.notifierService.closeDetailSubject.next(); + // const dialogRef = this.dialog.open(CheckInFormComponent, { + // data: { + // keyResult: this.keyResult, + // }, + // width: '719px', + // }); + // dialogRef.afterClosed().subscribe((result) => { + // if (result != undefined && result != '') { + // this.checkInService.createCheckIn(result.data).subscribe((createdCheckIn) => { + // this.keyResult = { ...this.keyResult, lastCheckIn: createdCheckIn }; + // this.changeDetectorRef.detectChanges(); + // }); + // } + // }); } } diff --git a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html index 91ea612b1d..e6ddd391d2 100644 --- a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html +++ b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html @@ -10,9 +10,8 @@

Sidepanel

Kein passender switch case!
- - +
diff --git a/frontend/src/app/shared/types/enums/CloseState.ts b/frontend/src/app/shared/types/enums/CloseState.ts new file mode 100644 index 0000000000..b50bf3898b --- /dev/null +++ b/frontend/src/app/shared/types/enums/CloseState.ts @@ -0,0 +1,6 @@ +export enum CloseState { + SAVED, + DELETED, + CANCELED, + UNKNOWN, +} From 4f1979ebac06bcd1f43944b422bc304eb1cd5806 Mon Sep 17 00:00:00 2001 From: megli2 Date: Tue, 10 Oct 2023 11:18:27 +0200 Subject: [PATCH 07/63] implement sidepanel backdrop and focus trap --- frontend/src/app/app.module.ts | 3 +++ .../objective-detail/objective-detail.component.ts | 1 + .../custom/sidepanel/sidepanel.component.html | 13 ++++++++++++- .../custom/sidepanel/sidepanel.component.scss | 9 +++++---- .../shared/custom/sidepanel/sidepanel.component.ts | 13 +++++++++++-- 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 48f380b536..d762edef71 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -60,6 +60,7 @@ import { ParseUnitValuePipe } from './shared/pipes/parse-unit-value/parse-unit-v import { ScoringComponent } from './shared/scoring/scoring/scoring.component'; import { QuarterFilterComponent } from './quarter-filter/quarter-filter.component'; import { SidepanelComponent } from './shared/custom/sidepanel/sidepanel.component'; +import { CdkOverlayOrigin, OverlayModule } from '@angular/cdk/overlay'; function initOauthFactory(configService: ConfigService, oauthService: OAuthService) { return async () => { @@ -137,6 +138,7 @@ export const MY_FORMATS = { MatInputModule, MatTooltipModule, MatAutocompleteModule, + OverlayModule, ToastrModule.forRoot(), MatProgressSpinnerModule, TranslateModule.forRoot({ @@ -157,6 +159,7 @@ export const MY_FORMATS = { MatDividerModule, MatSidenavModule, MatCheckboxModule, + CdkOverlayOrigin, ], providers: [ { diff --git a/frontend/src/app/objective-detail/objective-detail.component.ts b/frontend/src/app/objective-detail/objective-detail.component.ts index 92ef316504..978dd0245a 100644 --- a/frontend/src/app/objective-detail/objective-detail.component.ts +++ b/frontend/src/app/objective-detail/objective-detail.component.ts @@ -40,6 +40,7 @@ export class ObjectiveDetailComponent { } openAddKeyResultDialog() { + console.log('Open Dialog'); // this.dialog // .open(KeyResultDialogComponent, { // width: '45em', diff --git a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html index e6ddd391d2..5d04cac464 100644 --- a/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html +++ b/frontend/src/app/shared/custom/sidepanel/sidepanel.component.html @@ -6,7 +6,18 @@ >