diff --git a/apps/web/pages/new-booker/d/[link]/[slug].tsx b/apps/web/pages/new-booker/d/[link]/[slug].tsx index 187847c781..21178d8805 100644 --- a/apps/web/pages/new-booker/d/[link]/[slug].tsx +++ b/apps/web/pages/new-booker/d/[link]/[slug].tsx @@ -14,7 +14,7 @@ import PageWrapper from "@components/PageWrapper"; type PageProps = inferSSRProps; -export default function Type({ slug, user, booking, away, isBrandingHidden }: PageProps) { +export default function Type({ slug, user, booking, away, isBrandingHidden, isTeamEvent }: PageProps) { return (
); @@ -57,6 +58,11 @@ async function getUserPageProps(context: GetServerSidePropsContext) { username: true, }, }, + team: { + select: { + id: true, + }, + }, }, }, }, @@ -96,9 +102,11 @@ async function getUserPageProps(context: GetServerSidePropsContext) { booking = await getBookingByUidOrRescheduleUid(`${rescheduleUid}`); } + const isTeamEvent = !!hashedLink.eventType?.team?.id; + // We use this to both prefetch the query on the server, // as well as to check if the event exist, so we c an show a 404 otherwise. - const eventData = await ssr.viewer.public.event.fetch({ username, eventSlug: slug }); + const eventData = await ssr.viewer.public.event.fetch({ username, eventSlug: slug, isTeamEvent }); if (!eventData) { return { @@ -114,6 +122,9 @@ async function getUserPageProps(context: GetServerSidePropsContext) { slug, trpcState: ssr.dehydrate(), isBrandingHidden: user?.hideBranding, + // Sending the team event from the server, because this template file + // is reused for both team and user events. + isTeamEvent, }, }; } diff --git a/apps/web/pages/new-booker/team/[slug]/[type].tsx b/apps/web/pages/new-booker/team/[slug]/[type].tsx index 70234d7760..df934ec639 100644 --- a/apps/web/pages/new-booker/team/[slug]/[type].tsx +++ b/apps/web/pages/new-booker/team/[slug]/[type].tsx @@ -30,6 +30,7 @@ export default function Type({ slug, user, booking, away, isBrandingHidden }: Pa rescheduleBooking={booking} isAway={away} hideBranding={isBrandingHidden} + isTeamEvent /> ); @@ -71,7 +72,11 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => // We use this to both prefetch the query on the server, // as well as to check if the event exist, so we c an show a 404 otherwise. - const eventData = await ssr.viewer.public.event.fetch({ username: teamSlug, eventSlug: meetingSlug }); + const eventData = await ssr.viewer.public.event.fetch({ + username: teamSlug, + eventSlug: meetingSlug, + isTeamEvent: true, + }); if (!eventData) { return { diff --git a/packages/features/bookings/Booker/Booker.tsx b/packages/features/bookings/Booker/Booker.tsx index 92e76378d5..72ce20eebf 100644 --- a/packages/features/bookings/Booker/Booker.tsx +++ b/packages/features/bookings/Booker/Booker.tsx @@ -36,6 +36,7 @@ const BookerComponent = ({ month, rescheduleBooking, hideBranding = false, + isTeamEvent, }: BookerProps) => { const isMobile = useMediaQuery("(max-width: 768px)"); const isTablet = useMediaQuery("(max-width: 1024px)"); @@ -81,6 +82,7 @@ const BookerComponent = ({ rescheduleUid, rescheduleBooking, layout: defaultLayout, + isTeamEvent, }); useEffect(() => { diff --git a/packages/features/bookings/Booker/store.ts b/packages/features/bookings/Booker/store.ts index 1d83f475f6..894c3517f3 100644 --- a/packages/features/bookings/Booker/store.ts +++ b/packages/features/bookings/Booker/store.ts @@ -22,6 +22,7 @@ type StoreInitializeType = { rescheduleUid: string | null; rescheduleBooking: GetBookingType | null | undefined; layout: BookerLayout; + isTeamEvent?: boolean; }; export type BookerStore = { @@ -89,6 +90,12 @@ export type BookerStore = { */ formValues: Record; setFormValues: (values: Record) => void; + /** + * Force event being a team event, so we only query for team events instead + * of also include 'user' events and return the first event that matches with + * both the slug and the event slug. + */ + isTeamEvent: boolean; }; /** @@ -137,6 +144,7 @@ export const useBookerStore = create((set, get) => ({ updateQueryParam("month", month ?? ""); get().setSelectedDate(null); }, + isTeamEvent: false, initialize: ({ username, eventSlug, @@ -145,6 +153,7 @@ export const useBookerStore = create((set, get) => ({ rescheduleUid = null, rescheduleBooking = null, layout, + isTeamEvent, }: StoreInitializeType) => { const selectedDateInStore = get().selectedDate; @@ -165,6 +174,7 @@ export const useBookerStore = create((set, get) => ({ rescheduleUid, rescheduleBooking, layout: layout || BookerLayouts.MONTH_VIEW, + isTeamEvent: isTeamEvent || false, // Preselect today's date in week / column view, since they use this to show the week title. selectedDate: selectedDateInStore || @@ -207,9 +217,29 @@ export const useInitializeBookerStore = ({ rescheduleUid = null, rescheduleBooking = null, layout, + isTeamEvent, }: StoreInitializeType) => { const initializeStore = useBookerStore((state) => state.initialize); useEffect(() => { - initializeStore({ username, eventSlug, month, eventId, rescheduleUid, rescheduleBooking, layout }); - }, [initializeStore, username, eventSlug, month, eventId, rescheduleUid, rescheduleBooking, layout]); + initializeStore({ + username, + eventSlug, + month, + eventId, + rescheduleUid, + rescheduleBooking, + layout, + isTeamEvent, + }); + }, [ + initializeStore, + username, + eventSlug, + month, + eventId, + rescheduleUid, + rescheduleBooking, + layout, + isTeamEvent, + ]); }; diff --git a/packages/features/bookings/Booker/types.ts b/packages/features/bookings/Booker/types.ts index f02fbe84f2..dc932adfce 100644 --- a/packages/features/bookings/Booker/types.ts +++ b/packages/features/bookings/Booker/types.ts @@ -41,6 +41,13 @@ export interface BookerProps { * within the atom (i.e. without a server side component). */ rescheduleBooking?: GetBookingType; + /** + * If this boolean is passed, we will only check team events with this slug and event slug. + * If it's not passed, we will first query a generic user event, and only if that doesn't exist + * fetch the team event. In case there's both a team + user with the same slug AND same event slug, + * that will always result in the user event being returned. + */ + isTeamEvent?: boolean; } export type BookerState = "loading" | "selecting_date" | "selecting_time" | "booking"; diff --git a/packages/features/bookings/Booker/utils/event.ts b/packages/features/bookings/Booker/utils/event.ts index 6532968f9f..0cdb6f9b79 100644 --- a/packages/features/bookings/Booker/utils/event.ts +++ b/packages/features/bookings/Booker/utils/event.ts @@ -16,9 +16,10 @@ import { useBookerStore } from "../store"; */ export const useEvent = () => { const [username, eventSlug] = useBookerStore((state) => [state.username, state.eventSlug], shallow); + const isTeamEvent = useBookerStore((state) => state.isTeamEvent); return trpc.viewer.public.event.useQuery( - { username: username ?? "", eventSlug: eventSlug ?? "" }, + { username: username ?? "", eventSlug: eventSlug ?? "", isTeamEvent }, { refetchOnWindowFocus: false, enabled: Boolean(username) && Boolean(eventSlug) } ); }; diff --git a/packages/features/eventtypes/lib/getPublicEvent.ts b/packages/features/eventtypes/lib/getPublicEvent.ts index 3cf964b87e..a872c05808 100644 --- a/packages/features/eventtypes/lib/getPublicEvent.ts +++ b/packages/features/eventtypes/lib/getPublicEvent.ts @@ -76,7 +76,12 @@ const publicEventSelect = Prisma.validator()({ hidden: true, }); -export const getPublicEvent = async (username: string, eventSlug: string, prisma: PrismaClient) => { +export const getPublicEvent = async ( + username: string, + eventSlug: string, + isTeamEvent: boolean | undefined, + prisma: PrismaClient +) => { const usernameList = username.split("+"); // In case of dynamic group event, we fetch user's data and use the default event. @@ -141,24 +146,26 @@ export const getPublicEvent = async (username: string, eventSlug: string, prisma }; } + const usersOrTeamQuery = isTeamEvent + ? { + team: { + slug: username, + }, + } + : { + users: { + some: { + username, + }, + }, + team: null, + }; + // In case it's not a group event, it's either a single user or a team, and we query that data. const event = await prisma.eventType.findFirst({ where: { slug: eventSlug, - OR: [ - { - users: { - some: { - username, - }, - }, - }, - { - team: { - slug: username, - }, - }, - ], + ...usersOrTeamQuery, }, select: publicEventSelect, }); diff --git a/packages/trpc/server/routers/publicViewer/event.handler.ts b/packages/trpc/server/routers/publicViewer/event.handler.ts index 8933f38d68..6daee0a968 100644 --- a/packages/trpc/server/routers/publicViewer/event.handler.ts +++ b/packages/trpc/server/routers/publicViewer/event.handler.ts @@ -10,6 +10,6 @@ interface EventHandlerOptions { } export const eventHandler = async ({ ctx, input }: EventHandlerOptions) => { - const event = await getPublicEvent(input.username, input.eventSlug, ctx.prisma); + const event = await getPublicEvent(input.username, input.eventSlug, input.isTeamEvent, ctx.prisma); return event; }; diff --git a/packages/trpc/server/routers/publicViewer/event.schema.ts b/packages/trpc/server/routers/publicViewer/event.schema.ts index 74e67be447..d5c01b9248 100644 --- a/packages/trpc/server/routers/publicViewer/event.schema.ts +++ b/packages/trpc/server/routers/publicViewer/event.schema.ts @@ -3,6 +3,7 @@ import z from "zod"; export const ZEventInputSchema = z.object({ username: z.string(), eventSlug: z.string(), + isTeamEvent: z.boolean().optional(), }); export type TEventInputSchema = z.infer;