diff --git a/.env.example b/.env.example index 6627107..ed4abb5 100644 --- a/.env.example +++ b/.env.example @@ -41,3 +41,10 @@ STRIPE_API_VERSION='2023-10-16' FORGOT_PASSWORD_RESET_TOKEN_KEY= FORGOT_PASSWORD_RESET_TOKEN_EXPIRY= HASHING_LIMIT=72 # bcrypt has hashing limit of 72 characters + +GOOGLE_CLIENT_ID= +GOOGLE_SECRET= + +CACHE_TIMEOUT= # time after which the cache will be deleted, in milliseconds +CACHING_PAGES= # pages which will be cached on the cache warming +CACHING_PAGES_LIMIT= # number of accommodations per page, to chaching \ No newline at end of file diff --git a/src/accommodation/accommodation.controller.ts b/src/accommodation/accommodation.controller.ts index dde8198..2f3ba9c 100644 --- a/src/accommodation/accommodation.controller.ts +++ b/src/accommodation/accommodation.controller.ts @@ -46,6 +46,7 @@ import { AccommodationService } from './accommodation.service'; import { MediaAllDto } from './dto/accommodation-media.dto'; import AccommodationResponseDto, { AccommodationDto } from './dto/accommodation-response.dto'; import CreateAccommodationDto from './dto/create-accommodation.dto'; +import AccommodationBookingsDto from './dto/get-accommodation-bookings.dto'; import { OrderAndFilterReviewDto } from './dto/get-review.dto'; import { GetUserAccommodationsDto } from './dto/get-user-accommodations.dto'; import ListOfAccommodationsResponseDto from './dto/list-of-accommodations.dto'; @@ -490,4 +491,43 @@ export class AccommodationController { return { succes: true, data: media }; } + + @ApiOperation({ summary: 'Get all reservations, for one accommodation' }) + @ApiResponse({ + status: 200, + description: 'All reservations', + type: AccommodationResponseDto, + }) + @ApiUnauthorizedResponse({ + status: 401, + description: 'Unauthorized', + }) + @ApiResponse({ + status: 404, + description: 'Not found', + }) + @ApiResponse({ + status: 500, + description: 'Internal Server Error', + }) + @ApiParam({ + name: 'id', + type: String, + description: 'Accommodation id', + required: true, + }) + @ApiBearerAuth() + @UseGuards(UserGuard) + @Get('/:id/reservations') + async getAllReservations( + @Param('id') id: string, + @CurrentUser('id') userId: string, + @Query() options: AccommodationBookingsDto + ) { + const data = await this.accommodationService.getAllReservations(id, userId, options); + return { + succes: true, + data, + }; + } } diff --git a/src/accommodation/accommodation.service.ts b/src/accommodation/accommodation.service.ts index 30a9e38..a106e61 100644 --- a/src/accommodation/accommodation.service.ts +++ b/src/accommodation/accommodation.service.ts @@ -18,6 +18,7 @@ import { GlobalException } from 'src/exceptions/global.exception'; import { normalizeCityName } from 'src/helpers/normalizeCityName.helper'; import { normalizeCountryName } from 'src/helpers/normalizeCountryName.helper'; import { PrismaService } from 'src/prisma/prisma.service'; +import AccommodationBookingsDto from './dto/get-accommodation-bookings.dto'; import { OrderAndFilterReviewDto, reviewOrderBy } from './dto/get-review.dto'; import { GetUserAccommodationsDto } from './dto/get-user-accommodations.dto'; import { OrderAndFilterDto, OrderBy } from './dto/orderAndFilter.dto'; @@ -34,6 +35,11 @@ interface UploadImageResponse { }; } +type BookingWhereInput = { + accommodationId: string; + OR?: ({ startDate: { gte: string } } | { endDate: { gte: string } })[]; +}; + const { ACCOMMODATION_MAX_PRICE, ACCOMMODATION_MAX_PEOPLE, @@ -818,6 +824,89 @@ export class AccommodationService { } } + async getAllReservations( + accommodationId: string, + userId: string, + options: AccommodationBookingsDto + ) { + try { + const { currentMonth, nextMonth, orderByStartDate } = options; + + const accommodation = await this.prisma.accommodation.findUnique({ + include: { booking: true }, + where: { + id: accommodationId, + ownerId: userId, + }, + }); + + if (!accommodation) throw new NotFoundException(ErrorsTypes.NOT_FOUND_ACCOMMODATION); + + const findBookingQuery = { + select: { + id: true, + userId: true, + startDate: true, + endDate: true, + status: true, + user: { + select: { + firstName: true, + lastName: true, + profile: { + select: { + imageUrl: true, + }, + }, + }, + }, + payment: { + select: { + totalAmount: true, + }, + }, + }, + + where: { + accommodationId: accommodation.id, + } as BookingWhereInput, + }; + + if (currentMonth && nextMonth) { + currentMonth.setDate(1); + currentMonth.setUTCHours(0, 0, 0, 0); + + nextMonth.setDate(1); + nextMonth.setUTCHours(0, 0, 0, 0); + + findBookingQuery.where = { + accommodationId: accommodation.id, + OR: [ + { + startDate: { + gte: currentMonth.toISOString(), + }, + }, + { + endDate: { + gte: currentMonth.toISOString(), + }, + }, + ], + }; + } + + const data = await this.prisma.booking.findMany({ + ...findBookingQuery, + orderBy: { startDate: orderByStartDate }, + }); + return data; + } catch (error) { + if (error instanceof HttpException) throw error; + throw new GlobalException(ErrorsTypes.ACCOMMODATION_FAILED_TO_GET, error.message); + } + } + private getCountByRating(ratingCounts: { rating: number; _count: number }[]) { const countByRating: Record = {}; diff --git a/src/accommodation/dto/get-accommodation-bookings.dto.ts b/src/accommodation/dto/get-accommodation-bookings.dto.ts new file mode 100644 index 0000000..bd9ffcf --- /dev/null +++ b/src/accommodation/dto/get-accommodation-bookings.dto.ts @@ -0,0 +1,19 @@ +import { Transform } from 'class-transformer'; +import { IsDate, IsEnum, IsOptional } from 'class-validator'; +import { SortOrder } from 'src/enums/sortOrder.enum'; + +export default class AccommodationBookingsDto { + @IsOptional() + @Transform(({ value }) => new Date(value)) + @IsDate() + currentMonth?: Date; + + @IsOptional() + @Transform(({ value }) => new Date(value)) + @IsDate() + nextMonth?: Date; + + @IsEnum(SortOrder) + @IsOptional() + public orderByStartDate?: SortOrder; +}