Skip to content

Commit f87d4de

Browse files
authored
fix: UI nits in QA verison of onboarding v3 (calcom#25532)
* fix create button * fix subdomain prefix in orgs details view * remove redundant text * fix prefix on link fields * brand mode colours * Fix banner positioning * remove app descrtipion plus add mr to account for button * fix gaps * improve free plan welcome modal
1 parent fd6c6e6 commit f87d4de

14 files changed

Lines changed: 117 additions & 38 deletions

File tree

apps/web/app/(use-page-wrapper)/onboarding/teams/invite/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const ServerPage = async () => {
3434
return <TeamInviteEmailView userEmail={userEmail} />;
3535
}
3636

37-
return <TeamInviteView userEmail={userEmail} />;
37+
return <TeamInviteEmailView userEmail={userEmail} />;
3838
};
3939

4040
export default ServerPage;

apps/web/modules/onboarding/components/EmailInviteForm.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export function EmailInviteForm({
5050
<div className="flex h-full w-full flex-col gap-4">
5151
<div className="flex flex-col gap-2">
5252
{showTeamSelect ? (
53-
<div className="grid grid-cols-2">
53+
<div className="mr-7 grid grid-cols-2">
5454
<Label className="text-emphasis mb-0 text-sm font-medium" htmlFor="invites.0.email">
5555
{t("email")}
5656
</Label>

apps/web/modules/onboarding/components/OnboardingLayout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export const OnboardingLayout = ({ userEmail, currentStep, totalSteps, children
3232
{/* Logo and container - centered */}
3333
<div className="flex w-full flex-1 flex-col items-center justify-center gap-6">
3434
<Logo className="mt-4 h-5 w-auto shrink-0" />
35-
<div className="border-subtle bg-default grid max-h-[690px] min-h-0 w-full max-w-[532px] flex-1 grid-cols-1 gap-6 overflow-hidden rounded-2xl border px-4 py-2 sm:px-12 sm:py-10 xl:h-[690px] xl:max-w-[1130px] xl:grid-cols-[40%_1fr] xl:pl-10 xl:pr-0">
35+
<div className="border-subtle bg-default grid max-h-[690px] min-h-0 w-full max-w-[532px] flex-1 grid-cols-1 gap-12 overflow-hidden rounded-2xl border px-4 py-2 sm:px-12 sm:py-10 xl:h-[690px] xl:max-w-[1130px] xl:grid-cols-[40%_1fr] xl:pl-10 xl:pr-0">
3636
{/* Column 1 - Always visible, 40% on xl+ */}
3737
<div className="flex h-full min-h-0 flex-col">{column1}</div>
3838
{/* Column 2 - Hidden on mobile, visible on xl+, 60% on xl+ */}

apps/web/modules/onboarding/components/onboarding-browser-view.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import classNames from "classnames";
44
import { AnimatePresence, motion } from "framer-motion";
55
import { usePathname } from "next/navigation";
66

7+
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
8+
import { subdomainSuffix } from "@calcom/features/ee/organizations/lib/orgDomains";
79
import { WEBAPP_URL } from "@calcom/lib/constants";
810
import { useLocale } from "@calcom/lib/hooks/useLocale";
911
import { Avatar } from "@calcom/ui/components/avatar";
@@ -18,6 +20,21 @@ type OnboardingBrowserViewProps = {
1820
teamSlug?: string;
1921
};
2022

23+
const getDisplayUrl = (
24+
orgSlug: string | null | undefined,
25+
username: string | null | undefined,
26+
teamSlug: string | undefined
27+
): string => {
28+
if (orgSlug) {
29+
return teamSlug !== undefined
30+
? `${orgSlug}.${subdomainSuffix()}/team/${teamSlug || ""}`
31+
: `${orgSlug}.${subdomainSuffix()}/${username || ""}`;
32+
}
33+
34+
const webappUrl = WEBAPP_URL.replace(/^https?:\/\//, "");
35+
return teamSlug !== undefined ? `${webappUrl}/team/${teamSlug || ""}` : `${webappUrl}/${username || ""}`;
36+
};
37+
2138
export const OnboardingBrowserView = ({
2239
avatar,
2340
name,
@@ -27,9 +44,9 @@ export const OnboardingBrowserView = ({
2744
}: OnboardingBrowserViewProps) => {
2845
const { t } = useLocale();
2946
const pathname = usePathname();
30-
const webappUrl = WEBAPP_URL.replace(/^https?:\/\//, "");
31-
const displayUrl =
32-
teamSlug !== undefined ? `${webappUrl}/team/${teamSlug || ""}` : `${webappUrl}/${username || ""}`;
47+
const orgBranding = useOrgBranding();
48+
49+
const displayUrl = getDisplayUrl(orgBranding?.slug, username, teamSlug);
3350

3451
// Animation variants for entry and exit
3552
const containerVariants = {

apps/web/modules/onboarding/components/onboarding-invite-browser-view.tsx

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -133,22 +133,44 @@ export const OnboardingInviteBrowserView = ({
133133
duration: 0.5,
134134
ease: "backOut",
135135
}}>
136-
<div className="bg-default border-subtle flex flex-col gap-4 rounded-2xl border p-8">
137-
<div className="flex flex-col items-start gap-4">
138-
<Avatar
139-
size="lg"
140-
imageSrc={avatar || undefined}
141-
alt={displayName}
142-
className="border-default h-12 w-12 border-2"
143-
/>
144-
<div className="flex w-full flex-col items-start gap-1">
145-
<h2 className="text-emphasis font-cal w-full text-left text-xl font-semibold leading-tight">
146-
{displayName}
147-
</h2>
148-
<p className="text-subtle text-left text-sm font-normal leading-tight">
149-
{displayBio || "We're emailing you all the details"}
150-
</p>
151-
</div>
136+
<div className="bg-default border-subtle flex flex-col rounded-2xl border">
137+
<div className="relative p-1">
138+
{/* Banner Image */}
139+
{organizationBrand.banner && (
140+
<div className="border-subtle relative h-36 w-full overflow-hidden rounded-xl border">
141+
{/* eslint-disable-next-line @next/next/no-img-element */}
142+
<img
143+
src={organizationBrand.banner}
144+
alt={displayName}
145+
className="h-full w-full object-cover"
146+
/>
147+
</div>
148+
)}
149+
150+
{/* Organization Avatar - Overlaying the banner */}
151+
{organizationBrand.banner && avatar && (
152+
<div className="absolute -bottom-6 left-4">
153+
<Avatar size="lg" imageSrc={avatar} alt={displayName} className="h-12 w-12 border" />
154+
</div>
155+
)}
156+
</div>
157+
158+
{/* Organization Info */}
159+
<div className={`flex flex-col items-start gap-1 px-4 pb-4 pt-8`}>
160+
{!organizationBrand.banner && avatar && (
161+
<Avatar
162+
size="lg"
163+
imageSrc={avatar}
164+
alt={displayName}
165+
className="border-default mb-4 h-12 w-12 border-2"
166+
/>
167+
)}
168+
<h2 className="text-emphasis font-cal w-full text-left text-xl font-semibold leading-tight">
169+
{displayName}
170+
</h2>
171+
<p className="text-subtle text-left text-sm font-normal leading-tight">
172+
{displayBio || "We're emailing you all the details"}
173+
</p>
152174
</div>
153175
</div>
154176

apps/web/modules/onboarding/components/onboarding-organization-browser-view.tsx

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,30 @@
33
import classNames from "classnames";
44
import { AnimatePresence, motion } from "framer-motion";
55
import { usePathname } from "next/navigation";
6+
import { useEffect, useRef } from "react";
67

7-
import { WEBAPP_URL } from "@calcom/lib/constants";
8+
import { subdomainSuffix } from "@calcom/features/ee/organizations/lib/orgDomains";
89
import { useLocale } from "@calcom/lib/hooks/useLocale";
910
import { Avatar } from "@calcom/ui/components/avatar";
1011
import { Button } from "@calcom/ui/components/button";
1112
import { Icon, type IconName } from "@calcom/ui/components/icon";
1213

14+
// Helper function to darken a hex color
15+
const darkenColor = (hex: string, amount: number): string => {
16+
const num = parseInt(hex.replace("#", ""), 16);
17+
const r = Math.max(0, Math.min(255, ((num >> 16) & 0xff) * (1 - amount)));
18+
const g = Math.max(0, Math.min(255, ((num >> 8) & 0xff) * (1 - amount)));
19+
const b = Math.max(0, Math.min(255, (num & 0xff) * (1 - amount)));
20+
return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, "0")}`;
21+
};
22+
1323
type OnboardingOrganizationBrowserViewProps = {
1424
avatar?: string | null;
1525
name?: string;
1626
bio?: string;
1727
slug?: string;
1828
bannerUrl?: string | null;
29+
brandColor?: string;
1930
};
2031

2132
export const OnboardingOrganizationBrowserView = ({
@@ -24,11 +35,22 @@ export const OnboardingOrganizationBrowserView = ({
2435
bio,
2536
slug,
2637
bannerUrl,
38+
brandColor,
2739
}: OnboardingOrganizationBrowserViewProps) => {
2840
const { t } = useLocale();
2941
const pathname = usePathname();
30-
const webappUrl = WEBAPP_URL.replace(/^https?:\/\//, "");
31-
const displayUrl = `${webappUrl}/${slug || ""}`;
42+
const containerRef = useRef<HTMLDivElement>(null);
43+
const displayUrl = slug ? `${slug}.${subdomainSuffix()}` : subdomainSuffix();
44+
45+
// Update CSS variables when brandColor changes
46+
useEffect(() => {
47+
if (brandColor && containerRef.current) {
48+
containerRef.current.style.setProperty("--cal-brand", brandColor);
49+
// Set brand-emphasis as a slightly darker version for hover states
50+
const darkerColor = darkenColor(brandColor, 0.1);
51+
containerRef.current.style.setProperty("--cal-brand-emphasis", darkerColor);
52+
}
53+
}, [brandColor]);
3254

3355
// Animation variants for entry and exit
3456
const containerVariants = {
@@ -79,7 +101,9 @@ export const OnboardingOrganizationBrowserView = ({
79101
];
80102

81103
return (
82-
<div className="bg-default border-subtle hidden h-full w-full flex-col overflow-hidden rounded-l-2xl border-y border-s xl:flex">
104+
<div
105+
ref={containerRef}
106+
className="bg-default border-subtle hidden h-full w-full flex-col overflow-hidden rounded-l-2xl border-y border-s xl:flex">
83107
{/* Browser header */}
84108
<div className="border-subtle bg-default flex min-w-0 shrink-0 items-center gap-3 rounded-t-2xl border-b p-3">
85109
{/* Navigation buttons */}
@@ -164,7 +188,7 @@ export const OnboardingOrganizationBrowserView = ({
164188
</div>
165189
<p className="text-subtle text-sm font-medium leading-tight">{event.description}</p>
166190
</div>
167-
<Button color="secondary" size="sm" EndIcon="arrow-right" tabIndex={-1}>
191+
<Button color="primary" size="sm" EndIcon="arrow-right" tabIndex={-1}>
168192
{t("book_now")}
169193
</Button>
170194
</div>

apps/web/modules/onboarding/components/onboarding-teams-browser-view.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,6 @@ export const OnboardingTeamsBrowserView = ({
108108
{/* Teams Info */}
109109
<div className="flex flex-col gap-2 px-4 pb-4 pt-12">
110110
<h2 className="text-emphasis text-xl font-semibold leading-tight">{t("teams")}</h2>
111-
<p className="text-subtle text-sm leading-normal">
112-
{hasValidTeams
113-
? t("onboarding_teams_browser_view_subtitle")
114-
: t("onboarding_teams_browser_view_placeholder_subtitle")}
115-
</p>
116111
</div>
117112
</div>
118113

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ export function useCreateTeam() {
1818
const createTeamMutation = trpc.viewer.teams.create.useMutation();
1919
const inviteMemberMutation = trpc.viewer.teams.inviteMember.useMutation();
2020

21-
const createTeam = async (store: OnboardingState) => {
21+
const createTeam = async (store?: OnboardingState) => {
2222
setIsSubmitting(true);
2323

2424
try {
25-
const { teamDetails, teamBrand } = store;
25+
// Get the latest state from the store to ensure we have the most up-to-date values
26+
const currentStore = store || useOnboardingStore.getState();
27+
const { teamDetails, teamBrand } = currentStore;
2628

2729
// Validate team details - if empty, redirect back to team details step
2830
if (!teamDetails.name || !teamDetails.name.trim() || !teamDetails.slug || !teamDetails.slug.trim()) {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useSession } from "next-auth/react";
12
import { useRouter } from "next/navigation";
23
import { useState } from "react";
34

@@ -11,6 +12,7 @@ import type { OnboardingState } from "../store/onboarding-store";
1112

1213
export const useSubmitOnboarding = () => {
1314
const router = useRouter();
15+
const { update: updateSession } = useSession();
1416
const [isSubmitting, setIsSubmitting] = useState(false);
1517
const [error, setError] = useState<string | null>(null);
1618
const flags = useFlagMap();
@@ -78,6 +80,8 @@ export const useSubmitOnboarding = () => {
7880
// No checkout URL means billing is disabled (self-hosted flow)
7981
// Organization has already been created by the backend
8082
showToast("Organization created successfully!", "success");
83+
// Refresh session to get updated organization data
84+
await updateSession();
8185
// Set flag to show welcome modal after personal onboarding redirect
8286
setShowNewOrgModalFlag();
8387
skipToPersonal(resetOnboarding);

apps/web/modules/onboarding/organization/brand/organization-brand-view.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ export const OrganizationBrandView = ({ userEmail }: OrganizationBrandViewProps)
189189
bio={organizationDetails.bio}
190190
slug={organizationDetails.link}
191191
bannerUrl={bannerPreview}
192+
brandColor={brandColor}
192193
/>
193194
</OnboardingLayout>
194195
);

0 commit comments

Comments
 (0)