diff --git a/packages/wallets/src/components/Dropzone/Dropzone.tsx b/packages/wallets/src/components/Dropzone/Dropzone.tsx index 6e67c5d66f15..d98517c25408 100644 --- a/packages/wallets/src/components/Dropzone/Dropzone.tsx +++ b/packages/wallets/src/components/Dropzone/Dropzone.tsx @@ -17,6 +17,7 @@ import './Dropzone.scss'; type TProps = { buttonText?: ReactNode; + defaultFile?: File; description?: ReactNode; descriptionColor?: ComponentProps['color']; descriptionSize?: ComponentProps['size']; @@ -31,8 +32,15 @@ type TProps = { titleType?: ComponentProps['weight']; }; +type TFile = { + file: File; + name: string; + preview: string; +}; + const Dropzone: React.FC = ({ buttonText, + defaultFile, description, descriptionColor = 'general', descriptionSize = 'md', @@ -46,13 +54,9 @@ const Dropzone: React.FC = ({ title = false, titleType = 'normal', }) => { - const [files, setFiles] = useState< - { - file: File; - name: string; - preview: string; - }[] - >([]); + const [file, setFile] = useState( + defaultFile ? { file: defaultFile, name: defaultFile.name, preview: URL.createObjectURL(defaultFile) } : null + ); const [showHoverMessage, setShowHoverMessage] = useState(false); const [errorMessage, setErrorMessage] = useState(null); const { getInputProps, getRootProps, open, rootRef } = useDropzone({ @@ -64,14 +68,14 @@ const Dropzone: React.FC = ({ onDragLeave: () => setShowHoverMessage(false), onDrop: acceptedFiles => { setShowHoverMessage(false); - setFiles( - acceptedFiles.map(file => - Object.assign(file, { - file, - preview: URL.createObjectURL(file), - }) - ) - ); + const acceptedFile = acceptedFiles?.[0]; + if (acceptedFile) { + setFile({ + file: acceptedFile, + name: acceptedFile.name, + preview: URL.createObjectURL(acceptedFile), + }); + } }, onDropAccepted() { setErrorMessage(null); @@ -82,17 +86,14 @@ const Dropzone: React.FC = ({ }); useEffect(() => { - if (files.length > 0 && onFileChange) { - onFileChange(files[0].file); + if (file && onFileChange) { + onFileChange(file.file); } - }, [files]); // eslint-disable-line react-hooks/exhaustive-deps + }, [file]); // eslint-disable-line react-hooks/exhaustive-deps - const removeFile = useCallback( - (file: { name: string; preview: string }) => () => { - setFiles(prev => prev.filter(f => f.name !== file.name)); - }, - [] - ); + const removeFile = useCallback(() => { + setFile(null); + }, []); return (
}> @@ -103,12 +104,12 @@ const Dropzone: React.FC = ({ className={classNames( 'wallets-dropzone', { 'wallets-dropzone--hover': showHoverMessage }, - { 'wallets-dropzone--active': files.length > 0 } + { 'wallets-dropzone--active': file } )} >
{showHoverMessage && {hoverMessage}} - {!showHoverMessage && !files.length && ( + {!showHoverMessage && !file && (
{icon}
{title && ( @@ -131,31 +132,29 @@ const Dropzone: React.FC = ({ )}
)} - {!showHoverMessage && - files.length > 0 && - files.map(file => ( - -
- {hasFrame && } - } - onClick={removeFile(file)} - size='sm' - /> -
- {description && ( - - {description} - - )} -
- ))} + {!showHoverMessage && file && ( + +
+ {hasFrame && } + } + onClick={removeFile} + size='sm' + /> +
+ {description && ( + + {description} + + )} +
+ )}
diff --git a/packages/wallets/src/features/accounts/screens/ManualDocumentUpload/components/DrivingLicenseDocumentUpload/DrivingLicenseDocumentUpload.tsx b/packages/wallets/src/features/accounts/screens/ManualDocumentUpload/components/DrivingLicenseDocumentUpload/DrivingLicenseDocumentUpload.tsx index b7f59741cb86..12d1e24c5c78 100644 --- a/packages/wallets/src/features/accounts/screens/ManualDocumentUpload/components/DrivingLicenseDocumentUpload/DrivingLicenseDocumentUpload.tsx +++ b/packages/wallets/src/features/accounts/screens/ManualDocumentUpload/components/DrivingLicenseDocumentUpload/DrivingLicenseDocumentUpload.tsx @@ -2,18 +2,30 @@ import React from 'react'; import { Divider, Dropzone, FlowTextField, useFlow, WalletText } from '../../../../../../components'; import DrivingLicenseCardBack from '../../../../../../public/images/accounts/document-back.svg'; import DrivingLicenseCardFront from '../../../../../../public/images/accounts/driving-license-front.svg'; +import { documentRequiredValidator, expiryDateValidator } from '../../../../validations'; import { DocumentRuleHints } from '../DocumentRuleHints'; import './DrivingLicenseDocumentUpload.scss'; const DrivingLicenseDocumentUpload = () => { - const { setFormValues } = useFlow(); + const { formValues, setFormValues } = useFlow(); return (
First, enter your Driving licence number and the expiry date.
- - + +
@@ -22,6 +34,7 @@ const DrivingLicenseDocumentUpload = () => {
} @@ -32,6 +45,7 @@ const DrivingLicenseDocumentUpload = () => {
} diff --git a/packages/wallets/src/features/accounts/screens/ManualDocumentUpload/components/IdentityCardDocumentUpload/IdentityCardDocumentUpload.tsx b/packages/wallets/src/features/accounts/screens/ManualDocumentUpload/components/IdentityCardDocumentUpload/IdentityCardDocumentUpload.tsx index 9ead1c44ca89..e1e69b7dfc90 100644 --- a/packages/wallets/src/features/accounts/screens/ManualDocumentUpload/components/IdentityCardDocumentUpload/IdentityCardDocumentUpload.tsx +++ b/packages/wallets/src/features/accounts/screens/ManualDocumentUpload/components/IdentityCardDocumentUpload/IdentityCardDocumentUpload.tsx @@ -2,18 +2,30 @@ import React from 'react'; import { Divider, Dropzone, FlowTextField, useFlow, WalletText } from '../../../../../../components'; import IdentityCardBack from '../../../../../../public/images/accounts/document-back.svg'; import IdentityCardFront from '../../../../../../public/images/accounts/identity-card-front.svg'; +import { documentRequiredValidator, expiryDateValidator } from '../../../../validations'; import { DocumentRuleHints } from '../DocumentRuleHints'; import './IdentityCardDocumentUpload.scss'; const IdentityCardDocumentUpload = () => { - const { setFormValues } = useFlow(); + const { formValues, setFormValues } = useFlow(); return (
First, enter your Identity card number and the expiry date.
- - + +
@@ -22,6 +34,7 @@ const IdentityCardDocumentUpload = () => {
} @@ -32,6 +45,7 @@ const IdentityCardDocumentUpload = () => {
} diff --git a/packages/wallets/src/features/accounts/screens/ManualDocumentUpload/components/NIMCSlipDocumentUpload/NIMCSlipDocumentUpload.tsx b/packages/wallets/src/features/accounts/screens/ManualDocumentUpload/components/NIMCSlipDocumentUpload/NIMCSlipDocumentUpload.tsx index b320296aa0e3..b18a8299e046 100644 --- a/packages/wallets/src/features/accounts/screens/ManualDocumentUpload/components/NIMCSlipDocumentUpload/NIMCSlipDocumentUpload.tsx +++ b/packages/wallets/src/features/accounts/screens/ManualDocumentUpload/components/NIMCSlipDocumentUpload/NIMCSlipDocumentUpload.tsx @@ -2,16 +2,22 @@ import React from 'react'; import { Divider, Dropzone, FlowTextField, useFlow, WalletText } from '../../../../../../components'; import NIMCSlipFront from '../../../../../../public/images/accounts/nimc-slip-front.svg'; import ProofOfAgeIcon from '../../../../../../public/images/accounts/proof-of-age.svg'; +import { documentRequiredValidator } from '../../../../validations'; import { DocumentRuleHints } from '../DocumentRuleHints'; import './NIMCSlipDocumentUpload.scss'; const NIMCSlipDocumentUpload = () => { - const { setFormValues } = useFlow(); + const { formValues, setFormValues } = useFlow(); return (
First, enter your NIMC slip number. - +
Next, upload both of the following documents. @@ -19,6 +25,7 @@ const NIMCSlipDocumentUpload = () => {
} @@ -29,6 +36,7 @@ const NIMCSlipDocumentUpload = () => {
} diff --git a/packages/wallets/src/features/accounts/screens/ManualDocumentUpload/components/PassportDocumentUpload/PassportDocumentUpload.tsx b/packages/wallets/src/features/accounts/screens/ManualDocumentUpload/components/PassportDocumentUpload/PassportDocumentUpload.tsx index 7a382d925615..ed8840a8b2c1 100644 --- a/packages/wallets/src/features/accounts/screens/ManualDocumentUpload/components/PassportDocumentUpload/PassportDocumentUpload.tsx +++ b/packages/wallets/src/features/accounts/screens/ManualDocumentUpload/components/PassportDocumentUpload/PassportDocumentUpload.tsx @@ -2,24 +2,38 @@ import React from 'react'; import { Dropzone, FlowTextField, useFlow } from '../../../../../../components'; import { Divider, WalletText } from '../../../../../../components/Base'; import PassportPlaceholder from '../../../../../../public/images/accounts/passport-placeholder.svg'; +import { documentRequiredValidator, expiryDateValidator } from '../../../../validations'; import { DocumentRuleHints } from '../DocumentRuleHints'; import './PassportDocumentUpload.scss'; const PassportDocumentUpload = () => { - const { setFormValues } = useFlow(); + const { formValues, setFormValues } = useFlow(); return (
First, enter your Passport number and the expiry date.
- - + +
Next, upload the page of your passport that contains your photo. } diff --git a/packages/wallets/src/features/accounts/validations.ts b/packages/wallets/src/features/accounts/validations.ts index db5b0bd3ed16..8b79310459ee 100644 --- a/packages/wallets/src/features/accounts/validations.ts +++ b/packages/wallets/src/features/accounts/validations.ts @@ -10,15 +10,26 @@ export const passportValidator = Yup.string() .max(8) .required('Please enter your Passport number. Example: G1234567'); +export const documentRequiredValidator = (documentType: string) => Yup.string().required(`${documentType} is required`); + export const ssnitValidator = Yup.string() .matches(/^[A-Z]\d{12}$/, 'Please enter the correct format. Example: C123456789012') .max(13) .required('Please enter your SSNIT number. Example: C123456789012'); +export const nimcSlipValidator = Yup.string().matches( + /^\d{11}$/, + 'Please enter your document number. Example: 12345678901' +); + export const requiredValidator = Yup.string().required('This field is required'); export const dateOfBirthValidator = Yup.date().required('Please enter your date of birth'); +export const expiryDateValidator = Yup.date() + .min(new Date(), 'Expiry date cannot be today date or in the past') + .required('Expiry date is required'); + export const firstNameValidator = Yup.string().required('Please enter your first name'); export const lastNameValidator = Yup.string().required('Please enter your last name'); diff --git a/packages/wallets/src/features/cfd/flows/Verification/Verification.tsx b/packages/wallets/src/features/cfd/flows/Verification/Verification.tsx index 340adc2607f4..655acbc39351 100644 --- a/packages/wallets/src/features/cfd/flows/Verification/Verification.tsx +++ b/packages/wallets/src/features/cfd/flows/Verification/Verification.tsx @@ -38,6 +38,42 @@ type TVerificationProps = { selectedJurisdiction: THooks.AvailableMT5Accounts['shortcode']; }; +type TManualVerificationFooter = { + context: TFlowProviderContext; + isNextDisabled: boolean; + isNextLoading: boolean; + nextFlowHandler: () => void; +}; + +const getManualVerificationFooter = ({ + context, + isNextDisabled, + isNextLoading, + nextFlowHandler, +}: TManualVerificationFooter) => { + if (!context.formValues.selectedManualDocument) return undefined; + + const onClickBack = () => { + if (context.currentScreenId === 'selfieScreen') { + context.switchScreen('manualScreen'); + } else context.setFormValues('selectedManualDocument', ''); + }; + + // eslint-disable-next-line react/display-name + return () => ( + + + + + ); +}; + const Verification: FC = ({ selectedJurisdiction }) => { const { data: poiStatus, isSuccess: isSuccessPOIStatus } = usePOI(); const { data: poaStatus, isSuccess: isSuccessPOAStatus } = usePOA(); @@ -100,35 +136,23 @@ const Verification: FC = ({ selectedJurisdiction }) => { !formValues.drivingLicenceNumber || !formValues.drivingLicenseExpiryDate || !formValues.drivingLicenseCardFront || - !formValues.drivingLicenseCardBack || - isManualUploadLoading + !formValues.drivingLicenseCardBack ); } else if (formValues.selectedManualDocument === 'passport') { - return ( - !formValues.passportNumber || - !formValues.passportExpiryDate || - !formValues.passportCard || - isManualUploadLoading - ); + return !formValues.passportNumber || !formValues.passportExpiryDate || !formValues.passportCard; } else if (formValues.selectedManualDocument === 'identity-card') { return ( !formValues.identityCardNumber || !formValues.identityCardExpiryDate || !formValues.identityCardFront || - !formValues.identityCardBack || - isManualUploadLoading + !formValues.identityCardBack ); } else if (formValues.selectedManualDocument === 'nimc-slip') { - return ( - !formValues.nimcNumber || - !formValues.nimcCardFront || - !formValues.nimcCardBack || - isManualUploadLoading - ); + return !formValues.nimcNumber || !formValues.nimcCardFront || !formValues.nimcCardBack; } return !formValues.selectedManualDocument; case 'selfieScreen': - return !formValues.selfie; + return !formValues.selfie || isManualUploadLoading || isUploadLoading; case 'onfidoScreen': return !formValues.hasSubmittedOnfido; case 'personalDetailsScreen': @@ -166,11 +190,13 @@ const Verification: FC = ({ selectedJurisdiction }) => { last_name: formValues.lastName, }); } else if (currentScreenId === 'selfieScreen') { + await uploadDocument(formValues); await upload({ document_issuing_country: settings?.country_code ?? undefined, document_type: 'selfie_with_id', file: formValues.selfie, }); + setFormValues('selectedManualDocument', ''); } // handle screen switching @@ -182,8 +208,6 @@ const Verification: FC = ({ selectedJurisdiction }) => { show(); } } else if (currentScreenId === 'manualScreen') { - await uploadDocument(formValues); - setFormValues('selectedManualDocument', ''); switchScreen('selfieScreen'); } else if (currentScreenId === 'poaScreen') { updateSettings({ @@ -234,39 +258,22 @@ const Verification: FC = ({ selectedJurisdiction }) => { return ( { - if (context.currentScreenId === 'manualScreen') - return ( - - - context.setFormValues('selectedManualDocument', '') - } - size='lg' - text='Back' - variant='outlined' - /> - nextFlowHandler(context)} - size='lg' - text='Next' - /> - - ); - return ( - nextFlowHandler(context)} - size='lg' - text='Next' - /> - ); - } + context.currentScreenId === 'manualScreen' || context.currentScreenId === 'selfieScreen' + ? getManualVerificationFooter({ + context, + isNextDisabled: isNextDisabled(context), + isNextLoading: isNextLoading(context), + nextFlowHandler: () => nextFlowHandler(context), + }) + : () => ( + nextFlowHandler(context)} + size='lg' + text='Next' + /> + ) } title='Add a real MT5 account' > diff --git a/packages/wallets/src/public/images/accounts/driving-license-front.svg b/packages/wallets/src/public/images/accounts/driving-license-front.svg index d3c0303fbbf3..1de0fcba9197 100644 --- a/packages/wallets/src/public/images/accounts/driving-license-front.svg +++ b/packages/wallets/src/public/images/accounts/driving-license-front.svg @@ -1 +1,9 @@ - \ No newline at end of file + + + + + + + + +