refactor: platform bookings hooks & bookings API (#16963)

* chore: delete unused bookings hook

* refactor: group bookings hooks in folder

* refactor: rename useGetBooking, useGetBookings to useBooking, useBookings

* refactor: booking inputs and outputs

* chore: make bookings controller work with new inputs and outputs

* make examples app work

* refactor: deprecate fields instead of removing them

* readd eventtypeid

* swagger regenerate

* make recurring booking backwards compatible

* fix test

* update changelog

* swagger update

* fix import

* chore: update lock file
This commit is contained in:
Lauris Skraucis authored and GitHub committed 2024-10-08 14:05:28 +02:00
1 parent d99f171597
commit a10d3599e2
32 files changed
+1308 -824

No files matched your search

@@ -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,
},
});
}
@@ -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,
@@ -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";
@@ -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);
@@ -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`);
@@ -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
@@ -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" });
File diff suppressed because it is too large. Load diff
+67
View File
@@ -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
@@ -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";
@@ -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<GetBookingOutput_2024_08_13>(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;
};
@@ -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<GetBookingsOutput_2024_08_13>(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;
};
@@ -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;
@@ -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 };
@@ -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<BookingResponse>) => void;
@@ -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<BookingResponse[]>) => void;
@@ -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";
@@ -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<Date | undefined>();
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();
};
@@ -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<ApiResponse<Awaited<ReturnType<typeof getBookingInfo>>["bookingInfo"]>>(pathname)
.then((res) => {
if (res.data.status === SUCCESS_STATUS) {
return (res.data as ApiSuccessResponse<Awaited<ReturnType<typeof getBookingInfo>>["bookingInfo"]>)
.data;
}
throw new Error(res.data.error.message);
});
},
enabled: !!uid,
});
return bookingQuery;
};
@@ -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<ApiResponse<Awaited<ReturnType<typeof getAllUserBookings>>>>(pathname, {
params: input,
})
.then((res) => {
if (res.data.status === SUCCESS_STATUS) {
return (res.data as ApiSuccessResponse<Awaited<ReturnType<typeof getAllUserBookings>>>).data;
}
throw new Error(res.data.error.message);
});
},
});
return bookingsQuery;
};
+3 -3
View File
@@ -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";
+1 -1
View File
@@ -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";
@@ -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 (
<main
className={`flex min-h-screen flex-col ${inter.className} main text-default flex min-h-full w-full flex-col items-center overflow-visible`}>
<Navbar username={props.calUsername} />
{isLoading && <p>Loading...</p>}
{!isLoading && booking && (
<div
key={booking.id}
className="my-10 w-[440px] overflow-hidden rounded-md border-[0.7px] border-black px-10 py-5">
{booking.status === "ACCEPTED" ? (
<div className="mx-2 my-4 flex flex-col items-center justify-center text-center">
<Icon
name="circle-check-big"
className="my-5 flex h-[40px] w-[40px] rounded-full bg-green-500"
/>
<h1 className="text-xl font-bold">This meeting is scheduled</h1>
<p>We sent an email with a calendar invitation with the details to everyone.</p>
</div>
) : (
<div className="mx-2 my-4 flex flex-col items-center justify-center text-center">
<Icon name="x" className="my-5 flex h-[40px] w-[40px] rounded-full bg-red-400" />
<h4 className="text-2xl font-bold">This event is cancelled</h4>
</div>
)}
<hr className="mx-2 bg-black text-black" />
<div className="mx-2 my-7 flex flex-col gap-y-3">
<div className="flex gap-[70px]">
<div>
<h4>What</h4>
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 (
<main
className={`flex min-h-screen flex-col ${inter.className} main text-default flex min-h-full w-full flex-col items-center overflow-visible`}>
<Navbar username={props.calUsername} />
{isLoading && <p>Loading...</p>}
{!isLoading && booking && (
<div
key={booking.id}
className="my-10 w-[440px] overflow-hidden rounded-md border-[0.7px] border-black px-10 py-5">
{booking.status === "accepted" ? (
<div className="mx-2 my-4 flex flex-col items-center justify-center text-center">
<Icon
name="circle-check-big"
className="my-5 flex h-[40px] w-[40px] rounded-full bg-green-500"
/>
<h1 className="text-xl font-bold">This meeting is scheduled</h1>
<p>We sent an email with a calendar invitation with the details to everyone.</p>
</div>
<div>
<p>{booking.title}</p>
) : (
<div className="mx-2 my-4 flex flex-col items-center justify-center text-center">
<Icon name="x" className="my-5 flex h-[40px] w-[40px] rounded-full bg-red-400" />
<h4 className="text-2xl font-bold">This event is cancelled</h4>
</div>
</div>
<div className="flex gap-[70px]">
<div>
<h4>When</h4>
</div>
<div>
)}
<hr className="mx-2 bg-black text-black" />
<div className="mx-2 my-7 flex flex-col gap-y-3">
<div className="flex gap-[70px]">
<div>
<p
style={{
textDecoration: booking.status === "ACCEPTED" ? "normal" : "line-through",
}}>
{`${day}, ${month} ${dateToday}, ${year}`}
</p>
<h4>What</h4>
</div>
<div>
<p
style={{
textDecoration: booking.status === "ACCEPTED" ? "normal" : "line-through",
}}>
{`${startTime}`} - {`${endTime}`}
</p>
<p>{booking.title}</p>
</div>
</div>
</div>
<div className="flex gap-[70px]">
<div>Who</div>
<div>
<div className="flex gap-[70px]">
<div>
<h4>When</h4>
</div>
<div>
<div>
<h4>
{booking.user?.name}{" "}
<span className="rounded-md bg-blue-800 px-2 text-sm text-white">Host</span>
</h4>
<p>{booking.user?.email}</p>
<p
style={{
textDecoration: booking.status === "accepted" ? "normal" : "line-through",
}}>
{`${day}, ${month} ${dateToday}, ${year}`}
</p>
</div>
<div>
<p
style={{
textDecoration: booking.status === "accepted" ? "normal" : "line-through",
}}>
{`${startTime}`} - {`${endTime}`}
</p>
</div>
</div>
{booking.attendees.map((attendee, i) => {
return (
<div key={`${i}-${attendee.name}`}>
<br />
<div>
<h4>{`${attendee.name}`}</h4>
<p>{`${attendee.email}`}</p>
</div>
</div>
<div className="flex gap-[70px]">
<div>Who</div>
<div>
<div>
<div>
<h4>
{booking.hosts[0]?.name}{" "}
<span className="rounded-md bg-blue-800 px-2 text-sm text-white">Host</span>
</h4>
</div>
);
})}
</div>
{booking.attendees.map((attendee, i) => {
return (
<div key={`${i}-${attendee.name}`}>
<br />
<div>
<h4>{`${attendee.name}`}</h4>
</div>
</div>
);
})}
</div>
</div>
{!!booking.location && (
<div className="flex gap-[70px]">
<div>
<h4>Where</h4>
</div>
<div>
<p>{booking.location}</p>
</div>
</div>
)}
{!!booking.bookingFieldsResponses?.notes && (
<div className="flex gap-[70px]">
<div className="w-[40px]">
<h4>Additional notes</h4>
</div>
<div>
<p>{`${booking.bookingFieldsResponses.notes}`}</p>
</div>
</div>
)}
</div>
{!!booking.location && booking.location.startsWith("http") && (
<div className="flex gap-[70px]">
<div>
<h4>Where</h4>
{booking.status === "accepted" && (
<>
<hr className="mx-3" />
<div className="mx-2 my-3 text-center">
<p>
Need to make a change?{" "}
<button
className="underline"
onClick={() => {
router.push(
`/booking?rescheduleUid=${booking?.uid}&eventTypeSlug=${booking?.eventType.slug}`
);
}}>
Reschedule
</button>{" "}
or{" "}
<button
className="underline"
onClick={() => {
cancelBooking({
id: booking.id,
uid: booking.uid,
cancellationReason: "User request",
allRemainingBookings: true,
});
}}>
Cancel
</button>
</p>
</div>
<div>
<p>{booking.location}</p>
</div>
</div>
)}
{booking.responses.notes && (
<div className="flex gap-[70px]">
<div className="w-[40px]">
<h4>Additional notes</h4>
</div>
<div>
<p>{`${booking.responses.notes}`}</p>
</div>
</div>
</>
)}
</div>
{booking.status === "ACCEPTED" && (
<>
<hr className="mx-3" />
<div className="mx-2 my-3 text-center">
<p>
Need to make a change?{" "}
<button
className="underline"
onClick={() => {
router.push(
`/booking?rescheduleUid=${booking?.uid}&eventTypeSlug=${booking?.eventType?.slug}`
);
}}>
Reschedule
</button>{" "}
or{" "}
<button
className="underline"
onClick={() => {
cancelBooking({
id: booking.id,
uid: booking.uid,
cancellationReason: "User request",
allRemainingBookings: true,
});
}}>
Cancel
</button>
</p>
</div>
</>
)}
</div>
)}
</main>
);
)}
</main>
);
}
}
@@ -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
<h1 className="my-4 text-2xl font-semibold">{props.calUsername} Bookings</h1>
{isLoading && <p>Loading...</p>}
{!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}`);
}}>
<div>
<div className="px-6">{`${day}, ${dayjs(booking.startTime).date()} ${month}`}</div>
<div className="px-6">{`${day}, ${dayjs(booking.start).date()} ${month}`}</div>
<div className="px-6">
<p>
{startTime} - {endTime}
@@ -64,7 +64,7 @@ export default function Bookings(props: { calUsername: string; calEmail: string
</div>
<div className="px-6">
<p>
{booking?.user?.name} and {booking.attendees[0].name}
{booking?.hosts?.[0]?.name} and {booking.attendees[0].name}
</p>{" "}
<p />
</div>
@@ -30,7 +30,6 @@ export default function Bookings(props: { calUsername: string; calEmail: string
{!isLoadingEvents && !eventTypeId && Boolean(eventTypes?.length) && !rescheduleUid && (
<div className="flex flex-col gap-4">
<h1>User event types</h1>
{eventTypes?.map(
(event: { id: number; slug: string; title: string; lengthInMinutes: number }) => {
const formatEventSlug = event.slug
@@ -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({
@@ -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<string, unknown>;
}
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<string, unknown>;
}
@@ -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;
}
@@ -23,4 +23,6 @@ export class GetBookingsOutput_2024_08_13 {
})
@ValidateNested({ each: true })
data!: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[];
error?: Error;
}
@@ -1 +1,3 @@
export * from "./booking.output";
export * from "./get-booking.output";
export * from "./get-bookings.output";
+37 -207
View File
@@ -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
linkType: hard