Skip to content

Commit fa884e8

Browse files
authored
chore: Onboarding path service plus redirects (calcom#24679)
* i18n * WIP icon stuff * More icon work * More gradient tuning * Mvoe plan-icon to planicon.tsx * Fix cubic suggestion * Fix darkmode icon and gradients * Fix type error * Onboarding path service * Update usages of onboarding path and getting started hook
1 parent 3418a25 commit fa884e8

10 files changed

Lines changed: 72 additions & 16 deletions

File tree

apps/web/lib/pages/auth/verify-email.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { z } from "zod";
44
import dayjs from "@calcom/dayjs";
55
import { OrganizationRepository } from "@calcom/features/ee/organizations/repositories/OrganizationRepository";
66
import { StripeBillingService } from "@calcom/features/ee/billing/stripe-billing-service";
7+
import { OnboardingPathService } from "@calcom/features/onboarding/lib/onboarding-path.service";
78
import { WEBAPP_URL } from "@calcom/lib/constants";
89
import { IS_STRIPE_ENABLED } from "@calcom/lib/constants";
910
import { prisma } from "@calcom/prisma";
@@ -172,7 +173,9 @@ export async function handler(req: NextApiRequest, res: NextApiResponse) {
172173

173174
await moveUserToMatchingOrg({ email: user.email });
174175

175-
return res.redirect(`${WEBAPP_URL}/${hasCompletedOnboarding ? "/event-types" : "/getting-started"}`);
176+
const gettingStartedPath = await OnboardingPathService.getGettingStartedPath(prisma);
177+
178+
return res.redirect(`${WEBAPP_URL}${hasCompletedOnboarding ? "/event-types" : gettingStartedPath}`);
176179
}
177180

178181
export async function cleanUpVerificationTokens(id: number) {

apps/web/modules/auth/verify-email-view.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,23 @@ import useEmailVerifyCheck from "@calcom/trpc/react/hooks/useEmailVerifyCheck";
1111
import { showToast } from "@calcom/ui/components/toast";
1212
import { Button } from "@calcom/ui/components/button";
1313
import { EmptyScreen } from "@calcom/ui/components/empty-screen";
14+
import { useFlagMap } from "@calcom/features/flags/context/provider";
1415

1516
function VerifyEmailPage() {
1617
const { data } = useEmailVerifyCheck();
1718
const { data: session } = useSession();
1819
const router = useRouter();
1920
const { t, isLocaleReady } = useLocale();
2021
const mutation = trpc.viewer.auth.resendVerifyEmail.useMutation();
22+
const flags = useFlagMap();
2123

2224
useEffect(() => {
2325
if (data?.isVerified) {
24-
router.replace("/getting-started");
26+
const gettingStartedPath = flags["onboarding-v3"] ? "/onboarding/getting-started" : "/getting-started";
27+
router.replace(gettingStartedPath);
2528
}
2629
// eslint-disable-next-line react-hooks/exhaustive-deps
27-
}, [data?.isVerified]);
30+
}, [data?.isVerified, flags]);
2831
if (!isLocaleReady) {
2932
return null;
3033
}

apps/web/modules/onboarding/hooks/useCreateTeam.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { useRouter } from "next/navigation";
22
import { useState } from "react";
33

4+
import { useFlagMap } from "@calcom/features/flags/context/provider";
45
import { trpc } from "@calcom/trpc/react";
56

67
import type { OnboardingState } from "../store/onboarding-store";
78

89
export function useCreateTeam() {
910
const router = useRouter();
1011
const [isSubmitting, setIsSubmitting] = useState(false);
12+
const flags = useFlagMap();
1113

1214
const createTeamMutation = trpc.viewer.teams.create.useMutation();
1315

@@ -31,7 +33,11 @@ export function useCreateTeam() {
3133
}
3234

3335
if (result.team) {
34-
router.push("/getting-started");
36+
// Not sure we need this flag check - keeping it here for safe keeping as this is called only from v3 onboarding flow
37+
const gettingStartedPath = flags["onboarding-v3"]
38+
? "/onboarding/getting-started"
39+
: "/getting-started";
40+
router.push(gettingStartedPath);
3541
}
3642
} catch (error) {
3743
console.error("Failed to create team:", error);

apps/web/modules/onboarding/hooks/useSubmitOnboarding.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import { useState } from "react";
44
import { CreationSource } from "@calcom/prisma/enums";
55
import { trpc } from "@calcom/trpc/react";
66
import { showToast } from "@calcom/ui/components/toast";
7+
import { useFlagMap } from "@calcom/features/flags/context/provider";
78

89
import type { OnboardingState } from "../store/onboarding-store";
910

1011
export const useSubmitOnboarding = () => {
1112
const router = useRouter();
1213
const [isSubmitting, setIsSubmitting] = useState(false);
1314
const [error, setError] = useState<string | null>(null);
15+
const flags = useFlagMap();
1416

1517
const intentToCreateOrg = trpc.viewer.organizations.intentToCreateOrg.useMutation();
1618

@@ -80,7 +82,8 @@ export const useSubmitOnboarding = () => {
8082
showToast("Organization created successfully!", "success");
8183
// TODO: after this redirect we need to hard refresh the page to see org
8284
resetOnboarding();
83-
router.push("/getting-started");
85+
const gettingStartedPath = flags["onboarding-v3"] ? "/onboarding/getting-started" : "/getting-started";
86+
router.push(gettingStartedPath);
8487
} catch (err) {
8588
const errorMessage = err instanceof Error ? err.message : "Failed to create organization";
8689
setError(errorMessage);

apps/web/server/lib/auth/sso/[provider]/getServerSideProps.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import stripe from "@calcom/features/ee/payments/server/stripe";
77
import { hostedCal, isSAMLLoginEnabled, samlProductID, samlTenantID } from "@calcom/features/ee/sso/lib/saml";
88
import { ssoTenantProduct } from "@calcom/features/ee/sso/lib/sso";
99
import { checkUsername } from "@calcom/features/profile/lib/checkUsername";
10+
import { OnboardingPathService } from "@calcom/features/onboarding/lib/onboarding-path.service";
1011
import { IS_PREMIUM_USERNAME_ENABLED } from "@calcom/lib/constants";
1112
import prisma from "@calcom/prisma";
1213

@@ -18,7 +19,12 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
1819
const providerParam = asStringOrNull(context.query.provider);
1920
const emailParam = asStringOrNull(context.query.email);
2021
const usernameParam = asStringOrNull(context.query.username);
21-
const successDestination = `/getting-started${usernameParam ? `?username=${usernameParam}` : ""}`;
22+
23+
const successDestination = await OnboardingPathService.getGettingStartedPathWithParams(
24+
prisma,
25+
usernameParam ? { username: usernameParam } : undefined
26+
);
27+
2228
if (!providerParam) {
2329
throw new Error(`File is not named sso/[provider]`);
2430
}

packages/features/auth/lib/hooks/useRedirectToOnboardingIfNeeded.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,10 @@ export function useRedirectToOnboardingIfNeeded() {
4242

4343
useEffect(() => {
4444
if (canRedirect) {
45-
router.replace("/getting-started");
45+
const gettingStartedPath = flags["onboarding-v3"] ? "/onboarding/getting-started" : "/getting-started";
46+
router.replace(gettingStartedPath);
4647
}
47-
}, [canRedirect, router]);
48+
}, [canRedirect, router, flags]);
4849

4950
return {
5051
isLoading,

packages/features/ee/teams/services/teamService.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { TeamRepository } from "@calcom/features/ee/teams/repositories/TeamRepos
77
import { WorkflowService } from "@calcom/features/ee/workflows/lib/service/WorkflowService";
88
import { createAProfileForAnExistingUser } from "@calcom/features/profile/lib/createAProfileForAnExistingUser";
99
import { ProfileRepository } from "@calcom/features/profile/repositories/ProfileRepository";
10+
import { OnboardingPathService } from "@calcom/features/onboarding/lib/onboarding-path.service";
1011
import { WEBAPP_URL } from "@calcom/lib/constants";
1112
import { deleteDomain } from "@calcom/lib/domainManager/organization";
1213
import logger from "@calcom/lib/logger";
@@ -76,7 +77,7 @@ export class TeamService {
7677
if (!existingToken) throw new TRPCError({ code: "NOT_FOUND", message: "Invite token not found" });
7778
return {
7879
token: existingToken.token,
79-
inviteLink: TeamService.buildInviteLink(existingToken.token, isOrganizationOrATeamInOrganization),
80+
inviteLink: await TeamService.buildInviteLink(existingToken.token, isOrganizationOrATeamInOrganization),
8081
};
8182
}
8283

@@ -93,14 +94,18 @@ export class TeamService {
9394

9495
return {
9596
token,
96-
inviteLink: TeamService.buildInviteLink(token, isOrganizationOrATeamInOrganization),
97+
inviteLink: await TeamService.buildInviteLink(token, isOrganizationOrATeamInOrganization),
9798
};
9899
}
99100

100-
private static buildInviteLink(token: string, isOrgContext: boolean): string {
101+
private static async buildInviteLink(token: string, isOrgContext: boolean): Promise<string> {
101102
const teamInviteLink = `${WEBAPP_URL}/teams?token=${token}`;
102-
const orgInviteLink = `${WEBAPP_URL}/signup?token=${token}&callbackUrl=/getting-started`;
103-
return isOrgContext ? orgInviteLink : teamInviteLink;
103+
if (!isOrgContext) {
104+
return teamInviteLink;
105+
}
106+
const gettingStartedPath = await OnboardingPathService.getGettingStartedPath(prisma);
107+
const orgInviteLink = `${WEBAPP_URL}/signup?token=${token}&callbackUrl=${gettingStartedPath}`;
108+
return orgInviteLink;
104109
}
105110
/**
106111
* Deletes a team and all its associated data in a safe, transactional order.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
2+
import type { PrismaClient } from "@calcom/prisma";
3+
4+
export class OnboardingPathService {
5+
static async getGettingStartedPath(prisma: PrismaClient): Promise<string> {
6+
const featuresRepository = new FeaturesRepository(prisma);
7+
const onboardingV3Enabled = await featuresRepository.checkIfFeatureIsEnabledGlobally("onboarding-v3");
8+
return onboardingV3Enabled ? "/onboarding/getting-started" : "/getting-started";
9+
}
10+
11+
static async getGettingStartedPathWithParams(
12+
prisma: PrismaClient,
13+
queryParams?: Record<string, string>
14+
): Promise<string> {
15+
const basePath = await OnboardingPathService.getGettingStartedPath(prisma);
16+
17+
if (!queryParams || Object.keys(queryParams).length === 0) {
18+
return basePath;
19+
}
20+
21+
const params = new URLSearchParams(queryParams);
22+
return `${basePath}?${params.toString()}`;
23+
}
24+
}

packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { PermissionCheckService } from "@calcom/features/pbac/services/permissio
99
import { createAProfileForAnExistingUser } from "@calcom/features/profile/lib/createAProfileForAnExistingUser";
1010
import { ProfileRepository } from "@calcom/features/profile/repositories/ProfileRepository";
1111
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
12+
import { OnboardingPathService } from "@calcom/features/onboarding/lib/onboarding-path.service";
1213
import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "@calcom/lib/availability";
1314
import { ENABLE_PROFILE_SWITCHER, WEBAPP_URL } from "@calcom/lib/constants";
1415
import logger from "@calcom/lib/logger";
@@ -503,12 +504,13 @@ export async function sendSignupToOrganizationEmail({
503504
}) {
504505
try {
505506
const verificationToken = await createVerificationToken(usernameOrEmail, teamId);
507+
const gettingStartedPath = await OnboardingPathService.getGettingStartedPath(prisma);
506508
await sendTeamInviteEmail({
507509
language: translation,
508510
from: inviterName || `${team.name}'s admin`,
509511
to: usernameOrEmail,
510512
teamName: team.name,
511-
joinLink: `${WEBAPP_URL}/signup?token=${verificationToken.token}&callbackUrl=/getting-started`,
513+
joinLink: `${WEBAPP_URL}/signup?token=${verificationToken.token}&callbackUrl=${gettingStartedPath}`,
512514
isCalcomMember: false,
513515
isOrg: isOrg,
514516
parentTeamName: team?.parent?.name,
@@ -717,7 +719,8 @@ export const sendExistingUserTeamInviteEmails = async ({
717719
if (!user.completedOnboarding && !user.password?.hash && user.identityProvider === "CAL") {
718720
const verificationToken = await createVerificationToken(user.email, teamId);
719721

720-
inviteTeamOptions.joinLink = `${WEBAPP_URL}/signup?token=${verificationToken.token}&callbackUrl=/getting-started`;
722+
const gettingStartedPath = await OnboardingPathService.getGettingStartedPath(prisma);
723+
inviteTeamOptions.joinLink = `${WEBAPP_URL}/signup?token=${verificationToken.token}&callbackUrl=${gettingStartedPath}`;
721724
inviteTeamOptions.isCalcomMember = false;
722725
} else if (!isAutoJoin) {
723726
let verificationToken = await prisma.verificationToken.findFirst({

packages/trpc/server/routers/viewer/teams/resendInvitation.handler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { sendTeamInviteEmail } from "@calcom/emails";
22
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
3+
import { OnboardingPathService } from "@calcom/features/onboarding/lib/onboarding-path.service";
34
import { WEBAPP_URL } from "@calcom/lib/constants";
45
import { getTranslation } from "@calcom/lib/server/i18n";
56
import { VerificationTokenRepository } from "@calcom/lib/server/repository/verificationToken";
@@ -51,7 +52,8 @@ export const resendInvitationHandler = async ({ ctx, input }: InviteMemberOption
5152
if (user?.completedOnboarding) {
5253
inviteTeamOptions.joinLink = `${WEBAPP_URL}/teams?token=${verificationToken.token}&autoAccept=true`;
5354
} else {
54-
inviteTeamOptions.joinLink = `${WEBAPP_URL}/signup?token=${verificationToken.token}&callbackUrl=/getting-started`;
55+
const gettingStartedPath = await OnboardingPathService.getGettingStartedPath(prisma);
56+
inviteTeamOptions.joinLink = `${WEBAPP_URL}/signup?token=${verificationToken.token}&callbackUrl=${gettingStartedPath}`;
5557
inviteTeamOptions.isCalcomMember = false;
5658
}
5759
} catch (error) {

0 commit comments

Comments
 (0)