feat: add SMS preview component
This commit is contained in:
parent
0d5f5167ef
commit
91e209b6ab
6 changed files with 169 additions and 36 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import { makeStyles, Paper } from '@material-ui/core'
|
||||
import { format } from 'date-fns/fp'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
|
|
@ -8,11 +9,12 @@ import { DeleteDialog } from 'src/components/DeleteDialog'
|
|||
import { IconButton } from 'src/components/buttons'
|
||||
import { Switch } from 'src/components/inputs'
|
||||
import DataTable from 'src/components/tables/DataTable'
|
||||
import { H4 } from 'src/components/typography'
|
||||
import { H4, P, Label3 } from 'src/components/typography'
|
||||
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
|
||||
import { ReactComponent as WhiteLogo } from 'src/styling/icons/menu/logo-white.svg'
|
||||
|
||||
import CustomSMSModal from './CustomSMSModal'
|
||||
import styles from './SMSNotices.styles'
|
||||
import CustomSMSModal from './SMSNoticesModal'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
|
|
@ -53,12 +55,45 @@ const DISABLE_SMS_NOTICE = gql`
|
|||
}
|
||||
`
|
||||
|
||||
const multiReplace = (str, obj) => {
|
||||
var re = new RegExp(Object.keys(obj).join('|'), 'gi')
|
||||
|
||||
return str.replace(re, function(matched) {
|
||||
return obj[matched.toLowerCase()]
|
||||
})
|
||||
}
|
||||
|
||||
const SMSPreview = ({ sms, coords }) => {
|
||||
const classes = useStyles(coords)
|
||||
|
||||
const matches = {
|
||||
'#code': 123,
|
||||
'#timestamp': format('HH:mm', new Date())
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.smsPreview}>
|
||||
<div className={classes.smsPreviewContainer}>
|
||||
<div className={classes.smsPreviewIcon}>
|
||||
<WhiteLogo width={22} height={22} />
|
||||
</div>
|
||||
<Paper className={classes.smsPreviewContent}>
|
||||
<P noMargin>{multiReplace(sms?.message, matches)}</P>
|
||||
</Paper>
|
||||
<Label3>{format('HH:mm', new Date())}</Label3>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const SMSNotices = () => {
|
||||
const classes = useStyles()
|
||||
|
||||
const [deleteDialog, setDeleteDialog] = useState(false)
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
const [selectedSMS, setSelectedSMS] = useState(null)
|
||||
const [previewOpen, setPreviewOpen] = useState(false)
|
||||
const [previewCoords, setPreviewCoords] = useState({ x: 0, y: 0 })
|
||||
const [errorMsg, setErrorMsg] = useState('')
|
||||
|
||||
const { data: messagesData, loading: messagesLoading } = useQuery(
|
||||
|
|
@ -88,8 +123,6 @@ const SMSNotices = () => {
|
|||
setDeleteDialog(false)
|
||||
}
|
||||
|
||||
console.log(messagesData)
|
||||
|
||||
const elements = [
|
||||
{
|
||||
header: 'Message name',
|
||||
|
|
@ -106,6 +139,7 @@ const SMSNotices = () => {
|
|||
view: it => (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setPreviewOpen(false)
|
||||
setSelectedSMS(it)
|
||||
setShowModal(true)
|
||||
}}>
|
||||
|
|
@ -129,13 +163,35 @@ const SMSNotices = () => {
|
|||
checked={it.enabled}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
header: '',
|
||||
width: 100,
|
||||
size: 'sm',
|
||||
textAlign: 'center',
|
||||
view: it => (
|
||||
<IconButton
|
||||
onClick={e => {
|
||||
setSelectedSMS(it)
|
||||
setPreviewCoords({
|
||||
x: e.currentTarget.getBoundingClientRect().right + 50,
|
||||
y:
|
||||
window.innerHeight -
|
||||
5 -
|
||||
e.currentTarget.getBoundingClientRect().bottom
|
||||
})
|
||||
setPreviewOpen(true)
|
||||
}}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={classes.header}>
|
||||
<H4>SMS Notices</H4>
|
||||
<H4>SMS notices</H4>
|
||||
</div>
|
||||
{showModal && (
|
||||
<CustomSMSModal
|
||||
|
|
@ -156,6 +212,7 @@ const SMSNotices = () => {
|
|||
}}
|
||||
errorMessage={errorMsg}
|
||||
/>
|
||||
{previewOpen && <SMSPreview sms={selectedSMS} coords={previewCoords} />}
|
||||
<DataTable
|
||||
emptyText="No SMS notices so far"
|
||||
elements={elements}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,35 @@ const styles = {
|
|||
},
|
||||
submit: {
|
||||
margin: [['auto', 0, 0, 'auto']]
|
||||
},
|
||||
smsPreview: {
|
||||
position: 'absolute',
|
||||
left: ({ x }) => x,
|
||||
bottom: ({ y }) => y,
|
||||
width: 350,
|
||||
overflow: 'visible'
|
||||
},
|
||||
smsPreviewContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-end',
|
||||
'& > *': {
|
||||
marginRight: 10
|
||||
}
|
||||
},
|
||||
smsPreviewIcon: {
|
||||
display: 'flex',
|
||||
width: 36,
|
||||
height: 36,
|
||||
borderRadius: 18,
|
||||
backgroundColor: '#16D6D3',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
smsPreviewContent: {
|
||||
width: 225,
|
||||
padding: 15,
|
||||
borderRadius: '15px 15px 15px 0px'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { makeStyles } from '@material-ui/core'
|
||||
import { makeStyles, Chip } from '@material-ui/core'
|
||||
import { Form, Formik, Field } from 'formik'
|
||||
import * as R from 'ramda'
|
||||
import React from 'react'
|
||||
|
|
@ -27,14 +27,14 @@ const prefill = {
|
|||
.required('The message content is required!')
|
||||
.trim()
|
||||
.test({
|
||||
name: 'has-code-tag',
|
||||
message: 'A #code tag is missing from the message!',
|
||||
name: 'has-code',
|
||||
message: 'The confirmation code is missing from the message!',
|
||||
exclusive: false,
|
||||
test: value => value?.match(/#code/g || [])?.length > 0
|
||||
})
|
||||
.test({
|
||||
name: 'has-single-code-tag',
|
||||
message: 'There should be a single #code tag!',
|
||||
name: 'has-single-code',
|
||||
message: 'There should be a single confirmation code!',
|
||||
exclusive: false,
|
||||
test: value => value?.match(/#code/g || [])?.length === 1
|
||||
})
|
||||
|
|
@ -43,22 +43,33 @@ 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
|
||||
})
|
||||
// .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
|
||||
// })
|
||||
}
|
||||
}
|
||||
|
||||
const CustomSMSModal = ({ showModal, onClose, sms, creationError, submit }) => {
|
||||
const chips = {
|
||||
smsCode: [{ code: '#code', display: 'Confirmation code', removable: false }],
|
||||
cashOutDispenseReady: []
|
||||
}
|
||||
|
||||
const SMSNoticesModal = ({
|
||||
showModal,
|
||||
onClose,
|
||||
sms,
|
||||
creationError,
|
||||
submit
|
||||
}) => {
|
||||
const classes = useStyles()
|
||||
|
||||
const initialValues = {
|
||||
|
|
@ -111,7 +122,7 @@ const CustomSMSModal = ({ showModal, onClose, sms, creationError, submit }) => {
|
|||
onSubmit={(values, errors, touched) =>
|
||||
handleSubmit(values, errors, touched)
|
||||
}>
|
||||
{({ values, errors, touched }) => (
|
||||
{({ values, errors, touched, setFieldValue }) => (
|
||||
<Form id="sms-notice" className={classes.form}>
|
||||
<Field
|
||||
name="message"
|
||||
|
|
@ -121,6 +132,22 @@ const CustomSMSModal = ({ showModal, onClose, sms, creationError, submit }) => {
|
|||
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]
|
||||
)}
|
||||
<div className={classes.footer}>
|
||||
{getErrorMsg(errors, touched, creationError) && (
|
||||
<ErrorMessage>
|
||||
|
|
@ -143,4 +170,4 @@ const CustomSMSModal = ({ showModal, onClose, sms, creationError, submit }) => {
|
|||
)
|
||||
}
|
||||
|
||||
export default CustomSMSModal
|
||||
export default SMSNoticesModal
|
||||
|
|
@ -17,7 +17,7 @@ import Notifications from 'src/pages/Notifications/Notifications'
|
|||
import CoinAtmRadar from 'src/pages/OperatorInfo/CoinATMRadar'
|
||||
import ContactInfo from 'src/pages/OperatorInfo/ContactInfo'
|
||||
import ReceiptPrinting from 'src/pages/OperatorInfo/ReceiptPrinting'
|
||||
import CustomSMS from 'src/pages/OperatorInfo/SMSNotices/SMSNotices'
|
||||
import SMSNotices from 'src/pages/OperatorInfo/SMSNotices/SMSNotices'
|
||||
import TermsConditions from 'src/pages/OperatorInfo/TermsConditions'
|
||||
import ServerLogs from 'src/pages/ServerLogs'
|
||||
import Services from 'src/pages/Services/Services'
|
||||
|
|
@ -174,11 +174,11 @@ const getLamassuRoutes = () => [
|
|||
component: ReceiptPrinting
|
||||
},
|
||||
{
|
||||
key: 'custom-sms',
|
||||
label: 'Custom SMS',
|
||||
route: '/settings/operator-info/custom-sms',
|
||||
key: 'sms-notices',
|
||||
label: 'SMS notices',
|
||||
route: '/settings/operator-info/sms-notices',
|
||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||
component: CustomSMS
|
||||
component: SMSNotices
|
||||
},
|
||||
{
|
||||
key: 'coin-atm-radar',
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import Notifications from 'src/pages/Notifications/Notifications'
|
|||
import CoinAtmRadar from 'src/pages/OperatorInfo/CoinATMRadar'
|
||||
import ContactInfo from 'src/pages/OperatorInfo/ContactInfo'
|
||||
import ReceiptPrinting from 'src/pages/OperatorInfo/ReceiptPrinting'
|
||||
import CustomSMS from 'src/pages/OperatorInfo/SMSNotices/SMSNotices'
|
||||
import SMSNotices from 'src/pages/OperatorInfo/SMSNotices/SMSNotices'
|
||||
import TermsConditions from 'src/pages/OperatorInfo/TermsConditions'
|
||||
import ServerLogs from 'src/pages/ServerLogs'
|
||||
import Services from 'src/pages/Services/Services'
|
||||
|
|
@ -169,11 +169,11 @@ const getPazuzRoutes = () => [
|
|||
component: ReceiptPrinting
|
||||
},
|
||||
{
|
||||
key: 'custom-sms',
|
||||
label: 'Custom SMS',
|
||||
route: '/settings/operator-info/custom-sms',
|
||||
key: 'sms-notices',
|
||||
label: 'SMS notices',
|
||||
route: '/settings/operator-info/sms-notices',
|
||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||
component: CustomSMS
|
||||
component: SMSNotices
|
||||
},
|
||||
{
|
||||
key: 'coin-atm-radar',
|
||||
|
|
|
|||
20
new-lamassu-admin/src/styling/icons/menu/logo-white.svg
Normal file
20
new-lamassu-admin/src/styling/icons/menu/logo-white.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 7 KiB |
Loading…
Add table
Add a link
Reference in a new issue