## What does this PR do? - Redesigns the organization onboarding flow by merging the brand and details pages - Improves the organization details page with a scrollable interface and visual previews - Adds a new organization-specific browser preview component ## Visual Demo (For contributors especially) #### Image Demo: - The PR replaces the separate brand page with an integrated details page that includes logo and banner uploads - The new organization browser view shows a preview of the organization profile with the selected branding ## Mandatory Tasks (DO NOT REMOVE) - [ ] I have self-reviewed the code. - [ ] I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox. - [ ] I confirm automated tests are in place that prove my fix is effective or that my feature works. ## How should this be tested? - Go through the organization onboarding flow - Test uploading logos and banners - Verify that the organization browser preview updates in real-time with the form inputs - Confirm that the form validation works correctly for organization name and slug - Check that the scrollable interface works properly with fade effects at top and bottom ## Checklist - I have read the [contributing guide](https://github.com/calcom/cal.com/blob/main/CONTRIBUTING.md) - My code follows the style guidelines of this project - I have commented my code, particularly in hard-to-understand areas - I have checked if my changes generate no new warnings <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Split organization brand from the details step and added live previews for organizations and teams. Revamped org/team invites with reusable components, a dedicated email-invite page, and a CSV upload modal. - **New Features** - Separate Brand step with logo, banner, and color; instant org preview via OnboardingOrganizationBrowserView. - Teams browser preview added; invites include email substep (/onboarding/organization/invite/email, /onboarding/teams/invite/email), CSV upload (template + parsing), and Google Workspace (behind flag). - Shared components (EmailInviteForm, InviteOptions, RoleSelector) used across org and team invites. - **Refactors** - Updated org flow: Details → Brand → Teams → Invites; OnboardingLayout now supports dynamic step counts (org=4, team=3, personal=2). - UI polish (OnboardingCard header padding) and org-specific previews now replace generic views across details/brand/invites/teams; ensured org welcome modal takes precedence over personal. <sup>Written for commit d9b55c0b5505aa0d4ca1c4298a513bcd90606915. Summary will update automatically on new commits.</sup> <!-- End of auto-generated description by cubic. -->
80 lines
3.0 KiB
TypeScript
80 lines
3.0 KiB
TypeScript
"use client";
|
|
|
|
import classNames from "classnames";
|
|
import { signOut } from "next-auth/react";
|
|
import { Children, type ReactNode } from "react";
|
|
|
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|
import { Button } from "@calcom/ui/components/button";
|
|
import { Logo } from "@calcom/ui/components/logo";
|
|
|
|
type OnboardingLayoutProps = {
|
|
userEmail: string;
|
|
currentStep: number;
|
|
totalSteps: number;
|
|
children: ReactNode;
|
|
};
|
|
|
|
export const OnboardingLayout = ({ userEmail, currentStep, totalSteps, children }: OnboardingLayoutProps) => {
|
|
const { t } = useLocale();
|
|
|
|
const handleSignOut = () => {
|
|
signOut({ callbackUrl: "/auth/logout" });
|
|
};
|
|
|
|
// Extract children as array
|
|
const childrenArray = Children.toArray(children);
|
|
const column1 = childrenArray[0];
|
|
const column2 = childrenArray[1];
|
|
|
|
return (
|
|
<div className="bg-muted flex min-h-screen w-full flex-col items-center justify-between overflow-clip rounded-[12px] px-6 py-10">
|
|
{/* Logo and container - centered */}
|
|
<div className="flex w-full flex-1 flex-col items-center justify-center gap-8">
|
|
<Logo className="h-5 w-auto shrink-0" />
|
|
<div className="border-subtle bg-default grid w-full max-w-[1130px] grid-cols-1 gap-6 overflow-hidden rounded-2xl border px-12 py-10 xl:h-[690px] xl:grid-cols-[40%_1fr] xl:pl-10 xl:pr-0">
|
|
{/* Column 1 - Always visible, 40% on xl+ */}
|
|
<div className="flex min-h-0 flex-col">{column1}</div>
|
|
{/* Column 2 - Hidden on mobile, visible on xl+, 60% on xl+ */}
|
|
{column2 && (
|
|
<div className="hidden h-full max-h-full min-h-0 flex-col overflow-hidden xl:flex">{column2}</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Footer with progress dots and sign out */}
|
|
<div className="flex w-full flex-col items-center justify-center gap-4 px-10 py-8">
|
|
<div className="flex items-center gap-3">
|
|
{Array.from({ length: totalSteps }, (_, i) => i + 1).map((step) => {
|
|
const isCurrentStep = step === currentStep;
|
|
const isCompleted = step < currentStep;
|
|
return (
|
|
<div key={step} className="relative flex shrink-0 items-center justify-center">
|
|
<div
|
|
className={classNames("absolute rounded-full transition-all", {
|
|
"h-2 w-2": !isCurrentStep,
|
|
"h-3 w-3": isCurrentStep,
|
|
"bg-emphasis": isCompleted || isCurrentStep,
|
|
"bg-muted": !isCompleted && !isCurrentStep,
|
|
})}
|
|
/>
|
|
{(isCompleted || isCurrentStep) && (
|
|
<div
|
|
className={classNames("bg-emphasis absolute rounded-full", {
|
|
"h-1 w-1": !isCurrentStep,
|
|
"h-1.5 w-1.5": isCurrentStep,
|
|
})}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
<Button onClick={handleSignOut} color="minimal" className="text-subtle h-7">
|
|
{t("sign_out")}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|