fix: inverted name and phone positions on the customers list

fix: use the correct variables for getting the customers status

fix: customer name was showing as 'undefined undefined' when not present

fix: use the phone number as a fallback for the customer name when it's
not present

fix: removed phone number compliance card

fix: set a fixed size for the popup photos
This commit is contained in:
Liordino Neto 2020-10-23 12:51:35 -03:00 committed by Josh Harvey
parent f53a934092
commit 15618df4ef
10 changed files with 117 additions and 100 deletions

View file

@ -437,13 +437,15 @@ function batch () {
* @returns {array} Array of customers with it's transactions aggregations * @returns {array} Array of customers with it's transactions aggregations
*/ */
function getCustomersList () { function getCustomersList () {
const sql = `select id, name, authorized_override, front_camera_path, front_camera_override, const sql = `select id, authorized_override, days_suspended, front_camera_path, front_camera_override,
phone, sms_override, id_card_data, id_card_data_override, id_card_data_expiration, phone, sms_override, id_card_data, id_card_data_override, id_card_data_expiration,
id_card_photo_path, id_card_photo_override, us_ssn, us_ssn_override, sanctions, sanctions_at, id_card_photo_path, id_card_photo_override, us_ssn, us_ssn_override, sanctions, sanctions_at,
sanctions_override, total_txs, total_spent, created as last_active, fiat as last_tx_fiat, sanctions_override, total_txs, total_spent, created as last_active, fiat as last_tx_fiat,
fiat_code as last_tx_fiat_code, tx_class as last_tx_class fiat_code as last_tx_fiat_code, tx_class as last_tx_class
from ( from (
select c.id, c.name, c.authorized_override, c.front_camera_path, c.front_camera_override, select c.id, c.authorized_override,
greatest(0, date_part('day', c.suspended_until - now())) as days_suspended,
c.front_camera_path, c.front_camera_override,
c.phone, c.sms_override, c.id_card_data, c.id_card_data_override, c.id_card_data_expiration, c.phone, c.sms_override, c.id_card_data, c.id_card_data_override, c.id_card_data_expiration,
c.id_card_photo_path, c.id_card_photo_override, c.us_ssn, c.us_ssn_override, c.sanctions, c.id_card_photo_path, c.id_card_photo_override, c.us_ssn, c.us_ssn_override, c.sanctions,
c.sanctions_at, c.sanctions_override, t.tx_class, t.fiat, t.fiat_code, t.created, c.sanctions_at, c.sanctions_override, t.tx_class, t.fiat, t.fiat_code, t.created,
@ -473,13 +475,15 @@ function getCustomersList () {
* @returns {array} Array of customers with it's transactions aggregations * @returns {array} Array of customers with it's transactions aggregations
*/ */
function getCustomerById (id) { function getCustomerById (id) {
const sql = `select id, name, authorized_override, front_camera_path, front_camera_override, const sql = `select id, authorized_override, days_suspended, front_camera_path, front_camera_override,
phone, sms_override, id_card_data, id_card_data_override, id_card_data_expiration, phone, sms_override, id_card_data, id_card_data_override, id_card_data_expiration,
id_card_photo_path, id_card_photo_override, us_ssn, us_ssn_override, sanctions, sanctions_at, id_card_photo_path, id_card_photo_override, us_ssn, us_ssn_override, sanctions, sanctions_at,
sanctions_override, total_txs, total_spent, created as last_active, fiat as last_tx_fiat, sanctions_override, total_txs, total_spent, created as last_active, fiat as last_tx_fiat,
fiat_code as last_tx_fiat_code, tx_class as last_tx_class fiat_code as last_tx_fiat_code, tx_class as last_tx_class
from ( from (
select c.id, c.name, c.authorized_override, c.front_camera_path, c.front_camera_override, select c.id, c.authorized_override,
greatest(0, date_part('day', c.suspended_until - now())) as days_suspended,
c.front_camera_path, c.front_camera_override,
c.phone, c.sms_override, c.id_card_data, c.id_card_data_override, c.id_card_data_expiration, c.phone, c.sms_override, c.id_card_data, c.id_card_data_override, c.id_card_data_expiration,
c.id_card_photo_path, c.id_card_photo_override, c.us_ssn, c.us_ssn_override, c.sanctions, c.id_card_photo_path, c.id_card_photo_override, c.us_ssn, c.us_ssn_override, c.sanctions,
c.sanctions_at, c.sanctions_override, t.tx_class, t.fiat, t.fiat_code, t.created, c.sanctions_at, c.sanctions_override, t.tx_class, t.fiat, t.fiat_code, t.created,

View file

@ -65,8 +65,8 @@ const typeDefs = gql`
type Customer { type Customer {
id: ID! id: ID!
name: String
authorizedOverride: String authorizedOverride: String
daysSuspended: Int
frontCameraPath: String frontCameraPath: String
frontCameraOverride: String frontCameraOverride: String
phone: String phone: String
@ -91,7 +91,6 @@ const typeDefs = gql`
} }
input CustomerInput { input CustomerInput {
name: String
authorizedOverride: String authorizedOverride: String
frontCameraPath: String frontCameraPath: String
frontCameraOverride: String frontCameraOverride: String

View file

@ -11,42 +11,49 @@ import imagePopperStyles from './ImagePopper.styles'
const useStyles = makeStyles(imagePopperStyles) const useStyles = makeStyles(imagePopperStyles)
const ImagePopper = memo(({ className, width, height, src }) => { const ImagePopper = memo(
const classes = useStyles({ width, height }) ({ className, width, height, popupWidth, popupHeight, src }) => {
const [popperAnchorEl, setPopperAnchorEl] = useState(null) const classes = useStyles({
width,
height,
popupWidth,
popupHeight
})
const [popperAnchorEl, setPopperAnchorEl] = useState(null)
const handleOpenPopper = event => { const handleOpenPopper = event => {
setPopperAnchorEl(popperAnchorEl ? null : event.currentTarget) setPopperAnchorEl(popperAnchorEl ? null : event.currentTarget)
}
const handleClosePopper = () => {
setPopperAnchorEl(null)
}
const popperOpen = Boolean(popperAnchorEl)
const Image = ({ className }) => (
<img className={classnames(className)} src={src} alt="" />
)
return (
<ClickAwayListener onClickAway={handleClosePopper}>
<div className={classnames(classes.row, className)}>
<Image className={classes.image} />
<FeatureButton
Icon={ZoomIcon}
InverseIcon={ZoomIconInverse}
className={classes.button}
onClick={handleOpenPopper}
/>
<Popper open={popperOpen} anchorEl={popperAnchorEl} placement="top">
<div className={classes.popoverContent}>
<Image className={classes.popupImage} />
</div>
</Popper>
</div>
</ClickAwayListener>
)
} }
)
const handleClosePopper = () => {
setPopperAnchorEl(null)
}
const popperOpen = Boolean(popperAnchorEl)
const Image = ({ className }) => (
<img className={classnames(className)} src={src} alt="" />
)
return (
<ClickAwayListener onClickAway={handleClosePopper}>
<div className={classnames(classes.row, className)}>
<Image className={classes.unzoomedImg} />
<FeatureButton
Icon={ZoomIcon}
InverseIcon={ZoomIconInverse}
className={classes.button}
onClick={handleOpenPopper}
/>
<Popper open={popperOpen} anchorEl={popperAnchorEl} placement="top">
<div className={classes.popoverContent}>
<Image />
</div>
</Popper>
</div>
</ClickAwayListener>
)
})
export default ImagePopper export default ImagePopper

View file

@ -3,12 +3,17 @@ export default {
display: 'flex', display: 'flex',
flexDirection: 'row' flexDirection: 'row'
}, },
unzoomedImg: ({ width, height }) => ({ image: ({ width, height }) => ({
objectFit: 'cover', objectFit: 'cover',
borderRadius: '8px 0px 0px 8px', borderRadius: '8px 0px 0px 8px',
width, width,
height height
}), }),
popupImage: ({ popupWidth, popupHeight }) => ({
objectFit: 'cover',
width: popupWidth,
height: popupHeight
}),
button: ({ height }) => ({ button: ({ height }) => ({
borderRadius: '0px 8px 8px 0px', borderRadius: '0px 8px 8px 0px',
height height

View file

@ -24,6 +24,7 @@ import {
TransactionsList, TransactionsList,
ComplianceDetails ComplianceDetails
} from './components' } from './components'
import { getFormattedPhone, getName } from './helper'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
@ -32,7 +33,6 @@ const GET_CUSTOMER = gql`
config config
customer(customerId: $customerId) { customer(customerId: $customerId) {
id id
name
authorizedOverride authorizedOverride
frontCameraPath frontCameraPath
frontCameraOverride frontCameraOverride
@ -131,7 +131,7 @@ const CustomerProfile = memo(() => {
const sortedTransactions = R.sort(R.descend(R.prop('cryptoAtoms')))( const sortedTransactions = R.sort(R.descend(R.prop('cryptoAtoms')))(
rawTransactions rawTransactions
) )
const name = getName(customerData)
const blocked = const blocked =
R.path(['authorizedOverride'])(customerData) === OVERRIDE_REJECTED R.path(['authorizedOverride'])(customerData) === OVERRIDE_REJECTED
@ -148,7 +148,12 @@ const CustomerProfile = memo(() => {
Customers Customers
</Label1> </Label1>
<Label2 noMargin className={classes.labelLink}> <Label2 noMargin className={classes.labelLink}>
{R.path(['name'])(customerData) ?? R.path(['phone'])(customerData)} {name.length
? name
: getFormattedPhone(
R.path(['phone'])(customerData),
locale.country
)}
</Label2> </Label2>
</Breadcrumbs> </Breadcrumbs>
<div> <div>

View file

@ -13,7 +13,7 @@ const GET_CUSTOMERS = gql`
config config
customers { customers {
id id
name idCardData
phone phone
totalTxs totalTxs
totalSpent totalSpent
@ -22,6 +22,7 @@ const GET_CUSTOMERS = gql`
lastTxFiatCode lastTxFiatCode
lastTxClass lastTxClass
authorizedOverride authorizedOverride
daysSuspended
} }
} }
` `

View file

@ -1,5 +1,4 @@
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import { parsePhoneNumberFromString } from 'libphonenumber-js'
import moment from 'moment' import moment from 'moment'
import * as R from 'ramda' import * as R from 'ramda'
import React from 'react' import React from 'react'
@ -12,38 +11,23 @@ import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-ou
import { ifNotNull } from 'src/utils/nullCheck' import { ifNotNull } from 'src/utils/nullCheck'
import styles from './CustomersList.styles' import styles from './CustomersList.styles'
import { getAuthorizedStatus, getFormattedPhone, getName } from './helper'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
const CUSTOMER_VERIFIED = 'verified'
const CUSTOMER_BLOCKED = 'blocked'
const CustomersList = ({ data, locale, onClick, loading }) => { const CustomersList = ({ data, locale, onClick, loading }) => {
const classes = useStyles() const classes = useStyles()
const getAuthorizedStatus = authorizedOverride =>
authorizedOverride === CUSTOMER_VERIFIED
? { label: 'Authorized', type: 'success' }
: authorizedOverride === CUSTOMER_BLOCKED
? { label: 'Blocked', type: 'error' }
: { label: 'Suspended', type: 'warning' }
const elements = [ const elements = [
{
header: 'Name',
width: 241,
view: R.path(['name'])
},
{ {
header: 'Phone', header: 'Phone',
width: 172, width: 172,
view: it => view: it => getFormattedPhone(it.phone, locale.country)
it.phone && locale.country },
? parsePhoneNumberFromString( {
it.phone, header: 'Name',
locale.country width: 241,
).formatInternational() view: getName
: ''
}, },
{ {
header: 'Total TXs', header: 'Total TXs',
@ -84,9 +68,7 @@ const CustomersList = ({ data, locale, onClick, loading }) => {
{ {
header: 'Status', header: 'Status',
width: 188, width: 188,
view: it => ( view: it => <MainStatus statuses={[getAuthorizedStatus(it)]} />
<MainStatus statuses={[getAuthorizedStatus(it.authorizedOverride)]} />
)
} }
] ]

View file

@ -1,6 +1,5 @@
import { Box } from '@material-ui/core' import { Box } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import { parsePhoneNumberFromString } from 'libphonenumber-js'
import * as R from 'ramda' import * as R from 'ramda'
import React from 'react' import React from 'react'
@ -23,6 +22,8 @@ const useStyles = makeStyles(complianceDetailsStyles)
const imageWidth = 165 const imageWidth = 165
const imageHeight = 45 const imageHeight = 45
const popupImageWidth = 360
const popupImageHeight = 240
const Photo = ({ show, src }) => { const Photo = ({ show, src }) => {
const classes = useStyles({ width: imageWidth }) const classes = useStyles({ width: imageWidth })
@ -30,7 +31,13 @@ const Photo = ({ show, src }) => {
return ( return (
<> <>
{show ? ( {show ? (
<ImagePopper src={src} width={imageWidth} height={imageHeight} /> <ImagePopper
src={src}
width={imageWidth}
height={imageHeight}
popupWidth={popupImageWidth}
popupHeight={popupImageHeight}
/>
) : ( ) : (
<div className={classes.photoWrapper}> <div className={classes.photoWrapper}>
<CrossedCameraIcon /> <CrossedCameraIcon />
@ -43,14 +50,6 @@ const Photo = ({ show, src }) => {
const ComplianceDetails = ({ customer, locale, updateCustomer }) => { const ComplianceDetails = ({ customer, locale, updateCustomer }) => {
const classes = useStyles({ width: imageWidth }) const classes = useStyles({ width: imageWidth })
const phone =
customer.phone && locale.country
? parsePhoneNumberFromString(
customer.phone,
locale.country
).formatInternational()
: ''
const sanctions = R.path(['sanctions'])(customer) const sanctions = R.path(['sanctions'])(customer)
const sanctionsAt = R.path(['sanctionsAt'])(customer) const sanctionsAt = R.path(['sanctionsAt'])(customer)
const sanctionsDisplay = !sanctionsAt const sanctionsDisplay = !sanctionsAt
@ -66,15 +65,6 @@ const ComplianceDetails = ({ customer, locale, updateCustomer }) => {
<IdDataCard customerData={customer} updateCustomer={updateCustomer} /> <IdDataCard customerData={customer} updateCustomer={updateCustomer} />
<Box className={classes.complianceDetailsGrid}> <Box className={classes.complianceDetailsGrid}>
<Box className={classes.firstColumn}> <Box className={classes.firstColumn}>
<PropertyCard
title={'Phone nº'}
state={R.path(['smsOverride'])(customer)}
authorize={() =>
updateCustomer({ smsOverride: OVERRIDE_AUTHORIZED })
}
reject={() => updateCustomer({ smsOverride: OVERRIDE_REJECTED })}>
<Field label={'Phone'} display={phone} />
</PropertyCard>
<PropertyCard <PropertyCard
title={'ID photo'} title={'ID photo'}
state={R.path(['idCardPhotoOverride'])(customer)} state={R.path(['idCardPhotoOverride'])(customer)}

View file

@ -1,5 +1,4 @@
import { makeStyles, Box } from '@material-ui/core' import { makeStyles, Box } from '@material-ui/core'
import { parsePhoneNumberFromString } from 'libphonenumber-js'
import * as R from 'ramda' import * as R from 'ramda'
import React, { memo } from 'react' import React, { memo } from 'react'
@ -10,6 +9,7 @@ import { ReactComponent as LawIconInverse } from 'src/styling/icons/circle butto
import { ReactComponent as LawIcon } from 'src/styling/icons/circle buttons/law/zodiac.svg' import { ReactComponent as LawIcon } from 'src/styling/icons/circle buttons/law/zodiac.svg'
import mainStyles from '../CustomersList.styles' import mainStyles from '../CustomersList.styles'
import { getFormattedPhone, getName } from '../helper'
import FrontCameraPhoto from './FrontCameraPhoto' import FrontCameraPhoto from './FrontCameraPhoto'
@ -22,13 +22,7 @@ const CustomerDetails = memo(({ customer, locale, setShowCompliance }) => {
{ {
header: 'Phone number', header: 'Phone number',
size: 172, size: 172,
value: value: getFormattedPhone(customer.phone, locale.country)
customer.phone && locale.country
? parsePhoneNumberFromString(
customer.phone,
locale.country
).formatInternational()
: ''
}, },
{ {
header: 'ID number', header: 'ID number',
@ -42,6 +36,8 @@ const CustomerDetails = memo(({ customer, locale, setShowCompliance }) => {
} }
] ]
const name = getName(customer)
return ( return (
<Box display="flex"> <Box display="flex">
<FrontCameraPhoto <FrontCameraPhoto
@ -51,7 +47,9 @@ const CustomerDetails = memo(({ customer, locale, setShowCompliance }) => {
<div className={classes.name}> <div className={classes.name}>
<IdIcon className={classes.idIcon} /> <IdIcon className={classes.idIcon} />
<H2 noMargin> <H2 noMargin>
{R.path(['name'])(customer) ?? R.path(['phone'])(customer)} {name.length
? name
: getFormattedPhone(R.path(['phone'])(customer), locale.country)}
</H2> </H2>
<SubpageButton <SubpageButton
className={classes.subpageButton} className={classes.subpageButton}

View file

@ -0,0 +1,26 @@
import { parsePhoneNumberFromString } from 'libphonenumber-js'
import * as R from 'ramda'
const CUSTOMER_BLOCKED = 'blocked'
const getAuthorizedStatus = it =>
it.authorizedOverride === CUSTOMER_BLOCKED
? { label: 'Blocked', type: 'error' }
: it.daysSuspended > 0
? { label: `${it.daysSuspended} day suspension`, type: 'warning' }
: { label: 'Authorized', type: 'success' }
const getFormattedPhone = (phone, country) =>
phone && country
? parsePhoneNumberFromString(phone, country).formatInternational()
: ''
const getName = it => {
const idData = R.path(['idCardData'])(it)
return `${R.path(['firstName'])(idData) ?? ''} ${R.path(['lastName'])(
idData
) ?? ''}`.trim()
}
export { getAuthorizedStatus, getFormattedPhone, getName }