Jelajahi Sumber

User profile editing with custom display name

Lukas Angerer 2 tahun lalu
induk
melakukan
42b8ebd869

+ 20 - 0
src/RunnersMeet.Client/src/app/pages/profile-page/profile-page.component.html

@@ -1,3 +1,23 @@
+
+<h1>User Profile</h1>
+<dl class="pivot-table">
+	<dt>ID</dt>
+	<dd>{{ userValidationResult?.userProfile?.userId }}</dd>
+</dl>
+
+<form #mainForm="ngForm" class="vertical-form" (ngSubmit)="updateProfile(mainForm)" *ngIf="userValidationResult">
+	<div class="form-field form-field--full-width">
+		<span class="p-float-label">
+			<input type="text" pInputText id="displayName" name="displayName" [(ngModel)]="displayName" required minlength="2">
+			<label for="displayName">Display Name</label>
+		</span>
+	</div>
+	<div class="button-group">
+		<button type="submit" pButton>Update</button>
+	</div>
+</form>
+
+
 <div *ngIf="true | isDebug">
 	<h3>User Profile</h3>
 	<dl>

+ 14 - 1
src/RunnersMeet.Client/src/app/pages/profile-page/profile-page.component.ts

@@ -1,6 +1,8 @@
 import { Component } from '@angular/core';
+import { NgForm } from '@angular/forms';
 import { AuthService } from '@auth0/auth0-angular';
 import { PermissionService } from 'src/app/users/permission.service';
+import { UsersApiService } from 'src/app/users/users-api.service';
 import { UserValidationResult } from 'src/app/users/validate-user-result';
 
 @Component({
@@ -10,13 +12,24 @@ import { UserValidationResult } from 'src/app/users/validate-user-result';
 })
 export class ProfilePageComponent {
 	public userValidationResult: UserValidationResult | null = null;
+	public displayName: string = '';
 
 	public constructor(
 		public readonly authService: AuthService,
-		public readonly permissionService: PermissionService
+		private readonly permissionService: PermissionService,
+		private readonly usersApiService: UsersApiService
 	) {
 		this.permissionService.userValidationResult.then(userValidationResult => {
 			this.userValidationResult = userValidationResult;
+			this.displayName = userValidationResult?.userProfile.displayName ?? '';
 		});
 	}
+
+	public updateProfile(form: NgForm): void {
+		if (form.valid) {
+			this.usersApiService.updateProfile({
+				displayName: this.displayName,
+			});
+		}
+	}
 }

+ 2 - 1
src/RunnersMeet.Client/src/app/tracks/track-edit/track-edit.component.html

@@ -18,7 +18,8 @@
 <form class="vertical-form" (ngSubmit)="updateTrack($event)" *ngIf="track">
 	<div class="form-field form-field--full-width">
 		<span class="p-float-label">
-			<input type="text" pInputText name="displayName" [(ngModel)]="track.displayName" placeholder="Title">
+			<input type="text" pInputText id="displayName" name="displayName" [(ngModel)]="track.displayName">
+			<label for="displayName">Title</label>
 		</span>
 	</div>
 	<div class="form-field form-field--full-width">

+ 5 - 1
src/RunnersMeet.Client/src/app/users/user-profile.ts

@@ -1,5 +1,9 @@
-export class UserProfile
+export class UserProfile implements UserProfileData
 {
 	public userId: string = '';;
 	public displayName: string = '';
 }
+
+export interface UserProfileData {
+	displayName: string;
+}

+ 5 - 0
src/RunnersMeet.Client/src/app/users/users-api.service.ts

@@ -4,6 +4,7 @@ import { User } from '@auth0/auth0-angular';
 import { lastValueFrom } from 'rxjs';
 import { ConfigService } from '../config.service';
 import { UserValidationResult } from './validate-user-result';
+import { UserProfile, UserProfileData } from './user-profile';
 
 @Injectable({
 	providedIn: 'root'
@@ -22,4 +23,8 @@ export class UsersApiService {
 				return result;
 			});
 	}
+
+	public updateProfile(profileData: UserProfileData): Promise<UserProfile> {
+		return lastValueFrom(this.http.put<UserProfile>(this.config.apiUri('/api/users/profile'), profileData));
+	}
 }

+ 15 - 0
src/RunnersMeet.Server/Controllers/UsersController.cs

@@ -35,4 +35,19 @@ public class UsersController : ControllerBase
 			Claims = ApiUser.Current.Claims,
 		};
 	}
+
+	[HttpPut("profile")]
+	public ActionResult<UserProfile> Update(UserProfileData data)
+	{
+		if (!ApiUser.Current.IsValidUser)
+		{
+			throw new ApiException("UsersController.Validate call without a User / authentication token");
+		}
+
+		var userProfile = _requestRouter
+			.For(new UpdateUserRequest(ApiUser.Current.UserId, data))
+			.Process<UserProfile>();
+
+		return userProfile;
+	}
 }

+ 1 - 2
src/RunnersMeet.Server/Domain/UserProfile.cs

@@ -5,9 +5,8 @@ using LiteDB;
 
 namespace RunnersMeet.Server.Domain;
 
-public class UserProfile
+public class UserProfile : UserProfileData
 {
 	public ObjectId UserProfileId { get; set; } = ObjectId.Empty;
 	public string UserId { get; set; } = String.Empty;
-	public string DisplayName { get; set; } = String.Empty;
 }

+ 10 - 0
src/RunnersMeet.Server/Domain/UserProfileData.cs

@@ -0,0 +1,10 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace RunnersMeet.Server.Domain;
+
+public class UserProfileData
+{
+	[Required]
+	[MinLength(3)]
+	public string DisplayName { get; set; } = String.Empty;
+}

+ 30 - 0
src/RunnersMeet.Server/Persistence/UpdateUserCommand.cs

@@ -0,0 +1,30 @@
+using RunnersMeet.Server.Domain;
+
+namespace RunnersMeet.Server.Persistence;
+
+public sealed record UpdateUserRequest(string UserId, UserProfileData Data);
+
+public class UpdateUserCommand : IRequestHandler<UpdateUserRequest, UserProfile>
+{
+	private readonly IDatabase _database;
+
+	public UpdateUserCommand(IDatabase database)
+	{
+		_database = database;
+	}
+
+	public UserProfile Handle(UpdateUserRequest request)
+	{
+		var user = _database.Users.FindOne(user => user.UserId == request.UserId);
+
+		if (user == null)
+		{
+			throw new ArgumentException($"User with UserId {request.UserId} does not exist", nameof(request.UserId));
+		}
+
+		user.DisplayName = request.Data.DisplayName;
+		_database.Users.Update(user);
+
+		return user;
+	}
+}