diff --git a/lib/customers.js b/lib/customers.js
index e96155f9..0f765bc9 100644
--- a/lib/customers.js
+++ b/lib/customers.js
@@ -993,6 +993,7 @@ function addCustomField (customerId, label, value) {
}
})
)
+ .then(res => !_.isNil(res))
}
function saveCustomField (customerId, fieldId, newValue) {
diff --git a/lib/new-admin/graphql/types/customer.type.js b/lib/new-admin/graphql/types/customer.type.js
index bdbf3a94..765f65e1 100644
--- a/lib/new-admin/graphql/types/customer.type.js
+++ b/lib/new-admin/graphql/types/customer.type.js
@@ -1,12 +1,6 @@
const { gql } = require('apollo-server-express')
const typeDef = gql`
- type CustomerCustomField {
- id: ID
- label: String
- value: String
- }
-
type Customer {
id: ID!
authorizedOverride: String
@@ -86,6 +80,12 @@ const typeDef = gql`
content: String
}
+ type CustomerCustomField {
+ id: ID
+ label: String
+ value: String
+ }
+
type Query {
customers(phone: String, name: String, address: String, id: String): [Customer] @auth
customer(customerId: ID!): Customer @auth
@@ -94,9 +94,9 @@ const typeDef = gql`
type Mutation {
setCustomer(customerId: ID!, customerInput: CustomerInput): Customer @auth
- addCustomField(customerId: ID!, label: String!, value: String!): CustomerCustomField @auth
- saveCustomField(customerId: ID!, fieldId: ID!, value: String!): CustomerCustomField @auth
- removeCustomField(customerId: ID!, fieldId: ID!): CustomerCustomField @auth
+ addCustomField(customerId: ID!, label: String!, value: String!): Boolean @auth
+ saveCustomField(customerId: ID!, fieldId: ID!, value: String!): Boolean @auth
+ removeCustomField(customerId: ID!, fieldId: ID!): Boolean @auth
editCustomer(customerId: ID!, customerEdit: CustomerEdit): Customer @auth
deleteEditedData(customerId: ID!, customerEdit: CustomerEdit): Customer @auth
replacePhoto(customerId: ID!, photoType: String, newPhoto: UploadGQL): Customer @auth
diff --git a/new-lamassu-admin/src/pages/Customers/CustomerData.js b/new-lamassu-admin/src/pages/Customers/CustomerData.js
index 18a53cac..a5346a0e 100644
--- a/new-lamassu-admin/src/pages/Customers/CustomerData.js
+++ b/new-lamassu-admin/src/pages/Customers/CustomerData.js
@@ -1,6 +1,6 @@
import Grid from '@material-ui/core/Grid'
import { makeStyles } from '@material-ui/core/styles'
-import { parse, format, isValid } from 'date-fns/fp'
+import { parse, format } from 'date-fns/fp'
import _ from 'lodash/fp'
import * as R from 'ramda'
import { useState, React } from 'react'
@@ -26,6 +26,7 @@ import { URI } from 'src/utils/apollo'
import styles from './CustomerData.styles.js'
import { EditableCard } from './components'
+import { customerDataElements, customerDataschemas } from './helper.js'
const useStyles = makeStyles(styles)
@@ -84,8 +85,8 @@ const CustomerData = ({
R.compose(R.toLower, R.path(['customInfoRequest', 'customRequest', 'name']))
)
- const customEntries = null // get customer custom entries
- const customRequirements = [] // get customer custom requirements
+ const customFields = []
+ const customRequirements = []
const customInfoRequests = sortByName(
R.path(['customInfoRequests'])(customer) ?? []
)
@@ -94,85 +95,6 @@ const CustomerData = ({
const getVisibleCards = _.filter(elem => elem.isAvailable)
- const schemas = {
- idScan: Yup.object().shape({
- firstName: Yup.string().required(),
- lastName: Yup.string().required(),
- documentNumber: Yup.string().required(),
- dateOfBirth: Yup.string()
- .test({
- test: val => isValid(parse(new Date(), 'yyyy-MM-dd', val))
- })
- .required(),
- gender: Yup.string().required(),
- country: Yup.string().required(),
- expirationDate: Yup.string()
- .test({
- test: val => isValid(parse(new Date(), 'yyyy-MM-dd', val))
- })
- .required()
- }),
- usSsn: Yup.object().shape({
- usSsn: Yup.string().required()
- }),
- idCardPhoto: Yup.object().shape({
- idCardPhoto: Yup.mixed().required()
- }),
- frontCamera: Yup.object().shape({
- frontCamera: Yup.mixed().required()
- })
- }
-
- const idScanElements = [
- {
- name: 'firstName',
- label: 'First name',
- component: TextInput
- },
- {
- name: 'documentNumber',
- label: 'ID number',
- component: TextInput
- },
- {
- name: 'dateOfBirth',
- label: 'Birthdate',
- component: TextInput
- },
- {
- name: 'gender',
- label: 'Gender',
- component: TextInput
- },
- {
- name: 'lastName',
- label: 'Last name',
- component: TextInput
- },
- {
- name: 'expirationDate',
- label: 'Expiration Date',
- component: TextInput
- },
- {
- name: 'country',
- label: 'Country',
- component: TextInput
- }
- ]
-
- const usSsnElements = [
- {
- name: 'usSsn',
- label: 'US SSN',
- component: TextInput,
- size: 190
- }
- ]
-
- const idCardPhotoElements = [{ name: 'idCardPhoto' }]
- const frontCameraElements = [{ name: 'frontCamera' }]
-
const initialValues = {
idScan: {
firstName: R.path(['firstName'])(idData) ?? '',
@@ -214,7 +136,7 @@ const CustomerData = ({
const cards = [
{
- fields: idScanElements,
+ fields: customerDataElements.idScanElements,
title: 'ID Scan',
titleIcon: ,
state: R.path(['idCardDataOverride'])(customer),
@@ -226,7 +148,7 @@ const CustomerData = ({
editCustomer({
idCardData: _.merge(idData, formatDates(values))
}),
- validationSchema: schemas.idScan,
+ validationSchema: customerDataschemas.idScan,
initialValues: initialValues.idScan,
isAvailable: !_.isNil(idData)
},
@@ -257,7 +179,7 @@ const CustomerData = ({
isAvailable: !_.isNil(sanctions)
},
{
- fields: frontCameraElements,
+ fields: customerDataElements.frontCameraElements,
title: 'Front facing camera',
titleIcon: ,
state: R.path(['frontCameraOverride'])(customer),
@@ -279,12 +201,12 @@ const CustomerData = ({
/>
) : null,
hasImage: true,
- validationSchema: schemas.frontCamera,
+ validationSchema: customerDataschemas.frontCamera,
initialValues: initialValues.frontCamera,
isAvailable: !_.isNil(customer.frontCameraPath)
},
{
- fields: idCardPhotoElements,
+ fields: customerDataElements.idCardPhotoElements,
title: 'ID card image',
titleIcon: ,
state: R.path(['idCardPhotoOverride'])(customer),
@@ -304,12 +226,12 @@ const CustomerData = ({
/>
) : null,
hasImage: true,
- validationSchema: schemas.idCardPhoto,
+ validationSchema: customerDataschemas.idCardPhoto,
initialValues: initialValues.idCardPhoto,
isAvailable: !_.isNil(customer.idCardPhotoPath)
},
{
- fields: usSsnElements,
+ fields: customerDataElements.usSsnElements,
title: 'US SSN',
titleIcon: ,
state: R.path(['usSsnOverride'])(customer),
@@ -317,7 +239,7 @@ const CustomerData = ({
reject: () => updateCustomer({ usSsnOverride: OVERRIDE_REJECTED }),
save: values => editCustomer({ usSsn: values.usSsn }),
deleteEditedData: () => deleteEditedData({ usSsn: null }),
- validationSchema: schemas.usSsn,
+ validationSchema: customerDataschemas.usSsn,
initialValues: initialValues.usSsn,
isAvailable: !_.isNil(customer.usSsn)
}
@@ -374,6 +296,29 @@ const CustomerData = ({
})
}, customInfoRequests)
+ R.forEach(it => {
+ customFields.push({
+ fields: [
+ {
+ name: it.label,
+ label: it.label,
+ value: it.value ?? '',
+ component: TextInput
+ }
+ ],
+ title: it.label,
+ titleIcon: ,
+ save: () => {},
+ deleteEditedData: () => {},
+ validationSchema: Yup.object().shape({
+ [it.label]: Yup.string()
+ }),
+ initialValues: {
+ [it.label]: it.value ?? ''
+ }
+ })
+ }, R.path(['customFields'])(customer) ?? [])
+
const editableCard = (
{
title,
@@ -444,9 +389,21 @@ const CustomerData = ({
)}
- {customEntries && (
+ {!_.isEmpty(customFields) && (
Custom data entry
+
+
+ {customFields.map((elem, idx) => {
+ return isEven(idx) ? editableCard(elem, idx) : null
+ })}
+
+
+ {customFields.map((elem, idx) => {
+ return !isEven(idx) ? editableCard(elem, idx) : null
+ })}
+
+
)}
{!R.isEmpty(customRequirements) && (
diff --git a/new-lamassu-admin/src/pages/Customers/CustomerProfile.js b/new-lamassu-admin/src/pages/Customers/CustomerProfile.js
index 91b076ee..d83960d3 100644
--- a/new-lamassu-admin/src/pages/Customers/CustomerProfile.js
+++ b/new-lamassu-admin/src/pages/Customers/CustomerProfile.js
@@ -236,6 +236,21 @@ const GET_DATA = gql`
}
`
+const SET_CUSTOM_ENTRY = gql`
+ mutation addCustomField($customerId: ID!, $label: String!, $value: String!) {
+ addCustomField(customerId: $customerId, label: $label, value: $value)
+ }
+`
+
+const GET_ACTIVE_CUSTOM_REQUESTS = gql`
+ query customInfoRequests($onlyEnabled: Boolean) {
+ customInfoRequests(onlyEnabled: $onlyEnabled) {
+ id
+ customRequest
+ }
+ }
+`
+
const CustomerProfile = memo(() => {
const history = useHistory()
@@ -255,6 +270,16 @@ const CustomerProfile = memo(() => {
const { data: configResponse, loading: configLoading } = useQuery(GET_DATA)
+ const { data: activeCustomRequests } = useQuery(GET_ACTIVE_CUSTOM_REQUESTS, {
+ variables: {
+ onlyEnabled: true
+ }
+ })
+
+ const [setCustomEntry] = useMutation(SET_CUSTOM_ENTRY, {
+ onCompleted: () => getCustomer()
+ })
+
const [replaceCustomerPhoto] = useMutation(REPLACE_CUSTOMER_PHOTO, {
onCompleted: () => getCustomer()
})
@@ -294,6 +319,17 @@ const CustomerProfile = memo(() => {
onCompleted: () => getCustomer()
})
+ const saveCustomEntry = it => {
+ setCustomEntry({
+ variables: {
+ customerId,
+ label: it.title,
+ value: it.data
+ }
+ })
+ setWizard(null)
+ }
+
const updateCustomer = it =>
setCustomer({
variables: {
@@ -385,6 +421,12 @@ const CustomerProfile = memo(() => {
const timezone = R.path(['config', 'locale_timezone'], configResponse)
+ const customRequirementOptions =
+ activeCustomRequests?.customInfoRequests?.map(it => ({
+ value: it.id,
+ display: it.customRequest.name
+ })) ?? []
+
const classes = useStyles()
return (
@@ -544,8 +586,11 @@ const CustomerProfile = memo(() => {
{wizard && (
{}}
+ save={saveCustomEntry}
+ addPhoto={replacePhoto}
+ addCustomerData={editCustomer}
onClose={() => setWizard(null)}
+ customRequirementOptions={customRequirementOptions}
/>
)}
diff --git a/new-lamassu-admin/src/pages/Customers/Wizard.js b/new-lamassu-admin/src/pages/Customers/Wizard.js
index 87bebaa8..16db1647 100644
--- a/new-lamassu-admin/src/pages/Customers/Wizard.js
+++ b/new-lamassu-admin/src/pages/Customers/Wizard.js
@@ -1,5 +1,5 @@
import { makeStyles } from '@material-ui/core'
-import { Form, Formik } from 'formik'
+import { Form, Formik, Field } from 'formik'
import * as R from 'ramda'
import React, { useState, Fragment } from 'react'
@@ -7,6 +7,7 @@ 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 { Dropdown } from 'src/components/inputs/formik'
import { comet } from 'src/styling/variables'
import { entryType, customElements } from './helper'
@@ -41,6 +42,10 @@ const styles = {
margin: [[0, 4, 0, 2]],
borderBottom: `1px solid ${comet}`,
display: 'inline-block'
+ },
+ dropdownField: {
+ marginTop: 16,
+ minWidth: 155
}
}
@@ -57,7 +62,14 @@ const getStep = (step, selectedValues) => {
}
}
-const Wizard = ({ onClose, save, error }) => {
+const Wizard = ({
+ onClose,
+ save,
+ error,
+ customRequirementOptions,
+ addCustomerData,
+ addPhoto
+}) => {
const classes = useStyles()
const [selectedValues, setSelectedValues] = useState(null)
@@ -66,6 +78,7 @@ const Wizard = ({ onClose, save, error }) => {
step: 1
})
+ const isCustom = values => values?.requirement === 'custom'
const isLastStep = step === LAST_STEP
const stepOptions = getStep(step, selectedValues)
@@ -103,18 +116,34 @@ const Wizard = ({ onClose, save, error }) => {
onSubmit={onContinue}
initialValues={stepOptions.initialValues}
validationSchema={stepOptions.schema}>
-
+ {({ values }) => (
+
+ )}
>
diff --git a/new-lamassu-admin/src/pages/Customers/components/EditableCard.js b/new-lamassu-admin/src/pages/Customers/components/EditableCard.js
index 0239db8a..65507e91 100644
--- a/new-lamassu-admin/src/pages/Customers/components/EditableCard.js
+++ b/new-lamassu-admin/src/pages/Customers/components/EditableCard.js
@@ -150,7 +150,7 @@ const EditableCard = ({
{title}
- {state && (
+ {state && authorize && (
@@ -279,7 +279,7 @@ const EditableCard = ({
Cancel
- {authorized.label !== 'Accepted' && (
+ {authorize && authorized.label !== 'Accepted' && (
)}
- {authorized.label !== 'Rejected' && (
+ {authorize && authorized.label !== 'Rejected' && (
{
) ?? ''}`.trim()
}
+// Manual Entry Wizard
+
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' }
+ { display: 'Text', code: 'text' }
+ // TODO: Requires backend modifications to support File and Image
+ // { 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' }
+ { display: 'US SSN', code: 'usSsn' },
+ { display: 'Customer camera', code: 'facephoto' }
]
const customTextOptions = [
@@ -108,7 +111,7 @@ const customTextSchema = Yup.object().shape({
data: Yup.string().required()
})
-const EntryType = () => {
+const EntryType = ({ hasCustomRequirementOptions }) => {
const classes = useStyles()
const { values } = useFormikContext()
@@ -154,7 +157,17 @@ const EntryType = () => {
isValid(parse(new Date(), 'yyyy-MM-dd', val))
+ })
+ .required(),
+ gender: Yup.string().required(),
+ country: Yup.string().required(),
+ expirationDate: Yup.string()
+ .test({
+ test: val => isValid(parse(new Date(), 'yyyy-MM-dd', val))
+ })
+ .required()
+ }),
+ usSsn: Yup.object().shape({
+ usSsn: Yup.string().required()
+ }),
+ idCardPhoto: Yup.object().shape({
+ idCardPhoto: Yup.mixed().required()
+ }),
+ frontCamera: Yup.object().shape({
+ frontCamera: Yup.mixed().required()
+ })
+}
+
const mapKeys = pair => {
const [key, value] = pair
if (key === 'txCustomerPhotoPath' || key === 'frontCameraPath') {
@@ -245,5 +339,7 @@ export {
getName,
entryType,
customElements,
- formatPhotosData
+ formatPhotosData,
+ customerDataElements,
+ customerDataschemas
}
diff --git a/new-lamassu-admin/src/pages/Dashboard/SystemPerformance/Graphs/RefScatterplot.js b/new-lamassu-admin/src/pages/Dashboard/SystemPerformance/Graphs/RefScatterplot.js
index f2eb4e78..ef092185 100644
--- a/new-lamassu-admin/src/pages/Dashboard/SystemPerformance/Graphs/RefScatterplot.js
+++ b/new-lamassu-admin/src/pages/Dashboard/SystemPerformance/Graphs/RefScatterplot.js
@@ -62,7 +62,7 @@ const Graph = ({ data, timeFrame, timezone }) => {
[]
)
- const filterDay = useMemo(
+ const filterDay = useCallback(
x => (timeFrame === 'day' ? x.getUTCHours() === 0 : x.getUTCDate() === 1),
[timeFrame]
)
diff --git a/new-lamassu-admin/src/pages/Triggers/helper.js b/new-lamassu-admin/src/pages/Triggers/helper.js
index fcf91e71..20a11664 100644
--- a/new-lamassu-admin/src/pages/Triggers/helper.js
+++ b/new-lamassu-admin/src/pages/Triggers/helper.js
@@ -554,7 +554,7 @@ const Requirement = () => {
}
const options = enableCustomRequirement
? [...requirementOptions, customInfoOption]
- : [...requirementOptions, { ...customInfoOption, disabled: true }]
+ : [...requirementOptions]
const titleClass = {
[classes.error]:
(!!errors.requirement && !isSuspend) || (isSuspend && hasRequirementError)