diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 40e4874..098bcdd 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -414,7 +414,7 @@ model Slots { mentor Mentor @relation(fields: [mentorId], references: [userId]) lockedBy String user User @relation(fields: [lockedBy], references: [id]) - date DateTime + date DateTime @db.Date startTime Int endTime Int expiresAt DateTime diff --git a/src/application/useCases/booking/CreateBookingIntent.usecase.ts b/src/application/useCases/booking/CreateBookingIntent.usecase.ts index ee29534..93723b6 100644 --- a/src/application/useCases/booking/CreateBookingIntent.usecase.ts +++ b/src/application/useCases/booking/CreateBookingIntent.usecase.ts @@ -5,6 +5,7 @@ import type { IPaymentService } from "@application/ports/services/IPaymentServic import type { ICreateBookingIntentUsecase } from "@application/ports/usecase/booking/ICreateBookingIntent.usecase"; import type { CreateOrderResponse } from "@application/types/PaymentService.types"; import { TYPES } from "@config/DI-container/TYPES"; +import { ConflictError } from "@domain/errors/ConflictError"; import { DayOfWeekIndex } from "@domain/mentor/entities/availability.entity"; import { inject, injectable } from "inversify"; @injectable() @@ -31,17 +32,26 @@ export class CreateBookingIntentUsecase implements ICreateBookingIntentUsecase { ); if (!availability) { - throw new Error("Requested time slot is not available."); + throw new ConflictError("Mentor is unavailable for the requested time"); } + console.log("date", data.date.toLocaleDateString("en-CA")); + // store the day with same time to avoid issues with data comparison + const date = new Date( + `${data.date.toLocaleDateString("en-CA")}T00:00:00.000Z`, + ); + console.log(date); + const slot = await this._slotRepository.checkOverlap( data.mentorId, - data.date, + date, data.startTime, data.endTime, ); - + console.log("slot", slot); if (slot) { - throw new Error("Requested time slot overlaps with an existing booking."); + throw new ConflictError( + "Requested time slot overlaps with an existing booking.", + ); } const order = await this._paymentService.createOrder({ @@ -50,6 +60,7 @@ export class CreateBookingIntentUsecase implements ICreateBookingIntentUsecase { }); const finalData = { ...data, + date, orderId: order.orderId, provider: this._paymentService.getPaymentProviderName(), }; diff --git a/src/domain/errors/ConflictError.ts b/src/domain/errors/ConflictError.ts new file mode 100644 index 0000000..e775fda --- /dev/null +++ b/src/domain/errors/ConflictError.ts @@ -0,0 +1,12 @@ +import { DomainError } from "./DomainError.abstract"; +import { DomainErrorCode } from "./DomainErrorCode"; + +export class ConflictError extends DomainError { + serialize(): { message: string; field?: string }[] { + return [{ message: this.message }]; + } + + constructor(message: string = "Resource already exists") { + super(DomainErrorCode.CONFLICT, message); + } +} diff --git a/src/domain/errors/DomainErrorCode.ts b/src/domain/errors/DomainErrorCode.ts index fd81d59..dd666aa 100644 --- a/src/domain/errors/DomainErrorCode.ts +++ b/src/domain/errors/DomainErrorCode.ts @@ -4,6 +4,7 @@ export const DomainErrorCode = { BAD_REQUEST: "BAD_REQUEST", UNAUTHORIZED: "UNAUTHORIZED", FORBIDDEN: "FORBIDDEN", + CONFLICT: "CONFLICT", } as const; // This extracts the values into a strict TypeScript type diff --git a/src/presentation/constants/httpStatusForErrorCode.ts b/src/presentation/constants/httpStatusForErrorCode.ts index d165b08..e4cc858 100644 --- a/src/presentation/constants/httpStatusForErrorCode.ts +++ b/src/presentation/constants/httpStatusForErrorCode.ts @@ -10,6 +10,7 @@ const errorCodeToHttpStatusMap: Record = { [DomainErrorCode.NOT_FOUND]: HTTPSTATUS.NOT_FOUND, [DomainErrorCode.ALREADY_EXISTS]: HTTPSTATUS.CONFLICT, [AppErrorCode.INVALID_CREDENTIALS]: HTTPSTATUS.BAD_REQUEST, + [DomainErrorCode.CONFLICT]: HTTPSTATUS.CONFLICT, }; export const getHttpStatusForErrorCode = (code: ErrorCode): HTTPSTATUSTYPE => {