feat: add photos list and carousel
This commit is contained in:
parent
9553bf8fc9
commit
c93e1028a4
14 changed files with 342 additions and 202 deletions
|
|
@ -683,18 +683,18 @@ function getCustomersList (phone = null, name = null, address = null, id = null)
|
|||
*/
|
||||
function getCustomerById (id) {
|
||||
const passableErrorCodes = _.map(Pgp.as.text, TX_PASSTHROUGH_ERROR_CODES).join(',')
|
||||
const sql = `SELECT id, authorized_override, days_suspended, is_suspended, front_camera_path, front_camera_override,
|
||||
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,
|
||||
const sql = `SELECT id, authorized_override, days_suspended, is_suspended, front_camera_path, front_camera_at, front_camera_override,
|
||||
phone, sms_override, id_card_data_at, id_card_data, id_card_data_override, id_card_data_expiration,
|
||||
id_card_photo_path, id_card_photo_at, id_card_photo_override, us_ssn_at, us_ssn, us_ssn_override, sanctions, sanctions_at,
|
||||
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, subscriber_info, custom_fields, notes
|
||||
FROM (
|
||||
SELECT c.id, c.authorized_override,
|
||||
greatest(0, date_part('day', c.suspended_until - now())) AS days_suspended,
|
||||
c.suspended_until > now() AS is_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.id_card_photo_path, c.id_card_photo_override, c.us_ssn, c.us_ssn_override, c.sanctions,
|
||||
c.front_camera_path, c.front_camera_override, c.front_camera_at,
|
||||
c.phone, c.sms_override, c.id_card_data, c.id_card_data_at, c.id_card_data_override, c.id_card_data_expiration,
|
||||
c.id_card_photo_path, c.id_card_photo_at, c.id_card_photo_override, c.us_ssn, c.us_ssn_at, c.us_ssn_override, c.sanctions,
|
||||
c.sanctions_at, c.sanctions_override, c.subscriber_info, t.tx_class, t.fiat, t.fiat_code, t.created, cn.notes,
|
||||
row_number() OVER (PARTITION BY c.id ORDER BY t.created DESC) AS rn,
|
||||
sum(CASE WHEN t.id IS NOT NULL THEN 1 ELSE 0 END) OVER (PARTITION BY c.id) AS total_txs,
|
||||
|
|
|
|||
68
new-lamassu-admin/src/pages/Customers/CustomerPhotos.js
Normal file
68
new-lamassu-admin/src/pages/Customers/CustomerPhotos.js
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import { makeStyles, Paper } from '@material-ui/core'
|
||||
import { format } from 'date-fns/fp'
|
||||
import * as R from 'ramda'
|
||||
import { React, useState } from 'react'
|
||||
|
||||
import { InformativeDialog } from 'src/components/InformativeDialog'
|
||||
import { Label2, H3 } from 'src/components/typography'
|
||||
import { ReactComponent as CameraIcon } from 'src/styling/icons/ID/photo/comet.svg'
|
||||
import { URI } from 'src/utils/apollo'
|
||||
|
||||
import styles from './CustomerPhotos.styles'
|
||||
import PhotosCarousel from './components/PhotosCarousel'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const CustomerPhotos = ({ photosData, set }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
const [photosDialog, setPhotosDialog] = useState(false)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={classes.header}>
|
||||
<H3 className={classes.title}>{'Photos & files'}</H3>
|
||||
</div>
|
||||
<div className={classes.photosChipList}>
|
||||
{R.map(
|
||||
it => (
|
||||
<PhotoCard
|
||||
date={it.date}
|
||||
src={`${URI}/${it.photoDir}/${it.path}`}
|
||||
setPhotosDialog={setPhotosDialog}
|
||||
/>
|
||||
),
|
||||
photosData
|
||||
)}
|
||||
</div>
|
||||
<InformativeDialog
|
||||
open={photosDialog}
|
||||
title={`Photo roll`}
|
||||
data={<PhotosCarousel photosData={photosData} />}
|
||||
onDissmised={() => {
|
||||
setPhotosDialog(false)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const PhotoCard = ({ date, src, setPhotosDialog }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<Paper
|
||||
className={classes.photoCardChip}
|
||||
onClick={() => setPhotosDialog(true)}>
|
||||
<img className={classes.image} src={src} alt="" />
|
||||
<div className={classes.footer}>
|
||||
<CameraIcon />
|
||||
<Label2 className={classes.date}>
|
||||
{format('yyyy-MM-dd', new Date(date))}
|
||||
</Label2>
|
||||
</div>
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
|
||||
export default CustomerPhotos
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
const styles = {
|
||||
header: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row'
|
||||
},
|
||||
title: {
|
||||
marginTop: 7,
|
||||
marginRight: 24,
|
||||
marginBottom: 32
|
||||
},
|
||||
photosChipList: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap'
|
||||
},
|
||||
image: {
|
||||
objectFit: 'cover',
|
||||
objectPosition: 'center',
|
||||
width: 224,
|
||||
height: 200
|
||||
},
|
||||
photoCardChip: {
|
||||
margin: [[0, 16, 0, 0]]
|
||||
},
|
||||
footer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
margin: [[8, 0, 0, 8]]
|
||||
},
|
||||
date: {
|
||||
margin: [[0, 0, 8, 12]]
|
||||
}
|
||||
}
|
||||
|
||||
export default styles
|
||||
|
|
@ -24,6 +24,7 @@ import { fromNamespace, namespaces } from 'src/utils/config'
|
|||
|
||||
import CustomerData from './CustomerData'
|
||||
import CustomerNotes from './CustomerNotes'
|
||||
import CustomerPhotos from './CustomerPhotos'
|
||||
import styles from './CustomerProfile.styles'
|
||||
import {
|
||||
CustomerDetails,
|
||||
|
|
@ -31,7 +32,7 @@ import {
|
|||
CustomerSidebar,
|
||||
Wizard
|
||||
} from './components'
|
||||
import { getFormattedPhone, getName } from './helper'
|
||||
import { getFormattedPhone, getName, formatPhotosData } from './helper'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
|
|
@ -367,6 +368,18 @@ const CustomerProfile = memo(() => {
|
|||
const isCustomerData = clickedItem === 'customerData'
|
||||
const isOverview = clickedItem === 'overview'
|
||||
const isNotes = clickedItem === 'notes'
|
||||
const isPhotos = clickedItem === 'photos'
|
||||
|
||||
const frontCameraData = R.pick(['frontCameraPath', 'frontCameraAt'])(
|
||||
customerData
|
||||
)
|
||||
const txPhotosData =
|
||||
sortedTransactions &&
|
||||
R.map(R.pick(['id', 'txCustomerPhotoPath', 'txCustomerPhotoAt']))(
|
||||
sortedTransactions
|
||||
)
|
||||
|
||||
const photosData = formatPhotosData(R.append(frontCameraData, txPhotosData))
|
||||
|
||||
const loading = customerLoading && configLoading
|
||||
|
||||
|
|
@ -488,6 +501,7 @@ const CustomerProfile = memo(() => {
|
|||
justifyContent="space-between">
|
||||
<CustomerDetails
|
||||
customer={customerData}
|
||||
photosData={photosData}
|
||||
locale={locale}
|
||||
setShowCompliance={() => setShowCompliance(!showCompliance)}
|
||||
/>
|
||||
|
|
@ -524,6 +538,11 @@ const CustomerProfile = memo(() => {
|
|||
timezone={timezone}></CustomerNotes>
|
||||
</div>
|
||||
)}
|
||||
{isPhotos && (
|
||||
<div>
|
||||
<CustomerPhotos photosData={photosData} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{wizard && (
|
||||
<Wizard
|
||||
|
|
|
|||
|
|
@ -12,88 +12,73 @@ import PhotosCard from './PhotosCard'
|
|||
|
||||
const useStyles = makeStyles(mainStyles)
|
||||
|
||||
const CustomerDetails = memo(
|
||||
({ txData, customer, locale, setShowCompliance }) => {
|
||||
const classes = useStyles()
|
||||
const CustomerDetails = memo(({ customer, photosData, locale }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
const idNumber = R.path(['idCardData', 'documentNumber'])(customer)
|
||||
const usSsn = R.path(['usSsn'])(customer)
|
||||
const idNumber = R.path(['idCardData', 'documentNumber'])(customer)
|
||||
const usSsn = R.path(['usSsn'])(customer)
|
||||
|
||||
const elements = [
|
||||
{
|
||||
header: 'Phone number',
|
||||
size: 172,
|
||||
value: getFormattedPhone(customer.phone, locale.country)
|
||||
}
|
||||
]
|
||||
const elements = [
|
||||
{
|
||||
header: 'Phone number',
|
||||
size: 172,
|
||||
value: getFormattedPhone(customer.phone, locale.country)
|
||||
}
|
||||
]
|
||||
|
||||
if (idNumber)
|
||||
elements.push({
|
||||
header: 'ID number',
|
||||
size: 172,
|
||||
value: idNumber
|
||||
})
|
||||
if (idNumber)
|
||||
elements.push({
|
||||
header: 'ID number',
|
||||
size: 172,
|
||||
value: idNumber
|
||||
})
|
||||
|
||||
if (usSsn)
|
||||
elements.push({
|
||||
header: 'US SSN',
|
||||
size: 127,
|
||||
value: usSsn
|
||||
})
|
||||
if (usSsn)
|
||||
elements.push({
|
||||
header: 'US SSN',
|
||||
size: 127,
|
||||
value: usSsn
|
||||
})
|
||||
|
||||
const name = getName(customer)
|
||||
const name = getName(customer)
|
||||
|
||||
return (
|
||||
<Box display="flex">
|
||||
<PhotosCard
|
||||
frontCameraData={R.pick(['frontCameraPath', 'frontCameraAt'])(
|
||||
customer
|
||||
)}
|
||||
txPhotosData={
|
||||
txData &&
|
||||
R.map(R.pick(['id', 'txCustomerPhotoPath', 'txCustomerPhotoAt']))(
|
||||
txData
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Box display="flex" flexDirection="column">
|
||||
<div className={classes.name}>
|
||||
<IdIcon className={classes.idIcon} />
|
||||
<H2 noMargin>
|
||||
{name.length
|
||||
? name
|
||||
: getFormattedPhone(
|
||||
R.path(['phone'])(customer),
|
||||
locale.country
|
||||
)}
|
||||
</H2>
|
||||
</div>
|
||||
<Box display="flex" mt="auto">
|
||||
{elements.map(({ size, header }, idx) => (
|
||||
<Label1
|
||||
noMargin
|
||||
key={idx}
|
||||
className={classes.label}
|
||||
style={{ width: size }}>
|
||||
{header}
|
||||
</Label1>
|
||||
))}
|
||||
</Box>
|
||||
<Box display="flex">
|
||||
{elements.map(({ size, value }, idx) => (
|
||||
<P
|
||||
noMargin
|
||||
key={idx}
|
||||
className={classes.value}
|
||||
style={{ width: size }}>
|
||||
{value}
|
||||
</P>
|
||||
))}
|
||||
</Box>
|
||||
return (
|
||||
<Box display="flex">
|
||||
<PhotosCard photosData={photosData} />
|
||||
<Box display="flex" flexDirection="column">
|
||||
<div className={classes.name}>
|
||||
<IdIcon className={classes.idIcon} />
|
||||
<H2 noMargin>
|
||||
{name.length
|
||||
? name
|
||||
: getFormattedPhone(R.path(['phone'])(customer), locale.country)}
|
||||
</H2>
|
||||
</div>
|
||||
<Box display="flex" mt="auto">
|
||||
{elements.map(({ size, header }, idx) => (
|
||||
<Label1
|
||||
noMargin
|
||||
key={idx}
|
||||
className={classes.label}
|
||||
style={{ width: size }}>
|
||||
{header}
|
||||
</Label1>
|
||||
))}
|
||||
</Box>
|
||||
<Box display="flex">
|
||||
{elements.map(({ size, value }, idx) => (
|
||||
<P
|
||||
noMargin
|
||||
key={idx}
|
||||
className={classes.value}
|
||||
style={{ width: size }}>
|
||||
{value}
|
||||
</P>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
)
|
||||
</Box>
|
||||
)
|
||||
})
|
||||
|
||||
export default CustomerDetails
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import { ReactComponent as NoteReversedIcon } from 'src/styling/icons/customer-n
|
|||
import { ReactComponent as NoteIcon } from 'src/styling/icons/customer-nav/note/white.svg'
|
||||
import { ReactComponent as OverviewReversedIcon } from 'src/styling/icons/customer-nav/overview/comet.svg'
|
||||
import { ReactComponent as OverviewIcon } from 'src/styling/icons/customer-nav/overview/white.svg'
|
||||
import { ReactComponent as PhotosReversedIcon } from 'src/styling/icons/customer-nav/photos/comet.svg'
|
||||
import { ReactComponent as Photos } from 'src/styling/icons/customer-nav/photos/white.svg'
|
||||
|
||||
import styles from './CustomerSidebar.styles.js'
|
||||
|
||||
|
|
@ -33,6 +35,12 @@ const CustomerSidebar = ({ isSelected, onClick }) => {
|
|||
display: 'Notes',
|
||||
Icon: NoteIcon,
|
||||
InverseIcon: NoteReversedIcon
|
||||
},
|
||||
{
|
||||
code: 'photos',
|
||||
display: 'Photos & files',
|
||||
Icon: Photos,
|
||||
InverseIcon: PhotosReversedIcon
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -4,58 +4,21 @@ import { makeStyles } from '@material-ui/core/styles'
|
|||
import * as R from 'ramda'
|
||||
import React, { memo, useState } from 'react'
|
||||
|
||||
import { Carousel } from 'src/components/Carousel'
|
||||
import { InformativeDialog } from 'src/components/InformativeDialog'
|
||||
import { Info2, Label1 } from 'src/components/typography'
|
||||
import { Info2 } from 'src/components/typography'
|
||||
import { ReactComponent as CrossedCameraIcon } from 'src/styling/icons/ID/photo/crossed-camera.svg'
|
||||
import { URI } from 'src/utils/apollo'
|
||||
|
||||
import CopyToClipboard from '../../Transactions/CopyToClipboard'
|
||||
|
||||
import styles from './PhotosCard.styles'
|
||||
import PhotosCarousel from './PhotosCarousel'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const Label = ({ children }) => {
|
||||
const classes = useStyles()
|
||||
return <Label1 className={classes.label}>{children}</Label1>
|
||||
}
|
||||
|
||||
const PhotosCard = memo(({ frontCameraData, txPhotosData }) => {
|
||||
const PhotosCard = memo(({ photosData }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
const [photosDialog, setPhotosDialog] = useState(false)
|
||||
|
||||
const mapKeys = pair => {
|
||||
const [key, value] = pair
|
||||
if (key === 'txCustomerPhotoPath' || key === 'frontCameraPath') {
|
||||
return ['path', value]
|
||||
}
|
||||
if (key === 'txCustomerPhotoAt' || key === 'frontCameraAt') {
|
||||
return ['date', value]
|
||||
}
|
||||
return pair
|
||||
}
|
||||
|
||||
const addPhotoDir = R.map(it => {
|
||||
const hasFrontCameraData = R.has('id')(it)
|
||||
return hasFrontCameraData
|
||||
? { ...it, photoDir: 'operator-data/customersphotos' }
|
||||
: { ...it, photoDir: 'front-camera-photo' }
|
||||
})
|
||||
|
||||
const standardizeKeys = R.map(
|
||||
R.compose(R.fromPairs, R.map(mapKeys), R.toPairs)
|
||||
)
|
||||
|
||||
const filterByPhotoAvailable = R.filter(
|
||||
tx => !R.isNil(tx.date) && !R.isNil(tx.path)
|
||||
)
|
||||
|
||||
const photosData = filterByPhotoAvailable(
|
||||
addPhotoDir(standardizeKeys(R.append(frontCameraData, txPhotosData)))
|
||||
)
|
||||
|
||||
const singlePhoto = R.head(photosData)
|
||||
|
||||
return (
|
||||
|
|
@ -97,41 +60,4 @@ const PhotosCard = memo(({ frontCameraData, txPhotosData }) => {
|
|||
)
|
||||
})
|
||||
|
||||
export const PhotosCarousel = memo(({ photosData }) => {
|
||||
const classes = useStyles()
|
||||
const [currentIndex, setCurrentIndex] = useState(0)
|
||||
|
||||
const isFaceCustomerPhoto = !R.has('id')(photosData[currentIndex])
|
||||
|
||||
const slidePhoto = index => setCurrentIndex(index)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Carousel photosData={photosData} slidePhoto={slidePhoto} />
|
||||
{!isFaceCustomerPhoto && (
|
||||
<div className={classes.firstRow}>
|
||||
<Label>Session ID</Label>
|
||||
<CopyToClipboard>
|
||||
{photosData && photosData[currentIndex]?.id}
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
)}
|
||||
<div className={classes.secondRow}>
|
||||
<div>
|
||||
<div>
|
||||
<Label>Date</Label>
|
||||
<div>{photosData && photosData[currentIndex]?.date}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Taken by</Label>
|
||||
<div>
|
||||
{!isFaceCustomerPhoto ? 'Acceptance of T&C' : 'Compliance scan'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
export default PhotosCard
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import typographyStyles from 'src/components/typography/styles'
|
||||
import { zircon, backgroundColor, offColor } from 'src/styling/variables'
|
||||
|
||||
const { p } = typographyStyles
|
||||
import { zircon, backgroundColor } from 'src/styling/variables'
|
||||
|
||||
export default {
|
||||
photo: {
|
||||
|
|
@ -41,43 +38,5 @@ export default {
|
|||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: 'flex'
|
||||
},
|
||||
label: {
|
||||
color: offColor,
|
||||
margin: [[0, 0, 6, 0]]
|
||||
},
|
||||
firstRow: {
|
||||
padding: [[8]],
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
secondRow: {
|
||||
extend: p,
|
||||
display: 'flex',
|
||||
padding: [[8]],
|
||||
'& > div': {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
'& > div': {
|
||||
width: 144,
|
||||
height: 37,
|
||||
marginBottom: 15,
|
||||
marginRight: 55
|
||||
}
|
||||
}
|
||||
},
|
||||
imgWrapper: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: 'flex',
|
||||
width: 550,
|
||||
height: 550
|
||||
},
|
||||
imgInner: {
|
||||
objectFit: 'cover',
|
||||
objectPosition: 'center',
|
||||
width: 550,
|
||||
height: 550,
|
||||
marginBottom: 40
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import * as R from 'ramda'
|
||||
import React, { memo, useState } from 'react'
|
||||
|
||||
import { Carousel } from 'src/components/Carousel'
|
||||
import { Label1 } from 'src/components/typography'
|
||||
|
||||
import CopyToClipboard from '../../Transactions/CopyToClipboard'
|
||||
|
||||
import styles from './PhotosCarousel.styles'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const PhotosCarousel = memo(({ photosData }) => {
|
||||
const classes = useStyles()
|
||||
const [currentIndex, setCurrentIndex] = useState(0)
|
||||
|
||||
const Label = ({ children }) => {
|
||||
const classes = useStyles()
|
||||
return <Label1 className={classes.label}>{children}</Label1>
|
||||
}
|
||||
|
||||
const isFaceCustomerPhoto = !R.has('id')(photosData[currentIndex])
|
||||
|
||||
const slidePhoto = index => setCurrentIndex(index)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Carousel photosData={photosData} slidePhoto={slidePhoto} />
|
||||
{!isFaceCustomerPhoto && (
|
||||
<div className={classes.firstRow}>
|
||||
<Label>Session ID</Label>
|
||||
<CopyToClipboard>
|
||||
{photosData && photosData[currentIndex]?.id}
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
)}
|
||||
<div className={classes.secondRow}>
|
||||
<div>
|
||||
<div>
|
||||
<Label>Date</Label>
|
||||
<div>{photosData && photosData[currentIndex]?.date}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Taken by</Label>
|
||||
<div>
|
||||
{!isFaceCustomerPhoto ? 'Acceptance of T&C' : 'Compliance scan'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
export default PhotosCarousel
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import typographyStyles from 'src/components/typography/styles'
|
||||
import { offColor } from 'src/styling/variables'
|
||||
|
||||
const { p } = typographyStyles
|
||||
|
||||
export default {
|
||||
label: {
|
||||
color: offColor,
|
||||
margin: [[0, 0, 6, 0]]
|
||||
},
|
||||
firstRow: {
|
||||
padding: [[8]],
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
secondRow: {
|
||||
extend: p,
|
||||
display: 'flex',
|
||||
padding: [[8]],
|
||||
'& > div': {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
'& > div': {
|
||||
width: 144,
|
||||
height: 37,
|
||||
marginBottom: 15,
|
||||
marginRight: 55
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,10 +5,12 @@ import CustomerSidebar from './CustomerSidebar'
|
|||
import EditableCard from './EditableCard'
|
||||
import Field from './Field'
|
||||
import IdDataCard from './IdDataCard'
|
||||
import PhotosCarousel from './PhotosCarousel'
|
||||
import TransactionsList from './TransactionsList'
|
||||
import Upload from './Upload'
|
||||
|
||||
export {
|
||||
PhotosCarousel,
|
||||
CustomerDetails,
|
||||
IdDataCard,
|
||||
TransactionsList,
|
||||
|
|
|
|||
|
|
@ -209,10 +209,41 @@ const entryType = {
|
|||
initialValues: { entryType: '' }
|
||||
}
|
||||
|
||||
const mapKeys = pair => {
|
||||
const [key, value] = pair
|
||||
if (key === 'txCustomerPhotoPath' || key === 'frontCameraPath') {
|
||||
return ['path', value]
|
||||
}
|
||||
if (key === 'txCustomerPhotoAt' || key === 'frontCameraAt') {
|
||||
return ['date', value]
|
||||
}
|
||||
return pair
|
||||
}
|
||||
|
||||
const addPhotoDir = R.map(it => {
|
||||
const hasFrontCameraData = R.has('id')(it)
|
||||
return hasFrontCameraData
|
||||
? { ...it, photoDir: 'operator-data/customersphotos' }
|
||||
: { ...it, photoDir: 'front-camera-photo' }
|
||||
})
|
||||
|
||||
const standardizeKeys = R.map(R.compose(R.fromPairs, R.map(mapKeys), R.toPairs))
|
||||
|
||||
const filterByPhotoAvailable = R.filter(
|
||||
tx => !R.isNil(tx.date) && !R.isNil(tx.path)
|
||||
)
|
||||
|
||||
const formatPhotosData = R.compose(
|
||||
filterByPhotoAvailable,
|
||||
addPhotoDir,
|
||||
standardizeKeys
|
||||
)
|
||||
|
||||
export {
|
||||
getAuthorizedStatus,
|
||||
getFormattedPhone,
|
||||
getName,
|
||||
entryType,
|
||||
customElements
|
||||
customElements,
|
||||
formatPhotosData
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>icon/customer-nav/photos/comet</title>
|
||||
<g id="icon/customer-nav/photos/comet" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<rect id="Rectangle" stroke="#5F668A" stroke-width="2" x="1" y="1" width="18" height="18" rx="1"></rect>
|
||||
<circle id="Oval" stroke="#5F668A" stroke-width="2" cx="15" cy="5" r="1"></circle>
|
||||
<polyline id="Path" stroke="#5F668A" stroke-width="2" stroke-linejoin="round" points="1 19 7 13 13 19"></polyline>
|
||||
<path d="M13.3333333,14 L18,19 L13.3333333,19 L11,16.5 L13.3333333,14 Z" id="Combined-Shape" stroke="#5F668A" stroke-width="2" stroke-linejoin="round"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 850 B |
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>icon/customer-nav/photos/white</title>
|
||||
<g id="icon/customer-nav/photos/white" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<rect id="Rectangle" stroke="#FFFFFF" stroke-width="2" x="1" y="1" width="18" height="18" rx="1"></rect>
|
||||
<circle id="Oval" stroke="#FFFFFF" stroke-width="2" cx="15" cy="5" r="1"></circle>
|
||||
<polyline id="Path" stroke="#FFFFFF" stroke-width="2" stroke-linejoin="round" points="1 19 7 13 13 19"></polyline>
|
||||
<path d="M13.3333333,14 L18,19 L13.3333333,19 L11,16.5 L13.3333333,14 Z" id="Combined-Shape" stroke="#FFFFFF" stroke-width="2" stroke-linejoin="round"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 850 B |
Loading…
Add table
Add a link
Reference in a new issue