From 7467830d9294a09d00a57fa9efb48ea1e573a8ae Mon Sep 17 00:00:00 2001 From: Yanick Minder <79108296+kcinay055679@users.noreply.github.com> Date: Mon, 4 Nov 2024 16:51:13 +0100 Subject: [PATCH 01/44] Fix/fix git history (#1125) * fix history * fix hist --- backend/src/main/resources/application-staging.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/resources/application-staging.properties b/backend/src/main/resources/application-staging.properties index 38e4a47571..18f39eeb0e 100644 --- a/backend/src/main/resources/application-staging.properties +++ b/backend/src/main/resources/application-staging.properties @@ -6,7 +6,7 @@ logging.level.ch.puzzle.okr=DEBUG connect.src=http://localhost:8544 http://localhost:8545 -hibernate.connection.url=jdbc:postgresql://localhost:5432/okr +hibernate.connection.url=jdbc:postgresql://localost:5432/okr hibernate.connection.username=user hibernate.connection.password=pwd hibernate.multiTenancy=SCHEMA @@ -18,7 +18,7 @@ okr.datasource.driver-class-name=org.postgresql.Driver okr.user.champion.usernames=peggimann # pitc -okr.tenants.pitc.datasource.url=jdbc:postgresql://localhost:5432/okr +okr.tenants.pitc.datasource.url=jdbc:postgresql://localost:5432/okr okr.tenants.pitc.datasource.username=user okr.tenants.pitc.datasource.password=pwd okr.tenants.pitc.datasource.schema=okr_pitc @@ -29,7 +29,7 @@ okr.tenants.pitc.security.oauth2.frontend.client-id=pitc_okr_staging # acme -okr.tenants.acme.datasource.url=jdbc:postgresql://localhost:5432/okr +okr.tenants.acme.datasource.url=jdbc:postgresql://localost:5432/okr okr.tenants.acme.datasource.username=user okr.tenants.acme.datasource.password=pwd okr.tenants.acme.datasource.schema=okr_acme From 7c0e846e30197fd5c849575c5d2e0ba92d2dcece Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 15 Oct 2024 10:50:14 +0200 Subject: [PATCH 02/44] jar is now debuggable --- .run/OkrApplication-local-prod-debug.run.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100755 .run/OkrApplication-local-prod-debug.run.xml diff --git a/.run/OkrApplication-local-prod-debug.run.xml b/.run/OkrApplication-local-prod-debug.run.xml new file mode 100755 index 0000000000..15ae63f4ef --- /dev/null +++ b/.run/OkrApplication-local-prod-debug.run.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file From 56fa137b57556b1e75e7f228fae44575013045c0 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 15 Oct 2024 10:57:07 +0200 Subject: [PATCH 03/44] add jar debug dev tools only on profile --- backend/pom.xml | 27 --------------------------- pom.xml | 1 - 2 files changed, 28 deletions(-) diff --git a/backend/pom.xml b/backend/pom.xml index 2e00fc4f7d..db58d3f963 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -83,15 +83,6 @@ 3.26.3 test - - org.springframework.boot - spring-boot-devtools - - - org.springframework - springloaded - 1.2.8.RELEASE - @@ -300,23 +291,5 @@ - - no-formatter - - - - net.revelc.code.formatter - formatter-maven-plugin - 2.23.0 - - - code-format - none - - - - - - diff --git a/pom.xml b/pom.xml index deb6767423..a782d1a155 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,6 @@ build-for-docker debug - no-formatter From 5f1c3ebae8b585f75d40bc92f8833bc002e22515 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Thu, 17 Oct 2024 08:28:59 +0200 Subject: [PATCH 04/44] rename intelij config and change log level of spring to debug in staging config --- .run/OkrApplication-local-prod-debug.run.xml | 17 ----------------- docker/dev-with-prod/docker-compose.yml | 5 ++++- 2 files changed, 4 insertions(+), 18 deletions(-) delete mode 100755 .run/OkrApplication-local-prod-debug.run.xml diff --git a/.run/OkrApplication-local-prod-debug.run.xml b/.run/OkrApplication-local-prod-debug.run.xml deleted file mode 100755 index 15ae63f4ef..0000000000 --- a/.run/OkrApplication-local-prod-debug.run.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/docker/dev-with-prod/docker-compose.yml b/docker/dev-with-prod/docker-compose.yml index 0fbb4064c0..301b254959 100644 --- a/docker/dev-with-prod/docker-compose.yml +++ b/docker/dev-with-prod/docker-compose.yml @@ -9,7 +9,10 @@ services: dockerfile: local-prod.Dockerfile restart: always environment: - SPRING_PROFILES_ACTIVE: dev + SPRING_PROFILES_ACTIVE: staging + LOGGING_LEVEL_ORG_SPRINGFRAMEWORK: DEBUG + LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_SECURITY: DEBUG + SPRING_FLYWAY_LOCATIONS: classpath:db/migration,classpath:db/data-migration,classpath:db/callback volumes: - ../../../okr/backend/target:/app-root/backend network_mode: "host" From bca6c95426a9dcc83567824b49433ea3d467a3b6 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Fri, 18 Oct 2024 11:15:01 +0200 Subject: [PATCH 05/44] rename folder --- docker/dev-with-prod/docker-compose.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docker/dev-with-prod/docker-compose.yml b/docker/dev-with-prod/docker-compose.yml index 301b254959..0fbb4064c0 100644 --- a/docker/dev-with-prod/docker-compose.yml +++ b/docker/dev-with-prod/docker-compose.yml @@ -9,10 +9,7 @@ services: dockerfile: local-prod.Dockerfile restart: always environment: - SPRING_PROFILES_ACTIVE: staging - LOGGING_LEVEL_ORG_SPRINGFRAMEWORK: DEBUG - LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_SECURITY: DEBUG - SPRING_FLYWAY_LOCATIONS: classpath:db/migration,classpath:db/data-migration,classpath:db/callback + SPRING_PROFILES_ACTIVE: dev volumes: - ../../../okr/backend/target:/app-root/backend network_mode: "host" From 9d90b9069037e6b064c1a32acc945867511aca50 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Fri, 18 Oct 2024 12:02:52 +0200 Subject: [PATCH 06/44] update docker compose file --- docker/dev-with-prod/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/dev-with-prod/docker-compose.yml b/docker/dev-with-prod/docker-compose.yml index 0fbb4064c0..eca4375ee8 100644 --- a/docker/dev-with-prod/docker-compose.yml +++ b/docker/dev-with-prod/docker-compose.yml @@ -51,7 +51,7 @@ services: restart: on-failure volumes: - ../../../okr:/opt - command: [ "/bin/bash", "-c", "cd /opt/frontend && rm -rf dist && npm ci && npm run watch:prod" ] + command: [ "/bin/bash", "-c", "cd /opt/frontend && rm -rf dist && mkdir -p dist && npm ci && npm run watch:prod" ] healthcheck: test: bash -c "[ -f /opt/frontend/dist/frontend/index.html ]" interval: 10s From 783ddaaa7102108542351e8d148a5ae2f3501281 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Fri, 18 Oct 2024 12:25:52 +0200 Subject: [PATCH 07/44] use external profile to disable formatter --- backend/pom.xml | 18 ++++++++++++++++++ pom.xml | 1 + 2 files changed, 19 insertions(+) diff --git a/backend/pom.xml b/backend/pom.xml index db58d3f963..5889b583ef 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -291,5 +291,23 @@ + + no-formatter + + + + net.revelc.code.formatter + formatter-maven-plugin + 2.23.0 + + + code-format + none + + + + + + diff --git a/pom.xml b/pom.xml index a782d1a155..deb6767423 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,7 @@ build-for-docker debug + no-formatter From 11dc0a98344761a9ce11b4ec7a9a5013683aaefa Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Fri, 18 Oct 2024 12:28:39 +0200 Subject: [PATCH 08/44] clean up --- .../src/main/resources/application-staging.properties | 10 ++++------ docker/dev-with-prod/docker-compose.yml | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/backend/src/main/resources/application-staging.properties b/backend/src/main/resources/application-staging.properties index 18f39eeb0e..31ce92a3c2 100644 --- a/backend/src/main/resources/application-staging.properties +++ b/backend/src/main/resources/application-staging.properties @@ -1,12 +1,10 @@ # logging level for staging -logging.level.ch.puzzle.okr=DEBUG -#logging.level.org.flywaydb.core=DEBUG - +logging.level.org.springframework=debug connect.src=http://localhost:8544 http://localhost:8545 -hibernate.connection.url=jdbc:postgresql://localost:5432/okr +hibernate.connection.url=jdbc:postgresql://okr-dev-db:5432/okr hibernate.connection.username=user hibernate.connection.password=pwd hibernate.multiTenancy=SCHEMA @@ -18,7 +16,7 @@ okr.datasource.driver-class-name=org.postgresql.Driver okr.user.champion.usernames=peggimann # pitc -okr.tenants.pitc.datasource.url=jdbc:postgresql://localost:5432/okr +okr.tenants.pitc.datasource.url=jdbc:postgresql://okr-dev-db:5432/okr okr.tenants.pitc.datasource.username=user okr.tenants.pitc.datasource.password=pwd okr.tenants.pitc.datasource.schema=okr_pitc @@ -29,7 +27,7 @@ okr.tenants.pitc.security.oauth2.frontend.client-id=pitc_okr_staging # acme -okr.tenants.acme.datasource.url=jdbc:postgresql://localost:5432/okr +okr.tenants.acme.datasource.url=jdbc:postgresql://okr-dev-db:5432/okr okr.tenants.acme.datasource.username=user okr.tenants.acme.datasource.password=pwd okr.tenants.acme.datasource.schema=okr_acme diff --git a/docker/dev-with-prod/docker-compose.yml b/docker/dev-with-prod/docker-compose.yml index eca4375ee8..0fbb4064c0 100644 --- a/docker/dev-with-prod/docker-compose.yml +++ b/docker/dev-with-prod/docker-compose.yml @@ -51,7 +51,7 @@ services: restart: on-failure volumes: - ../../../okr:/opt - command: [ "/bin/bash", "-c", "cd /opt/frontend && rm -rf dist && mkdir -p dist && npm ci && npm run watch:prod" ] + command: [ "/bin/bash", "-c", "cd /opt/frontend && rm -rf dist && npm ci && npm run watch:prod" ] healthcheck: test: bash -c "[ -f /opt/frontend/dist/frontend/index.html ]" interval: 10s From 0624f477637299a61fbb5a396d77dd2116025fc3 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Fri, 18 Oct 2024 10:36:03 +0200 Subject: [PATCH 09/44] refactor dialog calls with config by introducing a service --- .../team-management/member-list/member-list.component.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/team-management/member-list/member-list.component.ts b/frontend/src/app/team-management/member-list/member-list.component.ts index 1ae5ba4865..87afca569e 100644 --- a/frontend/src/app/team-management/member-list/member-list.component.ts +++ b/frontend/src/app/team-management/member-list/member-list.component.ts @@ -6,7 +6,11 @@ import { User } from '../../shared/types/model/User'; import { convertFromUsers, UserTableEntry } from '../../shared/types/model/UserTableEntry'; import { TeamService } from '../../services/team.service'; import { Team } from '../../shared/types/model/Team'; -import { AddMemberToTeamDialogComponent } from '../add-member-to-team-dialog/add-member-to-team-dialog.component'; +import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; +import { + AddMemberToTeamDialogComponent, + AddMemberToTeamDialogComponentData, +} from '../add-member-to-team-dialog/add-member-to-team-dialog.component'; import { AddEditTeamDialog } from '../add-edit-team-dialog/add-edit-team-dialog.component'; import { MatTableDataSource } from '@angular/material/table'; import { InviteUserDialogComponent } from '../invite-user-dialog/invite-user-dialog.component'; From f697191b2b48682bc1c8226acdade15baf978adf Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Mon, 21 Oct 2024 12:26:28 +0200 Subject: [PATCH 10/44] refactor frontend tests related to dialogService --- .../team-management/member-list/member-list.component.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/frontend/src/app/team-management/member-list/member-list.component.ts b/frontend/src/app/team-management/member-list/member-list.component.ts index 87afca569e..1ae5ba4865 100644 --- a/frontend/src/app/team-management/member-list/member-list.component.ts +++ b/frontend/src/app/team-management/member-list/member-list.component.ts @@ -6,11 +6,7 @@ import { User } from '../../shared/types/model/User'; import { convertFromUsers, UserTableEntry } from '../../shared/types/model/UserTableEntry'; import { TeamService } from '../../services/team.service'; import { Team } from '../../shared/types/model/Team'; -import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; -import { - AddMemberToTeamDialogComponent, - AddMemberToTeamDialogComponentData, -} from '../add-member-to-team-dialog/add-member-to-team-dialog.component'; +import { AddMemberToTeamDialogComponent } from '../add-member-to-team-dialog/add-member-to-team-dialog.component'; import { AddEditTeamDialog } from '../add-edit-team-dialog/add-edit-team-dialog.component'; import { MatTableDataSource } from '@angular/material/table'; import { InviteUserDialogComponent } from '../invite-user-dialog/invite-user-dialog.component'; From f7af3f4655ff86b0ebda8589c83ad3e08794ba66 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Thu, 24 Oct 2024 09:00:42 +0200 Subject: [PATCH 11/44] remove backend/pom.xml from branch --- backend/pom.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/backend/pom.xml b/backend/pom.xml index 5889b583ef..2e00fc4f7d 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -83,6 +83,15 @@ 3.26.3 test + + org.springframework.boot + spring-boot-devtools + + + org.springframework + springloaded + 1.2.8.RELEASE + From 159e2c279bead7c02bd13a347972b66bdb41f025 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Wed, 23 Oct 2024 09:44:05 +0200 Subject: [PATCH 12/44] introduce new objective menu action service to simplify dialog creation of dialog actions --- .../objective-menu-actions.service.spec.ts | 16 +++++ .../objective-menu-actions.service.ts | 60 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 frontend/src/app/services/objective-menu-actions.service.spec.ts create mode 100644 frontend/src/app/services/objective-menu-actions.service.ts diff --git a/frontend/src/app/services/objective-menu-actions.service.spec.ts b/frontend/src/app/services/objective-menu-actions.service.spec.ts new file mode 100644 index 0000000000..aef8e3b228 --- /dev/null +++ b/frontend/src/app/services/objective-menu-actions.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ObjectiveMenuActionsService } from './objective-menu-actions.service'; + +describe('ObjectiveMenuActionsService', () => { + let service: ObjectiveMenuActionsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ObjectiveMenuActionsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/services/objective-menu-actions.service.ts b/frontend/src/app/services/objective-menu-actions.service.ts new file mode 100644 index 0000000000..cf3b56842e --- /dev/null +++ b/frontend/src/app/services/objective-menu-actions.service.ts @@ -0,0 +1,60 @@ +import { Injectable } from '@angular/core'; +import { DialogService } from './dialog.service'; +import { MatDialogRef } from '@angular/material/dialog'; +import { ObjectiveFormComponent } from '../shared/dialog/objective-dialog/objective-form.component'; +import { CompleteDialogComponent } from '../shared/dialog/complete-dialog/complete-dialog.component'; + +export type ObjectiveMenuAction = { + (): MatDialogRef; + (arg1: any): MatDialogRef; + (arg1: any, arg2: any): MatDialogRef; +}; + +export interface ObjectiveMenuEntry { + displayName: string; + action: ObjectiveMenuAction; +} + +@Injectable({ + providedIn: 'root', +}) +export class ObjectiveMenuActionsService { + constructor(private readonly dialogService: DialogService) {} + + getOngoingMenuActions(objectiveId: number): ObjectiveMenuEntry[] { + return [ + ...this.getDefaultActions(objectiveId), + this.saveObjectiveAsDraftAction(), + this.completeObjectiveAction(objectiveId), + ]; + } + + getDefaultActions(objectiveId: number): ObjectiveMenuEntry[] { + return [this.editObjectiveAction(objectiveId), this.duplicateObjectiveAction(objectiveId)]; + } + + private editObjectiveAction(objectiveId: number): ObjectiveMenuEntry { + const config = { data: { objectiveId: objectiveId } }; + const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); + return { displayName: 'Objective bearbeiten', action: action }; + } + + private duplicateObjectiveAction(objectiveId: number): ObjectiveMenuEntry { + const config = { data: { objectiveId: objectiveId } }; + const action = () => this.dialogService.open(ObjectiveFormComponent, config); + return { displayName: 'Objective duplizieren', action: action }; + } + + private completeObjectiveAction(objectiveId: number): ObjectiveMenuEntry { + const config = { + data: { objectiveTitle: 'Objective Title Placeholder' }, + }; + const action = () => this.dialogService.open(CompleteDialogComponent, config); + return { displayName: 'Objective abschliessen', action: action }; + } + + private saveObjectiveAsDraftAction(): ObjectiveMenuEntry { + const action = () => this.dialogService.openConfirmDialog('CONFIRMATION.DRAFT_CREATE'); + return { displayName: 'Objective als Draft speichern', action: action }; + } +} From 30e958d686975fc12797fdeadebfc6d391d841e8 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Wed, 23 Oct 2024 12:42:49 +0200 Subject: [PATCH 13/44] refactor objective --- .../objective/objective.component.html | 151 +++++++++--------- .../objective/objective.component.ts | 150 +++++------------ .../objective-menu-actions.service.ts | 50 ++++-- 3 files changed, 154 insertions(+), 197 deletions(-) diff --git a/frontend/src/app/components/objective/objective.component.html b/frontend/src/app/components/objective/objective.component.html index 48fd01a194..bcdc98d44a 100644 --- a/frontend/src/app/components/objective/objective.component.html +++ b/frontend/src/app/components/objective/objective.component.html @@ -1,81 +1,82 @@ -
-
-
-
- +
+
+
+
+ The objectives state +

{{ objective.title }}

+
+
- -
-
- -
+
+ +
-
- -
+
+ +
+
- - - - + + + diff --git a/frontend/src/app/components/objective/objective.component.ts b/frontend/src/app/components/objective/objective.component.ts index 94ea0f3b01..63f2efe049 100644 --- a/frontend/src/app/components/objective/objective.component.ts +++ b/frontend/src/app/components/objective/objective.component.ts @@ -16,6 +16,7 @@ import { KeyresultDialogComponent } from '../keyresult-dialog/keyresult-dialog.c import { TranslateService } from '@ngx-translate/core'; import { GJ_REGEX_PATTERN } from '../../shared/constantLibary'; import { DialogService } from '../../services/dialog.service'; +import { ObjectiveMenuActionsService, ObjectiveMenuEntry } from '../../services/objective-menu-actions.service'; @Component({ selector: 'app-objective-column', @@ -26,10 +27,7 @@ import { DialogService } from '../../services/dialog.service'; export class ObjectiveComponent implements OnInit { @Input() isWritable!: boolean; - - menuEntries: MenuEntry[] = []; isComplete: boolean = false; - isBacklogQuarter: boolean = false; protected readonly trackByFn = trackByFn; @ViewChild('menuButton') private menuButton!: ElementRef; @@ -39,6 +37,7 @@ export class ObjectiveComponent implements OnInit { private refreshDataService: RefreshDataService, private objectiveService: ObjectiveService, private translate: TranslateService, + private objectiveMenuActionsService: ObjectiveMenuActionsService, ) {} @Input() @@ -48,14 +47,7 @@ export class ObjectiveComponent implements OnInit { public objective$ = new BehaviorSubject({} as ObjectiveMin); - ngOnInit() { - if (this.objective$.value.state.includes('successful') || this.objective$.value.state.includes('not-successful')) { - this.isComplete = true; - } - if (!GJ_REGEX_PATTERN.test(this.objective$.value.quarter.label)) { - this.isBacklogQuarter = true; - } - } + ngOnInit() {} formatObjectiveState(state: string): string { const lastIndex = state.lastIndexOf('-'); @@ -70,107 +62,43 @@ export class ObjectiveComponent implements OnInit { return this.translate.instant('INFORMATION.OBJECTIVE_STATE_TOOLTIP'); } - getMenu(): void { - if (this.objective$.value.state.includes('successful') || this.objective$.value.state.includes('not-successful')) { - this.menuEntries = this.getCompletedMenuActions(); - } else { - if (this.objective$.value.state === State.ONGOING) { - this.menuEntries = this.getOngoingMenuActions(); - } else { - this.menuEntries = this.getDraftMenuActions(); - } - } - } - - getOngoingMenuActions() { - return [ - ...this.getDefaultMenuActions(), - ...[ - { - displayName: 'Objective abschliessen', - action: 'complete', - dialog: { dialog: CompleteDialogComponent, data: { objectiveTitle: this.objective$.value.title } }, - }, - { - displayName: 'Objective als Draft speichern', - action: 'todraft', - dialog: { - dialog: ConfirmDialogComponent, - data: { - title: this.translate.instant('CONFIRMATION.TO_DRAFT.TITLE'), - text: this.translate.instant('CONFIRMATION.TO_DRAFT.TEXT'), - }, - }, - }, - ], - ]; + isObjectiveComplete(objective: ObjectiveMin): boolean { + return objective.state == State.SUCCESSFUL || objective.state == State.NOTSUCCESSFUL; } - getDraftMenuActions() { - const action = this.isBacklogQuarter ? 'releaseBacklog' : 'release'; - let menuEntries = { - displayName: 'Objective veröffentlichen', - action: action, - dialog: { - dialog: this.isBacklogQuarter ? ObjectiveFormComponent : ConfirmDialogComponent, - data: { - title: this.translate.instant('CONFIRMATION.RELEASE.TITLE'), - text: this.translate.instant('CONFIRMATION.RELEASE.TEXT'), - action: action, - objectiveId: this.isBacklogQuarter ? this.objective$.value.id : undefined, - }, - }, - }; - - return [...this.getDefaultMenuActions(), menuEntries]; - } - - getDefaultMenuActions() { - return [ - { - displayName: 'Objective bearbeiten', - dialog: { dialog: ObjectiveFormComponent, data: { objectiveId: this.objective$.value.id } }, - }, - { - displayName: 'Objective duplizieren', - action: 'duplicate', - dialog: { dialog: ObjectiveFormComponent, data: { objectiveId: this.objective$.value.id } }, - }, - ]; - } - - getCompletedMenuActions() { - return [ - { displayName: 'Objective wiedereröffnen', action: 'reopen' }, - { - displayName: 'Objective duplizieren', - action: 'duplicate', - dialog: { dialog: ObjectiveFormComponent, data: { objectiveId: this.objective$.value.id } }, - }, - ]; - } - - redirect(menuEntry: MenuEntry) { - if (menuEntry.dialog) { - const matDialogRef = this.dialogService.open(menuEntry.dialog.dialog, { - data: { - title: menuEntry.dialog.data.title, - action: menuEntry.action, - text: menuEntry.dialog.data.text, - objective: menuEntry.dialog.data, - objectiveTitle: menuEntry.dialog.data.objectiveTitle, - }, - ...((menuEntry.action == 'release' || menuEntry.action == 'todraft') && { width: 'auto' }), - }); - matDialogRef.afterClosed().subscribe((result) => { - this.menuButton.nativeElement.focus(); - if (result) { - this.handleDialogResult(menuEntry, result); - } - }); - } else { - this.reopenRedirect(menuEntry); + getMenu(objective: ObjectiveMin): ObjectiveMenuEntry[] { + if (this.isObjectiveComplete(objective)) { + return this.objectiveMenuActionsService.getCompletedMenuActions(objective); + } else if (objective.state === State.ONGOING) { + return this.objectiveMenuActionsService.getOngoingMenuActions(objective); + } else if (objective.state === State.DRAFT) { + return this.objectiveMenuActionsService.getDraftMenuActions(objective); } + //Probably throw an error here + return []; + } + + redirect(menuEntry: ObjectiveMenuEntry) { + // if (menuEntry.dialog) { + // const matDialogRef = this.dialogService.open(menuEntry.dialog.dialog, { + // data: { + // title: menuEntry.dialog.data.title, + // action: menuEntry.action, + // text: menuEntry.action, + // objective: menuEntry.dialog.data, + // objectiveTitle: menuEntry.dialog.data.objectiveTitle, + // }, + // ...((menuEntry.action == 'release' || menuEntry.action == 'todraft') && { width: 'auto' }), + // }); + // matDialogRef.afterClosed().subscribe((result) => { + // this.menuButton.nativeElement.focus(); + // if (result) { + // this.handleDialogResult(menuEntry, result); + // } + // }); + // } else { + // this.reopenRedirect(menuEntry); + // } } handleDialogResult(menuEntry: MenuEntry, result: { endState: string; comment: string | null; objective: any }) { @@ -241,8 +169,8 @@ export class ObjectiveComponent implements OnInit { } } - openObjectiveDetail() { - this.router.navigate(['details/objective', this.objective$.value.id]); + openObjectiveDetail(objectiveId: number) { + this.router.navigate(['details/objective', objectiveId]); } openAddKeyResultDialog() { diff --git a/frontend/src/app/services/objective-menu-actions.service.ts b/frontend/src/app/services/objective-menu-actions.service.ts index cf3b56842e..1e7df20dee 100644 --- a/frontend/src/app/services/objective-menu-actions.service.ts +++ b/frontend/src/app/services/objective-menu-actions.service.ts @@ -3,6 +3,8 @@ import { DialogService } from './dialog.service'; import { MatDialogRef } from '@angular/material/dialog'; import { ObjectiveFormComponent } from '../shared/dialog/objective-dialog/objective-form.component'; import { CompleteDialogComponent } from '../shared/dialog/complete-dialog/complete-dialog.component'; +import { ObjectiveMin } from '../shared/types/model/ObjectiveMin'; +import { GJ_REGEX_PATTERN } from '../shared/constantLibary'; export type ObjectiveMenuAction = { (): MatDialogRef; @@ -21,33 +23,55 @@ export interface ObjectiveMenuEntry { export class ObjectiveMenuActionsService { constructor(private readonly dialogService: DialogService) {} - getOngoingMenuActions(objectiveId: number): ObjectiveMenuEntry[] { + isInBacklogQuarter(objective: ObjectiveMin) { + return !GJ_REGEX_PATTERN.test(objective.quarter.label); + } + + getOngoingMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { return [ - ...this.getDefaultActions(objectiveId), + ...this.getDefaultActions(objective), this.saveObjectiveAsDraftAction(), - this.completeObjectiveAction(objectiveId), + this.completeObjectiveAction(objective), ]; } - getDefaultActions(objectiveId: number): ObjectiveMenuEntry[] { - return [this.editObjectiveAction(objectiveId), this.duplicateObjectiveAction(objectiveId)]; + getDefaultActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { + return [this.editObjectiveAction(objective), this.duplicateObjectiveAction(objective)]; + } + + getDraftMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { + const releaseAction = this.isInBacklogQuarter(objective) + ? this.releaseFromDraftInBacklogAction(objective) + : this.releaseFromDraftAction(objective); + return [releaseAction]; + } + + releaseFromDraftAction(objective: ObjectiveMin): ObjectiveMenuEntry { + const action = () => this.dialogService.openConfirmDialog('CONFIRMATION.RELEASE'); + return { displayName: 'Objective veröffentlichen', action: action }; } - private editObjectiveAction(objectiveId: number): ObjectiveMenuEntry { - const config = { data: { objectiveId: objectiveId } }; + releaseFromDraftInBacklogAction(objective: ObjectiveMin): ObjectiveMenuEntry { + const config = { data: { action: 'releaseBacklog', objectiveId: objective } }; + const action = () => this.dialogService.open(ObjectiveFormComponent, config); + return { displayName: 'Objective veröffentlichen', action: action }; + } + + private editObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { + const config = { data: { objectiveId: objective.id } }; const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); return { displayName: 'Objective bearbeiten', action: action }; } - private duplicateObjectiveAction(objectiveId: number): ObjectiveMenuEntry { - const config = { data: { objectiveId: objectiveId } }; + private duplicateObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { + const config = { data: { objectiveId: objective.id } }; const action = () => this.dialogService.open(ObjectiveFormComponent, config); return { displayName: 'Objective duplizieren', action: action }; } - private completeObjectiveAction(objectiveId: number): ObjectiveMenuEntry { + private completeObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { const config = { - data: { objectiveTitle: 'Objective Title Placeholder' }, + data: { objectiveTitle: objective.title }, }; const action = () => this.dialogService.open(CompleteDialogComponent, config); return { displayName: 'Objective abschliessen', action: action }; @@ -57,4 +81,8 @@ export class ObjectiveMenuActionsService { const action = () => this.dialogService.openConfirmDialog('CONFIRMATION.DRAFT_CREATE'); return { displayName: 'Objective als Draft speichern', action: action }; } + + getCompletedMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { + return []; + } } From 12663d54a2cba4e21fca024487bd9c5415ecef18 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Wed, 23 Oct 2024 14:06:28 +0200 Subject: [PATCH 14/44] reimplement generic menu --- .../objective/objective.component.html | 4 +-- .../objective/objective.component.ts | 27 +++++-------------- .../objective-menu-actions.service.ts | 11 +++----- 3 files changed, 12 insertions(+), 30 deletions(-) diff --git a/frontend/src/app/components/objective/objective.component.html b/frontend/src/app/components/objective/objective.component.html index bcdc98d44a..ea8f8cad23 100644 --- a/frontend/src/app/components/objective/objective.component.html +++ b/frontend/src/app/components/objective/objective.component.html @@ -70,13 +70,13 @@

{{ objective.title }}

yPosition="below" > diff --git a/frontend/src/app/components/objective/objective.component.ts b/frontend/src/app/components/objective/objective.component.ts index 63f2efe049..681939915f 100644 --- a/frontend/src/app/components/objective/objective.component.ts +++ b/frontend/src/app/components/objective/objective.component.ts @@ -22,7 +22,6 @@ import { ObjectiveMenuActionsService, ObjectiveMenuEntry } from '../../services/ selector: 'app-objective-column', templateUrl: './objective.component.html', styleUrls: ['./objective.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, }) export class ObjectiveComponent implements OnInit { @Input() @@ -67,6 +66,7 @@ export class ObjectiveComponent implements OnInit { } getMenu(objective: ObjectiveMin): ObjectiveMenuEntry[] { + console.log('get'); if (this.isObjectiveComplete(objective)) { return this.objectiveMenuActionsService.getCompletedMenuActions(objective); } else if (objective.state === State.ONGOING) { @@ -79,26 +79,9 @@ export class ObjectiveComponent implements OnInit { } redirect(menuEntry: ObjectiveMenuEntry) { - // if (menuEntry.dialog) { - // const matDialogRef = this.dialogService.open(menuEntry.dialog.dialog, { - // data: { - // title: menuEntry.dialog.data.title, - // action: menuEntry.action, - // text: menuEntry.action, - // objective: menuEntry.dialog.data, - // objectiveTitle: menuEntry.dialog.data.objectiveTitle, - // }, - // ...((menuEntry.action == 'release' || menuEntry.action == 'todraft') && { width: 'auto' }), - // }); - // matDialogRef.afterClosed().subscribe((result) => { - // this.menuButton.nativeElement.focus(); - // if (result) { - // this.handleDialogResult(menuEntry, result); - // } - // }); - // } else { - // this.reopenRedirect(menuEntry); - // } + console.log('test'); + console.log(menuEntry.action); + const matDialogRef = menuEntry.action(); } handleDialogResult(menuEntry: MenuEntry, result: { endState: string; comment: string | null; objective: any }) { @@ -189,4 +172,6 @@ export class ObjectiveComponent implements OnInit { this.refreshDataService.markDataRefresh(); }); } + + protected readonly console = console; } diff --git a/frontend/src/app/services/objective-menu-actions.service.ts b/frontend/src/app/services/objective-menu-actions.service.ts index 1e7df20dee..2c489e379a 100644 --- a/frontend/src/app/services/objective-menu-actions.service.ts +++ b/frontend/src/app/services/objective-menu-actions.service.ts @@ -6,15 +6,12 @@ import { CompleteDialogComponent } from '../shared/dialog/complete-dialog/comple import { ObjectiveMin } from '../shared/types/model/ObjectiveMin'; import { GJ_REGEX_PATTERN } from '../shared/constantLibary'; -export type ObjectiveMenuAction = { - (): MatDialogRef; - (arg1: any): MatDialogRef; - (arg1: any, arg2: any): MatDialogRef; -}; +export type ObjectiveMenuAction = () => MatDialogRef; export interface ObjectiveMenuEntry { displayName: string; action: ObjectiveMenuAction; + afterAction?: (arg1: any, arg2: any) => any; } @Injectable({ @@ -53,7 +50,7 @@ export class ObjectiveMenuActionsService { releaseFromDraftInBacklogAction(objective: ObjectiveMin): ObjectiveMenuEntry { const config = { data: { action: 'releaseBacklog', objectiveId: objective } }; - const action = () => this.dialogService.open(ObjectiveFormComponent, config); + const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); return { displayName: 'Objective veröffentlichen', action: action }; } @@ -79,7 +76,7 @@ export class ObjectiveMenuActionsService { private saveObjectiveAsDraftAction(): ObjectiveMenuEntry { const action = () => this.dialogService.openConfirmDialog('CONFIRMATION.DRAFT_CREATE'); - return { displayName: 'Objective als Draft speichern', action: action }; + return { displayName: 'Objective als Draft speicherns', action: action }; } getCompletedMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { From c05c734872b98aeb73c6176d319415966cb931d9 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Wed, 23 Oct 2024 14:52:01 +0200 Subject: [PATCH 15/44] use pipe with map to generate menu entries --- .../objective/objective.component.html | 150 +++++++++--------- .../objective/objective.component.ts | 42 ++--- .../objective-menu-actions.service.ts | 35 ++-- frontend/src/app/shared/common.ts | 11 ++ 4 files changed, 116 insertions(+), 122 deletions(-) diff --git a/frontend/src/app/components/objective/objective.component.html b/frontend/src/app/components/objective/objective.component.html index ea8f8cad23..5cfa23b459 100644 --- a/frontend/src/app/components/objective/objective.component.html +++ b/frontend/src/app/components/objective/objective.component.html @@ -1,82 +1,80 @@ -
-
-
-
-
- The objectives state -

{{ objective.title }}

-
- + [attr.data-testId]="'objective-state'" + [src]="'assets/icons/' + objective.state" + alt="The objectives state" + class="icon" + matTooltip="{{ getStateTooltip() + ' ' + formatObjectiveState(objective.state) }}" + matTooltipPosition="above" + /> +

{{ objective.title }}

+ + -
- -
- -
- -
+
+
+ +
+ +
- - -
+ + + diff --git a/frontend/src/app/components/objective/objective.component.ts b/frontend/src/app/components/objective/objective.component.ts index 681939915f..c2206dd24e 100644 --- a/frontend/src/app/components/objective/objective.component.ts +++ b/frontend/src/app/components/objective/objective.component.ts @@ -1,20 +1,16 @@ -import { ChangeDetectionStrategy, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { MenuEntry } from '../../shared/types/menu-entry'; import { ObjectiveMin } from '../../shared/types/model/ObjectiveMin'; import { Router } from '@angular/router'; -import { ObjectiveFormComponent } from '../../shared/dialog/objective-dialog/objective-form.component'; -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, map } from 'rxjs'; import { RefreshDataService } from '../../services/refresh-data.service'; import { State } from '../../shared/types/enums/State'; import { ObjectiveService } from '../../services/objective.service'; -import { ConfirmDialogComponent } from '../../shared/dialog/confirm-dialog/confirm-dialog.component'; -import { CompleteDialogComponent } from '../../shared/dialog/complete-dialog/complete-dialog.component'; import { Completed } from '../../shared/types/model/Completed'; import { Objective } from '../../shared/types/model/Objective'; -import { trackByFn } from '../../shared/common'; +import { isObjectiveComplete, trackByFn } from '../../shared/common'; import { KeyresultDialogComponent } from '../keyresult-dialog/keyresult-dialog.component'; import { TranslateService } from '@ngx-translate/core'; -import { GJ_REGEX_PATTERN } from '../../shared/constantLibary'; import { DialogService } from '../../services/dialog.service'; import { ObjectiveMenuActionsService, ObjectiveMenuEntry } from '../../services/objective-menu-actions.service'; @@ -24,11 +20,13 @@ import { ObjectiveMenuActionsService, ObjectiveMenuEntry } from '../../services/ styleUrls: ['./objective.component.scss'], }) export class ObjectiveComponent implements OnInit { - @Input() - isWritable!: boolean; + @Input() isWritable!: boolean; isComplete: boolean = false; + public objective$ = new BehaviorSubject({} as ObjectiveMin); + menuEntries = this.objective$.pipe(map((objective) => this.objectiveMenuActionsService.getMenu(objective))); protected readonly trackByFn = trackByFn; - @ViewChild('menuButton') private menuButton!: ElementRef; + protected readonly console = console; + protected readonly isObjectiveComplete = isObjectiveComplete; constructor( private dialogService: DialogService, @@ -39,13 +37,10 @@ export class ObjectiveComponent implements OnInit { private objectiveMenuActionsService: ObjectiveMenuActionsService, ) {} - @Input() - set objective(objective: ObjectiveMin) { + @Input() set objective(objective: ObjectiveMin) { this.objective$.next(objective); } - public objective$ = new BehaviorSubject({} as ObjectiveMin); - ngOnInit() {} formatObjectiveState(state: string): string { @@ -61,23 +56,6 @@ export class ObjectiveComponent implements OnInit { return this.translate.instant('INFORMATION.OBJECTIVE_STATE_TOOLTIP'); } - isObjectiveComplete(objective: ObjectiveMin): boolean { - return objective.state == State.SUCCESSFUL || objective.state == State.NOTSUCCESSFUL; - } - - getMenu(objective: ObjectiveMin): ObjectiveMenuEntry[] { - console.log('get'); - if (this.isObjectiveComplete(objective)) { - return this.objectiveMenuActionsService.getCompletedMenuActions(objective); - } else if (objective.state === State.ONGOING) { - return this.objectiveMenuActionsService.getOngoingMenuActions(objective); - } else if (objective.state === State.DRAFT) { - return this.objectiveMenuActionsService.getDraftMenuActions(objective); - } - //Probably throw an error here - return []; - } - redirect(menuEntry: ObjectiveMenuEntry) { console.log('test'); console.log(menuEntry.action); @@ -172,6 +150,4 @@ export class ObjectiveComponent implements OnInit { this.refreshDataService.markDataRefresh(); }); } - - protected readonly console = console; } diff --git a/frontend/src/app/services/objective-menu-actions.service.ts b/frontend/src/app/services/objective-menu-actions.service.ts index 2c489e379a..e47ee600da 100644 --- a/frontend/src/app/services/objective-menu-actions.service.ts +++ b/frontend/src/app/services/objective-menu-actions.service.ts @@ -4,7 +4,8 @@ import { MatDialogRef } from '@angular/material/dialog'; import { ObjectiveFormComponent } from '../shared/dialog/objective-dialog/objective-form.component'; import { CompleteDialogComponent } from '../shared/dialog/complete-dialog/complete-dialog.component'; import { ObjectiveMin } from '../shared/types/model/ObjectiveMin'; -import { GJ_REGEX_PATTERN } from '../shared/constantLibary'; +import { State } from '../shared/types/enums/State'; +import { isInBacklogQuarter, isObjectiveComplete } from '../shared/common'; export type ObjectiveMenuAction = () => MatDialogRef; @@ -20,11 +21,19 @@ export interface ObjectiveMenuEntry { export class ObjectiveMenuActionsService { constructor(private readonly dialogService: DialogService) {} - isInBacklogQuarter(objective: ObjectiveMin) { - return !GJ_REGEX_PATTERN.test(objective.quarter.label); + getMenu(objective: ObjectiveMin): ObjectiveMenuEntry[] { + if (isObjectiveComplete(objective)) { + return this.getCompletedMenuActions(objective); + } else if (objective.state === State.ONGOING) { + return this.getOngoingMenuActions(objective); + } else if (objective.state === State.DRAFT) { + return this.getDraftMenuActions(objective); + } + //Probably throw an error here + return []; } - getOngoingMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { + private getOngoingMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { return [ ...this.getDefaultActions(objective), this.saveObjectiveAsDraftAction(), @@ -32,36 +41,36 @@ export class ObjectiveMenuActionsService { ]; } - getDefaultActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { + private getDefaultActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { return [this.editObjectiveAction(objective), this.duplicateObjectiveAction(objective)]; } - getDraftMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { - const releaseAction = this.isInBacklogQuarter(objective) + private getDraftMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { + const releaseAction = isInBacklogQuarter(objective) ? this.releaseFromDraftInBacklogAction(objective) : this.releaseFromDraftAction(objective); return [releaseAction]; } - releaseFromDraftAction(objective: ObjectiveMin): ObjectiveMenuEntry { + private releaseFromDraftAction(objective: ObjectiveMin): ObjectiveMenuEntry { const action = () => this.dialogService.openConfirmDialog('CONFIRMATION.RELEASE'); return { displayName: 'Objective veröffentlichen', action: action }; } - releaseFromDraftInBacklogAction(objective: ObjectiveMin): ObjectiveMenuEntry { - const config = { data: { action: 'releaseBacklog', objectiveId: objective } }; + private releaseFromDraftInBacklogAction(objective: ObjectiveMin): ObjectiveMenuEntry { + const config = { data: { objective: { objectiveId: objective }, action: 'releaseBacklog' } }; const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); return { displayName: 'Objective veröffentlichen', action: action }; } private editObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { - const config = { data: { objectiveId: objective.id } }; + const config = { data: { objective: { objectiveId: objective.id } } }; const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); return { displayName: 'Objective bearbeiten', action: action }; } private duplicateObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { - const config = { data: { objectiveId: objective.id } }; + const config = { data: { objective: { objectiveId: objective.id } } }; const action = () => this.dialogService.open(ObjectiveFormComponent, config); return { displayName: 'Objective duplizieren', action: action }; } @@ -79,7 +88,7 @@ export class ObjectiveMenuActionsService { return { displayName: 'Objective als Draft speicherns', action: action }; } - getCompletedMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { + private getCompletedMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { return []; } } diff --git a/frontend/src/app/shared/common.ts b/frontend/src/app/shared/common.ts index 57f70b2c0e..c5f9734b20 100644 --- a/frontend/src/app/shared/common.ts +++ b/frontend/src/app/shared/common.ts @@ -1,5 +1,8 @@ import { FormGroup } from '@angular/forms'; import { KeyResultMetricMin } from './types/model/KeyResultMetricMin'; +import { ObjectiveMin } from './types/model/ObjectiveMin'; +import { State } from './types/enums/State'; +import { GJ_REGEX_PATTERN } from './constantLibary'; export function getNumberOrNull(str: string | null | undefined): number | null { if (str === null || str === undefined || str.toString().trim() === '') { @@ -91,6 +94,14 @@ export function isMobileDevice() { return window.navigator.userAgent.toLowerCase().includes('mobile'); } +export function isInBacklogQuarter(objective: ObjectiveMin) { + return !GJ_REGEX_PATTERN.test(objective.quarter.label); +} + +export function isObjectiveComplete(objective: ObjectiveMin): boolean { + return objective.state == State.SUCCESSFUL || objective.state == State.NOTSUCCESSFUL; +} + export function hasFormFieldErrors(formGroup: FormGroup, field: string) { if (formGroup.get(field)?.dirty || formGroup.get(field)?.touched) { return formGroup.get(field)?.errors; From 234fc14abaf5f205676d42f053d8639dc123af6a Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Wed, 23 Oct 2024 15:05:03 +0200 Subject: [PATCH 16/44] introduce afterAction property --- .../objective/objective.component.html | 2 +- .../objective/objective.component.ts | 11 +++++----- .../objective-menu-actions.service.ts | 22 +++++++++++++------ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/frontend/src/app/components/objective/objective.component.html b/frontend/src/app/components/objective/objective.component.html index 5cfa23b459..0fb9423609 100644 --- a/frontend/src/app/components/objective/objective.component.html +++ b/frontend/src/app/components/objective/objective.component.html @@ -50,7 +50,7 @@

{{ objective.title }}

color="primary" class="fw-bold px-0 pe-2 ms-2" [attr.data-testId]="'add-keyResult'" - (click)="openAddKeyResultDialog(); $event.stopPropagation()" + (click)="openAddKeyResultDialog(objective); $event.stopPropagation()" (keydown.enter)="$event.stopPropagation()" > diff --git a/frontend/src/app/components/objective/objective.component.ts b/frontend/src/app/components/objective/objective.component.ts index c2206dd24e..644bcd1ca5 100644 --- a/frontend/src/app/components/objective/objective.component.ts +++ b/frontend/src/app/components/objective/objective.component.ts @@ -57,9 +57,10 @@ export class ObjectiveComponent implements OnInit { } redirect(menuEntry: ObjectiveMenuEntry) { - console.log('test'); - console.log(menuEntry.action); const matDialogRef = menuEntry.action(); + matDialogRef.afterClosed().subscribe((result) => { + menuEntry.afterAction(result); + }); } handleDialogResult(menuEntry: MenuEntry, result: { endState: string; comment: string | null; objective: any }) { @@ -134,18 +135,18 @@ export class ObjectiveComponent implements OnInit { this.router.navigate(['details/objective', objectiveId]); } - openAddKeyResultDialog() { + openAddKeyResultDialog(objective: ObjectiveMin) { this.dialogService .open(KeyresultDialogComponent, { data: { - objective: this.objective$.value, + objective: objective, keyResult: null, }, }) .afterClosed() .subscribe((result) => { if (result?.openNew) { - this.openAddKeyResultDialog(); + this.openAddKeyResultDialog(objective); } this.refreshDataService.markDataRefresh(); }); diff --git a/frontend/src/app/services/objective-menu-actions.service.ts b/frontend/src/app/services/objective-menu-actions.service.ts index e47ee600da..8789bcf551 100644 --- a/frontend/src/app/services/objective-menu-actions.service.ts +++ b/frontend/src/app/services/objective-menu-actions.service.ts @@ -12,7 +12,7 @@ export type ObjectiveMenuAction = () => MatDialogRef; export interface ObjectiveMenuEntry { displayName: string; action: ObjectiveMenuAction; - afterAction?: (arg1: any, arg2: any) => any; + afterAction: (arg?: any) => any; } @Injectable({ @@ -54,25 +54,29 @@ export class ObjectiveMenuActionsService { private releaseFromDraftAction(objective: ObjectiveMin): ObjectiveMenuEntry { const action = () => this.dialogService.openConfirmDialog('CONFIRMATION.RELEASE'); - return { displayName: 'Objective veröffentlichen', action: action }; + const afterAction = () => {}; + return { displayName: 'Objective veröffentlichen', action: action, afterAction: afterAction }; } private releaseFromDraftInBacklogAction(objective: ObjectiveMin): ObjectiveMenuEntry { const config = { data: { objective: { objectiveId: objective }, action: 'releaseBacklog' } }; const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); - return { displayName: 'Objective veröffentlichen', action: action }; + const afterAction = () => {}; + return { displayName: 'Objective veröffentlichen', action: action, afterAction }; } private editObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { const config = { data: { objective: { objectiveId: objective.id } } }; const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); - return { displayName: 'Objective bearbeiten', action: action }; + const afterAction = () => {}; + return { displayName: 'Objective bearbeiten', action: action, afterAction: afterAction }; } private duplicateObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { const config = { data: { objective: { objectiveId: objective.id } } }; const action = () => this.dialogService.open(ObjectiveFormComponent, config); - return { displayName: 'Objective duplizieren', action: action }; + const afterAction = () => {}; + return { displayName: 'Objective duplizieren', action: action, afterAction: afterAction }; } private completeObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { @@ -80,12 +84,16 @@ export class ObjectiveMenuActionsService { data: { objectiveTitle: objective.title }, }; const action = () => this.dialogService.open(CompleteDialogComponent, config); - return { displayName: 'Objective abschliessen', action: action }; + const afterAction = () => {}; + + return { displayName: 'Objective abschliessen', action: action, afterAction: afterAction }; } private saveObjectiveAsDraftAction(): ObjectiveMenuEntry { const action = () => this.dialogService.openConfirmDialog('CONFIRMATION.DRAFT_CREATE'); - return { displayName: 'Objective als Draft speicherns', action: action }; + const afterAction = () => {}; + + return { displayName: 'Objective als Draft speicherns', action: action, afterAction: afterAction }; } private getCompletedMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { From 69591cdbebec2993469aa32d4b84527696337cc1 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Wed, 23 Oct 2024 15:57:16 +0200 Subject: [PATCH 17/44] add ObjectiveAfterActionFactory --- .../ObjectiveMenuAfterActionFactory.ts | 52 ++++++ .../objective/objective.component.html | 149 +++++++++--------- .../objective/objective.component.ts | 55 +------ .../objective-menu-actions.service.ts | 79 ++++++---- frontend/src/assets/i18n/de.json | 4 + 5 files changed, 186 insertions(+), 153 deletions(-) create mode 100644 frontend/src/app/components/objective/ObjectiveMenuAfterActionFactory.ts diff --git a/frontend/src/app/components/objective/ObjectiveMenuAfterActionFactory.ts b/frontend/src/app/components/objective/ObjectiveMenuAfterActionFactory.ts new file mode 100644 index 0000000000..ffdf30909f --- /dev/null +++ b/frontend/src/app/components/objective/ObjectiveMenuAfterActionFactory.ts @@ -0,0 +1,52 @@ +import { DialogService } from '../../services/dialog.service'; +import { Objective } from '../../shared/types/model/Objective'; +import { State } from '../../shared/types/enums/State'; +import { Completed } from '../../shared/types/model/Completed'; +import { ObjectiveService } from '../../services/objective.service'; +import { RefreshDataService } from '../../services/refresh-data.service'; + +export class ObjectiveMenuAfterActionFactory { + constructor( + private readonly dialogService: DialogService, + private readonly objectiveService: ObjectiveService, + private readonly refreshDataService: RefreshDataService, + ) {} + + completeObjective(objective: Objective, result: { endState: string; comment: string | null; objective: any }) { + objective.state = result.endState as State; + const completed: Completed = { + id: null, + version: objective.version, + objective: objective, + comment: result.comment, + }; + this.objectiveService.updateObjective(objective).subscribe(() => { + this.objectiveService.createCompleted(completed).subscribe(() => { + this.refreshDataService.markDataRefresh(); + }); + }); + } + + releaseFromQuarter(objective: Objective) { + objective.state = 'ONGOING' as State; + this.objectiveService.updateObjective(objective).subscribe(() => { + this.refreshDataService.markDataRefresh(); + }); + } + + objectiveBackToDraft(objective: Objective) { + objective.state = 'DRAFT' as State; + this.objectiveService.updateObjective(objective).subscribe(() => { + this.refreshDataService.markDataRefresh(); + }); + } + + objectiveReopen(objective: Objective) { + objective.state = 'ONGOING' as State; + this.objectiveService.updateObjective(objective).subscribe(() => { + this.objectiveService.deleteCompleted(objective.id).subscribe(() => { + this.refreshDataService.markDataRefresh(); + }); + }); + } +} diff --git a/frontend/src/app/components/objective/objective.component.html b/frontend/src/app/components/objective/objective.component.html index 0fb9423609..54ddd22757 100644 --- a/frontend/src/app/components/objective/objective.component.html +++ b/frontend/src/app/components/objective/objective.component.html @@ -1,80 +1,81 @@ -
-
-
-
- +
+
+
+
+ The objectives state +

{{ objective.title }}

+
+
- -
-
- -
+
+ +
-
- -
+
+ +
+
-
- - - + + + diff --git a/frontend/src/app/components/objective/objective.component.ts b/frontend/src/app/components/objective/objective.component.ts index 644bcd1ca5..f198fdb3a8 100644 --- a/frontend/src/app/components/objective/objective.component.ts +++ b/frontend/src/app/components/objective/objective.component.ts @@ -56,10 +56,12 @@ export class ObjectiveComponent implements OnInit { return this.translate.instant('INFORMATION.OBJECTIVE_STATE_TOOLTIP'); } - redirect(menuEntry: ObjectiveMenuEntry) { + redirect(menuEntry: ObjectiveMenuEntry, objectiveMin: ObjectiveMin) { const matDialogRef = menuEntry.action(); matDialogRef.afterClosed().subscribe((result) => { - menuEntry.afterAction(result); + this.objectiveService.getFullObjective(objectiveMin.id).subscribe((objective) => { + menuEntry.afterAction(objective, result); + }); }); } @@ -67,15 +69,12 @@ export class ObjectiveComponent implements OnInit { if (menuEntry.action) { this.objectiveService.getFullObjective(this.objective$.value.id).subscribe((objective) => { if (menuEntry.action == 'complete') { - this.completeObjective(objective, result); } else if (menuEntry.action == 'release') { - this.releaseObjective(objective); } else if (menuEntry.action == 'duplicate') { this.refreshDataService.markDataRefresh(); } else if (menuEntry.action == 'releaseBacklog') { this.refreshDataService.markDataRefresh(); } else if (menuEntry.action == 'todraft') { - this.objectiveBackToDraft(objective); } }); } else { @@ -85,52 +84,6 @@ export class ObjectiveComponent implements OnInit { } } - completeObjective(objective: Objective, result: { endState: string; comment: string | null; objective: any }) { - objective.state = result.endState as State; - const completed: Completed = { - id: null, - version: objective.version, - objective: objective, - comment: result.comment, - }; - this.objectiveService.updateObjective(objective).subscribe(() => { - this.objectiveService.createCompleted(completed).subscribe(() => { - this.isComplete = true; - this.refreshDataService.markDataRefresh(); - }); - }); - } - - releaseObjective(objective: Objective) { - objective.state = 'ONGOING' as State; - this.objectiveService.updateObjective(objective).subscribe(() => { - this.refreshDataService.markDataRefresh(); - }); - } - - objectiveBackToDraft(objective: Objective) { - objective.state = 'DRAFT' as State; - this.objectiveService.updateObjective(objective).subscribe(() => { - this.refreshDataService.markDataRefresh(); - }); - } - - reopenRedirect(menuEntry: MenuEntry) { - if (menuEntry.action === 'reopen') { - this.objectiveService.getFullObjective(this.objective$.value.id).subscribe((objective) => { - objective.state = 'ONGOING' as State; - this.objectiveService.updateObjective(objective).subscribe(() => { - this.objectiveService.deleteCompleted(objective.id).subscribe(() => { - this.isComplete = false; - this.refreshDataService.markDataRefresh(); - }); - }); - }); - } else { - this.router.navigate([menuEntry.route!]); - } - } - openObjectiveDetail(objectiveId: number) { this.router.navigate(['details/objective', objectiveId]); } diff --git a/frontend/src/app/services/objective-menu-actions.service.ts b/frontend/src/app/services/objective-menu-actions.service.ts index 8789bcf551..86e8acd173 100644 --- a/frontend/src/app/services/objective-menu-actions.service.ts +++ b/frontend/src/app/services/objective-menu-actions.service.ts @@ -6,22 +6,38 @@ import { CompleteDialogComponent } from '../shared/dialog/complete-dialog/comple import { ObjectiveMin } from '../shared/types/model/ObjectiveMin'; import { State } from '../shared/types/enums/State'; import { isInBacklogQuarter, isObjectiveComplete } from '../shared/common'; +import { ObjectiveMenuAfterActionFactory } from '../components/objective/ObjectiveMenuAfterActionFactory'; +import { ObjectiveService } from './objective.service'; +import { RefreshDataService } from './refresh-data.service'; +import { Objective } from '../shared/types/model/Objective'; export type ObjectiveMenuAction = () => MatDialogRef; +export type ObjectiveMenuAfterAction = (objective: Objective, dialogResult: any) => any; export interface ObjectiveMenuEntry { displayName: string; action: ObjectiveMenuAction; - afterAction: (arg?: any) => any; + afterAction: ObjectiveMenuAfterAction; } @Injectable({ providedIn: 'root', }) export class ObjectiveMenuActionsService { - constructor(private readonly dialogService: DialogService) {} + afterActions: ObjectiveMenuAfterActionFactory; + constructor( + private readonly dialogService: DialogService, + objectiveService: ObjectiveService, + private readonly refreshDataService: RefreshDataService, + ) { + this.afterActions = new ObjectiveMenuAfterActionFactory(dialogService, objectiveService, refreshDataService); + } getMenu(objective: ObjectiveMin): ObjectiveMenuEntry[] { + return [...this.getDefaultActions(objective), ...this.getSpecificMenuEntries(objective)]; + } + + private getSpecificMenuEntries(objective: ObjectiveMin): ObjectiveMenuEntry[] { if (isObjectiveComplete(objective)) { return this.getCompletedMenuActions(objective); } else if (objective.state === State.ONGOING) { @@ -33,49 +49,50 @@ export class ObjectiveMenuActionsService { return []; } - private getOngoingMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { - return [ - ...this.getDefaultActions(objective), - this.saveObjectiveAsDraftAction(), - this.completeObjectiveAction(objective), - ]; - } - private getDefaultActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { - return [this.editObjectiveAction(objective), this.duplicateObjectiveAction(objective)]; + return [this.duplicateObjectiveAction(objective)]; } private getDraftMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { const releaseAction = isInBacklogQuarter(objective) - ? this.releaseFromDraftInBacklogAction(objective) - : this.releaseFromDraftAction(objective); + ? this.releaseFromBacklogAction(objective) + : this.releaseFromQuarterAction(objective); return [releaseAction]; } - private releaseFromDraftAction(objective: ObjectiveMin): ObjectiveMenuEntry { - const action = () => this.dialogService.openConfirmDialog('CONFIRMATION.RELEASE'); - const afterAction = () => {}; + private getOngoingMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { + return [this.editObjectiveAction(objective), this.completeObjectiveAction(objective), this.objectiveBackToDraft()]; + } + + private getCompletedMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { + return [this.objectiveReopen()]; + } + + private releaseFromQuarterAction(objective: ObjectiveMin): ObjectiveMenuEntry { + const action: ObjectiveMenuAction = () => this.dialogService.openConfirmDialog('CONFIRMATION.RELEASE'); + const afterAction: ObjectiveMenuAfterAction = (objective, dialogResult) => + this.afterActions.releaseFromQuarter(objective); return { displayName: 'Objective veröffentlichen', action: action, afterAction: afterAction }; } - private releaseFromDraftInBacklogAction(objective: ObjectiveMin): ObjectiveMenuEntry { + private releaseFromBacklogAction(objective: ObjectiveMin): ObjectiveMenuEntry { const config = { data: { objective: { objectiveId: objective }, action: 'releaseBacklog' } }; const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); - const afterAction = () => {}; + const afterAction: ObjectiveMenuAfterAction = () => this.refreshDataService.markDataRefresh(); return { displayName: 'Objective veröffentlichen', action: action, afterAction }; } private editObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { const config = { data: { objective: { objectiveId: objective.id } } }; const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); - const afterAction = () => {}; + const afterAction: ObjectiveMenuAfterAction = () => {}; return { displayName: 'Objective bearbeiten', action: action, afterAction: afterAction }; } private duplicateObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { const config = { data: { objective: { objectiveId: objective.id } } }; - const action = () => this.dialogService.open(ObjectiveFormComponent, config); - const afterAction = () => {}; + const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); + const afterAction: ObjectiveMenuAfterAction = () => this.refreshDataService.markDataRefresh(); return { displayName: 'Objective duplizieren', action: action, afterAction: afterAction }; } @@ -83,20 +100,26 @@ export class ObjectiveMenuActionsService { const config = { data: { objectiveTitle: objective.title }, }; - const action = () => this.dialogService.open(CompleteDialogComponent, config); - const afterAction = () => {}; + const action: ObjectiveMenuAction = () => this.dialogService.open(CompleteDialogComponent, config); + const afterAction: ObjectiveMenuAfterAction = (obj: Objective, result: any) => + this.afterActions.completeObjective(obj, result); return { displayName: 'Objective abschliessen', action: action, afterAction: afterAction }; } - private saveObjectiveAsDraftAction(): ObjectiveMenuEntry { - const action = () => this.dialogService.openConfirmDialog('CONFIRMATION.DRAFT_CREATE'); - const afterAction = () => {}; + private objectiveBackToDraft(): ObjectiveMenuEntry { + const action: ObjectiveMenuAction = () => this.dialogService.openConfirmDialog('CONFIRMATION.DRAFT_CREATE'); + const afterAction: ObjectiveMenuAfterAction = (obj: Objective, result: any) => + this.afterActions.objectiveBackToDraft(obj); return { displayName: 'Objective als Draft speicherns', action: action, afterAction: afterAction }; } - private getCompletedMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { - return []; + private objectiveReopen(): ObjectiveMenuEntry { + const action: ObjectiveMenuAction = () => this.dialogService.openConfirmDialog('CONFIRMATION.REOPEN'); + const afterAction: ObjectiveMenuAfterAction = (obj: Objective, result: any) => + this.afterActions.objectiveReopen(obj); + + return { displayName: 'Objective wiedereröffnen', action: action, afterAction: afterAction }; } } diff --git a/frontend/src/assets/i18n/de.json b/frontend/src/assets/i18n/de.json index f723866d85..17d9a36978 100644 --- a/frontend/src/assets/i18n/de.json +++ b/frontend/src/assets/i18n/de.json @@ -33,6 +33,10 @@ "TITLE": "Check-in im Draft-Status", "TEXT": "Dein Objective befindet sich noch im DRAFT Status. Möchtest du das Check-in trotzdem erfassen?" }, + "REOPEN": { + "TITLE": "Objective wiedereröffnen", + "TEXT": "Soll dieses Objective wiedereröffnet werden?" + }, "RELEASE": { "TITLE": "Objective veröffentlichen", "TEXT": "Soll dieses Objective veröffentlicht werden?" From 424045d891fc95a7fd73e3d9061efa98f025a095 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Wed, 23 Oct 2024 16:06:21 +0200 Subject: [PATCH 18/44] introduce objectiveMenuAction file --- .../objective/ObjectiveMenuActionFactory.ts | 75 +++++++++++++++++ ...actory.ts => ObjectiveMenuAfterActions.ts} | 4 +- .../objective/objective.component.ts | 23 ----- .../objective-menu-actions.service.ts | 84 ++++--------------- 4 files changed, 93 insertions(+), 93 deletions(-) create mode 100644 frontend/src/app/components/objective/ObjectiveMenuActionFactory.ts rename frontend/src/app/components/objective/{ObjectiveMenuAfterActionFactory.ts => ObjectiveMenuAfterActions.ts} (91%) diff --git a/frontend/src/app/components/objective/ObjectiveMenuActionFactory.ts b/frontend/src/app/components/objective/ObjectiveMenuActionFactory.ts new file mode 100644 index 0000000000..917ef05ae8 --- /dev/null +++ b/frontend/src/app/components/objective/ObjectiveMenuActionFactory.ts @@ -0,0 +1,75 @@ +import { DialogService } from '../../services/dialog.service'; +import { Objective } from '../../shared/types/model/Objective'; +import { RefreshDataService } from '../../services/refresh-data.service'; +import { ObjectiveMin } from '../../shared/types/model/ObjectiveMin'; +import { ObjectiveFormComponent } from '../../shared/dialog/objective-dialog/objective-form.component'; +import { CompleteDialogComponent } from '../../shared/dialog/complete-dialog/complete-dialog.component'; +import { + ObjectiveMenuAction, + ObjectiveMenuAfterAction, + ObjectiveMenuEntry, +} from '../../services/objective-menu-actions.service'; +import { ObjectiveMenuAfterActions } from './ObjectiveMenuAfterActions'; + +export class ObjectiveMenuActions { + constructor( + private readonly dialogService: DialogService, + private readonly refreshDataService: RefreshDataService, + private readonly afterActions: ObjectiveMenuAfterActions, + ) {} + + releaseFromQuarterAction(objective: ObjectiveMin): ObjectiveMenuEntry { + const action: ObjectiveMenuAction = () => this.dialogService.openConfirmDialog('CONFIRMATION.RELEASE'); + const afterAction: ObjectiveMenuAfterAction = (objective, dialogResult) => + this.afterActions.releaseFromQuarter(objective); + return { displayName: 'Objective veröffentlichen', action: action, afterAction: afterAction }; + } + + releaseFromBacklogAction(objective: ObjectiveMin): ObjectiveMenuEntry { + const config = { data: { objective: { objectiveId: objective }, action: 'releaseBacklog' } }; + const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); + const afterAction: ObjectiveMenuAfterAction = () => this.refreshDataService.markDataRefresh(); + return { displayName: 'Objective veröffentlichen', action: action, afterAction }; + } + + editObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { + const config = { data: { objective: { objectiveId: objective.id } } }; + const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); + const afterAction: ObjectiveMenuAfterAction = () => {}; + return { displayName: 'Objective bearbeiten', action: action, afterAction: afterAction }; + } + + duplicateObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { + const config = { data: { objective: { objectiveId: objective.id } } }; + const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); + const afterAction: ObjectiveMenuAfterAction = () => this.refreshDataService.markDataRefresh(); + return { displayName: 'Objective duplizieren', action: action, afterAction: afterAction }; + } + + completeObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { + const config = { + data: { objectiveTitle: objective.title }, + }; + const action: ObjectiveMenuAction = () => this.dialogService.open(CompleteDialogComponent, config); + const afterAction: ObjectiveMenuAfterAction = (obj: Objective, result: any) => + this.afterActions.completeObjective(obj, result); + + return { displayName: 'Objective abschliessen', action: action, afterAction: afterAction }; + } + + objectiveBackToDraft(): ObjectiveMenuEntry { + const action: ObjectiveMenuAction = () => this.dialogService.openConfirmDialog('CONFIRMATION.DRAFT_CREATE'); + const afterAction: ObjectiveMenuAfterAction = (obj: Objective, result: any) => + this.afterActions.objectiveBackToDraft(obj); + + return { displayName: 'Objective als Draft speicherns', action: action, afterAction: afterAction }; + } + + objectiveReopen(): ObjectiveMenuEntry { + const action: ObjectiveMenuAction = () => this.dialogService.openConfirmDialog('CONFIRMATION.REOPEN'); + const afterAction: ObjectiveMenuAfterAction = (obj: Objective, result: any) => + this.afterActions.objectiveReopen(obj); + + return { displayName: 'Objective wiedereröffnen', action: action, afterAction: afterAction }; + } +} diff --git a/frontend/src/app/components/objective/ObjectiveMenuAfterActionFactory.ts b/frontend/src/app/components/objective/ObjectiveMenuAfterActions.ts similarity index 91% rename from frontend/src/app/components/objective/ObjectiveMenuAfterActionFactory.ts rename to frontend/src/app/components/objective/ObjectiveMenuAfterActions.ts index ffdf30909f..5691305fa9 100644 --- a/frontend/src/app/components/objective/ObjectiveMenuAfterActionFactory.ts +++ b/frontend/src/app/components/objective/ObjectiveMenuAfterActions.ts @@ -1,13 +1,11 @@ -import { DialogService } from '../../services/dialog.service'; import { Objective } from '../../shared/types/model/Objective'; import { State } from '../../shared/types/enums/State'; import { Completed } from '../../shared/types/model/Completed'; import { ObjectiveService } from '../../services/objective.service'; import { RefreshDataService } from '../../services/refresh-data.service'; -export class ObjectiveMenuAfterActionFactory { +export class ObjectiveMenuAfterActions { constructor( - private readonly dialogService: DialogService, private readonly objectiveService: ObjectiveService, private readonly refreshDataService: RefreshDataService, ) {} diff --git a/frontend/src/app/components/objective/objective.component.ts b/frontend/src/app/components/objective/objective.component.ts index f198fdb3a8..f0623ac54a 100644 --- a/frontend/src/app/components/objective/objective.component.ts +++ b/frontend/src/app/components/objective/objective.component.ts @@ -1,13 +1,9 @@ import { Component, Input, OnInit } from '@angular/core'; -import { MenuEntry } from '../../shared/types/menu-entry'; import { ObjectiveMin } from '../../shared/types/model/ObjectiveMin'; import { Router } from '@angular/router'; import { BehaviorSubject, map } from 'rxjs'; import { RefreshDataService } from '../../services/refresh-data.service'; -import { State } from '../../shared/types/enums/State'; import { ObjectiveService } from '../../services/objective.service'; -import { Completed } from '../../shared/types/model/Completed'; -import { Objective } from '../../shared/types/model/Objective'; import { isObjectiveComplete, trackByFn } from '../../shared/common'; import { KeyresultDialogComponent } from '../keyresult-dialog/keyresult-dialog.component'; import { TranslateService } from '@ngx-translate/core'; @@ -65,25 +61,6 @@ export class ObjectiveComponent implements OnInit { }); } - handleDialogResult(menuEntry: MenuEntry, result: { endState: string; comment: string | null; objective: any }) { - if (menuEntry.action) { - this.objectiveService.getFullObjective(this.objective$.value.id).subscribe((objective) => { - if (menuEntry.action == 'complete') { - } else if (menuEntry.action == 'release') { - } else if (menuEntry.action == 'duplicate') { - this.refreshDataService.markDataRefresh(); - } else if (menuEntry.action == 'releaseBacklog') { - this.refreshDataService.markDataRefresh(); - } else if (menuEntry.action == 'todraft') { - } - }); - } else { - if (result?.objective) { - this.refreshDataService.markDataRefresh(); - } - } - } - openObjectiveDetail(objectiveId: number) { this.router.navigate(['details/objective', objectiveId]); } diff --git a/frontend/src/app/services/objective-menu-actions.service.ts b/frontend/src/app/services/objective-menu-actions.service.ts index 86e8acd173..765678ed24 100644 --- a/frontend/src/app/services/objective-menu-actions.service.ts +++ b/frontend/src/app/services/objective-menu-actions.service.ts @@ -1,15 +1,14 @@ import { Injectable } from '@angular/core'; import { DialogService } from './dialog.service'; import { MatDialogRef } from '@angular/material/dialog'; -import { ObjectiveFormComponent } from '../shared/dialog/objective-dialog/objective-form.component'; -import { CompleteDialogComponent } from '../shared/dialog/complete-dialog/complete-dialog.component'; import { ObjectiveMin } from '../shared/types/model/ObjectiveMin'; import { State } from '../shared/types/enums/State'; import { isInBacklogQuarter, isObjectiveComplete } from '../shared/common'; -import { ObjectiveMenuAfterActionFactory } from '../components/objective/ObjectiveMenuAfterActionFactory'; +import { ObjectiveMenuAfterActions } from '../components/objective/ObjectiveMenuAfterActions'; import { ObjectiveService } from './objective.service'; import { RefreshDataService } from './refresh-data.service'; import { Objective } from '../shared/types/model/Objective'; +import { ObjectiveMenuActions } from '../components/objective/ObjectiveMenuActionFactory'; export type ObjectiveMenuAction = () => MatDialogRef; export type ObjectiveMenuAfterAction = (objective: Objective, dialogResult: any) => any; @@ -24,13 +23,15 @@ export interface ObjectiveMenuEntry { providedIn: 'root', }) export class ObjectiveMenuActionsService { - afterActions: ObjectiveMenuAfterActionFactory; + afterActions: ObjectiveMenuAfterActions; + actions: ObjectiveMenuActions; constructor( - private readonly dialogService: DialogService, + dialogService: DialogService, objectiveService: ObjectiveService, - private readonly refreshDataService: RefreshDataService, + refreshDataService: RefreshDataService, ) { - this.afterActions = new ObjectiveMenuAfterActionFactory(dialogService, objectiveService, refreshDataService); + this.afterActions = new ObjectiveMenuAfterActions(objectiveService, refreshDataService); + this.actions = new ObjectiveMenuActions(dialogService, refreshDataService, this.afterActions); } getMenu(objective: ObjectiveMin): ObjectiveMenuEntry[] { @@ -50,76 +51,25 @@ export class ObjectiveMenuActionsService { } private getDefaultActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { - return [this.duplicateObjectiveAction(objective)]; + return [this.actions.duplicateObjectiveAction(objective)]; } private getDraftMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { const releaseAction = isInBacklogQuarter(objective) - ? this.releaseFromBacklogAction(objective) - : this.releaseFromQuarterAction(objective); + ? this.actions.releaseFromBacklogAction(objective) + : this.actions.releaseFromQuarterAction(objective); return [releaseAction]; } private getOngoingMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { - return [this.editObjectiveAction(objective), this.completeObjectiveAction(objective), this.objectiveBackToDraft()]; + return [ + this.actions.editObjectiveAction(objective), + this.actions.completeObjectiveAction(objective), + this.actions.objectiveBackToDraft(), + ]; } private getCompletedMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { - return [this.objectiveReopen()]; - } - - private releaseFromQuarterAction(objective: ObjectiveMin): ObjectiveMenuEntry { - const action: ObjectiveMenuAction = () => this.dialogService.openConfirmDialog('CONFIRMATION.RELEASE'); - const afterAction: ObjectiveMenuAfterAction = (objective, dialogResult) => - this.afterActions.releaseFromQuarter(objective); - return { displayName: 'Objective veröffentlichen', action: action, afterAction: afterAction }; - } - - private releaseFromBacklogAction(objective: ObjectiveMin): ObjectiveMenuEntry { - const config = { data: { objective: { objectiveId: objective }, action: 'releaseBacklog' } }; - const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); - const afterAction: ObjectiveMenuAfterAction = () => this.refreshDataService.markDataRefresh(); - return { displayName: 'Objective veröffentlichen', action: action, afterAction }; - } - - private editObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { - const config = { data: { objective: { objectiveId: objective.id } } }; - const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); - const afterAction: ObjectiveMenuAfterAction = () => {}; - return { displayName: 'Objective bearbeiten', action: action, afterAction: afterAction }; - } - - private duplicateObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { - const config = { data: { objective: { objectiveId: objective.id } } }; - const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); - const afterAction: ObjectiveMenuAfterAction = () => this.refreshDataService.markDataRefresh(); - return { displayName: 'Objective duplizieren', action: action, afterAction: afterAction }; - } - - private completeObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { - const config = { - data: { objectiveTitle: objective.title }, - }; - const action: ObjectiveMenuAction = () => this.dialogService.open(CompleteDialogComponent, config); - const afterAction: ObjectiveMenuAfterAction = (obj: Objective, result: any) => - this.afterActions.completeObjective(obj, result); - - return { displayName: 'Objective abschliessen', action: action, afterAction: afterAction }; - } - - private objectiveBackToDraft(): ObjectiveMenuEntry { - const action: ObjectiveMenuAction = () => this.dialogService.openConfirmDialog('CONFIRMATION.DRAFT_CREATE'); - const afterAction: ObjectiveMenuAfterAction = (obj: Objective, result: any) => - this.afterActions.objectiveBackToDraft(obj); - - return { displayName: 'Objective als Draft speicherns', action: action, afterAction: afterAction }; - } - - private objectiveReopen(): ObjectiveMenuEntry { - const action: ObjectiveMenuAction = () => this.dialogService.openConfirmDialog('CONFIRMATION.REOPEN'); - const afterAction: ObjectiveMenuAfterAction = (obj: Objective, result: any) => - this.afterActions.objectiveReopen(obj); - - return { displayName: 'Objective wiedereröffnen', action: action, afterAction: afterAction }; + return [this.actions.objectiveReopen()]; } } From 3ddc34d026d1dd665384a478c04a0071bcb8754f Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Wed, 23 Oct 2024 16:39:46 +0200 Subject: [PATCH 19/44] refactor objective status tooltip --- .../objective/objective.component.html | 2 +- .../objective/objective.component.ts | 22 +++++-------------- frontend/src/app/shared/common.ts | 4 ++++ frontend/src/assets/i18n/de.json | 5 +---- 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/frontend/src/app/components/objective/objective.component.html b/frontend/src/app/components/objective/objective.component.html index 54ddd22757..6d6e8115dc 100644 --- a/frontend/src/app/components/objective/objective.component.html +++ b/frontend/src/app/components/objective/objective.component.html @@ -15,7 +15,7 @@ [src]="'assets/icons/' + objective.state" alt="The objectives state" class="icon" - matTooltip="{{ getStateTooltip() + ' ' + formatObjectiveState(objective.state) }}" + matTooltip="{{ getStateTooltip(objective.state) }}" matTooltipPosition="above" />

{{ objective.title }}

diff --git a/frontend/src/app/components/objective/objective.component.ts b/frontend/src/app/components/objective/objective.component.ts index f0623ac54a..e79de7c11b 100644 --- a/frontend/src/app/components/objective/objective.component.ts +++ b/frontend/src/app/components/objective/objective.component.ts @@ -1,10 +1,10 @@ import { Component, Input, OnInit } from '@angular/core'; import { ObjectiveMin } from '../../shared/types/model/ObjectiveMin'; import { Router } from '@angular/router'; -import { BehaviorSubject, map } from 'rxjs'; +import { map, Subject } from 'rxjs'; import { RefreshDataService } from '../../services/refresh-data.service'; import { ObjectiveService } from '../../services/objective.service'; -import { isObjectiveComplete, trackByFn } from '../../shared/common'; +import { getStateByValue, isObjectiveComplete, trackByFn } from '../../shared/common'; import { KeyresultDialogComponent } from '../keyresult-dialog/keyresult-dialog.component'; import { TranslateService } from '@ngx-translate/core'; import { DialogService } from '../../services/dialog.service'; @@ -17,11 +17,9 @@ import { ObjectiveMenuActionsService, ObjectiveMenuEntry } from '../../services/ }) export class ObjectiveComponent implements OnInit { @Input() isWritable!: boolean; - isComplete: boolean = false; - public objective$ = new BehaviorSubject({} as ObjectiveMin); + public objective$ = new Subject(); menuEntries = this.objective$.pipe(map((objective) => this.objectiveMenuActionsService.getMenu(objective))); protected readonly trackByFn = trackByFn; - protected readonly console = console; protected readonly isObjectiveComplete = isObjectiveComplete; constructor( @@ -39,17 +37,9 @@ export class ObjectiveComponent implements OnInit { ngOnInit() {} - formatObjectiveState(state: string): string { - const lastIndex = state.lastIndexOf('-'); - if (lastIndex !== -1) { - return state.substring(0, lastIndex).toUpperCase(); - } else { - return state.toUpperCase(); - } - } - - getStateTooltip(): string { - return this.translate.instant('INFORMATION.OBJECTIVE_STATE_TOOLTIP'); + getStateTooltip(stateString: string): string { + const state = getStateByValue(stateString); + return this.translate.instant('INFORMATION.OBJECTIVE_STATE_TOOLTIP', { state: state }); } redirect(menuEntry: ObjectiveMenuEntry, objectiveMin: ObjectiveMin) { diff --git a/frontend/src/app/shared/common.ts b/frontend/src/app/shared/common.ts index c5f9734b20..8dd48aceca 100644 --- a/frontend/src/app/shared/common.ts +++ b/frontend/src/app/shared/common.ts @@ -109,3 +109,7 @@ export function hasFormFieldErrors(formGroup: FormGroup, field: string) { return false; } } + +export function getStateByValue(value: string): string { + return Object.keys(State).find((key) => State[key as keyof typeof State] === value) ?? ''; +} diff --git a/frontend/src/assets/i18n/de.json b/frontend/src/assets/i18n/de.json index 17d9a36978..be9c8d61a4 100644 --- a/frontend/src/assets/i18n/de.json +++ b/frontend/src/assets/i18n/de.json @@ -23,10 +23,7 @@ "ordinal": "Ordinal" }, "INFORMATION": { - "OBJECTIVE_STATE_TOOLTIP": "Der Status dieses Objectives ist", - "DELETE_TEAM": "Möchtest du dieses Team wirklich löschen? Zugehörige Objectives werden dadurch in allen Quartalen ebenfalls gelöscht!", - "DELETE_OBJECTIVE": "Möchtest du dieses Objective wirklich löschen? Zugehörige Key Results werden dadurch ebenfalls gelöscht!", - "DELETE_KEY_RESULT": "Möchtest du dieses Key Result wirklich löschen? Zugehörige Check-ins werden dadurch ebenfalls gelöscht!" + "OBJECTIVE_STATE_TOOLTIP": "Der Status dieses Objectives ist: {{state}}" }, "CONFIRMATION": { "DRAFT_CREATE": { From 2f89f2ef3f6870ae64ca523645def75b1432e8a9 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Thu, 24 Oct 2024 09:45:15 +0200 Subject: [PATCH 20/44] clean up objective menu action service --- .../app/services/objective-menu-actions.service.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/services/objective-menu-actions.service.ts b/frontend/src/app/services/objective-menu-actions.service.ts index 765678ed24..3f5c0adc47 100644 --- a/frontend/src/app/services/objective-menu-actions.service.ts +++ b/frontend/src/app/services/objective-menu-actions.service.ts @@ -55,10 +55,7 @@ export class ObjectiveMenuActionsService { } private getDraftMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { - const releaseAction = isInBacklogQuarter(objective) - ? this.actions.releaseFromBacklogAction(objective) - : this.actions.releaseFromQuarterAction(objective); - return [releaseAction]; + return [this.getReleaseAction(objective)]; } private getOngoingMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { @@ -72,4 +69,10 @@ export class ObjectiveMenuActionsService { private getCompletedMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { return [this.actions.objectiveReopen()]; } + + private getReleaseAction(objective: ObjectiveMin): ObjectiveMenuEntry { + return isInBacklogQuarter(objective) + ? this.actions.releaseFromBacklogAction(objective) + : this.actions.releaseFromQuarterAction(objective); + } } From f011cb9775a9d39ba134b9ab249b38fe8604d56e Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Fri, 25 Oct 2024 10:36:10 +0200 Subject: [PATCH 21/44] rename objective actions file --- .../{ObjectiveMenuActionFactory.ts => ObjectiveMenuActions.ts} | 0 frontend/src/app/services/objective-menu-actions.service.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename frontend/src/app/components/objective/{ObjectiveMenuActionFactory.ts => ObjectiveMenuActions.ts} (100%) diff --git a/frontend/src/app/components/objective/ObjectiveMenuActionFactory.ts b/frontend/src/app/components/objective/ObjectiveMenuActions.ts similarity index 100% rename from frontend/src/app/components/objective/ObjectiveMenuActionFactory.ts rename to frontend/src/app/components/objective/ObjectiveMenuActions.ts diff --git a/frontend/src/app/services/objective-menu-actions.service.ts b/frontend/src/app/services/objective-menu-actions.service.ts index 3f5c0adc47..f8feec6efb 100644 --- a/frontend/src/app/services/objective-menu-actions.service.ts +++ b/frontend/src/app/services/objective-menu-actions.service.ts @@ -8,7 +8,7 @@ import { ObjectiveMenuAfterActions } from '../components/objective/ObjectiveMenu import { ObjectiveService } from './objective.service'; import { RefreshDataService } from './refresh-data.service'; import { Objective } from '../shared/types/model/Objective'; -import { ObjectiveMenuActions } from '../components/objective/ObjectiveMenuActionFactory'; +import { ObjectiveMenuActions } from '../components/objective/ObjectiveMenuActions'; export type ObjectiveMenuAction = () => MatDialogRef; export type ObjectiveMenuAfterAction = (objective: Objective, dialogResult: any) => any; From 942df1e17276a91a63a925ca18caa36bf55a47a0 Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 28 Oct 2024 11:37:12 +0100 Subject: [PATCH 22/44] Try to fix frontend unit tests --- .../components/objective/objective.component.spec.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/components/objective/objective.component.spec.ts b/frontend/src/app/components/objective/objective.component.spec.ts index c429f95f77..37fa475c23 100644 --- a/frontend/src/app/components/objective/objective.component.spec.ts +++ b/frontend/src/app/components/objective/objective.component.spec.ts @@ -25,6 +25,8 @@ import { ObjectiveMin } from '../../shared/types/model/ObjectiveMin'; import { MenuEntry } from '../../shared/types/menu-entry'; import { of } from 'rxjs'; import { ObjectiveService } from '../../services/objective.service'; +import { ObjectiveMenuActions } from './ObjectiveMenuActions'; +import { ObjectiveMenuActionsService } from '../../services/objective-menu-actions.service'; const overviewServiceMock = { getObjectiveWithKeyresults: jest.fn(), @@ -39,6 +41,8 @@ const objectiveServiceMock = { }; describe('ObjectiveColumnComponent', () => { let component: ObjectiveComponent; + let bum: ObjectiveMenuActions; + let bam: ObjectiveMenuActionsService; let fixture: ComponentFixture; let loader: HarnessLoader; beforeEach(() => { @@ -116,19 +120,19 @@ describe('ObjectiveColumnComponent', () => { }); it('Correct method should be called when back to draft is clicked', () => { - jest.spyOn(component, 'objectiveBackToDraft'); + jest.spyOn(bum, 'objectiveBackToDraft'); component.objective$.next(objectiveMin); fixture.detectChanges(); const menuEntry: MenuEntry = - component.getOngoingMenuActions()[ - component + bam.getOngoingMenuActions()[ + bam .getOngoingMenuActions() .map((menuAction) => menuAction.action) .indexOf('todraft') ]; component.handleDialogResult(menuEntry, { endState: '', comment: null, objective: objective }); fixture.detectChanges(); - expect(component.objectiveBackToDraft).toHaveBeenCalled(); + expect(bum.objectiveBackToDraft).toHaveBeenCalled(); }); test('Should set isBacklogQuarter right', async () => { From 44690e2c708ab02add94c2bf1b3afdd07794d292 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Mon, 4 Nov 2024 16:56:56 +0100 Subject: [PATCH 23/44] restore staging.properties --- backend/src/main/resources/application-staging.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/resources/application-staging.properties b/backend/src/main/resources/application-staging.properties index 31ce92a3c2..effd67d65a 100644 --- a/backend/src/main/resources/application-staging.properties +++ b/backend/src/main/resources/application-staging.properties @@ -4,7 +4,7 @@ logging.level.org.springframework=debug connect.src=http://localhost:8544 http://localhost:8545 -hibernate.connection.url=jdbc:postgresql://okr-dev-db:5432/okr +hibernate.connection.url=jdbc:postgresql://localhost:5432/okr hibernate.connection.username=user hibernate.connection.password=pwd hibernate.multiTenancy=SCHEMA @@ -16,7 +16,7 @@ okr.datasource.driver-class-name=org.postgresql.Driver okr.user.champion.usernames=peggimann # pitc -okr.tenants.pitc.datasource.url=jdbc:postgresql://okr-dev-db:5432/okr +okr.tenants.pitc.datasource.url=jdbc:postgresql://localhost:5432/okr okr.tenants.pitc.datasource.username=user okr.tenants.pitc.datasource.password=pwd okr.tenants.pitc.datasource.schema=okr_pitc @@ -27,7 +27,7 @@ okr.tenants.pitc.security.oauth2.frontend.client-id=pitc_okr_staging # acme -okr.tenants.acme.datasource.url=jdbc:postgresql://okr-dev-db:5432/okr +okr.tenants.acme.datasource.url=jdbc:postgresql://localhost:5432/okr okr.tenants.acme.datasource.username=user okr.tenants.acme.datasource.password=pwd okr.tenants.acme.datasource.schema=okr_acme From 88a7202f617391401cb405a9b0d923849d877986 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 5 Nov 2024 08:42:24 +0100 Subject: [PATCH 24/44] fix angular build --- frontend/angular.json | 1 + frontend/package.json | 2 +- frontend/tsconfig.app.json | 5 ++++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/angular.json b/frontend/angular.json index ee55dc2591..0ce4a3b939 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -17,6 +17,7 @@ "build": { "builder": "@angular-devkit/build-angular:browser", "options": { + "aot": true, "outputPath": "dist/frontend", "index": "src/index.html", "main": "src/main.ts", diff --git a/frontend/package.json b/frontend/package.json index 08e2415442..e62b0a87d2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,7 +25,6 @@ "@angular/animations": "^18.2.8", "@angular/cdk": "^18.2.9", "@angular/common": "^18.2.8", - "@angular/compiler": "^18.2.8", "@angular/core": "^18.2.8", "@angular/forms": "^18.2.8", "@angular/material": "^18.2.9", @@ -43,6 +42,7 @@ "tslib": "^2.8.0" }, "devDependencies": { + "@angular/compiler": "^18.2.8", "@angular-devkit/build-angular": "^18.2.9", "@angular/cli": "~18.2.9", "@angular/compiler-cli": "^18.2.8", diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json index dc270b957f..ccebbdf42b 100644 --- a/frontend/tsconfig.app.json +++ b/frontend/tsconfig.app.json @@ -7,5 +7,8 @@ "typeRoots": ["src/custom-types"] }, "files": ["src/main.ts"], - "include": ["src/**/*.d.ts"] + "include": ["src/**/*.d.ts"], + "angularCompilerOptions": { + "enableIvy": true + } } From b79af1f8e62d7d497b03e7875e6179b7436b18de Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 5 Nov 2024 09:29:27 +0100 Subject: [PATCH 25/44] place functions in related files instead of common.ts --- .../components/objective/objective.component.ts | 14 +++++++++++--- .../app/services/objective-menu-actions.service.ts | 14 +++++++++++--- frontend/src/app/shared/common.ts | 12 ------------ 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/frontend/src/app/components/objective/objective.component.ts b/frontend/src/app/components/objective/objective.component.ts index e79de7c11b..eb9a9ce14f 100644 --- a/frontend/src/app/components/objective/objective.component.ts +++ b/frontend/src/app/components/objective/objective.component.ts @@ -4,11 +4,12 @@ import { Router } from '@angular/router'; import { map, Subject } from 'rxjs'; import { RefreshDataService } from '../../services/refresh-data.service'; import { ObjectiveService } from '../../services/objective.service'; -import { getStateByValue, isObjectiveComplete, trackByFn } from '../../shared/common'; +import { trackByFn } from '../../shared/common'; import { KeyresultDialogComponent } from '../keyresult-dialog/keyresult-dialog.component'; import { TranslateService } from '@ngx-translate/core'; import { DialogService } from '../../services/dialog.service'; import { ObjectiveMenuActionsService, ObjectiveMenuEntry } from '../../services/objective-menu-actions.service'; +import { State } from '../../shared/types/enums/State'; @Component({ selector: 'app-objective-column', @@ -20,7 +21,6 @@ export class ObjectiveComponent implements OnInit { public objective$ = new Subject(); menuEntries = this.objective$.pipe(map((objective) => this.objectiveMenuActionsService.getMenu(objective))); protected readonly trackByFn = trackByFn; - protected readonly isObjectiveComplete = isObjectiveComplete; constructor( private dialogService: DialogService, @@ -38,7 +38,7 @@ export class ObjectiveComponent implements OnInit { ngOnInit() {} getStateTooltip(stateString: string): string { - const state = getStateByValue(stateString); + const state = this.getStateByValue(stateString); return this.translate.instant('INFORMATION.OBJECTIVE_STATE_TOOLTIP', { state: state }); } @@ -71,4 +71,12 @@ export class ObjectiveComponent implements OnInit { this.refreshDataService.markDataRefresh(); }); } + + isObjectiveComplete(objective: ObjectiveMin): boolean { + return objective.state == State.SUCCESSFUL || objective.state == State.NOTSUCCESSFUL; + } + + getStateByValue(value: string): string { + return Object.keys(State).find((key) => State[key as keyof typeof State] === value) ?? ''; + } } diff --git a/frontend/src/app/services/objective-menu-actions.service.ts b/frontend/src/app/services/objective-menu-actions.service.ts index f8feec6efb..2057c8d3a3 100644 --- a/frontend/src/app/services/objective-menu-actions.service.ts +++ b/frontend/src/app/services/objective-menu-actions.service.ts @@ -3,12 +3,12 @@ import { DialogService } from './dialog.service'; import { MatDialogRef } from '@angular/material/dialog'; import { ObjectiveMin } from '../shared/types/model/ObjectiveMin'; import { State } from '../shared/types/enums/State'; -import { isInBacklogQuarter, isObjectiveComplete } from '../shared/common'; import { ObjectiveMenuAfterActions } from '../components/objective/ObjectiveMenuAfterActions'; import { ObjectiveService } from './objective.service'; import { RefreshDataService } from './refresh-data.service'; import { Objective } from '../shared/types/model/Objective'; import { ObjectiveMenuActions } from '../components/objective/ObjectiveMenuActions'; +import { GJ_REGEX_PATTERN } from '../shared/constantLibary'; export type ObjectiveMenuAction = () => MatDialogRef; export type ObjectiveMenuAfterAction = (objective: Objective, dialogResult: any) => any; @@ -39,7 +39,7 @@ export class ObjectiveMenuActionsService { } private getSpecificMenuEntries(objective: ObjectiveMin): ObjectiveMenuEntry[] { - if (isObjectiveComplete(objective)) { + if (this.isObjectiveComplete(objective)) { return this.getCompletedMenuActions(objective); } else if (objective.state === State.ONGOING) { return this.getOngoingMenuActions(objective); @@ -71,8 +71,16 @@ export class ObjectiveMenuActionsService { } private getReleaseAction(objective: ObjectiveMin): ObjectiveMenuEntry { - return isInBacklogQuarter(objective) + return this.isInBacklogQuarter(objective) ? this.actions.releaseFromBacklogAction(objective) : this.actions.releaseFromQuarterAction(objective); } + + private isObjectiveComplete(objective: ObjectiveMin): boolean { + return objective.state == State.SUCCESSFUL || objective.state == State.NOTSUCCESSFUL; + } + + private isInBacklogQuarter(objective: ObjectiveMin) { + return !GJ_REGEX_PATTERN.test(objective.quarter.label); + } } diff --git a/frontend/src/app/shared/common.ts b/frontend/src/app/shared/common.ts index 8dd48aceca..08d308d47d 100644 --- a/frontend/src/app/shared/common.ts +++ b/frontend/src/app/shared/common.ts @@ -94,14 +94,6 @@ export function isMobileDevice() { return window.navigator.userAgent.toLowerCase().includes('mobile'); } -export function isInBacklogQuarter(objective: ObjectiveMin) { - return !GJ_REGEX_PATTERN.test(objective.quarter.label); -} - -export function isObjectiveComplete(objective: ObjectiveMin): boolean { - return objective.state == State.SUCCESSFUL || objective.state == State.NOTSUCCESSFUL; -} - export function hasFormFieldErrors(formGroup: FormGroup, field: string) { if (formGroup.get(field)?.dirty || formGroup.get(field)?.touched) { return formGroup.get(field)?.errors; @@ -109,7 +101,3 @@ export function hasFormFieldErrors(formGroup: FormGroup, field: string) { return false; } } - -export function getStateByValue(value: string): string { - return Object.keys(State).find((key) => State[key as keyof typeof State] === value) ?? ''; -} From 8b31a448e1cca40e0847578b4077217cc63c934a Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 5 Nov 2024 09:34:22 +0100 Subject: [PATCH 26/44] restore angular config files --- frontend/angular.json | 1 - frontend/package.json | 2 +- frontend/tsconfig.app.json | 5 +---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/frontend/angular.json b/frontend/angular.json index 0ce4a3b939..ee55dc2591 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -17,7 +17,6 @@ "build": { "builder": "@angular-devkit/build-angular:browser", "options": { - "aot": true, "outputPath": "dist/frontend", "index": "src/index.html", "main": "src/main.ts", diff --git a/frontend/package.json b/frontend/package.json index e62b0a87d2..08e2415442 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,6 +25,7 @@ "@angular/animations": "^18.2.8", "@angular/cdk": "^18.2.9", "@angular/common": "^18.2.8", + "@angular/compiler": "^18.2.8", "@angular/core": "^18.2.8", "@angular/forms": "^18.2.8", "@angular/material": "^18.2.9", @@ -42,7 +43,6 @@ "tslib": "^2.8.0" }, "devDependencies": { - "@angular/compiler": "^18.2.8", "@angular-devkit/build-angular": "^18.2.9", "@angular/cli": "~18.2.9", "@angular/compiler-cli": "^18.2.8", diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json index ccebbdf42b..dc270b957f 100644 --- a/frontend/tsconfig.app.json +++ b/frontend/tsconfig.app.json @@ -7,8 +7,5 @@ "typeRoots": ["src/custom-types"] }, "files": ["src/main.ts"], - "include": ["src/**/*.d.ts"], - "angularCompilerOptions": { - "enableIvy": true - } + "include": ["src/**/*.d.ts"] } From fdb13181d4ee4e163a94059720ff1ec5e296d592 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 5 Nov 2024 09:38:33 +0100 Subject: [PATCH 27/44] fix action service tests --- .../app/services/objective-menu-actions.service.spec.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/services/objective-menu-actions.service.spec.ts b/frontend/src/app/services/objective-menu-actions.service.spec.ts index aef8e3b228..49c52b9fa1 100644 --- a/frontend/src/app/services/objective-menu-actions.service.spec.ts +++ b/frontend/src/app/services/objective-menu-actions.service.spec.ts @@ -1,12 +1,19 @@ import { TestBed } from '@angular/core/testing'; import { ObjectiveMenuActionsService } from './objective-menu-actions.service'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; +import { provideRouter } from '@angular/router'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; describe('ObjectiveMenuActionsService', () => { let service: ObjectiveMenuActionsService; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + providers: [TranslateService, provideRouter([]), provideHttpClient(), provideHttpClientTesting()], + }); service = TestBed.inject(ObjectiveMenuActionsService); }); From f569af5cd8dbc42ea8b7a6a38746a8d9e5526980 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 5 Nov 2024 10:03:17 +0100 Subject: [PATCH 28/44] add test for actions service --- .../objective-menu-actions.service.spec.ts | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/frontend/src/app/services/objective-menu-actions.service.spec.ts b/frontend/src/app/services/objective-menu-actions.service.spec.ts index 49c52b9fa1..f912b12b6c 100644 --- a/frontend/src/app/services/objective-menu-actions.service.spec.ts +++ b/frontend/src/app/services/objective-menu-actions.service.spec.ts @@ -5,9 +5,16 @@ import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { provideRouter } from '@angular/router'; import { provideHttpClient } from '@angular/common/http'; import { provideHttpClientTesting } from '@angular/common/http/testing'; +import { ObjectiveService } from './objective.service'; +import { RefreshDataService } from './refresh-data.service'; +import { DialogService } from './dialog.service'; +import { ObjectiveMin } from '../shared/types/model/ObjectiveMin'; +import { State } from '../shared/types/enums/State'; +import { objectiveMin } from '../shared/testData'; describe('ObjectiveMenuActionsService', () => { let service: ObjectiveMenuActionsService; + let specificMenuEntriesSpy: jest.SpyInstance; beforeEach(() => { TestBed.configureTestingModule({ @@ -15,9 +22,71 @@ describe('ObjectiveMenuActionsService', () => { providers: [TranslateService, provideRouter([]), provideHttpClient(), provideHttpClientTesting()], }); service = TestBed.inject(ObjectiveMenuActionsService); + + specificMenuEntriesSpy = jest.spyOn(service as any, 'getSpecificMenuEntries'); }); it('should be created', () => { expect(service).toBeTruthy(); }); + + describe('getMenu', () => { + it('should return default and specific menu entries for an ongoing objective', () => { + let spyOn = jest.spyOn(service as any, 'getOngoingMenuActions'); + + let objectiveMinLocal: ObjectiveMin = objectiveMin; + objectiveMinLocal.state = State.ONGOING; + service.getMenu(objectiveMinLocal); + expect(spyOn).toHaveBeenCalledTimes(1); + }); + + it('should return draft menu entries for a draft objective', () => { + let spyOn = jest.spyOn(service as any, 'getDraftMenuActions'); + + let objectiveMinLocal: ObjectiveMin = objectiveMin; + objectiveMinLocal.state = State.DRAFT; + service.getMenu(objectiveMinLocal); + expect(spyOn).toHaveBeenCalledTimes(1); + }); + + it('should return completed menu entries for a successful objective', () => { + let spyOn = jest.spyOn(service as any, 'getCompletedMenuActions'); + + let objectiveMinLocal: ObjectiveMin = objectiveMin; + objectiveMinLocal.state = State.SUCCESSFUL; + service.getMenu(objectiveMinLocal); + expect(spyOn).toHaveBeenCalledTimes(1); + }); + + it('should return completed menu entries for a non-successful objective', () => { + let spyOn = jest.spyOn(service as any, 'getCompletedMenuActions'); + + let objectiveMinLocal: ObjectiveMin = objectiveMin; + objectiveMinLocal.state = State.NOTSUCCESSFUL; + service.getMenu(objectiveMinLocal); + expect(spyOn).toHaveBeenCalledTimes(1); + }); + afterEach(() => { + expect(specificMenuEntriesSpy).toHaveBeenCalledTimes(1); + }); + }); + + describe('getReleaseAction', () => { + it('should return release from backlog action for an objective in backlog quarter', () => { + jest.spyOn(service as any, 'isInBacklogQuarter').mockReturnValue(true); + let spyOn = jest.spyOn(service as any, 'isInBacklogQuarter').mockReturnValue(true); + let objectiveMinLocal: ObjectiveMin = objectiveMin; + // @ts-expect-error + service.getReleaseAction(objectiveMinLocal); + expect(spyOn).toHaveBeenCalledTimes(1); + }); + + it('should return release from quarter action for an objective in non-backlog quarter', () => { + let spyOn = jest.spyOn(service as any, 'isInBacklogQuarter').mockReturnValue(false); + let objectiveMinLocal: ObjectiveMin = objectiveMin; + // @ts-expect-error + service.getReleaseAction(objectiveMinLocal); + expect(spyOn).toHaveBeenCalledTimes(1); + }); + }); }); From 2822b9c8a49f35653df9472e42d122597057d87c Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 5 Nov 2024 10:09:57 +0100 Subject: [PATCH 29/44] clean up --- .../objective/objective.component.html | 2 + .../objective/objective.component.spec.ts | 44 +------------------ .../objective/objective.component.ts | 13 +++--- 3 files changed, 10 insertions(+), 49 deletions(-) diff --git a/frontend/src/app/components/objective/objective.component.html b/frontend/src/app/components/objective/objective.component.html index 6d6e8115dc..f7c1a03f97 100644 --- a/frontend/src/app/components/objective/objective.component.html +++ b/frontend/src/app/components/objective/objective.component.html @@ -1,3 +1,5 @@ +test +
{ const button = fixture.debugElement.query(By.css('[data-testId="add-keyResult"]')); expect(button).toBeFalsy(); }); - - it('Correct method should be called when back to draft is clicked', () => { - jest.spyOn(bum, 'objectiveBackToDraft'); - component.objective$.next(objectiveMin); - fixture.detectChanges(); - const menuEntry: MenuEntry = - bam.getOngoingMenuActions()[ - bam - .getOngoingMenuActions() - .map((menuAction) => menuAction.action) - .indexOf('todraft') - ]; - component.handleDialogResult(menuEntry, { endState: '', comment: null, objective: objective }); - fixture.detectChanges(); - expect(bum.objectiveBackToDraft).toHaveBeenCalled(); - }); - - test('Should set isBacklogQuarter right', async () => { - expect(component.isBacklogQuarter).toBeFalsy(); - - objectiveMin.quarter.label = 'Backlog'; - - component.objective = objectiveMin; - fixture.detectChanges(); - component.ngOnInit(); - - expect(component.isBacklogQuarter).toBeTruthy(); - }); - - test('Should return correct menu entries when backlog', async () => { - objectiveMin.quarter.label = 'Backlog'; - component.objective = objectiveMin; - fixture.detectChanges(); - component.ngOnInit(); - - let menuActions = component.getDraftMenuActions(); - - expect(menuActions.length).toEqual(3); - expect(menuActions[0].displayName).toEqual('Objective bearbeiten'); - expect(menuActions[1].displayName).toEqual('Objective duplizieren'); - expect(menuActions[2].displayName).toEqual('Objective veröffentlichen'); - }); }); diff --git a/frontend/src/app/components/objective/objective.component.ts b/frontend/src/app/components/objective/objective.component.ts index eb9a9ce14f..cf167fad17 100644 --- a/frontend/src/app/components/objective/objective.component.ts +++ b/frontend/src/app/components/objective/objective.component.ts @@ -23,15 +23,16 @@ export class ObjectiveComponent implements OnInit { protected readonly trackByFn = trackByFn; constructor( - private dialogService: DialogService, - private router: Router, - private refreshDataService: RefreshDataService, - private objectiveService: ObjectiveService, - private translate: TranslateService, - private objectiveMenuActionsService: ObjectiveMenuActionsService, + private readonly dialogService: DialogService, + private readonly router: Router, + private readonly refreshDataService: RefreshDataService, + private readonly objectiveService: ObjectiveService, + private readonly translate: TranslateService, + private readonly objectiveMenuActionsService: ObjectiveMenuActionsService, ) {} @Input() set objective(objective: ObjectiveMin) { + console.log('objective', objective); this.objective$.next(objective); } From 254775fd993ba89fb35451a89c321e3d4b79b0ab Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 5 Nov 2024 10:15:07 +0100 Subject: [PATCH 30/44] display correct options for draft and use replay subject --- .../src/app/components/objective/objective.component.html | 2 -- frontend/src/app/components/objective/objective.component.ts | 4 ++-- frontend/src/app/services/objective-menu-actions.service.ts | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/components/objective/objective.component.html b/frontend/src/app/components/objective/objective.component.html index f7c1a03f97..6d6e8115dc 100644 --- a/frontend/src/app/components/objective/objective.component.html +++ b/frontend/src/app/components/objective/objective.component.html @@ -1,5 +1,3 @@ -test -
(); + public objective$ = new ReplaySubject(); menuEntries = this.objective$.pipe(map((objective) => this.objectiveMenuActionsService.getMenu(objective))); protected readonly trackByFn = trackByFn; diff --git a/frontend/src/app/services/objective-menu-actions.service.ts b/frontend/src/app/services/objective-menu-actions.service.ts index 2057c8d3a3..d79d961d2f 100644 --- a/frontend/src/app/services/objective-menu-actions.service.ts +++ b/frontend/src/app/services/objective-menu-actions.service.ts @@ -55,7 +55,7 @@ export class ObjectiveMenuActionsService { } private getDraftMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { - return [this.getReleaseAction(objective)]; + return [this.getReleaseAction(objective), this.actions.editObjectiveAction(objective)]; } private getOngoingMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { From 56445eb25316caa0743494c7bf06ad356b1277d8 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 5 Nov 2024 10:36:18 +0100 Subject: [PATCH 31/44] fix check-in tests --- .../src/app/components/objective/objective.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/objective/objective.component.html b/frontend/src/app/components/objective/objective.component.html index 6d6e8115dc..d6664c2911 100644 --- a/frontend/src/app/components/objective/objective.component.html +++ b/frontend/src/app/components/objective/objective.component.html @@ -1,4 +1,4 @@ -
+
{{ objective.title }} {{ menuEntry.displayName }} -
+
From 9797b4f973b9536d3eccb0a1186311bde4285dad Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 5 Nov 2024 11:20:51 +0100 Subject: [PATCH 32/44] fix objective backlog tests --- .../src/app/components/objective/ObjectiveMenuActions.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/components/objective/ObjectiveMenuActions.ts b/frontend/src/app/components/objective/ObjectiveMenuActions.ts index 917ef05ae8..f3b8afc6d7 100644 --- a/frontend/src/app/components/objective/ObjectiveMenuActions.ts +++ b/frontend/src/app/components/objective/ObjectiveMenuActions.ts @@ -26,7 +26,7 @@ export class ObjectiveMenuActions { } releaseFromBacklogAction(objective: ObjectiveMin): ObjectiveMenuEntry { - const config = { data: { objective: { objectiveId: objective }, action: 'releaseBacklog' } }; + const config = { data: { objective: { objectiveId: objective.id }, action: 'releaseBacklog' } }; const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); const afterAction: ObjectiveMenuAfterAction = () => this.refreshDataService.markDataRefresh(); return { displayName: 'Objective veröffentlichen', action: action, afterAction }; @@ -35,12 +35,14 @@ export class ObjectiveMenuActions { editObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { const config = { data: { objective: { objectiveId: objective.id } } }; const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); - const afterAction: ObjectiveMenuAfterAction = () => {}; + const afterAction: ObjectiveMenuAfterAction = () => { + this.refreshDataService.markDataRefresh(); + }; return { displayName: 'Objective bearbeiten', action: action, afterAction: afterAction }; } duplicateObjectiveAction(objective: ObjectiveMin): ObjectiveMenuEntry { - const config = { data: { objective: { objectiveId: objective.id } } }; + const config = { data: { objective: { objectiveId: objective.id }, action: 'duplicate' } }; const action: ObjectiveMenuAction = () => this.dialogService.open(ObjectiveFormComponent, config); const afterAction: ObjectiveMenuAfterAction = () => this.refreshDataService.markDataRefresh(); return { displayName: 'Objective duplizieren', action: action, afterAction: afterAction }; From 55d2b8682e8ccac282acbf24f6b688171c9a3451 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 5 Nov 2024 11:35:04 +0100 Subject: [PATCH 33/44] fix objective e2e tests --- frontend/cypress/e2e/objective.cy.ts | 10 +++++++--- .../app/components/objective/ObjectiveMenuActions.ts | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/cypress/e2e/objective.cy.ts b/frontend/cypress/e2e/objective.cy.ts index 83eb98496e..5fbceadcc4 100644 --- a/frontend/cypress/e2e/objective.cy.ts +++ b/frontend/cypress/e2e/objective.cy.ts @@ -100,7 +100,7 @@ describe('OKR Objective e2e tests', () => { .should('have.attr', 'src', `assets/icons/not-successful-icon.svg`); }); - it(`Reopen Successful Objective`, () => { + it.only(`Reopen Successful Objective`, () => { cy.getByTestId('add-objective').first().click(); cy.fillOutObjective('This objective will be reopened after', 'safe', undefined, '', false); @@ -129,6 +129,10 @@ describe('OKR Objective e2e tests', () => { .contains('Objective wiedereröffnen') .click(); + cy.contains('Objective wiedereröffnen'); + cy.contains('Soll dieses Objective wiedereröffnet werden?'); + cy.getByTestId('confirmYes').click(); + cy.getByTestId('objective') .filter(':contains("This objective will be reopened after")') .last() @@ -136,7 +140,7 @@ describe('OKR Objective e2e tests', () => { .should('have.attr', 'src', `assets/icons/ongoing-icon.svg`); }); - it('Ongoing objective back to draft state', () => { + it.only('Ongoing objective back to draft state', () => { onlyOn('chrome'); cy.getByTestId('add-objective').first().click(); cy.fillOutObjective('This objective will be returned to draft state', 'safe', undefined, '', false); @@ -154,7 +158,7 @@ describe('OKR Objective e2e tests', () => { .tabForward(); cy.contains('Objective als Draft speichern'); cy.contains('Soll dieses Objective als Draft gespeichert werden?'); - cy.focused().click().wait(500); + cy.getByTestId('confirmYes').click(); cy.getByTestId('objective') .filter(':contains("This objective will be returned to draft state")') diff --git a/frontend/src/app/components/objective/ObjectiveMenuActions.ts b/frontend/src/app/components/objective/ObjectiveMenuActions.ts index f3b8afc6d7..01ce7543e4 100644 --- a/frontend/src/app/components/objective/ObjectiveMenuActions.ts +++ b/frontend/src/app/components/objective/ObjectiveMenuActions.ts @@ -60,7 +60,7 @@ export class ObjectiveMenuActions { } objectiveBackToDraft(): ObjectiveMenuEntry { - const action: ObjectiveMenuAction = () => this.dialogService.openConfirmDialog('CONFIRMATION.DRAFT_CREATE'); + const action: ObjectiveMenuAction = () => this.dialogService.openConfirmDialog('CONFIRMATION.TO_DRAFT'); const afterAction: ObjectiveMenuAfterAction = (obj: Objective, result: any) => this.afterActions.objectiveBackToDraft(obj); From 18085021ee2aab9141e8c7018ceab6db552b9ffb Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 5 Nov 2024 12:01:10 +0100 Subject: [PATCH 34/44] fix tabbing tests --- frontend/cypress/e2e/objective.cy.ts | 4 ++-- frontend/cypress/e2e/tab.cy.ts | 4 +++- frontend/src/app/services/objective-menu-actions.service.ts | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/frontend/cypress/e2e/objective.cy.ts b/frontend/cypress/e2e/objective.cy.ts index 5fbceadcc4..a947105d2a 100644 --- a/frontend/cypress/e2e/objective.cy.ts +++ b/frontend/cypress/e2e/objective.cy.ts @@ -100,7 +100,7 @@ describe('OKR Objective e2e tests', () => { .should('have.attr', 'src', `assets/icons/not-successful-icon.svg`); }); - it.only(`Reopen Successful Objective`, () => { + it(`Reopen Successful Objective`, () => { cy.getByTestId('add-objective').first().click(); cy.fillOutObjective('This objective will be reopened after', 'safe', undefined, '', false); @@ -140,7 +140,7 @@ describe('OKR Objective e2e tests', () => { .should('have.attr', 'src', `assets/icons/ongoing-icon.svg`); }); - it.only('Ongoing objective back to draft state', () => { + it('Ongoing objective back to draft state', () => { onlyOn('chrome'); cy.getByTestId('add-objective').first().click(); cy.fillOutObjective('This objective will be returned to draft state', 'safe', undefined, '', false); diff --git a/frontend/cypress/e2e/tab.cy.ts b/frontend/cypress/e2e/tab.cy.ts index 7909c48142..695e00310b 100644 --- a/frontend/cypress/e2e/tab.cy.ts +++ b/frontend/cypress/e2e/tab.cy.ts @@ -219,6 +219,8 @@ describe('Tab workflow tests', () => { it('Duplicate objective with tab', () => { openThreeDotMenu(); cy.realPress('ArrowDown'); + cy.realPress('ArrowDown'); + cy.realPress('ArrowDown'); cy.focused().contains('Objective duplizieren'); cy.realPress('Enter'); cy.wait(500); @@ -233,6 +235,7 @@ describe('Tab workflow tests', () => { cy.focused().contains('Speichern'); cy.realPress('Enter'); cy.wait(500); + cy.get('.objective').first().focus(); cy.tabBackwardUntil('[data-testId="quarterFilter"]'); cy.focused().contains('GJ'); cy.realPress('ArrowDown'); @@ -242,7 +245,6 @@ describe('Tab workflow tests', () => { it('Complete objective dialog with tab', () => { openThreeDotMenu(); cy.realPress('ArrowDown'); - cy.realPress('ArrowDown'); cy.focused().contains('Objective abschliessen'); cy.realPress('Enter'); cy.wait(500); diff --git a/frontend/src/app/services/objective-menu-actions.service.ts b/frontend/src/app/services/objective-menu-actions.service.ts index d79d961d2f..51ea9cd237 100644 --- a/frontend/src/app/services/objective-menu-actions.service.ts +++ b/frontend/src/app/services/objective-menu-actions.service.ts @@ -35,7 +35,7 @@ export class ObjectiveMenuActionsService { } getMenu(objective: ObjectiveMin): ObjectiveMenuEntry[] { - return [...this.getDefaultActions(objective), ...this.getSpecificMenuEntries(objective)]; + return [...this.getSpecificMenuEntries(objective), ...this.getDefaultActions(objective)]; } private getSpecificMenuEntries(objective: ObjectiveMin): ObjectiveMenuEntry[] { @@ -55,7 +55,7 @@ export class ObjectiveMenuActionsService { } private getDraftMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { - return [this.getReleaseAction(objective), this.actions.editObjectiveAction(objective)]; + return [this.actions.editObjectiveAction(objective), this.getReleaseAction(objective)]; } private getOngoingMenuActions(objective: ObjectiveMin): ObjectiveMenuEntry[] { From 4b8a8fe2d73cb5b6c35215411e371d17363199f2 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 5 Nov 2024 12:45:19 +0100 Subject: [PATCH 35/44] fix tabbing tests --- frontend/cypress/e2e/tab.cy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/cypress/e2e/tab.cy.ts b/frontend/cypress/e2e/tab.cy.ts index 695e00310b..4443e4a84f 100644 --- a/frontend/cypress/e2e/tab.cy.ts +++ b/frontend/cypress/e2e/tab.cy.ts @@ -72,7 +72,7 @@ describe('Tab workflow tests', () => { } function openKeyresultDetail() { - cy.get('.objective').first().focus(); + cy.get("[src='assets/icons/ongoing-icon.svg']").parentsUntil('.objective').focus(); cy.tabForwardUntil('[data-testId="key-result"]'); cy.focused().contains('Fail'); cy.focused().contains('Commit'); From 65ccc9cb2105d1b59275eab6210e98c449c0ed03 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 5 Nov 2024 13:07:38 +0100 Subject: [PATCH 36/44] schtibitz e2e strategy from pulfer --- .github/workflows/frontend-test-action.yml | 20 ++++++++++++++++++++ frontend/cypress/e2e/tab.cy.ts | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/frontend-test-action.yml b/.github/workflows/frontend-test-action.yml index 2f34b3596b..4750d9778a 100644 --- a/.github/workflows/frontend-test-action.yml +++ b/.github/workflows/frontend-test-action.yml @@ -22,8 +22,27 @@ jobs: - name: Run unit tests run: npm test + get-e2e-files: + runs-on: ubuntu-24.04 + outputs: + file_list: ${{ steps.generate-file-list.outputs.file_list }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Generate file list + id: generate-file-list + run: | + FILES=$(find frontend/cypress/e2e -type f -exec basename {} \; | jq -R . | jq -s . | jq -c) + echo $FILES + echo "file_list=$FILES" >> $GITHUB_OUTPUT + e2e: + needs: get-e2e-files runs-on: ubuntu-24.04 + strategy: + matrix: + file: ${{ fromJSON(needs.get-e2e-files.outputs.file_list) }} steps: - name: Checkout uses: actions/checkout@v4 @@ -62,6 +81,7 @@ jobs: wait-on-timeout: 120 browser: chrome headed: true + spec: cypress/e2e/${{ matrix.file }} - uses: actions/upload-artifact@v4 if: always() diff --git a/frontend/cypress/e2e/tab.cy.ts b/frontend/cypress/e2e/tab.cy.ts index 4443e4a84f..188b8a1144 100644 --- a/frontend/cypress/e2e/tab.cy.ts +++ b/frontend/cypress/e2e/tab.cy.ts @@ -72,7 +72,7 @@ describe('Tab workflow tests', () => { } function openKeyresultDetail() { - cy.get("[src='assets/icons/ongoing-icon.svg']").parentsUntil('.objective').focus(); + cy.get("[src='assets/icons/ongoing-icon.svg']").parentsUntil('#objective-column').last().focus(); cy.tabForwardUntil('[data-testId="key-result"]'); cy.focused().contains('Fail'); cy.focused().contains('Commit'); From 704a919ae42ac50a12c4847172fda9a1065c7be3 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 5 Nov 2024 13:35:24 +0100 Subject: [PATCH 37/44] remove console.log --- frontend/src/app/components/objective/objective.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/app/components/objective/objective.component.ts b/frontend/src/app/components/objective/objective.component.ts index 50beb97d8a..396fd264a3 100644 --- a/frontend/src/app/components/objective/objective.component.ts +++ b/frontend/src/app/components/objective/objective.component.ts @@ -32,7 +32,6 @@ export class ObjectiveComponent implements OnInit { ) {} @Input() set objective(objective: ObjectiveMin) { - console.log('objective', objective); this.objective$.next(objective); } From a5713a089ea3a0ec34c86e1d418dc38d6cb54a53 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 5 Nov 2024 13:59:29 +0100 Subject: [PATCH 38/44] restore focus --- .../src/app/components/objective/objective.component.html | 1 + frontend/src/app/components/objective/objective.component.ts | 5 ++++- frontend/src/app/services/dialog.service.ts | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/components/objective/objective.component.html b/frontend/src/app/components/objective/objective.component.html index d6664c2911..71cd720191 100644 --- a/frontend/src/app/components/objective/objective.component.html +++ b/frontend/src/app/components/objective/objective.component.html @@ -21,6 +21,7 @@

{{ objective.title }}