diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/bookings.repository.ts b/apps/api/v2/src/ee/bookings/2024-08-13/bookings.repository.ts index fea95d8b84..6e430a0776 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/bookings.repository.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/bookings.repository.ts @@ -14,7 +14,7 @@ export class BookingsRepository_2024_08_13 { }); } - async getByIdsWithAttendeesAndUser(ids: number[]) { + async getByIdsWithAttendeesAndUserAndEvent(ids: number[]) { return this.dbRead.prisma.booking.findMany({ where: { id: { @@ -24,6 +24,7 @@ export class BookingsRepository_2024_08_13 { include: { attendees: true, user: true, + eventType: true, }, }); } @@ -36,7 +37,7 @@ export class BookingsRepository_2024_08_13 { }); } - async getByIdWithAttendeesAndUser(id: number) { + async getByIdWithAttendeesAndUserAndEvent(id: number) { return this.dbRead.prisma.booking.findUnique({ where: { id, @@ -44,11 +45,12 @@ export class BookingsRepository_2024_08_13 { include: { attendees: true, user: true, + eventType: true, }, }); } - async getByUidWithAttendeesAndUser(uid: string) { + async getByUidWithAttendeesAndUserAndEvent(uid: string) { return this.dbRead.prisma.booking.findUnique({ where: { uid, @@ -56,11 +58,12 @@ export class BookingsRepository_2024_08_13 { include: { attendees: true, user: true, + eventType: true, }, }); } - async getRecurringByUidWithAttendeesAndUser(uid: string) { + async getRecurringByUidWithAttendeesAndUserAndEvent(uid: string) { return this.dbRead.prisma.booking.findMany({ where: { recurringEventId: uid, @@ -68,6 +71,7 @@ export class BookingsRepository_2024_08_13 { include: { attendees: true, user: true, + eventType: true, }, }); } diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/controllers/bookings.controller.ts b/apps/api/v2/src/ee/bookings/2024-08-13/controllers/bookings.controller.ts index 6dae1f40c5..a967f9d5ad 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/controllers/bookings.controller.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/controllers/bookings.controller.ts @@ -1,8 +1,6 @@ import { BookingUidGuard } from "@/ee/bookings/2024-08-13/guards/booking-uid.guard"; import { CancelBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/cancel-booking.output"; import { CreateBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/create-booking.output"; -import { GetBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/get-booking.output"; -import { GetBookingsOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/get-bookings.output"; import { MarkAbsentBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/mark-absent.output"; import { RescheduleBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/reschedule-booking.output"; import { BookingsService_2024_08_13 } from "@/ee/bookings/2024-08-13/services/bookings.service"; @@ -36,6 +34,7 @@ import { User } from "@prisma/client"; import { Request } from "express"; import { BOOKING_READ, BOOKING_WRITE, SUCCESS_STATUS } from "@calcom/platform-constants"; +import { GetBookingOutput_2024_08_13, GetBookingsOutput_2024_08_13 } from "@calcom/platform-types"; import { CreateBookingInputPipe, CreateBookingInput, diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/controllers/team-bookings.controller.e2e-spec.ts b/apps/api/v2/src/ee/bookings/2024-08-13/controllers/team-bookings.controller.e2e-spec.ts index 1f3139e60c..6a6cf93032 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/controllers/team-bookings.controller.e2e-spec.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/controllers/team-bookings.controller.e2e-spec.ts @@ -1,7 +1,6 @@ import { bootstrap } from "@/app"; import { AppModule } from "@/app.module"; import { CreateBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/create-booking.output"; -import { GetBookingsOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/get-bookings.output"; import { CreateScheduleInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input"; import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module"; import { SchedulesService_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/services/schedules.service"; @@ -29,6 +28,7 @@ import { CreateBookingInput_2024_08_13, BookingOutput_2024_08_13, RecurringBookingOutput_2024_08_13, + GetBookingsOutput_2024_08_13, } from "@calcom/platform-types"; import { PlatformOAuthClient, Team } from "@calcom/prisma/client"; diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/controllers/user-bookings.controller.e2e-spec.ts b/apps/api/v2/src/ee/bookings/2024-08-13/controllers/user-bookings.controller.e2e-spec.ts index 1203c8bf6a..cabca2c978 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/controllers/user-bookings.controller.e2e-spec.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/controllers/user-bookings.controller.e2e-spec.ts @@ -2,8 +2,6 @@ import { bootstrap } from "@/app"; import { AppModule } from "@/app.module"; import { CancelBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/cancel-booking.output"; import { CreateBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/create-booking.output"; -import { GetBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/get-booking.output"; -import { GetBookingsOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/get-bookings.output"; import { MarkAbsentBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/mark-absent.output"; import { RescheduleBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/reschedule-booking.output"; import { CreateScheduleInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input"; @@ -30,6 +28,7 @@ import { VERSION_2024_08_13, X_CAL_CLIENT_ID, } from "@calcom/platform-constants"; +import { GetBookingOutput_2024_08_13, GetBookingsOutput_2024_08_13 } from "@calcom/platform-types"; import { CreateBookingInput_2024_08_13, BookingOutput_2024_08_13, @@ -58,6 +57,7 @@ describe("Bookings Endpoints 2024-08-13", () => { let user: User; let eventTypeId: number; + const eventTypeSlug = "peer-coding"; let recurringEventTypeId: number; let createdBooking: BookingOutput_2024_08_13; @@ -100,7 +100,7 @@ describe("Bookings Endpoints 2024-08-13", () => { }; await schedulesService.createUserSchedule(user.id, userSchedule); const event = await eventTypesRepositoryFixture.create( - { title: "peer coding", slug: "peer-coding", length: 60 }, + { title: "peer coding", slug: eventTypeSlug, length: 60 }, user.id ); eventTypeId = event.id; @@ -184,7 +184,10 @@ describe("Bookings Endpoints 2024-08-13", () => { timeZone: "Europe/Rome", language: "it", }, - meetingUrl: "https://meet.google.com/abc-def-ghi", + location: "https://meet.google.com/abc-def-ghi", + bookingFieldsResponses: { + customField: "customValue", + }, }; return request(app.getHttpServer()) @@ -208,14 +211,24 @@ describe("Bookings Endpoints 2024-08-13", () => { expect(data.end).toEqual(new Date(Date.UTC(2030, 0, 8, 14, 0, 0)).toISOString()); expect(data.duration).toEqual(60); expect(data.eventTypeId).toEqual(eventTypeId); + expect(data.eventType).toEqual({ + id: eventTypeId, + slug: eventTypeSlug, + }); expect(data.attendees[0]).toEqual({ name: body.attendee.name, timeZone: body.attendee.timeZone, language: body.attendee.language, absent: false, }); - expect(data.meetingUrl).toEqual(body.meetingUrl); + expect(data.location).toEqual(body.location); + expect(data.meetingUrl).toEqual(body.location); expect(data.absentHost).toEqual(false); + expect(data.bookingFieldsResponses).toEqual({ + name: body.attendee.name, + email: body.attendee.email, + ...body.bookingFieldsResponses, + }); createdBooking = data; } else { throw new Error( @@ -235,7 +248,7 @@ describe("Bookings Endpoints 2024-08-13", () => { timeZone: "Europe/Rome", language: "it", }, - meetingUrl: "https://meet.google.com/abc-def-ghi", + location: "https://meet.google.com/abc-def-ghi", }; return request(app.getHttpServer()) @@ -268,7 +281,7 @@ describe("Bookings Endpoints 2024-08-13", () => { language: body.attendee.language, absent: false, }); - expect(firstBooking.meetingUrl).toEqual(body.meetingUrl); + expect(firstBooking.location).toEqual(body.location); expect(firstBooking.recurringBookingUid).toBeDefined(); expect(firstBooking.absentHost).toEqual(false); @@ -288,7 +301,7 @@ describe("Bookings Endpoints 2024-08-13", () => { language: body.attendee.language, absent: false, }); - expect(secondBooking.meetingUrl).toEqual(body.meetingUrl); + expect(secondBooking.location).toEqual(body.location); expect(secondBooking.absentHost).toEqual(false); const thirdBooking = data[2]; @@ -307,7 +320,7 @@ describe("Bookings Endpoints 2024-08-13", () => { language: body.attendee.language, absent: false, }); - expect(thirdBooking.meetingUrl).toEqual(body.meetingUrl); + expect(thirdBooking.location).toEqual(body.location); expect(thirdBooking.absentHost).toEqual(false); createdRecurringBooking = data; @@ -343,7 +356,7 @@ describe("Bookings Endpoints 2024-08-13", () => { expect(data.duration).toEqual(createdBooking.duration); expect(data.eventTypeId).toEqual(createdBooking.eventTypeId); expect(data.attendees[0]).toEqual(createdBooking.attendees[0]); - expect(data.meetingUrl).toEqual(createdBooking.meetingUrl); + expect(data.location).toEqual(createdBooking.location); expect(data.absentHost).toEqual(createdBooking.absentHost); } else { throw new Error( @@ -377,7 +390,7 @@ describe("Bookings Endpoints 2024-08-13", () => { expect(data.eventTypeId).toEqual(createdRecurringBooking[0].eventTypeId); expect(data.recurringBookingUid).toEqual(createdRecurringBooking[0].recurringBookingUid); expect(data.attendees[0]).toEqual(createdRecurringBooking[0].attendees[0]); - expect(data.meetingUrl).toEqual(createdRecurringBooking[0].meetingUrl); + expect(data.location).toEqual(createdRecurringBooking[0].location); expect(data.absentHost).toEqual(createdRecurringBooking[0].absentHost); } else { throw new Error( @@ -414,7 +427,7 @@ describe("Bookings Endpoints 2024-08-13", () => { expect(firstBooking.eventTypeId).toEqual(createdRecurringBooking[0].eventTypeId); expect(firstBooking.recurringBookingUid).toEqual(recurringBookingUid); expect(firstBooking.attendees[0]).toEqual(createdRecurringBooking[0].attendees[0]); - expect(firstBooking.meetingUrl).toEqual(createdRecurringBooking[0].meetingUrl); + expect(firstBooking.location).toEqual(createdRecurringBooking[0].location); expect(firstBooking.absentHost).toEqual(createdRecurringBooking[0].absentHost); const secondBooking = data[1]; @@ -428,7 +441,7 @@ describe("Bookings Endpoints 2024-08-13", () => { expect(secondBooking.eventTypeId).toEqual(createdRecurringBooking[1].eventTypeId); expect(secondBooking.recurringBookingUid).toEqual(recurringBookingUid); expect(secondBooking.attendees[0]).toEqual(createdRecurringBooking[1].attendees[0]); - expect(secondBooking.meetingUrl).toEqual(createdRecurringBooking[1].meetingUrl); + expect(secondBooking.location).toEqual(createdRecurringBooking[1].location); expect(secondBooking.absentHost).toEqual(createdRecurringBooking[1].absentHost); const thirdBooking = data[2]; @@ -442,7 +455,7 @@ describe("Bookings Endpoints 2024-08-13", () => { expect(thirdBooking.eventTypeId).toEqual(createdRecurringBooking[2].eventTypeId); expect(thirdBooking.recurringBookingUid).toEqual(recurringBookingUid); expect(thirdBooking.attendees[0]).toEqual(createdRecurringBooking[2].attendees[0]); - expect(thirdBooking.meetingUrl).toEqual(createdRecurringBooking[2].meetingUrl); + expect(thirdBooking.location).toEqual(createdRecurringBooking[2].location); expect(thirdBooking.absentHost).toEqual(createdRecurringBooking[2].absentHost); createdRecurringBooking = data; @@ -764,7 +777,7 @@ describe("Bookings Endpoints 2024-08-13", () => { expect(data.duration).toEqual(createdBooking.duration); expect(data.eventTypeId).toEqual(createdBooking.eventTypeId); expect(data.attendees[0]).toEqual(createdBooking.attendees[0]); - expect(data.meetingUrl).toEqual(createdBooking.meetingUrl); + expect(data.location).toEqual(createdBooking.location); expect(data.absentHost).toEqual(createdBooking.absentHost); rescheduledBooking = data; @@ -815,7 +828,7 @@ describe("Bookings Endpoints 2024-08-13", () => { expect(data.recurringBookingUid).toEqual(createdRecurringBooking[0].recurringBookingUid); expect(data.eventTypeId).toEqual(createdRecurringBooking[0].eventTypeId); expect(data.attendees[0]).toEqual(createdRecurringBooking[0].attendees[0]); - expect(data.meetingUrl).toEqual(createdRecurringBooking[0].meetingUrl); + expect(data.location).toEqual(createdRecurringBooking[0].location); expect(data.absentHost).toEqual(createdRecurringBooking[0].absentHost); const oldBooking = await bookingsRepositoryFixture.getByUid(createdRecurringBooking[0].uid); @@ -885,7 +898,7 @@ describe("Bookings Endpoints 2024-08-13", () => { expect(data.duration).toEqual(booking.duration); expect(data.eventTypeId).toEqual(booking.eventTypeId); expect(data.attendees[0]).toEqual(booking.attendees[0]); - expect(data.meetingUrl).toEqual(booking.meetingUrl); + expect(data.location).toEqual(booking.location); }); }); @@ -918,7 +931,7 @@ describe("Bookings Endpoints 2024-08-13", () => { expect(data.eventTypeId).toEqual(booking.eventTypeId); expect(data.attendees[0].absent).toEqual(true); expect(data.absentHost).toEqual(booking.absentHost); - expect(data.meetingUrl).toEqual(booking.meetingUrl); + expect(data.location).toEqual(booking.location); }); }); }); @@ -955,7 +968,7 @@ describe("Bookings Endpoints 2024-08-13", () => { expect(data.duration).toEqual(rescheduledBooking.duration); expect(data.eventTypeId).toEqual(rescheduledBooking.eventTypeId); expect(data.attendees[0]).toEqual(rescheduledBooking.attendees[0]); - expect(data.meetingUrl).toEqual(rescheduledBooking.meetingUrl); + expect(data.location).toEqual(rescheduledBooking.location); expect(data.absentHost).toEqual(rescheduledBooking.absentHost); const cancelledBooking = await bookingsRepositoryFixture.getByUid(rescheduledBooking.uid); diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts b/apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts index e9956fcbe4..2ba8af9c27 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts @@ -73,7 +73,9 @@ export class BookingsService_2024_08_13 { const bookingRequest = await this.inputService.createBookingRequest(request, body); const booking = await handleInstantMeeting(bookingRequest); - const databaseBooking = await this.bookingsRepository.getByIdWithAttendeesAndUser(booking.bookingId); + const databaseBooking = await this.bookingsRepository.getByIdWithAttendeesAndUserAndEvent( + booking.bookingId + ); if (!databaseBooking) { throw new Error(`Booking with id=${booking.bookingId} was not found in the database`); } @@ -94,7 +96,7 @@ export class BookingsService_2024_08_13 { throw new Error("Recurring booking was not created"); } - const recurringBooking = await this.bookingsRepository.getRecurringByUidWithAttendeesAndUser(uid); + const recurringBooking = await this.bookingsRepository.getRecurringByUidWithAttendeesAndUserAndEvent(uid); return this.outputService.getOutputRecurringBookings(recurringBooking); } @@ -106,7 +108,7 @@ export class BookingsService_2024_08_13 { throw new Error("Booking was not created"); } - const databaseBooking = await this.bookingsRepository.getByIdWithAttendeesAndUser(booking.id); + const databaseBooking = await this.bookingsRepository.getByIdWithAttendeesAndUserAndEvent(booking.id); if (!databaseBooking) { throw new Error(`Booking with id=${booking.id} was not found in the database`); } @@ -115,7 +117,7 @@ export class BookingsService_2024_08_13 { } async getBooking(uid: string) { - const booking = await this.bookingsRepository.getByUidWithAttendeesAndUser(uid); + const booking = await this.bookingsRepository.getByUidWithAttendeesAndUserAndEvent(uid); if (booking) { const isRecurring = !!booking.recurringEventId; @@ -125,7 +127,7 @@ export class BookingsService_2024_08_13 { return this.outputService.getOutputBooking(booking); } - const recurringBooking = await this.bookingsRepository.getRecurringByUidWithAttendeesAndUser(uid); + const recurringBooking = await this.bookingsRepository.getRecurringByUidWithAttendeesAndUserAndEvent(uid); if (!recurringBooking.length) { throw new NotFoundException(`Booking with uid=${uid} was not found in the database`); } @@ -149,7 +151,7 @@ export class BookingsService_2024_08_13 { // note(Lauris): fetchedBookings don't have attendees information and responses and i don't want to add them to the handler query, // because its used elsewhere in code that does not need that information, so i get ids, fetch bookings and then return them formatted in same order as ids. const ids = fetchedBookings.bookings.map((booking) => booking.id); - const bookings = await this.bookingsRepository.getByIdsWithAttendeesAndUser(ids); + const bookings = await this.bookingsRepository.getByIdsWithAttendeesAndUserAndEvent(ids); const bookingMap = new Map(bookings.map((booking) => [booking.id, booking])); const orderedBookings = ids.map((id) => bookingMap.get(id)); @@ -162,6 +164,7 @@ export class BookingsService_2024_08_13 { const formatted = { ...booking, + eventType: booking.eventType, eventTypeId: booking.eventTypeId, startTime: new Date(booking.startTime), endTime: new Date(booking.endTime), @@ -191,7 +194,7 @@ export class BookingsService_2024_08_13 { throw new Error("Booking was not created"); } - const databaseBooking = await this.bookingsRepository.getByIdWithAttendeesAndUser(booking.id); + const databaseBooking = await this.bookingsRepository.getByIdWithAttendeesAndUserAndEvent(booking.id); if (!databaseBooking) { throw new Error(`Booking with id=${booking.id} was not found in the database`); } @@ -226,7 +229,7 @@ export class BookingsService_2024_08_13 { userId: bookingOwnerId, }); - const booking = await this.bookingsRepository.getByUidWithAttendeesAndUser(bookingUid); + const booking = await this.bookingsRepository.getByUidWithAttendeesAndUserAndEvent(bookingUid); if (!booking) { throw new Error(`Booking with uid=${bookingUid} was not found in the database`); diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts b/apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts index a74123242b..1190a638c3 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts @@ -89,7 +89,7 @@ export class InputBookingsService_2024_08_13 { ? await this.createBookingRequestOAuthClientParams(oAuthClientId) : DEFAULT_PLATFORM_PARAMS; - const location = request.body.meetingUrl; + const location = request.body.location || request.body.meetingUrl; Object.assign(newRequest, { userId, ...oAuthParams, platformBookingLocation: location }); newRequest.body = { ...bodyTransformed, noEmail: !oAuthParams.arePlatformEmailsEnabled }; @@ -147,7 +147,7 @@ export class InputBookingsService_2024_08_13 { ? await this.createBookingRequestOAuthClientParams(oAuthClientId) : DEFAULT_PLATFORM_PARAMS; - const location = request.body.meetingUrl; + const location = request.body.location || request.body.meetingUrl; Object.assign(newRequest, { userId, ...oAuthParams, @@ -257,7 +257,7 @@ export class InputBookingsService_2024_08_13 { } async transformInputRescheduleBooking(bookingUid: string, inputBooking: RescheduleBookingInput_2024_08_13) { - const booking = await this.bookingsRepository.getByUidWithAttendeesAndUser(bookingUid); + const booking = await this.bookingsRepository.getByUidWithAttendeesAndUserAndEvent(bookingUid); if (!booking) { throw new NotFoundException(`Booking with uid=${bookingUid} not found`); } @@ -390,7 +390,9 @@ export class InputBookingsService_2024_08_13 { async transformInputCancelBooking(bookingUid: string, inputBooking: CancelBookingInput_2024_08_13) { let allRemainingBookings = false; let uid = bookingUid; - const recurringBooking = await this.bookingsRepository.getRecurringByUidWithAttendeesAndUser(bookingUid); + const recurringBooking = await this.bookingsRepository.getRecurringByUidWithAttendeesAndUserAndEvent( + bookingUid + ); if (recurringBooking.length) { // note(Lauirs): this means that bookingUid is equal to recurringEventId on individual bookings of recurring one aka main recurring event diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts b/apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts index 27a2318fd3..967f3a3133 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts @@ -7,14 +7,20 @@ import { z } from "zod"; import { BookingOutput_2024_08_13, RecurringBookingOutput_2024_08_13 } from "@calcom/platform-types"; import { Booking } from "@calcom/prisma/client"; -export const bookingResponsesSchema = z.object({ - email: z.string(), - name: z.string(), - guests: z.array(z.string()).optional(), - rescheduledReason: z.string().optional(), -}); +export const bookingResponsesSchema = z + .object({ + email: z.string(), + name: z.string(), + guests: z.array(z.string()).optional(), + rescheduledReason: z.string().optional(), + }) + .passthrough(); type DatabaseBooking = Booking & { + eventType: { + id: number; + slug: string; + } | null; attendees: { name: string; email: string; @@ -38,6 +44,8 @@ export class OutputBookingsService_2024_08_13 { const booking = { id: databaseBooking.id, uid: databaseBooking.uid, + title: databaseBooking.title, + description: databaseBooking.description, hosts: [databaseBooking.user], status: databaseBooking.status.toLowerCase(), cancellationReason: databaseBooking.cancellationReason || undefined, @@ -46,6 +54,8 @@ export class OutputBookingsService_2024_08_13 { start: databaseBooking.startTime, end: databaseBooking.endTime, duration, + eventType: databaseBooking.eventType, + // note(Lauris): eventTypeId is deprecated eventTypeId: databaseBooking.eventTypeId, attendees: databaseBooking.attendees.map((attendee) => ({ name: attendee.name, @@ -55,18 +65,22 @@ export class OutputBookingsService_2024_08_13 { absent: !!attendee.noShow, })), guests: bookingResponses.guests, + location: databaseBooking.location, + // note(Lauris): meetingUrl is deprecated meetingUrl: databaseBooking.location, absentHost: !!databaseBooking.noShowHost, }; - return plainToClass(BookingOutput_2024_08_13, booking, { strategy: "excludeAll" }); + const bookingTransformed = plainToClass(BookingOutput_2024_08_13, booking, { strategy: "excludeAll" }); + bookingTransformed.bookingFieldsResponses = bookingResponses; + return bookingTransformed; } async getOutputRecurringBookings(databaseBookings: DatabaseBooking[]) { const transformed = []; for (const booking of databaseBookings) { - const databaseBooking = await this.bookingsRepository.getByIdWithAttendeesAndUser(booking.id); + const databaseBooking = await this.bookingsRepository.getByIdWithAttendeesAndUserAndEvent(booking.id); if (!databaseBooking) { throw new Error(`Booking with id=${booking.id} was not found in the database`); } @@ -87,6 +101,8 @@ export class OutputBookingsService_2024_08_13 { const booking = { id: databaseBooking.id, uid: databaseBooking.uid, + title: databaseBooking.title, + description: databaseBooking.description, hosts: [databaseBooking.user], status: databaseBooking.status.toLowerCase(), cancellationReason: databaseBooking.cancellationReason || undefined, @@ -95,6 +111,8 @@ export class OutputBookingsService_2024_08_13 { start: databaseBooking.startTime, end: databaseBooking.endTime, duration, + eventType: databaseBooking.eventType, + // note(Lauris): eventTypeId is deprecated eventTypeId: databaseBooking.eventTypeId, attendees: databaseBooking.attendees.map((attendee) => ({ name: attendee.name, @@ -104,9 +122,12 @@ export class OutputBookingsService_2024_08_13 { absent: !!attendee.noShow, })), guests: bookingResponses.guests, + location: databaseBooking.location, + // note(Lauris): meetingUrl is deprecated meetingUrl: databaseBooking.location, recurringBookingUid: databaseBooking.recurringEventId, absentHost: !!databaseBooking.noShowHost, + bookingFieldsResponses: databaseBooking.responses, }; return plainToClass(RecurringBookingOutput_2024_08_13, booking, { strategy: "excludeAll" }); diff --git a/apps/api/v2/swagger/documentation.json b/apps/api/v2/swagger/documentation.json index 36dcbc6d12..de7a64c438 100644 --- a/apps/api/v2/swagger/documentation.json +++ b/apps/api/v2/swagger/documentation.json @@ -1193,7 +1193,7 @@ }, { "name": "eventSlug", - "required": true, + "required": false, "in": "query", "description": "Slug of team event type to return.", "schema": { @@ -3639,7 +3639,7 @@ }, { "name": "username", - "required": true, + "required": false, "in": "query", "description": "The username of the user to get event types for. If only username provided will get all event types.", "schema": { @@ -3648,7 +3648,7 @@ }, { "name": "eventSlug", - "required": true, + "required": false, "in": "query", "description": "Slug of event type to return. Notably, if eventSlug is provided then username must be provided too, because multiple users can have event with same slug.", "schema": { @@ -3657,7 +3657,7 @@ }, { "name": "usernames", - "required": true, + "required": false, "in": "query", "description": "Get dynamic event type for multiple usernames separated by comma. e.g `usernames=alice,bob`", "schema": { @@ -4142,7 +4142,7 @@ } }, "tags": [ - "oauth" + "OAuth" ] } }, @@ -4195,7 +4195,7 @@ } }, "tags": [ - "oauth" + "OAuth" ] } }, @@ -4244,7 +4244,7 @@ } }, "tags": [ - "oauth", + "OAuth", "Managed users" ] } @@ -5036,6 +5036,7 @@ "/v2/stripe/connect": { "get": { "operationId": "StripeController_redirect", + "summary": "Get stripe connect URL", "parameters": [ { "name": "Authorization", @@ -5066,6 +5067,7 @@ "/v2/stripe/save": { "get": { "operationId": "StripeController_save", + "summary": "Save stripe credentials", "parameters": [ { "name": "state", @@ -5104,6 +5106,7 @@ "/v2/stripe/check": { "get": { "operationId": "StripeController_check", + "summary": "Check stripe connection", "parameters": [], "responses": { "200": { @@ -6435,8 +6438,7 @@ }, "required": [ "type", - "value", - "rolling" + "value" ] }, "CalendarDaysWindow_2024_06_14": { @@ -6464,8 +6466,7 @@ }, "required": [ "type", - "value", - "rolling" + "value" ] }, "RangeWindow_2024_06_14": { @@ -6497,6 +6498,227 @@ "value" ] }, + "BaseBookingLimitsCount_2024_06_14": { + "type": "object", + "properties": { + "day": { + "type": "number", + "description": "The number of bookings per day", + "example": 1 + }, + "week": { + "type": "number", + "description": "The number of bookings per week", + "example": 2 + }, + "month": { + "type": "number", + "description": "The number of bookings per month", + "example": 3 + }, + "year": { + "type": "number", + "description": "The number of bookings per year", + "example": 4 + } + } + }, + "Disabled_2024_06_14": { + "type": "object", + "properties": { + "disabled": { + "type": "boolean", + "description": "Indicates if the option is disabled", + "example": true, + "default": false + } + }, + "required": [ + "disabled" + ] + }, + "BaseBookingLimitsDuration_2024_06_14": { + "type": "object", + "properties": { + "day": { + "type": "number", + "description": "The duration of bookings per day (must be a multiple of 15)", + "example": 60 + }, + "week": { + "type": "number", + "description": "The duration of bookings per week (must be a multiple of 15)", + "example": 120 + }, + "month": { + "type": "number", + "description": "The duration of bookings per month (must be a multiple of 15)", + "example": 180 + }, + "year": { + "type": "number", + "description": "The duration of bookings per year (must be a multiple of 15)", + "example": 240 + } + } + }, + "Recurrence_2024_06_14": { + "type": "object", + "properties": { + "interval": { + "type": "number", + "example": 10, + "description": "Repeats every {count} week | month | year" + }, + "occurrences": { + "type": "number", + "example": 10, + "description": "Repeats for a maximum of {count} events" + }, + "frequency": { + "type": "string", + "enum": [ + "yearly", + "monthly", + "weekly" + ] + } + }, + "required": [ + "interval", + "occurrences", + "frequency" + ] + }, + "NoticeThreshold_2024_06_14": { + "type": "object", + "properties": { + "unit": { + "type": "string", + "description": "The unit of time for the notice threshold (e.g., minutes, hours)", + "example": "minutes" + }, + "count": { + "type": "number", + "description": "The time value for the notice threshold", + "example": 30 + } + }, + "required": [ + "unit", + "count" + ] + }, + "BaseConfirmationPolicy_2024_06_14": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "The policy that determines when confirmation is required", + "example": "always" + }, + "noticeThreshold": { + "description": "The notice threshold required before confirmation is needed. Required when type is 'time'.", + "allOf": [ + { + "$ref": "#/components/schemas/NoticeThreshold_2024_06_14" + } + ] + } + }, + "required": [ + "type" + ] + }, + "Seats_2024_06_14": { + "type": "object", + "properties": { + "seatsPerTimeSlot": { + "type": "number", + "description": "Number of seats available per time slot", + "example": 4 + }, + "showAttendeeInfo": { + "type": "boolean", + "description": "Show attendee information to other guests", + "example": true + }, + "showAvailabilityCount": { + "type": "boolean", + "description": "Display the count of available seats", + "example": true + } + }, + "required": [ + "seatsPerTimeSlot", + "showAttendeeInfo", + "showAvailabilityCount" + ] + }, + "BookerLayouts_2024_06_14": { + "type": "object", + "properties": { + "defaultLayout": { + "type": "string", + "enum": [ + "month", + "week", + "column" + ] + }, + "enabledLayouts": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "month", + "week", + "column" + ] + } + } + }, + "required": [ + "defaultLayout", + "enabledLayouts" + ] + }, + "EventTypeColor_2024_06_14": { + "type": "object", + "properties": { + "lightThemeHex": { + "type": "string", + "description": "Color used for event types in light theme", + "example": "#292929" + }, + "darkThemeHex": { + "type": "string", + "description": "Color used for event types in dark theme", + "example": "#fafafa" + } + }, + "required": [ + "lightThemeHex", + "darkThemeHex" + ] + }, + "DestinationCalendar_2024_06_14": { + "type": "object", + "properties": { + "integration": { + "type": "string", + "description": "The integration type of the destination calendar. Refer to the /api/v2/calendars endpoint to retrieve the integration type of your connected calendars." + }, + "externalId": { + "type": "string", + "description": "The external ID of the destination calendar. Refer to the /api/v2/calendars endpoint to retrieve the external IDs of your connected calendars." + } + }, + "required": [ + "integration", + "externalId" + ] + }, "CreateEventTypeInput_2024_06_14": { "type": "object", "properties": { @@ -6602,63 +6824,124 @@ "description": "If you want that this event has different schedule than user's default one you can specify it here." }, "bookingLimitsCount": { - "type": "object", - "description": "Limit how many times this event can be booked" + "description": "Limit how many times this event can be booked", + "oneOf": [ + { + "$ref": "#/components/schemas/BaseBookingLimitsCount_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] }, "onlyShowFirstAvailableSlot": { "type": "boolean", "description": "This will limit your availability for this event type to one slot per day, scheduled at the earliest available time." }, "bookingLimitsDuration": { - "type": "object", - "description": "Limit total amount of time that this event can be booked" + "description": "Limit total amount of time that this event can be booked", + "oneOf": [ + { + "$ref": "#/components/schemas/BaseBookingLimitsDuration_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] }, "bookingWindow": { - "type": "array", "description": "Limit how far in the future this event can be booked", - "items": { - "oneOf": [ - { - "$ref": "#/components/schemas/BusinessDaysWindow_2024_06_14" - }, - { - "$ref": "#/components/schemas/CalendarDaysWindow_2024_06_14" - }, - { - "$ref": "#/components/schemas/RangeWindow_2024_06_14" - } - ] - } + "oneOf": [ + { + "$ref": "#/components/schemas/BusinessDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/CalendarDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/RangeWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] }, "offsetStart": { "type": "number", "description": "Offset timeslots shown to bookers by a specified number of minutes" }, + "bookerLayouts": { + "description": "Should booker have week, month or column view. Specify default layout and enabled layouts user can pick.", + "allOf": [ + { + "$ref": "#/components/schemas/BookerLayouts_2024_06_14" + } + ] + }, + "confirmationPolicy": { + "description": "Specify how the booking needs to be manually confirmed before it is pushed to the integrations and a confirmation mail is sent.", + "oneOf": [ + { + "$ref": "#/components/schemas/BaseConfirmationPolicy_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] + }, + "recurrence": { + "description": "Create a recurring event type.", + "oneOf": [ + { + "$ref": "#/components/schemas/Recurrence_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] + }, + "requiresBookerEmailVerification": { + "type": "boolean" + }, + "hideCalendarNotes": { + "type": "boolean" + }, + "lockTimeZoneToggleOnBookingPage": { + "type": "boolean" + }, + "color": { + "$ref": "#/components/schemas/EventTypeColor_2024_06_14" + }, + "seats": { + "description": "Create an event type with multiple seats.", + "oneOf": [ + { + "$ref": "#/components/schemas/Seats_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] + }, "customName": { "type": "string", "description": "Customizable event name with valid variables: \n {Event type title}, {Organiser}, {Scheduler}, {Location}, {Organiser first name}, \n {Scheduler first name}, {Scheduler last name}, {Event duration}, {LOCATION}, \n {HOST/ATTENDEE}, {HOST}, {ATTENDEE}, {USER}", "example": "{Event type title} between {Organiser} and {Scheduler}" + }, + "destinationCalendar": { + "$ref": "#/components/schemas/DestinationCalendar_2024_06_14" + }, + "useDestinationCalendarEmail": { + "type": "boolean" + }, + "hideCalendarEventDetails": { + "type": "boolean" } }, "required": [ "lengthInMinutes", "title", - "slug", - "description", - "locations", - "bookingFields", - "disableGuests", - "slotInterval", - "minimumBookingNotice", - "beforeEventBuffer", - "afterEventBuffer", - "scheduleId", - "bookingLimitsCount", - "onlyShowFirstAvailableSlot", - "bookingLimitsDuration", - "bookingWindow", - "offsetStart", - "customName" + "slug" ] }, "EmailDefaultFieldOutput_2024_06_14": { @@ -7455,104 +7738,6 @@ "isDefault" ] }, - "BaseBookingLimitsDuration_2024_06_14": { - "type": "object", - "properties": { - "day": { - "type": "number", - "minimum": 15, - "description": "The duration of bookings per day (must be a multiple of 15)", - "example": 60 - }, - "week": { - "type": "number", - "minimum": 15, - "description": "The duration of bookings per week (must be a multiple of 15)", - "example": 120 - }, - "month": { - "type": "number", - "minimum": 15, - "description": "The duration of bookings per month (must be a multiple of 15)", - "example": 180 - }, - "year": { - "type": "number", - "minimum": 15, - "description": "The duration of bookings per year (must be a multiple of 15)", - "example": 240 - }, - "disabled": { - "type": "boolean", - "default": false - } - } - }, - "BookerLayouts_2024_06_14": { - "type": "object", - "properties": {} - }, - "EventTypeColor_2024_06_14": { - "type": "object", - "properties": { - "lightThemeHex": { - "type": "string", - "description": "Color used for event types in light theme", - "example": "#292929" - }, - "darkThemeHex": { - "type": "string", - "description": "Color used for event types in dark theme", - "example": "#fafafa" - } - }, - "required": [ - "lightThemeHex", - "darkThemeHex" - ] - }, - "Seats_2024_06_14": { - "type": "object", - "properties": { - "seatsPerTimeSlot": { - "type": "number", - "description": "Number of seats available per time slot", - "example": 4 - }, - "showAttendeeInfo": { - "type": "boolean", - "description": "Show attendee information to other guests", - "example": true - }, - "showAvailabilityCount": { - "type": "boolean", - "description": "Display the count of available seats", - "example": true - } - }, - "required": [ - "seatsPerTimeSlot", - "showAttendeeInfo", - "showAvailabilityCount" - ] - }, - "DestinationCalendar_2024_06_14": { - "type": "object", - "properties": { - "integration": { - "type": "string", - "description": "The integration type of the destination calendar. Refer to the /api/v2/calendars endpoint to retrieve the integration type of your connected calendars." - }, - "externalId": { - "type": "string", - "description": "The external ID of the destination calendar. Refer to the /api/v2/calendars endpoint to retrieve the external IDs of your connected calendars." - } - }, - "required": [ - "integration", - "externalId" - ] - }, "EventTypeOutput_2024_06_14": { "type": "object", "properties": { @@ -7764,6 +7949,9 @@ "useDestinationCalendarEmail": { "type": "boolean" }, + "hideCalendarEventDetails": { + "type": "boolean" + }, "ownerId": { "type": "number", "example": 10 @@ -7813,6 +8001,7 @@ "customName", "destinationCalendar", "useDestinationCalendarEmail", + "hideCalendarEventDetails", "ownerId", "users" ] @@ -7990,64 +8179,120 @@ "description": "If you want that this event has different schedule than user's default one you can specify it here." }, "bookingLimitsCount": { - "type": "object", - "description": "Limit how many times this event can be booked" + "description": "Limit how many times this event can be booked", + "oneOf": [ + { + "$ref": "#/components/schemas/BaseBookingLimitsCount_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] }, "onlyShowFirstAvailableSlot": { "type": "boolean", "description": "This will limit your availability for this event type to one slot per day, scheduled at the earliest available time." }, "bookingLimitsDuration": { - "type": "object", - "description": "Limit total amount of time that this event can be booked" + "description": "Limit total amount of time that this event can be booked", + "oneOf": [ + { + "$ref": "#/components/schemas/BaseBookingLimitsDuration_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] }, "bookingWindow": { - "type": "array", "description": "Limit how far in the future this event can be booked", - "items": { - "oneOf": [ - { - "$ref": "#/components/schemas/BusinessDaysWindow_2024_06_14" - }, - { - "$ref": "#/components/schemas/CalendarDaysWindow_2024_06_14" - }, - { - "$ref": "#/components/schemas/RangeWindow_2024_06_14" - } - ] - } + "oneOf": [ + { + "$ref": "#/components/schemas/BusinessDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/CalendarDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/RangeWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] }, "offsetStart": { "type": "number", "description": "Offset timeslots shown to bookers by a specified number of minutes" }, + "bookerLayouts": { + "description": "Should booker have week, month or column view. Specify default layout and enabled layouts user can pick.", + "allOf": [ + { + "$ref": "#/components/schemas/BookerLayouts_2024_06_14" + } + ] + }, + "confirmationPolicy": { + "description": "Specify how the booking needs to be manually confirmed before it is pushed to the integrations and a confirmation mail is sent.", + "oneOf": [ + { + "$ref": "#/components/schemas/BaseConfirmationPolicy_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] + }, + "recurrence": { + "description": "Create a recurring event type.", + "oneOf": [ + { + "$ref": "#/components/schemas/Recurrence_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] + }, + "requiresBookerEmailVerification": { + "type": "boolean" + }, + "hideCalendarNotes": { + "type": "boolean" + }, + "lockTimeZoneToggleOnBookingPage": { + "type": "boolean" + }, + "color": { + "$ref": "#/components/schemas/EventTypeColor_2024_06_14" + }, + "seats": { + "description": "Create an event type with multiple seats.", + "oneOf": [ + { + "$ref": "#/components/schemas/Seats_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] + }, "customName": { "type": "string", "description": "Customizable event name with valid variables: \n {Event type title}, {Organiser}, {Scheduler}, {Location}, {Organiser first name}, \n {Scheduler first name}, {Scheduler last name}, {Event duration}, {LOCATION}, \n {HOST/ATTENDEE}, {HOST}, {ATTENDEE}, {USER}", "example": "{Event type title} between {Organiser} and {Scheduler}" + }, + "destinationCalendar": { + "$ref": "#/components/schemas/DestinationCalendar_2024_06_14" + }, + "useDestinationCalendarEmail": { + "type": "boolean" + }, + "hideCalendarEventDetails": { + "type": "boolean" } - }, - "required": [ - "lengthInMinutes", - "title", - "slug", - "description", - "locations", - "bookingFields", - "disableGuests", - "slotInterval", - "minimumBookingNotice", - "beforeEventBuffer", - "afterEventBuffer", - "scheduleId", - "bookingLimitsCount", - "onlyShowFirstAvailableSlot", - "bookingLimitsDuration", - "bookingWindow", - "offsetStart", - "customName" - ] + } }, "UpdateEventTypeOutput_2024_06_14": { "type": "object", @@ -9270,6 +9515,32 @@ "data" ] }, + "Host": { + "type": "object", + "properties": { + "userId": { + "type": "number", + "description": "Which user is the host of this event" + }, + "mandatory": { + "type": "boolean", + "description": "Only relevant for round robin event types. If true then the user must attend round robin event always." + }, + "priority": { + "type": "string", + "enum": [ + "lowest", + "low", + "medium", + "high", + "highest" + ] + } + }, + "required": [ + "userId" + ] + }, "CreateTeamEventTypeInput_2024_06_14": { "type": "object", "properties": { @@ -9375,50 +9646,126 @@ "description": "If you want that this event has different schedule than user's default one you can specify it here." }, "bookingLimitsCount": { - "type": "object", - "description": "Limit how many times this event can be booked" + "description": "Limit how many times this event can be booked", + "oneOf": [ + { + "$ref": "#/components/schemas/BaseBookingLimitsCount_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] }, "onlyShowFirstAvailableSlot": { "type": "boolean", "description": "This will limit your availability for this event type to one slot per day, scheduled at the earliest available time." }, "bookingLimitsDuration": { - "type": "object", - "description": "Limit total amount of time that this event can be booked" + "description": "Limit total amount of time that this event can be booked", + "oneOf": [ + { + "$ref": "#/components/schemas/BaseBookingLimitsDuration_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] }, "bookingWindow": { - "type": "array", "description": "Limit how far in the future this event can be booked", - "items": { - "oneOf": [ - { - "$ref": "#/components/schemas/BusinessDaysWindow_2024_06_14" - }, - { - "$ref": "#/components/schemas/CalendarDaysWindow_2024_06_14" - }, - { - "$ref": "#/components/schemas/RangeWindow_2024_06_14" - } - ] - } + "oneOf": [ + { + "$ref": "#/components/schemas/BusinessDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/CalendarDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/RangeWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] }, "offsetStart": { "type": "number", "description": "Offset timeslots shown to bookers by a specified number of minutes" }, + "bookerLayouts": { + "description": "Should booker have week, month or column view. Specify default layout and enabled layouts user can pick.", + "allOf": [ + { + "$ref": "#/components/schemas/BookerLayouts_2024_06_14" + } + ] + }, + "confirmationPolicy": { + "description": "Specify how the booking needs to be manually confirmed before it is pushed to the integrations and a confirmation mail is sent.", + "oneOf": [ + { + "$ref": "#/components/schemas/BaseConfirmationPolicy_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] + }, + "recurrence": { + "description": "Create a recurring event type.", + "oneOf": [ + { + "$ref": "#/components/schemas/Recurrence_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] + }, + "requiresBookerEmailVerification": { + "type": "boolean" + }, + "hideCalendarNotes": { + "type": "boolean" + }, + "lockTimeZoneToggleOnBookingPage": { + "type": "boolean" + }, + "color": { + "$ref": "#/components/schemas/EventTypeColor_2024_06_14" + }, + "seats": { + "description": "Create an event type with multiple seats.", + "oneOf": [ + { + "$ref": "#/components/schemas/Seats_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] + }, "customName": { "type": "string", "description": "Customizable event name with valid variables: \n {Event type title}, {Organiser}, {Scheduler}, {Location}, {Organiser first name}, \n {Scheduler first name}, {Scheduler last name}, {Event duration}, {LOCATION}, \n {HOST/ATTENDEE}, {HOST}, {ATTENDEE}, {USER}", "example": "{Event type title} between {Organiser} and {Scheduler}" }, + "destinationCalendar": { + "$ref": "#/components/schemas/DestinationCalendar_2024_06_14" + }, + "useDestinationCalendarEmail": { + "type": "boolean" + }, + "hideCalendarEventDetails": { + "type": "boolean" + }, "schedulingType": { "type": "object" }, "hosts": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/Host" } }, "assignAllTeamMembers": { @@ -9430,24 +9777,8 @@ "lengthInMinutes", "title", "slug", - "description", - "locations", - "bookingFields", - "disableGuests", - "slotInterval", - "minimumBookingNotice", - "beforeEventBuffer", - "afterEventBuffer", - "scheduleId", - "bookingLimitsCount", - "onlyShowFirstAvailableSlot", - "bookingLimitsDuration", - "bookingWindow", - "offsetStart", - "customName", "schedulingType", - "hosts", - "assignAllTeamMembers" + "hosts" ] }, "CreateTeamEventTypeOutput": { @@ -9480,38 +9811,6 @@ "data" ] }, - "Recurrence_2024_06_14": { - "type": "object", - "properties": { - "interval": { - "type": "number", - "example": 10, - "description": "Repeats every {count} week | month | year" - }, - "occurrences": { - "type": "number", - "example": 10, - "description": "Repeats for a maximum of {count} events" - }, - "frequency": { - "enum": [ - "yearly", - "monthly", - "weekly" - ], - "type": "string" - }, - "disabled": { - "type": "boolean", - "default": false - } - }, - "required": [ - "interval", - "occurrences", - "frequency" - ] - }, "TeamEventTypeResponseHost": { "type": "object", "properties": { @@ -10074,76 +10373,130 @@ "description": "If you want that this event has different schedule than user's default one you can specify it here." }, "bookingLimitsCount": { - "type": "object", - "description": "Limit how many times this event can be booked" + "description": "Limit how many times this event can be booked", + "oneOf": [ + { + "$ref": "#/components/schemas/BaseBookingLimitsCount_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] }, "onlyShowFirstAvailableSlot": { "type": "boolean", "description": "This will limit your availability for this event type to one slot per day, scheduled at the earliest available time." }, "bookingLimitsDuration": { - "type": "object", - "description": "Limit total amount of time that this event can be booked" + "description": "Limit total amount of time that this event can be booked", + "oneOf": [ + { + "$ref": "#/components/schemas/BaseBookingLimitsDuration_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] }, "bookingWindow": { - "type": "array", "description": "Limit how far in the future this event can be booked", - "items": { - "oneOf": [ - { - "$ref": "#/components/schemas/BusinessDaysWindow_2024_06_14" - }, - { - "$ref": "#/components/schemas/CalendarDaysWindow_2024_06_14" - }, - { - "$ref": "#/components/schemas/RangeWindow_2024_06_14" - } - ] - } + "oneOf": [ + { + "$ref": "#/components/schemas/BusinessDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/CalendarDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/RangeWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] }, "offsetStart": { "type": "number", "description": "Offset timeslots shown to bookers by a specified number of minutes" }, + "bookerLayouts": { + "description": "Should booker have week, month or column view. Specify default layout and enabled layouts user can pick.", + "allOf": [ + { + "$ref": "#/components/schemas/BookerLayouts_2024_06_14" + } + ] + }, + "confirmationPolicy": { + "description": "Specify how the booking needs to be manually confirmed before it is pushed to the integrations and a confirmation mail is sent.", + "oneOf": [ + { + "$ref": "#/components/schemas/BaseConfirmationPolicy_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] + }, + "recurrence": { + "description": "Create a recurring event type.", + "oneOf": [ + { + "$ref": "#/components/schemas/Recurrence_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] + }, + "requiresBookerEmailVerification": { + "type": "boolean" + }, + "hideCalendarNotes": { + "type": "boolean" + }, + "lockTimeZoneToggleOnBookingPage": { + "type": "boolean" + }, + "color": { + "$ref": "#/components/schemas/EventTypeColor_2024_06_14" + }, + "seats": { + "description": "Create an event type with multiple seats.", + "oneOf": [ + { + "$ref": "#/components/schemas/Seats_2024_06_14" + }, + { + "$ref": "#/components/schemas/Disabled_2024_06_14" + } + ] + }, "customName": { "type": "string", "description": "Customizable event name with valid variables: \n {Event type title}, {Organiser}, {Scheduler}, {Location}, {Organiser first name}, \n {Scheduler first name}, {Scheduler last name}, {Event duration}, {LOCATION}, \n {HOST/ATTENDEE}, {HOST}, {ATTENDEE}, {USER}", "example": "{Event type title} between {Organiser} and {Scheduler}" }, + "destinationCalendar": { + "$ref": "#/components/schemas/DestinationCalendar_2024_06_14" + }, + "useDestinationCalendarEmail": { + "type": "boolean" + }, + "hideCalendarEventDetails": { + "type": "boolean" + }, "hosts": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/Host" } }, "assignAllTeamMembers": { "type": "boolean", "description": "If true, all current and future team members will be assigned to this event type" } - }, - "required": [ - "lengthInMinutes", - "title", - "slug", - "description", - "locations", - "bookingFields", - "disableGuests", - "slotInterval", - "minimumBookingNotice", - "beforeEventBuffer", - "afterEventBuffer", - "scheduleId", - "bookingLimitsCount", - "onlyShowFirstAvailableSlot", - "bookingLimitsDuration", - "bookingWindow", - "offsetStart", - "customName", - "hosts", - "assignAllTeamMembers" - ] + } }, "UpdateTeamEventTypeOutput": { "type": "object", @@ -11849,12 +12202,18 @@ }, "meetingUrl": { "type": "string", - "description": "Meeting URL just for this booking. Displayed in email and calendar event. If not provided then cal video link will be generated.", + "description": "Deprecated - use 'location' instead. Meeting URL just for this booking. Displayed in email and calendar event. If not provided then cal video link will be generated.", + "example": "https://example.com/meeting", + "deprecated": true + }, + "location": { + "type": "string", + "description": "Location for this booking. Displayed in email and calendar event.", "example": "https://example.com/meeting" }, "bookingFieldsResponses": { "type": "object", - "description": "Booking field responses.", + "description": "Booking field responses consisting of an object with booking field slug as keys and user response as values.", "example": { "customField": "customValue" } @@ -11900,12 +12259,18 @@ }, "meetingUrl": { "type": "string", - "description": "Meeting URL just for this booking. Displayed in email and calendar event. If not provided then cal video link will be generated.", + "description": "Deprecated - use 'location' instead. Meeting URL just for this booking. Displayed in email and calendar event. If not provided then cal video link will be generated.", + "example": "https://example.com/meeting", + "deprecated": true + }, + "location": { + "type": "string", + "description": "Location for this booking. Displayed in email and calendar event.", "example": "https://example.com/meeting" }, "bookingFieldsResponses": { "type": "object", - "description": "Booking field responses.", + "description": "Booking field responses consisting of an object with booking field slug as keys and user response as values.", "example": { "customField": "customValue" } @@ -11955,9 +12320,9 @@ "type": "string" } }, - "meetingUrl": { + "location": { "type": "string", - "description": "Meeting URL just for this booking. Displayed in email and calendar event. If not provided then cal video link will be generated.", + "description": "Location for this booking. Displayed in email and calendar event.", "example": "https://example.com/meeting" }, "bookingFieldsResponses": { @@ -11974,26 +12339,21 @@ "attendee" ] }, - "Host": { + "EventType": { "type": "object", "properties": { "id": { "type": "number", "example": 1 }, - "name": { + "slug": { "type": "string", - "example": "Jane Doe" - }, - "timeZone": { - "type": "string", - "example": "America/Los_Angeles" + "example": "some-event" } }, "required": [ "id", - "name", - "timeZone" + "slug" ] }, "BookingOutput_2024_08_13": { @@ -12007,6 +12367,14 @@ "type": "string", "example": "booking_uid_123" }, + "title": { + "type": "string", + "example": "Consultation" + }, + "description": { + "type": "string", + "example": "Learn how to integrate scheduling into marketplace." + }, "hosts": { "type": "array", "items": { @@ -12050,7 +12418,12 @@ }, "eventTypeId": { "type": "number", - "example": 45 + "example": 50, + "deprecated": true, + "description": "Deprecated - rely on 'eventType' object containing the id instead." + }, + "eventType": { + "$ref": "#/components/schemas/EventType" }, "attendees": { "type": "array", @@ -12069,23 +12442,39 @@ } }, "meetingUrl": { + "type": "string", + "description": "Deprecated - rely on 'location' field instead.", + "example": "https://example.com/recurring-meeting", + "deprecated": true + }, + "location": { "type": "string", "example": "https://example.com/meeting" }, "absentHost": { "type": "boolean", "example": true + }, + "bookingFieldsResponses": { + "type": "object", + "description": "Booking field responses consisting of an object with booking field slug as keys and user response as values.", + "example": { + "customField": "customValue" + } } }, "required": [ "id", "uid", + "title", + "description", "hosts", "status", "start", "end", "duration", "eventTypeId", + "eventType", "attendees", "absentHost" ] @@ -12101,6 +12490,14 @@ "type": "string", "example": "recurring_uid_123" }, + "title": { + "type": "string", + "example": "Recurring meeting" + }, + "description": { + "type": "string", + "example": "Learn how to integrate scheduling into marketplace." + }, "hosts": { "type": "array", "items": { @@ -12143,7 +12540,12 @@ }, "eventTypeId": { "type": "number", - "example": 50 + "example": 50, + "deprecated": true, + "description": "Deprecated - rely on 'eventType' object containing the id instead." + }, + "eventType": { + "$ref": "#/components/schemas/EventType" }, "recurringBookingUid": { "type": "string", @@ -12166,23 +12568,39 @@ } }, "meetingUrl": { + "type": "string", + "description": "Deprecated - rely on 'location' field instead.", + "example": "https://example.com/recurring-meeting", + "deprecated": true + }, + "location": { "type": "string", "example": "https://example.com/recurring-meeting" }, "absentHost": { "type": "boolean", "example": false + }, + "bookingFieldsResponses": { + "type": "object", + "description": "Booking field responses consisting of an object with booking field slug as keys and user response as values.", + "example": { + "customField": "customValue" + } } }, "required": [ "id", "uid", + "title", + "description", "hosts", "status", "start", "end", "duration", "eventTypeId", + "eventType", "recurringBookingUid", "attendees", "absentHost" @@ -12246,6 +12664,9 @@ } ], "description": "Booking data, which can be either a BookingOutput object, a RecurringBookingOutput object, or an array of RecurringBookingOutput objects" + }, + "error": { + "type": "object" } }, "required": [ @@ -12277,6 +12698,9 @@ ] }, "description": "Array of booking data, which can contain either BookingOutput objects or RecurringBookingOutput objects" + }, + "error": { + "type": "object" } }, "required": [ diff --git a/packages/platform/atoms/CHANGELOG.md b/packages/platform/atoms/CHANGELOG.md new file mode 100644 index 0000000000..e9154df370 --- /dev/null +++ b/packages/platform/atoms/CHANGELOG.md @@ -0,0 +1,67 @@ +## 1.0.63 +1. 💥 BREAKING - `useGetBooking` hook has been renamed to `useBooking` hook and `useGetBookings` hook to `useBookings` and the data returned has different +structure. Here is example response from `useBooking`: +``` +{ + "id": 73, + "uid": "wPnPGRQCnEi8nij7eaQ5E1", + "title": "with confirmation and limit between Jane Doe and dojgqhylze-clxyyy21o0003sbk7yw5z6tzg-example", + "description": "", + "hosts": [ + { + "id": 95, + "name": "Jane Doe", + "timeZone": "Europe/Madrid" + } + ], + "status": "accepted", + "start": "2024-10-07T08:00:00.000Z", + "end": "2024-10-07T09:00:00.000Z", + "duration": 60, + "eventTypeId": 1207, + "eventType": { + "id": 1207, + "slug": "with-confirmation-and-limit" + }, + "attendees": [ + { + "name": "dojgqhylze-clxyyy21o0003sbk7yw5z6tzg-example", + "timeZone": "Europe/Madrid", + "language": "en", + "absent": false + } + ], + "guests": [], + "meetingUrl": "integrations:daily", + "location": "integrations:daily", + "absentHost": false, + "bookingFieldsResponses": { + "email": "dojgqhylze@example.com", + "name": "dojgqhylze-clxyyy21o0003sbk7yw5z6tzg-example", + "guests": [], + "phone": "+37128224288", + "location": { + "value": "integrations:daily", + "optionValue": "" + } + } +} +``` +It is important to note that `useBooking` can return one booking, one individual recurrence of recurring booking or if id of recurring booking is passed then array +of all recurrences within recurring booking. Response schema can be viewed in api docs https://cal.com/docs/api-reference/v2/bookings/get-a-booking and switching +between data response objects in the dropdown. This means that in your frontend you need to handle case if its an individual object or an array of bookings: +``` +const { isLoading, data: booking, refetch } = useBooking((router.query.bookingUid as string) ?? ""); + +if (!Array.isArray(booking)) { + ... + return abc +} else { + ... + return xyz +} +``` + +2. FIX - phone booking field not showing correctly https://linear.app/calcom/issue/CAL-4465/platform-fix-phone-booking-field +3. FEAT - ability to hide booker event type details sidebar https://linear.app/calcom/issue/CAL-4443/platform-feat-booker-hide-event-details +4. FEAT - make booker name and email booker properties read only if specified https://linear.app/calcom/issue/CAL-4441/platform-feat-make-name-and-email-fields-not-editable \ No newline at end of file diff --git a/packages/platform/atoms/booker/BookerPlatformWrapper.tsx b/packages/platform/atoms/booker/BookerPlatformWrapper.tsx index 975df51837..b01fe714c4 100644 --- a/packages/platform/atoms/booker/BookerPlatformWrapper.tsx +++ b/packages/platform/atoms/booker/BookerPlatformWrapper.tsx @@ -25,21 +25,21 @@ import { transformApiEventTypeForAtom, transformApiTeamEventTypeForAtom, } from "../event-types/atom-api-transformers/transformApiEventTypeForAtom"; +import type { UseCreateBookingInput } from "../hooks/bookings/useCreateBooking"; +import { useCreateBooking } from "../hooks/bookings/useCreateBooking"; +import { useCreateInstantBooking } from "../hooks/bookings/useCreateInstantBooking"; +import { useCreateRecurringBooking } from "../hooks/bookings/useCreateRecurringBooking"; +import { + useGetBookingForReschedule, + QUERY_KEY as BOOKING_RESCHEDULE_KEY, +} from "../hooks/bookings/useGetBookingForReschedule"; +import { useHandleBookEvent } from "../hooks/bookings/useHandleBookEvent"; import { useEventType } from "../hooks/event-types/public/useEventType"; import { useTeamEventType } from "../hooks/event-types/public/useTeamEventType"; import { useAtomsContext } from "../hooks/useAtomsContext"; import { useAvailableSlots } from "../hooks/useAvailableSlots"; import { useCalendarsBusyTimes } from "../hooks/useCalendarsBusyTimes"; import { useConnectedCalendars } from "../hooks/useConnectedCalendars"; -import type { UseCreateBookingInput } from "../hooks/useCreateBooking"; -import { useCreateBooking } from "../hooks/useCreateBooking"; -import { useCreateInstantBooking } from "../hooks/useCreateInstantBooking"; -import { useCreateRecurringBooking } from "../hooks/useCreateRecurringBooking"; -import { - useGetBookingForReschedule, - QUERY_KEY as BOOKING_RESCHEDULE_KEY, -} from "../hooks/useGetBookingForReschedule"; -import { useHandleBookEvent } from "../hooks/useHandleBookEvent"; import { useMe } from "../hooks/useMe"; import { useSlots } from "../hooks/useSlots"; import { AtomsWrapper } from "../src/components/atoms-wrapper"; diff --git a/packages/platform/atoms/hooks/bookings/useBooking.ts b/packages/platform/atoms/hooks/bookings/useBooking.ts new file mode 100644 index 0000000000..d6922fbf45 --- /dev/null +++ b/packages/platform/atoms/hooks/bookings/useBooking.ts @@ -0,0 +1,33 @@ +import { useQuery } from "@tanstack/react-query"; + +import { + V2_ENDPOINTS, + SUCCESS_STATUS, + CAL_API_VERSION_HEADER, + VERSION_2024_08_13, +} from "@calcom/platform-constants"; +import type { GetBookingOutput_2024_08_13 } from "@calcom/platform-types"; + +import http from "../../lib/http"; + +export const useBooking = (uid: string) => { + const pathname = `/${V2_ENDPOINTS.bookings}/${uid}`; + const headers = { + [CAL_API_VERSION_HEADER]: VERSION_2024_08_13, + }; + + const bookingQuery = useQuery({ + queryKey: ["use-booking", uid], + queryFn: async () => { + return http.get(pathname, { headers }).then((res) => { + if (res.data.status === SUCCESS_STATUS) { + return res.data.data; + } + throw new Error(res.data?.error?.message); + }); + }, + enabled: !!uid, + }); + + return bookingQuery; +}; diff --git a/packages/platform/atoms/hooks/bookings/useBookings.ts b/packages/platform/atoms/hooks/bookings/useBookings.ts new file mode 100644 index 0000000000..125b1bd8f4 --- /dev/null +++ b/packages/platform/atoms/hooks/bookings/useBookings.ts @@ -0,0 +1,54 @@ +import { useQuery } from "@tanstack/react-query"; + +import { + CAL_API_VERSION_HEADER, + SUCCESS_STATUS, + V2_ENDPOINTS, + VERSION_2024_08_13, +} from "@calcom/platform-constants"; +import type { GetBookingsOutput_2024_08_13 } from "@calcom/platform-types"; +import type { GetBookingsInput_2024_08_13 } from "@calcom/platform-types"; + +import http from "../../lib/http"; + +export const useBookings = (input: GetBookingsInput_2024_08_13) => { + const pathname = `/${V2_ENDPOINTS.bookings}`; + const headers = { + [CAL_API_VERSION_HEADER]: VERSION_2024_08_13, + }; + + const bookingsQuery = useQuery({ + queryKey: [ + "use-bookings", + input.status, + input.attendeeEmail, + input.attendeeName, + input.eventTypeIds, + input.eventTypeId, + input.teamsIds, + input.teamId, + input.afterStart, + input.beforeEnd, + input.sortStart, + input.sortEnd, + input.sortCreated, + input.take, + input.skip, + ], + queryFn: async () => { + return http + .get(pathname, { + params: input, + headers, + }) + .then((res) => { + if (res.data.status === SUCCESS_STATUS) { + return res.data.data; + } + throw new Error(res.data?.error?.message); + }); + }, + }); + + return bookingsQuery; +}; diff --git a/packages/platform/atoms/hooks/useCancelBooking.ts b/packages/platform/atoms/hooks/bookings/useCancelBooking.ts similarity index 97% rename from packages/platform/atoms/hooks/useCancelBooking.ts rename to packages/platform/atoms/hooks/bookings/useCancelBooking.ts index b9e669cc07..9370e1eed6 100644 --- a/packages/platform/atoms/hooks/useCancelBooking.ts +++ b/packages/platform/atoms/hooks/bookings/useCancelBooking.ts @@ -5,7 +5,7 @@ import { SUCCESS_STATUS } from "@calcom/platform-constants"; import type { ApiResponse, ApiErrorResponse } from "@calcom/platform-types"; import type { bookingCancelSchema } from "@calcom/prisma/zod-utils"; -import http from "../lib/http"; +import http from "../../lib/http"; interface IUseCancelBooking { onSuccess?: () => void; diff --git a/packages/platform/atoms/hooks/useCreateBooking.ts b/packages/platform/atoms/hooks/bookings/useCreateBooking.ts similarity index 97% rename from packages/platform/atoms/hooks/useCreateBooking.ts rename to packages/platform/atoms/hooks/bookings/useCreateBooking.ts index 0d51178416..dc3dc73f21 100644 --- a/packages/platform/atoms/hooks/useCreateBooking.ts +++ b/packages/platform/atoms/hooks/bookings/useCreateBooking.ts @@ -5,7 +5,7 @@ import type { BookingResponse } from "@calcom/platform-libraries"; import type { ApiResponse, ApiErrorResponse, ApiSuccessResponse } from "@calcom/platform-types"; import type { BookingCreateBody } from "@calcom/prisma/zod-utils"; -import http from "../lib/http"; +import http from "../../lib/http"; export type UseCreateBookingInput = BookingCreateBody & { locationUrl?: string }; diff --git a/packages/platform/atoms/hooks/useCreateInstantBooking.ts b/packages/platform/atoms/hooks/bookings/useCreateInstantBooking.ts similarity index 97% rename from packages/platform/atoms/hooks/useCreateInstantBooking.ts rename to packages/platform/atoms/hooks/bookings/useCreateInstantBooking.ts index 6196e3bca8..834f75204a 100644 --- a/packages/platform/atoms/hooks/useCreateInstantBooking.ts +++ b/packages/platform/atoms/hooks/bookings/useCreateInstantBooking.ts @@ -5,7 +5,7 @@ import type { BookingResponse } from "@calcom/platform-libraries"; import type { ApiResponse, ApiErrorResponse, ApiSuccessResponse } from "@calcom/platform-types"; import type { BookingCreateBody } from "@calcom/prisma/zod-utils"; -import http from "../lib/http"; +import http from "../../lib/http"; interface IUseCreateInstantBooking { onSuccess?: (res: ApiSuccessResponse) => void; diff --git a/packages/platform/atoms/hooks/useCreateRecurringBooking.ts b/packages/platform/atoms/hooks/bookings/useCreateRecurringBooking.ts similarity index 97% rename from packages/platform/atoms/hooks/useCreateRecurringBooking.ts rename to packages/platform/atoms/hooks/bookings/useCreateRecurringBooking.ts index 7eb97badcb..5f4ebd46b7 100644 --- a/packages/platform/atoms/hooks/useCreateRecurringBooking.ts +++ b/packages/platform/atoms/hooks/bookings/useCreateRecurringBooking.ts @@ -4,7 +4,7 @@ import { SUCCESS_STATUS } from "@calcom/platform-constants"; import type { BookingResponse, RecurringBookingCreateBody } from "@calcom/platform-libraries"; import type { ApiResponse, ApiErrorResponse, ApiSuccessResponse } from "@calcom/platform-types"; -import http from "../lib/http"; +import http from "../../lib/http"; interface IUseCreateRecurringBooking { onSuccess?: (res: ApiSuccessResponse) => void; diff --git a/packages/platform/atoms/hooks/useGetBookingForReschedule.ts b/packages/platform/atoms/hooks/bookings/useGetBookingForReschedule.ts similarity index 94% rename from packages/platform/atoms/hooks/useGetBookingForReschedule.ts rename to packages/platform/atoms/hooks/bookings/useGetBookingForReschedule.ts index ca7999ebc8..f7985024a5 100644 --- a/packages/platform/atoms/hooks/useGetBookingForReschedule.ts +++ b/packages/platform/atoms/hooks/bookings/useGetBookingForReschedule.ts @@ -4,8 +4,8 @@ import { V2_ENDPOINTS, SUCCESS_STATUS } from "@calcom/platform-constants"; import type { getBookingForReschedule } from "@calcom/platform-libraries"; import type { ApiResponse, ApiSuccessResponse } from "@calcom/platform-types"; -import http from "../lib/http"; -import { useAtomsContext } from "./useAtomsContext"; +import http from "../../lib/http"; +import { useAtomsContext } from "../useAtomsContext"; export const QUERY_KEY = "user-booking"; diff --git a/packages/platform/atoms/hooks/useHandleBookEvent.ts b/packages/platform/atoms/hooks/bookings/useHandleBookEvent.ts similarity index 100% rename from packages/platform/atoms/hooks/useHandleBookEvent.ts rename to packages/platform/atoms/hooks/bookings/useHandleBookEvent.ts diff --git a/packages/platform/atoms/hooks/useBookings.tsx b/packages/platform/atoms/hooks/useBookings.tsx deleted file mode 100644 index 673754bc6c..0000000000 --- a/packages/platform/atoms/hooks/useBookings.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { useState } from "react"; - -import type { IUseBookings } from "@calcom/features/bookings/Booker/components/hooks/useBookings"; -import { useBookerStore } from "@calcom/features/bookings/Booker/store"; - -export const useBookings = ({ event, hashedLink, bookingForm, metadata }: IUseBookings) => { - const eventSlug = useBookerStore((state) => state.eventSlug); - const setFormValues = useBookerStore((state) => state.setFormValues); - const rescheduleUid = useBookerStore((state) => state.rescheduleUid); - const bookingData = useBookerStore((state) => state.bookingData); - const timeslot = useBookerStore((state) => state.selectedTimeslot); - const seatedEventData = useBookerStore((state) => state.seatedEventData); - const [expiryTime, setExpiryTime] = useState(); - const recurringEventCount = useBookerStore((state) => state.recurringEventCount); - const isInstantMeeting = useBookerStore((state) => state.isInstantMeeting); - const duration = useBookerStore((state) => state.selectedDuration); - const hasInstantMeetingTokenExpired = expiryTime && new Date(expiryTime) < new Date(); -}; diff --git a/packages/platform/atoms/hooks/useGetBooking.ts b/packages/platform/atoms/hooks/useGetBooking.ts deleted file mode 100644 index b2a9089989..0000000000 --- a/packages/platform/atoms/hooks/useGetBooking.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; - -import { V2_ENDPOINTS, SUCCESS_STATUS } from "@calcom/platform-constants"; -import type { getBookingInfo } from "@calcom/platform-libraries"; -import type { ApiResponse, ApiSuccessResponse } from "@calcom/platform-types"; - -import http from "../lib/http"; - -export const QUERY_KEY = "user-booking"; - -export const useGetBooking = (uid = "") => { - const pathname = `/${V2_ENDPOINTS.bookings}/${uid}`; - - const bookingQuery = useQuery({ - queryKey: [QUERY_KEY, uid], - queryFn: () => { - return http - .get>["bookingInfo"]>>(pathname) - .then((res) => { - if (res.data.status === SUCCESS_STATUS) { - return (res.data as ApiSuccessResponse>["bookingInfo"]>) - .data; - } - throw new Error(res.data.error.message); - }); - }, - enabled: !!uid, - }); - - return bookingQuery; -}; diff --git a/packages/platform/atoms/hooks/useGetBookings.ts b/packages/platform/atoms/hooks/useGetBookings.ts deleted file mode 100644 index 70edd64c1e..0000000000 --- a/packages/platform/atoms/hooks/useGetBookings.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; - -import { SUCCESS_STATUS, V2_ENDPOINTS } from "@calcom/platform-constants"; -import type { getAllUserBookings } from "@calcom/platform-libraries"; -import type { ApiResponse, ApiSuccessResponse } from "@calcom/platform-types"; -import type { GetBookingsInput_2024_04_15 } from "@calcom/platform-types/bookings"; - -import http from "../lib/http"; - -export const QUERY_KEY = "user-bookings"; - -export const useGetBookings = (input: GetBookingsInput_2024_04_15) => { - const pathname = `/${V2_ENDPOINTS.bookings}`; - - const bookingsQuery = useQuery({ - queryKey: [QUERY_KEY, input?.limit ?? 50, input?.cursor ?? 0, input?.filters?.status ?? "upcoming"], - queryFn: () => { - return http - .get>>>(pathname, { - params: input, - }) - .then((res) => { - if (res.data.status === SUCCESS_STATUS) { - return (res.data as ApiSuccessResponse>>).data; - } - throw new Error(res.data.error.message); - }); - }, - }); - - return bookingsQuery; -}; diff --git a/packages/platform/atoms/index.ts b/packages/platform/atoms/index.ts index 651ca848f6..aaf7f99261 100644 --- a/packages/platform/atoms/index.ts +++ b/packages/platform/atoms/index.ts @@ -9,9 +9,9 @@ export { useEventTypes } from "./hooks/event-types/public/useEventTypes"; export { useTeamEventTypes } from "./hooks/event-types/public/useTeamEventTypes"; export { useEventType as useEvent } from "./hooks/event-types/public/useEventType"; export { useEventTypeById } from "./hooks/event-types/private/useEventTypeById"; -export { useCancelBooking } from "./hooks/useCancelBooking"; -export { useGetBooking } from "./hooks/useGetBooking"; -export { useGetBookings } from "./hooks/useGetBookings"; +export { useCancelBooking } from "./hooks/bookings/useCancelBooking"; +export { useBooking } from "./hooks/bookings/useBooking"; +export { useBookings } from "./hooks/bookings/useBookings"; export { useMe } from "./hooks/useMe"; export { OutlookConnect } from "./connect/outlook/OutlookConnect"; export * as Connect from "./connect"; diff --git a/packages/platform/atoms/monorepo.ts b/packages/platform/atoms/monorepo.ts index 9fc219f347..86cc52e728 100644 --- a/packages/platform/atoms/monorepo.ts +++ b/packages/platform/atoms/monorepo.ts @@ -3,7 +3,7 @@ export { CalProvider } from "./cal-provider/CalProvider"; export { useIsPlatform } from "./hooks/useIsPlatform"; export { useAtomsContext } from "./hooks/useAtomsContext"; export { useEventTypeById } from "./hooks/event-types/private/useEventTypeById"; -export { useHandleBookEvent } from "./hooks/useHandleBookEvent"; +export { useHandleBookEvent } from "./hooks/bookings/useHandleBookEvent"; export * as Dialog from "./src/components/ui/dialog"; export { Timezone } from "./timezone"; export { SelectedCalendarsSettingsWebWrapper } from "./selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper"; diff --git a/packages/platform/examples/base/src/pages/[bookingUid].tsx b/packages/platform/examples/base/src/pages/[bookingUid].tsx index eca7906e0d..b0729bba2a 100644 --- a/packages/platform/examples/base/src/pages/[bookingUid].tsx +++ b/packages/platform/examples/base/src/pages/[bookingUid].tsx @@ -3,7 +3,7 @@ import { Inter } from "next/font/google"; // eslint-disable-next-line @calcom/eslint/deprecated-imports-next-router import { useRouter } from "next/router"; -import { useGetBooking, useCancelBooking } from "@calcom/atoms"; +import { useBooking, useCancelBooking } from "@calcom/atoms"; import dayjs from "@calcom/dayjs"; import { Icon } from "@calcom/ui"; @@ -12,158 +12,159 @@ const inter = Inter({ subsets: ["latin"] }); export default function Bookings(props: { calUsername: string; calEmail: string }) { const router = useRouter(); - const { isLoading, data: booking, refetch } = useGetBooking((router.query.bookingUid as string) ?? ""); - const startTime = dayjs(booking?.startTime).format(12 === 12 ? "h:mma" : "HH:mm"); - const endTime = dayjs(booking?.endTime).format(12 === 12 ? "h:mma" : "HH:mm"); - const date = dayjs(booking?.startTime).toDate(); - const dateToday = dayjs(booking?.startTime).date(); - const year = dayjs(booking?.startTime).year(); - const day = dayjs(date).format("dddd"); - const month = dayjs(date).format("MMMM"); - + const { isLoading, data: booking, refetch } = useBooking((router.query.bookingUid as string) ?? ""); const { mutate: cancelBooking } = useCancelBooking({ onSuccess: () => { refetch(); }, }); - return ( -
- - {isLoading &&

Loading...

} - {!isLoading && booking && ( -
- {booking.status === "ACCEPTED" ? ( -
- -

This meeting is scheduled

-

We sent an email with a calendar invitation with the details to everyone.

-
- ) : ( -
- -

This event is cancelled

-
- )} -
-
-
-
-

What

+ + if (!Array.isArray(booking)) { + const startTime = dayjs(booking?.start).format(12 === 12 ? "h:mma" : "HH:mm"); + const endTime = dayjs(booking?.end).format(12 === 12 ? "h:mma" : "HH:mm"); + const date = dayjs(booking?.start).toDate(); + const dateToday = dayjs(booking?.start).date(); + const year = dayjs(booking?.start).year(); + const day = dayjs(date).format("dddd"); + const month = dayjs(date).format("MMMM"); + + return ( +
+ + {isLoading &&

Loading...

} + {!isLoading && booking && ( +
+ {booking.status === "accepted" ? ( +
+ +

This meeting is scheduled

+

We sent an email with a calendar invitation with the details to everyone.

-
-

{booking.title}

+ ) : ( +
+ +

This event is cancelled

-
-
-
-

When

-
-
+ )} +
+
+
-

- {`${day}, ${month} ${dateToday}, ${year}`} -

+

What

-

- {`${startTime}`} - {`${endTime}`} -

+

{booking.title}

-
-
-
Who
-
+
+
+

When

+
-

- {booking.user?.name}{" "} - Host -

-

{booking.user?.email}

+

+ {`${day}, ${month} ${dateToday}, ${year}`} +

+
+
+

+ {`${startTime}`} - {`${endTime}`} +

- {booking.attendees.map((attendee, i) => { - return ( -
-
-
-

{`${attendee.name}`}

-

{`${attendee.email}`}

-
+
+
+
Who
+
+
+
+

+ {booking.hosts[0]?.name}{" "} + Host +

- ); - })} +
+ {booking.attendees.map((attendee, i) => { + return ( +
+
+
+

{`${attendee.name}`}

+
+
+ ); + })} +
+ {!!booking.location && ( +
+
+

Where

+
+
+

{booking.location}

+
+
+ )} + {!!booking.bookingFieldsResponses?.notes && ( +
+
+

Additional notes

+
+
+

{`${booking.bookingFieldsResponses.notes}`}

+
+
+ )}
- {!!booking.location && booking.location.startsWith("http") && ( -
-
-

Where

+ + {booking.status === "accepted" && ( + <> +
+
+

+ Need to make a change?{" "} + {" "} + or{" "} + +

-
-

{booking.location}

-
-
- )} - {booking.responses.notes && ( -
-
-

Additional notes

-
-
-

{`${booking.responses.notes}`}

-
-
+ )}
- - {booking.status === "ACCEPTED" && ( - <> -
-
-

- Need to make a change?{" "} - {" "} - or{" "} - -

-
- - )} -
- )} -
- ); + )} +
+ ); + } } diff --git a/packages/platform/examples/base/src/pages/bookings.tsx b/packages/platform/examples/base/src/pages/bookings.tsx index 44a016e00f..708e7ae4d7 100644 --- a/packages/platform/examples/base/src/pages/bookings.tsx +++ b/packages/platform/examples/base/src/pages/bookings.tsx @@ -3,22 +3,22 @@ import { Inter } from "next/font/google"; // eslint-disable-next-line @calcom/eslint/deprecated-imports-next-router import { useRouter } from "next/router"; -import { useGetBookings } from "@calcom/atoms"; +import { useBookings } from "@calcom/atoms"; import dayjs from "@calcom/dayjs"; const inter = Inter({ subsets: ["latin"] }); export default function Bookings(props: { calUsername: string; calEmail: string }) { - const { isLoading: isLoadingUpcomingBookings, data: upcomingBookings } = useGetBookings({ - limit: 50, - cursor: 0, - filters: { status: "upcoming" }, + const { isLoading: isLoadingUpcomingBookings, data: upcomingBookings } = useBookings({ + take: 50, + skip: 0, + status: ["upcoming"], }); - const { isLoading: isLoadingPastBookings, data: pastBookings } = useGetBookings({ - limit: 50, - cursor: 0, - filters: { status: "past" }, + const { isLoading: isLoadingPastBookings, data: pastBookings } = useBookings({ + take: 50, + skip: 0, + status: ["past"], }); const isLoading = isLoadingUpcomingBookings || isLoadingPastBookings; const router = useRouter(); @@ -30,13 +30,13 @@ export default function Bookings(props: { calUsername: string; calEmail: string

{props.calUsername} Bookings

{isLoading &&

Loading...

} {!isLoading && - pastBookings?.bookings && - upcomingBookings?.bookings && - (Boolean(upcomingBookings?.bookings.length) || Boolean(pastBookings?.bookings.length)) && - [...pastBookings?.bookings, ...upcomingBookings?.bookings].map((booking) => { - const date = dayjs(booking.startTime).toDate(); - const startTime = dayjs(booking?.startTime).format(12 === 12 ? "h:mma" : "HH:mm"); - const endTime = dayjs(booking?.endTime).format(12 === 12 ? "h:mma" : "HH:mm"); + pastBookings && + upcomingBookings && + (Boolean(upcomingBookings?.length) || Boolean(pastBookings?.length)) && + [...pastBookings, ...upcomingBookings].map((booking) => { + const date = dayjs(booking.start).toDate(); + const startTime = dayjs(booking?.start).format(12 === 12 ? "h:mma" : "HH:mm"); + const endTime = dayjs(booking?.end).format(12 === 12 ? "h:mma" : "HH:mm"); const day = dayjs(date).format("dddd"); const month = dayjs(date).format("MMMM"); @@ -48,7 +48,7 @@ export default function Bookings(props: { calUsername: string; calEmail: string router.push(`/${booking.uid}`); }}>
-
{`${day}, ${dayjs(booking.startTime).date()} ${month}`}
+
{`${day}, ${dayjs(booking.start).date()} ${month}`}

{startTime} - {endTime} @@ -64,7 +64,7 @@ export default function Bookings(props: { calUsername: string; calEmail: string

- {booking?.user?.name} and {booking.attendees[0].name} + {booking?.hosts?.[0]?.name} and {booking.attendees[0].name}

{" "}

diff --git a/packages/platform/examples/base/src/pages/event-types.tsx b/packages/platform/examples/base/src/pages/event-types.tsx index 818e9ff793..69a8a82311 100644 --- a/packages/platform/examples/base/src/pages/event-types.tsx +++ b/packages/platform/examples/base/src/pages/event-types.tsx @@ -30,7 +30,6 @@ export default function Bookings(props: { calUsername: string; calEmail: string {!isLoadingEvents && !eventTypeId && Boolean(eventTypes?.length) && !rescheduleUid && (
-

User event types

{eventTypes?.map( (event: { id: number; slug: string; title: string; lengthInMinutes: number }) => { const formatEventSlug = event.slug diff --git a/packages/platform/types/bookings/2024-08-13/inputs/create-booking.input.ts b/packages/platform/types/bookings/2024-08-13/inputs/create-booking.input.ts index 6982c1f474..0eb4ef5a4f 100644 --- a/packages/platform/types/bookings/2024-08-13/inputs/create-booking.input.ts +++ b/packages/platform/types/bookings/2024-08-13/inputs/create-booking.input.ts @@ -89,16 +89,28 @@ export class CreateBookingInput_2024_08_13 { @IsOptional() guests?: string[]; - @ApiPropertyOptional({ + @ApiProperty({ type: String, description: - "Meeting URL just for this booking. Displayed in email and calendar event. If not provided then cal video link will be generated.", + "Deprecated - use 'location' instead. Meeting URL just for this booking. Displayed in email and calendar event. If not provided then cal video link will be generated.", example: "https://example.com/meeting", + required: false, + deprecated: true, }) @IsUrl() @IsOptional() meetingUrl?: string; + @ApiPropertyOptional({ + type: String, + description: "Location for this booking. Displayed in email and calendar event.", + example: "https://example.com/meeting", + required: false, + }) + @IsUrl() + @IsOptional() + location?: string; + // todo(Lauris): expose after refactoring metadata https://app.campsite.co/cal/posts/zysq8w9rwm9c // @ApiProperty({ // type: Object, @@ -112,7 +124,8 @@ export class CreateBookingInput_2024_08_13 { @ApiPropertyOptional({ type: Object, - description: "Booking field responses.", + description: + "Booking field responses consisting of an object with booking field slug as keys and user response as values.", example: { customField: "customValue" }, required: false, }) @@ -169,14 +182,13 @@ export class CreateRecurringBookingInput_2024_08_13 { @ApiProperty({ type: String, - description: - "Meeting URL just for this booking. Displayed in email and calendar event. If not provided then cal video link will be generated.", + description: "Location for this booking. Displayed in email and calendar event.", example: "https://example.com/meeting", required: false, }) @IsUrl() @IsOptional() - meetingUrl?: string; + location?: string; // todo(Lauris): expose after refactoring metadata https://app.campsite.co/cal/posts/zysq8w9rwm9c // @ApiProperty({ diff --git a/packages/platform/types/bookings/2024-08-13/outputs/booking.output.ts b/packages/platform/types/bookings/2024-08-13/outputs/booking.output.ts index 46f2e1a8c0..e4a0769258 100644 --- a/packages/platform/types/bookings/2024-08-13/outputs/booking.output.ts +++ b/packages/platform/types/bookings/2024-08-13/outputs/booking.output.ts @@ -6,6 +6,7 @@ import { IsDateString, IsEnum, IsInt, + IsObject, IsOptional, IsString, IsTimeZone, @@ -56,6 +57,18 @@ class Host { timeZone!: string; } +class EventType { + @ApiProperty({ type: Number, example: 1 }) + @IsInt() + @Expose() + id!: number; + + @ApiProperty({ type: String, example: "some-event" }) + @IsString() + @Expose() + slug!: string; +} + export class BookingOutput_2024_08_13 { @ApiProperty({ type: Number, example: 123 }) @IsInt() @@ -67,6 +80,16 @@ export class BookingOutput_2024_08_13 { @Expose() uid!: string; + @ApiProperty({ type: String, example: "Consultation" }) + @IsString() + @Expose() + title!: string; + + @ApiProperty({ type: String, example: "Learn how to integrate scheduling into marketplace." }) + @IsString() + @Expose() + description!: string; + @ApiProperty({ type: [Host] }) @ValidateNested({ each: true }) @Type(() => Host) @@ -111,11 +134,21 @@ export class BookingOutput_2024_08_13 { @Expose() duration!: number; - @ApiProperty({ type: Number, example: 45 }) + @ApiProperty({ + type: Number, + example: 50, + deprecated: true, + description: "Deprecated - rely on 'eventType' object containing the id instead.", + }) @IsInt() @Expose() eventTypeId!: number; + @ApiProperty({ type: EventType }) + @Type(() => EventType) + @Expose() + eventType!: EventType; + @ApiProperty({ type: [Attendee] }) @ValidateNested({ each: true }) @Type(() => Attendee) @@ -129,16 +162,38 @@ export class BookingOutput_2024_08_13 { @Expose() guests?: string[]; - @ApiProperty({ type: String, required: false, example: "https://example.com/meeting" }) + @ApiProperty({ + type: String, + required: false, + description: "Deprecated - rely on 'location' field instead.", + example: "https://example.com/recurring-meeting", + deprecated: true, + }) @IsUrl() @IsOptional() @Expose() meetingUrl?: string; + @ApiProperty({ type: String, required: false, example: "https://example.com/meeting" }) + @IsOptional() + @Expose() + location!: string; + @ApiProperty({ type: Boolean, example: true }) @IsBoolean() @Expose() absentHost!: boolean; + + @ApiProperty({ + type: Object, + description: + "Booking field responses consisting of an object with booking field slug as keys and user response as values.", + example: { customField: "customValue" }, + required: false, + }) + @IsObject() + @Expose() + bookingFieldsResponses!: Record; } export class RecurringBookingOutput_2024_08_13 { @@ -152,6 +207,16 @@ export class RecurringBookingOutput_2024_08_13 { @Expose() uid!: string; + @ApiProperty({ type: String, example: "Recurring meeting" }) + @IsString() + @Expose() + title!: string; + + @ApiProperty({ type: String, example: "Learn how to integrate scheduling into marketplace." }) + @IsString() + @Expose() + description!: string; + @ApiProperty({ type: [Host] }) @ValidateNested({ each: true }) @Type(() => Host) @@ -196,11 +261,21 @@ export class RecurringBookingOutput_2024_08_13 { @Expose() duration!: number; - @ApiProperty({ type: Number, example: 50 }) + @ApiProperty({ + type: Number, + example: 50, + deprecated: true, + description: "Deprecated - rely on 'eventType' object containing the id instead.", + }) @IsInt() @Expose() eventTypeId!: number; + @ApiProperty({ type: EventType }) + @Type(() => EventType) + @Expose() + eventType!: EventType; + @ApiProperty({ type: String, example: "recurring_uid_987" }) @IsString() @Expose() @@ -219,14 +294,36 @@ export class RecurringBookingOutput_2024_08_13 { @Expose() guests?: string[]; - @ApiProperty({ type: String, required: false, example: "https://example.com/recurring-meeting" }) + @ApiProperty({ + type: String, + required: false, + description: "Deprecated - rely on 'location' field instead.", + example: "https://example.com/recurring-meeting", + deprecated: true, + }) @IsUrl() @IsOptional() @Expose() meetingUrl?: string; + @ApiProperty({ type: String, required: false, example: "https://example.com/recurring-meeting" }) + @IsOptional() + @Expose() + location!: string; + @ApiProperty({ type: Boolean, example: false }) @IsBoolean() @Expose() absentHost!: boolean; + + @ApiProperty({ + type: Object, + description: + "Booking field responses consisting of an object with booking field slug as keys and user response as values.", + example: { customField: "customValue" }, + required: false, + }) + @IsObject() + @Expose() + bookingFieldsResponses!: Record; } diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/outputs/get-booking.output.ts b/packages/platform/types/bookings/2024-08-13/outputs/get-booking.output.ts similarity index 98% rename from apps/api/v2/src/ee/bookings/2024-08-13/outputs/get-booking.output.ts rename to packages/platform/types/bookings/2024-08-13/outputs/get-booking.output.ts index aa4745785e..407428f4a0 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/outputs/get-booking.output.ts +++ b/packages/platform/types/bookings/2024-08-13/outputs/get-booking.output.ts @@ -23,4 +23,6 @@ export class GetBookingOutput_2024_08_13 { }) @Type(() => Object) data!: BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13[]; + + error?: Error; } diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/outputs/get-bookings.output.ts b/packages/platform/types/bookings/2024-08-13/outputs/get-bookings.output.ts similarity index 98% rename from apps/api/v2/src/ee/bookings/2024-08-13/outputs/get-bookings.output.ts rename to packages/platform/types/bookings/2024-08-13/outputs/get-bookings.output.ts index 3f3c0623a2..7eaac49732 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/outputs/get-bookings.output.ts +++ b/packages/platform/types/bookings/2024-08-13/outputs/get-bookings.output.ts @@ -23,4 +23,6 @@ export class GetBookingsOutput_2024_08_13 { }) @ValidateNested({ each: true }) data!: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[]; + + error?: Error; } diff --git a/packages/platform/types/bookings/2024-08-13/outputs/index.ts b/packages/platform/types/bookings/2024-08-13/outputs/index.ts index 6b714c9061..b5e66021ec 100644 --- a/packages/platform/types/bookings/2024-08-13/outputs/index.ts +++ b/packages/platform/types/bookings/2024-08-13/outputs/index.ts @@ -1 +1,3 @@ export * from "./booking.output"; +export * from "./get-booking.output"; +export * from "./get-bookings.output"; diff --git a/yarn.lock b/yarn.lock index 0abe9a68b0..982999ae07 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4134,7 +4134,7 @@ __metadata: dependencies: "@calcom/platform-constants": "*" "@calcom/platform-enums": "*" - "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.43" + "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.44" "@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2" "@calcom/platform-types": "*" "@calcom/platform-utils": "*" @@ -4180,6 +4180,7 @@ __metadata: passport: ^0.7.0 passport-jwt: ^4.0.1 prettier: ^2.8.6 + qs-stringify: ^1.2.1 querystring: ^0.2.1 reflect-metadata: ^0.1.13 rxjs: ^7.8.1 @@ -4287,7 +4288,7 @@ __metadata: languageName: unknown linkType: soft -"@calcom/atoms@*, @calcom/atoms@workspace:^, @calcom/atoms@workspace:packages/platform/atoms": +"@calcom/atoms@*, @calcom/atoms@workspace:packages/platform/atoms": version: 0.0.0-use.local resolution: "@calcom/atoms@workspace:packages/platform/atoms" dependencies: @@ -5131,14 +5132,14 @@ __metadata: languageName: node linkType: hard -"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.43": - version: 0.0.43 - resolution: "@calcom/platform-libraries@npm:0.0.43" +"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.44": + version: 0.0.44 + resolution: "@calcom/platform-libraries@npm:0.0.44" dependencies: "@calcom/core": "*" "@calcom/features": "*" "@calcom/lib": "*" - checksum: c9a6ede11ddd5ec413aacc6e8f6624446d964a60a303469fce329c9a925d8a436931ce91b3d1dfa943c8e8740d7b041e5b0522dfc387c8309008cb255d391b5c + checksum: 0d46c2c695a8a776f7401e298d0f7d0b3227c9fe38e1746aaf747c22fe28d881939c31c54613e696a595b72af21fe14b342dabe99c59b44d6576f4e2f37bf76f languageName: node linkType: hard @@ -5772,7 +5773,7 @@ __metadata: tailwindcss-animate: ^1.0.6 tailwindcss-radix: ^2.6.0 ts-node: ^10.9.1 - turndown: ^7.1.1 + turndown: ^7.1.3 typescript: ^4.9.4 uuid: ^8.3.2 zod: ^3.22.4 @@ -5794,7 +5795,6 @@ __metadata: dependencies: "@algora/sdk": ^0.1.2 "@calcom/app-store": "*" - "@calcom/atoms": "workspace:^" "@calcom/config": "*" "@calcom/dayjs": "*" "@calcom/embed-react": "workspace:^" @@ -5840,7 +5840,6 @@ __metadata: "@vercel/og": ^0.5.0 autoprefixer: ^10.4.12 bcryptjs: ^2.4.3 - class-variance-authority: ^0.7.0 clsx: ^1.2.1 cobe: ^0.4.1 concurrently: ^7.6.0 @@ -5853,7 +5852,6 @@ __metadata: env-cmd: ^10.1.0 eslint: ^8.34.0 fathom-client: ^3.5.0 - framer-motion: ^11.0.25 globby: ^13.1.3 graphql: ^16.8.0 graphql-codegen: ^0.4.0 @@ -5863,7 +5861,7 @@ __metadata: i18n-unused: ^0.13.0 iframe-resizer-react: ^1.1.0 keen-slider: ^6.8.0 - lucide-react: ^0.364.0 + lucide-react: ^0.171.0 micro: ^10.0.1 next: ^14.1.3 next-auth: ^4.22.1 @@ -5875,7 +5873,7 @@ __metadata: prism-react-renderer: ^1.3.5 react: ^18.2.0 react-confetti: ^6.0.1 - react-datocms: ^5.0.3 + react-datocms: ^3.1.0 react-device-detect: ^2.2.2 react-dom: ^18.2.0 react-fast-marquee: ^1.6.4 @@ -5887,7 +5885,6 @@ __metadata: react-merge-refs: 1.1.0 react-resize-detector: ^9.1.0 react-twemoji: ^0.3.0 - react-twitter-embed: ^4.0.4 react-use-measure: ^2.1.1 react-wrap-balancer: ^1.0.0 remark: ^14.0.2 @@ -9401,6 +9398,13 @@ __metadata: languageName: node linkType: hard +"@mixmark-io/domino@npm:^2.2.0": + version: 2.2.0 + resolution: "@mixmark-io/domino@npm:2.2.0" + checksum: 547af4a8f74cb8e1f2f86e84dc968e7a722522d4748d8a1c9d30c5d1ce6a190ec400866338cc1f717b31beabed6e61c44edf17db5cf380317b63a1e7f253080f + languageName: node + linkType: hard + "@mongodb-js/saslprep@npm:^1.1.0": version: 1.1.4 resolution: "@mongodb-js/saslprep@npm:1.1.4" @@ -9486,60 +9490,6 @@ __metadata: languageName: node linkType: hard -"@mux/mux-player-react@npm:*": - version: 3.0.0 - resolution: "@mux/mux-player-react@npm:3.0.0" - dependencies: - "@mux/mux-player": 3.0.0 - "@mux/playback-core": 0.26.0 - prop-types: ^15.7.2 - peerDependencies: - "@types/react": ^17.0.0 || ^18 || ^19 - react: ^17.0.2 || ^18 || ^19 - react-dom: ^17.0.2 || ^18 || ^19 - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: ab399677b9f044e74f1c56b02bf75e02154886b2b4a778073d8179440c6ac2fee51692f4ad75ba25d6fe7c35a5a1b2ebe2ae807b7a36e3669bea65392b373b10 - languageName: node - linkType: hard - -"@mux/mux-player@npm:3.0.0": - version: 3.0.0 - resolution: "@mux/mux-player@npm:3.0.0" - dependencies: - "@mux/mux-video": 0.21.0 - "@mux/playback-core": 0.26.0 - media-chrome: ~4.1.1 - player.style: ^0.0.8 - checksum: f2110d03eb49e2468b964cda2f70ea201cefafc35b738f6247637711d97f93f2a0828d51e0da04439a131ee6cdb530fc6ebaec228d268013cc6d30e68a6fda2f - languageName: node - linkType: hard - -"@mux/mux-video@npm:0.21.0": - version: 0.21.0 - resolution: "@mux/mux-video@npm:0.21.0" - dependencies: - "@mux/playback-core": 0.26.0 - castable-video: ~1.1.0 - custom-media-element: ~1.3.1 - media-tracks: ~0.3.2 - checksum: 33b9083c84ea3ad0f7c59e545790b605f4cdc47714f488fa6ab269dd8531060deac6c4a1237d132722d706f68c786561b712b83b5581398ffaa7f0a3fc642c31 - languageName: node - linkType: hard - -"@mux/playback-core@npm:0.26.0": - version: 0.26.0 - resolution: "@mux/playback-core@npm:0.26.0" - dependencies: - hls.js: ~1.5.11 - mux-embed: ^5.3.0 - checksum: 648300310eee8038654c6f2aa5be2ab0bf6ad281a6bb01ea5ffe84e501230959655917a4800d96841aa5342e1e06cc97c32228b0eeb462f57264e5dd524f1b95 - languageName: node - linkType: hard - "@ndelangen/get-tarball@npm:^3.0.7": version: 3.0.9 resolution: "@ndelangen/get-tarball@npm:3.0.9" @@ -22145,15 +22095,6 @@ __metadata: languageName: node linkType: hard -"castable-video@npm:~1.1.0": - version: 1.1.0 - resolution: "castable-video@npm:1.1.0" - dependencies: - custom-media-element: ~1.3.2 - checksum: 0b7280140e2e855f66ad824c8352d8422695f3a0195757341e8c0b5828df9c0279af70154d7ef514e9d7849814b74625c4df7a652853bd624ecb205a232c295e - languageName: node - linkType: hard - "ccount@npm:^2.0.0": version: 2.0.1 resolution: "ccount@npm:2.0.1" @@ -22614,15 +22555,6 @@ __metadata: languageName: node linkType: hard -"class-variance-authority@npm:^0.7.0": - version: 0.7.0 - resolution: "class-variance-authority@npm:0.7.0" - dependencies: - clsx: 2.0.0 - checksum: e7fd1fab433ef06f52a1b7b241b70b4a185864deef199d3b0a2c3412f1cc179517288264c383f3b971a00d76811625fc8f7ffe709e6170219e88cd7368f08a20 - languageName: node - linkType: hard - "classnames@npm:^2.2.5, classnames@npm:^2.2.6": version: 2.3.2 resolution: "classnames@npm:2.3.2" @@ -22895,13 +22827,6 @@ __metadata: languageName: node linkType: hard -"clsx@npm:2.0.0": - version: 2.0.0 - resolution: "clsx@npm:2.0.0" - checksum: a2cfb2351b254611acf92faa0daf15220f4cd648bdf96ce369d729813b85336993871a4bf6978ddea2b81b5a130478339c20d9d0b5c6fc287e5147f0c059276e - languageName: node - linkType: hard - "clsx@npm:^1.1.1": version: 1.1.1 resolution: "clsx@npm:1.1.1" @@ -24209,13 +24134,6 @@ __metadata: languageName: node linkType: hard -"custom-media-element@npm:~1.3.1, custom-media-element@npm:~1.3.2": - version: 1.3.2 - resolution: "custom-media-element@npm:1.3.2" - checksum: 9ec2ff88c3c5ba7d20b9484c0359b4cfe08ad6804f3116934ffbae2f2e0e04d2a832beb6f0bec56027a969ab366727d217406f750890613c988de6de5330cb4f - languageName: node - linkType: hard - "d3-array@npm:2 - 3, d3-array@npm:2.10.0 - 3, d3-array@npm:^3.1.6": version: 3.2.3 resolution: "d3-array@npm:3.2.3" @@ -25179,13 +25097,6 @@ __metadata: languageName: node linkType: hard -"domino@npm:^2.1.6": - version: 2.1.6 - resolution: "domino@npm:2.1.6" - checksum: 9b1b6d2661efd8bf942b70d5e11ac0de6a63f17e49b7eb227d9a612fa7b7c12b7775520d64f498988a8ee334ea9c59a463c84ea510b0af17dd3e13fdce120410 - languageName: node - linkType: hard - "dompurify@npm:=2.3.3": version: 2.3.3 resolution: "dompurify@npm:2.3.3" @@ -28400,26 +28311,6 @@ __metadata: languageName: node linkType: hard -"framer-motion@npm:^11.0.25": - version: 11.5.6 - resolution: "framer-motion@npm:11.5.6" - dependencies: - tslib: ^2.4.0 - peerDependencies: - "@emotion/is-prop-valid": "*" - react: ^18.0.0 - react-dom: ^18.0.0 - peerDependenciesMeta: - "@emotion/is-prop-valid": - optional: true - react: - optional: true - react-dom: - optional: true - checksum: b37bc0856e7c887ba8f7c656bda46a1b492a9c2a093e2ede4b98392292685550a1cb646a1ea2575c3c54d371d2af77c4c6b1b07ebe4e2facfdf4bf6e9de58ba4 - languageName: node - linkType: hard - "fresh@npm:0.5.2, fresh@npm:^0.5.2": version: 0.5.2 resolution: "fresh@npm:0.5.2" @@ -30095,13 +29986,6 @@ __metadata: languageName: node linkType: hard -"hls.js@npm:~1.5.11": - version: 1.5.15 - resolution: "hls.js@npm:1.5.15" - checksum: 1c2dbc2b482678e9e7fa045b11dbcdb2b0e31ac3fb541e31ea8dd8d11d239bb771fb6cb8d195d79b434cca79856cb22988738464ff2ac3fce45e039c6799580a - languageName: node - linkType: hard - "hmac-drbg@npm:^1.0.1": version: 1.0.1 resolution: "hmac-drbg@npm:1.0.1" @@ -34630,12 +34514,12 @@ __metadata: languageName: node linkType: hard -"lucide-react@npm:^0.364.0": - version: 0.364.0 - resolution: "lucide-react@npm:0.364.0" +"lucide-react@npm:^0.171.0": + version: 0.171.0 + resolution: "lucide-react@npm:0.171.0" peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 - checksum: 954f6274df46ffa172130d9e60eb630a377435c7dbf0ddb5ceebf284602dde3b99755dcd5c0757e697ee734a32c808a3cd2c25ad88fec1d8f627a2317f00e3e6 + checksum: 768ffe368c52a518ee339203d86ff4479989ab4d79c0716f721900c4bb7392ef6ff7a14807f6a685abd74d27f4c1778170bff77a0ab4c3e06c17944b557d8300 languageName: node linkType: hard @@ -35176,20 +35060,6 @@ __metadata: languageName: node linkType: hard -"media-chrome@npm:^4.1.0, media-chrome@npm:~4.1.1": - version: 4.1.1 - resolution: "media-chrome@npm:4.1.1" - checksum: a8bba93bc3878f62ae9cd52b307c1f9e6ab45ffe9d568917e01852b1f0c16272e48ab70a8f3eb6f4efa8904a4dd8f9a2ef71ed709a6b84ca9d4ce0825ad49fc4 - languageName: node - linkType: hard - -"media-tracks@npm:~0.3.2": - version: 0.3.3 - resolution: "media-tracks@npm:0.3.3" - checksum: 4795af3f171d7ad3a68d1ac1c1e8166a735244fe57d3fc0ec53b1c7410799e524756fc0bfb389632aebb148436b03baade36ca34a3f5377776c3968f6d2cc580 - languageName: node - linkType: hard - "media-typer@npm:0.3.0": version: 0.3.0 resolution: "media-typer@npm:0.3.0" @@ -36531,13 +36401,6 @@ __metadata: languageName: node linkType: hard -"mux-embed@npm:^5.3.0": - version: 5.3.0 - resolution: "mux-embed@npm:5.3.0" - checksum: 237cd2eef9f04c05020114eb871f12351e123a57f275efd789d01cacf56e4e4ef12c102e25067dfdcbdfec4989d4eedaf775648dfcfc3032e6555aa5fbead3cc - languageName: node - linkType: hard - "mysql2@npm:3.9.1": version: 3.9.1 resolution: "mysql2@npm:3.9.1" @@ -39404,15 +39267,6 @@ __metadata: languageName: node linkType: hard -"player.style@npm:^0.0.8": - version: 0.0.8 - resolution: "player.style@npm:0.0.8" - dependencies: - media-chrome: ^4.1.0 - checksum: 009f59bc8e7f84e82adcacec4df3ea11094d7097413a5755bb6a586150c9aa6114f7d7840f215b8b98c20f3eabbf40b7eb1d20731384253fea3122bd62b9512e - languageName: node - linkType: hard - "playwright-core@npm:1.45.3": version: 1.45.3 resolution: "playwright-core@npm:1.45.3" @@ -40892,21 +40746,20 @@ __metadata: languageName: node linkType: hard -"react-datocms@npm:^5.0.3": - version: 5.0.3 - resolution: "react-datocms@npm:5.0.3" +"react-datocms@npm:^3.1.0": + version: 3.1.4 + resolution: "react-datocms@npm:3.1.4" dependencies: - "@mux/mux-player-react": "*" datocms-listen: ^0.1.9 datocms-structured-text-generic-html-renderer: ^2.0.1 datocms-structured-text-utils: ^2.0.1 - react-intersection-observer: ^9.4.3 + react-intersection-observer: ^8.33.1 react-string-replace: ^1.1.0 universal-base64: ^2.1.0 use-deep-compare-effect: ^1.6.1 peerDependencies: react: ">= 16.12.0" - checksum: 22c20152afb54424acfe967a2c8c525cd9f132a33374f2aba0231f16ea64dade389b096e2dac8de9ffded612bc32e9891d725609ee947639fe1cef907cb143f5 + checksum: 54aba12aef4937175c2011548a8a576c96c8d8a596e84d191826910624c1d596e76a49782689dc236388a10803b02e700ac820cb7500cca7fd147a81f6c544c3 languageName: node linkType: hard @@ -41167,16 +41020,12 @@ __metadata: languageName: node linkType: hard -"react-intersection-observer@npm:^9.4.3": - version: 9.13.1 - resolution: "react-intersection-observer@npm:9.13.1" +"react-intersection-observer@npm:^8.33.1": + version: 8.34.0 + resolution: "react-intersection-observer@npm:8.34.0" peerDependencies: - react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - react-dom: - optional: true - checksum: 92b3e7d9293365635bf5ede89b912a670030c6244879dce36fa9437e6749c637151087fbcaa72cc160b2fa7045bd5ac861a8535e0a2b7b8f230f11f2e0391005 + react: ^15.0.0 || ^16.0.0 || ^17.0.0|| ^18.0.0 + checksum: 7713fecfd1512c7f5a60f9f0bf15403b8f8bbd4110bcafaeaea6de36a0e0eb60368c3638f99e9c97b75ad8fc787ea48c241dcb5c694f821d7f2976f709082cc5 languageName: node linkType: hard @@ -41720,18 +41569,6 @@ __metadata: languageName: node linkType: hard -"react-twitter-embed@npm:^4.0.4": - version: 4.0.4 - resolution: "react-twitter-embed@npm:4.0.4" - dependencies: - scriptjs: ^2.5.9 - peerDependencies: - react: ^16.0.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 - checksum: cdb3c5bd04c4da0efa767476be47c0a3865fb6335f2a1b9e242170167b51615c38164223278cef60c77143c4bac27ba582cbea054d0af3f138104fa5ec537c4c - languageName: node - linkType: hard - "react-universal-interface@npm:^0.6.2": version: 0.6.2 resolution: "react-universal-interface@npm:0.6.2" @@ -43619,13 +43456,6 @@ __metadata: languageName: node linkType: hard -"scriptjs@npm:^2.5.9": - version: 2.5.9 - resolution: "scriptjs@npm:2.5.9" - checksum: fc84cb6b60b6fb9aa6f1b3bc59fc94b233bd5241ed3a04233579014382b5eb60640269c87d8657902acc09f9b785ee33230c218627cea00e653564bda8f5acb6 - languageName: node - linkType: hard - "scuid@npm:^1.1.0": version: 1.1.0 resolution: "scuid@npm:1.1.0" @@ -47020,12 +46850,12 @@ __metadata: languageName: node linkType: hard -"turndown@npm:^7.1.1": - version: 7.1.1 - resolution: "turndown@npm:7.1.1" +"turndown@npm:^7.1.3": + version: 7.2.0 + resolution: "turndown@npm:7.2.0" dependencies: - domino: ^2.1.6 - checksum: f2d77632bd69bc93298b619f051dc0c050fabee15188afe874eab713f8ffdcaea89106a536c19db0962a677b5f7177f5aa84b45ef38e06d6e489a49f11abd80d + "@mixmark-io/domino": ^2.2.0 + checksum: a0bdf2fcc8cd531c94edbd7c384ae58a13251e7b472f7245fac74a316c08f8d04eb9dddfce6a5e6170ddb09c0494154a0d69312e5d9c2a7223a936a7e369ac7c languageName: node linkType: hard @@ -50057,4 +49887,4 @@ __metadata: resolution: "zwitch@npm:2.0.4" checksum: f22ec5fc2d5f02c423c93d35cdfa83573a3a3bd98c66b927c368ea4d0e7252a500df2a90a6b45522be536a96a73404393c958e945fdba95e6832c200791702b6 languageName: node - linkType: hard \ No newline at end of file + linkType: hard