From 66234ffba94b2921d926621a4e7abf4b14cd95b4 Mon Sep 17 00:00:00 2001 From: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Date: Mon, 27 Oct 2025 17:32:06 +0000 Subject: [PATCH] chore: illustrations to getting-started page in onboarding v3 (#24673) * 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/components/plan-icon.tsx | 107 +++++++++++++++++ .../getting-started/onboarding-view.tsx | 108 ++++++++++++------ apps/web/public/static/locales/en/common.json | 11 ++ 3 files changed, 191 insertions(+), 35 deletions(-) create mode 100644 apps/web/modules/onboarding/components/plan-icon.tsx diff --git a/apps/web/modules/onboarding/components/plan-icon.tsx b/apps/web/modules/onboarding/components/plan-icon.tsx new file mode 100644 index 0000000000..bd04740979 --- /dev/null +++ b/apps/web/modules/onboarding/components/plan-icon.tsx @@ -0,0 +1,107 @@ +import classNames from "@calcom/ui/classNames"; +import { Icon, type IconName } from "@calcom/ui/components/icon"; + +export function PlanIcon({ icon, variant = "single" }: { icon: IconName; variant?: "single" | "double" }) { + const renderIconContainer = (index: number) => { + // For double variant: 55px icon width + 24px gap = 79px between centers, so ±39.5px from center + const leftPosition = variant === "double" ? `calc(50% + ${index === 0 ? -39.5 : 39.5}px)` : "50%"; + + return ( +
+ {/* Icon */} +
+ +
+ + {/* Inner highlight/shine effect */} +
+
+ ); + }; + + return ( +
+ {/* Outer ring - SVG with linear gradient */} + + + + + + + + + + + {/* Middle ring - SVG with linear gradient */} + + + + + + + + + + + {/* Main icon container(s) with gradient background */} + {variant === "single" ? renderIconContainer(0) : [renderIconContainer(0), renderIconContainer(1)]} +
+ ); +} diff --git a/apps/web/modules/onboarding/getting-started/onboarding-view.tsx b/apps/web/modules/onboarding/getting-started/onboarding-view.tsx index e2d0f8252c..d9e49c1c27 100644 --- a/apps/web/modules/onboarding/getting-started/onboarding-view.tsx +++ b/apps/web/modules/onboarding/getting-started/onboarding-view.tsx @@ -3,12 +3,16 @@ import { useRouter } from "next/navigation"; import { isCompanyEmail } from "@calcom/features/ee/organizations/lib/utils"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import classNames from "@calcom/ui/classNames"; import { Badge } from "@calcom/ui/components/badge"; import { Button } from "@calcom/ui/components/button"; +import { type IconName } from "@calcom/ui/components/icon"; import { Logo } from "@calcom/ui/components/logo"; import { RadioAreaGroup } from "@calcom/ui/components/radio"; import { OnboardingContinuationPrompt } from "../components/onboarding-continuation-prompt"; +import { PlanIcon } from "../components/plan-icon"; import { useOnboardingStore, type PlanType } from "../store/onboarding-store"; type OnboardingViewProps = { @@ -18,6 +22,7 @@ type OnboardingViewProps = { export const OnboardingView = ({ userName, userEmail }: OnboardingViewProps) => { const router = useRouter(); + const { t } = useLocale(); const { selectedPlan, setSelectedPlan } = useOnboardingStore(); const handleContinue = () => { @@ -30,25 +35,36 @@ export const OnboardingView = ({ userName, userEmail }: OnboardingViewProps) => } }; + const planIconByType: Record = { + personal: "user", + team: "users", + organization: "users", + }; + const allPlans = [ { id: "personal" as PlanType, - title: "For personal use", - badge: "Free", - description: "Good for individuals who are just starting out and simply want the essentials.", + title: t("onboarding_plan_personal_title"), + badge: t("onboarding_plan_personal_badge"), + description: t("onboarding_plan_personal_description"), + icon: planIconByType.personal, + variant: "single" as const, }, { id: "team" as PlanType, - title: "With my team", - badge: "$15 p/mo p/person", - description: - "Highly recommended for small teams who seek to upgrade their time and perform better as a unit.", + title: t("onboarding_plan_team_title"), + badge: t("onboarding_plan_team_badge"), + description: t("onboarding_plan_team_description"), + icon: planIconByType.team, + variant: "single" as const, }, { id: "organization" as PlanType, - title: "For my organization", - badge: "$37 p/mo p/person", - description: "Robust scheduling for larger teams looking to have more control, privacy, and security.", + title: t("onboarding_plan_organization_title"), + badge: t("onboarding_plan_organization_badge"), + description: t("onboarding_plan_organization_description"), + icon: planIconByType.organization, + variant: "double" as const, }, ]; @@ -89,9 +105,11 @@ export const OnboardingView = ({ userName, userEmail }: OnboardingViewProps) => {/* Card Header */}
-

Hey {userName}

+

+ {t("onboarding_welcome_message", { userName })} +

- To personalize your experience, what do you plan to use Cal.com for? + {t("onboarding_welcome_question")}

@@ -101,35 +119,55 @@ export const OnboardingView = ({ userName, userEmail }: OnboardingViewProps) => value={selectedPlan ?? undefined} onValueChange={(value) => setSelectedPlan(value as PlanType)} className="flex w-full flex-col gap-1 rounded-[10px]"> - {plans.map((plan) => ( - -
-
-

{plan.title}

- - {plan.badge} - + {plans.map((plan) => { + const isSelected = selectedPlan === plan.id; + + return ( + button]:left-auto [&>button]:right-6 [&>button]:mt-0 [&>button]:transform" + )} + classNames={{ + container: "flex w-full items-center gap-4 p-4 pl-5 pr-12 md:p-5 md:pr-14", + }}> +
+ +
+
+

{plan.title}

+ + + {plan.badge} + + +
+ + {plan.badge} + +

+ {plan.description} +

+
- - {plan.badge} - -

- {plan.description} -

-
- - ))} + + ); + })} {/* Footer */}
diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index b178ff4523..9f08bed0f1 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -3886,5 +3886,16 @@ "onboarding_skip_for_now": "Skip for now", "onboarding_or_divider": "or", "onboarding_team_required": "Team is required", + "onboarding_welcome_message": "Hey {{userName}}", + "onboarding_welcome_question": "To personalize your experience, what do you plan to use Cal.com for?", + "onboarding_plan_personal_title": "For personal use", + "onboarding_plan_personal_badge": "Free", + "onboarding_plan_personal_description": "Good for individuals who are just starting out and simply want the essentials.", + "onboarding_plan_team_title": "With my team", + "onboarding_plan_team_description": "Highly recommended for small teams who seek to upgrade their time and perform better as a unit.", + "onboarding_plan_organization_title": "For my organization", + "onboarding_plan_team_badge": "$15/user/mo", + "onboarding_plan_organization_badge": "$37/user/mo", + "onboarding_plan_organization_description": "Robust scheduling for larger teams looking to have more control, privacy, and security.", "ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑" }