diff --git a/packages/account/build/webpack.config.js b/packages/account/build/webpack.config.js index e28c13beb8af..0c6d04607b6f 100644 --- a/packages/account/build/webpack.config.js +++ b/packages/account/build/webpack.config.js @@ -50,6 +50,7 @@ module.exports = function (env) { 'proof-of-address-container': 'Sections/Verification/ProofOfAddress/proof-of-address-container.jsx', 'reset-trading-password-modal': 'Components/reset-trading-password-modal', 'self-exclusion': 'Components/self-exclusion', + 'selfie-upload': 'Components/poi/status/unsupported/card-details/selfie-upload.jsx', 'scrollbars-container': 'Components/scrollbars-container', 'sent-email-modal': 'Components/sent-email-modal', 'terms-of-use': 'Components/terms-of-use', diff --git a/packages/account/src/Components/poi/status/unsupported/card-details/selfie-upload.jsx b/packages/account/src/Components/poi/status/unsupported/card-details/selfie-upload.jsx index aee638f92191..14b33fe0c78c 100644 --- a/packages/account/src/Components/poi/status/unsupported/card-details/selfie-upload.jsx +++ b/packages/account/src/Components/poi/status/unsupported/card-details/selfie-upload.jsx @@ -9,7 +9,36 @@ import Uploader from './uploader.jsx'; import { setInitialValues, validateFields } from './utils'; import { ROOT_CLASS, SELFIE_DOCUMENT } from '../constants'; -const SelfieUpload = ({ initial_values, goBack, onConfirm, onFileDrop }) => { +const PaymentAgentSignupSelfieHeader = () => { + return ( + <> + {!isMobile() ? ( + + {localize('Selfie verification')} + + ) : null} + + {localize("First, we'll need to verify your identity. Please upload your selfie here.")} + + + ); +}; + +const SelfieUpload = ({ + dispatch, + initial_values, + is_pa_signup, + goBack, + onConfirm, + onFileDrop, + setSelfieStepEnabled, +}) => { + const [formik_values, setFormikValues] = React.useState({}); + + React.useEffect(() => { + dispatch?.(setSelfieStepEnabled(!!formik_values[SELFIE_DOCUMENT.name])); + }, [formik_values, dispatch, setSelfieStepEnabled]); + return (
{ initialValues={initial_values || setInitialValues([SELFIE_DOCUMENT])} validate={values => validateFields(values, undefined, [SELFIE_DOCUMENT])} onSubmit={onConfirm} + innerRef={formik_actions => is_pa_signup && setFormikValues(formik_actions?.values || {})} > {({ values, isValid, isSubmitting, touched }) => { const is_form_touched = Object.keys(touched).length > 0; @@ -28,9 +58,13 @@ const SelfieUpload = ({ initial_values, goBack, onConfirm, onFileDrop }) => { return (
- - {localize('Upload your selfie')} - + {is_pa_signup ? ( + + ) : ( + + {localize('Upload your selfie')} + + )}
{ />
- - {localize( - 'Before uploading, please ensure that you’re facing forward in the selfie, your face is within the frame, and your eyes are clearly visible even if you’re wearing glasses.' - )} - + {is_pa_signup ? ( + + {localize( + 'Face forward and remove your glasses if necessary. Make sure your eyes are clearly visible and your face is within the frame.' + )} + + ) : ( + + {localize( + 'Before uploading, please ensure that you’re facing forward in the selfie, your face is within the frame, and your eyes are clearly visible even if you’re wearing glasses.' + )} + + )}
-
-
+ {!is_pa_signup && ( +
+
+ )}
); }} @@ -73,9 +117,15 @@ const SelfieUpload = ({ initial_values, goBack, onConfirm, onFileDrop }) => { }; SelfieUpload.propTypes = { + dispatch: PropTypes.func, initial_values: PropTypes.object, + is_pa_signup: PropTypes.bool, goBack: PropTypes.func, + onFileDrop: PropTypes.func, onConfirm: PropTypes.func, + setSelfieStepEnabled: PropTypes.func, }; +PaymentAgentSignupSelfieHeader.displayName = 'PaymentAgentSignupSelfieHeader'; + export default SelfieUpload; diff --git a/packages/account/src/Components/poi/status/unsupported/constants.js b/packages/account/src/Components/poi/status/unsupported/constants.js index fea16504f784..a839357a88ec 100644 --- a/packages/account/src/Components/poi/status/unsupported/constants.js +++ b/packages/account/src/Components/poi/status/unsupported/constants.js @@ -1,6 +1,6 @@ import { localize } from '@deriv/translations'; -export const ROOT_CLASS = 'manual-poi-details'; +export const ROOT_CLASS = window.location.pathname.includes('cashier') ? 'pa-signup-selfie' : 'manual-poi-details'; export const DOCUMENT_TYPES = { NATIONAL_IDENTITY_CARD: 'national_identity_card', diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-signup/signup-wizard-steps/selfie-step/selfie-step.tsx b/packages/cashier/src/pages/payment-agent/payment-agent-signup/signup-wizard-steps/selfie-step/selfie-step.tsx new file mode 100644 index 000000000000..fc620e1707f3 --- /dev/null +++ b/packages/cashier/src/pages/payment-agent/payment-agent-signup/signup-wizard-steps/selfie-step/selfie-step.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { SelfieUpload } from '@deriv/account'; +import { setSelfie, setSelfieStepEnabled, TActionsTypes } from '../../signup-wizard/steps-reducer'; +import './selfie.scss'; + +type TSelfieError = { + code: string; + message: string; +}; + +type TSelfieFile = { + lastModified: number; + lastModifiedDate: Date; + name: string; + path: string; + size: number; + type: string; + webkitRelativePath: string; +}; + +export type TSelfie = { + document_type: string; + errors: TSelfieError[] | []; + file: TSelfieFile; + icon: string; + info: string; + name: string; + pageType: string; +}; + +type TSelfieStep = { + selfie: { selfie_with_id: TSelfie } | null; + dispatch: React.Dispatch; +}; + +const SelfieStep = ({ selfie, dispatch }: TSelfieStep) => { + const onFileDrop = (value: TSelfie) => dispatch(setSelfie({ selfie_with_id: value })); + + //TODO: change the description for the selfie depending on the step number + return ( + + ); +}; + +export default SelfieStep; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-signup/signup-wizard-steps/selfie-step/selfie.scss b/packages/cashier/src/pages/payment-agent/payment-agent-signup/signup-wizard-steps/selfie-step/selfie.scss new file mode 100644 index 000000000000..ec7eec1c83f7 --- /dev/null +++ b/packages/cashier/src/pages/payment-agent/payment-agent-signup/signup-wizard-steps/selfie-step/selfie.scss @@ -0,0 +1,203 @@ +.pa-signup-selfie { + max-width: 67.2rem; + height: 100%; + display: flex; + + &--mobile { + padding: 0; + } + + h3 { + margin-bottom: 2.5rem; + } + + h2 { + margin-bottom: 2.4rem; + } + &__form { + flex: 1; + display: flex; + flex-direction: column; + } + &__fields-content { + flex: auto; + } + &__fields-wrap { + display: flex; + margin-bottom: 0.7rem; + + .pa-signup-selfie--mobile & { + flex-wrap: wrap; + } + } + &__field { + margin-bottom: 3.5rem; + + &:first-of-type { + margin-right: 2.4rem; + + .pa-signup-selfie--mobile & { + margin-right: 0; + } + } + } + &__divider { + height: 2px; + background-color: var(--general-section-1); + margin-bottom: 2.4rem; + + &--m16 { + margin-bottom: 1.6rem; + } + } + &__uploaders-wrap { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + + .dc-file-dropzone__message { + width: 100%; + max-width: none; + pointer-events: all; + } + } + //new css + &__uploader { + position: relative; + width: 100%; + max-width: 42.4rem; + height: 25.8rem; + margin: 3.2rem auto 1.6rem; + + @include mobile { + margin-top: 2.4rem; + } + + //new css + .dc-file-dropzone { + border: 2px dashed var(--border-normal); + } + + .pa-signup-selfie--mobile & { + max-width: none; + } + } + &__uploader-details { + width: 100%; + display: flex; + align-items: center; + flex-direction: column; + justify-content: space-between; + padding: 0 1.5rem; + min-height: 19.2rem; + + &--preview { + padding: 0 2.4rem; + max-height: 20.3rem; + } + + p { + margin-bottom: 2.4rem; + + @at-root .pa-signup-selfie__uploader-details--preview p { + margin-bottom: 0; + } + } + } + &__uploader-image { + position: relative; + width: 100%; + max-width: 27.6rem; + height: 16.6rem; + border-radius: 0.8rem; + background-size: cover; + background-repeat: no-repeat; + background-position: center center; + margin-bottom: 1.6rem; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + &--has-frame { + background-size: contain; + } + + .pa-signup-selfie--mobile & { + height: 13.4rem; + } + } + &__uploader-frame { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + width: 100%; + height: auto; + } + &__uploader-remove { + position: absolute; + top: 0.8rem; + right: 0.8rem; + cursor: pointer; + transition: transform 0.25s linear; + + &:hover { + transform: scale(1.25, 1.25); + } + } + &__uploader-icon { + width: 12.8rem; + height: 7.9rem; + margin-bottom: 1.5rem; + } + &__icons { + display: flex; + flex-wrap: wrap; + margin-bottom: 1.7rem; + } + &__icons-item { + flex: 0.25; + display: flex; + flex-direction: column; + align-items: center; + padding: 0 1rem; + min-height: 9.2rem; + + .pa-signup-selfie--mobile & { + flex: none; + width: 50%; + min-height: 7.6rem; + } + + p { + max-width: 12.4rem; + + .pa-signup-selfie--mobile & { + max-width: 13.2rem; + } + } + + svg { + height: 4.8rem; + } + } + //new css + &__notice { + max-width: 42.4rem; + margin: 0 auto; + } + &__btns { + height: 15.2rem; + flex: none; + border-top: 2px solid var(--general-section-1); + display: flex; + align-items: center; + justify-content: flex-end; + padding-bottom: 8rem; + + .dc-btn:not(:last-of-type) { + margin-right: 0.8rem; + } + } +} diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-signup/signup-wizard/signup-wizard.tsx b/packages/cashier/src/pages/payment-agent/payment-agent-signup/signup-wizard/signup-wizard.tsx index 0e9bf565c5ca..b68f5bb18de3 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-signup/signup-wizard/signup-wizard.tsx +++ b/packages/cashier/src/pages/payment-agent/payment-agent-signup/signup-wizard/signup-wizard.tsx @@ -6,6 +6,8 @@ import { Localize, localize } from '@deriv/translations'; import { Wizard } from '@deriv/ui'; import CancelWizardDialog from '../cancel-wizard-dialog'; import SelectCountryStep from '../signup-wizard-steps/select-country-step'; +import SelfieStep from '../signup-wizard-steps/selfie-step/selfie-step'; +import { stepReducer, initial_state } from './steps-reducer'; import './signup-wizard.scss'; type TSignupWizardProps = { @@ -16,6 +18,9 @@ const SignupWizard = ({ closeWizard }: TSignupWizardProps) => { const [is_cancel_wizard_dialog_active, setIsCancelWizardDialogActive] = React.useState(false); const [current_step_key, setCurrentStepKey] = React.useState(); const [is_country_selected, setIsCountrySelected] = React.useState(false); + + const [steps_state, dispatch] = React.useReducer(stepReducer, initial_state); + const is_final_step = current_step_key === 'complete_step'; const wizard_root_el = document.getElementById('wizard_root'); @@ -69,15 +74,12 @@ const SignupWizard = ({ closeWizard }: TSignupWizardProps) => { > - - <> - - - - - - - + + <> diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-signup/signup-wizard/steps-reducer.ts b/packages/cashier/src/pages/payment-agent/payment-agent-signup/signup-wizard/steps-reducer.ts new file mode 100644 index 000000000000..5db0c7901613 --- /dev/null +++ b/packages/cashier/src/pages/payment-agent/payment-agent-signup/signup-wizard/steps-reducer.ts @@ -0,0 +1,45 @@ +import { TSelfie } from '../signup-wizard-steps/selfie-step/selfie-step'; + +type TStepsState = { + selfie: { + selfie_with_id: TSelfie; + } | null; + is_selfie_step_enabled: boolean; +}; + +const ACTION_TYPES = { + SET_SELFIE: 'SET_SELFIE', + SET_SELFIE_STEP_ENABLED: 'SET_SELFIE_STEP_ENABLED', +} as const; + +// Action creators +export const setSelfie = (value: { selfie_with_id: TSelfie }) => { + return { + type: ACTION_TYPES.SET_SELFIE, + value, + }; +}; + +export const setSelfieStepEnabled = (value: boolean) => { + return { + type: ACTION_TYPES.SET_SELFIE_STEP_ENABLED, + value, + }; +}; + +// Initial state +export const initial_state = { selfie: null, is_selfie_step_enabled: false }; + +// Reducer +export const stepReducer = (state: TStepsState, action: TActionsTypes): TStepsState => { + switch (action.type) { + case ACTION_TYPES.SET_SELFIE: + return { ...state, selfie: action.value }; + case ACTION_TYPES.SET_SELFIE_STEP_ENABLED: + return { ...state, is_selfie_step_enabled: action.value }; + default: + return state; + } +}; + +export type TActionsTypes = ReturnType;