From d751cca0f46d6615f0c37634516543d839a978b6 Mon Sep 17 00:00:00 2001 From: Hariom Balhara Date: Thu, 3 Nov 2022 19:54:07 +0530 Subject: [PATCH] Add "Choose common schedule toggle" to team events (#5343) * Add Choose common schedule togglee * Fix types * Add translations * Improve variable name * Move setting to config so that all such lightweight boolean settings can exist here * Update apps/web/public/static/locales/en/common.json Co-authored-by: Alex van Andel Co-authored-by: Alex van Andel --- .../v2/eventtype/AvailabilityTab.tsx | 179 ++++++++++-------- apps/web/pages/event-types/[type]/index.tsx | 2 +- apps/web/public/static/locales/en/common.json | 4 +- packages/core/getUserAvailability.ts | 31 ++- packages/prisma/zod-utils.ts | 5 + packages/trpc/server/routers/viewer/slots.tsx | 12 +- packages/ui/v2/core/SettingsToggle.tsx | 4 +- 7 files changed, 144 insertions(+), 93 deletions(-) diff --git a/apps/web/components/v2/eventtype/AvailabilityTab.tsx b/apps/web/components/v2/eventtype/AvailabilityTab.tsx index 797e4ccec8..c5d7b8131f 100644 --- a/apps/web/components/v2/eventtype/AvailabilityTab.tsx +++ b/apps/web/components/v2/eventtype/AvailabilityTab.tsx @@ -9,7 +9,7 @@ import { weekdayNames } from "@calcom/lib/weekday"; import { trpc } from "@calcom/trpc/react"; import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery"; import { Icon } from "@calcom/ui"; -import { Badge } from "@calcom/ui/v2"; +import { Badge, SettingsToggle } from "@calcom/ui/v2"; import Button from "@calcom/ui/v2/core/Button"; import Select from "@calcom/ui/v2/core/form/select"; import { SkeletonText } from "@calcom/ui/v2/core/skeleton"; @@ -97,91 +97,114 @@ const format = (date: Date, hour12: boolean) => new Date(dayjs.utc(date).format("YYYY-MM-DDTHH:mm:ss")) ); -export const AvailabilityTab = () => { +export const AvailabilityTab = ({ isTeamEvent }: { isTeamEvent: boolean }) => { const { t, i18n } = useLocale(); const { watch } = useFormContext(); - const me = useMeQuery(); - const timeFormat = me?.data?.timeFormat; + + const EventTypeSchedule = () => { + const me = useMeQuery(); + const timeFormat = me?.data?.timeFormat; + return ( +
+
+
+ +
+ ( + { + field.onChange(selected?.value || null); + }} + /> + )} + /> +
+
+
    + {weekdayNames(i18n.language, 1, "long").map((day, index) => { + const isAvailable = !!filterDays(index).length; + return ( +
  1. + + {day} + + {isLoading ? ( + + ) : isAvailable ? ( +
    + {filterDays(index).map((dayRange, i) => ( +
    + + {format(dayRange.startTime, timeFormat === 12)} + + - +
    {format(dayRange.endTime, timeFormat === 12)}
    +
    + ))} +
    + ) : ( + {t("unavailable")} + )} +
  2. + ); + })} +
+
+
+ + + {schedule?.timeZone || } + + +
+
+
+ ); + }; + const scheduleId = watch("schedule"); const { isLoading, data: schedule } = trpc.useQuery(["viewer.availability.schedule", { scheduleId }]); const filterDays = (dayNum: number) => schedule?.schedule.availability.filter((item) => item.days.includes((dayNum + 1) % 7)) || []; - return ( - <> -
-
- -
- ( - { - field.onChange(selected?.value || null); - }} - /> - )} - /> -
+ if (!isTeamEvent) { + return ; + } -
-
    - {weekdayNames(i18n.language, 1, "long").map((day, index) => { - const isAvailable = !!filterDays(index).length; - return ( -
  1. - - {day} - - {isLoading ? ( - - ) : isAvailable ? ( -
    - {filterDays(index).map((dayRange, i) => ( -
    - - {format(dayRange.startTime, timeFormat === 12)} - - - -
    {format(dayRange.endTime, timeFormat === 12)}
    -
    - ))} -
    - ) : ( - {t("unavailable")} - )} -
  2. - ); - })} -
