diff --git a/packages/cfd/src/features/Containers/cfd-password-modal/cfd-create-password-form.tsx b/packages/cfd/src/features/Containers/cfd-password-modal/cfd-create-password-form.tsx new file mode 100644 index 000000000000..7346e234367d --- /dev/null +++ b/packages/cfd/src/features/Containers/cfd-password-modal/cfd-create-password-form.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import { TCFDPasswordFormReusedProps, TCFDPasswordFormValues, TOnSubmitPassword } from './types'; +import ChangePasswordConfirmation from '../../../Containers/cfd-change-password-confirmation'; +import { CreatePassword } from './create-password'; +import { CFD_PLATFORMS } from '../../../Helpers/cfd-config'; + +import { FormikHelpers } from 'formik'; +import { MultiStep } from '@deriv/components'; + +type TCFDCreatePasswordFormProps = TCFDPasswordFormReusedProps & { + has_mt5_account: boolean; + submitPassword: TOnSubmitPassword; + is_real_financial_stp: boolean; +}; + +type TMultiStepRefProps = { + goNextStep: () => void; + goPrevStep: () => void; +}; + +export const CFDCreatePasswordForm = ({ + has_mt5_account, + platform, + error_message, + validatePassword, + submitPassword, + is_real_financial_stp, +}: TCFDCreatePasswordFormProps) => { + const multi_step_ref = React.useRef(); + const [password, setPassword] = React.useState(''); + + const onSubmit: TOnSubmitPassword = (values, actions) => { + if (platform === CFD_PLATFORMS.MT5 && has_mt5_account) { + setPassword(values.password); + multi_step_ref.current?.goNextStep(); + } else { + submitPassword(values, actions); + } + }; + + const steps = [ + { + component: ( + + ), + }, + { + component: ( + ) => + submitPassword({ password }, actions) + } + onCancel={() => multi_step_ref.current?.goPrevStep()} + /> + ), + }, + ]; + + return ; +}; diff --git a/packages/cfd/src/features/Containers/cfd-password-modal/cfd-password-form.tsx b/packages/cfd/src/features/Containers/cfd-password-modal/cfd-password-form.tsx new file mode 100644 index 000000000000..99e5eb1f9703 --- /dev/null +++ b/packages/cfd/src/features/Containers/cfd-password-modal/cfd-password-form.tsx @@ -0,0 +1,261 @@ +import React from 'react'; +import { TCFDPasswordFormReusedProps, TCFDPasswordFormValues, TOnSubmitPassword } from './types'; +import { localize, Localize } from '@deriv/translations'; +import { Formik, FormikErrors } from 'formik'; +import { + isDesktop, + isMobile, + getCFDPlatformLabel, + getCFDPlatformNames, + getFormattedJurisdictionCode, + getLegalEntityName, +} from '@deriv/shared'; +import { Text, FormSubmitButton, PasswordInput } from '@deriv/components'; +import { CFDCreatePasswordForm } from './cfd-create-password-form'; +import { CFD_PLATFORMS } from '../../../Helpers/cfd-config'; + +const getCancelButtonLabel = ({ + should_set_trading_password, + error_type, +}: Pick) => { + if (should_set_trading_password && error_type !== 'PasswordReset') { + return isDesktop() ? null : localize('Cancel'); + } + + return localize('Forgot password?'); +}; + +type TCFDPasswordFormProps = TCFDPasswordFormReusedProps & { + account_title: string; + account_type: { + category?: string; + type?: string; + }; + closeModal: () => void; + error_type?: string; + form_error?: string; + has_mt5_account: boolean; + is_bvi: boolean; + is_dxtrade_allowed: boolean; + is_real_financial_stp: boolean; + jurisdiction_selected_shortcode: string; + onCancel: () => void; + onForgotPassword: () => void; + should_set_trading_password: boolean; + show_eu_related_content: boolean; + submitPassword: TOnSubmitPassword; +}; + +const handlePasswordInputChange = ( + e: React.ChangeEvent, + handleChange: (el: React.ChangeEvent) => void, + validateForm: (values?: TCFDPasswordFormValues) => Promise>, + setFieldTouched: (field: string, isTouched?: boolean, shouldValidate?: boolean) => void +) => { + handleChange(e); + validateForm().then(() => { + setFieldTouched('password', true); + }); +}; + +export const CFDPasswordForm = ({ + account_title, + account_type, + closeModal, + error_message, + error_type, + form_error, + has_mt5_account, + is_real_financial_stp, + jurisdiction_selected_shortcode, + onCancel, + onForgotPassword, + platform, + should_set_trading_password, + show_eu_related_content, + submitPassword, + validatePassword, +}: TCFDPasswordFormProps) => { + const button_label = React.useMemo(() => { + if (error_type === 'PasswordReset') { + return localize('Try later'); + } + return localize('Add account'); + }, [error_type]); + + const has_cancel_button = (isDesktop() ? !should_set_trading_password : true) || error_type === 'PasswordReset'; + + const cancel_button_label = getCancelButtonLabel({ should_set_trading_password, error_type }); + + const handleCancel = () => { + if (!has_cancel_button) { + return undefined; + } + if (should_set_trading_password) { + return onCancel(); + } + + return onForgotPassword(); + }; + + if (error_type === 'PasswordReset') { + return ( +
+
+ + + +
+ + {({ handleSubmit }) => ( +
+ + + )} +
+
+ ); + } + + if (should_set_trading_password) { + return ( + + ); + } + + const accountTitle = () => { + switch (platform) { + case CFD_PLATFORMS.CTRADER: + case CFD_PLATFORMS.DXTRADE: + return 'CFD'; + default: + return account_title; + } + }; + + const showJuristiction = () => { + if (platform === CFD_PLATFORMS.DXTRADE) { + return ''; + } else if (!show_eu_related_content) { + return getFormattedJurisdictionCode(jurisdiction_selected_shortcode); + } + return 'CFDs'; + }; + + return ( + + {({ + errors, + isSubmitting, + handleBlur, + handleChange, + handleSubmit, + setFieldTouched, + touched, + values, + validateForm, + }) => ( +
+
+ {!should_set_trading_password && ( + + {account_type.category === 'real' && ( + + )} + {account_type.category === 'demo' && ( + + )} + + )} +
+ ) => { + handlePasswordInputChange(e, handleChange, validateForm, setFieldTouched); + }} + data_testId={`dt_${platform}_password`} + /> +
+ + {is_real_financial_stp && ( +
+ +
+ )} + {error_type === 'PasswordError' && ( + + + + )} +
+ + + )} +
+ ); +}; diff --git a/packages/cfd/src/features/Containers/cfd-password-modal/cfd-password-modal.scss b/packages/cfd/src/features/Containers/cfd-password-modal/cfd-password-modal.scss new file mode 100644 index 000000000000..1f3daf354080 --- /dev/null +++ b/packages/cfd/src/features/Containers/cfd-password-modal/cfd-password-modal.scss @@ -0,0 +1,130 @@ +.cfd-password-manager { + margin: 5.6rem auto 3.2rem; + width: 100%; + height: 100%; + + @media only screen and (max-height: 645px) { + margin-top: 1.5rem; + overflow: auto; + } + + &--paragraph { + margin-bottom: 3.2rem; + } + &--error-message { + margin-bottom: 1em; + } + &__investor-wrapper { + margin: 3.2rem 0.5rem 0; + + & .dc-input__label { + top: 0.9rem; + } + @include mobile { + padding-bottom: 12rem; + } + + .cfd-password-manager--paragraph:first-child { + margin-bottom: 0.8rem; + } + } + .dc-password-meter__container, + .dc-password-input { + width: 30rem; + margin: auto; + } + &__investor-form { + width: 100%; + max-width: 300px; + margin: 2.4em auto 0; + + @include mobile { + max-width: unset; + } + } + &__new-password { + margin-bottom: 8.6rem; + } + &__actions { + align-items: center; + display: flex; + flex-flow: column nowrap; + } + &--button { + margin-top: 1.6rem; + + @include mobile { + width: 100%; + margin-top: 0.8rem; + max-width: 30rem; + white-space: normal; + } + } + &__success { + text-align: center; + margin-top: 3.2rem; + + &-header { + @include typeface(--paragraph-center-bold-black); + margin: 1.6rem 0 0.8rem; + } + &-btn { + margin-top: 3.2rem; + width: 6.4rem; + + @include mobile { + width: 100%; + max-width: 30rem; + } + } + } + .multi-step__header { + margin: 0 0 2.4rem; + } + .multi-step__component { + text-align: center; + } + @include desktop { + max-width: calc(450px + 1rem); // needs extra padding for the scrollbar, otherwise it will cover the words + } + @include mobile { + padding: 0 1.6rem; + margin-top: 2.4rem; + + &__scroll-wrapper { + overflow-y: auto; + overflow-x: hidden; + } + /* iPhone SE screen width fixes due to UI space restrictions */ + @media only screen and (max-width: 320px) { + padding: 0; + } + } +} + +.dc-modal { + &__container { + min-width: initial; + &_cfd-password-modal { + &__description { + @include typeface(--xsmall-left-normal-black); + line-height: 1.5; + max-width: calc(min(100vw, 349px)); + margin: -2.7rem 0 2.4rem; + color: var(--text-less-prominent); + } + &__account-title { + margin-bottom: 1.6rem; + } + } + &_cfd-dashboard__compare-accounts { + width: 904px; + } + &_cfd-password-manager__modal { + height: 100% !important; + min-height: calc(100vh - 22rem); + width: 904px; + max-height: calc(100vh - 48px - 36px) !important; + } + } +} diff --git a/packages/cfd/src/features/Containers/cfd-password-modal/cfd-password-modal.tsx b/packages/cfd/src/features/Containers/cfd-password-modal/cfd-password-modal.tsx new file mode 100644 index 000000000000..aeb48db078c2 --- /dev/null +++ b/packages/cfd/src/features/Containers/cfd-password-modal/cfd-password-modal.tsx @@ -0,0 +1,568 @@ +import React from 'react'; +import { FormikErrors } from 'formik'; +import { useHistory } from 'react-router'; +import { SentEmailModal } from '@deriv/account'; +import { + getDxCompanies, + getMtCompanies, + getDerivezCompanies, + TMtCompanies, + TDxCompanies, + TDerivezCompanies, +} from '../../../Stores/Modules/CFD/Helpers/cfd-config'; +import { MobileDialog, Modal } from '@deriv/components'; +import { + getAuthenticationStatusInfo, + getCFDPlatformLabel, + getErrorMessages, + getFormattedJurisdictionCode, + isDesktop, + isMobile, + routes, + validLength, + validPassword, +} from '@deriv/shared'; +import { localize, Localize } from '@deriv/translations'; +import { + useAccountStatus, + useAvailableMT5Accounts, + useCreateMT5Account, + useCreateOtherCFDAccount, + useSettings, + useTradingPlatformPasswordChange, + useVerifyEmail, +} from '@deriv/api'; +import SuccessDialog from '../../../Components/success-dialog.jsx'; +import '../../../sass/cfd.scss'; +import './cfd-password-modal.scss'; +import { observer, useStore } from '@deriv/stores'; +import { useCfdStore } from '../../../Stores/Modules/CFD/Helpers/useCfdStores'; +import { PasswordModalHeader } from './password-modal-header'; +import { CFDPasswordForm } from './cfd-password-form'; +import { IconType } from './icon-type'; +import { TCFDPasswordFormValues, TOnSubmitPassword } from './types'; +import { CFD_PLATFORMS, CATEGORY, JURISDICTION, MARKET_TYPE } from '../../../Helpers/cfd-config'; + +type TReviewMsgForMT5 = { + is_selected_mt5_verified: boolean; + jurisdiction_selected_shortcode: string; + manual_status: string; +}; + +type TCFDPasswordModalProps = { + error_type?: string; + form_error?: string; + platform: string; +}; + +type TAccountType = 'all' | 'financial' | 'gaming' | 'demo'; + +type TAccountCategory = 'real' | 'demo'; + +type TCFDOtherPlatform = 'dxtrade' | 'derivez' | 'ctrader'; + +const ReviewMessageForMT5 = ({ + is_selected_mt5_verified, + jurisdiction_selected_shortcode, + manual_status, +}: TReviewMsgForMT5) => { + if (is_selected_mt5_verified) { + return ( + + ); + } else if ( + jurisdiction_selected_shortcode === JURISDICTION.BVI || + jurisdiction_selected_shortcode === JURISDICTION.VANUATU + ) { + if (manual_status === 'pending') { + return ; + } + return ; + } else if ( + jurisdiction_selected_shortcode === JURISDICTION.LABUAN || + jurisdiction_selected_shortcode === JURISDICTION.MALTA_INVEST + ) { + return ; + } + return null; +}; + +const CFDPasswordModal = observer(({ form_error, platform }: TCFDPasswordModalProps) => { + const { client, traders_hub } = useStore(); + + const { + email, + account_status, + landing_companies, + is_logged_in, + is_dxtrade_allowed, + mt5_login_list, + updateAccountStatus, + } = client; + const { show_eu_related_content } = traders_hub; + + const { + account_title, + account_type, + disableCFDPasswordModal, + error_message, + error_type, + getAccountStatus, + has_cfd_error, + is_cfd_success_dialog_enabled, + is_cfd_password_modal_enabled, + jurisdiction_selected_shortcode, + setError, + setCFDSuccessDialog, + } = useCfdStore(); + + const history = useHistory(); + const { mutateAsync: tradingPlatformPasswordChange } = useTradingPlatformPasswordChange(); + const { + data: mt5_account_data, + mutate: createMT5Account, + status: mt5_create_account_status, + error: mt5_create_account_error, + } = useCreateMT5Account(); + const { + mutate: createCFDAccount, + status: cfd_create_account_status, + error: cfd_create_account_error, + } = useCreateOtherCFDAccount(); + const { mutate: verifyEmail } = useVerifyEmail(); + const { data: availableMT5Accounts } = useAvailableMT5Accounts(); + const { data: settings } = useSettings(); + const { data: account_status_hook } = useAccountStatus(); + + const [is_password_modal_exited, setPasswordModalExited] = React.useState(true); + const is_bvi = landing_companies?.mt_financial_company?.financial_stp?.shortcode === JURISDICTION.BVI; + const has_mt5_account = Boolean(mt5_login_list?.length); + const should_set_trading_password = + Array.isArray(account_status?.status) && + account_status.status.includes( + platform === CFD_PLATFORMS.MT5 ? 'mt5_password_not_set' : 'dxtrade_password_not_set' + ); + const is_password_error = error_type === 'PasswordError'; + const is_password_reset = error_type === 'PasswordReset'; + const [is_sent_email_modal_open, setIsSentEmailModalOpen] = React.useState(false); + + const { poi_verified_for_bvi_labuan_vanuatu, poi_verified_for_maltainvest, poa_verified, manual_status } = + getAuthenticationStatusInfo(account_status); + + const [is_selected_mt5_verified, setIsSelectedMT5Verified] = React.useState(false); + + const marketType = account_type.type; + const accountType = marketType === MARKET_TYPE.SYNTHETIC ? MARKET_TYPE.GAMING : marketType; + + const getVerificationStatus = () => { + switch (jurisdiction_selected_shortcode) { + case JURISDICTION.SVG: + setIsSelectedMT5Verified(true); + break; + case JURISDICTION.BVI: + case JURISDICTION.VANUATU: + setIsSelectedMT5Verified(poi_verified_for_bvi_labuan_vanuatu); + break; + case JURISDICTION.LABUAN: + setIsSelectedMT5Verified(poi_verified_for_bvi_labuan_vanuatu && poa_verified); + break; + case JURISDICTION.MALTA_INVEST: + setIsSelectedMT5Verified(poi_verified_for_maltainvest && poa_verified); + break; + default: + } + }; + + React.useEffect(() => { + if (is_logged_in) { + updateAccountStatus(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + React.useEffect(() => { + getVerificationStatus(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [jurisdiction_selected_shortcode, account_status]); + + React.useEffect(() => { + if (mt5_create_account_status === 'error' && mt5_create_account_error) { + setError(true, mt5_create_account_error as unknown as Error); + } + if (mt5_create_account_status === 'success') { + setError(false); + setCFDSuccessDialog(true); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [mt5_create_account_status, mt5_create_account_error]); + + React.useEffect(() => { + if (cfd_create_account_status === 'error' && mt5_create_account_error) { + setError(true, cfd_create_account_error as unknown as Error); + } + if (cfd_create_account_status === 'success') { + setError(false); + setCFDSuccessDialog(true); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [cfd_create_account_status, cfd_create_account_error]); + + const validatePassword = (values: TCFDPasswordFormValues) => { + const errors: FormikErrors = {}; + if ( + !validLength(values.password, { + min: 8, + max: 25, + }) + ) { + errors.password = localize('You should enter {{min_number}}-{{max_number}} characters.', { + min_number: 8, + max_number: 25, + }); + } else if (!validPassword(values.password)) { + errors.password = getErrorMessages().password(); + } + if (values.password?.toLowerCase() === email.toLowerCase()) { + errors.password = localize('Your password cannot be the same as your email address.'); + } + return errors; + }; + + const closeDialogs = () => { + setCFDSuccessDialog(false); + setError(false); + }; + + const closeModal = () => { + closeDialogs(); + disableCFDPasswordModal(); + }; + + const closeOpenSuccess = () => { + disableCFDPasswordModal(); + closeDialogs(); + if (account_type.category === CATEGORY.REAL && mt5_create_account_status === 'success') { + sessionStorage.setItem('cfd_transfer_to_login_id', mt5_account_data?.login || ''); + history.push(routes.cashier_acc_transfer); + } + }; + + const handleForgotPassword = () => { + closeModal(); + let redirect_to = platform === CFD_PLATFORMS.MT5 ? 1 : 2; + + // if account type is real convert redirect_to from 1 or 2 to 10 or 20 + // and if account type is demo convert redirect_to from 1 or 2 to 11 or 21 + if (account_type.category === CATEGORY.REAL) { + redirect_to = Number(`${redirect_to}0`); + } else if (account_type.category === CATEGORY.DEMO) { + redirect_to = Number(`${redirect_to}1`); + } + + const password_reset_code = + platform === CFD_PLATFORMS.MT5 + ? 'trading_platform_mt5_password_reset' + : 'trading_platform_dxtrade_password_reset'; + verifyEmail({ + verify_email: email, + type: password_reset_code, + url_parameters: { + redirect_to, + }, + }); + setIsSentEmailModalOpen(true); + }; + + const submitPassword: TOnSubmitPassword = (values, actions) => { + (async () => { + if (platform === CFD_PLATFORMS.MT5) { + if (account_status_hook?.is_mt5_password_not_set) { + await tradingPlatformPasswordChange({ + new_password: values.password, + platform: CFD_PLATFORMS.MT5, + }); + } + createMT5Account({ + payload: { + account_type: + account_type.category === CATEGORY.DEMO + ? CATEGORY.DEMO + : (accountType as unknown as TAccountType), + address: settings?.address_line_1 || '', + city: settings?.address_city || '', + company: 'svg', + country: settings?.country_code || '', + email: settings?.email || '', + leverage: availableMT5Accounts?.find(acc => acc.market_type === marketType)?.leverage || 500, + mainPassword: values.password, + ...(marketType === MARKET_TYPE.FINANCIAL && { mt5_account_type: MARKET_TYPE.FINANCIAL }), + ...(marketType === MARKET_TYPE.ALL && { sub_account_category: 'swap_free' }), + name: settings?.first_name || '', + phone: settings?.phone || '', + state: settings?.address_state || '', + zipCode: settings?.address_postcode || '', + }, + }); + + if (mt5_create_account_status === 'success') { + actions.setStatus({ success: true }); + actions.setSubmitting(false); + } else if (mt5_create_account_status === 'error' && mt5_create_account_error) { + actions.resetForm({}); + actions.setSubmitting(false); + actions.setStatus({ success: false }); + } + } else { + if (CFD_PLATFORMS.DXTRADE && account_status_hook?.is_dxtrade_password_not_set) { + await tradingPlatformPasswordChange({ + new_password: values.password, + platform: CFD_PLATFORMS.DXTRADE, + }); + } + createCFDAccount({ + payload: { + account_type: account_type.category as unknown as TAccountCategory, + market_type: MARKET_TYPE.ALL, + password: values.password, + platform: platform as unknown as TCFDOtherPlatform, + }, + }); + if (cfd_create_account_status === 'success') { + actions.setStatus({ success: true }); + actions.setSubmitting(false); + } else if (cfd_create_account_status === 'error' && cfd_create_account_error) { + actions.resetForm({}); + actions.setSubmitting(false); + actions.setStatus({ success: false }); + } + } + })().catch(error => { + setError(true, error); + }); + }; + + const should_show_password = + is_cfd_password_modal_enabled && + !is_cfd_success_dialog_enabled && + (!has_cfd_error || is_password_error || is_password_reset); + + const should_show_success = + !has_cfd_error && is_cfd_success_dialog_enabled && is_cfd_password_modal_enabled && is_password_modal_exited; + + const should_show_sent_email_modal = is_sent_email_modal_open && is_password_modal_exited; + + const is_real_financial_stp = [account_type.category, account_type.type].join('_') === 'real_financial_stp'; + + const should_show_password_modal = React.useMemo(() => { + if (should_show_password) { + return should_set_trading_password ? true : isDesktop(); + } + return false; + }, [should_set_trading_password, should_show_password]); + + const should_show_password_dialog = React.useMemo(() => { + if (should_show_password) { + if (!should_set_trading_password) return isMobile(); + } + return false; + }, [should_set_trading_password, should_show_password]); + + const success_modal_submit_label = React.useMemo(() => { + if (account_type.category === CATEGORY.REAL) { + if (platform === CFD_PLATFORMS.MT5) { + return is_selected_mt5_verified ? localize('Transfer now') : localize('OK'); + } + return localize('Transfer now'); + } + return localize('Continue'); + }, [platform, account_type, is_selected_mt5_verified]); + + const getSubmitText = () => { + const { category, type } = account_type; + if (!category && !type) return ''; + + const category_label = category === CATEGORY.REAL ? localize('real') : localize('demo'); + let type_label = ''; + switch (platform) { + case CFD_PLATFORMS.MT5: + type_label = + getMtCompanies(show_eu_related_content)[category as keyof TMtCompanies][ + type as keyof TMtCompanies['demo' | 'real'] + ].short_title; + break; + case CFD_PLATFORMS.DXTRADE: + type_label = + getDxCompanies()[category as keyof TDxCompanies][type as keyof TDxCompanies['demo' | 'real']] + .short_title; + break; + case CFD_PLATFORMS.DERIVEZ: + type_label = + getDerivezCompanies()[category as keyof TDerivezCompanies][ + type as keyof TDerivezCompanies['demo' | 'real'] + ].short_title; + break; + default: + type_label = ''; + break; + } + + const jurisdiction_label = + jurisdiction_selected_shortcode && getFormattedJurisdictionCode(jurisdiction_selected_shortcode); + const mt5_platform_label = jurisdiction_selected_shortcode !== JURISDICTION.MALTA_INVEST ? 'Deriv MT5' : ''; + + const accountTypes = () => { + if (platform === CFD_PLATFORMS.DXTRADE && type_label === 'Derived') { + return 'Synthetic'; + } else if (platform === CFD_PLATFORMS.DERIVEZ || platform === CFD_PLATFORMS.CTRADER) { + return 'CFDs'; + } + return type_label; + }; + + if (category === CATEGORY.REAL) { + return ( + + , ]} + /> + {platform === CFD_PLATFORMS.DXTRADE ? ( + + ) : ( + + )} + + ); + } + + return ( + , ]} + /> + ); + }; + + const cfd_password_form = ( + + ); + + const password_modal = ( + ( + + )} + onUnmount={() => getAccountStatus(platform)} + onExited={() => setPasswordModalExited(true)} + onEntered={() => setPasswordModalExited(false)} + width={isMobile() ? '32.8rem' : 'auto'} + > + {cfd_password_form} + + ); + + const password_dialog = ( + + + + {cfd_password_form} + + ); + + return ( + + {password_modal} + {password_dialog} + + } + icon_size='xlarge' + text_submit={success_modal_submit_label} + has_cancel={ + platform === CFD_PLATFORMS.MT5 + ? is_selected_mt5_verified && account_type.category === CATEGORY.REAL + : account_type.category === CATEGORY.REAL + } + has_close_icon={false} + width={isMobile() ? '32.8rem' : 'auto'} + is_medium_button={isMobile()} + /> + setIsSentEmailModalOpen(false)} + onClickSendEmail={handleForgotPassword} + /> + + ); +}); + +export default CFDPasswordModal; diff --git a/packages/cfd/src/features/Containers/cfd-password-modal/create-password.tsx b/packages/cfd/src/features/Containers/cfd-password-modal/create-password.tsx new file mode 100644 index 000000000000..378d1b2c3429 --- /dev/null +++ b/packages/cfd/src/features/Containers/cfd-password-modal/create-password.tsx @@ -0,0 +1,131 @@ +import { Formik, FormikErrors } from 'formik'; +import React from 'react'; +import { TCFDPasswordFormReusedProps, TCFDPasswordFormValues, TOnSubmitPassword } from './types'; +import { Text, Icon, PasswordMeter, PasswordInput, FormSubmitButton } from '@deriv/components'; +import { localize, Localize } from '@deriv/translations'; +import { getCFDPlatformLabel, getErrorMessages } from '@deriv/shared'; +import { CFD_PLATFORMS } from '../../../Helpers/cfd-config'; + +type TCFDCreatePasswordProps = TCFDPasswordFormReusedProps & { + password: string; + onSubmit: TOnSubmitPassword; + is_real_financial_stp: boolean; +}; + +const handlePasswordInputChange = ( + e: React.ChangeEvent, + handleChange: (el: React.ChangeEvent) => void, + validateForm: (values?: TCFDPasswordFormValues) => Promise>, + setFieldTouched: (field: string, isTouched?: boolean, shouldValidate?: boolean) => void +) => { + handleChange(e); + validateForm().then(() => { + setFieldTouched('password', true); + }); +}; + +export const CreatePassword = ({ + password, + platform, + validatePassword, + onSubmit, + error_message, + is_real_financial_stp, +}: TCFDCreatePasswordProps) => { + return ( + + {({ + errors, + isSubmitting, + handleBlur, + handleChange, + handleSubmit, + setFieldTouched, + touched, + values, + validateForm, + }) => ( +
+
+ + + + + + + +
+ + {() => ( + ) => { + handlePasswordInputChange(e, handleChange, validateForm, setFieldTouched); + }} + data_testId={`dt_${platform}_password`} + /> + )} + +
+ {is_real_financial_stp && ( +
+ +
+ )} + 0} + is_loading={isSubmitting} + label={localize('Create {{platform}} password', { + platform: getCFDPlatformLabel(platform), + })} + is_center={true} + /> +
+
+ )} +
+ ); +}; diff --git a/packages/cfd/src/features/Containers/cfd-password-modal/icon-type.tsx b/packages/cfd/src/features/Containers/cfd-password-modal/icon-type.tsx new file mode 100644 index 000000000000..0f44926a4591 --- /dev/null +++ b/packages/cfd/src/features/Containers/cfd-password-modal/icon-type.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { Icon } from '@deriv/components'; +import { CFD_PLATFORMS, routes } from '@deriv/shared'; +import TradingPlatformIcon from '../../../Assets/svgs/trading-platform'; + +type TIconTypeProps = { + platform: string; + type?: string; + show_eu_related_content: boolean; +}; + +export const IconType = React.memo(({ platform, type, show_eu_related_content }: TIconTypeProps) => { + const traders_hub = window.location.pathname === routes.traders_hub; + if (platform === CFD_PLATFORMS.DXTRADE) { + return ; + } else if (platform === CFD_PLATFORMS.DERIVEZ) { + return ; + } else if (traders_hub) { + if (platform === CFD_PLATFORMS.CTRADER) { + return ; + } + switch (type) { + case 'synthetic': + return ; + case 'all': + return ; + case 'financial': + if (show_eu_related_content) { + return ; + } + return ; + default: + return ; + } + } else { + switch (type) { + case 'synthetic': + return ; + case 'all': + return ; + case 'financial': + if (show_eu_related_content) { + return ; + } + return ; + default: + return ; + } + } +}); +IconType.displayName = 'IconType'; diff --git a/packages/cfd/src/features/Containers/cfd-password-modal/password-modal-header.tsx b/packages/cfd/src/features/Containers/cfd-password-modal/password-modal-header.tsx new file mode 100644 index 000000000000..cc2fbfb3e3d7 --- /dev/null +++ b/packages/cfd/src/features/Containers/cfd-password-modal/password-modal-header.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { getCFDPlatformLabel, isMobile } from '@deriv/shared'; +import { Localize } from '@deriv/translations'; +import { Text } from '@deriv/components'; + +type TPasswordModalHeaderProps = { + should_set_trading_password: boolean; + is_password_reset_error: boolean; + platform: string; + has_mt5_account?: boolean; +}; + +export const PasswordModalHeader = ({ + should_set_trading_password, + is_password_reset_error, + platform, +}: TPasswordModalHeaderProps) => { + const element = isMobile() ? 'p' : 'span'; + const alignment = 'center'; + const font_size = 's'; + const style = isMobile() + ? { + padding: '2rem', + } + : {}; + + return ( + + {!should_set_trading_password && !is_password_reset_error && ( + + )} + {is_password_reset_error && } + + ); +}; diff --git a/packages/cfd/src/features/Containers/cfd-password-modal/types.tsx b/packages/cfd/src/features/Containers/cfd-password-modal/types.tsx new file mode 100644 index 000000000000..25c6e0b68a48 --- /dev/null +++ b/packages/cfd/src/features/Containers/cfd-password-modal/types.tsx @@ -0,0 +1,14 @@ +import { FormikErrors, FormikHelpers } from 'formik'; + +export type TCFDPasswordFormValues = { password: string }; + +export type TOnSubmitPassword = ( + values: TCFDPasswordFormValues, + actions: FormikHelpers +) => void; + +export type TCFDPasswordFormReusedProps = { + platform: string; + error_message: string; + validatePassword: (values: TCFDPasswordFormValues) => FormikErrors; +};