feat: overview rework and customer data mosaic view
This commit is contained in:
parent
3102c2bef0
commit
4b461c0a57
8 changed files with 376 additions and 43 deletions
168
new-lamassu-admin/src/pages/Customers/CustomerData.js
Normal file
168
new-lamassu-admin/src/pages/Customers/CustomerData.js
Normal 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
|
||||
34
new-lamassu-admin/src/pages/Customers/CustomerData.styles.js
Normal file
34
new-lamassu-admin/src/pages/Customers/CustomerData.styles.js
Normal 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]]
|
||||
}
|
||||
}
|
||||
|
|
@ -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,17 +232,39 @@ 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
|
||||
|
|
@ -244,6 +272,17 @@ const CustomerProfile = memo(() => {
|
|||
updateCustomer={updateCustomer}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{isCustomerData && (
|
||||
<div>
|
||||
<CustomerData
|
||||
customer={customerData}
|
||||
updateCustomer={updateCustomer}></CustomerData>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -15,14 +15,26 @@ export default {
|
|||
customerDetails: {
|
||||
marginBottom: 18
|
||||
},
|
||||
customerActions: {
|
||||
customerBlock: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
'& button': {
|
||||
marginRight: 15
|
||||
margin: [[8, 0, 4, 0]],
|
||||
padding: [[0, 35, 0]]
|
||||
},
|
||||
'& > :last-child': {
|
||||
marginRight: 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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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')
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue