add get All api for dashboard & mobile

This commit is contained in:
Mhd Zayd Skaff
2025-07-10 17:46:24 +03:00
parent b026fa593b
commit a585eea329
6 changed files with 224 additions and 80 deletions

View File

@ -103,6 +103,11 @@ export class ControllerRoute {
public static readonly GET_ALL_BOOKINGS_DESCRIPTION =
'This endpoint retrieves all bookings.';
public static readonly GET_MY_BOOKINGS_SUMMARY = 'Get my bookings';
public static readonly GET_MY_BOOKINGS_DESCRIPTION =
'This endpoint retrieves all bookings for the authenticated user.';
};
};
static COMMUNITY = class {

View File

@ -2,11 +2,24 @@ 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 { Body, Controller, Post, Req, UseGuards } from '@nestjs/common';
import {
Body,
Controller,
Get,
Post,
Query,
Req,
UseGuards,
} from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
import { plainToInstance } from 'class-transformer';
import { AdminRoleGuard } from 'src/guards/admin.role.guard';
import { BookingRequestDto } from '../dtos/booking-request.dto';
import { BookingResponseDto } from '../dtos/booking-response.dto';
import { CreateBookingDto } from '../dtos/create-booking.dto';
import { MyBookingRequestDto } from '../dtos/my-booking-request.dto';
import { BookingService } from '../services/booking.service';
@ApiTags('Booking Module')
@ -39,38 +52,56 @@ export class BookingController {
});
}
// @ApiBearerAuth()
// @UseGuards(JwtAuthGuard)
// @Get()
// @ApiOperation({
// summary:
// ControllerRoute.BOOKABLE_SPACES.ACTIONS.GET_ALL_BOOKABLE_SPACES_SUMMARY,
// description:
// ControllerRoute.BOOKABLE_SPACES.ACTIONS
// .GET_ALL_BOOKABLE_SPACES_DESCRIPTION,
// })
// async findAll(
// @Query() query: BookableSpaceRequestDto,
// @Req() req: Request,
// ): Promise<PageResponse<BookableSpaceResponseDto>> {
// const project = req['user']?.project?.uuid;
// if (!project) {
// throw new Error('Project UUID is required in the request');
// }
// const { data, pagination } = await this.bookableSpaceService.findAll(
// query,
// project,
// );
// return new PageResponse<BookableSpaceResponseDto>(
// {
// data: data.map((space) =>
// plainToInstance(BookableSpaceResponseDto, space, {
// excludeExtraneousValues: true,
// }),
// ),
// message: 'Successfully fetched all bookable spaces',
// },
// pagination,
// );
// }
@ApiBearerAuth()
@UseGuards(AdminRoleGuard)
@Get()
@ApiOperation({
summary: ControllerRoute.BOOKING.ACTIONS.GET_ALL_BOOKINGS_SUMMARY,
description: ControllerRoute.BOOKING.ACTIONS.GET_ALL_BOOKINGS_DESCRIPTION,
})
async findAll(
@Query() query: BookingRequestDto,
@Req() req: Request,
): Promise<BaseResponseDto> {
const project = req['user']?.project?.uuid;
if (!project) {
throw new Error('Project UUID is required in the request');
}
const result = await this.bookingService.findAll(query, project);
return new SuccessResponseDto({
data: plainToInstance(BookingResponseDto, result, {
excludeExtraneousValues: true,
}),
message: 'Successfully fetched all bookings',
});
}
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Get('my-bookings')
@ApiOperation({
summary: ControllerRoute.BOOKING.ACTIONS.GET_MY_BOOKINGS_SUMMARY,
description: ControllerRoute.BOOKING.ACTIONS.GET_MY_BOOKINGS_DESCRIPTION,
})
async findMyBookings(
@Query() query: MyBookingRequestDto,
@Req() req: Request,
): Promise<BaseResponseDto> {
const userUuid = req['user']?.uuid;
const project = req['user']?.project?.uuid;
if (!project) {
throw new Error('Project UUID is required in the request');
}
const result = await this.bookingService.findMyBookings(
query,
userUuid,
project,
);
return new SuccessResponseDto({
data: plainToInstance(BookingResponseDto, result, {
excludeExtraneousValues: true,
}),
message: 'Successfully fetched all bookings',
});
}
}

View File

@ -0,0 +1,14 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, Matches } from 'class-validator';
export class BookingRequestDto {
@ApiProperty({
description: 'Month in MM/YYYY format',
example: '07/2025',
})
@IsNotEmpty()
@Matches(/^(0[1-9]|1[0-2])\/\d{4}$/, {
message: 'Date must be in MM/YYYY format',
})
month: string;
}

View File

