From 368e80408d04834d8bfc4ff774633d3e82f0a5f3 Mon Sep 17 00:00:00 2001 From: Mhd Zayd Skaff Date: Sun, 27 Jul 2025 10:22:09 +0300 Subject: [PATCH] add update booking settings API --- libs/common/src/constants/controller-route.ts | 5 ++ .../src/modules/user/entities/user.entity.ts | 5 +- src/users/controllers/user.controller.ts | 25 ++++++++++ .../dtos/update-user-booking-settings.dto.ts | 29 ++++++++++++ src/users/services/user.service.ts | 47 ++++++++++++++++++- 5 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 src/users/dtos/update-user-booking-settings.dto.ts diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index 5f7e78e..0bc70f5 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -448,6 +448,11 @@ export class ControllerRoute { public static readonly UPDATE_NAME_DESCRIPTION = 'This endpoint updates the name for a user identified by their UUID.'; + public static readonly UPDATE_BOOKING_SETTINGS_SUMMARY = + 'Update booking settings by user UUID'; + public static readonly UPDATE_BOOKING_SETTINGS_DESCRIPTION = + 'This endpoint updates the booking settings for a user identified by their UUID.'; + public static readonly DELETE_USER_SUMMARY = 'Delete user by UUID'; public static readonly DELETE_USER_DESCRIPTION = 'This endpoint deletes a user identified by their UUID. Accessible only by users with the Super Admin role.'; diff --git a/libs/common/src/modules/user/entities/user.entity.ts b/libs/common/src/modules/user/entities/user.entity.ts index fe6aa27..f0a14ef 100644 --- a/libs/common/src/modules/user/entities/user.entity.ts +++ b/libs/common/src/modules/user/entities/user.entity.ts @@ -11,6 +11,7 @@ import { } from 'typeorm'; import { OtpType } from '../../../../src/constants/otp-type.enum'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { BookingEntity } from '../../booking/entities/booking.entity'; import { ClientEntity } from '../../client/entities'; import { DeviceNotificationEntity, @@ -29,7 +30,6 @@ import { UserOtpDto, UserSpaceDto, } from '../dtos'; -import { BookingEntity } from '../../booking/entities/booking.entity'; @Entity({ name: 'user' }) export class UserEntity extends AbstractEntity { @@ -101,6 +101,9 @@ export class UserEntity extends AbstractEntity { @Column({ type: 'timestamp', nullable: true }) appAgreementAcceptedAt: Date; + @Column({ type: Boolean, default: false }) + bookingEnabled: boolean; + @OneToMany(() => UserSpaceEntity, (userSpace) => userSpace.user, { onDelete: 'CASCADE', }) diff --git a/src/users/controllers/user.controller.ts b/src/users/controllers/user.controller.ts index 7d8bf4b..76d49ce 100644 --- a/src/users/controllers/user.controller.ts +++ b/src/users/controllers/user.controller.ts @@ -25,6 +25,7 @@ import { UpdateTimezoneDataDto, } from '../dtos'; import { UserService } from '../services/user.service'; +import { UpdateBookingSettingsDto } from '../dtos/update-user-booking-settings.dto'; @ApiTags('User Module') @Controller({ @@ -138,6 +139,30 @@ export class UserController { }; } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Put('/booking-settings/:userUuid') + @ApiOperation({ + summary: ControllerRoute.USER.ACTIONS.UPDATE_BOOKING_SETTINGS_SUMMARY, + description: + ControllerRoute.USER.ACTIONS.UPDATE_BOOKING_SETTINGS_DESCRIPTION, + }) + async updateBookingSettingsByUserUuid( + @Param('userUuid') userUuid: string, + @Body() dto: UpdateBookingSettingsDto, + ) { + const userData = await this.userService.updateBookingSettingsByUserUuid( + userUuid, + dto, + ); + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'Booking settings updated successfully', + data: userData, + }; + } + @ApiBearerAuth() @UseGuards(SuperAdminRoleGuard) @Delete('/:userUuid') diff --git a/src/users/dtos/update-user-booking-settings.dto.ts b/src/users/dtos/update-user-booking-settings.dto.ts new file mode 100644 index 0000000..9c31507 --- /dev/null +++ b/src/users/dtos/update-user-booking-settings.dto.ts @@ -0,0 +1,29 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsInt, IsOptional, IsPositive, Min } from 'class-validator'; + +export class UpdateBookingSettingsDto { + @ApiProperty({ + description: 'bookingEnable', + required: false, + }) + @IsBoolean() + @IsOptional() + bookingEnable?: boolean; + + @ApiProperty({ + description: 'increase user booking balance by top up amount', + required: false, + }) + @IsPositive() + @IsOptional() + topUp?: number; + + @ApiProperty({ + description: 'replace user booking balance by required amount', + required: false, + }) + @IsInt() + @Min(0) + @IsOptional() + balance?: number; +} diff --git a/src/users/services/user.service.ts b/src/users/services/user.service.ts index 89c06f8..cdedd52 100644 --- a/src/users/services/user.service.ts +++ b/src/users/services/user.service.ts @@ -10,6 +10,7 @@ import { HttpStatus, Injectable, } from '@nestjs/common'; +import { UpdateBookingSettingsDto } from '../dtos/update-user-booking-settings.dto'; import { UpdateNameDto, UpdateProfilePictureDataDto, @@ -33,7 +34,7 @@ export class UserService { relations: ['region', 'timezone', 'roleType', 'project'], }); if (!user) { - throw new BadRequestException('Invalid room UUID'); + throw new BadRequestException('Invalid user UUID'); } // Use the utility function to remove the base64 prefix @@ -53,6 +54,8 @@ export class UserService { appAgreementAcceptedAt: user?.appAgreementAcceptedAt, role: user?.roleType, project: user?.project, + bookingPoints: user?.bookingPoints ?? 0, + bookingEnabled: user?.bookingEnabled ?? false, }; } catch (err) { if (err instanceof BadRequestException) { @@ -245,6 +248,48 @@ export class UserService { ); } } + + async updateBookingSettingsByUserUuid( + userUuid: string, + { balance, bookingEnable, topUp }: UpdateBookingSettingsDto, + ) { + try { + let user = await this.getUserDetailsByUserUuid(userUuid); + if (!user) { + throw new HttpException('User not found', HttpStatus.NOT_FOUND); + } + + if (balance && topUp) { + throw new BadRequestException( + 'Please provide either balance or topUp, not both', + ); + } + + if (topUp) { + user.bookingPoints += topUp; + } + if (balance) { + user.bookingPoints = balance; + } + if (bookingEnable !== undefined) { + user.bookingEnabled = bookingEnable; + } + + user = await this.userRepository.save(user); + + return { + uuid: user.uuid, + bookingEnabled: user.bookingEnabled, + bookingPoints: user.bookingPoints, + }; + } catch (err) { + throw new HttpException( + err.message || 'User not found', + HttpStatus.NOT_FOUND, + ); + } + } + async acceptWebAgreement(userUuid: string) { await this.userRepository.update( { uuid: userUuid },