Merge pull request #1127 from josepfo/fix/twilio-api-ui-fields
fix: format twilio api response
This commit is contained in:
commit
df73dd847f
6 changed files with 203 additions and 114 deletions
|
|
@ -125,7 +125,7 @@ async function updateCustomer (id, data, userToken) {
|
|||
}
|
||||
|
||||
if (data.subscriberInfo) {
|
||||
Promise.all([getCustomerById(id), settingsLoader.loadLatest()])
|
||||
await Promise.all([getCustomerById(id), settingsLoader.loadLatest()])
|
||||
.then(([customer, config]) => sms.getLookup(config, customer.phone))
|
||||
.then(res => updateSubscriberData(id, res, userToken))
|
||||
.catch(logger.error)
|
||||
|
|
@ -575,6 +575,7 @@ function getCustomerById (id) {
|
|||
.then(assignCustomerData)
|
||||
.then(getCustomInfoRequestsData)
|
||||
.then(camelizeDeep)
|
||||
.then(formatSubscriberInfo)
|
||||
}
|
||||
|
||||
function assignCustomerData (customer) {
|
||||
|
|
@ -582,6 +583,26 @@ function assignCustomerData (customer) {
|
|||
.then(customerEditedData => selectLatestData(customer, customerEditedData))
|
||||
}
|
||||
|
||||
function formatSubscriberInfo(customer) {
|
||||
const subscriberInfo = customer.subscriberInfo
|
||||
if(!subscriberInfo) return customer
|
||||
const result = subscriberInfo.result
|
||||
if(subscriberInfo.status !== 'successful' || _.isEmpty(result)) return customer
|
||||
|
||||
const name = _.get('belongs_to.name')(result)
|
||||
const street = _.get('current_addresses[0].street_line_1')(result)
|
||||
const city = _.get('current_addresses[0].city')(result)
|
||||
const stateCode = _.get('current_addresses[0].state_code')(result)
|
||||
const postalCode = _.get('current_addresses[0].postal_code')(result)
|
||||
|
||||
customer.subscriberInfo = {
|
||||
name,
|
||||
address: `${street ?? ''} ${city ?? ''}${street || city ? ',' : ''} ${stateCode ?? ''} ${postalCode ?? ''}`
|
||||
}
|
||||
|
||||
return customer
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the specific customer manually edited data
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { DialogActions, DialogContent, Dialog } from '@material-ui/core'
|
||||
import Grid from '@material-ui/core/Grid'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { parse, format } from 'date-fns/fp'
|
||||
|
|
@ -7,9 +6,9 @@ import { useState, React } from 'react'
|
|||
import * as Yup from 'yup'
|
||||
|
||||
import ImagePopper from 'src/components/ImagePopper'
|
||||
import { FeatureButton, Button, IconButton } from 'src/components/buttons'
|
||||
import { FeatureButton } from 'src/components/buttons'
|
||||
import { TextInput } from 'src/components/inputs/formik'
|
||||
import { H3, Info3, H2 } from 'src/components/typography'
|
||||
import { H3, Info3 } from 'src/components/typography'
|
||||
import {
|
||||
OVERRIDE_AUTHORIZED,
|
||||
OVERRIDE_REJECTED
|
||||
|
|
@ -17,7 +16,6 @@ import {
|
|||
import { ReactComponent as CardIcon } from 'src/styling/icons/ID/card/comet.svg'
|
||||
import { ReactComponent as PhoneIcon } from 'src/styling/icons/ID/phone/comet.svg'
|
||||
import { ReactComponent as CrossedCameraIcon } from 'src/styling/icons/ID/photo/crossed-camera.svg'
|
||||
import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.svg'
|
||||
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/comet.svg'
|
||||
import { ReactComponent as CustomerListViewReversedIcon } from 'src/styling/icons/circle buttons/customer-list-view/white.svg'
|
||||
import { ReactComponent as CustomerListViewIcon } from 'src/styling/icons/circle buttons/customer-list-view/zodiac.svg'
|
||||
|
|
@ -74,11 +72,11 @@ const CustomerData = ({
|
|||
updateCustomRequest,
|
||||
authorizeCustomRequest,
|
||||
updateCustomEntry,
|
||||
retrieveAdditionalData
|
||||
retrieveAdditionalDataDialog,
|
||||
setRetrieve
|
||||
}) => {
|
||||
const classes = useStyles()
|
||||
const [listView, setListView] = useState(false)
|
||||
const [retrieve, setRetrieve] = useState(false)
|
||||
|
||||
const idData = R.path(['idCardData'])(customer)
|
||||
const rawExpirationDate = R.path(['expirationDate'])(idData)
|
||||
|
|
@ -103,7 +101,7 @@ const CustomerData = ({
|
|||
)
|
||||
|
||||
const phone = R.path(['phone'])(customer)
|
||||
const smsData = R.path(['subscriberInfo', 'result'])(customer)
|
||||
const smsData = R.path(['subscriberInfo'])(customer)
|
||||
|
||||
const isEven = elem => elem % 2 === 0
|
||||
|
||||
|
|
@ -179,7 +177,8 @@ const CustomerData = ({
|
|||
}),
|
||||
validationSchema: customerDataSchemas.idCardData,
|
||||
initialValues: initialValues.idCardData,
|
||||
isAvailable: !R.isNil(idData)
|
||||
isAvailable: !R.isNil(idData),
|
||||
editable: true
|
||||
},
|
||||
{
|
||||
fields: smsDataElements,
|
||||
|
|
@ -199,7 +198,8 @@ const CustomerData = ({
|
|||
retrieveAdditionalData: () => setRetrieve(true),
|
||||
initialValues: initialValues.smsData,
|
||||
isAvailable: !R.isNil(phone),
|
||||
hasAdditionalData: !R.isNil(smsData) && !R.isEmpty(smsData)
|
||||
hasAdditionalData: !R.isNil(smsData) && !R.isEmpty(smsData),
|
||||
editable: false
|
||||
},
|
||||
{
|
||||
title: 'Name',
|
||||
|
|
@ -207,7 +207,8 @@ const CustomerData = ({
|
|||
authorize: () => {},
|
||||
reject: () => {},
|
||||
save: () => {},
|
||||
isAvailable: false
|
||||
isAvailable: false,
|
||||
editable: true
|
||||
},
|
||||
{
|
||||
title: 'Sanctions check',
|
||||
|
|
@ -217,7 +218,8 @@ const CustomerData = ({
|
|||
updateCustomer({ sanctionsOverride: OVERRIDE_AUTHORIZED }),
|
||||
reject: () => updateCustomer({ sanctionsOverride: OVERRIDE_REJECTED }),
|
||||
children: <Info3>{sanctionsDisplay}</Info3>,
|
||||
isAvailable: !R.isNil(sanctions)
|
||||
isAvailable: !R.isNil(sanctions),
|
||||
editable: true
|
||||
},
|
||||
{
|
||||
fields: customerDataElements.frontCamera,
|
||||
|
|
@ -244,7 +246,8 @@ const CustomerData = ({
|
|||
hasImage: true,
|
||||
validationSchema: customerDataSchemas.frontCamera,
|
||||
initialValues: initialValues.frontCamera,
|
||||
isAvailable: !R.isNil(customer.frontCameraPath)
|
||||
isAvailable: !R.isNil(customer.frontCameraPath),
|
||||
editable: true
|
||||
},
|
||||
{
|
||||
fields: customerDataElements.idCardPhoto,
|
||||
|
|
@ -269,7 +272,8 @@ const CustomerData = ({
|
|||
hasImage: true,
|
||||
validationSchema: customerDataSchemas.idCardPhoto,
|
||||
initialValues: initialValues.idCardPhoto,
|
||||
isAvailable: !R.isNil(customer.idCardPhotoPath)
|
||||
isAvailable: !R.isNil(customer.idCardPhotoPath),
|
||||
editable: true
|
||||
},
|
||||
{
|
||||
fields: customerDataElements.usSsn,
|
||||
|
|
@ -282,7 +286,8 @@ const CustomerData = ({
|
|||
deleteEditedData: () => deleteEditedData({ usSsn: null }),
|
||||
validationSchema: customerDataSchemas.usSsn,
|
||||
initialValues: initialValues.usSsn,
|
||||
isAvailable: !R.isNil(customer.usSsn)
|
||||
isAvailable: !R.isNil(customer.usSsn),
|
||||
editable: true
|
||||
}
|
||||
]
|
||||
|
||||
|
|
@ -373,7 +378,7 @@ const CustomerData = ({
|
|||
name: it,
|
||||
label: onlyFirstToUpper(it),
|
||||
component: TextInput,
|
||||
editable: true
|
||||
editable: false
|
||||
})
|
||||
}, R.keys(smsData) ?? [])
|
||||
|
||||
|
|
@ -392,7 +397,8 @@ const CustomerData = ({
|
|||
validationSchema,
|
||||
initialValues,
|
||||
hasImage,
|
||||
hasAdditionalData
|
||||
hasAdditionalData,
|
||||
editable
|
||||
},
|
||||
idx
|
||||
) => {
|
||||
|
|
@ -412,7 +418,8 @@ const CustomerData = ({
|
|||
initialValues={initialValues}
|
||||
save={save}
|
||||
deleteEditedData={deleteEditedData}
|
||||
retrieveAdditionalData={retrieveAdditionalData}></EditableCard>
|
||||
retrieveAdditionalData={retrieveAdditionalData}
|
||||
editable={editable}></EditableCard>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -491,67 +498,9 @@ const CustomerData = ({
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
<RetrieveDataDialog
|
||||
setRetrieve={setRetrieve}
|
||||
retrieveAdditionalData={retrieveAdditionalData}
|
||||
open={retrieve}></RetrieveDataDialog>
|
||||
{retrieveAdditionalDataDialog}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const RetrieveDataDialog = ({
|
||||
setRetrieve,
|
||||
retrieveAdditionalData,
|
||||
open,
|
||||
props
|
||||
}) => {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
aria-labelledby="form-dialog-title"
|
||||
PaperProps={{
|
||||
style: {
|
||||
borderRadius: 8,
|
||||
minWidth: 656,
|
||||
bottom: 125,
|
||||
right: 7
|
||||
}
|
||||
}}
|
||||
{...props}>
|
||||
<div className={classes.closeButton}>
|
||||
<IconButton
|
||||
size={16}
|
||||
aria-label="close"
|
||||
onClick={() => setRetrieve(false)}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
<H2 className={classes.dialogTitle}>{'Retrieve API data from Twilio'}</H2>
|
||||
<DialogContent className={classes.dialogContent}>
|
||||
<Info3>{`With this action you'll be using Twilio's API to retrieve additional
|
||||
data from this user. This includes name and address, if available.\n`}</Info3>
|
||||
<Info3>{` There is a small cost from Twilio for each retrieval. Would you like
|
||||
to proceed?`}</Info3>
|
||||
</DialogContent>
|
||||
<DialogActions className={classes.dialogActions}>
|
||||
<Button
|
||||
backgroundColor="grey"
|
||||
className={classes.cancelButton}
|
||||
onClick={() => setRetrieve(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
retrieveAdditionalData()
|
||||
setRetrieve(false)
|
||||
}}>
|
||||
Confirm
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default CustomerData
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { offColor, spacer } from 'src/styling/variables'
|
||||
import { offColor } from 'src/styling/variables'
|
||||
|
||||
export default {
|
||||
header: {
|
||||
|
|
@ -45,26 +45,5 @@ export default {
|
|||
left: '100%',
|
||||
marginLeft: 15
|
||||
}
|
||||
},
|
||||
closeButton: {
|
||||
display: 'flex',
|
||||
padding: [[spacer * 2, spacer * 2, 0, spacer * 2]],
|
||||
paddingRight: spacer * 1.5,
|
||||
justifyContent: 'end'
|
||||
},
|
||||
dialogTitle: {
|
||||
margin: [[0, spacer * 2, spacer, spacer * 4 + spacer]]
|
||||
},
|
||||
dialogContent: {
|
||||
width: 615,
|
||||
marginLeft: 16
|
||||
},
|
||||
dialogActions: {
|
||||
padding: spacer * 4,
|
||||
paddingTop: spacer * 2
|
||||
},
|
||||
cancelButton: {
|
||||
marginRight: 8,
|
||||
padding: 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,27 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { makeStyles, Breadcrumbs, Box } from '@material-ui/core'
|
||||
import {
|
||||
makeStyles,
|
||||
Breadcrumbs,
|
||||
Box,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
Dialog
|
||||
} from '@material-ui/core'
|
||||
import NavigateNextIcon from '@material-ui/icons/NavigateNext'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { memo, useState } from 'react'
|
||||
import { useHistory, useParams } from 'react-router-dom'
|
||||
|
||||
import { ActionButton } from 'src/components/buttons'
|
||||
import ErrorMessage from 'src/components/ErrorMessage'
|
||||
import { Button, IconButton, ActionButton } from 'src/components/buttons'
|
||||
import { Switch } from 'src/components/inputs'
|
||||
import { Label1, Label2 } from 'src/components/typography'
|
||||
import { Label1, Label2, H2, Info3 } from 'src/components/typography'
|
||||
import {
|
||||
OVERRIDE_AUTHORIZED,
|
||||
OVERRIDE_REJECTED
|
||||
} from 'src/pages/Customers/components/propertyCard'
|
||||
import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.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 BlockReversedIcon } from 'src/styling/icons/button/block/white.svg'
|
||||
|
|
@ -280,9 +289,10 @@ const GET_ACTIVE_CUSTOM_REQUESTS = gql`
|
|||
const CustomerProfile = memo(() => {
|
||||
const history = useHistory()
|
||||
|
||||
const [retrieve, setRetrieve] = useState(false)
|
||||
const [showCompliance, setShowCompliance] = useState(false)
|
||||
const [wizard, setWizard] = useState(false)
|
||||
const [error] = useState(null)
|
||||
const [error, setError] = useState(null)
|
||||
const [clickedItem, setClickedItem] = useState('overview')
|
||||
const { id: customerId } = useParams()
|
||||
|
||||
|
|
@ -323,7 +333,11 @@ const CustomerProfile = memo(() => {
|
|||
})
|
||||
|
||||
const [setCustomer] = useMutation(SET_CUSTOMER, {
|
||||
onCompleted: () => getCustomer()
|
||||
onCompleted: () => {
|
||||
getCustomer()
|
||||
setRetrieve(false)
|
||||
},
|
||||
onError: error => setError(error)
|
||||
})
|
||||
|
||||
const [authorizeCustomRequest] = useMutation(SET_AUTHORIZED_REQUEST, {
|
||||
|
|
@ -640,7 +654,20 @@ const CustomerProfile = memo(() => {
|
|||
updateCustomRequest={setCustomerCustomInfoRequest}
|
||||
authorizeCustomRequest={authorizeCustomRequest}
|
||||
updateCustomEntry={updateCustomEntry}
|
||||
retrieveAdditionalData={retrieveAdditionalData}></CustomerData>
|
||||
setRetrieve={setRetrieve}
|
||||
retrieveAdditionalDataDialog={
|
||||
<RetrieveDataDialog
|
||||
onDismissed={() => {
|
||||
setError(null)
|
||||
setRetrieve(false)
|
||||
}}
|
||||
onConfirmed={() => {
|
||||
setError(null)
|
||||
retrieveAdditionalData()
|
||||
}}
|
||||
error={error}
|
||||
open={retrieve}></RetrieveDataDialog>
|
||||
}></CustomerData>
|
||||
</div>
|
||||
)}
|
||||
{isNotes && (
|
||||
|
|
@ -677,4 +704,64 @@ const CustomerProfile = memo(() => {
|
|||
)
|
||||
})
|
||||
|
||||
const RetrieveDataDialog = ({
|
||||
onConfirmed,
|
||||
onDismissed,
|
||||
open,
|
||||
error,
|
||||
props
|
||||
}) => {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
aria-labelledby="form-dialog-title"
|
||||
PaperProps={{
|
||||
style: {
|
||||
borderRadius: 8,
|
||||
minWidth: 656,
|
||||
bottom: 125,
|
||||
right: 7
|
||||
}
|
||||
}}
|
||||
{...props}>
|
||||
<div className={classes.closeButton}>
|
||||
<IconButton
|
||||
size={16}
|
||||
aria-label="close"
|
||||
onClick={() => onDismissed(false)}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
<H2 className={classes.dialogTitle}>{'Retrieve API data from Twilio'}</H2>
|
||||
<DialogContent className={classes.dialogContent}>
|
||||
<Info3>{`With this action you'll be using Twilio's API to retrieve additional
|
||||
data from this user. This includes name and address, if available.\n`}</Info3>
|
||||
<Info3>{` There is a small cost from Twilio for each retrieval. Would you like
|
||||
to proceed?`}</Info3>
|
||||
</DialogContent>
|
||||
{error && (
|
||||
<ErrorMessage className={classes.errorMessage}>
|
||||
Failed to save
|
||||
</ErrorMessage>
|
||||
)}
|
||||
<DialogActions className={classes.dialogActions}>
|
||||
<Button
|
||||
backgroundColor="grey"
|
||||
className={classes.cancelButton}
|
||||
onClick={() => onDismissed(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
onConfirmed()
|
||||
}}>
|
||||
Confirm
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default CustomerProfile
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { comet, subheaderColor } from 'src/styling/variables'
|
||||
import { comet, subheaderColor, spacer } from 'src/styling/variables'
|
||||
|
||||
export default {
|
||||
labelLink: {
|
||||
|
|
@ -53,5 +53,29 @@ export default {
|
|||
backgroundColor: subheaderColor,
|
||||
borderRadius: 8,
|
||||
padding: [[0, 5]]
|
||||
},
|
||||
closeButton: {
|
||||
display: 'flex',
|
||||
padding: [[spacer * 2, spacer * 2, 0, spacer * 2]],
|
||||
paddingRight: spacer * 1.5,
|
||||
justifyContent: 'end'
|
||||
},
|
||||
dialogTitle: {
|
||||
margin: [[0, spacer * 2, spacer, spacer * 4 + spacer]]
|
||||
},
|
||||
dialogContent: {
|
||||
width: 615,
|
||||
marginLeft: 16
|
||||
},
|
||||
dialogActions: {
|
||||
padding: spacer * 4,
|
||||
paddingTop: spacer * 2
|
||||
},
|
||||
cancelButton: {
|
||||
marginRight: 8,
|
||||
padding: 0
|
||||
},
|
||||
errorMessage: {
|
||||
marginLeft: 38
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,7 +145,8 @@ const EditableCard = ({
|
|||
initialValues,
|
||||
deleteEditedData,
|
||||
retrieveAdditionalData,
|
||||
hasAdditionalData = true
|
||||
hasAdditionalData = true,
|
||||
editable
|
||||
}) => {
|
||||
const classes = useStyles()
|
||||
|
||||
|
|
@ -274,6 +275,7 @@ const EditableCard = ({
|
|||
</ActionButton>
|
||||
)}
|
||||
</div>
|
||||
{editable && (
|
||||
<ActionButton
|
||||
color="primary"
|
||||
Icon={EditIcon}
|
||||
|
|
@ -281,6 +283,33 @@ const EditableCard = ({
|
|||
onClick={() => setEditing(true)}>
|
||||
Edit
|
||||
</ActionButton>
|
||||
)}
|
||||
{!editable &&
|
||||
authorize &&
|
||||
authorized.label !== 'Accepted' && (
|
||||
<div className={classes.button}>
|
||||
<ActionButton
|
||||
color="spring"
|
||||
type="button"
|
||||
Icon={AuthorizeIcon}
|
||||
InverseIcon={AuthorizeIcon}
|
||||
onClick={() => authorize()}>
|
||||
Authorize
|
||||
</ActionButton>
|
||||
</div>
|
||||
)}
|
||||
{!editable &&
|
||||
authorize &&
|
||||
authorized.label !== 'Rejected' && (
|
||||
<ActionButton
|
||||
color="tomato"
|
||||
type="button"
|
||||
Icon={BlockIcon}
|
||||
InverseIcon={BlockIcon}
|
||||
onClick={() => reject()}>
|
||||
Reject
|
||||
</ActionButton>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{editing && (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue