import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation'; import { DOCUMENSO_CLOUD_ENTERPRISE_CTA_URL } from '@documenso/lib/constants/app'; import { formatAvatarUrl } from '@documenso/lib/utils/avatars'; import { cn } from '@documenso/ui/lib/utils'; import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar'; import { Trans } from '@lingui/react/macro'; import { AnimatePresence, motion, useReducedMotion } from 'framer-motion'; import { BadgeCheckIcon, MailIcon } from 'lucide-react'; import { BrandingLogoIcon } from '../branding-logo-icon'; import { EASE, POP, SPRING } from './motion'; import { SettingsUpsellCard } from './settings-upsell-card'; import { useTimedCycle } from './use-timed-cycle'; /** * Named sender identities cycled through while the preview is in its branded * state — one per branded cycle step, shown as the sender name and address. */ const BRANDED_SENDERS = [ { name: 'Support', email: 'support@example.com' }, { name: 'Team', email: 'hello@example.com' }, { name: 'Sales', email: 'sales@example.com' }, { name: 'Example', email: 'noreply@example.com' }, ]; /** * How long the initial unbranded (Documenso default) state is shown before * the first flip starts. Shown exactly once — the cycle never returns to it. */ const INITIAL_STATE_DURATION_MS = 2500; /** * How long each branded sender identity is shown before cycling to the next, * giving the viewer time to read the changed address. */ const BRANDED_STATE_DURATION_MS = 5000; /** * One duration per cycle step: the unbranded state first, then one step per * named sender identity, derived from the identity count so the two cannot * drift. */ const EMAIL_CYCLE_DURATIONS_MS = [INITIAL_STATE_DURATION_MS, ...BRANDED_SENDERS.map(() => BRANDED_STATE_DURATION_MS)]; export const EmailDomainsUpsell = () => { const organisation = useCurrentOrganisation(); const isReducedMotion = useReducedMotion(); // Loop from index 1: the unbranded Documenso intro plays exactly once, // then the cycle rotates through the branded senders only. const cycleIndex = useTimedCycle(EMAIL_CYCLE_DURATIONS_MS, 1); const isBranded = cycleIndex > 0; const brandedSender = BRANDED_SENDERS[cycleIndex - 1] ?? BRANDED_SENDERS[0]; const isStatic = isReducedMotion ?? false; return ( Enterprise} title={Unlock Email Domains} description={ Send documents from your own domain. Email domains are available on the Enterprise plan. } features={[ Send emails to recipients from your domain, Easy DNS setup with auto-generated DKIM and SPF records, Named senders with defaults per team, template or document, ]} ctaLabel={Contact Sales} ctaTo={DOCUMENSO_CLOUD_ENTERPRISE_CTA_URL} ctaExternal preview={
{isBranded ? ( ) : ( )} {isBranded ? Sending from your domain : Sending from app.documenso.com}
{/* * Remounts with its keyed parent on every cycle step so the * pop replays each change. Single-value spring (POP * overshoots past 1) instead of scale keyframes — keyframe * arrays are unreliable on strict-mode remounts. */} {isBranded ? ( {organisation.avatarImageId && ( )} {brandedSender.name[0]} ) : ( )}
{isBranded ? brandedSender.name : 'Documenso'} {/* Inside the keyed row so it exits with the name and pops back in on every cycle step. */} {isBranded && ( )}
{isBranded ? brandedSender.email : 'noreply@app.documenso.com'}

Please sign: Example.pdf

{organisation.name} has invited you to sign this document.

{/* * Keyed remount replays the sweep on every cycle step. No opacity * envelope — keyframe arrays are unreliable on strict-mode * remounts; both endpoints sit outside the overflow-hidden card, * so the clip provides the fade in/out instead. */}
} /> ); };