@ -0,0 +1,90 @@
import { ApiProperty } from '@nestjs/swagger';
import { Expose, Transform, Type } from 'class-transformer';
export class BookingUserResponseDto {
@ApiProperty()
@Expose()
uuid: string;
@ApiProperty()
@Expose()
firstName: string;
@ApiProperty()
@Expose()
lastName: string;
@ApiProperty({
type: String,
nullable: true,
})
@Expose()
email: string;
@ApiProperty({
type: String,
nullable: true,
})
@Expose()
@Transform(({ obj }) => {
return {
companyName: obj.inviteUser?.companyName || null,
};
})
@ApiProperty({
type: String,
nullable: true,
})
@Expose()
phoneNumber: string;
}
export class BookingSpaceResponseDto {
@ApiProperty()
@Expose()
uuid: string;
@ApiProperty()
@Expose()
spaceName: string;
}
export class BookingResponseDto {
@ApiProperty()
@Expose()
uuid: string;
@ApiProperty({
type: Date,
})
@Expose()
date: Date;
@ApiProperty()
@Expose()
startTime: string;
@ApiProperty()
@Expose()
endTime: string;
@ApiProperty({
type: Number,
})
@Expose()
cost: number;
@ApiProperty({
type: BookingUserResponseDto,
})
@Type(() => BookingUserResponseDto)
@Expose()
user: BookingUserResponseDto;
@ApiProperty({
type: BookingSpaceResponseDto,
})
@Type(() => BookingSpaceResponseDto)
@Expose()
space: BookingSpaceResponseDto;
}

View File

@ -0,0 +1,14 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsIn, IsOptional } from 'class-validator';
export class MyBookingRequestDto {
@ApiProperty({
description: 'Filter bookings by time period',
example: 'past',
enum: ['past', 'future'],
required: false,
})
@IsOptional()
@IsIn(['past', 'future'])
when?: 'past' | 'future';
}

View File

@ -11,7 +11,10 @@ import {
Injectable,
NotFoundException,
} from '@nestjs/common';
import { Between } from 'typeorm/find-options/operator/Between';
import { BookingRequestDto } from '../dtos/booking-request.dto';
import { CreateBookingDto } from '../dtos/create-booking.dto';
import { MyBookingRequestDto } from '../dtos/my-booking-request.dto';
@Injectable()
export class BookingService {
@ -46,52 +49,39 @@ export class BookingService {
return this.createBookings(space, userUuid, dto);
}
// async findAll(
// { active, page, size, configured, search }: BookingRequestDto,
// project: string,
// ): Promise<{
// data: BaseResponseDto['data'];
// pagination: PageResponseDto;
// }> {
// let qb = this.spaceRepository
// .createQueryBuilder('space')
// .leftJoinAndSelect('space.parent', 'parentSpace')
// .leftJoinAndSelect('space.community', 'community')
// .where('community.project = :project', { project });
async findAll({ month }: BookingRequestDto, project: string) {
const [monthNumber, year] = month.split('/').map(Number);
const fromDate = new Date(year, monthNumber - 1, 1);
const toDate = new Date(year, monthNumber, 0, 23, 59, 59);
return this.bookingEntityRepository.find({
where: {
space: { community: { project: { uuid: project } } },
date: Between(fromDate, toDate),
},
relations: ['space', 'user', 'user.inviteUser'],
order: { date: 'DESC' },
});
}
// 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')
// .andWhere('bookableConfig.uuid IS NOT NULL');
// if (active !== undefined) {
// qb = qb.andWhere('bookableConfig.active = :active', { active });
// }
// } else {
// qb = qb
// .leftJoinAndSelect('space.bookableConfig', 'bookableConfig')
// .andWhere('bookableConfig.uuid IS NULL');
// }
// const customModel = TypeORMCustomModel(this.spaceRepository);
// const { baseResponseDto, paginationResponseDto } =
// await customModel.findAll({ page, size, modelName: 'space' }, qb);
// return {
// data: baseResponseDto.data.map((space) => {
// return {
// ...space,
// virtualLocation: `${space.community?.name} - ${space.parent ? space.parent?.spaceName + ' - ' : ''}${space.spaceName}`,
// };
// }),
// pagination: paginationResponseDto,
// };
// }
async findMyBookings(
{ when }: MyBookingRequestDto,
userUuid: string,
project: string,
) {
return this.bookingEntityRepository.find({
where: {
user: { uuid: userUuid },
space: { community: { project: { uuid: project } } },
// date: when
// ? when === 'past'
// ? LessThanOrEqual(new Date())
// : MoreThanOrEqual(new Date())
// : undefined,
},
relations: ['space', 'user'],
order: { date: 'DESC' },
});
}
/**
* Fetch space by UUID and throw an error if not found or if not configured for booking