diff --git a/apps/learn-card-app/src/assets/sass/login.scss b/apps/learn-card-app/src/assets/sass/login.scss index 234ad5b819..d404b41924 100644 --- a/apps/learn-card-app/src/assets/sass/login.scss +++ b/apps/learn-card-app/src/assets/sass/login.scss @@ -38,6 +38,27 @@ } } +.PhoneInput.login-phone-input.gameflow { + .PhoneInputInput { + background-color: var(--ion-color-grayscale-100); + border: 1px solid var(--ion-color-grayscale-200); + color: var(--ion-color-grayscale-900); + + &::placeholder { + color: var(--ion-color-grayscale-500); + } + } + + .PhoneInputCountry { + background-color: var(--ion-color-grayscale-100); + border: 1px solid var(--ion-color-grayscale-200); + + .PhoneInputCountryIcon { + border: solid 1px var(--ion-color-grayscale-400); + } + } +} + .react-code-input:not(.small) > input:not([value='']) { background-color: white; transition: background-color 0.3s ease-in; @@ -45,12 +66,15 @@ .react-code-input.small { display: flex !important; - justify-content: space-between; - padding-left: 2px; - padding-right: 2px; + width: 100%; + justify-content: center; + flex-wrap: nowrap; + gap: 8px; & > input { - width: 14%; + flex: 0 0 auto; + width: clamp(34px, 9vw, 46px); + min-width: 0; border-radius: 12px; font-size: 24px; height: 60px; @@ -68,11 +92,56 @@ } @media (max-width: 479px) { - width: 14%; + width: clamp(30px, 10vw, 44px); } @media (max-width: 380px) { - width: 12.25%; + width: clamp(28px, 10vw, 40px); + } + } +} + +.react-code-input.gameflow:not(.small) { + & > input { + background-color: var(--ion-color-grayscale-100); + border: 1px solid var(--ion-color-grayscale-200); + color: var(--ion-color-grayscale-900); + + &:focus { + border: 1px solid var(--ion-color-grayscale-300); + background-color: white; + } + } +} + +.react-code-input.small.gameflow { + display: grid !important; + grid-template-columns: repeat(6, minmax(0, 1fr)); + width: 100%; + max-width: 100%; + gap: 8px; + + @media (max-width: 420px) { + gap: 6px; + } + + @media (max-width: 360px) { + gap: 5px; + } + + & > input { + flex: initial; + width: 100% !important; + min-width: 0; + background-color: var(--ion-color-grayscale-100); + border: 1px solid var(--ion-color-grayscale-200); + color: var(--ion-color-grayscale-900); + font-size: 20px; + height: 56px; + + &:focus { + border: 1px solid var(--ion-color-grayscale-300); + background-color: white; } } } diff --git a/apps/learn-card-app/src/components/learncard/AccountSwitcherModal.tsx b/apps/learn-card-app/src/components/learncard/AccountSwitcherModal.tsx index a4fd6ef304..ba39b28621 100644 --- a/apps/learn-card-app/src/components/learncard/AccountSwitcherModal.tsx +++ b/apps/learn-card-app/src/components/learncard/AccountSwitcherModal.tsx @@ -254,7 +254,7 @@ const AccountSwitcherModal: React.FC = ({ className="shrink-0 w-full max-w-[400px] py-[10px] px-[15px] text-[20px] bg-grayscale-900 rounded-full font-notoSans text-white shadow-button-bottom disabled:opacity-60" disabled={!familyCredential?.boostId || isSwitching} > - Add Player + Add New Player - {socialLogins.map(socialLogin => { - const { id, src, onClick, alt } = socialLogin; - return ( - - ); - })} - + {showSocialLogins && ( +
+ + {socialLogins.map(socialLogin => { + const { id, src, onClick, alt } = socialLogin; + return ( + + ); + })} +
+ )} {LoginTypeForm} -
-
-
- - +
diff --git a/apps/learn-card-app/src/pages/launchPad/PostConsentFlowDataFeed.tsx b/apps/learn-card-app/src/pages/launchPad/PostConsentFlowDataFeed.tsx index 773f4a6b9a..074d9f62dc 100644 --- a/apps/learn-card-app/src/pages/launchPad/PostConsentFlowDataFeed.tsx +++ b/apps/learn-card-app/src/pages/launchPad/PostConsentFlowDataFeed.tsx @@ -57,7 +57,7 @@ const PostConsentFlowDataFeed = () => { ); return ( -
+
diff --git a/apps/learn-card-app/src/pages/login/forms/EmailForm.tsx b/apps/learn-card-app/src/pages/login/forms/EmailForm.tsx index 7e4a8f0c6e..a352846aba 100644 --- a/apps/learn-card-app/src/pages/login/forms/EmailForm.tsx +++ b/apps/learn-card-app/src/pages/login/forms/EmailForm.tsx @@ -45,6 +45,14 @@ type EmailFormProps = { buttonClassName?: string; suppressRedirect?: boolean; customRedirectUrl?: string; + resetRedirectPath?: string | null; + smallVerificationInput?: boolean; + emailInputClassName?: string; + emailInputVariant?: 'default' | 'appStore'; + emailErrorPlacement?: 'inline' | 'below'; + startOverClassNameOverride?: string; + resendCodeButtonClassNameOverride?: string; + verificationCodeInputClassName?: string; setShowSocialLogins: React.Dispatch>; showSocialLogins: boolean; }; @@ -56,6 +64,14 @@ const EmailForm: React.FC = ({ buttonClassName = '', suppressRedirect = false, customRedirectUrl = '', + resetRedirectPath = '/login', + smallVerificationInput = false, + emailInputClassName, + emailInputVariant = 'default', + emailErrorPlacement = 'inline', + startOverClassNameOverride, + resendCodeButtonClassNameOverride, + verificationCodeInputClassName, setShowSocialLogins, showSocialLogins, }) => { @@ -71,9 +87,9 @@ const EmailForm: React.FC = ({ const verificationEmail = redirectStore.get.email(); const shouldVerifyCode = Boolean(query.get('verifyCode') || verificationEmail); - const [email, setEmail] = useState(''); + const [email, setEmail] = useState(''); const [code, setCode] = useState(''); - const [password, setPassword] = useState(''); + const [password, setPassword] = useState(''); const [currentStep, setCurrentStep] = useState(EmailFormStepsEnum.email); const [errors, setErrors] = useState>({}); @@ -209,7 +225,7 @@ const EmailForm: React.FC = ({ if (enableMagicLinkLogin) { try { setIsLoading(true); - await sendSignInLink(email as string, customRedirectUrl); + await sendSignInLink(email, customRedirectUrl); setIsLoading(false); } catch (e) { setIsLoading(false); @@ -218,8 +234,8 @@ const EmailForm: React.FC = ({ } else { try { setIsLoading(true); - await sendLoginVerificationCode({ email: email as string }); - redirectStore.set.email(email as string); + await sendLoginVerificationCode({ email }); + redirectStore.set.email(email); setCurrentStep(EmailFormStepsEnum.verification); setIsLoading(false); } catch (e) { @@ -271,7 +287,9 @@ const EmailForm: React.FC = ({ const resetForm = () => { setCurrentStep(EmailFormStepsEnum.email); - history.replace('/login'); + if (resetRedirectPath !== null) { + history.replace(resetRedirectPath); + } redirectStore.set.email(null); setEmail(''); setPassword(''); @@ -282,19 +300,47 @@ const EmailForm: React.FC = ({ let disabled = isLoading; if (currentStep === EmailFormStepsEnum.email) { formTitle = null; + + const defaultEmailInputClassName = + 'bg-emerald-600 text-white placeholder:text-white white-placeholder'; + const resolvedEmailInputClassName = emailInputClassName ?? defaultEmailInputClassName; + + const emailError = errors.email?.[0]; + + const emailInputBaseClassName = + emailInputVariant === 'appStore' + ? 'w-full px-4 py-3 bg-grayscale-100 border rounded-[15px] font-medium text-base text-grayscale-900 placeholder:text-grayscale-500 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500' + : 'rounded-[15px] w-full ion-padding font-medium tracking-widest text-base focus:outline-none focus:ring-0 focus:border-transparent'; + + const emailInputErrorClassName = + emailInputVariant === 'appStore' + ? emailError + ? 'border-red-300' + : 'border-grayscale-200' + : emailError + ? 'login-input-email-error' + : ''; + activeStep = ( -
+
setEmail(e.target.value)} value={email} type="text" /> - {errors.email &&

{errors.email}

} + {emailError && + (emailErrorPlacement === 'below' ? ( +

{emailError}

+ ) : ( +

{emailError}

+ ))}
); // buttonTitle = 'Continue'; @@ -309,7 +355,10 @@ const EmailForm: React.FC = ({ } text-center`} > Enter verification code or{' '} - + start over

@@ -325,13 +374,13 @@ const EmailForm: React.FC = ({ fields={6} type="text" onChange={e => setCode(e)} - className={`react-code-input ${ - errors.code || codeError ? 'react-code-input-error' : '' - }`} + className={`react-code-input ${smallVerificationInput ? 'small' : ''} ${ + verificationCodeInputClassName ?? '' + } ${errors.code || codeError ? 'react-code-input-error' : ''}`} /> - {errors?.code && ( + {errors?.code?.[0] && (

- {errors?.code} + {errors?.code?.[0]}

)} {codeError && ( @@ -350,7 +399,7 @@ const EmailForm: React.FC = ({ className="bg-grayscale-100 text-grayscale-800 rounded-[15px] ion-padding font-medium tracking-widest text-base" placeholder="Password" // todo: add view password toggle - onIonInput={e => setPassword(e.detail.value)} + onIonInput={e => setPassword(e.detail.value ?? '')} value={password} type="password" /> @@ -370,7 +419,7 @@ const EmailForm: React.FC = ({ className="bg-grayscale-100 text-grayscale-800 rounded-[15px] ion-padding font-medium tracking-widest text-base" placeholder="Password" // todo: add view password toggle - onIonInput={e => setPassword(e.detail.value)} + onIonInput={e => setPassword(e.detail.value ?? '')} value={password} type="password" /> @@ -425,14 +474,20 @@ const EmailForm: React.FC = ({ e.stopPropagation(); handleResendCode(); }} - className="text-white font-bold mt-4 border-b-white border-solid border-b-[1px]" + className={ + resendCodeButtonClassNameOverride ?? + 'text-white font-bold mt-4 border-b-white border-solid border-b-[1px]' + } > {resendCodeButtonText} ) : ( diff --git a/apps/learn-card-app/src/pages/login/forms/PhoneForm.tsx b/apps/learn-card-app/src/pages/login/forms/PhoneForm.tsx index 90830aec5a..bd26f5b3ec 100644 --- a/apps/learn-card-app/src/pages/login/forms/PhoneForm.tsx +++ b/apps/learn-card-app/src/pages/login/forms/PhoneForm.tsx @@ -35,6 +35,10 @@ type PhoneFormProps = { formTitleClassNameOverride?: string; buttonClassName?: string; smallVerificationInput?: boolean; + phoneInputClassNameOverride?: string; + verificationCodeInputClassName?: string; + startOverClassNameOverride?: string; + resendCodeButtonClassNameOverride?: string; setShowSocialLogins: React.Dispatch>; showSocialLogins: boolean; }; @@ -44,6 +48,10 @@ const PhoneForm: React.FC = ({ formTitleClassNameOverride, buttonClassName, smallVerificationInput, + phoneInputClassNameOverride, + verificationCodeInputClassName, + startOverClassNameOverride, + resendCodeButtonClassNameOverride, setShowSocialLogins, showSocialLogins, }) => { @@ -266,13 +274,13 @@ const PhoneForm: React.FC = ({ defaultCountry="US" value={phone} onChange={setPhone} - className={`login-phone-input ${ + className={`login-phone-input ${phoneInputClassNameOverride ?? ''} ${ errors?.phone || error ? 'login-phone-input-error' : '' }`} /> - {errors?.phone && ( + {errors?.phone?.[0] && (

- {errors?.phone} + {errors?.phone?.[0]}

)} {error && ( @@ -290,7 +298,10 @@ const PhoneForm: React.FC = ({ } text-center`} > Enter verification code or{' '} - + start over

@@ -307,12 +318,12 @@ const PhoneForm: React.FC = ({ type="text" onChange={e => setCode(e)} className={`react-code-input ${smallVerificationInput ? 'small' : ''} ${ - errors.code || codeError ? 'react-code-input-error' : '' - }`} + verificationCodeInputClassName ?? '' + } ${errors.code || codeError ? 'react-code-input-error' : ''}`} /> - {errors?.code && ( + {errors?.code?.[0] && (

- {errors?.code} + {errors?.code?.[0]}

)} {codeError && ( @@ -320,7 +331,7 @@ const PhoneForm: React.FC = ({ )} ); - buttonTitle = buttonTitle = isLoading ? 'Verifying...' : 'Verify'; + buttonTitle = isLoading ? 'Verifying...' : 'Verify'; } else if (currentStep === PhoneFormStepsEnum.passwordExistingUser) { formTitle =

Password

; activeStep = ( @@ -398,14 +409,20 @@ const PhoneForm: React.FC = ({ e.stopPropagation(); handleOnClick(e, true); }} - className="text-white font-bold mt-4 border-b-white border-solid border-b-[1px]" + className={ + resendCodeButtonClassNameOverride ?? + 'text-white font-bold mt-4 border-b-white border-solid border-b-[1px]' + } > {resendCodeButtonText} ) : ( diff --git a/apps/learn-card-app/tests/game-flow.page.ts b/apps/learn-card-app/tests/game-flow.page.ts index d6c1809d17..3f0a8fd95a 100644 --- a/apps/learn-card-app/tests/game-flow.page.ts +++ b/apps/learn-card-app/tests/game-flow.page.ts @@ -143,7 +143,7 @@ export class GameFlowPage { await expect(this.page.getByText("Who's Playing?")).toBeVisible(); await expect( - this.page.locator('footer').getByRole('button', { name: 'Add Player' }) + this.page.locator('footer').getByRole('button', { name: 'Add New Player' }) ).toBeVisible(); await expect( this.page @@ -168,7 +168,7 @@ export class GameFlowPage { } async clickAddPlayer() { - await this.page.getByRole('button', { name: 'Add Player' }).click(); + await this.page.getByRole('button', { name: 'Add New Player' }).click(); } async validateConfirmationPage() {