From 550bc2b7a46a7c0c442c5f6d6565bcb8d9b05269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Oliveira?= Date: Mon, 18 Oct 2021 13:42:55 +0100 Subject: [PATCH] feat: add dynamic position to editable cards, reject/authorize buttons --- .../src/pages/Customers/CustomerData.js | 293 ++++++++++++++---- .../pages/Customers/CustomerData.styles.js | 17 - .../src/pages/Customers/CustomerProfile.js | 21 +- .../Customers/components/ComplianceDetails.js | 131 -------- .../components/ComplianceDetails.styles.js | 25 -- .../Customers/components/CustomerDetails.js | 10 - .../Customers/components/EditableCard.js | 182 ++++++++--- .../components/EditableCard.styles.js | 41 ++- .../src/pages/Customers/components/index.js | 2 - 9 files changed, 403 insertions(+), 319 deletions(-) delete mode 100644 new-lamassu-admin/src/pages/Customers/components/ComplianceDetails.js delete mode 100644 new-lamassu-admin/src/pages/Customers/components/ComplianceDetails.styles.js diff --git a/new-lamassu-admin/src/pages/Customers/CustomerData.js b/new-lamassu-admin/src/pages/Customers/CustomerData.js index efc19626..6293893c 100644 --- a/new-lamassu-admin/src/pages/Customers/CustomerData.js +++ b/new-lamassu-admin/src/pages/Customers/CustomerData.js @@ -1,21 +1,28 @@ -import { CardContent, Card } from '@material-ui/core' import Grid from '@material-ui/core/Grid' import { makeStyles } from '@material-ui/core/styles' +import _ from 'lodash/fp' import moment from 'moment' import * as R from 'ramda' import { useState, React } from 'react' +import * as Yup from 'yup' -import { Tooltip } from 'src/components/Tooltip' +import ImagePopper from 'src/components/ImagePopper' import { FeatureButton } from 'src/components/buttons' import { TextInput } from 'src/components/inputs/formik' -import { H3 } from 'src/components/typography' +import { H3, Info3 } from 'src/components/typography' +import { + OVERRIDE_AUTHORIZED, + OVERRIDE_REJECTED +} from 'src/pages/Customers/components/propertyCard' 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 CrossedCameraIcon } from 'src/styling/icons/ID/photo/crossed-camera.svg' import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/comet.svg' import { ReactComponent as CustomerListViewReversedIcon } from 'src/styling/icons/circle buttons/customer-list-view/white.svg' import { ReactComponent as CustomerListViewIcon } from 'src/styling/icons/circle buttons/customer-list-view/zodiac.svg' import { ReactComponent as OverviewReversedIcon } from 'src/styling/icons/circle buttons/overview/white.svg' import { ReactComponent as OverviewIcon } from 'src/styling/icons/circle buttons/overview/zodiac.svg' +import { URI } from 'src/utils/apollo' import { ifNotNull } from 'src/utils/nullCheck' import styles from './CustomerData.styles.js' @@ -24,38 +31,91 @@ import { getName } from './helper.js' const useStyles = makeStyles(styles) +const imageWidth = 165 +const imageHeight = 45 +const popupImageWidth = 360 +const popupImageHeight = 240 + +const Photo = ({ show, src }) => { + const classes = useStyles({ width: imageWidth }) + + return ( + <> + {show ? ( + + ) : ( +
+ +
+ )} + + ) +} + const CustomerData = ({ customer, updateCustomer }) => { const classes = useStyles() + const [listView, setListView] = useState(false) 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 = [ - { - name: 'name', - label: 'Name', - value: `${getName(customer)}` ?? '', - component: TextInput, - size: 190 - } - ] + const sanctions = R.path(['sanctions'])(customer) + const sanctionsAt = R.path(['sanctionsAt'])(customer) + const sanctionsDisplay = !sanctionsAt + ? 'Not checked yet' + : sanctions + ? 'Passed' + : 'Failed' + + const isEven = elem => elem % 2 === 0 + + const getVisibleCards = _.filter( + elem => !_.isEmpty(elem.data) || !_.isNil(elem.children) + ) + + const getAvailableFields = _.filter(({ value }) => value !== '') + + const schemas = { + idScan: Yup.object().shape({ + name: Yup.string(), + idNumber: Yup.string(), + birthDate: Yup.string(), + age: Yup.string(), + gender: Yup.string(), + state: Yup.string(), + expirationDate: Yup.string() + }), + usSsn: Yup.object().shape({ + usSsn: Yup.string() + }) + } const idScanElements = [ { name: 'name', label: 'Name', value: `${getName(customer)}`, - component: TextInput, - size: 190 + component: TextInput }, { - name: 'ID number', + name: 'idNumber', label: 'ID number', - value: R.path(['documentNumber'])(idData), - component: TextInput, - size: 160 + value: R.path(['documentNumber'])(idData) ?? '', + component: TextInput + }, + { + name: 'birthDate', + label: 'Birth Date', + value: ifNotNull(rawDob, moment.utc(rawDob).format('YYYY-MM-DD')), + component: TextInput }, { name: 'age', @@ -64,36 +124,166 @@ const CustomerData = ({ customer, updateCustomer }) => { rawDob, moment.utc().diff(moment.utc(rawDob).format('YYYY-MM-DD'), 'years') ), - component: TextInput, - size: 50 + component: TextInput }, { name: 'gender', label: 'Gender', - value: R.path(['gender'])(idData), - component: TextInput, - size: 80 + value: R.path(['gender'])(idData) ?? '', + component: TextInput }, { - name: country === 'Canada' ? 'province' : 'state', + name: 'state', label: country === 'Canada' ? 'Province' : 'State', - value: R.path(['state'])(idData), - component: TextInput, - size: 120 + value: R.path(['state'])(idData) ?? '', + component: TextInput }, { - name: 'expiration date', + name: 'expirationDate', label: 'Expiration Date', value: ifNotNull( rawExpirationDate, moment.utc(rawExpirationDate).format('YYYY-MM-DD') ), - component: TextInput, - size: 120 + component: TextInput } ] - const [listView, setListView] = useState(false) + const usSsnElements = [ + { + name: 'us ssn', + label: 'US SSN', + value: `${customer.usSsn ?? ''}`, + component: TextInput, + size: 190 + } + ] + + const initialValues = { + idScan: { + name: '', + idNumber: '', + birthDate: '', + age: '', + gender: '', + state: '', + expirationDate: '' + }, + usSsn: { + usSsn: '' + } + } + + const cards = [ + { + data: getAvailableFields(idScanElements), + title: 'ID Scan', + titleIcon: , + state: R.path(['idCardDataOverride'])(customer), + authorize: () => + updateCustomer({ idCardDataOverride: OVERRIDE_AUTHORIZED }), + reject: () => updateCustomer({ idCardDataOverride: OVERRIDE_REJECTED }), + save: values => console.log(values), + validationSchema: schemas.idScan, + initialValues: initialValues.idScan + }, + { + title: 'SMS Confirmation', + titleIcon: , + authorize: () => {}, + reject: () => {}, + save: () => {} + }, + { + title: 'Name', + titleIcon: , + authorize: () => {}, + reject: () => {}, + save: () => {} + }, + { + title: 'Sanctions check', + titleIcon: , + state: R.path(['sanctionsOverride'])(customer), + authorize: () => + updateCustomer({ sanctionsOverride: OVERRIDE_AUTHORIZED }), + reject: () => updateCustomer({ sanctionsOverride: OVERRIDE_REJECTED }), + save: () => {}, + children: {sanctionsDisplay} + }, + { + title: 'Front facing camera', + titleIcon: , + state: R.path(['frontCameraOverride'])(customer), + authorize: () => + updateCustomer({ frontCameraOverride: OVERRIDE_AUTHORIZED }), + reject: () => updateCustomer({ frontCameraOverride: OVERRIDE_REJECTED }), + save: () => {}, + children: customer.frontCameraPath ? ( + + ) : null + }, + { + title: 'ID card image', + titleIcon: , + state: R.path(['idCardPhotoOverride'])(customer), + authorize: () => + updateCustomer({ idCardPhotoOverride: OVERRIDE_AUTHORIZED }), + reject: () => updateCustomer({ idCardPhotoOverride: OVERRIDE_REJECTED }), + save: () => {}, + children: customer.idCardPhotoPath ? ( + + ) : null + }, + { + data: getAvailableFields(usSsnElements), + title: 'US SSN', + titleIcon: , + state: R.path(['usSsnOverride'])(customer), + authorize: () => updateCustomer({ usSsnOverride: OVERRIDE_AUTHORIZED }), + reject: () => updateCustomer({ usSsnOverride: OVERRIDE_REJECTED }), + save: () => {}, + validationSchema: schemas.usSsn, + initialValues: initialValues.usSsn + } + ] + + const editableCard = ({ + title, + authorize, + reject, + state, + titleIcon, + data, + save, + children, + validationSchema, + initialValues + }) => { + return ( + + ) + } + + const visibleCards = getVisibleCards(cards) return (
@@ -105,7 +295,6 @@ const CustomerData = ({ customer, updateCustomer }) => { Icon={OverviewIcon} InverseIcon={OverviewReversedIcon} onClick={() => setListView(false)} - variant="contained" /> { onClick={() => setListView(true)}>
- {listView &&

{''}

} {!listView && ( - - -
- -

{'Name'}

- -
- {}}> -
-
- - -
- -

{'ID Scan'}

- -
- {}}> -
-
+ {visibleCards.map((elem, idx) => { + return isEven(idx) ? editableCard(elem) : null + })}
- - -
- -

{'SMS Confirmation'}

- -
-
-
+ {visibleCards.map((elem, idx) => { + return !isEven(idx) ? editableCard(elem) : null + })}
)} diff --git a/new-lamassu-admin/src/pages/Customers/CustomerData.styles.js b/new-lamassu-admin/src/pages/Customers/CustomerData.styles.js index 47793122..5dfecd1a 100644 --- a/new-lamassu-admin/src/pages/Customers/CustomerData.styles.js +++ b/new-lamassu-admin/src/pages/Customers/CustomerData.styles.js @@ -8,29 +8,12 @@ export default { marginTop: 7, marginRight: 24 }, - 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]] - }, viewIcons: { marginRight: 12 } diff --git a/new-lamassu-admin/src/pages/Customers/CustomerProfile.js b/new-lamassu-admin/src/pages/Customers/CustomerProfile.js index 1c2d6fe3..657cbf47 100644 --- a/new-lamassu-admin/src/pages/Customers/CustomerProfile.js +++ b/new-lamassu-admin/src/pages/Customers/CustomerProfile.js @@ -25,7 +25,6 @@ import styles from './CustomerProfile.styles' import { CustomerDetails, TransactionsList, - ComplianceDetails, CustomerSidebar } from './components' import { getFormattedPhone, getName } from './helper' @@ -260,22 +259,14 @@ const CustomerProfile = memo(() => { setShowCompliance={() => setShowCompliance(!showCompliance)} /> - {!showCompliance && ( -
- -
- )} - {showCompliance && ( - + - )} +
)} {isCustomerData && ( diff --git a/new-lamassu-admin/src/pages/Customers/components/ComplianceDetails.js b/new-lamassu-admin/src/pages/Customers/components/ComplianceDetails.js deleted file mode 100644 index d5d66773..00000000 --- a/new-lamassu-admin/src/pages/Customers/components/ComplianceDetails.js +++ /dev/null @@ -1,131 +0,0 @@ -import { Box } from '@material-ui/core' -import { makeStyles } from '@material-ui/core/styles' -import * as R from 'ramda' -import React from 'react' - -import ImagePopper from 'src/components/ImagePopper' -import { H3, Info3 } from 'src/components/typography' -import { - PropertyCard, - OVERRIDE_AUTHORIZED, - OVERRIDE_REJECTED -} from 'src/pages/Customers/components/propertyCard' -import { ReactComponent as CrossedCameraIcon } from 'src/styling/icons/ID/photo/crossed-camera.svg' -import { URI } from 'src/utils/apollo' - -import { complianceDetailsStyles } from './ComplianceDetails.styles' -import Field from './Field' - -import { IdDataCard } from './' - -const useStyles = makeStyles(complianceDetailsStyles) - -const imageWidth = 165 -const imageHeight = 45 -const popupImageWidth = 360 -const popupImageHeight = 240 - -const Photo = ({ show, src }) => { - const classes = useStyles({ width: imageWidth }) - - return ( - <> - {show ? ( - - ) : ( -
- -
- )} - - ) -} - -const ComplianceDetails = ({ customer, updateCustomer }) => { - const classes = useStyles({ width: imageWidth }) - - const sanctions = R.path(['sanctions'])(customer) - const sanctionsAt = R.path(['sanctionsAt'])(customer) - const sanctionsDisplay = !sanctionsAt - ? 'Not checked yet' - : sanctions - ? 'Passed' - : 'Failed' - - return ( -
-

Compliance details

-
- - - - - updateCustomer({ idCardPhotoOverride: OVERRIDE_AUTHORIZED }) - } - reject={() => - updateCustomer({ idCardPhotoOverride: OVERRIDE_REJECTED }) - }> - - - - updateCustomer({ frontCameraOverride: OVERRIDE_AUTHORIZED }) - } - reject={() => - updateCustomer({ frontCameraOverride: OVERRIDE_REJECTED }) - }> - - - - - - updateCustomer({ usSsnOverride: OVERRIDE_AUTHORIZED }) - } - reject={() => - updateCustomer({ usSsnOverride: OVERRIDE_REJECTED }) - }> - - - - updateCustomer({ sanctionsOverride: OVERRIDE_AUTHORIZED }) - } - reject={() => - updateCustomer({ sanctionsOverride: OVERRIDE_REJECTED }) - }> - {sanctionsDisplay} - - - -
-
- ) -} - -export default ComplianceDetails diff --git a/new-lamassu-admin/src/pages/Customers/components/ComplianceDetails.styles.js b/new-lamassu-admin/src/pages/Customers/components/ComplianceDetails.styles.js deleted file mode 100644 index 1bb1d48f..00000000 --- a/new-lamassu-admin/src/pages/Customers/components/ComplianceDetails.styles.js +++ /dev/null @@ -1,25 +0,0 @@ -const complianceDetailsStyles = { - complianceDetailsGrid: { - display: 'flex', - flexDirection: 'row' - }, - firstColumn: { - display: 'flex', - flexDirection: 'column', - width: '100%', - marginRight: 10 - }, - lastColumn: { - display: 'flex', - flexDirection: 'column', - width: '100%', - marginLeft: 10 - }, - photoWrapper: ({ width }) => ({ - display: 'flex', - justifyContent: 'center', - width - }) -} - -export { complianceDetailsStyles } diff --git a/new-lamassu-admin/src/pages/Customers/components/CustomerDetails.js b/new-lamassu-admin/src/pages/Customers/components/CustomerDetails.js index 27714ee9..d5412738 100644 --- a/new-lamassu-admin/src/pages/Customers/components/CustomerDetails.js +++ b/new-lamassu-admin/src/pages/Customers/components/CustomerDetails.js @@ -2,11 +2,8 @@ import { makeStyles, Box } from '@material-ui/core' import * as R from 'ramda' import React, { memo } from 'react' -import { SubpageButton } from 'src/components/buttons' import { H2, Label1, P } from 'src/components/typography' import { ReactComponent as IdIcon } from 'src/styling/icons/ID/card/zodiac.svg' -import { ReactComponent as LawIconInverse } from 'src/styling/icons/circle buttons/law/white.svg' -import { ReactComponent as LawIcon } from 'src/styling/icons/circle buttons/law/zodiac.svg' import mainStyles from '../CustomersList.styles' import { getFormattedPhone, getName } from '../helper' @@ -70,13 +67,6 @@ const CustomerDetails = memo( locale.country )} - - Compliance details - {elements.map(({ size, header }, idx) => ( diff --git a/new-lamassu-admin/src/pages/Customers/components/EditableCard.js b/new-lamassu-admin/src/pages/Customers/components/EditableCard.js index 44959e17..e3a7f230 100644 --- a/new-lamassu-admin/src/pages/Customers/components/EditableCard.js +++ b/new-lamassu-admin/src/pages/Customers/components/EditableCard.js @@ -1,3 +1,4 @@ +import { CardContent, Card, Grid } from '@material-ui/core' import { makeStyles } from '@material-ui/core/styles' import classnames from 'classnames' import { Form, Formik, Field as FormikField } from 'formik' @@ -5,14 +6,23 @@ import { useState, React } from 'react' import ErrorMessage from 'src/components/ErrorMessage' import PromptWhenDirty from 'src/components/PromptWhenDirty' +import { MainStatus } from 'src/components/Status' +import { Tooltip } from 'src/components/Tooltip' import { ActionButton } from 'src/components/buttons' -import { Label1, Info3 } from 'src/components/typography' +import { Label1, Info3, H3 } from 'src/components/typography' +import { + OVERRIDE_AUTHORIZED, + OVERRIDE_REJECTED, + OVERRIDE_PENDING +} from 'src/pages/Customers/components/propertyCard' import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg' import { ReactComponent as EditReversedIcon } from 'src/styling/icons/action/edit/white.svg' import { ReactComponent as AuthorizeReversedIcon } from 'src/styling/icons/button/authorize/white.svg' import { ReactComponent as AuthorizeIcon } from 'src/styling/icons/button/authorize/zodiac.svg' import { ReactComponent as CancelReversedIcon } from 'src/styling/icons/button/cancel/white.svg' import { ReactComponent as CancelIcon } from 'src/styling/icons/button/cancel/zodiac.svg' +import { ReactComponent as SaveReversedIcon } from 'src/styling/icons/circle buttons/save/white.svg' +import { ReactComponent as SaveIcon } from 'src/styling/icons/circle buttons/save/zodiac.svg' import { comet } from 'src/styling/variables' import styles from './EditableCard.styles.js' @@ -92,66 +102,136 @@ const EditableField = ({ editing, field, size, ...props }) => { ) } -const EditableCard = ({ data, save }) => { +const EditableCard = ({ + data, + save, + authorize, + reject, + state, + title, + titleIcon, + children +}) => { const classes = useStyles() const [editing, setEditing] = useState(false) const [error, setError] = useState(null) + const label1ClassNames = { + [classes.label1]: true, + [classes.label1Pending]: state === OVERRIDE_PENDING, + [classes.label1Rejected]: state === OVERRIDE_REJECTED, + [classes.label1Accepted]: state === OVERRIDE_AUTHORIZED + } + const isNotAuthorized = + state === OVERRIDE_REJECTED || state === OVERRIDE_PENDING + const authorized = + state === OVERRIDE_PENDING + ? { label: 'Pending', type: 'neutral' } + : state === OVERRIDE_REJECTED + ? { label: 'Rejected', type: 'error' } + : { label: 'Accepted', type: 'success' } + + const editableField = field => { + return + } + return (
- save(values)} - onReset={() => { - setEditing(false) - setError(false) - }}> -
- -
- {data.map((field, idx) => ( - - ))} + + +
+ {titleIcon} +

{title}

+ +
+ +
-
- {!editing && ( -
- setEditing(true)}> - {`Edit`} - + save(values)} + onReset={() => { + setEditing(false) + setError(false) + }}> + + +
+ + + {data?.map((field, idx) => { + return idx >= 0 && idx < 4 ? editableField(field) : null + })} + + + {data?.map((field, idx) => { + return idx >= 4 ? editableField(field) : null + })} + +
- )} - {editing && ( -
-
- - Save - -
- - Cancel - - {error && Failed to save changes} + {children} +
+ {!editing && ( +
+ setEditing(true)}> + {`Edit`} + +
+ )} + {editing && ( +
+ {data && ( +
+ + Save + +
+ )} +
+ + Cancel + +
+ authorize() : () => reject() + }> + {isNotAuthorized ? 'Authorize' : 'Reject'} + + {error && ( + Failed to save changes + )} +
+ )}
- )} -
- -
+ + + +
) } diff --git a/new-lamassu-admin/src/pages/Customers/components/EditableCard.styles.js b/new-lamassu-admin/src/pages/Customers/components/EditableCard.styles.js index a59797a4..1c2a8603 100644 --- a/new-lamassu-admin/src/pages/Customers/components/EditableCard.styles.js +++ b/new-lamassu-admin/src/pages/Customers/components/EditableCard.styles.js @@ -1,13 +1,50 @@ +import { tomato, spring4, comet } from 'src/styling/variables' + export default { + label1: { + display: 'flex', + width: 85, + justifyContent: 'right' + }, + label1Pending: { + color: comet + }, + label1Rejected: { + color: tomato + }, + label1Accepted: { + color: spring4 + }, editButton: { + marginTop: 30, display: 'flex', justifyContent: 'right' }, - saveButton: { - marginRight: 12 + button: { + marginRight: 8 }, editingButtons: { + marginTop: 30, display: 'flex', justifyContent: 'right' + }, + card: { + borderRadius: 10, + marginRight: 15, + marginBottom: 15 + }, + cardHeader: { + display: 'flex', + flexDirection: 'row', + marginBottom: 15 + }, + editIcon: { + marginTop: 5 + }, + cardIcon: { + marginTop: 7 + }, + cardTitle: { + margin: [[8, 15, 15, 15]] } } diff --git a/new-lamassu-admin/src/pages/Customers/components/index.js b/new-lamassu-admin/src/pages/Customers/components/index.js index 65a03ca2..a8b7b184 100644 --- a/new-lamassu-admin/src/pages/Customers/components/index.js +++ b/new-lamassu-admin/src/pages/Customers/components/index.js @@ -1,4 +1,3 @@ -import ComplianceDetails from './ComplianceDetails' import CustomerDetails from './CustomerDetails' import CustomerSidebar from './CustomerSidebar' import EditableCard from './EditableCard' @@ -10,7 +9,6 @@ export { CustomerDetails, IdDataCard, TransactionsList, - ComplianceDetails, CustomerSidebar, Field, EditableCard