diff --git a/lib/custom-sms.js b/lib/custom-sms.js new file mode 100644 index 00000000..3ba6d9c0 --- /dev/null +++ b/lib/custom-sms.js @@ -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 +} diff --git a/lib/new-admin/graphql/resolvers/index.js b/lib/new-admin/graphql/resolvers/index.js index 033593d7..f93c2bc5 100644 --- a/lib/new-admin/graphql/resolvers/index.js +++ b/lib/new-admin/graphql/resolvers/index.js @@ -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, diff --git a/lib/new-admin/graphql/resolvers/sms.resolver.js b/lib/new-admin/graphql/resolvers/sms.resolver.js new file mode 100644 index 00000000..6936dfe8 --- /dev/null +++ b/lib/new-admin/graphql/resolvers/sms.resolver.js @@ -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 diff --git a/lib/new-admin/graphql/types/index.js b/lib/new-admin/graphql/types/index.js index 8745bdb3..e24322ef 100644 --- a/lib/new-admin/graphql/types/index.js +++ b/lib/new-admin/graphql/types/index.js @@ -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, diff --git a/lib/new-admin/graphql/types/sms.type.js b/lib/new-admin/graphql/types/sms.type.js new file mode 100644 index 00000000..c6528b54 --- /dev/null +++ b/lib/new-admin/graphql/types/sms.type.js @@ -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 diff --git a/migrations/1627518944902-custom-sms.js b/migrations/1627518944902-custom-sms.js new file mode 100644 index 00000000..ff19c1c5 --- /dev/null +++ b/migrations/1627518944902-custom-sms.js @@ -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() +} diff --git a/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js b/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js new file mode 100644 index 00000000..188e7081 --- /dev/null +++ b/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js @@ -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 => ( + { + console.log('edit') + }}> + + + ) + }, + { + header: 'Delete', + width: 120, + size: 'sm', + textAlign: 'center', + view: it => ( + { + console.log('delete') + }}> + + + ) + } + ] + + return ( + <> +
+

Custom SMS message

+ + setShowModal(true)}> + Add custom SMS + + +
+ {showModal && ( + setShowModal(false)} + machineOptions={machineOptions} + /> + )} + + + ) +} + +export default CustomSMS diff --git a/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMSModal.js b/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMSModal.js new file mode 100644 index 00000000..29bd732d --- /dev/null +++ b/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMSModal.js @@ -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 && ( + + +
+ + + +
+
+ )} + + ) +} + +export default CustomSMSModal diff --git a/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.styles.js b/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.styles.js index 9bdfe706..d69919e0 100644 --- a/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.styles.js +++ b/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.styles.js @@ -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 }, diff --git a/new-lamassu-admin/src/routing/lamassu.routes.js b/new-lamassu-admin/src/routing/lamassu.routes.js index 7394adf8..2626e30b 100644 --- a/new-lamassu-admin/src/routing/lamassu.routes.js +++ b/new-lamassu-admin/src/routing/lamassu.routes.js @@ -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', diff --git a/new-lamassu-admin/src/routing/pazuz.routes.js b/new-lamassu-admin/src/routing/pazuz.routes.js index a0d45648..eaed555c 100644 --- a/new-lamassu-admin/src/routing/pazuz.routes.js +++ b/new-lamassu-admin/src/routing/pazuz.routes.js @@ -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',