feat: addd manual entry option and wizard first step
This commit is contained in:
parent
d435f24105
commit
c31005d6ef
7 changed files with 341 additions and 4 deletions
|
|
@ -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 AuthorizeIcon } from 'src/styling/icons/button/authorize/zodiac.svg'
|
||||||
import { ReactComponent as BlockReversedIcon } from 'src/styling/icons/button/block/white.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 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 DiscountReversedIcon } from 'src/styling/icons/button/discount/white.svg'
|
||||||
import { ReactComponent as Discount } from 'src/styling/icons/button/discount/zodiac.svg'
|
import { ReactComponent as Discount } from 'src/styling/icons/button/discount/zodiac.svg'
|
||||||
import { fromNamespace, namespaces } from 'src/utils/config'
|
import { fromNamespace, namespaces } from 'src/utils/config'
|
||||||
|
|
@ -25,7 +27,8 @@ import styles from './CustomerProfile.styles'
|
||||||
import {
|
import {
|
||||||
CustomerDetails,
|
CustomerDetails,
|
||||||
TransactionsList,
|
TransactionsList,
|
||||||
CustomerSidebar
|
CustomerSidebar,
|
||||||
|
Wizard
|
||||||
} from './components'
|
} from './components'
|
||||||
import { getFormattedPhone, getName } from './helper'
|
import { getFormattedPhone, getName } from './helper'
|
||||||
|
|
||||||
|
|
@ -110,7 +113,10 @@ const SET_CUSTOMER = gql`
|
||||||
|
|
||||||
const CustomerProfile = memo(() => {
|
const CustomerProfile = memo(() => {
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
|
|
||||||
const [showCompliance, setShowCompliance] = useState(false)
|
const [showCompliance, setShowCompliance] = useState(false)
|
||||||
|
const [wizard, setWizard] = useState(false)
|
||||||
|
const [error] = useState(null)
|
||||||
const [clickedItem, setClickedItem] = useState('overview')
|
const [clickedItem, setClickedItem] = useState('overview')
|
||||||
const { id: customerId } = useParams()
|
const { id: customerId } = useParams()
|
||||||
|
|
||||||
|
|
@ -184,6 +190,16 @@ const CustomerProfile = memo(() => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Label1 className={classes.actionLabel}>Actions</Label1>
|
<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>
|
<div>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
className={classes.customerDiscount}
|
className={classes.customerDiscount}
|
||||||
|
|
@ -301,6 +317,13 @@ const CustomerProfile = memo(() => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{wizard && (
|
||||||
|
<Wizard
|
||||||
|
error={error?.message}
|
||||||
|
save={() => {}}
|
||||||
|
onClose={() => setWizard(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,16 @@ export default {
|
||||||
padding: [[0, props.blocked ? 35 : 48, 0]]
|
padding: [[0, props.blocked ? 35 : 48, 0]]
|
||||||
}),
|
}),
|
||||||
customerDiscount: {
|
customerDiscount: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
margin: [[0, 0, 4, 0]],
|
||||||
|
padding: [[0, 23.5, 0]]
|
||||||
|
},
|
||||||
|
customerManualDataEntry: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
margin: [[8, 0, 4, 0]],
|
margin: [[8, 0, 4, 0]],
|
||||||
padding: [[0, 24, 0]]
|
padding: [[0, 40.5, 0]]
|
||||||
},
|
},
|
||||||
panels: {
|
panels: {
|
||||||
display: 'flex'
|
display: 'flex'
|
||||||
|
|
|
||||||
115
new-lamassu-admin/src/pages/Customers/Wizard.js
Normal file
115
new-lamassu-admin/src/pages/Customers/Wizard.js
Normal 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
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import Wizard from '../Wizard'
|
||||||
|
|
||||||
import CustomerDetails from './CustomerDetails'
|
import CustomerDetails from './CustomerDetails'
|
||||||
import CustomerSidebar from './CustomerSidebar'
|
import CustomerSidebar from './CustomerSidebar'
|
||||||
import EditableCard from './EditableCard'
|
import EditableCard from './EditableCard'
|
||||||
|
|
@ -11,5 +13,6 @@ export {
|
||||||
TransactionsList,
|
TransactionsList,
|
||||||
CustomerSidebar,
|
CustomerSidebar,
|
||||||
Field,
|
Field,
|
||||||
EditableCard
|
EditableCard,
|
||||||
|
Wizard
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 { parsePhoneNumberFromString } from 'libphonenumber-js'
|
||||||
import * as R from 'ramda'
|
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'
|
const CUSTOMER_BLOCKED = 'blocked'
|
||||||
|
|
||||||
|
|
@ -27,4 +103,88 @@ const getName = it => {
|
||||||
) ?? ''}`.trim()
|
) ?? ''}`.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 }
|
||||||
|
|
|
||||||
15
new-lamassu-admin/src/styling/icons/button/data/white.svg
Normal file
15
new-lamassu-admin/src/styling/icons/button/data/white.svg
Normal 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 |
15
new-lamassu-admin/src/styling/icons/button/data/zodiac.svg
Normal file
15
new-lamassu-admin/src/styling/icons/button/data/zodiac.svg
Normal 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 |
Loading…
Add table
Add a link
Reference in a new issue