feat: twilio api data confirmation dialog

This commit is contained in:
José Oliveira 2022-01-03 22:42:47 +00:00
parent 894161a998
commit 5406a3cfdd
7 changed files with 172 additions and 47 deletions

View file

@ -7,8 +7,15 @@ import styles from './Button.styles'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
const ActionButton = memo( const ActionButton = memo(
({ size = 'lg', children, className, buttonClassName, ...props }) => { ({
const classes = useStyles({ size }) size = 'lg',
children,
className,
buttonClassName,
backgroundColor,
...props
}) => {
const classes = useStyles({ size, backgroundColor })
return ( return (
<div className={classnames(className, classes.wrapper)}> <div className={classnames(className, classes.wrapper)}>
<button <button

View file

@ -5,6 +5,9 @@ import {
secondaryColor, secondaryColor,
secondaryColorDark, secondaryColorDark,
secondaryColorDarker, secondaryColorDarker,
offColor,
offDarkColor,
offDarkerColor,
spacer spacer
} from 'src/styling/variables' } from 'src/styling/variables'
@ -28,11 +31,11 @@ export default {
const shadowSize = height / 12 const shadowSize = height / 12
return { height: height + shadowSize / 2 } return { height: height + shadowSize / 2 }
}, },
button: ({ size }) => { button: ({ size, backgroundColor }) => {
const height = pickSize(size) const height = pickSize(size)
const shadowSize = size === 'xl' ? 3 : height / 12 const shadowSize = size === 'xl' ? 3 : height / 12
const padding = size === 'xl' ? 20 : height / 2 const padding = size === 'xl' ? 20 : height / 2
const isGrey = backgroundColor === 'grey'
return { return {
extend: size === 'xl' ? h1 : h3, extend: size === 'xl' ? h1 : h3,
border: 'none', border: 'none',
@ -40,7 +43,7 @@ export default {
cursor: 'pointer', cursor: 'pointer',
fontWeight: 900, fontWeight: 900,
outline: 0, outline: 0,
backgroundColor: secondaryColor, backgroundColor: isGrey ? offDarkColor : secondaryColor,
'&:disabled': { '&:disabled': {
backgroundColor: disabledColor, backgroundColor: disabledColor,
boxShadow: 'none', boxShadow: 'none',
@ -56,15 +59,19 @@ export default {
height, height,
padding: `0 ${padding}px`, padding: `0 ${padding}px`,
borderRadius: height / 4, borderRadius: height / 4,
boxShadow: `0 ${shadowSize}px ${secondaryColorDark}`, boxShadow: `0 ${shadowSize}px ${isGrey ? offColor : secondaryColorDark}`,
'&:hover': { '&:hover': {
backgroundColor: secondaryColorDark, backgroundColor: isGrey ? offColor : secondaryColorDark,
boxShadow: `0 ${shadowSize}px ${secondaryColorDarker}` boxShadow: `0 ${shadowSize}px ${
isGrey ? offDarkerColor : secondaryColorDarker
}`
}, },
'&:active': { '&:active': {
marginTop: shadowSize / 2, marginTop: shadowSize / 2,
backgroundColor: secondaryColorDark, backgroundColor: isGrey ? offDarkColor : secondaryColorDark,
boxShadow: `0 ${shadowSize / 2}px ${secondaryColorDarker}` boxShadow: `0 ${shadowSize / 2}px ${
isGrey ? offDarkerColor : secondaryColorDarker
}`
} }
} }
} }

View file

@ -1,3 +1,4 @@
import { DialogActions, DialogContent, Dialog } from '@material-ui/core'
import Grid from '@material-ui/core/Grid' import Grid from '@material-ui/core/Grid'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import { parse, format } from 'date-fns/fp' import { parse, format } from 'date-fns/fp'
@ -7,9 +8,9 @@ import { useState, React } from 'react'
import * as Yup from 'yup' import * as Yup from 'yup'
import ImagePopper from 'src/components/ImagePopper' import ImagePopper from 'src/components/ImagePopper'
import { FeatureButton } from 'src/components/buttons' import { FeatureButton, Button, IconButton } from 'src/components/buttons'
import { TextInput } from 'src/components/inputs/formik' import { TextInput } from 'src/components/inputs/formik'
import { H3, Info3 } from 'src/components/typography' import { H3, Info3, H2 } from 'src/components/typography'
import { import {
OVERRIDE_AUTHORIZED, OVERRIDE_AUTHORIZED,
OVERRIDE_REJECTED OVERRIDE_REJECTED
@ -17,6 +18,7 @@ import {
import { ReactComponent as CardIcon } from 'src/styling/icons/ID/card/comet.svg' 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 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 CrossedCameraIcon } from 'src/styling/icons/ID/photo/crossed-camera.svg'
import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.svg'
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/comet.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 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 CustomerListViewIcon } from 'src/styling/icons/circle buttons/customer-list-view/zodiac.svg'
@ -71,10 +73,12 @@ const CustomerData = ({
deleteEditedData, deleteEditedData,
updateCustomRequest, updateCustomRequest,
authorizeCustomRequest, authorizeCustomRequest,
updateCustomEntry updateCustomEntry,
retrieveAditionalData
}) => { }) => {
const classes = useStyles() const classes = useStyles()
const [listView, setListView] = useState(false) const [listView, setListView] = useState(false)
const [retrieve, setRetrieve] = useState(false)
const idData = R.path(['idCardData'])(customer) const idData = R.path(['idCardData'])(customer)
const rawExpirationDate = R.path(['expirationDate'])(idData) const rawExpirationDate = R.path(['expirationDate'])(idData)
@ -162,10 +166,11 @@ const CustomerData = ({
authorize: () => {}, authorize: () => {},
reject: () => {}, reject: () => {},
save: () => {}, save: () => {},
retrieveAditionalData: () => setRetrieve(true),
validationSchema: customerDataSchemas.smsData, validationSchema: customerDataSchemas.smsData,
initialValues: initialValues.smsData, initialValues: initialValues.smsData,
isAvailable: !_.isNil(phone), isAvailable: !_.isNil(phone),
isDeletable: !_.isNil(smsData) || !_.isEmpty(smsData.result) hasAditionalData: !_.isNil(smsData) && !_.isEmpty(smsData.result)
}, },
{ {
title: 'Name', title: 'Name',
@ -341,10 +346,12 @@ const CustomerData = ({
fields, fields,
save, save,
deleteEditedData, deleteEditedData,
retrieveAditionalData,
children, children,
validationSchema, validationSchema,
initialValues, initialValues,
hasImage hasImage,
hasAditionalData
}, },
idx idx
) => { ) => {
@ -357,12 +364,14 @@ const CustomerData = ({
state={state} state={state}
titleIcon={titleIcon} titleIcon={titleIcon}
hasImage={hasImage} hasImage={hasImage}
hasAditionalData={hasAditionalData}
fields={fields} fields={fields}
children={children} children={children}
validationSchema={validationSchema} validationSchema={validationSchema}
initialValues={initialValues} initialValues={initialValues}
save={save} save={save}
deleteEditedData={deleteEditedData}></EditableCard> deleteEditedData={deleteEditedData}
retrieveAditionalData={retrieveAditionalData}></EditableCard>
) )
} }
@ -441,8 +450,67 @@ const CustomerData = ({
</div> </div>
)} )}
</div> </div>
<RetrieveDataDialog
setRetrieve={setRetrieve}
retrieveAditionalData={retrieveAditionalData}
open={retrieve}></RetrieveDataDialog>
</div> </div>
) )
} }
const RetrieveDataDialog = ({
setRetrieve,
retrieveAditionalData,
open,
props
}) => {
const classes = useStyles()
return (
<Dialog
open={open}
aria-labelledby="form-dialog-title"
PaperProps={{
style: {
borderRadius: 8,
minWidth: 656,
bottom: 125,
right: 7
}
}}
{...props}>
<div className={classes.closeButton}>
<IconButton
size={16}
aria-label="close"
onClick={() => setRetrieve(false)}>
<CloseIcon />
</IconButton>
</div>
<H2 className={classes.dialogTitle}>{'Retrieve API data from Twilio'}</H2>
<DialogContent className={classes.dialogContent}>
<Info3>{`With this action you'll be using Twilio's API to retrieve additional
data from this user. This includes name and address, if available.\n`}</Info3>
<Info3>{` There is a small cost from Twilio for each retrieval. Would you like
to proceed?`}</Info3>
</DialogContent>
<DialogActions className={classes.dialogActions}>
<Button
backgroundColor="grey"
className={classes.cancelButton}
onClick={() => setRetrieve(false)}>
Cancel
</Button>
<Button
onClick={() => {
retrieveAditionalData()
setRetrieve(false)
}}>
Confirm
</Button>
</DialogActions>
</Dialog>
)
}
export default CustomerData export default CustomerData

View file

@ -1,4 +1,4 @@
import { offColor } from 'src/styling/variables' import { offColor, spacer } from 'src/styling/variables'
export default { export default {
header: { header: {
@ -45,5 +45,26 @@ export default {
left: '100%', left: '100%',
marginLeft: 15 marginLeft: 15
} }
},
closeButton: {
display: 'flex',
padding: [[spacer * 2, spacer * 2, 0, spacer * 2]],
paddingRight: spacer * 1.5,
justifyContent: 'end'
},
dialogTitle: {
margin: [[0, spacer * 2, spacer, spacer * 4 + spacer]]
},
dialogContent: {
width: 615,
marginLeft: 16
},
dialogActions: {
padding: spacer * 4,
paddingTop: spacer * 2
},
cancelButton: {
marginRight: 8,
padding: 0
} }
} }

View file

@ -439,6 +439,16 @@ const CustomerProfile = memo(() => {
} }
}) })
const retrieveAditionalData = () =>
setCustomer({
variables: {
customerId,
customerInput: {
subscriberInfo: true
}
}
})
const onClickSidebarItem = code => setClickedItem(code) const onClickSidebarItem = code => setClickedItem(code)
const configData = R.path(['config'])(customerResponse) ?? [] const configData = R.path(['config'])(customerResponse) ?? []
@ -559,25 +569,6 @@ const CustomerProfile = memo(() => {
}> }>
{`${blocked ? 'Authorize' : 'Block'} customer`} {`${blocked ? 'Authorize' : 'Block'} customer`}
</ActionButton> </ActionButton>
<ActionButton
color="primary"
className={classes.actionButton}
Icon={blocked ? AuthorizeIcon : BlockIcon}
InverseIcon={
blocked ? AuthorizeReversedIcon : BlockReversedIcon
}
onClick={() =>
setCustomer({
variables: {
customerId,
customerInput: {
subscriberInfo: true
}
}
})
}>
{`Retrieve information`}
</ActionButton>
</div> </div>
</div> </div>
<div> <div>
@ -598,6 +589,22 @@ const CustomerProfile = memo(() => {
{`Test user`} {`Test user`}
</div> </div>
</div> </div>
<ActionButton
color="primary"
className={classes.actionButton}
Icon={blocked ? AuthorizeIcon : BlockIcon}
InverseIcon={
blocked ? AuthorizeReversedIcon : BlockReversedIcon
}
onClick={() =>
updateCustomer({
authorizedOverride: blocked
? OVERRIDE_AUTHORIZED
: OVERRIDE_REJECTED
})
}>
{`${blocked ? 'Authorize' : 'Block'} customer`}
</ActionButton>
</div> </div>
</> </>
)} )}
@ -637,7 +644,8 @@ const CustomerProfile = memo(() => {
deleteEditedData={deleteEditedData} deleteEditedData={deleteEditedData}
updateCustomRequest={setCustomerCustomInfoRequest} updateCustomRequest={setCustomerCustomInfoRequest}
authorizeCustomRequest={authorizeCustomRequest} authorizeCustomRequest={authorizeCustomRequest}
updateCustomEntry={updateCustomEntry}></CustomerData> updateCustomEntry={updateCustomEntry}
retrieveAditionalData={retrieveAditionalData}></CustomerData>
</div> </div>
)} )}
{isNotes && ( {isNotes && (

View file

@ -23,6 +23,8 @@ import { ReactComponent as EditReversedIcon } from 'src/styling/icons/action/edi
import { ReactComponent as AuthorizeIcon } from 'src/styling/icons/button/authorize/white.svg' import { ReactComponent as AuthorizeIcon } from 'src/styling/icons/button/authorize/white.svg'
import { ReactComponent as BlockIcon } from 'src/styling/icons/button/block/white.svg' import { ReactComponent as BlockIcon } from 'src/styling/icons/button/block/white.svg'
import { ReactComponent as CancelReversedIcon } from 'src/styling/icons/button/cancel/white.svg' import { ReactComponent as CancelReversedIcon } from 'src/styling/icons/button/cancel/white.svg'
import { ReactComponent as DataReversedIcon } from 'src/styling/icons/button/data/white.svg'
import { ReactComponent as DataIcon } from 'src/styling/icons/button/data/zodiac.svg'
import { ReactComponent as ReplaceReversedIcon } from 'src/styling/icons/button/replace/white.svg' import { ReactComponent as ReplaceReversedIcon } from 'src/styling/icons/button/replace/white.svg'
import { ReactComponent as SaveReversedIcon } from 'src/styling/icons/circle buttons/save/white.svg' import { ReactComponent as SaveReversedIcon } from 'src/styling/icons/circle buttons/save/white.svg'
import { comet } from 'src/styling/variables' import { comet } from 'src/styling/variables'
@ -118,7 +120,8 @@ const EditableCard = ({
validationSchema, validationSchema,
initialValues, initialValues,
deleteEditedData, deleteEditedData,
isDeletable retrieveAdditionalData,
hasAdditionalData = true
}) => { }) => {
const classes = useStyles() const classes = useStyles()
@ -211,19 +214,28 @@ const EditableCard = ({
<div className={classes.edit}> <div className={classes.edit}>
{!editing && ( {!editing && (
<div className={classes.editButton}> <div className={classes.editButton}>
{// TODO: Remove false condition for next release
false && (
<div className={classes.deleteButton}> <div className={classes.deleteButton}>
{false && (
<ActionButton <ActionButton
color="primary" color="primary"
type="button" type="button"
Icon={DeleteIcon} Icon={DeleteIcon}
InverseIcon={DeleteReversedIcon} InverseIcon={DeleteReversedIcon}
onClick={() => deleteEditedData()}> onClick={() => deleteEditedData()}>
{`Delete`} Delete
</ActionButton> </ActionButton>
</div>
)} )}
{!hasAdditionalData && (
<ActionButton
color="primary"
type="button"
Icon={DataIcon}
InverseIcon={DataReversedIcon}
onClick={() => retrieveAdditionalData()}>
Retrieve API data
</ActionButton>
)}
</div>
<ActionButton <ActionButton
color="primary" color="primary"
Icon={EditIcon} Icon={EditIcon}

View file

@ -432,9 +432,11 @@ const customerDataSchemas = {
frontCamera: Yup.object().shape({ frontCamera: Yup.object().shape({
frontCamera: Yup.mixed().required() frontCamera: Yup.mixed().required()
}), }),
smsData: Yup.object().shape({ smsData: Yup.object()
.shape({
phoneNumber: Yup.mixed().required() phoneNumber: Yup.mixed().required()
}) })
.required()
} }
const requirementElements = { const requirementElements = {