diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index 5f7e78e..df5ba21 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -424,6 +424,10 @@ export class ControllerRoute { public static readonly ROUTE = '/user'; static ACTIONS = class { + public static readonly GET_USERS_WITH_BOOKABLE_SPACES_SUMMARY = + 'Retrieve list of users that has bookable spaces'; + public static readonly GET_USERS_WITH_BOOKABLE_SPACES_DESCRIPTION = + 'This endpoint retrieves all the users that have access to bookable spaces, paginated & accepts sorting'; public static readonly GET_USER_DETAILS_SUMMARY = 'Retrieve user details by user UUID'; public static readonly GET_USER_DETAILS_DESCRIPTION = diff --git a/libs/common/src/modules/user/entities/user.entity.ts b/libs/common/src/modules/user/entities/user.entity.ts index fe6aa27..a5c41b2 100644 --- a/libs/common/src/modules/user/entities/user.entity.ts +++ b/libs/common/src/modules/user/entities/user.entity.ts @@ -44,6 +44,7 @@ export class UserEntity extends AbstractEntity { nullable: true, type: 'text', default: defaultProfilePicture, + select: false, }) public profilePicture: string; diff --git a/src/users/controllers/user-space.controller.ts b/src/users/controllers/user-space.controller.ts index 519a033..fcd3b25 100644 --- a/src/users/controllers/user-space.controller.ts +++ b/src/users/controllers/user-space.controller.ts @@ -1,11 +1,11 @@ import { ControllerRoute } from '@app/common/constants/controller-route'; import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; import { Controller, Get, Param, UseGuards } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { UserSpaceService } from '../services'; -import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; -import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { UserParamDto } from '../dtos'; +import { UserSpaceService } from '../services'; @ApiTags('User Module') @Controller({ diff --git a/src/users/controllers/user.controller.ts b/src/users/controllers/user.controller.ts index 7d8bf4b..5b668c5 100644 --- a/src/users/controllers/user.controller.ts +++ b/src/users/controllers/user.controller.ts @@ -11,6 +11,7 @@ import { Param, Patch, Put, + Query, Req, UseGuards, } from '@nestjs/common'; @@ -24,6 +25,7 @@ import { UpdateRegionDataDto, UpdateTimezoneDataDto, } from '../dtos'; +import { UsersWithBookableSpacesFilterDto } from '../dtos/users-with-bookable-spaces-filter.dto'; import { UserService } from '../services/user.service'; @ApiTags('User Module') @@ -34,6 +36,21 @@ import { UserService } from '../services/user.service'; export class UserController { constructor(private readonly userService: UserService) {} + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get('with-bookable-spaces') + @ApiOperation({ + summary: + ControllerRoute.USER.ACTIONS.GET_USERS_WITH_BOOKABLE_SPACES_SUMMARY, + description: + ControllerRoute.USER.ACTIONS.GET_USERS_WITH_BOOKABLE_SPACES_DESCRIPTION, + }) + async getUsersWithBookableSpaces( + @Query() dto: UsersWithBookableSpacesFilterDto, + ): Promise { + return this.userService.getUsersWithBookableSpaces(dto); + } + @ApiBearerAuth() @UseGuards(JwtAuthGuard) @Get(':userUuid') diff --git a/src/users/dtos/users-with-bookable-spaces-filter.dto.ts b/src/users/dtos/users-with-bookable-spaces-filter.dto.ts new file mode 100644 index 0000000..3e2ee33 --- /dev/null +++ b/src/users/dtos/users-with-bookable-spaces-filter.dto.ts @@ -0,0 +1,21 @@ +import { PaginationRequestGetListDto } from '@app/common/dto/pagination.request.dto'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsOptional, ValidateIf } from 'class-validator'; + +export class UsersWithBookableSpacesFilterDto extends PaginationRequestGetListDto { + @ApiProperty({ + enum: ['accessStartDate', 'accessEndDate'], + required: false, + }) + @IsEnum(['accessStartDate', 'accessEndDate']) + @ValidateIf((o) => o.sortDirection) + sortBy: 'accessStartDate' | 'accessEndDate'; + + @ApiProperty({ + enum: ['asc', 'desc'], + required: false, + }) + @IsEnum(['asc', 'desc']) + @IsOptional() + sortDirection: 'asc' | 'desc'; +} diff --git a/src/users/services/user.service.ts b/src/users/services/user.service.ts index 89c06f8..d015973 100644 --- a/src/users/services/user.service.ts +++ b/src/users/services/user.service.ts @@ -1,15 +1,18 @@ +import { PageResponse } from '@app/common/dto/pagination.response.dto'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { removeBase64Prefix } from '@app/common/helper/removeBase64Prefix'; import { RegionRepository } from '@app/common/modules/region/repositories'; import { TimeZoneRepository } from '@app/common/modules/timezone/repositories'; import { UserEntity } from '@app/common/modules/user/entities'; import { UserRepository } from '@app/common/modules/user/repositories'; +import { getPaginationResponseDto } from '@app/common/util/getPaginationResponseDto'; import { BadRequestException, HttpException, HttpStatus, Injectable, } from '@nestjs/common'; +import { UsersWithBookableSpacesFilterDto } from '../dtos/users-with-bookable-spaces-filter.dto'; import { UpdateNameDto, UpdateProfilePictureDataDto, @@ -63,6 +66,38 @@ export class UserService { } } + async getUsersWithBookableSpaces({ + sortBy, + sortDirection, + page, + size, + }: UsersWithBookableSpacesFilterDto) { + size = size ?? 10; + page = page ?? 1; + const qb = this.userRepository + .createQueryBuilder('user') + .innerJoin('user.userSpaces', 'userSpaces') + .innerJoin('userSpaces.space', 'space') + .innerJoin('space.bookableConfig', 'bookableConfig') + .where('bookableConfig.uuid IS NOT NULL') + .leftJoinAndSelect('user.inviteUser', 'inviteUser') + .take(size) + .skip((page - 1) * size) + .distinct(true); + if (sortBy) { + qb.orderBy( + ':sortBy', + sortDirection == 'desc' ? 'DESC' : 'ASC', + ).setParameter('sortBy', sortBy); + } + + const [data, count] = await qb.getManyAndCount(); + return new PageResponse( + { message: 'users fetched successfully', data }, + getPaginationResponseDto(count, page, size), + ); + } + async updateProfilePictureByUserUuid( userUuid: string, updateProfilePictureDataDto: UpdateProfilePictureDataDto,