diff --git a/apps/web/components/booking/BookingListItem.tsx b/apps/web/components/booking/BookingListItem.tsx index 86933ef541..cfcc5d9abb 100644 --- a/apps/web/components/booking/BookingListItem.tsx +++ b/apps/web/components/booking/BookingListItem.tsx @@ -18,6 +18,7 @@ import getPaymentAppData from "@calcom/lib/getPaymentAppData"; import { useBookerUrl } from "@calcom/lib/hooks/useBookerUrl"; import { useCopy } from "@calcom/lib/hooks/useCopy"; import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { useGetTheme } from "@calcom/lib/hooks/useTheme"; import { getEveryFreqFor } from "@calcom/lib/recurringStrings"; import { BookingStatus, SchedulingType } from "@calcom/prisma/enums"; import { bookingMetadataSchema } from "@calcom/prisma/zod-utils"; @@ -100,6 +101,12 @@ function BookingListItem(booking: BookingItemProps) { const location = booking.location as ReturnType; const locationVideoCallUrl = bookingMetadataSchema.parse(booking?.metadata || {})?.videoCallUrl; + const { resolvedTheme, forcedTheme } = useGetTheme(); + const hasDarkTheme = !forcedTheme && resolvedTheme === "dark"; + const eventTypeColor = + booking.eventType.eventTypeColor && + booking.eventType.eventTypeColor[hasDarkTheme ? "darkEventTypeColor" : "lightEventTypeColor"]; + const locationToDisplay = getSuccessPageLocationMessage( locationVideoCallUrl ? locationVideoCallUrl : location, t, @@ -355,79 +362,82 @@ function BookingListItem(booking: BookingItemProps) { /> )} - - -
-
{startTime}
-
- {formatTime(booking.startTime, userTimeFormat, userTimeZone)} -{" "} - {formatTime(booking.endTime, userTimeFormat, userTimeZone)} - -
- {!isPending && ( -
- {(provider?.label || locationToDisplay?.startsWith("https://")) && - locationToDisplay.startsWith("http") && ( - e.stopPropagation()} - target="_blank" - title={locationToDisplay} - rel="noreferrer" - className="text-sm leading-6 text-blue-600 hover:underline dark:text-blue-400"> -
- {provider?.iconUrl && ( - {`${provider?.label} - )} - {provider?.label - ? t("join_event_location", { eventLocationType: provider?.label }) - : t("join_meeting")} -
-
- )} -
- )} - {isPending && ( - - {t("unconfirmed")} - - )} - {booking.eventType?.team && ( - - {booking.eventType.team.name} - - )} - {booking.paid && !booking.payment[0] ? ( - - {t("error_collecting_card")} - - ) : booking.paid ? ( - - {booking.payment[0].paymentOption === "HOLD" ? t("card_held") : t("paid")} - - ) : null} - {recurringDates !== undefined && ( -
- +
+ {eventTypeColor &&
} + +
+
{startTime}
+
+ {formatTime(booking.startTime, userTimeFormat, userTimeZone)} -{" "} + {formatTime(booking.endTime, userTimeFormat, userTimeZone)} +
- )} -
- + {!isPending && ( + + )} + {isPending && ( + + {t("unconfirmed")} + + )} + {booking.eventType?.team && ( + + {booking.eventType.team.name} + + )} + {booking.paid && !booking.payment[0] ? ( + + {t("error_collecting_card")} + + ) : booking.paid ? ( + + {booking.payment[0].paymentOption === "HOLD" ? t("card_held") : t("paid")} + + ) : null} + {recurringDates !== undefined && ( +
+ +
+ )} +
+ +
diff --git a/apps/web/components/eventtype/EventAdvancedTab.tsx b/apps/web/components/eventtype/EventAdvancedTab.tsx index 04c4095d03..d6eaa871b3 100644 --- a/apps/web/components/eventtype/EventAdvancedTab.tsx +++ b/apps/web/components/eventtype/EventAdvancedTab.tsx @@ -19,8 +19,10 @@ import type { EditableSchema } from "@calcom/features/form-builder/schema"; import { BookerLayoutSelector } from "@calcom/features/settings/BookerLayoutSelector"; import { classNames } from "@calcom/lib"; import cx from "@calcom/lib/classNames"; +import { DEFAULT_LIGHT_BRAND_COLOR, DEFAULT_DARK_BRAND_COLOR } from "@calcom/lib/constants"; import { APP_NAME, IS_VISUAL_REGRESSION_TESTING, WEBSITE_URL } from "@calcom/lib/constants"; import { generateHashedLink } from "@calcom/lib/generateHashedLink"; +import { checkWCAGContrastColor } from "@calcom/lib/getBrandColours"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import type { Prisma } from "@calcom/prisma/client"; import { SchedulingType } from "@calcom/prisma/enums"; @@ -38,6 +40,7 @@ import { TextField, Tooltip, showToast, + ColorPicker, } from "@calcom/ui"; import RequiresConfirmationController from "./RequiresConfirmationController"; @@ -51,6 +54,8 @@ export const EventAdvancedTab = ({ eventType, team }: Pick(); const { t } = useLocale(); const [showEventNameTip, setShowEventNameTip] = useState(false); + const [darkModeError, setDarkModeError] = useState(false); + const [lightModeError, setLightModeError] = useState(false); const [hashedLinkVisible, setHashedLinkVisible] = useState(!!formMethods.getValues("hashedLink")); const [redirectUrlVisible, setRedirectUrlVisible] = useState(!!formMethods.getValues("successRedirectUrl")); const [useEventTypeDestinationCalendarEmail, setUseEventTypeDestinationCalendarEmail] = useState( @@ -131,9 +136,20 @@ export const EventAdvancedTab = ({ eventType, team }: Pick setShowEventNameTip(false); + + const [isEventTypeColorChecked, setIsEventTypeColorChecked] = useState(!!eventType.eventTypeColor); + + const [eventTypeColorState, setEventTypeColorState] = useState( + eventType.eventTypeColor || { + lightEventTypeColor: DEFAULT_LIGHT_BRAND_COLOR, + darkEventTypeColor: DEFAULT_DARK_BRAND_COLOR, + } + ); + const displayDestinationCalendarSelector = !!connectedCalendarsQuery.data?.connectedCalendars.length && (!team || isChildrenManagedEventType); @@ -537,6 +553,82 @@ export const EventAdvancedTab = ({ eventType, team }: Pick )} /> + ( + { + const value = e ? eventTypeColorState : null; + formMethods.setValue("eventTypeColor", value, { + shouldDirty: true, + }); + setIsEventTypeColorChecked(e); + }} + childrenClassName="lg:ml-0"> +
+
+

{t("light_event_type_color")}

+ { + if (checkWCAGContrastColor("#ffffff", value)) { + const newVal = { + ...eventTypeColorState, + lightEventTypeColor: value, + }; + setLightModeError(false); + formMethods.setValue("eventTypeColor", newVal, { shouldDirty: true }); + setEventTypeColorState(newVal); + } else { + setLightModeError(true); + } + }} + /> + {lightModeError ? ( +
+ +
+ ) : null} +
+ +
+

{t("dark_event_type_color")}

+ { + if (checkWCAGContrastColor("#101010", value)) { + const newVal = { + ...eventTypeColorState, + darkEventTypeColor: value, + }; + setDarkModeError(false); + formMethods.setValue("eventTypeColor", newVal, { shouldDirty: true }); + setEventTypeColorState(newVal); + } else { + setDarkModeError(true); + } + }} + /> + {darkModeError ? ( +
+ +
+ ) : null} +
+
+
+ )} + /> {isRoundRobinEventType && ( { const { t } = useLocale(); + const { resolvedTheme, forcedTheme } = useGetTheme(); + const hasDarkTheme = !forcedTheme && resolvedTheme === "dark"; + const parsedeventTypeColor = parseEventTypeColor(type.eventTypeColor); + const eventTypeColor = + parsedeventTypeColor && parsedeventTypeColor[hasDarkTheme ? "darkEventTypeColor" : "lightEventTypeColor"]; const content = () => (
@@ -238,40 +245,46 @@ const Item = ({
); - return readOnly ? ( -
- {content()} - -
- ) : ( - -
- - {type.title} - - {group.profile.slug ? ( - - {`/${group.profile.slug}/${type.slug}`} - - ) : null} - {readOnly && ( - - {t("readonly")} - + return ( +
+ {eventTypeColor && ( +
+ )} +
+ {readOnly ? ( +
+ {content()} + +
+ ) : ( + +
+ + {type.title} + + {group.profile.slug ? ( + + {`/${group.profile.slug}/${type.slug}`} + + ) : null} + {readOnly && ( + + {t("readonly")} + + )} +
+ + )}
- - +
); }; @@ -470,7 +483,7 @@ export const EventTypeList = ({ return (
  • -
    +
    {!(firstItem && firstItem.id === type.id) && ( moveEventType(index, -1)} arrowDirection="up" /> )} diff --git a/apps/web/modules/event-types/views/event-types-single-view.tsx b/apps/web/modules/event-types/views/event-types-single-view.tsx index e53fd01e23..ee64f07cb6 100644 --- a/apps/web/modules/event-types/views/event-types-single-view.tsx +++ b/apps/web/modules/event-types/views/event-types-single-view.tsx @@ -282,6 +282,7 @@ const EventTypePage = (props: EventTypeSetupProps & { allActiveWorkflows?: Workf length: eventType.length, hidden: eventType.hidden, hashedLink: eventType.hashedLink?.link || undefined, + eventTypeColor: eventType.eventTypeColor || null, periodDates: { startDate: periodDates.startDate, endDate: periodDates.endDate, @@ -518,6 +519,8 @@ const EventTypePage = (props: EventTypeSetupProps & { allActiveWorkflows?: Workf const updatedFields: Partial = {}; Object.keys(dirtyFields).forEach((key) => { const typedKey = key as keyof typeof dirtyFields; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore updatedFields[typedKey] = undefined; const isDirty = isFieldDirty(typedKey); if (isDirty) { @@ -545,6 +548,7 @@ const EventTypePage = (props: EventTypeSetupProps & { allActiveWorkflows?: Workf onlyShowFirstAvailableSlot, durationLimits, recurringEvent, + eventTypeColor, locations, metadata, customInputs, @@ -615,6 +619,7 @@ const EventTypePage = (props: EventTypeSetupProps & { allActiveWorkflows?: Workf bookingLimits, onlyShowFirstAvailableSlot, durationLimits, + eventTypeColor, seatsPerTimeSlot, seatsShowAttendees, seatsShowAvailabilityCount, @@ -703,6 +708,7 @@ const EventTypePage = (props: EventTypeSetupProps & { allActiveWorkflows?: Workf onlyShowFirstAvailableSlot, durationLimits, recurringEvent, + eventTypeColor, locations, metadata, customInputs, @@ -765,6 +771,7 @@ const EventTypePage = (props: EventTypeSetupProps & { allActiveWorkflows?: Workf bookingLimits, onlyShowFirstAvailableSlot, durationLimits, + eventTypeColor, seatsPerTimeSlot, seatsShowAttendees, seatsShowAvailabilityCount, diff --git a/apps/web/pages/settings/my-account/appearance.tsx b/apps/web/pages/settings/my-account/appearance.tsx index a2f0bb36b9..9fe645435b 100644 --- a/apps/web/pages/settings/my-account/appearance.tsx +++ b/apps/web/pages/settings/my-account/appearance.tsx @@ -348,12 +348,11 @@ const AppearanceView = ({ defaultValue={DEFAULT_BRAND_COLOURS.light} resetDefaultValue={DEFAULT_LIGHT_BRAND_COLOR} onChange={(value) => { - try { - checkWCAGContrastColor("#ffffff", value); + if (checkWCAGContrastColor("#ffffff", value)) { setLightModeError(false); brandColorsFormMethods.setValue("brandColor", value, { shouldDirty: true }); - } catch (err) { - setLightModeError(false); + } else { + setLightModeError(true); } }} /> @@ -377,11 +376,10 @@ const AppearanceView = ({ defaultValue={DEFAULT_BRAND_COLOURS.dark} resetDefaultValue={DEFAULT_DARK_BRAND_COLOR} onChange={(value) => { - try { - checkWCAGContrastColor("#101010", value); + if (checkWCAGContrastColor("#101010", value)) { setDarkModeError(false); brandColorsFormMethods.setValue("darkBrandColor", value, { shouldDirty: true }); - } catch (err) { + } else { setDarkModeError(true); } }} diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index 6c3a579e35..1972e02d06 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -70,6 +70,8 @@ "meeting_awaiting_payment": "Your meeting is awaiting payment", "dark_theme_contrast_error": "Dark Theme color doesn't pass contrast check. We recommend you change this colour so your buttons will be more visible.", "light_theme_contrast_error": "Light Theme color doesn't pass contrast check. We recommend you change this colour so your buttons will be more visible.", + "event_type_color_light_theme_contrast_error": "Light Theme color doesn't pass contrast check. We recommend you change this color so your event types color will be more visible.", + "event_type_color_dark_theme_contrast_error": "Dark Theme color doesn't pass contrast check. We recommend you change this color so your event types color will be more visible.", "payment_not_created_error": "Payment could not be created", "couldnt_charge_card_error": "Could not charge card for Payment", "no_available_users_found_error": "No available users found. Could you try another time slot?", @@ -776,6 +778,8 @@ "brand_color": "Brand Color", "light_brand_color": "Brand Color (Light Theme)", "dark_brand_color": "Brand Color (Dark Theme)", + "light_event_type_color": "Event Type Color (Light Theme)", + "dark_event_type_color": "Event Type Color (Dark Theme)", "file_not_named": "File is not named [idOrSlug]/[user]", "create_team": "Create Team", "name": "Name", @@ -2496,6 +2500,8 @@ "unable_to_subscribe_to_the_platform": "An error occurred while trying to subscribe to the platform plan, please try again later", "updating_oauth_client_error": "An error occurred while updating the OAuth client, please try again later", "creating_oauth_client_error": "An error occurred while creating the OAuth client, please try again later", + "event_type_color": "Event type color", + "event_type_color_description": "This is only used for event type & booking differentiation within the app. It is not displayed to bookers.", "mark_as_no_show_title": "Mark as no show", "x_marked_as_no_show": "{{x}} marked as no-show", "x_unmarked_as_no_show": "{{x}} unmarked as no-show", diff --git a/apps/web/test/lib/handleChildrenEventTypes.test.ts b/apps/web/test/lib/handleChildrenEventTypes.test.ts index 6f84b0928a..afc0abe74f 100644 --- a/apps/web/test/lib/handleChildrenEventTypes.test.ts +++ b/apps/web/test/lib/handleChildrenEventTypes.test.ts @@ -146,6 +146,7 @@ describe("handleChildrenEventTypes", () => { bookingLimits: undefined, durationLimits: undefined, recurringEvent: undefined, + eventTypeColor: undefined, userId: 4, }, }); @@ -301,6 +302,7 @@ describe("handleChildrenEventTypes", () => { bookingLimits: undefined, durationLimits: undefined, recurringEvent: undefined, + eventTypeColor: undefined, hashedLink: undefined, lockTimeZoneToggleOnBookingPage: false, requiresBookerEmailVerification: false, @@ -417,6 +419,7 @@ describe("handleChildrenEventTypes", () => { bookingLimits: undefined, durationLimits: undefined, recurringEvent: undefined, + eventTypeColor: undefined, hashedLink: undefined, locations: [], lockTimeZoneToggleOnBookingPage: false, diff --git a/packages/features/ee/components/BrandColorsForm.tsx b/packages/features/ee/components/BrandColorsForm.tsx index df924b1920..4651e6faef 100644 --- a/packages/features/ee/components/BrandColorsForm.tsx +++ b/packages/features/ee/components/BrandColorsForm.tsx @@ -66,12 +66,11 @@ const BrandColorsForm = ({ defaultValue={brandColor || DEFAULT_LIGHT_BRAND_COLOR} resetDefaultValue={DEFAULT_LIGHT_BRAND_COLOR} onChange={(value) => { - try { - checkWCAGContrastColor("#ffffff", value); + if (checkWCAGContrastColor("#ffffff", value)) { setLightModeError(false); brandColorsFormMethods.setValue("brandColor", value, { shouldDirty: true }); - } catch (err) { - setLightModeError(false); + } else { + setLightModeError(true); } }} /> @@ -95,11 +94,10 @@ const BrandColorsForm = ({ defaultValue={darkBrandColor || DEFAULT_DARK_BRAND_COLOR} resetDefaultValue={DEFAULT_DARK_BRAND_COLOR} onChange={(value) => { - try { - checkWCAGContrastColor("#101010", value); + if (checkWCAGContrastColor("#101010", value)) { setDarkModeError(false); brandColorsFormMethods.setValue("darkBrandColor", value, { shouldDirty: true }); - } catch (err) { + } else { setDarkModeError(true); } }} diff --git a/packages/features/ee/managed-event-types/lib/handleChildrenEventTypes.ts b/packages/features/ee/managed-event-types/lib/handleChildrenEventTypes.ts index ed5d19b163..29ca66d784 100644 --- a/packages/features/ee/managed-event-types/lib/handleChildrenEventTypes.ts +++ b/packages/features/ee/managed-event-types/lib/handleChildrenEventTypes.ts @@ -185,6 +185,7 @@ export default async function handleChildrenEventTypes({ metadata: (managedEventTypeValues.metadata as Prisma.InputJsonValue) ?? undefined, bookingFields: (managedEventTypeValues.bookingFields as Prisma.InputJsonValue) ?? undefined, durationLimits: (managedEventTypeValues.durationLimits as Prisma.InputJsonValue) ?? undefined, + eventTypeColor: (managedEventTypeValues.eventTypeColor as Prisma.InputJsonValue) ?? undefined, onlyShowFirstAvailableSlot: managedEventTypeValues.onlyShowFirstAvailableSlot ?? false, userId, users: { diff --git a/packages/features/eventtypes/lib/types.ts b/packages/features/eventtypes/lib/types.ts index 6188b509c0..4aea36d1de 100644 --- a/packages/features/eventtypes/lib/types.ts +++ b/packages/features/eventtypes/lib/types.ts @@ -6,6 +6,7 @@ import type { PeriodType, SchedulingType } from "@calcom/prisma/enums"; import type { BookerLayoutSettings, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import type { customInputSchema } from "@calcom/prisma/zod-utils"; import type { eventTypeBookingFields } from "@calcom/prisma/zod-utils"; +import type { eventTypeColor } from "@calcom/prisma/zod-utils"; import type { RouterOutputs } from "@calcom/trpc/react"; import type { IntervalLimit, RecurringEvent } from "@calcom/types/Calendar"; @@ -47,6 +48,7 @@ export type FormValues = { hidden: boolean; hideCalendarNotes: boolean; hashedLink: string | undefined; + eventTypeColor: z.infer; locations: { type: EventLocationType["type"]; address?: string; diff --git a/packages/lib/event-types/getEventTypeById.ts b/packages/lib/event-types/getEventTypeById.ts index 67ff434a28..af60ac6eb1 100644 --- a/packages/lib/event-types/getEventTypeById.ts +++ b/packages/lib/event-types/getEventTypeById.ts @@ -4,7 +4,7 @@ import { getLocationGroupedOptions } from "@calcom/app-store/server"; import { getEventTypeAppData } from "@calcom/app-store/utils"; import type { LocationObject } from "@calcom/core/location"; import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields"; -import { parseBookingLimit, parseDurationLimit, parseRecurringEvent } from "@calcom/lib"; +import { parseBookingLimit, parseDurationLimit, parseRecurringEvent, parseEventTypeColor } from "@calcom/lib"; import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl"; import { getTranslation } from "@calcom/lib/server/i18n"; import { EventTypeRepository } from "@calcom/lib/server/repository/eventType"; @@ -108,6 +108,7 @@ export const getEventTypeById = async ({ recurringEvent: parseRecurringEvent(restEventType.recurringEvent), bookingLimits: parseBookingLimit(restEventType.bookingLimits), durationLimits: parseDurationLimit(restEventType.durationLimits), + eventTypeColor: parseEventTypeColor(restEventType.eventTypeColor), locations: locations as unknown as LocationObject[], metadata: parsedMetaData, customInputs: parsedCustomInputs, diff --git a/packages/lib/index.ts b/packages/lib/index.ts index 5d28b67b54..96f77226b6 100644 --- a/packages/lib/index.ts +++ b/packages/lib/index.ts @@ -3,6 +3,7 @@ export { default as isPrismaObj, isPrismaObjOrUndefined } from "./isPrismaObj"; export * from "./isRecurringEvent"; export * from "./isBookingLimits"; export * from "./isDurationLimits"; +export * from "./isEventTypeColor"; export * from "./validateIntervalLimitOrder"; export * from "./schedules"; export * from "./event-types"; diff --git a/packages/lib/isEventTypeColor.ts b/packages/lib/isEventTypeColor.ts new file mode 100644 index 0000000000..77b7e1cb85 --- /dev/null +++ b/packages/lib/isEventTypeColor.ts @@ -0,0 +1,15 @@ +import type { z } from "zod"; + +import { eventTypeColor as eventTypeColorSchema } from "@calcom/prisma/zod-utils"; + +type EventTypeColor = z.infer; +export function isEventTypeColor(obj: unknown): obj is EventTypeColor { + return eventTypeColorSchema.safeParse(obj).success; +} + +export function parseEventTypeColor(obj: unknown): EventTypeColor { + let eventTypeColor: EventTypeColor = null; + if (isEventTypeColor(obj)) eventTypeColor = obj; + + return eventTypeColor; +} diff --git a/packages/lib/server/eventTypeSelect.ts b/packages/lib/server/eventTypeSelect.ts index f2d539f0a8..29d68c42ab 100644 --- a/packages/lib/server/eventTypeSelect.ts +++ b/packages/lib/server/eventTypeSelect.ts @@ -51,4 +51,5 @@ export const eventTypeSelect = Prisma.validator()({ secondaryEmailId: true, bookingLimits: true, durationLimits: true, + eventTypeColor: true, }); diff --git a/packages/lib/server/queries/teams/index.ts b/packages/lib/server/queries/teams/index.ts index 1c1b1e3481..647c4fa230 100644 --- a/packages/lib/server/queries/teams/index.ts +++ b/packages/lib/server/queries/teams/index.ts @@ -346,6 +346,7 @@ export async function updateNewTeamMemberEventTypes(userId: number, teamId: numb metadata: (managedEventTypeValues.metadata as Prisma.InputJsonValue) ?? undefined, bookingFields: (managedEventTypeValues.bookingFields as Prisma.InputJsonValue) ?? undefined, durationLimits: (managedEventTypeValues.durationLimits as Prisma.InputJsonValue) ?? undefined, + eventTypeColor: (managedEventTypeValues.eventTypeColor as Prisma.InputJsonValue) ?? undefined, onlyShowFirstAvailableSlot: managedEventTypeValues.onlyShowFirstAvailableSlot ?? false, userId, users: { diff --git a/packages/lib/server/repository/eventType.ts b/packages/lib/server/repository/eventType.ts index f6c27e6f1e..e1a6110c6c 100644 --- a/packages/lib/server/repository/eventType.ts +++ b/packages/lib/server/repository/eventType.ts @@ -452,6 +452,7 @@ export class EventTypeRepository { afterEventBuffer: true, slotInterval: true, hashedLink: true, + eventTypeColor: true, bookingLimits: true, onlyShowFirstAvailableSlot: true, durationLimits: true, diff --git a/packages/lib/test/builder.ts b/packages/lib/test/builder.ts index d75f352429..7e33ea5061 100644 --- a/packages/lib/test/builder.ts +++ b/packages/lib/test/builder.ts @@ -126,6 +126,7 @@ export const buildEventType = (eventType?: Partial): EventType => { parentId: null, profileId: null, secondaryEmailId: null, + eventTypeColor: null, ...eventType, }; }; diff --git a/packages/prisma/migrations/20240810164200_event_type_color/migration.sql b/packages/prisma/migrations/20240810164200_event_type_color/migration.sql new file mode 100644 index 0000000000..caa0ac1e78 --- /dev/null +++ b/packages/prisma/migrations/20240810164200_event_type_color/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "EventType" ADD COLUMN "eventTypeColor" JSONB; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 85f420791c..cb005a2d39 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -137,6 +137,8 @@ model EventType { assignAllTeamMembers Boolean @default(false) useEventTypeDestinationCalendarEmail Boolean @default(false) aiPhoneCallConfig AIPhoneCallConfiguration? + /// @zod.custom(imports.eventTypeColor) + eventTypeColor Json? rescheduleWithSameRoundRobinHost Boolean @default(false) secondaryEmailId Int? diff --git a/packages/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts index 71c2d51c47..eca0100aab 100644 --- a/packages/prisma/zod-utils.ts +++ b/packages/prisma/zod-utils.ts @@ -187,6 +187,13 @@ export const iso8601 = z.string().transform((val, ctx) => { return d; }); +export const eventTypeColor = z + .object({ + lightEventTypeColor: z.string(), + darkEventTypeColor: z.string(), + }) + .nullable(); + export const intervalLimitsType = z .object({ PER_DAY: z.number().optional(), @@ -663,6 +670,7 @@ export const allManagedEventTypeProps: { [k in keyof Omit { recurringEvent, bookingLimits, durationLimits, + eventTypeColor, metadata, workflows, hashedLink, @@ -112,6 +113,7 @@ export const duplicateHandler = async ({ ctx, input }: DuplicateOptions) => { recurringEvent: recurringEvent || undefined, bookingLimits: bookingLimits ?? undefined, durationLimits: durationLimits ?? undefined, + eventTypeColor: eventTypeColor ?? undefined, metadata: metadata === null ? Prisma.DbNull : metadata, bookingFields: eventType.bookingFields === null ? Prisma.DbNull : eventType.bookingFields, }; diff --git a/packages/trpc/server/routers/viewer/eventTypes/update.handler.ts b/packages/trpc/server/routers/viewer/eventTypes/update.handler.ts index 7f8f8eee6b..d9ae73b3c5 100644 --- a/packages/trpc/server/routers/viewer/eventTypes/update.handler.ts +++ b/packages/trpc/server/routers/viewer/eventTypes/update.handler.ts @@ -53,6 +53,7 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => { destinationCalendar, customInputs, recurringEvent, + eventTypeColor, users, children, assignAllTeamMembers, @@ -137,6 +138,7 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => { ...rest, bookingFields, metadata: rest.metadata === null ? Prisma.DbNull : (rest.metadata as Prisma.InputJsonObject), + eventTypeColor: eventTypeColor === null ? Prisma.DbNull : (eventTypeColor as Prisma.InputJsonObject), }; data.locations = locations ?? undefined; if (periodType) {