From a9e83544c54cc38926a18662e5f9c66cf7b0791e Mon Sep 17 00:00:00 2001 From: Nils-Johan Gynther Date: Fri, 17 Apr 2026 20:44:23 +0200 Subject: [PATCH] feat(profile): add user profile management with first and last name fields --- .../migration.sql | 2 + backend/prisma/schema.prisma | 2 + backend/src/users/users.controller.ts | 52 +++++ backend/src/users/users.module.ts | 2 + backend/src/users/users.service.ts | 4 + frontend/app/Navigation.tsx | 34 +++- frontend/app/api/profile/route.ts | 32 +++ frontend/app/profil/ProfileClient.tsx | 188 ++++++++++++++++++ frontend/app/profil/page.tsx | 16 ++ 9 files changed, 329 insertions(+), 3 deletions(-) create mode 100644 backend/prisma/migrations/20260417300000_add_user_profile_fields/migration.sql create mode 100644 backend/src/users/users.controller.ts create mode 100644 frontend/app/api/profile/route.ts create mode 100644 frontend/app/profil/ProfileClient.tsx create mode 100644 frontend/app/profil/page.tsx diff --git a/backend/prisma/migrations/20260417300000_add_user_profile_fields/migration.sql b/backend/prisma/migrations/20260417300000_add_user_profile_fields/migration.sql new file mode 100644 index 00000000..817009d9 --- /dev/null +++ b/backend/prisma/migrations/20260417300000_add_user_profile_fields/migration.sql @@ -0,0 +1,2 @@ +ALTER TABLE `User` ADD COLUMN `firstName` VARCHAR(191) NULL; +ALTER TABLE `User` ADD COLUMN `lastName` VARCHAR(191) NULL; diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 7d01ed1c..ecd5c3b4 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -11,6 +11,8 @@ model User { id Int @id @default(autoincrement()) username String @unique email String @unique + firstName String? + lastName String? passwordHash String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt diff --git a/backend/src/users/users.controller.ts b/backend/src/users/users.controller.ts new file mode 100644 index 00000000..8e177114 --- /dev/null +++ b/backend/src/users/users.controller.ts @@ -0,0 +1,52 @@ +import { Controller, Get, Patch, Body } from '@nestjs/common'; +import { IsEmail, IsOptional, IsString, MaxLength } from 'class-validator'; +import { UsersService } from './users.service'; +import { CurrentUser } from '../auth/decorators/current-user.decorator'; + +class UpdateProfileDto { + @IsOptional() + @IsString() + @MaxLength(100) + firstName?: string; + + @IsOptional() + @IsString() + @MaxLength(100) + lastName?: string; + + @IsOptional() + @IsEmail() + email?: string; +} + +@Controller('api/users') +export class UsersController { + constructor(private readonly usersService: UsersService) {} + + @Get('me') + async getMe(@CurrentUser() user: { userId: number; username: string }) { + const found = await this.usersService.findById(user.userId); + return { + id: found?.id, + username: found?.username, + email: found?.email, + firstName: found?.firstName, + lastName: found?.lastName, + }; + } + + @Patch('me') + async updateMe( + @CurrentUser() user: { userId: number; username: string }, + @Body() dto: UpdateProfileDto, + ) { + const updated = await this.usersService.updateProfile(user.userId, dto); + return { + id: updated.id, + username: updated.username, + email: updated.email, + firstName: updated.firstName, + lastName: updated.lastName, + }; + } +} diff --git a/backend/src/users/users.module.ts b/backend/src/users/users.module.ts index d9c11340..a34ebc08 100644 --- a/backend/src/users/users.module.ts +++ b/backend/src/users/users.module.ts @@ -1,10 +1,12 @@ import { Module } from '@nestjs/common'; import { UsersService } from './users.service'; +import { UsersController } from './users.controller'; import { PrismaModule } from '../prisma/prisma.module'; @Module({ imports: [PrismaModule], providers: [UsersService], + controllers: [UsersController], exports: [UsersService], }) export class UsersModule {} diff --git a/backend/src/users/users.service.ts b/backend/src/users/users.service.ts index 31b214a6..1debd471 100644 --- a/backend/src/users/users.service.ts +++ b/backend/src/users/users.service.ts @@ -16,4 +16,8 @@ export class UsersService { create(data: { username: string; email: string; passwordHash: string }) { return this.prisma.user.create({ data }); } + + updateProfile(id: number, data: { firstName?: string; lastName?: string; email?: string }) { + return this.prisma.user.update({ where: { id }, data }); + } } diff --git a/frontend/app/Navigation.tsx b/frontend/app/Navigation.tsx index 08052190..00597537 100644 --- a/frontend/app/Navigation.tsx +++ b/frontend/app/Navigation.tsx @@ -39,9 +39,37 @@ export default async function Navigation() { {session?.user && ( <> - - đŸ‘€ {session.user.name} - + + + {session.user.name?.charAt(0).toUpperCase()} + + {session.user.name} +
+
+ + ); +} diff --git a/frontend/app/profil/page.tsx b/frontend/app/profil/page.tsx new file mode 100644 index 00000000..1df53b9e --- /dev/null +++ b/frontend/app/profil/page.tsx @@ -0,0 +1,16 @@ +import Navigation from '../Navigation'; +import ProfileClient from './ProfileClient'; + +export const metadata = { title: 'Min profil' }; + +export default function ProfilPage() { + return ( + <> + +
+

Min profil

+ +
+ + ); +}