diff --git a/apps/web/pages/getting-started/[[...step]].tsx b/apps/web/pages/getting-started/[[...step]].tsx
new file mode 100644
index 0000000000..f6aeff986b
--- /dev/null
+++ b/apps/web/pages/getting-started/[[...step]].tsx
@@ -0,0 +1,190 @@
+import { GetServerSidePropsContext } from "next";
+import Head from "next/head";
+import { useRouter } from "next/router";
+import { z } from "zod";
+
+import { getSession } from "@calcom/lib/auth";
+import { useLocale } from "@calcom/lib/hooks/useLocale";
+import { User } from "@calcom/prisma/client";
+
+import prisma from "@lib/prisma";
+
+import { StepCard } from "@components/getting-started/components/StepCard";
+import { Steps } from "@components/getting-started/components/Steps";
+import { ConnectedCalendars } from "@components/getting-started/steps-views/ConnectCalendars";
+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";
+
+interface IOnboardingPageProps {
+ user: User;
+}
+
+const INITIAL_STEP = "user-settings";
+const steps = ["user-settings", "connected-calendar", "setup-availability", "user-profile"] as const;
+
+const stepTransform = (step: typeof steps[number]) => {
+ const stepIndex = steps.indexOf(step);
+ if (stepIndex > -1) {
+ return steps[stepIndex];
+ }
+ return INITIAL_STEP;
+};
+
+const stepRouteSchema = z.object({
+ step: z.array(z.enum(steps)).default([INITIAL_STEP]),
+});
+
+const OnboardingPage = (props: IOnboardingPageProps) => {
+ const router = useRouter();
+
+ const { user } = props;
+ const { t } = useLocale();
+
+ const result = stepRouteSchema.safeParse(router.query);
+ const currentStep = result.success ? result.data.step[0] : INITIAL_STEP;
+
+ const headers = [
+ {
+ title: `${t("welcome_to_calcom")}!`,
+ subtitle: [`${t("we_just_need_basic_info")}`],
+ skipText: `${t("skip")}`,
+ },
+ {
+ title: `${t("connect_your_calendar")}`,
+ subtitle: [`${t("connect_your_calendar_instructions")}`],
+ skipText: `${t("do_this_later")}`,
+ },
+ {
+ title: `${t("set_availability")}`,
+ subtitle: [
+ `${t("set_availability_getting_started_subtitle_1")}`,
+ `${t("set_availability_getting_started_subtitle_2")}`,
+ ],
+ skipText: `${t("do_this_later")}`,
+ },
+ {
+ title: `${t("nearly_there")}`,
+ subtitle: [`${t("nearly_there_instructions")}`],
+ },
+ ];
+
+ const goToIndex = (index: number) => {
+ const newStep = steps[index];
+ router.push(
+ {
+ pathname: `/getting-started/${stepTransform(newStep)}`,
+ },
+ undefined
+ );
+ };
+
+ const currentStepIndex = steps.indexOf(currentStep);
+
+ return (
+
+
+
Cal.com - {t("getting_started")}
+
+
+
+
+
+
+
+
+ {currentStep === "user-settings" && goToIndex(1)} />}
+
+ {currentStep === "connected-calendar" && goToIndex(2)} />}
+
+ {currentStep === "setup-availability" && (
+ goToIndex(3)} defaultScheduleId={user.defaultScheduleId} />
+ )}
+
+ {currentStep === "user-profile" && }
+
+ {headers[currentStepIndex]?.skipText && (
+
+ )}
+
+
+
+
+ );
+};
+
+export const getServerSideProps = async (context: GetServerSidePropsContext) => {
+ const crypto = await import("crypto");
+ const session = await getSession(context);
+
+ if (!session?.user?.id) {
+ return { redirect: { permanent: false, destination: "/auth/login" } };
+ }
+
+ const user = await prisma.user.findUnique({
+ where: {
+ id: session.user.id,
+ },
+ select: {
+ id: true,
+ username: true,
+ name: true,
+ email: true,
+ bio: true,
+ avatar: true,
+ timeZone: true,
+ weekStart: true,
+ hideBranding: true,
+ theme: true,
+ plan: true,
+ brandColor: true,
+ darkBrandColor: true,
+ metadata: true,
+ timeFormat: true,
+ allowDynamicBooking: true,
+ defaultScheduleId: true,
+ completedOnboarding: true,
+ },
+ });
+
+ if (!user) {
+ throw new Error("User from session not found");
+ }
+
+ return {
+ props: {
+ user: {
+ ...user,
+ emailMd5: crypto.createHash("md5").update(user.email).digest("hex"),
+ },
+ },
+ };
+};
+
+export default OnboardingPage;
diff --git a/apps/web/pages/getting-started.tsx b/apps/web/pages/old-getting-started.tsx
similarity index 99%
rename from apps/web/pages/getting-started.tsx
rename to apps/web/pages/old-getting-started.tsx
index aa31e6a502..cd14bbfa79 100644
--- a/apps/web/pages/getting-started.tsx
+++ b/apps/web/pages/old-getting-started.tsx
@@ -1,3 +1,4 @@
+// @@DEPRECATED, use new getting-started.tsx instead
import { zodResolver } from "@hookform/resolvers/zod";
import { Prisma } from "@prisma/client";
import classnames from "classnames";
@@ -43,9 +44,9 @@ import { TRPCClientErrorLike } from "@trpc/client";
// Embed isn't applicable to onboarding, so ignore the rule
/* eslint-disable @calcom/eslint/avoid-web-storage */
-type ScheduleFormValues = {
+export interface ScheduleFormValues {
schedule: ScheduleType;
-};
+}
let mutationComplete: ((err: Error | null) => void) | null;
diff --git a/apps/web/pages/v2/availability/[schedule].tsx b/apps/web/pages/v2/availability/[schedule].tsx
index b5b8437e3b..0c887071a4 100644
--- a/apps/web/pages/v2/availability/[schedule].tsx
+++ b/apps/web/pages/v2/availability/[schedule].tsx
@@ -4,14 +4,15 @@ import { useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { z } from "zod";
-import { DEFAULT_SCHEDULE, availabilityAsString } from "@calcom/lib/availability";
+import { Schedule } from "@calcom/features/schedules";
+import { availabilityAsString, DEFAULT_SCHEDULE } from "@calcom/lib/availability";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { stringOrNumber } from "@calcom/prisma/zod-utils";
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
import { BadgeCheckIcon } from "@calcom/ui/Icon";
import Shell from "@calcom/ui/Shell";
import TimezoneSelect from "@calcom/ui/form/TimezoneSelect";
-import { Button, Switch, Schedule, Form, TextField, showToast } from "@calcom/ui/v2";
+import { Button, Form, showToast, Switch, TextField } from "@calcom/ui/v2";
import { QueryCell } from "@lib/QueryCell";
import { HttpError } from "@lib/core/http/error";
@@ -62,9 +63,9 @@ export function AvailabilityForm(props: inferQueryOutput<"viewer.availability.sc
}}
className="-mx-5 flex flex-col sm:mx-0 xl:flex-row">
-
+
{t("change_start_end")}
-
+