From 2a635065573ab4084623ce812961c699e09ba312 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Fri, 10 Jan 2025 14:09:18 +0100 Subject: [PATCH 1/2] convert User to class --- .../application-top-bar.component.ts | 3 +- .../key-result-dialog.component.ts | 7 +-- .../key-result-form.component.html | 23 +------- .../key-result-form.component.ts | 12 ++-- .../key-result-type.component.html | 56 +++++++++++++------ .../key-result-type.component.ts | 17 ++++++ .../team-filter/team-filter.component.ts | 3 +- .../shared/types/model/user-table-entry.ts | 33 ++++++----- frontend/src/app/shared/types/model/user.ts | 37 +++++++++--- .../delete-user/delete-user.component.html | 2 +- .../delete-user/delete-user.component.ts | 10 ++-- .../member-detail.component.html | 2 +- .../member-detail/member-detail.component.ts | 6 +- .../member-list-mobile.component.html | 2 +- .../member-list-mobile.component.ts | 3 - .../search-team-management.component.ts | 3 +- 16 files changed, 122 insertions(+), 97 deletions(-) diff --git a/frontend/src/app/components/application-top-bar/application-top-bar.component.ts b/frontend/src/app/components/application-top-bar/application-top-bar.component.ts index 9d0fe1a583..3902d5bfc8 100644 --- a/frontend/src/app/components/application-top-bar/application-top-bar.component.ts +++ b/frontend/src/app/components/application-top-bar/application-top-bar.component.ts @@ -4,7 +4,6 @@ import { BehaviorSubject, Subscription } from 'rxjs'; import { ConfigService } from '../../services/config.service'; import { NavigationEnd, Router } from '@angular/router'; import { UserService } from '../../services/user.service'; -import { getFullNameOfUser } from '../../shared/types/model/user'; @Component({ selector: 'app-application-top-bar', @@ -62,7 +61,7 @@ export class ApplicationTopBarComponent implements OnInit, OnDestroy { // user is loaded on base route resolver. We have to wait until routing is done. this.router.events.subscribe((val) => { if (!this.userFullName && val instanceof NavigationEnd) { - this.userFullName = getFullNameOfUser(this.userService.getCurrentUser()); + this.userFullName = this.userService.getCurrentUser().fullName; this.cd.markForCheck(); } }); diff --git a/frontend/src/app/components/key-result-dialog/key-result-dialog.component.ts b/frontend/src/app/components/key-result-dialog/key-result-dialog.component.ts index b4a768df73..375eb2e7bc 100644 --- a/frontend/src/app/components/key-result-dialog/key-result-dialog.component.ts +++ b/frontend/src/app/components/key-result-dialog/key-result-dialog.component.ts @@ -86,14 +86,11 @@ export class KeyResultDialogComponent { } isTouchedOrDirty(name: string) { - return this.keyResultForm.get(name)?.dirty || this.keyResultForm.get(name)?.touched; + return this.keyResultForm.get(name)?.dirty || this.keyResultForm.get(name)?.touched || false; } invalidOwner(): boolean { - return ( - !!this.isTouchedOrDirty('owner') && - (typeof this.keyResultForm.value.owner === 'string' || !this.keyResultForm.value.owner) - ); + return this.isTouchedOrDirty('owner') && (typeof this.keyResultForm.value.owner === 'string' || !this.keyResultForm.value.owner); } getDialogTitle(): string { diff --git a/frontend/src/app/components/key-result-form/key-result-form.component.html b/frontend/src/app/components/key-result-form/key-result-form.component.html index 3d33f023b4..56d19c9245 100644 --- a/frontend/src/app/components/key-result-form/key-result-form.component.html +++ b/frontend/src/app/components/key-result-form/key-result-form.component.html @@ -19,30 +19,9 @@ (formValidityEmitter)="updateFormValidity()" [keyResult]="keyResult" [keyResultForm]="keyResultForm" + [users]="filteredUsers$ " > -
- -
- - - - {{ user.firstName + " " + user.lastName }} - - - - {{ getErrorMessage("MUST_SELECT", "Owner", null, null) }} - -
-
diff --git a/frontend/src/app/components/key-result-form/key-result-form.component.ts b/frontend/src/app/components/key-result-form/key-result-form.component.ts index 82fbae245f..7fa551bcbd 100644 --- a/frontend/src/app/components/key-result-form/key-result-form.component.ts +++ b/frontend/src/app/components/key-result-form/key-result-form.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; -import { getFullNameOfUser, User } from '../../shared/types/model/user'; +import { User } from '../../shared/types/model/user'; import { KeyResult } from '../../shared/types/model/key-result'; import { KeyResultMetric } from '../../shared/types/model/key-result-metric'; import { KeyResultOrdinal } from '../../shared/types/model/key-result-ordinal'; @@ -19,7 +19,7 @@ import { TranslateService } from '@ngx-translate/core'; export class KeyResultFormComponent implements OnInit, OnDestroy { users$!: Observable; - filteredUsers$: Observable | undefined = of([]); + filteredUsers$: Observable = of([]); actionList$: BehaviorSubject = new BehaviorSubject([] as Action[]); @@ -40,7 +40,7 @@ export class KeyResultFormComponent implements OnInit, OnDestroy { ngOnInit(): void { this.users$ = this.userService.getUsers(); - this.filteredUsers$ = this.keyResultForm.get('owner')?.valueChanges.pipe(startWith(''), filter((value) => typeof value === 'string'), switchMap((value) => this.filter(value))); + this.filteredUsers$ = this.keyResultForm.get('owner')?.valueChanges.pipe(startWith(''), filter((value) => typeof value === 'string'), switchMap((value) => this.filter(value))) || of([]); if (this.keyResult) { this.keyResultForm.patchValue({ actionList: this.keyResult.actionList }); this.keyResultForm.controls['title'].setValue(this.keyResult.title); @@ -77,7 +77,7 @@ export class KeyResultFormComponent implements OnInit, OnDestroy { .subscribe((users) => { const loggedInUser = this.getFullNameOfLoggedInUser(); users.forEach((user) => { - if (getFullNameOfUser(user) === loggedInUser) { + if (user.fullName === loggedInUser) { this.keyResultForm.controls['owner'].setValue(user); } }); @@ -123,7 +123,7 @@ export class KeyResultFormComponent implements OnInit, OnDestroy { filter(value: string): Observable { const filterValue = value.toLowerCase(); - return this.users$.pipe(map((users) => users.filter((user) => getFullNameOfUser(user) + return this.users$.pipe(map((users) => users.filter((user) => user.fullName .toLowerCase() .includes(filterValue)))); } @@ -136,7 +136,7 @@ export class KeyResultFormComponent implements OnInit, OnDestroy { } getFullNameOfUser(user: User): string { - return user ? getFullNameOfUser(user) : ''; + return user?.fullName || ''; } getKeyResultId(): number | null { diff --git a/frontend/src/app/components/key-result-type/key-result-type.component.html b/frontend/src/app/components/key-result-type/key-result-type.component.html index 59622ff963..5b3524475b 100644 --- a/frontend/src/app/components/key-result-type/key-result-type.component.html +++ b/frontend/src/app/components/key-result-type/key-result-type.component.html @@ -26,28 +26,52 @@
-
-
+
+
-
- - - {{ getErrorMessage("MUST_SELECT", "Einheit", null, null) }} + + + {{ getErrorMessage("MUST_SELECT", "Einheit", null, null) }} + +
+
+ +
+
+ +
+ + + + {{ user.firstName + " " + user.lastName }} + + + + {{ getErrorMessage("MUST_SELECT", "Owner", null, null) }}
+
diff --git a/frontend/src/app/components/key-result-type/key-result-type.component.ts b/frontend/src/app/components/key-result-type/key-result-type.component.ts index 8024f8168a..3c6fbac380 100644 --- a/frontend/src/app/components/key-result-type/key-result-type.component.ts +++ b/frontend/src/app/components/key-result-type/key-result-type.component.ts @@ -6,6 +6,8 @@ import { KeyResultOrdinal } from '../../shared/types/model/key-result-ordinal'; import { Unit } from '../../shared/types/enums/unit'; import { formInputCheck, hasFormFieldErrors } from '../../shared/common'; import { TranslateService } from '@ngx-translate/core'; +import { User } from '../../shared/types/model/user'; +import { Observable, Subject } from 'rxjs'; @Component({ selector: 'app-key-result-type', @@ -18,6 +20,8 @@ export class KeyResultTypeComponent implements OnInit { @Input() keyResult!: KeyResult | null; + @Input() users: Observable = new Subject(); + isMetric = true; @@ -112,4 +116,17 @@ export class KeyResultTypeComponent implements OnInit { return field + this.translate.instant('DIALOG_ERRORS.' + error) .format(firstNumber, secondNumber); } + + isTouchedOrDirty(name: string) { + return this.keyResultForm.get(name)?.dirty || this.keyResultForm.get(name)?.touched; + } + + invalidOwner(): boolean { + return ( + !!this.isTouchedOrDirty('owner') && + (typeof this.keyResultForm.value.owner === 'string' || !this.keyResultForm.value.owner) + ); + } + + protected readonly User = User; } diff --git a/frontend/src/app/components/team-filter/team-filter.component.ts b/frontend/src/app/components/team-filter/team-filter.component.ts index 907ea57b89..9570566669 100644 --- a/frontend/src/app/components/team-filter/team-filter.component.ts +++ b/frontend/src/app/components/team-filter/team-filter.component.ts @@ -6,7 +6,6 @@ import { ActivatedRoute, Router } from '@angular/router'; import { areEqual, getValueFromQuery, optionalReplaceWithNulls, trackByFn } from '../../shared/common'; import { RefreshDataService } from '../../services/refresh-data.service'; import { UserService } from '../../services/user.service'; -import { extractTeamsFromUser } from '../../shared/types/model/user'; import { BreakpointObserver } from '@angular/cdk/layout'; @Component({ @@ -70,7 +69,7 @@ export class TeamFilterComponent implements OnInit, OnDestroy { const knownTeams = this.getAllTeamIds() .filter((teamId) => teamIds?.includes(teamId)); if (knownTeams.length == 0) { - this.activeTeams = extractTeamsFromUser(this.userService.getCurrentUser()) + this.activeTeams = this.userService.getCurrentUser().teamList .map((team) => team.id); } else { this.activeTeams = knownTeams; diff --git a/frontend/src/app/shared/types/model/user-table-entry.ts b/frontend/src/app/shared/types/model/user-table-entry.ts index 204a1d523d..c2ca65bb3d 100644 --- a/frontend/src/app/shared/types/model/user-table-entry.ts +++ b/frontend/src/app/shared/types/model/user-table-entry.ts @@ -2,15 +2,20 @@ import { User } from './user'; import { UserRole } from '../enums/user-role'; import { UserTeam } from './user-team'; -export interface UserTableEntry { - id: number; - firstName: string; - lastName: string; - email: string; +export class UserTableEntry extends User { teams: string[]; + roles: string[]; - isOkrChampion: boolean; - userTeamList: UserTeam[]; + + constructor( + id: number, firstName: string, lastName: string, email: string, userTeamList: UserTeam[], isOkrChampion: boolean, teams: string[], roles: string[] + ) { + super( + id, firstName, lastName, email, userTeamList, isOkrChampion + ); + this.teams = teams; + this.roles = roles; + } } export const convertFromUsers = (users: User[], teamId: number | null): UserTableEntry[] => { @@ -43,14 +48,8 @@ export const convertFromUser = (user: User): UserTableEntry => { roles.push(UserRole.TEAM_MEMBER); } - return { - id: user.id, - firstName: user.firstName, - lastName: user.lastName, - email: user.email, - teams, - roles, - isOkrChampion: user.isOkrChampion, - userTeamList: user.userTeamList - }; + + return new UserTableEntry( + user.id, user.firstName, user.lastName, user.email, user.userTeamList, user.isOkrChampion, teams, roles + ); }; diff --git a/frontend/src/app/shared/types/model/user.ts b/frontend/src/app/shared/types/model/user.ts index d329646f42..af1eeb906f 100644 --- a/frontend/src/app/shared/types/model/user.ts +++ b/frontend/src/app/shared/types/model/user.ts @@ -1,19 +1,38 @@ import { UserTeam } from './user-team'; -import { UserTableEntry } from './user-table-entry'; -export interface User { +export class User { id: number; + firstName: string; + lastName: string; + email: string; + userTeamList: UserTeam[]; + isOkrChampion: boolean; -} -export const extractTeamsFromUser = (user: User) => { - return user.userTeamList.map((u) => u.team); -}; + constructor( + id: number, firstName: string, lastName: string, email: string, userTeamList: UserTeam[], isOkrChampion: boolean + ) { + this.id = id; + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + this.userTeamList = userTeamList; + this.isOkrChampion = isOkrChampion; + } + + get fullName() { + return `${this.firstName} ${this.lastName}`; + } -export const getFullNameOfUser = (user: User | UserTableEntry) => { - return `${user.firstName} ${user.lastName}`; -}; + get teamList() { + return this.userTeamList.map((ut) => ut.team); + } + + static getFullNameOfUser(user: User) { + return user.fullName; + } +} diff --git a/frontend/src/app/team-management/delete-user/delete-user.component.html b/frontend/src/app/team-management/delete-user/delete-user.component.html index 5d0f3c128f..1dd30e802a 100644 --- a/frontend/src/app/team-management/delete-user/delete-user.component.html +++ b/frontend/src/app/team-management/delete-user/delete-user.component.html @@ -13,7 +13,7 @@ class="add-cross-button" src="/assets/icons/delete-blue-icon.svg" /> - {{ getFullNameFromUser(user) }} löschen + {{ user.fullName }} löschen diff --git a/frontend/src/app/team-management/delete-user/delete-user.component.ts b/frontend/src/app/team-management/delete-user/delete-user.component.ts index dee3f760ff..7384182a83 100644 --- a/frontend/src/app/team-management/delete-user/delete-user.component.ts +++ b/frontend/src/app/team-management/delete-user/delete-user.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnInit, OnDestroy } from '@angular/core'; import { UserService } from '../../services/user.service'; -import { getFullNameOfUser, User } from '../../shared/types/model/user'; +import { User } from '../../shared/types/model/user'; import { Location } from '@angular/common'; import { Observable, Subject, takeUntil, tap } from 'rxjs'; import { UserTeam } from '../../shared/types/model/user-team'; @@ -78,12 +78,12 @@ export class DeleteUserComponent implements OnInit, OnDestroy { deleteUserWithChecks() { if (this.isUserMemberOfTeams()) { const dialogTitle = 'User kann nicht gelöscht werden'; - const dialogText = `${getFullNameOfUser(this.user)} ist in folgenden Teams und kann daher nicht gelöscht werden: ${this.getDialogDetailsUserTeams()}`; + const dialogText = `${this.user.fullName} ist in folgenden Teams und kann daher nicht gelöscht werden: ${this.getDialogDetailsUserTeams()}`; this.showUnableToDeleteUserDialog(dialogTitle, dialogText); return; } else if (this.isUserOwnerOfKeyResults()) { const dialogTitle = 'User kann nicht gelöscht werden'; - const dialogText = `${getFullNameOfUser(this.user)} ist Owner folgender KeyResults und kann daher nicht gelöscht werden: \n\n${this.dialogDetailsUserKeyResults()}`; + const dialogText = `${this.user.fullName} ist Owner folgender KeyResults und kann daher nicht gelöscht werden: \n\n${this.dialogDetailsUserKeyResults()}`; this.showUnableToDeleteUserDialog(dialogTitle, dialogText); return; } @@ -148,13 +148,11 @@ export class DeleteUserComponent implements OnInit, OnDestroy { this.location.back(); }, error: () => { - throw Error(`unable to delete user ${getFullNameOfUser(this.user)} (with id ${this.user.id})`); + throw Error(`unable to delete user ${this.user.fullName} (with id ${this.user.id})`); } })) .subscribe(); } }); } - - protected readonly getFullNameFromUser = getFullNameOfUser; } diff --git a/frontend/src/app/team-management/member-detail/member-detail.component.html b/frontend/src/app/team-management/member-detail/member-detail.component.html index 7264cd843b..c2652e2dc2 100644 --- a/frontend/src/app/team-management/member-detail/member-detail.component.html +++ b/frontend/src/app/team-management/member-detail/member-detail.component.html @@ -7,7 +7,7 @@

- {{ getFullNameFromUser(userRef) }} + {{ userRef.fullName }} (ich)