Fixing Singup sequence FLASHING💥 (#11371)

After investiagting the different options ([see related
issue](https://github.com/twentyhq/core-team-issues/issues/660#issuecomment-2766030972))
I decided to add a "Verify Component" and a to build a custom Layout for
this route.

Reason I cannot use the default one is to have all preloaded once the
user changes website and lands on the verify route.

Reason I did not modify the DefaultLayout to match our need is that is
would require many changes in order to avoid preloading states for our
specific usecase.

Fixes https://github.com/twentyhq/core-team-issues/issues/660

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Guillim
2025-04-04 17:25:15 +02:00
committed by GitHub
parent 609e06fd14
commit 10e140495c
22 changed files with 302 additions and 143 deletions

View File

@ -0,0 +1,100 @@
import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState';
import { useRecoilValue } from 'recoil';
import { Logo } from '@/auth/components/Logo';
import { Title } from '@/auth/components/Title';
import { DEFAULT_WORKSPACE_NAME } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceName';
import { useMemo } from 'react';
import { useWorkspaceFromInviteHash } from '@/auth/sign-in-up/hooks/useWorkspaceFromInviteHash';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { isNonEmptyString } from '@sniptt/guards';
import { motion } from 'framer-motion';
import { isDefined } from 'twenty-shared/utils';
import { Loader } from 'twenty-ui/feedback';
import { MainButton } from 'twenty-ui/input';
import { PublicWorkspaceDataOutput } from '~/generated/graphql';
const StyledContentContainer = styled(motion.div)`
margin-bottom: ${({ theme }) => theme.spacing(8)};
margin-top: ${({ theme }) => theme.spacing(4)};
`;
const StyledForm = styled.form`
align-items: center;
display: flex;
flex-direction: column;
width: 100%;
margin-top: ${({ theme }) => theme.spacing(10)};
`;
const StandardContent = ({
workspacePublicData,
signInUpForm,
title,
}: {
workspacePublicData: PublicWorkspaceDataOutput | null;
signInUpForm: JSX.Element | null;
title: string;
}) => {
return (
<>
<Logo secondaryLogo={workspacePublicData?.logo} />
<Title animate={false}>{title}</Title>
{signInUpForm}
</>
);
};
export const SignInUpLoading = () => {
const { t } = useLingui();
const workspacePublicData = useRecoilValue(workspacePublicDataState);
const { workspaceInviteHash, workspace: workspaceFromInviteHash } =
useWorkspaceFromInviteHash();
const title = useMemo(() => {
if (isDefined(workspaceInviteHash)) {
return `Join ${workspaceFromInviteHash?.displayName ?? ''} team`;
}
const workspaceName = !isDefined(workspacePublicData?.displayName)
? DEFAULT_WORKSPACE_NAME
: !isNonEmptyString(workspacePublicData?.displayName)
? t`Your Workspace`
: workspacePublicData?.displayName;
return t`Welcome to ${workspaceName}`;
}, [
workspaceFromInviteHash?.displayName,
workspaceInviteHash,
workspacePublicData?.displayName,
t,
]);
return (
<StandardContent
workspacePublicData={workspacePublicData}
signInUpForm={
<>
<p style={{ color: 'red', backgroundColor: 'blue' }}>
SignInUpLoading
</p>
<StyledContentContainer>
<StyledForm>
<MainButton
disabled={true}
title={t`Continue`}
type="submit"
variant={'primary'}
Icon={() => <Loader />}
fullWidth
/>
</StyledForm>
</StyledContentContainer>
</>
}
title={title}
/>
);
};

View File

@ -11,16 +11,16 @@ import { billingState } from '@/client-config/states/billingState';
import styled from '@emotion/styled';
import { Trans, useLingui } from '@lingui/react/macro';
import { useRecoilState, useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
import { Loader } from 'twenty-ui/feedback';
import { CardPicker, MainButton } from 'twenty-ui/input';
import { ActionLink, CAL_LINK } from 'twenty-ui/navigation';
import {
BillingPlanKey,
BillingPriceLicensedDto,
SubscriptionInterval,
useBillingBaseProductPricesQuery,
} from '~/generated/graphql';
import { isDefined } from 'twenty-shared/utils';
import { ActionLink, CAL_LINK } from 'twenty-ui/navigation';
import { CardPicker, MainButton } from 'twenty-ui/input';
import { Loader } from 'twenty-ui/feedback';
const StyledSubscriptionContainer = styled.div<{
withLongerMarginBottom: boolean;
@ -80,6 +80,10 @@ const StyledLinkGroup = styled.div`
}
`;
const StyledChooseYourPlanPlaceholder = styled.div`
height: 566px;
`;
export const ChooseYourPlan = () => {
const billing = useRecoilValue(billingState);
const { t } = useLingui();
@ -182,82 +186,87 @@ export const ChooseYourPlan = () => {
)?.baseProduct.name;
return (
isDefined(baseProductPrice) &&
isDefined(billing) && (
<>
<Title noMarginTop>
{hasWithoutCreditCardTrialPeriod
? t`Choose your Trial`
: t`Get your subscription`}
</Title>
{hasWithoutCreditCardTrialPeriod ? (
<SubTitle>
<Trans>Cancel anytime</Trans>
</SubTitle>
) : (
withCreditCardTrialPeriod && (
<>
{isDefined(baseProductPrice) && isDefined(billing) ? (
<>
<Title noMarginTop>
{hasWithoutCreditCardTrialPeriod
? t`Choose your Trial`
: t`Get your subscription`}
</Title>
{hasWithoutCreditCardTrialPeriod ? (
<SubTitle>
{t`Enjoy a ${withCreditCardTrialPeriodDuration}-days free trial`}
<Trans>Cancel anytime</Trans>
</SubTitle>
)
)}
<StyledSubscriptionContainer
withLongerMarginBottom={!hasWithoutCreditCardTrialPeriod}
>
<StyledSubscriptionPriceContainer>
<SubscriptionPrice
type={baseProductPrice.recurringInterval}
price={baseProductPrice.unitAmount / 100}
/>
</StyledSubscriptionPriceContainer>
<StyledBenefitsContainer>
{benefits.map((benefit) => (
<SubscriptionBenefit key={benefit}>{benefit}</SubscriptionBenefit>
))}
</StyledBenefitsContainer>
</StyledSubscriptionContainer>
{hasWithoutCreditCardTrialPeriod && (
<StyledChooseTrialContainer>
{billing.trialPeriods.map((trialPeriod) => (
<CardPicker
checked={
billingCheckoutSession.requirePaymentMethod ===
trialPeriod.isCreditCardRequired
}
handleChange={handleTrialPeriodChange(
trialPeriod.isCreditCardRequired,
)}
key={trialPeriod.duration}
>
<TrialCard
duration={trialPeriod.duration}
withCreditCard={trialPeriod.isCreditCardRequired}
/>
</CardPicker>
))}
</StyledChooseTrialContainer>
)}
<MainButton
title={t`Continue`}
onClick={handleCheckoutSession}
width={200}
Icon={() => isSubmitting && <Loader />}
disabled={isSubmitting}
/>
<StyledLinkGroup>
<ActionLink onClick={signOut}>
<Trans>Log out</Trans>
</ActionLink>
<span />
<ActionLink onClick={handleSwitchPlan(alternatePlan)}>
<Trans>Switch to {alternatePlanName}</Trans>
</ActionLink>
<span />
<ActionLink href={CAL_LINK} target="_blank" rel="noreferrer">
<Trans>Book a Call</Trans>
</ActionLink>
</StyledLinkGroup>
</>
)
) : (
withCreditCardTrialPeriod && (
<SubTitle>
{t`Enjoy a ${withCreditCardTrialPeriodDuration}-days free trial`}
</SubTitle>
)
)}
<StyledSubscriptionContainer
withLongerMarginBottom={!hasWithoutCreditCardTrialPeriod}
>
<StyledSubscriptionPriceContainer>
<SubscriptionPrice
type={baseProductPrice.recurringInterval}
price={baseProductPrice.unitAmount / 100}
/>
</StyledSubscriptionPriceContainer>
<StyledBenefitsContainer>
{benefits.map((benefit) => (
<SubscriptionBenefit key={benefit}>
{benefit}
</SubscriptionBenefit>
))}
</StyledBenefitsContainer>
</StyledSubscriptionContainer>
{hasWithoutCreditCardTrialPeriod && (
<StyledChooseTrialContainer>
{billing.trialPeriods.map((trialPeriod) => (
<CardPicker
checked={
billingCheckoutSession.requirePaymentMethod ===
trialPeriod.isCreditCardRequired
}
handleChange={handleTrialPeriodChange(
trialPeriod.isCreditCardRequired,
)}
key={trialPeriod.duration}
>
<TrialCard
duration={trialPeriod.duration}
withCreditCard={trialPeriod.isCreditCardRequired}
/>
</CardPicker>
))}
</StyledChooseTrialContainer>
)}
<MainButton
title={t`Continue`}
onClick={handleCheckoutSession}
width={200}
Icon={() => isSubmitting && <Loader />}
disabled={isSubmitting}
/>
<StyledLinkGroup>
<ActionLink onClick={signOut}>
<Trans>Log out</Trans>
</ActionLink>
<span />
<ActionLink onClick={handleSwitchPlan(alternatePlan)}>
<Trans>Switch to {alternatePlanName}</Trans>
</ActionLink>
<span />
<ActionLink href={CAL_LINK} target="_blank" rel="noreferrer">
<Trans>Book a Call</Trans>
</ActionLink>
</StyledLinkGroup>
</>
) : (
<StyledChooseYourPlanPlaceholder />
)}
</>
);
};