feat: add sms preview

feat: add reset to default button
feat: add attachable values
feat: connect the sms receipt notice to the sms receipt request
This commit is contained in:
Sérgio Salgado 2022-02-10 00:36:32 +00:00
parent 91e209b6ab
commit cd01d894a3
10 changed files with 186 additions and 69 deletions

View file

@ -6,11 +6,14 @@ import * as R from 'ramda'
import React, { useState } from 'react'
import { DeleteDialog } from 'src/components/DeleteDialog'
import { HoverableTooltip } from 'src/components/Tooltip'
import { IconButton } from 'src/components/buttons'
import { Switch } from 'src/components/inputs'
import DataTable from 'src/components/tables/DataTable'
import { H4, P, Label3 } from 'src/components/typography'
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
import { ReactComponent as ExpandIconClosed } from 'src/styling/icons/action/expand/closed.svg'
import { ReactComponent as ExpandIconOpen } from 'src/styling/icons/action/expand/open.svg'
import { ReactComponent as WhiteLogo } from 'src/styling/icons/menu/logo-white.svg'
import styles from './SMSNotices.styles'
@ -63,6 +66,26 @@ const multiReplace = (str, obj) => {
})
}
const formatContent = content => {
const fragments = R.split(/\n/)(content)
return R.map((it, idx) => {
if (idx === fragments.length) return <>{it}</>
return (
<>
{it}
<br />
</>
)
}, fragments)
}
const TOOLTIPS = {
smsCode: ``,
cashOutDispenseReady: ``,
smsReceipt: formatContent(`The contents of this notice will be appended to the end of the SMS receipt sent, and not replace it.\n
To edit the contents of the SMS receipt, please go to the 'Receipt' tab`)
}
const SMSPreview = ({ sms, coords }) => {
const classes = useStyles(coords)
@ -78,7 +101,13 @@ const SMSPreview = ({ sms, coords }) => {
<WhiteLogo width={22} height={22} />
</div>
<Paper className={classes.smsPreviewContent}>
<P noMargin>{multiReplace(sms?.message, matches)}</P>
<P noMargin>
{R.isEmpty(sms?.message) ? (
<i>No content available</i>
) : (
formatContent(multiReplace(sms?.message, matches))
)}
</P>
</Paper>
<Label3>{format('HH:mm', new Date())}</Label3>
</div>
@ -118,8 +147,8 @@ const SMSNotices = () => {
const loading = messagesLoading
const handleClose = () => {
setSelectedSMS(null)
setShowModal(false)
setSelectedSMS(null)
setDeleteDialog(false)
}
@ -129,7 +158,17 @@ const SMSNotices = () => {
width: 500,
size: 'sm',
textAlign: 'left',
view: it => R.prop('messageName', it)
view: it =>
!R.isEmpty(TOOLTIPS[it.event]) ? (
<div className={classes.messageWithTooltip}>
{R.prop('messageName', it)}
<HoverableTooltip width={250}>
<P>{TOOLTIPS[it.event]}</P>
</HoverableTooltip>
</div>
) : (
R.prop('messageName', it)
)
},
{
header: 'Edit',
@ -180,9 +219,15 @@ const SMSNotices = () => {
5 -
e.currentTarget.getBoundingClientRect().bottom
})
setPreviewOpen(true)
R.equals(selectedSMS, it)
? setPreviewOpen(!previewOpen)
: setPreviewOpen(true)
}}>
<EditIcon />
{R.equals(selectedSMS, it) && previewOpen ? (
<ExpandIconOpen />
) : (
<ExpandIconClosed />
)}
</IconButton>
)
}

View file

@ -1,4 +1,9 @@
import { spacer } from 'src/styling/variables'
import {
spacer,
fontMonospaced,
fontSize5,
fontColor
} from 'src/styling/variables'
const styles = {
header: {
@ -52,6 +57,38 @@ const styles = {
width: 225,
padding: 15,
borderRadius: '15px 15px 15px 0px'
},
chipButtons: {
width: 480,
display: 'flex',
flexDirection: 'column',
alignItems: 'space-between',
'& > div': {
marginTop: 15
},
'& > div:first-child': {
marginTop: 0
},
'& > div > div': {
margin: [[0, 5, 0, 5]]
},
'& > div > div > span': {
lineHeight: '120%',
color: fontColor,
fontSize: fontSize5,
fontFamily: fontMonospaced,
fontWeight: 500
},
marginLeft: 'auto',
marginRight: 'auto'
},
resetToDefault: {
width: 145
},
messageWithTooltip: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center'
}
}

View file

