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