import { makeStyles, Box } from '@material-ui/core' import classnames from 'classnames' import { Field, useFormikContext } from 'formik' import * as R from 'ramda' import React, { memo } from 'react' import * as Yup from 'yup' import { NumberInput, RadioGroup } from 'src/components/inputs/formik' import { H4, Label2, Label1, Info1, Info2 } from 'src/components/typography' import { errorColor } from 'src/styling/variables' import { transformNumber } from 'src/utils/number' // import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg' // import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg' 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 direction = Yup.string().required() const triggerType = Yup.string().required() const threshold = Yup.object().shape({ threshold: Yup.number() .nullable() .transform(transformNumber) .label('Invalid threshold'), thresholdDays: Yup.number() .transform(transformNumber) .nullable() .label('Invalid threshold days') }) const requirement = Yup.object().shape({ requirement: Yup.string().required(), suspensionDays: Yup.number() .transform(transformNumber) .nullable() }) const Schema = Yup.object() .shape({ triggerType, requirement, threshold // direction }) .test(({ threshold, triggerType }, context) => { const errorMessages = { txAmount: threshold => 'Amount must be non negative', txVolume: threshold => { const thresholdMessage = 'Volume must be non negative' const thresholdDaysMessage = 'Days must be positive' let message = '' if (threshold.threshold < 0) message = message.concat(thresholdMessage) if (threshold.thresholdDays <= 0) message = message ? message.concat(', ' + thresholdDaysMessage) : message.concat(thresholdDaysMessage) console.log(message) return message }, txVelocity: threshold => { const thresholdMessage = 'Transactions must be non negative' const thresholdDaysMessage = 'Days must be positive' let message = '' if (threshold.threshold <= 0) message = message.concat(thresholdMessage) if (threshold.thresholdDays <= 0) message = message ? message.concat(', ' + thresholdDaysMessage) : message.concat(thresholdDaysMessage) console.log(message) return message }, consecutiveDays: threshold => 'Days must be non negative' } const thresholdValidator = { txAmount: threshold => threshold.threshold >= 0, txVolume: threshold => threshold.threshold >= 0 && threshold.thresholdDays > 0, txVelocity: threshold => threshold.threshold > 0 && threshold.thresholdDays > 0, consecutiveDays: threshold => threshold.thresholdDays > 0 } return ( (triggerType && thresholdValidator?.[triggerType](threshold)) || context.createError({ path: 'threshold', message: errorMessages?.[triggerType](threshold) }) ) }) .test(({ requirement }, context) => { const requirementValidator = requirement => requirement.requirement === 'suspend' ? requirement.suspensionDays > 0 : true return ( (requirement && requirementValidator(requirement)) || context.createError({ path: 'requirement', message: 'Suspension days must be positive' }) ) }) // Direction V2 only // const directionSchema = Yup.object().shape({ direction }) // const directionOptions = [ // { // display: 'Both', // code: 'both' // }, // { // display: 'Only cash-in', // code: 'cashIn' // }, // { // display: 'Only cash-out', // code: 'cashOut' // } // ] // const directionOptions2 = [ // { // display: ( // <> // in // // ), // code: 'cashIn' // }, // { // display: ( // <> // out // // ), // code: 'cashOut' // }, // { // display: ( // <> // // // // // // // // // // ), // code: 'both' // } // ] // const Direction = () => { // const classes = useStyles() // const { errors } = useFormikContext() // const titleClass = { // [classes.error]: errors.direction // } // return ( // <> // //

// In which type of transactions will it trigger? //

//
// // // ) // } // const txDirection = { // schema: directionSchema, // options: directionOptions, // Component: Direction, // initialValues: { direction: '' } // } // TYPE const typeSchema = Yup.object() .shape({ triggerType: Yup.string().required(), threshold: Yup.object({ threshold: Yup.number() .transform(transformNumber) .nullable(), thresholdDays: Yup.number() .transform(transformNumber) .nullable() }) }) .test( 'are-fields-set', 'All fields must be set.', ({ triggerType, threshold }, context) => { const validator = { txAmount: threshold => threshold.threshold >= 0, txVolume: threshold => threshold.threshold >= 0 && threshold.thresholdDays > 0, txVelocity: threshold => threshold.threshold > 0 && threshold.thresholdDays > 0, consecutiveDays: threshold => threshold.thresholdDays > 0 } return ( (triggerType && validator?.[triggerType](threshold)) || context.createError({ path: 'threshold' }) ) } ) const typeOptions = [ { display: 'Transaction amount', code: 'txAmount' }, { display: 'Transaction volume', code: 'txVolume' }, { display: 'Transaction velocity', code: 'txVelocity' }, { display: 'Consecutive days', code: 'consecutiveDays' } ] const Type = ({ ...props }) => { const classes = useStyles() const { errors, touched, values } = useFormikContext() const typeClass = { [classes.error]: errors.triggerType && touched.triggerType } const containsType = R.contains(values?.triggerType) const isThresholdCurrencyEnabled = containsType(['txAmount', 'txVolume']) const isTransactionAmountEnabled = containsType(['txVelocity']) const isThresholdDaysEnabled = containsType(['txVolume', 'txVelocity']) const isConsecutiveDaysEnabled = containsType(['consecutiveDays']) const thresholdClass = { [classes.error]: errors.threshold && ((!containsType(['consecutiveDays']) && touched.threshold?.threshold) || (!containsType(['txAmount']) && touched.threshold?.thresholdDays)) } const isRadioGroupActive = () => { return ( isThresholdCurrencyEnabled || isTransactionAmountEnabled || isThresholdDaysEnabled || isConsecutiveDaysEnabled ) } return ( <>

