diff --git a/apps/web/app/(use-page-wrapper)/(main-nav)/availability/error.tsx b/apps/web/app/(use-page-wrapper)/(main-nav)/availability/error.tsx index a722a613a4..7fa72bb7b1 100644 --- a/apps/web/app/(use-page-wrapper)/(main-nav)/availability/error.tsx +++ b/apps/web/app/(use-page-wrapper)/(main-nav)/availability/error.tsx @@ -1,7 +1,9 @@ "use client"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Alert } from "@calcom/ui/components/alert"; export default function Error() { - return ; + const { t } = useLocale(); + return ; } diff --git a/apps/web/app/(use-page-wrapper)/(main-nav)/event-types/actions.ts b/apps/web/app/(use-page-wrapper)/(main-nav)/event-types/actions.ts new file mode 100644 index 0000000000..4f68f668fa --- /dev/null +++ b/apps/web/app/(use-page-wrapper)/(main-nav)/event-types/actions.ts @@ -0,0 +1,7 @@ +"use server"; + +import { revalidatePath } from "next/cache"; + +export async function revalidateEventTypesList() { + revalidatePath("/event-types"); +} diff --git a/apps/web/app/(use-page-wrapper)/(main-nav)/event-types/error.tsx b/apps/web/app/(use-page-wrapper)/(main-nav)/event-types/error.tsx new file mode 100644 index 0000000000..7fa72bb7b1 --- /dev/null +++ b/apps/web/app/(use-page-wrapper)/(main-nav)/event-types/error.tsx @@ -0,0 +1,9 @@ +"use client"; + +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { Alert } from "@calcom/ui/components/alert"; + +export default function Error() { + const { t } = useLocale(); + return ; +} diff --git a/apps/web/app/(use-page-wrapper)/(main-nav)/event-types/loading.tsx b/apps/web/app/(use-page-wrapper)/(main-nav)/event-types/loading.tsx new file mode 100644 index 0000000000..6e6622a8df --- /dev/null +++ b/apps/web/app/(use-page-wrapper)/(main-nav)/event-types/loading.tsx @@ -0,0 +1,5 @@ +import { EventTypesSkeleton } from "./skeleton"; + +export default function Loading() { + return ; +} diff --git a/apps/web/app/(use-page-wrapper)/(main-nav)/event-types/page.tsx b/apps/web/app/(use-page-wrapper)/(main-nav)/event-types/page.tsx index c4e938fc91..387e13e921 100644 --- a/apps/web/app/(use-page-wrapper)/(main-nav)/event-types/page.tsx +++ b/apps/web/app/(use-page-wrapper)/(main-nav)/event-types/page.tsx @@ -8,6 +8,7 @@ import { redirect } from "next/navigation"; import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; import { getTeamsFiltersFromQuery } from "@calcom/features/filters/lib/getTeamsFiltersFromQuery"; import { eventTypesRouter } from "@calcom/trpc/server/routers/viewer/eventTypes/_router"; +import { meRouter } from "@calcom/trpc/server/routers/viewer/me/_router"; import { buildLegacyCtx } from "@lib/buildLegacyCtx"; @@ -32,19 +33,27 @@ const Page = async ({ params, searchParams }: PageProps) => { } const t = await getTranslate(); - - const caller = await createRouterCaller(eventTypesRouter); const filters = getTeamsFiltersFromQuery(_searchParams); - const initialData = await caller.getUserEventGroups({ filters }); + + const [meCaller, eventTypesCaller] = await Promise.all([ + createRouterCaller(meRouter), + createRouterCaller(eventTypesRouter), + ]); + + const [me, userEventGroupsData] = await Promise.all([ + meCaller.get(), + eventTypesCaller.getUserEventGroups({ filters }), + ]); return ( }> - + CTA={}> + ); }; export default Page; +export const revalidate = 3600; // 1 hour in seconds diff --git a/apps/web/app/(use-page-wrapper)/(main-nav)/event-types/skeleton.tsx b/apps/web/app/(use-page-wrapper)/(main-nav)/event-types/skeleton.tsx new file mode 100644 index 0000000000..089ee88c5f --- /dev/null +++ b/apps/web/app/(use-page-wrapper)/(main-nav)/event-types/skeleton.tsx @@ -0,0 +1,15 @@ +"use client"; + +import { ShellMainAppDir } from "app/(use-page-wrapper)/(main-nav)/ShellMainAppDir"; + +import { EventTypesSkeletonLoader } from "@calcom/features/eventtypes/components/SkeletonLoader"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; + +export function EventTypesSkeleton() { + const { t } = useLocale(); + return ( + + + + ); +} diff --git a/apps/web/app/(use-page-wrapper)/event-types/[type]/actions.ts b/apps/web/app/(use-page-wrapper)/event-types/[type]/actions.ts new file mode 100644 index 0000000000..feff0576fb --- /dev/null +++ b/apps/web/app/(use-page-wrapper)/event-types/[type]/actions.ts @@ -0,0 +1,7 @@ +"use server"; + +import { revalidatePath } from "next/cache"; + +export async function revalidateEventTypeEditPage(id: number) { + revalidatePath(`/event-types/${id}`); +} diff --git a/apps/web/app/(use-page-wrapper)/event-types/[type]/error.tsx b/apps/web/app/(use-page-wrapper)/event-types/[type]/error.tsx new file mode 100644 index 0000000000..0951beed67 --- /dev/null +++ b/apps/web/app/(use-page-wrapper)/event-types/[type]/error.tsx @@ -0,0 +1,14 @@ +"use client"; + +import Shell from "@calcom/features/shell/Shell"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { Alert } from "@calcom/ui/components/alert"; + +export default function Error() { + const { t } = useLocale(); + return ( + + + + ); +} diff --git a/apps/web/app/(use-page-wrapper)/event-types/[type]/loading.tsx b/apps/web/app/(use-page-wrapper)/event-types/[type]/loading.tsx new file mode 100644 index 0000000000..676c81856a --- /dev/null +++ b/apps/web/app/(use-page-wrapper)/event-types/[type]/loading.tsx @@ -0,0 +1,5 @@ +import { EventTypeEditPageSkeleton } from "./skeleton"; + +export default function Loading() { + return ; +} diff --git a/apps/web/app/(use-page-wrapper)/event-types/[type]/page.tsx b/apps/web/app/(use-page-wrapper)/event-types/[type]/page.tsx index 06f47635e9..ebab01769b 100644 --- a/apps/web/app/(use-page-wrapper)/event-types/[type]/page.tsx +++ b/apps/web/app/(use-page-wrapper)/event-types/[type]/page.tsx @@ -1,14 +1,15 @@ -import type { PageProps as _PageProps } from "app/_types"; +import { createRouterCaller } from "app/_trpc/context"; +import type { PageProps } from "app/_types"; import { _generateMetadata } from "app/_utils"; import { cookies, headers } from "next/headers"; +import { redirect } from "next/navigation"; import { z } from "zod"; -import { EventTypeRepository } from "@calcom/lib/server/repository/eventType"; +import { EventTypeWebWrapper } from "@calcom/atoms/event-types/wrappers/EventTypeWebWrapper"; +import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; +import { eventTypesRouter } from "@calcom/trpc/server/routers/viewer/eventTypes/_router"; -import { buildLegacyCtx } from "@lib/buildLegacyCtx"; -import { getServerSideProps } from "@lib/event-types/[type]/getServerSideProps"; - -import EventTypePageWrapper from "~/event-types/views/event-types-single-view"; +import { buildLegacyRequest } from "@lib/buildLegacyCtx"; const querySchema = z.object({ type: z @@ -19,36 +20,34 @@ const querySchema = z.object({ .transform((val) => Number(val)), }); -export const generateMetadata = async ({ params }: _PageProps) => { - const parsed = querySchema.safeParse(await params); - if (!parsed.success) { - return await _generateMetadata( - (t) => `${t("event_type")}`, - () => "", - undefined, - undefined, - "/event-types" - ); - } - - const data = await EventTypeRepository.findTitleById({ - id: parsed.data.type, - }); - +export const generateMetadata = async () => { return await _generateMetadata( - (t) => (data?.title ? `${data.title} | ${t("event_type")}` : `${t("event_type")}`), + (t) => `${t("event_type")}`, () => "", undefined, undefined, - `/event-types/${parsed.data.type}` + "/event-types" ); }; -const ServerPage = async ({ params, searchParams }: _PageProps) => { - const legacyCtx = buildLegacyCtx(await headers(), await cookies(), await params, await searchParams); - const props = await getServerSideProps(legacyCtx); +const ServerPage = async ({ params }: PageProps) => { + const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) }); + if (!session?.user?.id) { + return redirect("/auth/login"); + } - return ; + const parsed = querySchema.safeParse(await params); + if (!parsed.success) { + throw new Error("Invalid Event Type id"); + } + const eventTypeId = parsed.data.type; + const caller = await createRouterCaller(eventTypesRouter); + const data = await caller.get({ id: eventTypeId }); + if (!data?.eventType) { + throw new Error("This event type does not exist"); + } + + return ; }; export default ServerPage; diff --git a/apps/web/app/(use-page-wrapper)/event-types/[type]/skeleton.tsx b/apps/web/app/(use-page-wrapper)/event-types/[type]/skeleton.tsx new file mode 100644 index 0000000000..2f95b6ac84 --- /dev/null +++ b/apps/web/app/(use-page-wrapper)/event-types/[type]/skeleton.tsx @@ -0,0 +1,105 @@ +"use client"; + +import Shell from "@calcom/features/shell/Shell"; +import { + SkeletonButton, + SkeletonContainer, + SkeletonText, + SkeletonAvatar, +} from "@calcom/ui/components/skeleton"; + +export const EventTypeEditPageSkeleton = () => { + const backButtonSkeleton = ( +
+
+ +
+
+ +
+
+ + +
+ + +
+
+
+ ); + + return ( + } afterHeading={backButtonSkeleton}> +
+
+
+
+ {/* Tab navigation items */} + {Array.from({ length: 8 }).map((_, index) => ( +
+
+ +
+
+ + +
+
+ ))} +
+
+
+
+
+ +
+ +
+ +
+ {/* Form fields */} +
+ + +
+ +
+ +
+ + +
+
+ +
+ + +
+ +
+ +
+ + +
+
+ +
+ + +
+
+ +
+ + +
+
+
+
+
+
+ ); +}; diff --git a/apps/web/app/(use-page-wrapper)/settings/(admin-layout)/admin/organizations/[id]/edit/error.tsx b/apps/web/app/(use-page-wrapper)/settings/(admin-layout)/admin/organizations/[id]/edit/error.tsx index a722a613a4..7fa72bb7b1 100644 --- a/apps/web/app/(use-page-wrapper)/settings/(admin-layout)/admin/organizations/[id]/edit/error.tsx +++ b/apps/web/app/(use-page-wrapper)/settings/(admin-layout)/admin/organizations/[id]/edit/error.tsx @@ -1,7 +1,9 @@ "use client"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Alert } from "@calcom/ui/components/alert"; export default function Error() { - return ; + const { t } = useLocale(); + return ; } diff --git a/apps/web/app/(use-page-wrapper)/settings/(admin-layout)/admin/users/[id]/edit/error.tsx b/apps/web/app/(use-page-wrapper)/settings/(admin-layout)/admin/users/[id]/edit/error.tsx index a722a613a4..7fa72bb7b1 100644 --- a/apps/web/app/(use-page-wrapper)/settings/(admin-layout)/admin/users/[id]/edit/error.tsx +++ b/apps/web/app/(use-page-wrapper)/settings/(admin-layout)/admin/users/[id]/edit/error.tsx @@ -1,7 +1,9 @@ "use client"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Alert } from "@calcom/ui/components/alert"; export default function Error() { - return ; + const { t } = useLocale(); + return ; } diff --git a/apps/web/app/(use-page-wrapper)/workflows/[workflow]/error.tsx b/apps/web/app/(use-page-wrapper)/workflows/[workflow]/error.tsx index a722a613a4..7fa72bb7b1 100644 --- a/apps/web/app/(use-page-wrapper)/workflows/[workflow]/error.tsx +++ b/apps/web/app/(use-page-wrapper)/workflows/[workflow]/error.tsx @@ -1,7 +1,9 @@ "use client"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Alert } from "@calcom/ui/components/alert"; export default function Error() { - return ; + const { t } = useLocale(); + return ; } diff --git a/apps/web/lib/event-types/[type]/getServerSideProps.ts b/apps/web/lib/event-types/[type]/getServerSideProps.ts deleted file mode 100644 index 393adc5b0e..0000000000 --- a/apps/web/lib/event-types/[type]/getServerSideProps.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { createRouterCaller } from "app/_trpc/context"; -import type { GetServerSidePropsContext } from "next"; -import { redirect, notFound } from "next/navigation"; - -import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; -import logger from "@calcom/lib/logger"; -import { safeStringify } from "@calcom/lib/safeStringify"; -import type { RouterOutputs } from "@calcom/trpc"; -import { eventTypesRouter } from "@calcom/trpc/server/routers/viewer/eventTypes/_router"; - -import { asStringOrThrow } from "@lib/asStringOrNull"; - -export type PageProps = { - type: number; - data: RouterOutputs["viewer"]["eventTypes"]["get"]; -}; - -export const getServerSideProps = async (context: GetServerSidePropsContext) => { - const { req, query } = context; - - const session = await getServerSession({ req }); - - const typeParam = parseInt(asStringOrThrow(query.type)); - - if (Number.isNaN(typeParam)) { - return notFound(); - } - - if (!session?.user?.id) { - return redirect("/auth/login"); - } - const getEventTypeById = async (eventTypeId: number) => { - const caller = await createRouterCaller(eventTypesRouter); - - try { - return await caller.get({ id: eventTypeId }); - } catch (e: unknown) { - logger.error(safeStringify(e)); - // reject, user has no access to this event type. - return null; - } - }; - const data = await getEventTypeById(typeParam); - if (!data?.eventType) { - return redirect("/event-types"); - } - return { - data, - type: typeParam, - }; -}; diff --git a/apps/web/modules/event-types/views/event-types-listing-view.tsx b/apps/web/modules/event-types/views/event-types-listing-view.tsx index 678c4eb423..861e584c58 100644 --- a/apps/web/modules/event-types/views/event-types-listing-view.tsx +++ b/apps/web/modules/event-types/views/event-types-listing-view.tsx @@ -14,10 +14,7 @@ import { EventTypeEmbedButton, EventTypeEmbedDialog } from "@calcom/features/emb import { EventTypeDescription } from "@calcom/features/eventtypes/components"; import CreateEventTypeDialog from "@calcom/features/eventtypes/components/CreateEventTypeDialog"; import { DuplicateDialog } from "@calcom/features/eventtypes/components/DuplicateDialog"; -import { - InfiniteSkeletonLoader, - EventTypesSkeletonLoader, -} from "@calcom/features/eventtypes/components/SkeletonLoader"; +import { InfiniteSkeletonLoader } from "@calcom/features/eventtypes/components/SkeletonLoader"; import { parseEventTypeColor } from "@calcom/lib"; import { APP_NAME, WEBSITE_URL } from "@calcom/lib/constants"; import { useCopy } from "@calcom/lib/hooks/useCopy"; @@ -32,7 +29,6 @@ import { SchedulingType } from "@calcom/prisma/enums"; import type { RouterOutputs } from "@calcom/trpc/react"; import { trpc } from "@calcom/trpc/react"; import classNames from "@calcom/ui/classNames"; -import { Alert } from "@calcom/ui/components/alert"; import { ArrowButton } from "@calcom/ui/components/arrow-button"; import { UserAvatarGroup } from "@calcom/ui/components/avatar"; import { Badge } from "@calcom/ui/components/badge"; @@ -58,8 +54,6 @@ import { Skeleton } from "@calcom/ui/components/skeleton"; import { showToast } from "@calcom/ui/components/toast"; import { Tooltip } from "@calcom/ui/components/tooltip"; -import useMeQuery from "@lib/hooks/useMeQuery"; - import { TRPCClientError } from "@trpc/client"; type GetUserEventGroupsResponse = RouterOutputs["viewer"]["eventTypes"]["getUserEventGroups"]; @@ -839,7 +833,6 @@ const CreateFirstEventTypeView = ({ slug, searchTerm }: { slug: string; searchTe const CTA = ({ profileOptions, - isOrganization, }: { profileOptions: { teamId: number | null | undefined; @@ -848,7 +841,6 @@ const CTA = ({ membershipRole: MembershipRole | null | undefined; slug: string | null; }[]; - isOrganization: boolean; }) => { const { t } = useLocale(); @@ -890,28 +882,16 @@ const EmptyEventTypeList = ({ }; const InfiniteScrollMain = ({ - status, - errorMessage, eventTypeGroups, profiles, }: { - status: string; - errorMessage?: string; - eventTypeGroups: GetUserEventGroupsResponse["eventTypeGroups"] | undefined; - profiles: GetUserEventGroupsResponse["profiles"] | undefined; + eventTypeGroups: GetUserEventGroupsResponse["eventTypeGroups"]; + profiles: GetUserEventGroupsResponse["profiles"]; }) => { const searchParams = useSearchParams(); const { data } = useTypedQuery(querySchema); const orgBranding = useOrgBranding(); - if (status === "error") { - return ; - } - - if (!eventTypeGroups || !profiles || status === "pending") { - return ; - } - const tabs = eventTypeGroups.map((item) => ({ name: item.profile.name ?? "", href: item.teamId ? `/event-types?teamId=${item.teamId}` : "/event-types", @@ -947,25 +927,13 @@ const InfiniteScrollMain = ({ }; type Props = { - initialData: RouterOutputs["viewer"]["eventTypes"]["getUserEventGroups"]; - filters?: { teamIds?: number[] | undefined; userIds?: number[] | undefined; upIds?: string[] | undefined }; + userEventGroupsData: GetUserEventGroupsResponse; + user: RouterOutputs["viewer"]["me"]["get"]; }; -export const EventTypesCTA = ({ initialData, filters }: Props) => { - const { data: user } = useMeQuery(); - const { data: userEventGroupsQuery } = trpc.viewer.eventTypes.getUserEventGroups.useQuery( - filters && { filters }, - { - refetchOnWindowFocus: false, - gcTime: 1 * 60 * 60 * 1000, - staleTime: 1 * 60 * 60 * 1000, - } - ); - - const getUserEventGroupsData = userEventGroupsQuery ?? initialData; - +export const EventTypesCTA = ({ userEventGroupsData }: Omit) => { const profileOptions = - getUserEventGroupsData?.profiles + userEventGroupsData?.profiles ?.filter((profile) => !profile.readOnly) ?.filter((profile) => !profile.eventTypesLockedByOrg) ?.map((profile) => { @@ -978,28 +946,14 @@ export const EventTypesCTA = ({ initialData, filters }: Props) => { }; }) ?? []; - return ; + return ; }; -const EventTypesPage = ({ initialData, filters }: Props) => { - const { data: user } = useMeQuery(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars +const EventTypesPage = ({ userEventGroupsData, user }: Props) => { const [_showProfileBanner, setShowProfileBanner] = useState(false); const orgBranding = useOrgBranding(); const router = useRouter(); - // TODO: Maybe useSuspenseQuery to focus on success case only? Remember that it would crash the page when there is an error in query. Also, it won't support skeleton - const { - data: getUserEventGroupsData, - status: getUserEventGroupsStatus, - error: getUserEventGroupsStatusError, - } = trpc.viewer.eventTypes.getUserEventGroups.useQuery(filters && { filters }, { - refetchOnWindowFocus: false, - gcTime: 1 * 60 * 60 * 1000, - staleTime: 1 * 60 * 60 * 1000, - initialData, - }); - useEffect(() => { /** * During signup, if the account already exists, we redirect the user to /event-types instead of onboarding. @@ -1019,10 +973,8 @@ const EventTypesPage = ({ initialData, filters }: Props) => { return ( ); }; 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 deleted file mode 100644 index 3351ec7680..0000000000 --- a/apps/web/modules/event-types/views/event-types-single-view.tsx +++ /dev/null @@ -1,11 +0,0 @@ -"use client"; - -import { EventTypeWebWrapper as EventType } from "@calcom/atoms/event-types/wrappers/EventTypeWebWrapper"; - -import type { PageProps } from "@lib/event-types/[type]/getServerSideProps"; - -const EventTypePageWrapper = ({ type, data }: PageProps) => { - return ; -}; - -export default EventTypePageWrapper; diff --git a/packages/features/ee/teams/pages/team-profile-view.tsx b/packages/features/ee/teams/pages/team-profile-view.tsx index e55d802d17..6e253099e0 100644 --- a/packages/features/ee/teams/pages/team-profile-view.tsx +++ b/packages/features/ee/teams/pages/team-profile-view.tsx @@ -123,8 +123,8 @@ const ProfileView = () => { const deleteTeamMutation = trpc.viewer.teams.delete.useMutation({ async onSuccess() { - await utils.viewer.teams.list.invalidate(); revalidateTeamsList(); + await utils.viewer.teams.list.invalidate(); await utils.viewer.eventTypes.getUserEventGroups.invalidate(); await utils.viewer.eventTypes.getByViewer.invalidate(); showToast(t("your_team_disbanded_successfully"), "success"); diff --git a/packages/features/eventtypes/components/SkeletonLoader.tsx b/packages/features/eventtypes/components/SkeletonLoader.tsx index 0913a9dc54..2a0643b979 100644 --- a/packages/features/eventtypes/components/SkeletonLoader.tsx +++ b/packages/features/eventtypes/components/SkeletonLoader.tsx @@ -34,11 +34,11 @@ export function InfiniteSkeletonLoader() { ); } -export function TabsSkeletonLoader() { +function TabsSkeletonLoader() { return ( -
+