From 7947b1b84383b33aa3835eaffc83838ad11dc5b2 Mon Sep 17 00:00:00 2001 From: "Abdullah." <125115953+mabdullahabaid@users.noreply.github.com> Date: Tue, 21 Apr 2026 18:03:17 +0500 Subject: [PATCH] Fix self-hosting pricing page design. (#19930) Resolve: https://discord.com/channels/1130383047699738754/1496108238909739079 --- .../activate/EnterpriseActivateClient.tsx | 220 ++++++++++++------ .../src/app/enterprise/activate/page.tsx | 100 ++++++-- 2 files changed, 229 insertions(+), 91 deletions(-) diff --git a/packages/twenty-website-new/src/app/enterprise/activate/EnterpriseActivateClient.tsx b/packages/twenty-website-new/src/app/enterprise/activate/EnterpriseActivateClient.tsx index fb23a66e239..044c0642310 100644 --- a/packages/twenty-website-new/src/app/enterprise/activate/EnterpriseActivateClient.tsx +++ b/packages/twenty-website-new/src/app/enterprise/activate/EnterpriseActivateClient.tsx @@ -1,6 +1,12 @@ 'use client'; +import { + BaseButton, + buttonBaseStyles, +} from '@/design-system/components/Button/BaseButton'; +import { Body, Heading } from '@/design-system/components'; import { theme } from '@/theme'; +import { css } from '@linaria/core'; import { styled } from '@linaria/react'; import { useSearchParams } from 'next/navigation'; import { useEffect, useState } from 'react'; @@ -11,21 +17,10 @@ type ActivationResult = { subscriptionId: string; }; -const PageWrap = styled.div` - box-sizing: border-box; - margin-left: auto; - margin-right: auto; - margin-top: ${theme.spacing(12)}; - max-width: 700px; - min-height: 60vh; - padding-left: ${theme.spacing(4)}; - padding-right: ${theme.spacing(4)}; -`; - -const Title = styled.h1` - font-size: ${theme.font.size(8)}; - font-weight: 600; - margin-bottom: ${theme.spacing(4)}; +const ContentStack = styled.div` + display: flex; + flex-direction: column; + gap: ${theme.spacing(6)}; `; const ErrorBox = styled.div` @@ -33,27 +28,27 @@ const ErrorBox = styled.div` border: 1px solid ${theme.colors.accent.pink[70]}; border-radius: ${theme.radius(2)}; color: ${theme.colors.accent.pink[100]}; + font-family: ${theme.font.family.sans}; + font-size: ${theme.font.size(4)}; + line-height: 1.55; padding: ${theme.spacing(4)}; `; -const SuccessLead = styled.p` +const successLeadClassName = css` color: ${theme.colors.accent.green[100]}; - margin-bottom: ${theme.spacing(4)}; `; const LicenseeRow = styled.div` - margin-bottom: ${theme.spacing(6)}; + align-items: baseline; + display: flex; + flex-wrap: wrap; + gap: ${theme.spacing(1)}; `; -const KeyLabel = styled.div` - font-weight: 600; - margin-bottom: ${theme.spacing(2)}; -`; - -const KeyHint = styled.p` - color: ${theme.colors.primary.text[60]}; - font-size: ${theme.font.size(3)}; - margin-bottom: ${theme.spacing(2)}; +const KeySection = styled.div` + display: flex; + flex-direction: column; + gap: ${theme.spacing(2)}; `; const KeyBlock = styled.div` @@ -69,34 +64,63 @@ const KeyBlock = styled.div` word-break: break-all; `; -const CopyButton = styled.button<{ $copied: boolean }>` - background-color: ${({ $copied }) => - $copied ? theme.colors.accent.green[100] : theme.colors.primary.text[100]}; - border: none; - border-radius: ${theme.radius(1)}; - color: ${theme.colors.primary.background[100]}; - cursor: pointer; - font-size: ${theme.font.size(2)}; - padding: ${theme.spacing(2)} ${theme.spacing(3)}; +const CopyTrigger = styled.button<{ $copied: boolean }>` + ${buttonBaseStyles} position: absolute; right: ${theme.spacing(2)}; top: ${theme.spacing(2)}; + + ${({ $copied }) => + $copied + ? ` + & [data-slot='button-base-shape'] path, + & [data-slot='button-base-shape'] rect { + fill: ${theme.colors.accent.green[100]} !important; + } + + & [data-slot='button-hover-fill'] { + opacity: 0 !important; + pointer-events: none; + } + + & [data-slot='button-label'] { + color: ${theme.colors.primary.background[100]} !important; + } + + &:is(:hover, :focus-visible) [data-slot='button-label'] { + color: ${theme.colors.primary.background[100]} !important; + } + ` + : ''} `; const NextStepsBox = styled.div` background-color: ${theme.colors.primary.border[5]}; - border: 1px solid ${theme.colors.accent.blue[70]}; + border: 1px solid ${theme.colors.primary.border[20]}; + border-left: 3px solid ${theme.colors.highlight[100]}; border-radius: ${theme.radius(2)}; - margin-top: ${theme.spacing(8)}; + display: flex; + flex-direction: column; + gap: ${theme.spacing(2)}; padding: ${theme.spacing(4)}; `; const NextStepsList = styled.ol` - line-height: 1.75; - margin-top: ${theme.spacing(2)}; + display: flex; + flex-direction: column; + gap: ${theme.spacing(2)}; + list-style-position: outside; + margin: 0; padding-left: ${theme.spacing(5)}; `; +const nextStepItemClassName = css` + &::marker { + color: ${theme.colors.primary.text[60]}; + font-weight: ${theme.font.weight.medium}; + } +`; + export function EnterpriseActivateClient() { const searchParams = useSearchParams(); const sessionId = searchParams.get('session_id'); @@ -159,53 +183,105 @@ export function EnterpriseActivateClient() { }; return ( - - {'Enterprise activation'} - - {loading &&

{'Activating your enterprise license…'}

} + + {loading && ( + + )} {error !== null && {error}} {result !== null && ( -
- - {'Your enterprise license has been activated successfully.'} - + <> + - {'Licensee:'} {result.licensee} + + - {'Your enterprise key'} - - { - 'Copy this key and paste it into your Twenty self-hosted instance settings.' - } - + + + - - {result.enterpriseKey} - void handleCopy()} - type="button" - > - {copied ? 'Copied!' : 'Copy'} - - + + {result.enterpriseKey} + void handleCopy()} + type="button" + > + + + + - {'Next steps'} + -
  • {'Copy the enterprise key above.'}
  • -
  • - {'Open your Twenty self-hosted instance Settings → Enterprise.'} +
  • + +
  • +
  • + +
  • +
  • +
  • -
  • {'Paste the key and click Activate.'}
  • -
    + )} -
    + ); } diff --git a/packages/twenty-website-new/src/app/enterprise/activate/page.tsx b/packages/twenty-website-new/src/app/enterprise/activate/page.tsx index 2dcd23c1af6..bfd80968fe4 100644 --- a/packages/twenty-website-new/src/app/enterprise/activate/page.tsx +++ b/packages/twenty-website-new/src/app/enterprise/activate/page.tsx @@ -1,20 +1,16 @@ +import { MENU_DATA } from '@/app/_constants'; +import { EnterpriseActivateClient } from '@/app/enterprise/activate/EnterpriseActivateClient'; +import { Body, Container, Eyebrow } from '@/design-system/components'; +import type { HeadingType } from '@/design-system/components/Heading/types/Heading'; +import { Pages } from '@/enums/pages'; +import { fetchCommunityStats } from '@/lib/community/fetch-community-stats'; +import { mergeSocialLinkLabels } from '@/lib/community/merge-social-link-labels'; +import { Hero } from '@/sections/Hero/components'; +import { Menu } from '@/sections/Menu/components'; import { theme } from '@/theme'; -import { css } from '@linaria/core'; import type { Metadata } from 'next'; import { Suspense } from 'react'; - -import { EnterpriseActivateClient } from './EnterpriseActivateClient'; - -const activateFallbackClassName = css` - box-sizing: border-box; - color: ${theme.colors.primary.text[60]}; - margin-left: auto; - margin-right: auto; - margin-top: ${theme.spacing(12)}; - max-width: 700px; - padding-left: ${theme.spacing(4)}; - padding-right: ${theme.spacing(4)}; -`; +import { styled } from '@linaria/react'; export const metadata: Metadata = { title: 'Enterprise activation | Twenty', @@ -22,16 +18,82 @@ export const metadata: Metadata = { 'Complete activation for your Twenty self-hosted enterprise license.', }; +const ENTERPRISE_ACTIVATE_HEADING: HeadingType[] = [ + { text: 'Enterprise ', fontFamily: 'serif' }, + { text: 'activation', fontFamily: 'sans' }, +]; + +const ENTERPRISE_ACTIVATE_BODY = { + text: 'Your checkout is complete. Follow the steps below to copy your license key into your Twenty instance.', +}; + +const ActivatePageContent = styled.section` + background-color: ${theme.colors.primary.background[100]}; + flex: 1; + padding-bottom: ${theme.spacing(20)}; + padding-top: ${theme.spacing(8)}; + width: 100%; +`; + +const ActivateContentInner = styled.div` + margin-left: auto; + margin-right: auto; + max-width: 640px; + width: 100%; +`; + function EnterpriseActivateFallback() { return ( -
    {'Loading activation…'}
    + ); } -export default function EnterpriseActivatePage() { +export default async function EnterpriseActivatePage() { + const stats = await fetchCommunityStats(); + const menuSocialLinks = mergeSocialLinkLabels(MENU_DATA.socialLinks, stats); + return ( - }> - - + <> + + + + + + + + + + + + + + + + + }> + + + + + + ); }