Choose trigger type

{isRadioGroupActive() && (

Threshold

)}
{isThresholdCurrencyEnabled && ( <> {props.currency} )} {isTransactionAmountEnabled && ( <> transactions )} {isThresholdDaysEnabled && ( <> in days )} {isConsecutiveDaysEnabled && ( <> consecutive days )}
) } const type = currency => ({ schema: typeSchema, options: typeOptions, Component: Type, props: { currency }, initialValues: { triggerType: '', threshold: { threshold: '', thresholdDays: '' } } }) const requirementSchema = Yup.object().shape({ requirement: Yup.object({ requirement: Yup.string().required(), suspensionDays: Yup.number().when('requirement', { is: value => value === 'suspend', then: Yup.number() .required() .min(1), otherwise: Yup.number() .nullable() .transform(() => null) }) }).required() }) const requirementOptions = [ { display: 'SMS verification', code: 'sms' }, { display: 'ID card image', code: 'idCardPhoto' }, { display: 'ID data', code: 'idCardData' }, { display: 'Customer camera', code: 'facephoto' }, { display: 'Sanctions', code: 'sanctions' }, { display: 'US SSN', code: 'usSsn' }, // { display: 'Super user', code: 'superuser' }, { display: 'Suspend', code: 'suspend' }, { display: 'Block', code: 'block' } ] const Requirement = () => { const classes = useStyles() const { touched, errors, values } = useFormikContext() const titleClass = { [classes.error]: !R.isEmpty(R.omit(['suspensionDays'], errors.requirement)) || (errors.requirement && touched.requirement && errors.requirement.suspensionDays && touched.requirement.suspensionDays) } const isSuspend = values?.requirement?.requirement === 'suspend' return ( <>

Choose a requirement

{isSuspend && ( )} ) } const requirements = { schema: requirementSchema, options: requirementOptions, Component: Requirement, initialValues: { requirement: { requirement: '', suspensionDays: '' } } } const getView = (data, code, compare) => it => { if (!data) return '' return R.compose(R.prop(code), R.find(R.propEq(compare ?? 'code', it)))(data) } // const DirectionDisplay = ({ code }) => { // const classes = useStyles() // const displayName = getView(directionOptions, 'display')(code) // const showCashIn = code === 'cashIn' || code === 'both' // const showCashOut = code === 'cashOut' || code === 'both' // return ( //
// {showCashOut && } // {showCashIn && } // {displayName} //
// ) // } const RequirementInput = () => { const { values } = useFormikContext() const classes = useStyles() const requirement = values?.requirement?.requirement const isSuspend = requirement === 'suspend' const display = getView(requirementOptions, 'display')(requirement) return ( {`${display} ${isSuspend ? 'for' : ''}`} {isSuspend && ( )} {isSuspend && 'days'} ) } const RequirementView = ({ requirement, suspensionDays }) => { const classes = useStyles() const display = getView(requirementOptions, 'display')(requirement) const isSuspend = requirement === 'suspend' return ( {`${display} ${isSuspend ? 'for' : ''}`} {isSuspend && ( {suspensionDays} )} {isSuspend && 'days'} ) } 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 ? ( ) : ( {threshold} ) const ThresholdDays = isEdit ? ( ) : ( {thresholdDays} ) switch (config?.triggerType) { case 'txAmount': return ( {Threshold} {currency} ) case 'txVolume': return ( {Threshold} {currency} in {ThresholdDays} days ) case 'txVelocity': return ( {Threshold} transactions in {ThresholdDays} days ) case 'consecutiveDays': return ( {ThresholdDays} days ) default: return '' } } const ThresholdInput = memo(({ currency }) => { const { values } = useFormikContext() return }) const ThresholdView = ({ config, currency }) => { return } const getElements = (currency, classes) => [ { name: 'triggerType', size: 'sm', width: 230, input: ({ field: { value: name } }) => ( <>{getView(typeOptions, 'display')(name)} ), view: getView(typeOptions, 'display'), inputProps: { options: typeOptions, valueProp: 'code', labelProp: 'display', optionsLimit: null } }, { name: 'requirement', size: 'sm', width: 230, bypassField: true, input: RequirementInput, view: it => }, { name: 'threshold', size: 'sm', width: 284, textAlign: 'right', input: () => , view: (it, config) => } // { // name: 'direction', // size: 'sm', // width: 282, // view: it => , // input: RadioGroup, // inputProps: { // labelClassName: classes.tableRadioLabel, // className: classes.tableRadioGroup, // options: directionOptions2 // } // } ] const triggerOrder = R.map(R.prop('code'))(typeOptions) const sortBy = [ R.comparator( (a, b) => triggerOrder.indexOf(a.triggerType) < triggerOrder.indexOf(b.triggerType) ) ] 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, // txDirection, type, requirements, sortBy, fromServer, toServer, getView, requirementOptions }