chore: use monorepo organization
This commit is contained in:
parent
deaf7d6ecc
commit
a687827f7e
1099 changed files with 8184 additions and 11535 deletions
793
packages/admin-ui/src/pages/Triggers/helper.jsx
Normal file
793
packages/admin-ui/src/pages/Triggers/helper.jsx
Normal file
|
|
@ -0,0 +1,793 @@
|
|||
import classnames from 'classnames'
|
||||
import { Field, useFormikContext } from 'formik'
|
||||
import * as R from 'ramda'
|
||||
import React, { memo } from 'react'
|
||||
import { H4, Label2, Label1, Info1, Info2 } from 'src/components/typography'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
import { NumberInput, RadioGroup, Dropdown } from 'src/components/inputs/formik'
|
||||
import { transformNumber } from 'src/utils/number'
|
||||
import { onlyFirstToUpper } from 'src/utils/string'
|
||||
|
||||
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 greater than or equal to 0',
|
||||
txVolume: threshold => {
|
||||
const thresholdMessage = 'Volume must be greater than or equal to 0'
|
||||
const thresholdDaysMessage = 'Days must be greater than 0'
|
||||
const message = []
|
||||
if (threshold.threshold < 0) message.push(thresholdMessage)
|
||||
if (threshold.thresholdDays <= 0) message.push(thresholdDaysMessage)
|
||||
return message.join(', ')
|
||||
},
|
||||
txVelocity: threshold => {
|
||||
const thresholdMessage = 'Transactions must be greater than 0'
|
||||
const thresholdDaysMessage = 'Days must be greater than 0'
|
||||
const message = []
|
||||
if (threshold.threshold <= 0) message.push(thresholdMessage)
|
||||
if (threshold.thresholdDays <= 0) message.push(thresholdDaysMessage)
|
||||
return message.join(', ')
|
||||
},
|
||||
consecutiveDays: threshold => 'Days must be greater than 0'
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
if (triggerType && thresholdValidator[triggerType](threshold)) return
|
||||
|
||||
return context.createError({
|
||||
path: 'threshold',
|
||||
message: errorMessages[triggerType](threshold)
|
||||
})
|
||||
})
|
||||
.test(({ requirement }, context) => {
|
||||
const requirementValidator = requirement =>
|
||||
requirement.requirement === 'suspend'
|
||||
? requirement.suspensionDays > 0
|
||||
: true
|
||||
|
||||
if (requirement && requirementValidator(requirement)) return
|
||||
|
||||
return context.createError({
|
||||
path: 'requirement',
|
||||
message: 'Suspension days must be greater than 0'
|
||||
})
|
||||
})
|
||||
|
||||
// TYPE
|
||||
const typeSchema = Yup.object()
|
||||
.shape({
|
||||
triggerType: Yup.string('The trigger type must be a string').required(
|
||||
'The trigger type is required'
|
||||
),
|
||||
threshold: Yup.object({
|
||||
threshold: Yup.number().transform(transformNumber).nullable(),
|
||||
thresholdDays: Yup.number().transform(transformNumber).nullable()
|
||||
})
|
||||
})
|
||||
.test(({ threshold, triggerType }, context) => {
|
||||
const errorMessages = {
|
||||
txAmount: threshold => 'Amount must be greater than or equal to 0',
|
||||
txVolume: threshold => {
|
||||
const thresholdMessage = 'Volume must be greater than or equal to 0'
|
||||
const thresholdDaysMessage = 'Days must be greater than 0'
|
||||
const message = []
|
||||
if (!threshold.threshold || threshold.threshold < 0)
|
||||
message.push(thresholdMessage)
|
||||
if (!threshold.thresholdDays || threshold.thresholdDays <= 0)
|
||||
message.push(thresholdDaysMessage)
|
||||
return message.join(', ')
|
||||
},
|
||||
txVelocity: threshold => {
|
||||
const thresholdMessage = 'Transactions must be greater than 0'
|
||||
const thresholdDaysMessage = 'Days must be greater than 0'
|
||||
const message = []
|
||||
if (!threshold.threshold || threshold.threshold <= 0)
|
||||
message.push(thresholdMessage)
|
||||
if (!threshold.thresholdDays || threshold.thresholdDays <= 0)
|
||||
message.push(thresholdDaysMessage)
|
||||
return message.join(', ')
|
||||
},
|
||||
consecutiveDays: threshold => 'Days must be greater than 0'
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
if (!triggerType) return
|
||||
|
||||
if (triggerType && thresholdValidator[triggerType](threshold)) return
|
||||
|
||||
return context.createError({
|
||||
path: 'threshold',
|
||||
message: errorMessages[triggerType](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 { errors, touched, values, setTouched, handleChange } =
|
||||
useFormikContext()
|
||||
|
||||
const typeClass = {
|
||||
'text-tomato': 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 hasAmountError =
|
||||
!!errors.threshold &&
|
||||
!!touched.threshold?.threshold &&
|
||||
!isConsecutiveDaysEnabled &&
|
||||
(!values.threshold?.threshold || values.threshold?.threshold < 0)
|
||||
const hasDaysError =
|
||||
!!errors.threshold &&
|
||||
!!touched.threshold?.thresholdDays &&
|
||||
!containsType(['txAmount']) &&
|
||||
(!values.threshold?.thresholdDays || values.threshold?.thresholdDays < 0)
|
||||
|
||||
const triggerTypeError = !!(hasDaysError || hasAmountError)
|
||||
|
||||
const thresholdClass = {
|
||||
'text-tomato': triggerTypeError
|
||||
}
|
||||
|
||||
const isRadioGroupActive = () => {
|
||||
return (
|
||||
isThresholdCurrencyEnabled ||
|
||||
isTransactionAmountEnabled ||
|
||||
isThresholdDaysEnabled ||
|
||||
isConsecutiveDaysEnabled
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center">
|
||||
<H4 className={classnames(typeClass)}>Choose trigger type</H4>
|
||||
</div>
|
||||
<Field
|
||||
component={RadioGroup}
|
||||
name="triggerType"
|
||||
options={typeOptions}
|
||||
labelClassName="h-10 py-0 px-3"
|
||||
radioClassName="p-1 m-1"
|
||||
className="flex-row"
|
||||
onChange={e => {
|
||||
handleChange(e)
|
||||
setTouched({
|
||||
threshold: false,
|
||||
thresholdDays: false
|
||||
})
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="flex flex-col">
|
||||
{isRadioGroupActive() && (
|
||||
<H4 className={classnames(thresholdClass, 'mt-12')}>Threshold</H4>
|
||||
)}
|
||||
<div className="flex flex-row">
|
||||
{isThresholdCurrencyEnabled && (
|
||||
<>
|
||||
<Field
|
||||
className="mr-2 w-19"
|
||||
component={NumberInput}
|
||||
size="lg"
|
||||
name="threshold.threshold"
|
||||
error={hasAmountError}
|
||||
/>
|
||||
<Info1 className="mt-2">{props.currency}</Info1>
|
||||
</>
|
||||
)}
|
||||
{isTransactionAmountEnabled && (
|
||||
<>
|
||||
<Field
|
||||
className="mr-2 w-19"
|
||||
component={NumberInput}
|
||||
size="lg"
|
||||
name="threshold.threshold"
|
||||
error={hasAmountError}
|
||||
/>
|
||||
<Info1 className="mt-2">transactions</Info1>
|
||||
</>
|
||||
)}
|
||||
{isThresholdDaysEnabled && (
|
||||
<>
|
||||
<Info1 className={classnames(typeClass, 'mx-2 mt-2')}>in</Info1>
|
||||
<Field
|
||||
className="mr-2 w-19"
|
||||
component={NumberInput}
|
||||
size="lg"
|
||||
name="threshold.thresholdDays"
|
||||
error={hasDaysError}
|
||||
/>
|
||||
<Info1 className="mt-2">days</Info1>
|
||||
</>
|
||||
)}
|
||||
{isConsecutiveDaysEnabled && (
|
||||
<>
|
||||
<Field
|
||||
className="mr-2 w-19"
|
||||
component={NumberInput}
|
||||
size="lg"
|
||||
name="threshold.thresholdDays"
|
||||
error={hasDaysError}
|
||||
/>
|
||||
<Info1 className="mt-2">consecutive days</Info1>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
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: schema => schema.nullable().transform(transformNumber),
|
||||
otherwise: schema => schema.nullable().transform(() => null)
|
||||
}),
|
||||
customInfoRequestId: Yup.string().when('requirement', {
|
||||
is: value => value !== 'custom',
|
||||
then: schema => schema.nullable().transform(() => '')
|
||||
}),
|
||||
externalService: Yup.string().when('requirement', {
|
||||
is: value => value !== 'external',
|
||||
then: schema => schema.nullable().transform(() => '')
|
||||
})
|
||||
}).required()
|
||||
})
|
||||
.test(({ requirement }, context) => {
|
||||
const requirementValidator = (requirement, type) => {
|
||||
switch (type) {
|
||||
case 'suspend':
|
||||
return requirement.requirement === type
|
||||
? requirement.suspensionDays > 0
|
||||
: true
|
||||
case 'custom':
|
||||
return requirement.requirement === type
|
||||
? !R.isNil(requirement.customInfoRequestId)
|
||||
: true
|
||||
case 'external':
|
||||
return requirement.requirement === type
|
||||
? !R.isNil(requirement.externalService)
|
||||
: true
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (requirement && !requirementValidator(requirement, 'suspend'))
|
||||
return context.createError({
|
||||
path: 'requirement',
|
||||
message: 'Suspension days must be greater than 0'
|
||||
})
|
||||
|
||||
if (requirement && !requirementValidator(requirement, 'custom'))
|
||||
return context.createError({
|
||||
path: 'requirement',
|
||||
message: 'You must select an item'
|
||||
})
|
||||
|
||||
if (requirement && !requirementValidator(requirement, 'external'))
|
||||
return context.createError({
|
||||
path: 'requirement',
|
||||
message: 'You must select an item'
|
||||
})
|
||||
})
|
||||
|
||||
const requirementOptions = [
|
||||
{ display: 'SMS verification', code: 'sms' },
|
||||
{
|
||||
display: 'Email verification',
|
||||
code: 'email'
|
||||
},
|
||||
{ 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' },
|
||||
{
|
||||
display: 'External verification',
|
||||
code: 'external'
|
||||
}
|
||||
]
|
||||
|
||||
const hasRequirementError = (errors, touched, values) =>
|
||||
!!errors.requirement &&
|
||||
!!touched.requirement?.suspensionDays &&
|
||||
(!values.requirement?.suspensionDays ||
|
||||
values.requirement?.suspensionDays < 0)
|
||||
|
||||
const hasCustomRequirementError = (errors, touched, values) =>
|
||||
!!errors.requirement &&
|
||||
!!touched.requirement?.customInfoRequestId &&
|
||||
(!values.requirement?.customInfoRequestId ||
|
||||
!R.isNil(values.requirement?.customInfoRequestId))
|
||||
|
||||
const hasExternalRequirementError = (errors, touched, values) =>
|
||||
!!errors.requirement &&
|
||||
!!touched.requirement?.externalService &&
|
||||
!values.requirement?.externalService
|
||||
|
||||
const Requirement = ({
|
||||
config = {},
|
||||
triggers,
|
||||
emailAuth,
|
||||
complianceServices,
|
||||
customInfoRequests = []
|
||||
}) => {
|
||||
const { touched, errors, values, handleChange, setTouched } =
|
||||
useFormikContext()
|
||||
|
||||
const isSuspend = values?.requirement?.requirement === 'suspend'
|
||||
const isCustom = values?.requirement?.requirement === 'custom'
|
||||
const isExternal = values?.requirement?.requirement === 'external'
|
||||
|
||||
const customRequirementsInUse = R.reduce(
|
||||
(acc, value) => {
|
||||
if (value.requirement.requirement === 'custom')
|
||||
acc.push({
|
||||
triggerType: value.triggerType,
|
||||
id: value.requirement.customInfoRequestId
|
||||
})
|
||||
return acc
|
||||
},
|
||||
[],
|
||||
triggers
|
||||
)
|
||||
|
||||
const availableCustomRequirements = R.filter(
|
||||
it =>
|
||||
!R.includes(
|
||||
{
|
||||
triggerType: config.triggerType,
|
||||
id: it.id
|
||||
},
|
||||
customRequirementsInUse
|
||||
),
|
||||
customInfoRequests
|
||||
)
|
||||
|
||||
const makeCustomReqOptions = () =>
|
||||
availableCustomRequirements.map(it => ({
|
||||
value: it.id,
|
||||
display: it.customRequest.name
|
||||
}))
|
||||
|
||||
const enableCustomRequirement = !R.isEmpty(availableCustomRequirements)
|
||||
|
||||
const customInfoOption = {
|
||||
display: 'Custom information requirement',
|
||||
code: 'custom'
|
||||
}
|
||||
|
||||
const itemToRemove = emailAuth ? 'sms' : 'email'
|
||||
const reqOptions = requirementOptions.filter(it => it.code !== itemToRemove)
|
||||
const options = R.clone(reqOptions)
|
||||
|
||||
enableCustomRequirement && options.push(customInfoOption)
|
||||
|
||||
const titleClass = {
|
||||
'text-tomato':
|
||||
(!!errors.requirement && !isSuspend && !isCustom && !isExternal) ||
|
||||
(isSuspend && hasRequirementError(errors, touched, values)) ||
|
||||
(isCustom && hasCustomRequirementError(errors, touched, values)) ||
|
||||
(isExternal && hasExternalRequirementError(errors, touched, values))
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center">
|
||||
<H4 className={classnames(titleClass)}>Choose a requirement</H4>
|
||||
</div>
|
||||
<Field
|
||||
component={RadioGroup}
|
||||
name="requirement.requirement"
|
||||
options={options}
|
||||
labelClassName="h-10 p-0"
|
||||
radioClassName="p-1 m-1"
|
||||
className="flex-row grid grid-cols-[182px_162px_181px]"
|
||||
onChange={e => {
|
||||
handleChange(e)
|
||||
setTouched({
|
||||
suspensionDays: false
|
||||
})
|
||||
}}
|
||||
/>
|
||||
{isSuspend && (
|
||||
<Field
|
||||
className="mr-2 w-19"
|
||||
component={NumberInput}
|
||||
label="Days"
|
||||
size="lg"
|
||||
name="requirement.suspensionDays"
|
||||
error={hasRequirementError(errors, touched, values)}
|
||||
/>
|
||||
)}
|
||||
{isCustom && (
|
||||
<div>
|
||||
<Field
|
||||
className="mt-4 min-w-[155px]"
|
||||
component={Dropdown}
|
||||
label="Available requests"
|
||||
name="requirement.customInfoRequestId"
|
||||
options={makeCustomReqOptions()}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{isExternal && (
|
||||
<div className="flex flex-col gap-4">
|
||||
<Field
|
||||
className="mt-4 w-[155px]"
|
||||
component={Dropdown}
|
||||
label="Service"
|
||||
name="requirement.externalService"
|
||||
options={complianceServices.map(it => ({
|
||||
value: it.code,
|
||||
display: it.display
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const requirements = (
|
||||
config,
|
||||
triggers,
|
||||
customInfoRequests,
|
||||
complianceServices,
|
||||
emailAuth
|
||||
) => ({
|
||||
schema: requirementSchema,
|
||||
options: requirementOptions,
|
||||
Component: Requirement,
|
||||
props: {
|
||||
config,
|
||||
triggers,
|
||||
customInfoRequests,
|
||||
emailAuth,
|
||||
complianceServices
|
||||
},
|
||||
hasRequirementError: hasRequirementError,
|
||||
hasCustomRequirementError: hasCustomRequirementError,
|
||||
hasExternalRequirementError: hasExternalRequirementError,
|
||||
initialValues: {
|
||||
requirement: {
|
||||
requirement: '',
|
||||
suspensionDays: '',
|
||||
customInfoRequestId: '',
|
||||
externalService: ''
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const getView = (data, code, compare) => it => {
|
||||
if (!data) return ''
|
||||
|
||||
return R.compose(R.prop(code), R.find(R.propEq(compare ?? 'code', it)))(data)
|
||||
}
|
||||
|
||||
const customReqIdMatches = customReqId => it => {
|
||||
return it.id === customReqId
|
||||
}
|
||||
|
||||
const RequirementInput = ({ customInfoRequests = [] }) => {
|
||||
const { values } = useFormikContext()
|
||||
const requirement = values?.requirement?.requirement
|
||||
const customRequestId =
|
||||
R.path(['requirement', 'customInfoRequestId'])(values) ?? ''
|
||||
const isSuspend = requirement === 'suspend'
|
||||
const display = customRequestId
|
||||
? (R.path(['customRequest', 'name'])(
|
||||
R.find(customReqIdMatches(customRequestId))(customInfoRequests)
|
||||
) ?? '')
|
||||
: getView(requirementOptions, 'display')(requirement)
|
||||
|
||||
return (
|
||||
<div className="flex items-baseline">
|
||||
{`${display} ${isSuspend ? 'for' : ''}`}
|
||||
{isSuspend && (
|
||||
<Field
|
||||
bold
|
||||
className="w-8"
|
||||
name="requirement.suspensionDays"
|
||||
component={NumberInput}
|
||||
textAlign="center"
|
||||
/>
|
||||
)}
|
||||
{isSuspend && 'days'}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const RequirementView = ({
|
||||
requirement,
|
||||
suspensionDays,
|
||||
customInfoRequestId,
|
||||
externalService,
|
||||
customInfoRequests = []
|
||||
}) => {
|
||||
const display =
|
||||
requirement === 'custom'
|
||||
? (R.path(['customRequest', 'name'])(
|
||||
R.find(customReqIdMatches(customInfoRequestId))(customInfoRequests)
|
||||
) ?? '')
|
||||
: requirement === 'external'
|
||||
? `External verification (${onlyFirstToUpper(externalService)})`
|
||||
: getView(requirementOptions, 'display')(requirement)
|
||||
const isSuspend = requirement === 'suspend'
|
||||
return (
|
||||
<div className="flex items-baseline">
|
||||
{`${display} ${isSuspend ? 'for' : ''}`}
|
||||
{isSuspend && (
|
||||
<Info2 className="mx-2" noMargin>
|
||||
{suspensionDays}
|
||||
</Info2>
|
||||
)}
|
||||
{isSuspend && 'days'}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const DisplayThreshold = ({ config, currency, isEdit }) => {
|
||||
const inputClasses = {
|
||||
'-mt-1': true,
|
||||
'w-13': config?.triggerType === 'txVelocity',
|
||||
'w-15': 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={NumberInput}
|
||||
textAlign="right"
|
||||
/>
|
||||
) : (
|
||||
<Info2 noMargin>{threshold}</Info2>
|
||||
)
|
||||
const ThresholdDays = isEdit ? (
|
||||
<Field
|
||||
bold
|
||||
className={classnames(inputClasses)}
|
||||
name="threshold.thresholdDays"
|
||||
component={NumberInput}
|
||||
textAlign="right"
|
||||
/>
|
||||
) : (
|
||||
<Info2 noMargin>{thresholdDays}</Info2>
|
||||
)
|
||||
|
||||
switch (config?.triggerType) {
|
||||
case 'txAmount':
|
||||
return (
|
||||
<div className="flex items-baseline justify-end">
|
||||
{Threshold}
|
||||
<Label2 noMargin className="ml-2">
|
||||
{currency}
|
||||
</Label2>
|
||||
</div>
|
||||
)
|
||||
case 'txVolume':
|
||||
return (
|
||||
<div className="flex items-baseline justify-end">
|
||||
{Threshold}
|
||||
<Label2 noMargin className="ml-2">
|
||||
{currency}
|
||||
</Label2>
|
||||
<Label1 noMargin className="mx-2">
|
||||
in
|
||||
</Label1>
|
||||
{ThresholdDays}
|
||||
<Label1 noMargin className="ml-2">
|
||||
days
|
||||
</Label1>
|
||||
</div>
|
||||
)
|
||||
case 'txVelocity':
|
||||
return (
|
||||
<div className="flex items-baseline justify-end">
|
||||
{Threshold}
|
||||
<Label1 className="mx-2" noMargin>
|
||||
transactions in
|
||||
</Label1>
|
||||
{ThresholdDays}
|
||||
<Label1 className="ml-2" noMargin>
|
||||
days
|
||||
</Label1>
|
||||
</div>
|
||||
)
|
||||
case 'consecutiveDays':
|
||||
return (
|
||||
<div className="flex items-baseline justify-end">
|
||||
{ThresholdDays}
|
||||
<Label1 className="mx-2" noMargin>
|
||||
days
|
||||
</Label1>
|
||||
</div>
|
||||
)
|
||||
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, customInfoRequests) => [
|
||||
{
|
||||
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: 260,
|
||||
bypassField: true,
|
||||
input: () => <RequirementInput customInfoRequests={customInfoRequests} />,
|
||||
view: it => (
|
||||
<RequirementView {...it} customInfoRequests={customInfoRequests} />
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'threshold',
|
||||
size: 'sm',
|
||||
width: 254,
|
||||
textAlign: 'right',
|
||||
input: () => <ThresholdInput currency={currency} />,
|
||||
view: (it, config) => <ThresholdView config={config} currency={currency} />
|
||||
}
|
||||
]
|
||||
|
||||
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 => {
|
||||
return R.map(
|
||||
({
|
||||
requirement,
|
||||
suspensionDays,
|
||||
threshold,
|
||||
thresholdDays,
|
||||
customInfoRequestId,
|
||||
externalService,
|
||||
...rest
|
||||
}) => ({
|
||||
requirement: {
|
||||
requirement,
|
||||
suspensionDays,
|
||||
customInfoRequestId,
|
||||
externalService
|
||||
},
|
||||
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,
|
||||
customInfoRequestId: requirement.customInfoRequestId,
|
||||
externalService: requirement.externalService,
|
||||
...rest
|
||||
}))(triggers)
|
||||
|
||||
export {
|
||||
Schema,
|
||||
getElements, // txDirection,
|
||||
type,
|
||||
requirements,
|
||||
sortBy,
|
||||
fromServer,
|
||||
toServer,
|
||||
getView,
|
||||
requirementOptions
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue