Skip to content

Commit

Permalink
feat: Add hideCalendarEventDetails option to event types (#16817)
Browse files Browse the repository at this point in the history
* Add `hideCalendarEventDetails` to DB

Co-authored-by: Alex van Andel <emrysal@users.noreply.github.com>

* Add option to hide calendar event details

* Pass `hideCalendarEventDetails` to event object

* Add to API

* Add to CalendarEvent class

* Pass `hideCalendarEventDetails` to calendar services

* Adjust test

* fix: move `hideCalendarEventDetails` to `api/v2/event-types_2024_06_14` and remove from older versions

- Added `hideCalendarEventDetails` to `api/v2/event-types_2024_06_14`
- Removed `hideCalendarEventDetails` from `api/v1` and `api/v2/event-types_2024_04_15`

* fix: calEventRaw is undefined (use event instead)

* chore: Remove debug artifact

* fix: description id + update to copy

* fix: Attempt at fixing type error

* Add hideCalendarEventDetails to test builder

---------

Co-authored-by: Alex van Andel <emrysal@users.noreply.github.com>
Co-authored-by: Somay Chauhan <somaychauhan98@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
  • Loading branch information
4 people authored Sep 26, 2024
1 parent b6c6e41 commit ae7fae7
Show file tree
Hide file tree
Showing 29 changed files with 93 additions and 5 deletions.
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/platform-libraries@0.0.41",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.43",
"@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2",
"@calcom/platform-types": "*",
"@calcom/platform-utils": "*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ describe("Event types Endpoints", () => {
},
requiresBookerEmailVerification: false,
hideCalendarNotes: false,
hideCalendarEventDetails: false,
lockTimeZoneToggleOnBookingPage: true,
color: {
darkThemeHex: "#292929",
Expand Down Expand Up @@ -297,6 +298,7 @@ describe("Event types Endpoints", () => {
);

expect(createdEventType.hideCalendarNotes).toEqual(body.hideCalendarNotes);
expect(createdEventType.hideCalendarEventDetails).toEqual(body.hideCalendarEventDetails);
expect(createdEventType.lockTimeZoneToggleOnBookingPage).toEqual(
body.lockTimeZoneToggleOnBookingPage
);
Expand Down Expand Up @@ -351,6 +353,7 @@ describe("Event types Endpoints", () => {
eventType.requiresBookerEmailVerification
);
expect(fetchedEventType.hideCalendarNotes).toEqual(eventType.hideCalendarNotes);
expect(fetchedEventType.hideCalendarEventDetails).toEqual(eventType.hideCalendarEventDetails);
expect(fetchedEventType.lockTimeZoneToggleOnBookingPage).toEqual(
eventType.lockTimeZoneToggleOnBookingPage
);
Expand Down Expand Up @@ -667,6 +670,7 @@ describe("Event types Endpoints", () => {
},
requiresBookerEmailVerification: true,
hideCalendarNotes: true,
hideCalendarEventDetails: true,
lockTimeZoneToggleOnBookingPage: true,
color: {
darkThemeHex: "#292929",
Expand Down Expand Up @@ -706,6 +710,7 @@ describe("Event types Endpoints", () => {
body.requiresBookerEmailVerification
);
expect(updatedEventType.hideCalendarNotes).toEqual(body.hideCalendarNotes);
expect(updatedEventType.hideCalendarEventDetails).toEqual(body.hideCalendarEventDetails);
expect(updatedEventType.lockTimeZoneToggleOnBookingPage).toEqual(
body.lockTimeZoneToggleOnBookingPage
);
Expand All @@ -724,6 +729,7 @@ describe("Event types Endpoints", () => {
eventType.customName = updatedEventType.customName;
eventType.requiresBookerEmailVerification = updatedEventType.requiresBookerEmailVerification;
eventType.hideCalendarNotes = updatedEventType.hideCalendarNotes;
eventType.hideCalendarEventDetails = updatedEventType.hideCalendarEventDetails;
eventType.lockTimeZoneToggleOnBookingPage = updatedEventType.lockTimeZoneToggleOnBookingPage;
eventType.color = updatedEventType.color;
});
Expand Down Expand Up @@ -774,6 +780,7 @@ describe("Event types Endpoints", () => {
eventType.requiresBookerEmailVerification
);
expect(fetchedEventType.hideCalendarNotes).toEqual(eventType.hideCalendarNotes);
expect(fetchedEventType.hideCalendarEventDetails).toEqual(eventType.hideCalendarEventDetails);
expect(fetchedEventType.lockTimeZoneToggleOnBookingPage).toEqual(
eventType.lockTimeZoneToggleOnBookingPage
);
Expand Down Expand Up @@ -811,6 +818,7 @@ describe("Event types Endpoints", () => {
eventType.requiresBookerEmailVerification
);
expect(fetchedEventType.hideCalendarNotes).toEqual(eventType.hideCalendarNotes);
expect(fetchedEventType.hideCalendarEventDetails).toEqual(eventType.hideCalendarEventDetails);
expect(fetchedEventType.lockTimeZoneToggleOnBookingPage).toEqual(
eventType.lockTimeZoneToggleOnBookingPage
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ type Input = Pick<
| "eventName"
| "destinationCalendar"
| "useEventTypeDestinationCalendarEmail"
| "hideCalendarEventDetails"
>;

@Injectable()
Expand Down Expand Up @@ -111,6 +112,7 @@ export class OutputEventTypesService_2024_06_14 {
hideCalendarNotes,
seatsShowAttendees,
useEventTypeDestinationCalendarEmail,
hideCalendarEventDetails,
} = databaseEventType;

const locations = this.transformLocations(databaseEventType.locations);
Expand Down Expand Up @@ -182,6 +184,7 @@ export class OutputEventTypesService_2024_06_14 {
customName,
destinationCalendar,
useDestinationCalendarEmail: useEventTypeDestinationCalendarEmail,
hideCalendarEventDetails,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ describe("Organizations Event Types Endpoints", () => {
},
requiresBookerEmailVerification: true,
hideCalendarNotes: true,
hideCalendarEventDetails: true,
lockTimeZoneToggleOnBookingPage: true,
color: {
darkThemeHex: "#292929",
Expand Down Expand Up @@ -330,6 +331,7 @@ describe("Organizations Event Types Endpoints", () => {
expect(data.confirmationPolicy).toEqual(body.confirmationPolicy);
expect(data.requiresBookerEmailVerification).toEqual(body.requiresBookerEmailVerification);
expect(data.hideCalendarNotes).toEqual(body.hideCalendarNotes);
expect(data.hideCalendarEventDetails).toEqual(body.hideCalendarEventDetails);
expect(data.lockTimeZoneToggleOnBookingPage).toEqual(body.lockTimeZoneToggleOnBookingPage);
expect(data.color).toEqual(body.color);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type Input = Pick<
| "requiresConfirmationWillBlockSlot"
| "eventName"
| "useEventTypeDestinationCalendarEmail"
| "hideCalendarEventDetails"
>;

@Injectable()
Expand Down
7 changes: 7 additions & 0 deletions apps/api/v2/swagger/documentation.json
Original file line number Diff line number Diff line change
Expand Up @@ -7504,6 +7504,9 @@
"hideCalendarNotes": {
"type": "boolean"
},
"hideCalendarEventDetails": {
"type": "boolean"
},
"color": {
"$ref": "#/components/schemas/EventTypeColor_2024_06_14"
},
Expand Down Expand Up @@ -7565,6 +7568,7 @@
"confirmationPolicy",
"requiresBookerEmailVerification",
"hideCalendarNotes",
"hideCalendarEventDetails",
"color",
"seats",
"offsetStart",
Expand Down Expand Up @@ -9509,6 +9513,9 @@
"hideCalendarNotes": {
"type": "boolean"
},
"hideCalendarEventDetails": {
"type": "boolean"
},
"color": {
"$ref": "#/components/schemas/EventTypeColor_2024_06_14"
},
Expand Down
1 change: 1 addition & 0 deletions apps/web/playwright/webhook.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ test.describe("BOOKING_CREATED", async () => {
location: "[redacted/dynamic]",
destinationCalendar: null,
hideCalendarNotes: false,
hideCalendarEventDetails: false,
requiresConfirmation: "[redacted/dynamic]",
eventTypeId: "[redacted/dynamic]",
seatsShowAttendees: true,
Expand Down
2 changes: 2 additions & 0 deletions apps/web/public/static/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -2613,5 +2613,7 @@
"limit_team_booking_frequency_description": "Limit how many times members can be booked across all team event types (collective and round-robin)",
"booking_limits_updated_successfully": "Booking limits updated successfully",
"you_are_unauthorized_to_make_this_change_to_the_booking": "You are unauthorized to make this change to the booking",
"hide_calendar_event_details": "Hide calendar event details on shared calendars",
"description_hide_calendar_event_details": "When a calendar is shared, events are visible to readers but their details are hidden from those without write access.",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
3 changes: 3 additions & 0 deletions packages/app-store/googlecalendar/lib/CalendarService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,9 @@ export default class GoogleCalendarService implements Calendar {
: true,
iCalUID: formattedCalEvent.iCalUID,
};
if (calEventRaw.hideCalendarEventDetails) {
payload.visibility = "private";
}

if (formattedCalEvent.location) {
payload["location"] = getLocation(formattedCalEvent);
Expand Down
13 changes: 9 additions & 4 deletions packages/app-store/office365calendar/lib/CalendarService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Calendar as OfficeCalendar, User } from "@microsoft/microsoft-graph-types-beta";
import type { Calendar as OfficeCalendar, User, Event } from "@microsoft/microsoft-graph-types-beta";
import type { DefaultBodyType } from "msw";

import dayjs from "@calcom/dayjs";
Expand Down Expand Up @@ -266,7 +266,7 @@ export default class Office365CalendarService implements Calendar {
}

private translateEvent = (event: CalendarEvent) => {
return {
const office365Event: Event = {
subject: event.title,
body: {
contentType: "text",
Expand All @@ -280,6 +280,7 @@ export default class Office365CalendarService implements Calendar {
dateTime: dayjs(event.endTime).tz(event.organizer.timeZone).format("YYYY-MM-DDTHH:mm:ss"),
timeZone: event.organizer.timeZone,
},
hideAttendees: !event.seatsPerTimeSlot ? false : !event.seatsShowAttendees,
organizer: {
emailAddress: {
address: event.destinationCalendar
Expand All @@ -295,7 +296,7 @@ export default class Office365CalendarService implements Calendar {
address: attendee.email,
name: attendee.name,
},
type: "required",
type: "required" as const,
})),
...(event.team?.members
? event.team?.members
Expand All @@ -309,13 +310,17 @@ export default class Office365CalendarService implements Calendar {
address: destinationCalendar?.externalId ?? member.email,
name: member.name,
},
type: "required",
type: "required" as const,
};
})
: []),
],
location: event.location ? { displayName: getLocation(event) } : undefined,
};
if (event.hideCalendarEventDetails) {
office365Event.sensitivity = "private";
}
return office365Event;
};

private fetcher = async (endpoint: string, init?: RequestInit | undefined) => {
Expand Down
7 changes: 7 additions & 0 deletions packages/core/builders/CalendarEvent/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ export class CalendarEventBuilder implements ICalendarEventBuilder {
metadata: true,
destinationCalendar: true,
hideCalendarNotes: true,
hideCalendarEventDetails: true,
},
});
} catch (error) {
Expand Down Expand Up @@ -206,6 +207,12 @@ export class CalendarEventBuilder implements ICalendarEventBuilder {
this.calendarEvent.hideCalendarNotes = hideCalendarNotes;
}

public setHideCalendarEventDetails(
hideCalendarEventDetails: CalendarEventClass["hideCalendarEventDetails"]
) {
this.calendarEvent.hideCalendarEventDetails = hideCalendarEventDetails;
}

public setDescription(description: CalendarEventClass["description"]) {
this.calendarEvent.description = description;
}
Expand Down
1 change: 1 addition & 0 deletions packages/core/builders/CalendarEvent/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class CalendarEventClass implements CalendarEvent {
cancellationReason?: string | null;
rejectionReason?: string | null;
hideCalendarNotes?: boolean;
hideCalendarEventDetails?: boolean;
additionalNotes?: string | null | undefined;
recurrence?: string;
iCalUID?: string | null;
Expand Down
1 change: 1 addition & 0 deletions packages/emails/lib/generateIcsString.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ const generateIcsString = ({
location: location ?? undefined,
method: "REQUEST",
status,
...(event.hideCalendarEventDetails ? { classification: "PRIVATE" } : {}),
});
if (icsEvent.error) {
throw icsEvent.error;
Expand Down
1 change: 1 addition & 0 deletions packages/features/bookings/lib/handleNewBooking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,7 @@ async function handler(
conferenceCredentialId,
destinationCalendar,
hideCalendarNotes: eventType.hideCalendarNotes,
hideCalendarEventDetails: eventType.hideCalendarEventDetails,
requiresConfirmation: !isConfirmedByDefault,
eventTypeId: eventType.id,
// if seats are not enabled we should default true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const getEventTypesFromDB = async (eventTypeId: number) => {
metadata: true,
destinationCalendar: true,
hideCalendarNotes: true,
hideCalendarEventDetails: true,
seatsPerTimeSlot: true,
recurringEvent: true,
seatsShowAttendees: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
const seatsLocked = shouldLockDisableProps("seatsPerTimeSlotEnabled");
const requiresBookerEmailVerificationProps = shouldLockDisableProps("requiresBookerEmailVerification");
const hideCalendarNotesLocked = shouldLockDisableProps("hideCalendarNotes");
const hideCalendarEventDetailsLocked = shouldLockDisableProps("hideCalendarEventDetails");
const eventTypeColorLocked = shouldLockDisableProps("eventTypeColor");
const lockTimeZoneToggleOnBookingPageLocked = shouldLockDisableProps("lockTimeZoneToggleOnBookingPage");

Expand Down Expand Up @@ -323,6 +324,21 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
/>
)}
/>
<Controller
name="hideCalendarEventDetails"
render={({ field: { value, onChange } }) => (
<SettingsToggle
labelClassName="text-sm"
toggleSwitchAtTheEnd={true}
switchContainerClassName="border-subtle rounded-lg border py-6 px-4 sm:px-6"
title={t("hide_calendar_event_details")}
{...hideCalendarEventDetailsLocked}
description={t("description_hide_calendar_event_details")}
checked={value}
onCheckedChange={(e) => onChange(e)}
/>
)}
/>
<Controller
name="successRedirectUrl"
render={({ field: { value, onChange } }) => (
Expand Down
1 change: 1 addition & 0 deletions packages/lib/CalendarService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ export default abstract class BaseCalendarService implements Calendar {
* [UPDATE]: Since we're not using the PUBLISH method to publish the iCalendar event and creating the event directly on iCal,
* this shouldn't be an issue and we should be able to add attendees to the event right here.
*/
...(event.hideCalendarEventDetails ? { classification: "PRIVATE" } : {}),
});

if (error || !iCalString)
Expand Down
1 change: 1 addition & 0 deletions packages/lib/defaultEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ const commons = {
onlyShowFirstAvailableSlot: false,
id: 0,
hideCalendarNotes: false,
hideCalendarEventDetails: false,
recurringEvent: null,
destinationCalendar: null,
team: null,
Expand Down
1 change: 1 addition & 0 deletions packages/lib/server/eventTypeSelect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,5 @@ export const eventTypeSelect = Prisma.validator<Prisma.EventTypeSelect>()({
bookingLimits: true,
durationLimits: true,
eventTypeColor: true,
hideCalendarEventDetails: true,
});
1 change: 1 addition & 0 deletions packages/lib/server/repository/eventType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ export class EventTypeRepository {
requiresBookerEmailVerification: true,
recurringEvent: true,
hideCalendarNotes: true,
hideCalendarEventDetails: true,
disableGuests: true,
minimumBookingNotice: true,
beforeEventBuffer: true,
Expand Down
1 change: 1 addition & 0 deletions packages/lib/test/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export const buildEventType = (eventType?: Partial<EventType>): EventType => {
requiresConfirmationWillBlockSlot: false,
disableGuests: false,
hideCalendarNotes: false,
hideCalendarEventDetails: false,
minimumBookingNotice: 120,
beforeEventBuffer: 0,
afterEventBuffer: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export const useEventTypeForm = ({
endDate: periodDates.endDate,
},
hideCalendarNotes: eventType.hideCalendarNotes,
hideCalendarEventDetails: eventType.hideCalendarEventDetails,
offsetStart: eventType.offsetStart,
bookingFields: eventType.bookingFields,
periodType: eventType.periodType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,10 @@ export class CreateEventTypeInput_2024_06_14 {
@IsOptional()
@IsBoolean()
useDestinationCalendarEmail?: boolean;

@IsOptional()
@IsBoolean()
hideCalendarEventDetails?: boolean;
}

export enum HostPriority {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@ export class UpdateEventTypeInput_2024_06_14 {
@IsOptional()
@IsBoolean()
useDestinationCalendarEmail?: boolean;

@IsOptional()
@IsBoolean()
hideCalendarEventDetails?: boolean;
}

@ApiExtraModels(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,11 @@ class BaseEventTypeOutput_2024_06_14 {
@IsBoolean()
@DocsProperty()
useDestinationCalendarEmail?: boolean;

@IsOptional()
@IsBoolean()
@DocsProperty()
hideCalendarEventDetails?: boolean;
}

export class TeamEventTypeResponseHost extends TeamEventTypeHostInput {
Expand Down Expand Up @@ -385,4 +390,9 @@ export class TeamEventTypeOutput_2024_06_14 extends BaseEventTypeOutput_2024_06_
@IsEnum(SchedulingTypeEnum)
@DocsProperty({ enum: SchedulingTypeEnum })
schedulingType!: EventTypesOutputSchedulingType | null;

@IsOptional()
@IsBoolean()
@DocsProperty()
hideCalendarEventDetails?: boolean;
}
Loading

0 comments on commit ae7fae7

Please sign in to comment.