From 7e4ab9a0b5ddb3a151a8c762e1beb339013f1438 Mon Sep 17 00:00:00 2001 From: ali-fs Date: Wed, 17 Mar 2021 10:14:05 +0330 Subject: [PATCH 01/27] WIP: New real account signup flow --- scripts/config/pages.js | 3 +- src/javascript/app/base/binary_pages.js | 6 +- src/javascript/app/base/client.js | 34 +-- src/javascript/app/common/form_manager.js | 4 +- src/javascript/app/common/form_progress.js | 51 ++++ src/javascript/app/pages/user/accounts.js | 18 +- .../user/new_account/financial_acc_opening.js | 171 ------------ .../address-details-config.js | 77 ++++++ .../currency-selector-config.js | 30 +++ .../financial-details-config.js | 142 ++++++++++ .../personal-details-config.js | 134 ++++++++++ .../terms-of-use-config.js | 36 +++ .../wizard-step-config.js | 21 ++ .../address_detail_form.js | 52 ++++ .../new_account_modules/currency_form.js | 248 ++++++++++++++++++ .../financial_detail_form.js | 17 ++ .../personal_detail_form.js | 65 +++++ .../new_account_modules/terms_of_use_form.js | 31 +++ .../user/new_account/real_acc_opening.js | 70 ----- .../user/new_account/real_account_opening.js | 93 +++++++ src/sass/_common/common.scss | 4 + src/sass/_common/form_progress.scss | 74 ++++++ src/sass/common.scss | 1 + .../_common/components/forms_common_rows.jsx | 113 ++++++-- .../new_account_steps/address_detail_form.jsx | 31 +++ .../new_account_steps/currency_form.jsx | 36 +++ .../new_account_steps/financial_info_form.jsx | 161 ++++++++++++ .../personal_detail_form.jsx | 50 ++++ .../new_account_steps/terms_of_use_form.jsx | 37 +++ .../app/_includes/pep_declaration.jsx | 2 +- src/templates/app/new_account/financial.jsx | 99 ------- src/templates/app/new_account/real.jsx | 75 ------ .../app/new_account/real_account.jsx | 40 +++ 33 files changed, 1535 insertions(+), 491 deletions(-) create mode 100644 src/javascript/app/common/form_progress.js delete mode 100644 src/javascript/app/pages/user/new_account/financial_acc_opening.js create mode 100644 src/javascript/app/pages/user/new_account/new_account_form_config/address-details-config.js create mode 100644 src/javascript/app/pages/user/new_account/new_account_form_config/currency-selector-config.js create mode 100644 src/javascript/app/pages/user/new_account/new_account_form_config/financial-details-config.js create mode 100644 src/javascript/app/pages/user/new_account/new_account_form_config/personal-details-config.js create mode 100644 src/javascript/app/pages/user/new_account/new_account_form_config/terms-of-use-config.js create mode 100644 src/javascript/app/pages/user/new_account/new_account_form_config/wizard-step-config.js create mode 100644 src/javascript/app/pages/user/new_account/new_account_modules/address_detail_form.js create mode 100644 src/javascript/app/pages/user/new_account/new_account_modules/currency_form.js create mode 100644 src/javascript/app/pages/user/new_account/new_account_modules/financial_detail_form.js create mode 100644 src/javascript/app/pages/user/new_account/new_account_modules/personal_detail_form.js create mode 100644 src/javascript/app/pages/user/new_account/new_account_modules/terms_of_use_form.js delete mode 100644 src/javascript/app/pages/user/new_account/real_acc_opening.js create mode 100644 src/javascript/app/pages/user/new_account/real_account_opening.js create mode 100644 src/sass/_common/form_progress.scss create mode 100644 src/templates/app/_includes/new_account_steps/address_detail_form.jsx create mode 100644 src/templates/app/_includes/new_account_steps/currency_form.jsx create mode 100644 src/templates/app/_includes/new_account_steps/financial_info_form.jsx create mode 100644 src/templates/app/_includes/new_account_steps/personal_detail_form.jsx create mode 100644 src/templates/app/_includes/new_account_steps/terms_of_use_form.jsx delete mode 100644 src/templates/app/new_account/financial.jsx delete mode 100644 src/templates/app/new_account/real.jsx create mode 100644 src/templates/app/new_account/real_account.jsx diff --git a/scripts/config/pages.js b/scripts/config/pages.js index 4b5229e9d1953..6fe80d27f0219 100644 --- a/scripts/config/pages.js +++ b/scripts/config/pages.js @@ -21,8 +21,7 @@ module.exports = [ ['trading', 'app/trade/trading', 'default', 'SmartTrader'], - ['new_account/maltainvestws', 'app/new_account/financial', 'default', 'Financial Account Opening'], - ['new_account/realws', 'app/new_account/real', 'default', 'Real Money Account Opening'], + ['new_account/real_account', 'app/new_account/real_account', 'default', 'Real Account Opening'], ['new_account/virtualws', 'app/new_account/virtual', 'default', 'Create New Virtual-money Account'], ['new_account/welcome', 'app/new_account/welcome_page', 'default', 'Welcome to Binary.com'], diff --git a/src/javascript/app/base/binary_pages.js b/src/javascript/app/base/binary_pages.js index 79287522c7371..1e692518f0e42 100644 --- a/src/javascript/app/base/binary_pages.js +++ b/src/javascript/app/base/binary_pages.js @@ -40,8 +40,7 @@ const Accounts = require('../pages/user/accounts'); const LostPassword = require('../pages/user/lost_password'); const MetaTrader = require('../pages/user/metatrader/metatrader'); const TypesOfAccounts = require('../pages/user/metatrader/types_of_accounts'); -const FinancialAccOpening = require('../pages/user/new_account/financial_acc_opening'); -const RealAccOpening = require('../pages/user/new_account/real_acc_opening'); +const RealAccountOpening = require('../pages/user/new_account/real_account_opening'); const VirtualAccOpening = require('../pages/user/new_account/virtual_acc_opening'); const WelcomePage = require('../pages/user/new_account/welcome_page'); const ResetPassword = require('../pages/user/reset_password'); @@ -105,7 +104,6 @@ const pages_config = { logged_inws : { module: LoggedInHandler }, lost_passwordws : { module: LostPassword, not_authenticated: true }, malta : { module: StaticPages.Locations }, - maltainvestws : { module: FinancialAccOpening, is_authenticated: true }, market_timesws : { module: TradingTimesUI, no_mf: true }, metals : { module: GetStarted.Metals }, metatrader : { module: MetaTrader, is_authenticated: true, needs_currency: true }, @@ -116,7 +114,7 @@ const pages_config = { portfoliows : { module: Portfolio, is_authenticated: true, needs_currency: true }, professional : { module: professionalClient, is_authenticated: true, only_real: true }, profit_tablews : { module: ProfitTable, is_authenticated: true, needs_currency: true }, - realws : { module: RealAccOpening, is_authenticated: true }, + real_account : { module: RealAccountOpening, is_authenticated: true }, redirect : { module: Redirect }, regulation : { module: Regulation }, reset_passwordws : { module: ResetPassword, not_authenticated: true }, diff --git a/src/javascript/app/base/client.js b/src/javascript/app/base/client.js index 5896325b567b4..b27e463bbca2d 100644 --- a/src/javascript/app/base/client.js +++ b/src/javascript/app/base/client.js @@ -100,39 +100,7 @@ const Client = (() => { }; const getUpgradeInfo = () => { - const upgrade_info = ClientBase.getBasicUpgradeInfo(); - - let upgrade_links = {}; - if (upgrade_info.can_upgrade_to.length) { - const upgrade_link_map = { - realws : ['svg', 'iom', 'malta'], - maltainvestws: ['maltainvest'], - }; - - Object.keys(upgrade_link_map).forEach(link => { - const res = upgrade_link_map[link].find(lc => upgrade_info.can_upgrade_to.includes(lc)); - if (res) { - upgrade_links = { - ...upgrade_links, - [res]: link, - }; - } - }); - } - - let transformed_upgrade_links = {}; - Object.keys(upgrade_links).forEach(link => { - transformed_upgrade_links = { - ...transformed_upgrade_links, - [link]: `new_account/${upgrade_links[link]}`, - }; - }); - - return Object.assign(upgrade_info, { - upgrade_links : transformed_upgrade_links, - is_current_path: !!Object.values(upgrade_links) - .find(link => new RegExp(link, 'i').test(window.location.pathname)), - }); + return ClientBase.getBasicUpgradeInfo(); }; const defaultRedirectUrl = () => urlFor('trading'); diff --git a/src/javascript/app/common/form_manager.js b/src/javascript/app/common/form_manager.js index 058a910bdb6c7..4d484bcc80eaf 100644 --- a/src/javascript/app/common/form_manager.js +++ b/src/javascript/app/common/form_manager.js @@ -134,7 +134,9 @@ const FormManager = (() => { if (!can_submit) return; if (Validation.validate(options.form_selector)) { const req = $.extend({}, options.obj_request, getFormData(options.form_selector)); - if (typeof options.fnc_additional_check === 'function') { + if (typeof options.get_submitted_data === 'function') { + options.get_submitted_data(getFormData(options.form_selector)) + } else if (typeof options.fnc_additional_check === 'function') { Promise.resolve(options.fnc_additional_check(req)).then((result) => { if (result) submit(req); }); diff --git a/src/javascript/app/common/form_progress.js b/src/javascript/app/common/form_progress.js new file mode 100644 index 0000000000000..6f0f646fe6038 --- /dev/null +++ b/src/javascript/app/common/form_progress.js @@ -0,0 +1,51 @@ +const getElementById = require('../../_common/common_functions').getElementById; +const isMobile = require('../../_common/os_detect').isMobile; + + +const FormProgress = (() => { + const render = (identifire, steps, current_step) => { + const el_header = document.createElement('div') + el_header.className = 'form-progress__header'; + const el_steps = document.createElement('div') + el_steps.className = 'form-progress__steps'; + + const el_steps_before = document.createElement('div') + el_steps_before.className = 'form-progress__steps--before'; + el_steps_before.style.width = `calc(100% * ${steps.length - 1} / ${steps.length})` + el_steps.appendChild(el_steps_before) + + steps.forEach((step, index) => { + const el_step = document.createElement('div') + el_step.key = index + 1 + el_step.className = 'form-progress__step'; + + const el_identifier = document.createElement('div') + el_identifier.className = `identifier${index <= current_step ? ' identifier--active' : ''}` + el_identifier.innerHTML = index + 1 + el_step.appendChild(el_identifier) + + const el_title = document.createElement('p') + el_title.className = `form-progress__step-title` + el_title.innerHTML = step.title + el_step.appendChild(el_title) + el_steps.appendChild(el_step) + }) + const el_steps_after = document.createElement('div') + el_steps_after.className = 'form-progress__steps--after'; + el_steps_after.style.width = `calc(${current_step / steps.length} * 100%)` + el_steps.appendChild(el_steps_after) + + + el_header.appendChild(el_steps) + getElementById(identifire).childNodes.forEach(node => node.parentNode.removeChild(node)) + getElementById(identifire).appendChild(el_header) + } + + + return { + render + }; +})(); + +module.exports = FormProgress; + diff --git a/src/javascript/app/pages/user/accounts.js b/src/javascript/app/pages/user/accounts.js index 090d157769951..bcf857372c1aa 100644 --- a/src/javascript/app/pages/user/accounts.js +++ b/src/javascript/app/pages/user/accounts.js @@ -72,7 +72,7 @@ const Accounts = (() => { const clearPopup = () => { SetCurrency.cleanupPopup(); }; - + const doneLoading = (element_to_show) => { $(element_to_show).setVisibility(1); $('#accounts_loading').remove(); @@ -86,7 +86,7 @@ const Accounts = (() => { const populateNewAccounts = (upgrade_info) => { const table_headers = TableHeaders.get(); upgrade_info.type.forEach((new_account_type, index) => { - const account = { + const account = { real : new_account_type === 'real', financial: new_account_type === 'financial', }; @@ -95,7 +95,7 @@ const Accounts = (() => { $(form_id).find('tbody') .append($('') .append($('', { datath: table_headers.account }).html($('', { - text : new_account_title, + text: new_account_title, 'data-balloon': `${localize('Counterparty')}: ${getCompanyName(account)}, ${localize( 'Jurisdiction')}: ${getCompanyCountry(account)}`, 'data-balloon-length': 'large', @@ -106,7 +106,7 @@ const Accounts = (() => { '', { class: 'button', - href : urlFor(upgrade_info.upgrade_links[upgrade_info.can_upgrade_to[index]]), + href : urlFor('/new_account/real_account', `account_type=${upgrade_info.can_upgrade_to[index]}`), }, ) .html($('', { text: localize('Create account') }))))); @@ -251,12 +251,12 @@ const Accounts = (() => { const populateMultiAccount = () => { const table_headers = TableHeaders.get(); - const account = { real: 1 }; + const account = { real: 1 }; $(form_id).find('tbody') .append($('', { id: 'new_account_opening' }) .append($('', { datath: table_headers.account }).html($('', { - text : localize('Real Account'), - 'data-balloon' : `${localize('Counterparty')}: ${getCompanyName(account)}, ${localize('Jurisdiction')}: ${getCompanyCountry(account)}`, + text: localize('Real Account'), + 'data-balloon': `${localize('Counterparty')}: ${getCompanyName(account)}, ${localize('Jurisdiction')}: ${getCompanyCountry(account)}`, 'data-balloon-length': 'large', }))) .append($('', { text: getAvailableMarkets({ real: 1 }), datath: table_headers.available_markets })) @@ -267,10 +267,10 @@ const Accounts = (() => { doneLoading('#new_accounts_wrapper'); }; - const onUnload = () =>{ + const onUnload = () => { clearPopup(); }; - + return { onLoad, onUnload, diff --git a/src/javascript/app/pages/user/new_account/financial_acc_opening.js b/src/javascript/app/pages/user/new_account/financial_acc_opening.js deleted file mode 100644 index 2f6eb1dc5b56b..0000000000000 --- a/src/javascript/app/pages/user/new_account/financial_acc_opening.js +++ /dev/null @@ -1,171 +0,0 @@ -const moment = require('moment'); -const BinaryPjax = require('../../../base/binary_pjax'); -const Client = require('../../../base/client'); -const BinarySocket = require('../../../base/socket'); -const AccountOpening = require('../../../common/account_opening'); -const FormManager = require('../../../common/form_manager'); -const localize = require('../../../../_common/localize').localize; -const isEmptyObject = require('../../../../_common/utility').isEmptyObject; -const toISOFormat = require('../../../../_common/string_util').toISOFormat; -const State = require('../../../../_common/storage').State; - -const FinancialAccOpening = (() => { - const form_id = '#financial-form'; - - let get_settings, - txt_secret_answer; - - const doneLoading = () => { - $('#financial_loading').remove(); - $('#financial_wrapper').setVisibility(1); - }; - - const onLoad = () => { - const client_details = sessionStorage.getItem('client_form_response'); - - if (Client.hasAccountType('financial') || !Client.get('residence')) { - BinaryPjax.loadPreviousUrl(); - return; - } - - if (sessionStorage.getItem('is_risk_disclaimer')){ - handleResponse(JSON.parse(client_details)); - } - - const req_financial_assessment = BinarySocket.send({ get_financial_assessment: 1 }).then((response) => { - const get_financial_assessment = response.get_financial_assessment; - if (!isEmptyObject(get_financial_assessment)) { - const keys = Object.keys(get_financial_assessment); - keys.forEach((key) => { - const val = get_financial_assessment[key]; - $(`#${key}`).val(val); - }); - - } - }); - - const req_settings = BinarySocket.wait('get_settings').then((response) => { - get_settings = response.get_settings; - let $element, - value; - Object.keys(get_settings).forEach((key) => { - $element = $(`#${key}`); - value = get_settings[key]; - // date_of_birth can be 0 as a valid epoch - if (key === 'date_of_birth' && value !== 'null') { - const moment_val = moment.utc(value * 1000); - get_settings[key] = moment_val.format('DD MMM, YYYY'); - $element.attr({ - 'data-value': toISOFormat(moment_val), - 'value' : toISOFormat(moment_val), - 'type' : 'text', - }); - $('.input-disabled').attr('disabled', 'disabled'); - } else if (value) $element.val(value); - }); - }); - - Promise.all([req_settings, req_financial_assessment]).then(() => { - const client_form_response = client_details ? JSON.parse(client_details).echo_req : {}; - if (!isEmptyObject(client_form_response)) { - const keys = Object.keys(client_form_response); - keys.forEach((key) => { - const val = client_form_response[key]; - $(`#${key}`).val(val); - }); - - } - - AccountOpening.populateForm(form_id, getValidations, true); - - // date_of_birth can be 0 as a valid epoch - if ('date_of_birth' in get_settings && get_settings.date_of_birth !== 'null') { - $('#date_of_birth').val(get_settings.date_of_birth); - } - FormManager.handleSubmit({ - form_selector : form_id, - obj_request : { new_account_maltainvest: 1, accept_risk: 0 }, - fnc_additional_check: storeSecretAnswer, - fnc_response_handler: handleResponse, - }); - }); - - $('#tax_information_note_toggle').off('click').on('click', (e) => { - e.stopPropagation(); - $('#tax_information_note_toggle').toggleClass('open'); - $('#tax_information_note').slideToggle(); - }); - - $('#financial_risk_decline').off('click').on('click', () => { - sessionStorage.removeItem('is_risk_disclaimer'); - }); - - AccountOpening.showHidePulser(0); - AccountOpening.registerPepToggle(); - }; - - // API won't return secret answer in echo_req, it will return so we should store it in FE before sending it after accept_risk - const storeSecretAnswer = (request) => { - txt_secret_answer = request.secret_answer; - return true; - }; - - const getValidations = () => { - let validations = - AccountOpening.commonValidations().concat(AccountOpening.selectCheckboxValidation(form_id), [ - { selector: '#citizen', validations: ['req'] }, - { selector: '#tax_residence', validations: ['req'] }, - { - selector : '#tax_identification_number', - validations: [ - 'req', - ['tax_id', { residence_list: State.getResponse('residence_list'), $warning: $('#tax_id_warning'), $tax_residence: $('#tax_residence') }], - ['length', { min: 1, max: 20 }], - ], - }, - { selector: '#chk_tax_id', validations: [['req', { hide_asterisk: true, message: localize('Please confirm that all the information above is true and complete.') }]], exclude_request: 1 }, - ]); - const place_of_birth = State.getResponse('get_settings.place_of_birth'); - if (place_of_birth) { - validations = validations.concat([{ request_field: 'place_of_birth', value: place_of_birth }]); - } - doneLoading(); - return validations; - }; - - const handleResponse = (response) => { - sessionStorage.setItem('client_form_response', JSON.stringify(response)); - if ('error' in response && response.error.code === 'show risk disclaimer') { - sessionStorage.setItem('is_risk_disclaimer', true); - $(form_id).setVisibility(0); - $('#client_message').setVisibility(0); - const risk_form_id = '#financial-risk'; - $(risk_form_id).setVisibility(1); - $.scrollTo($(risk_form_id), 500, { offset: -10 }); - - FormManager.init(risk_form_id, []); - - const echo_req = $.extend({}, response.echo_req); - echo_req.accept_risk = 1; - echo_req.secret_answer = txt_secret_answer; // update from to the previous value stored in FE - FormManager.handleSubmit({ - form_selector : risk_form_id, - obj_request : echo_req, - fnc_response_handler: handleResponse, - }); - doneLoading(); - } else { - sessionStorage.removeItem('is_risk_disclaimer'); - AccountOpening.handleNewAccount(response, response.msg_type); - } - }; - - const onUnload = () => { AccountOpening.showHidePulser(1); }; - - return { - onLoad, - onUnload, - }; -})(); - -module.exports = FinancialAccOpening; diff --git a/src/javascript/app/pages/user/new_account/new_account_form_config/address-details-config.js b/src/javascript/app/pages/user/new_account/new_account_form_config/address-details-config.js new file mode 100644 index 0000000000000..3ddae3e5a2550 --- /dev/null +++ b/src/javascript/app/pages/user/new_account/new_account_form_config/address-details-config.js @@ -0,0 +1,77 @@ +const localize = require('../../../../../_common/localize').localize; +// import { generateValidationFunction, getDefaultFields, getErrorMessages } from '@deriv/shared'; +const AddressDetailForm = require('../new_account_modules/address_detail_form'); + +const getAddressDetailsConfig = ({ account_settings, is_svg }) => { + if (!account_settings) { + return {}; + } + + return [ + { + id: 'address_line_1', + section: 'address_section', + supported_in: ['svg', 'iom', 'malta', 'maltainvest'], + default_value: account_settings.address_line_1 || '', + rules: ['req', 'address', ['length', { min: 1, max: 700 }]], + // ['po_box', getErrorMessages().po_box()], + // ].filter(x => (is_svg ? x.indexOf('po_box') !== 0 : x)), + }, + { + id: 'address_line_2', + section: 'address_section', + supported_in: ['svg', 'iom', 'malta', 'maltainvest'], + default_value: account_settings.address_line_2 || '', + rules: [['length', { min: 0, max: 70 }]], + // ['po_box', getErrorMessages().po_box()], + // ].filter(x => (is_svg ? x.indexOf('po_box') !== 0 : x)), + }, + { + id: 'address_city', + section: 'address_section', + supported_in: ['svg', 'iom', 'malta', 'maltainvest'], + default_value: account_settings.address_city || '', + rules: [ + 'req', + ['regular', { regex: /^[a-zA-Z\s\W'.-]{1,35}$/, },], + ], + }, + { + id: 'address_state', + section: 'address_section', + supported_in: ['svg', 'iom', 'malta', 'maltainvest'], + default_value: account_settings.address_state || '', + // Isle of Man Clients do not need to fill out state since API states_list is empty. + rules: account_settings.residence === 'im' ? [] : ['req', ['regular', { regex: /^[\w\s\W'.-;,]{0,60}$/ }]] + }, + { + id: 'address_postcode', + section: 'address_section', + supported_in: ['svg', 'iom', 'malta', 'maltainvest'], + default_value: account_settings.address_postcode || '', + // GB residence are required to fill in the post code. + rules: [ + ['length', { min: 0, max: 20 }], + ...(/^(im|gb)$/.test(account_settings.residence) ? ['req'] : []), + // ['postcode', getErrorMessages().postcode()], + ], + }, + ]; +}; + +const getRequiredFields = (landing_company, all_fields) => { + return all_fields.filter(field => field.supported_in.includes(landing_company)) +}; + +const addressDetailsConfig = ({ upgrade_info, real_account_signup_target, account_settings }) => { + const is_svg = upgrade_info && upgrade_info.can_upgrade_to === 'svg'; + const config = getAddressDetailsConfig({ account_settings, is_svg }); + return { + title: localize('Address'), + body_module: AddressDetailForm, + body_module_step: 'address_detail_step', + fields: getRequiredFields(real_account_signup_target, config), + }; +}; + +module.exports = addressDetailsConfig; diff --git a/src/javascript/app/pages/user/new_account/new_account_form_config/currency-selector-config.js b/src/javascript/app/pages/user/new_account/new_account_form_config/currency-selector-config.js new file mode 100644 index 0000000000000..a41ab2a73da92 --- /dev/null +++ b/src/javascript/app/pages/user/new_account/new_account_form_config/currency-selector-config.js @@ -0,0 +1,30 @@ +const localize = require('../../../../../_common/localize').localize; +const CurrencyForm = require('../new_account_modules/currency_form'); + +const currency_selector_fields = [ + { + id: 'currency', + supported_in: ['maltainvest', 'malta', 'svg', 'iom'], + default_value: '', + rules: [['custom', { + value: () => $('.selected').find('.currency-name').text(), + func: (value) => value !== '', + message: 'Please select the currency for this account.' + }]], + } +] + +const getRequiredFields = (landing_company, all_fields) => { + return all_fields.filter(field => field.supported_in.includes(landing_company)) +}; + +const currencySelectorConfig = ({ real_account_signup_target }, CurrencySelector) => { + return { + title: localize('Account currency'), + body_module: CurrencyForm, + body_module_step: 'currency_step', + fields: getRequiredFields(real_account_signup_target, currency_selector_fields), + }; +}; + +module.exports = currencySelectorConfig; diff --git a/src/javascript/app/pages/user/new_account/new_account_form_config/financial-details-config.js b/src/javascript/app/pages/user/new_account/new_account_form_config/financial-details-config.js new file mode 100644 index 0000000000000..87e3d7372499c --- /dev/null +++ b/src/javascript/app/pages/user/new_account/new_account_form_config/financial-details-config.js @@ -0,0 +1,142 @@ +const localize = require('../../../../../_common/localize').localize; +const FinancialDetailForm = require('../new_account_modules/financial_detail_form'); + +const getFinancialDetailsConfig = ({ financial_assessment }) => { + return [ + { + id: 'income_source', + section: 'financial_information', + default_value: financial_assessment.income_source || '', + supported_in: ['maltainvest'], + rules: ['req'], + }, + { + id: 'employment_status', + section: 'financial_information', + default_value: financial_assessment.employment_status || '', + supported_in: ['maltainvest'], + rules: ['req'], + }, + { + id: 'employment_industry', + section: 'financial_information', + default_value: financial_assessment.employment_industry || '', + supported_in: ['maltainvest'], + rules: ['req'], + }, + { + id: 'occupation', + section: 'financial_information', + default_value: financial_assessment.occupation || '', + supported_in: ['maltainvest'], + rules: ['req'], + }, + { + id: 'source_of_wealth', + section: 'financial_information', + default_value: financial_assessment.source_of_wealth || '', + supported_in: ['maltainvest'], + rules: ['req'], + }, + { + id: 'education_level', + section: 'financial_information', + supported_in: ['maltainvest'], + default_value: financial_assessment.education_level || '', + rules: ['req'], + }, + { + id: 'net_income', + section: 'financial_information', + default_value: financial_assessment.net_income || '', + supported_in: ['maltainvest'], + rules: ['req'], + }, + { + id: 'estimated_worth', + section: 'financial_information', + default_value: financial_assessment.estimated_worth || '', + supported_in: ['maltainvest'], + rules: ['req'], + }, + { + id: 'account_turnover', + section: 'financial_information', + supported_in: ['maltainvest'], + default_value: financial_assessment.account_turnover || '', + rules: ['req'], + }, + { + id: 'forex_trading_experience', + section: 'trading_experience', + supported_in: ['maltainvest'], + default_value: financial_assessment.forex_trading_experience || '', + rules: ['req'], + }, + { + id: 'forex_trading_frequency', + section: 'trading_experience', + supported_in: ['maltainvest'], + default_value: financial_assessment.forex_trading_frequency || '', + rules: ['req'], + }, + { + id: 'binary_options_trading_experience', + section: 'trading_experience', + supported_in: ['maltainvest'], + default_value: financial_assessment.binary_options_trading_experience || '', + rules: ['req'], + }, + { + id: 'binary_options_trading_frequency', + section: 'trading_experience', + supported_in: ['maltainvest'], + default_value: financial_assessment.binary_options_trading_frequency || '', + rules: ['req'], + }, + { + id: 'cfd_trading_experience', + section: 'trading_experience', + supported_in: ['maltainvest'], + default_value: financial_assessment.cfd_trading_experience || '', + rules: ['req'], + }, + { + id: 'cfd_trading_frequency', + section: 'trading_experience', + supported_in: ['maltainvest'], + default_value: financial_assessment.cfd_trading_frequency || '', + rules: ['req'], + }, + { + id: 'other_instruments_trading_experience', + section: 'trading_experience', + default_value: financial_assessment.other_instruments_trading_experience || '', + supported_in: ['maltainvest'], + rules: ['req'], + }, + { + id: 'other_instruments_trading_frequency', + section: 'trading_experience', + default_value: financial_assessment.other_instruments_trading_frequency || '', + supported_in: ['maltainvest'], + rules: ['req'], + }, + ]; +}; + + +const getRequiredFields = (landing_company, all_fields) => { + return all_fields.filter(field => field.supported_in.includes(landing_company)) +}; + +const financialDetailsConfig = ({ real_account_signup_target, financial_assessment }) => { + const config = getFinancialDetailsConfig({ financial_assessment }); + return { + title: localize('Financial assessment'), + body_module: FinancialDetailForm, + body_module_step: 'financial_info_step', + fields: getRequiredFields(real_account_signup_target, config), + }; +}; +module.exports = financialDetailsConfig; diff --git a/src/javascript/app/pages/user/new_account/new_account_form_config/personal-details-config.js b/src/javascript/app/pages/user/new_account/new_account_form_config/personal-details-config.js new file mode 100644 index 0000000000000..c6470ccb267a2 --- /dev/null +++ b/src/javascript/app/pages/user/new_account/new_account_form_config/personal-details-config.js @@ -0,0 +1,134 @@ +const moment = require('moment'); +const localize = require('../../../../../_common/localize').localize; +const PersonalDetailForm = require('../new_account_modules/personal_detail_form'); + +// import { toMoment, getErrorMessages, generateValidationFunction, getDefaultFields, validLength } from '@deriv/shared'; + +const getCountryName = (residence_list, countryCode) => { + const country = residence_list.find(item => item.value === countryCode) + return country ? country.text : '' +} +const getPersonalDetailsConfig = ({ residence_list, account_settings }) => { + if (!residence_list || !account_settings) { + return {}; + } + + const disabled_items = [ + ...Object.keys(account_settings).filter(field_name => field_name !== 'account_opening_reason' && !!field_name), + ]; + + // minimum characters required is 9 numbers (excluding +- signs or space) + const min_phone_number = 9; + const max_phone_number = 35; + + const config = [ + + { + id: 'salutation', + section: 'name', + supported_in: ['iom', 'malta', 'maltainvest'], + default_value: account_settings.salutation || '', + rules: ['req'], + }, + { + id: 'first_name', + section: 'name', + supported_in: ['svg', 'iom', 'malta', 'maltainvest'], + default_value: account_settings.first_name || '', + rules: ['req', 'letter_symbol', ['length', { min: 2, max: 50 }]], + }, + { + id: 'last_name', + section: 'name', + supported_in: ['svg', 'iom', 'malta', 'maltainvest'], + default_value: account_settings.last_name || '', + rules: ['req', 'letter_symbol', ['length', { min: 2, max: 50 }]], + }, + { + id: 'date_of_birth', + section: 'detail', + supported_in: ['svg', 'iom', 'malta', 'maltainvest'], + default_value: account_settings.date_of_birth + ? moment.utc(account_settings.date_of_birth * 1000) + : '', + rules: ['req'], + }, + { + id: 'place_of_birth', + section: 'detail', + supported_in: ['maltainvest', 'iom', 'malta'], + default_value: account_settings.place_of_birth + ? getCountryName(residence_list, account_settings.place_of_birth) + : account_settings.country_code, + rules: ['req'], + }, + { + id: 'citizen', + section: 'detail', + supported_in: ['iom', 'malta', 'maltainvest'], + default_value: account_settings.citizen + ? getCountryName(residence_list, account_settings.citizen) + : account_settings.country_code, + rules: ['req'], + }, + { + id: 'phone', + section: 'detail', + supported_in: ['svg', 'iom', 'malta', 'maltainvest'], + default_value: account_settings.phone || '', + rules: ['req', 'phone', ['length', { min: 9, max: 35, value: () => $('#phone').val().replace(/\D/g,'') }]], + }, + { + id: 'tax_residence', + section: 'tax', + supported_in: ['maltainvest'], + default_value: account_settings.tax_residence + ? getCountryName(residence_list, account_settings.tax_residence) + : account_settings.country_code, + rules: ['req', ['length', { min: 1, max: 20 }]], + }, + { + id: 'tax_identification_number', + section: 'tax', + supported_in: ['maltainvest'], + default_value: account_settings.tax_identification_number || '', + rules: [ + 'req', + ['tax_id', { $warning: $('#tax_id_warning'), $tax_residence: $('#tax_residence') }], + ['length', { min: 1, max: 20 }], + ], + }, + { + id: 'tax_identification_confirm', + section: 'tax', + supported_in: ['maltainvest'], + default_value: false, + rules: ['req'], + }, + { + id: 'account_opening_reason', + section: 'account_opening_reason', + supported_in: ['iom', 'malta', 'maltainvest'], + default_value: account_settings.account_opening_reason || '', + rules: ['req'], + }, + ]; + + return [config, disabled_items]; +}; + +const getRequiredFields = (landing_company, all_fields) => { + return all_fields.filter(field => field.supported_in.includes(landing_company)) +}; + +const personalDetailsConfig = ({ real_account_signup_target, residence_list, account_settings }) => { + const [config, disabled_items] = getPersonalDetailsConfig({ residence_list, account_settings }); + return { + title: localize('Personal details'), + body_module: PersonalDetailForm, + body_module_step: 'personal_detail_step', + fields: getRequiredFields(real_account_signup_target, config), + }; +}; + +module.exports = personalDetailsConfig; diff --git a/src/javascript/app/pages/user/new_account/new_account_form_config/terms-of-use-config.js b/src/javascript/app/pages/user/new_account/new_account_form_config/terms-of-use-config.js new file mode 100644 index 0000000000000..588f03ce11121 --- /dev/null +++ b/src/javascript/app/pages/user/new_account/new_account_form_config/terms-of-use-config.js @@ -0,0 +1,36 @@ +// import { isDesktop, getDefaultFields } from '@deriv/shared'; +const localize = require('../../../../../_common/localize').localize; +const TermsOfUseForm = require('../new_account_modules/terms_of_use_form'); + +const getTermsOfUseConfig = [ + { + id: 'agreed_tos', + section: 'terms_of_use_section', + supported_in: ['svg', 'iom'], + default_value: false, + rules: ['req'] + }, + { + id: 'agreed_tnc', + section: 'terms_of_use_section', + supported_in: ['svg', 'iom'], + default_value: false, + rules: ['req'] + }, +] + + +const getRequiredFields = (landing_company, all_fields) => { + return all_fields.filter(field => field.supported_in.includes(landing_company)) +}; + +const termsOfUseConfig = ({ real_account_signup_target }) => { + return { + title: localize('Terms of use'), + body_module: TermsOfUseForm, + body_module_step: 'terms_of_use_step', + fields: getRequiredFields(real_account_signup_target, getTermsOfUseConfig), + }; +}; + +module.exports = termsOfUseConfig; diff --git a/src/javascript/app/pages/user/new_account/new_account_form_config/wizard-step-config.js b/src/javascript/app/pages/user/new_account/new_account_form_config/wizard-step-config.js new file mode 100644 index 0000000000000..c6b0ea5b8acb2 --- /dev/null +++ b/src/javascript/app/pages/user/new_account/new_account_form_config/wizard-step-config.js @@ -0,0 +1,21 @@ +const addressDetailsConfig = require('./address-details-config') +const currencySelectorConfig = require('./currency-selector-config') +const financialDetailsConfig = require('./financial-details-config') +const personalDetailsConfig = require('./personal-details-config') +const termsOfUseConfig = require('./terms-of-use-config') + + +const shouldShowFinancialDetails = ({ real_account_signup_target }) => real_account_signup_target === 'maltainvest'; +const shouldShowPersonalAndAddressDetailsAndCurrency = ({ real_account_signup_target }) => real_account_signup_target !== 'samoa'; + +const getSteps = props => { + return [ + ...(shouldShowPersonalAndAddressDetailsAndCurrency(props) ? [currencySelectorConfig(props)] : []), + ...(shouldShowPersonalAndAddressDetailsAndCurrency(props) ? [personalDetailsConfig(props)] : []), + ...(shouldShowPersonalAndAddressDetailsAndCurrency(props) ? [addressDetailsConfig(props)] : []), + ...(shouldShowFinancialDetails(props) ? [financialDetailsConfig(props)] : []), + termsOfUseConfig(props), + ]; +}; + +module.exports = getSteps \ No newline at end of file diff --git a/src/javascript/app/pages/user/new_account/new_account_modules/address_detail_form.js b/src/javascript/app/pages/user/new_account/new_account_modules/address_detail_form.js new file mode 100644 index 0000000000000..ab5bec6a216eb --- /dev/null +++ b/src/javascript/app/pages/user/new_account/new_account_modules/address_detail_form.js @@ -0,0 +1,52 @@ +const SelectMatcher = require('@binary-com/binary-style').select2Matcher; +const Client = require('../../../../base/client'); +const BinarySocket = require('../../../../base/socket'); +const getElementById = require('../../../../../_common/common_functions').getElementById; +const makeOption = require('../../../../../_common/common_functions').makeOption; +const localize = require('../../../../../_common/localize').localize; + +const AddressDetailForm = (() => { + + const init = async (fields) => { + if (fields.some(field => field.id === 'address_state')) { + let $address_state = $('#address_state'); + const client_state = (await BinarySocket.send({ get_settings: 1 })).get_settings.address_state + const state_list = (await BinarySocket.send({ states_list: Client.get('residence') })).states_list; + if (state_list && state_list.length > 0) { + $address_state.append(makeOption({ text: localize('Please select'), value: '' })) + state_list.forEach((state) => { $address_state.append(makeOption({ text: state.text, value: state.value })); }); + $address_state.select2({ + matcher(params, data) { + return SelectMatcher(params, data); + }, + }); + if (client_state) $address_state.val(client_state); + } else { + $address_state.replaceWith($('', { id: 'address_state', name: 'address_state', type: 'text', maxlength: '35', 'data-lpignore': true })); + $address_state = $('#address_state'); + if (client_state) $address_state.text(client_state); + } + } + + const texts = ['address_line_1', 'address_line_2', 'address_city', 'address_postcode'] + + fields.forEach(field => { + if (texts.includes(field.id)) { + $(`#${field.id}`).text(field.default_value); + $(`#${field.id}`) + .val(field.default_value) // Set value for validation + .attr({ 'data-force': true, 'data-value': field.default_value }); + } + + getElementById(`${field.section}_section`).setVisibility(1) + getElementById(`${field.id}_row`).setVisibility(1) + }) + }; + + return { + init, + }; + +})(); + +module.exports = AddressDetailForm; diff --git a/src/javascript/app/pages/user/new_account/new_account_modules/currency_form.js b/src/javascript/app/pages/user/new_account/new_account_modules/currency_form.js new file mode 100644 index 0000000000000..d4624a73f8545 --- /dev/null +++ b/src/javascript/app/pages/user/new_account/new_account_modules/currency_form.js @@ -0,0 +1,248 @@ +const GetCurrency = require('../../../../pages/user/get_currency'); +const Client = require('../../../../base/client'); +const BinarySocket = require('../../../../base/socket'); +const Currency = require('../../../../common/currency'); +const localize = require('../../../../../_common/localize').localize; +const Url = require('../../../../../_common/url'); + +const SetCurrency = (() => { + let $submit; + + const init = async () => { + const $currency_list = $('.currency_list'); + const $error = $('#set_currency').find('.error-msg'); + const landing_company = (await BinarySocket.wait('landing_company')).landing_company; + const payout_currencies = (await BinarySocket.wait('payout_currencies')).payout_currencies; + + populateCurrencies(getAvailableCurrencies(landing_company, payout_currencies)); + + onSelection($currency_list, $error, true); + }; + + const getAvailableCurrencies = (landing_company, payout_currencies) => + Client.get('landing_company_shortcode') === 'svg' ? GetCurrency.getCurrencies(landing_company) : payout_currencies; + + const populateCurrencies = (currencies) => { + console.log({currencies}); + const $fiat_currencies = $('
'); + const $cryptocurrencies = $('
'); + currencies.forEach((c) => { + const $wrapper = $('
', { class: 'gr-2 gr-4-m currency_wrapper', id: c }); + const $image = $('
').append($('', { src: Url.urlForStatic(`images/pages/set_currency/${c.toLowerCase()}.svg`) })); + const $name = $('
', { class: 'currency-name' }); + + if (Currency.isCryptocurrency(c)) { + const $display_name = $('', { + text: Currency.getCurrencyName(c) || c, + ...(/^UST$/.test(c) && { + 'data-balloon' : localize('Tether Omni (USDT) is a version of Tether that\'s pegged to USD and is built on the Bitcoin blockchain.'), + 'data-balloon-length': 'medium', + 'data-balloon-pos' : 'top', + 'class' : 'show-mobile', + }), + ...(/^eUSDT/.test(c) && { + 'data-balloon' : localize('Tether ERC20 (eUSDT) is a version of Tether that\'s pegged to USD and is hosted on the Ethereum platform.'), + 'data-balloon-length': 'medium', + 'data-balloon-pos' : 'top', + 'class' : 'show-mobile', + }), + }); + + $name.append($display_name).append($('
')).append(`(${Currency.getCurrencyDisplayCode(c)})`); + } else { + $name.text(c); + } + + $wrapper.append($image).append($name); + (Currency.isCryptocurrency(c) ? $cryptocurrencies : $fiat_currencies).append($wrapper); + }); + const fiat_currencies = $fiat_currencies.html(); + if (fiat_currencies) { + $('#fiat_currencies').setVisibility(1); + $('#fiat_currency_list').html(fiat_currencies).parent().setVisibility(1); + } + const crypto_currencies = $cryptocurrencies.html(); + if (crypto_currencies) { + $('#crypto_currencies').setVisibility(1); + $('#crypto_currency_list').html(crypto_currencies).parent().setVisibility(1); + } + const has_one_group = (!fiat_currencies && crypto_currencies) || (fiat_currencies && !crypto_currencies); + if (has_one_group) { + $('#set_currency_text').text(localize('Please select the currency for this account:')); + } else { + $('#set_currency_text').text(localize('Do you want this to be a fiat account or crypto account? Please choose one:')); + } + + $('#set_currency_loading').remove(); + $('#set_currency, .select_currency').setVisibility(1); + }; + + const onSelection = ($currency_list, $error, should_show_confirmation) => { + $('.currency_wrapper').off('click dblclick').on('click dblclick', function () { + removeError($error, true); + const $clicked_currency = $(this); + $currency_list.find('> div').removeClass('selected'); + $clicked_currency.addClass('selected'); + }); + }; + + // const onConfirm = ($currency_list, $error, should_create_account) => { + // removeError($error); + // const $selected_currency = $currency_list.find('.selected'); + // if ($selected_currency.length) { + // const selected_currency = $selected_currency.attr('id'); + // let request = {}; + // if (should_create_account) { + // request = populateReqMultiAccount(selected_currency); + // } else { + // request = { set_account_currency: selected_currency }; + // } + // BinarySocket.send(request).then((response_c) => { + // if ($submit) { + // $submit.removeClass('button-disabled'); + // } + // if (response_c.error) { + // if (popup_action === 'multi_account' && /InsufficientAccountDetails|InputValidationFailed/.test(response_c.error.code)) { + // cleanupPopup(); + // setIsForNewAccount(true); + // // ask client to set any missing information + // BinaryPjax.load(Url.urlFor('user/settings/detailsws')); + // } else { + // $error.text(response_c.error.message).setVisibility(1); + // } + // } else { + // const previous_currency = Client.get('currency'); + // // Use the client_id while creating a new account + // const new_account_loginid = popup_action === 'multi_account' ? response_c.new_account_real.client_id : undefined; + // Client.set('currency', selected_currency, new_account_loginid); + // BinarySocket.send({ balance: 1 }); + // BinarySocket.send({ payout_currencies: 1 }, { forced: true }); + // Header.displayAccountStatus(); + + // if (typeof onConfirmAdditional === 'function') { + // onConfirmAdditional(); + // } + + // let redirect_url; + // if (is_new_account) { + // if (Client.isAccountOfType('financial')) { + // const get_account_status = State.getResponse('get_account_status'); + // if (!/authenticated/.test(get_account_status.status)) { + // redirect_url = Url.urlFor('user/authenticate'); + // } + // } + // // Do not redirect MLT clients to cashier, because they need to set self exclusion before trading + // if (!redirect_url && /^(malta)$/i.test(Client.get('landing_company_shortcode'))) { + // redirect_url = Url.urlFor('user/security/self_exclusionws'); + // } + // // Do not redirect MX clients to cashier, because they need to set max limit before making deposit + // if (!redirect_url && !/^(iom)$/i.test(Client.get('landing_company_shortcode'))) { + // redirect_url = Url.urlFor('cashier'); + // } + // } else if (/[set|change]_currency/.test(popup_action)) { + // const previous_currency_display = Currency.getCurrencyDisplayCode(previous_currency); + // const selected_currency_display = Currency.getCurrencyDisplayCode(selected_currency); + // $('.select_currency').setVisibility(0); + // $('#congratulations_message').html( + // popup_action === 'set_currency' ? + // localize('You have successfully set your account currency to [_1].', [`${selected_currency_display}`]) : + // localize('You have successfully changed your account currency from [_1] to [_2].', [ `${previous_currency_display}`, `${selected_currency_display}` ]) + // ); + // $('.btn_cancel, #deposit_btn, #set_currency, #show_new_account').setVisibility(1); + // $(`#${Client.get('loginid')}`).find('td[datath="Currency"]').text(selected_currency_display); + // } else if (popup_action === 'multi_account') { + // const new_account = response_c.new_account_real; + // localStorage.setItem('is_new_account', 1); + // cleanupPopup(); + // // add new account to store and refresh the page + // Client.processNewAccount({ + // email : Client.get('email'), + // loginid : new_account.client_id, + // token : new_account.oauth_token, + // redirect_url: Url.urlFor('user/set-currency'), + // }); + // return; + // } else { + // redirect_url = BinaryPjax.getPreviousUrl(); + // } + + // if (redirect_url) { + // window.location.href = redirect_url; // load without pjax + // } else { + // Header.populateAccountsList(); // update account title + // $('.select_currency').setVisibility(0); + // $('#deposit_btn') + // .off('click dblclick') + // .on('click dblclick', () => { + // if (popup_action) { + // cleanupPopup(); + // } + // BinaryPjax.load(`${Url.urlFor('cashier/forwardws')}?action=deposit`); + // }) + // .setVisibility(1); + // } + // } + // }); + // } else { + // removeError(null, true); + // $error.text(localize('Please choose a currency')).setVisibility(1); + // } + // }; + + /** + * Remove error text if $error is defined + * Enable confirm button if is_btn_enabled is true + * + * @param {object} $error // error text jquery element + * @param {boolean} is_btn_enabled // Enable button + */ + const removeError = ($error, is_btn_enabled) => { + if ($error){ + $error.setVisibility(0); + } + if ($submit && is_btn_enabled) { + $submit.removeClass('button-disabled'); + } + }; + + // const populateReqMultiAccount = (selected_currency) => { + // const get_settings = State.getResponse('get_settings'); + + // return ({ + // new_account_real : 1, + // currency : selected_currency, + // date_of_birth : moment.utc(+get_settings.date_of_birth * 1000).format('YYYY-MM-DD'), + // salutation : get_settings.salutation, + // first_name : get_settings.first_name, + // last_name : get_settings.last_name, + // address_line_1 : get_settings.address_line_1, + // address_line_2 : get_settings.address_line_2, + // address_city : get_settings.address_city, + // address_state : get_settings.address_state, + // address_postcode : get_settings.address_postcode, + // phone : get_settings.phone, + // account_opening_reason: get_settings.account_opening_reason, + // citizen : get_settings.citizen, + // place_of_birth : get_settings.place_of_birth, + // residence : Client.get('residence'), + // ...(get_settings.tax_identification_number && { + // tax_identification_number: get_settings.tax_identification_number, + // }), + // ...(get_settings.tax_residence && { + // tax_residence: get_settings.tax_residence, + // }), + // }); + // }; + + const cleanupPopup = () => { + localStorage.removeItem('popup_action'); + $('.lightbox').remove(); + }; + + return { + init, + cleanupPopup, + }; +})(); + +module.exports = SetCurrency; diff --git a/src/javascript/app/pages/user/new_account/new_account_modules/financial_detail_form.js b/src/javascript/app/pages/user/new_account/new_account_modules/financial_detail_form.js new file mode 100644 index 0000000000000..9bd9135485af5 --- /dev/null +++ b/src/javascript/app/pages/user/new_account/new_account_modules/financial_detail_form.js @@ -0,0 +1,17 @@ +const getElementById = require('../../../../../_common/common_functions').getElementById; + +const FinancialDetailForm = (() => { + + const init = async (fields) => { + fields.forEach(field => { + getElementById(`${field.section}_section`).setVisibility(1) + getElementById(`${field.id}_row`).setVisibility(1) + }) + }; + + return { + init, + }; +})(); + +module.exports = FinancialDetailForm; diff --git a/src/javascript/app/pages/user/new_account/new_account_modules/personal_detail_form.js b/src/javascript/app/pages/user/new_account/new_account_modules/personal_detail_form.js new file mode 100644 index 0000000000000..902f49fc8df54 --- /dev/null +++ b/src/javascript/app/pages/user/new_account/new_account_modules/personal_detail_form.js @@ -0,0 +1,65 @@ +const SelectMatcher = require('@binary-com/binary-style').select2Matcher; +const moment = require('moment'); +const Client = require('../../../../base/client'); +const generateBirthDate = require('../../../../../app/common/attach_dom/birth_date_picker'); +const BinarySocket = require('../../../../base/socket'); +const getElementById = require('../../../../../_common/common_functions').getElementById; +const makeOption = require('../../../../../_common/common_functions').makeOption; +const State = require('../../../../../_common/storage').State; + +const PersonalDetailForm = (() => { + + const init = async (fields) => { + const el_phone = getElementById('phone') + const residence_list = (await BinarySocket.send({ residence_list: 1 })).residence_list + const client_residence = Client.get('residence') || ''; + + const $options = $('
'); + const $options_with_disabled = $('
'); + residence_list.forEach((residence) => { + $options.append(makeOption({ text: residence.text, value: residence.value })); + $options_with_disabled.append(makeOption({ text: residence.text, value: residence.value, is_disabled: residence.disabled })); + }); + const landing_company = State.getResponse('landing_company'); + + const phone_config = fields.find(field => field.id === 'phone') + if (phone_config) { + const residence_phone_idd = residence_list.find(residence => residence.value === client_residence).phone_idd + $('#phone').val(phone_config.default_value !== '' ? phone_config.default_value : `+${residence_phone_idd}`); + } + + const selects = ['place_of_birth', 'citizen', 'tax_residence'] + const texts = ['first_name', 'last_name', 'tax_identification_number'] + + fields.forEach(field => { + if (selects.includes(field.id)) { + $(`#${field.id}`).html((field.id === 'tax_residence' ? $options_with_disabled : $options).html()).val(field.default_value); + $(`#${field.id}`).select2({ + matcher(params, data) { + return SelectMatcher(params, data); + }, + }); + } + if (texts.includes(field.id)) { + $(`#${field.id}`).text(field.default_value); + $(`#${field.id}`) + .val(field.default_value) // Set value for validation + .attr({ 'data-force': true, 'data-value': field.default_value }); + } + if (['date_of_birth'].includes(field.id)) { + generateBirthDate(landing_company.minimum_age); + if (field.default_value !== '') $(`#${field.id}`) + .attr('data-value', field.default_value) + .val(moment(field.default_value).format('DD MMM, YYYY')); + } + getElementById(`${field.section}_section`).setVisibility(1) + getElementById(`${field.id}_row`).setVisibility(1) + }) + }; + + return { + init, + }; +})(); + +module.exports = PersonalDetailForm; diff --git a/src/javascript/app/pages/user/new_account/new_account_modules/terms_of_use_form.js b/src/javascript/app/pages/user/new_account/new_account_modules/terms_of_use_form.js new file mode 100644 index 0000000000000..f09ed7fd10e5a --- /dev/null +++ b/src/javascript/app/pages/user/new_account/new_account_modules/terms_of_use_form.js @@ -0,0 +1,31 @@ +const getElementById = require('../../../../../_common/common_functions').getElementById; +const State = require('../../../../../_common/storage').State; + +const TermsOfUseForm = (() => { + + const init = async (fields, is_financial) => { + const landing_company = State.getResponse('landing_company'); + console.log({ landing_company }); + const lc_to_upgrade_to = landing_company[is_financial ? 'financial_company' : 'gaming_company'] || landing_company.financial_company; + getElementById('lc-name').innerHTML = lc_to_upgrade_to.name; + getElementById('lc-country').innerHTML = lc_to_upgrade_to.country; + + $('#pep_declaration_note_toggle').off('click').on('click', (e) => { + e.stopPropagation(); + $('#pep_declaration_note_toggle').toggleClass('open'); + $('#pep_declaration_note').slideToggle(); + }); + + fields.forEach(field => { + getElementById(`${field.section}_section`).setVisibility(1) + getElementById(`${field.id}_row`).setVisibility(1) + }) + }; + + return { + init, + }; + +})(); + +module.exports = TermsOfUseForm; diff --git a/src/javascript/app/pages/user/new_account/real_acc_opening.js b/src/javascript/app/pages/user/new_account/real_acc_opening.js deleted file mode 100644 index 6a0cf1247b719..0000000000000 --- a/src/javascript/app/pages/user/new_account/real_acc_opening.js +++ /dev/null @@ -1,70 +0,0 @@ -const BinaryPjax = require('../../../base/binary_pjax'); -const Client = require('../../../base/client'); -const BinarySocket = require('../../../base/socket'); -const AccountOpening = require('../../../common/account_opening'); -const FormManager = require('../../../common/form_manager'); -const getElementById = require('../../../../_common/common_functions').getElementById; -const State = require('../../../../_common/storage').State; - -const RealAccOpening = (() => { - const form_id = '#frm_real'; - - const onLoad = () => { - if (Client.get('residence')) { - BinarySocket.wait('get_settings', 'landing_company', 'get_account_status').then(() => { - if (AccountOpening.redirectAccount()) return; - const is_unwelcome_uk = State.getResponse('get_account_status.status').some(status => status === 'unwelcome') && (/gb/.test(Client.get('residence'))); - - if (State.getResponse('authorize.upgradeable_landing_companies').some(item => item === 'svg')) { - getElementById('risk_disclaimer').setVisibility(1); - } - if (is_unwelcome_uk) { - getElementById('ukgc_age_verification').setVisibility(1); - } - - const get_settings = State.getResponse('get_settings'); - if (get_settings.has_secret_answer) { - $('.security').hide(); - } - - AccountOpening.populateForm(form_id, getValidations, false); - - FormManager.handleSubmit({ - form_selector : form_id, - obj_request : { new_account_real: 1 }, - fnc_response_handler: handleResponse, - }); - }); - } else { - BinaryPjax.loadPreviousUrl(); - } - AccountOpening.showHidePulser(0); - AccountOpening.registerPepToggle(); - }; - - const getValidations = () => { - let validations = AccountOpening.commonValidations().concat(AccountOpening.selectCheckboxValidation(form_id)); - const place_of_birth = State.getResponse('get_settings.place_of_birth'); - if (place_of_birth) { - validations = validations.concat([{ request_field: 'place_of_birth', value: place_of_birth }]); - } - if ( - State.getResponse('authorize.upgradeable_landing_companies') - .some(item => ['malta', 'iom'].some(lc => lc === item)) - ) { - validations = validations.concat([{ selector: '#citizen', validations: ['req'] }]); - } - return validations; - }; - - const handleResponse = response => (AccountOpening.handleNewAccount(response, response.msg_type)); - - const onUnload = () => { AccountOpening.showHidePulser(1); }; - - return { - onLoad, - onUnload, - }; -})(); - -module.exports = RealAccOpening; diff --git a/src/javascript/app/pages/user/new_account/real_account_opening.js b/src/javascript/app/pages/user/new_account/real_account_opening.js new file mode 100644 index 0000000000000..bb0084468b4bf --- /dev/null +++ b/src/javascript/app/pages/user/new_account/real_account_opening.js @@ -0,0 +1,93 @@ +const Client = require('../../../base/client'); +const BinarySocket = require('../../../base/socket'); +const AccountOpening = require('../../../common/account_opening'); +const FormManager = require('../../../common/form_manager'); +const FormProgress = require('../../../common/form_progress'); +const getElementById = require('../../../../_common/common_functions').getElementById; +const param = require('../../../../_common/url').param; +const getSteps = require('./new_account_form_config/wizard-step-config') + +const RealAccountOpening = (() => { + let real_account_signup_target, + steps, + current_step; + + let action_previous_buttons; + + const onLoad = async () => { + real_account_signup_target = param('account_type') + + const residence_list_promise = BinarySocket.send({ residence_list: 1 }) + const account_settings_promise = BinarySocket.send({ get_settings: 1 }) + const financial_assessment_promise = BinarySocket.send({ get_financial_assessment: 1 }) + + const [residence_list_response, account_settings_response, financial_assessment_response] = await Promise.all([residence_list_promise, account_settings_promise, financial_assessment_promise]) + const account_settings = account_settings_response.get_settings + const residence_list = residence_list_response.residence_list + const financial_assessment = financial_assessment_response.get_financial_assessment || {} + const upgrade_info = Client.getUpgradeInfo(); + + action_previous_buttons = document.getElementsByClassName('action_previous'); + Array.from(action_previous_buttons).forEach((item) => { + item.addEventListener('click', onClickPrevious); + }); + + + steps = getSteps({ real_account_signup_target, residence_list, account_settings, upgrade_info, financial_assessment }) + console.log('steps: ', steps); + current_step = 0; + steps.forEach(step => { + step.body_module.init(step.fields, real_account_signup_target === 'maltainvest'); + }) + getElementById('loading').setVisibility(0) + getElementById('real_account_wrapper').setVisibility(1) + renderStep() + + }; + + const renderStep = (previous_step = 0) => { + FormProgress.render('form_progress', steps, current_step) + + if (previous_step >= 0) getElementById(steps[previous_step].body_module_step).setVisibility(0) + getElementById(steps[current_step].body_module_step).setVisibility(1) + + FormManager.init(`#${steps[current_step].body_module_step}_form`, getValidationRules(steps[current_step])); + FormManager.handleSubmit({ + form_selector: `#${steps[current_step].body_module_step}_form`, + get_submitted_data: onStepSubmitted, + }); + } + + const onStepSubmitted = (data) => { + console.log('Data submitted: ', data); + if (current_step == steps.length - 1) { + alert('submit') + } else { + current_step++; + renderStep(current_step - 1) + } + } + + const onClickPrevious = () => { + current_step--; + renderStep(current_step + 1) + } + + const getValidationRules = (step) => { + return step.fields.map(field => { + return ({ + selector: `#${field.id}`, + validations: field.rules, + }) + }) + } + + const onUnload = () => { AccountOpening.showHidePulser(1); }; + + return { + onLoad, + onUnload, + }; +})(); + +module.exports = RealAccountOpening; diff --git a/src/sass/_common/common.scss b/src/sass/_common/common.scss index 79805744e7059..a92bce94d1db2 100755 --- a/src/sass/_common/common.scss +++ b/src/sass/_common/common.scss @@ -194,6 +194,10 @@ label { line-height: 25px; } +.fieldset_margin_top { + margin-top: 2rem; +} + /* used for form label in grid structure */ .form_label { @include BIDIR_VALUE(text-align, right, left); diff --git a/src/sass/_common/form_progress.scss b/src/sass/_common/form_progress.scss new file mode 100644 index 0000000000000..b85bc997a9d8e --- /dev/null +++ b/src/sass/_common/form_progress.scss @@ -0,0 +1,74 @@ +.form-progress { + width: 100%; + + &__header { + align-items: center; + display: flex; + flex-direction: column; + justify-content: center; + margin-bottom: 3.2rem; + } + &__step { + align-items: center; + display: flex; + flex-direction: column; + height: 6rem; + justify-content: space-around; + width: 160px; + + & .identifier { + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + width: 40px; + height: 40px; + background-color: $COLOR_LIGHT_GRAY; + border: 1px solid $COLOR_LIGHT_GRAY; + color: $COLOR_BLACK; + line-height: 40px; + z-index: 2; + + &--active { + background-color: $COLOR_ORANGE !important; + color: $COLOR_WHITE; + border: 1px solid $COLOR_ORANGE !important; + transition: all 0.3s ease; + } + } + &-title { + font-size: $FONT_SIZE_XS; + } + } + &__steps { + align-items: center; + display: flex; + justify-content: center; + margin-top: 20px; + position: relative; + + &--before { + bottom: 0; + content: ''; + left: 0; + margin: 0 auto; /* this centers the line to the full width specified */ + position: absolute; /* positioning must be absolute here, and relative positioning must be applied to the parent */ + right: 0; + top: 1.4rem; + border-top: 2px solid $COLOR_LIGHT_GRAY; + z-index: 0; + } + &--after { + border-top: 2px solid $COLOR_ORANGE; + bottom: 0; + content: ''; + left: 0; + margin: 0 auto; /* this centers the line to the full width specified */ + position: absolute; /* positioning must be absolute here, and relative positioning must be applied to the parent */ + top: 1.4rem; + transition: width 0.3s ease; + transform: translateX(100px); + z-index: 1; + } + } +} diff --git a/src/sass/common.scss b/src/sass/common.scss index 7fd1f4cbe14cf..7ea5a4079020c 100755 --- a/src/sass/common.scss +++ b/src/sass/common.scss @@ -11,6 +11,7 @@ @import '_common/jquery_elements'; @import '_common/table'; @import '_common/img_lightbox'; +@import '_common/form_progress.scss'; /* language based rules */ @import '_common/base/language_base'; diff --git a/src/templates/_common/components/forms_common_rows.jsx b/src/templates/_common/components/forms_common_rows.jsx index fbe5d406ece12..fb9e560d5257a 100644 --- a/src/templates/_common/components/forms_common_rows.jsx +++ b/src/templates/_common/components/forms_common_rows.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React from 'react'; import { Fieldset, FormRow } from './forms.jsx'; export const Salutation = ({ className, row_class, row_id }) => ( @@ -53,14 +53,26 @@ export const DateOfBirth = ({ className, row_class, row_id }) => ( /> ); -export const Citizenship = ({ className, row_class }) => ( +export const PlaceOfBirth = ({ className, row_class, row_id }) => ( + +); + +export const Citizenship = ({ className, row_class, row_id }) => ( ); @@ -78,7 +90,7 @@ export const Residence = ({ className, row_class, row_id }) => ( ); -export const AccountOpeningReason = ({ row_id, row_class }) => ( +export const AccountOpeningReason = ({ row_id, row_class }) => ( ( ); -export const AddressLine1 = ({ hint }) => ( +export const TaxResidence = ({ className, row_class, row_id }) => ( + +); + +export const TaxIdentificationNumber = ({ className, hint, row_class, row_id }) => ( + +); + + +export const TaxIdentificationConfirm = ({ className, hint, row_class, row_id }) => ( +
+
+ + +
+
+); + + + +export const AddressLine1 = ({ hint, row_class, row_id }) => ( ( label={it.L('First line')} attributes={{ maxLength: '70', 'data-lpignore': true }} hint={hint} + row_class={row_class} + row_id={row_id} /> ); -export const AddressLine2 = ({ hint }) => ( +export const AddressLine2 = ({ hint, row_class, row_id }) => ( ( label={it.L('Second line')} attributes={{ maxLength: '70', 'data-lpignore': true }} hint={hint} + row_class={row_class} + row_id={row_id} /> ); -export const AddressCity = ({ hint }) => ( +export const AddressCity = ({ hint, row_class, row_id }) => ( ( label={it.L('Town/City')} attributes={{ maxLength: 35, 'data-lpignore': true }} hint={hint} + row_class={row_class} + row_id={row_id} /> ); -export const AddressState = () => ( - +export const AddressState = (row_class, row_id) => ( + ); -export const AddressPostcode = ({ children, hint }) => ( +export const AddressPostcode = ({ children, hint, row_class, row_id }) => ( ( hint={hint} has_geovalidator row_class='postcode-form-row' + row_class={row_class} + row_id={row_id} > {children} @@ -182,21 +247,17 @@ export const SecretAnswer = () => ( ); export const Tnc = () => ( -
-
-
- - -
+
); @@ -209,11 +270,11 @@ export const Jurisdiction = () => ( ); export const RiskDisclaimer = () => ( -
+

{it.L('The financial trading services contained within this site are only suitable for customers who accept the possibility of losing all the money they invest and who understand and have experience of the risk involved in the acquisition of financial contracts. Transactions in financial contracts carry a high degree of risk. If purchased contracts expire worthless, you will suffer a total loss of your investment, which consists of the contract premium.')}

-
+
); export const ClientMessage = () => ( diff --git a/src/templates/app/_includes/new_account_steps/address_detail_form.jsx b/src/templates/app/_includes/new_account_steps/address_detail_form.jsx new file mode 100644 index 0000000000000..fce0411090eca --- /dev/null +++ b/src/templates/app/_includes/new_account_steps/address_detail_form.jsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { + AddressLine1, + AddressLine2, + AddressCity, + AddressState, + AddressPostcode, +} from '../../../_common/components/forms_common_rows.jsx'; + +const AddressDetailForm = () => ( +
+
+
+

{it.L('Only use an address for which you have proof of residence - ')}

+

{it.L('Only use an address for which you have proof of residence - a recent utility bill (e.g. electricity, water, gas, landline, or internet), bank statement, or government-issued letter with your name and this address.')}

+
+ + + + + + +
+
+ {it.L('Previous')} + +
+
+); + +export default AddressDetailForm; diff --git a/src/templates/app/_includes/new_account_steps/currency_form.jsx b/src/templates/app/_includes/new_account_steps/currency_form.jsx new file mode 100644 index 0000000000000..73915cdbaf598 --- /dev/null +++ b/src/templates/app/_includes/new_account_steps/currency_form.jsx @@ -0,0 +1,36 @@ +import React from 'react'; + +const Currencies = ({ text, id }) => ( + +
+
+
{text}
+
+
+
+
+
+ +); + +const CurrencyForm = () => ( + +
+
+
+
+ + +
+

+

+ +
+
+
+ +
+
+); + +export default CurrencyForm; diff --git a/src/templates/app/_includes/new_account_steps/financial_info_form.jsx b/src/templates/app/_includes/new_account_steps/financial_info_form.jsx new file mode 100644 index 0000000000000..fe355fedfa39b --- /dev/null +++ b/src/templates/app/_includes/new_account_steps/financial_info_form.jsx @@ -0,0 +1,161 @@ +import React from 'react'; +import { Fieldset, FormRow } from '../../../_common/components/forms.jsx'; + +const Experience = () => ( + + + + + + +); + +const Frequency = () => ( + + + + + + + +); + +const SelectRow = ({ id, label, con }) => ( + {con()} +); + +const Values = () => ( + + + + + + + + +); + +const TradingExperienceForm = () => ( +
+ + + + + + + + + + + +
+); + +const FinancialInformationForm = () => ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+); + +const FinancialInfoForm = () => ( + +
+ + +
+ {it.L('Previous')} + +
+ +
+); + +export default FinancialInfoForm; diff --git a/src/templates/app/_includes/new_account_steps/personal_detail_form.jsx b/src/templates/app/_includes/new_account_steps/personal_detail_form.jsx new file mode 100644 index 0000000000000..9a8bda7588c6d --- /dev/null +++ b/src/templates/app/_includes/new_account_steps/personal_detail_form.jsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { + Salutation, + FirstName, + LastName, + DateOfBirth, + Citizenship, + PlaceOfBirth, + AccountOpeningReason, + Phone, + TaxResidence, + TaxIdentificationNumber, + TaxIdentificationConfirm, +} from '../../../_common/components/forms_common_rows.jsx'; +import { Fieldset } from '../../../_common/components/forms.jsx'; + + + +const PersonalDetailForm = () => ( + +
+
+ + + +
+
+ + + + +
+
+ + + +
+
+ +
+ +
+ {it.L('Previous')} + +
+
+
+); + +export default PersonalDetailForm; diff --git a/src/templates/app/_includes/new_account_steps/terms_of_use_form.jsx b/src/templates/app/_includes/new_account_steps/terms_of_use_form.jsx new file mode 100644 index 0000000000000..96c87f6a4f23c --- /dev/null +++ b/src/templates/app/_includes/new_account_steps/terms_of_use_form.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import PepDeclaration from '../../_includes/pep_declaration.jsx'; +import ProfessionalClient from '../../_includes/professional_client.jsx'; +import { + Jurisdiction, + RiskDisclaimer, + Tnc, +} from '../../../_common/components/forms_common_rows.jsx'; + +const TermsOfUseForm = () => ( + +
+ + + + + +
+
+

{it.L('Appropriateness Test: WARNING: In providing our services to you, we are required to obtain information from you in order to assess whether a given product or service is appropriate for you (that is, whether you possess the experience and knowledge to understand the risks involved).')}

+

{it.L('On the basis of the information provided in relation to your knowledge and experience, we consider that the investments available via this website are not appropriate for you.')}

+

{it.L('By clicking Accept below and proceeding with the Account Opening you should note that you may be exposing yourself to risks (which may be significant, including the risk of loss of the entire sum invested) that you may not have the knowledge and experience to properly assess or mitigate.')}

+

+ + {it.L('Decline')} +

+
+
+
+ {it.L('Previous')} + +
+ +
+); + +export default TermsOfUseForm; diff --git a/src/templates/app/_includes/pep_declaration.jsx b/src/templates/app/_includes/pep_declaration.jsx index ae6cba22dc76d..43ed82f6677c7 100644 --- a/src/templates/app/_includes/pep_declaration.jsx +++ b/src/templates/app/_includes/pep_declaration.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { Fieldset } from '../../_common/components/forms.jsx'; const PepDeclaration = () => ( -
+