Merge branch 'dev' into backport/analytics-adjustments
This commit is contained in:
commit
e30c0e0aab
90 changed files with 801 additions and 287 deletions
|
|
@ -150,16 +150,15 @@ function postProcess (r, pi, isBlacklisted, addressReuse, walletScore) {
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
// Important: We don't know what kind of error this is
|
// Important: We don't know what kind of error this is
|
||||||
// so not safe to assume that funds weren't sent.
|
// so not safe to assume that funds weren't sent.
|
||||||
// Therefore, don't set sendPending to false except for
|
|
||||||
// errors (like InsufficientFundsError) that are guaranteed
|
// Setting sendPending to true ensures that the transaction gets
|
||||||
// not to send.
|
// silently terminated and no retries are done
|
||||||
const sendPending = err.name !== 'InsufficientFundsError'
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sendTime: 'now()^',
|
sendTime: 'now()^',
|
||||||
error: err.message,
|
error: err.message,
|
||||||
errorCode: err.name,
|
errorCode: err.name,
|
||||||
sendPending
|
sendPending: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(sendRec => {
|
.then(sendRec => {
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ const mapValuesWithKey = _.mapValues.convert({cap: false})
|
||||||
|
|
||||||
function convertBigNumFields (obj) {
|
function convertBigNumFields (obj) {
|
||||||
const convert = (value, key) => {
|
const convert = (value, key) => {
|
||||||
if (_.includes(key, [ 'cryptoAtoms', 'receivedCryptoAtoms', 'fiat' ])) {
|
if (_.includes(key, [ 'cryptoAtoms', 'receivedCryptoAtoms', 'fiat', 'fixedFee', 'fixedFeeCrypto' ])) {
|
||||||
return value.toString()
|
return value.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ function mapCoin (rates, deviceId, settings, cryptoCode) {
|
||||||
const cashInFee = showCommissions ? commissions.cashIn / 100 : null
|
const cashInFee = showCommissions ? commissions.cashIn / 100 : null
|
||||||
const cashOutFee = showCommissions ? commissions.cashOut / 100 : null
|
const cashOutFee = showCommissions ? commissions.cashOut / 100 : null
|
||||||
const cashInFixedFee = showCommissions ? commissions.fixedFee : null
|
const cashInFixedFee = showCommissions ? commissions.fixedFee : null
|
||||||
|
const cashOutFixedFee = showCommissions ? commissions.cashOutFixedFee : null
|
||||||
const cashInRate = showCommissions ? _.invoke('cashIn.toNumber', buildedRates) : null
|
const cashInRate = showCommissions ? _.invoke('cashIn.toNumber', buildedRates) : null
|
||||||
const cashOutRate = showCommissions ? _.invoke('cashOut.toNumber', buildedRates) : null
|
const cashOutRate = showCommissions ? _.invoke('cashOut.toNumber', buildedRates) : null
|
||||||
|
|
||||||
|
|
@ -37,6 +38,7 @@ function mapCoin (rates, deviceId, settings, cryptoCode) {
|
||||||
cashInFee,
|
cashInFee,
|
||||||
cashOutFee,
|
cashOutFee,
|
||||||
cashInFixedFee,
|
cashInFixedFee,
|
||||||
|
cashOutFixedFee,
|
||||||
cashInRate,
|
cashInRate,
|
||||||
cashOutRate
|
cashOutRate
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ function getWithEmail (email) {
|
||||||
*
|
*
|
||||||
* @param {string} id Customer's id
|
* @param {string} id Customer's id
|
||||||
* @param {object} data Fields to update
|
* @param {object} data Fields to update
|
||||||
* @param {string} Acting user's token
|
* @param {string} userToken Acting user's token
|
||||||
*
|
*
|
||||||
* @returns {Promise} Newly updated Customer
|
* @returns {Promise} Newly updated Customer
|
||||||
*/
|
*/
|
||||||
|
|
@ -114,6 +114,7 @@ function update (id, data, userToken) {
|
||||||
async function updateCustomer (id, data, userToken) {
|
async function updateCustomer (id, data, userToken) {
|
||||||
const formattedData = _.pick(
|
const formattedData = _.pick(
|
||||||
[
|
[
|
||||||
|
'sanctions',
|
||||||
'authorized_override',
|
'authorized_override',
|
||||||
'id_card_photo_override',
|
'id_card_photo_override',
|
||||||
'id_card_data_override',
|
'id_card_data_override',
|
||||||
|
|
@ -229,7 +230,7 @@ function enhanceEditedPhotos (fields) {
|
||||||
/**
|
/**
|
||||||
* Remove the edited data from the db record
|
* Remove the edited data from the db record
|
||||||
*
|
*
|
||||||
* @name enhanceOverrideFields
|
* @name deleteEditedData
|
||||||
* @function
|
* @function
|
||||||
*
|
*
|
||||||
* @param {string} id Customer's id
|
* @param {string} id Customer's id
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ const staticConfig = ({ currentConfigVersion, deviceId, deviceName, pq, settings
|
||||||
'cashInCommission',
|
'cashInCommission',
|
||||||
'cashInFee',
|
'cashInFee',
|
||||||
'cashOutCommission',
|
'cashOutCommission',
|
||||||
|
'cashOutFee',
|
||||||
'cryptoCode',
|
'cryptoCode',
|
||||||
'cryptoCodeDisplay',
|
'cryptoCodeDisplay',
|
||||||
'cryptoNetwork',
|
'cryptoNetwork',
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ type Coin {
|
||||||
display: String!
|
display: String!
|
||||||
minimumTx: String!
|
minimumTx: String!
|
||||||
cashInFee: String!
|
cashInFee: String!
|
||||||
|
cashOutFee: String!
|
||||||
cashInCommission: String!
|
cashInCommission: String!
|
||||||
cashOutCommission: String!
|
cashOutCommission: String!
|
||||||
cryptoNetwork: String!
|
cryptoNetwork: String!
|
||||||
|
|
|
||||||
15
lib/middlewares/addRWBytes.js
Normal file
15
lib/middlewares/addRWBytes.js
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
const addRWBytes = () => (req, res, next) => {
|
||||||
|
const handle = () => {
|
||||||
|
res.removeListener('finish', handle)
|
||||||
|
res.removeListener('close', handle)
|
||||||
|
res.bytesRead = req.connection.bytesRead
|
||||||
|
res.bytesWritten = req.connection.bytesWritten
|
||||||
|
}
|
||||||
|
|
||||||
|
res.on('finish', handle)
|
||||||
|
res.on('close', handle)
|
||||||
|
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = addRWBytes
|
||||||
|
|
@ -14,6 +14,7 @@ const machine = require('./machine.resolver')
|
||||||
const notification = require('./notification.resolver')
|
const notification = require('./notification.resolver')
|
||||||
const pairing = require('./pairing.resolver')
|
const pairing = require('./pairing.resolver')
|
||||||
const rates = require('./rates.resolver')
|
const rates = require('./rates.resolver')
|
||||||
|
const sanctions = require('./sanctions.resolver')
|
||||||
const scalar = require('./scalar.resolver')
|
const scalar = require('./scalar.resolver')
|
||||||
const settings = require('./settings.resolver')
|
const settings = require('./settings.resolver')
|
||||||
const sms = require('./sms.resolver')
|
const sms = require('./sms.resolver')
|
||||||
|
|
@ -37,6 +38,7 @@ const resolvers = [
|
||||||
notification,
|
notification,
|
||||||
pairing,
|
pairing,
|
||||||
rates,
|
rates,
|
||||||
|
sanctions,
|
||||||
scalar,
|
scalar,
|
||||||
settings,
|
settings,
|
||||||
sms,
|
sms,
|
||||||
|
|
|
||||||
13
lib/new-admin/graphql/resolvers/sanctions.resolver.js
Normal file
13
lib/new-admin/graphql/resolvers/sanctions.resolver.js
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
const sanctions = require('../../../sanctions')
|
||||||
|
const authentication = require('../modules/userManagement')
|
||||||
|
|
||||||
|
const resolvers = {
|
||||||
|
Query: {
|
||||||
|
checkAgainstSanctions: (...[, { customerId }, context]) => {
|
||||||
|
const token = authentication.getToken(context)
|
||||||
|
return sanctions.checkByUser(customerId, token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = resolvers
|
||||||
|
|
@ -14,6 +14,7 @@ const machine = require('./machine.type')
|
||||||
const notification = require('./notification.type')
|
const notification = require('./notification.type')
|
||||||
const pairing = require('./pairing.type')
|
const pairing = require('./pairing.type')
|
||||||
const rates = require('./rates.type')
|
const rates = require('./rates.type')
|
||||||
|
const sanctions = require('./sanctions.type')
|
||||||
const scalar = require('./scalar.type')
|
const scalar = require('./scalar.type')
|
||||||
const settings = require('./settings.type')
|
const settings = require('./settings.type')
|
||||||
const sms = require('./sms.type')
|
const sms = require('./sms.type')
|
||||||
|
|
@ -37,6 +38,7 @@ const types = [
|
||||||
notification,
|
notification,
|
||||||
pairing,
|
pairing,
|
||||||
rates,
|
rates,
|
||||||
|
sanctions,
|
||||||
scalar,
|
scalar,
|
||||||
settings,
|
settings,
|
||||||
sms,
|
sms,
|
||||||
|
|
|
||||||
13
lib/new-admin/graphql/types/sanctions.type.js
Normal file
13
lib/new-admin/graphql/types/sanctions.type.js
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
const { gql } = require('apollo-server-express')
|
||||||
|
|
||||||
|
const typeDef = gql`
|
||||||
|
type SanctionMatches {
|
||||||
|
ofacSanctioned: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type Query {
|
||||||
|
checkAgainstSanctions(customerId: ID): SanctionMatches @auth
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
module.exports = typeDef
|
||||||
|
|
@ -23,7 +23,7 @@ const typeDef = gql`
|
||||||
errorCode: String
|
errorCode: String
|
||||||
operatorCompleted: Boolean
|
operatorCompleted: Boolean
|
||||||
sendPending: Boolean
|
sendPending: Boolean
|
||||||
cashInFee: String
|
fixedFee: String
|
||||||
minimumTx: Float
|
minimumTx: Float
|
||||||
customerId: ID
|
customerId: ID
|
||||||
isAnonymous: Boolean
|
isAnonymous: Boolean
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,19 @@ function batch (
|
||||||
excludeTestingCustomers = false,
|
excludeTestingCustomers = false,
|
||||||
simplified
|
simplified
|
||||||
) {
|
) {
|
||||||
const packager = _.flow(_.flatten, _.orderBy(_.property('created'), ['desc']), _.map(camelize), addProfits, addNames)
|
const packager = _.flow(
|
||||||
|
_.flatten,
|
||||||
|
_.orderBy(_.property('created'), ['desc']),
|
||||||
|
_.map(_.flow(
|
||||||
|
camelize,
|
||||||
|
_.mapKeys(k =>
|
||||||
|
k == 'cashInFee' ? 'fixedFee' :
|
||||||
|
k
|
||||||
|
)
|
||||||
|
)),
|
||||||
|
addProfits,
|
||||||
|
addNames
|
||||||
|
)
|
||||||
|
|
||||||
const cashInSql = `SELECT 'cashIn' AS tx_class, txs.*,
|
const cashInSql = `SELECT 'cashIn' AS tx_class, txs.*,
|
||||||
c.phone AS customer_phone,
|
c.phone AS customer_phone,
|
||||||
|
|
@ -153,7 +165,7 @@ function batch (
|
||||||
function advancedBatch (data) {
|
function advancedBatch (data) {
|
||||||
const fields = ['txClass', 'id', 'deviceId', 'toAddress', 'cryptoAtoms',
|
const fields = ['txClass', 'id', 'deviceId', 'toAddress', 'cryptoAtoms',
|
||||||
'cryptoCode', 'fiat', 'fiatCode', 'fee', 'status', 'fiatProfit', 'cryptoAmount',
|
'cryptoCode', 'fiat', 'fiatCode', 'fee', 'status', 'fiatProfit', 'cryptoAmount',
|
||||||
'dispense', 'notified', 'redeem', 'phone', 'error',
|
'dispense', 'notified', 'redeem', 'phone', 'error', 'fixedFee',
|
||||||
'created', 'confirmedAt', 'hdIndex', 'swept', 'timedout',
|
'created', 'confirmedAt', 'hdIndex', 'swept', 'timedout',
|
||||||
'dispenseConfirmed', 'provisioned1', 'provisioned2', 'provisioned3', 'provisioned4',
|
'dispenseConfirmed', 'provisioned1', 'provisioned2', 'provisioned3', 'provisioned4',
|
||||||
'provisionedRecycler1', 'provisionedRecycler2', 'provisionedRecycler3', 'provisionedRecycler4', 'provisionedRecycler5', 'provisionedRecycler6',
|
'provisionedRecycler1', 'provisionedRecycler2', 'provisionedRecycler3', 'provisionedRecycler4', 'provisionedRecycler5', 'provisionedRecycler6',
|
||||||
|
|
@ -169,7 +181,9 @@ function advancedBatch (data) {
|
||||||
...it,
|
...it,
|
||||||
status: getStatus(it),
|
status: getStatus(it),
|
||||||
fiatProfit: getProfit(it).toString(),
|
fiatProfit: getProfit(it).toString(),
|
||||||
cryptoAmount: getCryptoAmount(it).toString()
|
cryptoAmount: getCryptoAmount(it).toString(),
|
||||||
|
fixedFee: it.fixedFee ?? null,
|
||||||
|
fee: it.fee ?? null,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return _.compose(_.map(_.pick(fields)), addAdvancedFields)(data)
|
return _.compose(_.map(_.pick(fields)), addAdvancedFields)(data)
|
||||||
|
|
|
||||||
|
|
@ -249,6 +249,7 @@ function plugins (settings, deviceId) {
|
||||||
const commissions = configManager.getCommissions(cryptoCode, deviceId, settings.config)
|
const commissions = configManager.getCommissions(cryptoCode, deviceId, settings.config)
|
||||||
const minimumTx = new BN(commissions.minimumTx)
|
const minimumTx = new BN(commissions.minimumTx)
|
||||||
const cashInFee = new BN(commissions.fixedFee)
|
const cashInFee = new BN(commissions.fixedFee)
|
||||||
|
const cashOutFee = new BN(commissions.cashOutFixedFee)
|
||||||
const cashInCommission = new BN(commissions.cashIn)
|
const cashInCommission = new BN(commissions.cashIn)
|
||||||
const cashOutCommission = _.isNumber(commissions.cashOut) ? new BN(commissions.cashOut) : null
|
const cashOutCommission = _.isNumber(commissions.cashOut) ? new BN(commissions.cashOut) : null
|
||||||
const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode)
|
const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode)
|
||||||
|
|
@ -261,6 +262,7 @@ function plugins (settings, deviceId) {
|
||||||
isCashInOnly: Boolean(cryptoRec.isCashinOnly),
|
isCashInOnly: Boolean(cryptoRec.isCashinOnly),
|
||||||
minimumTx: BN.max(minimumTx, cashInFee),
|
minimumTx: BN.max(minimumTx, cashInFee),
|
||||||
cashInFee,
|
cashInFee,
|
||||||
|
cashOutFee,
|
||||||
cashInCommission,
|
cashInCommission,
|
||||||
cashOutCommission,
|
cashOutCommission,
|
||||||
cryptoNetwork,
|
cryptoNetwork,
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,11 @@ const nocache = require('nocache')
|
||||||
|
|
||||||
const logger = require('./logger')
|
const logger = require('./logger')
|
||||||
|
|
||||||
|
const addRWBytes = require('./middlewares/addRWBytes')
|
||||||
const authorize = require('./middlewares/authorize')
|
const authorize = require('./middlewares/authorize')
|
||||||
|
const computeSchema = require('./middlewares/compute-schema')
|
||||||
const errorHandler = require('./middlewares/errorHandler')
|
const errorHandler = require('./middlewares/errorHandler')
|
||||||
const filterOldRequests = require('./middlewares/filterOldRequests')
|
const filterOldRequests = require('./middlewares/filterOldRequests')
|
||||||
const computeSchema = require('./middlewares/compute-schema')
|
|
||||||
const findOperatorId = require('./middlewares/operatorId')
|
const findOperatorId = require('./middlewares/operatorId')
|
||||||
const populateDeviceId = require('./middlewares/populateDeviceId')
|
const populateDeviceId = require('./middlewares/populateDeviceId')
|
||||||
const populateSettings = require('./middlewares/populateSettings')
|
const populateSettings = require('./middlewares/populateSettings')
|
||||||
|
|
@ -50,11 +51,15 @@ const configRequiredRoutes = [
|
||||||
]
|
]
|
||||||
|
|
||||||
// middleware setup
|
// middleware setup
|
||||||
|
app.use(addRWBytes())
|
||||||
app.use(compression({ threshold: 500 }))
|
app.use(compression({ threshold: 500 }))
|
||||||
app.use(helmet())
|
app.use(helmet())
|
||||||
app.use(nocache())
|
app.use(nocache())
|
||||||
app.use(express.json({ limit: '2mb' }))
|
app.use(express.json({ limit: '2mb' }))
|
||||||
app.use(morgan(':method :url :status :response-time ms -- :req[content-length]/:res[content-length] b', { stream: logger.stream }))
|
|
||||||
|
morgan.token('bytesRead', (_req, res) => res.bytesRead)
|
||||||
|
morgan.token('bytesWritten', (_req, res) => res.bytesWritten)
|
||||||
|
app.use(morgan(':method :url :status :response-time ms -- :bytesRead/:bytesWritten B', { stream: logger.stream }))
|
||||||
|
|
||||||
// app /pair and /ca routes
|
// app /pair and /ca routes
|
||||||
app.use('/', pairingRoutes)
|
app.use('/', pairingRoutes)
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ function updateCustomer (req, res, next) {
|
||||||
.then(_.merge(patch))
|
.then(_.merge(patch))
|
||||||
.then(newPatch => customers.updatePhotoCard(id, newPatch))
|
.then(newPatch => customers.updatePhotoCard(id, newPatch))
|
||||||
.then(newPatch => customers.updateFrontCamera(id, newPatch))
|
.then(newPatch => customers.updateFrontCamera(id, newPatch))
|
||||||
.then(newPatch => customers.update(id, newPatch, null, txId))
|
.then(newPatch => customers.update(id, newPatch, null))
|
||||||
.then(customer => {
|
.then(customer => {
|
||||||
createPendingManualComplianceNotifs(settings, customer, deviceId)
|
createPendingManualComplianceNotifs(settings, customer, deviceId)
|
||||||
respond(req, res, { customer })
|
respond(req, res, { customer })
|
||||||
|
|
|
||||||
44
lib/sanctions.js
Normal file
44
lib/sanctions.js
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
const _ = require('lodash/fp')
|
||||||
|
const ofac = require('./ofac')
|
||||||
|
const T = require('./time')
|
||||||
|
const logger = require('./logger')
|
||||||
|
const customers = require('./customers')
|
||||||
|
|
||||||
|
const sanctionStatus = {
|
||||||
|
loaded: false,
|
||||||
|
timestamp: null
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadOrUpdateSanctions = () => {
|
||||||
|
if (!sanctionStatus.loaded || (sanctionStatus.timestamp && Date.now() > sanctionStatus.timestamp + T.day)) {
|
||||||
|
logger.info('No sanction lists loaded. Loading sanctions...')
|
||||||
|
return ofac.load()
|
||||||
|
.then(() => {
|
||||||
|
logger.info('OFAC sanction list loaded!')
|
||||||
|
sanctionStatus.loaded = true
|
||||||
|
sanctionStatus.timestamp = Date.now()
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
logger.error('Couldn\'t load OFAC sanction list!')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkByUser = (customerId, userToken) => {
|
||||||
|
return Promise.all([loadOrUpdateSanctions(), customers.getCustomerById(customerId)])
|
||||||
|
.then(([, customer]) => {
|
||||||
|
const { firstName, lastName, dateOfBirth } = customer?.idCardData
|
||||||
|
const birthdate = _.replace(/-/g, '')(dateOfBirth)
|
||||||
|
const ofacMatches = ofac.match({ firstName, lastName }, birthdate, { threshold: 0.85, fullNameThreshold: 0.95, debug: false })
|
||||||
|
const isOfacSanctioned = _.size(ofacMatches) > 0
|
||||||
|
customers.updateCustomer(customerId, { sanctions: !isOfacSanctioned }, userToken)
|
||||||
|
|
||||||
|
return { ofacSanctioned: isOfacSanctioned }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
checkByUser
|
||||||
|
}
|
||||||
|
|
@ -40,6 +40,7 @@ function massage (tx, pi) {
|
||||||
: {
|
: {
|
||||||
cryptoAtoms: new BN(r.cryptoAtoms),
|
cryptoAtoms: new BN(r.cryptoAtoms),
|
||||||
fiat: new BN(r.fiat),
|
fiat: new BN(r.fiat),
|
||||||
|
fixedFee: new BN(r.fixedFee),
|
||||||
rawTickerPrice: r.rawTickerPrice ? new BN(r.rawTickerPrice) : null,
|
rawTickerPrice: r.rawTickerPrice ? new BN(r.rawTickerPrice) : null,
|
||||||
commissionPercentage: new BN(r.commissionPercentage)
|
commissionPercentage: new BN(r.commissionPercentage)
|
||||||
}
|
}
|
||||||
|
|
@ -69,7 +70,7 @@ function cancel (txId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function customerHistory (customerId, thresholdDays) {
|
function customerHistory (customerId, thresholdDays) {
|
||||||
const sql = `SELECT * FROM (
|
const sql = `SELECT ch.id, ch.created, ch.fiat, ch.direction FROM (
|
||||||
SELECT txIn.id, txIn.created, txIn.fiat, 'cashIn' AS direction,
|
SELECT txIn.id, txIn.created, txIn.fiat, 'cashIn' AS direction,
|
||||||
((NOT txIn.send_confirmed) AND (txIn.created <= now() - interval $3)) AS expired
|
((NOT txIn.send_confirmed) AND (txIn.created <= now() - interval $3)) AS expired
|
||||||
FROM cash_in_txs txIn
|
FROM cash_in_txs txIn
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
const db = require('./db')
|
||||||
|
|
||||||
|
exports.up = next => db.multi([
|
||||||
|
'ALTER TABLE cash_out_txs ADD COLUMN fixed_fee numeric(14, 5) NOT NULL DEFAULT 0;'
|
||||||
|
], next)
|
||||||
|
|
||||||
|
exports.down = next => next()
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
const { saveConfig } = require('../lib/new-settings-loader')
|
||||||
|
|
||||||
|
exports.up = next => saveConfig({ 'commissions_cashOutFixedFee': 0 })
|
||||||
|
.then(next)
|
||||||
|
.catch(next)
|
||||||
|
|
||||||
|
exports.down = next => next()
|
||||||
|
|
@ -13,9 +13,10 @@ const useStyles = makeStyles({
|
||||||
display: 'flex'
|
display: 'flex'
|
||||||
},
|
},
|
||||||
imgInner: {
|
imgInner: {
|
||||||
objectFit: 'cover',
|
objectFit: 'contain',
|
||||||
objectPosition: 'center',
|
objectPosition: 'center',
|
||||||
width: 500,
|
width: 500,
|
||||||
|
height: 400,
|
||||||
marginBottom: 40
|
marginBottom: 40
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,16 @@ const useStyles = makeStyles({
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
marginTop: 4
|
marginTop: 4
|
||||||
},
|
},
|
||||||
|
relativelyPositioned: {
|
||||||
|
position: 'relative'
|
||||||
|
},
|
||||||
|
safeSpace: {
|
||||||
|
position: 'absolute',
|
||||||
|
backgroundColor: '#0000',
|
||||||
|
height: 40,
|
||||||
|
left: '-50%',
|
||||||
|
width: '200%'
|
||||||
|
},
|
||||||
popoverContent: ({ width }) => ({
|
popoverContent: ({ width }) => ({
|
||||||
width,
|
width,
|
||||||
padding: [[10, 15]]
|
padding: [[10, 15]]
|
||||||
|
|
@ -27,6 +37,10 @@ const usePopperHandler = width => {
|
||||||
setHelpPopperAnchorEl(helpPopperAnchorEl ? null : event.currentTarget)
|
setHelpPopperAnchorEl(helpPopperAnchorEl ? null : event.currentTarget)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const openHelpPopper = event => {
|
||||||
|
setHelpPopperAnchorEl(event.currentTarget)
|
||||||
|
}
|
||||||
|
|
||||||
const handleCloseHelpPopper = () => {
|
const handleCloseHelpPopper = () => {
|
||||||
setHelpPopperAnchorEl(null)
|
setHelpPopperAnchorEl(null)
|
||||||
}
|
}
|
||||||
|
|
@ -38,25 +52,32 @@ const usePopperHandler = width => {
|
||||||
helpPopperAnchorEl,
|
helpPopperAnchorEl,
|
||||||
helpPopperOpen,
|
helpPopperOpen,
|
||||||
handleOpenHelpPopper,
|
handleOpenHelpPopper,
|
||||||
|
openHelpPopper,
|
||||||
handleCloseHelpPopper
|
handleCloseHelpPopper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Tooltip = memo(({ children, width, Icon = HelpIcon }) => {
|
const HelpTooltip = memo(({ children, width }) => {
|
||||||
const handler = usePopperHandler(width)
|
const handler = usePopperHandler(width)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ClickAwayListener onClickAway={handler.handleCloseHelpPopper}>
|
<ClickAwayListener onClickAway={handler.handleCloseHelpPopper}>
|
||||||
<div>
|
<div
|
||||||
|
className={handler.classes.relativelyPositioned}
|
||||||
|
onMouseLeave={handler.handleCloseHelpPopper}>
|
||||||
|
{handler.helpPopperOpen && (
|
||||||
|
<div className={handler.classes.safeSpace}></div>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={handler.classes.transparentButton}
|
className={handler.classes.transparentButton}
|
||||||
onClick={handler.handleOpenHelpPopper}>
|
onMouseEnter={handler.openHelpPopper}>
|
||||||
<Icon />
|
<HelpIcon />
|
||||||
</button>
|
</button>
|
||||||
<Popper
|
<Popper
|
||||||
open={handler.helpPopperOpen}
|
open={handler.helpPopperOpen}
|
||||||
anchorEl={handler.helpPopperAnchorEl}
|
anchorEl={handler.helpPopperAnchorEl}
|
||||||
|
arrowEnabled={true}
|
||||||
placement="bottom">
|
placement="bottom">
|
||||||
<div className={handler.classes.popoverContent}>{children}</div>
|
<div className={handler.classes.popoverContent}>{children}</div>
|
||||||
</Popper>
|
</Popper>
|
||||||
|
|
@ -69,11 +90,10 @@ const HoverableTooltip = memo(({ parentElements, children, width }) => {
|
||||||
const handler = usePopperHandler(width)
|
const handler = usePopperHandler(width)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<ClickAwayListener onClickAway={handler.handleCloseHelpPopper}>
|
||||||
<div>
|
<div>
|
||||||
{!R.isNil(parentElements) && (
|
{!R.isNil(parentElements) && (
|
||||||
<div
|
<div onMouseEnter={handler.handleOpenHelpPopper}>
|
||||||
onMouseEnter={handler.handleOpenHelpPopper}
|
|
||||||
onMouseLeave={handler.handleCloseHelpPopper}>
|
|
||||||
{parentElements}
|
{parentElements}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -81,7 +101,6 @@ const HoverableTooltip = memo(({ parentElements, children, width }) => {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onMouseEnter={handler.handleOpenHelpPopper}
|
onMouseEnter={handler.handleOpenHelpPopper}
|
||||||
onMouseLeave={handler.handleCloseHelpPopper}
|
|
||||||
className={handler.classes.transparentButton}>
|
className={handler.classes.transparentButton}>
|
||||||
<HelpIcon />
|
<HelpIcon />
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -93,7 +112,8 @@ const HoverableTooltip = memo(({ parentElements, children, width }) => {
|
||||||
<div className={handler.classes.popoverContent}>{children}</div>
|
<div className={handler.classes.popoverContent}>{children}</div>
|
||||||
</Popper>
|
</Popper>
|
||||||
</div>
|
</div>
|
||||||
|
</ClickAwayListener>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
export { Tooltip, HoverableTooltip }
|
export { HoverableTooltip, HelpTooltip }
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import {
|
||||||
TDoubleLevelHead,
|
TDoubleLevelHead,
|
||||||
ThDoubleLevel
|
ThDoubleLevel
|
||||||
} from 'src/components/fake-table/Table'
|
} from 'src/components/fake-table/Table'
|
||||||
import { startCase } from 'src/utils/string'
|
import { sentenceCase } from 'src/utils/string'
|
||||||
|
|
||||||
import TableCtx from './Context'
|
import TableCtx from './Context'
|
||||||
|
|
||||||
|
|
@ -22,22 +22,27 @@ const styles = {
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const groupSecondHeader = elements => {
|
const groupSecondHeader = elements => {
|
||||||
const [toSHeader, noSHeader] = R.partition(R.has('doubleHeader'))(elements)
|
const doubleHeader = R.prop('doubleHeader')
|
||||||
|
const sameDoubleHeader = (a, b) => doubleHeader(a) === doubleHeader(b)
|
||||||
if (!toSHeader.length) {
|
const group = R.pipe(
|
||||||
return [elements, THead]
|
R.groupWith(sameDoubleHeader),
|
||||||
|
R.map(group =>
|
||||||
|
R.isNil(doubleHeader(group[0])) // No doubleHeader
|
||||||
|
? group
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
width: R.sum(R.map(R.prop('width'), group)),
|
||||||
|
elements: group,
|
||||||
|
name: doubleHeader(group[0])
|
||||||
}
|
}
|
||||||
|
]
|
||||||
const index = R.indexOf(toSHeader[0], elements)
|
),
|
||||||
const width = R.compose(R.sum, R.map(R.path(['width'])))(toSHeader)
|
R.reduce(R.concat, [])
|
||||||
|
|
||||||
const innerElements = R.insert(
|
|
||||||
index,
|
|
||||||
{ width, elements: toSHeader, name: toSHeader[0].doubleHeader },
|
|
||||||
noSHeader
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return [innerElements, TDoubleLevelHead]
|
return R.all(R.pipe(doubleHeader, R.isNil), elements)
|
||||||
|
? [elements, THead]
|
||||||
|
: [group(elements), TDoubleLevelHead]
|
||||||
}
|
}
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
|
|
@ -99,7 +104,7 @@ const Header = () => {
|
||||||
<>{attachOrderedByToComplexHeader(header) ?? header}</>
|
<>{attachOrderedByToComplexHeader(header) ?? header}</>
|
||||||
) : (
|
) : (
|
||||||
<span className={orderClasses}>
|
<span className={orderClasses}>
|
||||||
{!R.isNil(display) ? display : startCase(name)}{' '}
|
{!R.isNil(display) ? display : sentenceCase(name)}{' '}
|
||||||
{!R.isNil(orderedBy) && R.equals(name, orderedBy.code) && '-'}
|
{!R.isNil(orderedBy) && R.equals(name, orderedBy.code) && '-'}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -187,7 +187,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => {
|
||||||
display: 'Restart services for'
|
display: 'Restart services for'
|
||||||
})
|
})
|
||||||
}}>
|
}}>
|
||||||
Restart Services
|
Restart services
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
{machine.model === 'aveiro' && (
|
{machine.model === 'aveiro' && (
|
||||||
<ActionButton
|
<ActionButton
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import * as R from 'ramda'
|
||||||
import React, { useContext } from 'react'
|
import React, { useContext } from 'react'
|
||||||
|
|
||||||
import AppContext from 'src/AppContext'
|
import AppContext from 'src/AppContext'
|
||||||
import { HoverableTooltip } from 'src/components/Tooltip'
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
import TitleSection from 'src/components/layout/TitleSection'
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
import DataTable from 'src/components/tables/DataTable'
|
import DataTable from 'src/components/tables/DataTable'
|
||||||
import { H4, Info2, P } from 'src/components/typography'
|
import { H4, Info2, P } from 'src/components/typography'
|
||||||
|
|
@ -128,9 +128,9 @@ const Accounting = () => {
|
||||||
<span className={classes.operation}>
|
<span className={classes.operation}>
|
||||||
{it.description}
|
{it.description}
|
||||||
{!!it.extraInfo && (
|
{!!it.extraInfo && (
|
||||||
<HoverableTooltip width={175}>
|
<HelpTooltip width={175}>
|
||||||
<P>{it.extraInfo}</P>
|
<P>{it.extraInfo}</P>
|
||||||
</HoverableTooltip>
|
</HelpTooltip>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ const MACHINE_OPTIONS = [{ code: 'all', display: 'All machines' }]
|
||||||
const REPRESENTING_OPTIONS = [
|
const REPRESENTING_OPTIONS = [
|
||||||
{ code: 'overTime', display: 'Over time' },
|
{ code: 'overTime', display: 'Over time' },
|
||||||
{ code: 'volumeOverTime', display: 'Volume' },
|
{ code: 'volumeOverTime', display: 'Volume' },
|
||||||
{ code: 'topMachines', display: 'Top Machines' },
|
{ code: 'topMachines', display: 'Top machines' },
|
||||||
{ code: 'hourOfTheDay', display: 'Hour of the day' }
|
{ code: 'hourOfTheDay', display: 'Hour of the day' }
|
||||||
]
|
]
|
||||||
const PERIOD_OPTIONS = [
|
const PERIOD_OPTIONS = [
|
||||||
|
|
@ -81,7 +81,7 @@ const GET_TRANSACTIONS = gql`
|
||||||
hasError: error
|
hasError: error
|
||||||
deviceId
|
deviceId
|
||||||
fiat
|
fiat
|
||||||
cashInFee
|
fixedFee
|
||||||
fiatCode
|
fiatCode
|
||||||
cryptoAtoms
|
cryptoAtoms
|
||||||
cryptoCode
|
cryptoCode
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,13 @@ import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import { HoverableTooltip } from 'src/components/Tooltip'
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
import { Link, Button, IconButton } from 'src/components/buttons'
|
import {
|
||||||
|
Link,
|
||||||
|
Button,
|
||||||
|
IconButton,
|
||||||
|
SupportLinkButton
|
||||||
|
} from 'src/components/buttons'
|
||||||
import { Switch } from 'src/components/inputs'
|
import { Switch } from 'src/components/inputs'
|
||||||
import Sidebar from 'src/components/layout/Sidebar'
|
import Sidebar from 'src/components/layout/Sidebar'
|
||||||
import TitleSection from 'src/components/layout/TitleSection'
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
|
|
@ -251,13 +256,13 @@ const Blacklist = () => {
|
||||||
value={enablePaperWalletOnly}
|
value={enablePaperWalletOnly}
|
||||||
/>
|
/>
|
||||||
<Label2>{enablePaperWalletOnly ? 'On' : 'Off'}</Label2>
|
<Label2>{enablePaperWalletOnly ? 'On' : 'Off'}</Label2>
|
||||||
<HoverableTooltip width={304}>
|
<HelpTooltip width={304}>
|
||||||
<P>
|
<P>
|
||||||
The "Enable paper wallet (only)" option means that only paper
|
The "Enable paper wallet (only)" option means that only paper
|
||||||
wallets will be printed for users, and they won't be permitted
|
wallets will be printed for users, and they won't be permitted
|
||||||
to scan an address from their own wallet.
|
to scan an address from their own wallet.
|
||||||
</P>
|
</P>
|
||||||
</HoverableTooltip>
|
</HelpTooltip>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
display="flex"
|
||||||
|
|
@ -273,13 +278,21 @@ const Blacklist = () => {
|
||||||
value={rejectAddressReuse}
|
value={rejectAddressReuse}
|
||||||
/>
|
/>
|
||||||
<Label2>{rejectAddressReuse ? 'On' : 'Off'}</Label2>
|
<Label2>{rejectAddressReuse ? 'On' : 'Off'}</Label2>
|
||||||
<HoverableTooltip width={304}>
|
<HelpTooltip width={304}>
|
||||||
<P>
|
<P>
|
||||||
The "Reject reused addresses" option means that all addresses
|
The "Reject reused addresses" option means that all addresses
|
||||||
that are used once will be automatically rejected if there's
|
that are used once will be automatically rejected if there's
|
||||||
an attempt to use them again on a new transaction.
|
an attempt to use them again on a new transaction.
|
||||||
</P>
|
</P>
|
||||||
</HoverableTooltip>
|
<P>
|
||||||
|
For details please read the relevant knowledgebase article:
|
||||||
|
</P>
|
||||||
|
<SupportLinkButton
|
||||||
|
link="https://support.lamassu.is/hc/en-us/articles/360033622211-Reject-Address-Reuse"
|
||||||
|
label="Reject Address Reuse"
|
||||||
|
bottomSpace="1"
|
||||||
|
/>
|
||||||
|
</HelpTooltip>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<BlacklistTable
|
<BlacklistTable
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import { HoverableTooltip } from 'src/components/Tooltip'
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
|
import { SupportLinkButton } from 'src/components/buttons'
|
||||||
import { NamespacedTable as EditableTable } from 'src/components/editableTable'
|
import { NamespacedTable as EditableTable } from 'src/components/editableTable'
|
||||||
import { Switch } from 'src/components/inputs'
|
import { Switch } from 'src/components/inputs'
|
||||||
import TitleSection from 'src/components/layout/TitleSection'
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
|
|
@ -92,7 +93,21 @@ const CashOut = ({ name: SCREEN_KEY }) => {
|
||||||
return (
|
return (
|
||||||
!loading && (
|
!loading && (
|
||||||
<>
|
<>
|
||||||
<TitleSection title="Cash-out">
|
<TitleSection
|
||||||
|
title="Cash-out"
|
||||||
|
appendix={
|
||||||
|
<HelpTooltip width={320}>
|
||||||
|
<P>
|
||||||
|
For details on configuring cash-out, please read the relevant
|
||||||
|
knowledgebase article:
|
||||||
|
</P>
|
||||||
|
<SupportLinkButton
|
||||||
|
link="https://support.lamassu.is/hc/en-us/articles/115003720192-Enabling-cash-out-on-the-admin"
|
||||||
|
label="Enabling cash-out on the admin"
|
||||||
|
bottomSpace="1"
|
||||||
|
/>
|
||||||
|
</HelpTooltip>
|
||||||
|
}>
|
||||||
<div className={classes.fudgeFactor}>
|
<div className={classes.fudgeFactor}>
|
||||||
<P>Transaction fudge factor</P>
|
<P>Transaction fudge factor</P>
|
||||||
<Switch
|
<Switch
|
||||||
|
|
@ -105,7 +120,7 @@ const CashOut = ({ name: SCREEN_KEY }) => {
|
||||||
<Label2 className={classes.switchLabel}>
|
<Label2 className={classes.switchLabel}>
|
||||||
{fudgeFactorActive ? 'On' : 'Off'}
|
{fudgeFactorActive ? 'On' : 'Off'}
|
||||||
</Label2>
|
</Label2>
|
||||||
<HoverableTooltip width={304}>
|
<HelpTooltip width={304}>
|
||||||
<P>
|
<P>
|
||||||
Automatically accept customer deposits as complete if their
|
Automatically accept customer deposits as complete if their
|
||||||
received amount is 100 crypto atoms or less.
|
received amount is 100 crypto atoms or less.
|
||||||
|
|
@ -114,7 +129,13 @@ const CashOut = ({ name: SCREEN_KEY }) => {
|
||||||
(Crypto atoms are the smallest unit in each cryptocurrency.
|
(Crypto atoms are the smallest unit in each cryptocurrency.
|
||||||
E.g., satoshis in Bitcoin, or wei in Ethereum.)
|
E.g., satoshis in Bitcoin, or wei in Ethereum.)
|
||||||
</P>
|
</P>
|
||||||
</HoverableTooltip>
|
<P>For details please read the relevant knowledgebase article:</P>
|
||||||
|
<SupportLinkButton
|
||||||
|
link="https://support.lamassu.is/hc/en-us/articles/360050838011-Automatically-accepting-undersent-deposits-with-Fudge-Factor-"
|
||||||
|
label="Lamassu Support Article"
|
||||||
|
bottomSpace="1"
|
||||||
|
/>
|
||||||
|
</HelpTooltip>
|
||||||
</div>
|
</div>
|
||||||
</TitleSection>
|
</TitleSection>
|
||||||
<EditableTable
|
<EditableTable
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ const WizardStep = ({
|
||||||
to zero. Make sure you physically put cash inside the cash
|
to zero. Make sure you physically put cash inside the cash
|
||||||
cassettes to allow the machine to dispense it to your users. If
|
cassettes to allow the machine to dispense it to your users. If
|
||||||
you already did, make sure you set the correct cash cassette bill
|
you already did, make sure you set the correct cash cassette bill
|
||||||
count for this machine on your Cash Boxes & Cassettes tab under
|
count for this machine on your Cash boxes & cassettes tab under
|
||||||
Maintenance.
|
Maintenance.
|
||||||
</P>
|
</P>
|
||||||
<Info2 className={classes.title}>Default Commissions</Info2>
|
<Info2 className={classes.title}>Default Commissions</Info2>
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,16 @@ import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
|
import { SupportLinkButton } from 'src/components/buttons'
|
||||||
import TitleSection from 'src/components/layout/TitleSection'
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
import { ReactComponent as ReverseListingViewIcon } from 'src/styling/icons/circle buttons/listing-view/white.svg'
|
import { ReactComponent as ReverseListingViewIcon } from 'src/styling/icons/circle buttons/listing-view/white.svg'
|
||||||
import { ReactComponent as ListingViewIcon } from 'src/styling/icons/circle buttons/listing-view/zodiac.svg'
|
import { ReactComponent as ListingViewIcon } from 'src/styling/icons/circle buttons/listing-view/zodiac.svg'
|
||||||
import { ReactComponent as OverrideLabelIcon } from 'src/styling/icons/status/spring2.svg'
|
import { ReactComponent as OverrideLabelIcon } from 'src/styling/icons/status/spring2.svg'
|
||||||
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
||||||
|
|
||||||
|
import { P } from '../../components/typography'
|
||||||
|
|
||||||
import CommissionsDetails from './components/CommissionsDetails'
|
import CommissionsDetails from './components/CommissionsDetails'
|
||||||
import CommissionsList from './components/CommissionsList'
|
import CommissionsList from './components/CommissionsList'
|
||||||
|
|
||||||
|
|
@ -118,6 +122,24 @@ const Commissions = ({ name: SCREEN_KEY }) => {
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
iconClassName={classes.listViewButton}
|
iconClassName={classes.listViewButton}
|
||||||
|
appendix={
|
||||||
|
<HelpTooltip width={320}>
|
||||||
|
<P>
|
||||||
|
For details about commissions, please read the relevant
|
||||||
|
knowledgebase articles:
|
||||||
|
</P>
|
||||||
|
<SupportLinkButton
|
||||||
|
link="https://support.lamassu.is/hc/en-us/articles/115001211752-Fixed-fees-Minimum-transaction"
|
||||||
|
label="Fixed fees & Minimum transaction"
|
||||||
|
bottomSpace="1"
|
||||||
|
/>
|
||||||
|
<SupportLinkButton
|
||||||
|
link="https://support.lamassu.is/hc/en-us/articles/360061558352-Commissions-and-Profit-Calculations"
|
||||||
|
label="SCommissions and Profit Calculations"
|
||||||
|
bottomSpace="1"
|
||||||
|
/>
|
||||||
|
</HelpTooltip>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!showMachines && !loading && (
|
{!showMachines && !loading && (
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ const SHOW_ALL = {
|
||||||
const ORDER_OPTIONS = [
|
const ORDER_OPTIONS = [
|
||||||
{
|
{
|
||||||
code: 'machine',
|
code: 'machine',
|
||||||
display: 'Machine Name'
|
display: 'Machine name'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'cryptoCurrencies',
|
code: 'cryptoCurrencies',
|
||||||
|
|
@ -53,7 +53,7 @@ const ORDER_OPTIONS = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'fixedFee',
|
code: 'fixedFee',
|
||||||
display: 'Fixed Fee'
|
display: 'Fixed fee'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'minimumTx',
|
code: 'minimumTx',
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ const getOverridesFields = (getData, currency, auxElements) => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'cryptoCurrencies',
|
name: 'cryptoCurrencies',
|
||||||
width: 280,
|
width: 145,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
view: displayCodeArray(cryptoData),
|
view: displayCodeArray(cryptoData),
|
||||||
input: Autocomplete,
|
input: Autocomplete,
|
||||||
|
|
@ -108,7 +108,7 @@ const getOverridesFields = (getData, currency, auxElements) => {
|
||||||
header: cashInHeader,
|
header: cashInHeader,
|
||||||
name: 'cashIn',
|
name: 'cashIn',
|
||||||
display: 'Cash-in',
|
display: 'Cash-in',
|
||||||
width: 130,
|
width: 123,
|
||||||
input: NumberInput,
|
input: NumberInput,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
suffix: '%',
|
suffix: '%',
|
||||||
|
|
@ -121,7 +121,7 @@ const getOverridesFields = (getData, currency, auxElements) => {
|
||||||
header: cashOutHeader,
|
header: cashOutHeader,
|
||||||
name: 'cashOut',
|
name: 'cashOut',
|
||||||
display: 'Cash-out',
|
display: 'Cash-out',
|
||||||
width: 130,
|
width: 127,
|
||||||
input: NumberInput,
|
input: NumberInput,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
suffix: '%',
|
suffix: '%',
|
||||||
|
|
@ -133,7 +133,7 @@ const getOverridesFields = (getData, currency, auxElements) => {
|
||||||
{
|
{
|
||||||
name: 'fixedFee',
|
name: 'fixedFee',
|
||||||
display: 'Fixed fee',
|
display: 'Fixed fee',
|
||||||
width: 144,
|
width: 126,
|
||||||
input: NumberInput,
|
input: NumberInput,
|
||||||
doubleHeader: 'Cash-in only',
|
doubleHeader: 'Cash-in only',
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
|
|
@ -146,7 +146,7 @@ const getOverridesFields = (getData, currency, auxElements) => {
|
||||||
{
|
{
|
||||||
name: 'minimumTx',
|
name: 'minimumTx',
|
||||||
display: 'Minimum Tx',
|
display: 'Minimum Tx',
|
||||||
width: 169,
|
width: 140,
|
||||||
doubleHeader: 'Cash-in only',
|
doubleHeader: 'Cash-in only',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
editingAlign: 'right',
|
editingAlign: 'right',
|
||||||
|
|
@ -156,6 +156,20 @@ const getOverridesFields = (getData, currency, auxElements) => {
|
||||||
inputProps: {
|
inputProps: {
|
||||||
decimalPlaces: 2
|
decimalPlaces: 2
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cashOutFixedFee',
|
||||||
|
display: 'Fixed fee',
|
||||||
|
width: 134,
|
||||||
|
doubleHeader: 'Cash-out only',
|
||||||
|
textAlign: 'center',
|
||||||
|
editingAlign: 'right',
|
||||||
|
input: NumberInput,
|
||||||
|
suffix: currency,
|
||||||
|
bold: bold,
|
||||||
|
inputProps: {
|
||||||
|
decimalPlaces: 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -218,6 +232,21 @@ const mainFields = currency => [
|
||||||
inputProps: {
|
inputProps: {
|
||||||
decimalPlaces: 2
|
decimalPlaces: 2
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cashOutFixedFee',
|
||||||
|
display: 'Fixed fee',
|
||||||
|
width: 169,
|
||||||
|
size: 'lg',
|
||||||
|
doubleHeader: 'Cash-out only',
|
||||||
|
textAlign: 'center',
|
||||||
|
editingAlign: 'right',
|
||||||
|
input: NumberInput,
|
||||||
|
suffix: currency,
|
||||||
|
bold: bold,
|
||||||
|
inputProps: {
|
||||||
|
decimalPlaces: 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -245,7 +274,7 @@ const getSchema = locale => {
|
||||||
.max(percentMax)
|
.max(percentMax)
|
||||||
.required(),
|
.required(),
|
||||||
fixedFee: Yup.number()
|
fixedFee: Yup.number()
|
||||||
.label('Fixed Fee')
|
.label('Cash-in fixed fee')
|
||||||
.min(0)
|
.min(0)
|
||||||
.max(highestBill)
|
.max(highestBill)
|
||||||
.required(),
|
.required(),
|
||||||
|
|
@ -253,6 +282,11 @@ const getSchema = locale => {
|
||||||
.label('Minimum Tx')
|
.label('Minimum Tx')
|
||||||
.min(0)
|
.min(0)
|
||||||
.max(highestBill)
|
.max(highestBill)
|
||||||
|
.required(),
|
||||||
|
cashOutFixedFee: Yup.number()
|
||||||
|
.label('Cash-out fixed fee')
|
||||||
|
.min(0)
|
||||||
|
.max(highestBill)
|
||||||
.required()
|
.required()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -326,7 +360,7 @@ const getOverridesSchema = (values, rawData, locale) => {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.label('Crypto Currencies')
|
.label('Crypto currencies')
|
||||||
.required()
|
.required()
|
||||||
.min(1),
|
.min(1),
|
||||||
cashIn: Yup.number()
|
cashIn: Yup.number()
|
||||||
|
|
@ -340,7 +374,7 @@ const getOverridesSchema = (values, rawData, locale) => {
|
||||||
.max(percentMax)
|
.max(percentMax)
|
||||||
.required(),
|
.required(),
|
||||||
fixedFee: Yup.number()
|
fixedFee: Yup.number()
|
||||||
.label('Fixed Fee')
|
.label('Cash-in fixed fee')
|
||||||
.min(0)
|
.min(0)
|
||||||
.max(highestBill)
|
.max(highestBill)
|
||||||
.required(),
|
.required(),
|
||||||
|
|
@ -348,6 +382,11 @@ const getOverridesSchema = (values, rawData, locale) => {
|
||||||
.label('Minimum Tx')
|
.label('Minimum Tx')
|
||||||
.min(0)
|
.min(0)
|
||||||
.max(highestBill)
|
.max(highestBill)
|
||||||
|
.required(),
|
||||||
|
cashOutFixedFee: Yup.number()
|
||||||
|
.label('Cash-out fixed fee')
|
||||||
|
.min(0)
|
||||||
|
.max(highestBill)
|
||||||
.required()
|
.required()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -356,7 +395,8 @@ const defaults = {
|
||||||
cashIn: '',
|
cashIn: '',
|
||||||
cashOut: '',
|
cashOut: '',
|
||||||
fixedFee: '',
|
fixedFee: '',
|
||||||
minimumTx: ''
|
minimumTx: '',
|
||||||
|
cashOutFixedFee: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const overridesDefaults = {
|
const overridesDefaults = {
|
||||||
|
|
@ -365,7 +405,8 @@ const overridesDefaults = {
|
||||||
cashIn: '',
|
cashIn: '',
|
||||||
cashOut: '',
|
cashOut: '',
|
||||||
fixedFee: '',
|
fixedFee: '',
|
||||||
minimumTx: ''
|
minimumTx: '',
|
||||||
|
cashOutFixedFee: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const getOrder = ({ machine, cryptoCurrencies }) => {
|
const getOrder = ({ machine, cryptoCurrencies }) => {
|
||||||
|
|
@ -385,6 +426,7 @@ const createCommissions = (cryptoCode, deviceId, isDefault, config) => {
|
||||||
fixedFee: config.fixedFee,
|
fixedFee: config.fixedFee,
|
||||||
cashOut: config.cashOut,
|
cashOut: config.cashOut,
|
||||||
cashIn: config.cashIn,
|
cashIn: config.cashIn,
|
||||||
|
cashOutFixedFee: config.cashOutFixedFee,
|
||||||
machine: deviceId,
|
machine: deviceId,
|
||||||
cryptoCurrencies: [cryptoCode],
|
cryptoCurrencies: [cryptoCode],
|
||||||
default: isDefault,
|
default: isDefault,
|
||||||
|
|
@ -437,7 +479,7 @@ const getListCommissionsSchema = locale => {
|
||||||
.label('Machine')
|
.label('Machine')
|
||||||
.required(),
|
.required(),
|
||||||
cryptoCurrencies: Yup.array()
|
cryptoCurrencies: Yup.array()
|
||||||
.label('Crypto Currency')
|
.label('Crypto currency')
|
||||||
.required()
|
.required()
|
||||||
.min(1),
|
.min(1),
|
||||||
cashIn: Yup.number()
|
cashIn: Yup.number()
|
||||||
|
|
@ -451,7 +493,7 @@ const getListCommissionsSchema = locale => {
|
||||||
.max(percentMax)
|
.max(percentMax)
|
||||||
.required(),
|
.required(),
|
||||||
fixedFee: Yup.number()
|
fixedFee: Yup.number()
|
||||||
.label('Fixed Fee')
|
.label('Cash-in fixed fee')
|
||||||
.min(0)
|
.min(0)
|
||||||
.max(highestBill)
|
.max(highestBill)
|
||||||
.required(),
|
.required(),
|
||||||
|
|
@ -459,6 +501,11 @@ const getListCommissionsSchema = locale => {
|
||||||
.label('Minimum Tx')
|
.label('Minimum Tx')
|
||||||
.min(0)
|
.min(0)
|
||||||
.max(highestBill)
|
.max(highestBill)
|
||||||
|
.required(),
|
||||||
|
cashOutFixedFee: Yup.number()
|
||||||
|
.label('Cash-out fixed fee')
|
||||||
|
.min(0)
|
||||||
|
.max(highestBill)
|
||||||
.required()
|
.required()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -487,7 +534,7 @@ const getListCommissionsFields = (getData, currency, defaults) => {
|
||||||
{
|
{
|
||||||
name: 'cryptoCurrencies',
|
name: 'cryptoCurrencies',
|
||||||
display: 'Crypto Currency',
|
display: 'Crypto Currency',
|
||||||
width: 255,
|
width: 150,
|
||||||
view: R.prop(0),
|
view: R.prop(0),
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
editable: false
|
editable: false
|
||||||
|
|
@ -496,7 +543,7 @@ const getListCommissionsFields = (getData, currency, defaults) => {
|
||||||
header: cashInHeader,
|
header: cashInHeader,
|
||||||
name: 'cashIn',
|
name: 'cashIn',
|
||||||
display: 'Cash-in',
|
display: 'Cash-in',
|
||||||
width: 130,
|
width: 120,
|
||||||
input: NumberInput,
|
input: NumberInput,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
suffix: '%',
|
suffix: '%',
|
||||||
|
|
@ -509,7 +556,7 @@ const getListCommissionsFields = (getData, currency, defaults) => {
|
||||||
header: cashOutHeader,
|
header: cashOutHeader,
|
||||||
name: 'cashOut',
|
name: 'cashOut',
|
||||||
display: 'Cash-out',
|
display: 'Cash-out',
|
||||||
width: 140,
|
width: 126,
|
||||||
input: NumberInput,
|
input: NumberInput,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
greenText: true,
|
greenText: true,
|
||||||
|
|
@ -522,7 +569,7 @@ const getListCommissionsFields = (getData, currency, defaults) => {
|
||||||
{
|
{
|
||||||
name: 'fixedFee',
|
name: 'fixedFee',
|
||||||
display: 'Fixed fee',
|
display: 'Fixed fee',
|
||||||
width: 144,
|
width: 140,
|
||||||
input: NumberInput,
|
input: NumberInput,
|
||||||
doubleHeader: 'Cash-in only',
|
doubleHeader: 'Cash-in only',
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
|
|
@ -535,7 +582,7 @@ const getListCommissionsFields = (getData, currency, defaults) => {
|
||||||
{
|
{
|
||||||
name: 'minimumTx',
|
name: 'minimumTx',
|
||||||
display: 'Minimum Tx',
|
display: 'Minimum Tx',
|
||||||
width: 144,
|
width: 140,
|
||||||
input: NumberInput,
|
input: NumberInput,
|
||||||
doubleHeader: 'Cash-in only',
|
doubleHeader: 'Cash-in only',
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
|
|
@ -544,6 +591,20 @@ const getListCommissionsFields = (getData, currency, defaults) => {
|
||||||
inputProps: {
|
inputProps: {
|
||||||
decimalPlaces: 2
|
decimalPlaces: 2
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cashOutFixedFee',
|
||||||
|
display: 'Fixed fee',
|
||||||
|
width: 140,
|
||||||
|
input: NumberInput,
|
||||||
|
doubleHeader: 'Cash-out only',
|
||||||
|
textAlign: 'center',
|
||||||
|
editingAlign: 'right',
|
||||||
|
suffix: currency,
|
||||||
|
textStyle: obj => getTextStyle(obj),
|
||||||
|
inputProps: {
|
||||||
|
decimalPlaces: 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,10 +73,13 @@ const CustomerData = ({
|
||||||
authorizeCustomRequest,
|
authorizeCustomRequest,
|
||||||
updateCustomEntry,
|
updateCustomEntry,
|
||||||
retrieveAdditionalDataDialog,
|
retrieveAdditionalDataDialog,
|
||||||
setRetrieve
|
setRetrieve,
|
||||||
|
checkAgainstSanctions
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const [listView, setListView] = useState(false)
|
const [listView, setListView] = useState(false)
|
||||||
|
const [previewPhoto, setPreviewPhoto] = useState(null)
|
||||||
|
const [previewCard, setPreviewCard] = useState(null)
|
||||||
|
|
||||||
const idData = R.path(['idCardData'])(customer)
|
const idData = R.path(['idCardData'])(customer)
|
||||||
const rawExpirationDate = R.path(['expirationDate'])(idData)
|
const rawExpirationDate = R.path(['expirationDate'])(idData)
|
||||||
|
|
@ -172,6 +175,12 @@ const CustomerData = ({
|
||||||
idCardData: R.merge(idData, formatDates(values))
|
idCardData: R.merge(idData, formatDates(values))
|
||||||
}),
|
}),
|
||||||
validationSchema: customerDataSchemas.idCardData,
|
validationSchema: customerDataSchemas.idCardData,
|
||||||
|
checkAgainstSanctions: () =>
|
||||||
|
checkAgainstSanctions({
|
||||||
|
variables: {
|
||||||
|
customerId: R.path(['id'])(customer)
|
||||||
|
}
|
||||||
|
}),
|
||||||
initialValues: initialValues.idCardData,
|
initialValues: initialValues.idCardData,
|
||||||
isAvailable: !R.isNil(idData),
|
isAvailable: !R.isNil(idData),
|
||||||
editable: true
|
editable: true
|
||||||
|
|
@ -213,9 +222,6 @@ const CustomerData = ({
|
||||||
{
|
{
|
||||||
title: 'Name',
|
title: 'Name',
|
||||||
titleIcon: <EditIcon className={classes.editIcon} />,
|
titleIcon: <EditIcon className={classes.editIcon} />,
|
||||||
authorize: () => {},
|
|
||||||
reject: () => {},
|
|
||||||
save: () => {},
|
|
||||||
isAvailable: false,
|
isAvailable: false,
|
||||||
editable: true
|
editable: true
|
||||||
},
|
},
|
||||||
|
|
@ -226,7 +232,7 @@ const CustomerData = ({
|
||||||
authorize: () =>
|
authorize: () =>
|
||||||
updateCustomer({ sanctionsOverride: OVERRIDE_AUTHORIZED }),
|
updateCustomer({ sanctionsOverride: OVERRIDE_AUTHORIZED }),
|
||||||
reject: () => updateCustomer({ sanctionsOverride: OVERRIDE_REJECTED }),
|
reject: () => updateCustomer({ sanctionsOverride: OVERRIDE_REJECTED }),
|
||||||
children: <Info3>{sanctionsDisplay}</Info3>,
|
children: () => <Info3>{sanctionsDisplay}</Info3>,
|
||||||
isAvailable: !R.isNil(sanctions),
|
isAvailable: !R.isNil(sanctions),
|
||||||
editable: true
|
editable: true
|
||||||
},
|
},
|
||||||
|
|
@ -238,20 +244,33 @@ const CustomerData = ({
|
||||||
authorize: () =>
|
authorize: () =>
|
||||||
updateCustomer({ frontCameraOverride: OVERRIDE_AUTHORIZED }),
|
updateCustomer({ frontCameraOverride: OVERRIDE_AUTHORIZED }),
|
||||||
reject: () => updateCustomer({ frontCameraOverride: OVERRIDE_REJECTED }),
|
reject: () => updateCustomer({ frontCameraOverride: OVERRIDE_REJECTED }),
|
||||||
save: values =>
|
save: values => {
|
||||||
replacePhoto({
|
setPreviewPhoto(null)
|
||||||
|
return replacePhoto({
|
||||||
newPhoto: values.frontCamera,
|
newPhoto: values.frontCamera,
|
||||||
photoType: 'frontCamera'
|
photoType: 'frontCamera'
|
||||||
}),
|
})
|
||||||
|
},
|
||||||
|
cancel: () => setPreviewPhoto(null),
|
||||||
deleteEditedData: () => deleteEditedData({ frontCamera: null }),
|
deleteEditedData: () => deleteEditedData({ frontCamera: null }),
|
||||||
children: customer.frontCameraPath ? (
|
children: values => {
|
||||||
|
if (values.frontCamera !== previewPhoto) {
|
||||||
|
setPreviewPhoto(values.frontCamera)
|
||||||
|
}
|
||||||
|
|
||||||
|
return customer.frontCameraPath ? (
|
||||||
<Photo
|
<Photo
|
||||||
show={customer.frontCameraPath}
|
show={customer.frontCameraPath}
|
||||||
src={`${URI}/front-camera-photo/${R.path(['frontCameraPath'])(
|
src={
|
||||||
|
!R.isNil(previewPhoto)
|
||||||
|
? URL.createObjectURL(previewPhoto)
|
||||||
|
: `${URI}/front-camera-photo/${R.path(['frontCameraPath'])(
|
||||||
customer
|
customer
|
||||||
)}`}
|
)}`
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
) : null,
|
) : null
|
||||||
|
},
|
||||||
hasImage: true,
|
hasImage: true,
|
||||||
validationSchema: customerDataSchemas.frontCamera,
|
validationSchema: customerDataSchemas.frontCamera,
|
||||||
initialValues: initialValues.frontCamera,
|
initialValues: initialValues.frontCamera,
|
||||||
|
|
@ -266,18 +285,33 @@ const CustomerData = ({
|
||||||
authorize: () =>
|
authorize: () =>
|
||||||
updateCustomer({ idCardPhotoOverride: OVERRIDE_AUTHORIZED }),
|
updateCustomer({ idCardPhotoOverride: OVERRIDE_AUTHORIZED }),
|
||||||
reject: () => updateCustomer({ idCardPhotoOverride: OVERRIDE_REJECTED }),
|
reject: () => updateCustomer({ idCardPhotoOverride: OVERRIDE_REJECTED }),
|
||||||
save: values =>
|
save: values => {
|
||||||
replacePhoto({
|
setPreviewCard(null)
|
||||||
|
return replacePhoto({
|
||||||
newPhoto: values.idCardPhoto,
|
newPhoto: values.idCardPhoto,
|
||||||
photoType: 'idCardPhoto'
|
photoType: 'idCardPhoto'
|
||||||
}),
|
})
|
||||||
|
},
|
||||||
|
cancel: () => setPreviewCard(null),
|
||||||
deleteEditedData: () => deleteEditedData({ idCardPhoto: null }),
|
deleteEditedData: () => deleteEditedData({ idCardPhoto: null }),
|
||||||
children: customer.idCardPhotoPath ? (
|
children: values => {
|
||||||
|
if (values.idCardPhoto !== previewCard) {
|
||||||
|
setPreviewCard(values.idCardPhoto)
|
||||||
|
}
|
||||||
|
|
||||||
|
return customer.idCardPhotoPath ? (
|
||||||
<Photo
|
<Photo
|
||||||
show={customer.idCardPhotoPath}
|
show={customer.idCardPhotoPath}
|
||||||
src={`${URI}/id-card-photo/${R.path(['idCardPhotoPath'])(customer)}`}
|
src={
|
||||||
|
!R.isNil(previewCard)
|
||||||
|
? URL.createObjectURL(previewCard)
|
||||||
|
: `${URI}/id-card-photo/${R.path(['idCardPhotoPath'])(
|
||||||
|
customer
|
||||||
|
)}`
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
) : null,
|
) : null
|
||||||
|
},
|
||||||
hasImage: true,
|
hasImage: true,
|
||||||
validationSchema: customerDataSchemas.idCardPhoto,
|
validationSchema: customerDataSchemas.idCardPhoto,
|
||||||
initialValues: initialValues.idCardPhoto,
|
initialValues: initialValues.idCardPhoto,
|
||||||
|
|
@ -292,6 +326,7 @@ const CustomerData = ({
|
||||||
authorize: () => updateCustomer({ usSsnOverride: OVERRIDE_AUTHORIZED }),
|
authorize: () => updateCustomer({ usSsnOverride: OVERRIDE_AUTHORIZED }),
|
||||||
reject: () => updateCustomer({ usSsnOverride: OVERRIDE_REJECTED }),
|
reject: () => updateCustomer({ usSsnOverride: OVERRIDE_REJECTED }),
|
||||||
save: values => editCustomer(values),
|
save: values => editCustomer(values),
|
||||||
|
children: () => {},
|
||||||
deleteEditedData: () => deleteEditedData({ usSsn: null }),
|
deleteEditedData: () => deleteEditedData({ usSsn: null }),
|
||||||
validationSchema: customerDataSchemas.usSsn,
|
validationSchema: customerDataSchemas.usSsn,
|
||||||
initialValues: initialValues.usSsn,
|
initialValues: initialValues.usSsn,
|
||||||
|
|
@ -427,6 +462,7 @@ const CustomerData = ({
|
||||||
titleIcon,
|
titleIcon,
|
||||||
fields,
|
fields,
|
||||||
save,
|
save,
|
||||||
|
cancel,
|
||||||
deleteEditedData,
|
deleteEditedData,
|
||||||
retrieveAdditionalData,
|
retrieveAdditionalData,
|
||||||
children,
|
children,
|
||||||
|
|
@ -434,7 +470,8 @@ const CustomerData = ({
|
||||||
initialValues,
|
initialValues,
|
||||||
hasImage,
|
hasImage,
|
||||||
hasAdditionalData,
|
hasAdditionalData,
|
||||||
editable
|
editable,
|
||||||
|
checkAgainstSanctions
|
||||||
},
|
},
|
||||||
idx
|
idx
|
||||||
) => {
|
) => {
|
||||||
|
|
@ -453,8 +490,10 @@ const CustomerData = ({
|
||||||
validationSchema={validationSchema}
|
validationSchema={validationSchema}
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
save={save}
|
save={save}
|
||||||
|
cancel={cancel}
|
||||||
deleteEditedData={deleteEditedData}
|
deleteEditedData={deleteEditedData}
|
||||||
retrieveAdditionalData={retrieveAdditionalData}
|
retrieveAdditionalData={retrieveAdditionalData}
|
||||||
|
checkAgainstSanctions={checkAgainstSanctions}
|
||||||
editable={editable}></EditableCard>
|
editable={editable}></EditableCard>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation, useLazyQuery } from '@apollo/react-hooks'
|
||||||
import {
|
import {
|
||||||
makeStyles,
|
makeStyles,
|
||||||
Breadcrumbs,
|
Breadcrumbs,
|
||||||
|
|
@ -292,6 +292,14 @@ const GET_ACTIVE_CUSTOM_REQUESTS = gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const CHECK_AGAINST_SANCTIONS = gql`
|
||||||
|
query checkAgainstSanctions($customerId: ID) {
|
||||||
|
checkAgainstSanctions(customerId: $customerId) {
|
||||||
|
ofacSanctioned
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const CustomerProfile = memo(() => {
|
const CustomerProfile = memo(() => {
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
|
|
||||||
|
|
@ -400,6 +408,10 @@ const CustomerProfile = memo(() => {
|
||||||
onCompleted: () => getCustomer()
|
onCompleted: () => getCustomer()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const [checkAgainstSanctions] = useLazyQuery(CHECK_AGAINST_SANCTIONS, {
|
||||||
|
onCompleted: () => getCustomer()
|
||||||
|
})
|
||||||
|
|
||||||
const updateCustomer = it =>
|
const updateCustomer = it =>
|
||||||
setCustomer({
|
setCustomer({
|
||||||
variables: {
|
variables: {
|
||||||
|
|
@ -662,6 +674,7 @@ const CustomerProfile = memo(() => {
|
||||||
authorizeCustomRequest={authorizeCustomRequest}
|
authorizeCustomRequest={authorizeCustomRequest}
|
||||||
updateCustomEntry={updateCustomEntry}
|
updateCustomEntry={updateCustomEntry}
|
||||||
setRetrieve={setRetrieve}
|
setRetrieve={setRetrieve}
|
||||||
|
checkAgainstSanctions={checkAgainstSanctions}
|
||||||
retrieveAdditionalDataDialog={
|
retrieveAdditionalDataDialog={
|
||||||
<RetrieveDataDialog
|
<RetrieveDataDialog
|
||||||
onDismissed={() => {
|
onDismissed={() => {
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ const CustomersList = ({
|
||||||
view: getName
|
view: getName
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Total TXs',
|
header: 'Total Txs',
|
||||||
width: 126,
|
width: 126,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
view: it => `${Number.parseInt(it.totalTxs)}`
|
view: it => `${Number.parseInt(it.totalTxs)}`
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ const CustomerSidebar = ({ isSelected, onClick }) => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'customerData',
|
code: 'customerData',
|
||||||
display: 'Customer Data',
|
display: 'Customer data',
|
||||||
Icon: CustomerDataIcon,
|
Icon: CustomerDataIcon,
|
||||||
InverseIcon: CustomerDataReversedIcon
|
InverseIcon: CustomerDataReversedIcon
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,12 @@ import { makeStyles } from '@material-ui/core/styles'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import { Form, Formik, Field as FormikField } from 'formik'
|
import { Form, Formik, Field as FormikField } from 'formik'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import { useState, React } from 'react'
|
import { useState, React, useRef } from 'react'
|
||||||
|
|
||||||
import ErrorMessage from 'src/components/ErrorMessage'
|
import ErrorMessage from 'src/components/ErrorMessage'
|
||||||
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
||||||
import { MainStatus } from 'src/components/Status'
|
import { MainStatus } from 'src/components/Status'
|
||||||
|
// import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
import { ActionButton } from 'src/components/buttons'
|
import { ActionButton } from 'src/components/buttons'
|
||||||
import { Label1, P, H3 } from 'src/components/typography'
|
import { Label1, P, H3 } from 'src/components/typography'
|
||||||
import {
|
import {
|
||||||
|
|
@ -132,23 +133,27 @@ const ReadOnlyField = ({ field, value, ...props }) => {
|
||||||
|
|
||||||
const EditableCard = ({
|
const EditableCard = ({
|
||||||
fields,
|
fields,
|
||||||
save,
|
save = () => {},
|
||||||
authorize,
|
cancel = () => {},
|
||||||
|
authorize = () => {},
|
||||||
hasImage,
|
hasImage,
|
||||||
reject,
|
reject = () => {},
|
||||||
state,
|
state,
|
||||||
title,
|
title,
|
||||||
titleIcon,
|
titleIcon,
|
||||||
children,
|
children = () => {},
|
||||||
validationSchema,
|
validationSchema,
|
||||||
initialValues,
|
initialValues,
|
||||||
deleteEditedData,
|
deleteEditedData,
|
||||||
retrieveAdditionalData,
|
retrieveAdditionalData,
|
||||||
hasAdditionalData = true,
|
hasAdditionalData = true,
|
||||||
editable
|
editable,
|
||||||
|
checkAgainstSanctions
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const formRef = useRef()
|
||||||
|
|
||||||
const [editing, setEditing] = useState(false)
|
const [editing, setEditing] = useState(false)
|
||||||
const [input, setInput] = useState(null)
|
const [input, setInput] = useState(null)
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
|
|
@ -178,7 +183,7 @@ const EditableCard = ({
|
||||||
<H3 className={classes.cardTitle}>{title}</H3>
|
<H3 className={classes.cardTitle}>{title}</H3>
|
||||||
{
|
{
|
||||||
// TODO: Enable for next release
|
// TODO: Enable for next release
|
||||||
/* <HoverableTooltip width={304}></HoverableTooltip> */
|
/* <HelpTooltip width={304}></HelpTooltip> */
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
{state && authorize && (
|
{state && authorize && (
|
||||||
|
|
@ -187,8 +192,9 @@ const EditableCard = ({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{children}
|
{children(formRef.current?.values ?? {})}
|
||||||
<Formik
|
<Formik
|
||||||
|
innerRef={formRef}
|
||||||
validateOnBlur={false}
|
validateOnBlur={false}
|
||||||
validateOnChange={false}
|
validateOnChange={false}
|
||||||
enableReinitialize
|
enableReinitialize
|
||||||
|
|
@ -273,6 +279,16 @@ const EditableCard = ({
|
||||||
Retrieve API data
|
Retrieve API data
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
)}
|
)}
|
||||||
|
{checkAgainstSanctions && (
|
||||||
|
<ActionButton
|
||||||
|
color="primary"
|
||||||
|
type="button"
|
||||||
|
Icon={DataIcon}
|
||||||
|
InverseIcon={DataReversedIcon}
|
||||||
|
onClick={() => checkAgainstSanctions()}>
|
||||||
|
Check against OFAC sanction list
|
||||||
|
</ActionButton>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{editable && (
|
{editable && (
|
||||||
<ActionButton
|
<ActionButton
|
||||||
|
|
@ -359,6 +375,7 @@ const EditableCard = ({
|
||||||
color="secondary"
|
color="secondary"
|
||||||
Icon={CancelReversedIcon}
|
Icon={CancelReversedIcon}
|
||||||
InverseIcon={CancelReversedIcon}
|
InverseIcon={CancelReversedIcon}
|
||||||
|
onClick={() => cancel()}
|
||||||
type="reset">
|
type="reset">
|
||||||
Cancel
|
Cancel
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ const IdDataCard = memo(({ customerData, updateCustomer }) => {
|
||||||
size: 160
|
size: 160
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Birth Date',
|
header: 'Birth date',
|
||||||
display:
|
display:
|
||||||
(rawDob &&
|
(rawDob &&
|
||||||
format('yyyy-MM-dd')(parse(new Date(), 'yyyyMMdd', rawDob))) ??
|
format('yyyy-MM-dd')(parse(new Date(), 'yyyyMMdd', rawDob))) ??
|
||||||
|
|
@ -61,7 +61,7 @@ const IdDataCard = memo(({ customerData, updateCustomer }) => {
|
||||||
size: 120
|
size: 120
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Expiration Date',
|
header: 'Expiration date',
|
||||||
display: ifNotNull(
|
display: ifNotNull(
|
||||||
rawExpirationDate,
|
rawExpirationDate,
|
||||||
format('yyyy-MM-dd', rawExpirationDate)
|
format('yyyy-MM-dd', rawExpirationDate)
|
||||||
|
|
|
||||||
|
|
@ -411,7 +411,7 @@ const customerDataElements = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'expirationDate',
|
name: 'expirationDate',
|
||||||
label: 'Expiration Date',
|
label: 'Expiration date',
|
||||||
component: TextInput,
|
component: TextInput,
|
||||||
editable: true
|
editable: true
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,7 @@ import React, { useEffect, useRef, useCallback } from 'react'
|
||||||
|
|
||||||
import { backgroundColor, zircon, primaryColor } from 'src/styling/variables'
|
import { backgroundColor, zircon, primaryColor } from 'src/styling/variables'
|
||||||
|
|
||||||
const transactionProfit = tx => {
|
const transactionProfit = R.prop('profit')
|
||||||
const cashInFee = tx.cashInFee ? Number.parseFloat(tx.cashInFee) : 0
|
|
||||||
const commission =
|
|
||||||
Number.parseFloat(tx.commissionPercentage) * Number.parseFloat(tx.fiat)
|
|
||||||
return commission + cashInFee
|
|
||||||
}
|
|
||||||
|
|
||||||
const mockPoint = (tx, offsetMs, profit) => {
|
const mockPoint = (tx, offsetMs, profit) => {
|
||||||
const date = new Date(new Date(tx.created).getTime() + offsetMs).toISOString()
|
const date = new Date(new Date(tx.created).getTime() + offsetMs).toISOString()
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ const GET_DATA = gql`
|
||||||
transactions(excludeTestingCustomers: $excludeTestingCustomers) {
|
transactions(excludeTestingCustomers: $excludeTestingCustomers) {
|
||||||
fiatCode
|
fiatCode
|
||||||
fiat
|
fiat
|
||||||
cashInFee
|
fixedFee
|
||||||
commissionPercentage
|
commissionPercentage
|
||||||
created
|
created
|
||||||
txClass
|
txClass
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,7 @@ const Funding = () => {
|
||||||
{funding.length && (
|
{funding.length && (
|
||||||
<div className={classes.total}>
|
<div className={classes.total}>
|
||||||
<Label1 className={classes.totalTitle}>
|
<Label1 className={classes.totalTitle}>
|
||||||
Total Crypto Balance
|
Total crypto balance
|
||||||
</Label1>
|
</Label1>
|
||||||
<Info1 noMargin>
|
<Info1 noMargin>
|
||||||
{getConfirmedTotal(funding)}
|
{getConfirmedTotal(funding)}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import Modal from 'src/components/Modal'
|
import Modal from 'src/components/Modal'
|
||||||
import { Link } from 'src/components/buttons'
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
|
import { Link, SupportLinkButton } from 'src/components/buttons'
|
||||||
import { Table as EditableTable } from 'src/components/editableTable'
|
import { Table as EditableTable } from 'src/components/editableTable'
|
||||||
import Section from 'src/components/layout/Section'
|
import Section from 'src/components/layout/Section'
|
||||||
import TitleSection from 'src/components/layout/TitleSection'
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
|
|
@ -61,8 +62,9 @@ const GET_DATA = gql`
|
||||||
`
|
`
|
||||||
|
|
||||||
const SAVE_CONFIG = gql`
|
const SAVE_CONFIG = gql`
|
||||||
mutation Save($config: JSONObject) {
|
mutation Save($config: JSONObject, $accounts: JSONObject) {
|
||||||
saveConfig(config: $config)
|
saveConfig(config: $config)
|
||||||
|
saveAccounts(accounts: $accounts)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
@ -134,9 +136,9 @@ const Locales = ({ name: SCREEN_KEY }) => {
|
||||||
return save(newConfig)
|
return save(newConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
const save = config => {
|
const save = (config, accounts) => {
|
||||||
setDataToSave(null)
|
setDataToSave(null)
|
||||||
return saveConfig({ variables: { config } })
|
return saveConfig({ variables: { config, accounts } })
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveOverrides = it => {
|
const saveOverrides = it => {
|
||||||
|
|
@ -162,8 +164,8 @@ const Locales = ({ name: SCREEN_KEY }) => {
|
||||||
const onEditingDefault = (it, editing) => setEditingDefault(editing)
|
const onEditingDefault = (it, editing) => setEditingDefault(editing)
|
||||||
const onEditingOverrides = (it, editing) => setEditingOverrides(editing)
|
const onEditingOverrides = (it, editing) => setEditingOverrides(editing)
|
||||||
|
|
||||||
const wizardSave = it =>
|
const wizardSave = (config, accounts) =>
|
||||||
save(toNamespace(namespaces.WALLETS)(it)).then(it => {
|
save(toNamespace(namespaces.WALLETS)(config), accounts).then(it => {
|
||||||
onChangeFunction()
|
onChangeFunction()
|
||||||
setOnChangeFunction(null)
|
setOnChangeFunction(null)
|
||||||
return it
|
return it
|
||||||
|
|
@ -176,7 +178,22 @@ const Locales = ({ name: SCREEN_KEY }) => {
|
||||||
close={() => setDataToSave(null)}
|
close={() => setDataToSave(null)}
|
||||||
save={() => dataToSave && save(dataToSave)}
|
save={() => dataToSave && save(dataToSave)}
|
||||||
/>
|
/>
|
||||||
<TitleSection title="Locales" />
|
<TitleSection
|
||||||
|
title="Locales"
|
||||||
|
appendix={
|
||||||
|
<HelpTooltip width={320}>
|
||||||
|
<P>
|
||||||
|
For details on configuring languages, please read the relevant
|
||||||
|
knowledgebase article:
|
||||||
|
</P>
|
||||||
|
<SupportLinkButton
|
||||||
|
link="https://support.lamassu.is/hc/en-us/articles/360016257471-Setting-multiple-machine-languages"
|
||||||
|
label="Setting multiple machine languages"
|
||||||
|
bottomSpace="1"
|
||||||
|
/>
|
||||||
|
</HelpTooltip>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<Section>
|
<Section>
|
||||||
<EditableTable
|
<EditableTable
|
||||||
title="Default settings"
|
title="Default settings"
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ const LocaleSchema = Yup.object().shape({
|
||||||
.label('Country')
|
.label('Country')
|
||||||
.required(),
|
.required(),
|
||||||
fiatCurrency: Yup.string()
|
fiatCurrency: Yup.string()
|
||||||
.label('Fiat Currency')
|
.label('Fiat currency')
|
||||||
.required(),
|
.required(),
|
||||||
languages: Yup.array()
|
languages: Yup.array()
|
||||||
.label('Languages')
|
.label('Languages')
|
||||||
|
|
@ -165,7 +165,7 @@ const LocaleSchema = Yup.object().shape({
|
||||||
.min(1)
|
.min(1)
|
||||||
.max(4),
|
.max(4),
|
||||||
cryptoCurrencies: Yup.array()
|
cryptoCurrencies: Yup.array()
|
||||||
.label('Crypto Currencies')
|
.label('Crypto currencies')
|
||||||
.required()
|
.required()
|
||||||
.min(1),
|
.min(1),
|
||||||
timezone: Yup.string()
|
timezone: Yup.string()
|
||||||
|
|
@ -186,7 +186,7 @@ const OverridesSchema = Yup.object().shape({
|
||||||
.min(1)
|
.min(1)
|
||||||
.max(4),
|
.max(4),
|
||||||
cryptoCurrencies: Yup.array()
|
cryptoCurrencies: Yup.array()
|
||||||
.label('Crypto Currencies')
|
.label('Crypto currencies')
|
||||||
.required()
|
.required()
|
||||||
.min(1)
|
.min(1)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import * as Yup from 'yup'
|
||||||
|
|
||||||
import ErrorMessage from 'src/components/ErrorMessage'
|
import ErrorMessage from 'src/components/ErrorMessage'
|
||||||
import Modal from 'src/components/Modal'
|
import Modal from 'src/components/Modal'
|
||||||
import { HoverableTooltip } from 'src/components/Tooltip'
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
import { Button } from 'src/components/buttons'
|
import { Button } from 'src/components/buttons'
|
||||||
import { NumberInput, Autocomplete } from 'src/components/inputs/formik'
|
import { NumberInput, Autocomplete } from 'src/components/inputs/formik'
|
||||||
import { H3, TL1, P } from 'src/components/typography'
|
import { H3, TL1, P } from 'src/components/typography'
|
||||||
|
|
@ -99,7 +99,7 @@ const IndividualDiscountModal = ({
|
||||||
<div>
|
<div>
|
||||||
<div className={classes.discountRateWrapper}>
|
<div className={classes.discountRateWrapper}>
|
||||||
<H3>Define discount rate</H3>
|
<H3>Define discount rate</H3>
|
||||||
<HoverableTooltip width={304}>
|
<HelpTooltip width={304}>
|
||||||
<P>
|
<P>
|
||||||
This is a percentage discount off of your existing
|
This is a percentage discount off of your existing
|
||||||
commission rates for a customer entering this code at
|
commission rates for a customer entering this code at
|
||||||
|
|
@ -110,7 +110,7 @@ const IndividualDiscountModal = ({
|
||||||
code is set for 50%, then you'll instead be charging 4%
|
code is set for 50%, then you'll instead be charging 4%
|
||||||
on transactions using the code.
|
on transactions using the code.
|
||||||
</P>
|
</P>
|
||||||
</HoverableTooltip>
|
</HelpTooltip>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.discountInput}>
|
<div className={classes.discountInput}>
|
||||||
<Field
|
<Field
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import * as Yup from 'yup'
|
||||||
|
|
||||||
import ErrorMessage from 'src/components/ErrorMessage'
|
import ErrorMessage from 'src/components/ErrorMessage'
|
||||||
import Modal from 'src/components/Modal'
|
import Modal from 'src/components/Modal'
|
||||||
import { HoverableTooltip } from 'src/components/Tooltip'
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
import { Button } from 'src/components/buttons'
|
import { Button } from 'src/components/buttons'
|
||||||
import { TextInput, NumberInput } from 'src/components/inputs/formik'
|
import { TextInput, NumberInput } from 'src/components/inputs/formik'
|
||||||
import { H3, TL1, P } from 'src/components/typography'
|
import { H3, TL1, P } from 'src/components/typography'
|
||||||
|
|
@ -69,7 +69,7 @@ const PromoCodesModal = ({ showModal, onClose, errorMsg, addCode }) => {
|
||||||
/>
|
/>
|
||||||
<div className={classes.modalLabel2Wrapper}>
|
<div className={classes.modalLabel2Wrapper}>
|
||||||
<H3 className={classes.modalLabel2}>Define discount rate</H3>
|
<H3 className={classes.modalLabel2}>Define discount rate</H3>
|
||||||
<HoverableTooltip width={304}>
|
<HelpTooltip width={304}>
|
||||||
<P>
|
<P>
|
||||||
This is a percentage discount off of your existing
|
This is a percentage discount off of your existing
|
||||||
commission rates for a customer entering this code at the
|
commission rates for a customer entering this code at the
|
||||||
|
|
@ -80,7 +80,7 @@ const PromoCodesModal = ({ showModal, onClose, errorMsg, addCode }) => {
|
||||||
set for 50%, then you'll instead be charging 4% on
|
set for 50%, then you'll instead be charging 4% on
|
||||||
transactions using the code.
|
transactions using the code.
|
||||||
</P>
|
</P>
|
||||||
</HoverableTooltip>
|
</HelpTooltip>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.discountInput}>
|
<div className={classes.discountInput}>
|
||||||
<Field
|
<Field
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ const Logs = () => {
|
||||||
<>
|
<>
|
||||||
<div className={classes.titleWrapper}>
|
<div className={classes.titleWrapper}>
|
||||||
<div className={classes.titleAndButtonsContainer}>
|
<div className={classes.titleAndButtonsContainer}>
|
||||||
<Title>Machine Logs</Title>
|
<Title>Machine logs</Title>
|
||||||
{logsResponse && (
|
{logsResponse && (
|
||||||
<div className={classes.buttonsWrapper}>
|
<div className={classes.buttonsWrapper}>
|
||||||
<LogsDowloaderPopover
|
<LogsDowloaderPopover
|
||||||
|
|
|
||||||
|
|
@ -64,10 +64,11 @@ const Commissions = ({ name: SCREEN_KEY, id: deviceId }) => {
|
||||||
cashIn: config.cashIn,
|
cashIn: config.cashIn,
|
||||||
cashOut: config.cashOut,
|
cashOut: config.cashOut,
|
||||||
fixedFee: config.fixedFee,
|
fixedFee: config.fixedFee,
|
||||||
minimumTx: config.minimumTx
|
minimumTx: config.minimumTx,
|
||||||
|
cashOutFixedFee: config.cashOutFixedFee
|
||||||
},
|
},
|
||||||
R.project(
|
R.project(
|
||||||
['cashIn', 'cashOut', 'fixedFee', 'minimumTx'],
|
['cashIn', 'cashOut', 'fixedFee', 'minimumTx', 'cashOutFixedFee'],
|
||||||
R.filter(
|
R.filter(
|
||||||
o =>
|
o =>
|
||||||
R.includes(coin.code, o.cryptoCurrencies) ||
|
R.includes(coin.code, o.cryptoCurrencies) ||
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,14 @@ const getOverridesFields = currency => {
|
||||||
doubleHeader: 'Cash-in only',
|
doubleHeader: 'Cash-in only',
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
suffix: currency
|
suffix: currency
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cashOutFixedFee',
|
||||||
|
display: 'Fixed fee',
|
||||||
|
width: 155,
|
||||||
|
doubleHeader: 'Cash-out only',
|
||||||
|
textAlign: 'right',
|
||||||
|
suffix: currency
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ const GET_TRANSACTIONS = gql`
|
||||||
hasError: error
|
hasError: error
|
||||||
deviceId
|
deviceId
|
||||||
fiat
|
fiat
|
||||||
cashInFee
|
fixedFee
|
||||||
fiatCode
|
fiatCode
|
||||||
cryptoAtoms
|
cryptoAtoms
|
||||||
cryptoCode
|
cryptoCode
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ import React, { useState } from 'react'
|
||||||
|
|
||||||
import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper'
|
import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper'
|
||||||
import Modal from 'src/components/Modal'
|
import Modal from 'src/components/Modal'
|
||||||
import { IconButton, Button } from 'src/components/buttons'
|
import { HelpTooltip } from 'src/components/Tooltip.js'
|
||||||
|
import { IconButton, Button, SupportLinkButton } from 'src/components/buttons'
|
||||||
import { RadioGroup } from 'src/components/inputs'
|
import { RadioGroup } from 'src/components/inputs'
|
||||||
import TitleSection from 'src/components/layout/TitleSection'
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
import { EmptyTable } from 'src/components/table'
|
import { EmptyTable } from 'src/components/table'
|
||||||
|
|
@ -204,7 +205,7 @@ const CashCassettes = () => {
|
||||||
!dataLoading && (
|
!dataLoading && (
|
||||||
<>
|
<>
|
||||||
<TitleSection
|
<TitleSection
|
||||||
title="Cash Boxes & Cassettes"
|
title="Cash boxes & cassettes"
|
||||||
buttons={[
|
buttons={[
|
||||||
{
|
{
|
||||||
text: 'Cash box history',
|
text: 'Cash box history',
|
||||||
|
|
@ -229,7 +230,20 @@ const CashCassettes = () => {
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
iconClassName={classes.listViewButton}
|
iconClassName={classes.listViewButton}
|
||||||
className={classes.tableWidth}>
|
className={classes.tableWidth}
|
||||||
|
appendix={
|
||||||
|
<HelpTooltip width={220}>
|
||||||
|
<P>
|
||||||
|
For details on configuring cash boxes and cassettes, please read
|
||||||
|
the relevant knowledgebase article:
|
||||||
|
</P>
|
||||||
|
<SupportLinkButton
|
||||||
|
link="https://support.lamassu.is/hc/en-us/articles/4420839641229-Cash-Boxes-Cassettess"
|
||||||
|
label="Cash Boxes & Cassettes"
|
||||||
|
bottomSpace="1"
|
||||||
|
/>
|
||||||
|
</HelpTooltip>
|
||||||
|
}>
|
||||||
{!showHistory && (
|
{!showHistory && (
|
||||||
<Box alignItems="center" justifyContent="flex-end">
|
<Box alignItems="center" justifyContent="flex-end">
|
||||||
<Label1 className={classes.cashboxReset}>Cash box resets</Label1>
|
<Label1 className={classes.cashboxReset}>Cash box resets</Label1>
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@ const CashboxHistory = ({ machines, currency, timezone }) => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'billCount',
|
name: 'billCount',
|
||||||
header: 'Bill Count',
|
header: 'Bill count',
|
||||||
width: 115,
|
width: 115,
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
input: NumberInput,
|
input: NumberInput,
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ const MachineDetailsRow = ({ it: machine, onActionSuccess, timezone }) => {
|
||||||
<Item xs>
|
<Item xs>
|
||||||
<Container className={classes.row}>
|
<Container className={classes.row}>
|
||||||
<Item xs={2}>
|
<Item xs={2}>
|
||||||
<Label>Machine Model</Label>
|
<Label>Machine model</Label>
|
||||||
<span>{modelPrettifier[machine.model]}</span>
|
<span>{modelPrettifier[machine.model]}</span>
|
||||||
</Item>
|
</Item>
|
||||||
<Item xs={4}>
|
<Item xs={4}>
|
||||||
|
|
@ -126,7 +126,7 @@ const MachineDetailsRow = ({ it: machine, onActionSuccess, timezone }) => {
|
||||||
</span>
|
</span>
|
||||||
</Item>
|
</Item>
|
||||||
<Item xs={2}>
|
<Item xs={2}>
|
||||||
<Label>Packet Loss</Label>
|
<Label>Packet loss</Label>
|
||||||
<span>
|
<span>
|
||||||
{machine.packetLoss
|
{machine.packetLoss
|
||||||
? new BigNumber(machine.packetLoss).toFixed(3).toString() +
|
? new BigNumber(machine.packetLoss).toFixed(3).toString() +
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ const MachineStatus = () => {
|
||||||
|
|
||||||
const elements = [
|
const elements = [
|
||||||
{
|
{
|
||||||
header: 'Machine Name',
|
header: 'Machine name',
|
||||||
width: 250,
|
width: 250,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
|
|
@ -111,7 +111,7 @@ const MachineStatus = () => {
|
||||||
: 'unknown'
|
: 'unknown'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Software Version',
|
header: 'Software version',
|
||||||
width: 200,
|
width: 200,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
|
|
@ -134,7 +134,7 @@ const MachineStatus = () => {
|
||||||
<>
|
<>
|
||||||
<div className={classes.titleWrapper}>
|
<div className={classes.titleWrapper}>
|
||||||
<div className={classes.titleAndButtonsContainer}>
|
<div className={classes.titleAndButtonsContainer}>
|
||||||
<Title>Machine Status</Title>
|
<Title>Machine status</Title>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.headerLabels}>
|
<div className={classes.headerLabels}>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import React from 'react'
|
||||||
|
|
||||||
import ErrorMessage from 'src/components/ErrorMessage'
|
import ErrorMessage from 'src/components/ErrorMessage'
|
||||||
import Stepper from 'src/components/Stepper'
|
import Stepper from 'src/components/Stepper'
|
||||||
import { HoverableTooltip } from 'src/components/Tooltip'
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
import { Button } from 'src/components/buttons'
|
import { Button } from 'src/components/buttons'
|
||||||
import { Cashbox } from 'src/components/inputs/cashbox/Cashbox'
|
import { Cashbox } from 'src/components/inputs/cashbox/Cashbox'
|
||||||
import { NumberInput, RadioGroup } from 'src/components/inputs/formik'
|
import { NumberInput, RadioGroup } from 'src/components/inputs/formik'
|
||||||
|
|
@ -245,12 +245,12 @@ const WizardStep = ({
|
||||||
classes.centerAlignment
|
classes.centerAlignment
|
||||||
)}>
|
)}>
|
||||||
<P>Since previous update</P>
|
<P>Since previous update</P>
|
||||||
<HoverableTooltip width={215}>
|
<HelpTooltip width={215}>
|
||||||
<P>
|
<P>
|
||||||
Number of bills inside the cash box, since the last
|
Number of bills inside the cash box, since the last
|
||||||
cash box changes.
|
cash box changes.
|
||||||
</P>
|
</P>
|
||||||
</HoverableTooltip>
|
</HelpTooltip>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={classnames(
|
className={classnames(
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import Modal from 'src/components/Modal'
|
import Modal from 'src/components/Modal'
|
||||||
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
|
import { SupportLinkButton } from 'src/components/buttons'
|
||||||
import TitleSection from 'src/components/layout/TitleSection'
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
import { P } from 'src/components/typography'
|
import { P } from 'src/components/typography'
|
||||||
import FormRenderer from 'src/pages/Services/FormRenderer'
|
import FormRenderer from 'src/pages/Services/FormRenderer'
|
||||||
|
|
@ -159,7 +161,24 @@ const Notifications = ({
|
||||||
!loading && (
|
!loading && (
|
||||||
<>
|
<>
|
||||||
<NotificationsCtx.Provider value={contextValue}>
|
<NotificationsCtx.Provider value={contextValue}>
|
||||||
{displayTitle && <TitleSection title="Notifications" />}
|
{displayTitle && (
|
||||||
|
<TitleSection
|
||||||
|
title="Notifications"
|
||||||
|
appendix={
|
||||||
|
<HelpTooltip width={250}>
|
||||||
|
<P>
|
||||||
|
For details on configuring notifications, please read the
|
||||||
|
relevant knowledgebase article:
|
||||||
|
</P>
|
||||||
|
<SupportLinkButton
|
||||||
|
link="https://support.lamassu.is/hc/en-us/articles/115001210592-Enabling-notifications"
|
||||||
|
label="Enabling notifications"
|
||||||
|
bottomSpace="1"
|
||||||
|
/>
|
||||||
|
</HelpTooltip>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{displayThirdPartyProvider && (
|
{displayThirdPartyProvider && (
|
||||||
<Section
|
<Section
|
||||||
title="Third party providers"
|
title="Third party providers"
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ const CryptoBalanceAlerts = ({ section, fieldWidth }) => {
|
||||||
section={section}
|
section={section}
|
||||||
decoration={currency}
|
decoration={currency}
|
||||||
className={classes.cryptoBalanceAlertsForm}
|
className={classes.cryptoBalanceAlertsForm}
|
||||||
title="Default (Low Balance)"
|
title="Default (Low balance)"
|
||||||
label="Alert me under"
|
label="Alert me under"
|
||||||
editing={isEditing(LOW_BALANCE_KEY)}
|
editing={isEditing(LOW_BALANCE_KEY)}
|
||||||
disabled={isDisabled(LOW_BALANCE_KEY)}
|
disabled={isDisabled(LOW_BALANCE_KEY)}
|
||||||
|
|
@ -49,7 +49,7 @@ const CryptoBalanceAlerts = ({ section, fieldWidth }) => {
|
||||||
save={save}
|
save={save}
|
||||||
decoration={currency}
|
decoration={currency}
|
||||||
className={classes.cryptoBalanceAlertsSecondForm}
|
className={classes.cryptoBalanceAlertsSecondForm}
|
||||||
title="Default (High Balance)"
|
title="Default (High balance)"
|
||||||
label="Alert me over"
|
label="Alert me over"
|
||||||
editing={isEditing(HIGH_BALANCE_KEY)}
|
editing={isEditing(HIGH_BALANCE_KEY)}
|
||||||
disabled={isDisabled(HIGH_BALANCE_KEY)}
|
disabled={isDisabled(HIGH_BALANCE_KEY)}
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ const CryptoBalanceOverrides = ({ section }) => {
|
||||||
.nullable()
|
.nullable()
|
||||||
.required(),
|
.required(),
|
||||||
[LOW_BALANCE_KEY]: Yup.number()
|
[LOW_BALANCE_KEY]: Yup.number()
|
||||||
.label('Low Balance')
|
.label('Low balance')
|
||||||
.when(HIGH_BALANCE_KEY, {
|
.when(HIGH_BALANCE_KEY, {
|
||||||
is: HIGH_BALANCE_KEY => !HIGH_BALANCE_KEY,
|
is: HIGH_BALANCE_KEY => !HIGH_BALANCE_KEY,
|
||||||
then: Yup.number().required()
|
then: Yup.number().required()
|
||||||
|
|
@ -73,7 +73,7 @@ const CryptoBalanceOverrides = ({ section }) => {
|
||||||
.max(CURRENCY_MAX)
|
.max(CURRENCY_MAX)
|
||||||
.nullable(),
|
.nullable(),
|
||||||
[HIGH_BALANCE_KEY]: Yup.number()
|
[HIGH_BALANCE_KEY]: Yup.number()
|
||||||
.label('High Balance')
|
.label('High balance')
|
||||||
.when(LOW_BALANCE_KEY, {
|
.when(LOW_BALANCE_KEY, {
|
||||||
is: LOW_BALANCE_KEY => !LOW_BALANCE_KEY,
|
is: LOW_BALANCE_KEY => !LOW_BALANCE_KEY,
|
||||||
then: Yup.number().required()
|
then: Yup.number().required()
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import {
|
||||||
} from 'src/components/fake-table/Table'
|
} from 'src/components/fake-table/Table'
|
||||||
import { Switch } from 'src/components/inputs'
|
import { Switch } from 'src/components/inputs'
|
||||||
import { fromNamespace, toNamespace } from 'src/utils/config'
|
import { fromNamespace, toNamespace } from 'src/utils/config'
|
||||||
import { startCase } from 'src/utils/string'
|
import { sentenceCase } from 'src/utils/string'
|
||||||
|
|
||||||
import NotificationsCtx from '../NotificationsContext'
|
import NotificationsCtx from '../NotificationsContext'
|
||||||
|
|
||||||
|
|
@ -62,7 +62,7 @@ const Row = ({
|
||||||
return (
|
return (
|
||||||
<Tr>
|
<Tr>
|
||||||
<Td width={channelSize}>
|
<Td width={channelSize}>
|
||||||
{shouldUpperCase ? R.toUpper(namespace) : startCase(namespace)}
|
{shouldUpperCase ? R.toUpper(namespace) : sentenceCase(namespace)}
|
||||||
</Td>
|
</Td>
|
||||||
<Cell name="balance" disabled={disabled} />
|
<Cell name="balance" disabled={disabled} />
|
||||||
<Cell name="transactions" disabled={disabled} />
|
<Cell name="transactions" disabled={disabled} />
|
||||||
|
|
@ -127,7 +127,7 @@ const Setup = ({ wizard, forceDisable }) => {
|
||||||
<Th width={channelSize - widthAdjust}>Channel</Th>
|
<Th width={channelSize - widthAdjust}>Channel</Th>
|
||||||
{Object.keys(sizes).map(it => (
|
{Object.keys(sizes).map(it => (
|
||||||
<Th key={it} width={sizes[it] - widthAdjust} textAlign="center">
|
<Th key={it} width={sizes[it] - widthAdjust} textAlign="center">
|
||||||
{startCase(it)}
|
{sentenceCase(it)}
|
||||||
</Th>
|
</Th>
|
||||||
))}
|
))}
|
||||||
</THead>
|
</THead>
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,14 @@ import { makeStyles } from '@material-ui/core/styles'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import React, { memo } from 'react'
|
import React, { memo } from 'react'
|
||||||
|
|
||||||
import { HoverableTooltip } from 'src/components/Tooltip'
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
import { BooleanPropertiesTable } from 'src/components/booleanPropertiesTable'
|
import { BooleanPropertiesTable } from 'src/components/booleanPropertiesTable'
|
||||||
import { Switch } from 'src/components/inputs'
|
import { Switch } from 'src/components/inputs'
|
||||||
import { H4, P, Label2 } from 'src/components/typography'
|
import { H4, P, Label2 } from 'src/components/typography'
|
||||||
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
||||||
|
|
||||||
|
import { SupportLinkButton } from '../../components/buttons'
|
||||||
|
|
||||||
import { global } from './OperatorInfo.styles'
|
import { global } from './OperatorInfo.styles'
|
||||||
|
|
||||||
const useStyles = makeStyles(global)
|
const useStyles = makeStyles(global)
|
||||||
|
|
@ -66,7 +68,7 @@ const CoinATMRadar = memo(({ wizard }) => {
|
||||||
<div>
|
<div>
|
||||||
<div className={classes.header}>
|
<div className={classes.header}>
|
||||||
<H4>Coin ATM Radar share settings</H4>
|
<H4>Coin ATM Radar share settings</H4>
|
||||||
<HoverableTooltip width={304}>
|
<HelpTooltip width={320}>
|
||||||
<P>
|
<P>
|
||||||
For details on configuring this panel, please read the relevant
|
For details on configuring this panel, please read the relevant
|
||||||
knowledgebase article{' '}
|
knowledgebase article{' '}
|
||||||
|
|
@ -78,7 +80,12 @@ const CoinATMRadar = memo(({ wizard }) => {
|
||||||
</a>
|
</a>
|
||||||
.
|
.
|
||||||
</P>
|
</P>
|
||||||
</HoverableTooltip>
|
<SupportLinkButton
|
||||||
|
link="https://support.lamassu.is/hc/en-us/articles/360023720472-Coin-ATM-Radar"
|
||||||
|
label="Lamassu Support Article"
|
||||||
|
bottomSpace="1"
|
||||||
|
/>
|
||||||
|
</HelpTooltip>
|
||||||
</div>
|
</div>
|
||||||
<Row
|
<Row
|
||||||
title={'Share information?'}
|
title={'Share information?'}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ import * as Yup from 'yup'
|
||||||
|
|
||||||
import ErrorMessage from 'src/components/ErrorMessage'
|
import ErrorMessage from 'src/components/ErrorMessage'
|
||||||
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
||||||
import { Link, IconButton } from 'src/components/buttons'
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
|
import { Link, IconButton, SupportLinkButton } from 'src/components/buttons'
|
||||||
import Switch from 'src/components/inputs/base/Switch'
|
import Switch from 'src/components/inputs/base/Switch'
|
||||||
import { TextInput } from 'src/components/inputs/formik'
|
import { TextInput } from 'src/components/inputs/formik'
|
||||||
import { P, H4, Info3, Label1, Label2, Label3 } from 'src/components/typography'
|
import { P, H4, Info3, Label1, Label2, Label3 } from 'src/components/typography'
|
||||||
|
|
@ -136,7 +137,7 @@ const ContactInfo = ({ wizard }) => {
|
||||||
const fields = [
|
const fields = [
|
||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
label: 'Full name',
|
label: 'Company name',
|
||||||
value: info.name ?? '',
|
value: info.name ?? '',
|
||||||
component: TextInput
|
component: TextInput
|
||||||
},
|
},
|
||||||
|
|
@ -160,7 +161,7 @@ const ContactInfo = ({ wizard }) => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'companyNumber',
|
name: 'companyNumber',
|
||||||
label: 'Company number',
|
label: 'Company registration number',
|
||||||
value: info.companyNumber ?? '',
|
value: info.companyNumber ?? '',
|
||||||
component: TextInput
|
component: TextInput
|
||||||
}
|
}
|
||||||
|
|
@ -189,6 +190,17 @@ const ContactInfo = ({ wizard }) => {
|
||||||
<>
|
<>
|
||||||
<div className={classes.header}>
|
<div className={classes.header}>
|
||||||
<H4>Contact information</H4>
|
<H4>Contact information</H4>
|
||||||
|
<HelpTooltip width={320}>
|
||||||
|
<P>
|
||||||
|
For details on configuring this panel, please read the relevant
|
||||||
|
knowledgebase article:
|
||||||
|
</P>
|
||||||
|
<SupportLinkButton
|
||||||
|
link="https://support.lamassu.is/hc/en-us/articles/360033051732-Enabling-Operator-Info"
|
||||||
|
label="Lamassu Support Article"
|
||||||
|
bottomSpace="1"
|
||||||
|
/>
|
||||||
|
</HelpTooltip>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.switchRow}>
|
<div className={classes.switchRow}>
|
||||||
<P>Info card enabled?</P>
|
<P>Info card enabled?</P>
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,14 @@ import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { memo } from 'react'
|
import React, { memo } from 'react'
|
||||||
|
|
||||||
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
import { BooleanPropertiesTable } from 'src/components/booleanPropertiesTable'
|
import { BooleanPropertiesTable } from 'src/components/booleanPropertiesTable'
|
||||||
import { Switch } from 'src/components/inputs'
|
import { Switch } from 'src/components/inputs'
|
||||||
import { H4, P, Label2 } from 'src/components/typography'
|
import { H4, P, Label2 } from 'src/components/typography'
|
||||||
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
||||||
|
|
||||||
|
import { SupportLinkButton } from '../../components/buttons'
|
||||||
|
|
||||||
import { global } from './OperatorInfo.styles'
|
import { global } from './OperatorInfo.styles'
|
||||||
|
|
||||||
const useStyles = makeStyles(global)
|
const useStyles = makeStyles(global)
|
||||||
|
|
@ -47,6 +50,17 @@ const ReceiptPrinting = memo(({ wizard }) => {
|
||||||
<>
|
<>
|
||||||
<div className={classes.header}>
|
<div className={classes.header}>
|
||||||
<H4>Receipt options</H4>
|
<H4>Receipt options</H4>
|
||||||
|
<HelpTooltip width={320}>
|
||||||
|
<P>
|
||||||
|
For details on configuring this panel, please read the relevant
|
||||||
|
knowledgebase article:
|
||||||
|
</P>
|
||||||
|
<SupportLinkButton
|
||||||
|
link="https://support.lamassu.is/hc/en-us/articles/360058513951-Receipt-options-printers"
|
||||||
|
label="Lamassu Support Article"
|
||||||
|
bottomSpace="1"
|
||||||
|
/>
|
||||||
|
</HelpTooltip>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.switchRow}>
|
<div className={classes.switchRow}>
|
||||||
<P>Enable receipt printing</P>
|
<P>Enable receipt printing</P>
|
||||||
|
|
@ -109,7 +123,7 @@ const ReceiptPrinting = memo(({ wizard }) => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'companyNumber',
|
name: 'companyNumber',
|
||||||
display: 'Company number'
|
display: 'Company registration number'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'machineLocation',
|
name: 'machineLocation',
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import { HoverableTooltip } from 'src/components/Tooltip'
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
import { IconButton } from 'src/components/buttons'
|
import { IconButton, SupportLinkButton } from 'src/components/buttons'
|
||||||
import { Switch } from 'src/components/inputs'
|
import { Switch } from 'src/components/inputs'
|
||||||
import DataTable from 'src/components/tables/DataTable'
|
import DataTable from 'src/components/tables/DataTable'
|
||||||
import { H4, P, Label3 } from 'src/components/typography'
|
import { H4, P, Label3 } from 'src/components/typography'
|
||||||
|
|
@ -162,9 +162,9 @@ const SMSNotices = () => {
|
||||||
!R.isEmpty(TOOLTIPS[it.event]) ? (
|
!R.isEmpty(TOOLTIPS[it.event]) ? (
|
||||||
<div className={classes.messageWithTooltip}>
|
<div className={classes.messageWithTooltip}>
|
||||||
{R.prop('messageName', it)}
|
{R.prop('messageName', it)}
|
||||||
<HoverableTooltip width={250}>
|
<HelpTooltip width={250}>
|
||||||
<P>{TOOLTIPS[it.event]}</P>
|
<P>{TOOLTIPS[it.event]}</P>
|
||||||
</HoverableTooltip>
|
</HelpTooltip>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
R.prop('messageName', it)
|
R.prop('messageName', it)
|
||||||
|
|
@ -237,6 +237,17 @@ const SMSNotices = () => {
|
||||||
<>
|
<>
|
||||||
<div className={classes.header}>
|
<div className={classes.header}>
|
||||||
<H4>SMS notices</H4>
|
<H4>SMS notices</H4>
|
||||||
|
<HelpTooltip width={320}>
|
||||||
|
<P>
|
||||||
|
For details on configuring this panel, please read the relevant
|
||||||
|
knowledgebase article:
|
||||||
|
</P>
|
||||||
|
<SupportLinkButton
|
||||||
|
link="https://support.lamassu.is/hc/en-us/articles/115001205591-SMS-Phone-Verification"
|
||||||
|
label="Lamassu Support Article"
|
||||||
|
bottomSpace="1"
|
||||||
|
/>
|
||||||
|
</HelpTooltip>
|
||||||
</div>
|
</div>
|
||||||
{showModal && (
|
{showModal && (
|
||||||
<CustomSMSModal
|
<CustomSMSModal
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ import * as Yup from 'yup'
|
||||||
|
|
||||||
import ErrorMessage from 'src/components/ErrorMessage'
|
import ErrorMessage from 'src/components/ErrorMessage'
|
||||||
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
||||||
import { Link, IconButton } from 'src/components/buttons'
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
|
import { Link, IconButton, SupportLinkButton } from 'src/components/buttons'
|
||||||
import { Switch } from 'src/components/inputs'
|
import { Switch } from 'src/components/inputs'
|
||||||
import { TextInput } from 'src/components/inputs/formik'
|
import { TextInput } from 'src/components/inputs/formik'
|
||||||
import { H4, Info2, Info3, Label2, Label3, P } from 'src/components/typography'
|
import { H4, Info2, Info3, Label2, Label3, P } from 'src/components/typography'
|
||||||
|
|
@ -171,6 +172,17 @@ const TermsConditions = () => {
|
||||||
<>
|
<>
|
||||||
<div className={classes.header}>
|
<div className={classes.header}>
|
||||||
<H4>Terms & Conditions</H4>
|
<H4>Terms & Conditions</H4>
|
||||||
|
<HelpTooltip width={320}>
|
||||||
|
<P>
|
||||||
|
For details on configuring this panel, please read the relevant
|
||||||
|
knowledgebase article:
|
||||||
|
</P>
|
||||||
|
<SupportLinkButton
|
||||||
|
link="https://support.lamassu.is/hc/en-us/articles/360015982211-Terms-and-Conditions"
|
||||||
|
label="Lamassu Support Article"
|
||||||
|
bottomSpace="1"
|
||||||
|
/>
|
||||||
|
</HelpTooltip>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.switchRow}>
|
<div className={classes.switchRow}>
|
||||||
<P>Show on screen</P>
|
<P>Show on screen</P>
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ const Services = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.wrapper}>
|
<div className={classes.wrapper}>
|
||||||
<TitleSection title="3rd Party Services" />
|
<TitleSection title="Third-Party services" />
|
||||||
<Grid container spacing={4}>
|
<Grid container spacing={4}>
|
||||||
{R.values(schemas).map(schema => (
|
{R.values(schemas).map(schema => (
|
||||||
<Grid item key={schema.code}>
|
<Grid item key={schema.code}>
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,14 @@ export default {
|
||||||
elements: [
|
elements: [
|
||||||
{
|
{
|
||||||
code: 'apiKey',
|
code: 'apiKey',
|
||||||
display: 'API Key',
|
display: 'API key',
|
||||||
component: TextInputFormik,
|
component: TextInputFormik,
|
||||||
face: true,
|
face: true,
|
||||||
long: true
|
long: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'privateKey',
|
code: 'privateKey',
|
||||||
display: 'Private Key',
|
display: 'Private key',
|
||||||
component: SecretInputFormik
|
component: SecretInputFormik
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,14 @@ export default {
|
||||||
elements: [
|
elements: [
|
||||||
{
|
{
|
||||||
code: 'apiKey',
|
code: 'apiKey',
|
||||||
display: 'API Key',
|
display: 'API key',
|
||||||
component: TextInputFormik,
|
component: TextInputFormik,
|
||||||
face: true,
|
face: true,
|
||||||
long: true
|
long: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'privateKey',
|
code: 'privateKey',
|
||||||
display: 'Private Key',
|
display: 'Private key',
|
||||||
component: SecretInputFormik
|
component: SecretInputFormik
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ export default {
|
||||||
elements: [
|
elements: [
|
||||||
{
|
{
|
||||||
code: 'token',
|
code: 'token',
|
||||||
display: 'API Token',
|
display: 'API token',
|
||||||
component: TextInput,
|
component: TextInput,
|
||||||
face: true,
|
face: true,
|
||||||
long: true
|
long: true
|
||||||
|
|
@ -47,52 +47,52 @@ export default {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'BTCWalletId',
|
code: 'BTCWalletId',
|
||||||
display: 'BTC Wallet ID',
|
display: 'BTC wallet ID',
|
||||||
component: TextInput
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'BTCWalletPassphrase',
|
code: 'BTCWalletPassphrase',
|
||||||
display: 'BTC Wallet Passphrase',
|
display: 'BTC wallet passphrase',
|
||||||
component: SecretInput
|
component: SecretInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'LTCWalletId',
|
code: 'LTCWalletId',
|
||||||
display: 'LTC Wallet ID',
|
display: 'LTC wallet ID',
|
||||||
component: TextInput
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'LTCWalletPassphrase',
|
code: 'LTCWalletPassphrase',
|
||||||
display: 'LTC Wallet Passphrase',
|
display: 'LTC wallet passphrase',
|
||||||
component: SecretInput
|
component: SecretInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'ZECWalletId',
|
code: 'ZECWalletId',
|
||||||
display: 'ZEC Wallet ID',
|
display: 'ZEC wallet ID',
|
||||||
component: TextInput
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'ZECWalletPassphrase',
|
code: 'ZECWalletPassphrase',
|
||||||
display: 'ZEC Wallet Passphrase',
|
display: 'ZEC wallet passphrase',
|
||||||
component: SecretInput
|
component: SecretInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'BCHWalletId',
|
code: 'BCHWalletId',
|
||||||
display: 'BCH Wallet ID',
|
display: 'BCH wallet ID',
|
||||||
component: TextInput
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'BCHWalletPassphrase',
|
code: 'BCHWalletPassphrase',
|
||||||
display: 'BCH Wallet Passphrase',
|
display: 'BCH wallet passphrase',
|
||||||
component: SecretInput
|
component: SecretInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'DASHWalletId',
|
code: 'DASHWalletId',
|
||||||
display: 'DASH Wallet ID',
|
display: 'DASH wallet ID',
|
||||||
component: TextInput
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'DASHWalletPassphrase',
|
code: 'DASHWalletPassphrase',
|
||||||
display: 'DASH Wallet Passphrase',
|
display: 'DASH wallet passphrase',
|
||||||
component: SecretInput
|
component: SecretInput
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,14 @@ export default {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'key',
|
code: 'key',
|
||||||
display: 'API Key',
|
display: 'API key',
|
||||||
component: TextInputFormik,
|
component: TextInputFormik,
|
||||||
face: true,
|
face: true,
|
||||||
long: true
|
long: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'secret',
|
code: 'secret',
|
||||||
display: 'API Secret',
|
display: 'API secret',
|
||||||
component: SecretInputFormik
|
component: SecretInputFormik
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,14 @@ export default {
|
||||||
elements: [
|
elements: [
|
||||||
{
|
{
|
||||||
code: 'token',
|
code: 'token',
|
||||||
display: 'API Token',
|
display: 'API token',
|
||||||
component: TextInput,
|
component: TextInput,
|
||||||
face: true,
|
face: true,
|
||||||
long: true
|
long: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'confidenceFactor',
|
code: 'confidenceFactor',
|
||||||
display: 'Confidence Factor',
|
display: 'Confidence factor',
|
||||||
component: NumberInput,
|
component: NumberInput,
|
||||||
face: true
|
face: true
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,21 @@ export default {
|
||||||
elements: [
|
elements: [
|
||||||
{
|
{
|
||||||
code: 'apiKey',
|
code: 'apiKey',
|
||||||
display: 'API Key',
|
display: 'API key',
|
||||||
|
component: TextInputFormik,
|
||||||
|
face: true,
|
||||||
|
long: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: 'uid',
|
||||||
|
display: 'User ID',
|
||||||
component: TextInputFormik,
|
component: TextInputFormik,
|
||||||
face: true,
|
face: true,
|
||||||
long: true
|
long: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'privateKey',
|
code: 'privateKey',
|
||||||
display: 'Private Key',
|
display: 'Private key',
|
||||||
component: SecretInputFormik
|
component: SecretInputFormik
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -28,6 +35,9 @@ export default {
|
||||||
apiKey: Yup.string('The API key must be a string')
|
apiKey: Yup.string('The API key must be a string')
|
||||||
.max(100, 'The API key is too long')
|
.max(100, 'The API key is too long')
|
||||||
.required('The API key is required'),
|
.required('The API key is required'),
|
||||||
|
uid: Yup.string('The User ID must be a string')
|
||||||
|
.max(100, 'The User ID is too long')
|
||||||
|
.required('The User ID is required'),
|
||||||
privateKey: Yup.string('The private key must be a string')
|
privateKey: Yup.string('The private key must be a string')
|
||||||
.max(100, 'The private key is too long')
|
.max(100, 'The private key is too long')
|
||||||
.test(secretTest(account?.privateKey, 'private key'))
|
.test(secretTest(account?.privateKey, 'private key'))
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,12 @@ export default {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'clientKey',
|
code: 'clientKey',
|
||||||
display: 'Client Key',
|
display: 'Client key',
|
||||||
component: TextInputFormik
|
component: TextInputFormik
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'clientSecret',
|
code: 'clientSecret',
|
||||||
display: 'Client Secret',
|
display: 'Client secret',
|
||||||
component: SecretInputFormik
|
component: SecretInputFormik
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,14 @@ export default {
|
||||||
elements: [
|
elements: [
|
||||||
{
|
{
|
||||||
code: 'apiKey',
|
code: 'apiKey',
|
||||||
display: 'API Key',
|
display: 'API key',
|
||||||
component: TextInputFormik,
|
component: TextInputFormik,
|
||||||
face: true,
|
face: true,
|
||||||
long: true
|
long: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'privateKey',
|
code: 'privateKey',
|
||||||
display: 'Private Key',
|
display: 'Private key',
|
||||||
component: SecretInputFormik
|
component: SecretInputFormik
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export default {
|
||||||
elements: [
|
elements: [
|
||||||
{
|
{
|
||||||
code: 'apiKey',
|
code: 'apiKey',
|
||||||
display: 'API Key',
|
display: 'API key',
|
||||||
component: TextInputFormik
|
component: TextInputFormik
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -19,13 +19,13 @@ export default {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'fromEmail',
|
code: 'fromEmail',
|
||||||
display: 'From Email',
|
display: 'From email',
|
||||||
component: TextInputFormik,
|
component: TextInputFormik,
|
||||||
face: true
|
face: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'toEmail',
|
code: 'toEmail',
|
||||||
display: 'To Email',
|
display: 'To email',
|
||||||
component: TextInputFormik,
|
component: TextInputFormik,
|
||||||
face: true
|
face: true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ const singleBitgo = code => ({
|
||||||
elements: [
|
elements: [
|
||||||
{
|
{
|
||||||
code: 'token',
|
code: 'token',
|
||||||
display: 'API Token',
|
display: 'API token',
|
||||||
component: TextInput,
|
component: TextInput,
|
||||||
face: true,
|
face: true,
|
||||||
long: true
|
long: true
|
||||||
|
|
@ -34,12 +34,12 @@ const singleBitgo = code => ({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: `${code}WalletId`,
|
code: `${code}WalletId`,
|
||||||
display: `${code} Wallet ID`,
|
display: `${code} wallet ID`,
|
||||||
component: TextInput
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: `${code}WalletPassphrase`,
|
code: `${code}WalletPassphrase`,
|
||||||
display: `${code} Wallet Passphrase`,
|
display: `${code} wallet passphrase`,
|
||||||
component: SecretInput
|
component: SecretInput
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -17,18 +17,18 @@ export default {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'authToken',
|
code: 'authToken',
|
||||||
display: 'Auth Token',
|
display: 'Auth token',
|
||||||
component: SecretInputFormik
|
component: SecretInputFormik
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'fromNumber',
|
code: 'fromNumber',
|
||||||
display: 'Twilio Number (international format)',
|
display: 'Twilio number (international format)',
|
||||||
component: TextInputFormik,
|
component: TextInputFormik,
|
||||||
face: true
|
face: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'toNumber',
|
code: 'toNumber',
|
||||||
display: 'Notifications Number (international format)',
|
display: 'Notifications number (international format)',
|
||||||
component: TextInputFormik,
|
component: TextInputFormik,
|
||||||
face: true
|
face: true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ const SessionManagement = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TitleSection title="Session Management" />
|
<TitleSection title="Session management" />
|
||||||
<DataTable
|
<DataTable
|
||||||
loading={loading}
|
loading={loading}
|
||||||
elements={elements}
|
elements={elements}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ const CopyToClipboard = ({
|
||||||
buttonClassname,
|
buttonClassname,
|
||||||
children,
|
children,
|
||||||
wrapperClassname,
|
wrapperClassname,
|
||||||
|
removeSpace = true,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
const [anchorEl, setAnchorEl] = useState(null)
|
const [anchorEl, setAnchorEl] = useState(null)
|
||||||
|
|
@ -46,7 +47,8 @@ const CopyToClipboard = ({
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
<div className={classnames(classes.buttonWrapper, buttonClassname)}>
|
<div className={classnames(classes.buttonWrapper, buttonClassname)}>
|
||||||
<ReactCopyToClipboard text={R.replace(/\s/g, '')(children)}>
|
<ReactCopyToClipboard
|
||||||
|
text={removeSpace ? R.replace(/\s/g, '')(children) : children}>
|
||||||
<button
|
<button
|
||||||
aria-describedby={id}
|
aria-describedby={id}
|
||||||
onClick={event => handleClick(event)}>
|
onClick={event => handleClick(event)}>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import * as R from 'ramda'
|
||||||
import React, { memo, useState } from 'react'
|
import React, { memo, useState } from 'react'
|
||||||
|
|
||||||
import { ConfirmDialog } from 'src/components/ConfirmDialog'
|
import { ConfirmDialog } from 'src/components/ConfirmDialog'
|
||||||
import { HoverableTooltip } from 'src/components/Tooltip'
|
import { HelpTooltip, HoverableTooltip } from 'src/components/Tooltip'
|
||||||
import { IDButton, ActionButton } from 'src/components/buttons'
|
import { IDButton, ActionButton } from 'src/components/buttons'
|
||||||
import { P, Label1 } from 'src/components/typography'
|
import { P, Label1 } from 'src/components/typography'
|
||||||
import { ReactComponent as CardIdInverseIcon } from 'src/styling/icons/ID/card/white.svg'
|
import { ReactComponent as CardIdInverseIcon } from 'src/styling/icons/ID/card/white.svg'
|
||||||
|
|
@ -133,9 +133,9 @@ const DetailsRow = ({ it: tx, timezone }) => {
|
||||||
const commission = BigNumber(tx.profit).toFixed(2, 1) // ROUND_DOWN
|
const commission = BigNumber(tx.profit).toFixed(2, 1) // ROUND_DOWN
|
||||||
const commissionPercentage =
|
const commissionPercentage =
|
||||||
Number.parseFloat(tx.commissionPercentage, 2) * 100
|
Number.parseFloat(tx.commissionPercentage, 2) * 100
|
||||||
const cashInFee = isCashIn ? Number.parseFloat(tx.cashInFee) : 0
|
const fixedFee = Number.parseFloat(tx.fixedFee) || 0
|
||||||
const fiat = BigNumber(tx.fiat)
|
const fiat = BigNumber(tx.fiat)
|
||||||
.minus(cashInFee)
|
.minus(fixedFee)
|
||||||
.toFixed(2, 1) // ROUND_DOWN
|
.toFixed(2, 1) // ROUND_DOWN
|
||||||
const crypto = getCryptoAmount(tx)
|
const crypto = getCryptoAmount(tx)
|
||||||
const cryptoFee = tx.fee ? `${getCryptoFeeAmount(tx)} ${tx.fiatCode}` : 'N/A'
|
const cryptoFee = tx.fee ? `${getCryptoFeeAmount(tx)} ${tx.fiatCode}` : 'N/A'
|
||||||
|
|
@ -191,6 +191,13 @@ const DetailsRow = ({ it: tx, timezone }) => {
|
||||||
<>
|
<>
|
||||||
<Label>Transaction status</Label>
|
<Label>Transaction status</Label>
|
||||||
<span className={classes.bold}>{getStatus(tx)}</span>
|
<span className={classes.bold}>{getStatus(tx)}</span>
|
||||||
|
{getStatusDetails(tx) ? (
|
||||||
|
<CopyToClipboard removeSpace={false} className={classes.errorCopy}>
|
||||||
|
{getStatusDetails(tx)}
|
||||||
|
</CopyToClipboard>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -350,7 +357,7 @@ const DetailsRow = ({ it: tx, timezone }) => {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label>Fixed fee</Label>
|
<Label>Fixed fee</Label>
|
||||||
<div>{isCashIn ? `${cashInFee} ${tx.fiatCode}` : 'N/A'}</div>
|
<div>{`${fixedFee} ${tx.fiatCode}`}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.secondRow}>
|
<div className={classes.secondRow}>
|
||||||
|
|
@ -358,9 +365,9 @@ const DetailsRow = ({ it: tx, timezone }) => {
|
||||||
<div className={classes.addressHeader}>
|
<div className={classes.addressHeader}>
|
||||||
<Label>Address</Label>
|
<Label>Address</Label>
|
||||||
{!R.isNil(tx.walletScore) && (
|
{!R.isNil(tx.walletScore) && (
|
||||||
<HoverableTooltip parentElements={walletScoreEl}>
|
<HelpTooltip parentElements={walletScoreEl}>
|
||||||
{`Chain analysis score: ${tx.walletScore}/10`}
|
{`Chain analysis score: ${tx.walletScore}/10`}
|
||||||
</HoverableTooltip>
|
</HelpTooltip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -392,13 +399,7 @@ const DetailsRow = ({ it: tx, timezone }) => {
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.lastRow}>
|
<div className={classes.lastRow}>
|
||||||
<div className={classes.status}>
|
<div className={classes.status}>
|
||||||
{getStatusDetails(tx) ? (
|
{errorElements}
|
||||||
<HoverableTooltip parentElements={errorElements} width={200}>
|
|
||||||
<P>{getStatusDetails(tx)}</P>
|
|
||||||
</HoverableTooltip>
|
|
||||||
) : (
|
|
||||||
errorElements
|
|
||||||
)}
|
|
||||||
{((tx.txClass === 'cashOut' && getStatus(tx) === 'Pending') ||
|
{((tx.txClass === 'cashOut' && getStatus(tx) === 'Pending') ||
|
||||||
(tx.txClass === 'cashIn' && getStatus(tx) === 'Batched')) && (
|
(tx.txClass === 'cashIn' && getStatus(tx) === 'Batched')) && (
|
||||||
<ActionButton
|
<ActionButton
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import typographyStyles from 'src/components/typography/styles'
|
import typographyStyles from 'src/components/typography/styles'
|
||||||
import { offColor, comet, white, tomato } from 'src/styling/variables'
|
import { offColor, comet, white, tomato } from 'src/styling/variables'
|
||||||
|
|
||||||
const { p } = typographyStyles
|
const { p, label3 } = typographyStyles
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
wrapper: {
|
wrapper: {
|
||||||
|
|
@ -137,5 +137,10 @@ export default {
|
||||||
},
|
},
|
||||||
swept: {
|
swept: {
|
||||||
width: 250
|
width: 250
|
||||||
|
},
|
||||||
|
errorCopy: {
|
||||||
|
extend: label3,
|
||||||
|
lineBreak: 'normal',
|
||||||
|
maxWidth: 180
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper'
|
||||||
import SearchBox from 'src/components/SearchBox'
|
import SearchBox from 'src/components/SearchBox'
|
||||||
import SearchFilter from 'src/components/SearchFilter'
|
import SearchFilter from 'src/components/SearchFilter'
|
||||||
import Title from 'src/components/Title'
|
import Title from 'src/components/Title'
|
||||||
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
|
import { SupportLinkButton } from 'src/components/buttons'
|
||||||
import DataTable from 'src/components/tables/DataTable'
|
import DataTable from 'src/components/tables/DataTable'
|
||||||
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
|
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
|
||||||
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
|
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
|
||||||
|
|
@ -105,7 +107,7 @@ const GET_TRANSACTIONS = gql`
|
||||||
deviceId
|
deviceId
|
||||||
fiat
|
fiat
|
||||||
fee
|
fee
|
||||||
cashInFee
|
fixedFee
|
||||||
fiatCode
|
fiatCode
|
||||||
cryptoAtoms
|
cryptoAtoms
|
||||||
cryptoCode
|
cryptoCode
|
||||||
|
|
@ -233,7 +235,22 @@ const Transactions = () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Status',
|
header: 'Status',
|
||||||
view: it => getStatus(it),
|
view: it => {
|
||||||
|
if (getStatus(it) === 'Pending')
|
||||||
|
return (
|
||||||
|
<div className={classes.pendingBox}>
|
||||||
|
{'Pending'}
|
||||||
|
<HelpTooltip width={285}>
|
||||||
|
<SupportLinkButton
|
||||||
|
link="https://support.lamassu.is/hc/en-us/articles/115001210452-Cancelling-cash-out-transactions"
|
||||||
|
label="Cancelling cash-out transactions"
|
||||||
|
bottomSpace="0"
|
||||||
|
/>
|
||||||
|
</HelpTooltip>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
else return getStatus(it)
|
||||||
|
},
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
width: 80
|
width: 80
|
||||||
|
|
@ -323,7 +340,7 @@ const Transactions = () => {
|
||||||
loading={filtersLoading}
|
loading={filtersLoading}
|
||||||
filters={filters}
|
filters={filters}
|
||||||
options={filterOptions}
|
options={filterOptions}
|
||||||
inputPlaceholder={'Search Transactions'}
|
inputPlaceholder={'Search transactions'}
|
||||||
onChange={onFilterChange}
|
onChange={onFilterChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import Modal from 'src/components/Modal'
|
import Modal from 'src/components/Modal'
|
||||||
import { HoverableTooltip } from 'src/components/Tooltip'
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
import { Link, SupportLinkButton } from 'src/components/buttons'
|
import { Link, SupportLinkButton } from 'src/components/buttons'
|
||||||
import { Switch } from 'src/components/inputs'
|
import { Switch } from 'src/components/inputs'
|
||||||
import TitleSection from 'src/components/layout/TitleSection'
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
|
|
@ -187,13 +187,13 @@ const Triggers = () => {
|
||||||
<Label2 className={classes.switchLabel}>
|
<Label2 className={classes.switchLabel}>
|
||||||
{rejectAddressReuse ? 'On' : 'Off'}
|
{rejectAddressReuse ? 'On' : 'Off'}
|
||||||
</Label2>
|
</Label2>
|
||||||
<HoverableTooltip width={304}>
|
<HelpTooltip width={304}>
|
||||||
<P>
|
<P>
|
||||||
This option requires a user to scan a different cryptocurrency
|
This option requires a user to scan a different cryptocurrency
|
||||||
address if they attempt to scan one that had been previously
|
address if they attempt to scan one that had been previously
|
||||||
used for a transaction in your network
|
used for a transaction in your network
|
||||||
</P>
|
</P>
|
||||||
</HoverableTooltip>
|
</HelpTooltip>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -241,7 +241,7 @@ const Users = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TitleSection title="User Management" />
|
<TitleSection title="User management" />
|
||||||
<Box
|
<Box
|
||||||
marginBottom={3}
|
marginBottom={3}
|
||||||
marginTop={-5}
|
marginTop={-5}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import Modal from 'src/components/Modal'
|
import Modal from 'src/components/Modal'
|
||||||
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
|
import { SupportLinkButton } from 'src/components/buttons'
|
||||||
import { NamespacedTable as EditableTable } from 'src/components/editableTable'
|
import { NamespacedTable as EditableTable } from 'src/components/editableTable'
|
||||||
import TitleSection from 'src/components/layout/TitleSection'
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
import FormRenderer from 'src/pages/Services/FormRenderer'
|
import FormRenderer from 'src/pages/Services/FormRenderer'
|
||||||
|
|
@ -13,6 +15,8 @@ import { ReactComponent as ReverseSettingsIcon } from 'src/styling/icons/circle
|
||||||
import { ReactComponent as SettingsIcon } from 'src/styling/icons/circle buttons/settings/zodiac.svg'
|
import { ReactComponent as SettingsIcon } from 'src/styling/icons/circle buttons/settings/zodiac.svg'
|
||||||
import { fromNamespace, toNamespace } from 'src/utils/config'
|
import { fromNamespace, toNamespace } from 'src/utils/config'
|
||||||
|
|
||||||
|
import { P } from '../../components/typography'
|
||||||
|
|
||||||
import AdvancedWallet from './AdvancedWallet'
|
import AdvancedWallet from './AdvancedWallet'
|
||||||
import styles from './Wallet.styles.js'
|
import styles from './Wallet.styles.js'
|
||||||
import Wizard from './Wizard'
|
import Wizard from './Wizard'
|
||||||
|
|
@ -115,7 +119,7 @@ const Wallet = ({ name: SCREEN_KEY }) => {
|
||||||
<>
|
<>
|
||||||
<div className={classes.header}>
|
<div className={classes.header}>
|
||||||
<TitleSection
|
<TitleSection
|
||||||
title="Wallet Settings"
|
title="Wallet settings"
|
||||||
buttons={[
|
buttons={[
|
||||||
{
|
{
|
||||||
text: 'Advanced settings',
|
text: 'Advanced settings',
|
||||||
|
|
@ -124,6 +128,19 @@ const Wallet = ({ name: SCREEN_KEY }) => {
|
||||||
toggle: setAdvancedSettings
|
toggle: setAdvancedSettings
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
|
appendix={
|
||||||
|
<HelpTooltip width={340}>
|
||||||
|
<P>
|
||||||
|
For details on configuring wallets, please read the relevant
|
||||||
|
knowledgebase article:
|
||||||
|
</P>
|
||||||
|
<SupportLinkButton
|
||||||
|
link="https://support.lamassu.is/hc/en-us/articles/360000725832-Wallets-Exchange-Linkage-and-Volatility"
|
||||||
|
label="Wallets, Exchange Linkage, and Volatility"
|
||||||
|
bottomSpace="1"
|
||||||
|
/>
|
||||||
|
</HelpTooltip>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{!advancedSettings && (
|
{!advancedSettings && (
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ const getAdvancedWalletElements = () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'allowTransactionBatching',
|
name: 'allowTransactionBatching',
|
||||||
header: `Allow BTC Transaction Batching`,
|
header: `Allow BTC transaction batching`,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
stripe: true,
|
stripe: true,
|
||||||
width: 260,
|
width: 260,
|
||||||
|
|
@ -119,7 +119,7 @@ const getAdvancedWalletElements = () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'feeMultiplier',
|
name: 'feeMultiplier',
|
||||||
header: `BTC Miner's Fee`,
|
header: `BTC miner's fee`,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
stripe: true,
|
stripe: true,
|
||||||
width: 250,
|
width: 250,
|
||||||
|
|
@ -179,7 +179,7 @@ const getAdvancedWalletElementsOverrides = (
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'feeMultiplier',
|
name: 'feeMultiplier',
|
||||||
header: `Miner's Fee`,
|
header: `Miner's fee`,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
stripe: true,
|
stripe: true,
|
||||||
width: 250,
|
width: 250,
|
||||||
|
|
@ -280,7 +280,7 @@ const getElements = (cryptoCurrencies, accounts, onChange, wizard = false) => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'zeroConf',
|
name: 'zeroConf',
|
||||||
header: 'Confidence Checking',
|
header: 'Confidence checking',
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
stripe: true,
|
stripe: true,
|
||||||
view: (it, row) => {
|
view: (it, row) => {
|
||||||
|
|
@ -304,7 +304,7 @@ const getElements = (cryptoCurrencies, accounts, onChange, wizard = false) => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'zeroConfLimit',
|
name: 'zeroConfLimit',
|
||||||
header: '0-conf Limit',
|
header: '0-conf limit',
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
stripe: true,
|
stripe: true,
|
||||||
view: (it, row) =>
|
view: (it, row) =>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import gql from 'graphql-tag'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import InfoMessage from 'src/components/InfoMessage'
|
import InfoMessage from 'src/components/InfoMessage'
|
||||||
import { HoverableTooltip } from 'src/components/Tooltip'
|
import { HelpTooltip } from 'src/components/Tooltip'
|
||||||
import { Button, SupportLinkButton } from 'src/components/buttons'
|
import { Button, SupportLinkButton } from 'src/components/buttons'
|
||||||
import { RadioGroup } from 'src/components/inputs'
|
import { RadioGroup } from 'src/components/inputs'
|
||||||
import { H1, H4, P } from 'src/components/typography'
|
import { H1, H4, P } from 'src/components/typography'
|
||||||
|
|
@ -102,7 +102,7 @@ function Twilio({ doContinue }) {
|
||||||
<H4 noMargin className={classnames(titleClasses)}>
|
<H4 noMargin className={classnames(titleClasses)}>
|
||||||
Will you setup a two way machine or compliance?
|
Will you setup a two way machine or compliance?
|
||||||
</H4>
|
</H4>
|
||||||
<HoverableTooltip width={304}>
|
<HelpTooltip width={304}>
|
||||||
<P>
|
<P>
|
||||||
Two-way machines allow your customers not only to buy (cash-in)
|
Two-way machines allow your customers not only to buy (cash-in)
|
||||||
but also sell cryptocurrencies (cash-out).
|
but also sell cryptocurrencies (cash-out).
|
||||||
|
|
@ -111,7 +111,7 @@ function Twilio({ doContinue }) {
|
||||||
You’ll need an SMS service for cash-out transactions and for any
|
You’ll need an SMS service for cash-out transactions and for any
|
||||||
compliance triggers
|
compliance triggers
|
||||||
</P>
|
</P>
|
||||||
</HoverableTooltip>
|
</HelpTooltip>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ const getLamassuRoutes = () => [
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: 'cash_units',
|
key: 'cash_units',
|
||||||
label: 'Cash Units',
|
label: 'Cash units',
|
||||||
route: '/maintenance/cash-units',
|
route: '/maintenance/cash-units',
|
||||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||||
component: CashUnits
|
component: CashUnits
|
||||||
|
|
@ -63,14 +63,14 @@ const getLamassuRoutes = () => [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'logs',
|
key: 'logs',
|
||||||
label: 'Machine Logs',
|
label: 'Machine logs',
|
||||||
route: '/maintenance/logs',
|
route: '/maintenance/logs',
|
||||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||||
component: MachineLogs
|
component: MachineLogs
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'machine-status',
|
key: 'machine-status',
|
||||||
label: 'Machine Status',
|
label: 'Machine status',
|
||||||
route: '/maintenance/machine-status',
|
route: '/maintenance/machine-status',
|
||||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||||
component: MachineStatus
|
component: MachineStatus
|
||||||
|
|
@ -130,7 +130,7 @@ const getLamassuRoutes = () => [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'services',
|
key: 'services',
|
||||||
label: '3rd Party Services',
|
label: 'Third-party services',
|
||||||
route: '/settings/3rd-party-services',
|
route: '/settings/3rd-party-services',
|
||||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||||
component: Services
|
component: Services
|
||||||
|
|
@ -144,9 +144,9 @@ const getLamassuRoutes = () => [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: namespaces.OPERATOR_INFO,
|
key: namespaces.OPERATOR_INFO,
|
||||||
label: 'Operator Info',
|
label: 'Operator info',
|
||||||
route: '/settings/operator-info',
|
route: '/settings/operator-info',
|
||||||
title: 'Operator Information',
|
title: 'Operator information',
|
||||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||||
get component() {
|
get component() {
|
||||||
return () => (
|
return () => (
|
||||||
|
|
@ -232,7 +232,7 @@ const getLamassuRoutes = () => [
|
||||||
key: 'loyalty',
|
key: 'loyalty',
|
||||||
label: 'Loyalty',
|
label: 'Loyalty',
|
||||||
route: '/compliance/loyalty',
|
route: '/compliance/loyalty',
|
||||||
title: 'Loyalty Panel',
|
title: 'Loyalty panel',
|
||||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||||
get component() {
|
get component() {
|
||||||
return () => (
|
return () => (
|
||||||
|
|
@ -247,14 +247,14 @@ const getLamassuRoutes = () => [
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: 'individual-discounts',
|
key: 'individual-discounts',
|
||||||
label: 'Individual Discounts',
|
label: 'Individual discounts',
|
||||||
route: '/compliance/loyalty/individual-discounts',
|
route: '/compliance/loyalty/individual-discounts',
|
||||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||||
component: IndividualDiscounts
|
component: IndividualDiscounts
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'promo-codes',
|
key: 'promo-codes',
|
||||||
label: 'Promo Codes',
|
label: 'Promo codes',
|
||||||
route: '/compliance/loyalty/codes',
|
route: '/compliance/loyalty/codes',
|
||||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||||
component: PromoCodes
|
component: PromoCodes
|
||||||
|
|
@ -280,14 +280,14 @@ const getLamassuRoutes = () => [
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: 'user-management',
|
key: 'user-management',
|
||||||
label: 'User Management',
|
label: 'User management',
|
||||||
route: '/system/user-management',
|
route: '/system/user-management',
|
||||||
allowedRoles: [ROLES.SUPERUSER],
|
allowedRoles: [ROLES.SUPERUSER],
|
||||||
component: UserManagement
|
component: UserManagement
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'session-management',
|
key: 'session-management',
|
||||||
label: 'Session Management',
|
label: 'Session management',
|
||||||
route: '/system/session-management',
|
route: '/system/session-management',
|
||||||
allowedRoles: [ROLES.SUPERUSER],
|
allowedRoles: [ROLES.SUPERUSER],
|
||||||
component: SessionManagement
|
component: SessionManagement
|
||||||
|
|
|
||||||
|
|
@ -56,14 +56,14 @@ const getPazuzRoutes = () => [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'logs',
|
key: 'logs',
|
||||||
label: 'Machine Logs',
|
label: 'Machine logs',
|
||||||
route: '/maintenance/logs',
|
route: '/maintenance/logs',
|
||||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||||
component: MachineLogs
|
component: MachineLogs
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'machine-status',
|
key: 'machine-status',
|
||||||
label: 'Machine Status',
|
label: 'Machine status',
|
||||||
route: '/maintenance/machine-status',
|
route: '/maintenance/machine-status',
|
||||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||||
component: MachineStatus
|
component: MachineStatus
|
||||||
|
|
@ -123,9 +123,9 @@ const getPazuzRoutes = () => [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: namespaces.OPERATOR_INFO,
|
key: namespaces.OPERATOR_INFO,
|
||||||
label: 'Operator Info',
|
label: 'Operator info',
|
||||||
route: '/settings/operator-info',
|
route: '/settings/operator-info',
|
||||||
title: 'Operator Information',
|
title: 'Operator information',
|
||||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||||
get component() {
|
get component() {
|
||||||
return () => (
|
return () => (
|
||||||
|
|
@ -225,14 +225,14 @@ const getPazuzRoutes = () => [
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: 'individual-discounts',
|
key: 'individual-discounts',
|
||||||
label: 'Individual Discounts',
|
label: 'Individual discounts',
|
||||||
route: '/compliance/loyalty/individual-discounts',
|
route: '/compliance/loyalty/individual-discounts',
|
||||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||||
component: IndividualDiscounts
|
component: IndividualDiscounts
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'promo-codes',
|
key: 'promo-codes',
|
||||||
label: 'Promo Codes',
|
label: 'Promo codes',
|
||||||
route: '/compliance/loyalty/codes',
|
route: '/compliance/loyalty/codes',
|
||||||
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
|
||||||
component: PromoCodes
|
component: PromoCodes
|
||||||
|
|
@ -290,14 +290,14 @@ const getPazuzRoutes = () => [
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: 'user-management',
|
key: 'user-management',
|
||||||
label: 'User Management',
|
label: 'User management',
|
||||||
route: '/system/user-management',
|
route: '/system/user-management',
|
||||||
allowedRoles: [ROLES.SUPERUSER],
|
allowedRoles: [ROLES.SUPERUSER],
|
||||||
component: UserManagement
|
component: UserManagement
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'session-management',
|
key: 'session-management',
|
||||||
label: 'Session Management',
|
label: 'Session management',
|
||||||
route: '/system/session-management',
|
route: '/system/session-management',
|
||||||
allowedRoles: [ROLES.SUPERUSER],
|
allowedRoles: [ROLES.SUPERUSER],
|
||||||
component: SessionManagement
|
component: SessionManagement
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,9 @@ export default {
|
||||||
// forcing styling onto inner container
|
// forcing styling onto inner container
|
||||||
'.ReactVirtualized__Grid__innerScrollContainer': {
|
'.ReactVirtualized__Grid__innerScrollContainer': {
|
||||||
overflow: 'inherit !important'
|
overflow: 'inherit !important'
|
||||||
|
},
|
||||||
|
'.ReactVirtualized__Grid.ReactVirtualized__List': {
|
||||||
|
overflowY: 'overlay !important'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,15 @@ const startCase = R.compose(
|
||||||
splitOnUpper
|
splitOnUpper
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const sentenceCase = R.compose(onlyFirstToUpper, S.joinWith(' '), splitOnUpper)
|
||||||
|
|
||||||
const singularOrPlural = (amount, singularStr, pluralStr) =>
|
const singularOrPlural = (amount, singularStr, pluralStr) =>
|
||||||
parseInt(amount) === 1 ? singularStr : pluralStr
|
parseInt(amount) === 1 ? singularStr : pluralStr
|
||||||
|
|
||||||
export { startCase, onlyFirstToUpper, formatLong, singularOrPlural }
|
export {
|
||||||
|
startCase,
|
||||||
|
onlyFirstToUpper,
|
||||||
|
formatLong,
|
||||||
|
singularOrPlural,
|
||||||
|
sentenceCase
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue