fix(front): constrain 2FA sign-in screens and dedupe their shared shell (#22886)
## Problem On the card-less onboarding sign-in (`/welcome`), the **2FA verification** step renders with a full-viewport-wide submit button. The 2FA **verify** and **provision** forms hard-code `width: 100%` on their root `StyledForm`. That was harmless while `/welcome` rendered inside the `AuthModal` `medium` card, which bounded the width. Since #22398 removed v1 onboarding, `/welcome` renders card-less under `BlankLayout`, so nothing bounds those forms and they stretch to the full viewport. Other steps are **not** affected, which is why only 2FA looks wrong: - Sign-in form: root sets `width: ONBOARDING_CONTENT_BLOCK_WIDTH; max-width: 100%` -> capped at 440. - SSO selection / workspace-scope: base container (`min-width: 240`, no width) -> shrink-to-fit. - **2FA verify / provision: `width: 100%` -> full viewport.** ## Fix Cap the two 2FA forms at `ONBOARDING_CONTENT_BLOCK_WIDTH` (with `max-width: 100%`), so they sit in the same block as the sign-in page instead of forcing full-width. ## Refactor (same PR) The verify and provision components (both introduced together in #13141) duplicated their layout shell. Extracted the shared instruction-text and main-content blocks into `SignInUpTwoFactorAuthenticationStyles.ts`. The form container stays local to each component since the element differs (a `div` in provision, a `form` in verification). ## Verification - Reproduced the flex box-model at 1280px: `width:100%` root -> 1196px full-width button; `width: 440px` -> 440px centered button. - lint + format + typecheck pass on the changed files.
This commit is contained in:
+11
-30
@@ -4,6 +4,11 @@ import {
|
||||
SignInUpStep,
|
||||
signInUpStepState,
|
||||
} from '@/auth/states/signInUpStepState';
|
||||
import {
|
||||
StyledTwoFactorInstructions,
|
||||
StyledTwoFactorMainContent,
|
||||
} from '@/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationStyles';
|
||||
import { ONBOARDING_CONTENT_BLOCK_WIDTH } from '@/onboarding/constants/OnboardingContentBlockWidth';
|
||||
import { extractSecretFromOtpUri } from '@/settings/two-factor-authentication/utils/extractSecretFromOtpUri';
|
||||
import { styled } from '@linaria/react';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
@@ -20,36 +25,12 @@ import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
|
||||
|
||||
const QRCode = resolveCjsModuleDefaultExport(QRCodeModule);
|
||||
|
||||
const StyledMainContentContainer = styled.div`
|
||||
margin-bottom: ${themeCssVariables.spacing[8]};
|
||||
margin-top: ${themeCssVariables.spacing[4]};
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const StyledTextContainer = styled.div`
|
||||
align-items: center;
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
|
||||
margin-bottom: ${themeCssVariables.spacing[4]};
|
||||
max-width: 280px;
|
||||
text-align: center;
|
||||
|
||||
& > a {
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledForm = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
width: ${ONBOARDING_CONTENT_BLOCK_WIDTH}px;
|
||||
`;
|
||||
|
||||
const StyledCopySetupKeyLink = styled.button`
|
||||
@@ -94,13 +75,13 @@ export const SignInUpTwoFactorAuthenticationProvision = () => {
|
||||
<>
|
||||
<TwoFactorAuthenticationSetupEffect />
|
||||
<StyledForm>
|
||||
<StyledTextContainer>
|
||||
<StyledTwoFactorInstructions>
|
||||
<Trans>
|
||||
Use authenticator apps and browser extensions like 1Password, Authy,
|
||||
Microsoft Authenticator to generate one-time passwords
|
||||
</Trans>
|
||||
</StyledTextContainer>
|
||||
<StyledMainContentContainer>
|
||||
</StyledTwoFactorInstructions>
|
||||
<StyledTwoFactorMainContent>
|
||||
{!qrCode ? <Loader /> : <QRCode value={qrCode} />}
|
||||
{qrCode && (
|
||||
<StyledCopySetupKeyLink onClick={handleCopySetupKey}>
|
||||
@@ -108,7 +89,7 @@ export const SignInUpTwoFactorAuthenticationProvision = () => {
|
||||
<Trans>Copy Setup Key</Trans>
|
||||
</StyledCopySetupKeyLink>
|
||||
)}
|
||||
</StyledMainContentContainer>
|
||||
</StyledTwoFactorMainContent>
|
||||
<MainButton
|
||||
title={t`Next`}
|
||||
onClick={handleClick}
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
export const StyledTwoFactorMainContent = styled.div`
|
||||
margin-bottom: ${themeCssVariables.spacing[8]};
|
||||
margin-top: ${themeCssVariables.spacing[4]};
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
export const StyledTwoFactorInstructions = styled.div`
|
||||
align-items: center;
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
margin-bottom: ${themeCssVariables.spacing[4]};
|
||||
max-width: 280px;
|
||||
text-align: center;
|
||||
|
||||
& > a {
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
`;
|
||||
+11
-20
@@ -10,7 +10,12 @@ import {
|
||||
SignInUpStep,
|
||||
signInUpStepState,
|
||||
} from '@/auth/states/signInUpStepState';
|
||||
import {
|
||||
StyledTwoFactorInstructions,
|
||||
StyledTwoFactorMainContent,
|
||||
} from '@/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationStyles';
|
||||
import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken';
|
||||
import { ONBOARDING_CONTENT_BLOCK_WIDTH } from '@/onboarding/constants/OnboardingContentBlockWidth';
|
||||
import { useCaptcha } from '@/client-config/hooks/useCaptcha';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
@@ -25,17 +30,12 @@ import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
|
||||
const StyledMainContentContainer = styled.div`
|
||||
margin-bottom: ${themeCssVariables.spacing[8]};
|
||||
margin-top: ${themeCssVariables.spacing[4]};
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const StyledForm = styled.form`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
width: ${ONBOARDING_CONTENT_BLOCK_WIDTH}px;
|
||||
`;
|
||||
|
||||
const StyledSlot = styled.div<{ isActive: boolean }>`
|
||||
@@ -161,15 +161,6 @@ const StyledOTPContainer = styled.div`
|
||||
const StyledSlotGroup = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
const StyledTextContainer = styled.div`
|
||||
align-items: center;
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
|
||||
margin-bottom: ${themeCssVariables.spacing[4]};
|
||||
max-width: 280px;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const StyledActionBackLinkContainer = styled.div`
|
||||
margin: ${themeCssVariables.spacing[3]} 0 0;
|
||||
@@ -228,10 +219,10 @@ export const SignInUpTOTPVerification = () => {
|
||||
|
||||
return (
|
||||
<StyledForm onSubmit={form.handleSubmit(submitOTP)}>
|
||||
<StyledTextContainer>
|
||||
<StyledTwoFactorInstructions>
|
||||
<Trans>Paste the code below</Trans>
|
||||
</StyledTextContainer>
|
||||
<StyledMainContentContainer>
|
||||
</StyledTwoFactorInstructions>
|
||||
<StyledTwoFactorMainContent>
|
||||
{/* // oxlint-disable-next-line react/jsx-props-no-spreading */}
|
||||
<Controller
|
||||
name="otp"
|
||||
@@ -270,7 +261,7 @@ export const SignInUpTOTPVerification = () => {
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</StyledMainContentContainer>
|
||||
</StyledTwoFactorMainContent>
|
||||
<MainButton
|
||||
title={t`Submit`}
|
||||
type="submit"
|
||||
|
||||
Reference in New Issue
Block a user