Skip to content

Commit 64bf3a4

Browse files
committed
Implement typeahead
1 parent d04f222 commit 64bf3a4

File tree

2 files changed

+128
-47
lines changed

2 files changed

+128
-47
lines changed

frontend/src/app/shared/dialog/objective-dialog/objective-form.component.html

+19-18
Original file line numberDiff line numberDiff line change
@@ -68,25 +68,26 @@
6868
</div>
6969

7070
<div class="d-flex flex-column gap-2 col-6">
71-
<label class="text-black" for="alignment">Bezug (optional)</label>
72-
<select
73-
class="custom-select bg-white select-width"
71+
<label class="text-black">Bezug (optional)</label>
72+
<input
73+
#input
74+
type="text"
7475
formControlName="alignment"
75-
id="alignment"
76-
(change)="changeFirstAlignmentPossibility()"
77-
[attr.data-testId]="'alignmentSelect'"
78-
>
79-
<ng-container *ngFor="let alignment of alignmentPossibilities$ | async; let i = index">
80-
<option [value]="'O' + alignment.objectiveId">
81-
{{ alignment.objectiveTitle }}
82-
</option>
83-
<ng-container *ngFor="let keyResult of alignment.keyResultAlignmentsDtos; let i = index">
84-
<option [value]="'K' + keyResult.keyResultId">
85-
{{ keyResult.keyResultTitle }}
86-
</option>
87-
</ng-container>
88-
</ng-container>
89-
</select>
76+
class="custom-select bg-white select-width"
77+
placeholder="Bezug wählen"
78+
[matAutocomplete]="auto"
79+
(input)="filter()"
80+
(focus)="filter(); input.select()"
81+
[value]="displayedValue"
82+
/>
83+
<mat-autocomplete requireSelection #auto="matAutocomplete" [displayWith]="displayWith">
84+
@for (option of filteredOptions; track option) {
85+
<mat-option [value]="option">{{ option.objectiveTitle }}</mat-option>
86+
@for (kroption of option.keyResultAlignmentsDtos; track kroption) {
87+
<mat-option [value]="kroption">{{ kroption.keyResultTitle }}</mat-option>
88+
}
89+
}
90+
</mat-autocomplete>
9091
</div>
9192
</div>
9293
<mat-checkbox

frontend/src/app/shared/dialog/objective-dialog/objective-form.component.ts