-
-
- - - {schedule?.timeZone || } - - -
-
- + return ( + ( + { + onChange(!checked); + }} + title={t("choose_common_schedule_team_event")} + description={t("choose_common_schedule_team_event_description")}> + + + )} + /> ); }; diff --git a/apps/web/pages/event-types/[type]/index.tsx b/apps/web/pages/event-types/[type]/index.tsx index 3c8c43a1a0..4b1fa3050b 100644 --- a/apps/web/pages/event-types/[type]/index.tsx +++ b/apps/web/pages/event-types/[type]/index.tsx @@ -192,7 +192,7 @@ const EventTypePage = (props: inferSSRProps) => { teamMembers={teamMembers} /> ), - availability: , + availability: , team: ( !!data.username || !!data.userId, "Either username or userId should be filled in."); -const getEventType = (id: number) => - prisma.eventType.findUnique({ +const getEventType = async (id: number) => { + const eventType = await prisma.eventType.findUnique({ where: { id }, select: { id: true, seatsPerTimeSlot: true, bookingLimits: true, timeZone: true, + metadata: true, schedule: { select: { availability: true, @@ -50,6 +51,14 @@ const getEventType = (id: number) => }, }, }); + if (!eventType) { + return eventType; + } + return { + ...eventType, + metadata: EventTypeMetaDataSchema.parse(eventType.metadata), + }; +}; type EventType = Awaited>; @@ -200,13 +209,15 @@ export async function getUserAvailability( }); } } - const schedule = eventType?.schedule - ? { ...eventType?.schedule } - : { - ...currentUser.schedules.filter( - (schedule) => !currentUser.defaultScheduleId || schedule.id === currentUser.defaultScheduleId - )[0], - }; + + const schedule = + !eventType?.metadata?.config?.useHostSchedulesForTeamEvent && eventType?.schedule + ? { ...eventType?.schedule } + : { + ...currentUser.schedules.filter( + (schedule) => !currentUser.defaultScheduleId || schedule.id === currentUser.defaultScheduleId + )[0], + }; const startGetWorkingHours = performance.now(); diff --git a/packages/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts index 3444ce490b..9e17729bbb 100644 --- a/packages/prisma/zod-utils.ts +++ b/packages/prisma/zod-utils.ts @@ -32,6 +32,11 @@ export const EventTypeMetaDataSchema = z giphyThankYouPage: z.string().optional(), apps: z.object(appDataSchemas).partial().optional(), additionalNotesRequired: z.boolean().optional(), + config: z + .object({ + useHostSchedulesForTeamEvent: z.boolean().optional(), + }) + .optional(), }) .nullable(); diff --git a/packages/trpc/server/routers/viewer/slots.tsx b/packages/trpc/server/routers/viewer/slots.tsx index 72939da7f0..9723ee8fa5 100644 --- a/packages/trpc/server/routers/viewer/slots.tsx +++ b/packages/trpc/server/routers/viewer/slots.tsx @@ -11,6 +11,7 @@ import logger from "@calcom/lib/logger"; import { performance } from "@calcom/lib/server/perfObserver"; import getTimeSlots from "@calcom/lib/slots"; import prisma, { availabilityUserSelect } from "@calcom/prisma"; +import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import { EventBusyDate } from "@calcom/types/Calendar"; import { TimeRange } from "@calcom/types/schedule"; @@ -104,7 +105,7 @@ export const slotsRouter = createRouter().query("getSchedule", { }); async function getEventType(ctx: { prisma: typeof prisma }, input: z.infer) { - return ctx.prisma.eventType.findUnique({ + const eventType = await ctx.prisma.eventType.findUnique({ where: { id: input.eventTypeId, }, @@ -124,6 +125,7 @@ async function getEventType(ctx: { prisma: typeof prisma }, input: z.infer) { diff --git a/packages/ui/v2/core/SettingsToggle.tsx b/packages/ui/v2/core/SettingsToggle.tsx index a81d873bc5..0778e5c475 100644 --- a/packages/ui/v2/core/SettingsToggle.tsx +++ b/packages/ui/v2/core/SettingsToggle.tsx @@ -44,8 +44,8 @@ function SettingsToggle({ {children && ( -
- {checked && children} +
+ {checked &&
{children}
}
)}