feat: overview rework and customer data mosaic view

This commit is contained in:
José Oliveira 2021-09-17 11:32:43 +01:00
parent 3102c2bef0
commit 4b461c0a57
8 changed files with 376 additions and 43 deletions

View file

@ -0,0 +1,168 @@
import { Box, CardContent, Card } from '@material-ui/core'
import Grid from '@material-ui/core/Grid'
import { makeStyles } from '@material-ui/core/styles'
import moment from 'moment'
import * as R from 'ramda'
import { useState, React } from 'react'
import { Tooltip } from 'src/components/Tooltip'
import { SubpageButton } from 'src/components/buttons'
import { H3 } from 'src/components/typography'
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 EditIcon } from 'src/styling/icons/action/edit/disabled.svg'
import { ReactComponent as ReverseListingViewIcon } from 'src/styling/icons/circle buttons/listing-view/white.svg'
import { ReactComponent as ListingViewIcon } from 'src/styling/icons/circle buttons/listing-view/zodiac.svg'
import { ifNotNull } from 'src/utils/nullCheck'
import styles from './CustomerData.styles.js'
import { Field } from './components'
import { getName } from './helper.js'
const useStyles = makeStyles(styles)
const CustomerData = ({ customer, updateCustomer }) => {
const classes = useStyles()
const idData = R.path(['idCardData'])(customer)
const rawExpirationDate = R.path(['expirationDate'])(idData)
const country = R.path(['country'])(idData)
const rawDob = R.path(['dateOfBirth'])(idData)
const nameElements = [
{
header: 'Name',
display: `${getName(customer)}`,
size: 190
}
]
const idScanElementsFirstRow = [
{
header: 'Name',
display: `${getName(customer)}`,
size: 190
},
{
header: 'ID number',
display: R.path(['documentNumber'])(idData),
size: 160
},
{
header: 'Age',
display: ifNotNull(
rawDob,
moment.utc().diff(moment.utc(rawDob).format('YYYY-MM-DD'), 'years')
),
size: 50
}
]
const idScanElementsSecondRow = [
{
header: 'Gender',
display: R.path(['gender'])(idData),
size: 80
},
{
header: country === 'Canada' ? 'Province' : 'State',
display: R.path(['state'])(idData),
size: 120
},
{
header: 'Expiration Date',
display: ifNotNull(
rawExpirationDate,
moment.utc(rawExpirationDate).format('YYYY-MM-DD')
)
}
]
const [listView, setListView] = useState(false)
return (
<div>
<div className={classes.header}>
<H3 className={classes.title}>{'Customer data'}</H3>
<SubpageButton
className={classes.subpageButton}
Icon={ListingViewIcon}
InverseIcon={ReverseListingViewIcon}
toggle={setListView}></SubpageButton>
</div>
<div>
{listView && <H3>{''}</H3>}
{!listView && (
<Grid container>
<Grid container direction="column" item xs={6}>
<Card className={classes.leftSideCard}>
<CardContent>
<div className={classes.cardHeader}>
<EditIcon className={classes.editIcon} />
<H3 className={classes.cardTitle}>{'Name'}</H3>
<Tooltip width={304}></Tooltip>
</div>
<Box display="flex" alignItems="center">
{nameElements.map(({ header, display, size }, idx) => (
<Field
key={idx}
label={header}
display={display}
size={size}
/>
))}
</Box>
</CardContent>
</Card>
<Card className={classes.leftSideCard}>
<CardContent>
<div className={classes.cardHeader}>
<PhoneIcon className={classes.cardIcon} />
<H3 className={classes.cardTitle}>{'ID Scan'}</H3>
<Tooltip width={304}></Tooltip>
</div>
<Box display="flex" alignItems="center">
{idScanElementsFirstRow.map(
({ header, display, size }, idx) => (
<Field
key={idx}
label={header}
display={display}
size={size}
/>
)
)}
</Box>
<Box display="flex" alignItems="center">
{idScanElementsSecondRow.map(
({ header, display, size }, idx) => (
<Field
key={idx}
label={header}
display={display}
size={size}
/>
)
)}
</Box>
</CardContent>
</Card>
</Grid>
<Grid container direction="column" item xs={6}>
<Card className={classes.rightSideCard}>
<CardContent>
<div className={classes.cardHeader}>
<CardIcon className={classes.cardIcon} />
<H3 className={classes.cardTitle}>{'SMS Confirmation'}</H3>
<Tooltip width={304}></Tooltip>
</div>
</CardContent>
</Card>
</Grid>
</Grid>
)}
</div>
</div>
)
}
export default CustomerData

