Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e33560ef82 | ||
|
|
84f34b2244 | ||
|
|
086bae03d3 | ||
|
|
4a0a429774 | ||
|
|
c4142ab4e7 | ||
|
|
95f8046db9 | ||
|
|
d8a87e9f04 | ||
|
|
86dcd6f0ff | ||
|
|
411a14dd3d | ||
|
|
03bc8db64a | ||
|
|
fe7fd8bc79 | ||
|
|
92eb41c117 | ||
|
|
a1fca56aac |
@@ -19,5 +19,17 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
files: ["packages/app-store/**/*.{ts,tsx,js,jsx}"],
|
||||||
|
rules: {
|
||||||
|
"no-restricted-imports": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
paths: ["@calcom/trpc"],
|
||||||
|
patterns: ["@calcom/trpc/*"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
Vendored
@@ -38,7 +38,7 @@
|
|||||||
"@axiomhq/winston": "^1.2.0",
|
"@axiomhq/winston": "^1.2.0",
|
||||||
"@calcom/platform-constants": "*",
|
"@calcom/platform-constants": "*",
|
||||||
"@calcom/platform-enums": "*",
|
"@calcom/platform-enums": "*",
|
||||||
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.344",
|
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.347",
|
||||||
"@calcom/platform-types": "*",
|
"@calcom/platform-types": "*",
|
||||||
"@calcom/platform-utils": "*",
|
"@calcom/platform-utils": "*",
|
||||||
"@calcom/prisma": "*",
|
"@calcom/prisma": "*",
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
|||||||
slug: seatedEventTypeSlug,
|
slug: seatedEventTypeSlug,
|
||||||
length: 60,
|
length: 60,
|
||||||
seatsPerTimeSlot: 3,
|
seatsPerTimeSlot: 3,
|
||||||
|
seatsShowAttendees: true,
|
||||||
},
|
},
|
||||||
user.id
|
user.id
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -53,8 +53,10 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
|||||||
let apiKeyString: string;
|
let apiKeyString: string;
|
||||||
|
|
||||||
let seatedEventTypeId: number;
|
let seatedEventTypeId: number;
|
||||||
|
let seatedEventTypeIdAttendeesDisabledId: number;
|
||||||
|
|
||||||
const seatedEventSlug = `seated-bookings-event-type-${randomString()}`;
|
const seatedEventSlug = `seated-bookings-event-type-${randomString()}`;
|
||||||
|
const seatedEventSlugAttendeesDisabled = `seated-bookings-event-type-attendees-disabled-${randomString()}`;
|
||||||
|
|
||||||
let createdSeatedBooking: CreateSeatedBookingOutput_2024_08_13;
|
let createdSeatedBooking: CreateSeatedBookingOutput_2024_08_13;
|
||||||
let createdSeatedBooking2: CreateSeatedBookingOutput_2024_08_13;
|
let createdSeatedBooking2: CreateSeatedBookingOutput_2024_08_13;
|
||||||
@@ -121,6 +123,28 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
|||||||
);
|
);
|
||||||
seatedEventTypeId = seatedEvent.id;
|
seatedEventTypeId = seatedEvent.id;
|
||||||
|
|
||||||
|
const seatedEventAttendeesDisabled = await eventTypesRepositoryFixture.create(
|
||||||
|
{
|
||||||
|
title: `seated-bookings-2024-08-13-event-type-attendees-disabled-${randomString()}`,
|
||||||
|
slug: seatedEventSlugAttendeesDisabled,
|
||||||
|
length: 60,
|
||||||
|
seatsPerTimeSlot: 5,
|
||||||
|
seatsShowAttendees: false,
|
||||||
|
seatsShowAvailabilityCount: false,
|
||||||
|
locations: [{ type: "inPerson", address: "via 10, rome, italy" }],
|
||||||
|
metadata: {
|
||||||
|
disableStandardEmails: {
|
||||||
|
all: {
|
||||||
|
attendee: true,
|
||||||
|
host: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
user.id
|
||||||
|
);
|
||||||
|
seatedEventTypeIdAttendeesDisabledId = seatedEventAttendeesDisabled.id;
|
||||||
|
|
||||||
app = moduleRef.createNestApplication();
|
app = moduleRef.createNestApplication();
|
||||||
bootstrap(app as NestExpressApplication);
|
bootstrap(app as NestExpressApplication);
|
||||||
|
|
||||||
@@ -414,6 +438,63 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should book an event type with attendees disabled", async () => {
|
||||||
|
const body: CreateBookingInput_2024_08_13 = {
|
||||||
|
start: new Date(Date.UTC(2030, 0, 9, 14, 0, 0)).toISOString(),
|
||||||
|
eventTypeId: seatedEventTypeIdAttendeesDisabledId,
|
||||||
|
attendee: {
|
||||||
|
name: nameAttendeeOne,
|
||||||
|
email: emailAttendeeOne,
|
||||||
|
timeZone: "Europe/Rome",
|
||||||
|
language: "it",
|
||||||
|
},
|
||||||
|
bookingFieldsResponses: {
|
||||||
|
codingLanguage: "TypeScript",
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
userId: "100",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post("/v2/bookings")
|
||||||
|
.send(body)
|
||||||
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
|
||||||
|
.expect(201)
|
||||||
|
.then(async (response) => {
|
||||||
|
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
|
||||||
|
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||||
|
expect(responseBody.data).toBeDefined();
|
||||||
|
expect(responseDataIsCreateSeatedBooking(responseBody.data)).toBe(true);
|
||||||
|
|
||||||
|
if (responseDataIsCreateSeatedBooking(responseBody.data)) {
|
||||||
|
const data: CreateSeatedBookingOutput_2024_08_13 = responseBody.data;
|
||||||
|
expect(data.seatUid).toBeDefined();
|
||||||
|
expect(data.id).toBeDefined();
|
||||||
|
expect(data.uid).toBeDefined();
|
||||||
|
expect(data.hosts[0].id).toEqual(user.id);
|
||||||
|
expect(data.status).toEqual("accepted");
|
||||||
|
expect(data.start).toEqual(body.start);
|
||||||
|
expect(data.end).toEqual(
|
||||||
|
DateTime.fromISO(body.start, { zone: "utc" }).plus({ hours: 1 }).toISO()
|
||||||
|
);
|
||||||
|
expect(data.duration).toEqual(60);
|
||||||
|
expect(data.eventTypeId).toEqual(seatedEventTypeIdAttendeesDisabledId);
|
||||||
|
expect(data.eventType).toEqual({
|
||||||
|
id: seatedEventTypeIdAttendeesDisabledId,
|
||||||
|
slug: seatedEventSlugAttendeesDisabled,
|
||||||
|
});
|
||||||
|
expect(data.attendees.length).toEqual(0);
|
||||||
|
expect(data.location).toBeDefined();
|
||||||
|
expect(data.absentHost).toEqual(false);
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
"Invalid response data - expected seated booking but received non array response"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("cancel seated booking", () => {
|
describe("cancel seated booking", () => {
|
||||||
describe("cancel seated booking as attendee", () => {
|
describe("cancel seated booking as attendee", () => {
|
||||||
it("should cancel seated booking", async () => {
|
it("should cancel seated booking", async () => {
|
||||||
|
|||||||
@@ -576,10 +576,13 @@ export class BookingsService_2024_08_13 {
|
|||||||
return this.outputService.getOutputRecurringBooking(booking);
|
return this.outputService.getOutputRecurringBooking(booking);
|
||||||
}
|
}
|
||||||
if (isRecurring && isSeated) {
|
if (isRecurring && isSeated) {
|
||||||
return this.outputService.getOutputRecurringSeatedBooking(booking);
|
return this.outputService.getOutputRecurringSeatedBooking(
|
||||||
|
booking,
|
||||||
|
!!booking.eventType?.seatsShowAttendees
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (isSeated) {
|
if (isSeated) {
|
||||||
return this.outputService.getOutputSeatedBooking(booking);
|
return this.outputService.getOutputSeatedBooking(booking, !!booking.eventType?.seatsShowAttendees);
|
||||||
}
|
}
|
||||||
return this.outputService.getOutputBooking(booking);
|
return this.outputService.getOutputBooking(booking);
|
||||||
}
|
}
|
||||||
@@ -591,7 +594,10 @@ export class BookingsService_2024_08_13 {
|
|||||||
const ids = recurringBooking.map((booking) => booking.id);
|
const ids = recurringBooking.map((booking) => booking.id);
|
||||||
const isRecurringSeated = !!recurringBooking[0].eventType?.seatsPerTimeSlot;
|
const isRecurringSeated = !!recurringBooking[0].eventType?.seatsPerTimeSlot;
|
||||||
if (isRecurringSeated) {
|
if (isRecurringSeated) {
|
||||||
return this.outputService.getOutputRecurringSeatedBookings(ids);
|
return this.outputService.getOutputRecurringSeatedBookings(
|
||||||
|
ids,
|
||||||
|
!!recurringBooking[0].eventType?.seatsShowAttendees
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.outputService.getOutputRecurringBookings(ids);
|
return this.outputService.getOutputRecurringBookings(ids);
|
||||||
@@ -657,9 +663,19 @@ export class BookingsService_2024_08_13 {
|
|||||||
if (isRecurring && !isSeated) {
|
if (isRecurring && !isSeated) {
|
||||||
formattedBookings.push(this.outputService.getOutputRecurringBooking(formatted));
|
formattedBookings.push(this.outputService.getOutputRecurringBooking(formatted));
|
||||||
} else if (isRecurring && isSeated) {
|
} else if (isRecurring && isSeated) {
|
||||||
formattedBookings.push(this.outputService.getOutputRecurringSeatedBooking(formatted));
|
formattedBookings.push(
|
||||||
|
this.outputService.getOutputRecurringSeatedBooking(
|
||||||
|
formatted,
|
||||||
|
!!formatted.eventType?.seatsShowAttendees
|
||||||
|
)
|
||||||
|
);
|
||||||
} else if (isSeated) {
|
} else if (isSeated) {
|
||||||
formattedBookings.push(await this.outputService.getOutputSeatedBooking(formatted));
|
formattedBookings.push(
|
||||||
|
await this.outputService.getOutputSeatedBooking(
|
||||||
|
formatted,
|
||||||
|
!!formatted.eventType?.seatsShowAttendees
|
||||||
|
)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
formattedBookings.push(await this.outputService.getOutputBooking(formatted));
|
formattedBookings.push(await this.outputService.getOutputBooking(formatted));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ type DatabaseBooking = Booking & {
|
|||||||
eventType: {
|
eventType: {
|
||||||
id: number;
|
id: number;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
seatsShowAttendees?: boolean | null;
|
||||||
} | null;
|
} | null;
|
||||||
attendees: {
|
attendees: {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -167,6 +168,7 @@ export class OutputBookingsService_2024_08_13 {
|
|||||||
getUserDefinedMetadata(databaseMetadata: DatabaseMetadata) {
|
getUserDefinedMetadata(databaseMetadata: DatabaseMetadata) {
|
||||||
if (databaseMetadata === null) return {};
|
if (databaseMetadata === null) return {};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const { videoCallUrl, ...userDefinedMetadata } = databaseMetadata;
|
const { videoCallUrl, ...userDefinedMetadata } = databaseMetadata;
|
||||||
|
|
||||||
return userDefinedMetadata;
|
return userDefinedMetadata;
|
||||||
@@ -266,11 +268,14 @@ export class OutputBookingsService_2024_08_13 {
|
|||||||
databaseBooking: DatabaseBooking,
|
databaseBooking: DatabaseBooking,
|
||||||
seatUid: string
|
seatUid: string
|
||||||
): Promise<CreateSeatedBookingOutput_2024_08_13> {
|
): Promise<CreateSeatedBookingOutput_2024_08_13> {
|
||||||
const getSeatedBookingOutput = await this.getOutputSeatedBooking(databaseBooking);
|
const getSeatedBookingOutput = await this.getOutputSeatedBooking(
|
||||||
|
databaseBooking,
|
||||||
|
!!databaseBooking.eventType?.seatsShowAttendees
|
||||||
|
);
|
||||||
return { ...getSeatedBookingOutput, seatUid };
|
return { ...getSeatedBookingOutput, seatUid };
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOutputSeatedBooking(databaseBooking: DatabaseBooking) {
|
async getOutputSeatedBooking(databaseBooking: DatabaseBooking, showAttendees: boolean) {
|
||||||
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
|
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
|
||||||
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
|
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
|
||||||
const duration = dateEnd.diff(dateStart, "minutes").minutes;
|
const duration = dateEnd.diff(dateStart, "minutes").minutes;
|
||||||
@@ -317,41 +322,43 @@ export class OutputBookingsService_2024_08_13 {
|
|||||||
const parsed = plainToClass(GetSeatedBookingOutput_2024_08_13, booking, { strategy: "excludeAll" });
|
const parsed = plainToClass(GetSeatedBookingOutput_2024_08_13, booking, { strategy: "excludeAll" });
|
||||||
|
|
||||||
// note(Lauris): I don't know why plainToClass erases booking.attendees[n].responses so attaching manually
|
// note(Lauris): I don't know why plainToClass erases booking.attendees[n].responses so attaching manually
|
||||||
parsed.attendees = databaseBooking.attendees.map((attendee) => {
|
parsed.attendees = showAttendees
|
||||||
const { responses } = safeParse(
|
? databaseBooking.attendees.map((attendee) => {
|
||||||
seatedBookingDataSchema,
|
const { responses } = safeParse(
|
||||||
attendee.bookingSeat?.data,
|
seatedBookingDataSchema,
|
||||||
defaultSeatedBookingData,
|
attendee.bookingSeat?.data,
|
||||||
false
|
defaultSeatedBookingData,
|
||||||
);
|
false
|
||||||
|
);
|
||||||
|
|
||||||
const attendeeData = {
|
const attendeeData = {
|
||||||
name: attendee.name,
|
name: attendee.name,
|
||||||
email: attendee.email,
|
email: attendee.email,
|
||||||
timeZone: attendee.timeZone,
|
timeZone: attendee.timeZone,
|
||||||
language: attendee.locale,
|
language: attendee.locale,
|
||||||
absent: !!attendee.noShow,
|
absent: !!attendee.noShow,
|
||||||
seatUid: attendee.bookingSeat?.referenceUid,
|
seatUid: attendee.bookingSeat?.referenceUid,
|
||||||
bookingFieldsResponses: {},
|
bookingFieldsResponses: {},
|
||||||
};
|
};
|
||||||
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
|
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
|
||||||
attendeeParsed.bookingFieldsResponses = responses || {};
|
attendeeParsed.bookingFieldsResponses = responses || {};
|
||||||
attendeeParsed.metadata = safeParse(
|
attendeeParsed.metadata = safeParse(
|
||||||
seatedBookingMetadataSchema,
|
seatedBookingMetadataSchema,
|
||||||
attendee.bookingSeat?.metadata,
|
attendee.bookingSeat?.metadata,
|
||||||
defaultSeatedBookingMetadata,
|
defaultSeatedBookingMetadata,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
// note(Lauris): as of now email is not returned for privacy
|
// note(Lauris): as of now email is not returned for privacy
|
||||||
delete attendeeParsed.bookingFieldsResponses.email;
|
delete attendeeParsed.bookingFieldsResponses.email;
|
||||||
|
|
||||||
return attendeeParsed;
|
return attendeeParsed;
|
||||||
});
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOutputRecurringSeatedBookings(bookingsIds: number[]) {
|
async getOutputRecurringSeatedBookings(bookingsIds: number[], showAttendees: boolean) {
|
||||||
const transformed = [];
|
const transformed = [];
|
||||||
|
|
||||||
for (const bookingId of bookingsIds) {
|
for (const bookingId of bookingsIds) {
|
||||||
@@ -361,7 +368,7 @@ export class OutputBookingsService_2024_08_13 {
|
|||||||
throw new Error(`Booking with id=${bookingId} was not found in the database`);
|
throw new Error(`Booking with id=${bookingId} was not found in the database`);
|
||||||
}
|
}
|
||||||
|
|
||||||
transformed.push(this.getOutputRecurringSeatedBooking(databaseBooking));
|
transformed.push(this.getOutputRecurringSeatedBooking(databaseBooking, showAttendees));
|
||||||
}
|
}
|
||||||
|
|
||||||
return transformed.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime());
|
return transformed.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime());
|
||||||
@@ -386,11 +393,14 @@ export class OutputBookingsService_2024_08_13 {
|
|||||||
databaseBooking: DatabaseBooking,
|
databaseBooking: DatabaseBooking,
|
||||||
seatUid: string
|
seatUid: string
|
||||||
): CreateRecurringSeatedBookingOutput_2024_08_13 {
|
): CreateRecurringSeatedBookingOutput_2024_08_13 {
|
||||||
const getRecurringSeatedBookingOutput = this.getOutputRecurringSeatedBooking(databaseBooking);
|
const getRecurringSeatedBookingOutput = this.getOutputRecurringSeatedBooking(
|
||||||
|
databaseBooking,
|
||||||
|
!!databaseBooking.eventType?.seatsShowAttendees
|
||||||
|
);
|
||||||
return { ...getRecurringSeatedBookingOutput, seatUid };
|
return { ...getRecurringSeatedBookingOutput, seatUid };
|
||||||
}
|
}
|
||||||
|
|
||||||
getOutputRecurringSeatedBooking(databaseBooking: DatabaseBooking) {
|
getOutputRecurringSeatedBooking(databaseBooking: DatabaseBooking, showAttendees: boolean) {
|
||||||
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
|
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
|
||||||
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
|
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
|
||||||
const duration = dateEnd.diff(dateStart, "minutes").minutes;
|
const duration = dateEnd.diff(dateStart, "minutes").minutes;
|
||||||
@@ -430,35 +440,37 @@ export class OutputBookingsService_2024_08_13 {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// note(Lauris): I don't know why plainToClass erases booking.attendees[n].responses so attaching manually
|
// note(Lauris): I don't know why plainToClass erases booking.attendees[n].responses so attaching manually
|
||||||
parsed.attendees = databaseBooking.attendees.map((attendee) => {
|
parsed.attendees = showAttendees
|
||||||
const { responses } = safeParse(
|
? databaseBooking.attendees.map((attendee) => {
|
||||||
seatedBookingDataSchema,
|
const { responses } = safeParse(
|
||||||
attendee.bookingSeat?.data,
|
seatedBookingDataSchema,
|
||||||
defaultSeatedBookingData,
|
attendee.bookingSeat?.data,
|
||||||
false
|
defaultSeatedBookingData,
|
||||||
);
|
false
|
||||||
|
);
|
||||||
|
|
||||||
const attendeeData = {
|
const attendeeData = {
|
||||||
name: attendee.name,
|
name: attendee.name,
|
||||||
email: attendee.email,
|
email: attendee.email,
|
||||||
timeZone: attendee.timeZone,
|
timeZone: attendee.timeZone,
|
||||||
language: attendee.locale,
|
language: attendee.locale,
|
||||||
absent: !!attendee.noShow,
|
absent: !!attendee.noShow,
|
||||||
seatUid: attendee.bookingSeat?.referenceUid,
|
seatUid: attendee.bookingSeat?.referenceUid,
|
||||||
bookingFieldsResponses: {},
|
bookingFieldsResponses: {},
|
||||||
};
|
};
|
||||||
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
|
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
|
||||||
attendeeParsed.bookingFieldsResponses = responses || {};
|
attendeeParsed.bookingFieldsResponses = responses || {};
|
||||||
attendeeParsed.metadata = safeParse(
|
attendeeParsed.metadata = safeParse(
|
||||||
seatedBookingMetadataSchema,
|
seatedBookingMetadataSchema,
|
||||||
attendee.bookingSeat?.metadata,
|
attendee.bookingSeat?.metadata,
|
||||||
defaultSeatedBookingMetadata,
|
defaultSeatedBookingMetadata,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
// note(Lauris): as of now email is not returned for privacy
|
// note(Lauris): as of now email is not returned for privacy
|
||||||
delete attendeeParsed.bookingFieldsResponses.email;
|
delete attendeeParsed.bookingFieldsResponses.email;
|
||||||
return attendeeParsed;
|
return attendeeParsed;
|
||||||
});
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import type { PageProps as _PageProps } from "app/_types";
|
|||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { StripeService } from "@calcom/lib/server/service/stripe";
|
|
||||||
|
|
||||||
import VerifyPage from "~/auth/verify-view";
|
import VerifyPage from "~/auth/verify-view";
|
||||||
|
|
||||||
const querySchema = z.object({
|
const querySchema = z.object({
|
||||||
@@ -14,18 +12,10 @@ const querySchema = z.object({
|
|||||||
|
|
||||||
export const generateMetadata = async ({ params, searchParams }: _PageProps) => {
|
export const generateMetadata = async ({ params, searchParams }: _PageProps) => {
|
||||||
const p = { ...(await params), ...(await searchParams) };
|
const p = { ...(await params), ...(await searchParams) };
|
||||||
const { sessionId, stripeCustomerId } = querySchema.parse(p);
|
const { sessionId } = querySchema.parse(p);
|
||||||
|
|
||||||
const data = await StripeService.getCheckoutSession({
|
|
||||||
stripeCustomerId,
|
|
||||||
checkoutSessionId: sessionId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { hasPaymentFailed } = data;
|
|
||||||
|
|
||||||
return await _generateMetadata(
|
return await _generateMetadata(
|
||||||
() =>
|
() => (sessionId ? "Payment Page" : `Verify your email`),
|
||||||
hasPaymentFailed ? "Your payment failed" : sessionId ? "Payment successful!" : `Verify your email`,
|
|
||||||
() => "",
|
() => "",
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const resolveEndpoint = (links: any) => {
|
|||||||
// to the correct API endpoints.
|
// to the correct API endpoints.
|
||||||
// - viewer.me - 2 segment paths like this are for logged in requests
|
// - viewer.me - 2 segment paths like this are for logged in requests
|
||||||
// - viewer.public.i18n - 3 segments paths can be public or authed
|
// - viewer.public.i18n - 3 segments paths can be public or authed
|
||||||
|
// - viewer.eventTypes.heavy.create - 4 segments paths for heavy sub-router
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
return (ctx: any) => {
|
return (ctx: any) => {
|
||||||
const parts = ctx.op.path.split(".");
|
const parts = ctx.op.path.split(".");
|
||||||
@@ -23,7 +24,11 @@ const resolveEndpoint = (links: any) => {
|
|||||||
if (parts.length == 2) {
|
if (parts.length == 2) {
|
||||||
endpoint = parts[0] as keyof typeof links;
|
endpoint = parts[0] as keyof typeof links;
|
||||||
path = parts[1];
|
path = parts[1];
|
||||||
} else {
|
} else if (parts.length >= 3 && parts[2] === "heavy") {
|
||||||
|
endpoint = parts[1] + "/heavy" as keyof typeof links;
|
||||||
|
path = parts[3];
|
||||||
|
}
|
||||||
|
else {
|
||||||
endpoint = parts[1] as keyof typeof links;
|
endpoint = parts[1] as keyof typeof links;
|
||||||
path = parts.splice(2, parts.length - 2).join(".");
|
path = parts.splice(2, parts.length - 2).join(".");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ const UserProfile = ({ user }: UserProfileProps) => {
|
|||||||
const [imageSrc, setImageSrc] = useState<string>(user?.avatar || "");
|
const [imageSrc, setImageSrc] = useState<string>(user?.avatar || "");
|
||||||
const utils = trpc.useUtils();
|
const utils = trpc.useUtils();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const createEventType = trpc.viewer.eventTypes.create.useMutation();
|
const createEventType = trpc.viewer.eventTypes.heavy.create.useMutation();
|
||||||
const telemetry = useTelemetry();
|
const telemetry = useTelemetry();
|
||||||
const [firstRender, setFirstRender] = useState(true);
|
const [firstRender, setFirstRender] = useState(true);
|
||||||
|
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ const OnboardingPage = ({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [eventTypeGroups]);
|
}, [eventTypeGroups]);
|
||||||
|
|
||||||
const updateMutation = trpc.viewer.eventTypes.update.useMutation({
|
const updateMutation = trpc.viewer.eventTypes.heavy.update.useMutation({
|
||||||
onSuccess: async (data) => {
|
onSuccess: async (data) => {
|
||||||
showToast(t("event_type_updated_successfully", { eventTypeTitle: data.eventType?.title }), "success");
|
showToast(t("event_type_updated_successfully", { eventTypeTitle: data.eventType?.title }), "success");
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ export const InfiniteEventTypeList = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const setHiddenMutation = trpc.viewer.eventTypes.update.useMutation({
|
const setHiddenMutation = trpc.viewer.eventTypes.heavy.update.useMutation({
|
||||||
onMutate: async (data) => {
|
onMutate: async (data) => {
|
||||||
await utils.viewer.eventTypes.getEventTypesFromGroup.cancel();
|
await utils.viewer.eventTypes.getEventTypesFromGroup.cancel();
|
||||||
const previousValue = utils.viewer.eventTypes.getEventTypesFromGroup.getInfiniteData({
|
const previousValue = utils.viewer.eventTypes.getEventTypesFromGroup.getInfiniteData({
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@calcom/web",
|
"name": "@calcom/web",
|
||||||
"version": "5.6.14",
|
"version": "5.6.15",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"analyze": "ANALYZE=true next build",
|
"analyze": "ANALYZE=true next build",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { getRoutedUrl } from "@calcom/features/routing-forms/lib/getRoutedUrl";
|
||||||
import { defaultHandler } from "@calcom/lib/server/defaultHandler";
|
import { defaultHandler } from "@calcom/lib/server/defaultHandler";
|
||||||
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
|
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
|
||||||
import { getRoutedUrl } from "@calcom/lib/server/getRoutedUrl";
|
|
||||||
|
|
||||||
export default defaultHandler({
|
export default defaultHandler({
|
||||||
OPTIONS: Promise.resolve({
|
OPTIONS: Promise.resolve({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import appBasecamp3 from "@calcom/app-store/basecamp3/trpc-router";
|
|
||||||
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
|
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
|
||||||
|
import appBasecamp3 from "@calcom/trpc/server/routers/apps/basecamp3/_router";
|
||||||
|
|
||||||
export default createNextApiHandler(appBasecamp3);
|
export default createNextApiHandler(appBasecamp3);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import appRoutingForms from "@calcom/app-store/routing-forms/trpc-router";
|
|
||||||
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
|
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
|
||||||
|
import appRoutingForms from "@calcom/trpc/server/routers/apps/routing-forms/_router";
|
||||||
|
|
||||||
export default createNextApiHandler(appRoutingForms);
|
export default createNextApiHandler(appRoutingForms);
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
|
||||||
|
import { eventTypesRouter } from "@calcom/trpc/server/routers/viewer/eventTypes/heavy/_router";
|
||||||
|
|
||||||
|
export default createNextApiHandler(eventTypesRouter);
|
||||||
@@ -113,7 +113,7 @@ test("multiple duration selection updates event length correctly", async ({ page
|
|||||||
await page.getByTestId("vertical-tab-event_advanced_tab_title").click();
|
await page.getByTestId("vertical-tab-event_advanced_tab_title").click();
|
||||||
await page.fill('[name="eventName"]', "{Event duration} event btwn {Organiser} {Scheduler}");
|
await page.fill('[name="eventName"]', "{Event duration} event btwn {Organiser} {Scheduler}");
|
||||||
await page.locator('[data-testid="update-eventtype"]').click();
|
await page.locator('[data-testid="update-eventtype"]').click();
|
||||||
await page.waitForResponse("/api/trpc/eventTypes/update?batch=1");
|
await page.waitForResponse("/api/trpc/eventTypes/heavy/update?batch=1");
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.goto(`/${user.username}/multiple-duration`);
|
await page.goto(`/${user.username}/multiple-duration`);
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ test.describe("Event Types tests", () => {
|
|||||||
expect(formTitle).toBe(firstTitle);
|
expect(formTitle).toBe(firstTitle);
|
||||||
expect(formSlug).toContain(firstSlug);
|
expect(formSlug).toContain(firstSlug);
|
||||||
|
|
||||||
const submitPromise = page.waitForResponse("/api/trpc/eventTypes/duplicate?batch=1");
|
const submitPromise = page.waitForResponse("/api/trpc/eventTypes/heavy/duplicate?batch=1");
|
||||||
await page.getByTestId("continue").click();
|
await page.getByTestId("continue").click();
|
||||||
const response = await submitPromise;
|
const response = await submitPromise;
|
||||||
expect(response.status()).toBe(200);
|
expect(response.status()).toBe(200);
|
||||||
@@ -144,7 +144,7 @@ test.describe("Event Types tests", () => {
|
|||||||
await page.waitForURL((url) => {
|
await page.waitForURL((url) => {
|
||||||
return !!url.pathname.match(/\/event-types\/.+/);
|
return !!url.pathname.match(/\/event-types\/.+/);
|
||||||
});
|
});
|
||||||
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/update?batch=1", {
|
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/heavy/update?batch=1", {
|
||||||
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -167,7 +167,7 @@ test.describe("Event Types tests", () => {
|
|||||||
await page.locator("[data-testid=add-location]").click();
|
await page.locator("[data-testid=add-location]").click();
|
||||||
await fillLocation(page, locationData[2], 2);
|
await fillLocation(page, locationData[2], 2);
|
||||||
|
|
||||||
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/update?batch=1", {
|
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/heavy/update?batch=1", {
|
||||||
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -314,7 +314,7 @@ test.describe("Event Types tests", () => {
|
|||||||
const locationAddress = "New Delhi";
|
const locationAddress = "New Delhi";
|
||||||
|
|
||||||
await fillLocation(page, locationAddress, 0, false);
|
await fillLocation(page, locationAddress, 0, false);
|
||||||
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/update?batch=1", {
|
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/heavy/update?batch=1", {
|
||||||
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -429,7 +429,7 @@ test.describe("Event Types tests", () => {
|
|||||||
await page.locator('[aria-label="Timezone Select"]').fill("New York");
|
await page.locator('[aria-label="Timezone Select"]').fill("New York");
|
||||||
await page.keyboard.press("Enter");
|
await page.keyboard.press("Enter");
|
||||||
|
|
||||||
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/update?batch=1", {
|
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/heavy/update?batch=1", {
|
||||||
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
||||||
});
|
});
|
||||||
await page.goto("/event-types");
|
await page.goto("/event-types");
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export function createBookingPageFixture(page: Page) {
|
|||||||
await page.goto("/event-types");
|
await page.goto("/event-types");
|
||||||
},
|
},
|
||||||
updateEventType: async () => {
|
updateEventType: async () => {
|
||||||
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/update?batch=1", {
|
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/heavy/update?batch=1", {
|
||||||
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ test.describe("private links creation and usage", () => {
|
|||||||
const $url = await page.locator('[data-testid="private-link-url"]').inputValue();
|
const $url = await page.locator('[data-testid="private-link-url"]').inputValue();
|
||||||
|
|
||||||
// click update
|
// click update
|
||||||
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/update?batch=1", {
|
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/heavy/update?batch=1", {
|
||||||
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
||||||
});
|
});
|
||||||
// book using generated url hash
|
// book using generated url hash
|
||||||
@@ -74,7 +74,7 @@ test.describe("private links creation and usage", () => {
|
|||||||
await page.locator("[data-testid=event-slug]").first().fill("somethingrandom");
|
await page.locator("[data-testid=event-slug]").first().fill("somethingrandom");
|
||||||
await expect(page.locator('[data-testid="event-slug"]').first()).toHaveValue("somethingrandom");
|
await expect(page.locator('[data-testid="event-slug"]').first()).toHaveValue("somethingrandom");
|
||||||
|
|
||||||
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/update?batch=1", {
|
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/heavy/update?batch=1", {
|
||||||
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
||||||
});
|
});
|
||||||
await page.locator("[data-testid=vertical-tab-event_advanced_tab_title]").click();
|
await page.locator("[data-testid=vertical-tab-event_advanced_tab_title]").click();
|
||||||
@@ -113,7 +113,7 @@ test.describe("private links creation and usage", () => {
|
|||||||
await page.locator('[data-testid="private-link-expiration-settings-save"]').click();
|
await page.locator('[data-testid="private-link-expiration-settings-save"]').click();
|
||||||
await page.waitForLoadState("networkidle");
|
await page.waitForLoadState("networkidle");
|
||||||
// click update
|
// click update
|
||||||
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/update?batch=1", {
|
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/heavy/update?batch=1", {
|
||||||
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
||||||
});
|
});
|
||||||
// book using generated url hash
|
// book using generated url hash
|
||||||
@@ -164,7 +164,7 @@ test.describe("private links creation and usage", () => {
|
|||||||
await page.locator('[data-testid="private-link-expiration-settings-save"]').click();
|
await page.locator('[data-testid="private-link-expiration-settings-save"]').click();
|
||||||
await page.waitForLoadState("networkidle");
|
await page.waitForLoadState("networkidle");
|
||||||
// click update
|
// click update
|
||||||
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/update?batch=1", {
|
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/heavy/update?batch=1", {
|
||||||
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
||||||
});
|
});
|
||||||
// book using generated url hash
|
// book using generated url hash
|
||||||
|
|||||||
@@ -476,7 +476,7 @@ export async function gotoBookingPage(page: Page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function saveEventType(page: Page) {
|
export async function saveEventType(page: Page) {
|
||||||
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/update?batch=1", {
|
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/heavy/update?batch=1", {
|
||||||
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -677,7 +677,7 @@ async function openBookingFormInPreviewTab(context: PlaywrightTestArgs["context"
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveEventType(page: Page) {
|
async function saveEventType(page: Page) {
|
||||||
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/update?batch=1", {
|
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/heavy/update?batch=1", {
|
||||||
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,5 +239,5 @@ async function gotoBookingPage(page: Page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveAndWaitForResponse(page: Page) {
|
async function saveAndWaitForResponse(page: Page) {
|
||||||
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/update?batch=1");
|
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/heavy/update?batch=1");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -407,7 +407,7 @@ test.describe("Bookings", () => {
|
|||||||
await page.goto(`/event-types/${teamEvent.id}?tabName=advanced`);
|
await page.goto(`/event-types/${teamEvent.id}?tabName=advanced`);
|
||||||
await page.getByTestId("requires-confirmation").click();
|
await page.getByTestId("requires-confirmation").click();
|
||||||
await page.getByTestId("update-eventtype").click();
|
await page.getByTestId("update-eventtype").click();
|
||||||
await page.waitForResponse((response) => response.url().includes("/api/trpc/eventTypes/update"));
|
await page.waitForResponse((response) => response.url().includes("/api/trpc/eventTypes/heavy/update"));
|
||||||
|
|
||||||
await doOnOrgDomain(
|
await doOnOrgDomain(
|
||||||
{
|
{
|
||||||
@@ -783,7 +783,7 @@ const markPhoneNumberAsRequiredAndEmailAsOptional = async (page: Page, eventId:
|
|||||||
const emailRequiredFiled = await page.locator('[data-testid="field-required"]').first();
|
const emailRequiredFiled = await page.locator('[data-testid="field-required"]').first();
|
||||||
await emailRequiredFiled.click();
|
await emailRequiredFiled.click();
|
||||||
await page.getByTestId("field-add-save").click();
|
await page.getByTestId("field-add-save").click();
|
||||||
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/update?batch=1", {
|
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/heavy/update?batch=1", {
|
||||||
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -797,7 +797,7 @@ const markPhoneNumberAsRequiredField = async (page: Page, eventId: number) => {
|
|||||||
const phoneRequiredFiled = await page.locator('[data-testid="field-required"]').first();
|
const phoneRequiredFiled = await page.locator('[data-testid="field-required"]').first();
|
||||||
await phoneRequiredFiled.click();
|
await phoneRequiredFiled.click();
|
||||||
await page.getByTestId("field-add-save").click();
|
await page.getByTestId("field-add-save").click();
|
||||||
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/update?batch=1", {
|
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/heavy/update?batch=1", {
|
||||||
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -458,7 +458,7 @@ test.describe("Organization", () => {
|
|||||||
await page.locator('[data-testid="fixed-hosts-select"]').click();
|
await page.locator('[data-testid="fixed-hosts-select"]').click();
|
||||||
await page.locator(`text="${invitedUserEmail}"`).click();
|
await page.locator(`text="${invitedUserEmail}"`).click();
|
||||||
await page.locator('[data-testid="update-eventtype"]').click();
|
await page.locator('[data-testid="update-eventtype"]').click();
|
||||||
await page.waitForResponse("/api/trpc/eventTypes/update?batch=1");
|
await page.waitForResponse("/api/trpc/eventTypes/heavy/update?batch=1");
|
||||||
|
|
||||||
await expectPageToBeNotFound({ page, url: `/team/${team.slug}/${teamEvent.slug}` });
|
await expectPageToBeNotFound({ page, url: `/team/${team.slug}/${teamEvent.slug}` });
|
||||||
await doOnOrgDomain(
|
await doOnOrgDomain(
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ test.describe("Payment app", () => {
|
|||||||
await page.getByPlaceholder("Price").click();
|
await page.getByPlaceholder("Price").click();
|
||||||
await page.getByPlaceholder("Price").fill("200");
|
await page.getByPlaceholder("Price").fill("200");
|
||||||
await page.getByText("SatoshissatsCurrencyBTCPayment optionCollect payment on booking").click();
|
await page.getByText("SatoshissatsCurrencyBTCPayment optionCollect payment on booking").click();
|
||||||
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/update?batch=1", {
|
await submitAndWaitForResponse(page, "/api/trpc/eventTypes/heavy/update?batch=1", {
|
||||||
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
action: () => page.locator("[data-testid=update-eventtype]").click(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { wrapGetServerSidePropsWithSentry } from "@sentry/nextjs";
|
import { wrapGetServerSidePropsWithSentry } from "@sentry/nextjs";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
|
|
||||||
import { getRoutedUrl, hasEmbedPath } from "@calcom/lib/server/getRoutedUrl";
|
import { getRoutedUrl, hasEmbedPath } from "@calcom/features/routing-forms/lib/getRoutedUrl";
|
||||||
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import Link from "next/link";
|
|||||||
|
|
||||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
|
||||||
import classNames from "@calcom/ui/classNames";
|
import classNames from "@calcom/ui/classNames";
|
||||||
import { Icon } from "@calcom/ui/components/icon";
|
import { Icon } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
@@ -13,7 +12,7 @@ export const AppDependencyComponent = ({
|
|||||||
dependencyData,
|
dependencyData,
|
||||||
}: {
|
}: {
|
||||||
appName: string;
|
appName: string;
|
||||||
dependencyData: RouterOutputs["viewer"]["apps"]["queryForDependencies"];
|
dependencyData?: { name: string; slug: string; installed: boolean }[];
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,13 @@ import Link from "next/link";
|
|||||||
import { useAppContextWithSchema } from "@calcom/app-store/EventTypeAppContext";
|
import { useAppContextWithSchema } from "@calcom/app-store/EventTypeAppContext";
|
||||||
import { useIsPlatform } from "@calcom/atoms/hooks/useIsPlatform";
|
import { useIsPlatform } from "@calcom/atoms/hooks/useIsPlatform";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
|
||||||
import classNames from "@calcom/ui/classNames";
|
import classNames from "@calcom/ui/classNames";
|
||||||
import { Button } from "@calcom/ui/components/button";
|
import { Button } from "@calcom/ui/components/button";
|
||||||
import { Switch } from "@calcom/ui/components/form";
|
import { Switch } from "@calcom/ui/components/form";
|
||||||
import { Icon } from "@calcom/ui/components/icon";
|
import { Icon } from "@calcom/ui/components/icon";
|
||||||
import { Section } from "@calcom/ui/components/section";
|
import { Section } from "@calcom/ui/components/section";
|
||||||
|
|
||||||
import type { CredentialOwner } from "../types";
|
import type { AppCardApp } from "../types";
|
||||||
import OmniInstallAppButton from "./OmniInstallAppButton";
|
import OmniInstallAppButton from "./OmniInstallAppButton";
|
||||||
|
|
||||||
export default function AppCard({
|
export default function AppCard({
|
||||||
@@ -27,9 +26,7 @@ export default function AppCard({
|
|||||||
hideSettingsIcon = false,
|
hideSettingsIcon = false,
|
||||||
hideAppCardOptions = false,
|
hideAppCardOptions = false,
|
||||||
}: {
|
}: {
|
||||||
app: RouterOutputs["viewer"]["apps"]["integrations"]["items"][number] & {
|
app: AppCardApp;
|
||||||
credentialOwner?: CredentialOwner;
|
|
||||||
};
|
|
||||||
description?: React.ReactNode;
|
description?: React.ReactNode;
|
||||||
switchChecked?: boolean;
|
switchChecked?: boolean;
|
||||||
switchOnClick?: (e: boolean) => void;
|
switchOnClick?: (e: boolean) => void;
|
||||||
|
|||||||
@@ -4,20 +4,15 @@ import type { GetAppData, SetAppData } from "@calcom/app-store/EventTypeAppConte
|
|||||||
import EventTypeAppContext from "@calcom/app-store/EventTypeAppContext";
|
import EventTypeAppContext from "@calcom/app-store/EventTypeAppContext";
|
||||||
import { EventTypeAddonMap } from "@calcom/app-store/apps.browser.generated";
|
import { EventTypeAddonMap } from "@calcom/app-store/apps.browser.generated";
|
||||||
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
|
||||||
import { ErrorBoundary } from "@calcom/ui/components/errorBoundary";
|
import { ErrorBoundary } from "@calcom/ui/components/errorBoundary";
|
||||||
|
|
||||||
import type { EventTypeAppCardComponentProps, CredentialOwner } from "../types";
|
import type { EventTypeAppCardComponentProps, AppCardApp } from "../types";
|
||||||
import { DynamicComponent } from "./DynamicComponent";
|
import { DynamicComponent } from "./DynamicComponent";
|
||||||
|
|
||||||
export type EventTypeApp = RouterOutputs["viewer"]["apps"]["integrations"]["items"][number] & {
|
|
||||||
credentialOwner?: CredentialOwner;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type EventTypeForAppCard = EventTypeAppCardComponentProps["eventType"];
|
export type EventTypeForAppCard = EventTypeAppCardComponentProps["eventType"];
|
||||||
|
|
||||||
export const EventTypeAppCard = (props: {
|
export const EventTypeAppCard = (props: {
|
||||||
app: EventTypeApp;
|
app: AppCardApp;
|
||||||
eventType: EventTypeForAppCard;
|
eventType: EventTypeForAppCard;
|
||||||
getAppData: GetAppData;
|
getAppData: GetAppData;
|
||||||
setAppData: SetAppData;
|
setAppData: SetAppData;
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import { render, screen } from "@testing-library/react";
|
import { render, screen } from "@testing-library/react";
|
||||||
import type { CredentialOwner } from "types";
|
import type { AppCardApp } from "types";
|
||||||
import { vi } from "vitest";
|
import { vi } from "vitest";
|
||||||
|
|
||||||
import type { RouterOutputs } from "@calcom/trpc";
|
|
||||||
|
|
||||||
import { DynamicComponent } from "./DynamicComponent";
|
import { DynamicComponent } from "./DynamicComponent";
|
||||||
import { EventTypeAppCard } from "./EventTypeAppCardInterface";
|
import { EventTypeAppCard } from "./EventTypeAppCardInterface";
|
||||||
|
|
||||||
@@ -26,9 +24,7 @@ const mockProps = {
|
|||||||
name: "TestApp",
|
name: "TestApp",
|
||||||
slug: "testapp",
|
slug: "testapp",
|
||||||
credentialOwner: {},
|
credentialOwner: {},
|
||||||
} as RouterOutputs["viewer"]["apps"]["integrations"]["items"][number] & {
|
} as AppCardApp,
|
||||||
credentialOwner?: CredentialOwner;
|
|
||||||
},
|
|
||||||
eventType: {},
|
eventType: {},
|
||||||
getAppData: getAppDataMock,
|
getAppData: getAppDataMock,
|
||||||
setAppData: setAppDataMock,
|
setAppData: setAppDataMock,
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
// Copied from node_modules/stripe/types/Errors.d.ts because stripe lib doesn't expose these types
|
|
||||||
// Ref: https://github.com/stripe/stripe-node/blob/master/types/Errors.d.ts#L194
|
|
||||||
interface StripeInvalidRequestError extends Error {
|
|
||||||
readonly type: "StripeInvalidRequestError";
|
|
||||||
readonly rawType: "invalid_request_error";
|
|
||||||
/**
|
|
||||||
* A human-readable message giving more details about the error. For card errors, these messages can
|
|
||||||
* be shown to your users.
|
|
||||||
*/
|
|
||||||
readonly message: string;
|
|
||||||
/**
|
|
||||||
* For card errors, a short string describing the kind of card error that occurred.
|
|
||||||
*
|
|
||||||
* @docs https://stripe.com/docs/error-codes
|
|
||||||
*/
|
|
||||||
readonly code?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A URL to more information about the error code reported.
|
|
||||||
*
|
|
||||||
* @docs https://stripe.com/docs/error-codes
|
|
||||||
*/
|
|
||||||
readonly doc_url?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Typically a 4xx or 5xx.
|
|
||||||
*/
|
|
||||||
readonly statusCode?: number;
|
|
||||||
|
|
||||||
readonly raw: unknown;
|
|
||||||
|
|
||||||
readonly headers: {
|
|
||||||
[key: string]: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
readonly requestId: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The parameter the error relates to if the error is parameter-specific. You can use this to display a
|
|
||||||
* message near the correct form field, for example.
|
|
||||||
*/
|
|
||||||
readonly param?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const errorSchema = z.object({
|
|
||||||
name: z.string(),
|
|
||||||
message: z.string(),
|
|
||||||
stack: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const stripeInvalidRequestErrorSchema = errorSchema.extend({
|
|
||||||
type: z.literal("StripeInvalidRequestError"),
|
|
||||||
rawType: z.literal("invalid_request_error"),
|
|
||||||
code: z.string().optional(),
|
|
||||||
doc_url: z.string().optional(),
|
|
||||||
statusCode: z.number().optional(),
|
|
||||||
raw: z.unknown(),
|
|
||||||
headers: z.record(z.string()),
|
|
||||||
requestId: z.string(),
|
|
||||||
param: z.string().optional(),
|
|
||||||
});
|
|
||||||
@@ -16,7 +16,7 @@ function useIsAppEnabled(app: EventTypeAppCardApp) {
|
|||||||
const credentialId = getAppData("credentialId");
|
const credentialId = getAppData("credentialId");
|
||||||
const isAppEnabledForCredential =
|
const isAppEnabledForCredential =
|
||||||
isAppEnabled &&
|
isAppEnabled &&
|
||||||
(app.userCredentialIds.some((id) => id === credentialId) ||
|
(app.userCredentialIds.some((id: number) => id === credentialId) ||
|
||||||
app.credentialOwner.credentialId === credentialId);
|
app.credentialOwner.credentialId === credentialId);
|
||||||
return isAppEnabledForCredential ?? false; // Default to false if undefined
|
return isAppEnabledForCredential ?? false; // Default to false if undefined
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export { default } from "./trpc/_router";
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
export const MeetLocationType = "integrations:google:meet";
|
export const MeetLocationType = "integrations:google:meet";
|
||||||
|
|
||||||
export const MSTeamsLocationType = "integrations:office365_video";
|
export const MSTeamsLocationType = "integrations:office365_video";
|
||||||
|
|
||||||
|
export const DailyLocationType = "integrations:daily";
|
||||||
|
|||||||
@@ -1310,8 +1310,10 @@ describe("Date Optimization Benchmarks", () => {
|
|||||||
)}ms, Speedup: ${speedupRatio.toFixed(1)}x`
|
)}ms, Speedup: ${speedupRatio.toFixed(1)}x`
|
||||||
);
|
);
|
||||||
|
|
||||||
const minSpeedup = process.env.CI ? 1.5 : 5; // Lower threshold for CI
|
if (!process.env.CI) {
|
||||||
expect(speedupRatio).toBeGreaterThan(minSpeedup);
|
const minSpeedup = 5; // Assert significant performance improvement (at least 5x faster)
|
||||||
|
expect(speedupRatio).toBeGreaterThan(minSpeedup);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,12 @@ import type { EventLocationTypeFromAppMeta } from "../types/App";
|
|||||||
import {
|
import {
|
||||||
MeetLocationType as importedMeetLocationType,
|
MeetLocationType as importedMeetLocationType,
|
||||||
MSTeamsLocationType as importedMSTeamsLocationType,
|
MSTeamsLocationType as importedMSTeamsLocationType,
|
||||||
|
DailyLocationType as importedDailyLocationType,
|
||||||
} from "./constants";
|
} from "./constants";
|
||||||
|
|
||||||
export const MeetLocationType = importedMeetLocationType;
|
export const MeetLocationType = importedMeetLocationType;
|
||||||
export const MSTeamsLocationType = importedMSTeamsLocationType;
|
export const MSTeamsLocationType = importedMSTeamsLocationType;
|
||||||
|
export const DailyLocationType = importedDailyLocationType;
|
||||||
|
|
||||||
export type DefaultEventLocationType = {
|
export type DefaultEventLocationType = {
|
||||||
default: true;
|
default: true;
|
||||||
@@ -69,7 +71,6 @@ export type EventLocationTypeFromApp = Ensure<
|
|||||||
|
|
||||||
export type EventLocationType = DefaultEventLocationType | EventLocationTypeFromApp;
|
export type EventLocationType = DefaultEventLocationType | EventLocationTypeFromApp;
|
||||||
|
|
||||||
export const DailyLocationType = "integrations:daily";
|
|
||||||
export const CalVideoLocationType = DailyLocationType;
|
export const CalVideoLocationType = DailyLocationType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
|
|||||||
import { sendGenericWebhookPayload } from "@calcom/features/webhooks/lib/sendPayload";
|
import { sendGenericWebhookPayload } from "@calcom/features/webhooks/lib/sendPayload";
|
||||||
import { WebhookTriggerEvents } from "@calcom/prisma/enums";
|
import { WebhookTriggerEvents } from "@calcom/prisma/enums";
|
||||||
|
|
||||||
import { _onFormSubmission } from "./utils";
|
import { _onFormSubmission } from "./formSubmissionUtils";
|
||||||
|
|
||||||
// Mock dependencies
|
// Mock dependencies
|
||||||
vi.mock("@calcom/lib/getOrgIdFromMemberOrTeamId", () => ({
|
vi.mock("@calcom/lib/getOrgIdFromMemberOrTeamId", () => ({
|
||||||
@@ -1,13 +1,244 @@
|
|||||||
import type { Prisma } from "@prisma/client";
|
import type { Prisma } from "@prisma/client";
|
||||||
|
import type { App_RoutingForms_Form, User } from "@prisma/client";
|
||||||
|
|
||||||
|
import dayjs from "@calcom/dayjs";
|
||||||
|
import type { Tasker } from "@calcom/features/tasker/tasker";
|
||||||
|
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
|
||||||
|
import { sendGenericWebhookPayload } from "@calcom/features/webhooks/lib/sendPayload";
|
||||||
|
import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
|
||||||
|
import logger from "@calcom/lib/logger";
|
||||||
|
import { withReporting } from "@calcom/lib/sentryWrapper";
|
||||||
import { prisma } from "@calcom/prisma";
|
import { prisma } from "@calcom/prisma";
|
||||||
import type { App_RoutingForms_Form } from "@calcom/prisma/client";
|
import { WebhookTriggerEvents } from "@calcom/prisma/enums";
|
||||||
import { RoutingFormSettings } from "@calcom/prisma/zod-utils";
|
import { RoutingFormSettings } from "@calcom/prisma/zod-utils";
|
||||||
|
import type { Ensure } from "@calcom/types/utils";
|
||||||
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
import { onFormSubmission } from "../trpc/utils";
|
import type { FormResponse, SerializableForm, SerializableField, OrderedResponses } from "../types/types";
|
||||||
import type { FormResponse, SerializableForm } from "../types/types";
|
|
||||||
|
const moduleLogger = logger.getSubLogger({ prefix: ["routing-forms/lib/formSubmissionUtils"] });
|
||||||
|
|
||||||
|
type SelectFieldWebhookResponse = string | number | string[] | { label: string; id: string | null };
|
||||||
|
export type FORM_SUBMITTED_WEBHOOK_RESPONSES = Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Deprecates `value` prop as it now has both the id(that doesn't change) and the label(that can change but is human friendly)
|
||||||
|
*/
|
||||||
|
response: number | string | string[] | SelectFieldWebhookResponse | SelectFieldWebhookResponse[];
|
||||||
|
/**
|
||||||
|
* @deprecated Use `response` instead
|
||||||
|
*/
|
||||||
|
value: FormResponse[keyof FormResponse]["value"];
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
|
function isOptionsField(field: Pick<SerializableField, "type" | "options">) {
|
||||||
|
return (field.type === "select" || field.type === "multiselect") && field.options;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFieldResponse({
|
||||||
|
field,
|
||||||
|
fieldResponseValue,
|
||||||
|
}: {
|
||||||
|
fieldResponseValue: FormResponse[keyof FormResponse]["value"];
|
||||||
|
field: Pick<SerializableField, "type" | "options">;
|
||||||
|
}) {
|
||||||
|
if (!isOptionsField(field)) {
|
||||||
|
return {
|
||||||
|
value: fieldResponseValue,
|
||||||
|
response: fieldResponseValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!field.options) {
|
||||||
|
return {
|
||||||
|
value: fieldResponseValue,
|
||||||
|
response: fieldResponseValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const valueArray = fieldResponseValue instanceof Array ? fieldResponseValue : [fieldResponseValue];
|
||||||
|
|
||||||
|
const chosenOptions = valueArray.map((idOrLabel) => {
|
||||||
|
const foundOptionById = field.options?.find((option) => {
|
||||||
|
return option.id === idOrLabel;
|
||||||
|
});
|
||||||
|
if (foundOptionById) {
|
||||||
|
return {
|
||||||
|
label: foundOptionById.label,
|
||||||
|
id: foundOptionById.id,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
label: idOrLabel.toString(),
|
||||||
|
id: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
// value is a legacy prop that is just sending the labels which can change
|
||||||
|
value: chosenOptions.map((option) => option.label),
|
||||||
|
// response is new prop that is sending the label along with id(which doesn't change)
|
||||||
|
response: chosenOptions,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sendResponseEmail = async (
|
||||||
|
form: Pick<App_RoutingForms_Form, "id" | "name" | "fields">,
|
||||||
|
orderedResponses: OrderedResponses,
|
||||||
|
toAddresses: string[]
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
if (typeof window === "undefined") {
|
||||||
|
const { default: ResponseEmail } = await import("../emails/templates/response-email");
|
||||||
|
const email = new ResponseEmail({ form: form, toAddresses, orderedResponses });
|
||||||
|
await email.sendEmail();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
moduleLogger.error("Error sending response email", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function getWebhookTargetEntity(form: { teamId?: number | null; user: { id: number } }) {
|
||||||
|
// If it's a team form, the target must be team webhook
|
||||||
|
// If it's a user form, the target must be user webhook
|
||||||
|
const isTeamForm = form.teamId;
|
||||||
|
return { userId: isTeamForm ? null : form.user.id, teamId: isTeamForm ? form.teamId : null };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not called in preview mode or dry run mode
|
||||||
|
* It takes care of sending webhooks and emails for form submissions
|
||||||
|
*/
|
||||||
|
export async function _onFormSubmission(
|
||||||
|
form: Ensure<
|
||||||
|
SerializableForm<App_RoutingForms_Form> & { user: Pick<User, "id" | "email">; userWithEmails?: string[] },
|
||||||
|
"fields"
|
||||||
|
>,
|
||||||
|
response: FormResponse,
|
||||||
|
responseId: number,
|
||||||
|
chosenAction?: {
|
||||||
|
type: "customPageMessage" | "externalRedirectUrl" | "eventTypeRedirectUrl";
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
const fieldResponsesByIdentifier: FORM_SUBMITTED_WEBHOOK_RESPONSES = {};
|
||||||
|
|
||||||
|
for (const [fieldId, fieldResponse] of Object.entries(response)) {
|
||||||
|
const field = form.fields.find((f) => f.id === fieldId);
|
||||||
|
if (!field) {
|
||||||
|
throw new Error(`Field with id ${fieldId} not found`);
|
||||||
|
}
|
||||||
|
// Use the label lowercased as the key to identify a field.
|
||||||
|
// TODO: We seem to be using label from the response, Can we not use the field.label
|
||||||
|
const key =
|
||||||
|
form.fields.find((f) => f.id === fieldId)?.identifier ||
|
||||||
|
(fieldResponse.label as keyof typeof fieldResponsesByIdentifier);
|
||||||
|
fieldResponsesByIdentifier[key] = getFieldResponse({
|
||||||
|
fieldResponseValue: fieldResponse.value,
|
||||||
|
field,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { userId, teamId } = getWebhookTargetEntity(form);
|
||||||
|
|
||||||
|
const orgId = await getOrgIdFromMemberOrTeamId({ memberId: userId, teamId });
|
||||||
|
|
||||||
|
const subscriberOptionsFormSubmitted = {
|
||||||
|
userId,
|
||||||
|
teamId,
|
||||||
|
orgId,
|
||||||
|
triggerEvent: WebhookTriggerEvents.FORM_SUBMITTED,
|
||||||
|
};
|
||||||
|
|
||||||
|
const subscriberOptionsFormSubmittedNoEvent = {
|
||||||
|
userId,
|
||||||
|
teamId,
|
||||||
|
orgId,
|
||||||
|
triggerEvent: WebhookTriggerEvents.FORM_SUBMITTED_NO_EVENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
const webhooksFormSubmitted = await getWebhooks(subscriberOptionsFormSubmitted);
|
||||||
|
const webhooksFormSubmittedNoEvent = await getWebhooks(subscriberOptionsFormSubmittedNoEvent);
|
||||||
|
|
||||||
|
const promisesFormSubmitted = webhooksFormSubmitted.map((webhook) => {
|
||||||
|
sendGenericWebhookPayload({
|
||||||
|
secretKey: webhook.secret,
|
||||||
|
triggerEvent: "FORM_SUBMITTED",
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
webhook,
|
||||||
|
data: {
|
||||||
|
formId: form.id,
|
||||||
|
formName: form.name,
|
||||||
|
teamId: form.teamId,
|
||||||
|
responses: fieldResponsesByIdentifier,
|
||||||
|
},
|
||||||
|
rootData: {
|
||||||
|
// Send responses unwrapped at root level for backwards compatibility
|
||||||
|
...Object.entries(fieldResponsesByIdentifier).reduce((acc, [key, value]) => {
|
||||||
|
acc[key] = value.value;
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<string, FormResponse[keyof FormResponse]["value"]>),
|
||||||
|
},
|
||||||
|
}).catch((e) => {
|
||||||
|
console.error(`Error executing routing form webhook`, webhook, e);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof window === "undefined") {
|
||||||
|
try {
|
||||||
|
const tasker: Tasker = await (await import("@calcom/features/tasker")).default;
|
||||||
|
const promisesFormSubmittedNoEvent = webhooksFormSubmittedNoEvent.map((webhook) => {
|
||||||
|
const scheduledAt = dayjs().add(15, "minute").toDate();
|
||||||
|
|
||||||
|
return tasker.create(
|
||||||
|
"triggerFormSubmittedNoEventWebhook",
|
||||||
|
{
|
||||||
|
responseId,
|
||||||
|
form: {
|
||||||
|
id: form.id,
|
||||||
|
name: form.name,
|
||||||
|
teamId: form.teamId ?? null,
|
||||||
|
},
|
||||||
|
responses: fieldResponsesByIdentifier,
|
||||||
|
redirect: chosenAction,
|
||||||
|
webhook,
|
||||||
|
},
|
||||||
|
{ scheduledAt }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const promises = [...promisesFormSubmitted, ...promisesFormSubmittedNoEvent];
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
const orderedResponses = form.fields.reduce((acc, field) => {
|
||||||
|
acc.push(response[field.id]);
|
||||||
|
return acc;
|
||||||
|
}, [] as OrderedResponses);
|
||||||
|
|
||||||
|
if (form.teamId) {
|
||||||
|
if (form.userWithEmails?.length) {
|
||||||
|
moduleLogger.debug(
|
||||||
|
`Preparing to send Form Response email for Form:${form.id} to users: ${form.userWithEmails.join(
|
||||||
|
","
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
await sendResponseEmail(form, orderedResponses, form.userWithEmails);
|
||||||
|
}
|
||||||
|
} else if (form.settings?.emailOwnerOnSubmission) {
|
||||||
|
moduleLogger.debug(
|
||||||
|
`Preparing to send Form Response email for Form:${form.id} to form owner: ${form.user.email}`
|
||||||
|
);
|
||||||
|
await sendResponseEmail(form, orderedResponses, [form.user.email]);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
moduleLogger.error("Error triggering routing form response side effects", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const onFormSubmission = withReporting(_onFormSubmission, "onFormSubmission");
|
||||||
|
|
||||||
export type TargetRoutingFormForResponse = SerializableForm<
|
export type TargetRoutingFormForResponse = SerializableForm<
|
||||||
App_RoutingForms_Form & {
|
App_RoutingForms_Form & {
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ import type { z } from "zod";
|
|||||||
|
|
||||||
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
|
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
|
||||||
import { areTheySiblingEntities } from "@calcom/lib/entityPermissionUtils.shared";
|
import { areTheySiblingEntities } from "@calcom/lib/entityPermissionUtils.shared";
|
||||||
|
import type { EventTypesByViewer } from "@calcom/lib/event-types/getEventTypesByViewer";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { buildEmptyQueryValue, raqbQueryValueUtils } from "@calcom/lib/raqb/raqbUtils";
|
import { buildEmptyQueryValue, raqbQueryValueUtils } from "@calcom/lib/raqb/raqbUtils";
|
||||||
import { SchedulingType } from "@calcom/prisma/enums";
|
import { SchedulingType } from "@calcom/prisma/enums";
|
||||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
|
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
|
||||||
import classNames from "@calcom/ui/classNames";
|
import classNames from "@calcom/ui/classNames";
|
||||||
@@ -63,8 +63,6 @@ import type {
|
|||||||
import type { zodRoutes } from "../../zod";
|
import type { zodRoutes } from "../../zod";
|
||||||
import { RouteActionType } from "../../zod";
|
import { RouteActionType } from "../../zod";
|
||||||
|
|
||||||
type EventTypesByGroup = RouterOutputs["viewer"]["eventTypes"]["getByViewer"];
|
|
||||||
|
|
||||||
type Form = inferSSRProps<typeof getServerSideProps>["form"];
|
type Form = inferSSRProps<typeof getServerSideProps>["form"];
|
||||||
|
|
||||||
type SetRoute = (id: string, route: Partial<EditFormRoute>) => void;
|
type SetRoute = (id: string, route: Partial<EditFormRoute>) => void;
|
||||||
@@ -141,7 +139,7 @@ const buildEventsData = ({
|
|||||||
form,
|
form,
|
||||||
route,
|
route,
|
||||||
}: {
|
}: {
|
||||||
eventTypesByGroup: EventTypesByGroup | undefined;
|
eventTypesByGroup: EventTypesByViewer | undefined;
|
||||||
form: Form;
|
form: Form;
|
||||||
route: EditFormRoute;
|
route: EditFormRoute;
|
||||||
}) => {
|
}) => {
|
||||||
@@ -372,7 +370,7 @@ const Route = ({
|
|||||||
moveDown?: { fn: () => void; check: () => boolean } | null;
|
moveDown?: { fn: () => void; check: () => boolean } | null;
|
||||||
appUrl: string;
|
appUrl: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
eventTypesByGroup: EventTypesByGroup;
|
eventTypesByGroup: EventTypesByViewer;
|
||||||
attributes?: Attribute[];
|
attributes?: Attribute[];
|
||||||
cardOptions?: {
|
cardOptions?: {
|
||||||
collapsible?: boolean;
|
collapsible?: boolean;
|
||||||
@@ -1129,7 +1127,7 @@ const Routes = ({
|
|||||||
hookForm: UseFormReturn<RoutingFormWithResponseCount>;
|
hookForm: UseFormReturn<RoutingFormWithResponseCount>;
|
||||||
appUrl: string;
|
appUrl: string;
|
||||||
attributes?: Attribute[];
|
attributes?: Attribute[];
|
||||||
eventTypesByGroup: EventTypesByGroup;
|
eventTypesByGroup: EventTypesByViewer;
|
||||||
}) => {
|
}) => {
|
||||||
const { routes: serializedRoutes } = hookForm.getValues();
|
const { routes: serializedRoutes } = hookForm.getValues();
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export { default } from "./trpc/_router";
|
|
||||||
@@ -1,237 +0,0 @@
|
|||||||
import type { App_RoutingForms_Form, User } from "@prisma/client";
|
|
||||||
|
|
||||||
import dayjs from "@calcom/dayjs";
|
|
||||||
import type { Tasker } from "@calcom/features/tasker/tasker";
|
|
||||||
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
|
|
||||||
import { sendGenericWebhookPayload } from "@calcom/features/webhooks/lib/sendPayload";
|
|
||||||
import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
|
|
||||||
import logger from "@calcom/lib/logger";
|
|
||||||
import { withReporting } from "@calcom/lib/sentryWrapper";
|
|
||||||
import { WebhookTriggerEvents } from "@calcom/prisma/enums";
|
|
||||||
import type { Ensure } from "@calcom/types/utils";
|
|
||||||
|
|
||||||
import type { SerializableField, OrderedResponses } from "../types/types";
|
|
||||||
import type { FormResponse, SerializableForm } from "../types/types";
|
|
||||||
|
|
||||||
const moduleLogger = logger.getSubLogger({ prefix: ["routing-forms/trpc/utils"] });
|
|
||||||
|
|
||||||
type SelectFieldWebhookResponse = string | number | string[] | { label: string; id: string | null };
|
|
||||||
export type FORM_SUBMITTED_WEBHOOK_RESPONSES = Record<
|
|
||||||
string,
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Deprecates `value` prop as it now has both the id(that doesn't change) and the label(that can change but is human friendly)
|
|
||||||
*/
|
|
||||||
response: number | string | string[] | SelectFieldWebhookResponse | SelectFieldWebhookResponse[];
|
|
||||||
/**
|
|
||||||
* @deprecated Use `response` instead
|
|
||||||
*/
|
|
||||||
value: FormResponse[keyof FormResponse]["value"];
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
|
|
||||||
function isOptionsField(field: Pick<SerializableField, "type" | "options">) {
|
|
||||||
return (field.type === "select" || field.type === "multiselect") && field.options;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getFieldResponse({
|
|
||||||
field,
|
|
||||||
fieldResponseValue,
|
|
||||||
}: {
|
|
||||||
fieldResponseValue: FormResponse[keyof FormResponse]["value"];
|
|
||||||
field: Pick<SerializableField, "type" | "options">;
|
|
||||||
}) {
|
|
||||||
if (!isOptionsField(field)) {
|
|
||||||
return {
|
|
||||||
value: fieldResponseValue,
|
|
||||||
response: fieldResponseValue,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!field.options) {
|
|
||||||
return {
|
|
||||||
value: fieldResponseValue,
|
|
||||||
response: fieldResponseValue,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const valueArray = fieldResponseValue instanceof Array ? fieldResponseValue : [fieldResponseValue];
|
|
||||||
|
|
||||||
const chosenOptions = valueArray.map((idOrLabel) => {
|
|
||||||
const foundOptionById = field.options?.find((option) => {
|
|
||||||
return option.id === idOrLabel;
|
|
||||||
});
|
|
||||||
if (foundOptionById) {
|
|
||||||
return {
|
|
||||||
label: foundOptionById.label,
|
|
||||||
id: foundOptionById.id,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
label: idOrLabel.toString(),
|
|
||||||
id: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
// value is a legacy prop that is just sending the labels which can change
|
|
||||||
value: chosenOptions.map((option) => option.label),
|
|
||||||
// response is new prop that is sending the label along with id(which doesn't change)
|
|
||||||
response: chosenOptions,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Not called in preview mode or dry run mode
|
|
||||||
* It takes care of sending webhooks and emails for form submissions
|
|
||||||
*/
|
|
||||||
export async function _onFormSubmission(
|
|
||||||
form: Ensure<
|
|
||||||
SerializableForm<App_RoutingForms_Form> & { user: Pick<User, "id" | "email">; userWithEmails?: string[] },
|
|
||||||
"fields"
|
|
||||||
>,
|
|
||||||
response: FormResponse,
|
|
||||||
responseId: number,
|
|
||||||
chosenAction?: {
|
|
||||||
type: "customPageMessage" | "externalRedirectUrl" | "eventTypeRedirectUrl";
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
const fieldResponsesByIdentifier: FORM_SUBMITTED_WEBHOOK_RESPONSES = {};
|
|
||||||
|
|
||||||
for (const [fieldId, fieldResponse] of Object.entries(response)) {
|
|
||||||
const field = form.fields.find((f) => f.id === fieldId);
|
|
||||||
if (!field) {
|
|
||||||
throw new Error(`Field with id ${fieldId} not found`);
|
|
||||||
}
|
|
||||||
// Use the label lowercased as the key to identify a field.
|
|
||||||
// TODO: We seem to be using label from the response, Can we not use the field.label
|
|
||||||
const key =
|
|
||||||
form.fields.find((f) => f.id === fieldId)?.identifier ||
|
|
||||||
(fieldResponse.label as keyof typeof fieldResponsesByIdentifier);
|
|
||||||
fieldResponsesByIdentifier[key] = getFieldResponse({
|
|
||||||
fieldResponseValue: fieldResponse.value,
|
|
||||||
field,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const { userId, teamId } = getWebhookTargetEntity(form);
|
|
||||||
|
|
||||||
const orgId = await getOrgIdFromMemberOrTeamId({ memberId: userId, teamId });
|
|
||||||
|
|
||||||
const subscriberOptionsFormSubmitted = {
|
|
||||||
userId,
|
|
||||||
teamId,
|
|
||||||
orgId,
|
|
||||||
triggerEvent: WebhookTriggerEvents.FORM_SUBMITTED,
|
|
||||||
};
|
|
||||||
|
|
||||||
const subscriberOptionsFormSubmittedNoEvent = {
|
|
||||||
userId,
|
|
||||||
teamId,
|
|
||||||
orgId,
|
|
||||||
triggerEvent: WebhookTriggerEvents.FORM_SUBMITTED_NO_EVENT,
|
|
||||||
};
|
|
||||||
|
|
||||||
const webhooksFormSubmitted = await getWebhooks(subscriberOptionsFormSubmitted);
|
|
||||||
const webhooksFormSubmittedNoEvent = await getWebhooks(subscriberOptionsFormSubmittedNoEvent);
|
|
||||||
|
|
||||||
const promisesFormSubmitted = webhooksFormSubmitted.map((webhook) => {
|
|
||||||
sendGenericWebhookPayload({
|
|
||||||
secretKey: webhook.secret,
|
|
||||||
triggerEvent: "FORM_SUBMITTED",
|
|
||||||
createdAt: new Date().toISOString(),
|
|
||||||
webhook,
|
|
||||||
data: {
|
|
||||||
formId: form.id,
|
|
||||||
formName: form.name,
|
|
||||||
teamId: form.teamId,
|
|
||||||
responses: fieldResponsesByIdentifier,
|
|
||||||
},
|
|
||||||
rootData: {
|
|
||||||
// Send responses unwrapped at root level for backwards compatibility
|
|
||||||
...Object.entries(fieldResponsesByIdentifier).reduce((acc, [key, value]) => {
|
|
||||||
acc[key] = value.value;
|
|
||||||
return acc;
|
|
||||||
}, {} as Record<string, FormResponse[keyof FormResponse]["value"]>),
|
|
||||||
},
|
|
||||||
}).catch((e) => {
|
|
||||||
console.error(`Error executing routing form webhook`, webhook, e);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (typeof window === "undefined") {
|
|
||||||
try {
|
|
||||||
const tasker: Tasker = await (await import("@calcom/features/tasker")).default;
|
|
||||||
const promisesFormSubmittedNoEvent = webhooksFormSubmittedNoEvent.map((webhook) => {
|
|
||||||
const scheduledAt = dayjs().add(15, "minute").toDate();
|
|
||||||
|
|
||||||
return tasker.create(
|
|
||||||
"triggerFormSubmittedNoEventWebhook",
|
|
||||||
{
|
|
||||||
responseId,
|
|
||||||
form: {
|
|
||||||
id: form.id,
|
|
||||||
name: form.name,
|
|
||||||
teamId: form.teamId ?? null,
|
|
||||||
},
|
|
||||||
responses: fieldResponsesByIdentifier,
|
|
||||||
redirect: chosenAction,
|
|
||||||
webhook,
|
|
||||||
},
|
|
||||||
{ scheduledAt }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const promises = [...promisesFormSubmitted, ...promisesFormSubmittedNoEvent];
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
|
||||||
const orderedResponses = form.fields.reduce((acc, field) => {
|
|
||||||
acc.push(response[field.id]);
|
|
||||||
return acc;
|
|
||||||
}, [] as OrderedResponses);
|
|
||||||
|
|
||||||
if (form.teamId) {
|
|
||||||
if (form.userWithEmails?.length) {
|
|
||||||
moduleLogger.debug(
|
|
||||||
`Preparing to send Form Response email for Form:${form.id} to users: ${form.userWithEmails.join(
|
|
||||||
","
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
await sendResponseEmail(form, orderedResponses, form.userWithEmails);
|
|
||||||
}
|
|
||||||
} else if (form.settings?.emailOwnerOnSubmission) {
|
|
||||||
moduleLogger.debug(
|
|
||||||
`Preparing to send Form Response email for Form:${form.id} to form owner: ${form.user.email}`
|
|
||||||
);
|
|
||||||
await sendResponseEmail(form, orderedResponses, [form.user.email]);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
moduleLogger.error("Error triggering routing form response side effects", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export const onFormSubmission = withReporting(_onFormSubmission, "onFormSubmission");
|
|
||||||
|
|
||||||
export const sendResponseEmail = async (
|
|
||||||
form: Pick<App_RoutingForms_Form, "id" | "name" | "fields">,
|
|
||||||
orderedResponses: OrderedResponses,
|
|
||||||
toAddresses: string[]
|
|
||||||
) => {
|
|
||||||
try {
|
|
||||||
if (typeof window === "undefined") {
|
|
||||||
const { default: ResponseEmail } = await import("../emails/templates/response-email");
|
|
||||||
const email = new ResponseEmail({ form: form, toAddresses, orderedResponses });
|
|
||||||
await email.sendEmail();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
moduleLogger.error("Error sending response email", e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function getWebhookTargetEntity(form: { teamId?: number | null; user: { id: number } }) {
|
|
||||||
// If it's a team form, the target must be team webhook
|
|
||||||
// If it's a user form, the target must be user webhook
|
|
||||||
const isTeamForm = form.teamId;
|
|
||||||
return { userId: isTeamForm ? null : form.user.id, teamId: isTeamForm ? form.teamId : null };
|
|
||||||
}
|
|
||||||
Vendored
+5
-2
@@ -1,8 +1,8 @@
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
|
|
||||||
|
import type { ConnectedApps } from "@calcom/lib/getConnectedApps";
|
||||||
import type { EventTypeFormMetadataSchema } from "@calcom/prisma/zod-utils";
|
import type { EventTypeFormMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
|
||||||
import type { ButtonProps } from "@calcom/ui/components/button";
|
import type { ButtonProps } from "@calcom/ui/components/button";
|
||||||
|
|
||||||
export type IntegrationOAuthCallbackState = {
|
export type IntegrationOAuthCallbackState = {
|
||||||
@@ -22,8 +22,11 @@ export type CredentialOwner = {
|
|||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EventTypeAppCardApp = RouterOutputs["viewer"]["apps"]["integrations"]["items"][number] & {
|
export type AppCardApp = ConnectedApps[number] & {
|
||||||
credentialOwner?: CredentialOwner;
|
credentialOwner?: CredentialOwner;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EventTypeAppCardApp = AppCardApp & {
|
||||||
credentialIds?: number[];
|
credentialIds?: number[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useCallback, useState } from "react";
|
|||||||
import { InstallAppButton } from "@calcom/app-store/InstallAppButton";
|
import { InstallAppButton } from "@calcom/app-store/InstallAppButton";
|
||||||
import { AppSettings } from "@calcom/app-store/_components/AppSettings";
|
import { AppSettings } from "@calcom/app-store/_components/AppSettings";
|
||||||
import { getLocationFromApp, type EventLocationType } from "@calcom/app-store/locations";
|
import { getLocationFromApp, type EventLocationType } from "@calcom/app-store/locations";
|
||||||
import type { CredentialOwner } from "@calcom/app-store/types";
|
import type { AppCardApp } from "@calcom/app-store/types";
|
||||||
import AppListCard from "@calcom/features/apps/components/AppListCard";
|
import AppListCard from "@calcom/features/apps/components/AppListCard";
|
||||||
import type { UpdateUsersDefaultConferencingAppParams } from "@calcom/features/apps/components/AppSetDefaultLinkDialog";
|
import type { UpdateUsersDefaultConferencingAppParams } from "@calcom/features/apps/components/AppSetDefaultLinkDialog";
|
||||||
import { AppSetDefaultLinkDialog } from "@calcom/features/apps/components/AppSetDefaultLinkDialog";
|
import { AppSetDefaultLinkDialog } from "@calcom/features/apps/components/AppSetDefaultLinkDialog";
|
||||||
@@ -69,13 +69,7 @@ export const AppList = ({
|
|||||||
showToast("Default app updated successfully", "success");
|
showToast("Default app updated successfully", "success");
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const ChildAppCard = ({
|
const ChildAppCard = ({ item }: { item: AppCardApp }) => {
|
||||||
item,
|
|
||||||
}: {
|
|
||||||
item: RouterOutputs["viewer"]["apps"]["integrations"]["items"][number] & {
|
|
||||||
credentialOwner?: CredentialOwner;
|
|
||||||
};
|
|
||||||
}) => {
|
|
||||||
const appSlug = item?.slug;
|
const appSlug = item?.slug;
|
||||||
const appIsDefault =
|
const appIsDefault =
|
||||||
appSlug === defaultConferencingApp?.appSlug ||
|
appSlug === defaultConferencingApp?.appSlug ||
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { Prisma, WorkflowReminder } from "@prisma/client";
|
import type { Prisma, WorkflowReminder } from "@prisma/client";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
|
|
||||||
|
import { DailyLocationType } from "@calcom/app-store/constants";
|
||||||
import { FAKE_DAILY_CREDENTIAL } from "@calcom/app-store/dailyvideo/lib/VideoApiAdapter";
|
import { FAKE_DAILY_CREDENTIAL } from "@calcom/app-store/dailyvideo/lib/VideoApiAdapter";
|
||||||
import { DailyLocationType } from "@calcom/app-store/locations";
|
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
import { sendCancelledEmailsAndSMS } from "@calcom/emails";
|
import { sendCancelledEmailsAndSMS } from "@calcom/emails";
|
||||||
import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventResponses";
|
import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventResponses";
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { CalendarEventBuilder } from "@calcom/features/CalendarEventBuilder";
|
|||||||
import type { BookingDataSchemaGetter } from "@calcom/features/bookings/lib/dto/types";
|
import type { BookingDataSchemaGetter } from "@calcom/features/bookings/lib/dto/types";
|
||||||
import type { CreateRegularBookingData, CreateBookingMeta } from "@calcom/features/bookings/lib/dto/types";
|
import type { CreateRegularBookingData, CreateBookingMeta } from "@calcom/features/bookings/lib/dto/types";
|
||||||
import type { CheckBookingAndDurationLimitsService } from "@calcom/features/bookings/lib/handleNewBooking/checkBookingAndDurationLimits";
|
import type { CheckBookingAndDurationLimitsService } from "@calcom/features/bookings/lib/handleNewBooking/checkBookingAndDurationLimits";
|
||||||
|
import { handlePayment } from "@calcom/features/bookings/lib/handlePayment";
|
||||||
import { handleWebhookTrigger } from "@calcom/features/bookings/lib/handleWebhookTrigger";
|
import { handleWebhookTrigger } from "@calcom/features/bookings/lib/handleWebhookTrigger";
|
||||||
import { isEventTypeLoggingEnabled } from "@calcom/features/bookings/lib/isEventTypeLoggingEnabled";
|
import { isEventTypeLoggingEnabled } from "@calcom/features/bookings/lib/isEventTypeLoggingEnabled";
|
||||||
import type { CacheService } from "@calcom/features/calendar-cache/lib/getShouldServeCache";
|
import type { CacheService } from "@calcom/features/calendar-cache/lib/getShouldServeCache";
|
||||||
@@ -57,7 +58,6 @@ import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
|
|||||||
import { HttpError } from "@calcom/lib/http-error";
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
import type { CheckBookingLimitsService } from "@calcom/lib/intervalLimits/server/checkBookingLimits";
|
import type { CheckBookingLimitsService } from "@calcom/lib/intervalLimits/server/checkBookingLimits";
|
||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
import { handlePayment } from "@calcom/lib/payment/handlePayment";
|
|
||||||
import { getPiiFreeCalendarEvent, getPiiFreeEventType } from "@calcom/lib/piiFreeData";
|
import { getPiiFreeCalendarEvent, getPiiFreeEventType } from "@calcom/lib/piiFreeData";
|
||||||
import { safeStringify } from "@calcom/lib/safeStringify";
|
import { safeStringify } from "@calcom/lib/safeStringify";
|
||||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
import type { Booking, User, Webhook } from "@prisma/client";
|
import type { Booking, User, Webhook } from "@prisma/client";
|
||||||
import { afterAll, beforeAll, describe, expect, test, vi } from "vitest";
|
import { afterAll, beforeAll, describe, expect, test, vi } from "vitest";
|
||||||
|
|
||||||
import { DailyLocationType } from "@calcom/app-store/locations";
|
import { DailyLocationType } from "@calcom/app-store/constants";
|
||||||
import { getMeetingSessionsFromRoomName } from "@calcom/features/tasker/tasks/triggerNoShow/getMeetingSessionsFromRoomName";
|
import { getMeetingSessionsFromRoomName } from "@calcom/features/tasker/tasks/triggerNoShow/getMeetingSessionsFromRoomName";
|
||||||
import { triggerHostNoShow } from "@calcom/features/tasker/tasks/triggerNoShow/triggerHostNoShow";
|
import { triggerHostNoShow } from "@calcom/features/tasker/tasks/triggerNoShow/triggerHostNoShow";
|
||||||
import { sendGenericWebhookPayload } from "@calcom/features/webhooks/lib/sendPayload";
|
import { sendGenericWebhookPayload } from "@calcom/features/webhooks/lib/sendPayload";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DailyLocationType } from "@calcom/app-store/locations";
|
import { DailyLocationType } from "@calcom/app-store/constants";
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
import tasker from "@calcom/features/tasker";
|
import tasker from "@calcom/features/tasker";
|
||||||
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
|
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { uuid } from "short-uuid";
|
|||||||
|
|
||||||
import { sendScheduledSeatsEmailsAndSMS } from "@calcom/emails";
|
import { sendScheduledSeatsEmailsAndSMS } from "@calcom/emails";
|
||||||
import { refreshCredentials } from "@calcom/features/bookings/lib/getAllCredentialsForUsersOnEvent/refreshCredentials";
|
import { refreshCredentials } from "@calcom/features/bookings/lib/getAllCredentialsForUsersOnEvent/refreshCredentials";
|
||||||
|
import { handlePayment } from "@calcom/features/bookings/lib/handlePayment";
|
||||||
import {
|
import {
|
||||||
allowDisablingAttendeeConfirmationEmails,
|
allowDisablingAttendeeConfirmationEmails,
|
||||||
allowDisablingHostConfirmationEmails,
|
allowDisablingHostConfirmationEmails,
|
||||||
@@ -11,7 +12,6 @@ import {
|
|||||||
import EventManager from "@calcom/lib/EventManager";
|
import EventManager from "@calcom/lib/EventManager";
|
||||||
import { ErrorCode } from "@calcom/lib/errorCodes";
|
import { ErrorCode } from "@calcom/lib/errorCodes";
|
||||||
import { HttpError } from "@calcom/lib/http-error";
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
import { handlePayment } from "@calcom/lib/payment/handlePayment";
|
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
import { BookingStatus } from "@calcom/prisma/enums";
|
import { BookingStatus } from "@calcom/prisma/enums";
|
||||||
import { eventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils";
|
import { eventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils";
|
||||||
|
|||||||
+6
-8
@@ -1,16 +1,14 @@
|
|||||||
import stripe from "@calcom/features/ee/payments/server/stripe";
|
import stripe from "@calcom/features/ee/payments/server/stripe";
|
||||||
import {
|
|
||||||
ZStripeCheckoutSessionInputSchema,
|
type StripeCheckoutSessionInput = {
|
||||||
type TStripeCheckoutSessionInputSchema,
|
checkoutSessionId?: string;
|
||||||
} from "@calcom/trpc/server/routers/publicViewer/stripeCheckoutSession.schema";
|
stripeCustomerId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export class StripeService {
|
export class StripeService {
|
||||||
static async getCheckoutSession(input: TStripeCheckoutSessionInputSchema) {
|
static async getCheckoutSession(input: StripeCheckoutSessionInput) {
|
||||||
const { checkoutSessionId, stripeCustomerId } = input;
|
const { checkoutSessionId, stripeCustomerId } = input;
|
||||||
|
|
||||||
// Moved the following data checks to superRefine
|
|
||||||
ZStripeCheckoutSessionInputSchema.parse(input);
|
|
||||||
|
|
||||||
let customerId: string;
|
let customerId: string;
|
||||||
let isPremiumUsername = false;
|
let isPremiumUsername = false;
|
||||||
let hasPaymentFailed = false;
|
let hasPaymentFailed = false;
|
||||||
@@ -70,7 +70,7 @@ const DuplicateDialog = () => {
|
|||||||
}
|
}
|
||||||
}, [searchParams?.get("dialog")]);
|
}, [searchParams?.get("dialog")]);
|
||||||
|
|
||||||
const duplicateMutation = trpc.viewer.eventTypes.duplicate.useMutation({
|
const duplicateMutation = trpc.viewer.eventTypes.heavy.duplicate.useMutation({
|
||||||
onSuccess: async ({ eventType }) => {
|
onSuccess: async ({ eventType }) => {
|
||||||
await router.replace(`/event-types/${eventType.id}`);
|
await router.replace(`/event-types/${eventType.id}`);
|
||||||
|
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ export type LocationFormValues = Pick<FormValues, "id" | "locations" | "bookingF
|
|||||||
|
|
||||||
export type EventTypeAssignedUsers = RouterOutputs["viewer"]["eventTypes"]["get"]["eventType"]["children"];
|
export type EventTypeAssignedUsers = RouterOutputs["viewer"]["eventTypes"]["get"]["eventType"]["children"];
|
||||||
export type EventTypeHosts = RouterOutputs["viewer"]["eventTypes"]["get"]["eventType"]["hosts"];
|
export type EventTypeHosts = RouterOutputs["viewer"]["eventTypes"]["get"]["eventType"]["hosts"];
|
||||||
export type EventTypeUpdateInput = RouterInputs["viewer"]["eventTypes"]["update"];
|
export type EventTypeUpdateInput = RouterInputs["viewer"]["eventTypes"]["heavy"]["update"];
|
||||||
export type TabMap = {
|
export type TabMap = {
|
||||||
advanced: React.ReactNode;
|
advanced: React.ReactNode;
|
||||||
ai?: React.ReactNode;
|
ai?: React.ReactNode;
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import type { FORM_SUBMITTED_WEBHOOK_RESPONSES } from "@calcom/app-store/routing-forms/lib/formSubmissionUtils";
|
||||||
import incompleteBookingActionFunctions from "@calcom/app-store/routing-forms/lib/incompleteBooking/actionFunctions";
|
import incompleteBookingActionFunctions from "@calcom/app-store/routing-forms/lib/incompleteBooking/actionFunctions";
|
||||||
import type { FORM_SUBMITTED_WEBHOOK_RESPONSES } from "@calcom/app-store/routing-forms/trpc/utils";
|
|
||||||
import { sendGenericWebhookPayload } from "@calcom/features/webhooks/lib/sendPayload";
|
import { sendGenericWebhookPayload } from "@calcom/features/webhooks/lib/sendPayload";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
|
|||||||
@@ -157,11 +157,12 @@ const getSeatReferenceId = (calEvent: Pick<CalendarEvent, "attendeeSeatId">): st
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getBookingUrl = (calEvent: CalendarEvent) => {
|
export const getBookingUrl = (calEvent: CalendarEvent) => {
|
||||||
|
const seatReferenceUid = getSeatReferenceId(calEvent);
|
||||||
if (calEvent.platformClientId) {
|
if (calEvent.platformClientId) {
|
||||||
if (!calEvent.platformBookingUrl) return "";
|
if (!calEvent.platformBookingUrl) return "";
|
||||||
return `${calEvent.platformBookingUrl}/${getUid(calEvent)}?slug=${calEvent.type}&username=${
|
return `${calEvent.platformBookingUrl}/${getUid(calEvent)}?slug=${calEvent.type}&username=${
|
||||||
calEvent.organizer.username
|
calEvent.organizer.username
|
||||||
}&changes=true`;
|
}${seatReferenceUid ? `&seatReferenceUid=${seatReferenceUid}` : ""}&changes=true`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${calEvent.bookerUrl ?? WEBAPP_URL}/booking/${getUid(calEvent)}?changes=true`;
|
return `${calEvent.bookerUrl ?? WEBAPP_URL}/booking/${getUid(calEvent)}?changes=true`;
|
||||||
@@ -174,6 +175,8 @@ export const getPlatformManageLink = (
|
|||||||
t: TFunction
|
t: TFunction
|
||||||
) => {
|
) => {
|
||||||
const shouldDisplayReschedule = !calEvent.recurringEvent && calEvent.platformRescheduleUrl;
|
const shouldDisplayReschedule = !calEvent.recurringEvent && calEvent.platformRescheduleUrl;
|
||||||
|
const seatUid = getSeatReferenceId(calEvent);
|
||||||
|
|
||||||
let res =
|
let res =
|
||||||
calEvent.platformBookingUrl || shouldDisplayReschedule || calEvent.platformCancelUrl
|
calEvent.platformBookingUrl || shouldDisplayReschedule || calEvent.platformCancelUrl
|
||||||
? `${t("need_to_reschedule_or_cancel")} `
|
? `${t("need_to_reschedule_or_cancel")} `
|
||||||
@@ -181,9 +184,9 @@ export const getPlatformManageLink = (
|
|||||||
if (calEvent.platformBookingUrl) {
|
if (calEvent.platformBookingUrl) {
|
||||||
res += `Check Here: ${calEvent.platformBookingUrl}/${getUid(calEvent)}?slug=${calEvent.type}&username=${
|
res += `Check Here: ${calEvent.platformBookingUrl}/${getUid(calEvent)}?slug=${calEvent.type}&username=${
|
||||||
calEvent.organizer.username
|
calEvent.organizer.username
|
||||||
}${calEvent?.team ? `&teamId=${calEvent.team.id}` : ""}&changes=true${
|
}${calEvent?.team ? `&teamId=${calEvent.team.id}` : ""}${
|
||||||
calEvent.platformCancelUrl || shouldDisplayReschedule ? ` ${t("or_lowercase")} ` : ""
|
seatUid ? `&seatReferenceUid=${seatUid}` : ""
|
||||||
}`;
|
}&changes=true${calEvent.platformCancelUrl || shouldDisplayReschedule ? ` ${t("or_lowercase")} ` : ""}`;
|
||||||
}
|
}
|
||||||
if (calEvent.platformCancelUrl) {
|
if (calEvent.platformCancelUrl) {
|
||||||
res += `${t("cancel")}: ${getCancelLink(calEvent)}`;
|
res += `${t("cancel")}: ${getCancelLink(calEvent)}`;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Prisma, SelectedCalendar } from "@prisma/client";
|
import type { Prisma, SelectedCalendar } from "@prisma/client";
|
||||||
|
|
||||||
import { DailyLocationType } from "@calcom/app-store/locations";
|
import { DailyLocationType } from "@calcom/app-store/constants";
|
||||||
import slugify from "@calcom/lib/slugify";
|
import slugify from "@calcom/lib/slugify";
|
||||||
import { PeriodType, SchedulingType } from "@calcom/prisma/enums";
|
import { PeriodType, SchedulingType } from "@calcom/prisma/enums";
|
||||||
import type { userSelect } from "@calcom/prisma/selects";
|
import type { userSelect } from "@calcom/prisma/selects";
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export const useCreateEventType = (
|
|||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const debouncedSearchTerm = useDebounce(searchTerm, 500);
|
const debouncedSearchTerm = useDebounce(searchTerm, 500);
|
||||||
|
|
||||||
const createMutation = trpc.viewer.eventTypes.create.useMutation({
|
const createMutation = trpc.viewer.eventTypes.heavy.create.useMutation({
|
||||||
onSuccess: async ({ eventType }) => {
|
onSuccess: async ({ eventType }) => {
|
||||||
onSuccessMutation(eventType);
|
onSuccessMutation(eventType);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import getAppKeysFromSlug from "@calcom/app-store/_utils/getAppKeysFromSlug";
|
import getAppKeysFromSlug from "@calcom/app-store/_utils/getAppKeysFromSlug";
|
||||||
import { DailyLocationType } from "@calcom/app-store/locations";
|
import { DailyLocationType } from "@calcom/app-store/constants";
|
||||||
import getApps from "@calcom/app-store/utils";
|
import getApps from "@calcom/app-store/utils";
|
||||||
import { getUsersCredentialsIncludeServiceAccountKey } from "@calcom/lib/server/getUsersCredentials";
|
import { getUsersCredentialsIncludeServiceAccountKey } from "@calcom/lib/server/getUsersCredentials";
|
||||||
import { userMetadata as userMetadataSchema } from "@calcom/prisma/zod-utils";
|
import { userMetadata as userMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { Prisma } from "@prisma/client";
|
|||||||
import type { ZodIssue } from "zod";
|
import type { ZodIssue } from "zod";
|
||||||
import { ZodError } from "zod";
|
import { ZodError } from "zod";
|
||||||
|
|
||||||
import { stripeInvalidRequestErrorSchema } from "@calcom/app-store/_utils/stripe.types";
|
|
||||||
import { ErrorCode } from "@calcom/lib/errorCodes";
|
import { ErrorCode } from "@calcom/lib/errorCodes";
|
||||||
import { ErrorWithCode } from "@calcom/lib/errors";
|
import { ErrorWithCode } from "@calcom/lib/errors";
|
||||||
|
|
||||||
@@ -11,6 +10,7 @@ import { getHTTPStatusCodeFromError } from "@trpc/server/http";
|
|||||||
|
|
||||||
import { HttpError } from "../http-error";
|
import { HttpError } from "../http-error";
|
||||||
import { redactError } from "../redactError";
|
import { redactError } from "../redactError";
|
||||||
|
import { stripeInvalidRequestErrorSchema } from "../stripe-error";
|
||||||
|
|
||||||
function hasName(cause: unknown): cause is { name: string } {
|
function hasName(cause: unknown): cause is { name: string } {
|
||||||
return !!cause && typeof cause === "object" && "name" in cause;
|
return !!cause && typeof cause === "object" && "name" in cause;
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const errorSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
message: z.string(),
|
||||||
|
stack: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const stripeInvalidRequestErrorSchema = errorSchema.extend({
|
||||||
|
type: z.literal("StripeInvalidRequestError"),
|
||||||
|
rawType: z.literal("invalid_request_error"),
|
||||||
|
code: z.string().optional(),
|
||||||
|
doc_url: z.string().optional(),
|
||||||
|
statusCode: z.number().optional(),
|
||||||
|
raw: z.unknown(),
|
||||||
|
headers: z.record(z.string()),
|
||||||
|
requestId: z.string(),
|
||||||
|
param: z.string().optional(),
|
||||||
|
});
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import short from "short-uuid";
|
import short from "short-uuid";
|
||||||
import { v5 as uuidv5 } from "uuid";
|
import { v5 as uuidv5 } from "uuid";
|
||||||
|
|
||||||
|
import { DailyLocationType } from "@calcom/app-store/constants";
|
||||||
import { getDailyAppKeys } from "@calcom/app-store/dailyvideo/lib/getDailyAppKeys";
|
import { getDailyAppKeys } from "@calcom/app-store/dailyvideo/lib/getDailyAppKeys";
|
||||||
import { DailyLocationType } from "@calcom/app-store/locations";
|
|
||||||
import { VideoApiAdapterMap } from "@calcom/app-store/video.adapters.generated";
|
import { VideoApiAdapterMap } from "@calcom/app-store/video.adapters.generated";
|
||||||
import { sendBrokenIntegrationEmail } from "@calcom/emails";
|
import { sendBrokenIntegrationEmail } from "@calcom/emails";
|
||||||
import { getUid } from "@calcom/lib/CalEventParser";
|
import { getUid } from "@calcom/lib/CalEventParser";
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ const EventTypeWeb = ({
|
|||||||
teamId: eventType.team?.id || eventType.parent?.teamId,
|
teamId: eventType.team?.id || eventType.parent?.teamId,
|
||||||
onlyInstalled: true,
|
onlyInstalled: true,
|
||||||
});
|
});
|
||||||
const updateMutation = trpc.viewer.eventTypes.update.useMutation({
|
const updateMutation = trpc.viewer.eventTypes.heavy.update.useMutation({
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
const currentValues = form.getValues();
|
const currentValues = form.getValues();
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export const useConnectedCalendars = (props: { enabled?: boolean }) => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
enabled: props?.enabled !== undefined ? props.enabled && isInit : isInit,
|
enabled: props?.enabled !== undefined ? props.enabled && isInit : isInit,
|
||||||
|
staleTime: 5000,
|
||||||
});
|
});
|
||||||
|
|
||||||
return calendars;
|
return calendars;
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ export { getPublicEvent, type PublicEventType } from "@calcom/features/eventtype
|
|||||||
|
|
||||||
export { getBulkUserEventTypes, getBulkTeamEventTypes } from "@calcom/lib/event-types/getBulkEventTypes";
|
export { getBulkUserEventTypes, getBulkTeamEventTypes } from "@calcom/lib/event-types/getBulkEventTypes";
|
||||||
|
|
||||||
export { createHandler as createEventType } from "@calcom/trpc/server/routers/viewer/eventTypes/create.handler";
|
export { createHandler as createEventType } from "@calcom/trpc/server/routers/viewer/eventTypes/heavy/create.handler";
|
||||||
export { updateHandler as updateEventType } from "@calcom/trpc/server/routers/viewer/eventTypes/update.handler";
|
export { updateHandler as updateEventType } from "@calcom/trpc/server/routers/viewer/eventTypes/heavy/update.handler";
|
||||||
|
|
||||||
export type { TUpdateInputSchema as TUpdateEventTypeInputSchema } from "@calcom/trpc/server/routers/viewer/eventTypes/update.schema";
|
export type { TUpdateInputSchema as TUpdateEventTypeInputSchema } from "@calcom/trpc/server/routers/viewer/eventTypes/heavy/update.schema";
|
||||||
export type { EventTypesPublic } from "@calcom/lib/event-types/getEventTypesPublic";
|
export type { EventTypesPublic } from "@calcom/lib/event-types/getEventTypesPublic";
|
||||||
export { getEventTypesPublic } from "@calcom/lib/event-types/getEventTypesPublic";
|
export { getEventTypesPublic } from "@calcom/lib/event-types/getEventTypesPublic";
|
||||||
export { parseEventTypeColor } from "@calcom/lib/isEventTypeColor";
|
export { parseEventTypeColor } from "@calcom/lib/isEventTypeColor";
|
||||||
@@ -24,7 +24,7 @@ export { getEventTypeById } from "@calcom/lib/event-types/getEventTypeById";
|
|||||||
export { getEventTypesByViewer } from "@calcom/lib/event-types/getEventTypesByViewer";
|
export { getEventTypesByViewer } from "@calcom/lib/event-types/getEventTypesByViewer";
|
||||||
export type { EventType } from "@calcom/lib/event-types/getEventTypeById";
|
export type { EventType } from "@calcom/lib/event-types/getEventTypeById";
|
||||||
export type { EventTypesByViewer } from "@calcom/lib/event-types/getEventTypesByViewer";
|
export type { EventTypesByViewer } from "@calcom/lib/event-types/getEventTypesByViewer";
|
||||||
export type { UpdateEventTypeReturn } from "@calcom/trpc/server/routers/viewer/eventTypes/update.handler";
|
export type { UpdateEventTypeReturn } from "@calcom/trpc/server/routers/viewer/eventTypes/heavy/update.handler";
|
||||||
export { updateNewTeamMemberEventTypes } from "@calcom/lib/server/queries/teams";
|
export { updateNewTeamMemberEventTypes } from "@calcom/lib/server/queries/teams";
|
||||||
|
|
||||||
export { bulkUpdateEventsToDefaultLocation } from "@calcom/lib/bulkUpdateEventsToDefaultLocation";
|
export { bulkUpdateEventsToDefaultLocation } from "@calcom/lib/bulkUpdateEventsToDefaultLocation";
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import {
|
|||||||
import { handleCreatePhoneCall } from "@calcom/features/handleCreatePhoneCall";
|
import { handleCreatePhoneCall } from "@calcom/features/handleCreatePhoneCall";
|
||||||
import handleMarkNoShow from "@calcom/features/handleMarkNoShow";
|
import handleMarkNoShow from "@calcom/features/handleMarkNoShow";
|
||||||
import * as instantMeetingMethods from "@calcom/features/instant-meeting/handleInstantMeeting";
|
import * as instantMeetingMethods from "@calcom/features/instant-meeting/handleInstantMeeting";
|
||||||
|
import { getRoutedUrl } from "@calcom/features/routing-forms/lib/getRoutedUrl";
|
||||||
import getAllUserBookings from "@calcom/lib/bookings/getAllUserBookings";
|
import getAllUserBookings from "@calcom/lib/bookings/getAllUserBookings";
|
||||||
import { symmetricEncrypt, symmetricDecrypt } from "@calcom/lib/crypto";
|
import { symmetricEncrypt, symmetricDecrypt } from "@calcom/lib/crypto";
|
||||||
import { getRoutedUrl } from "@calcom/lib/server/getRoutedUrl";
|
|
||||||
import { getTeamMemberEmailForResponseOrContactUsingUrlQuery } from "@calcom/lib/server/getTeamMemberEmailFromCrm";
|
import { getTeamMemberEmailForResponseOrContactUsingUrlQuery } from "@calcom/lib/server/getTeamMemberEmailFromCrm";
|
||||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||||
import type { Prisma } from "@calcom/prisma/client";
|
import type { Prisma } from "@calcom/prisma/client";
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export const ENDPOINTS = [
|
|||||||
"deploymentSetup",
|
"deploymentSetup",
|
||||||
"dsync",
|
"dsync",
|
||||||
"eventTypes",
|
"eventTypes",
|
||||||
|
"eventTypes/heavy",
|
||||||
"features",
|
"features",
|
||||||
"i18n",
|
"i18n",
|
||||||
"insights",
|
"insights",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const resolveEndpoint = (links: any) => {
|
|||||||
// to the correct API endpoints.
|
// to the correct API endpoints.
|
||||||
// - viewer.me - 2 segment paths like this are for logged in requests
|
// - viewer.me - 2 segment paths like this are for logged in requests
|
||||||
// - viewer.public.i18n - 3 segments paths can be public or authed
|
// - viewer.public.i18n - 3 segments paths can be public or authed
|
||||||
|
// - viewer.eventTypes.heavy.create - 4 segments paths for heavy sub-router
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
return (ctx: any) => {
|
return (ctx: any) => {
|
||||||
const parts = ctx.op.path.split(".");
|
const parts = ctx.op.path.split(".");
|
||||||
@@ -35,6 +36,9 @@ const resolveEndpoint = (links: any) => {
|
|||||||
if (parts.length == 2) {
|
if (parts.length == 2) {
|
||||||
endpoint = parts[0] as keyof typeof links;
|
endpoint = parts[0] as keyof typeof links;
|
||||||
path = parts[1];
|
path = parts[1];
|
||||||
|
} else if (parts.length >= 3 && parts[2] === "heavy") {
|
||||||
|
endpoint = parts[1] + "/heavy" as keyof typeof links;
|
||||||
|
path = parts[3];
|
||||||
} else {
|
} else {
|
||||||
endpoint = parts[1] as keyof typeof links;
|
endpoint = parts[1] as keyof typeof links;
|
||||||
path = parts.splice(2, parts.length - 2).join(".");
|
path = parts.splice(2, parts.length - 2).join(".");
|
||||||
|
|||||||
+3
-3
@@ -1,12 +1,12 @@
|
|||||||
|
import getAppKeysFromSlug from "@calcom/app-store/_utils/getAppKeysFromSlug";
|
||||||
|
import { refreshAccessToken } from "@calcom/app-store/basecamp3/lib/helpers";
|
||||||
|
import type { BasecampToken } from "@calcom/app-store/basecamp3/lib/types";
|
||||||
import type { PrismaClient } from "@calcom/prisma/client";
|
import type { PrismaClient } from "@calcom/prisma/client";
|
||||||
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
|
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
|
||||||
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
||||||
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
|
|
||||||
import { refreshAccessToken } from "../lib/helpers";
|
|
||||||
import type { BasecampToken } from "../lib/types";
|
|
||||||
import type { TProjectMutationInputSchema } from "./projectMutation.schema";
|
import type { TProjectMutationInputSchema } from "./projectMutation.schema";
|
||||||
|
|
||||||
interface ProjectMutationHandlerOptions {
|
interface ProjectMutationHandlerOptions {
|
||||||
+3
-4
@@ -1,13 +1,12 @@
|
|||||||
|
import getAppKeysFromSlug from "@calcom/app-store/_utils/getAppKeysFromSlug";
|
||||||
|
import { refreshAccessToken } from "@calcom/app-store/basecamp3/lib/helpers";
|
||||||
|
import type { BasecampToken } from "@calcom/app-store/basecamp3/lib/types";
|
||||||
import type { PrismaClient } from "@calcom/prisma/client";
|
import type { PrismaClient } from "@calcom/prisma/client";
|
||||||
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
|
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
|
||||||
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
||||||
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
|
|
||||||
import { refreshAccessToken } from "../lib/helpers";
|
|
||||||
import type { BasecampToken } from "../lib/types";
|
|
||||||
|
|
||||||
interface ProjectsHandlerOptions {
|
interface ProjectsHandlerOptions {
|
||||||
ctx: {
|
ctx: {
|
||||||
prisma: PrismaClient;
|
prisma: PrismaClient;
|
||||||
+1
-1
@@ -1,3 +1,4 @@
|
|||||||
|
import getConnectedForms from "@calcom/app-store/routing-forms/lib/getConnectedForms";
|
||||||
import { entityPrismaWhereClause } from "@calcom/lib/entityPermissionUtils.server";
|
import { entityPrismaWhereClause } from "@calcom/lib/entityPermissionUtils.server";
|
||||||
import type { PrismaClient } from "@calcom/prisma";
|
import type { PrismaClient } from "@calcom/prisma";
|
||||||
import { MembershipRole } from "@calcom/prisma/enums";
|
import { MembershipRole } from "@calcom/prisma/enums";
|
||||||
@@ -5,7 +6,6 @@ import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
|||||||
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
import getConnectedForms from "../lib/getConnectedForms";
|
|
||||||
import type { TDeleteFormInputSchema } from "./deleteForm.schema";
|
import type { TDeleteFormInputSchema } from "./deleteForm.schema";
|
||||||
import { checkPermissionOnExistingRoutingForm } from "./permissions";
|
import { checkPermissionOnExistingRoutingForm } from "./permissions";
|
||||||
|
|
||||||
+9
-10
@@ -1,6 +1,13 @@
|
|||||||
import type { App_RoutingForms_Form } from "@prisma/client";
|
import type { App_RoutingForms_Form } from "@prisma/client";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
|
|
||||||
|
import { createFallbackRoute } from "@calcom/app-store/routing-forms/lib/createFallbackRoute";
|
||||||
|
import { getSerializableForm } from "@calcom/app-store/routing-forms/lib/getSerializableForm";
|
||||||
|
import { isFallbackRoute } from "@calcom/app-store/routing-forms/lib/isFallbackRoute";
|
||||||
|
import isRouter from "@calcom/app-store/routing-forms/lib/isRouter";
|
||||||
|
import isRouterLinkedField from "@calcom/app-store/routing-forms/lib/isRouterLinkedField";
|
||||||
|
import type { SerializableForm } from "@calcom/app-store/routing-forms/types/types";
|
||||||
|
import { zodFields, zodRouterRoute, zodRoutes } from "@calcom/app-store/routing-forms/zod";
|
||||||
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
|
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
|
||||||
import { entityPrismaWhereClause, canEditEntity } from "@calcom/lib/entityPermissionUtils.server";
|
import { entityPrismaWhereClause, canEditEntity } from "@calcom/lib/entityPermissionUtils.server";
|
||||||
import type { PrismaClient } from "@calcom/prisma";
|
import type { PrismaClient } from "@calcom/prisma";
|
||||||
@@ -9,14 +16,6 @@ import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
|||||||
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
import { createFallbackRoute } from "../lib/createFallbackRoute";
|
|
||||||
import { getSerializableForm } from "../lib/getSerializableForm";
|
|
||||||
import { isFallbackRoute } from "../lib/isFallbackRoute";
|
|
||||||
import { isFormCreateEditAllowed } from "../lib/isFormCreateEditAllowed";
|
|
||||||
import isRouter from "../lib/isRouter";
|
|
||||||
import isRouterLinkedField from "../lib/isRouterLinkedField";
|
|
||||||
import type { SerializableForm } from "../types/types";
|
|
||||||
import { zodFields, zodRouterRoute, zodRoutes } from "../zod";
|
|
||||||
import type { TFormMutationInputSchema } from "./formMutation.schema";
|
import type { TFormMutationInputSchema } from "./formMutation.schema";
|
||||||
import { checkPermissionOnExistingRoutingForm } from "./permissions";
|
import { checkPermissionOnExistingRoutingForm } from "./permissions";
|
||||||
|
|
||||||
@@ -69,8 +68,8 @@ export const formMutationHandler = async ({ ctx, input }: FormMutationHandlerOpt
|
|||||||
|
|
||||||
inputFields = inputFields || [];
|
inputFields = inputFields || [];
|
||||||
inputRoutes = inputRoutes || [];
|
inputRoutes = inputRoutes || [];
|
||||||
type InputFields = typeof inputFields;
|
type InputFields = NonNullable<typeof inputFields>;
|
||||||
type InputRoutes = typeof inputRoutes;
|
type InputRoutes = NonNullable<typeof inputRoutes>;
|
||||||
let routes: InputRoutes;
|
let routes: InputRoutes;
|
||||||
let fields: InputFields;
|
let fields: InputFields;
|
||||||
type DuplicateFrom = NonNullable<typeof duplicateFrom>;
|
type DuplicateFrom = NonNullable<typeof duplicateFrom>;
|
||||||
+1
-2
@@ -1,9 +1,8 @@
|
|||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
|
import { zodFields, zodRoutes } from "@calcom/app-store/routing-forms/zod";
|
||||||
import { RoutingFormSettings } from "@calcom/prisma/zod-utils";
|
import { RoutingFormSettings } from "@calcom/prisma/zod-utils";
|
||||||
|
|
||||||
import { zodFields, zodRoutes } from "../zod";
|
|
||||||
|
|
||||||
export const ZFormMutationInputSchema = z.object({
|
export const ZFormMutationInputSchema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
+1
-3
@@ -1,11 +1,9 @@
|
|||||||
|
import { getSerializableForm } from "@calcom/app-store/routing-forms/lib/getSerializableForm";
|
||||||
import { entityPrismaWhereClause } from "@calcom/lib/entityPermissionUtils.server";
|
import { entityPrismaWhereClause } from "@calcom/lib/entityPermissionUtils.server";
|
||||||
import type { PrismaClient } from "@calcom/prisma";
|
import type { PrismaClient } from "@calcom/prisma";
|
||||||
import { MembershipRole } from "@calcom/prisma/enums";
|
import { MembershipRole } from "@calcom/prisma/enums";
|
||||||
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
||||||
|
|
||||||
import { TRPCError } from "@trpc/server";
|
|
||||||
|
|
||||||
import { getSerializableForm } from "../lib/getSerializableForm";
|
|
||||||
import type { TFormQueryInputSchema } from "./formQuery.schema";
|
import type { TFormQueryInputSchema } from "./formQuery.schema";
|
||||||
import { checkPermissionOnExistingRoutingForm } from "./permissions";
|
import { checkPermissionOnExistingRoutingForm } from "./permissions";
|
||||||
|
|
||||||
+2
-2
@@ -1,5 +1,7 @@
|
|||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
|
|
||||||
|
import { getSerializableForm } from "@calcom/app-store/routing-forms/lib/getSerializableForm";
|
||||||
|
import type { zodFields, zodRoutes } from "@calcom/app-store/routing-forms/zod";
|
||||||
import { hasFilter } from "@calcom/features/filters/lib/hasFilter";
|
import { hasFilter } from "@calcom/features/filters/lib/hasFilter";
|
||||||
import { entityPrismaWhereClause, canEditEntity } from "@calcom/lib/entityPermissionUtils.server";
|
import { entityPrismaWhereClause, canEditEntity } from "@calcom/lib/entityPermissionUtils.server";
|
||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
@@ -9,8 +11,6 @@ import type { Prisma } from "@calcom/prisma/client";
|
|||||||
import { entries } from "@calcom/prisma/zod-utils";
|
import { entries } from "@calcom/prisma/zod-utils";
|
||||||
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
||||||
|
|
||||||
import { getSerializableForm } from "../lib/getSerializableForm";
|
|
||||||
import type { zodFields, zodRoutes } from "../zod";
|
|
||||||
import type { TFormSchema } from "./forms.schema";
|
import type { TFormSchema } from "./forms.schema";
|
||||||
|
|
||||||
interface FormsHandlerOptions {
|
interface FormsHandlerOptions {
|
||||||
+1
-1
@@ -1,10 +1,10 @@
|
|||||||
|
import { enabledIncompleteBookingApps } from "@calcom/app-store/routing-forms/lib/enabledIncompleteBookingApps";
|
||||||
import type { PrismaClient } from "@calcom/prisma";
|
import type { PrismaClient } from "@calcom/prisma";
|
||||||
import { safeCredentialSelect } from "@calcom/prisma/selects/credential";
|
import { safeCredentialSelect } from "@calcom/prisma/selects/credential";
|
||||||
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
||||||
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
import { enabledIncompleteBookingApps } from "../lib/enabledIncompleteBookingApps";
|
|
||||||
import type { TGetIncompleteBookingSettingsInputSchema } from "./getIncompleteBookingSettings.schema";
|
import type { TGetIncompleteBookingSettingsInputSchema } from "./getIncompleteBookingSettings.schema";
|
||||||
|
|
||||||
interface GetIncompleteBookingSettingsOptions {
|
interface GetIncompleteBookingSettingsOptions {
|
||||||
+3
-3
@@ -1,5 +1,8 @@
|
|||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
|
|
||||||
|
import { enrichFormWithMigrationData } from "@calcom/app-store/routing-forms/enrichFormWithMigrationData";
|
||||||
|
import { getSerializableForm } from "@calcom/app-store/routing-forms/lib/getSerializableForm";
|
||||||
|
import type { FormResponse } from "@calcom/app-store/routing-forms/types/types";
|
||||||
import { canAccessEntity } from "@calcom/lib/entityPermissionUtils.server";
|
import { canAccessEntity } from "@calcom/lib/entityPermissionUtils.server";
|
||||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||||
import { prisma } from "@calcom/prisma";
|
import { prisma } from "@calcom/prisma";
|
||||||
@@ -7,9 +10,6 @@ import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
|||||||
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
import { enrichFormWithMigrationData } from "../enrichFormWithMigrationData";
|
|
||||||
import { getSerializableForm } from "../lib/getSerializableForm";
|
|
||||||
import type { FormResponse } from "../types/types";
|
|
||||||
import type { ZFormByResponseIdInputSchema } from "./_router";
|
import type { ZFormByResponseIdInputSchema } from "./_router";
|
||||||
|
|
||||||
type GetResponseWithFormFieldsOptions = {
|
type GetResponseWithFormFieldsOptions = {
|
||||||
+1
-1
@@ -1,10 +1,10 @@
|
|||||||
|
import incompleteBookingActionDataSchemas from "@calcom/app-store/routing-forms/lib/incompleteBooking/actionDataSchemas";
|
||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
import type { PrismaClient } from "@calcom/prisma";
|
import type { PrismaClient } from "@calcom/prisma";
|
||||||
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
||||||
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
import incompleteBookingActionDataSchemas from "../lib/incompleteBooking/actionDataSchemas";
|
|
||||||
import type { TSaveIncompleteBookingSettingsInputSchema } from "./saveIncompleteBookingSettings.schema";
|
import type { TSaveIncompleteBookingSettingsInputSchema } from "./saveIncompleteBookingSettings.schema";
|
||||||
|
|
||||||
const log = logger.getSubLogger({ prefix: ["incomplete-booking"] });
|
const log = logger.getSubLogger({ prefix: ["incomplete-booking"] });
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { StripeService } from "@calcom/lib/server/service/stripe";
|
import { StripeService } from "@calcom/features/ee/payments/server/stripe-service";
|
||||||
|
|
||||||
|
import { ZStripeCheckoutSessionInputSchema } from "./stripeCheckoutSession.schema";
|
||||||
import type { TStripeCheckoutSessionInputSchema } from "./stripeCheckoutSession.schema";
|
import type { TStripeCheckoutSessionInputSchema } from "./stripeCheckoutSession.schema";
|
||||||
|
|
||||||
type StripeCheckoutSessionOptions = {
|
type StripeCheckoutSessionOptions = {
|
||||||
@@ -7,7 +8,9 @@ type StripeCheckoutSessionOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const stripeCheckoutSessionHandler = async ({ input }: StripeCheckoutSessionOptions) => {
|
export const stripeCheckoutSessionHandler = async ({ input }: StripeCheckoutSessionOptions) => {
|
||||||
return await StripeService.getCheckoutSession(input);
|
const parsedInput = ZStripeCheckoutSessionInputSchema.parse(input);
|
||||||
|
|
||||||
|
return await StripeService.getCheckoutSession(parsedInput);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default stripeCheckoutSessionHandler;
|
export default stripeCheckoutSessionHandler;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import app_Basecamp3 from "@calcom/app-store/basecamp3/trpc-router";
|
|
||||||
import app_RoutingForms from "@calcom/app-store/routing-forms/trpc-router";
|
|
||||||
import { userAdminRouter } from "@calcom/features/ee/users/server/trpc-router";
|
import { userAdminRouter } from "@calcom/features/ee/users/server/trpc-router";
|
||||||
import { featureFlagRouter } from "@calcom/features/flags/server/router";
|
import { featureFlagRouter } from "@calcom/features/flags/server/router";
|
||||||
import { insightsRouter } from "@calcom/features/insights/server/trpc-router";
|
import { insightsRouter } from "@calcom/features/insights/server/trpc-router";
|
||||||
|
|
||||||
import { router } from "../../trpc";
|
import { router, mergeRouters } from "../../trpc";
|
||||||
|
import app_Basecamp3 from "../apps/basecamp3/_router";
|
||||||
|
import app_RoutingForms from "../apps/routing-forms/_router";
|
||||||
import { loggedInViewerRouter } from "../loggedInViewer/_router";
|
import { loggedInViewerRouter } from "../loggedInViewer/_router";
|
||||||
import { publicViewerRouter } from "../publicViewer/_router";
|
import { publicViewerRouter } from "../publicViewer/_router";
|
||||||
import { timezonesRouter } from "../publicViewer/timezones/_router";
|
import { timezonesRouter } from "../publicViewer/timezones/_router";
|
||||||
@@ -24,6 +24,7 @@ import { delegationCredentialRouter } from "./delegationCredential/_router";
|
|||||||
import { deploymentSetupRouter } from "./deploymentSetup/_router";
|
import { deploymentSetupRouter } from "./deploymentSetup/_router";
|
||||||
import { dsyncRouter } from "./dsync/_router";
|
import { dsyncRouter } from "./dsync/_router";
|
||||||
import { eventTypesRouter } from "./eventTypes/_router";
|
import { eventTypesRouter } from "./eventTypes/_router";
|
||||||
|
import { eventTypesRouter as heavyEventTypesRouter } from "./eventTypes/heavy/_router";
|
||||||
import { filterSegmentsRouter } from "./filterSegments/_router";
|
import { filterSegmentsRouter } from "./filterSegments/_router";
|
||||||
import { googleWorkspaceRouter } from "./googleWorkspace/_router";
|
import { googleWorkspaceRouter } from "./googleWorkspace/_router";
|
||||||
import { i18nRouter } from "./i18n/_router";
|
import { i18nRouter } from "./i18n/_router";
|
||||||
@@ -53,7 +54,12 @@ export const viewerRouter = router({
|
|||||||
calendars: calendarsRouter,
|
calendars: calendarsRouter,
|
||||||
calVideo: calVideoRouter,
|
calVideo: calVideoRouter,
|
||||||
credentials: credentialsRouter,
|
credentials: credentialsRouter,
|
||||||
eventTypes: eventTypesRouter,
|
eventTypes: mergeRouters(
|
||||||
|
eventTypesRouter,
|
||||||
|
router({
|
||||||
|
heavy: heavyEventTypesRouter,
|
||||||
|
})
|
||||||
|
),
|
||||||
availability: availabilityRouter,
|
availability: availabilityRouter,
|
||||||
teams: viewerTeamsRouter,
|
teams: viewerTeamsRouter,
|
||||||
timezones: timezonesRouter,
|
timezones: timezonesRouter,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { DailyLocationType } from "@calcom/app-store/locations";
|
import { DailyLocationType } from "@calcom/app-store/constants";
|
||||||
|
|
||||||
import { commonBookingSchema } from "./types";
|
import { commonBookingSchema } from "./types";
|
||||||
|
|
||||||
|
|||||||
@@ -4,15 +4,12 @@ import { logP } from "@calcom/lib/perf";
|
|||||||
|
|
||||||
import authedProcedure from "../../../procedures/authedProcedure";
|
import authedProcedure from "../../../procedures/authedProcedure";
|
||||||
import { router } from "../../../trpc";
|
import { router } from "../../../trpc";
|
||||||
import { ZCreateInputSchema } from "./create.schema";
|
|
||||||
import { ZDeleteInputSchema } from "./delete.schema";
|
import { ZDeleteInputSchema } from "./delete.schema";
|
||||||
import { ZDuplicateInputSchema } from "./duplicate.schema";
|
|
||||||
import { ZEventTypeInputSchema, ZGetEventTypesFromGroupSchema } from "./getByViewer.schema";
|
import { ZEventTypeInputSchema, ZGetEventTypesFromGroupSchema } from "./getByViewer.schema";
|
||||||
import { ZGetHashedLinkInputSchema } from "./getHashedLink.schema";
|
import { ZGetHashedLinkInputSchema } from "./getHashedLink.schema";
|
||||||
import { ZGetHashedLinksInputSchema } from "./getHashedLinks.schema";
|
import { ZGetHashedLinksInputSchema } from "./getHashedLinks.schema";
|
||||||
import { ZGetTeamAndEventTypeOptionsSchema } from "./getTeamAndEventTypeOptions.schema";
|
import { ZGetTeamAndEventTypeOptionsSchema } from "./getTeamAndEventTypeOptions.schema";
|
||||||
import { get } from "./procedures/get";
|
import { get } from "./procedures/get";
|
||||||
import { ZUpdateInputSchema } from "./update.schema";
|
|
||||||
import { eventOwnerProcedure } from "./util";
|
import { eventOwnerProcedure } from "./util";
|
||||||
|
|
||||||
type BookingsRouterHandlerCache = {
|
type BookingsRouterHandlerCache = {
|
||||||
@@ -22,11 +19,8 @@ type BookingsRouterHandlerCache = {
|
|||||||
getTeamAndEventTypeOptions?: typeof import("./getTeamAndEventTypeOptions.handler").getTeamAndEventTypeOptions;
|
getTeamAndEventTypeOptions?: typeof import("./getTeamAndEventTypeOptions.handler").getTeamAndEventTypeOptions;
|
||||||
list?: typeof import("./list.handler").listHandler;
|
list?: typeof import("./list.handler").listHandler;
|
||||||
listWithTeam?: typeof import("./listWithTeam.handler").listWithTeamHandler;
|
listWithTeam?: typeof import("./listWithTeam.handler").listWithTeamHandler;
|
||||||
create?: typeof import("./create.handler").createHandler;
|
|
||||||
get?: typeof import("./get.handler").getHandler;
|
get?: typeof import("./get.handler").getHandler;
|
||||||
update?: typeof import("./update.handler").updateHandler;
|
|
||||||
delete?: typeof import("./delete.handler").deleteHandler;
|
delete?: typeof import("./delete.handler").deleteHandler;
|
||||||
duplicate?: typeof import("./duplicate.handler").duplicateHandler;
|
|
||||||
bulkEventFetch?: typeof import("./bulkEventFetch.handler").bulkEventFetchHandler;
|
bulkEventFetch?: typeof import("./bulkEventFetch.handler").bulkEventFetchHandler;
|
||||||
bulkUpdateToDefaultLocation?: typeof import("./bulkUpdateToDefaultLocation.handler").bulkUpdateToDefaultLocationHandler;
|
bulkUpdateToDefaultLocation?: typeof import("./bulkUpdateToDefaultLocation.handler").bulkUpdateToDefaultLocationHandler;
|
||||||
};
|
};
|
||||||
@@ -115,26 +109,8 @@ export const eventTypesRouter = router({
|
|||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
create: authedProcedure.input(ZCreateInputSchema).mutation(async ({ ctx, input }) => {
|
|
||||||
const { createHandler } = await import("./create.handler");
|
|
||||||
|
|
||||||
return createHandler({
|
|
||||||
ctx,
|
|
||||||
input,
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
|
|
||||||
get,
|
get,
|
||||||
|
|
||||||
update: eventOwnerProcedure.input(ZUpdateInputSchema).mutation(async ({ ctx, input }) => {
|
|
||||||
const { updateHandler } = await import("./update.handler");
|
|
||||||
|
|
||||||
return updateHandler({
|
|
||||||
ctx,
|
|
||||||
input,
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
|
|
||||||
delete: eventOwnerProcedure.input(ZDeleteInputSchema).mutation(async ({ ctx, input }) => {
|
delete: eventOwnerProcedure.input(ZDeleteInputSchema).mutation(async ({ ctx, input }) => {
|
||||||
const { deleteHandler } = await import("./delete.handler");
|
const { deleteHandler } = await import("./delete.handler");
|
||||||
|
|
||||||
@@ -144,15 +120,6 @@ export const eventTypesRouter = router({
|
|||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
duplicate: eventOwnerProcedure.input(ZDuplicateInputSchema).mutation(async ({ ctx, input }) => {
|
|
||||||
const { duplicateHandler } = await import("./duplicate.handler");
|
|
||||||
|
|
||||||
return duplicateHandler({
|
|
||||||
ctx,
|
|
||||||
input,
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
|
|
||||||
bulkEventFetch: authedProcedure.query(async ({ ctx }) => {
|
bulkEventFetch: authedProcedure.query(async ({ ctx }) => {
|
||||||
const { bulkEventFetchHandler } = await import("./bulkEventFetch.handler");
|
const { bulkEventFetchHandler } = await import("./bulkEventFetch.handler");
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { logP } from "@calcom/lib/perf";
|
||||||
|
|
||||||
|
import authedProcedure from "../../../../procedures/authedProcedure";
|
||||||
|
import { router } from "../../../../trpc";
|
||||||
|
import { ZCreateInputSchema } from "./create.schema";
|
||||||
|
import { ZDuplicateInputSchema } from "./duplicate.schema";
|
||||||
|
import { eventOwnerProcedure } from "../util";
|
||||||
|
import { ZUpdateInputSchema } from "./update.schema";
|
||||||
|
|
||||||
|
type BookingsRouterHandlerCache = {
|
||||||
|
create?: typeof import("./create.handler").createHandler;
|
||||||
|
duplicate?: typeof import("./duplicate.handler").duplicateHandler;
|
||||||
|
update?: typeof import("./update.handler").updateHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
const UNSTABLE_HANDLER_CACHE: BookingsRouterHandlerCache = {};
|
||||||
|
|
||||||
|
export const eventTypesRouter = router({
|
||||||
|
create: authedProcedure.input(ZCreateInputSchema).mutation(async ({ ctx, input }) => {
|
||||||
|
const { createHandler } = await import("./create.handler");
|
||||||
|
|
||||||
|
return createHandler({
|
||||||
|
ctx,
|
||||||
|
input,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
duplicate: eventOwnerProcedure.input(ZDuplicateInputSchema).mutation(async ({ ctx, input }) => {
|
||||||
|
const { duplicateHandler } = await import("./duplicate.handler");
|
||||||
|
|
||||||
|
return duplicateHandler({
|
||||||
|
ctx,
|
||||||
|
input,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
update: eventOwnerProcedure.input(ZUpdateInputSchema).mutation(async ({ ctx, input }) => {
|
||||||
|
const { updateHandler } = await import("./update.handler");
|
||||||
|
|
||||||
|
return updateHandler({
|
||||||
|
ctx,
|
||||||
|
input,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
|
|
||||||
import { DailyLocationType } from "@calcom/app-store/locations";
|
import { DailyLocationType } from "@calcom/app-store/constants";
|
||||||
import { getDefaultLocations } from "@calcom/lib/server/getDefaultLocations";
|
import { getDefaultLocations } from "@calcom/lib/server/getDefaultLocations";
|
||||||
import { EventTypeRepository } from "@calcom/lib/server/repository/eventTypeRepository";
|
import { EventTypeRepository } from "@calcom/lib/server/repository/eventTypeRepository";
|
||||||
import type { PrismaClient } from "@calcom/prisma";
|
import type { PrismaClient } from "@calcom/prisma";
|
||||||
@@ -9,7 +9,7 @@ import type { EventTypeLocation } from "@calcom/prisma/zod/custom/eventtype";
|
|||||||
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
import type { TrpcSessionUser } from "../../../types";
|
import type { TrpcSessionUser } from "../../../../types";
|
||||||
import type { TCreateInputSchema } from "./create.schema";
|
import type { TCreateInputSchema } from "./create.schema";
|
||||||
|
|
||||||
type SessionUser = NonNullable<TrpcSessionUser>;
|
type SessionUser = NonNullable<TrpcSessionUser>;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import prismaMock from "../../../../../../tests/libs/__mocks__/prismaMock";
|
import prismaMock from "../../../../../../../tests/libs/__mocks__/prismaMock";
|
||||||
|
|
||||||
import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
|
import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
|
||||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user