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} +