feat: compliance triggers and customers up to spec
This commit is contained in:
parent
b153a23f25
commit
6f5cb385b0
8 changed files with 400 additions and 104 deletions
|
|
@ -93,6 +93,7 @@ const ECol = ({
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
|
bypassField,
|
||||||
input,
|
input,
|
||||||
editable = true,
|
editable = true,
|
||||||
size,
|
size,
|
||||||
|
|
@ -122,6 +123,9 @@ const ECol = ({
|
||||||
innerProps.getLabel = view
|
innerProps.getLabel = view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isEditing = editing && editable
|
||||||
|
const isField = !bypassField
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Td
|
<Td
|
||||||
className={{
|
className={{
|
||||||
|
|
@ -133,11 +137,11 @@ const ECol = ({
|
||||||
size={size}
|
size={size}
|
||||||
bold={bold}
|
bold={bold}
|
||||||
textAlign={textAlign}>
|
textAlign={textAlign}>
|
||||||
{editing && editable ? (
|
{isEditing && isField && (
|
||||||
<Field name={name} component={input} {...innerProps} />
|
<Field name={name} component={input} {...innerProps} />
|
||||||
) : (
|
|
||||||
values && <>{view(values[name])}</>
|
|
||||||
)}
|
)}
|
||||||
|
{isEditing && !isField && <config.input name={name} />}
|
||||||
|
{!isEditing && values && <>{view(values[name], values)}</>}
|
||||||
{suffix && (
|
{suffix && (
|
||||||
<SuffixComponent className={classes.suffix}>{suffix}</SuffixComponent>
|
<SuffixComponent className={classes.suffix}>{suffix}</SuffixComponent>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@ import { makeStyles } from '@material-ui/core/styles'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import React, { memo } from 'react'
|
import React, { memo } from 'react'
|
||||||
|
|
||||||
// import { ActionButton } from 'src/components/buttons'
|
import { ActionButton } from 'src/components/buttons'
|
||||||
import { H3 } from 'src/components/typography'
|
import { H3 } from 'src/components/typography'
|
||||||
// import { ReactComponent as AuthorizeReversedIcon } from 'src/styling/icons/button/authorize/white.svg'
|
import { ReactComponent as AuthorizeReversedIcon } from 'src/styling/icons/button/authorize/white.svg'
|
||||||
// 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 RejectReversedIcon } from 'src/styling/icons/button/cancel/white.svg'
|
import { ReactComponent as RejectReversedIcon } from 'src/styling/icons/button/cancel/white.svg'
|
||||||
// import { ReactComponent as RejectIcon } from 'src/styling/icons/button/cancel/zodiac.svg'
|
import { ReactComponent as RejectIcon } from 'src/styling/icons/button/cancel/zodiac.svg'
|
||||||
|
|
||||||
import { propertyCardStyles } from './PropertyCard.styles'
|
import { propertyCardStyles } from './PropertyCard.styles'
|
||||||
|
|
||||||
|
|
@ -24,48 +24,46 @@ const PropertyCard = memo(
|
||||||
|
|
||||||
const propertyCardClassNames = {
|
const propertyCardClassNames = {
|
||||||
[classes.propertyCard]: true,
|
[classes.propertyCard]: true,
|
||||||
[classes.propertyCardPending]: true
|
[classes.propertyCardPending]: state === OVERRIDE_PENDING,
|
||||||
// [classes.propertyCardPending]: state === OVERRIDE_PENDING
|
[classes.propertyCardRejected]: state === OVERRIDE_REJECTED,
|
||||||
// [classes.propertyCardRejected]: state === OVERRIDE_REJECTED,
|
[classes.propertyCardAccepted]: state === OVERRIDE_AUTHORIZED
|
||||||
// [classes.propertyCardAccepted]: state === OVERRIDE_AUTHORIZED
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// const label1ClassNames = {
|
const label1ClassNames = {
|
||||||
// [classes.label1]: true,
|
[classes.label1]: true,
|
||||||
// [classes.label1Pending]: true
|
[classes.label1Pending]: state === OVERRIDE_PENDING,
|
||||||
// [classes.label1Pending]: state === OVERRIDE_PENDING
|
[classes.label1Rejected]: state === OVERRIDE_REJECTED,
|
||||||
// [classes.label1Rejected]: state === OVERRIDE_REJECTED,
|
[classes.label1Accepted]: state === OVERRIDE_AUTHORIZED
|
||||||
// [classes.label1Accepted]: state === OVERRIDE_AUTHORIZED
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// const AuthorizeButton = () => (
|
const AuthorizeButton = () => (
|
||||||
// <ActionButton
|
<ActionButton
|
||||||
// className={classes.cardActionButton}
|
className={classes.cardActionButton}
|
||||||
// color="secondary"
|
color="secondary"
|
||||||
// Icon={AuthorizeIcon}
|
Icon={AuthorizeIcon}
|
||||||
// InverseIcon={AuthorizeReversedIcon}
|
InverseIcon={AuthorizeReversedIcon}
|
||||||
// onClick={() => authorize()}>
|
onClick={() => authorize()}>
|
||||||
// Authorize
|
Authorize
|
||||||
// </ActionButton>
|
</ActionButton>
|
||||||
// )
|
)
|
||||||
|
|
||||||
// const RejectButton = () => (
|
const RejectButton = () => (
|
||||||
// <ActionButton
|
<ActionButton
|
||||||
// className={classes.cardActionButton}
|
className={classes.cardActionButton}
|
||||||
// color="secondary"
|
color="secondary"
|
||||||
// Icon={RejectIcon}
|
Icon={RejectIcon}
|
||||||
// InverseIcon={RejectReversedIcon}
|
InverseIcon={RejectReversedIcon}
|
||||||
// onClick={() => reject()}>
|
onClick={() => reject()}>
|
||||||
// Reject
|
Reject
|
||||||
// </ActionButton>
|
</ActionButton>
|
||||||
// )
|
)
|
||||||
|
|
||||||
// const authorizedAsString =
|
const authorizedAsString =
|
||||||
// state === OVERRIDE_PENDING
|
state === OVERRIDE_PENDING
|
||||||
// ? 'Pending'
|
? 'Pending'
|
||||||
// : state === OVERRIDE_REJECTED
|
: state === OVERRIDE_REJECTED
|
||||||
// ? 'Rejected'
|
? 'Rejected'
|
||||||
// : 'Accepted'
|
: 'Accepted'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper
|
<Paper
|
||||||
|
|
@ -73,18 +71,17 @@ const PropertyCard = memo(
|
||||||
elevation={0}>
|
elevation={0}>
|
||||||
<div className={classes.rowSpaceBetween}>
|
<div className={classes.rowSpaceBetween}>
|
||||||
<H3>{title}</H3>
|
<H3>{title}</H3>
|
||||||
{/* <div className={classnames(label1ClassNames)}>
|
<div className={classnames(label1ClassNames)}>
|
||||||
{authorizedAsString}
|
{authorizedAsString}
|
||||||
</div> */}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Paper className={classes.cardProperties} elevation={0}>
|
<Paper className={classes.cardProperties} elevation={0}>
|
||||||
{children}
|
{children}
|
||||||
</Paper>
|
</Paper>
|
||||||
{/* V2 */}
|
<div className={classes.buttonsWrapper}>
|
||||||
{/* <div className={classes.buttonsWrapper}>
|
|
||||||
{state !== OVERRIDE_AUTHORIZED && AuthorizeButton()}
|
{state !== OVERRIDE_AUTHORIZED && AuthorizeButton()}
|
||||||
{state !== OVERRIDE_REJECTED && RejectButton()}
|
{state !== OVERRIDE_REJECTED && RejectButton()}
|
||||||
</div> */}
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ const propertyCardStyles = {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: 'calc(100% - 75px)',
|
height: 'calc(100% - 104px)',
|
||||||
padding: [[20]],
|
padding: [[20]],
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
boxShadow: '0 0 8px 0 rgba(0, 0, 0, 0.04)',
|
boxShadow: '0 0 8px 0 rgba(0, 0, 0, 0.04)',
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,12 @@ import { cpcStyles } from './Transactions.styles'
|
||||||
|
|
||||||
const useStyles = makeStyles(cpcStyles)
|
const useStyles = makeStyles(cpcStyles)
|
||||||
|
|
||||||
const CopyToClipboard = ({ className, children, ...props }) => {
|
const CopyToClipboard = ({
|
||||||
|
className,
|
||||||
|
buttonClassname,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
const [anchorEl, setAnchorEl] = useState(null)
|
const [anchorEl, setAnchorEl] = useState(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -39,7 +44,7 @@ const CopyToClipboard = ({ className, children, ...props }) => {
|
||||||
<div className={classnames(classes.address, className)}>
|
<div className={classnames(classes.address, className)}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.buttonWrapper}>
|
<div className={classnames(classes.buttonWrapper, buttonClassname)}>
|
||||||
<ReactCopyToClipboard text={R.replace(/\s/g, '')(children)}>
|
<ReactCopyToClipboard text={R.replace(/\s/g, '')(children)}>
|
||||||
<button
|
<button
|
||||||
aria-describedby={id}
|
aria-describedby={id}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import { fromNamespace, namespaces } from 'src/utils/config'
|
||||||
|
|
||||||
import { mainStyles } from './Triggers.styles'
|
import { mainStyles } from './Triggers.styles'
|
||||||
import Wizard from './Wizard'
|
import Wizard from './Wizard'
|
||||||
import { Schema, elements, sortBy } from './helper'
|
import { Schema, getElements, sortBy, fromServer, toServer } from './helper'
|
||||||
|
|
||||||
const useStyles = makeStyles(mainStyles)
|
const useStyles = makeStyles(mainStyles)
|
||||||
|
|
||||||
|
|
@ -34,7 +34,7 @@ const Triggers = () => {
|
||||||
const [error, setError] = useState(false)
|
const [error, setError] = useState(false)
|
||||||
|
|
||||||
const { data } = useQuery(GET_INFO)
|
const { data } = useQuery(GET_INFO)
|
||||||
const triggers = data?.config?.triggers ?? []
|
const triggers = fromServer(data?.config?.triggers ?? [])
|
||||||
|
|
||||||
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
||||||
onCompleted: () => setWizard(false),
|
onCompleted: () => setWizard(false),
|
||||||
|
|
@ -45,12 +45,14 @@ const Triggers = () => {
|
||||||
const add = rawConfig => {
|
const add = rawConfig => {
|
||||||
const toSave = R.concat([{ id: v4(), ...rawConfig }])(triggers)
|
const toSave = R.concat([{ id: v4(), ...rawConfig }])(triggers)
|
||||||
setError(false)
|
setError(false)
|
||||||
return saveConfig({ variables: { config: { triggers: toSave } } })
|
return saveConfig({ variables: { config: { triggers: toServer(toSave) } } })
|
||||||
}
|
}
|
||||||
|
|
||||||
const save = config => {
|
const save = config => {
|
||||||
setError(false)
|
setError(false)
|
||||||
return saveConfig({ variables: { config } })
|
return saveConfig({
|
||||||
|
variables: { config: { triggers: toServer(config.triggers) } }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const currency = R.path(['fiatCurrency'])(
|
const currency = R.path(['fiatCurrency'])(
|
||||||
|
|
@ -78,7 +80,7 @@ const Triggers = () => {
|
||||||
enableDelete
|
enableDelete
|
||||||
save={save}
|
save={save}
|
||||||
validationSchema={Schema}
|
validationSchema={Schema}
|
||||||
elements={elements}
|
elements={getElements(currency, classes)}
|
||||||
/>
|
/>
|
||||||
{wizard && (
|
{wizard && (
|
||||||
<Wizard
|
<Wizard
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,13 @@ const mainStyles = {
|
||||||
padding: 4,
|
padding: 4,
|
||||||
margin: 4
|
margin: 4
|
||||||
},
|
},
|
||||||
|
tableRadioGroup: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between'
|
||||||
|
},
|
||||||
|
tableRadioLabel: {
|
||||||
|
marginRight: 0
|
||||||
|
},
|
||||||
closeButton: {
|
closeButton: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
width: 16,
|
width: 16,
|
||||||
|
|
|
||||||
|
|
@ -88,19 +88,19 @@ const getTypeText = (config, currency) => {
|
||||||
switch (config.triggerType) {
|
switch (config.triggerType) {
|
||||||
case 'txAmount':
|
case 'txAmount':
|
||||||
return `makes a single transaction over ${orUnderline(
|
return `makes a single transaction over ${orUnderline(
|
||||||
config.threshold
|
config.threshold.threshold
|
||||||
)} ${currency}`
|
)} ${currency}`
|
||||||
case 'txVolume':
|
case 'txVolume':
|
||||||
return `makes transactions over ${orUnderline(
|
return `makes transactions over ${orUnderline(
|
||||||
config.threshold
|
config.threshold.threshold
|
||||||
)} ${currency} in ${orUnderline(config.days)} days`
|
)} ${currency} in ${orUnderline(config.threshold.thresholdDays)} days`
|
||||||
case 'txVelocity':
|
case 'txVelocity':
|
||||||
return `makes ${orUnderline(
|
return `makes ${orUnderline(
|
||||||
config.threshold
|
config.threshold.threshold
|
||||||
)} transactions in ${orUnderline(config.days)} days`
|
)} transactions in ${orUnderline(config.threshold.thresholdDays)} days`
|
||||||
case 'consecutiveDays':
|
case 'consecutiveDays':
|
||||||
return `at least one transaction every day for ${orUnderline(
|
return `at least one transaction every day for ${orUnderline(
|
||||||
config.days
|
config.threshold.thresholdDays
|
||||||
)} days`
|
)} days`
|
||||||
default:
|
default:
|
||||||
return ''
|
return ''
|
||||||
|
|
@ -108,7 +108,7 @@ const getTypeText = (config, currency) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const getRequirementText = config => {
|
const getRequirementText = config => {
|
||||||
switch (config.requirement) {
|
switch (config.requirement?.requirement) {
|
||||||
case 'sms':
|
case 'sms':
|
||||||
return 'asked to enter code provided through SMS verification'
|
return 'asked to enter code provided through SMS verification'
|
||||||
case 'idPhoto':
|
case 'idPhoto':
|
||||||
|
|
@ -122,7 +122,9 @@ const getRequirementText = config => {
|
||||||
case 'superuser':
|
case 'superuser':
|
||||||
return ''
|
return ''
|
||||||
case 'suspend':
|
case 'suspend':
|
||||||
return 'suspended'
|
return `suspended for ${orUnderline(
|
||||||
|
config.requirement.suspensionDays
|
||||||
|
)} days`
|
||||||
case 'block':
|
case 'block':
|
||||||
return 'blocked'
|
return 'blocked'
|
||||||
default:
|
default:
|
||||||
|
|
@ -155,7 +157,6 @@ const InfoPanel = ({ step, config = {}, liveValues = {}, currency }) => {
|
||||||
const GetValues = ({ setValues }) => {
|
const GetValues = ({ setValues }) => {
|
||||||
const { values } = useFormikContext()
|
const { values } = useFormikContext()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('triggered')
|
|
||||||
setValues && values && setValues(values)
|
setValues && values && setValues(values)
|
||||||
}, [setValues, values])
|
}, [setValues, values])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,11 @@ import { makeStyles, Box } from '@material-ui/core'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import { Field, useFormikContext } from 'formik'
|
import { Field, useFormikContext } from 'formik'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React from 'react'
|
import React, { memo } from 'react'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
import { TextInput, RadioGroup } from 'src/components/inputs/formik'
|
import { TextInput, RadioGroup } from 'src/components/inputs/formik'
|
||||||
import Autocomplete from 'src/components/inputs/formik/Autocomplete'
|
import { H4, Label2, Label1, Info2 } from 'src/components/typography'
|
||||||
import { H4 } from 'src/components/typography'
|
|
||||||
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
|
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
|
||||||
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
|
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
|
||||||
import { errorColor } from 'src/styling/variables'
|
import { errorColor } from 'src/styling/variables'
|
||||||
|
|
@ -40,13 +39,45 @@ const useStyles = makeStyles({
|
||||||
},
|
},
|
||||||
directionName: {
|
directionName: {
|
||||||
marginLeft: 6
|
marginLeft: 6
|
||||||
|
},
|
||||||
|
thresholdWrapper: {
|
||||||
|
display: 'flex'
|
||||||
|
},
|
||||||
|
thresholdField: {
|
||||||
|
margin: 10,
|
||||||
|
width: 208
|
||||||
|
},
|
||||||
|
space: {
|
||||||
|
marginLeft: 6,
|
||||||
|
marginRight: 6
|
||||||
|
},
|
||||||
|
lastSpace: {
|
||||||
|
marginLeft: 6
|
||||||
|
},
|
||||||
|
suspensionDays: {
|
||||||
|
width: 34
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
marginTop: -2
|
||||||
|
},
|
||||||
|
limitedInput: {
|
||||||
|
width: 50
|
||||||
|
},
|
||||||
|
daysInput: {
|
||||||
|
width: 60
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const cashDirection = Yup.string().required('Required')
|
const cashDirection = Yup.string().required('Required')
|
||||||
const triggerType = Yup.string().required('Required')
|
const triggerType = Yup.string().required('Required')
|
||||||
const threshold = Yup.number().required('Required')
|
const threshold = Yup.object().shape({
|
||||||
const requirement = Yup.string().required('Required')
|
threshold: Yup.number(),
|
||||||
|
thresholdDays: Yup.number()
|
||||||
|
})
|
||||||
|
const requirement = Yup.object().shape({
|
||||||
|
requirement: Yup.string().required('Required'),
|
||||||
|
suspensionDays: Yup.number()
|
||||||
|
})
|
||||||
|
|
||||||
const Schema = Yup.object().shape({
|
const Schema = Yup.object().shape({
|
||||||
triggerType,
|
triggerType,
|
||||||
|
|
@ -59,9 +90,52 @@ const Schema = Yup.object().shape({
|
||||||
const directionSchema = Yup.object().shape({ cashDirection })
|
const directionSchema = Yup.object().shape({ cashDirection })
|
||||||
|
|
||||||
const directionOptions = [
|
const directionOptions = [
|
||||||
{ display: 'Both', code: 'both' },
|
{
|
||||||
{ display: 'Only cash-in', code: 'cashIn' },
|
display: 'Both',
|
||||||
{ display: 'Only cash-out', code: 'cashOut' }
|
code: 'both'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
display: 'Only cash-in',
|
||||||
|
code: 'cashIn'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
display: 'Only cash-out',
|
||||||
|
code: 'cashOut'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const directionOptions2 = [
|
||||||
|
{
|
||||||
|
display: (
|
||||||
|
<>
|
||||||
|
<TxInIcon /> in
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
code: 'cashIn'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
display: (
|
||||||
|
<>
|
||||||
|
<TxOutIcon /> out
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
code: 'cashOut'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
display: (
|
||||||
|
<>
|
||||||
|
<Box display="flex">
|
||||||
|
<Box mr={0.25}>
|
||||||
|
<TxOutIcon />
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<TxInIcon />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
code: 'both'
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const Direction = () => {
|
const Direction = () => {
|
||||||
|
|
@ -113,12 +187,25 @@ const typeOptions = [
|
||||||
|
|
||||||
const Type = () => {
|
const Type = () => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const { errors, touched } = useFormikContext()
|
const { errors, touched, values } = useFormikContext()
|
||||||
|
|
||||||
const typeClass = {
|
const typeClass = {
|
||||||
[classes.error]: errors.triggerType && touched.triggerType
|
[classes.error]: errors.triggerType && touched.triggerType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const containsType = R.contains(values?.triggerType)
|
||||||
|
const isThresholdEnabled = containsType([
|
||||||
|
'txAmount',
|
||||||
|
'txVolume',
|
||||||
|
'txVelocity'
|
||||||
|
])
|
||||||
|
|
||||||
|
const isThresholdDaysEnabled = containsType([
|
||||||
|
'txVolume',
|
||||||
|
'txVelocity',
|
||||||
|
'consecutiveDays'
|
||||||
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box display="flex" alignItems="center">
|
<Box display="flex" alignItems="center">
|
||||||
|
|
@ -133,13 +220,26 @@ const Type = () => {
|
||||||
className={classes.radioGroup}
|
className={classes.radioGroup}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Field
|
<div className={classes.thresholdWrapper}>
|
||||||
component={TextInput}
|
{isThresholdEnabled && (
|
||||||
label="Threshold"
|
<Field
|
||||||
size="lg"
|
className={classes.thresholdField}
|
||||||
name="threshold"
|
component={TextInput}
|
||||||
options={typeOptions}
|
label="Threshold"
|
||||||
/>
|
size="lg"
|
||||||
|
name="threshold.threshold"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isThresholdDaysEnabled && (
|
||||||
|
<Field
|
||||||
|
className={classes.thresholdField}
|
||||||
|
component={TextInput}
|
||||||
|
label="Threshold Days"
|
||||||
|
size="lg"
|
||||||
|
name="threshold.thresholdDays"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -168,12 +268,14 @@ const requirementOptions = [
|
||||||
|
|
||||||
const Requirement = () => {
|
const Requirement = () => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const { errors } = useFormikContext()
|
const { errors, values } = useFormikContext()
|
||||||
|
|
||||||
const titleClass = {
|
const titleClass = {
|
||||||
[classes.error]: errors.requirement
|
[classes.error]: errors.requirement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isSuspend = values?.requirement?.requirement === 'suspend'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box display="flex" alignItems="center">
|
<Box display="flex" alignItems="center">
|
||||||
|
|
@ -181,12 +283,22 @@ const Requirement = () => {
|
||||||
</Box>
|
</Box>
|
||||||
<Field
|
<Field
|
||||||
component={RadioGroup}
|
component={RadioGroup}
|
||||||
name="requirement"
|
name="requirement.requirement"
|
||||||
options={requirementOptions}
|
options={requirementOptions}
|
||||||
labelClassName={classes.specialLabel}
|
labelClassName={classes.specialLabel}
|
||||||
radioClassName={classes.radio}
|
radioClassName={classes.radio}
|
||||||
className={classnames(classes.radioGroup, classes.specialGrid)}
|
className={classnames(classes.radioGroup, classes.specialGrid)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{isSuspend && (
|
||||||
|
<Field
|
||||||
|
className={classes.thresholdField}
|
||||||
|
component={TextInput}
|
||||||
|
label="Days"
|
||||||
|
size="lg"
|
||||||
|
name="requirement.suspensionDays"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -219,7 +331,149 @@ const DirectionDisplay = ({ code }) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const elements = [
|
const RequirementInput = () => {
|
||||||
|
const { values } = useFormikContext()
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const requirement = values?.requirement?.requirement
|
||||||
|
const isSuspend = requirement === 'suspend'
|
||||||
|
|
||||||
|
const display = getView(requirementOptions, 'display')(requirement)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box display="flex" alignItems="baseline">
|
||||||
|
{`${display} ${isSuspend ? 'for' : ''}`}
|
||||||
|
{isSuspend && (
|
||||||
|
<Field
|
||||||
|
bold
|
||||||
|
className={classes.suspensionDays}
|
||||||
|
name="requirement.suspensionDays"
|
||||||
|
component={TextInput}
|
||||||
|
textAlign="center"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isSuspend && 'days'}
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const RequirementView = ({ requirement, suspensionDays }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
const display = getView(requirementOptions, 'display')(requirement)
|
||||||
|
const isSuspend = requirement === 'suspend'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box display="flex" alignItems="baseline">
|
||||||
|
{`${display} ${isSuspend ? 'for' : ''}`}
|
||||||
|
{isSuspend && (
|
||||||
|
<Info2 className={classes.space} noMargin>
|
||||||
|
{suspensionDays}
|
||||||
|
</Info2>
|
||||||
|
)}
|
||||||
|
{isSuspend && 'days'}
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const DisplayThreshold = ({ config, currency, isEdit }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const inputClasses = {
|
||||||
|
[classes.input]: true,
|
||||||
|
[classes.limitedInput]: config?.triggerType === 'txVelocity',
|
||||||
|
[classes.daysInput]: config?.triggerType === 'consecutiveDays'
|
||||||
|
}
|
||||||
|
|
||||||
|
const threshold = config?.threshold?.threshold
|
||||||
|
const thresholdDays = config?.threshold?.thresholdDays
|
||||||
|
|
||||||
|
const Threshold = isEdit ? (
|
||||||
|
<Field
|
||||||
|
bold
|
||||||
|
className={classnames(inputClasses)}
|
||||||
|
name="threshold.threshold"
|
||||||
|
component={TextInput}
|
||||||
|
textAlign="right"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Info2 noMargin>{threshold}</Info2>
|
||||||
|
)
|
||||||
|
const ThresholdDays = isEdit ? (
|
||||||
|
<Field
|
||||||
|
bold
|
||||||
|
className={classnames(inputClasses)}
|
||||||
|
name="threshold.thresholdDays"
|
||||||
|
component={TextInput}
|
||||||
|
textAlign="right"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Info2 noMargin>{thresholdDays}</Info2>
|
||||||
|
)
|
||||||
|
|
||||||
|
switch (config?.triggerType) {
|
||||||
|
case 'txAmount':
|
||||||
|
return (
|
||||||
|
<Box display="flex" alignItems="baseline" justifyContent="right">
|
||||||
|
{Threshold}
|
||||||
|
<Label2 noMargin className={classes.lastSpace}>
|
||||||
|
{currency}
|
||||||
|
</Label2>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
case 'txVolume':
|
||||||
|
return (
|
||||||
|
<Box display="flex" alignItems="baseline" justifyContent="right">
|
||||||
|
{Threshold}
|
||||||
|
<Label2 noMargin className={classes.lastSpace}>
|
||||||
|
{currency}
|
||||||
|
</Label2>
|
||||||
|
<Label1 noMargin className={classes.space}>
|
||||||
|
in
|
||||||
|
</Label1>
|
||||||
|
{ThresholdDays}
|
||||||
|
<Label1 noMargin className={classes.lastSpace}>
|
||||||
|
days
|
||||||
|
</Label1>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
case 'txVelocity':
|
||||||
|
return (
|
||||||
|
<Box display="flex" alignItems="baseline" justifyContent="right">
|
||||||
|
{Threshold}
|
||||||
|
<Label1 className={classes.space} noMargin>
|
||||||
|
transactions in
|
||||||
|
</Label1>
|
||||||
|
{ThresholdDays}
|
||||||
|
<Label1 className={classes.lastSpace} noMargin>
|
||||||
|
days
|
||||||
|
</Label1>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
case 'consecutiveDays':
|
||||||
|
return (
|
||||||
|
<Box display="flex" alignItems="baseline" justifyContent="right">
|
||||||
|
{ThresholdDays}
|
||||||
|
<Label1 className={classes.lastSpace} noMargin>
|
||||||
|
days
|
||||||
|
</Label1>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThresholdInput = memo(({ currency }) => {
|
||||||
|
const { values } = useFormikContext()
|
||||||
|
|
||||||
|
return <DisplayThreshold isEdit={true} config={values} currency={currency} />
|
||||||
|
})
|
||||||
|
|
||||||
|
const ThresholdView = ({ config, currency }) => {
|
||||||
|
return <DisplayThreshold config={config} currency={currency} />
|
||||||
|
}
|
||||||
|
|
||||||
|
const getElements = (currency, classes) => [
|
||||||
{
|
{
|
||||||
name: 'triggerType',
|
name: 'triggerType',
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
|
|
@ -239,35 +493,28 @@ const elements = [
|
||||||
name: 'requirement',
|
name: 'requirement',
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
width: 230,
|
width: 230,
|
||||||
input: ({ field: { value: name } }) => (
|
bypassField: true,
|
||||||
<>{getView(requirementOptions, 'display')(name)}</>
|
input: RequirementInput,
|
||||||
),
|
view: it => <RequirementView {...it} />
|
||||||
view: getView(requirementOptions, 'display'),
|
|
||||||
inputProps: {
|
|
||||||
options: requirementOptions,
|
|
||||||
valueProp: 'code',
|
|
||||||
getLabel: R.path(['display']),
|
|
||||||
limit: null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'threshold',
|
name: 'threshold',
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
width: 260,
|
width: 284,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
input: TextInput
|
input: () => <ThresholdInput currency={currency} />,
|
||||||
|
view: (it, config) => <ThresholdView config={config} currency={currency} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'cashDirection',
|
name: 'cashDirection',
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
width: 282,
|
width: 282,
|
||||||
view: it => <DirectionDisplay code={it} />,
|
view: it => <DirectionDisplay code={it} />,
|
||||||
input: Autocomplete,
|
input: RadioGroup,
|
||||||
inputProps: {
|
inputProps: {
|
||||||
options: directionOptions,
|
labelClassName: classes.tableRadioLabel,
|
||||||
valueProp: 'code',
|
className: classes.tableRadioGroup,
|
||||||
getLabel: R.path(['display']),
|
options: directionOptions2
|
||||||
limit: null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -280,4 +527,37 @@ const sortBy = [
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
export { Schema, elements, direction, type, requirements, sortBy }
|
const fromServer = triggers =>
|
||||||
|
R.map(
|
||||||
|
({ requirement, suspensionDays, threshold, thresholdDays, ...rest }) => ({
|
||||||
|
requirement: {
|
||||||
|
requirement,
|
||||||
|
suspensionDays
|
||||||
|
},
|
||||||
|
threshold: {
|
||||||
|
threshold,
|
||||||
|
thresholdDays
|
||||||
|
},
|
||||||
|
...rest
|
||||||
|
})
|
||||||
|
)(triggers)
|
||||||
|
|
||||||
|
const toServer = triggers =>
|
||||||
|
R.map(({ requirement, threshold, ...rest }) => ({
|
||||||
|
requirement: requirement.requirement,
|
||||||
|
suspensionDays: requirement.suspensionDays,
|
||||||
|
threshold: threshold.threshold,
|
||||||
|
thresholdDays: threshold.thresholdDays,
|
||||||
|
...rest
|
||||||
|
}))(triggers)
|
||||||
|
|
||||||
|
export {
|
||||||
|
Schema,
|
||||||
|
getElements,
|
||||||
|
direction,
|
||||||
|
type,
|
||||||
|
requirements,
|
||||||
|
sortBy,
|
||||||
|
fromServer,
|
||||||
|
toServer
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue