Feat: add notification center row in notification settings table
This commit is contained in:
parent
1ab4b68168
commit
34f2b84fe2
8 changed files with 109 additions and 69 deletions
|
|
@ -3,7 +3,8 @@ const axios = require('axios')
|
||||||
|
|
||||||
const db = require('./db')
|
const db = require('./db')
|
||||||
const pairing = require('./pairing')
|
const pairing = require('./pairing')
|
||||||
const notifier = require('./notifier')
|
const checkPings = require('./notifier').checkPings
|
||||||
|
const checkStuckScreen = require('./notifier').checkStuckScreen
|
||||||
const dbm = require('./postgresql_interface')
|
const dbm = require('./postgresql_interface')
|
||||||
const configManager = require('./new-config-manager')
|
const configManager = require('./new-config-manager')
|
||||||
const settingsLoader = require('./new-settings-loader')
|
const settingsLoader = require('./new-settings-loader')
|
||||||
|
|
@ -41,7 +42,7 @@ function getMachineNames (config) {
|
||||||
|
|
||||||
return Promise.all([getMachines(), getConfig(config)])
|
return Promise.all([getMachines(), getConfig(config)])
|
||||||
.then(([machines, config]) => Promise.all(
|
.then(([machines, config]) => Promise.all(
|
||||||
[machines, notifier.checkPings(machines), dbm.machineEvents(), config]
|
[machines, checkPings(machines), dbm.machineEvents(), config]
|
||||||
))
|
))
|
||||||
.then(([machines, pings, events, config]) => {
|
.then(([machines, pings, events, config]) => {
|
||||||
const getStatus = (ping, stuck) => {
|
const getStatus = (ping, stuck) => {
|
||||||
|
|
@ -60,7 +61,7 @@ function getMachineNames (config) {
|
||||||
const statuses = [
|
const statuses = [
|
||||||
getStatus(
|
getStatus(
|
||||||
_.first(pings[r.deviceId]),
|
_.first(pings[r.deviceId]),
|
||||||
_.first(notifier.checkStuckScreen(events, r.name))
|
_.first(checkStuckScreen(events, r.name))
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ const ALERT_SEND_INTERVAL = T.hour
|
||||||
|
|
||||||
const NOTIFICATION_TYPES = {
|
const NOTIFICATION_TYPES = {
|
||||||
HIGH_VALUE_TX: 'highValueTransaction',
|
HIGH_VALUE_TX: 'highValueTransaction',
|
||||||
|
NORMAL_VALUE_TX: 'transaction',
|
||||||
FIAT_BALANCE: 'fiatBalance',
|
FIAT_BALANCE: 'fiatBalance',
|
||||||
CRYPTO_BALANCE: 'cryptoBalance',
|
CRYPTO_BALANCE: 'cryptoBalance',
|
||||||
COMPLIANCE: 'compliance',
|
COMPLIANCE: 'compliance',
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ const _ = require('lodash/fp')
|
||||||
|
|
||||||
const configManager = require('../new-config-manager')
|
const configManager = require('../new-config-manager')
|
||||||
const logger = require('../logger')
|
const logger = require('../logger')
|
||||||
const machineLoader = require('../machine-loader')
|
|
||||||
const queries = require('./queries')
|
const queries = require('./queries')
|
||||||
const settingsLoader = require('../new-settings-loader')
|
const settingsLoader = require('../new-settings-loader')
|
||||||
const customers = require('../customers')
|
const customers = require('../customers')
|
||||||
|
|
@ -10,14 +9,17 @@ const customers = require('../customers')
|
||||||
const utils = require('./utils')
|
const utils = require('./utils')
|
||||||
const emailFuncs = require('./email')
|
const emailFuncs = require('./email')
|
||||||
const smsFuncs = require('./sms')
|
const smsFuncs = require('./sms')
|
||||||
|
const codes = require('./codes')
|
||||||
const { STALE, STALE_STATE, PING } = require('./codes')
|
const { STALE, STALE_STATE, PING } = require('./codes')
|
||||||
|
|
||||||
const { NOTIFICATION_TYPES: {
|
const { NOTIFICATION_TYPES: {
|
||||||
HIGH_VALUE_TX,
|
HIGH_VALUE_TX,
|
||||||
|
NORMAL_VALUE_TX,
|
||||||
FIAT_BALANCE,
|
FIAT_BALANCE,
|
||||||
CRYPTO_BALANCE,
|
CRYPTO_BALANCE,
|
||||||
COMPLIANCE,
|
COMPLIANCE,
|
||||||
ERROR }
|
ERROR }
|
||||||
} = require('./codes')
|
} = codes
|
||||||
|
|
||||||
function buildMessage (alerts, notifications) {
|
function buildMessage (alerts, notifications) {
|
||||||
const smsEnabled = utils.isActive(notifications.sms)
|
const smsEnabled = utils.isActive(notifications.sms)
|
||||||
|
|
@ -50,7 +52,7 @@ function checkNotification (plugins) {
|
||||||
|
|
||||||
return getAlerts(plugins)
|
return getAlerts(plugins)
|
||||||
.then(alerts => {
|
.then(alerts => {
|
||||||
errorAlertsNotify(alerts)
|
notifyIfActive('errors', alerts).catch(console.error)
|
||||||
const currentAlertFingerprint = utils.buildAlertFingerprint(
|
const currentAlertFingerprint = utils.buildAlertFingerprint(
|
||||||
alerts,
|
alerts,
|
||||||
notifications
|
notifications
|
||||||
|
|
@ -81,7 +83,7 @@ function getAlerts (plugins) {
|
||||||
queries.machineEvents(),
|
queries.machineEvents(),
|
||||||
plugins.getMachineNames()
|
plugins.getMachineNames()
|
||||||
]).then(([balances, events, devices]) => {
|
]).then(([balances, events, devices]) => {
|
||||||
balancesNotify(balances)
|
notifyIfActive('balance', balances).catch(console.error)
|
||||||
return buildAlerts(checkPings(devices), balances, events, devices)
|
return buildAlerts(checkPings(devices), balances, events, devices)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -136,17 +138,24 @@ function checkStuckScreen (deviceEvents, machineName) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function notifCenterTransactionNotify (isHighValue, direction, fiat, fiatCode, deviceId, cryptoAddress) {
|
||||||
|
const messageSuffix = isHighValue ? 'High value' : ''
|
||||||
|
const message = `${messageSuffix} ${fiat} ${fiatCode} ${direction} transaction`
|
||||||
|
const detailB = utils.buildDetail({ deviceId: deviceId, direction, fiat, fiatCode, cryptoAddress })
|
||||||
|
return queries.addNotification(isHighValue ? HIGH_VALUE_TX : NORMAL_VALUE_TX, message, detailB)
|
||||||
|
}
|
||||||
|
|
||||||
function transactionNotify (tx, rec) {
|
function transactionNotify (tx, rec) {
|
||||||
return settingsLoader.loadLatest().then(settings => {
|
return settingsLoader.loadLatest().then(settings => {
|
||||||
const notifSettings = configManager.getGlobalNotifications(settings.config)
|
const notifSettings = configManager.getGlobalNotifications(settings.config)
|
||||||
const highValueTx = tx.fiat.gt(notifSettings.highValueTransaction || Infinity)
|
const highValueTx = tx.fiat.gt(notifSettings.highValueTransaction || Infinity)
|
||||||
const isCashOut = tx.direction === 'cashOut'
|
const isCashOut = tx.direction === 'cashOut'
|
||||||
// high value tx on database
|
|
||||||
if (highValueTx && (tx.direction === 'cashIn' || (tx.direction === 'cashOut' && rec.isRedemption))) {
|
// for notification center
|
||||||
const direction = tx.direction === 'cashOut' ? 'cash-out' : 'cash-in'
|
const directionDisplay = tx.direction === 'cashOut' ? 'cash-out' : 'cash-in'
|
||||||
const message = `${tx.fiat} ${tx.fiatCode} ${direction} transaction`
|
const readyToNotify = tx.direction === 'cashIn' || (tx.direction === 'cashOut' && rec.isRedemption)
|
||||||
const detailB = utils.buildDetail({ deviceId: tx.deviceId, direction, fiat: tx.fiat, fiatCode: tx.fiatCode, cryptoAddress: tx.toAddress })
|
if (readyToNotify) {
|
||||||
queries.addNotification(HIGH_VALUE_TX, message, detailB)
|
notifyIfActive('transactions', highValueTx, directionDisplay, tx.fiat, tx.fiatCode, tx.deviceId, tx.toAddress).catch(console.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// alert through sms or email any transaction or high value transaction, if SMS || email alerts are enabled
|
// alert through sms or email any transaction or high value transaction, if SMS || email alerts are enabled
|
||||||
|
|
@ -161,7 +170,7 @@ function transactionNotify (tx, rec) {
|
||||||
if (!zeroConf && rec.isRedemption) return sendRedemptionMessage(tx.id, rec.error)
|
if (!zeroConf && rec.isRedemption) return sendRedemptionMessage(tx.id, rec.error)
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
machineLoader.getMachineName(tx.deviceId),
|
queries.getMachineName(tx.deviceId),
|
||||||
customerPromise
|
customerPromise
|
||||||
]).then(([machineName, customer]) => {
|
]).then(([machineName, customer]) => {
|
||||||
return utils.buildTransactionMessage(tx, rec, highValueTx, machineName, customer)
|
return utils.buildTransactionMessage(tx, rec, highValueTx, machineName, customer)
|
||||||
|
|
@ -356,6 +365,23 @@ const customerComplianceNotify = (customer, deviceId, code, days = null) => {
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const notificationCenterFunctions = {
|
||||||
|
'compliance': customerComplianceNotify,
|
||||||
|
'balance': balancesNotify,
|
||||||
|
'errors': errorAlertsNotify,
|
||||||
|
'transactions': notifCenterTransactionNotify
|
||||||
|
}
|
||||||
|
|
||||||
|
// for notification center, check if type of notification is active before calling the respective notify function
|
||||||
|
const notifyIfActive = (type, ...args) => {
|
||||||
|
return settingsLoader.loadLatest().then(settings => {
|
||||||
|
const notificationSettings = configManager.getGlobalNotifications(settings.config).notificationCenter
|
||||||
|
if (!notificationCenterFunctions[type]) return Promise.reject(new Error(`Notification of type ${type} does not exist`))
|
||||||
|
if (!(notificationSettings.active && notificationSettings[type])) return Promise.resolve()
|
||||||
|
return notificationCenterFunctions[type](...args)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
transactionNotify,
|
transactionNotify,
|
||||||
checkNotification,
|
checkNotification,
|
||||||
|
|
@ -363,6 +389,6 @@ module.exports = {
|
||||||
checkStuckScreen,
|
checkStuckScreen,
|
||||||
sendRedemptionMessage,
|
sendRedemptionMessage,
|
||||||
blacklistNotify,
|
blacklistNotify,
|
||||||
customerComplianceNotify,
|
clearBlacklistNotification,
|
||||||
clearBlacklistNotification
|
notifyIfActive
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,75 +14,82 @@ compliance - notifications related to warnings triggered by compliance settings
|
||||||
error - notifications related to errors
|
error - notifications related to errors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
function getMachineName (machineId) {
|
||||||
|
const sql = 'SELECT * FROM devices WHERE device_id=$1'
|
||||||
|
return db.oneOrNone(sql, [machineId])
|
||||||
|
.then(it => it.name)
|
||||||
|
}
|
||||||
|
|
||||||
const addNotification = (type, message, detail) => {
|
const addNotification = (type, message, detail) => {
|
||||||
const sql = `INSERT INTO notifications (id, type, message, detail) values ($1, $2, $3, $4)`
|
const sql = `INSERT INTO notifications (id, type, message, detail) values ($1, $2, $3, $4)`
|
||||||
return db.oneOrNone(sql, [uuidv4(), type, message, detail])
|
return db.oneOrNone(sql, [uuidv4(), type, message, detail])
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAllValidNotifications = (type) => {
|
const getAllValidNotifications = (type) => {
|
||||||
const sql = `SELECT * FROM notifications WHERE type = $1 AND valid = 't'`
|
const sql = `SELECT * FROM notifications WHERE type = $1 AND valid = 't'`
|
||||||
return db.any(sql, [type])
|
return db.any(sql, [type])
|
||||||
}
|
}
|
||||||
|
|
||||||
const invalidateNotification = (detail, type) => {
|
const invalidateNotification = (detail, type) => {
|
||||||
detail = _.omitBy(_.isEmpty, detail)
|
detail = _.omitBy(_.isEmpty, detail)
|
||||||
const sql = `UPDATE notifications SET valid = 'f', read = 't' WHERE valid = 't' AND type = $1 AND detail::jsonb @> $2::jsonb`
|
const sql = `UPDATE notifications SET valid = 'f', read = 't' WHERE valid = 't' AND type = $1 AND detail::jsonb @> $2::jsonb`
|
||||||
return db.none(sql, [type, detail])
|
return db.none(sql, [type, detail])
|
||||||
}
|
}
|
||||||
|
|
||||||
const batchInvalidate = (ids) => {
|
const batchInvalidate = (ids) => {
|
||||||
const formattedIds = _.map(pgp.as.text, ids).join(',')
|
const formattedIds = _.map(pgp.as.text, ids).join(',')
|
||||||
const sql = `UPDATE notifications SET valid = 'f', read = 't' WHERE id IN ($1^)`
|
const sql = `UPDATE notifications SET valid = 'f', read = 't' WHERE id IN ($1^)`
|
||||||
return db.none(sql, [formattedIds])
|
return db.none(sql, [formattedIds])
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearBlacklistNotification = (cryptoCode, cryptoAddress) => {
|
const clearBlacklistNotification = (cryptoCode, cryptoAddress) => {
|
||||||
const sql = `UPDATE notifications SET valid = 'f', read = 't' WHERE type = 'compliance' AND detail->>'cryptoCode' = $1 AND detail->>'cryptoAddress' = $2 AND (detail->>'code' = 'BLOCKED' OR detail->>'code' = 'REUSED')`
|
const sql = `UPDATE notifications SET valid = 'f', read = 't' WHERE type = 'compliance' AND detail->>'cryptoCode' = $1 AND detail->>'cryptoAddress' = $2 AND (detail->>'code' = 'BLOCKED' OR detail->>'code' = 'REUSED')`
|
||||||
return db.none(sql, [cryptoCode, cryptoAddress])
|
return db.none(sql, [cryptoCode, cryptoAddress])
|
||||||
}
|
}
|
||||||
|
|
||||||
const getValidNotifications = (type, detail) => {
|
const getValidNotifications = (type, detail) => {
|
||||||
const sql = `SELECT * FROM notifications WHERE type = $1 AND valid = 't' AND detail @> $2`
|
const sql = `SELECT * FROM notifications WHERE type = $1 AND valid = 't' AND detail @> $2`
|
||||||
return db.any(sql, [type, detail])
|
return db.any(sql, [type, detail])
|
||||||
}
|
}
|
||||||
|
|
||||||
const getNotifications = () => {
|
const getNotifications = () => {
|
||||||
const sql = `SELECT * FROM notifications ORDER BY created DESC`
|
const sql = `SELECT * FROM notifications ORDER BY created DESC`
|
||||||
return db.any(sql)
|
return db.any(sql)
|
||||||
}
|
}
|
||||||
|
|
||||||
const markAsRead = (id) => {
|
const markAsRead = (id) => {
|
||||||
const sql = `UPDATE notifications SET read = 't' WHERE id = $1`
|
const sql = `UPDATE notifications SET read = 't' WHERE id = $1`
|
||||||
return db.none(sql, [id])
|
return db.none(sql, [id])
|
||||||
}
|
}
|
||||||
|
|
||||||
const markAllAsRead = () => {
|
const markAllAsRead = () => {
|
||||||
const sql = `UPDATE notifications SET read = 't'`
|
const sql = `UPDATE notifications SET read = 't'`
|
||||||
return db.none(sql)
|
return db.none(sql)
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasUnreadNotifications = () => {
|
const hasUnreadNotifications = () => {
|
||||||
const sql = `SELECT EXISTS (SELECT 1 FROM notifications WHERE read = 'f' LIMIT 1)`
|
const sql = `SELECT EXISTS (SELECT 1 FROM notifications WHERE read = 'f' LIMIT 1)`
|
||||||
return db.oneOrNone(sql).then(res => res.exists)
|
return db.oneOrNone(sql).then(res => res.exists)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAlerts = () => {
|
const getAlerts = () => {
|
||||||
const types = ['fiatBalance', 'cryptoBalance', 'error']
|
const types = ['fiatBalance', 'cryptoBalance', 'error']
|
||||||
const sql = `SELECT * FROM notifications WHERE valid = 't' AND type IN ($1:list) ORDER BY created DESC`
|
const sql = `SELECT * FROM notifications WHERE valid = 't' AND type IN ($1:list) ORDER BY created DESC`
|
||||||
return db.any(sql, [types])
|
return db.any(sql, [types])
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
machineEvents: dbm.machineEvents,
|
machineEvents: dbm.machineEvents,
|
||||||
addNotification,
|
addNotification,
|
||||||
getAllValidNotifications,
|
getAllValidNotifications,
|
||||||
invalidateNotification,
|
invalidateNotification,
|
||||||
batchInvalidate,
|
batchInvalidate,
|
||||||
clearBlacklistNotification,
|
clearBlacklistNotification,
|
||||||
getValidNotifications,
|
getValidNotifications,
|
||||||
getNotifications,
|
getNotifications,
|
||||||
markAsRead,
|
markAsRead,
|
||||||
markAllAsRead,
|
markAllAsRead,
|
||||||
hasUnreadNotifications,
|
hasUnreadNotifications,
|
||||||
getAlerts
|
getAlerts,
|
||||||
|
getMachineName
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -343,7 +343,7 @@ function triggerBlock (req, res, next) {
|
||||||
|
|
||||||
customers.update(id, { authorizedOverride: 'blocked' })
|
customers.update(id, { authorizedOverride: 'blocked' })
|
||||||
.then(customer => {
|
.then(customer => {
|
||||||
notifier.customerComplianceNotify(customer, req.deviceId, 'BLOCKED')
|
notifier.notifyIfActive('compliance', customer, req.deviceId, 'BLOCKED').catch(console.error)
|
||||||
return respond(req, res, { customer })
|
return respond(req, res, { customer })
|
||||||
})
|
})
|
||||||
.catch(next)
|
.catch(next)
|
||||||
|
|
@ -362,7 +362,7 @@ function triggerSuspend (req, res, next) {
|
||||||
date.setDate(date.getDate() + days);
|
date.setDate(date.getDate() + days);
|
||||||
customers.update(id, { suspendedUntil: date })
|
customers.update(id, { suspendedUntil: date })
|
||||||
.then(customer => {
|
.then(customer => {
|
||||||
notifier.customerComplianceNotify(customer, req.deviceId, 'SUSPENDED', days)
|
notifier.notifyIfActive('compliance', customer, req.deviceId, 'SUSPENDED', days).catch(console.error)
|
||||||
return respond(req, res, { customer })
|
return respond(req, res, { customer })
|
||||||
})
|
})
|
||||||
.catch(next)
|
.catch(next)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ const singleQuotify = (item) => `'${item}'`
|
||||||
|
|
||||||
var types = [
|
var types = [
|
||||||
'highValueTransaction',
|
'highValueTransaction',
|
||||||
|
'transaction',
|
||||||
'fiatBalance',
|
'fiatBalance',
|
||||||
'cryptoBalance',
|
'cryptoBalance',
|
||||||
'compliance',
|
'compliance',
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import Grid from '@material-ui/core/Grid'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import prettyMs from 'pretty-ms'
|
import prettyMs from 'pretty-ms'
|
||||||
|
import * as R from 'ramda'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { Label1, Label2, TL2 } from 'src/components/typography'
|
import { Label1, Label2, TL2 } from 'src/components/typography'
|
||||||
|
|
@ -14,6 +15,7 @@ import styles from './NotificationCenter.styles'
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const types = {
|
const types = {
|
||||||
|
transaction: { display: 'Transactions', icon: <Transaction /> },
|
||||||
highValueTransaction: { display: 'Transactions', icon: <Transaction /> },
|
highValueTransaction: { display: 'Transactions', icon: <Transaction /> },
|
||||||
fiatBalance: { display: 'Maintenance', icon: <Wrench /> },
|
fiatBalance: { display: 'Maintenance', icon: <Wrench /> },
|
||||||
cryptoBalance: { display: 'Maintenance', icon: <Wrench /> },
|
cryptoBalance: { display: 'Maintenance', icon: <Wrench /> },
|
||||||
|
|
@ -34,15 +36,18 @@ const NotificationRow = ({
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
const buildType = () => {
|
const typeDisplay = R.path([type, 'display'])(types) ?? null
|
||||||
return types[type].display
|
const icon = R.path([type, 'icon'])(types) ?? <Wrench />
|
||||||
}
|
const age = prettyMs(new Date().getTime() - new Date(created).getTime(), {
|
||||||
|
compact: true,
|
||||||
const buildAge = () => {
|
verbose: true
|
||||||
const createdDate = new Date(created)
|
})
|
||||||
const interval = +new Date() - createdDate
|
const notificationTitle =
|
||||||
return prettyMs(interval, { compact: true, verbose: true })
|
typeDisplay && deviceName
|
||||||
}
|
? `${typeDisplay} - ${deviceName}`
|
||||||
|
: !typeDisplay && deviceName
|
||||||
|
? `${deviceName}`
|
||||||
|
: `${typeDisplay}`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid
|
<Grid
|
||||||
|
|
@ -52,21 +57,19 @@ const NotificationRow = ({
|
||||||
!read && valid ? classes.unread : ''
|
!read && valid ? classes.unread : ''
|
||||||
)}>
|
)}>
|
||||||
<Grid item xs={2} className={classes.notificationRowIcon}>
|
<Grid item xs={2} className={classes.notificationRowIcon}>
|
||||||
{types[type].icon}
|
{icon}
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item container xs={7} direction="row">
|
<Grid item container xs={7} direction="row">
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Label2 className={classes.notificationTitle}>
|
<Label2 className={classes.notificationTitle}>
|
||||||
{`${buildType()} ${deviceName ? '- ' + deviceName : ''}`}
|
{notificationTitle}
|
||||||
</Label2>
|
</Label2>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<TL2 className={classes.notificationBody}>{message}</TL2>
|
<TL2 className={classes.notificationBody}>{message}</TL2>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Label1 className={classes.notificationSubtitle}>
|
<Label1 className={classes.notificationSubtitle}>{age}</Label1>
|
||||||
{buildAge(created)}
|
|
||||||
</Label1>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={3} style={{ zIndex: 1 }}>
|
<Grid item xs={3} style={{ zIndex: 1 }}>
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,7 @@ const Setup = ({ wizard, forceDisable }) => {
|
||||||
<TBody>
|
<TBody>
|
||||||
<Row namespace="email" forceDisable={forceDisable} />
|
<Row namespace="email" forceDisable={forceDisable} />
|
||||||
<Row namespace="sms" forceDisable={forceDisable} />
|
<Row namespace="sms" forceDisable={forceDisable} />
|
||||||
|
<Row namespace="notificationCenter" forceDisable={forceDisable} />
|
||||||
</TBody>
|
</TBody>
|
||||||
</Table>
|
</Table>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue