diff --git a/src/ReturnUserExperience/ReturnUserExperience.tsx b/src/ReturnUserExperience/ReturnUserExperience.tsx index 7e594ef3d7..ae23b12137 100644 --- a/src/ReturnUserExperience/ReturnUserExperience.tsx +++ b/src/ReturnUserExperience/ReturnUserExperience.tsx @@ -1,8 +1,9 @@ import React from 'react' -import { useSelector } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import styles from './returnUserExperience.module.css' import RuxInfo from 'src/ReturnUserExperience/RuxInfo' +import { RuxPhoneNumber } from 'src/ReturnUserExperience/RuxPhoneNumber' import { Stack } from '@mui/material' import { Icon } from '@mxenabled/mxui' @@ -12,6 +13,8 @@ import useAnalyticsEvent from 'src/hooks/useAnalyticsEvent' import { __ } from 'src/utilities/Intl' import { AnalyticEvents } from 'src/const/Analytics' import { RootState } from 'src/redux/Store' +import { ActionTypes } from 'src/redux/actions/Connect' +import { selectInitialConfig } from 'src/redux/reducers/configSlice' import { ClientLogo } from 'src/components/ClientLogo' export const RUXViews = { @@ -23,7 +26,10 @@ export const RUXViews = { export const ReturnUserExperience = React.forwardRef(() => { const [view, setView] = React.useState<(typeof RUXViews)[keyof typeof RUXViews]>(RUXViews.INFO) + const [userEnteredPhone, setUserEnteredPhone] = React.useState('') const clientGuid = useSelector((state: RootState) => state.profiles.client.guid) + const connectConfig = useSelector(selectInitialConfig) + const dispatch = useDispatch() const sendAnalyticsEvent = useAnalyticsEvent() const handleRuxInfoContinue = () => { @@ -31,15 +37,21 @@ export const ReturnUserExperience = React.forwardRef(() => { sendAnalyticsEvent(AnalyticEvents.RUX_INFO_CONTINUE_CLICKED) setView(RUXViews.PHONE_NUMBER) } + const handleContinueWithoutPhone = () => + dispatch({ type: ActionTypes.RESET_WIDGET_MFA_STEP, payload: connectConfig }) return (
{view !== RUXViews.LIST && ( -
- -
- + {view === RUXViews.INFO && ( + <> +
+ +
+ + + )}
@@ -47,6 +59,18 @@ export const ReturnUserExperience = React.forwardRef(() => { )} {view === RUXViews.INFO && } + + {view === RUXViews.PHONE_NUMBER && ( + { + // sendAnalyticsEvent(AnalyticEvents.RUX_PHONE_NUMBER_CONTINUE_CLICKED) + setView(RUXViews.OTP) + }} + setUserEnteredPhone={setUserEnteredPhone} + userEnteredPhone={userEnteredPhone} + /> + )}
) }) diff --git a/src/ReturnUserExperience/RuxPhoneNumber.tsx b/src/ReturnUserExperience/RuxPhoneNumber.tsx new file mode 100644 index 0000000000..b3023846f3 --- /dev/null +++ b/src/ReturnUserExperience/RuxPhoneNumber.tsx @@ -0,0 +1,130 @@ +import React from 'react' + +import { useTheme } from '@mui/material' +import InputAdornment from '@mui/material/InputAdornment' +import Stack from '@mui/material/Stack' +import Button from '@mui/material/Button' +import { Text } from '@mxenabled/mxui' +import { Link } from '@mui/material' + +import { __ } from 'src/utilities/Intl' +import useAnalyticsPath from 'src/hooks/useAnalyticsPath' +import { TextField } from 'src/privacy/input' +import { PageviewInfo } from 'src/const/Analytics' +import styles from './returnUserExperience.module.css' + +export const RuxPhoneNumber = ({ + handleContinueWithoutPhone, + handleRuxContinue, + userEnteredPhone, + setUserEnteredPhone, +}: { + handleContinueWithoutPhone: () => void + handleRuxContinue: () => void + userEnteredPhone: string + setUserEnteredPhone: (phone: string) => void +}) => { + useAnalyticsPath(...PageviewInfo.CONNECT_RUX_PHONE_NUMBER) + const { palette } = useTheme() + + return ( + <> + + + {__('Connect faster with your phone number')} + + + {__('Login or sign up with MX to securely access your saved accounts. ')} + + {__('Learn more about MX.')} + + + + + +
+ + Phone + + + +1 + +
+ + ), + style: { + paddingRight: '14px', + margin: '40px 0', + fontSize: '23px', + fontWeight: '400', + height: 'auto', + maxHeight: '60px', + }, + }} + fullWidth={true} + name="phoneNumber" + onChange={(e: React.ChangeEvent) => + setUserEnteredPhone(e.target.value.replace(/\D/g, '').slice(0, 10)) + } + required={true} + value={formatPhone(userEnteredPhone)} + /> + + + + {/* --TR: Full string 'By selecting "Get code", you agree to MX's Terms & Conditions' */} + {__('By selecting "Continue", you agree to ')} + + {/* TODO: Do we translate this below? */} + {__("MX's Terms & Conditions")} + + + + + + + ) +} + +export default RuxPhoneNumber + +const formatPhone = (value: string) => { + const digits = value.replace(/\D/g, '').slice(0, 10) + + if (digits.length === 0) return digits + if (digits.length <= 3) return `(${digits}` + if (digits.length <= 6) return `(${digits.slice(0, 3)}) ${digits.slice(3)}` + return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)} - ${digits.slice(6)}` +} diff --git a/src/ReturnUserExperience/__tests__/RuxPhoneNumber-test.tsx b/src/ReturnUserExperience/__tests__/RuxPhoneNumber-test.tsx new file mode 100644 index 0000000000..3b03246f0e --- /dev/null +++ b/src/ReturnUserExperience/__tests__/RuxPhoneNumber-test.tsx @@ -0,0 +1,99 @@ +import React from 'react' +import { RuxPhoneNumber } from 'src/ReturnUserExperience/RuxPhoneNumber' +import { render, screen } from 'src/utilities/testingLibrary' + +describe('RuxPhoneNumber', () => { + it('renders the main heading', () => { + render( + {}} + handleRuxContinue={() => {}} + setUserEnteredPhone={() => {}} + userEnteredPhone="" + />, + ) + const heading = screen.getByText('Connect faster with your phone number') + expect(heading).toBeInTheDocument() + }) + + it('renders the subtitle with a learn more link', () => { + render( + {}} + handleRuxContinue={() => {}} + setUserEnteredPhone={() => {}} + userEnteredPhone="" + />, + ) + const subtitle = screen.getByText( + /Login or sign up with MX to securely access your saved accounts./i, + ) + expect(subtitle).toBeInTheDocument() + + const link = screen.getByRole('link', { name: /learn more about mx/i }) + expect(link).toBeInTheDocument() + expect(link).toHaveAttribute('href', 'https://mx.com/learn-more') + expect(link).toHaveAttribute('target', '_blank') + expect(link).toHaveAttribute('rel', 'noopener noreferrer') + }) + + it('renders the phone number input with correct label', () => { + render( + {}} + handleRuxContinue={() => {}} + setUserEnteredPhone={() => {}} + userEnteredPhone="" + />, + ) + const phoneInput = screen.getByRole('textbox') + expect(phoneInput).toBeInTheDocument() + }) + + it('renders the continue without phone number button', () => { + render( + {}} + handleRuxContinue={() => {}} + setUserEnteredPhone={() => {}} + userEnteredPhone="" + />, + ) + const continueWithoutPhoneButton = screen.getByRole('button', { + name: 'Continue without phone number', + }) + expect(continueWithoutPhoneButton).toBeInTheDocument() + }) + + it('calls handleContinueWithoutPhone when the continue without phone number button is clicked', () => { + const handleContinueWithoutPhoneMock = vi.fn() + render( + {}} + setUserEnteredPhone={() => {}} + userEnteredPhone="" + />, + ) + const continueWithoutPhoneButton = screen.getByRole('button', { + name: 'Continue without phone number', + }) + continueWithoutPhoneButton.click() + expect(handleContinueWithoutPhoneMock).toHaveBeenCalledTimes(1) + }) + + it('calls handleRuxContinue when the continue button is clicked', () => { + const handleRuxContinueMock = vi.fn() + render( + {}} + handleRuxContinue={handleRuxContinueMock} + setUserEnteredPhone={() => {}} + userEnteredPhone="" + />, + ) + const continueButton = screen.getByRole('button', { name: 'Continue' }) + continueButton.click() + expect(handleRuxContinueMock).toHaveBeenCalledTimes(1) + }) +}) diff --git a/src/ReturnUserExperience/returnUserExperience.module.css b/src/ReturnUserExperience/returnUserExperience.module.css index fc76276baf..0e462743a7 100644 --- a/src/ReturnUserExperience/returnUserExperience.module.css +++ b/src/ReturnUserExperience/returnUserExperience.module.css @@ -71,6 +71,6 @@ .titleContainer { padding-top: 16px; - padding-right: 16px; - padding-left: 16px; + margin-right: -8px; + margin-left: -8px; } diff --git a/src/const/Analytics.js b/src/const/Analytics.js index 03405a3d45..f73a55b8ce 100644 --- a/src/const/Analytics.js +++ b/src/const/Analytics.js @@ -117,6 +117,7 @@ export const PageviewInfo = { CONNECT_OAUTH_ERROR: ['Connect Oauth Error', '/oauth_error'], CONNECT_NO_ELIGIBLE_ACCOUNTS: ['Connect No Eligible Accounts', '/no_eligible_accounts'], CONNECT_RUX_INFO: ['Connect RUX Info', '/rux_info'], + CONNECT_RUX_PHONE_NUMBER: ['Connect RUX Phone Number', '/rux_phone_number'], CONNECT_SEARCH: ['Connect Search', '/search'], CONNECT_SEARCH_FAILED: ['Connect Search Failed', '/search_failed'], CONNECT_SEARCH_NO_RESULTS: ['Connect Search No Results', '/no_results'], diff --git a/src/const/language/es.json b/src/const/language/es.json index e047c29e00..657366ed0e 100644 --- a/src/const/language/es.json +++ b/src/const/language/es.json @@ -433,6 +433,11 @@ "Connect your accounts": "Conecta tus cuentas", "%1 uses MX to connect your accounts. ": "%1 usa MX para conectar tus cuentas. ", "Learn more about MX.": "Obtén más información sobre MX.", + "Connect faster with your phone number": "Conéctate más rápido con tu número de teléfono", + "Login or sign up with MX to securely access your saved accounts. ": "Inicia sesión o regístrate con MX para acceder de forma segura a tus cuentas guardadas. ", + "By selecting \"Continue\", you agree to ": "Al seleccionar \"Continuar\", aceptas ", + "MX's Terms & Conditions": "los Términos y Condiciones de MX", + "Continue without phone number": "Continuar sin número de teléfono", "connect/disclosure/button\u0004Continue": "Continuar", "connect/disclosure/policy/text\u0004By clicking Continue, you agree to the ": "Al hacer clic en Continuar, tu aceptas la ", "connect/disclosure/policy/link\u0004MX Privacy Policy.": "Política de privacidad de Money Experience.", diff --git a/src/const/language/es.po b/src/const/language/es.po index 351207f983..dfc041ec9d 100644 --- a/src/const/language/es.po +++ b/src/const/language/es.po @@ -169,9 +169,11 @@ msgstr "Continuar" #: src/components/support/GeneralSupport.js #: src/components/support/SupportSuccess.js #: src/components/ConnectSuccessSurvey.tsx src/components/FindAccountInfo.js -#: src/components/LeavingNoticeFlat.js src/ReturnUserExperience/RuxInfo.tsx -#: src/views/mfa/DefaultMFA.js src/views/mfa/MFAImages.js -#: src/views/mfa/MFAOptions.js src/views/microdeposits/VerifyDeposits.js +#: src/components/LeavingNoticeFlat.js +#: src/ReturnUserExperience/RuxPhoneNumber.tsx +#: src/ReturnUserExperience/RuxInfo.tsx src/views/mfa/DefaultMFA.js +#: src/views/mfa/MFAImages.js src/views/mfa/MFAOptions.js +#: src/views/microdeposits/VerifyDeposits.js #: src/views/microdeposits/HowItWorks.js #: src/views/microdeposits/MicrodepositErrors.js #: src/views/microdeposits/PersonalInfoForm.js @@ -2165,7 +2167,8 @@ msgstr "Privado" #: src/ReturnUserExperience/RuxInfo.tsx msgid "We never sell your phone number or use it for marketing." -msgstr "Nunca vendemos tu número de teléfono ni lo usamos con fines de mercadotecnia." +msgstr "" +"Nunca vendemos tu número de teléfono ni lo usamos con fines de mercadotecnia." #: src/ReturnUserExperience/RuxInfo.tsx msgid "Connect your accounts" @@ -2175,6 +2178,30 @@ msgstr "Conecta tus cuentas" msgid "%1 uses MX to connect your accounts. " msgstr "%1 usa MX para conectar tus cuentas. " +#: src/ReturnUserExperience/RuxPhoneNumber.tsx #: src/ReturnUserExperience/RuxInfo.tsx msgid "Learn more about MX." msgstr "Obtén más información sobre MX." + +#: src/ReturnUserExperience/RuxPhoneNumber.tsx +msgid "Connect faster with your phone number" +msgstr "Conéctate más rápido con tu número de teléfono" + +#: src/ReturnUserExperience/RuxPhoneNumber.tsx +msgid "Login or sign up with MX to securely access your saved accounts. " +msgstr "" +"Inicia sesión o regístrate con MX para acceder de forma segura a tus cuentas " +"guardadas. " + +#. TR: Full string 'By selecting "Get code", you agree to MX's Terms & Conditions' */} +#: src/ReturnUserExperience/RuxPhoneNumber.tsx +msgid "By selecting \"Continue\", you agree to " +msgstr "Al seleccionar \"Continuar\", aceptas " + +#: src/ReturnUserExperience/RuxPhoneNumber.tsx +msgid "MX's Terms & Conditions" +msgstr "los Términos y Condiciones de MX" + +#: src/ReturnUserExperience/RuxPhoneNumber.tsx +msgid "Continue without phone number" +msgstr "Continuar sin número de teléfono" diff --git a/src/const/language/frCa.json b/src/const/language/frCa.json index cff752cd1c..cb4129e26b 100644 --- a/src/const/language/frCa.json +++ b/src/const/language/frCa.json @@ -436,6 +436,11 @@ "Connect your accounts": "Connectez vos comptes", "%1 uses MX to connect your accounts. ": "%1 utilise MX pour connecter vos comptes. ", "Learn more about MX.": "En savoir plus sur MX.", + "Connect faster with your phone number": "Connectez-vous plus rapidement avec votre numéro de téléphone", + "Login or sign up with MX to securely access your saved accounts. ": "Connectez-vous ou inscrivez-vous avec MX pour accéder en toute sécurité à vos comptes enregistrés. ", + "By selecting \"Continue\", you agree to ": "En sélectionnant \"Continuer\", vous acceptez ", + "MX's Terms & Conditions": "les conditions générales de MX", + "Continue without phone number": "Continuer sans numéro de téléphone", "connect/disclosure/policy/text\u0004By clicking Continue, you agree to the ": "En cliquant sur Continuer, vous acceptez la ", "connect/disclosure/policy/link\u0004MX Privacy Policy.": "Politique de confidentialité de MX.", "connect/disclosure/policy/link\u0004MX Privacy Policy": "Politique de confidentialité de MX.", diff --git a/src/const/language/frCa.po b/src/const/language/frCa.po index 7fac8eb59d..a3cc46c401 100644 --- a/src/const/language/frCa.po +++ b/src/const/language/frCa.po @@ -69,9 +69,11 @@ msgstr "Continuer" #: src/components/support/GeneralSupport.js #: src/components/support/SupportSuccess.js #: src/components/ConnectSuccessSurvey.tsx src/components/FindAccountInfo.js -#: src/components/LeavingNoticeFlat.js src/ReturnUserExperience/RuxInfo.tsx -#: src/views/mfa/DefaultMFA.js src/views/mfa/MFAImages.js -#: src/views/mfa/MFAOptions.js src/views/microdeposits/VerifyDeposits.js +#: src/components/LeavingNoticeFlat.js +#: src/ReturnUserExperience/RuxPhoneNumber.tsx +#: src/ReturnUserExperience/RuxInfo.tsx src/views/mfa/DefaultMFA.js +#: src/views/mfa/MFAImages.js src/views/mfa/MFAOptions.js +#: src/views/microdeposits/VerifyDeposits.js #: src/views/microdeposits/HowItWorks.js #: src/views/microdeposits/MicrodepositErrors.js #: src/views/microdeposits/PersonalInfoForm.js @@ -2258,7 +2260,9 @@ msgstr "Privé" #: src/ReturnUserExperience/RuxInfo.tsx msgid "We never sell your phone number or use it for marketing." -msgstr "Nous ne vendons jamais votre numéro de téléphone ni ne l’utilisons pour le marketing." +msgstr "" +"Nous ne vendons jamais votre numéro de téléphone ni ne l’utilisons pour le " +"marketing." #: src/ReturnUserExperience/RuxInfo.tsx msgid "Connect your accounts" @@ -2268,6 +2272,30 @@ msgstr "Connectez vos comptes" msgid "%1 uses MX to connect your accounts. " msgstr "%1 utilise MX pour connecter vos comptes. " +#: src/ReturnUserExperience/RuxPhoneNumber.tsx #: src/ReturnUserExperience/RuxInfo.tsx msgid "Learn more about MX." msgstr "En savoir plus sur MX." + +#: src/ReturnUserExperience/RuxPhoneNumber.tsx +msgid "Connect faster with your phone number" +msgstr "Connectez-vous plus rapidement avec votre numéro de téléphone" + +#: src/ReturnUserExperience/RuxPhoneNumber.tsx +msgid "Login or sign up with MX to securely access your saved accounts. " +msgstr "" +"Connectez-vous ou inscrivez-vous avec MX pour accéder en toute sécurité à " +"vos comptes enregistrés. " + +#. TR: Full string 'By selecting "Get code", you agree to MX's Terms & Conditions' */} +#: src/ReturnUserExperience/RuxPhoneNumber.tsx +msgid "By selecting \"Continue\", you agree to " +msgstr "En sélectionnant \"Continuer\", vous acceptez " + +#: src/ReturnUserExperience/RuxPhoneNumber.tsx +msgid "MX's Terms & Conditions" +msgstr "les conditions générales de MX" + +#: src/ReturnUserExperience/RuxPhoneNumber.tsx +msgid "Continue without phone number" +msgstr "Continuer sans numéro de téléphone"