diff --git a/backend/src/main/java/ch/puzzle/okr/controller/QuarterController.java b/backend/src/main/java/ch/puzzle/okr/controller/QuarterController.java index eb92ef5841..1cc1383dbc 100644 --- a/backend/src/main/java/ch/puzzle/okr/controller/QuarterController.java +++ b/backend/src/main/java/ch/puzzle/okr/controller/QuarterController.java @@ -17,7 +17,7 @@ import java.util.List; @RestController -@RequestMapping("api/v1/quarters") +@RequestMapping("api/v2/quarters") public class QuarterController { private final QuarterBusinessService quarterBusinessService; @@ -28,9 +28,17 @@ public QuarterController(QuarterBusinessService quarterBusinessService) { @Operation(summary = "Get quarters", description = "Get a List of quarters depending on current date") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Returned a List of quarters", content = { - @Content(mediaType = "application/json", schema = @Schema(implementation = TeamDto.class)) }) }) + @Content(mediaType = "application/json", schema = @Schema(implementation = Quarter.class)) }) }) @GetMapping("") public ResponseEntity> getCurrentQuarters() { return ResponseEntity.status(HttpStatus.OK).body(this.quarterBusinessService.getQuarters()); } + + @Operation(summary = "Get current quarter", description = "Get the current quarter depending on current date") + @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Returned the current quarter", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = Quarter.class)) }) }) + @GetMapping("/current") + public ResponseEntity getCurrentQuarter() { + return ResponseEntity.status(HttpStatus.OK).body(this.quarterBusinessService.getCurrentQuarter()); + } } diff --git a/backend/src/test/java/ch/puzzle/okr/controller/QuarterControllerIT.java b/backend/src/test/java/ch/puzzle/okr/controller/QuarterControllerIT.java index cfd844139f..5a582cd117 100644 --- a/backend/src/test/java/ch/puzzle/okr/controller/QuarterControllerIT.java +++ b/backend/src/test/java/ch/puzzle/okr/controller/QuarterControllerIT.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.BDDMockito; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -48,7 +49,7 @@ class QuarterControllerIT { void shouldGetAllQuarters() throws Exception { BDDMockito.given(quarterBusinessService.getQuarters()).willReturn(quaterList); - mvc.perform(get("/api/v1/quarters").contentType(MediaType.APPLICATION_JSON)) + mvc.perform(get("/api/v2/quarters").contentType(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()).andExpect(jsonPath("$", Matchers.hasSize(3))) .andExpect(jsonPath("$[0].id", Is.is(1))).andExpect(jsonPath("$[0].label", Is.is("GJ 22/23-Q2"))) .andExpect(jsonPath("$[0].startDate", Is.is(LocalDate.of(2022, 9, 1).toString()))) @@ -64,7 +65,14 @@ void shouldGetAllQuarters() throws Exception { void shouldGetAllTeamsIfNoTeamsExists() throws Exception { BDDMockito.given(quarterBusinessService.getQuarters()).willReturn(Collections.emptyList()); - mvc.perform(get("/api/v1/quarters").contentType(MediaType.APPLICATION_JSON)) + mvc.perform(get("/api/v2/quarters").contentType(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()).andExpect(jsonPath("$", Matchers.hasSize(0))); } -} \ No newline at end of file + + @Test + void shouldCallCurrentQuarterAfterRequest() throws Exception { + mvc.perform(get("/api/v2/quarters/current").contentType(MediaType.APPLICATION_JSON)); + + BDDMockito.verify(quarterBusinessService, Mockito.times(1)).getCurrentQuarter(); + } +} diff --git a/frontend/src/app/components/key-result-form/key-result-form.component.spec.ts b/frontend/src/app/components/key-result-form/key-result-form.component.spec.ts index 724f517822..d0d2479b8a 100644 --- a/frontend/src/app/components/key-result-form/key-result-form.component.spec.ts +++ b/frontend/src/app/components/key-result-form/key-result-form.component.spec.ts @@ -27,6 +27,7 @@ import { KeyResultMetric } from '../../shared/types/model/KeyResultMetric'; import { KeyResultOrdinal } from '../../shared/types/model/KeyResultOrdinal'; import { TranslateTestingModule } from 'ngx-translate-testing'; import * as de from '../../../assets/i18n/de.json'; +import { Quarter } from '../../shared/types/model/Quarter'; describe('KeyResultFormComponent', () => { let component: KeyResultFormComponent; @@ -70,7 +71,7 @@ describe('KeyResultFormComponent', () => { const keyResultObjective: KeyResultObjective = { id: 2, state: State.ONGOING, - quarter: { id: 1, label: 'GJ 22/23-Q2', endDate: new Date(), startDate: new Date() }, + quarter: new Quarter(1, 'GJ 22/23-Q2', new Date(), new Date()), }; const keyResultFormGroup = new FormGroup({ diff --git a/frontend/src/app/components/keyresult-dialog/keyresult-dialog.component.spec.ts b/frontend/src/app/components/keyresult-dialog/keyresult-dialog.component.spec.ts index 17d9f4d017..bf74d658ce 100644 --- a/frontend/src/app/components/keyresult-dialog/keyresult-dialog.component.spec.ts +++ b/frontend/src/app/components/keyresult-dialog/keyresult-dialog.component.spec.ts @@ -24,6 +24,7 @@ import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { DragDropModule } from '@angular/cdk/drag-drop'; import { UserService } from '../../services/user.service'; import { KeyResultFormComponent } from '../key-result-form/key-result-form.component'; +import { Quarter } from '../../shared/types/model/Quarter'; describe('KeyresultDialogComponent', () => { let component: KeyresultDialogComponent; @@ -55,7 +56,7 @@ describe('KeyresultDialogComponent', () => { let keyResultObjective: KeyResultObjective = { id: 2, state: State.ONGOING, - quarter: { id: 1, label: 'GJ 22/23-Q2', endDate: new Date(), startDate: new Date() }, + quarter: new Quarter(1, 'GJ 22/23-Q2', new Date(), new Date()), }; let fullKeyResultMetric = { diff --git a/frontend/src/app/components/quarter-filter/quarter-filter.component.html b/frontend/src/app/components/quarter-filter/quarter-filter.component.html index 14f0ca9cd9..6d5fd442b8 100644 --- a/frontend/src/app/components/quarter-filter/quarter-filter.component.html +++ b/frontend/src/app/components/quarter-filter/quarter-filter.component.html @@ -1,12 +1,12 @@ - - {{ getQuarterLabel(quarter, i) }} + + {{ quarter.fullLabel() }} diff --git a/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts b/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts index e2507f74ce..bcd2e4bdd5 100644 --- a/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts +++ b/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts @@ -2,7 +2,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { QuarterFilterComponent } from './quarter-filter.component'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { OverviewService } from '../../services/overview.service'; -import { quarter } from '../../shared/testData'; import { Observable, of } from 'rxjs'; import { Quarter } from '../../shared/types/model/Quarter'; import { QuarterService } from '../../services/quarter.service'; @@ -21,16 +20,19 @@ const overviewService = { }; const quarters = [ - { id: 999, label: 'Backlog', startDate: null, endDate: null }, - { ...quarter, id: 2 }, - { ...quarter, id: 5 }, - { ...quarter, id: 7 }, + new Quarter(999, 'Backlog', null, null), + new Quarter(2, '23.02.2025', new Date(), new Date()), + new Quarter(5, '23.02.2025', new Date(), new Date()), + new Quarter(7, '23.02.2025', new Date(), new Date()), ]; const quarterService = { getAllQuarters(): Observable { return of(quarters); }, + getCurrentQuarter(): Observable { + return of(quarters[2]); + }, }; describe('QuarterFilterComponent', () => { @@ -68,13 +70,14 @@ describe('QuarterFilterComponent', () => { it('should set correct default quarter if no route param is defined', async () => { jest.spyOn(component, 'changeDisplayedQuarter'); + jest.spyOn(quarters[2] as any, 'isCurrent').mockReturnValue(true); const quarterSelect = await loader.getHarness(MatSelectHarness); expect(quarterSelect).toBeTruthy(); component.ngOnInit(); fixture.detectChanges(); - expect(component.quarterId).toBe(quarters[2].id); + expect(component.currentQuarterId).toBe(quarters[2].id); expect(await quarterSelect.getValueText()).toBe(quarters[2].label + ' Aktuell'); - expect(component.changeDisplayedQuarter).toHaveBeenCalledTimes(0); + expect(component.changeDisplayedQuarter).toHaveBeenCalledTimes(1); }); it('should set correct value in form according to route param', async () => { @@ -89,7 +92,7 @@ describe('QuarterFilterComponent', () => { component.ngOnInit(); fixture.detectChanges(); - expect(component.quarterId).toBe(quarters[3].id); + expect(component.currentQuarterId).toBe(quarters[3].id); expect(await quarterSelect.getValueText()).toBe(quarters[3].label); expect(component.changeDisplayedQuarter).toHaveBeenCalledTimes(1); }); @@ -106,7 +109,7 @@ describe('QuarterFilterComponent', () => { routerHarness.detectChanges(); component.ngOnInit(); fixture.detectChanges(); - expect(component.quarterId).toBe(quarters[2].id); + expect(component.currentQuarterId).toBe(quarters[2].id); expect(await quarterSelect.getValueText()).toBe(quarters[2].label + ' Aktuell'); expect(component.changeDisplayedQuarter).toHaveBeenCalledTimes(1); expect(router.url).toBe('/?quarter=' + quarters[2].id); diff --git a/frontend/src/app/components/quarter-filter/quarter-filter.component.ts b/frontend/src/app/components/quarter-filter/quarter-filter.component.ts index 1374bbc135..023a242adf 100644 --- a/frontend/src/app/components/quarter-filter/quarter-filter.component.ts +++ b/frontend/src/app/components/quarter-filter/quarter-filter.component.ts @@ -1,10 +1,10 @@ import { ChangeDetectionStrategy, Component, EventEmitter, OnInit, Output } from '@angular/core'; import { QuarterService } from '../../services/quarter.service'; import { Quarter } from '../../shared/types/model/Quarter'; -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, forkJoin } from 'rxjs'; import { ActivatedRoute, Router } from '@angular/router'; -import { getQuarterLabel, getValueFromQuery } from '../../shared/common'; import { RefreshDataService } from '../../services/refresh-data.service'; +import { getValueFromQuery } from '../../shared/common'; @Component({ selector: 'app-quarter-filter', @@ -14,7 +14,7 @@ import { RefreshDataService } from '../../services/refresh-data.service'; export class QuarterFilterComponent implements OnInit { quarters: BehaviorSubject = new BehaviorSubject([]); @Output() quarterLabel$ = new EventEmitter(); - quarterId: number = -1; + currentQuarterId: number = -1; constructor( private quarterService: QuarterService, @@ -24,28 +24,30 @@ export class QuarterFilterComponent implements OnInit { ) {} ngOnInit() { - this.quarterService.getAllQuarters().subscribe((quarters) => { + const allQuarters$ = this.quarterService.getAllQuarters(); + const currentQuarter$ = this.quarterService.getCurrentQuarter(); + forkJoin([allQuarters$, currentQuarter$]).subscribe(([quarters, currentQuarter]) => { this.quarters.next(quarters); const quarterQuery = this.route.snapshot.queryParams['quarter']; const quarterId: number = getValueFromQuery(quarterQuery)[0]; if (quarters.map((quarter) => quarter.id).includes(quarterId)) { - this.quarterId = quarterId; + this.currentQuarterId = quarterId; this.changeDisplayedQuarter(); } else { - this.quarterId = quarters[2].id; - if (quarterQuery !== undefined) { - this.changeDisplayedQuarter(); - } else { + this.currentQuarterId = currentQuarter.id; + this.changeDisplayedQuarter(); + + if (quarterQuery === undefined) { this.refreshDataService.quarterFilterReady.next(); } } - const quarterLabel = quarters.find((e) => e.id == this.quarterId)?.label || ''; + const quarterLabel = quarters.find((e) => e.id == this.currentQuarterId)?.label || ''; this.quarterLabel$.next(quarterLabel); }); } changeDisplayedQuarter() { - const id = this.quarterId; + const id = this.currentQuarterId; const quarterLabel = this.quarters.getValue().find((e) => e.id == id)?.label || ''; this.quarterLabel$.next(quarterLabel); @@ -53,6 +55,4 @@ export class QuarterFilterComponent implements OnInit { .navigate([], { queryParams: { quarter: id } }) .then(() => this.refreshDataService.quarterFilterReady.next()); } - - protected readonly getQuarterLabel = getQuarterLabel; } diff --git a/frontend/src/app/services/quarter.service.ts b/frontend/src/app/services/quarter.service.ts index 3991320476..a811c6052e 100644 --- a/frontend/src/app/services/quarter.service.ts +++ b/frontend/src/app/services/quarter.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Quarter } from '../shared/types/model/Quarter'; -import { Observable } from 'rxjs'; +import { map, Observable } from 'rxjs'; @Injectable({ providedIn: 'root', @@ -10,6 +10,16 @@ export class QuarterService { constructor(private http: HttpClient) {} getAllQuarters(): Observable { - return this.http.get('/api/v1/quarters'); + return this.http + .get('/api/v2/quarters') + .pipe( + map((quarters) => + quarters.map((quarter) => new Quarter(quarter.id, quarter.label, quarter.startDate, quarter.endDate)), + ), + ); + } + + getCurrentQuarter(): Observable { + return this.http.get('/api/v2/quarters/current'); } } diff --git a/frontend/src/app/shared/common.ts b/frontend/src/app/shared/common.ts index ec0d3de2b3..57f70b2c0e 100644 --- a/frontend/src/app/shared/common.ts +++ b/frontend/src/app/shared/common.ts @@ -87,10 +87,6 @@ export function formInputCheck(form: FormGroup, propertyName: string) { } } -export function getQuarterLabel(quarter: any, index: number): string { - return index == 2 ? quarter.label + ' Aktuell' : quarter.label; -} - export function isMobileDevice() { return window.navigator.userAgent.toLowerCase().includes('mobile'); } diff --git a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.html b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.html index 28e461b3bf..742809d8f3 100644 --- a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.html +++ b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.html @@ -60,7 +60,7 @@ > diff --git a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts index 47ca3ba9ff..4e667435bc 100644 --- a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts +++ b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts @@ -1,4 +1,4 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { ObjectiveFormComponent } from './objective-form.component'; import { HttpClientTestingModule } from '@angular/common/http/testing'; @@ -35,14 +35,21 @@ let objectiveService = { deleteObjective: jest.fn(), }; +interface MatDialogDataInterface { + objective: { objectiveId: number | undefined; teamId: number | undefined }; +} + const quarterService = { getAllQuarters(): Observable { return of([ - { id: 1, startDate: quarter.startDate, endDate: quarter.endDate, label: quarter.label }, - { id: 2, startDate: quarter.startDate, endDate: quarter.endDate, label: quarter.label }, - { id: 999, startDate: null, endDate: null, label: 'Backlog' }, + new Quarter(1, quarter.label, quarter.startDate, quarter.endDate), + new Quarter(2, quarter.label, quarter.startDate, quarter.endDate), + new Quarter(999, 'Backlog', null, null), ]); }, + getCurrentQuarter(): Observable { + return of(new Quarter(2, quarter.label, quarter.startDate, quarter.endDate)); + }, }; const teamService = { @@ -58,7 +65,7 @@ const dialogMock = { close: jest.fn(), }; -let matDataMock: { objective: { objectiveId: number | undefined; teamId: number | undefined } } = { +let matDataMock: MatDialogDataInterface = { objective: { objectiveId: undefined, teamId: 1, @@ -115,65 +122,69 @@ describe('ObjectiveDialogComponent', () => { expect(component).toBeTruthy(); }); - it.each([['DRAFT'], ['ONGOING']])('onSubmit create', async (state: string) => { - //Prepare data - let title: string = 'title'; - let description: string = 'description'; - let createKeyresults: boolean = true; - let quarter: number = 0; - let team: number = 0; - teamService.getAllTeams().subscribe((teams) => { - team = teams[0].id; - }); - quarterService.getAllQuarters().subscribe((quarters) => { - quarter = quarters[1].id; - }); - - // Get input elements and set values - const titleInput: HTMLInputElement = fixture.debugElement.query(By.css('[data-testId="title"]')).nativeElement; - titleInput.value = title; - const descriptionInput: HTMLInputElement = fixture.debugElement.query( - By.css('[data-testId="description"]'), - ).nativeElement; - descriptionInput.value = description; - const checkBox = await loader.getHarness(MatCheckboxHarness); - await checkBox.check(); - const quarterSelect: HTMLSelectElement = fixture.debugElement.query(By.css('#quarter')).nativeElement; - quarterSelect.value = quarter.toString(); - - // Trigger update of form - fixture.detectChanges(); - titleInput.dispatchEvent(new Event('input')); - descriptionInput.dispatchEvent(new Event('input')); - quarterSelect.dispatchEvent(new Event('input')); - - const rawFormValue = component.objectiveForm.getRawValue(); - expect(rawFormValue.description).toBe(description); - expect(rawFormValue.quarter).toBe(quarter); - expect(rawFormValue.team).toBe(team); - expect(rawFormValue.title).toBe(title); - expect(rawFormValue.createKeyResults).toBe(createKeyresults); - - objectiveService.createObjective.mockReturnValue(of({ ...objective, state: state })); - component.onSubmit(state); - - expect(dialogMock.close).toHaveBeenCalledWith({ - addKeyResult: createKeyresults, - delete: false, - objective: { - description: description, - id: 5, - version: 1, - quarterId: 2, - quarterLabel: 'GJ 22/23-Q2', - state: State[state as keyof typeof State], - teamId: 2, - title: title, - writeable: true, - }, - teamId: 1, - }); - }); + it.each([['DRAFT'], ['ONGOING']])( + 'onSubmit create', + fakeAsync((state: string) => { + //Prepare data + let title: string = 'title'; + let description: string = 'description'; + let createKeyresults: boolean = true; + let quarter: number = 0; + let team: number = 0; + teamService.getAllTeams().subscribe((teams) => { + team = teams[0].id; + }); + quarterService.getAllQuarters().subscribe((quarters) => { + quarter = quarters[1].id; + }); + + // Get input elements and set values + const titleInput: HTMLInputElement = fixture.debugElement.query(By.css('[data-testId="title"]')).nativeElement; + titleInput.value = title; + const descriptionInput: HTMLInputElement = fixture.debugElement.query( + By.css('[data-testId="description"]'), + ).nativeElement; + descriptionInput.value = description; + loader.getHarness(MatCheckboxHarness).then((checkBox) => checkBox.check()); + tick(200); + const quarterSelect: HTMLSelectElement = fixture.debugElement.query( + By.css('[data-testId="quarterSelect"]'), + ).nativeElement; + quarterSelect.value = quarter.toString(); + // Trigger update of form + fixture.detectChanges(); + titleInput.dispatchEvent(new Event('input')); + descriptionInput.dispatchEvent(new Event('input')); + quarterSelect.dispatchEvent(new Event('change')); + + const rawFormValue = component.objectiveForm.getRawValue(); + expect(rawFormValue.description).toBe(description); + expect(rawFormValue.quarter).toBe(quarter.toString()); + expect(rawFormValue.team).toBe(team); + expect(rawFormValue.title).toBe(title); + expect(rawFormValue.createKeyResults).toBe(createKeyresults); + + objectiveService.createObjective.mockReturnValue(of({ ...objective, state: state })); + component.onSubmit(state); + + expect(dialogMock.close).toHaveBeenCalledWith({ + addKeyResult: createKeyresults, + delete: false, + objective: { + description: description, + id: 5, + version: 1, + quarterId: 2, + quarterLabel: 'GJ 22/23-Q2', + state: State[state as keyof typeof State], + teamId: 2, + title: title, + writeable: true, + }, + teamId: 1, + }); + }), + ); it('should create objective', () => { matDataMock.objective.objectiveId = undefined; @@ -292,12 +303,8 @@ describe('ObjectiveDialogComponent', () => { }); it('should return if option is allowed for quarter select', async () => { - let quarter: Quarter = { - id: 999, - label: 'Backlog', - startDate: null, - endDate: null, - }; + let quarter: Quarter = new Quarter(1, 'Backlog', null, null); + let data = { action: 'duplicate', objective: { @@ -310,7 +317,6 @@ describe('ObjectiveDialogComponent', () => { expect(component.allowedOption(quarter)).toBeTruthy(); - quarter.label = 'Backlog'; expect(component.allowedOption(quarter)).toBeTruthy(); data.action = 'releaseBacklog'; fixture.detectChanges(); diff --git a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.ts b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.ts index 33fbb38769..9875795967 100644 --- a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.ts +++ b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.ts @@ -10,7 +10,8 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { State } from '../../types/enums/State'; import { ObjectiveMin } from '../../types/model/ObjectiveMin'; import { Objective } from '../../types/model/Objective'; -import { formInputCheck, getQuarterLabel, getValueFromQuery, hasFormFieldErrors } from '../../common'; +import { ConfirmDialogComponent } from '../confirm-dialog/confirm-dialog.component'; +import { formInputCheck, getValueFromQuery, hasFormFieldErrors, isMobileDevice } from '../../common'; import { ActivatedRoute } from '@angular/router'; import { GJ_REGEX_PATTERN } from '../../constantLibary'; import { TranslateService } from '@ngx-translate/core'; @@ -32,6 +33,7 @@ export class ObjectiveFormComponent implements OnInit, OnDestroy { createKeyResults: new FormControl(false), }); quarters$: Observable = of([]); + currentQuarter$: Observable = of(); quarters: Quarter[] = []; teams$: Observable = of([]); currentTeam: Subject = new Subject(); @@ -82,16 +84,16 @@ export class ObjectiveFormComponent implements OnInit, OnDestroy { const isCreating: boolean = !!this.data.objective.objectiveId; this.teams$ = this.teamService.getAllTeams().pipe(takeUntil(this.unsubscribe$)); this.quarters$ = this.quarterService.getAllQuarters(); + this.currentQuarter$ = this.quarterService.getCurrentQuarter(); const objective$ = isCreating ? this.objectiveService.getFullObjective(this.data.objective.objectiveId!) : of(this.getDefaultObjective()); - - forkJoin([objective$, this.quarters$]).subscribe(([objective, quarters]) => { + forkJoin([objective$, this.quarters$, this.currentQuarter$]).subscribe(([objective, quarters, currentQuarter]) => { this.quarters = quarters; const teamId = isCreating ? objective.teamId : this.data.objective.teamId; - let quarterId = getValueFromQuery(this.route.snapshot.queryParams['quarter'], quarters[1].id)[0]; + const newEditQuarter = isCreating ? currentQuarter.id : objective.quarterId; + let quarterId = getValueFromQuery(this.route.snapshot.queryParams['quarter'], newEditQuarter)[0]; - let currentQuarter: Quarter | undefined = this.quarters.find((quarter) => quarter.id == quarterId); if (currentQuarter && !this.isBacklogQuarter(currentQuarter.label) && this.data.action == 'releaseBacklog') { quarterId = quarters[1].id; } @@ -215,6 +217,4 @@ export class ObjectiveFormComponent implements OnInit, OnDestroy { isBacklogQuarter(label: string) { return GJ_REGEX_PATTERN.test(label); } - - protected readonly getQuarterLabel = getQuarterLabel; } diff --git a/frontend/src/app/shared/testData.ts b/frontend/src/app/shared/testData.ts index 9a4a64044e..48ba98a0de 100644 --- a/frontend/src/app/shared/testData.ts +++ b/frontend/src/app/shared/testData.ts @@ -90,31 +90,13 @@ export const addedAction: Action = { keyResultId: 1, } as Action; -export const quarterMin: Quarter = { - id: 1, - label: 'GJ 23/24-Q1', -} as Quarter; +export const quarterMin: Quarter = new Quarter(1, 'GJ 23/24-Q1', null, null); -export const quarter1: Quarter = { - id: 1, - label: 'GJ 22/23-Q4', - startDate: new Date('2023-04-01'), - endDate: new Date('2023-07-30'), -} as Quarter; +export const quarter1: Quarter = new Quarter(1, 'GJ 22/23-Q4', new Date('2023-04-01'), new Date('2023-07-30')); -export const quarter2: Quarter = { - id: 2, - label: 'GJ 22/23-Q3', - startDate: new Date('2023-01-01'), - endDate: new Date('2023-03-31'), -} as Quarter; - -export const quarterBacklog: Quarter = { - id: 999, - label: 'GJ 23/24-Q1', - startDate: null, - endDate: null, -} as Quarter; +export const quarter2: Quarter = new Quarter(2, 'GJ 22/23-Q3', new Date('2023-01-01'), new Date('2023-03-31')); + +export const quarterBacklog: Quarter = new Quarter(999, 'GJ 23/24-Q1', null, null); export const quarterList: Quarter[] = [quarter1, quarter2, quarterBacklog]; @@ -308,12 +290,7 @@ export const overViewEntityResponse2: any = { export const overviews: OverviewEntity[] = [overViewEntityResponse1, overViewEntityResponse2]; -export const quarter: Quarter = { - id: 1, - label: '23.02.2025', - endDate: new Date(), - startDate: new Date(), -}; +export const quarter: Quarter = new Quarter(1, '23.02.2025', new Date(), new Date()); export const keyResultObjective: KeyResultObjective = { id: 1, @@ -435,12 +412,7 @@ export const keyResult: KeyResultOrdinal = { id: 301, version: 1, state: State.DRAFT, - quarter: { - id: 1, - label: 'GJ 23/24-Q1', - startDate: new Date(), - endDate: new Date(), - } as Quarter, + quarter: new Quarter(1, 'GJ 23/24-Q1', new Date(), new Date()), writeable: true, } as KeyResultObjective, lastCheckIn: { @@ -474,12 +446,7 @@ export const keyResultOrdinal: KeyResultOrdinal = { id: 301, version: 1, state: State.DRAFT, - quarter: { - id: 1, - label: 'GJ 23/24-Q1', - startDate: new Date(), - endDate: new Date(), - } as Quarter, + quarter: new Quarter(1, 'GJ 23/24-Q1', new Date(), new Date()), writeable: true, } as KeyResultObjective, lastCheckIn: { @@ -513,12 +480,7 @@ export const keyResultWriteableFalse: KeyResultOrdinal = { id: 301, version: 1, state: State.DRAFT, - quarter: { - id: 1, - label: 'GJ 23/24-Q1', - startDate: new Date(), - endDate: new Date(), - } as Quarter, + quarter: new Quarter(1, 'GJ 23/24-Q1', new Date(), new Date()), writeable: false, } as KeyResultObjective, lastCheckIn: { @@ -552,12 +514,7 @@ export const keyResultMetric: KeyResultMetric = { id: 302, version: 1, state: State.DRAFT, - quarter: { - id: 1, - label: 'GJ 23/24-Q1', - startDate: new Date(), - endDate: new Date(), - } as Quarter, + quarter: new Quarter(1, 'GJ 23/24-Q1', new Date(), new Date()), writeable: true, } as KeyResultObjective, lastCheckIn: { @@ -590,12 +547,7 @@ export const keyResultActions: KeyResultMetric = { objective: { id: 302, state: State.DRAFT, - quarter: { - id: 1, - label: 'GJ 23/24-Q1', - startDate: new Date(), - endDate: new Date(), - } as Quarter, + quarter: new Quarter(1, 'GJ 23/24-Q1', new Date(), new Date()), writeable: true, } as KeyResultObjective, lastCheckIn: { diff --git a/frontend/src/app/shared/types/model/Quarter.ts b/frontend/src/app/shared/types/model/Quarter.ts index 07d312e084..cc97f785dd 100644 --- a/frontend/src/app/shared/types/model/Quarter.ts +++ b/frontend/src/app/shared/types/model/Quarter.ts @@ -1,6 +1,24 @@ -export interface Quarter { - id: number; - label: string; - startDate: Date | null; - endDate: Date | null; +export class Quarter { + constructor(id: number, label: string, startDate: Date | null, endDate: Date | null) { + this.id = id; + this.label = label; + this.startDate = startDate; + this.endDate = endDate; + } + + readonly id: number; + readonly label: string; + readonly startDate: Date | null; + readonly endDate: Date | null; + + fullLabel(): string { + return this.isCurrent() ? this.label + ' Aktuell' : this.label; + } + + private isCurrent(): boolean { + if (this.startDate === null || this.endDate === null) { + return false; + } + return this.startDate <= new Date() && this.endDate >= new Date(); + } }