+109-29
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ChangeDetectionStrategy, Component, Inject, OnInit } from '@angular/core';
1+
import { ChangeDetectionStrategy, Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core';
22
import { FormControl, FormGroup, Validators } from '@angular/forms';
33
import { Quarter } from '../../types/model/Quarter';
44
import { TeamService } from '../../services/team.service';
@@ -24,12 +24,15 @@ import { AlignmentPossibility } from '../../types/model/AlignmentPossibility';
2424
changeDetection: ChangeDetectionStrategy.OnPush,
2525
})
2626
export class ObjectiveFormComponent implements OnInit {
27+
@ViewChild('input') input!: ElementRef<HTMLInputElement>;
28+
filteredOptions: AlignmentPossibility[] = [];
29+
2730
objectiveForm = new FormGroup({
2831
title: new FormControl<string>('', [Validators.required, Validators.minLength(2), Validators.maxLength(250)]),
2932
description: new FormControl<string>('', [Validators.maxLength(4096)]),
3033
quarter: new FormControl<number>(0, [Validators.required]),
3134
team: new FormControl<number>({ value: 0, disabled: true }, [Validators.required]),
32-
alignment: new FormControl<string>(''),
35+
alignment: new FormControl<AlignmentPossibility | null>(null),
3336
createKeyResults: new FormControl<boolean>(false),
3437
});
3538
quarters$: Observable<Quarter[]> = of([]);
@@ -65,6 +68,19 @@ export class ObjectiveFormComponent implements OnInit {
6568
onSubmit(submitType: any): void {
6669
const value = this.objectiveForm.getRawValue();
6770
const state = this.data.objective.objectiveId == null ? submitType : this.state;
71+
72+
let alignmentEntity: string | null = '';
73+
let alignment: any = value.alignment;
74+
if (alignment) {
75+
if (alignment?.objectiveId) {
76+
alignmentEntity = 'O' + alignment.objectiveId;
77+
} else {
78+
alignmentEntity = 'K' + alignment.keyResultId;
79+
}
80+
} else {
81+
alignmentEntity = null;
82+
}
83+
6884
let objectiveDTO: Objective = {
6985
id: this.data.objective.objectiveId,
7086
version: this.version,
@@ -73,7 +89,7 @@ export class ObjectiveFormComponent implements OnInit {
7389
title: value.title,
7490
teamId: value.team,
7591
state: state,
76-
alignedEntityId: value.alignment == 'Onull' ? null : value.alignment,
92+
alignedEntityId: alignmentEntity,
7793
} as unknown as Objective;
7894

7995
const submitFunction = this.getSubmitFunction(objectiveDTO.id, objectiveDTO);
@@ -106,14 +122,15 @@ export class ObjectiveFormComponent implements OnInit {
106122
this.teams$.subscribe((value) => {
107123
this.currentTeam.next(value.filter((team) => team.id == teamId)[0]);
108124
});
109-
this.generateAlignmentPossibilities(quarterId);
125+
this.generateAlignmentPossibilities(quarterId, objective);
110126

111127
this.objectiveForm.patchValue({
112128
title: objective.title,
113129
description: objective.description,
114130
team: teamId,
115131
quarter: quarterId,
116-
alignment: objective.alignedEntityId ? objective.alignedEntityId : 'Onull',
132+
// alignment: null,
133+
// alignment: objective.alignedEntityId ? objective.alignedEntityId : 'Onull',
117134
});
118135
});
119136
}
@@ -238,47 +255,110 @@ export class ObjectiveFormComponent implements OnInit {
238255
return GJ_REGEX_PATTERN.test(label);
239256
}
240257

241-
generateAlignmentPossibilities(quarterId: number) {
258+
generateAlignmentPossibilities(quarterId: number, objective: Objective | null) {
242259
this.alignmentPossibilities$ = this.objectiveService.getAlignmentPossibilities(quarterId);
243260
this.alignmentPossibilities$.subscribe((value: AlignmentPossibility[]) => {
244261
if (this.objective?.id) {
245262
value = value.filter((item: AlignmentPossibility) => !(item.objectiveId == this.objective!.id));
246263
}
247-
let firstSelectOption = {
248-
objectiveId: null,
249-
objectiveTitle: 'Kein Alignment',
250-
keyResultAlignmentsDtos: [],
251-
};
252-
if (value.length != 0) {
253-
if (this.objective?.alignedEntityId) {
254-
if (value[0].objectiveTitle == 'Bitte wählen') {
255-
value.splice(0, 1);
264+
// let firstSelectOption = {
265+
// objectiveId: null,
266+
// objectiveTitle: 'Kein Alignment',
267+
// keyResultAlignmentsDtos: [],
268+
// };
269+
// if (value.length != 0) {
270+
// if (this.objective?.alignedEntityId) {
271+
// if (value[0].objectiveTitle == 'Bitte wählen') {
272+
// value.splice(0, 1);
273+
// }
274+
// } else {
275+
// firstSelectOption.objectiveTitle = 'Bitte wählen';
276+
// }
277+
// }
278+
// value.unshift(firstSelectOption);
279+
280+
if (objective) {
281+
let alignment = objective.alignedEntityId;
282+
if (alignment) {
283+
let alignmentType = alignment.charAt(0);
284+
let alignmentId = parseInt(alignment.substring(1));
285+
if (alignmentType == 'O') {
286+
let element = value.find((ap) => ap.objectiveId == alignmentId) || null;
287+
this.objectiveForm.patchValue({
288+
alignment: element,
289+
});
290+
} else {
291+
for (let objectiveAlignment of value) {
292+
let keyResult = objectiveAlignment.keyResultAlignmentsDtos.find((kr) => kr.keyResultId == alignmentId);
293+
if (keyResult) {
294+
// TODO change this to keyresult
295+
this.objectiveForm.patchValue({
296+
alignment: objectiveAlignment,
297+
});
298+
}
299+
}
256300
}
257-
} else {
258-
firstSelectOption.objectiveTitle = 'Bitte wählen';
259301
}
302+
} else {
303+
this.objectiveForm.patchValue({
304+
alignment: null,
305+
});
260306
}
261-
value.unshift(firstSelectOption);
307+
308+
this.filteredOptions = value.slice();
262309
this.alignmentPossibilities$ = of(value);
263310
});
264311
}
265312

266313
updateAlignments() {
267-
this.generateAlignmentPossibilities(this.objectiveForm.value.quarter!);
268-
this.objectiveForm.patchValue({
269-
alignment: 'Onull',
270-
});
314+
this.generateAlignmentPossibilities(this.objectiveForm.value.quarter!, null);
315+
// this.objectiveForm.patchValue({
316+
// alignment: 'Onull',
317+
// });
271318
}
272319

273-
changeFirstAlignmentPossibility() {
274-
this.alignmentPossibilities$.subscribe((value: AlignmentPossibility[]) => {
275-
let element: AlignmentPossibility = value[0];
276-
element.objectiveTitle = 'Kein Alignment';
277-
value.splice(0, 1);
278-
value.unshift(element);
279-
this.alignmentPossibilities$ = of(value);
320+
// changeFirstAlignmentPossibility() {
321+
// this.alignmentPossibilities$.subscribe((value: AlignmentPossibility[]) => {
322+
// let element: AlignmentPossibility = value[0];
323+
// element.objectiveTitle = 'Kein Alignment';
324+
// value.splice(0, 1);
325+
// value.unshift(element);
326+
// this.alignmentPossibilities$ = of(value);
327+
// });
328+
// }
329+
330+
filter() {
331+
let filterValue = this.input.nativeElement.value.toLowerCase();
332+
this.alignmentPossibilities$.subscribe((value) => {
333+
// this.filteredOptions = value.filter((o) => o.objectiveTitle.toLowerCase().includes(filterValue));
334+
this.filteredOptions = value.filter(
335+
(o) =>
336+
o.objectiveTitle.toLowerCase().includes(filterValue) || // Check if objectiveTitle includes the filterValue
337+
o.keyResultAlignmentsDtos.some((kr) => kr.keyResultTitle.toLowerCase().includes(filterValue)), // Check if any keyResultTitle includes the filterValue
338+
);
280339
});
281340
}
282341

342+
displayWith(value: any): string {
343+
if (value) {
344+
if (value.objectiveId) {
345+
return value.objectiveTitle;
346+
} else {
347+
return value.keyResultTitle;
348+
}
349+
} else {
350+
return 'Bitte wählen';
351+
}
352+
}
353+
354+
get displayedValue(): string {
355+
if (this.input) {
356+
const inputValue = this.input.nativeElement.value;
357+
return inputValue.length > 40 ? inputValue.slice(0, 40) + '...' : inputValue;
358+
} else {
359+
return '';
360+
}
361+
}
362+
283363
protected readonly getQuarterLabel = getQuarterLabel;
284364
}

0 commit comments

Comments
 (0)