+ error={data.error || data.hasError}
+ shouldShowError={false}
+ errorMessage={data.errorMessage || data.hasError}>
{elements.map(({ view = it => it?.toString(), ...props }, idx) => (
{view(data)}
@@ -142,6 +146,7 @@ const DataTable = ({
width={width}
size={rowSize}
id={data[index].id ? data[index].id : index}
+ index={index}
expWidth={expWidth}
elements={elements}
data={data[index]}
diff --git a/new-lamassu-admin/src/components/typography/styles.js b/new-lamassu-admin/src/components/typography/styles.js
index 44cfac11..ac06d238 100644
--- a/new-lamassu-admin/src/components/typography/styles.js
+++ b/new-lamassu-admin/src/components/typography/styles.js
@@ -129,7 +129,7 @@ export default {
confirmationCode: {
extend: base,
fontSize: codeInputFontSize,
- fontFamily: fontPrimary,
+ fontFamily: fontSecondary,
fontWeight: 900
},
inline: {
diff --git a/new-lamassu-admin/src/pages/Accounting/Accounting.js b/new-lamassu-admin/src/pages/Accounting/Accounting.js
index 74d65a88..fbde444d 100644
--- a/new-lamassu-admin/src/pages/Accounting/Accounting.js
+++ b/new-lamassu-admin/src/pages/Accounting/Accounting.js
@@ -114,7 +114,7 @@ const Accounting = () => {
const { data: configResponse, loading: configLoading } = useQuery(GET_DATA)
const timezone = R.path(['config', 'locale_timezone'], configResponse)
- const loading = operatorLoading && configLoading
+ const loading = operatorLoading || configLoading
const operatorData = R.path(['operatorByUsername'], opData)
@@ -170,26 +170,22 @@ const Accounting = () => {
]
return (
- !loading && (
- <>
-
-
- Fiat balance history
-
- >
- )
+ <>
+
+
+ Fiat balance history
+
+ >
)
}
diff --git a/new-lamassu-admin/src/pages/AddMachine/AddMachine.js b/new-lamassu-admin/src/pages/AddMachine/AddMachine.js
index 45d531d1..5cc36b47 100644
--- a/new-lamassu-admin/src/pages/AddMachine/AddMachine.js
+++ b/new-lamassu-admin/src/pages/AddMachine/AddMachine.js
@@ -14,6 +14,7 @@ import { Button } from 'src/components/buttons'
import { TextInput } from 'src/components/inputs/formik'
import Sidebar from 'src/components/layout/Sidebar'
import { Info2, P } from 'src/components/typography'
+import { ReactComponent as CameraIcon } from 'src/styling/icons/ID/photo/zodiac.svg'
import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.svg'
import { ReactComponent as CompleteStageIconSpring } from 'src/styling/icons/stage/spring/complete.svg'
import { ReactComponent as CompleteStageIconZodiac } from 'src/styling/icons/stage/zodiac/complete.svg'
@@ -70,8 +71,18 @@ const QrCodeComponent = ({ classes, qrCode, name, count, onPaired }) => {
Scan QR code with your new cryptomat
-
-
+
+
+
+
+ Snap a picture and scan
+
diff --git a/new-lamassu-admin/src/pages/AddMachine/styles.js b/new-lamassu-admin/src/pages/AddMachine/styles.js
index 8e11e216..75d3c608 100644
--- a/new-lamassu-admin/src/pages/AddMachine/styles.js
+++ b/new-lamassu-admin/src/pages/AddMachine/styles.js
@@ -126,6 +126,23 @@ const styles = {
},
errorMessage: {
color: errorColor
+ },
+ qrCodeImageWrapper: {
+ display: 'flex',
+ flexDirection: 'column',
+ backgroundColor: 'white',
+ border: `5px solid ${primaryColor}`,
+ padding: 5,
+ borderRadius: 15
+ },
+ qrCodeScanMessage: {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ margin: [[0, 0, 20, 20]],
+ '& > p': {
+ marginLeft: 10
+ }
}
}
diff --git a/new-lamassu-admin/src/pages/Authentication/Input2FAState.js b/new-lamassu-admin/src/pages/Authentication/Input2FAState.js
index 34063f3d..31deaf63 100644
--- a/new-lamassu-admin/src/pages/Authentication/Input2FAState.js
+++ b/new-lamassu-admin/src/pages/Authentication/Input2FAState.js
@@ -1,6 +1,7 @@
import { useMutation, useLazyQuery } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core/styles'
import base64 from 'base-64'
+import { Form, Formik } from 'formik'
import gql from 'graphql-tag'
import React, { useContext, useState } from 'react'
import { useHistory } from 'react-router-dom'
@@ -120,14 +121,20 @@ const Input2FAState = ({ state, dispatch }) => {
Enter your two-factor authentication code
-
+ {/* TODO: refactor the 2FA CodeInput to properly use Formik */}
+ {}} initialValues={{}}>
+
+
{errorMessage && {errorMessage} }
-
+ {/* TODO: refactor the 2FA CodeInput to properly use Formik */}
+ {}} initialValues={{}}>
+
+
{getErrorMsg() && (
{getErrorMsg()}
)}
diff --git a/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js b/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js
index 231aae48..410b49e4 100644
--- a/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js
+++ b/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js
@@ -1,6 +1,7 @@
import { useMutation, useQuery, useLazyQuery } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core/styles'
import base64 from 'base-64'
+import { Form, Formik } from 'formik'
import gql from 'graphql-tag'
import QRCode from 'qrcode.react'
import React, { useContext, useState } from 'react'
@@ -125,6 +126,14 @@ const Setup2FAState = ({ state, dispatch }) => {
return null
}
+ const handleSubmit = () => {
+ if (twoFAConfirmation.length !== 6) {
+ setInvalidToken(true)
+ return
+ }
+ setup2FA(mutationOptions)
+ }
+
return (
secret &&
otpauth && (
@@ -159,28 +168,26 @@ const Setup2FAState = ({ state, dispatch }) => {
-
+ {/* TODO: refactor the 2FA CodeInput to properly use Formik */}
+ {}} initialValues={{}}>
+
+
{getErrorMsg() && (
{getErrorMsg()}
)}
-
diff --git a/new-lamassu-admin/src/pages/Authentication/shared.styles.js b/new-lamassu-admin/src/pages/Authentication/shared.styles.js
index c8d12568..20ed5058 100644
--- a/new-lamassu-admin/src/pages/Authentication/shared.styles.js
+++ b/new-lamassu-admin/src/pages/Authentication/shared.styles.js
@@ -100,6 +100,9 @@ const styles = {
},
error: {
color: errorColor
+ },
+ enterButton: {
+ display: 'none'
}
}
diff --git a/new-lamassu-admin/src/pages/Customers/CustomerPhotos.js b/new-lamassu-admin/src/pages/Customers/CustomerPhotos.js
new file mode 100644
index 00000000..6bb8a19c
--- /dev/null
+++ b/new-lamassu-admin/src/pages/Customers/CustomerPhotos.js
@@ -0,0 +1,81 @@
+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 }) => {
+ const classes = useStyles()
+
+ const [photosDialog, setPhotosDialog] = useState(false)
+ const [photoClickedIndex, setPhotoClickIndex] = useState(null)
+ const orderedPhotosData = !R.isNil(photoClickedIndex)
+ ? R.compose(R.flatten, R.reverse, R.splitAt(photoClickedIndex))(photosData)
+ : photosData
+
+ return (
+
+
+ {'Photos & files'}
+
+
+ {photosData.map((elem, idx) => (
+
+ ))}
+
+ }
+ onDissmised={() => {
+ setPhotosDialog(false)
+ setPhotoClickIndex(null)
+ }}
+ />
+
+ )
+}
+
+export const PhotoCard = ({
+ idx,
+ date,
+ src,
+ setPhotosDialog,
+ setPhotoClickIndex
+}) => {
+ const classes = useStyles()
+
+ return (
+ {
+ setPhotoClickIndex(idx)
+ setPhotosDialog(true)
+ }}>
+
+
+
+
+ {format('yyyy-MM-dd', new Date(date))}
+
+
+
+ )
+}
+
+export default CustomerPhotos
diff --git a/new-lamassu-admin/src/pages/Customers/CustomerPhotos.styles.js b/new-lamassu-admin/src/pages/Customers/CustomerPhotos.styles.js
new file mode 100644
index 00000000..a7fdffe4
--- /dev/null
+++ b/new-lamassu-admin/src/pages/Customers/CustomerPhotos.styles.js
@@ -0,0 +1,37 @@
+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,
+ borderTopLeftRadius: 4,
+ borderTopRightRadius: 4
+ },
+ 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
diff --git a/new-lamassu-admin/src/pages/Customers/CustomerProfile.js b/new-lamassu-admin/src/pages/Customers/CustomerProfile.js
index 626c5f38..91b076ee 100644
--- a/new-lamassu-admin/src/pages/Customers/CustomerProfile.js
+++ b/new-lamassu-admin/src/pages/Customers/CustomerProfile.js
@@ -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,12 +368,24 @@ const CustomerProfile = memo(() => {
const isCustomerData = clickedItem === 'customerData'
const isOverview = clickedItem === 'overview'
const isNotes = clickedItem === 'notes'
+ const isPhotos = clickedItem === 'photos'
- const loading = customerLoading && configLoading
+ 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
const timezone = R.path(['config', 'locale_timezone'], configResponse)
- const classes = useStyles({ blocked })
+ const classes = useStyles()
return (
<>
@@ -406,29 +419,26 @@ const CustomerProfile = memo(() => {
/>
Actions
-
+
setWizard(true)}>
{`Manual data entry`}
-
-
{}}>
{`Add individual discount`}
-
-
{isSuspended && (
{
)}
{
{
justifyContent="space-between">
setShowCompliance(!showCompliance)}
/>
@@ -524,6 +535,11 @@ const CustomerProfile = memo(() => {
timezone={timezone}>
)}
+ {isPhotos && (
+
+
+
+ )}
{wizard && (
({
+ actionButton: {
+ margin: [[0, 0, 4, 0]],
display: 'flex',
flexDirection: 'row',
- margin: [[0, 0, 4, 0]],
- padding: [[0, props.blocked ? 35 : 48, 0]]
- }),
- customerDiscount: {
- display: 'flex',
- flexDirection: 'row',
- margin: [[0, 0, 4, 0]],
- padding: [[0, 23.5, 0]]
+ justifyContent: 'center'
},
- customerManualDataEntry: {
+ actionBar: {
display: 'flex',
- flexDirection: 'row',
- margin: [[8, 0, 4, 0]],
- padding: [[0, 40.5, 0]]
- },
- retrieveInformation: {
- display: 'flex',
- flexDirection: 'row',
- margin: [[0, 0, 4, 0]],
- padding: [[0, 32.5, 0]]
+ flexDirection: 'column',
+ width: 219
},
panels: {
display: 'flex'
diff --git a/new-lamassu-admin/src/pages/Customers/components/CustomerDetails.js b/new-lamassu-admin/src/pages/Customers/components/CustomerDetails.js
index d5412738..8aebf62f 100644
--- a/new-lamassu-admin/src/pages/Customers/components/CustomerDetails.js
+++ b/new-lamassu-admin/src/pages/Customers/components/CustomerDetails.js
@@ -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 (
-
-
-
-
-
-
- {name.length
- ? name
- : getFormattedPhone(
- R.path(['phone'])(customer),
- locale.country
- )}
-
-
-
- {elements.map(({ size, header }, idx) => (
-
- {header}
-
- ))}
-
-
- {elements.map(({ size, value }, idx) => (
-
- {value}
-
- ))}
-
+ return (
+
+
+
+
+
+
+ {name.length
+ ? name
+ : getFormattedPhone(R.path(['phone'])(customer), locale.country)}
+
+
+
+ {elements.map(({ size, header }, idx) => (
+
+ {header}
+
+ ))}
+
+
+ {elements.map(({ size, value }, idx) => (
+
+ {value}
+
+ ))}
- )
- }
-)
+
+ )
+})
export default CustomerDetails
diff --git a/new-lamassu-admin/src/pages/Customers/components/CustomerSidebar.js b/new-lamassu-admin/src/pages/Customers/components/CustomerSidebar.js
index a6190f13..6bcf3444 100644
--- a/new-lamassu-admin/src/pages/Customers/components/CustomerSidebar.js
+++ b/new-lamassu-admin/src/pages/Customers/components/CustomerSidebar.js
@@ -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
}
]
diff --git a/new-lamassu-admin/src/pages/Customers/components/PhotosCard.js b/new-lamassu-admin/src/pages/Customers/components/PhotosCard.js
index 97674ec8..0181fdca 100644
--- a/new-lamassu-admin/src/pages/Customers/components/PhotosCard.js
+++ b/new-lamassu-admin/src/pages/Customers/components/PhotosCard.js
@@ -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 {children}
-}
-
-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 (
- <>
-
- {!isFaceCustomerPhoto && (
-
-
-
- {photosData && photosData[currentIndex]?.id}
-
-
- )}
-
-
-
-
- {photosData && photosData[currentIndex]?.date}
-
-
-
-
-
- {!isFaceCustomerPhoto ? 'Acceptance of T&C' : 'Compliance scan'}
-
-
-
- >
- )
-})
-
export default PhotosCard
diff --git a/new-lamassu-admin/src/pages/Customers/components/PhotosCard.styles.js b/new-lamassu-admin/src/pages/Customers/components/PhotosCard.styles.js
index 002c5bfb..5d974078 100644
--- a/new-lamassu-admin/src/pages/Customers/components/PhotosCard.styles.js
+++ b/new-lamassu-admin/src/pages/Customers/components/PhotosCard.styles.js
@@ -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
}
}
diff --git a/new-lamassu-admin/src/pages/Customers/components/PhotosCarousel.js b/new-lamassu-admin/src/pages/Customers/components/PhotosCarousel.js
new file mode 100644
index 00000000..e0d6f2e0
--- /dev/null
+++ b/new-lamassu-admin/src/pages/Customers/components/PhotosCarousel.js
@@ -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 {children}
+ }
+
+ const isFaceCustomerPhoto = !R.has('id')(photosData[currentIndex])
+
+ const slidePhoto = index => setCurrentIndex(index)
+
+ return (
+ <>
+
+ {!isFaceCustomerPhoto && (
+
+
+
+ {photosData && photosData[currentIndex]?.id}
+
+
+ )}
+
+
+
+
+ {photosData && photosData[currentIndex]?.date}
+
+
+
+
+
+ {!isFaceCustomerPhoto ? 'Acceptance of T&C' : 'Compliance scan'}
+
+
+
+ >
+ )
+})
+
+export default PhotosCarousel
diff --git a/new-lamassu-admin/src/pages/Customers/components/PhotosCarousel.styles.js b/new-lamassu-admin/src/pages/Customers/components/PhotosCarousel.styles.js
new file mode 100644
index 00000000..5c568cd9
--- /dev/null
+++ b/new-lamassu-admin/src/pages/Customers/components/PhotosCarousel.styles.js
@@ -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
+ }
+ }
+ }
+}
diff --git a/new-lamassu-admin/src/pages/Customers/components/TransactionsList.js b/new-lamassu-admin/src/pages/Customers/components/TransactionsList.js
index 8b55208c..ea8d765e 100644
--- a/new-lamassu-admin/src/pages/Customers/components/TransactionsList.js
+++ b/new-lamassu-admin/src/pages/Customers/components/TransactionsList.js
@@ -70,12 +70,7 @@ const TransactionsList = ({ customer, data, loading, locale }) => {
const tableElements = [
{
- header: 'Machine',
- width: 160,
- view: R.path(['machineName'])
- },
- {
- width: 125,
+ width: 40,
view: it => (
<>
{it.txClass === 'cashOut' ? (
@@ -86,6 +81,11 @@ const TransactionsList = ({ customer, data, loading, locale }) => {
>
)
},
+ {
+ header: 'Machine',
+ width: 160,
+ view: R.path(['machineName'])
+ },
{
header: 'Transaction ID',
width: 145,
diff --git a/new-lamassu-admin/src/pages/Customers/components/index.js b/new-lamassu-admin/src/pages/Customers/components/index.js
index 5e52dd81..7e3c3e19 100644
--- a/new-lamassu-admin/src/pages/Customers/components/index.js
+++ b/new-lamassu-admin/src/pages/Customers/components/index.js
@@ -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,
diff --git a/new-lamassu-admin/src/pages/Customers/helper.js b/new-lamassu-admin/src/pages/Customers/helper.js
index a5d776cc..719ae628 100644
--- a/new-lamassu-admin/src/pages/Customers/helper.js
+++ b/new-lamassu-admin/src/pages/Customers/helper.js
@@ -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
}
diff --git a/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscounts.js b/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscounts.js
index b9585d76..ad245ebd 100644
--- a/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscounts.js
+++ b/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscounts.js
@@ -64,7 +64,9 @@ const IndividualDiscounts = () => {
const [showModal, setShowModal] = useState(false)
const toggleModal = () => setShowModal(!showModal)
- const { data: discountResponse, loading } = useQuery(GET_INDIVIDUAL_DISCOUNTS)
+ const { data: discountResponse, loading: discountLoading } = useQuery(
+ GET_INDIVIDUAL_DISCOUNTS
+ )
const { data: customerData, loading: customerLoading } = useQuery(
GET_CUSTOMERS
)
@@ -160,24 +162,22 @@ const IndividualDiscounts = () => {
}
]
- const isLoading = loading || customerLoading
+ const loading = discountLoading || customerLoading
return (
<>
- {!isLoading && !R.isEmpty(discountResponse.individualDiscounts) && (
-
-
- Add new code
-
-
- )}
- {!isLoading && !R.isEmpty(discountResponse.individualDiscounts) && (
+ {!loading && !R.isEmpty(discountResponse.individualDiscounts) && (
<>
+
+
+ Add new code
+
+
{
/>
>
)}
- {!isLoading && R.isEmpty(discountResponse.individualDiscounts) && (
+ {!loading && R.isEmpty(discountResponse.individualDiscounts) && (
It seems there are no active individual customer discounts on your
diff --git a/new-lamassu-admin/src/pages/MachineLogs.js b/new-lamassu-admin/src/pages/MachineLogs.js
index 277b82c5..d06f4a2c 100644
--- a/new-lamassu-admin/src/pages/MachineLogs.js
+++ b/new-lamassu-admin/src/pages/MachineLogs.js
@@ -81,16 +81,21 @@ const Logs = () => {
const deviceId = selected?.deviceId
- const { data: machineResponse } = useQuery(GET_MACHINES)
+ const { data: machineResponse, loading: machinesLoading } = useQuery(
+ GET_MACHINES
+ )
- const { data: configResponse } = useQuery(GET_DATA)
+ const { data: configResponse, loading: configLoading } = useQuery(GET_DATA)
const timezone = R.path(['config', 'locale_timezone'], configResponse)
- const { data: logsResponse, loading } = useQuery(GET_MACHINE_LOGS, {
- variables: { deviceId, limit: NUM_LOG_RESULTS },
- skip: !selected,
- onCompleted: () => setSaveMessage('')
- })
+ const { data: logsResponse, loading: logsLoading } = useQuery(
+ GET_MACHINE_LOGS,
+ {
+ variables: { deviceId, limit: NUM_LOG_RESULTS },
+ skip: !selected,
+ onCompleted: () => setSaveMessage('')
+ }
+ )
if (machineResponse?.machines?.length && !selected) {
setSelected(machineResponse?.machines[0])
@@ -100,6 +105,8 @@ const Logs = () => {
return R.path(['deviceId'])(selected) === it.deviceId
}
+ const loading = machinesLoading || configLoading || logsLoading
+
return (
<>
diff --git a/new-lamassu-admin/src/pages/Machines/MachineComponents/Transactions/Transactions.js b/new-lamassu-admin/src/pages/Machines/MachineComponents/Transactions/Transactions.js
index dedd15e0..a5902611 100644
--- a/new-lamassu-admin/src/pages/Machines/MachineComponents/Transactions/Transactions.js
+++ b/new-lamassu-admin/src/pages/Machines/MachineComponents/Transactions/Transactions.js
@@ -82,7 +82,7 @@ const Transactions = ({ id }) => {
const { data: configData, loading: configLoading } = useQuery(GET_DATA)
const timezone = R.path(['config', 'locale_timezone'], configData)
- const loading = txLoading && configLoading
+ const loading = txLoading || configLoading
if (!loading && txResponse) {
txResponse.transactions = txResponse.transactions.splice(0, 5)
diff --git a/new-lamassu-admin/src/pages/Maintenance/CashboxHistory.js b/new-lamassu-admin/src/pages/Maintenance/CashboxHistory.js
index edc11a17..6e232f01 100644
--- a/new-lamassu-admin/src/pages/Maintenance/CashboxHistory.js
+++ b/new-lamassu-admin/src/pages/Maintenance/CashboxHistory.js
@@ -85,7 +85,7 @@ const CashboxHistory = ({ machines, currency }) => {
const { data: configData, loading: configLoading } = useQuery(GET_DATA)
const timezone = R.path(['config', 'locale_timezone'], configData)
- const loading = batchesLoading && configLoading
+ const loading = batchesLoading || configLoading
const batches = R.path(['cashboxBatches'])(batchesData)
@@ -252,16 +252,13 @@ const CashboxHistory = ({ machines, currency }) => {
]
return (
- <>
- {!loading && (
-
- )}
- >
+
)
}
diff --git a/new-lamassu-admin/src/pages/Maintenance/MachineStatus.js b/new-lamassu-admin/src/pages/Maintenance/MachineStatus.js
index 2c3ac5da..23512947 100644
--- a/new-lamassu-admin/src/pages/Maintenance/MachineStatus.js
+++ b/new-lamassu-admin/src/pages/Maintenance/MachineStatus.js
@@ -54,7 +54,11 @@ const MachineStatus = () => {
const history = useHistory()
const { state } = useLocation()
const addedMachineId = state?.id
- const { data: machinesResponse, refetch, loading } = useQuery(GET_MACHINES)
+ const {
+ data: machinesResponse,
+ refetch,
+ loading: machinesLoading
+ } = useQuery(GET_MACHINES)
const { data: configResponse, configLoading } = useQuery(GET_DATA)
const timezone = R.path(['config', 'locale_timezone'], configResponse)
@@ -114,6 +118,8 @@ const MachineStatus = () => {
)
+ const loading = machinesLoading || configLoading
+
return (
<>
@@ -132,7 +138,7 @@ const MachineStatus = () => {
{
const [saveMessage, setSaveMessage] = useState(null)
const [logLevel, setLogLevel] = useState(SHOW_ALL)
- const { data, loading } = useQuery(GET_SERVER_DATA, {
+ const { data, loading: dataLoading } = useQuery(GET_SERVER_DATA, {
onCompleted: () => setSaveMessage(''),
variables: {
limit: NUM_LOG_RESULTS
}
})
- const { data: configResponse, configLoading } = useQuery(GET_DATA)
+ const { data: configResponse, loading: configLoading } = useQuery(GET_DATA)
const timezone = R.path(['config', 'locale_timezone'], configResponse)
const defaultLogLevels = [
@@ -132,6 +132,8 @@ const Logs = () => {
setLogLevel(logLevel)
}
+ const loading = dataLoading || configLoading
+
return (
<>
@@ -206,8 +208,8 @@ const Logs = () => {
))}
- {loading && configLoading && {'Loading...'}}
- {!loading && !configLoading && !data?.serverLogs?.length && (
+ {loading && {'Loading...'}}
+ {!loading && !data?.serverLogs?.length && (
{'No activity so far'}
)}
diff --git a/new-lamassu-admin/src/pages/SessionManagement/SessionManagement.js b/new-lamassu-admin/src/pages/SessionManagement/SessionManagement.js
index 9ce5ab12..d40c5e0a 100644
--- a/new-lamassu-admin/src/pages/SessionManagement/SessionManagement.js
+++ b/new-lamassu-admin/src/pages/SessionManagement/SessionManagement.js
@@ -48,7 +48,7 @@ const SessionManagement = () => {
const { data: configResponse, loading: configLoading } = useQuery(GET_DATA)
const timezone = R.path(['config', 'locale_timezone'], configResponse)
- const loading = sessionsLoading && configLoading
+ const loading = sessionsLoading || configLoading
const elements = [
{
@@ -107,15 +107,14 @@ const SessionManagement = () => {
]
return (
- !loading && (
- <>
-
-
- >
- )
+ <>
+
+
+ >
)
}
diff --git a/new-lamassu-admin/src/pages/Transactions/Transactions.js b/new-lamassu-admin/src/pages/Transactions/Transactions.js
index 1ea2b927..016c8b37 100644
--- a/new-lamassu-admin/src/pages/Transactions/Transactions.js
+++ b/new-lamassu-admin/src/pages/Transactions/Transactions.js
@@ -15,6 +15,8 @@ import DataTable from 'src/components/tables/DataTable'
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
import { ReactComponent as CustomerLinkIcon } from 'src/styling/icons/month arrows/right.svg'
+import { ReactComponent as CustomerLinkWhiteIcon } from 'src/styling/icons/month arrows/right_white.svg'
+import { errorColor } from 'src/styling/variables'
import { formatDate } from 'src/utils/timezones'
import DetailsRow from './DetailsCard'
@@ -124,13 +126,13 @@ const Transactions = () => {
const history = useHistory()
const [filters, setFilters] = useState([])
- const { data: filtersResponse, loading: loadingFilters } = useQuery(
+ const { data: filtersResponse, loading: filtersLoading } = useQuery(
GET_TRANSACTION_FILTERS
)
const [variables, setVariables] = useState({ limit: NUM_LOG_RESULTS })
const {
data: txData,
- loading: loadingTransactions,
+ loading: transactionsLoading,
refetch,
startPolling,
stopPolling
@@ -185,7 +187,11 @@ const Transactions = () => {
{getCustomerDisplayName(it)}
{!it.isAnonymous && (
redirect(it.customerId)}>
-
+ {it.hasError ? (
+
+ ) : (
+
+ )}
)}
@@ -294,6 +300,14 @@ const Transactions = () => {
const filterOptions = R.path(['transactionFilters'])(filtersResponse)
+ const loading = transactionsLoading || filtersLoading || configLoading
+
+ const errorLabel = (
+
+ )
+
return (
<>
@@ -301,7 +315,7 @@ const Transactions = () => {
Transactions
{
Cash-out
+
+ {errorLabel}
+ Transaction error
+
{filters.length > 0 && (
@@ -342,7 +360,7 @@ const Transactions = () => {
/>
)}
div': {
+ marginLeft: 24
+ },
'& > div:first-child': {
- marginRight: 24
+ marginLeft: 0
},
'& span': {
extend: label1,
diff --git a/new-lamassu-admin/src/pages/UserManagement/modals/Input2FAModal.js b/new-lamassu-admin/src/pages/UserManagement/modals/Input2FAModal.js
index d3773fbb..f64b32e9 100644
--- a/new-lamassu-admin/src/pages/UserManagement/modals/Input2FAModal.js
+++ b/new-lamassu-admin/src/pages/UserManagement/modals/Input2FAModal.js
@@ -1,5 +1,6 @@
import { useLazyQuery } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core/styles'
+import { Form, Formik } from 'formik'
import gql from 'graphql-tag'
import React, { useState } from 'react'
@@ -48,6 +49,14 @@ const Input2FAModal = ({ showModal, handleClose, setConfirmation }) => {
return null
}
+ const handleSubmit = () => {
+ if (twoFACode.length !== 6) {
+ setInvalidCode(true)
+ return
+ }
+ confirm2FA({ variables: { code: twoFACode } })
+ }
+
return (
showModal && (
{
To make changes on this user, please confirm this action by entering
your two-factor authentication code below.
-
+ {/* TODO: refactor the 2FA CodeInput to properly use Formik */}
+ {}} initialValues={{}}>
+
+
{getErrorMsg() && (
{getErrorMsg()}
)}
- {
- if (twoFACode.length !== 6) {
- setInvalidCode(true)
- return
- }
- confirm2FA({ variables: { code: twoFACode } })
- }}>
+
Confirm
diff --git a/new-lamassu-admin/src/pages/Wizard/components/Wallet/ChooseExchange.js b/new-lamassu-admin/src/pages/Wizard/components/Wallet/ChooseExchange.js
index ff4e8959..5c3bb08b 100644
--- a/new-lamassu-admin/src/pages/Wizard/components/Wallet/ChooseExchange.js
+++ b/new-lamassu-admin/src/pages/Wizard/components/Wallet/ChooseExchange.js
@@ -38,7 +38,8 @@ const SAVE_ACCOUNTS = gql`
}
`
-const isConfigurable = it => !R.isNil(it) && !R.contains(it)(['mock-exchange'])
+const isConfigurable = it =>
+ !R.isNil(it) && !R.contains(it)(['mock-exchange', 'no-exchange'])
const ChooseExchange = ({ data: currentData, addData }) => {
const classes = useStyles()
diff --git a/new-lamassu-admin/src/styling/icons/customer-nav/photos/comet.svg b/new-lamassu-admin/src/styling/icons/customer-nav/photos/comet.svg
new file mode 100644
index 00000000..95ee5f2c
--- /dev/null
+++ b/new-lamassu-admin/src/styling/icons/customer-nav/photos/comet.svg
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/new-lamassu-admin/src/styling/icons/customer-nav/photos/white.svg b/new-lamassu-admin/src/styling/icons/customer-nav/photos/white.svg
new file mode 100644
index 00000000..e27fef8f
--- /dev/null
+++ b/new-lamassu-admin/src/styling/icons/customer-nav/photos/white.svg
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/new-lamassu-admin/src/styling/icons/month arrows/right_white.svg b/new-lamassu-admin/src/styling/icons/month arrows/right_white.svg
new file mode 100644
index 00000000..9d8cae10
--- /dev/null
+++ b/new-lamassu-admin/src/styling/icons/month arrows/right_white.svg
@@ -0,0 +1,24 @@
+
+
\ No newline at end of file
diff --git a/package.json b/package.json
index 030bf13b..52cb226e 100644
--- a/package.json
+++ b/package.json
@@ -77,7 +77,6 @@
"socket.io-client": "^2.0.3",
"talisman": "^0.20.0",
"twilio": "^3.6.1",
- "ua-parser-js": "^0.7.22",
"uuid": "8.3.2",
"web3": "^0.20.6",
"winston": "^2.4.2",
|