Files
calendar/packages/features/shell/components/WelcomeToCalcomModal.tsx
T
sean-brydonandGitHub 956f81fab3 chore: tidy up onboarding with new animations and illustrations (#25124)
<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Revamps onboarding with subtle route transitions, new org/teams illustrations, and clearer invite flows with real-time previews. Adds “Skip for now” on invites and guardrails to avoid incomplete team creation.

- **New Features**
  - Added email invite pages for organizations and teams with shared EmailInviteForm and RoleSelector.
  - Built a live invite browser preview that renders a 3×3 grid and updates as you type; works for orgs and teams.
  - Applied route-keyed framer-motion transitions to layout, cards, and browser views for smoother page changes.
  - Continuation prompt now detects saved org or team and routes to the right next step; localized copy.
  - Added loading state to plan selection to prevent double navigation.
  - Refreshed Organizations welcome modal with a new animated ring illustration and better scrolling.
  - Added “Skip for now” to org and team invite-by-email steps to proceed without invites.
  - Calendar browser preview now focuses on a time window around “now” for a more realistic demo.
  - Added an optional floating footer to keep actions visible on long, scrollable lists.

- **Refactors and Fixes**
  - Unified invite browser and replaced the org preview on the org email step.
  - Split and simplified legacy invite views; team and org routes are cleaner and easier to extend.
  - Streamlined the team invite step by removing a redundant action; non-admins are redirected to the email invite page.
  - Prevented creating teams with empty details by redirecting back to team details.
  - Minor UI cleanup: tighter borders, padding, and mobile max-heights across onboarding screens.
  - Hide team select on org invites when no teams exist.

<sup>Written for commit ae7f5277ab998560ae6e2f3f9144852a7b4959a7. Summary will update automatically on new commits.</sup>

<!-- End of auto-generated description by cubic. -->
2025-11-20 10:20:38 +00:00

110 lines
4.1 KiB
TypeScript

"use client";
import { APP_NAME } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button } from "@calcom/ui/components/button";
import { Dialog, DialogContent } from "@calcom/ui/components/dialog";
import { Icon } from "@calcom/ui/components/icon";
import { Logo } from "@calcom/ui/components/logo";
import { useWelcomeToCalcomModal } from "../hooks/useWelcomeToCalcomModal";
const features = ["unlimited_event_types", "workflows_feature", "and_more"];
export function WelcomeToCalcomModal() {
const { t } = useLocale();
const { isOpen, closeModal } = useWelcomeToCalcomModal();
const LARGE = { outer: 48, icon: 24 };
const RINGS = [60, 95, 130]; // Ring radii in px
const RING_STROKE = 1;
return (
<Dialog open={isOpen} onOpenChange={(open) => !open && closeModal()}>
<DialogContent size="default" className="!p-0">
<div className="flex flex-col gap-4 p-6">
<div className="flex flex-col items-center gap-1">
<Logo className="h-10 w-auto" />
</div>
{/* User illustration with rings */}
<div
className="relative mx-auto"
style={{
width: 320,
height: 220,
maskImage: "radial-gradient(ellipse 100% 60% at center, black 30%, transparent 85%)",
WebkitMaskImage: "radial-gradient(ellipse 100% 60% at center, black 30%, transparent 85%)",
}}>
{/* Center origin */}
<div className="absolute left-1/2 top-1/2" style={{ transform: "translate(-50%, -50%)" }}>
{/* Rings */}
{RINGS.map((r, i) => (
<div
key={i}
className="pointer-events-none absolute rounded-full border"
style={{
width: 2 * r,
height: 2 * r,
left: `calc(50% - ${r}px)`,
top: `calc(50% - ${r}px)`,
borderWidth: RING_STROKE,
borderColor: "var(--cal-border-subtle)",
}}
/>
))}
{/* Central user icon */}
<div
className="from-default to-muted border-subtle absolute flex items-center justify-center rounded-full border bg-gradient-to-b shadow-sm"
style={{
width: LARGE.outer,
height: LARGE.outer,
left: "50%",
top: "50%",
transform: "translate(-50%, -50%)",
}}>
<Icon
name="user"
className="text-emphasis opacity-70"
style={{ width: LARGE.icon, height: LARGE.icon }}
/>
</div>
</div>
</div>
<div className="mb-2 flex flex-col gap-2 text-center">
<h2 className="font-cal text-emphasis text-2xl leading-none">
{t("welcome_to_calcom", { appName: APP_NAME })}
</h2>
<p className="text-default text-sm leading-normal">{t("personal_welcome_description")}</p>
</div>
<div className="mb-2 flex flex-col gap-3">
{features.map((feature) => (
<div key={feature} className="flex items-start gap-2">
<Icon name="check" className="text-muted mt-0.5 h-4 w-4 flex-shrink-0" />
<span className="text-default text-sm font-medium leading-tight">{t(feature)}</span>
</div>
))}
</div>
</div>
<div className="bg-muted border-subtle mt-6 flex items-center justify-between rounded-b-2xl border-t px-8 py-6">
<Button
color="minimal"
href="https://cal.com/docs"
target="_blank"
EndIcon="external-link"
className="pointer-events-none opacity-0">
{t("learn_more")}
</Button>
<Button color="primary" onClick={closeModal}>
{t("continue")}
</Button>
</div>
</DialogContent>
</Dialog>
);
}