Merge pull request #1041 from josepfo/feat/manual-entry-populate-existing-requirement

feat: enable custom entries and custom information requirements
This commit is contained in:
Rafael Taranto 2022-01-18 19:15:14 +00:00 committed by GitHub
commit abcce7ff06
11 changed files with 525 additions and 192 deletions

View file

@ -1,11 +1,16 @@
import { makeStyles, Box } from '@material-ui/core'
import classnames from 'classnames'
import { parse, isValid, format } from 'date-fns/fp'
import { Field, useFormikContext } from 'formik'
import { parsePhoneNumberFromString } from 'libphonenumber-js'
import * as R from 'ramda'
import * as Yup from 'yup'
import { RadioGroup, TextInput } from 'src/components/inputs/formik'
import {
RadioGroup,
TextInput,
Autocomplete
} from 'src/components/inputs/formik'
import { H4 } from 'src/components/typography'
import { errorColor } from 'src/styling/variables'
import { MANUAL } from 'src/utils/constants'
@ -35,10 +40,21 @@ const useStyles = makeStyles({
specialGrid: {
display: 'grid',
gridTemplateColumns: [[182, 162, 141]]
},
picker: {
width: 150
},
field: {
'& > *:last-child': {
marginBottom: 24
}
}
})
const CUSTOMER_BLOCKED = 'blocked'
const CUSTOM = 'custom'
const REQUIREMENT = 'requirement'
const ID_CARD_DATA = 'idCardData'
const getAuthorizedStatus = (it, triggers) => {
const fields = [
@ -97,34 +113,46 @@ const getName = it => {
) ?? ''}`.trim()
}
// Manual Entry Wizard
const entryOptions = [
{ display: 'Custom entry', code: 'custom' },
{ display: 'Populate existing requirement', code: 'requirement' }
]
const dataOptions = [
{ display: 'Text', code: 'text' },
{ display: 'File', code: 'file' },
{ display: 'Image', code: 'image' }
{ display: 'Text', code: 'text' }
// TODO: Requires backend modifications to support File and Image
// { display: 'File', code: 'file' },
// { display: 'Image', code: 'image' }
]
const requirementOptions = [
{ display: 'Birthdate', code: 'birthdate' },
{ display: 'ID card image', code: 'idCardPhoto' },
{ display: 'ID data', code: 'idCardData' },
{ display: 'Customer camera', code: 'facephoto' },
{ display: 'US SSN', code: 'usSsn' }
{ display: 'US SSN', code: 'usSsn' },
{ display: 'Customer camera', code: 'frontCamera' }
]
const customTextOptions = [
{ display: 'Data entry title', code: 'title' },
{ display: 'Data entry', code: 'data' }
{ label: 'Data entry title', name: 'title' },
{ label: 'Data entry', name: 'data' }
]
const customUploadOptions = [{ display: 'Data entry title', code: 'title' }]
const customUploadOptions = [{ label: 'Data entry title', name: 'title' }]
const entryTypeSchema = Yup.object().shape({
entryType: Yup.string().required()
const entryTypeSchema = Yup.lazy(values => {
if (values.entryType === 'custom') {
return Yup.object().shape({
entryType: Yup.string().required(),
dataType: Yup.string().required()
})
} else if (values.entryType === 'requirement') {
return Yup.object().shape({
entryType: Yup.string().required(),
requirement: Yup.string().required()
})
}
})
const customFileSchema = Yup.object().shape({
@ -142,13 +170,18 @@ const customTextSchema = Yup.object().shape({
data: Yup.string().required()
})
const EntryType = () => {
const updateRequirementOptions = it => [
{
display: 'Custom information requirement',
code: 'custom'
},
...it
]
const EntryType = ({ customInfoRequirementOptions }) => {
const classes = useStyles()
const { values } = useFormikContext()
const CUSTOM = 'custom'
const REQUIREMENT = 'requirement'
const displayCustomOptions = values.entryType === CUSTOM
const displayRequirementOptions = values.entryType === REQUIREMENT
@ -188,7 +221,13 @@ const EntryType = () => {
<Field
component={RadioGroup}
name="requirement"
options={requirementOptions}
options={
requirementOptions
// TODO: Enable once custom info requirement manual entry is finished
// !R.isEmpty(customInfoRequirementOptions)
// ? updateRequirementOptions(requirementOptions)
// : requirementOptions
}
labelClassName={classes.label}
radioClassName={classes.radio}
className={classnames(classes.radioGroup, classes.specialGrid)}
@ -199,18 +238,73 @@ const EntryType = () => {
)
}
const CustomData = ({ selectedValues }) => {
const ManualDataEntry = ({ selectedValues, customInfoRequirementOptions }) => {
const classes = useStyles()
const typeOfEntrySelected = selectedValues?.entryType
const dataTypeSelected = selectedValues?.dataType
const upload = dataTypeSelected === 'file' || dataTypeSelected === 'image'
const requirementSelected = selectedValues?.requirement
const displayRequirements = typeOfEntrySelected === 'requirement'
const isCustomInfoRequirement = requirementSelected === CUSTOM
const updatedRequirementOptions = !R.isEmpty(customInfoRequirementOptions)
? updateRequirementOptions(requirementOptions)
: requirementOptions
const requirementName = displayRequirements
? R.find(R.propEq('code', requirementSelected))(updatedRequirementOptions)
.display
: ''
const title = displayRequirements
? `Requirement ${requirementName}`
: `Custom ${dataTypeSelected} entry`
const elements = displayRequirements
? requirementElements[requirementSelected]
: customElements[dataTypeSelected]
const upload = displayRequirements
? requirementSelected === 'idCardPhoto' ||
requirementSelected === 'frontCamera'
: dataTypeSelected === 'file' || dataTypeSelected === 'image'
return (
<>
<Box display="flex" alignItems="center">
<H4>{`Custom ${dataTypeSelected} entry`}</H4>
<H4>{title}</H4>
</Box>
{customElements[dataTypeSelected].options.map(({ display, code }) => (
<Field name={code} label={display} component={TextInput} width={390} />
))}
{upload && <Upload type={dataTypeSelected}></Upload>}
{isCustomInfoRequirement && (
<Autocomplete
fullWidth
label={`Available requests`}
className={classes.picker}
getOptionSelected={R.eqProps('code')}
labelProp={'display'}
options={customInfoRequirementOptions}
onChange={(evt, it) => {}}
/>
)}
<div className={classes.field}>
{!upload &&
!isCustomInfoRequirement &&
elements.options.map(({ label, name }) => (
<Field
name={name}
label={label}
component={TextInput}
width={390}
/>
))}
</div>
{upload && (
<Upload
type={
displayRequirements ? requirementSelected : dataTypeSelected
}></Upload>
)}
</>
)
}
@ -219,20 +313,23 @@ const customElements = {
text: {
schema: customTextSchema,
options: customTextOptions,
Component: CustomData,
initialValues: { data: '', title: '' }
Component: ManualDataEntry,
initialValues: { data: '', title: '' },
saveType: 'customEntry'
},
file: {
schema: customFileSchema,
options: customUploadOptions,
Component: CustomData,
initialValues: { file: '', title: '' }
Component: ManualDataEntry,
initialValues: { file: null, title: '' },
saveType: 'customEntryUpload'
},
image: {
schema: customImageSchema,
options: customUploadOptions,
Component: CustomData,
initialValues: { image: '', title: '' }
Component: ManualDataEntry,
initialValues: { image: null, title: '' },
saveType: 'customEntryUpload'
}
}
@ -243,6 +340,142 @@ const entryType = {
initialValues: { entryType: '' }
}
// Customer data
const customerDataElements = {
idCardData: [
{
name: 'firstName',
label: 'First name',
component: TextInput
},
{
name: 'documentNumber',
label: 'ID number',
component: TextInput
},
{
name: 'dateOfBirth',
label: 'Birthdate',
component: TextInput
},
{
name: 'gender',
label: 'Gender',
component: TextInput
},
{
name: 'lastName',
label: 'Last name',
component: TextInput
},
{
name: 'expirationDate',
label: 'Expiration Date',
component: TextInput
},
{
name: 'country',
label: 'Country',
component: TextInput
}
],
usSsn: [
{
name: 'usSsn',
label: 'US SSN',
component: TextInput,
size: 190
}
],
idCardPhoto: [{ name: 'idCardPhoto' }],
frontCamera: [{ name: 'frontCamera' }]
}
const customerDataSchemas = {
idCardData: Yup.object().shape({
firstName: Yup.string().required(),
lastName: Yup.string().required(),
documentNumber: Yup.string().required(),
dateOfBirth: Yup.string()
.test({
test: val => isValid(parse(new Date(), 'yyyy-MM-dd', val))
})
.required(),
gender: Yup.string().required(),
country: Yup.string().required(),
expirationDate: Yup.string()
.test({
test: val => isValid(parse(new Date(), 'yyyy-MM-dd', val))
})
.required()
}),
usSsn: Yup.object().shape({
usSsn: Yup.string().required()
}),
idCardPhoto: Yup.object().shape({
idCardPhoto: Yup.mixed().required()
}),
frontCamera: Yup.object().shape({
frontCamera: Yup.mixed().required()
})
}
const requirementElements = {
idCardData: {
schema: customerDataSchemas.idCardData,
options: customerDataElements.idCardData,
Component: ManualDataEntry,
initialValues: {
firstName: '',
lastName: '',
documentNumber: '',
dateOfBirth: '',
gender: '',
country: '',
expirationDate: ''
},
saveType: 'customerData'
},
usSsn: {
schema: customerDataSchemas.usSsn,
options: customerDataElements.usSsn,
Component: ManualDataEntry,
initialValues: { usSsn: '' },
saveType: 'customerData'
},
idCardPhoto: {
schema: customerDataSchemas.idCardPhoto,
options: customerDataElements.idCardPhoto,
Component: ManualDataEntry,
initialValues: { idCardPhoto: null },
saveType: 'customerDataUpload'
},
frontCamera: {
schema: customerDataSchemas.frontCamera,
options: customerDataElements.frontCamera,
Component: ManualDataEntry,
initialValues: { frontCamera: null },
saveType: 'customerDataUpload'
},
custom: {
// schema: customerDataSchemas.customInfoRequirement,
Component: ManualDataEntry,
initialValues: { customInfoRequirement: null },
saveType: 'customInfoRequirement'
}
}
const formatDates = values => {
R.map(
elem =>
(values[elem] = format('yyyyMMdd')(
parse(new Date(), 'yyyy-MM-dd', values[elem])
))
)(['dateOfBirth', 'expirationDate'])
return values
}
const mapKeys = pair => {
const [key, value] = pair
if (key === 'txCustomerPhotoPath' || key === 'frontCameraPath') {
@ -279,5 +512,12 @@ export {
getName,
entryType,
customElements,
formatPhotosData
requirementElements,
formatPhotosData,
customerDataElements,
customerDataSchemas,
formatDates,
REQUIREMENT,
CUSTOM,
ID_CARD_DATA
}