diff --git a/backend/src/main/java/ch/puzzle/okr/controller/ObjectiveController.java b/backend/src/main/java/ch/puzzle/okr/controller/ObjectiveController.java index f608d87770..672afebf37 100644 --- a/backend/src/main/java/ch/puzzle/okr/controller/ObjectiveController.java +++ b/backend/src/main/java/ch/puzzle/okr/controller/ObjectiveController.java @@ -1,6 +1,6 @@ package ch.puzzle.okr.controller; -import ch.puzzle.okr.dto.ObjectiveAlignmentsDto; +import ch.puzzle.okr.dto.AlignmentDto; import ch.puzzle.okr.dto.ObjectiveDto; import ch.puzzle.okr.mapper.ObjectiveMapper; import ch.puzzle.okr.models.Objective; @@ -48,11 +48,11 @@ public ResponseEntity getObjective( @Operation(summary = "Get Alignment possibilities", description = "Get all possibilities to create an Alignment") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Returned all Alignment possibilities for an Objective", content = { - @Content(mediaType = "application/json", schema = @Schema(implementation = ObjectiveAlignmentsDto.class)) }), + @Content(mediaType = "application/json", schema = @Schema(implementation = AlignmentDto.class)) }), @ApiResponse(responseCode = "401", description = "Not authorized to get Alignment possibilities", content = @Content), @ApiResponse(responseCode = "404", description = "Did not find any possibilities to create an Alignment", content = @Content) }) @GetMapping("/alignmentPossibilities/{quarterId}") - public ResponseEntity> getAlignmentPossibilities( + public ResponseEntity> getAlignmentPossibilities( @Parameter(description = "The Quarter ID for getting Alignment possibilities.", required = true) @PathVariable Long quarterId) { return ResponseEntity.status(HttpStatus.OK) .body(objectiveAuthorizationService.getAlignmentPossibilities(quarterId)); diff --git a/backend/src/main/java/ch/puzzle/okr/dto/AlignmentDto.java b/backend/src/main/java/ch/puzzle/okr/dto/AlignmentDto.java new file mode 100644 index 0000000000..16389518ea --- /dev/null +++ b/backend/src/main/java/ch/puzzle/okr/dto/AlignmentDto.java @@ -0,0 +1,6 @@ +package ch.puzzle.okr.dto; + +import java.util.List; + +public record AlignmentDto(Long teamId, String teamName, List alignmentObjectDtos) { +} diff --git a/backend/src/main/java/ch/puzzle/okr/dto/AlignmentObjectDto.java b/backend/src/main/java/ch/puzzle/okr/dto/AlignmentObjectDto.java new file mode 100644 index 0000000000..363278549b --- /dev/null +++ b/backend/src/main/java/ch/puzzle/okr/dto/AlignmentObjectDto.java @@ -0,0 +1,4 @@ +package ch.puzzle.okr.dto; + +public record AlignmentObjectDto(Long objectId, String objectTitle, String objectType) { +} diff --git a/backend/src/main/java/ch/puzzle/okr/service/authorization/ObjectiveAuthorizationService.java b/backend/src/main/java/ch/puzzle/okr/service/authorization/ObjectiveAuthorizationService.java index d5d7cdf105..cbeb5b484b 100644 --- a/backend/src/main/java/ch/puzzle/okr/service/authorization/ObjectiveAuthorizationService.java +++ b/backend/src/main/java/ch/puzzle/okr/service/authorization/ObjectiveAuthorizationService.java @@ -1,6 +1,6 @@ package ch.puzzle.okr.service.authorization; -import ch.puzzle.okr.dto.ObjectiveAlignmentsDto; +import ch.puzzle.okr.dto.AlignmentDto; import ch.puzzle.okr.models.Objective; import ch.puzzle.okr.models.authorization.AuthorizationUser; import ch.puzzle.okr.service.business.ObjectiveBusinessService; @@ -22,7 +22,7 @@ public Objective duplicateEntity(Long id, Objective objective) { return getBusinessService().duplicateObjective(id, objective, authorizationUser); } - public List getAlignmentPossibilities(Long quarterId) { + public List getAlignmentPossibilities(Long quarterId) { return getBusinessService().getAlignmentPossibilities(quarterId); } diff --git a/backend/src/main/java/ch/puzzle/okr/service/business/ObjectiveBusinessService.java b/backend/src/main/java/ch/puzzle/okr/service/business/ObjectiveBusinessService.java index 7cdec3444c..03822c9c1d 100644 --- a/backend/src/main/java/ch/puzzle/okr/service/business/ObjectiveBusinessService.java +++ b/backend/src/main/java/ch/puzzle/okr/service/business/ObjectiveBusinessService.java @@ -1,8 +1,9 @@ package ch.puzzle.okr.service.business; -import ch.puzzle.okr.dto.ObjectiveAlignmentsDto; -import ch.puzzle.okr.dto.keyresult.KeyResultAlignmentsDto; +import ch.puzzle.okr.dto.AlignmentDto; +import ch.puzzle.okr.dto.AlignmentObjectDto; import ch.puzzle.okr.models.Objective; +import ch.puzzle.okr.models.Team; import ch.puzzle.okr.models.authorization.AuthorizationUser; import ch.puzzle.okr.models.keyresult.KeyResult; import ch.puzzle.okr.models.keyresult.KeyResultMetric; @@ -16,9 +17,7 @@ import org.springframework.stereotype.Service; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +import java.util.*; import static ch.puzzle.okr.Constants.KEY_RESULT_TYPE_METRIC; import static ch.puzzle.okr.Constants.KEY_RESULT_TYPE_ORDINAL; @@ -52,26 +51,40 @@ public Objective getEntityById(Long id) { return objective; } - public List getAlignmentPossibilities(Long quarterId) { + public List getAlignmentPossibilities(Long quarterId) { validator.validateOnGet(quarterId); List objectivesByQuarter = objectivePersistenceService.findObjectiveByQuarterId(quarterId); - List objectiveAlignmentsDtos = new ArrayList<>(); - - objectivesByQuarter.forEach(objective -> { - List keyResults = keyResultBusinessService.getAllKeyResultsByObjective(objective.getId()); - List keyResultAlignmentsDtos = new ArrayList<>(); - keyResults.forEach(keyResult -> { - KeyResultAlignmentsDto keyResultAlignmentsDto = new KeyResultAlignmentsDto(keyResult.getId(), - "K - " + keyResult.getTitle()); - keyResultAlignmentsDtos.add(keyResultAlignmentsDto); + List alignmentDtoList = new ArrayList<>(); + + List teamList = new ArrayList<>(); + objectivesByQuarter.forEach(objective -> teamList.add(objective.getTeam())); + Set set = new HashSet<>(teamList); + teamList.clear(); + teamList.addAll(set); + + teamList.forEach(team -> { + List filteredObjectiveList = objectivesByQuarter.stream() + .filter(objective -> objective.getTeam().equals(team)).toList(); + List alignmentObjectDtos = new ArrayList<>(); + + filteredObjectiveList.forEach(objective -> { + AlignmentObjectDto objectiveDto = new AlignmentObjectDto(objective.getId(), + "O - " + objective.getTitle(), "objective"); + alignmentObjectDtos.add(objectiveDto); + + List keyResults = keyResultBusinessService.getAllKeyResultsByObjective(objective.getId()); + keyResults.forEach(keyResult -> { + AlignmentObjectDto keyResultDto = new AlignmentObjectDto(keyResult.getId(), + "KR - " + keyResult.getTitle(), "keyResult"); + alignmentObjectDtos.add(keyResultDto); + }); }); - ObjectiveAlignmentsDto objectiveAlignmentsDto = new ObjectiveAlignmentsDto(objective.getId(), - "O - " + objective.getTitle(), keyResultAlignmentsDtos); - objectiveAlignmentsDtos.add(objectiveAlignmentsDto); + AlignmentDto alignmentDto = new AlignmentDto(team.getId(), team.getName(), alignmentObjectDtos); + alignmentDtoList.add(alignmentDto); }); - return objectiveAlignmentsDtos; + return alignmentDtoList; } public List getEntitiesByTeamId(Long id) { 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 480423313d..1db25f4552 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 @@ -68,25 +68,27 @@
- - - - - - - - - + class="custom-select bg-white select-width" + placeholder="Bezug wählen" + [matAutocomplete]="auto" + (input)="filter()" + (focus)="filter(); input.select()" + [value]="displayedValue" + /> + + @for (group of filteredOptions; track group) { + + @for (name of group.alignmentObjects; track name) { + {{ name.objectTitle }} + } + + } +
; + filteredOptions: AlignmentPossibility[] = []; + objectiveForm = new FormGroup({ title: new FormControl('', [Validators.required, Validators.minLength(2), Validators.maxLength(250)]), description: new FormControl('', [Validators.maxLength(4096)]), quarter: new FormControl(0, [Validators.required]), team: new FormControl({ value: 0, disabled: true }, [Validators.required]), - alignment: new FormControl(''), + alignment: new FormControl(null), createKeyResults: new FormControl(false), }); quarters$: Observable = of([]); @@ -65,6 +68,19 @@ export class ObjectiveFormComponent implements OnInit { onSubmit(submitType: any): void { const value = this.objectiveForm.getRawValue(); const state = this.data.objective.objectiveId == null ? submitType : this.state; + + let alignmentEntity: string | null = ''; + let alignment: any = value.alignment; + if (alignment) { + if (alignment?.objectiveId) { + alignmentEntity = 'O' + alignment.objectiveId; + } else { + alignmentEntity = 'K' + alignment.keyResultId; + } + } else { + alignmentEntity = null; + } + let objectiveDTO: Objective = { id: this.data.objective.objectiveId, version: this.version, @@ -73,7 +89,7 @@ export class ObjectiveFormComponent implements OnInit { title: value.title, teamId: value.team, state: state, - alignedEntityId: value.alignment == 'Onull' ? null : value.alignment, + alignedEntityId: alignmentEntity, } as unknown as Objective; const submitFunction = this.getSubmitFunction(objectiveDTO.id, objectiveDTO); @@ -106,14 +122,15 @@ export class ObjectiveFormComponent implements OnInit { this.teams$.subscribe((value) => { this.currentTeam.next(value.filter((team) => team.id == teamId)[0]); }); - this.generateAlignmentPossibilities(quarterId); + this.generateAlignmentPossibilities(quarterId, objective, teamId!); this.objectiveForm.patchValue({ title: objective.title, description: objective.description, team: teamId, quarter: quarterId, - alignment: objective.alignedEntityId ? objective.alignedEntityId : 'Onull', + // alignment: null, + // alignment: objective.alignedEntityId ? objective.alignedEntityId : 'Onull', }); }); } @@ -238,47 +255,120 @@ export class ObjectiveFormComponent implements OnInit { return GJ_REGEX_PATTERN.test(label); } - generateAlignmentPossibilities(quarterId: number) { + generateAlignmentPossibilities(quarterId: number, objective: Objective | null, teamId: number | null) { this.alignmentPossibilities$ = this.objectiveService.getAlignmentPossibilities(quarterId); this.alignmentPossibilities$.subscribe((value: AlignmentPossibility[]) => { - if (this.objective?.id) { - value = value.filter((item: AlignmentPossibility) => !(item.objectiveId == this.objective!.id)); + if (teamId) { + value = value.filter((item: AlignmentPossibility) => !(item.teamId == teamId)); } - let firstSelectOption = { - objectiveId: null, - objectiveTitle: 'Kein Alignment', - keyResultAlignmentsDtos: [], - }; - if (value.length != 0) { - if (this.objective?.alignedEntityId) { - if (value[0].objectiveTitle == 'Bitte wählen') { - value.splice(0, 1); + // let firstSelectOption = { + // objectiveId: null, + // objectiveTitle: 'Kein Alignment', + // keyResultAlignmentsDtos: [], + // }; + // if (value.length != 0) { + // if (this.objective?.alignedEntityId) { + // if (value[0].objectiveTitle == 'Bitte wählen') { + // value.splice(0, 1); + // } + // } else { + // firstSelectOption.objectiveTitle = 'Bitte wählen'; + // } + // } + // value.unshift(firstSelectOption); + + if (objective) { + let alignment = objective.alignedEntityId; + if (alignment) { + let alignmentType = alignment.charAt(0); + let alignmentId = parseInt(alignment.substring(1)); + if (alignmentType == 'O') { + let element = + value.find((ap) => ap.alignmentObjects.find((apObjective) => apObjective.objectId == alignmentId)) || + null; + this.objectiveForm.patchValue({ + alignment: element, + }); } - } else { - firstSelectOption.objectiveTitle = 'Bitte wählen'; + // else { + // for (let objectiveAlignment of value) { + // let keyResult = objectiveAlignment.keyResultAlignmentsDtos.find((kr) => kr.keyResultId == alignmentId); + // if (keyResult) { + // // TODO change this to keyresult + // this.objectiveForm.patchValue({ + // alignment: objectiveAlignment, + // }); + // } + // } + // } } + } else { + this.objectiveForm.patchValue({ + alignment: null, + }); } - value.unshift(firstSelectOption); + + this.filteredOptions = value.slice(); this.alignmentPossibilities$ = of(value); }); } updateAlignments() { - this.generateAlignmentPossibilities(this.objectiveForm.value.quarter!); - this.objectiveForm.patchValue({ - alignment: 'Onull', + this.currentTeam.subscribe((team) => { + this.generateAlignmentPossibilities(this.objectiveForm.value.quarter!, null, team.id); }); + // this.objectiveForm.patchValue({ + // alignment: 'Onull', + // }); } - changeFirstAlignmentPossibility() { - this.alignmentPossibilities$.subscribe((value: AlignmentPossibility[]) => { - let element: AlignmentPossibility = value[0]; - element.objectiveTitle = 'Kein Alignment'; - value.splice(0, 1); - value.unshift(element); - this.alignmentPossibilities$ = of(value); + // changeFirstAlignmentPossibility() { + // this.alignmentPossibilities$.subscribe((value: AlignmentPossibility[]) => { + // let element: AlignmentPossibility = value[0]; + // element.objectiveTitle = 'Kein Alignment'; + // value.splice(0, 1); + // value.unshift(element); + // this.alignmentPossibilities$ = of(value); + // }); + // } + + filter() { + let filterValue = this.input.nativeElement.value.toLowerCase(); + this.alignmentPossibilities$.subscribe((value) => { + // this.filteredOptions = value.filter((o) => o.objectiveTitle.toLowerCase().includes(filterValue)); + this.filteredOptions = value.filter( + (o) => o.teamName.toLowerCase().includes(filterValue), + // || + // o.alignmentObjects.some((object) => object.objectTitle.toLowerCase().includes(filterValue)) + + // o.objectiveTitle.toLowerCase().includes(filterValue) || // Check if objectiveTitle includes the filterValue + // o.keyResultAlignmentsDtos.some((kr) => kr.keyResultTitle.toLowerCase().includes(filterValue)), // Check if any keyResultTitle includes the filterValue + ); + console.log(this.filteredOptions); }); } + displayWith(value: any): string { + return value; + // if (value) { + // if (value.objectiveId) { + // return value.objectiveTitle; + // } else { + // return value.keyResultTitle; + // } + // } else { + // return 'Bitte wählen'; + // } + } + + get displayedValue(): string { + if (this.input) { + const inputValue = this.input.nativeElement.value; + return inputValue.length > 40 ? inputValue.slice(0, 40) + '...' : inputValue; + } else { + return ''; + } + } + protected readonly getQuarterLabel = getQuarterLabel; } diff --git a/frontend/src/app/shared/types/model/AlignmentPossibility.ts b/frontend/src/app/shared/types/model/AlignmentPossibility.ts index e2b3e69673..5ec6950bca 100644 --- a/frontend/src/app/shared/types/model/AlignmentPossibility.ts +++ b/frontend/src/app/shared/types/model/AlignmentPossibility.ts @@ -1,8 +1,8 @@ export interface AlignmentPossibility { - objectiveId: number | null; - objectiveTitle: string; - keyResultAlignmentsDtos: { - keyResultId: number; - keyResultTitle: string; + teamId: number; + teamName: string; + alignmentObjects: { + objectId: number; + objectTitle: string; }[]; }