Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: handleNewBooking #4 #35

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/api/v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"dependencies": {
"@calcom/platform-constants": "*",
"@calcom/platform-enums": "*",
"@calcom/platform-libraries": "npm:@calcom/[email protected].34",
"@calcom/platform-libraries": "npm:@calcom/[email protected].36",
"@calcom/platform-libraries-0.0.2": "npm:@calcom/[email protected]",
"@calcom/platform-types": "*",
"@calcom/platform-utils": "*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
updateEventType,
EventTypesPublic,
getEventTypesPublic,
systemBeforeFieldEmail,
} from "@calcom/platform-libraries";
import { EventType } from "@calcom/prisma/client";

Expand Down Expand Up @@ -127,8 +128,17 @@ export class EventTypesService_2024_04_15 {
async updateEventType(eventTypeId: number, body: UpdateEventTypeInput_2024_04_15, user: UserWithProfile) {
this.checkCanUpdateEventType(user.id, eventTypeId);
const eventTypeUser = await this.getUserToUpdateEvent(user);
const bookingFields = [...(body.bookingFields || [])];

if (
!bookingFields.find((field) => field.type === "email") &&
!bookingFields.find((field) => field.type === "phone")
) {
bookingFields.push(systemBeforeFieldEmail);
}

await updateEventType({
input: { id: eventTypeId, ...body },
input: { id: eventTypeId, ...body, bookingFields },
ctx: {
user: eventTypeUser,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,6 @@ describe("Event types Endpoints", () => {
expect(createdEventType.description).toEqual(body.description);
expect(createdEventType.lengthInMinutes).toEqual(body.lengthInMinutes);
expect(createdEventType.locations).toEqual(body.locations);
expect(createdEventType.bookingFields).toEqual(body.bookingFields);
expect(createdEventType.ownerId).toEqual(user.id);
expect(createdEventType.scheduleId).toEqual(firstSchedule.id);
expect(createdEventType.bookingLimitsCount).toEqual(body.bookingLimitsCount);
Expand All @@ -259,6 +258,16 @@ describe("Event types Endpoints", () => {
expect(createdEventType.offsetStart).toEqual(body.offsetStart);
expect(createdEventType.bookingWindow).toEqual(body.bookingWindow);
expect(createdEventType.recurrence).toEqual(body.recurrence);

const responseBookingFields = body.bookingFields || [];
const expectedBookingFields = [
{ isDefault: true, required: true, slug: "name", type: "name" },
{ isDefault: true, required: true, slug: "email", type: "email" },
{ isDefault: true, required: false, slug: "rescheduleReason", type: "textarea" },
...responseBookingFields.map((field) => ({ isDefault: false, ...field })),
];

expect(createdEventType.bookingFields).toEqual(expectedBookingFields);
eventType = responseBody.data;
});
});
Expand Down Expand Up @@ -476,6 +485,16 @@ describe("Event types Endpoints", () => {
let legacyEventTypeId1: number;
let legacyEventTypeId2: number;

const expectedReturnSystemFields = [
{ isDefault: true, required: true, slug: "name", type: "name" },
{ isDefault: true, required: true, slug: "email", type: "email" },
{ isDefault: true, type: "radioInput", slug: "location", required: false },
{ isDefault: true, required: true, slug: "title", type: "text" },
{ isDefault: true, required: false, slug: "notes", type: "textarea" },
{ isDefault: true, required: false, slug: "guests", type: "multiemail" },
{ isDefault: true, required: false, slug: "rescheduleReason", type: "textarea" },
];

beforeAll(async () => {
const moduleRef = await withApiAuth(
userEmail,
Expand Down Expand Up @@ -545,7 +564,7 @@ describe("Event types Endpoints", () => {
.expect(400);
});

it("should return empty bookingFields if system fields are the only one in database", async () => {
it("should return system bookingFields stored in database", async () => {
const legacyEventTypeInput = {
title: "legacy event type",
description: "legacy event type description",
Expand Down Expand Up @@ -638,11 +657,11 @@ describe("Event types Endpoints", () => {
.then(async (response) => {
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14> = response.body;
const fetchedEventType = responseBody.data;
expect(fetchedEventType.bookingFields).toEqual([]);
expect(fetchedEventType.bookingFields).toEqual(expectedReturnSystemFields);
});
});

it("should return user created bookingFields among system fields in the database", async () => {
it("should return user created bookingFields with system fields", async () => {
const userDefinedBookingField = {
name: "team",
type: "textarea",
Expand Down Expand Up @@ -755,7 +774,9 @@ describe("Event types Endpoints", () => {
const fetchedEventType = responseBody.data;

expect(fetchedEventType.bookingFields).toEqual([
...expectedReturnSystemFields,
{
isDefault: false,
type: userDefinedBookingField.type,
slug: userDefinedBookingField.name,
label: userDefinedBookingField.label,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { Injectable } from "@nestjs/common";

import {
getEventTypeById,
transformApiEventTypeBookingFields,
transformApiEventTypeLocations,
transformBookingFieldsApiToInternal,
transformLocationsApiToInternal,
} from "@calcom/platform-libraries";
import { CreateEventTypeInput_2024_06_14 } from "@calcom/platform-types";
import type { PrismaClient } from "@calcom/prisma";
Expand All @@ -30,8 +30,8 @@ type InputEventTransformed = Omit<
> & {
length: number;
slug: string;
locations?: ReturnType<typeof transformApiEventTypeLocations>;
bookingFields?: ReturnType<typeof transformApiEventTypeBookingFields>;
locations?: ReturnType<typeof transformLocationsApiToInternal>;
bookingFields?: ReturnType<typeof transformBookingFieldsApiToInternal>;
};

@Injectable()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { Injectable } from "@nestjs/common";

import {
transformApiEventTypeBookingFields,
transformApiEventTypeLocations,
transformApiEventTypeIntervalLimits,
transformApiEventTypeFutureBookingLimits,
transformApiEventTypeRecurrence,
transformBookingFieldsApiToInternal,
transformLocationsApiToInternal,
transformIntervalLimitsApiToInternal,
transformFutureBookingLimitsApiToInternal,
transformRecurrenceApiToInternal,
systemBeforeFieldName,
systemBeforeFieldEmail,
systemAfterFieldRescheduleReason,
} from "@calcom/platform-libraries";
import { systemBeforeFieldLocation } from "@calcom/platform-libraries";
import { CreateEventTypeInput_2024_06_14, UpdateEventTypeInput_2024_06_14 } from "@calcom/platform-types";

@Injectable()
Expand All @@ -30,11 +34,12 @@ export class InputEventTypesService_2024_06_14 {
...rest
} = inputEventType;

const hasMultipleLocations = (locations || defaultLocations).length > 1;
const eventType = {
...rest,
length: lengthInMinutes,
locations: this.transformInputLocations(locations || defaultLocations),
bookingFields: this.transformInputBookingFields(bookingFields),
bookingFields: this.transformInputBookingFields(bookingFields, hasMultipleLocations),
bookingLimits: bookingLimitsCount ? this.transformInputIntervalLimits(bookingLimitsCount) : undefined,
durationLimits: bookingLimitsDuration
? this.transformInputIntervalLimits(bookingLimitsDuration)
Expand All @@ -59,11 +64,15 @@ export class InputEventTypesService_2024_06_14 {
...rest
} = inputEventType;

const hasMultipleLocations = !!(locations && locations?.length > 1);

const eventType = {
...rest,
length: lengthInMinutes,
locations: locations ? this.transformInputLocations(locations) : undefined,
bookingFields: bookingFields ? this.transformInputBookingFields(bookingFields) : undefined,
bookingFields: bookingFields
? this.transformInputBookingFields(bookingFields, hasMultipleLocations)
: undefined,
schedule: scheduleId,
bookingLimits: bookingLimitsCount ? this.transformInputIntervalLimits(bookingLimitsCount) : undefined,
durationLimits: bookingLimitsDuration
Expand All @@ -77,23 +86,35 @@ export class InputEventTypesService_2024_06_14 {
}

transformInputLocations(inputLocations: CreateEventTypeInput_2024_06_14["locations"]) {
return transformApiEventTypeLocations(inputLocations);
return transformLocationsApiToInternal(inputLocations);
}

transformInputBookingFields(inputBookingFields: CreateEventTypeInput_2024_06_14["bookingFields"]) {
return transformApiEventTypeBookingFields(inputBookingFields);
transformInputBookingFields(
inputBookingFields: CreateEventTypeInput_2024_06_14["bookingFields"],
hasMultipleLocations: boolean
) {
const defaultFieldsBefore = [systemBeforeFieldName, systemBeforeFieldEmail];
// note(Lauris): if event type has multiple locations then a radio button booking field has to be displayed to allow booker to pick location
if (hasMultipleLocations) {
defaultFieldsBefore.push(systemBeforeFieldLocation);
}

const customFields = transformBookingFieldsApiToInternal(inputBookingFields);
const defaultFieldsAfter = [systemAfterFieldRescheduleReason];

return [...defaultFieldsBefore, ...customFields, ...defaultFieldsAfter];
}

transformInputIntervalLimits(inputBookingFields: CreateEventTypeInput_2024_06_14["bookingLimitsCount"]) {
return transformApiEventTypeIntervalLimits(inputBookingFields);
return transformIntervalLimitsApiToInternal(inputBookingFields);
}

transformInputBookingWindow(inputBookingWindow: CreateEventTypeInput_2024_06_14["bookingWindow"]) {
const res = transformApiEventTypeFutureBookingLimits(inputBookingWindow);
const res = transformFutureBookingLimitsApiToInternal(inputBookingWindow);
return !!res ? res : {};
}

transformInputRecurrignEvent(recurrence: CreateEventTypeInput_2024_06_14["recurrence"]) {
return transformApiEventTypeRecurrence(recurrence);
return transformRecurrenceApiToInternal(recurrence);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ import type { EventType, User, Schedule } from "@prisma/client";
import {
EventTypeMetaDataSchema,
userMetadata,
getResponseEventTypeLocations,
getResponseEventTypeBookingFields,
transformLocationsInternalToApi,
transformBookingFieldsInternalToApi,
parseRecurringEvent,
TransformedLocationsSchema,
BookingFieldsSchema,
SystemField,
UserField,
CustomField,
parseBookingLimit,
getResponseEventTypeIntervalLimits,
getResponseEventTypeFutureBookingLimits,
getResponseEventTypeRecurrence,
transformIntervalLimitsInternalToApi,
transformFutureBookingLimitsInternalToApi,
transformRecurrenceInternalToApi,
} from "@calcom/platform-libraries";
import { TransformFutureBookingsLimitSchema_2024_06_14 } from "@calcom/platform-types";

Expand Down Expand Up @@ -144,19 +144,20 @@ export class OutputEventTypesService_2024_06_14 {

transformLocations(locations: any) {
if (!locations) return [];
return getResponseEventTypeLocations(TransformedLocationsSchema.parse(locations));
return transformLocationsInternalToApi(TransformedLocationsSchema.parse(locations));
}

transformBookingFields(inputBookingFields: (SystemField | UserField)[] | null) {
if (!inputBookingFields) return [];
const userFields = inputBookingFields.filter((field) => field.editable === "user") as UserField[];
return getResponseEventTypeBookingFields(userFields);
transformBookingFields(bookingFields: (SystemField | CustomField)[] | null) {
if (!bookingFields) return [];

return transformBookingFieldsInternalToApi(bookingFields);
}

transformRecurringEvent(recurringEvent: any) {
if (!recurringEvent) return null;
const recurringEventParsed = parseRecurringEvent(recurringEvent);
return getResponseEventTypeRecurrence(recurringEventParsed);
if (!recurringEventParsed) return null;
return transformRecurrenceInternalToApi(recurringEventParsed);
}

transformMetadata(metadata: any) {
Expand All @@ -182,10 +183,10 @@ export class OutputEventTypesService_2024_06_14 {

transformIntervalLimits(bookingLimits: any) {
const bookingLimitsParsed = parseBookingLimit(bookingLimits);
return getResponseEventTypeIntervalLimits(bookingLimitsParsed);
return transformIntervalLimitsInternalToApi(bookingLimitsParsed);
}

transformBookingWindow(bookingLimits: TransformFutureBookingsLimitSchema_2024_06_14) {
return getResponseEventTypeFutureBookingLimits(bookingLimits);
return transformFutureBookingLimitsInternalToApi(bookingLimits);
}
}
3 changes: 3 additions & 0 deletions apps/api/v2/test/setEnvVars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,7 @@ const env: Partial<Omit<Environment, "NODE_ENV">> = {
process.env = {
...env,
...process.env,
NEXT_PUBLIC_VAPID_PUBLIC_KEY:
"BIds0AQJ96xGBjTSMHTOqLBLutQE7Lu32KKdgSdy7A2cS4mKI2cgb3iGkhDJa5Siy-stezyuPm8qpbhmNxdNHMw",
VAPID_PRIVATE_KEY: "6cJtkASCar5sZWguIAW7OjvyixpBw9p8zL8WDDwk9Jk",
};
6 changes: 3 additions & 3 deletions apps/web/app/future/event-types/[type]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { cookies, headers } from "next/headers";

import { EventType } from "@calcom/atoms/monorepo";

import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@lib/event-types/[type]/getServerSideProps";
import type { PageProps as EventTypePageProps } from "@lib/event-types/[type]/getServerSideProps";

import EventTypePageWrapper from "~/event-types/views/event-types-single-view";

export const generateMetadata = async ({ params, searchParams }: PageProps) => {
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
const { eventType } = await getData(legacyCtx);
Expand All @@ -21,5 +21,5 @@ export const generateMetadata = async ({ params, searchParams }: PageProps) => {
};

const getData = withAppDirSsr(getServerSideProps);
const Page = (props: EventTypePageProps) => <EventTypePageWrapper {...props} isAppDir={true} />;
const Page = ({ type, ...rest }: EventTypePageProps) => <EventType {...rest} id={type} isAppDir={true} />;
export default WithLayout({ getLayout: null, getData, Page })<"P">;
33 changes: 3 additions & 30 deletions apps/web/modules/event-types/views/event-types-single-view.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,11 @@
"use client";

import { EventType } from "@calcom/features/eventtypes/components/EventType";
import { EventTypeAppDir } from "@calcom/features/eventtypes/components/EventTypeAppDir";
import type { EventTypeSetupProps } from "@calcom/features/eventtypes/lib/types";

/* eslint-disable @typescript-eslint/no-empty-function */
// eslint-disable-next-line @calcom/eslint/deprecated-imports-next-router
import { trpc } from "@calcom/trpc/react";
import { EventType } from "@calcom/atoms/monorepo";

import type { PageProps } from "@lib/event-types/[type]/getServerSideProps";

const EventTypePageWrapper = (props: PageProps & { isAppDir?: boolean }) => {
const { data } = trpc.viewer.eventTypes.get.useQuery({ id: props.type });

if (!data) return null;

const eventType = data.eventType;

const { data: workflows } = trpc.viewer.workflows.getAllActiveWorkflows.useQuery({
eventType: {
id: props.type,
teamId: eventType.teamId,
userId: eventType.userId,
parent: eventType.parent,
metadata: eventType.metadata,
},
});

const propsData = {
...(data as EventTypeSetupProps),
allActiveWorkflows: workflows,
};

return props.isAppDir ? <EventTypeAppDir {...propsData} /> : <EventType {...propsData} />;
const EventTypePageWrapper = ({ type, ...rest }: PageProps) => {
return <EventType {...rest} id={type} isAppDir={false} />;
};

export default EventTypePageWrapper;
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "4.5.0",
"version": "4.5.1",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",
Expand Down
6 changes: 3 additions & 3 deletions apps/web/public/static/locales/ar/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -2369,13 +2369,13 @@
"success_entry_created": "تم إنشاء إدخال جديد بنجاح",
"booking_redirect_email_subject": "إشعار إعادة توجيه الحجز",
"booking_redirect_email_title": "إشعار إعادة توجيه الحجز",
"booking_redirect_email_description": "لقد تلقيت إعادة توجيه للحجز من {{eventOwner}} بحيث سيتم إعادة توجيه روابط ملفهم الشخصي إلى ملفك الشخصي للفترة الزمنية: \"{{dates}}\"",
"booking_redirect_email_description": "لقد تلقيت إعادة توجيه حجز من {{eventOwner}}، لذا سيتم إعادة توجيه روابط ملفهم الشخصي إلى ملفك الشخصي خلال الفترة الزمنية {{dates}}.",
"booking_redirect_updated_email_subject": "إشعار تعديل إعادة توجيه الحجز",
"booking_redirect_updated_email_title": "إشعار تعديل إعادة توجيه الحجز",
"booking_redirect_updated_email_description": "تم تعديل إعادة توجيه الحجز الخاصة بك من {{eventOwner}} للفترة الزمنية \"{{oldDates}}\". الفترة الزمنية الجديدة لإعادة التوجيه هي: \"{{dates}}\"",
"booking_redirect_updated_email_description": "تم تحديث إعادة توجيه الحجز الخاصة بك من {{eventOwner}} للفترة الزمنية {{oldDates}}. الفترة الزمنية الجديدة لإعادة التوجيه هي {{dates}}.",
"booking_redirect_cancelled_email_subject": "إشعار إلغاء إعادة توجيه الحجز",
"booking_redirect_cancelled_email_title": "إشعار إلغاء إعادة توجيه الحجز",
"booking_redirect_cancelled_email_description": "تم إلغاء إعادة توجيه الحجز الخاصة بك من {{eventOwner}} للفترة الزمنية \"{{dates}}\".",
"booking_redirect_cancelled_email_description": "تم إلغاء إعادة توجيه الحجز الخاصة بك من {{eventOwner}} للفترة الزمنية {{dates}}.",
"success_accept_booking_redirect": "لقد قبلت طلب إعادة توجيه الحجز هذا.",
"success_reject_booking_redirect": "لقد رفضت طلب إعادة توجيه الحجز هذا.",
"copy_link_booking_redirect_request": "نسخ الرابط لمشاركة الطلب",
Expand Down
Loading
Loading