@ -6,8 +6,12 @@ import * as Yup from 'yup'
import ErrorMessage from 'src/components/ErrorMessage'
import Modal from 'src/components/Modal'
import { Button } from 'src/components/buttons'
import { ActionButton, Button } from 'src/components/buttons'
import { TextInput } from 'src/components/inputs/formik'
import { Info2 } from 'src/components/typography'
import { ReactComponent as DefaultIconReverse } from 'src/styling/icons/button/retry/white.svg'
import { ReactComponent as DefaultIcon } from 'src/styling/icons/button/retry/zodiac.svg'
import { zircon } from 'src/styling/variables'
import styles from './SMSNotices.styles'
@ -21,7 +25,7 @@ const getErrorMsg = (formikErrors, formikTouched, mutationError) => {
return null
}
const prefill = {
const PREFILL = {
smsCode: {
validator: Yup.string()
.required('The message content is required!')
@ -43,24 +47,28 @@ const prefill = {
validator: Yup.string()
.required('The message content is required!')
.trim()
// .test({
// name: 'has-timestamp-tag',
// message: 'A #timestamp tag is missing from the message!',
// exclusive: false,
// test: value => value?.match(/#timestamp/g || [])?.length > 0
// })
// .test({
// name: 'has-single-timestamp-tag',
// message: 'There should be a single #timestamp tag!',
// exclusive: false,
// test: value => value?.match(/#timestamp/g || [])?.length === 1
// })
},
smsReceipt: {
validator: Yup.string().trim()
}
}
const chips = {
smsCode: [{ code: '#code', display: 'Confirmation code', removable: false }],
cashOutDispenseReady: []
const CHIPS = {
smsCode: [
{ code: '#code', display: 'Confirmation code', obligatory: true },
{ code: '#timestamp', display: 'Timestamp', obligatory: false }
],
cashOutDispenseReady: [
{ code: '#timestamp', display: 'Timestamp', obligatory: false }
],
smsReceipt: [{ code: '#timestamp', display: 'Timestamp', obligatory: false }]
}
const DEFAULT_MESSAGES = {
smsCode: 'Your cryptomat code: #code',
cashOutDispenseReady:
'Your cash is waiting! Go to the Cryptomat and press Redeem within 24 hours. [#timestamp]',
smsReceipt: ''
}
const SMSNoticesModal = ({
@ -80,7 +88,7 @@ const SMSNoticesModal = ({
const validationSchema = Yup.object().shape({
event: Yup.string().required('An event is required!'),
message:
prefill[sms?.event]?.validator ??
PREFILL[sms?.event]?.validator ??
Yup.string()
.required('The message content is required!')
.trim()
@ -108,7 +116,7 @@ const SMSNoticesModal = ({
<>
{showModal && (
<Modal
title={`Edit SMS notice`}
title={`SMS notice - ${sms?.messageName}`}
closeOnBackdropClick={true}
width={600}
height={500}
@ -124,6 +132,17 @@ const SMSNoticesModal = ({
}>
{({ values, errors, touched, setFieldValue }) => (
<Form id="sms-notice" className={classes.form}>
<ActionButton
color="primary"
Icon={DefaultIcon}
InverseIcon={DefaultIconReverse}
className={classes.resetToDefault}
type="button"
onClick={() =>
setFieldValue('message', DEFAULT_MESSAGES[sms?.event])
}>
Reset to default
</ActionButton>
<Field
name="message"
label="Message content"
@ -132,22 +151,39 @@ const SMSNoticesModal = ({
rows={6}
component={TextInput}
/>
{R.map(
it => (
<Chip
label={it.display}
onClick={() => {
R.includes(it.code, values.message)
? setFieldValue('message', values.message)
: setFieldValue(
'message',
values.message.concat(' ', it.code, ' ')
)
}}
/>
),
chips[sms?.event]
{R.length(CHIPS[sms?.event]) > 0 && (
<Info2 noMargin>Values to attach</Info2>
)}
<div className={classes.chipButtons}>
{R.map(
it => (
<div>
{R.map(
ite => (
<Chip
label={ite.display}
size="small"
style={{ backgroundColor: zircon }}
disabled={R.includes(ite.code, values.message)}
className={classes.chip}
onClick={() => {
setFieldValue(
'message',
values.message.concat(
R.last(values.message) === ' ' ? '' : ' ',
ite.code
)
)
}}
/>
),
it
)}
</div>
),
R.splitEvery(3, CHIPS[sms?.event])
)}
</div>
<div className={classes.footer}>
{getErrorMsg(errors, touched, creationError) && (
<ErrorMessage>