Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 50 additions & 51 deletions packages/wallets/src/components/Dropzone/Dropzone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import './Dropzone.scss';

type TProps = {
buttonText?: ReactNode;
defaultFile?: File;
description?: ReactNode;
descriptionColor?: ComponentProps<typeof WalletText>['color'];
descriptionSize?: ComponentProps<typeof WalletText>['size'];
Expand All @@ -31,8 +32,15 @@ type TProps = {
titleType?: ComponentProps<typeof WalletText>['weight'];
};

type TFile = {
file: File;
name: string;
preview: string;
};

const Dropzone: React.FC<TProps> = ({
buttonText,
defaultFile,
description,
descriptionColor = 'general',
descriptionSize = 'md',
Expand All @@ -46,13 +54,9 @@ const Dropzone: React.FC<TProps> = ({
title = false,
titleType = 'normal',
}) => {
const [files, setFiles] = useState<
{
file: File;
name: string;
preview: string;
}[]
>([]);
const [file, setFile] = useState<TFile | null>(
defaultFile ? { file: defaultFile, name: defaultFile.name, preview: URL.createObjectURL(defaultFile) } : null
);
const [showHoverMessage, setShowHoverMessage] = useState(false);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const { getInputProps, getRootProps, open, rootRef } = useDropzone({
Expand All @@ -64,14 +68,14 @@ const Dropzone: React.FC<TProps> = ({
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);
Expand All @@ -82,17 +86,14 @@ const Dropzone: React.FC<TProps> = ({
});

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 (
<div {...getRootProps()} className='wallets-dropzone__container' ref={rootRef as RefObject<HTMLDivElement>}>
Expand All @@ -103,12 +104,12 @@ const Dropzone: React.FC<TProps> = ({
className={classNames(
'wallets-dropzone',
{ 'wallets-dropzone--hover': showHoverMessage },
{ 'wallets-dropzone--active': files.length > 0 }
{ 'wallets-dropzone--active': file }
)}
>
<div className='wallets-dropzone__content'>
{showHoverMessage && <WalletText size='sm'>{hoverMessage}</WalletText>}
{!showHoverMessage && !files.length && (
{!showHoverMessage && !file && (
<div className='wallets-dropzone__placeholder'>
<div className='wallets-dropzone__placeholder-icon'>{icon}</div>
{title && (
Expand All @@ -131,31 +132,29 @@ const Dropzone: React.FC<TProps> = ({
)}
</div>
)}
{!showHoverMessage &&
files.length > 0 &&
files.map(file => (
<React.Fragment key={file.name}>
<div
className={classNames('wallets-dropzone__thumb', {
'wallets-dropzone__thumb--has-frame': hasFrame,
})}
style={{ backgroundImage: `url(${file.preview})` }}
>
{hasFrame && <DropzoneFrame />}
<IconButton
className='wallets-dropzone__remove-file'
icon={<CloseIcon width={12} />}
onClick={removeFile(file)}
size='sm'
/>
</div>
{description && (
<WalletText align='center' color={descriptionColor}>
{description}
</WalletText>
)}
</React.Fragment>
))}
{!showHoverMessage && file && (
<React.Fragment key={file.name}>
<div
className={classNames('wallets-dropzone__thumb', {
'wallets-dropzone__thumb--has-frame': hasFrame,
})}
style={{ backgroundImage: `url(${file.preview})` }}
>
{hasFrame && <DropzoneFrame />}
<IconButton
className='wallets-dropzone__remove-file'
icon={<CloseIcon width={12} />}
onClick={removeFile}
size='sm'
/>
</div>
{description && (
<WalletText align='center' color={descriptionColor}>
{description}
</WalletText>
)}
</React.Fragment>
)}
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div className='wallets-driving-license-document-upload' data-testid='dt_driving-license-document-upload'>
<WalletText>First, enter your Driving licence number and the expiry date.</WalletText>
<div className='wallets-driving-license-document-upload__input-group'>
<FlowTextField label='Driving licence number*' name='drivingLicenceNumber' />
<FlowTextField label='Expiry date*' name='drivingLicenseExpiryDate' type='date' />
<FlowTextField
defaultValue={formValues.drivingLicenceNumber ?? ''}
label='Driving licence number*'
name='drivingLicenceNumber'
validationSchema={documentRequiredValidator('Driving licence number')}
/>
<FlowTextField
defaultValue={formValues.drivingLicenseExpiryDate ?? ''}
label='Expiry date*'
name='drivingLicenseExpiryDate'
type='date'
validationSchema={expiryDateValidator}
/>
</div>
<Divider />
<div className='wallets-driving-license-document-upload__document-section'>
Expand All @@ -22,6 +34,7 @@ const DrivingLicenseDocumentUpload = () => {
<div className='wallets-driving-license-document-upload__dropzones--left'>
<Dropzone
buttonText='Drop file or click here to upload'
defaultFile={formValues.drivingLicenseCardFront}
description='Upload the front of your driving licence.'
fileFormats={['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'application/pdf']}
icon={<DrivingLicenseCardFront />}
Expand All @@ -32,6 +45,7 @@ const DrivingLicenseDocumentUpload = () => {
<div className='wallets-driving-license-document-upload__dropzones--right'>
<Dropzone
buttonText='Drop file or click here to upload'
defaultFile={formValues.drivingLicenseCardBack}
description='Upload the back of your driving licence.'
fileFormats={['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'application/pdf']}
icon={<DrivingLicenseCardBack />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div className='wallets-identity-card-document-upload' data-testid='dt_identity-card-document-upload'>
<WalletText>First, enter your Identity card number and the expiry date.</WalletText>
<div className='wallets-identity-card-document-upload__input-group'>
<FlowTextField label='Identity card number*' name='identityCardNumber' />
<FlowTextField label='Expiry date*' name='identityCardExpiryDate' type='date' />
<FlowTextField
defaultValue={formValues.identityCardNumber ?? ''}
label='Identity card number*'
name='identityCardNumber'
validationSchema={documentRequiredValidator('Identity card number')}
/>
<FlowTextField
defaultValue={formValues.identityCardExpiryDate ?? ''}
label='Expiry date*'
name='identityCardExpiryDate'
type='date'
validationSchema={expiryDateValidator}
/>
</div>
<Divider />
<div className='wallets-identity-card-document-upload__document-section'>
Expand All @@ -22,6 +34,7 @@ const IdentityCardDocumentUpload = () => {
<div className='wallets-identity-card-document-upload__dropzones--left'>
<Dropzone
buttonText='Drop file or click here to upload'
defaultFile={formValues.identityCardFront}
description='Upload the front of your identity card.'
fileFormats={['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'application/pdf']}
icon={<IdentityCardFront />}
Expand All @@ -32,6 +45,7 @@ const IdentityCardDocumentUpload = () => {
<div className='wallets-identity-card-document-upload__dropzones--right'>
<Dropzone
buttonText='Drop file or click here to upload'
defaultFile={formValues.identityCardBack}
description='Upload the back of your identity card.'
fileFormats={['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'application/pdf']}
icon={<IdentityCardBack />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,30 @@ 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 (
<div className='wallets-nimc-slip-document-upload' data-testid='dt_driving-license-document-upload'>
<WalletText>First, enter your NIMC slip number.</WalletText>
<FlowTextField label='NIMC slip number*' name='nimcNumber' />
<FlowTextField
defaultValue={formValues.nimcNumber ?? ''}
label='NIMC slip number*'
name='nimcNumber'
validationSchema={documentRequiredValidator('NIMC slip number')}
/>
<Divider />
<div className='wallets-nimc-slip-document-upload__document-section'>
<WalletText>Next, upload both of the following documents.</WalletText>
<div className='wallets-nimc-slip-document-upload__dropzones'>
<div className='wallets-nimc-slip-document-upload__dropzones--left'>
<Dropzone
buttonText='Drop file or click here to upload'
defaultFile={formValues.nimcCardFront}
description='Upload your NIMC slip.'
fileFormats={['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'application/pdf']}
icon={<NIMCSlipFront />}
Expand All @@ -29,6 +36,7 @@ const NIMCSlipDocumentUpload = () => {
<div className='wallets-nimc-slip-document-upload__dropzones--right'>
<Dropzone
buttonText='Drop file or click here to upload'
defaultFile={formValues.nimcCardBack}
description='Upload your proof of age: birth certificate or age declaration document.'
fileFormats={['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'application/pdf']}
icon={<ProofOfAgeIcon />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div className='wallets-passport-document-upload' data-testid='dt_passport-document-upload'>
<WalletText>First, enter your Passport number and the expiry date.</WalletText>
<div className='wallets-passport-document-upload__input-group'>
<FlowTextField label='Passport number*' name='passportNumber' />
<FlowTextField label='Expiry date*' name='passportExpiryDate' placeholder='DD/MM/YYYY' type='date' />
<FlowTextField
defaultValue={formValues.passportNumber ?? ''}
label='Passport number*'
name='passportNumber'
validationSchema={documentRequiredValidator('Passport number')}
/>
<FlowTextField
defaultValue={formValues.passportExpiryDate ?? ''}
label='Expiry date*'
name='passportExpiryDate'
placeholder='DD/MM/YYYY'
type='date'
validationSchema={expiryDateValidator}
/>
</div>
<Divider />
<div className='wallets-passport-document-upload__document-section'>
<WalletText>Next, upload the page of your passport that contains your photo.</WalletText>
<Dropzone
buttonText='Drop file or click here to upload'
defaultFile={formValues.passportCard}
description='Upload the page of your passport that contains your photo.'
fileFormats={['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'application/pdf']}
icon={<PassportPlaceholder />}
Expand Down
11 changes: 11 additions & 0 deletions packages/wallets/src/features/accounts/validations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
Loading