feat: addd manual entry option and wizard first step

This commit is contained in:
José Oliveira 2021-10-19 10:36:03 +01:00
parent d435f24105
commit c31005d6ef
7 changed files with 341 additions and 4 deletions

View file

@ -16,6 +16,8 @@ import { ReactComponent as AuthorizeReversedIcon } from 'src/styling/icons/butto
import { ReactComponent as AuthorizeIcon } from 'src/styling/icons/button/authorize/zodiac.svg'
import { ReactComponent as BlockReversedIcon } from 'src/styling/icons/button/block/white.svg'
import { ReactComponent as BlockIcon } from 'src/styling/icons/button/block/zodiac.svg'
import { ReactComponent as DataReversedIcon } from 'src/styling/icons/button/data/white.svg'
import { ReactComponent as DataIcon } from 'src/styling/icons/button/data/zodiac.svg'
import { ReactComponent as DiscountReversedIcon } from 'src/styling/icons/button/discount/white.svg'
import { ReactComponent as Discount } from 'src/styling/icons/button/discount/zodiac.svg'
import { fromNamespace, namespaces } from 'src/utils/config'
@ -25,7 +27,8 @@ import styles from './CustomerProfile.styles'
import {
CustomerDetails,
TransactionsList,
CustomerSidebar
CustomerSidebar,
Wizard
} from './components'
import { getFormattedPhone, getName } from './helper'
@ -110,7 +113,10 @@ const SET_CUSTOMER = gql`
const CustomerProfile = memo(() => {
const history = useHistory()
const [showCompliance, setShowCompliance] = useState(false)
const [wizard, setWizard] = useState(false)
const [error] = useState(null)
const [clickedItem, setClickedItem] = useState('overview')
const { id: customerId } = useParams()
@ -184,6 +190,16 @@ const CustomerProfile = memo(() => {
/>
</div>
<Label1 className={classes.actionLabel}>Actions</Label1>
<div>
<ActionButton
className={classes.customerManualDataEntry}
color="primary"
Icon={DataIcon}
InverseIcon={DataReversedIcon}
onClick={() => setWizard(true)}>
{`Manual data entry`}
</ActionButton>
</div>
<div>
<ActionButton
className={classes.customerDiscount}
@ -301,6 +317,13 @@ const CustomerProfile = memo(() => {
</div>
)}
</div>
{wizard && (
<Wizard
error={error?.message}
save={() => {}}
onClose={() => setWizard(null)}
/>
)}
</div>
</>
)

View file

@ -22,10 +22,16 @@ export default {
padding: [[0, props.blocked ? 35 : 48, 0]]
}),
customerDiscount: {
display: 'flex',
flexDirection: 'row',
margin: [[0, 0, 4, 0]],
padding: [[0, 23.5, 0]]
},
customerManualDataEntry: {
display: 'flex',
flexDirection: 'row',
margin: [[8, 0, 4, 0]],
padding: [[0, 24, 0]]
padding: [[0, 40.5, 0]]
},
panels: {
display: 'flex'

View file

@ -0,0 +1,115 @@
import { makeStyles } from '@material-ui/core'
import { Form, Formik } from 'formik'
import * as R from 'ramda'
import React, { useState } from 'react'
import ErrorMessage from 'src/components/ErrorMessage'
import Modal from 'src/components/Modal'
import Stepper from 'src/components/Stepper'
import { Button } from 'src/components/buttons'
import { comet } from 'src/styling/variables'
import { entryType } from './helper'
const LAST_STEP = 2
const styles = {
stepper: {
margin: [[16, 0, 14, 0]]
},
submit: {
display: 'flex',
flexDirection: 'row',
margin: [['auto', 0, 24]]
},
button: {
marginLeft: 'auto'
},
form: {
height: '100%',
display: 'flex',
flexDirection: 'column'
},
infoTitle: {
margin: [[18, 0, 20, 0]]
},
infoCurrentText: {
color: comet
},
blankSpace: {
padding: [[0, 30]],
margin: [[0, 4, 0, 2]],
borderBottom: `1px solid ${comet}`,
display: 'inline-block'
}
}
const useStyles = makeStyles(styles)
const getStep = step => {
switch (step) {
case 1:
return entryType
default:
return entryType
}
}
const Wizard = ({ onClose, save, error }) => {
const classes = useStyles()
const [{ step, config }, setState] = useState({
step: 1
})
const isLastStep = step === LAST_STEP
const stepOptions = getStep(step)
const onContinue = async it => {
const newConfig = R.merge(config, stepOptions.schema.cast(it))
if (isLastStep) {
return save(newConfig)
}
setState({
step: step + 1,
config: newConfig
})
}
return (
<>
<Modal
title="Manual data entry"
handleClose={onClose}
width={520}
height={520}
open={true}>
<Stepper
className={classes.stepper}
steps={LAST_STEP}
currentStep={step}
/>
<Formik
validateOnBlur={false}
validateOnChange={false}
enableReinitialize
onSubmit={onContinue}
initialValues={stepOptions.initialValues}
validationSchema={stepOptions.schema}>
<Form className={classes.form}>
<stepOptions.Component {...stepOptions.props} />
<div className={classes.submit}>
{error && <ErrorMessage>Failed to save</ErrorMessage>}
<Button className={classes.button} type="submit">
{isLastStep ? 'Finish' : 'Next'}
</Button>
</div>
</Form>
</Formik>
</Modal>
</>
)
}
export default Wizard

View file

@ -1,3 +1,5 @@
import Wizard from '../Wizard'
import CustomerDetails from './CustomerDetails'
import CustomerSidebar from './CustomerSidebar'
import EditableCard from './EditableCard'
@ -11,5 +13,6 @@ export {
TransactionsList,
CustomerSidebar,
Field,
EditableCard
EditableCard,
Wizard
}

View file

@ -1,5 +1,81 @@
import { makeStyles, Box } from '@material-ui/core'
import classnames from 'classnames'
import { Field, useFormikContext } from 'formik'
import { parsePhoneNumberFromString } from 'libphonenumber-js'
import * as R from 'ramda'
import * as Yup from 'yup'
import { RadioGroup } from 'src/components/inputs/formik'
import { H4 } from 'src/components/typography'
import { errorColor } from 'src/styling/variables'
const useStyles = makeStyles({
radioLabel: {
height: 40,
padding: [[0, 10]]
},
radio: {
padding: 4,
margin: 4
},
radioGroup: {
flexDirection: 'row'
},
error: {
color: errorColor
},
specialLabel: {
height: 40,
padding: 0
},
specialGrid: {
display: 'grid',
gridTemplateColumns: [[182, 162, 141]]
},
directionIcon: {
marginRight: 2
},
directionName: {
marginLeft: 6
},
thresholdWrapper: {
display: 'flex',
flexDirection: 'column'
},
thresholdTitle: {
marginTop: 50
},
thresholdContentWrapper: {
display: 'flex',
flexDirection: 'row'
},
thresholdField: {
marginRight: 6,
width: 75
},
description: {
marginTop: 7
},
space: {
marginLeft: 6,
marginRight: 6
},
lastSpace: {
marginLeft: 6
},
suspensionDays: {
width: 34
},
input: {
marginTop: -2
},
limitedInput: {
width: 50
},
daysInput: {
width: 60
}
})
const CUSTOMER_BLOCKED = 'blocked'
@ -27,4 +103,88 @@ const getName = it => {
) ?? ''}`.trim()
}
export { getAuthorizedStatus, getFormattedPhone, getName }
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' }
]
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' }
]
const entryTypeSchema = Yup.object().shape({
entryType: Yup.object({
entryType: Yup.string().required()
}).required()
})
const EntryType = () => {
const classes = useStyles()
const { values } = useFormikContext()
const displayCustom = values.entryType === 'custom'
return (
<>
<Box display="flex" alignItems="center">
<H4>Type of entry</H4>
</Box>
<Field
component={RadioGroup}
name="entryType"
options={entryOptions}
labelClassName={classes.specialLabel}
radioClassName={classes.radio}
className={classnames(classes.radioGroup, classes.specialGrid)}
/>
{displayCustom && (
<div>
<Box display="flex" alignItems="center">
<H4>Type of data</H4>
</Box>
<Field
component={RadioGroup}
name="dataType"
options={dataOptions}
labelClassName={classes.specialLabel}
radioClassName={classes.radio}
className={classnames(classes.radioGroup, classes.specialGrid)}
/>
</div>
)}
{!displayCustom && (
<div>
<Box display="flex" alignItems="center">
<H4>Requirements</H4>
</Box>
<Field
component={RadioGroup}
name="requirement"
options={requirementOptions}
labelClassName={classes.specialLabel}
radioClassName={classes.radio}
className={classnames(classes.radioGroup, classes.specialGrid)}
/>
</div>
)}
</>
)
}
const entryType = {
schema: entryTypeSchema,
options: entryOptions,
Component: EntryType,
initialValues: { entryType: { entryType: '' } }
}
export { getAuthorizedStatus, getFormattedPhone, getName, entryType }

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>icon/button/data/white</title>
<g id="icon/button/data/white" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Data-icon---small" transform="translate(0.500000, 0.500000)" stroke="#FFFFFF">
<path d="M11,2.44444444 L11,5.5 C11,6.85002939 8.53756612,7.94444444 5.5,7.94444444 C2.521994,7.94444444 0.0939612041,6.81341452 1.11160464e-12,5.5 L0.00282594991,2.44444444" id="oval-3"></path>
<path d="M11,5.5 L11,8.55555556 C11,9.90558494 8.53756612,11 5.5,11 C2.521994,11 0.0939612041,9.86897007 1.11160464e-12,8.55555556 L0.00282594991,5.5" id="oval-2"></path>
<ellipse id="oval-1" cx="5.5" cy="2.44444444" rx="5.5" ry="2.44444444"></ellipse>
</g>
<g id="Group-9" transform="translate(0.000000, 4.000000)">
<circle id="Oval" stroke="#FFFFFF" fill="#5F668A" cx="4" cy="4" r="3.5"></circle>
<polygon id="Path" fill="#FFFFFF" fill-rule="nonzero" points="4.33333333 3.66666667 6 3.66666667 6 4.33333333 4.33333333 4.33333333 4.33333333 6 3.66666667 6 3.66666667 4.33333333 2 4.33333333 2 3.66666667 3.66666667 3.66666667 3.66666667 2 4.33333333 2"></polygon>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>icon/button/data/zodiac</title>
<g id="icon/button/data/zodiac" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Data-icon---small" transform="translate(1.500000, 0.500000)" stroke="#1B2559">
<path d="M10,2 L10,4.77777778 C10,6.00507722 7.76142375,7 5,7 C2.29272181,7 0.0854192765,5.97179097 0,4.77777778 L0.00256904537,2" id="oval-3"></path>
<path d="M10,5 L10,7.77777778 C10,9.00507722 7.76142375,10 5,10 C2.29272181,10 0.0854192765,8.97179097 0,7.77777778 L0.00256904537,5" id="oval-2"></path>
<ellipse id="oval-1" cx="5" cy="2" rx="5" ry="2"></ellipse>
</g>
<g id="Group-9" transform="translate(0.000000, 4.000000)">
<circle id="Oval" stroke="#1B2559" fill="#EBEFFF" cx="4" cy="4" r="3.5"></circle>
<polygon id="Path" fill="#1B2559" fill-rule="nonzero" points="4.33333333 3.66666667 6 3.66666667 6 4.33333333 4.33333333 4.33333333 4.33333333 6 3.66666667 6 3.66666667 4.33333333 2 4.33333333 2 3.66666667 3.66666667 3.66666667 3.66666667 2 4.33333333 2"></polygon>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB