diff --git a/apps/web/components/getting-started/steps-views/SetupAvailability.tsx b/apps/web/components/getting-started/steps-views/SetupAvailability.tsx deleted file mode 100644 index 390bb55cba..0000000000 --- a/apps/web/components/getting-started/steps-views/SetupAvailability.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { useForm } from "react-hook-form"; - -import { Schedule } from "@calcom/features/schedules"; -import { DEFAULT_SCHEDULE } from "@calcom/lib/availability"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import type { TRPCClientErrorLike } from "@calcom/trpc/react"; -import { trpc } from "@calcom/trpc/react"; -import type { AppRouter } from "@calcom/trpc/server/routers/_app"; -import { Button, Form, Icon } from "@calcom/ui"; - -interface ISetupAvailabilityProps { - nextStep: () => void; - defaultScheduleId?: number | null; -} - -const SetupAvailability = (props: ISetupAvailabilityProps) => { - const { defaultScheduleId } = props; - - const { t } = useLocale(); - const { nextStep } = props; - - const scheduleId = defaultScheduleId === null ? undefined : defaultScheduleId; - const queryAvailability = trpc.viewer.availability.schedule.get.useQuery( - { scheduleId: defaultScheduleId ?? undefined }, - { - enabled: !!scheduleId, - } - ); - - const availabilityForm = useForm({ - defaultValues: { - schedule: queryAvailability?.data?.availability || DEFAULT_SCHEDULE, - }, - }); - - const mutationOptions = { - onError: (error: TRPCClientErrorLike) => { - throw new Error(error.message); - }, - onSuccess: () => { - nextStep(); - }, - }; - const createSchedule = trpc.viewer.availability.schedule.create.useMutation(mutationOptions); - const updateSchedule = trpc.viewer.availability.schedule.update.useMutation(mutationOptions); - return ( -
{ - try { - if (defaultScheduleId) { - await updateSchedule.mutate({ - scheduleId: defaultScheduleId, - name: t("default_schedule_name"), - ...values, - }); - } else { - await createSchedule.mutate({ - name: t("default_schedule_name"), - ...values, - }); - } - } catch (error) { - if (error instanceof Error) { - // setError(error); - // @TODO: log error - } - } - }}> -
- -
- -
- -
-
- ); -}; - -export { SetupAvailability }; diff --git a/apps/web/modules/getting-started/[[...step]]/onboarding-view.tsx b/apps/web/modules/getting-started/[[...step]]/onboarding-view.tsx index 6bf55279c1..5b64fd34fa 100644 --- a/apps/web/modules/getting-started/[[...step]]/onboarding-view.tsx +++ b/apps/web/modules/getting-started/[[...step]]/onboarding-view.tsx @@ -4,30 +4,32 @@ import { signOut } from "next-auth/react"; import type { TFunction } from "next-i18next"; import Head from "next/head"; import { usePathname, useRouter } from "next/navigation"; -import { Suspense } from "react"; +import { Suspense, useEffect } from "react"; import { Toaster } from "react-hot-toast"; import { z } from "zod"; import { classNames } from "@calcom/lib"; +import { DEFAULT_SCHEDULE } from "@calcom/lib/availability"; import { APP_NAME } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useParamsWithFallback } from "@calcom/lib/hooks/useParamsWithFallback"; import { IdentityProvider } from "@calcom/prisma/enums"; import { trpc } from "@calcom/trpc"; +import type { TRPCClientErrorLike } from "@calcom/trpc/react"; +import type { AppRouter } from "@calcom/trpc/server/routers/_app"; import type { inferSSRProps } from "@calcom/types/inferSSRProps"; -import { Button, StepCard, Steps } from "@calcom/ui"; +import { Button, showToast, StepCard, Steps } from "@calcom/ui"; import { Icon } from "@calcom/ui"; import type { getServerSideProps } from "@lib/getting-started/[[...step]]/getServerSideProps"; import { ConnectedCalendars } from "@components/getting-started/steps-views/ConnectCalendars"; import { ConnectedVideoStep } from "@components/getting-started/steps-views/ConnectedVideoStep"; -import { SetupAvailability } from "@components/getting-started/steps-views/SetupAvailability"; import UserProfile from "@components/getting-started/steps-views/UserProfile"; import { UserSettings } from "@components/getting-started/steps-views/UserSettings"; const INITIAL_STEP = "user-settings"; -const BASE_STEPS = ["user-settings", "setup-availability", "user-profile"] as const; +const BASE_STEPS = ["user-settings", "user-profile"] as const; const EXTRA_STEPS = ["connected-calendar", "connected-video"] as const; type StepType = (typeof BASE_STEPS)[number] | (typeof EXTRA_STEPS)[number]; @@ -41,13 +43,6 @@ const getStepsAndHeadersForUser = (identityProvider: IdentityProvider, t: TFunct title: t("welcome_to_cal_header", { appName: APP_NAME }), subtitle: [t("we_just_need_basic_info"), t("edit_form_later_subtitle")], }, - { - title: t("set_availability"), - subtitle: [ - t("set_availability_getting_started_subtitle_1"), - t("set_availability_getting_started_subtitle_2"), - ], - }, { title: t("nearly_there"), subtitle: [t("nearly_there_instructions")], @@ -86,9 +81,7 @@ const getStepsAndHeadersForUser = (identityProvider: IdentityProvider, t: TFunct const stepRouteSchema = z.object({ step: z - .array( - z.enum(["user-settings", "setup-availability", "user-profile", "connected-calendar", "connected-video"]) - ) + .array(z.enum(["user-settings", "user-profile", "connected-calendar", "connected-video"])) .default([INITIAL_STEP]), from: z.string().optional(), }); @@ -97,10 +90,15 @@ export type PageProps = inferSSRProps; const OnboardingPage = (props: PageProps) => { const pathname = usePathname(); const params = useParamsWithFallback(); - const router = useRouter(); - const [user] = trpc.viewer.me.useSuspenseQuery(); const { t } = useLocale(); + const [user] = trpc.viewer.me.useSuspenseQuery(); + const scheduleMutationOptions = { + onError: (error: TRPCClientErrorLike) => { + showToast(error.message, "error"); + }, + }; + const createSchedule = trpc.viewer.availability.schedule.create.useMutation(scheduleMutationOptions); const result = stepRouteSchema.safeParse({ ...params, @@ -137,6 +135,20 @@ const OnboardingPage = (props: PageProps) => { router.push(`/getting-started/${stepTransform(newStep)}`); }; + useEffect(() => { + if (!user || user.completedOnboarding) { + return; + } + const createDefaultSchedule = async () => { + await createSchedule.mutateAsync({ + name: t("default_schedule_name"), + ...DEFAULT_SCHEDULE, + }); + }; + + createDefaultSchedule(); + }, []); + return (
{ {currentStep === "connected-video" && } - {currentStep === "setup-availability" && ( - - )} {currentStep === "user-profile" && } diff --git a/apps/web/playwright/onboarding.e2e.ts b/apps/web/playwright/onboarding.e2e.ts index 5fbd67dfc6..ee8cd88343 100644 --- a/apps/web/playwright/onboarding.e2e.ts +++ b/apps/web/playwright/onboarding.e2e.ts @@ -41,7 +41,7 @@ test.describe("Onboarding", () => { await page.locator("button[type=submit]").click(); if (identityProvider === IdentityProvider.GOOGLE) { - await expect(page).toHaveURL(/.*setup-availability/); + await expect(page).toHaveURL(/.*user-profile/); } else { await expect(page).toHaveURL(/.*connected-calendar/); } @@ -66,20 +66,11 @@ test.describe("Onboarding", () => { // tests skip button, we don't want to test entire flow. await page.locator("button[data-testid=skip-step]").click(); - await expect(page).toHaveURL(/.*setup-availability/); + await expect(page).toHaveURL(/.*user-profile/); }); } - await test.step("step 4 - Setup Availability", async () => { - const isDisabled = await page.locator("button[data-testid=save-availability]").isDisabled(); - await expect(isDisabled).toBe(false); - // same here, skip this step. - await page.locator("button[data-testid=save-availability]").click(); - - await expect(page).toHaveURL(/.*user-profile/); - }); - - await test.step("step 5- User Profile", async () => { + await test.step("step 4 - User Profile", async () => { await page.locator("button[type=submit]").click(); // should redirect to /event-types after onboarding