diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index dedf0f1..1c7ee7c 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -83,6 +83,12 @@ export class ControllerRoute { public static readonly GET_ALL_BOOKABLE_SPACES_DESCRIPTION = 'This endpoint retrieves all bookable spaces.'; + + public static readonly UPDATE_BOOKABLE_SPACES_SUMMARY = + 'Update existing bookable spaces'; + + public static readonly UPDATE_BOOKABLE_SPACES_DESCRIPTION = + 'This endpoint allows you to update existing bookable spaces by providing the required details.'; }; }; static COMMUNITY = class { diff --git a/src/booking/controllers/bookable-space.controller.ts b/src/booking/controllers/bookable-space.controller.ts index fc41dd5..65d7b33 100644 --- a/src/booking/controllers/bookable-space.controller.ts +++ b/src/booking/controllers/bookable-space.controller.ts @@ -6,7 +6,9 @@ import { Body, Controller, Get, + Param, Post, + Put, Query, Req, UseGuards, @@ -19,6 +21,7 @@ import { plainToInstance } from 'class-transformer'; import { CreateBookableSpaceDto } from '../dtos'; import { BookableSpaceRequestDto } from '../dtos/bookable-space-request.dto'; import { BookableSpaceResponseDto } from '../dtos/bookable-space-response.dto'; +import { UpdateBookableSpaceDto } from '../dtos/update-bookable-space.dto'; import { BookableSpaceService } from '../services'; @ApiTags('Booking Module') @@ -79,4 +82,25 @@ export class BookableSpaceController { pagination, ); } + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Put(':spaceUuid') + @ApiOperation({ + summary: + ControllerRoute.BOOKABLE_SPACES.ACTIONS.UPDATE_BOOKABLE_SPACES_SUMMARY, + description: + ControllerRoute.BOOKABLE_SPACES.ACTIONS + .UPDATE_BOOKABLE_SPACES_DESCRIPTION, + }) + async update( + @Param('spaceUuid') spaceUuid: string, + @Body() dto: UpdateBookableSpaceDto, + ): Promise { + const result = await this.bookableSpaceService.update(spaceUuid, dto); + return new SuccessResponseDto({ + data: result, + message: 'Successfully updated bookable spaces', + }); + } } diff --git a/src/booking/dtos/bookable-space-request.dto.ts b/src/booking/dtos/bookable-space-request.dto.ts index f0442cd..e958aa0 100644 --- a/src/booking/dtos/bookable-space-request.dto.ts +++ b/src/booking/dtos/bookable-space-request.dto.ts @@ -1,11 +1,11 @@ import { BooleanValues } from '@app/common/constants/boolean-values.enum'; -import { PaginationRequestGetListDto } from '@app/common/dto/pagination.request.dto'; +import { PaginationRequestWithSearchGetListDto } from '@app/common/dto/pagination-with-search.request.dto'; import { ApiProperty, OmitType } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { IsBoolean, IsNotEmpty, IsOptional } from 'class-validator'; export class BookableSpaceRequestDto extends OmitType( - PaginationRequestGetListDto, + PaginationRequestWithSearchGetListDto, ['includeSpaces'], ) { @ApiProperty({ diff --git a/src/booking/dtos/create-bookable-space.dto.ts b/src/booking/dtos/create-bookable-space.dto.ts index 6508c36..df40ee8 100644 --- a/src/booking/dtos/create-bookable-space.dto.ts +++ b/src/booking/dtos/create-bookable-space.dto.ts @@ -1,4 +1,3 @@ -// dtos/bookable-space.dto.ts import { DaysEnum } from '@app/common/constants/days.enum'; import { ApiProperty } from '@nestjs/swagger'; import { diff --git a/src/booking/dtos/update-bookable-space.dto.ts b/src/booking/dtos/update-bookable-space.dto.ts new file mode 100644 index 0000000..2b7f048 --- /dev/null +++ b/src/booking/dtos/update-bookable-space.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty, OmitType, PartialType } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; +import { CreateBookableSpaceDto } from './create-bookable-space.dto'; + +export class UpdateBookableSpaceDto extends PartialType( + OmitType(CreateBookableSpaceDto, ['spaceUuids']), +) { + @ApiProperty({ type: Boolean }) + @IsOptional() + @IsBoolean() + active?: boolean; +} diff --git a/src/booking/services/bookable-space.service.ts b/src/booking/services/bookable-space.service.ts index 791486e..ae8330b 100644 --- a/src/booking/services/bookable-space.service.ts +++ b/src/booking/services/bookable-space.service.ts @@ -14,6 +14,7 @@ import { import { In } from 'typeorm'; import { CreateBookableSpaceDto } from '../dtos'; import { BookableSpaceRequestDto } from '../dtos/bookable-space-request.dto'; +import { UpdateBookableSpaceDto } from '../dtos/update-bookable-space.dto'; @Injectable() export class BookableSpaceService { @@ -37,7 +38,7 @@ export class BookableSpaceService { } async findAll( - { active, page, size, configured }: BookableSpaceRequestDto, + { active, page, size, configured, search }: BookableSpaceRequestDto, project: string, ): Promise<{ data: BaseResponseDto['data']; @@ -49,6 +50,12 @@ export class BookableSpaceService { .leftJoinAndSelect('space.community', 'community') .where('community.project = :project', { project }); + if (search) { + qb = qb.andWhere( + 'space.spaceName ILIKE :search OR community.name ILIKE :search OR parentSpace.spaceName ILIKE :search', + { search: `%${search}%` }, + ); + } if (configured) { qb = qb .leftJoinAndSelect('space.bookableConfig', 'bookableConfig') @@ -77,6 +84,30 @@ export class BookableSpaceService { }; } + /** + * todo: if updating availability, send to the ones who have access to this space + * todo: if updating other fields, just send emails to all users who's bookings might be affected + */ + async update(spaceUuid: string, dto: UpdateBookableSpaceDto) { + // fetch spaces exist + const space = (await this.getSpacesOrFindMissing([spaceUuid]))[0]; + + if (!space.bookableConfig) { + throw new NotFoundException( + `Bookable configuration not found for space: ${spaceUuid}`, + ); + } + if (dto.startTime || dto.endTime) { + // Validate time slots first + this.validateTimeSlot( + dto.startTime || space.bookableConfig.startTime, + dto.endTime || space.bookableConfig.endTime, + ); + } + Object.assign(space.bookableConfig, dto); + return this.bookableSpaceEntityRepository.save(space.bookableConfig); + } + /** * Fetch spaces by UUIDs and throw an error if any are missing */ @@ -85,6 +116,7 @@ export class BookableSpaceService { ): Promise { const spaces = await this.spaceRepository.find({ where: { uuid: In(spaceUuids) }, + relations: ['bookableConfig'], }); if (spaces.length !== spaceUuids.length) {