View file

@ -0,0 +1,34 @@
export default {
header: {
display: 'flex',
flexDirection: 'row',
marginBottom: 15
},
title: {
marginTop: 7,
marginRight: 20
},
leftSideCard: {
borderRadius: 10,
marginRight: 10,
marginBottom: 15
},
rightSideCard: {
borderRadius: 10,
marginLeft: 10
},
cardHeader: {
display: 'flex',
flexDirection: 'row',
marginBottom: 15
},
editIcon: {
marginTop: 5
},
cardIcon: {
marginTop: 7
},
cardTitle: {
margin: [[8, 15, 15, 15]]
}
}

View file

@ -1,6 +1,7 @@
import { useQuery, useMutation } from '@apollo/react-hooks'
import { makeStyles, Breadcrumbs, Box } from '@material-ui/core'
import NavigateNextIcon from '@material-ui/icons/NavigateNext'
// import classnames from 'classnames'
import gql from 'graphql-tag'
import * as R from 'ramda'
import React, { memo, useState } from 'react'
@ -18,11 +19,13 @@ import { ReactComponent as BlockReversedIcon } from 'src/styling/icons/button/bl
import { ReactComponent as BlockIcon } from 'src/styling/icons/button/block/zodiac.svg'
import { fromNamespace, namespaces } from 'src/utils/config'
import CustomerData from './CustomerData'
import styles from './CustomerProfile.styles'
import {
CustomerDetails,
TransactionsList,
ComplianceDetails
ComplianceDetails,
CustomerSidebar
} from './components'
import { getFormattedPhone, getName } from './helper'
@ -109,6 +112,7 @@ const CustomerProfile = memo(() => {
const classes = useStyles()
const history = useHistory()
const [showCompliance, setShowCompliance] = useState(false)
const [clickedItem, setClickedItem] = useState('overview')
const { id: customerId } = useParams()
const { data: customerResponse, refetch: getCustomer, loading } = useQuery(
@ -130,6 +134,8 @@ const CustomerProfile = memo(() => {
}
})
const onClickSidebarItem = e => setClickedItem(e.code)
const configData = R.path(['config'])(customerResponse) ?? []
const locale = configData && fromNamespace(namespaces.LOCALE, configData)
const customerData = R.path(['customer'])(customerResponse) ?? []
@ -142,6 +148,8 @@ const CustomerProfile = memo(() => {
R.path(['authorizedOverride'])(customerData) === OVERRIDE_REJECTED
const isSuspended = customerData.isSuspended
const isCustomerData = clickedItem === 'customerData'
const isOverview = clickedItem === 'overview'
return (
<>
@ -164,21 +172,18 @@ const CustomerProfile = memo(() => {
)}
</Label2>
</Breadcrumbs>
<div>
<Box
className={classes.customerDetails}
display="flex"
justifyContent="space-between">
<CustomerDetails
customer={customerData}
txData={sortedTransactions}
locale={locale}
setShowCompliance={() => setShowCompliance(!showCompliance)}
/>
<div className={classes.panels}>
<div className={classes.leftSidePanel}>
{!loading && !customerData.isAnonymous && (
<div>
<div>
<CustomerSidebar
isSelected={it => it.code === clickedItem}
onClick={onClickSidebarItem}
/>
</div>
<Label1 className={classes.actionLabel}>Actions</Label1>
<div className={classes.customerActions}>
<div>
{isSuspended && (
<ActionButton
color="primary"
@ -194,6 +199,7 @@ const CustomerProfile = memo(() => {
)}
<ActionButton
color="primary"
className={classes.customerBlock}
Icon={blocked ? AuthorizeIcon : BlockIcon}
InverseIcon={
blocked ? AuthorizeReversedIcon : BlockReversedIcon
@ -226,24 +232,57 @@ const CustomerProfile = memo(() => {
{`Retrieve information`}
</ActionButton>
</div>
<div>
<ActionButton
className={classes.customerDiscount}
color="primary"
onClick={() => {}}>
{`Add individual discount`}
</ActionButton>
</div>
</div>
)}
</Box>
</div>
<div className={classes.rightSidePanel}>
{isOverview && (
<div>
<Box
className={classes.customerDetails}
display="flex"
justifyContent="space-between">
<CustomerDetails
customer={customerData}
locale={locale}
setShowCompliance={() => setShowCompliance(!showCompliance)}
/>
</Box>
{!showCompliance && (
<div>
<TransactionsList
customer={customerData}
data={sortedTransactions}
locale={locale}
loading={loading}
/>
</div>
)}
{showCompliance && (
<ComplianceDetails
customer={customerData}
updateCustomer={updateCustomer}
/>
)}
</div>
)}
{isCustomerData && (
<div>
<CustomerData
customer={customerData}
updateCustomer={updateCustomer}></CustomerData>
</div>
)}
</div>
</div>
{!showCompliance && (
<TransactionsList
customer={customerData}
data={sortedTransactions}
locale={locale}
loading={loading}
/>
)}
{showCompliance && (
<ComplianceDetails
customer={customerData}
updateCustomer={updateCustomer}
/>
)}
</>
)
})

View file

@ -15,14 +15,26 @@ export default {
customerDetails: {
marginBottom: 18
},
customerActions: {
customerBlock: {
display: 'flex',
flexDirection: 'row',
'& button': {
marginRight: 15
},
'& > :last-child': {
marginRight: 0
}
margin: [[8, 0, 4, 0]],
padding: [[0, 35, 0]]
},
customerDiscount: {
display: 'flex',
flexDirection: 'row',
margin: [[0, 0, 4, 0]],
padding: [[0, 34, 0]]
},
panels: {
display: 'flex'
},
rightSidePanel: {
display: 'block',
width: 1100
},
leftSidePanel: {
width: 300
}
}

View file

@ -0,0 +1,38 @@
import { makeStyles } from '@material-ui/core/styles'
import classnames from 'classnames'
import React from 'react'
import styles from './CustomerSidebar.styles.js'
const useStyles = makeStyles(styles)
const CustomerSidebar = ({ isSelected, onClick }) => {
const classes = useStyles()
const sideBarOptions = [
{
code: 'overview',
display: 'Overview'
},
{
code: 'customerData',
display: 'Customer Data'
}
]
return (
<div className={classes.sidebar}>
{sideBarOptions?.map(it => (
<div
className={classnames({
[classes.activeLink]: isSelected(it),
[classes.link]: true
})}
onClick={() => onClick(it)}>
{it.display}
</div>
))}
</div>
)
}
export default CustomerSidebar

View file

@ -0,0 +1,35 @@
import typographyStyles from 'src/components/typography/styles'
import { zircon, offDarkColor, white } from 'src/styling/variables'
const { tl2, p } = typographyStyles
const sidebarColor = zircon
export default {
sidebar: {
display: 'flex',
backgroundColor: sidebarColor,
width: 219,
flexDirection: 'column',
borderRadius: 5,
marginBottom: 50
},
link: {
extend: p,
position: 'relative',
color: offDarkColor,
padding: 15,
cursor: 'pointer'
},
activeLink: {
extend: tl2,
color: white,
backgroundColor: offDarkColor,
'&:first-child': {
borderRadius: '5px 5px 0px 0px'
},
'&:last-child': {
borderRadius: '0px 0px 5px 5px'
}
}
}

View file

@ -71,8 +71,7 @@ const TransactionsList = ({ customer, data, loading, locale }) => {
const tableElements = [
{
header: 'Direction',
width: 207,
width: 75,
view: it => (
<>
{it.txClass === 'cashOut' ? (
@ -80,20 +79,19 @@ const TransactionsList = ({ customer, data, loading, locale }) => {
) : (
<TxInIcon className={classes.txClassIconLeft} />
)}
{it.txClass === 'cashOut' ? 'Cash-out' : 'Cash-in'}
</>
)
},
{
header: 'Transaction ID',
width: 414,
width: 175,
view: it => (
<CopyToClipboard className={classes.txId}>{it.id}</CopyToClipboard>
)
},
{
header: 'Cash',
width: 146,
width: 175,
textAlign: 'right',
view: it => (
<>
@ -104,7 +102,7 @@ const TransactionsList = ({ customer, data, loading, locale }) => {
},
{
header: 'Crypto',
width: 142,
width: 175,
textAlign: 'right',
view: it => (
<>
@ -117,7 +115,7 @@ const TransactionsList = ({ customer, data, loading, locale }) => {
},
{
header: 'Date',
width: 157,
width: 160,
view: it => formatDate(it.created, timezone, 'YYYY-MM-D')
},
{

View file

@ -1,6 +1,15 @@
import ComplianceDetails from './ComplianceDetails'
import CustomerDetails from './CustomerDetails'
import CustomerSidebar from './CustomerSidebar'
import Field from './Field'
import IdDataCard from './IdDataCard'
import TransactionsList from './TransactionsList'
export { CustomerDetails, IdDataCard, TransactionsList, ComplianceDetails }
export {
CustomerDetails,
IdDataCard,
TransactionsList,
ComplianceDetails,
CustomerSidebar,
Field
}