feat: add custom SMS screen
feat: custom messages database migration feat: populate custom SMS screen with database data feat: custom SMS creation modal fix: small fixes on styling
This commit is contained in:
parent
0035684040
commit
3480bbf8f7
11 changed files with 301 additions and 0 deletions
17
lib/custom-sms.js
Normal file
17
lib/custom-sms.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
const uuid = require('uuid')
|
||||
const db = require('./db')
|
||||
|
||||
const getCustomMessages = () => {
|
||||
const sql = `SELECT * FROM custom_messages`
|
||||
return db.any(sql)
|
||||
}
|
||||
|
||||
const createCustomMessage = (event, deviceId, message) => {
|
||||
const sql = `INSERT INTO custom_message (event, device_id, message) VALUES ($2, $3, $4)`
|
||||
return db.none(sql, [uuid.v4(), event, deviceId, message])
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getCustomMessages,
|
||||
createCustomMessage
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ const pairing = require('./pairing.resolver')
|
|||
const rates = require('./rates.resolver')
|
||||
const scalar = require('./scalar.resolver')
|
||||
const settings = require('./settings.resolver')
|
||||
const sms = require('./sms.resolver')
|
||||
const status = require('./status.resolver')
|
||||
const transaction = require('./transaction.resolver')
|
||||
const user = require('./users.resolver')
|
||||
|
|
@ -36,6 +37,7 @@ const resolvers = [
|
|||
rates,
|
||||
scalar,
|
||||
settings,
|
||||
sms,
|
||||
status,
|
||||
transaction,
|
||||
user,
|
||||
|
|
|
|||
12
lib/new-admin/graphql/resolvers/sms.resolver.js
Normal file
12
lib/new-admin/graphql/resolvers/sms.resolver.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
const customSms = require('../../../custom-sms')
|
||||
|
||||
const resolvers = {
|
||||
Query: {
|
||||
customMessages: () => customSms.getCustomMessages()
|
||||
},
|
||||
Mutation: {
|
||||
createCustomMessage: (...[, { event, deviceId, message }]) => customSms.createCustomMessage(event, deviceId, message)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = resolvers
|
||||
|
|
@ -15,6 +15,7 @@ const pairing = require('./pairing.type')
|
|||
const rates = require('./rates.type')
|
||||
const scalar = require('./scalar.type')
|
||||
const settings = require('./settings.type')
|
||||
const sms = require('./sms.type')
|
||||
const status = require('./status.type')
|
||||
const transaction = require('./transaction.type')
|
||||
const user = require('./users.type')
|
||||
|
|
@ -36,6 +37,7 @@ const types = [
|
|||
rates,
|
||||
scalar,
|
||||
settings,
|
||||
sms,
|
||||
status,
|
||||
transaction,
|
||||
user,
|
||||
|
|
|
|||
25
lib/new-admin/graphql/types/sms.type.js
Normal file
25
lib/new-admin/graphql/types/sms.type.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
const { gql } = require('apollo-server-express')
|
||||
|
||||
const typeDef = gql`
|
||||
type CustomMessage {
|
||||
id: ID!
|
||||
event: CustomMessageEvent!
|
||||
deviceId: String
|
||||
message: String!
|
||||
}
|
||||
|
||||
enum CustomMessageEvent {
|
||||
smsCode
|
||||
cashOutDispenseReady
|
||||
}
|
||||
|
||||
type Query {
|
||||
customMessages: [CustomMessage] @auth
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createCustomMessage(event: CustomMessageEvent!, deviceId: String, message: String!): CustomMessage @auth
|
||||
}
|
||||
`
|
||||
|
||||
module.exports = typeDef
|
||||
20
migrations/1627518944902-custom-sms.js
Normal file
20
migrations/1627518944902-custom-sms.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
var db = require('./db')
|
||||
|
||||
exports.up = function (next) {
|
||||
var sql = [
|
||||
`CREATE TYPE custom_message_event AS ENUM('sms_code', 'cash_out_dispense_ready')`,
|
||||
`CREATE TABLE custom_messages (
|
||||
id UUID PRIMARY KEY,
|
||||
event custom_message_event NOT NULL,
|
||||
device_id TEXT REFERENCES devices(device_id),
|
||||
message TEXT NOT NULL
|
||||
)`,
|
||||
`CREATE UNIQUE INDEX uq_custom_message_per_device ON custom_messages (event, device_id)`
|
||||
]
|
||||
|
||||
db.multi(sql, next)
|
||||
}
|
||||
|
||||
exports.down = function (next) {
|
||||
next()
|
||||
}
|
||||
127
new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js
Normal file
127
new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import { useQuery } from '@apollo/react-hooks'
|
||||
import { makeStyles, Box } from '@material-ui/core'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import { Link, IconButton } from 'src/components/buttons'
|
||||
import DataTable from 'src/components/tables/DataTable'
|
||||
import { H4 } from 'src/components/typography'
|
||||
import { ReactComponent as DeleteIcon } from 'src/styling/icons/action/delete/enabled.svg'
|
||||
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
|
||||
|
||||
import { global } from '../OperatorInfo.styles'
|
||||
|
||||
import CustomSMSModal from './CustomSMSModal'
|
||||
|
||||
const useStyles = makeStyles(global)
|
||||
|
||||
const GET_CUSTOM_MESSAGES = gql`
|
||||
query customMessages {
|
||||
customMessages {
|
||||
id
|
||||
event
|
||||
deviceId
|
||||
message
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const GET_MACHINES = gql`
|
||||
{
|
||||
machines {
|
||||
name
|
||||
deviceId
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const CustomSMS = () => {
|
||||
const classes = useStyles()
|
||||
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
const { data: messagesData, loading: messagesLoading } = useQuery(
|
||||
GET_CUSTOM_MESSAGES
|
||||
)
|
||||
const { data: machinesData, loading: machinesLoading } = useQuery(
|
||||
GET_MACHINES
|
||||
)
|
||||
|
||||
const loading = messagesLoading && machinesLoading
|
||||
|
||||
const machineOptions =
|
||||
machinesData &&
|
||||
R.map(
|
||||
it => ({ code: it.deviceId, display: it.name }),
|
||||
R.path(['machines'])(machinesData)
|
||||
)
|
||||
|
||||
const elements = [
|
||||
{
|
||||
header: 'Message name',
|
||||
width: 400,
|
||||
size: 'sm',
|
||||
textAlign: 'left',
|
||||
view: it => it.event
|
||||
},
|
||||
{
|
||||
header: 'Edit',
|
||||
width: 120,
|
||||
size: 'sm',
|
||||
textAlign: 'center',
|
||||
view: it => (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
console.log('edit')
|
||||
}}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
)
|
||||
},
|
||||
{
|
||||
header: 'Delete',
|
||||
width: 120,
|
||||
size: 'sm',
|
||||
textAlign: 'center',
|
||||
view: it => (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
console.log('delete')
|
||||
}}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={classes.headerWithLink}>
|
||||
<H4>Custom SMS message</H4>
|
||||
<Box
|
||||
className={classes.tableWidth}
|
||||
display="flex"
|
||||
justifyContent="flex-end">
|
||||
<Link color="primary" onClick={() => setShowModal(true)}>
|
||||
Add custom SMS
|
||||
</Link>
|
||||
</Box>
|
||||
</div>
|
||||
{showModal && (
|
||||
<CustomSMSModal
|
||||
showModal={showModal}
|
||||
onClose={() => setShowModal(false)}
|
||||
machineOptions={machineOptions}
|
||||
/>
|
||||
)}
|
||||
<DataTable
|
||||
emptyText="No custom SMS so far"
|
||||
elements={elements}
|
||||
loading={loading}
|
||||
data={R.path(['customMessages'])(messagesData)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default CustomSMS
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
import { Form, Formik, Field } from 'formik'
|
||||
import React from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
import Modal from 'src/components/Modal'
|
||||
import { Autocomplete } from 'src/components/inputs/formik'
|
||||
|
||||
const EVENT_OPTIONS = [
|
||||
{ code: 'sms_code', display: 'On SMS confirmation code' },
|
||||
{ code: 'cash_out_dispense_ready', display: 'Cash out dispense ready' }
|
||||
]
|
||||
|
||||
const CustomSMSModal = ({
|
||||
showModal,
|
||||
onClose,
|
||||
customMessage,
|
||||
machineOptions
|
||||
}) => {
|
||||
const initialValues = {
|
||||
event: '',
|
||||
device: '',
|
||||
message: ''
|
||||
}
|
||||
|
||||
const validationSchema = {
|
||||
event: Yup.string().required('An event is required!'),
|
||||
device: Yup.string(),
|
||||
message: Yup.string()
|
||||
.required('The message content is required!')
|
||||
.trim()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{showModal && (
|
||||
<Modal
|
||||
title="Add custom SMS"
|
||||
closeOnBackdropClick={true}
|
||||
width={600}
|
||||
height={500}
|
||||
open={true}
|
||||
handleClose={onClose}>
|
||||
<Formik
|
||||
validateOnBlur={false}
|
||||
validateOnChange={false}
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}>
|
||||
<Form id="custom-sms">
|
||||
<Field
|
||||
name="event"
|
||||
fullWidth
|
||||
options={EVENT_OPTIONS}
|
||||
labelProp="display"
|
||||
valueProp="code"
|
||||
component={Autocomplete}
|
||||
/>
|
||||
<Field
|
||||
name="device"
|
||||
fullWidth
|
||||
options={machineOptions}
|
||||
labelProp="display"
|
||||
valueProp="code"
|
||||
component={Autocomplete}
|
||||
/>
|
||||
</Form>
|
||||
</Formik>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default CustomSMSModal
|
||||
|
|
@ -10,6 +10,13 @@ const global = {
|
|||
position: 'relative',
|
||||
flex: 'wrap'
|
||||
},
|
||||
headerWithLink: {
|
||||
display: 'flex',
|
||||
position: 'relative',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
width: 640
|
||||
},
|
||||
section: {
|
||||
marginBottom: 52
|
||||
},
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import MachineStatus from 'src/pages/Maintenance/MachineStatus'
|
|||
import Notifications from 'src/pages/Notifications/Notifications'
|
||||
import CoinAtmRadar from 'src/pages/OperatorInfo/CoinATMRadar'
|
||||
import ContactInfo from 'src/pages/OperatorInfo/ContactInfo'
|
||||
import CustomSMS from 'src/pages/OperatorInfo/CustomSMS/CustomSMS'
|
||||
import ReceiptPrinting from 'src/pages/OperatorInfo/ReceiptPrinting'
|
||||
import TermsConditions from 'src/pages/OperatorInfo/TermsConditions'
|
||||
import ServerLogs from 'src/pages/ServerLogs'
|
||||
|
|
@ -172,6 +173,13 @@ const getLamassuRoutes = () => [
|
|||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||
component: ReceiptPrinting
|
||||
},
|
||||
{
|
||||
key: 'custom-sms',
|
||||
label: 'Custom SMS',
|
||||
route: '/settings/operator-info/custom-sms',
|
||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||
component: CustomSMS
|
||||
},
|
||||
{
|
||||
key: 'coin-atm-radar',
|
||||
label: 'Coin ATM Radar',
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import MachineStatus from 'src/pages/Maintenance/MachineStatus'
|
|||
import Notifications from 'src/pages/Notifications/Notifications'
|
||||
import CoinAtmRadar from 'src/pages/OperatorInfo/CoinATMRadar'
|
||||
import ContactInfo from 'src/pages/OperatorInfo/ContactInfo'
|
||||
import CustomSMS from 'src/pages/OperatorInfo/CustomSMS/CustomSMS'
|
||||
import ReceiptPrinting from 'src/pages/OperatorInfo/ReceiptPrinting'
|
||||
import TermsConditions from 'src/pages/OperatorInfo/TermsConditions'
|
||||
import ServerLogs from 'src/pages/ServerLogs'
|
||||
|
|
@ -167,6 +168,13 @@ const getPazuzRoutes = () => [
|
|||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||
component: ReceiptPrinting
|
||||
},
|
||||
{
|
||||
key: 'custom-sms',
|
||||
label: 'Custom SMS',
|
||||
route: '/settings/operator-info/custom-sms',
|
||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||
component: CustomSMS
|
||||
},
|
||||
{
|
||||
key: 'coin-atm-radar',
|
||||
label: 'Coin ATM Radar',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue