feat: twilio api data confirmation dialog
This commit is contained in:
parent
894161a998
commit
5406a3cfdd
7 changed files with 172 additions and 47 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 && (
|
||||||
|
|
|
||||||
|
|
@ -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
|
<div className={classes.deleteButton}>
|
||||||
false && (
|
{false && (
|
||||||
<div className={classes.deleteButton}>
|
|
||||||
<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}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
phoneNumber: Yup.mixed().required()
|
.shape({
|
||||||
})
|
phoneNumber: Yup.mixed().required()
|
||||||
|
})
|
||||||
|
.required()
|
||||||
}
|
}
|
||||||
|
|
||||||
const requirementElements = {
|
const requirementElements = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue