fix: pre release screen fixes
This commit is contained in:
parent
1bcc87757b
commit
5dd8501a17
98 changed files with 1569 additions and 1149 deletions
10
TODO.json
10
TODO.json
|
|
@ -25,16 +25,6 @@
|
||||||
"rejectAddressReuseActive"
|
"rejectAddressReuseActive"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"code": "walletSettings",
|
|
||||||
"fields": [
|
|
||||||
"ticker",
|
|
||||||
"wallet",
|
|
||||||
"layer2",
|
|
||||||
"exchange",
|
|
||||||
"zeroConf"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"code": "notifications",
|
"code": "notifications",
|
||||||
"fields": [
|
"fields": [
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ function post (machineTx, pi) {
|
||||||
.then(([{ config }, blacklistItems]) => {
|
.then(([{ config }, blacklistItems]) => {
|
||||||
// TODO new-admin: addressReuse doesnt exist
|
// TODO new-admin: addressReuse doesnt exist
|
||||||
// const rejectAddressReuseActive = configManager.unscoped(config).rejectAddressReuseActive
|
// const rejectAddressReuseActive = configManager.unscoped(config).rejectAddressReuseActive
|
||||||
const rejectAddressReuseActive = true
|
const rejectAddressReuseActive = false
|
||||||
|
|
||||||
if (_.some(it => it.created_by_operator === true)(blacklistItems)) {
|
if (_.some(it => it.created_by_operator === true)(blacklistItems)) {
|
||||||
blacklisted = true
|
blacklisted = true
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ function mapCoin (rates, deviceId, settings, cryptoCode) {
|
||||||
|
|
||||||
const cashInFee = showCommissions ? commissions.cashIn / 100 : null
|
const cashInFee = showCommissions ? commissions.cashIn / 100 : null
|
||||||
const cashOutFee = showCommissions ? commissions.cashOut / 100 : null
|
const cashOutFee = showCommissions ? commissions.cashOut / 100 : null
|
||||||
|
const cashInFixedFee = showCommissions ? commissions.fixedFee : null
|
||||||
const cashInRate = showCommissions ? _.invoke('cashIn.toNumber', buildedRates) : null
|
const cashInRate = showCommissions ? _.invoke('cashIn.toNumber', buildedRates) : null
|
||||||
const cashOutRate = showCommissions ? _.invoke('cashOut.toNumber', buildedRates) : null
|
const cashOutRate = showCommissions ? _.invoke('cashOut.toNumber', buildedRates) : null
|
||||||
|
|
||||||
|
|
@ -77,6 +78,7 @@ function mapMachine (rates, settings, machineRow) {
|
||||||
|
|
||||||
// TODO new-admin: this is relaying info with backwards compatible triggers
|
// TODO new-admin: this is relaying info with backwards compatible triggers
|
||||||
// need to get in touch with coinatmradar before updating this
|
// need to get in touch with coinatmradar before updating this
|
||||||
|
// TODO all directions and max between them instead of min
|
||||||
const cashLimit = showLimitsAndVerification ? (
|
const cashLimit = showLimitsAndVerification ? (
|
||||||
!!compatTriggers.block
|
!!compatTriggers.block
|
||||||
? compatTriggers.block
|
? compatTriggers.block
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
const _ = require('lodash/fp')
|
const _ = require('lodash/fp')
|
||||||
|
|
||||||
function getBackwardsCompatibleTriggers (triggers) {
|
function getBackwardsCompatibleTriggers (triggers) {
|
||||||
const filtered = _.filter(_.matches({ triggerType: 'volume', cashDirection: 'both' }))(triggers)
|
const filtered = _.filter(_.matches({ triggerType: 'amount', cashDirection: 'both' }))(triggers)
|
||||||
const grouped = _.groupBy(_.prop('requirement'))(filtered)
|
const grouped = _.groupBy(_.prop('requirement'))(filtered)
|
||||||
return _.mapValues(_.compose(_.get('threshold'), _.minBy('threshold')))(grouped)
|
return _.mapValues(_.compose(_.get('threshold'), _.minBy('threshold')))(grouped)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -456,7 +456,7 @@ function getCustomerById (id) {
|
||||||
row_number() over (partition by c.id order by t.created desc) as rn,
|
row_number() over (partition by c.id order by t.created desc) as rn,
|
||||||
count(0) over (partition by c.id) as total_txs,
|
count(0) over (partition by c.id) as total_txs,
|
||||||
sum(t.fiat) over (partition by c.id) as total_spent
|
sum(t.fiat) over (partition by c.id) as total_spent
|
||||||
from customers c inner join (
|
from customers c left outer join (
|
||||||
select 'cashIn' as tx_class, id, fiat, fiat_code, created, customer_id
|
select 'cashIn' as tx_class, id, fiat, fiat_code, created, customer_id
|
||||||
from cash_in_txs where send_confirmed = true union
|
from cash_in_txs where send_confirmed = true union
|
||||||
select 'cashOut' as tx_class, id, fiat, fiat_code, created, customer_id
|
select 'cashOut' as tx_class, id, fiat, fiat_code, created, customer_id
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ const pgp = Pgp({
|
||||||
if (e.cn) logger.error('Database not reachable.')
|
if (e.cn) logger.error('Database not reachable.')
|
||||||
if (e.query) {
|
if (e.query) {
|
||||||
logger.error(e.query)
|
logger.error(e.query)
|
||||||
logger.error(e.params)
|
e.params && logger.error(e.params)
|
||||||
}
|
}
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
}
|
}
|
||||||
|
|
@ -21,11 +21,12 @@ const db = pgp(psqlUrl)
|
||||||
|
|
||||||
eventBus.subscribe('log', args => {
|
eventBus.subscribe('log', args => {
|
||||||
const { level, message, meta } = args
|
const { level, message, meta } = args
|
||||||
|
const msgToSave = message ? message : _.get('message', meta)
|
||||||
|
|
||||||
const sql = `insert into server_logs
|
const sql = `insert into server_logs
|
||||||
(id, device_id, message, log_level, meta) values ($1, $2, $3, $4, $5) returning *`
|
(id, device_id, message, log_level, meta) values ($1, $2, $3, $4, $5) returning *`
|
||||||
|
|
||||||
db.one(sql, [uuid.v4(), '', message, level, meta])
|
db.one(sql, [uuid.v4(), '', msgToSave, level, meta])
|
||||||
.then(_.mapKeys(_.camelCase))
|
.then(_.mapKeys(_.camelCase))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
const _ = require('lodash/fp')
|
const _ = require('lodash/fp')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
|
|
||||||
|
const logger = require('./logger')
|
||||||
const db = require('./db')
|
const db = require('./db')
|
||||||
const pairing = require('./pairing')
|
const pairing = require('./pairing')
|
||||||
const configManager = require('./new-config-manager')
|
const configManager = require('./new-config-manager')
|
||||||
|
|
@ -15,8 +16,10 @@ function getMachines () {
|
||||||
cashbox: r.cashbox,
|
cashbox: r.cashbox,
|
||||||
cassette1: r.cassette1,
|
cassette1: r.cassette1,
|
||||||
cassette2: r.cassette2,
|
cassette2: r.cassette2,
|
||||||
pairedAt: new Date(r.created).valueOf(),
|
version: r.version,
|
||||||
lastPing: new Date(r.last_online).valueOf(),
|
model: r.model,
|
||||||
|
pairedAt: new Date(r.created),
|
||||||
|
lastPing: new Date(r.last_online),
|
||||||
name: r.name,
|
name: r.name,
|
||||||
// TODO: we shall start using this JSON field at some point
|
// TODO: we shall start using this JSON field at some point
|
||||||
// location: r.location,
|
// location: r.location,
|
||||||
|
|
@ -36,19 +39,12 @@ function getMachineNames (config) {
|
||||||
const addName = r => {
|
const addName = r => {
|
||||||
const cashOutConfig = configManager.getCashOut(r.deviceId, config)
|
const cashOutConfig = configManager.getCashOut(r.deviceId, config)
|
||||||
|
|
||||||
const cashOut = cashOutConfig.active
|
const cashOut = !!cashOutConfig.active
|
||||||
|
|
||||||
// TODO new-admin: these two fields were not ever working
|
// TODO new-admin actually load status based on ping.
|
||||||
const machineModel = ''
|
|
||||||
const machineLocation = ''
|
|
||||||
|
|
||||||
// TODO: obtain next fields from somewhere
|
|
||||||
const printer = null
|
|
||||||
const pingTime = null
|
|
||||||
const statuses = [{label: 'Unknown detailed status', type: 'warning'}]
|
const statuses = [{label: 'Unknown detailed status', type: 'warning'}]
|
||||||
const softwareVersion = ''
|
|
||||||
|
|
||||||
return _.assign(r, {cashOut, machineModel, machineLocation, printer, pingTime, statuses, softwareVersion})
|
return _.assign(r, {cashOut, statuses})
|
||||||
}
|
}
|
||||||
|
|
||||||
return _.map(addName, machines)
|
return _.map(addName, machines)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
const _ = require('lodash/fp')
|
||||||
const { COINS, ALL_CRYPTOS } = require('./coins')
|
const { COINS, ALL_CRYPTOS } = require('./coins')
|
||||||
|
|
||||||
const { BTC, BCH, DASH, ETH, LTC, ZEC } = COINS
|
const { BTC, BCH, DASH, ETH, LTC, ZEC } = COINS
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ const configManager = require('../new-config-manager')
|
||||||
const wallet = require('../wallet')
|
const wallet = require('../wallet')
|
||||||
const ticker = require('../ticker')
|
const ticker = require('../ticker')
|
||||||
const coinUtils = require('../coin-utils')
|
const coinUtils = require('../coin-utils')
|
||||||
|
const logger = require('../logger')
|
||||||
|
|
||||||
function allScopes (cryptoScopes, machineScopes) {
|
function allScopes (cryptoScopes, machineScopes) {
|
||||||
const scopes = []
|
const scopes = []
|
||||||
|
|
@ -68,6 +69,9 @@ function getSingleCoinFunding (settings, fiatCode, cryptoCode) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Promise.allSettled not running on current version of node
|
||||||
|
const reflect = p => p.then(value => ({value, status: "fulfilled" }), error => ({error: error.toString(), status: "rejected" }))
|
||||||
|
|
||||||
function getFunding () {
|
function getFunding () {
|
||||||
return settingsLoader.loadLatest().then(settings => {
|
return settingsLoader.loadLatest().then(settings => {
|
||||||
const cryptoCodes = configManager.getAllCryptoCurrencies(settings.config)
|
const cryptoCodes = configManager.getAllCryptoCurrencies(settings.config)
|
||||||
|
|
@ -77,9 +81,10 @@ function getFunding () {
|
||||||
const cryptoDisplays = _.filter(pareCoins, cryptoCurrencies)
|
const cryptoDisplays = _.filter(pareCoins, cryptoCurrencies)
|
||||||
|
|
||||||
const promises = cryptoDisplays.map(it => getSingleCoinFunding(settings, fiatCode, it.cryptoCode))
|
const promises = cryptoDisplays.map(it => getSingleCoinFunding(settings, fiatCode, it.cryptoCode))
|
||||||
return Promise.all(promises)
|
return Promise.all(promises.map(reflect))
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
return _.toArray(_.merge(response, cryptoDisplays))
|
const mapped = response.map(it => _.merge({ errorMsg: it.error }, it.value))
|
||||||
|
return _.toArray(_.merge(mapped, cryptoDisplays))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ const serverLogs = require('../server-logs')
|
||||||
const pairing = require('../pairing')
|
const pairing = require('../pairing')
|
||||||
const { accounts: accountsConfig, coins, countries, currencies, languages } = require('../config')
|
const { accounts: accountsConfig, coins, countries, currencies, languages } = require('../config')
|
||||||
|
|
||||||
// TODO why does server logs messages can be null?
|
|
||||||
const typeDefs = gql`
|
const typeDefs = gql`
|
||||||
scalar JSON
|
scalar JSON
|
||||||
scalar JSONObject
|
scalar JSONObject
|
||||||
|
|
@ -54,6 +53,10 @@ const typeDefs = gql`
|
||||||
name: String!
|
name: String!
|
||||||
deviceId: ID!
|
deviceId: ID!
|
||||||
paired: Boolean!
|
paired: Boolean!
|
||||||
|
lastPing: Date
|
||||||
|
pairedAt: Date
|
||||||
|
version: String
|
||||||
|
model: String
|
||||||
cashbox: Int
|
cashbox: Int
|
||||||
cassette1: Int
|
cassette1: Int
|
||||||
cassette2: Int
|
cassette2: Int
|
||||||
|
|
@ -123,21 +126,22 @@ const typeDefs = gql`
|
||||||
|
|
||||||
type CoinFunds {
|
type CoinFunds {
|
||||||
cryptoCode: String!
|
cryptoCode: String!
|
||||||
fundingAddress: String!
|
errorMsg: String
|
||||||
fundingAddressUrl: String!
|
fundingAddress: String
|
||||||
confirmedBalance: String!
|
fundingAddressUrl: String
|
||||||
pending: String!
|
confirmedBalance: String
|
||||||
fiatConfirmedBalance: String!
|
pending: String
|
||||||
fiatPending: String!
|
fiatConfirmedBalance: String
|
||||||
fiatCode: String!
|
fiatPending: String
|
||||||
display: String!
|
fiatCode: String
|
||||||
unitScale: String!
|
display: String
|
||||||
|
unitScale: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProcessStatus {
|
type ProcessStatus {
|
||||||
name: String!
|
name: String!
|
||||||
state: String!
|
state: String!
|
||||||
uptime: Date!
|
uptime: Int!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Transaction {
|
type Transaction {
|
||||||
|
|
@ -156,6 +160,7 @@ const typeDefs = gql`
|
||||||
created: Date
|
created: Date
|
||||||
send: Boolean
|
send: Boolean
|
||||||
sendConfirmed: Boolean
|
sendConfirmed: Boolean
|
||||||
|
dispense: Boolean
|
||||||
timedout: Boolean
|
timedout: Boolean
|
||||||
sendTime: Date
|
sendTime: Date
|
||||||
errorCode: String
|
errorCode: String
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,10 @@ const xmlrpc = require('xmlrpc')
|
||||||
const logger = require('../logger')
|
const logger = require('../logger')
|
||||||
const { promisify } = require('util')
|
const { promisify } = require('util')
|
||||||
|
|
||||||
|
// TODO new-admin: add the following to supervisor config
|
||||||
|
// [inet_http_server]
|
||||||
|
// port = 127.0.0.1:9001
|
||||||
|
|
||||||
function getAllProcessInfo () {
|
function getAllProcessInfo () {
|
||||||
const convertStates = (state) => {
|
const convertStates = (state) => {
|
||||||
// From http://supervisord.org/subprocess.html#process-states
|
// From http://supervisord.org/subprocess.html#process-states
|
||||||
|
|
@ -45,7 +49,7 @@ function getAllProcessInfo () {
|
||||||
{
|
{
|
||||||
name: process.name,
|
name: process.name,
|
||||||
state: convertStates(process.statename),
|
state: convertStates(process.statename),
|
||||||
uptime: (process.statename === 'RUNNING') ? new Date(process.now) - new Date(process.start) : 0
|
uptime: (process.statename === 'RUNNING') ? process.now - process.start : 0
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ const FileAsync = require('lowdb/adapters/FileAsync')
|
||||||
const adapter = new FileAsync('db.json')
|
const adapter = new FileAsync('db.json')
|
||||||
let db = null
|
let db = null
|
||||||
|
|
||||||
|
// TODO new-admin save to actual db, like the old settings
|
||||||
low(adapter).then(it => {
|
low(adapter).then(it => {
|
||||||
db = it
|
db = it
|
||||||
})
|
})
|
||||||
|
|
@ -54,6 +55,7 @@ function loadLatest () {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO new-admin: grab correct version
|
||||||
function load (versionId) {
|
function load (versionId) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
if (!db) {
|
if (!db) {
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,6 @@ function plugins (settings, deviceId) {
|
||||||
const commissions = configManager.getCommissions(cryptoCode, deviceId, settings.config)
|
const commissions = configManager.getCommissions(cryptoCode, deviceId, settings.config)
|
||||||
const minimumTx = BN(commissions.minimumTx)
|
const minimumTx = BN(commissions.minimumTx)
|
||||||
const cashInFee = BN(commissions.fixedFee)
|
const cashInFee = BN(commissions.fixedFee)
|
||||||
logger.info('FEE', cashInFee)
|
|
||||||
const cashInCommission = BN(commissions.cashIn)
|
const cashInCommission = BN(commissions.cashIn)
|
||||||
const cashOutCommission = _.isNumber(commissions.cashOut) ? BN(commissions.cashOut) : null
|
const cashOutCommission = _.isNumber(commissions.cashOut) ? BN(commissions.cashOut) : null
|
||||||
const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode)
|
const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode)
|
||||||
|
|
@ -201,7 +200,7 @@ function plugins (settings, deviceId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function pollQueries (serialNumber, deviceTime, deviceRec) {
|
function pollQueries (serialNumber, deviceTime, deviceRec, machineVersion, machineModel) {
|
||||||
const localeConfig = configManager.getLocale(deviceId, settings.config)
|
const localeConfig = configManager.getLocale(deviceId, settings.config)
|
||||||
|
|
||||||
const fiatCode = localeConfig.fiatCurrency
|
const fiatCode = localeConfig.fiatCurrency
|
||||||
|
|
@ -210,7 +209,7 @@ function plugins (settings, deviceId) {
|
||||||
const tickerPromises = cryptoCodes.map(c => ticker.getRates(settings, fiatCode, c))
|
const tickerPromises = cryptoCodes.map(c => ticker.getRates(settings, fiatCode, c))
|
||||||
const balancePromises = cryptoCodes.map(c => fiatBalance(fiatCode, c))
|
const balancePromises = cryptoCodes.map(c => fiatBalance(fiatCode, c))
|
||||||
const testnetPromises = cryptoCodes.map(c => wallet.cryptoNetwork(settings, c))
|
const testnetPromises = cryptoCodes.map(c => wallet.cryptoNetwork(settings, c))
|
||||||
const pingPromise = recordPing(deviceTime)
|
const pingPromise = recordPing(deviceTime, machineVersion, machineModel)
|
||||||
const currentConfigVersionPromise = fetchCurrentConfigVersion()
|
const currentConfigVersionPromise = fetchCurrentConfigVersion()
|
||||||
|
|
||||||
const promises = [
|
const promises = [
|
||||||
|
|
@ -244,8 +243,10 @@ function plugins (settings, deviceId) {
|
||||||
return wallet.sendCoins(settings, tx.toAddress, tx.cryptoAtoms, tx.cryptoCode)
|
return wallet.sendCoins(settings, tx.toAddress, tx.cryptoAtoms, tx.cryptoCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
function recordPing (deviceTime) {
|
function recordPing (deviceTime, version, model) {
|
||||||
const devices = {
|
const devices = {
|
||||||
|
version,
|
||||||
|
model,
|
||||||
last_online: deviceTime
|
last_online: deviceTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ const logger = require('./logger')
|
||||||
const configManager = require('./new-config-manager')
|
const configManager = require('./new-config-manager')
|
||||||
const complianceTriggers = require('./compliance-triggers')
|
const complianceTriggers = require('./compliance-triggers')
|
||||||
const pairing = require('./pairing')
|
const pairing = require('./pairing')
|
||||||
// TODO new-admin: remove old settings loader from here.
|
|
||||||
const newSettingsLoader = require('./new-settings-loader')
|
const newSettingsLoader = require('./new-settings-loader')
|
||||||
const plugins = require('./plugins')
|
const plugins = require('./plugins')
|
||||||
const helpers = require('./route-helpers')
|
const helpers = require('./route-helpers')
|
||||||
|
|
@ -51,6 +50,8 @@ function checkHasLightning (settings) {
|
||||||
|
|
||||||
function poll (req, res, next) {
|
function poll (req, res, next) {
|
||||||
const machineVersion = req.query.version
|
const machineVersion = req.query.version
|
||||||
|
// TODO new-admin: pass this field from the machines
|
||||||
|
const machineModel = req.query.model
|
||||||
const deviceId = req.deviceId
|
const deviceId = req.deviceId
|
||||||
const deviceTime = req.deviceTime
|
const deviceTime = req.deviceTime
|
||||||
const serialNumber = req.query.sn
|
const serialNumber = req.query.sn
|
||||||
|
|
@ -64,13 +65,12 @@ function poll (req, res, next) {
|
||||||
const compatTriggers = complianceTriggers.getBackwardsCompatibleTriggers(triggers)
|
const compatTriggers = complianceTriggers.getBackwardsCompatibleTriggers(triggers)
|
||||||
|
|
||||||
const operatorInfo = configManager.getOperatorInfo(settings.config)
|
const operatorInfo = configManager.getOperatorInfo(settings.config)
|
||||||
const terms = configManager.getTermsConditions(settings.config)
|
|
||||||
const cashOutConfig = configManager.getCashOut(deviceId, settings.config)
|
const cashOutConfig = configManager.getCashOut(deviceId, settings.config)
|
||||||
const receipt = configManager.getReceipt(settings.config)
|
const receipt = configManager.getReceipt(settings.config)
|
||||||
|
|
||||||
pids[deviceId] = { pid, ts: Date.now() }
|
pids[deviceId] = { pid, ts: Date.now() }
|
||||||
|
|
||||||
return pi.pollQueries(serialNumber, deviceTime, req.query)
|
return pi.pollQueries(serialNumber, deviceTime, req.query, machineVersion, machineModel)
|
||||||
.then(results => {
|
.then(results => {
|
||||||
const cassettes = results.cassettes
|
const cassettes = results.cassettes
|
||||||
|
|
||||||
|
|
@ -103,7 +103,7 @@ function poll (req, res, next) {
|
||||||
sanctionsVerificationThreshold: compatTriggers.sancations,
|
sanctionsVerificationThreshold: compatTriggers.sancations,
|
||||||
frontCameraVerificationActive: !!compatTriggers.facephoto,
|
frontCameraVerificationActive: !!compatTriggers.facephoto,
|
||||||
frontCameraVerificationThreshold: compatTriggers.facephoto,
|
frontCameraVerificationThreshold: compatTriggers.facephoto,
|
||||||
receiptPrintingActive: receipt.active,
|
receiptPrintingActive: receipt.active === "on",
|
||||||
cassettes,
|
cassettes,
|
||||||
twoWayMode: cashOutConfig.active,
|
twoWayMode: cashOutConfig.active,
|
||||||
zeroConfLimit: cashOutConfig.zeroConfLimit,
|
zeroConfLimit: cashOutConfig.zeroConfLimit,
|
||||||
|
|
@ -389,7 +389,7 @@ const localApp = express()
|
||||||
app.use(compression({ threshold: 500 }))
|
app.use(compression({ threshold: 500 }))
|
||||||
app.use(helmet({ noCache: true }))
|
app.use(helmet({ noCache: true }))
|
||||||
app.use(bodyParser.json({ limit: '2mb' }))
|
app.use(bodyParser.json({ limit: '2mb' }))
|
||||||
app.use(morgan('dev', { skip, stream: logger.stream }))
|
app.use(morgan(':method :url :status :response-time ms - :res[content-length]', { stream: logger.stream }))
|
||||||
|
|
||||||
// These two have their own authorization
|
// These two have their own authorization
|
||||||
app.post('/pair', populateDeviceId, pair)
|
app.post('/pair', populateDeviceId, pair)
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ function _getRates (settings, fiatCode, cryptoCode) {
|
||||||
const config = settings.config
|
const config = settings.config
|
||||||
const plugin = configManager.getWalletSettings(cryptoCode, config).ticker
|
const plugin = configManager.getWalletSettings(cryptoCode, config).ticker
|
||||||
|
|
||||||
logger.info(plugin)
|
|
||||||
const account = settings.accounts[plugin]
|
const account = settings.accounts[plugin]
|
||||||
const ticker = ph.load(ph.TICKER, plugin)
|
const ticker = ph.load(ph.TICKER, plugin)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ function mergeStatusMode (a, b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWalletStatus (settings, tx) {
|
function getWalletStatus (settings, tx) {
|
||||||
const fudgeFactorEnabled = configManager.unscoped(settings.config).fudgeFactorActive
|
const fudgeFactorEnabled = configManager.getWalletSettings(tx.cryptoCode, settings.config).fudgeFactorActive
|
||||||
const fudgeFactor = fudgeFactorEnabled ? 100 : 0
|
const fudgeFactor = fudgeFactorEnabled ? 100 : 0
|
||||||
|
|
||||||
const walletStatusPromise = fetchWallet(settings, tx.cryptoCode)
|
const walletStatusPromise = fetchWallet(settings, tx.cryptoCode)
|
||||||
|
|
|
||||||
14
migrations/1592317667188-machine-version-name.js
Normal file
14
migrations/1592317667188-machine-version-name.js
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
const db = require('./db')
|
||||||
|
|
||||||
|
exports.up = function (next) {
|
||||||
|
var sql = [
|
||||||
|
'alter table devices add column version text',
|
||||||
|
'alter table devices add column model text'
|
||||||
|
]
|
||||||
|
|
||||||
|
db.multi(sql, next)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.down = function (next) {
|
||||||
|
next()
|
||||||
|
}
|
||||||
159
new-lamassu-admin/package-lock.json
generated
159
new-lamassu-admin/package-lock.json
generated
|
|
@ -4867,123 +4867,124 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"apollo-boost": {
|
|
||||||
"version": "0.4.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/apollo-boost/-/apollo-boost-0.4.7.tgz",
|
|
||||||
"integrity": "sha512-jfc3aqO0vpCV+W662EOG5gq4AH94yIsvSgAUuDvS3o/Z+8Joqn4zGC9CgLCDHusK30mFgtsEgwEe0pZoedohsQ==",
|
|
||||||
"requires": {
|
|
||||||
"apollo-cache": "^1.3.4",
|
|
||||||
"apollo-cache-inmemory": "^1.6.5",
|
|
||||||
"apollo-client": "^2.6.7",
|
|
||||||
"apollo-link": "^1.0.6",
|
|
||||||
"apollo-link-error": "^1.0.3",
|
|
||||||
"apollo-link-http": "^1.3.1",
|
|
||||||
"graphql-tag": "^2.4.2",
|
|
||||||
"ts-invariant": "^0.4.0",
|
|
||||||
"tslib": "^1.10.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": {
|
|
||||||
"version": "1.10.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
|
|
||||||
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apollo-cache": {
|
|
||||||
"version": "1.3.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.3.4.tgz",
|
|
||||||
"integrity": "sha512-7X5aGbqaOWYG+SSkCzJNHTz2ZKDcyRwtmvW4mGVLRqdQs+HxfXS4dUS2CcwrAj449se6tZ6NLUMnjko4KMt3KA==",
|
|
||||||
"requires": {
|
|
||||||
"apollo-utilities": "^1.3.3",
|
|
||||||
"tslib": "^1.10.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": {
|
|
||||||
"version": "1.10.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
|
|
||||||
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apollo-cache-inmemory": {
|
"apollo-cache-inmemory": {
|
||||||
"version": "1.6.5",
|
"version": "1.6.6",
|
||||||
"resolved": "https://registry.npmjs.org/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.5.tgz",
|
"resolved": "https://registry.npmjs.org/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.6.tgz",
|
||||||
"integrity": "sha512-koB76JUDJaycfejHmrXBbWIN9pRKM0Z9CJGQcBzIOtmte1JhEBSuzsOUu7NQgiXKYI4iGoMREcnaWffsosZynA==",
|
"integrity": "sha512-L8pToTW/+Xru2FFAhkZ1OA9q4V4nuvfoPecBM34DecAugUZEBhI2Hmpgnzq2hTKZ60LAMrlqiASm0aqAY6F8/A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"apollo-cache": "^1.3.4",
|
"apollo-cache": "^1.3.5",
|
||||||
"apollo-utilities": "^1.3.3",
|
"apollo-utilities": "^1.3.4",
|
||||||
"optimism": "^0.10.0",
|
"optimism": "^0.10.0",
|
||||||
"ts-invariant": "^0.4.0",
|
"ts-invariant": "^0.4.0",
|
||||||
"tslib": "^1.10.0"
|
"tslib": "^1.10.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"apollo-cache": {
|
||||||
|
"version": "1.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.3.5.tgz",
|
||||||
|
"integrity": "sha512-1XoDy8kJnyWY/i/+gLTEbYLnoiVtS8y7ikBr/IfmML4Qb+CM7dEEbIUOjnY716WqmZ/UpXIxTfJsY7rMcqiCXA==",
|
||||||
|
"requires": {
|
||||||
|
"apollo-utilities": "^1.3.4",
|
||||||
|
"tslib": "^1.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"apollo-utilities": {
|
||||||
|
"version": "1.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.4.tgz",
|
||||||
|
"integrity": "sha512-pk2hiWrCXMAy2fRPwEyhvka+mqwzeP60Jr1tRYi5xru+3ko94HI9o6lK0CT33/w4RDlxWchmdhDCrvdr+pHCig==",
|
||||||
|
"requires": {
|
||||||
|
"@wry/equality": "^0.1.2",
|
||||||
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
|
"ts-invariant": "^0.4.0",
|
||||||
|
"tslib": "^1.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tslib": {
|
"tslib": {
|
||||||
"version": "1.10.0",
|
"version": "1.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
|
||||||
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
|
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"apollo-client": {
|
"apollo-client": {
|
||||||
"version": "2.6.8",
|
"version": "2.6.10",
|
||||||
"resolved": "https://registry.npmjs.org/apollo-client/-/apollo-client-2.6.8.tgz",
|
"resolved": "https://registry.npmjs.org/apollo-client/-/apollo-client-2.6.10.tgz",
|
||||||
"integrity": "sha512-0zvJtAcONiozpa5z5zgou83iEKkBaXhhSSXJebFHRXs100SecDojyUWKjwTtBPn9HbM6o5xrvC5mo9VQ5fgAjw==",
|
"integrity": "sha512-jiPlMTN6/5CjZpJOkGeUV0mb4zxx33uXWdj/xQCfAMkuNAC3HN7CvYDyMHHEzmcQ5GV12LszWoQ/VlxET24CtA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/zen-observable": "^0.8.0",
|
"@types/zen-observable": "^0.8.0",
|
||||||
"apollo-cache": "1.3.4",
|
"apollo-cache": "1.3.5",
|
||||||
"apollo-link": "^1.0.0",
|
"apollo-link": "^1.0.0",
|
||||||
"apollo-utilities": "1.3.3",
|
"apollo-utilities": "1.3.4",
|
||||||
"symbol-observable": "^1.0.2",
|
"symbol-observable": "^1.0.2",
|
||||||
"ts-invariant": "^0.4.0",
|
"ts-invariant": "^0.4.0",
|
||||||
"tslib": "^1.10.0",
|
"tslib": "^1.10.0",
|
||||||
"zen-observable": "^0.8.0"
|
"zen-observable": "^0.8.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"apollo-cache": {
|
||||||
|
"version": "1.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.3.5.tgz",
|
||||||
|
"integrity": "sha512-1XoDy8kJnyWY/i/+gLTEbYLnoiVtS8y7ikBr/IfmML4Qb+CM7dEEbIUOjnY716WqmZ/UpXIxTfJsY7rMcqiCXA==",
|
||||||
|
"requires": {
|
||||||
|
"apollo-utilities": "^1.3.4",
|
||||||
|
"tslib": "^1.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"apollo-utilities": {
|
||||||
|
"version": "1.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.4.tgz",
|
||||||
|
"integrity": "sha512-pk2hiWrCXMAy2fRPwEyhvka+mqwzeP60Jr1tRYi5xru+3ko94HI9o6lK0CT33/w4RDlxWchmdhDCrvdr+pHCig==",
|
||||||
|
"requires": {
|
||||||
|
"@wry/equality": "^0.1.2",
|
||||||
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
|
"ts-invariant": "^0.4.0",
|
||||||
|
"tslib": "^1.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tslib": {
|
"tslib": {
|
||||||
"version": "1.10.0",
|
"version": "1.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
|
||||||
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
|
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"apollo-link": {
|
"apollo-link": {
|
||||||
"version": "1.2.13",
|
"version": "1.2.14",
|
||||||
"resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.13.tgz",
|
"resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.14.tgz",
|
||||||
"integrity": "sha512-+iBMcYeevMm1JpYgwDEIDt/y0BB7VWyvlm/7x+TIPNLHCTCMgcEgDuW5kH86iQZWo0I7mNwQiTOz+/3ShPFmBw==",
|
"integrity": "sha512-p67CMEFP7kOG1JZ0ZkYZwRDa369w5PIjtMjvrQd/HnIV8FRsHRqLqK+oAZQnFa1DDdZtOtHTi+aMIW6EatC2jg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"apollo-utilities": "^1.3.0",
|
"apollo-utilities": "^1.3.0",
|
||||||
"ts-invariant": "^0.4.0",
|
"ts-invariant": "^0.4.0",
|
||||||
"tslib": "^1.9.3",
|
"tslib": "^1.9.3",
|
||||||
"zen-observable-ts": "^0.8.20"
|
"zen-observable-ts": "^0.8.21"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"apollo-link-error": {
|
"apollo-link-error": {
|
||||||
"version": "1.1.12",
|
"version": "1.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/apollo-link-error/-/apollo-link-error-1.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/apollo-link-error/-/apollo-link-error-1.1.13.tgz",
|
||||||
"integrity": "sha512-psNmHyuy3valGikt/XHJfe0pKJnRX19tLLs6P6EHRxg+6q6JMXNVLYPaQBkL0FkwdTCB0cbFJAGRYCBviG8TDA==",
|
"integrity": "sha512-jAZOOahJU6bwSqb2ZyskEK1XdgUY9nkmeclCrW7Gddh1uasHVqmoYc4CKdb0/H0Y1J9lvaXKle2Wsw/Zx1AyUg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"apollo-link": "^1.2.13",
|
"apollo-link": "^1.2.14",
|
||||||
"apollo-link-http-common": "^0.2.15",
|
"apollo-link-http-common": "^0.2.16",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"apollo-link-http": {
|
"apollo-link-http": {
|
||||||
"version": "1.5.16",
|
"version": "1.5.17",
|
||||||
"resolved": "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.16.tgz",
|
"resolved": "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.17.tgz",
|
||||||
"integrity": "sha512-IA3xA/OcrOzINRZEECI6IdhRp/Twom5X5L9jMehfzEo2AXdeRwAMlH5LuvTZHgKD8V1MBnXdM6YXawXkTDSmJw==",
|
"integrity": "sha512-uWcqAotbwDEU/9+Dm9e1/clO7hTB2kQ/94JYcGouBVLjoKmTeJTUPQKcJGpPwUjZcSqgYicbFqQSoJIW0yrFvg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"apollo-link": "^1.2.13",
|
"apollo-link": "^1.2.14",
|
||||||
"apollo-link-http-common": "^0.2.15",
|
"apollo-link-http-common": "^0.2.16",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"apollo-link-http-common": {
|
"apollo-link-http-common": {
|
||||||
"version": "0.2.15",
|
"version": "0.2.16",
|
||||||
"resolved": "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.16.tgz",
|
||||||
"integrity": "sha512-+Heey4S2IPsPyTf8Ag3PugUupASJMW894iVps6hXbvwtg1aHSNMXUYO5VG7iRHkPzqpuzT4HMBanCTXPjtGzxg==",
|
"integrity": "sha512-2tIhOIrnaF4UbQHf7kjeQA/EmSorB7+HyJIIrUjJOKBgnXwuexi8aMecRlqTIDWcyVXCeqLhUnztMa6bOH/jTg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"apollo-link": "^1.2.13",
|
"apollo-link": "^1.2.14",
|
||||||
"ts-invariant": "^0.4.0",
|
"ts-invariant": "^0.4.0",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
}
|
}
|
||||||
|
|
@ -11762,9 +11763,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"graphql-tag": {
|
"graphql-tag": {
|
||||||
"version": "2.10.1",
|
"version": "2.10.3",
|
||||||
"resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.3.tgz",
|
||||||
"integrity": "sha512-jApXqWBzNXQ8jYa/HLkZJaVw9jgwNqZkywa2zfFn16Iv1Zb7ELNHkJaXHR7Quvd5SIGsy6Ny7SUKATgnu05uEg=="
|
"integrity": "sha512-4FOv3ZKfA4WdOKJeHdz6B3F/vxBLSgmBcGeAFPf4n1F64ltJUvOOerNj0rsJxONQGdhUMynQIvd6LzB+1J5oKA=="
|
||||||
},
|
},
|
||||||
"growly": {
|
"growly": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
|
|
@ -26657,9 +26658,9 @@
|
||||||
"integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ=="
|
"integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ=="
|
||||||
},
|
},
|
||||||
"zen-observable-ts": {
|
"zen-observable-ts": {
|
||||||
"version": "0.8.20",
|
"version": "0.8.21",
|
||||||
"resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.20.tgz",
|
"resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.21.tgz",
|
||||||
"integrity": "sha512-2rkjiPALhOtRaDX6pWyNqK1fnP5KkJJybYebopNSn6wDG1lxBoFs2+nwwXKoA6glHIrtwrfBBy6da0stkKtTAA==",
|
"integrity": "sha512-Yj3yXweRc8LdRMrCC8nIc4kkjWecPAUVh0TI0OUrWXx6aX790vLcDlWca6I4vsyCGH3LpWxq0dJRcMOFoVqmeg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "^1.9.3",
|
"tslib": "^1.9.3",
|
||||||
"zen-observable": "^0.8.0"
|
"zen-observable": "^0.8.0"
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,11 @@
|
||||||
"@material-ui/icons": "4.9.1",
|
"@material-ui/icons": "4.9.1",
|
||||||
"@material-ui/lab": "^4.0.0-alpha.47",
|
"@material-ui/lab": "^4.0.0-alpha.47",
|
||||||
"@use-hooks/axios": "1.3.0",
|
"@use-hooks/axios": "1.3.0",
|
||||||
"apollo-boost": "^0.4.7",
|
"apollo-cache-inmemory": "^1.6.6",
|
||||||
|
"apollo-client": "^2.6.10",
|
||||||
|
"apollo-link": "^1.2.14",
|
||||||
|
"apollo-link-error": "^1.1.13",
|
||||||
|
"apollo-link-http": "^1.5.17",
|
||||||
"axios": "0.19.0",
|
"axios": "0.19.0",
|
||||||
"bignumber.js": "9.0.0",
|
"bignumber.js": "9.0.0",
|
||||||
"classnames": "2.2.6",
|
"classnames": "2.2.6",
|
||||||
|
|
@ -17,6 +21,7 @@
|
||||||
"formik": "2.1.4",
|
"formik": "2.1.4",
|
||||||
"fuse.js": "^3.4.6",
|
"fuse.js": "^3.4.6",
|
||||||
"graphql": "^14.5.8",
|
"graphql": "^14.5.8",
|
||||||
|
"graphql-tag": "^2.10.3",
|
||||||
"jss-plugin-extend": "^10.0.0",
|
"jss-plugin-extend": "^10.0.0",
|
||||||
"libphonenumber-js": "^1.7.50",
|
"libphonenumber-js": "^1.7.50",
|
||||||
"moment": "2.24.0",
|
"moment": "2.24.0",
|
||||||
|
|
|
||||||
|
|
@ -6,42 +6,20 @@ import {
|
||||||
MuiThemeProvider,
|
MuiThemeProvider,
|
||||||
makeStyles
|
makeStyles
|
||||||
} from '@material-ui/core/styles'
|
} from '@material-ui/core/styles'
|
||||||
import ApolloClient from 'apollo-boost'
|
|
||||||
import { setAutoFreeze } from 'immer'
|
import { setAutoFreeze } from 'immer'
|
||||||
import { create } from 'jss'
|
import { create } from 'jss'
|
||||||
import extendJss from 'jss-plugin-extend'
|
import extendJss from 'jss-plugin-extend'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { BrowserRouter as Router } from 'react-router-dom'
|
import { BrowserRouter as Router } from 'react-router-dom'
|
||||||
|
|
||||||
|
import client from 'src/utils/apollo'
|
||||||
|
|
||||||
import Header from './components/layout/Header'
|
import Header from './components/layout/Header'
|
||||||
import { tree, Routes } from './routing/routes'
|
import { tree, Routes } from './routing/routes'
|
||||||
import global from './styling/global'
|
import global from './styling/global'
|
||||||
import theme from './styling/theme'
|
import theme from './styling/theme'
|
||||||
import { backgroundColor, mainWidth } from './styling/variables'
|
import { backgroundColor, mainWidth } from './styling/variables'
|
||||||
|
|
||||||
const defaultOptions = {
|
|
||||||
watchQuery: {
|
|
||||||
fetchPolicy: 'no-cache',
|
|
||||||
errorPolicy: 'ignore'
|
|
||||||
},
|
|
||||||
query: {
|
|
||||||
fetchPolicy: 'no-cache',
|
|
||||||
errorPolicy: 'all'
|
|
||||||
},
|
|
||||||
mutate: {
|
|
||||||
errorPolicy: 'all'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = new ApolloClient({
|
|
||||||
credentials: 'include',
|
|
||||||
defaultOptions,
|
|
||||||
uri:
|
|
||||||
process.env.NODE_ENV === 'development'
|
|
||||||
? 'https://localhost:8070/graphql/'
|
|
||||||
: '/graphql'
|
|
||||||
})
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
const whyDidYouRender = require('@welldone-software/why-did-you-render')
|
const whyDidYouRender = require('@welldone-software/why-did-you-render')
|
||||||
whyDidYouRender(React)
|
whyDidYouRender(React)
|
||||||
|
|
|
||||||
|
|
@ -3,41 +3,53 @@ import {
|
||||||
DialogActions,
|
DialogActions,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogContentText,
|
DialogContentText,
|
||||||
DialogTitle as MuiDialogTitle,
|
|
||||||
IconButton,
|
|
||||||
makeStyles
|
makeStyles
|
||||||
} from '@material-ui/core'
|
} from '@material-ui/core'
|
||||||
import React, { useState, memo } from 'react'
|
import React, { useEffect, useState, memo } from 'react'
|
||||||
|
|
||||||
import { Button } from '../components/buttons'
|
import { Button, IconButton } from 'src/components/buttons'
|
||||||
import { ReactComponent as CloseIcon } from '../styling/icons/action/close/zodiac.svg'
|
import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.svg'
|
||||||
import { spacer } from '../styling/variables'
|
|
||||||
|
|
||||||
import { TextInput } from './inputs'
|
import { TextInput } from './inputs'
|
||||||
import { H4, P } from './typography'
|
import { H4, P } from './typography'
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
|
label: {
|
||||||
|
fontSize: 16
|
||||||
|
},
|
||||||
|
spacing: {
|
||||||
|
padding: 32
|
||||||
|
},
|
||||||
|
wrapper: {
|
||||||
|
display: 'flex'
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
margin: [[20, 0, 24, 16]]
|
||||||
|
},
|
||||||
closeButton: {
|
closeButton: {
|
||||||
position: 'absolute',
|
padding: 0,
|
||||||
right: spacer,
|
margin: [[12, 12, 'auto', 'auto']]
|
||||||
top: spacer
|
// position: 'absolute',
|
||||||
|
// right: spacer,
|
||||||
|
// top: spacer
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const DialogTitle = ({ children, onClose }) => {
|
export const DialogTitle = ({ children, onClose }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
return (
|
return (
|
||||||
<MuiDialogTitle>
|
<div className={classes.wrapper}>
|
||||||
{children}
|
{children}
|
||||||
{onClose && (
|
{onClose && (
|
||||||
<IconButton
|
<IconButton
|
||||||
|
size={16}
|
||||||
aria-label="close"
|
aria-label="close"
|
||||||
className={classes.closeButton}
|
className={classes.closeButton}
|
||||||
onClick={onClose}>
|
onClick={onClose}>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
</MuiDialogTitle>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,9 +61,12 @@ export const ConfirmDialog = memo(
|
||||||
toBeConfirmed,
|
toBeConfirmed,
|
||||||
onConfirmed,
|
onConfirmed,
|
||||||
onDissmised,
|
onDissmised,
|
||||||
|
className,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
|
const classes = useStyles()
|
||||||
const [value, setValue] = useState('')
|
const [value, setValue] = useState('')
|
||||||
|
useEffect(() => setValue(''), [open])
|
||||||
const handleChange = event => {
|
const handleChange = event => {
|
||||||
setValue(event.target.value)
|
setValue(event.target.value)
|
||||||
}
|
}
|
||||||
|
|
@ -59,14 +74,14 @@ export const ConfirmDialog = memo(
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} aria-labelledby="form-dialog-title" {...props}>
|
<Dialog open={open} aria-labelledby="form-dialog-title" {...props}>
|
||||||
<DialogTitle id="customized-dialog-title" onClose={onDissmised}>
|
<DialogTitle id="customized-dialog-title" onClose={onDissmised}>
|
||||||
<H4>{title}</H4>
|
<H4 className={classes.title}>{title}</H4>
|
||||||
{subtitle && (
|
{subtitle && (
|
||||||
<DialogContentText>
|
<DialogContentText>
|
||||||
<P>{subtitle}</P>
|
<P>{subtitle}</P>
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
)}
|
)}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent className={className}>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={`Write '${toBeConfirmed}' to confirm`}
|
label={`Write '${toBeConfirmed}' to confirm`}
|
||||||
name="confirm-input"
|
name="confirm-input"
|
||||||
|
|
@ -78,11 +93,11 @@ export const ConfirmDialog = memo(
|
||||||
value={value}
|
value={value}
|
||||||
touched={{}}
|
touched={{}}
|
||||||
error={toBeConfirmed !== value}
|
error={toBeConfirmed !== value}
|
||||||
InputLabelProps={{ shrink: true }}
|
InputLabelProps={{ shrink: true, className: classes.label }}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions classes={{ spacing: classes.spacing }}>
|
||||||
<Button
|
<Button
|
||||||
color="green"
|
color="green"
|
||||||
disabled={toBeConfirmed !== value}
|
disabled={toBeConfirmed !== value}
|
||||||
|
|
|
||||||
55
new-lamassu-admin/src/components/HelpTooltip.js
Normal file
55
new-lamassu-admin/src/components/HelpTooltip.js
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { makeStyles, ClickAwayListener } from '@material-ui/core'
|
||||||
|
import React, { useState, memo } from 'react'
|
||||||
|
|
||||||
|
import Popper from 'src/components/Popper'
|
||||||
|
import { P } from 'src/components/typography'
|
||||||
|
import { ReactComponent as HelpIcon } from 'src/styling/icons/action/help/zodiac.svg'
|
||||||
|
|
||||||
|
const useStyles = makeStyles({
|
||||||
|
transparentButton: {
|
||||||
|
border: 'none',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
marginTop: 4,
|
||||||
|
cursor: 'pointer'
|
||||||
|
},
|
||||||
|
popoverContent: ({ width }) => ({
|
||||||
|
width,
|
||||||
|
padding: [[10, 15]]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const HelpTooltip = memo(({ children, width }) => {
|
||||||
|
const classes = useStyles({ width })
|
||||||
|
const [helpPopperAnchorEl, setHelpPopperAnchorEl] = useState(null)
|
||||||
|
|
||||||
|
const handleOpenHelpPopper = event => {
|
||||||
|
setHelpPopperAnchorEl(helpPopperAnchorEl ? null : event.currentTarget)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCloseHelpPopper = () => {
|
||||||
|
setHelpPopperAnchorEl(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const helpPopperOpen = Boolean(helpPopperAnchorEl)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ClickAwayListener onClickAway={handleCloseHelpPopper}>
|
||||||
|
<button
|
||||||
|
className={classes.transparentButton}
|
||||||
|
onClick={handleOpenHelpPopper}>
|
||||||
|
<HelpIcon />
|
||||||
|
<Popper
|
||||||
|
open={helpPopperOpen}
|
||||||
|
anchorEl={helpPopperAnchorEl}
|
||||||
|
placement="bottom"
|
||||||
|
onClose={handleCloseHelpPopper}>
|
||||||
|
<div className={classes.popoverContent}>
|
||||||
|
<P>{children}</P>
|
||||||
|
</div>
|
||||||
|
</Popper>
|
||||||
|
</button>
|
||||||
|
</ClickAwayListener>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default HelpTooltip
|
||||||
|
|
@ -5,11 +5,13 @@ import moment from 'moment'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState, useCallback } from 'react'
|
import React, { useState, useCallback } from 'react'
|
||||||
|
|
||||||
|
import { FeatureButton, Link } from 'src/components/buttons'
|
||||||
import { ReactComponent as Arrow } from 'src/styling/icons/arrow/download_logs.svg'
|
import { ReactComponent as Arrow } from 'src/styling/icons/arrow/download_logs.svg'
|
||||||
|
import { ReactComponent as DownloadInverseIcon } from 'src/styling/icons/button/download/white.svg'
|
||||||
|
import { ReactComponent as Download } from 'src/styling/icons/button/download/zodiac.svg'
|
||||||
import { primaryColor, offColor, zircon } from 'src/styling/variables'
|
import { primaryColor, offColor, zircon } from 'src/styling/variables'
|
||||||
|
|
||||||
import Popper from './Popper'
|
import Popper from './Popper'
|
||||||
import { Link } from './buttons'
|
|
||||||
import DateRangePicker from './date-range-picker/DateRangePicker'
|
import DateRangePicker from './date-range-picker/DateRangePicker'
|
||||||
import { RadioGroup } from './inputs'
|
import { RadioGroup } from './inputs'
|
||||||
import typographyStyles from './typography/styles'
|
import typographyStyles from './typography/styles'
|
||||||
|
|
@ -123,30 +125,26 @@ const styles = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
const ALL = 'all'
|
||||||
|
const RANGE = 'range'
|
||||||
|
|
||||||
const LogsDownloaderPopover = ({
|
const LogsDownloaderPopover = ({ name, getTimestamp, logs, title }) => {
|
||||||
id,
|
const [selectedRadio, setSelectedRadio] = useState(ALL)
|
||||||
name,
|
const [range, setRange] = useState({ from: null, to: null })
|
||||||
open,
|
const [anchorEl, setAnchorEl] = useState(null)
|
||||||
anchorEl,
|
|
||||||
getTimestamp,
|
|
||||||
logs,
|
|
||||||
title
|
|
||||||
}) => {
|
|
||||||
const radioButtonAll = 'all'
|
|
||||||
const radioButtonRange = 'range'
|
|
||||||
|
|
||||||
const [selectedRadio, setSelectedRadio] = useState(radioButtonAll)
|
|
||||||
const [range, setRange] = useState(null)
|
|
||||||
|
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
const dateRangePickerClasses = {
|
const dateRangePickerClasses = {
|
||||||
[classes.dateRangePickerShowing]: selectedRadio === radioButtonRange,
|
[classes.dateRangePickerShowing]: selectedRadio === RANGE,
|
||||||
[classes.dateRangePickerHidden]: selectedRadio === radioButtonAll
|
[classes.dateRangePickerHidden]: selectedRadio === ALL
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleRadioButtons = R.o(setSelectedRadio, R.path(['target', 'value']))
|
const handleRadioButtons = evt => {
|
||||||
|
const selectedRadio = R.path(['target', 'value'])(evt)
|
||||||
|
setSelectedRadio(selectedRadio)
|
||||||
|
if (selectedRadio === ALL) setRange({ from: null, to: null })
|
||||||
|
}
|
||||||
|
|
||||||
const handleRangeChange = useCallback(
|
const handleRangeChange = useCallback(
|
||||||
(from, to) => {
|
(from, to) => {
|
||||||
|
|
@ -164,7 +162,7 @@ const LogsDownloaderPopover = ({
|
||||||
return moment(date).format('YYYY-MM-DD_HH-mm')
|
return moment(date).format('YYYY-MM-DD_HH-mm')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedRadio === radioButtonAll) {
|
if (selectedRadio === ALL) {
|
||||||
const text = logs.map(it => JSON.stringify(it)).join('\n')
|
const text = logs.map(it => JSON.stringify(it)).join('\n')
|
||||||
const blob = new window.Blob([text], {
|
const blob = new window.Blob([text], {
|
||||||
type: 'text/plain;charset=utf-8'
|
type: 'text/plain;charset=utf-8'
|
||||||
|
|
@ -173,7 +171,7 @@ const LogsDownloaderPopover = ({
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedRadio === radioButtonRange) {
|
if (selectedRadio === RANGE) {
|
||||||
const text = logs
|
const text = logs
|
||||||
.filter(log =>
|
.filter(log =>
|
||||||
moment(getTimestamp(log)).isBetween(range.from, range.to, 'day', '[]')
|
moment(getTimestamp(log)).isBetween(range.from, range.to, 'day', '[]')
|
||||||
|
|
@ -190,51 +188,67 @@ const LogsDownloaderPopover = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleOpenRangePicker = event => {
|
||||||
|
setAnchorEl(anchorEl ? null : event.currentTarget)
|
||||||
|
}
|
||||||
|
|
||||||
const radioButtonOptions = [
|
const radioButtonOptions = [
|
||||||
{ display: 'All logs', code: radioButtonAll },
|
{ display: 'All logs', code: ALL },
|
||||||
{ display: 'Date range', code: radioButtonRange }
|
{ display: 'Date range', code: RANGE }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const open = Boolean(anchorEl)
|
||||||
|
const id = open ? 'date-range-popover' : undefined
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popper id={id} open={open} anchorEl={anchorEl} placement="bottom">
|
<>
|
||||||
<div className={classes.popoverContent}>
|
<FeatureButton
|
||||||
<div className={classes.popoverHeader}>{title}</div>
|
Icon={Download}
|
||||||
<div className={classes.radioButtonsContainer}>
|
InverseIcon={DownloadInverseIcon}
|
||||||
<RadioGroup
|
aria-describedby={id}
|
||||||
name="logs-select"
|
variant="contained"
|
||||||
value={selectedRadio}
|
onClick={handleOpenRangePicker}
|
||||||
options={radioButtonOptions}
|
/>
|
||||||
ariaLabel="logs-select"
|
<Popper id={id} open={open} anchorEl={anchorEl} placement="bottom">
|
||||||
onChange={handleRadioButtons}
|
<div className={classes.popoverContent}>
|
||||||
className={classes.radioButtons}
|
<div className={classes.popoverHeader}>{title}</div>
|
||||||
/>
|
<div className={classes.radioButtonsContainer}>
|
||||||
</div>
|
<RadioGroup
|
||||||
{selectedRadio === radioButtonRange && (
|
name="logs-select"
|
||||||
<div className={classnames(dateRangePickerClasses)}>
|
value={selectedRadio}
|
||||||
<div className={classes.dateContainerWrapper}>
|
options={radioButtonOptions}
|
||||||
{range && (
|
ariaLabel="logs-select"
|
||||||
<>
|
onChange={handleRadioButtons}
|
||||||
<DateContainer date={range.from}>From</DateContainer>
|
className={classes.radioButtons}
|
||||||
<div className={classes.arrowContainer}>
|
|
||||||
<Arrow className={classes.arrow} />
|
|
||||||
</div>
|
|
||||||
<DateContainer date={range.to}>To</DateContainer>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<DateRangePicker
|
|
||||||
maxDate={moment()}
|
|
||||||
onRangeChange={handleRangeChange}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
{selectedRadio === RANGE && (
|
||||||
<div className={classes.download}>
|
<div className={classnames(dateRangePickerClasses)}>
|
||||||
<Link color="primary" onClick={() => downloadLogs(range, logs)}>
|
<div className={classes.dateContainerWrapper}>
|
||||||
Download
|
{range && (
|
||||||
</Link>
|
<>
|
||||||
|
<DateContainer date={range.from}>From</DateContainer>
|
||||||
|
<div className={classes.arrowContainer}>
|
||||||
|
<Arrow className={classes.arrow} />
|
||||||
|
</div>
|
||||||
|
<DateContainer date={range.to}>To</DateContainer>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<DateRangePicker
|
||||||
|
maxDate={moment()}
|
||||||
|
onRangeChange={handleRangeChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={classes.download}>
|
||||||
|
<Link color="primary" onClick={() => downloadLogs(range, logs)}>
|
||||||
|
Download
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Popper>
|
||||||
</Popper>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import classnames from 'classnames'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { IconButton } from 'src/components/buttons'
|
import { IconButton } from 'src/components/buttons'
|
||||||
import { H1, H2 } from 'src/components/typography'
|
import { H1, H4 } from 'src/components/typography'
|
||||||
import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.svg'
|
import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.svg'
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
|
|
@ -18,29 +18,29 @@ const styles = {
|
||||||
height,
|
height,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
minHeight: 400,
|
minHeight: height ?? 400,
|
||||||
maxHeight: '90vh',
|
maxHeight: '90vh',
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
outline: 0
|
outline: 0
|
||||||
}),
|
}),
|
||||||
content: {
|
content: ({ small }) => ({
|
||||||
width: '100%',
|
width: '100%',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
padding: [[0, 32]]
|
padding: small ? [[0, 16]] : [[0, 32]]
|
||||||
},
|
}),
|
||||||
button: {
|
button: ({ small }) => ({
|
||||||
padding: 0,
|
padding: 0,
|
||||||
margin: [[20, 20, 'auto', 'auto']]
|
margin: small ? [[12, 12, 'auto', 'auto']] : [[16, 16, 'auto', 'auto']]
|
||||||
},
|
}),
|
||||||
header: {
|
header: {
|
||||||
display: 'flex'
|
display: 'flex'
|
||||||
},
|
},
|
||||||
title: {
|
title: ({ small }) => ({
|
||||||
margin: [[28, 0, 8, 32]]
|
margin: small ? [[20, 0, 8, 16]] : [[28, 0, 8, 32]]
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
@ -49,7 +49,7 @@ const Modal = ({
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
title,
|
title,
|
||||||
titleSmall,
|
small,
|
||||||
infoPanel,
|
infoPanel,
|
||||||
handleClose,
|
handleClose,
|
||||||
children,
|
children,
|
||||||
|
|
@ -58,9 +58,9 @@ const Modal = ({
|
||||||
closeOnBackdropClick,
|
closeOnBackdropClick,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles({ width, height })
|
const classes = useStyles({ width, height, small })
|
||||||
const TitleCase = titleSmall ? H2 : H1
|
const TitleCase = small ? H4 : H1
|
||||||
const closeSize = titleSmall ? 16 : 20
|
const closeSize = small ? 16 : 20
|
||||||
|
|
||||||
const innerClose = (evt, reason) => {
|
const innerClose = (evt, reason) => {
|
||||||
if (!closeOnBackdropClick && reason === 'backdropClick') return
|
if (!closeOnBackdropClick && reason === 'backdropClick') return
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,13 @@ import React, { useState } from 'react'
|
||||||
|
|
||||||
import { white } from 'src/styling/variables'
|
import { white } from 'src/styling/variables'
|
||||||
|
|
||||||
const Popover = ({ children, bgColor = white, arrowSize = 7, ...props }) => {
|
const Popover = ({
|
||||||
|
children,
|
||||||
|
bgColor = white,
|
||||||
|
arrowSize = 6,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
const [arrowRef, setArrowRef] = useState(null)
|
const [arrowRef, setArrowRef] = useState(null)
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
|
|
@ -27,7 +33,24 @@ const Popover = ({ children, bgColor = white, arrowSize = 7, ...props }) => {
|
||||||
borderLeft: [['2em', 'solid', 'transparent']],
|
borderLeft: [['2em', 'solid', 'transparent']],
|
||||||
borderRight: [['2em', 'solid', 'transparent']],
|
borderRight: [['2em', 'solid', 'transparent']],
|
||||||
borderBottom: [['2em', 'solid', bgColor]],
|
borderBottom: [['2em', 'solid', bgColor]],
|
||||||
marginTop: '-1.9em'
|
marginTop: '-1.9em',
|
||||||
|
'&:after': {
|
||||||
|
zIndex: -10,
|
||||||
|
content: '""',
|
||||||
|
position: 'absolute',
|
||||||
|
width: arrowSize * 3,
|
||||||
|
height: arrowSize * 3,
|
||||||
|
marginLeft: 0,
|
||||||
|
bottom: 0,
|
||||||
|
top: 'calc(50% - 0px)',
|
||||||
|
left: 0,
|
||||||
|
border: '5px solid #fff',
|
||||||
|
borderColor: 'transparent transparent #fff #fff',
|
||||||
|
transformOrigin: '0 0',
|
||||||
|
transform: 'rotate(45deg)',
|
||||||
|
boxShadow:
|
||||||
|
'0px 2px 1px -1px rgba(0,0,0,0.2),0px 1px 1px 0px rgba(0,0,0,0.14),0px 1px 3px 0px rgba(0,0,0,0.12)'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
arrowTop: {
|
arrowTop: {
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
|
|
@ -36,7 +59,24 @@ const Popover = ({ children, bgColor = white, arrowSize = 7, ...props }) => {
|
||||||
borderLeft: [['2em', 'solid', 'transparent']],
|
borderLeft: [['2em', 'solid', 'transparent']],
|
||||||
borderRight: [['2em', 'solid', 'transparent']],
|
borderRight: [['2em', 'solid', 'transparent']],
|
||||||
borderTop: [['2em', 'solid', bgColor]],
|
borderTop: [['2em', 'solid', bgColor]],
|
||||||
marginBottom: '-1.9em'
|
marginBottom: '-1.9em',
|
||||||
|
'&:after': {
|
||||||
|
zIndex: -10,
|
||||||
|
content: '""',
|
||||||
|
position: 'absolute',
|
||||||
|
width: arrowSize * 3,
|
||||||
|
height: arrowSize * 3,
|
||||||
|
marginLeft: 0,
|
||||||
|
bottom: 0,
|
||||||
|
top: -(arrowSize * 4 + 2),
|
||||||
|
left: 0,
|
||||||
|
border: '5px solid #fff',
|
||||||
|
borderColor: 'transparent transparent #fff #fff',
|
||||||
|
transformOrigin: '0 0',
|
||||||
|
transform: 'rotate(45deg)',
|
||||||
|
boxShadow:
|
||||||
|
'0px 2px 1px -1px rgba(0,0,0,0.2),0px 1px 1px 0px rgba(0,0,0,0.14),0px 1px 3px 0px rgba(0,0,0,0.12)'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
arrowRight: {
|
arrowRight: {
|
||||||
left: 0,
|
left: 0,
|
||||||
|
|
@ -81,6 +121,10 @@ const Popover = ({ children, bgColor = white, arrowSize = 7, ...props }) => {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
boundariesElement: 'scrollParent'
|
boundariesElement: 'scrollParent'
|
||||||
},
|
},
|
||||||
|
offset: {
|
||||||
|
enabled: true,
|
||||||
|
offset: '0, 10'
|
||||||
|
},
|
||||||
arrow: {
|
arrow: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
element: arrowRef
|
element: arrowRef
|
||||||
|
|
@ -94,7 +138,7 @@ const Popover = ({ children, bgColor = white, arrowSize = 7, ...props }) => {
|
||||||
modifiers={modifiers}
|
modifiers={modifiers}
|
||||||
className={classes.popover}
|
className={classes.popover}
|
||||||
{...props}>
|
{...props}>
|
||||||
<Paper className={classes.root}>
|
<Paper className={classnames(classes.root, className)}>
|
||||||
<span className={classnames(arrowClasses)} ref={setArrowRef} />
|
<span className={classnames(arrowClasses)} ref={setArrowRef} />
|
||||||
{children}
|
{children}
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react'
|
|
||||||
import Chip from '@material-ui/core/Chip'
|
import Chip from '@material-ui/core/Chip'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
tomato,
|
tomato,
|
||||||
|
|
@ -40,15 +40,21 @@ const useStyles = makeStyles({
|
||||||
fontSize: smallestFontSize,
|
fontSize: smallestFontSize,
|
||||||
fontWeight: inputFontWeight,
|
fontWeight: inputFontWeight,
|
||||||
fontFamily: inputFontFamily,
|
fontFamily: inputFontFamily,
|
||||||
paddingRight: spacer / 2,
|
padding: [[spacer / 2, spacer]],
|
||||||
paddingLeft: spacer / 2,
|
|
||||||
color: ({ type }) => colors[type]
|
color: ({ type }) => colors[type]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const Status = ({ status }) => {
|
const Status = ({ status, className }) => {
|
||||||
const classes = useStyles({ type: status.type })
|
const classes = useStyles({ type: status.type })
|
||||||
return <Chip type={status.type} label={status.label} classes={classes} />
|
return (
|
||||||
|
<Chip
|
||||||
|
type={status.type}
|
||||||
|
label={status.label}
|
||||||
|
className={className ?? null}
|
||||||
|
classes={classes}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const MainStatus = ({ statuses }) => {
|
const MainStatus = ({ statuses }) => {
|
||||||
|
|
@ -59,7 +65,7 @@ const MainStatus = ({ statuses }) => {
|
||||||
const plus = { label: `+${statuses.length - 1}`, type: mainStatus.type }
|
const plus = { label: `+${statuses.length - 1}`, type: mainStatus.type }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div style={{ marginLeft: -3 }}>
|
||||||
<Status status={mainStatus} />
|
<Status status={mainStatus} />
|
||||||
{statuses.length > 1 && <Status status={plus} />}
|
{statuses.length > 1 && <Status status={plus} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { H1 } from './typography'
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
title: {
|
title: {
|
||||||
marginTop: spacer * 3.5,
|
marginTop: spacer * 3,
|
||||||
marginBottom: spacer * 3
|
marginBottom: spacer * 3
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import typographyStyles from 'src/components/typography/styles'
|
||||||
import {
|
import {
|
||||||
white,
|
white,
|
||||||
fontColor,
|
fontColor,
|
||||||
|
|
@ -6,7 +7,6 @@ import {
|
||||||
offColor,
|
offColor,
|
||||||
offDarkColor
|
offDarkColor
|
||||||
} from 'src/styling/variables'
|
} from 'src/styling/variables'
|
||||||
import typographyStyles from 'src/components/typography/styles'
|
|
||||||
|
|
||||||
const { p } = typographyStyles
|
const { p } = typographyStyles
|
||||||
|
|
||||||
|
|
@ -27,7 +27,7 @@ export default {
|
||||||
extend: p,
|
extend: p,
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
border: 'none',
|
border: 'none',
|
||||||
height: 24,
|
height: 28,
|
||||||
outline: 0,
|
outline: 0,
|
||||||
borderRadius: 6,
|
borderRadius: 6,
|
||||||
padding: '0 8px',
|
padding: '0 8px',
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,19 @@ const styles = {
|
||||||
extend: baseButton,
|
extend: baseButton,
|
||||||
width: baseButton.height,
|
width: baseButton.height,
|
||||||
borderRadius: baseButton.height / 2,
|
borderRadius: baseButton.height / 2,
|
||||||
display: 'flex'
|
display: 'flex',
|
||||||
|
padding: 0
|
||||||
},
|
},
|
||||||
primary,
|
primary,
|
||||||
buttonIcon: {
|
buttonIcon: {
|
||||||
margin: 'auto',
|
margin: 'auto',
|
||||||
'& svg': {
|
'& svg': {
|
||||||
overflow: 'visible'
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
overflow: 'visible',
|
||||||
|
'& g': {
|
||||||
|
strokeWidth: 1.8
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
buttonIconActive: {} // required to extend primary
|
buttonIconActive: {} // required to extend primary
|
||||||
|
|
@ -25,29 +31,35 @@ const styles = {
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const FeatureButton = memo(({ className, Icon, InverseIcon, ...props }) => {
|
const FeatureButton = memo(
|
||||||
const classes = useStyles()
|
({ className, Icon, InverseIcon, children, ...props }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
const classNames = {
|
const classNames = {
|
||||||
[classes.featureButton]: true,
|
[classes.featureButton]: true,
|
||||||
[classes.primary]: true
|
[classes.primary]: true
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button className={classnames(classNames, className)} {...props}>
|
||||||
|
{Icon && (
|
||||||
|
<div className={classes.buttonIcon}>
|
||||||
|
<Icon />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{InverseIcon && (
|
||||||
|
<div
|
||||||
|
className={classnames(
|
||||||
|
classes.buttonIcon,
|
||||||
|
classes.buttonIconActive
|
||||||
|
)}>
|
||||||
|
<InverseIcon />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
return (
|
|
||||||
<button className={classnames(classNames, className)} {...props}>
|
|
||||||
{Icon && (
|
|
||||||
<div className={classes.buttonIcon}>
|
|
||||||
<Icon />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{InverseIcon && (
|
|
||||||
<div
|
|
||||||
className={classnames(classes.buttonIcon, classes.buttonIconActive)}>
|
|
||||||
<InverseIcon />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
export default FeatureButton
|
export default FeatureButton
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@ import { makeStyles } from '@material-ui/core/styles'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import React, { useState, memo } from 'react'
|
import React, { useState, memo } from 'react'
|
||||||
|
|
||||||
|
import Popover from 'src/components/Popper'
|
||||||
|
import typographyStyles from 'src/components/typography/styles'
|
||||||
import {
|
import {
|
||||||
subheaderColor,
|
subheaderColor,
|
||||||
subheaderDarkColor,
|
subheaderDarkColor,
|
||||||
offColor
|
offColor
|
||||||
} from 'src/styling/variables'
|
} from 'src/styling/variables'
|
||||||
import Popover from 'src/components/Popper'
|
|
||||||
import typographyStyles from 'src/components/typography/styles'
|
|
||||||
|
|
||||||
const { info2 } = typographyStyles
|
const { info2 } = typographyStyles
|
||||||
|
|
||||||
|
|
@ -70,6 +70,7 @@ const IDButton = memo(
|
||||||
InverseIcon,
|
InverseIcon,
|
||||||
popoverWidth = 152,
|
popoverWidth = 152,
|
||||||
children,
|
children,
|
||||||
|
popoverClassname,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
const [anchorEl, setAnchorEl] = useState(null)
|
const [anchorEl, setAnchorEl] = useState(null)
|
||||||
|
|
@ -117,6 +118,7 @@ const IDButton = memo(
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
<Popover
|
<Popover
|
||||||
|
className={popoverClassname}
|
||||||
id={id}
|
id={id}
|
||||||
open={open}
|
open={open}
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import React, { memo } from 'react'
|
||||||
|
|
||||||
import baseButtonStyles from './BaseButton.styles'
|
import baseButtonStyles from './BaseButton.styles'
|
||||||
|
|
||||||
const { baseButton } = baseButtonStyles
|
const { baseButton, primary } = baseButtonStyles
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
button: {
|
button: {
|
||||||
|
|
@ -12,19 +12,48 @@ const styles = {
|
||||||
borderRadius: baseButton.height / 2,
|
borderRadius: baseButton.height / 2,
|
||||||
outline: 0,
|
outline: 0,
|
||||||
padding: '0 20px'
|
padding: '0 20px'
|
||||||
}
|
},
|
||||||
|
primary,
|
||||||
|
buttonIcon: {
|
||||||
|
marginTop: 4,
|
||||||
|
marginRight: 4,
|
||||||
|
'& svg': {
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
overflow: 'visible'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buttonIconActive: {} // required to extend primary
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const SimpleButton = memo(({ className, children, color, size, ...props }) => {
|
const SimpleButton = memo(
|
||||||
const classes = useStyles()
|
({ className, Icon, InverseIcon, children, color, size, ...props }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button className={classnames(classes.button, className)} {...props}>
|
<button
|
||||||
{children}
|
className={classnames(classes.primary, classes.button, className)}
|
||||||
</button>
|
{...props}>
|
||||||
)
|
{Icon && (
|
||||||
})
|
<div className={classes.buttonIcon}>
|
||||||
|
<Icon />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{InverseIcon && (
|
||||||
|
<div
|
||||||
|
className={classnames(
|
||||||
|
classes.buttonIcon,
|
||||||
|
classes.buttonIconActive
|
||||||
|
)}>
|
||||||
|
<InverseIcon />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
export default SimpleButton
|
export default SimpleButton
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,35 @@
|
||||||
|
import * as R from 'ramda'
|
||||||
import React, { useContext } from 'react'
|
import React, { useContext } from 'react'
|
||||||
|
|
||||||
import { Td, THead } from 'src/components/fake-table/Table'
|
import {
|
||||||
|
Td,
|
||||||
|
THead,
|
||||||
|
TDoubleLevelHead,
|
||||||
|
ThDoubleLevel
|
||||||
|
} from 'src/components/fake-table/Table'
|
||||||
import { startCase } from 'src/utils/string'
|
import { startCase } from 'src/utils/string'
|
||||||
|
|
||||||
import TableCtx from './Context'
|
import TableCtx from './Context'
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
return [innerElements, TDoubleLevelHead]
|
||||||
|
}
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const {
|
const {
|
||||||
elements,
|
elements,
|
||||||
|
|
@ -16,15 +41,35 @@ const Header = () => {
|
||||||
toggleWidth,
|
toggleWidth,
|
||||||
DEFAULT_COL_SIZE
|
DEFAULT_COL_SIZE
|
||||||
} = useContext(TableCtx)
|
} = useContext(TableCtx)
|
||||||
|
|
||||||
|
const mapElement2 = (it, idx) => {
|
||||||
|
const { width, elements, name } = it
|
||||||
|
|
||||||
|
if (elements && elements.length) {
|
||||||
|
return (
|
||||||
|
<ThDoubleLevel key={idx} width={width} title={name}>
|
||||||
|
{elements.map(mapElement)}
|
||||||
|
</ThDoubleLevel>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapElement(it, idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapElement = (
|
||||||
|
{ name, width = DEFAULT_COL_SIZE, header, textAlign },
|
||||||
|
idx
|
||||||
|
) => (
|
||||||
|
<Td header key={idx} width={width} textAlign={textAlign}>
|
||||||
|
{header || startCase(name)}
|
||||||
|
</Td>
|
||||||
|
)
|
||||||
|
|
||||||
|
const [innerElements, HeaderElement] = groupSecondHeader(elements)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<THead>
|
<HeaderElement>
|
||||||
{elements.map(
|
{innerElements.map(mapElement2)}
|
||||||
({ name, width = DEFAULT_COL_SIZE, header, textAlign }, idx) => (
|
|
||||||
<Td header key={idx} width={width} textAlign={textAlign}>
|
|
||||||
{header || startCase(name)}
|
|
||||||
</Td>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
{enableEdit && (
|
{enableEdit && (
|
||||||
<Td header width={editWidth} textAlign="center">
|
<Td header width={editWidth} textAlign="center">
|
||||||
Edit
|
Edit
|
||||||
|
|
@ -40,7 +85,7 @@ const Header = () => {
|
||||||
Enable
|
Enable
|
||||||
</Td>
|
</Td>
|
||||||
)}
|
)}
|
||||||
</THead>
|
</HeaderElement>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ const ActionCol = ({ disabled, editing }) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const ECol = ({ editing, config }) => {
|
const ECol = ({ editing, config, extraPaddingRight, extraPaddingLeft }) => {
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
input,
|
input,
|
||||||
|
|
@ -115,7 +115,11 @@ const ECol = ({ editing, config }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Td
|
<Td
|
||||||
className={{ [classes.withSuffix]: suffix }}
|
className={{
|
||||||
|
[classes.extraPaddingRight]: extraPaddingRight,
|
||||||
|
[classes.extraPaddingLeft]: extraPaddingLeft,
|
||||||
|
[classes.withSuffix]: suffix
|
||||||
|
}}
|
||||||
width={width}
|
width={width}
|
||||||
size={size}
|
size={size}
|
||||||
bold={bold}
|
bold={bold}
|
||||||
|
|
@ -162,13 +166,31 @@ const ERow = ({ editing, disabled }) => {
|
||||||
const shouldStripe = stripeWhen && stripeWhen(values) && !editing
|
const shouldStripe = stripeWhen && stripeWhen(values) && !editing
|
||||||
|
|
||||||
const innerElements = shouldStripe ? groupStriped(elements) : elements
|
const innerElements = shouldStripe ? groupStriped(elements) : elements
|
||||||
|
const [toSHeader] = R.partition(R.has('doubleHeader'))(elements)
|
||||||
|
|
||||||
|
const extraPaddingLeftIndex = toSHeader?.length
|
||||||
|
? R.indexOf(toSHeader[0], elements)
|
||||||
|
: -1
|
||||||
|
|
||||||
|
const extraPaddingRightIndex = toSHeader?.length
|
||||||
|
? R.indexOf(toSHeader[toSHeader.length - 1], elements)
|
||||||
|
: -1
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tr
|
<Tr
|
||||||
size={rowSize}
|
size={rowSize}
|
||||||
error={errors && errors.length}
|
error={errors && errors.length}
|
||||||
errorMessage={errors && errors.toString()}>
|
errorMessage={errors && errors.toString()}>
|
||||||
{innerElements.map((it, idx) => {
|
{innerElements.map((it, idx) => {
|
||||||
return <ECol key={idx} config={it} editing={editing} />
|
return (
|
||||||
|
<ECol
|
||||||
|
key={idx}
|
||||||
|
config={it}
|
||||||
|
editing={editing}
|
||||||
|
extraPaddingRight={extraPaddingRightIndex === idx}
|
||||||
|
extraPaddingLeft={extraPaddingLeftIndex === idx}
|
||||||
|
/>
|
||||||
|
)
|
||||||
})}
|
})}
|
||||||
{(enableEdit || enableDelete || enableToggle) && (
|
{(enableEdit || enableDelete || enableToggle) && (
|
||||||
<ActionCol disabled={disabled} editing={editing} />
|
<ActionCol disabled={disabled} editing={editing} />
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,20 @@ export default {
|
||||||
cancelButton: {
|
cancelButton: {
|
||||||
marginRight: 20
|
marginRight: 20
|
||||||
},
|
},
|
||||||
withSuffix: ({ textAlign }) => ({
|
extraPaddingLeft: {
|
||||||
display: 'flex',
|
paddingLeft: 35
|
||||||
alignItems: 'baseline',
|
},
|
||||||
justifyContent: textAlign === 'right' && 'end'
|
extraPaddingRight: {
|
||||||
}),
|
paddingRight: 45
|
||||||
|
},
|
||||||
|
withSuffix: ({ textAlign }) => {
|
||||||
|
const justifyContent = textAlign === 'right' ? 'end' : textAlign
|
||||||
|
return {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'baseline',
|
||||||
|
justifyContent
|
||||||
|
}
|
||||||
|
},
|
||||||
suffix: {
|
suffix: {
|
||||||
marginLeft: 7
|
marginLeft: 7
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -65,8 +65,8 @@ const Th = ({ children, ...props }) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const ThDoubleLevel = ({ title, children, className }) => {
|
const ThDoubleLevel = ({ title, children, className, width }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles({ width })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classnames(className, classes.thDoubleLevel)}>
|
<div className={classnames(className, classes.thDoubleLevel)}>
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,13 @@ export default {
|
||||||
color: white,
|
color: white,
|
||||||
display: 'table-row'
|
display: 'table-row'
|
||||||
},
|
},
|
||||||
thDoubleLevel: {
|
thDoubleLevel: ({ width }) => ({
|
||||||
padding: [[0, spacer * 2]],
|
width,
|
||||||
display: 'table-cell',
|
display: 'table-cell',
|
||||||
'& > :first-child': {
|
'& > :first-child': {
|
||||||
|
margin: [[0, 10]],
|
||||||
extend: label1,
|
extend: label1,
|
||||||
|
fontWeight: 700,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|
@ -45,14 +47,12 @@ export default {
|
||||||
height: 28
|
height: 28
|
||||||
},
|
},
|
||||||
'& > :last-child': {
|
'& > :last-child': {
|
||||||
|
padding: [[0, 11]],
|
||||||
display: 'table-cell',
|
display: 'table-cell',
|
||||||
verticalAlign: 'middle',
|
verticalAlign: 'middle',
|
||||||
height: tableDoubleHeaderHeight - 28,
|
height: tableDoubleHeaderHeight - 28
|
||||||
'& > div': {
|
|
||||||
display: 'inline-block'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
cellDoubleLevel: {
|
cellDoubleLevel: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
padding: [[0, spacer * 2]]
|
padding: [[0, spacer * 2]]
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import { useSelect } from 'downshift'
|
import { useSelect } from 'downshift'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { ReactComponent as Arrowdown } from '../../../styling/icons/action/arrow/regular.svg'
|
import { ReactComponent as Arrowdown } from 'src/styling/icons/action/arrow/regular.svg'
|
||||||
import { startCase } from '../../../utils/string'
|
import { startCase } from 'src/utils/string'
|
||||||
|
|
||||||
import styles from './Select.styles'
|
import styles from './Select.styles'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@ import { bySize, bold } from 'src/styling/helpers'
|
||||||
import { secondaryColor } from 'src/styling/variables'
|
import { secondaryColor } from 'src/styling/variables'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
size: ({ size }) => bySize(size),
|
size: ({ size }) => ({
|
||||||
|
marginTop: size === 'lg' ? -2 : 0,
|
||||||
|
...bySize(size)
|
||||||
|
}),
|
||||||
bold,
|
bold,
|
||||||
root: ({ width, textAlign }) => ({
|
root: ({ width, textAlign }) => ({
|
||||||
width,
|
width,
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ const RadioGroupFormik = memo(({ label, ...props }) => {
|
||||||
options={props.options}
|
options={props.options}
|
||||||
ariaLabel={name}
|
ariaLabel={name}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
console.log(e)
|
|
||||||
onChange(e)
|
onChange(e)
|
||||||
props.resetError && props.resetError()
|
props.resetError && props.resetError()
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import Autocomplete from './Autocomplete'
|
import Autocomplete from './Autocomplete'
|
||||||
import Checkbox from './Checkbox'
|
import Checkbox from './Checkbox'
|
||||||
import RadioGroup from './RadioGroup'
|
import RadioGroup from './RadioGroup'
|
||||||
|
import SecretInput from './SecretInput'
|
||||||
import TextInput from './TextInput'
|
import TextInput from './TextInput'
|
||||||
|
|
||||||
export { Autocomplete, Checkbox, TextInput, RadioGroup }
|
export { Autocomplete, Checkbox, TextInput, SecretInput, RadioGroup }
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ const Sidebar = ({
|
||||||
[classes.link]: true
|
[classes.link]: true
|
||||||
})}
|
})}
|
||||||
onClick={() => onClick(it)}>
|
onClick={() => onClick(it)}>
|
||||||
{itemRender ? itemRender(it) : displayName(it)}
|
{itemRender ? itemRender(it, isSelected(it)) : displayName(it)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{children}
|
{children}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import classnames from 'classnames'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import ErrorMessage from 'src/components/ErrorMessage'
|
import ErrorMessage from 'src/components/ErrorMessage'
|
||||||
|
|
@ -8,10 +9,10 @@ import styles from './TitleSection.styles'
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const TitleSection = ({ title, error, labels }) => {
|
const TitleSection = ({ className, title, error, labels, children }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
return (
|
return (
|
||||||
<div className={classes.titleWrapper}>
|
<div className={classnames(classes.titleWrapper, className)}>
|
||||||
<div className={classes.titleAndButtonsContainer}>
|
<div className={classes.titleAndButtonsContainer}>
|
||||||
<Title>{title}</Title>
|
<Title>{title}</Title>
|
||||||
{error && (
|
{error && (
|
||||||
|
|
@ -19,6 +20,7 @@ const TitleSection = ({ title, error, labels }) => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.headerLabels}>{labels}</div>
|
<div className={classes.headerLabels}>{labels}</div>
|
||||||
|
{children}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { makeStyles } from '@material-ui/core/styles'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import React, { memo } from 'react'
|
import React, { memo } from 'react'
|
||||||
|
|
||||||
|
import typographyStyles from 'src/components/typography/styles'
|
||||||
import {
|
import {
|
||||||
tableCellColor,
|
tableCellColor,
|
||||||
tableCellHeight,
|
tableCellHeight,
|
||||||
|
|
@ -10,7 +11,6 @@ import {
|
||||||
tableErrorColor,
|
tableErrorColor,
|
||||||
tableSuccessColor
|
tableSuccessColor
|
||||||
} from 'src/styling/variables'
|
} from 'src/styling/variables'
|
||||||
import typographyStyles from 'src/components/typography/styles'
|
|
||||||
|
|
||||||
const { info2, p } = typographyStyles
|
const { info2, p } = typographyStyles
|
||||||
|
|
||||||
|
|
@ -19,8 +19,7 @@ const useStyles = makeStyles({
|
||||||
extend: p,
|
extend: p,
|
||||||
padding: 4,
|
padding: 4,
|
||||||
height: tableCellHeight,
|
height: tableCellHeight,
|
||||||
backgroundColor: tableCellColor,
|
backgroundColor: tableCellColor
|
||||||
boxShadow: '0 0 4px 0 rgba(0, 0, 0, 0.08)'
|
|
||||||
},
|
},
|
||||||
lg: {
|
lg: {
|
||||||
extend: info2,
|
extend: info2,
|
||||||
|
|
|
||||||
|
|
@ -6,37 +6,62 @@ import styles from './styles'
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
function H1({ children, className, ...props }) {
|
function H1({ children, noMargin, className, ...props }) {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
const classNames = {
|
||||||
|
[classes.h1]: true,
|
||||||
|
[classes.noMargin]: noMargin,
|
||||||
|
[className]: !!className
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<h1 className={classnames(classes.h1, className)} {...props}>
|
<h1 className={classnames(classNames)} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</h1>
|
</h1>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function H2({ children, className, ...props }) {
|
function H2({ children, noMargin, className, ...props }) {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
const classNames = {
|
||||||
|
[classes.h2]: true,
|
||||||
|
[classes.noMargin]: noMargin,
|
||||||
|
[className]: !!className
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<h2 className={classnames(classes.h2, className)} {...props}>
|
<h2 className={classnames(classNames)} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</h2>
|
</h2>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function H3({ children, className, ...props }) {
|
function H3({ children, noMargin, className, ...props }) {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
const classNames = {
|
||||||
|
[classes.h3]: true,
|
||||||
|
[classes.noMargin]: noMargin,
|
||||||
|
[className]: !!className
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<h3 className={classnames(classes.h3, className)} {...props}>
|
<h3 className={classnames(classNames)} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</h3>
|
</h3>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function H4({ children, className, ...props }) {
|
function H4({ children, noMargin, className, ...props }) {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
console.log(className)
|
||||||
|
const classNames = {
|
||||||
|
[classes.h4]: true,
|
||||||
|
[classes.noMargin]: noMargin,
|
||||||
|
[className]: !!className
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<h4 className={classnames(classes.h4, className)} {...props}>
|
<h4 className={classnames(classNames)} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</h4>
|
</h4>
|
||||||
)
|
)
|
||||||
|
|
@ -57,13 +82,13 @@ function pBuilder(elementClass) {
|
||||||
return ({ inline, noMargin, className, children, ...props }) => {
|
return ({ inline, noMargin, className, children, ...props }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const classNames = {
|
const classNames = {
|
||||||
|
[className]: !!className,
|
||||||
[classes[elementClass]]: elementClass,
|
[classes[elementClass]]: elementClass,
|
||||||
className: true,
|
|
||||||
[classes.inline]: inline,
|
[classes.inline]: inline,
|
||||||
[classes.noMarginP]: noMargin
|
[classes.noMargin]: noMargin
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<p className={classnames(classNames, className)} {...props}>
|
<p className={classnames(classNames)} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</p>
|
</p>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ export default {
|
||||||
inline: {
|
inline: {
|
||||||
display: 'inline'
|
display: 'inline'
|
||||||
},
|
},
|
||||||
noMarginP: {
|
noMargin: {
|
||||||
margin: 0
|
margin: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { useMutation } from '@apollo/react-hooks'
|
import { useMutation } from '@apollo/react-hooks'
|
||||||
import { Dialog, DialogContent, SvgIcon, IconButton } from '@material-ui/core'
|
import { Dialog, DialogContent, SvgIcon, IconButton } from '@material-ui/core'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { gql } from 'apollo-boost'
|
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import { Form, Formik, FastField } from 'formik'
|
import { Form, Formik, FastField } from 'formik'
|
||||||
|
import gql from 'graphql-tag'
|
||||||
import QRCode from 'qrcode.react'
|
import QRCode from 'qrcode.react'
|
||||||
import React, { memo, useState } from 'react'
|
import React, { memo, useState } from 'react'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,30 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { gql } from 'apollo-boost'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
import HelpTooltip from 'src/components/HelpTooltip'
|
||||||
import { NamespacedTable as EditableTable } from 'src/components/editableTable'
|
import { NamespacedTable as EditableTable } from 'src/components/editableTable'
|
||||||
|
import { Switch } from 'src/components/inputs'
|
||||||
import TitleSection from 'src/components/layout/TitleSection'
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
|
import { P, Label2 } from 'src/components/typography'
|
||||||
import { fromNamespace, toNamespace } from 'src/utils/config'
|
import { fromNamespace, toNamespace } from 'src/utils/config'
|
||||||
|
|
||||||
import Wizard from './Wizard'
|
import Wizard from './Wizard'
|
||||||
import { DenominationsSchema, getElements } from './helper'
|
import { DenominationsSchema, getElements } from './helper'
|
||||||
|
|
||||||
|
const useStyles = makeStyles({
|
||||||
|
fudgeFactor: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginRight: 156
|
||||||
|
},
|
||||||
|
switchLabel: {
|
||||||
|
margin: 6
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const SAVE_CONFIG = gql`
|
const SAVE_CONFIG = gql`
|
||||||
mutation Save($config: JSONObject) {
|
mutation Save($config: JSONObject) {
|
||||||
saveConfig(config: $config)
|
saveConfig(config: $config)
|
||||||
|
|
@ -30,6 +45,7 @@ const GET_INFO = gql`
|
||||||
`
|
`
|
||||||
|
|
||||||
const CashOut = ({ name: SCREEN_KEY }) => {
|
const CashOut = ({ name: SCREEN_KEY }) => {
|
||||||
|
const classes = useStyles()
|
||||||
const [wizard, setWizard] = useState(false)
|
const [wizard, setWizard] = useState(false)
|
||||||
const [error, setError] = useState(false)
|
const [error, setError] = useState(false)
|
||||||
const { data } = useQuery(GET_INFO)
|
const { data } = useQuery(GET_INFO)
|
||||||
|
|
@ -47,6 +63,7 @@ const CashOut = ({ name: SCREEN_KEY }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = data?.config && fromNamespace(SCREEN_KEY)(data.config)
|
const config = data?.config && fromNamespace(SCREEN_KEY)(data.config)
|
||||||
|
const fudgeFactorActive = config?.fudgeFactorActive ?? false
|
||||||
const locale = data?.config && fromNamespace('locale')(data.config)
|
const locale = data?.config && fromNamespace('locale')(data.config)
|
||||||
const machines = data?.machines ?? []
|
const machines = data?.machines ?? []
|
||||||
|
|
||||||
|
|
@ -58,7 +75,31 @@ const CashOut = ({ name: SCREEN_KEY }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TitleSection title="Cash-out" error={error} />
|
<TitleSection title="Cash-out" error={error}>
|
||||||
|
<div className={classes.fudgeFactor}>
|
||||||
|
<P>Transaction fudge factor</P>
|
||||||
|
<Switch
|
||||||
|
checked={fudgeFactorActive}
|
||||||
|
onChange={event => {
|
||||||
|
save({ fudgeFactorActive: event.target.checked })
|
||||||
|
}}
|
||||||
|
value={fudgeFactorActive}
|
||||||
|
/>
|
||||||
|
<Label2 className={classes.switchLabel}>
|
||||||
|
{fudgeFactorActive ? 'On' : 'Off'}
|
||||||
|
</Label2>
|
||||||
|
<HelpTooltip width={304}>
|
||||||
|
<P>
|
||||||
|
Automatically accept customer deposits as complete if their
|
||||||
|
received amount is 10 crypto atoms or less.
|
||||||
|
</P>
|
||||||
|
<P>
|
||||||
|
(Crypto atoms are the smallest unit in each cryptocurrency. E.g.,
|
||||||
|
satoshis in Bitcoin, or wei in Ethereum.)
|
||||||
|
</P>
|
||||||
|
</HelpTooltip>
|
||||||
|
</div>
|
||||||
|
</TitleSection>
|
||||||
<EditableTable
|
<EditableTable
|
||||||
name="test"
|
name="test"
|
||||||
namespaces={R.map(R.path(['deviceId']))(machines)}
|
namespaces={R.map(R.path(['deviceId']))(machines)}
|
||||||
|
|
|
||||||
|
|
@ -57,14 +57,13 @@ const WizardStep = ({
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
</Formik>
|
</Formik>
|
||||||
// TODO: there was a disabled link here showing the currency code; restore it
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{lastStep && (
|
{lastStep && (
|
||||||
<>
|
<>
|
||||||
<P>
|
<P>
|
||||||
When enabling cash out, your bill count will be authomatically set
|
When enabling cash out, your bill count will be automatically set to
|
||||||
to zero. Make sure you physically put cash inside the cashboxes to
|
zero. Make sure you physically put cash inside the cashboxes to
|
||||||
allow the machine to dispense it to your users. If you already did,
|
allow the machine to dispense it to your users. If you already did,
|
||||||
make sure you set the correct cash out bill count for this machine
|
make sure you set the correct cash out bill count for this machine
|
||||||
on your Cashboxes tab under Maintenance.
|
on your Cashboxes tab under Maintenance.
|
||||||
|
|
@ -72,7 +71,7 @@ const WizardStep = ({
|
||||||
<P>
|
<P>
|
||||||
When enabling cash out, default commissions will be set. To change
|
When enabling cash out, default commissions will be set. To change
|
||||||
commissions for this machine, please go to the Commissions tab under
|
commissions for this machine, please go to the Commissions tab under
|
||||||
Settings. where you can set exceptions for each of the available
|
Settings where you can set exceptions for each of the available
|
||||||
cryptocurrencies.
|
cryptocurrencies.
|
||||||
</P>
|
</P>
|
||||||
<div className={classes.submit}>
|
<div className={classes.submit}>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ const getElements = (machines, { fiatCurrency } = {}) => {
|
||||||
{
|
{
|
||||||
name: 'id',
|
name: 'id',
|
||||||
header: 'Machine',
|
header: 'Machine',
|
||||||
width: 254,
|
width: 200,
|
||||||
view: it => machines.find(({ deviceId }) => deviceId === it).name,
|
view: it => machines.find(({ deviceId }) => deviceId === it).name,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
editable: false
|
editable: false
|
||||||
|
|
@ -24,7 +24,8 @@ const getElements = (machines, { fiatCurrency } = {}) => {
|
||||||
view: it => `${it} ${fiatCurrency}`,
|
view: it => `${it} ${fiatCurrency}`,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
stripe: true,
|
stripe: true,
|
||||||
width: 265,
|
width: 200,
|
||||||
|
textAlign: 'right',
|
||||||
input: TextInput
|
input: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -33,7 +34,8 @@ const getElements = (machines, { fiatCurrency } = {}) => {
|
||||||
view: it => `${it} ${fiatCurrency}`,
|
view: it => `${it} ${fiatCurrency}`,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
stripe: true,
|
stripe: true,
|
||||||
width: 265,
|
textAlign: 'right',
|
||||||
|
width: 200,
|
||||||
input: TextInput
|
input: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -41,6 +43,7 @@ const getElements = (machines, { fiatCurrency } = {}) => {
|
||||||
header: '0-conf Limit',
|
header: '0-conf Limit',
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
stripe: true,
|
stripe: true,
|
||||||
|
textAlign: 'right',
|
||||||
width: 200,
|
width: 200,
|
||||||
input: TextInput
|
input: TextInput
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { gql } from 'apollo-boost'
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { Table as EditableTable } from 'src/components/editableTable'
|
import { Table as EditableTable } from 'src/components/editableTable'
|
||||||
import Section from 'src/components/layout/Section'
|
import Section from 'src/components/layout/Section'
|
||||||
import TitleSection from 'src/components/layout/TitleSection'
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
import { fromNamespace, toNamespace } from 'src/utils/config'
|
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
mainFields,
|
mainFields,
|
||||||
|
|
@ -44,6 +44,9 @@ const Commissions = ({ name: SCREEN_KEY }) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const config = data?.config && fromNamespace(SCREEN_KEY)(data.config)
|
const config = data?.config && fromNamespace(SCREEN_KEY)(data.config)
|
||||||
|
const currency = R.path(['fiatCurrency'])(
|
||||||
|
fromNamespace(namespaces.LOCALE)(data?.config)
|
||||||
|
)
|
||||||
|
|
||||||
const commission = config && !R.isEmpty(config) ? config : defaults
|
const commission = config && !R.isEmpty(config) ? config : defaults
|
||||||
|
|
||||||
|
|
@ -71,7 +74,7 @@ const Commissions = ({ name: SCREEN_KEY }) => {
|
||||||
save={save}
|
save={save}
|
||||||
validationSchema={schema}
|
validationSchema={schema}
|
||||||
data={R.of(commission)}
|
data={R.of(commission)}
|
||||||
elements={mainFields(data)}
|
elements={mainFields(currency)}
|
||||||
/>
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
<Section>
|
<Section>
|
||||||
|
|
@ -86,7 +89,7 @@ const Commissions = ({ name: SCREEN_KEY }) => {
|
||||||
save={saveOverrides}
|
save={saveOverrides}
|
||||||
validationSchema={OverridesSchema}
|
validationSchema={OverridesSchema}
|
||||||
data={commission.overrides ?? []}
|
data={commission.overrides ?? []}
|
||||||
elements={overrides(data)}
|
elements={overrides(data, currency)}
|
||||||
/>
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import * as Yup from 'yup'
|
||||||
import { TextInput } from 'src/components/inputs/formik'
|
import { TextInput } from 'src/components/inputs/formik'
|
||||||
import Autocomplete from 'src/components/inputs/formik/Autocomplete.js'
|
import Autocomplete from 'src/components/inputs/formik/Autocomplete.js'
|
||||||
|
|
||||||
const getOverridesFields = getData => {
|
const getOverridesFields = (getData, currency) => {
|
||||||
const getView = (data, code, compare) => it => {
|
const getView = (data, code, compare) => it => {
|
||||||
if (!data) return ''
|
if (!data) return ''
|
||||||
|
|
||||||
|
|
@ -39,7 +39,7 @@ const getOverridesFields = getData => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'cryptoCurrencies',
|
name: 'cryptoCurrencies',
|
||||||
width: 270,
|
width: 250,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
view: displayCodeArray(cryptoData),
|
view: displayCodeArray(cryptoData),
|
||||||
input: Autocomplete,
|
input: Autocomplete,
|
||||||
|
|
@ -54,69 +54,91 @@ const getOverridesFields = getData => {
|
||||||
name: 'cashIn',
|
name: 'cashIn',
|
||||||
display: 'Cash-in',
|
display: 'Cash-in',
|
||||||
width: 140,
|
width: 140,
|
||||||
input: TextInput
|
input: TextInput,
|
||||||
|
textAlign: 'right',
|
||||||
|
suffix: '%'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'cashOut',
|
name: 'cashOut',
|
||||||
display: 'Cash-out',
|
display: 'Cash-out',
|
||||||
width: 140,
|
width: 140,
|
||||||
input: TextInput
|
input: TextInput,
|
||||||
|
textAlign: 'right',
|
||||||
|
suffix: '%'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'fixedFee',
|
name: 'fixedFee',
|
||||||
display: 'Fixed fee',
|
display: 'Fixed fee',
|
||||||
width: 140,
|
width: 140,
|
||||||
input: TextInput
|
input: TextInput,
|
||||||
|
doubleHeader: 'Cash-in only',
|
||||||
|
textAlign: 'right',
|
||||||
|
suffix: currency
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'minimumTx',
|
name: 'minimumTx',
|
||||||
display: 'Minimun Tx',
|
display: 'Minimun Tx',
|
||||||
width: 140,
|
width: 140,
|
||||||
input: TextInput
|
input: TextInput,
|
||||||
|
doubleHeader: 'Cash-in only',
|
||||||
|
textAlign: 'right',
|
||||||
|
suffix: currency
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const mainFields = auxData => [
|
const mainFields = currency => [
|
||||||
{
|
{
|
||||||
name: 'cashIn',
|
name: 'cashIn',
|
||||||
display: 'Cash-in',
|
display: 'Cash-in',
|
||||||
width: 169,
|
width: 169,
|
||||||
size: 'lg',
|
size: 'lg',
|
||||||
input: TextInput
|
input: TextInput,
|
||||||
|
suffix: '%'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'cashOut',
|
name: 'cashOut',
|
||||||
display: 'Cash-out',
|
display: 'Cash-out',
|
||||||
width: 169,
|
width: 169,
|
||||||
size: 'lg',
|
size: 'lg',
|
||||||
input: TextInput
|
input: TextInput,
|
||||||
|
suffix: '%'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'fixedFee',
|
name: 'fixedFee',
|
||||||
display: 'Fixed fee',
|
display: 'Fixed fee',
|
||||||
width: 169,
|
width: 169,
|
||||||
size: 'lg',
|
size: 'lg',
|
||||||
input: TextInput
|
doubleHeader: 'Cash-in only',
|
||||||
|
textAlign: 'center',
|
||||||
|
input: TextInput,
|
||||||
|
suffix: currency
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'minimumTx',
|
name: 'minimumTx',
|
||||||
display: 'Minimun Tx',
|
display: 'Minimun Tx',
|
||||||
width: 169,
|
width: 169,
|
||||||
size: 'lg',
|
size: 'lg',
|
||||||
input: TextInput
|
doubleHeader: 'Cash-in only',
|
||||||
|
textAlign: 'center',
|
||||||
|
input: TextInput,
|
||||||
|
suffix: currency
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const overrides = auxData => {
|
const overrides = (auxData, currency) => {
|
||||||
const getData = R.path(R.__, auxData)
|
const getData = R.path(R.__, auxData)
|
||||||
|
|
||||||
return getOverridesFields(getData)
|
return getOverridesFields(getData, currency)
|
||||||
}
|
}
|
||||||
|
|
||||||
const schema = Yup.object().shape({
|
const schema = Yup.object().shape({
|
||||||
cashIn: Yup.number().required('Required'),
|
cashIn: Yup.number()
|
||||||
cashOut: Yup.number().required('Required'),
|
.max(100)
|
||||||
|
.required('Required'),
|
||||||
|
cashOut: Yup.number()
|
||||||
|
.max(100)
|
||||||
|
.required('Required'),
|
||||||
fixedFee: Yup.number().required('Required'),
|
fixedFee: Yup.number().required('Required'),
|
||||||
minimumTx: Yup.number().required('Required')
|
minimumTx: Yup.number().required('Required')
|
||||||
})
|
})
|
||||||
|
|
@ -124,8 +146,12 @@ const schema = Yup.object().shape({
|
||||||
const OverridesSchema = Yup.object().shape({
|
const OverridesSchema = Yup.object().shape({
|
||||||
machine: Yup.string().required('Required'),
|
machine: Yup.string().required('Required'),
|
||||||
cryptoCurrencies: Yup.array().required('Required'),
|
cryptoCurrencies: Yup.array().required('Required'),
|
||||||
cashIn: Yup.number().required('Required'),
|
cashIn: Yup.number()
|
||||||
cashOut: Yup.number().required('Required'),
|
.max(100)
|
||||||
|
.required('Required'),
|
||||||
|
cashOut: Yup.number()
|
||||||
|
.max(100)
|
||||||
|
.required('Required'),
|
||||||
fixedFee: Yup.number().required('Required'),
|
fixedFee: Yup.number().required('Required'),
|
||||||
minimumTx: Yup.number().required('Required')
|
minimumTx: Yup.number().required('Required')
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,23 @@
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
|
import { makeStyles, Breadcrumbs, Box } from '@material-ui/core'
|
||||||
|
import NavigateNextIcon from '@material-ui/icons/NavigateNext'
|
||||||
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { memo } from 'react'
|
import React, { memo } from 'react'
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
|
||||||
import { gql } from 'apollo-boost'
|
|
||||||
import { useHistory, useParams } from 'react-router-dom'
|
import { useHistory, useParams } from 'react-router-dom'
|
||||||
import Breadcrumbs from '@material-ui/core/Breadcrumbs'
|
|
||||||
import NavigateNextIcon from '@material-ui/icons/NavigateNext'
|
|
||||||
|
|
||||||
|
import { ActionButton } from 'src/components/buttons'
|
||||||
|
import { Label1, Label2 } from 'src/components/typography'
|
||||||
import {
|
import {
|
||||||
OVERRIDE_AUTHORIZED,
|
OVERRIDE_AUTHORIZED,
|
||||||
OVERRIDE_REJECTED
|
OVERRIDE_REJECTED
|
||||||
} from 'src/pages/Customers/components/propertyCard'
|
} from 'src/pages/Customers/components/propertyCard'
|
||||||
import { ActionButton } from 'src/components/buttons'
|
|
||||||
import { Label1 } from 'src/components/typography'
|
|
||||||
import { ReactComponent as BlockReversedIcon } from 'src/styling/icons/button/block/white.svg'
|
|
||||||
import { ReactComponent as BlockIcon } from 'src/styling/icons/button/block/zodiac.svg'
|
|
||||||
import { ReactComponent as AuthorizeReversedIcon } from 'src/styling/icons/button/authorize/white.svg'
|
import { ReactComponent as AuthorizeReversedIcon } from 'src/styling/icons/button/authorize/white.svg'
|
||||||
import { ReactComponent as AuthorizeIcon } from 'src/styling/icons/button/authorize/zodiac.svg'
|
import { ReactComponent as AuthorizeIcon } from 'src/styling/icons/button/authorize/zodiac.svg'
|
||||||
|
import { ReactComponent as BlockReversedIcon } from 'src/styling/icons/button/block/white.svg'
|
||||||
|
import { ReactComponent as BlockIcon } from 'src/styling/icons/button/block/zodiac.svg'
|
||||||
|
|
||||||
|
import styles from './CustomerProfile.styles'
|
||||||
import {
|
import {
|
||||||
CustomerDetails,
|
CustomerDetails,
|
||||||
IdDataCard,
|
IdDataCard,
|
||||||
|
|
@ -25,9 +25,8 @@ import {
|
||||||
IdCardPhotoCard,
|
IdCardPhotoCard,
|
||||||
TransactionsList
|
TransactionsList
|
||||||
} from './components'
|
} from './components'
|
||||||
import { mainStyles } from './Customers.styles'
|
|
||||||
|
|
||||||
const useStyles = makeStyles(mainStyles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const GET_CUSTOMER = gql`
|
const GET_CUSTOMER = gql`
|
||||||
query customer($customerId: ID!) {
|
query customer($customerId: ID!) {
|
||||||
|
|
@ -96,8 +95,7 @@ const CustomerProfile = memo(() => {
|
||||||
const { data: customerResponse, refetch: getCustomer } = useQuery(
|
const { data: customerResponse, refetch: getCustomer } = useQuery(
|
||||||
GET_CUSTOMER,
|
GET_CUSTOMER,
|
||||||
{
|
{
|
||||||
variables: { customerId },
|
variables: { customerId }
|
||||||
fetchPolicy: 'no-cache'
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -125,24 +123,25 @@ const CustomerProfile = memo(() => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Breadcrumbs
|
<Breadcrumbs
|
||||||
|
classes={{ root: classes.breadcrumbs }}
|
||||||
separator={<NavigateNextIcon fontSize="small" />}
|
separator={<NavigateNextIcon fontSize="small" />}
|
||||||
aria-label="breadcrumb">
|
aria-label="breadcrumb">
|
||||||
<Label1
|
<Label1
|
||||||
|
noMargin
|
||||||
className={classes.labelLink}
|
className={classes.labelLink}
|
||||||
onClick={() => history.push('/compliance/customers')}>
|
onClick={() => history.push('/compliance/customers')}>
|
||||||
Customers
|
Customers
|
||||||
</Label1>
|
</Label1>
|
||||||
<Label1 className={classes.bold}>
|
<Label2 noMargin className={classes.labelLink}>
|
||||||
{R.path(['name'])(customerData)}
|
Rafael{R.path(['name'])(customerData)}
|
||||||
</Label1>
|
</Label2>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
<div>
|
<div>
|
||||||
<div className={classes.header}>
|
<Box display="flex" justifyContent="space-between">
|
||||||
<CustomerDetails customer={customerData} />
|
<CustomerDetails customer={customerData} />
|
||||||
<div className={classes.rightAligned}>
|
<div>
|
||||||
<Label1 className={classes.label1}>Actions</Label1>
|
<Label1 className={classes.actionLabel}>Actions</Label1>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
className={classes.actionButton}
|
|
||||||
color="primary"
|
color="primary"
|
||||||
Icon={blocked ? AuthorizeIcon : BlockIcon}
|
Icon={blocked ? AuthorizeIcon : BlockIcon}
|
||||||
InverseIcon={blocked ? AuthorizeReversedIcon : BlockReversedIcon}
|
InverseIcon={blocked ? AuthorizeReversedIcon : BlockReversedIcon}
|
||||||
|
|
@ -156,8 +155,8 @@ const CustomerProfile = memo(() => {
|
||||||
{`${blocked ? 'Authorize' : 'Block'} customer`}
|
{`${blocked ? 'Authorize' : 'Block'} customer`}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Box>
|
||||||
<div className={classes.rowCenterAligned}>
|
<Box display="flex">
|
||||||
<IdDataCard
|
<IdDataCard
|
||||||
customerData={customerData}
|
customerData={customerData}
|
||||||
updateCustomer={updateCustomer}
|
updateCustomer={updateCustomer}
|
||||||
|
|
@ -170,7 +169,7 @@ const CustomerProfile = memo(() => {
|
||||||
customerData={customerData}
|
customerData={customerData}
|
||||||
updateCustomer={updateCustomer}
|
updateCustomer={updateCustomer}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
<TransactionsList data={transactionsData} />
|
<TransactionsList data={transactionsData} />
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { comet } from 'src/styling/variables'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
labelLink: {
|
||||||
|
cursor: 'pointer',
|
||||||
|
color: comet
|
||||||
|
},
|
||||||
|
breadcrumbs: {
|
||||||
|
margin: [[20, 0]]
|
||||||
|
},
|
||||||
|
actionLabel: {
|
||||||
|
color: comet,
|
||||||
|
margin: [[4, 0]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useQuery } from '@apollo/react-hooks'
|
import { useQuery } from '@apollo/react-hooks'
|
||||||
import { gql } from 'apollo-boost'
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useHistory } from 'react-router-dom'
|
import { useHistory } from 'react-router-dom'
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ import DataTable from 'src/components/tables/DataTable'
|
||||||
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
|
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
|
||||||
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
|
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
|
||||||
|
|
||||||
import { mainStyles } from './Customers.styles'
|
import styles from './CustomersList.styles'
|
||||||
|
|
||||||
const useStyles = makeStyles(mainStyles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const CustomersList = ({ data, onClick }) => {
|
const CustomersList = ({ data, onClick }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,17 @@
|
||||||
import typographyStyles from 'src/components/typography/styles'
|
import typographyStyles from 'src/components/typography/styles'
|
||||||
import baseStyles from 'src/pages/Logs.styles'
|
import baseStyles from 'src/pages/Logs.styles'
|
||||||
import { zircon, primaryColor, comet } from 'src/styling/variables'
|
import { zircon, primaryColor } from 'src/styling/variables'
|
||||||
|
|
||||||
const { label1 } = typographyStyles
|
const { label1 } = typographyStyles
|
||||||
const { titleWrapper, titleAndButtonsContainer } = baseStyles
|
const { titleWrapper, titleAndButtonsContainer } = baseStyles
|
||||||
|
|
||||||
const mainStyles = {
|
export default {
|
||||||
rightAligned: {
|
|
||||||
display: 'flex',
|
|
||||||
flexFlow: 'column nowrap',
|
|
||||||
right: 0
|
|
||||||
},
|
|
||||||
actionButton: {
|
|
||||||
height: 28
|
|
||||||
},
|
|
||||||
titleWrapper,
|
titleWrapper,
|
||||||
titleAndButtonsContainer,
|
titleAndButtonsContainer,
|
||||||
header: {
|
|
||||||
display: 'flex',
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: 'space-between'
|
|
||||||
},
|
|
||||||
customerDetails: {
|
|
||||||
display: 'flex',
|
|
||||||
flex: 1
|
|
||||||
},
|
|
||||||
row: {
|
row: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexFlow: 'row nowrap'
|
flexFlow: 'row nowrap'
|
||||||
},
|
},
|
||||||
rowCenterAligned: {
|
|
||||||
display: 'flex',
|
|
||||||
flexFlow: 'row nowrap',
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
rowSpaceBetween: {
|
rowSpaceBetween: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexFlow: 'row nowrap',
|
flexFlow: 'row nowrap',
|
||||||
|
|
@ -50,17 +28,6 @@ const mainStyles = {
|
||||||
textInput: {
|
textInput: {
|
||||||
width: 144
|
width: 144
|
||||||
},
|
},
|
||||||
label1: {
|
|
||||||
fontFamily: 'MuseoSans',
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: 500,
|
|
||||||
fontStretch: 'normal',
|
|
||||||
fontStyle: 'normal',
|
|
||||||
lineHeight: 1.33,
|
|
||||||
letterSpacing: 'normal',
|
|
||||||
color: comet,
|
|
||||||
margin: [[4, 0]]
|
|
||||||
},
|
|
||||||
p: {
|
p: {
|
||||||
fontFamily: 'MuseoSans',
|
fontFamily: 'MuseoSans',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
|
@ -71,9 +38,6 @@ const mainStyles = {
|
||||||
letterSpacing: 'normal',
|
letterSpacing: 'normal',
|
||||||
color: primaryColor
|
color: primaryColor
|
||||||
},
|
},
|
||||||
bold: {
|
|
||||||
fontWeight: 'bold'
|
|
||||||
},
|
|
||||||
txId: {
|
txId: {
|
||||||
fontFamily: 'MuseoSans',
|
fontFamily: 'MuseoSans',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
|
|
@ -106,7 +70,7 @@ const mainStyles = {
|
||||||
height: 92,
|
height: 92,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
backgroundColor: zircon,
|
backgroundColor: zircon,
|
||||||
margin: [[15, 28, 0, 0]],
|
margin: [[0, 28, 0, 0]],
|
||||||
padding: [[30]],
|
padding: [[30]],
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between'
|
justifyContent: 'space-between'
|
||||||
|
|
@ -124,9 +88,6 @@ const mainStyles = {
|
||||||
height: 240,
|
height: 240,
|
||||||
margin: [[32, 0, 0, 0]]
|
margin: [[32, 0, 0, 0]]
|
||||||
},
|
},
|
||||||
labelLink: {
|
|
||||||
cursor: 'pointer'
|
|
||||||
},
|
|
||||||
field: {
|
field: {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
width: 144,
|
width: 144,
|
||||||
|
|
@ -158,5 +119,3 @@ const mainStyles = {
|
||||||
maxHeight: 97
|
maxHeight: 97
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { mainStyles }
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles, Box } from '@material-ui/core'
|
||||||
import * as R from 'ramda'
|
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
import * as R from 'ramda'
|
||||||
import React, { memo } from 'react'
|
import React, { memo } from 'react'
|
||||||
|
|
||||||
import { H2 } from 'src/components/typography'
|
import { H2 } from 'src/components/typography'
|
||||||
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
|
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
|
||||||
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
|
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
|
||||||
|
|
||||||
import { mainStyles } from '../Customers.styles'
|
|
||||||
import { ifNotNull } from '../../../utils/nullCheck'
|
import { ifNotNull } from '../../../utils/nullCheck'
|
||||||
|
import styles from '../CustomersList.styles'
|
||||||
|
|
||||||
import FrontCameraPhoto from './FrontCameraPhoto'
|
import FrontCameraPhoto from './FrontCameraPhoto'
|
||||||
|
|
||||||
const useStyles = makeStyles(mainStyles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const CustomerDetails = memo(({ customer }) => {
|
const CustomerDetails = memo(({ customer }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
@ -61,30 +61,30 @@ const CustomerDetails = memo(({ customer }) => {
|
||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.customerDetails}>
|
<Box display="flex">
|
||||||
<FrontCameraPhoto
|
<FrontCameraPhoto
|
||||||
frontCameraPath={R.path(['frontCameraPath'])(customer)}
|
frontCameraPath={R.path(['frontCameraPath'])(customer)}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<div className={classes.rowCenterAligned}>
|
<Box display="flex">
|
||||||
<H2 className={classes.customerName}>{R.path(['name'])(customer)}</H2>
|
<H2 noMargin>Rafael{R.path(['name'])(customer)}</H2>
|
||||||
</div>
|
</Box>
|
||||||
<div className={classes.rowCenterAligned}>
|
<Box display="flex">
|
||||||
{elements.map(({ size, header }, idx) => (
|
{elements.map(({ size, header }, idx) => (
|
||||||
<div key={idx} className={classes.label1} style={{ width: size }}>
|
<div key={idx} className={classes.label1} style={{ width: size }}>
|
||||||
{header}
|
{header}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</Box>
|
||||||
<div className={classes.rowCenterAligned}>
|
<Box display="flex">
|
||||||
{elements.map(({ size, value }, idx) => (
|
{elements.map(({ size, value }, idx) => (
|
||||||
<div key={idx} className={classes.p} style={{ width: size }}>
|
<div key={idx} className={classes.p} style={{ width: size }}>
|
||||||
{value}
|
{value}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Box>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import React, { memo } from 'react'
|
||||||
|
|
||||||
import { Info3, Label1 } from 'src/components/typography'
|
import { Info3, Label1 } from 'src/components/typography'
|
||||||
|
|
||||||
import { mainStyles } from '../Customers.styles'
|
import mainStyles from '../CustomersList.styles'
|
||||||
|
|
||||||
const useStyles = makeStyles(mainStyles)
|
const useStyles = makeStyles(mainStyles)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
|
import { Paper } from '@material-ui/core'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import React, { memo } from 'react'
|
import React, { memo } from 'react'
|
||||||
import { Paper } from '@material-ui/core'
|
|
||||||
|
|
||||||
import { ReactComponent as CrossedCameraIcon } from 'src/styling/icons/ID/photo/crossed-camera.svg'
|
import { ReactComponent as CrossedCameraIcon } from 'src/styling/icons/ID/photo/crossed-camera.svg'
|
||||||
|
import { URI } from 'src/utils/apollo'
|
||||||
|
|
||||||
import { mainStyles } from '../Customers.styles'
|
import mainStyles from '../CustomersList.styles'
|
||||||
|
|
||||||
import { IMAGES_URI } from './variables'
|
|
||||||
|
|
||||||
const useStyles = makeStyles(mainStyles)
|
const useStyles = makeStyles(mainStyles)
|
||||||
|
|
||||||
|
|
@ -16,10 +15,7 @@ const FrontCameraPhoto = memo(({ frontCameraPath }) => {
|
||||||
return (
|
return (
|
||||||
<Paper className={classes.photo} elevation={0}>
|
<Paper className={classes.photo} elevation={0}>
|
||||||
{frontCameraPath ? (
|
{frontCameraPath ? (
|
||||||
<img
|
<img src={`${URI}/front-camera-photo/${frontCameraPath}`} alt="" />
|
||||||
src={`${IMAGES_URI}/front-camera-photo/${frontCameraPath}`}
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<CrossedCameraIcon />
|
<CrossedCameraIcon />
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import * as R from 'ramda'
|
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
import * as R from 'ramda'
|
||||||
import React, { memo } from 'react'
|
import React, { memo } from 'react'
|
||||||
|
|
||||||
import { ReactComponent as CrossedCameraIcon } from 'src/styling/icons/ID/photo/crossed-camera.svg'
|
|
||||||
import {
|
import {
|
||||||
PropertyCard,
|
PropertyCard,
|
||||||
OVERRIDE_AUTHORIZED,
|
OVERRIDE_AUTHORIZED,
|
||||||
OVERRIDE_REJECTED
|
OVERRIDE_REJECTED
|
||||||
} from 'src/pages/Customers/components/propertyCard'
|
} from 'src/pages/Customers/components/propertyCard'
|
||||||
|
import { ReactComponent as CrossedCameraIcon } from 'src/styling/icons/ID/photo/crossed-camera.svg'
|
||||||
|
import { URI } from 'src/utils/apollo'
|
||||||
|
|
||||||
import { mainStyles } from '../Customers.styles'
|
import mainStyles from '../CustomersList.styles'
|
||||||
|
|
||||||
import Field from './Field'
|
import Field from './Field'
|
||||||
import { IMAGES_URI } from './variables'
|
|
||||||
|
|
||||||
const useStyles = makeStyles(mainStyles)
|
const useStyles = makeStyles(mainStyles)
|
||||||
|
|
||||||
|
|
@ -33,7 +33,7 @@ const IdCardPhotoCard = memo(({ customerData, updateCustomer }) => {
|
||||||
{customerData.idCardPhotoPath ? (
|
{customerData.idCardPhotoPath ? (
|
||||||
<img
|
<img
|
||||||
className={classes.idCardPhoto}
|
className={classes.idCardPhoto}
|
||||||
src={`${IMAGES_URI}/id-card-photo/${R.path(['idCardPhotoPath'])(
|
src={`${URI}/id-card-photo/${R.path(['idCardPhotoPath'])(
|
||||||
customerData
|
customerData
|
||||||
)}`}
|
)}`}
|
||||||
alt=""
|
alt=""
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import * as R from 'ramda'
|
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
import * as R from 'ramda'
|
||||||
import React, { memo } from 'react'
|
import React, { memo } from 'react'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
@ -9,7 +9,7 @@ import {
|
||||||
OVERRIDE_REJECTED
|
OVERRIDE_REJECTED
|
||||||
} from 'src/pages/Customers/components/propertyCard'
|
} from 'src/pages/Customers/components/propertyCard'
|
||||||
|
|
||||||
import { mainStyles } from '../Customers.styles'
|
import mainStyles from '../CustomersList.styles'
|
||||||
|
|
||||||
import Field from './Field'
|
import Field from './Field'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
|
import { parsePhoneNumberFromString } from 'libphonenumber-js'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { memo } from 'react'
|
import React, { memo } from 'react'
|
||||||
import { parsePhoneNumberFromString } from 'libphonenumber-js'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PropertyCard,
|
PropertyCard,
|
||||||
|
|
@ -9,7 +9,7 @@ import {
|
||||||
OVERRIDE_REJECTED
|
OVERRIDE_REJECTED
|
||||||
} from 'src/pages/Customers/components/propertyCard'
|
} from 'src/pages/Customers/components/propertyCard'
|
||||||
|
|
||||||
import { mainStyles } from '../Customers.styles'
|
import mainStyles from '../CustomersList.styles'
|
||||||
|
|
||||||
import Field from './Field'
|
import Field from './Field'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-ou
|
||||||
import { toUnit } from 'src/utils/coin'
|
import { toUnit } from 'src/utils/coin'
|
||||||
|
|
||||||
import CopyToClipboard from '../../Transactions/CopyToClipboard'
|
import CopyToClipboard from '../../Transactions/CopyToClipboard'
|
||||||
import { mainStyles } from '../Customers.styles'
|
import mainStyles from '../CustomersList.styles'
|
||||||
|
|
||||||
const useStyles = makeStyles(mainStyles)
|
const useStyles = makeStyles(mainStyles)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
import { Paper } from '@material-ui/core'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import React, { memo } from 'react'
|
import React, { memo } from 'react'
|
||||||
import { Paper } from '@material-ui/core'
|
|
||||||
|
|
||||||
import { ActionButton } from 'src/components/buttons'
|
import { ActionButton } from 'src/components/buttons'
|
||||||
import { H3 } from 'src/components/typography'
|
import { H3 } from 'src/components/typography'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
const IMAGES_URI =
|
|
||||||
process.env.NODE_ENV === 'development' ? 'https://localhost:8070' : ''
|
|
||||||
|
|
||||||
export { IMAGES_URI }
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { useQuery } from '@apollo/react-hooks'
|
import { useQuery } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { gql } from 'apollo-boost'
|
|
||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
|
import gql from 'graphql-tag'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import QRCode from 'qrcode.react'
|
import QRCode from 'qrcode.react'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
@ -35,6 +35,7 @@ const GET_FUNDING = gql`
|
||||||
{
|
{
|
||||||
funding {
|
funding {
|
||||||
cryptoCode
|
cryptoCode
|
||||||
|
errorMsg
|
||||||
fundingAddress
|
fundingAddress
|
||||||
fundingAddressUrl
|
fundingAddressUrl
|
||||||
confirmedBalance
|
confirmedBalance
|
||||||
|
|
@ -55,6 +56,7 @@ const formatNumber = it => new BigNumber(it).toFormat(2)
|
||||||
const getConfirmedTotal = list => {
|
const getConfirmedTotal = list => {
|
||||||
return formatNumber(
|
return formatNumber(
|
||||||
list
|
list
|
||||||
|
.filter(it => !it.errorMsg)
|
||||||
.map(it => new BigNumber(it.fiatConfirmedBalance))
|
.map(it => new BigNumber(it.fiatConfirmedBalance))
|
||||||
.reduce(sumReducer, new BigNumber(0))
|
.reduce(sumReducer, new BigNumber(0))
|
||||||
)
|
)
|
||||||
|
|
@ -63,6 +65,7 @@ const getConfirmedTotal = list => {
|
||||||
const getPendingTotal = list => {
|
const getPendingTotal = list => {
|
||||||
return formatNumber(
|
return formatNumber(
|
||||||
list
|
list
|
||||||
|
.filter(it => !it.errorMsg)
|
||||||
.map(it => new BigNumber(it.fiatPending))
|
.map(it => new BigNumber(it.fiatPending))
|
||||||
.reduce(sumReducer, new BigNumber(0))
|
.reduce(sumReducer, new BigNumber(0))
|
||||||
)
|
)
|
||||||
|
|
@ -107,16 +110,29 @@ const Funding = () => {
|
||||||
setSelected(fundingResponse?.funding[0])
|
setSelected(fundingResponse?.funding[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemRender = it => {
|
const itemRender = (it, active) => {
|
||||||
|
const itemClass = {
|
||||||
|
[classes.item]: true,
|
||||||
|
[classes.inactiveItem]: !active
|
||||||
|
}
|
||||||
|
const wrapperClass = {
|
||||||
|
[classes.itemWrapper]: true,
|
||||||
|
[classes.error]: it.errorMsg
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.itemWrapper}>
|
<div className={classnames(wrapperClass)}>
|
||||||
<div className={classes.firstItem}>{it.display}</div>
|
<div className={classes.firstItem}>{it.display}</div>
|
||||||
<div className={classes.item}>
|
{!it.errorMsg && (
|
||||||
{it.fiatConfirmedBalance} {it.fiatCode}
|
<>
|
||||||
</div>
|
<div className={classnames(itemClass)}>
|
||||||
<div className={classes.item}>
|
{it.fiatConfirmedBalance} {it.fiatCode}
|
||||||
{it.confirmedBalance} {it.cryptoCode}
|
</div>
|
||||||
</div>
|
<div className={classnames(itemClass)}>
|
||||||
|
{it.confirmedBalance} {it.cryptoCode}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -149,7 +165,14 @@ const Funding = () => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
{selected && !viewHistory && (
|
{selected && !viewHistory && selected.errorMsg && (
|
||||||
|
<div className={classes.main}>
|
||||||
|
<div className={classes.firstSide}>
|
||||||
|
<Info3 className={classes.error}>{selected.errorMsg}</Info3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{selected && !viewHistory && !selected.errorMsg && (
|
||||||
<div className={classes.main}>
|
<div className={classes.main}>
|
||||||
<div className={classes.firstSide}>
|
<div className={classes.firstSide}>
|
||||||
<H3>Balance ({selected.display})</H3>
|
<H3>Balance ({selected.display})</H3>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@ import {
|
||||||
disabledColor2,
|
disabledColor2,
|
||||||
spacer,
|
spacer,
|
||||||
subheaderColor,
|
subheaderColor,
|
||||||
placeholderColor
|
errorColor,
|
||||||
|
placeholderColor,
|
||||||
|
comet
|
||||||
} from 'src/styling/variables'
|
} from 'src/styling/variables'
|
||||||
|
|
||||||
const { label1 } = typographyStyles
|
const { label1 } = typographyStyles
|
||||||
|
|
@ -25,6 +27,9 @@ export default {
|
||||||
secondSide: {
|
secondSide: {
|
||||||
marginTop: -29
|
marginTop: -29
|
||||||
},
|
},
|
||||||
|
error: {
|
||||||
|
color: errorColor
|
||||||
|
},
|
||||||
coinTotal: {
|
coinTotal: {
|
||||||
margin: `${spacer * 1.5}px 0`
|
margin: `${spacer * 1.5}px 0`
|
||||||
},
|
},
|
||||||
|
|
@ -51,7 +56,11 @@ export default {
|
||||||
extend: label1,
|
extend: label1,
|
||||||
margin: 2
|
margin: 2
|
||||||
},
|
},
|
||||||
|
inactiveItem: {
|
||||||
|
color: comet
|
||||||
|
},
|
||||||
firstItem: {
|
firstItem: {
|
||||||
|
fontWeight: 700,
|
||||||
margin: 2
|
margin: 2
|
||||||
},
|
},
|
||||||
total: {
|
total: {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { gql } from 'apollo-boost'
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,16 @@ export default {
|
||||||
fillColumn: {
|
fillColumn: {
|
||||||
width: '100%'
|
width: '100%'
|
||||||
},
|
},
|
||||||
|
shareButton: {
|
||||||
|
margin: 8,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
fontSize: 12,
|
||||||
|
padding: [[0, 12]]
|
||||||
|
},
|
||||||
|
shareIcon: {
|
||||||
|
marginRight: 6
|
||||||
|
},
|
||||||
button: {
|
button: {
|
||||||
margin: 8
|
margin: 8
|
||||||
},
|
},
|
||||||
|
|
@ -45,9 +55,9 @@ export default {
|
||||||
},
|
},
|
||||||
buttonsWrapper: {
|
buttonsWrapper: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
marginLeft: 10,
|
marginLeft: 16,
|
||||||
'& > *': {
|
'& > *': {
|
||||||
margin: 'auto 10px'
|
margin: 'auto 6px'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { gql } from 'apollo-boost'
|
import gql from 'graphql-tag'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper'
|
import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper'
|
||||||
import Title from 'src/components/Title'
|
import Title from 'src/components/Title'
|
||||||
import { FeatureButton, SimpleButton } from 'src/components/buttons'
|
import { SimpleButton } from 'src/components/buttons'
|
||||||
import Sidebar from 'src/components/layout/Sidebar'
|
import Sidebar from 'src/components/layout/Sidebar'
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
|
|
@ -18,8 +18,8 @@ import {
|
||||||
TableCell
|
TableCell
|
||||||
} from 'src/components/table'
|
} from 'src/components/table'
|
||||||
import { Info3 } from 'src/components/typography'
|
import { Info3 } from 'src/components/typography'
|
||||||
import { ReactComponent as DownloadActive } from 'src/styling/icons/button/download/white.svg'
|
import { ReactComponent as WhiteShareIcon } from 'src/styling/icons/circle buttons/share/white.svg'
|
||||||
import { ReactComponent as Download } from 'src/styling/icons/button/download/zodiac.svg'
|
import { ReactComponent as ShareIcon } from 'src/styling/icons/circle buttons/share/zodiac.svg'
|
||||||
|
|
||||||
import styles from './Logs.styles'
|
import styles from './Logs.styles'
|
||||||
|
|
||||||
|
|
@ -62,7 +62,6 @@ const Logs = () => {
|
||||||
|
|
||||||
const [selected, setSelected] = useState(null)
|
const [selected, setSelected] = useState(null)
|
||||||
const [saveMessage, setSaveMessage] = useState(null)
|
const [saveMessage, setSaveMessage] = useState(null)
|
||||||
const [anchorEl, setAnchorEl] = useState(null)
|
|
||||||
|
|
||||||
const deviceId = selected?.deviceId
|
const deviceId = selected?.deviceId
|
||||||
|
|
||||||
|
|
@ -76,7 +75,6 @@ const Logs = () => {
|
||||||
|
|
||||||
const { data: logsResponse } = useQuery(GET_MACHINE_LOGS, {
|
const { data: logsResponse } = useQuery(GET_MACHINE_LOGS, {
|
||||||
variables: { deviceId },
|
variables: { deviceId },
|
||||||
fetchPolicy: 'no-cache',
|
|
||||||
skip: !selected,
|
skip: !selected,
|
||||||
onCompleted: () => setSaveMessage('')
|
onCompleted: () => setSaveMessage('')
|
||||||
})
|
})
|
||||||
|
|
@ -89,13 +87,6 @@ const Logs = () => {
|
||||||
return R.path(['deviceId'])(selected) === it.deviceId
|
return R.path(['deviceId'])(selected) === it.deviceId
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOpenRangePicker = event => {
|
|
||||||
setAnchorEl(anchorEl ? null : event.currentTarget)
|
|
||||||
}
|
|
||||||
|
|
||||||
const open = Boolean(anchorEl)
|
|
||||||
const id = open ? 'date-range-popover' : undefined
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={classes.titleWrapper}>
|
<div className={classes.titleWrapper}>
|
||||||
|
|
@ -103,25 +94,17 @@ const Logs = () => {
|
||||||
<Title>Machine Logs</Title>
|
<Title>Machine Logs</Title>
|
||||||
{logsResponse && (
|
{logsResponse && (
|
||||||
<div className={classes.buttonsWrapper}>
|
<div className={classes.buttonsWrapper}>
|
||||||
<FeatureButton
|
|
||||||
Icon={Download}
|
|
||||||
InverseIcon={DownloadActive}
|
|
||||||
aria-describedby={id}
|
|
||||||
variant="contained"
|
|
||||||
onClick={handleOpenRangePicker}
|
|
||||||
/>
|
|
||||||
<LogsDowloaderPopover
|
<LogsDowloaderPopover
|
||||||
title="Download logs"
|
title="Download logs"
|
||||||
name="machine-logs"
|
name="machine-logs"
|
||||||
id={id}
|
|
||||||
open={open}
|
|
||||||
anchorEl={anchorEl}
|
|
||||||
logs={logsResponse.machineLogs}
|
logs={logsResponse.machineLogs}
|
||||||
getTimestamp={log => log.timestamp}
|
getTimestamp={log => log.timestamp}
|
||||||
/>
|
/>
|
||||||
<SimpleButton
|
<SimpleButton
|
||||||
className={classes.button}
|
className={classes.shareButton}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
|
Icon={ShareIcon}
|
||||||
|
InverseIcon={WhiteShareIcon}
|
||||||
onClick={sendSnapshot}>
|
onClick={sendSnapshot}>
|
||||||
Share with Lamassu
|
Share with Lamassu
|
||||||
</SimpleButton>
|
</SimpleButton>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { gql } from 'apollo-boost'
|
import gql from 'graphql-tag'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,20 @@
|
||||||
import { useMutation } from '@apollo/react-hooks'
|
import { useMutation } from '@apollo/react-hooks'
|
||||||
import { Dialog, DialogContent } from '@material-ui/core'
|
import { Dialog, DialogContent } from '@material-ui/core'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { gql } from 'apollo-boost'
|
import gql from 'graphql-tag'
|
||||||
import classnames from 'classnames'
|
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import { H4 } from 'src/components/typography'
|
import { DialogTitle, ConfirmDialog } from 'src/components/ConfirmDialog'
|
||||||
|
import { Status } from 'src/components/Status'
|
||||||
|
import ActionButton from 'src/components/buttons/ActionButton'
|
||||||
|
import { Label1, H4 } from 'src/components/typography'
|
||||||
|
import { ReactComponent as RebootReversedIcon } from 'src/styling/icons/button/reboot/white.svg'
|
||||||
|
import { ReactComponent as RebootIcon } from 'src/styling/icons/button/reboot/zodiac.svg'
|
||||||
|
import { ReactComponent as UnpairReversedIcon } from 'src/styling/icons/button/unpair/white.svg'
|
||||||
|
import { ReactComponent as UnpairIcon } from 'src/styling/icons/button/unpair/zodiac.svg'
|
||||||
|
|
||||||
import { DialogTitle, ConfirmDialog } from '../../components/ConfirmDialog'
|
import styles from './MachineDetailsCard.styles'
|
||||||
import { Status } from '../../components/Status'
|
|
||||||
import ActionButton from '../../components/buttons/ActionButton'
|
|
||||||
import { ReactComponent as DownloadReversedIcon } from '../../styling/icons/button/download/white.svg'
|
|
||||||
import { ReactComponent as DownloadIcon } from '../../styling/icons/button/download/zodiac.svg'
|
|
||||||
import { ReactComponent as RebootReversedIcon } from '../../styling/icons/button/reboot/white.svg'
|
|
||||||
import { ReactComponent as RebootIcon } from '../../styling/icons/button/reboot/zodiac.svg'
|
|
||||||
import { ReactComponent as ShutdownReversedIcon } from '../../styling/icons/button/shut down/white.svg'
|
|
||||||
import { ReactComponent as ShutdownIcon } from '../../styling/icons/button/shut down/zodiac.svg'
|
|
||||||
import { ReactComponent as UnpairReversedIcon } from '../../styling/icons/button/unpair/white.svg'
|
|
||||||
import { ReactComponent as UnpairIcon } from '../../styling/icons/button/unpair/zodiac.svg'
|
|
||||||
import { zircon } from '../../styling/variables'
|
|
||||||
import {
|
|
||||||
detailsRowStyles,
|
|
||||||
labelStyles
|
|
||||||
} from '../Transactions/Transactions.styles'
|
|
||||||
|
|
||||||
const MACHINE_ACTION = gql`
|
const MACHINE_ACTION = gql`
|
||||||
mutation MachineAction($deviceId: ID!, $action: MachineAction!) {
|
mutation MachineAction($deviceId: ID!, $action: MachineAction!) {
|
||||||
|
|
@ -33,30 +24,18 @@ const MACHINE_ACTION = gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const colDivider = {
|
const useStyles = makeStyles(styles)
|
||||||
background: zircon,
|
|
||||||
width: 2
|
|
||||||
}
|
|
||||||
|
|
||||||
const inlineChip = {
|
|
||||||
marginInlineEnd: '0.25em'
|
|
||||||
}
|
|
||||||
|
|
||||||
const useLStyles = makeStyles(labelStyles)
|
|
||||||
|
|
||||||
const Label = ({ children }) => {
|
const Label = ({ children }) => {
|
||||||
const classes = useLStyles()
|
const classes = useStyles()
|
||||||
|
return <Label1 className={classes.label}>{children}</Label1>
|
||||||
return <div className={classes.label}>{children}</div>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useMDStyles = makeStyles({ ...detailsRowStyles, colDivider, inlineChip })
|
|
||||||
|
|
||||||
const MachineDetailsRow = ({ it: machine }) => {
|
const MachineDetailsRow = ({ it: machine }) => {
|
||||||
const [errorDialog, setErrorDialog] = useState(false)
|
const [errorDialog, setErrorDialog] = useState(false)
|
||||||
const [dialogOpen, setOpen] = useState(false)
|
const [dialogOpen, setOpen] = useState(false)
|
||||||
const [actionMessage, setActionMessage] = useState(null)
|
const [actionMessage, setActionMessage] = useState(null)
|
||||||
const classes = useMDStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
const unpairDialog = () => setOpen(true)
|
const unpairDialog = () => setOpen(true)
|
||||||
|
|
||||||
|
|
@ -79,157 +58,111 @@ const MachineDetailsRow = ({ it: machine }) => {
|
||||||
<DialogContent>{actionMessage}</DialogContent>
|
<DialogContent>{actionMessage}</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<div className={classes.wrapper}>
|
<div className={classes.wrapper}>
|
||||||
<div className={classnames(classes.row)}>
|
<div className={classes.column1}>
|
||||||
<div className={classnames(classes.col)}>
|
<div className={classes.lastRow}>
|
||||||
<div className={classnames(classes.row)}>
|
<div className={classes.status}>
|
||||||
<div className={classnames(classes.col, classes.col2)}>
|
<Label>Statuses</Label>
|
||||||
<div className={classes.innerRow}>
|
<div>
|
||||||
<div>
|
{machine.statuses.map((status, index) => (
|
||||||
<Label>Statuses</Label>
|
<Status
|
||||||
<div>
|
className={classes.chips}
|
||||||
{machine.statuses.map((status, index) => (
|
status={status}
|
||||||
<Status status={status} key={index} />
|
key={index}
|
||||||
))}
|
/>
|
||||||
</div>
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</div>
|
||||||
<Label>Lamassu Support article</Label>
|
<div>
|
||||||
<div>
|
<Label>Lamassu Support article</Label>
|
||||||
{machine.statuses.map((...[, index]) => (
|
<div>
|
||||||
<span key={index} />
|
{machine.statuses.map((...[, index]) => (
|
||||||
))}
|
// TODO support articles
|
||||||
</div>
|
<span key={index}></span>
|
||||||
</div>
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={classes.separator} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={classes.column2}>
|
||||||
|
<div className={classes.row}>
|
||||||
|
<div className={classes.machineModel}>
|
||||||
|
<Label>Machine Model</Label>
|
||||||
|
<div>{machine.model ?? 'unknown'}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label>Paired at</Label>
|
||||||
|
<div>
|
||||||
|
{machine.pairedAt
|
||||||
|
? moment(machine.pairedAt).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
: 'N/A'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className={classes.lastRow}>
|
||||||
className={classnames(
|
<div>
|
||||||
classes.col,
|
<Label>Actions</Label>
|
||||||
classes.col2,
|
<div className={classes.actionRow}>
|
||||||
classes.colDivider
|
<ActionButton
|
||||||
)}
|
className={classes.action}
|
||||||
/>
|
color="primary"
|
||||||
<div className={classnames(classes.col)}>
|
Icon={UnpairIcon}
|
||||||
<div className={classnames(classes.row)}>
|
InverseIcon={UnpairReversedIcon}
|
||||||
<div className={classnames(classes.col, classes.col2)}>
|
disabled={loading}
|
||||||
<div className={classes.innerRow}>
|
onClick={unpairDialog}>
|
||||||
<div>
|
Unpair
|
||||||
<Label>Machine Model</Label>
|
</ActionButton>
|
||||||
<div>{machine.machineModel}</div>
|
<ConfirmDialog
|
||||||
</div>
|
open={dialogOpen}
|
||||||
<div className={classes.commissionWrapper}>
|
className={classes.dialog}
|
||||||
<Label>Address</Label>
|
title="Unpair this machine?"
|
||||||
<div>{machine.machineLocation}</div>
|
subtitle={false}
|
||||||
</div>
|
toBeConfirmed={machine.name}
|
||||||
</div>
|
onConfirmed={() => {
|
||||||
</div>
|
setOpen(false)
|
||||||
</div>
|
machineAction({
|
||||||
|
variables: {
|
||||||
<div className={classnames(classes.row)}>
|
deviceId: machine.deviceId,
|
||||||
<div className={classnames(classes.col, classes.col2)}>
|
action: 'unpair'
|
||||||
<div className={classes.innerRow}>
|
}
|
||||||
<div>
|
})
|
||||||
<Label>Paired at</Label>
|
}}
|
||||||
<div>
|
onDissmised={() => {
|
||||||
{moment(machine.pairedAt).format('YYYY-MM-DD HH:mm:ss')}
|
setOpen(false)
|
||||||
</div>
|
}}
|
||||||
</div>
|
/>
|
||||||
<div className={classes.commissionWrapper}>
|
<ActionButton
|
||||||
<Label>Software update</Label>
|
className={classes.action}
|
||||||
<div className={classes.innerRow}>
|
color="primary"
|
||||||
{machine.softwareVersion && (
|
Icon={RebootIcon}
|
||||||
<span className={classes.inlineChip}>
|
InverseIcon={RebootReversedIcon}
|
||||||
{machine.softwareVersion}
|
disabled={loading}
|
||||||
</span>
|
onClick={() => {
|
||||||
)}
|
machineAction({
|
||||||
<ActionButton
|
variables: {
|
||||||
className={classes.inlineChip}
|
deviceId: machine.deviceId,
|
||||||
disabled
|
action: 'reboot'
|
||||||
color="primary"
|
}
|
||||||
Icon={DownloadIcon}
|
})
|
||||||
InverseIcon={DownloadReversedIcon}>
|
}}>
|
||||||
Update
|
Reboot
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</div>
|
<ActionButton
|
||||||
</div>
|
className={classes.action}
|
||||||
</div>
|
disabled={loading}
|
||||||
</div>
|
color="primary"
|
||||||
</div>
|
Icon={RebootIcon}
|
||||||
|
InverseIcon={RebootReversedIcon}
|
||||||
<div className={classnames(classes.row)}>
|
onClick={() => {
|
||||||
<div className={classnames(classes.col, classes.col2)}>
|
machineAction({
|
||||||
<div className={classes.innerRow}>
|
variables: {
|
||||||
<div>
|
deviceId: machine.deviceId,
|
||||||
<Label>Printer</Label>
|
action: 'restartServices'
|
||||||
<div>{machine.printer || 'unknown'}</div>
|
}
|
||||||
</div>
|
})
|
||||||
<div className={classes.commissionWrapper}>
|
}}>
|
||||||
<Label>Actions</Label>
|
Restart Services
|
||||||
<div className={classes.innerRow}>
|
</ActionButton>
|
||||||
<ActionButton
|
|
||||||
className={classes.inlineChip}
|
|
||||||
color="primary"
|
|
||||||
Icon={UnpairIcon}
|
|
||||||
InverseIcon={UnpairReversedIcon}
|
|
||||||
disabled={loading}
|
|
||||||
onClick={unpairDialog}>
|
|
||||||
Unpair
|
|
||||||
</ActionButton>
|
|
||||||
<ConfirmDialog
|
|
||||||
open={dialogOpen}
|
|
||||||
title="Unpair this machine?"
|
|
||||||
subtitle={false}
|
|
||||||
toBeConfirmed={machine.name}
|
|
||||||
onConfirmed={() => {
|
|
||||||
setOpen(false)
|
|
||||||
machineAction({
|
|
||||||
variables: {
|
|
||||||
deviceId: machine.deviceId,
|
|
||||||
action: 'unpair'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
onDissmised={() => {
|
|
||||||
setOpen(false)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ActionButton
|
|
||||||
className={classes.inlineChip}
|
|
||||||
color="primary"
|
|
||||||
Icon={RebootIcon}
|
|
||||||
InverseIcon={RebootReversedIcon}
|
|
||||||
disabled={loading}
|
|
||||||
onClick={() => {
|
|
||||||
machineAction({
|
|
||||||
variables: {
|
|
||||||
deviceId: machine.deviceId,
|
|
||||||
action: 'reboot'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}>
|
|
||||||
Reboot
|
|
||||||
</ActionButton>
|
|
||||||
<ActionButton
|
|
||||||
className={classes.inlineChip}
|
|
||||||
disabled={loading}
|
|
||||||
color="primary"
|
|
||||||
Icon={ShutdownIcon}
|
|
||||||
InverseIcon={ShutdownReversedIcon}
|
|
||||||
onClick={() => {
|
|
||||||
machineAction({
|
|
||||||
variables: {
|
|
||||||
deviceId: machine.deviceId,
|
|
||||||
action: 'shutdown'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}>
|
|
||||||
Shutdown
|
|
||||||
</ActionButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { fade } from '@material-ui/core/styles/colorManipulator'
|
||||||
|
|
||||||
|
import { offColor, comet } from 'src/styling/variables'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
wrapper: {
|
||||||
|
display: 'flex',
|
||||||
|
marginTop: 24,
|
||||||
|
marginBottom: 32,
|
||||||
|
fontSize: 14
|
||||||
|
},
|
||||||
|
column1: {
|
||||||
|
width: 600
|
||||||
|
},
|
||||||
|
column2: {
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
lastRow: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row'
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginBottom: 36
|
||||||
|
},
|
||||||
|
actionRow: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginLeft: -4
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
marginRight: 4,
|
||||||
|
marginLeft: 4
|
||||||
|
},
|
||||||
|
dialog: {
|
||||||
|
width: 434
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
color: offColor,
|
||||||
|
margin: [[0, 0, 6, 0]]
|
||||||
|
},
|
||||||
|
chips: {
|
||||||
|
marginLeft: -2
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
width: 248
|
||||||
|
},
|
||||||
|
machineModel: {
|
||||||
|
width: 198
|
||||||
|
},
|
||||||
|
separator: {
|
||||||
|
width: 1,
|
||||||
|
height: 170,
|
||||||
|
zIndex: 1,
|
||||||
|
marginRight: 60,
|
||||||
|
marginLeft: 'auto',
|
||||||
|
background: fade(comet, 0.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { useQuery } from '@apollo/react-hooks'
|
import { useQuery } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import { gql } from 'apollo-boost'
|
import gql from 'graphql-tag'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
@ -20,6 +20,8 @@ const GET_MACHINES = gql`
|
||||||
machines {
|
machines {
|
||||||
name
|
name
|
||||||
deviceId
|
deviceId
|
||||||
|
lastPing
|
||||||
|
pairedAt
|
||||||
paired
|
paired
|
||||||
cashbox
|
cashbox
|
||||||
cassette1
|
cassette1
|
||||||
|
|
@ -42,35 +44,28 @@ const MachineStatus = () => {
|
||||||
const elements = [
|
const elements = [
|
||||||
{
|
{
|
||||||
header: 'Machine Name',
|
header: 'Machine Name',
|
||||||
width: 232,
|
width: 250,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
view: m => m.name
|
view: m => m.name
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Status',
|
header: 'Status',
|
||||||
width: 349,
|
width: 350,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
view: m => <MainStatus statuses={m.statuses} />
|
view: m => <MainStatus statuses={m.statuses} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Last ping',
|
header: 'Last ping',
|
||||||
width: 192,
|
width: 200,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
view: m => moment(m.lastPing).fromNow()
|
view: m => (m.lastPing ? moment(m.lastPing).fromNow() : 'unknown')
|
||||||
},
|
|
||||||
{
|
|
||||||
header: 'Ping Time',
|
|
||||||
width: 155,
|
|
||||||
size: 'sm',
|
|
||||||
textAlign: 'left',
|
|
||||||
view: m => m.pingTime || 'unknown'
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Software Version',
|
header: 'Software Version',
|
||||||
width: 201,
|
width: 200,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
view: m => m.softwareVersion || 'unknown'
|
view: m => m.softwareVersion || 'unknown'
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { gql } from 'apollo-boost'
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ const FiatBalanceOverrides = ({ section }) => {
|
||||||
display: 'Cash-out 1',
|
display: 'Cash-out 1',
|
||||||
width: 155,
|
width: 155,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
|
doubleHeader: 'Cash-out (Cassette Empty)',
|
||||||
bold: true,
|
bold: true,
|
||||||
input: TextInputFormik,
|
input: TextInputFormik,
|
||||||
suffix: 'notes'
|
suffix: 'notes'
|
||||||
|
|
@ -81,6 +82,7 @@ const FiatBalanceOverrides = ({ section }) => {
|
||||||
display: 'Cash-out 2',
|
display: 'Cash-out 2',
|
||||||
width: 155,
|
width: 155,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
|
doubleHeader: 'Cash-out (Cassette Empty)',
|
||||||
bold: true,
|
bold: true,
|
||||||
input: TextInputFormik,
|
input: TextInputFormik,
|
||||||
suffix: 'notes'
|
suffix: 'notes'
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { gql } from 'apollo-boost'
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState, memo } from 'react'
|
import React, { useState, memo } from 'react'
|
||||||
|
|
||||||
|
|
@ -47,7 +47,6 @@ const CoinATMRadar = memo(() => {
|
||||||
|
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
// TODO: treat errors on useMutation and useQuery
|
|
||||||
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
||||||
onCompleted: configResponse =>
|
onCompleted: configResponse =>
|
||||||
setCoinAtmRadarConfig(
|
setCoinAtmRadarConfig(
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import { gql } from 'apollo-boost'
|
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import { Form, Formik, Field as FormikField } from 'formik'
|
import { Form, Formik, Field as FormikField } from 'formik'
|
||||||
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
|
@ -22,14 +22,14 @@ import {
|
||||||
} from './OperatorInfo.styles'
|
} from './OperatorInfo.styles'
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({
|
const validationSchema = Yup.object().shape({
|
||||||
active: Yup.boolean().required(),
|
active: Yup.boolean(),
|
||||||
name: Yup.string().required(),
|
name: Yup.string(),
|
||||||
phone: Yup.string().required(),
|
phone: Yup.string(),
|
||||||
email: Yup.string()
|
email: Yup.string()
|
||||||
.email('Please enter a valid email address')
|
.email('Please enter a valid email address')
|
||||||
.required(),
|
.required(),
|
||||||
website: Yup.string().required(),
|
website: Yup.string(),
|
||||||
companyNumber: Yup.string().required()
|
companyNumber: Yup.string()
|
||||||
})
|
})
|
||||||
|
|
||||||
const fieldStyles = {
|
const fieldStyles = {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ const styles = {
|
||||||
height: '100%'
|
height: '100%'
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
|
flex: 1,
|
||||||
marginLeft: 48,
|
marginLeft: 48,
|
||||||
paddingTop: 15
|
paddingTop: 15
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// import { makeStyles } from '@material-ui/core/styles'
|
// import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { gql } from 'apollo-boost'
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState, memo } from 'react'
|
import React, { useState, memo } from 'react'
|
||||||
|
|
||||||
|
|
@ -49,10 +49,10 @@ const receiptPrintingOptions = [
|
||||||
code: 'off',
|
code: 'off',
|
||||||
display: 'Off'
|
display: 'Off'
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
code: 'optional',
|
// code: 'optional',
|
||||||
display: 'Optional (ask user)'
|
// display: 'Optional (ask user)'
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
code: 'on',
|
code: 'on',
|
||||||
display: 'On'
|
display: 'On'
|
||||||
|
|
@ -64,10 +64,8 @@ const ReceiptPrinting = memo(() => {
|
||||||
|
|
||||||
// const classes = useStyles()
|
// const classes = useStyles()
|
||||||
|
|
||||||
// TODO: treat errors on useMutation and useQuery
|
|
||||||
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
||||||
onCompleted: configResponse => {
|
onCompleted: configResponse => {
|
||||||
console.log(configResponse.saveConfig)
|
|
||||||
return setReceiptPrintingConfig(
|
return setReceiptPrintingConfig(
|
||||||
fromNamespace(namespaces.RECEIPT, configResponse.saveConfig)
|
fromNamespace(namespaces.RECEIPT, configResponse.saveConfig)
|
||||||
)
|
)
|
||||||
|
|
@ -87,7 +85,6 @@ const ReceiptPrinting = memo(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!receiptPrintingConfig) return null
|
if (!receiptPrintingConfig) return null
|
||||||
console.log(receiptPrintingConfig)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import { gql } from 'apollo-boost'
|
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import { Form, Formik, Field } from 'formik'
|
import { Form, Formik, Field } from 'formik'
|
||||||
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
|
@ -83,25 +83,30 @@ const TermsConditions = () => {
|
||||||
{
|
{
|
||||||
name: 'title',
|
name: 'title',
|
||||||
label: 'Screen title',
|
label: 'Screen title',
|
||||||
value: formData.title ?? ''
|
value: formData.title ?? '',
|
||||||
|
width: 282
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'text',
|
name: 'text',
|
||||||
label: 'Text content',
|
label: 'Text content',
|
||||||
value: formData.text ?? '',
|
value: formData.text ?? '',
|
||||||
multiline: true
|
width: 502,
|
||||||
|
multiline: true,
|
||||||
|
rows: 6
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'acceptButtonText',
|
name: 'acceptButtonText',
|
||||||
label: 'Accept button text',
|
label: 'Accept button text',
|
||||||
value: formData.acceptButtonText ?? '',
|
value: formData.acceptButtonText ?? '',
|
||||||
placeholder: 'I accept'
|
placeholder: 'I accept',
|
||||||
|
width: 282
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'cancelButtonText',
|
name: 'cancelButtonText',
|
||||||
label: 'Cancel button text',
|
label: 'Cancel button text',
|
||||||
value: formData.cancelButtonText ?? '',
|
value: formData.cancelButtonText ?? '',
|
||||||
placeholder: 'Cancel'
|
placeholder: 'Cancel',
|
||||||
|
width: 282
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -145,10 +150,12 @@ const TermsConditions = () => {
|
||||||
id={f.name}
|
id={f.name}
|
||||||
name={f.name}
|
name={f.name}
|
||||||
component={TextInputFormik}
|
component={TextInputFormik}
|
||||||
|
width={f.width}
|
||||||
placeholder={f.placeholder}
|
placeholder={f.placeholder}
|
||||||
type="text"
|
type="text"
|
||||||
label={f.label}
|
label={f.label}
|
||||||
multiline={f.multiline}
|
multiline={f.multiline}
|
||||||
|
rows={f.rows}
|
||||||
rowsMax="6"
|
rowsMax="6"
|
||||||
onFocus={() => setError(null)}
|
onFocus={() => setError(null)}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import { gql } from 'apollo-boost'
|
import gql from 'graphql-tag'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
@ -8,7 +8,7 @@ import React, { useState } from 'react'
|
||||||
import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper'
|
import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper'
|
||||||
import Title from 'src/components/Title'
|
import Title from 'src/components/Title'
|
||||||
import Uptime from 'src/components/Uptime'
|
import Uptime from 'src/components/Uptime'
|
||||||
import { FeatureButton, SimpleButton } from 'src/components/buttons'
|
import { SimpleButton } from 'src/components/buttons'
|
||||||
import { Select } from 'src/components/inputs'
|
import { Select } from 'src/components/inputs'
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
|
|
@ -20,8 +20,8 @@ import {
|
||||||
} from 'src/components/table'
|
} from 'src/components/table'
|
||||||
import { Info3 } from 'src/components/typography'
|
import { Info3 } from 'src/components/typography'
|
||||||
import typographyStyles from 'src/components/typography/styles'
|
import typographyStyles from 'src/components/typography/styles'
|
||||||
import { ReactComponent as DownloadActive } from 'src/styling/icons/button/download/white.svg'
|
import { ReactComponent as WhiteShareIcon } from 'src/styling/icons/circle buttons/share/white.svg'
|
||||||
import { ReactComponent as Download } from 'src/styling/icons/button/download/zodiac.svg'
|
import { ReactComponent as ShareIcon } from 'src/styling/icons/circle buttons/share/zodiac.svg'
|
||||||
import { offColor } from 'src/styling/variables'
|
import { offColor } from 'src/styling/variables'
|
||||||
|
|
||||||
import logsStyles from './Logs.styles'
|
import logsStyles from './Logs.styles'
|
||||||
|
|
@ -41,7 +41,6 @@ const localStyles = {
|
||||||
margin: 'auto 0 auto 0'
|
margin: 'auto 0 auto 0'
|
||||||
},
|
},
|
||||||
headerLine2: {
|
headerLine2: {
|
||||||
height: 60,
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
marginBottom: 24
|
marginBottom: 24
|
||||||
|
|
@ -61,24 +60,14 @@ const formatDate = date => {
|
||||||
return moment(date).format('YYYY-MM-DD HH:mm')
|
return moment(date).format('YYYY-MM-DD HH:mm')
|
||||||
}
|
}
|
||||||
|
|
||||||
const GET_VERSION = gql`
|
const GET_DATA = gql`
|
||||||
query {
|
|
||||||
serverVersion
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const GET_UPTIME = gql`
|
|
||||||
{
|
{
|
||||||
|
serverVersion
|
||||||
uptime {
|
uptime {
|
||||||
name
|
name
|
||||||
state
|
state
|
||||||
uptime
|
uptime
|
||||||
}
|
}
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const GET_SERVER_LOGS = gql`
|
|
||||||
{
|
|
||||||
serverLogs {
|
serverLogs {
|
||||||
logLevel
|
logLevel
|
||||||
id
|
id
|
||||||
|
|
@ -87,7 +76,6 @@ const GET_SERVER_LOGS = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const SUPPORT_LOGS = gql`
|
const SUPPORT_LOGS = gql`
|
||||||
mutation ServerSupportLogs {
|
mutation ServerSupportLogs {
|
||||||
serverSupportLogs {
|
serverSupportLogs {
|
||||||
|
|
@ -101,30 +89,19 @@ const Logs = () => {
|
||||||
|
|
||||||
const [saveMessage, setSaveMessage] = useState(null)
|
const [saveMessage, setSaveMessage] = useState(null)
|
||||||
const [logLevel, setLogLevel] = useState(SHOW_ALL)
|
const [logLevel, setLogLevel] = useState(SHOW_ALL)
|
||||||
const [anchorEl, setAnchorEl] = useState(null)
|
|
||||||
|
|
||||||
const { data: version } = useQuery(GET_VERSION)
|
const { data } = useQuery(GET_DATA, {
|
||||||
const serverVersion = version?.serverVersion
|
|
||||||
|
|
||||||
const { data: uptimeResponse } = useQuery(GET_UPTIME)
|
|
||||||
const processStates = uptimeResponse?.uptime ?? []
|
|
||||||
|
|
||||||
const { data: logsResponse } = useQuery(GET_SERVER_LOGS, {
|
|
||||||
fetchPolicy: 'no-cache',
|
|
||||||
onCompleted: () => setSaveMessage('')
|
onCompleted: () => setSaveMessage('')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const serverVersion = data?.serverVersion
|
||||||
|
const processStates = data?.uptime ?? []
|
||||||
|
|
||||||
const [sendSnapshot, { loading }] = useMutation(SUPPORT_LOGS, {
|
const [sendSnapshot, { loading }] = useMutation(SUPPORT_LOGS, {
|
||||||
onError: () => setSaveMessage('Failure saving snapshot'),
|
onError: () => setSaveMessage('Failure saving snapshot'),
|
||||||
onCompleted: () => setSaveMessage('✓ Saved latest snapshot')
|
onCompleted: () => setSaveMessage('✓ Saved latest snapshot')
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleOpenRangePicker = event => {
|
|
||||||
setAnchorEl(anchorEl ? null : event.currentTarget)
|
|
||||||
}
|
|
||||||
|
|
||||||
const open = Boolean(anchorEl)
|
|
||||||
const id = open ? 'date-range-popover' : undefined
|
|
||||||
const getLogLevels = R.compose(
|
const getLogLevels = R.compose(
|
||||||
R.prepend(SHOW_ALL),
|
R.prepend(SHOW_ALL),
|
||||||
R.uniq,
|
R.uniq,
|
||||||
|
|
@ -137,27 +114,19 @@ const Logs = () => {
|
||||||
<div className={classes.titleWrapper}>
|
<div className={classes.titleWrapper}>
|
||||||
<div className={classes.titleAndButtonsContainer}>
|
<div className={classes.titleAndButtonsContainer}>
|
||||||
<Title>Server</Title>
|
<Title>Server</Title>
|
||||||
{logsResponse && (
|
{data && (
|
||||||
<div className={classes.buttonsWrapper}>
|
<div className={classes.buttonsWrapper}>
|
||||||
<FeatureButton
|
|
||||||
Icon={Download}
|
|
||||||
InverseIcon={DownloadActive}
|
|
||||||
aria-describedby={id}
|
|
||||||
variant="contained"
|
|
||||||
onClick={handleOpenRangePicker}
|
|
||||||
/>
|
|
||||||
<LogsDowloaderPopover
|
<LogsDowloaderPopover
|
||||||
title="Download logs"
|
title="Download logs"
|
||||||
name="server-logs"
|
name="server-logs"
|
||||||
id={id}
|
logs={data.serverLogs}
|
||||||
open={open}
|
|
||||||
anchorEl={anchorEl}
|
|
||||||
logs={logsResponse.serverLogs}
|
|
||||||
getTimestamp={log => log.timestamp}
|
getTimestamp={log => log.timestamp}
|
||||||
/>
|
/>
|
||||||
<SimpleButton
|
<SimpleButton
|
||||||
className={classes.button}
|
className={classes.shareButton}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
|
Icon={ShareIcon}
|
||||||
|
InverseIcon={WhiteShareIcon}
|
||||||
onClick={sendSnapshot}>
|
onClick={sendSnapshot}>
|
||||||
Share with Lamassu
|
Share with Lamassu
|
||||||
</SimpleButton>
|
</SimpleButton>
|
||||||
|
|
@ -170,11 +139,11 @@ const Logs = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.headerLine2}>
|
<div className={classes.headerLine2}>
|
||||||
{logsResponse && (
|
{data && (
|
||||||
<Select
|
<Select
|
||||||
onSelectedItemChange={setLogLevel}
|
onSelectedItemChange={setLogLevel}
|
||||||
label="Level"
|
label="Level"
|
||||||
items={getLogLevels(logsResponse)}
|
items={getLogLevels(data)}
|
||||||
default={SHOW_ALL}
|
default={SHOW_ALL}
|
||||||
selectedItem={logLevel}
|
selectedItem={logLevel}
|
||||||
/>
|
/>
|
||||||
|
|
@ -197,8 +166,8 @@ const Logs = () => {
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{logsResponse &&
|
{data &&
|
||||||
logsResponse.serverLogs
|
data.serverLogs
|
||||||
.filter(
|
.filter(
|
||||||
log => logLevel === SHOW_ALL || log.logLevel === logLevel
|
log => logLevel === SHOW_ALL || log.logLevel === logLevel
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -45,10 +45,11 @@ const FormRenderer = ({
|
||||||
onSubmit={save}>
|
onSubmit={save}>
|
||||||
<Form className={classes.form}>
|
<Form className={classes.form}>
|
||||||
<Grid container spacing={3} className={classes.grid}>
|
<Grid container spacing={3} className={classes.grid}>
|
||||||
{elements.map(({ component, code, display }) => (
|
{elements.map(({ component, code, display, inputProps }) => (
|
||||||
<Grid item xs={12} key={code}>
|
<Grid item xs={12} key={code}>
|
||||||
<FastField
|
<FastField
|
||||||
component={component}
|
component={component}
|
||||||
|
{...inputProps}
|
||||||
name={code}
|
name={code}
|
||||||
label={display}
|
label={display}
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles, Grid } from '@material-ui/core'
|
import { makeStyles, Grid } from '@material-ui/core'
|
||||||
import { gql } from 'apollo-boost'
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
|
import {
|
||||||
import TextInputFormik from 'src/components/inputs/formik/TextInput'
|
TextInput,
|
||||||
|
SecretInput,
|
||||||
|
Autocomplete
|
||||||
|
} from 'src/components/inputs/formik'
|
||||||
|
|
||||||
const isDefined = it => it && it.length
|
const isDefined = it => it && it.length
|
||||||
|
|
||||||
|
|
@ -13,65 +16,71 @@ export default {
|
||||||
{
|
{
|
||||||
code: 'token',
|
code: 'token',
|
||||||
display: 'API Token',
|
display: 'API Token',
|
||||||
component: TextInputFormik,
|
component: TextInput,
|
||||||
face: true,
|
face: true,
|
||||||
long: true
|
long: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'environment',
|
code: 'environment',
|
||||||
display: 'Environment',
|
display: 'Environment',
|
||||||
component: TextInputFormik,
|
component: Autocomplete,
|
||||||
|
inputProps: {
|
||||||
|
options: ['prod', 'test']
|
||||||
|
// valueProp: 'deviceId',
|
||||||
|
// getLabel: R.path(['name']),
|
||||||
|
// limit: null
|
||||||
|
},
|
||||||
face: true
|
face: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'btcWalletId',
|
code: 'btcWalletId',
|
||||||
display: 'BTC Wallet ID',
|
display: 'BTC Wallet ID',
|
||||||
component: TextInputFormik
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'btcWalletPassphrase',
|
code: 'btcWalletPassphrase',
|
||||||
display: 'BTC Wallet Passphrase',
|
display: 'BTC Wallet Passphrase',
|
||||||
component: SecretInputFormik
|
component: SecretInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'ltcWalletId',
|
code: 'ltcWalletId',
|
||||||
display: 'LTC Wallet ID',
|
display: 'LTC Wallet ID',
|
||||||
component: TextInputFormik
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'ltcWalletPassphrase',
|
code: 'ltcWalletPassphrase',
|
||||||
display: 'LTC Wallet Passphrase',
|
display: 'LTC Wallet Passphrase',
|
||||||
component: SecretInputFormik
|
component: SecretInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'zecWalletId',
|
code: 'zecWalletId',
|
||||||
display: 'ZEC Wallet ID',
|
display: 'ZEC Wallet ID',
|
||||||
component: TextInputFormik
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'zecWalletPassphrase',
|
code: 'zecWalletPassphrase',
|
||||||
display: 'ZEC Wallet Passphrase',
|
display: 'ZEC Wallet Passphrase',
|
||||||
component: SecretInputFormik
|
component: SecretInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'bchWalletId',
|
code: 'bchWalletId',
|
||||||
display: 'BCH Wallet ID',
|
display: 'BCH Wallet ID',
|
||||||
component: TextInputFormik
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'bchWalletPassphrase',
|
code: 'bchWalletPassphrase',
|
||||||
display: 'BCH Wallet Passphrase',
|
display: 'BCH Wallet Passphrase',
|
||||||
component: SecretInputFormik
|
component: SecretInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'dashWalletId',
|
code: 'dashWalletId',
|
||||||
display: 'DASH Wallet ID',
|
display: 'DASH Wallet ID',
|
||||||
component: TextInputFormik
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'dashWalletPassphrase',
|
code: 'dashWalletPassphrase',
|
||||||
display: 'DASH Wallet Passphrase',
|
display: 'DASH Wallet Passphrase',
|
||||||
component: SecretInputFormik
|
component: SecretInput
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
validationSchema: Yup.object().shape({
|
validationSchema: Yup.object().shape({
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,14 @@ export default {
|
||||||
elements: [
|
elements: [
|
||||||
{
|
{
|
||||||
code: 'apiKey',
|
code: 'apiKey',
|
||||||
display: 'API Key',
|
display: 'Project ID',
|
||||||
component: TextInputFormik,
|
component: TextInputFormik,
|
||||||
face: true,
|
face: true,
|
||||||
long: true
|
long: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'apiSecret',
|
code: 'apiSecret',
|
||||||
display: 'API Secret',
|
display: 'Project Secret',
|
||||||
component: SecretInputFormik
|
component: SecretInputFormik
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles, Box } from '@material-ui/core'
|
||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
import classnames from 'classnames'
|
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import React, { memo } from 'react'
|
import React, { memo } from 'react'
|
||||||
|
|
||||||
import { IDButton } from 'src/components/buttons'
|
import { IDButton } from 'src/components/buttons'
|
||||||
|
import { Label1 } from 'src/components/typography'
|
||||||
import { ReactComponent as CardIdInverseIcon } from 'src/styling/icons/ID/card/white.svg'
|
import { ReactComponent as CardIdInverseIcon } from 'src/styling/icons/ID/card/white.svg'
|
||||||
import { ReactComponent as CardIdIcon } from 'src/styling/icons/ID/card/zodiac.svg'
|
import { ReactComponent as CardIdIcon } from 'src/styling/icons/ID/card/zodiac.svg'
|
||||||
import { ReactComponent as PhoneIdInverseIcon } from 'src/styling/icons/ID/phone/white.svg'
|
import { ReactComponent as PhoneIdInverseIcon } from 'src/styling/icons/ID/phone/white.svg'
|
||||||
|
|
@ -13,33 +13,55 @@ import { ReactComponent as CamIdInverseIcon } from 'src/styling/icons/ID/photo/w
|
||||||
import { ReactComponent as CamIdIcon } from 'src/styling/icons/ID/photo/zodiac.svg'
|
import { ReactComponent as CamIdIcon } from 'src/styling/icons/ID/photo/zodiac.svg'
|
||||||
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
|
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
|
||||||
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
|
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
|
||||||
|
import { URI } from 'src/utils/apollo'
|
||||||
import { toUnit } from 'src/utils/coin'
|
import { toUnit } from 'src/utils/coin'
|
||||||
import { onlyFirstToUpper } from 'src/utils/string'
|
import { onlyFirstToUpper } from 'src/utils/string'
|
||||||
|
|
||||||
import CopyToClipboard from './CopyToClipboard'
|
import CopyToClipboard from './CopyToClipboard'
|
||||||
import { detailsRowStyles, labelStyles } from './Transactions.styles'
|
import styles from './DetailsCard.styles'
|
||||||
|
|
||||||
const labelUseStyles = makeStyles(labelStyles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const Label = ({ children }) => {
|
const formatAddress = (address = '') => address.replace(/(.{5})/g, '$1 ')
|
||||||
const classes = labelUseStyles()
|
|
||||||
|
|
||||||
return <div className={classes.label}>{children}</div>
|
const getCashOutStatus = it => {
|
||||||
|
if (it.hasError) return 'Error'
|
||||||
|
if (it.dispense) return 'Success'
|
||||||
|
if (it.expired) return 'Expired'
|
||||||
|
return 'Pending'
|
||||||
}
|
}
|
||||||
|
|
||||||
const detailsUseStyles = makeStyles(detailsRowStyles)
|
const getCashInStatus = it => {
|
||||||
|
console.log(it)
|
||||||
|
if (it.operatorCompleted) return 'Cancelled'
|
||||||
|
if (it.hasError) return 'Error'
|
||||||
|
if (it.sendConfirmed) return 'Sent'
|
||||||
|
if (it.expired) return 'Expired'
|
||||||
|
return 'Pending'
|
||||||
|
}
|
||||||
|
|
||||||
const DetailsRow = ({ it: tx, ...props }) => {
|
const getStatus = it => {
|
||||||
const classes = detailsUseStyles()
|
if (it.class === 'cashOut') {
|
||||||
|
return getCashOutStatus(it)
|
||||||
|
}
|
||||||
|
return getCashInStatus(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Label = ({ children }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
return <Label1 className={classes.label}>{children}</Label1>
|
||||||
|
}
|
||||||
|
|
||||||
|
const DetailsRow = ({ it: tx }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
const addr = tx.toAddress
|
|
||||||
const txHash = tx.txHash
|
|
||||||
const fiat = Number.parseFloat(tx.fiat)
|
const fiat = Number.parseFloat(tx.fiat)
|
||||||
const crypto = toUnit(new BigNumber(tx.cryptoAtoms), tx.cryptoCode).toFormat(
|
const crypto = toUnit(new BigNumber(tx.cryptoAtoms), tx.cryptoCode)
|
||||||
5
|
|
||||||
)
|
|
||||||
const commissionPercentage = Number.parseFloat(tx.commissionPercentage, 2)
|
const commissionPercentage = Number.parseFloat(tx.commissionPercentage, 2)
|
||||||
const commission = fiat * commissionPercentage
|
const commission = fiat * commissionPercentage
|
||||||
|
const exchangeRate = Number(fiat / crypto).toFixed(3)
|
||||||
|
const displayExRate = `1 ${tx.cryptoCode} = ${exchangeRate} ${tx.fiatCode}`
|
||||||
|
|
||||||
const customer = tx.customerIdCardData && {
|
const customer = tx.customerIdCardData && {
|
||||||
name: `${onlyFirstToUpper(
|
name: `${onlyFirstToUpper(
|
||||||
tx.customerIdCardData.firstName
|
tx.customerIdCardData.firstName
|
||||||
|
|
@ -52,170 +74,140 @@ const DetailsRow = ({ it: tx, ...props }) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatAddress = (address = '') => {
|
|
||||||
return address.replace(/(.{5})/g, '$1 ')
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className={classes.wrapper}>
|
||||||
<div className={classes.wrapper}>
|
<div className={classes.row}>
|
||||||
<div className={classnames(classes.row)}>
|
<div className={classes.direction}>
|
||||||
<div className={classnames(classes.col, classes.col1)}>
|
<Label>Direction</Label>
|
||||||
{/* Column 1 */}
|
<div>
|
||||||
<div className={classes.innerRow}>
|
<span className={classes.txIcon}>
|
||||||
<div>
|
{tx.txClass === 'cashOut' ? <TxOutIcon /> : <TxInIcon />}
|
||||||
<Label>Direction</Label>
|
</span>
|
||||||
<div>
|
<span>{tx.txClass === 'cashOut' ? 'Cash-out' : 'Cash-in'}</span>
|
||||||
<span className={classes.txIcon}>
|
|
||||||
{tx.txClass === 'cashOut' ? <TxOutIcon /> : <TxInIcon />}
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
{tx.txClass === 'cashOut' ? 'Cash-out' : 'Cash-in'}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={classes.availableIds}>
|
|
||||||
<Label>Available IDs</Label>
|
|
||||||
<div>
|
|
||||||
{tx.customerPhone && (
|
|
||||||
<IDButton
|
|
||||||
name="phone"
|
|
||||||
Icon={PhoneIdIcon}
|
|
||||||
InverseIcon={PhoneIdInverseIcon}>
|
|
||||||
{tx.customerPhone}
|
|
||||||
</IDButton>
|
|
||||||
)}
|
|
||||||
{tx.customerIdCardPhotoPath && !tx.customerIdCardData && (
|
|
||||||
<IDButton
|
|
||||||
name="card"
|
|
||||||
Icon={CardIdIcon}
|
|
||||||
InverseIcon={CardIdInverseIcon}>
|
|
||||||
<img alt="" src={tx.customerIdCardPhotoPath} />
|
|
||||||
</IDButton>
|
|
||||||
)}
|
|
||||||
{tx.customerIdCardData && (
|
|
||||||
<IDButton
|
|
||||||
name="card"
|
|
||||||
Icon={CardIdIcon}
|
|
||||||
InverseIcon={CardIdInverseIcon}>
|
|
||||||
<div className={classes.idCardDataCard}>
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<Label>Name</Label>
|
|
||||||
<div>{customer.name}</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label>Age</Label>
|
|
||||||
<div>{customer.age}</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label>Country</Label>
|
|
||||||
<div>{customer.country}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<Label>ID number</Label>
|
|
||||||
<div>{customer.idCardNumber}</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label>Gender</Label>
|
|
||||||
<div />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label>Expiration date</Label>
|
|
||||||
<div>{customer.idCardExpirationDate}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</IDButton>
|
|
||||||
)}
|
|
||||||
{tx.customerFrontCameraPath && (
|
|
||||||
<IDButton
|
|
||||||
name="cam"
|
|
||||||
Icon={CamIdIcon}
|
|
||||||
InverseIcon={CamIdInverseIcon}>
|
|
||||||
<img alt="" src={tx.customerFrontCameraPath} />
|
|
||||||
</IDButton>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={classnames(classes.col, classes.col2)}>
|
|
||||||
{/* Column 2 */}
|
|
||||||
<div className={classes.innerRow}>
|
|
||||||
<div>
|
|
||||||
<Label>Exchange rate</Label>
|
|
||||||
<div>
|
|
||||||
{`1 ${tx.cryptoCode} = ${Number(fiat / crypto).toFixed(3)} ${
|
|
||||||
tx.fiatCode
|
|
||||||
}`}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={classes.commissionWrapper}>
|
|
||||||
<Label>Commission</Label>
|
|
||||||
<div>
|
|
||||||
{`${commission} ${tx.fiatCode} (${commissionPercentage *
|
|
||||||
100} %)`}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{tx.txClass === 'cashIn' && (
|
|
||||||
<div className={classes.innerRow}>
|
|
||||||
<Label>Fixed fee</Label>
|
|
||||||
<div>{Number.parseFloat(tx.cashInFee)}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={classnames(classes.col, classes.col3)}>
|
|
||||||
{/* Column 3 */}
|
|
||||||
<div className={classnames(classes.innerRow)}>
|
|
||||||
<div style={{ height: 43.4 }}>{/* Export to PDF */}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={classnames(classes.row)}>
|
|
||||||
<div className={classnames(classes.col, classes.col1)}>
|
<div className={classes.availableIds}>
|
||||||
{/* Column 1 */}
|
<Label>Available IDs</Label>
|
||||||
<div className={classes.innerRow}>
|
<Box display="flex" flexDirection="row">
|
||||||
<div>
|
{tx.customerPhone && (
|
||||||
<Label>BTC address</Label>
|
<IDButton
|
||||||
<div>
|
className={classes.idButton}
|
||||||
<CopyToClipboard className={classes.cryptoAddr}>
|
name="phone"
|
||||||
{formatAddress(addr)}
|
Icon={PhoneIdIcon}
|
||||||
</CopyToClipboard>
|
InverseIcon={PhoneIdInverseIcon}>
|
||||||
|
{tx.customerPhone}
|
||||||
|
</IDButton>
|
||||||
|
)}
|
||||||
|
{tx.customerIdCardPhotoPath && !tx.customerIdCardData && (
|
||||||
|
<IDButton
|
||||||
|
popoverClassname={classes.popover}
|
||||||
|
className={classes.idButton}
|
||||||
|
name="card"
|
||||||
|
Icon={CardIdIcon}
|
||||||
|
InverseIcon={CardIdInverseIcon}>
|
||||||
|
<img
|
||||||
|
className={classes.idCardPhoto}
|
||||||
|
src={`${URI}/id-card-photo/${tx.customerIdCardPhotoPath}`}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</IDButton>
|
||||||
|
)}
|
||||||
|
{tx.customerIdCardData && (
|
||||||
|
<IDButton
|
||||||
|
className={classes.idButton}
|
||||||
|
name="card"
|
||||||
|
Icon={CardIdIcon}
|
||||||
|
InverseIcon={CardIdInverseIcon}>
|
||||||
|
<div className={classes.idCardDataCard}>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<Label>Name</Label>
|
||||||
|
<div>{customer.name}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label>Age</Label>
|
||||||
|
<div>{customer.age}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label>Country</Label>
|
||||||
|
<div>{customer.country}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<Label>ID number</Label>
|
||||||
|
<div>{customer.idCardNumber}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label>Expiration date</Label>
|
||||||
|
<div>{customer.idCardExpirationDate}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</IDButton>
|
||||||
</div>
|
)}
|
||||||
|
{tx.customerFrontCameraPath && (
|
||||||
|
<IDButton
|
||||||
|
name="cam"
|
||||||
|
Icon={CamIdIcon}
|
||||||
|
InverseIcon={CamIdInverseIcon}>
|
||||||
|
<img
|
||||||
|
src={`${URI}/front-camera-photo/${tx.customerFrontCameraPath}`}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</IDButton>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</div>
|
||||||
|
<div className={classes.exchangeRate}>
|
||||||
|
<Label>Exchange rate</Label>
|
||||||
|
<div>{crypto > 0 ? displayExRate : '-'}</div>
|
||||||
|
</div>
|
||||||
|
<div className={classes.commission}>
|
||||||
|
<Label>Commission</Label>
|
||||||
|
<div>
|
||||||
|
{`${commission} ${tx.fiatCode} (${commissionPercentage * 100} %)`}
|
||||||
</div>
|
</div>
|
||||||
<div className={classnames(classes.col, classes.col2)}>
|
</div>
|
||||||
{/* Column 2 */}
|
<div>
|
||||||
<div className={classes.innerRow}>
|
<Label>Fixed fee</Label>
|
||||||
<div>
|
<div>
|
||||||
<Label>Transaction ID</Label>
|
{tx.txClass === 'cashIn'
|
||||||
<div>
|
? `${Number.parseFloat(tx.cashInFee)} ${tx.fiatCode}`
|
||||||
<CopyToClipboard className={classes.txId}>
|
: 'N/A'}
|
||||||
{txHash}
|
|
||||||
</CopyToClipboard>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={classnames(classes.col, classes.col3)}>
|
|
||||||
{/* Column 3 */}
|
|
||||||
<div className={classes.innerRow}>
|
|
||||||
<div>
|
|
||||||
<Label>Session ID</Label>
|
|
||||||
<CopyToClipboard className={classes.sessionId}>
|
|
||||||
{tx.id}
|
|
||||||
</CopyToClipboard>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
<div className={classes.secondRow}>
|
||||||
|
<div className={classes.address}>
|
||||||
|
<Label>BTC address</Label>
|
||||||
|
<div>
|
||||||
|
<CopyToClipboard>{formatAddress(tx.toAddress)}</CopyToClipboard>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={classes.transactionId}>
|
||||||
|
<Label>Transaction ID</Label>
|
||||||
|
<div>
|
||||||
|
{tx.txClass === 'cashOut' ? (
|
||||||
|
'N/A'
|
||||||
|
) : (
|
||||||
|
<CopyToClipboard>{tx.txHash}</CopyToClipboard>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={classes.sessionId}>
|
||||||
|
<Label>Session ID</Label>
|
||||||
|
<CopyToClipboard>{tx.id}</CopyToClipboard>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={classes.lastRow}>
|
||||||
|
<div>
|
||||||
|
<Label>Transaction status</Label>
|
||||||
|
<span className={classes.bold}>{getStatus(tx)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
import typographyStyles from 'src/components/typography/styles'
|
||||||
|
import { offColor } from 'src/styling/variables'
|
||||||
|
|
||||||
|
const { p } = typographyStyles
|
||||||
|
|
||||||
|
export default {
|
||||||
|
wrapper: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
marginTop: 24
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginBottom: 36
|
||||||
|
},
|
||||||
|
secondRow: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginBottom: 36
|
||||||
|
},
|
||||||
|
lastRow: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginBottom: 32
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
color: offColor,
|
||||||
|
margin: [[0, 0, 6, 0]]
|
||||||
|
},
|
||||||
|
txIcon: {
|
||||||
|
marginRight: 10
|
||||||
|
},
|
||||||
|
popover: {
|
||||||
|
height: 164,
|
||||||
|
width: 215
|
||||||
|
},
|
||||||
|
idButton: {
|
||||||
|
marginRight: 4
|
||||||
|
},
|
||||||
|
idCardDataCard: {
|
||||||
|
extend: p,
|
||||||
|
display: 'flex',
|
||||||
|
padding: [[11, 8]],
|
||||||
|
// rework this into a proper component
|
||||||
|
'& > div': {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
'& > div': {
|
||||||
|
width: 144,
|
||||||
|
height: 37,
|
||||||
|
marginBottom: 15,
|
||||||
|
'&:last-child': {
|
||||||
|
marginBottom: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bold: {
|
||||||
|
fontWeight: 700
|
||||||
|
},
|
||||||
|
direction: {
|
||||||
|
width: 233
|
||||||
|
},
|
||||||
|
availableIds: {
|
||||||
|
width: 232
|
||||||
|
},
|
||||||
|
exchangeRate: {
|
||||||
|
width: 250
|
||||||
|
},
|
||||||
|
commission: {
|
||||||
|
width: 217
|
||||||
|
},
|
||||||
|
address: {
|
||||||
|
width: 280
|
||||||
|
},
|
||||||
|
transactionId: {
|
||||||
|
width: 280
|
||||||
|
},
|
||||||
|
sessionId: {
|
||||||
|
width: 215
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,17 +1,14 @@
|
||||||
import { useQuery } from '@apollo/react-hooks'
|
import { useQuery } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { gql } from 'apollo-boost'
|
|
||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
|
import gql from 'graphql-tag'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper'
|
import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper'
|
||||||
import Title from 'src/components/Title'
|
import Title from 'src/components/Title'
|
||||||
import { FeatureButton } from 'src/components/buttons'
|
|
||||||
import DataTable from 'src/components/tables/DataTable'
|
import DataTable from 'src/components/tables/DataTable'
|
||||||
import { ReactComponent as DownloadInverseIcon } from 'src/styling/icons/button/download/white.svg'
|
|
||||||
import { ReactComponent as Download } from 'src/styling/icons/button/download/zodiac.svg'
|
|
||||||
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
|
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
|
||||||
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
|
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
|
||||||
import { toUnit } from 'src/utils/coin'
|
import { toUnit } from 'src/utils/coin'
|
||||||
|
|
@ -21,7 +18,6 @@ import { mainStyles } from './Transactions.styles'
|
||||||
|
|
||||||
const useStyles = makeStyles(mainStyles)
|
const useStyles = makeStyles(mainStyles)
|
||||||
|
|
||||||
// TODO customerIdCardData
|
|
||||||
const GET_TRANSACTIONS = gql`
|
const GET_TRANSACTIONS = gql`
|
||||||
{
|
{
|
||||||
transactions {
|
transactions {
|
||||||
|
|
@ -30,7 +26,12 @@ const GET_TRANSACTIONS = gql`
|
||||||
txHash
|
txHash
|
||||||
toAddress
|
toAddress
|
||||||
commissionPercentage
|
commissionPercentage
|
||||||
|
expired
|
||||||
machineName
|
machineName
|
||||||
|
operatorCompleted
|
||||||
|
sendConfirmed
|
||||||
|
dispense
|
||||||
|
hasError: error
|
||||||
deviceId
|
deviceId
|
||||||
fiat
|
fiat
|
||||||
cashInFee
|
cashInFee
|
||||||
|
|
@ -49,8 +50,6 @@ const GET_TRANSACTIONS = gql`
|
||||||
`
|
`
|
||||||
|
|
||||||
const Transactions = () => {
|
const Transactions = () => {
|
||||||
const [anchorEl, setAnchorEl] = useState(null)
|
|
||||||
|
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
const { data: txResponse } = useQuery(GET_TRANSACTIONS)
|
const { data: txResponse } = useQuery(GET_TRANSACTIONS)
|
||||||
|
|
@ -127,17 +126,6 @@ const Transactions = () => {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const handleOpenRangePicker = event => {
|
|
||||||
setAnchorEl(anchorEl ? null : event.currentTarget)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCloseRangePicker = () => {
|
|
||||||
setAnchorEl(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
const open = Boolean(anchorEl)
|
|
||||||
const id = open ? 'date-range-popover' : undefined
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={classes.titleWrapper}>
|
<div className={classes.titleWrapper}>
|
||||||
|
|
@ -145,22 +133,11 @@ const Transactions = () => {
|
||||||
<Title>Transactions</Title>
|
<Title>Transactions</Title>
|
||||||
{txResponse && (
|
{txResponse && (
|
||||||
<div className={classes.buttonsWrapper}>
|
<div className={classes.buttonsWrapper}>
|
||||||
<FeatureButton
|
|
||||||
Icon={Download}
|
|
||||||
InverseIcon={DownloadInverseIcon}
|
|
||||||
aria-describedby={id}
|
|
||||||
variant="contained"
|
|
||||||
onClick={handleOpenRangePicker}
|
|
||||||
/>
|
|
||||||
<LogsDowloaderPopover
|
<LogsDowloaderPopover
|
||||||
title="Download logs"
|
title="Download logs"
|
||||||
name="transactions"
|
name="transactions"
|
||||||
id={id}
|
|
||||||
open={open}
|
|
||||||
anchorEl={anchorEl}
|
|
||||||
logs={txResponse.transactions}
|
logs={txResponse.transactions}
|
||||||
getTimestamp={tx => tx.created}
|
getTimestamp={tx => tx.created}
|
||||||
onClose={handleCloseRangePicker}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import typographyStyles from 'src/components/typography/styles'
|
import typographyStyles from 'src/components/typography/styles'
|
||||||
import { offColor, white } from 'src/styling/variables'
|
|
||||||
import baseStyles from 'src/pages/Logs.styles'
|
import baseStyles from 'src/pages/Logs.styles'
|
||||||
|
import { offColor, white } from 'src/styling/variables'
|
||||||
|
|
||||||
const { label1, mono, p } = typographyStyles
|
const { label1, mono, p } = typographyStyles
|
||||||
const { titleWrapper, titleAndButtonsContainer, buttonsWrapper } = baseStyles
|
const { titleWrapper, titleAndButtonsContainer, buttonsWrapper } = baseStyles
|
||||||
|
|
@ -34,66 +34,6 @@ const cpcStyles = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const detailsRowStyles = {
|
const detailsRowStyles = {
|
||||||
wrapper: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column'
|
|
||||||
},
|
|
||||||
col: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column'
|
|
||||||
},
|
|
||||||
col1: {
|
|
||||||
width: 413
|
|
||||||
},
|
|
||||||
col2: {
|
|
||||||
width: 506
|
|
||||||
},
|
|
||||||
col3: {
|
|
||||||
width: 233
|
|
||||||
},
|
|
||||||
innerRow: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'space-between'
|
|
||||||
},
|
|
||||||
row: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
margin: [[25, 0]]
|
|
||||||
},
|
|
||||||
mono: {
|
|
||||||
extend: mono
|
|
||||||
},
|
|
||||||
txIcon: {
|
|
||||||
marginRight: 10
|
|
||||||
},
|
|
||||||
availableIds: {
|
|
||||||
width: 110,
|
|
||||||
marginRight: 61,
|
|
||||||
'& > div': {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
'& button': {
|
|
||||||
'&:first-child': {
|
|
||||||
marginRight: 4
|
|
||||||
},
|
|
||||||
'&:last-child': {
|
|
||||||
marginLeft: 4
|
|
||||||
},
|
|
||||||
'&:only-child': {
|
|
||||||
margin: 0
|
|
||||||
},
|
|
||||||
'&:nth-child(2):last-child': {
|
|
||||||
margin: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
commissionWrapper: {
|
|
||||||
width: 110,
|
|
||||||
marginRight: 155
|
|
||||||
},
|
|
||||||
idCardDataCard: {
|
idCardDataCard: {
|
||||||
extend: p,
|
extend: p,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
|
@ -110,15 +50,6 @@ const detailsRowStyles = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
cryptoAddr: {
|
|
||||||
width: 252
|
|
||||||
},
|
|
||||||
txId: {
|
|
||||||
width: 346
|
|
||||||
},
|
|
||||||
sessionId: {
|
|
||||||
width: 184
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import { gql } from 'apollo-boost'
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { v4 } from 'uuid'
|
import { v4 } from 'uuid'
|
||||||
|
|
||||||
import Title from 'src/components/Title'
|
import Title from 'src/components/Title'
|
||||||
import { FeatureButton, Link } from 'src/components/buttons'
|
import { Link } from 'src/components/buttons'
|
||||||
import { Table as EditableTable } from 'src/components/editableTable'
|
import { Table as EditableTable } from 'src/components/editableTable'
|
||||||
import { ReactComponent as ConfigureInverseIcon } from 'src/styling/icons/button/configure/white.svg'
|
|
||||||
import { ReactComponent as Configure } from 'src/styling/icons/button/configure/zodiac.svg'
|
|
||||||
|
|
||||||
import { mainStyles } from './Triggers.styles'
|
import { mainStyles } from './Triggers.styles'
|
||||||
import Wizard from './Wizard'
|
import Wizard from './Wizard'
|
||||||
|
|
@ -60,13 +58,6 @@ const Triggers = () => {
|
||||||
<div className={classes.titleWrapper}>
|
<div className={classes.titleWrapper}>
|
||||||
<div className={classes.titleAndButtonsContainer}>
|
<div className={classes.titleAndButtonsContainer}>
|
||||||
<Title>Compliance Triggers</Title>
|
<Title>Compliance Triggers</Title>
|
||||||
<div className={classes.buttonsWrapper}>
|
|
||||||
<FeatureButton
|
|
||||||
Icon={Configure}
|
|
||||||
InverseIcon={ConfigureInverseIcon}
|
|
||||||
variant="contained"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.headerLabels}>
|
<div className={classes.headerLabels}>
|
||||||
<Link color="primary" onClick={() => setWizard(true)}>
|
<Link color="primary" onClick={() => setWizard(true)}>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { gql } from 'apollo-boost'
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,10 +36,10 @@ const tree = [
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: 'logs',
|
key: 'cashboxes',
|
||||||
label: 'Logs',
|
label: 'Cashboxes',
|
||||||
route: '/maintenance/logs',
|
route: '/maintenance/cashboxes',
|
||||||
component: MachineLogs
|
component: Cashboxes
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'funding',
|
key: 'funding',
|
||||||
|
|
@ -48,10 +48,10 @@ const tree = [
|
||||||
component: Funding
|
component: Funding
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'server-logs',
|
key: 'logs',
|
||||||
label: 'Server',
|
label: 'Machine logs',
|
||||||
route: '/maintenance/server-logs',
|
route: '/maintenance/logs',
|
||||||
component: ServerLogs
|
component: MachineLogs
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'machine-status',
|
key: 'machine-status',
|
||||||
|
|
@ -60,10 +60,10 @@ const tree = [
|
||||||
component: MachineStatus
|
component: MachineStatus
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'cashboxes',
|
key: 'server-logs',
|
||||||
label: 'Cashboxes',
|
label: 'Server',
|
||||||
route: '/maintenance/cashboxes',
|
route: '/maintenance/server-logs',
|
||||||
component: Cashboxes
|
component: ServerLogs
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -83,7 +83,7 @@ const tree = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: namespaces.LOCALE,
|
key: namespaces.LOCALE,
|
||||||
label: 'Locale',
|
label: 'Locales',
|
||||||
route: '/settings/locale',
|
route: '/settings/locale',
|
||||||
component: Locales
|
component: Locales
|
||||||
},
|
},
|
||||||
|
|
@ -93,12 +93,6 @@ const tree = [
|
||||||
route: '/settings/cash-out',
|
route: '/settings/cash-out',
|
||||||
component: Cashout
|
component: Cashout
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: 'services',
|
|
||||||
label: '3rd party services',
|
|
||||||
route: '/settings/3rd-party-services',
|
|
||||||
component: Services
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
key: namespaces.NOTIFICATIONS,
|
key: namespaces.NOTIFICATIONS,
|
||||||
label: 'Notifications',
|
label: 'Notifications',
|
||||||
|
|
@ -106,16 +100,22 @@ const tree = [
|
||||||
component: Notifications
|
component: Notifications
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: namespaces.OPERATOR_INFO,
|
key: 'services',
|
||||||
label: 'Operator Info',
|
label: '3rd party services',
|
||||||
route: '/settings/operator-info',
|
route: '/settings/3rd-party-services',
|
||||||
component: OperatorInfo
|
component: Services
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: namespaces.WALLETS,
|
key: namespaces.WALLETS,
|
||||||
label: 'Wallet',
|
label: 'Wallet',
|
||||||
route: '/settings/wallet-settings',
|
route: '/settings/wallet-settings',
|
||||||
component: WalletSettings
|
component: WalletSettings
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: namespaces.OPERATOR_INFO,
|
||||||
|
label: 'Operator Info',
|
||||||
|
route: '/settings/operator-info',
|
||||||
|
component: OperatorInfo
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@
|
||||||
<title>icon/action/arrow/regular</title>
|
<title>icon/action/arrow/regular</title>
|
||||||
<desc>Created with Sketch.</desc>
|
<desc>Created with Sketch.</desc>
|
||||||
<defs>
|
<defs>
|
||||||
<path d="M5.3501239,7.53208616 L0.473798314,2.73082122 C-0.158421727,2.1051411 -0.158421727,1.0952488 0.476737158,0.466675069 C1.11220338,-0.155816755 2.1378971,-0.155816755 2.77494316,0.468226909 L6.49990857,4.13723769 L10.2264532,0.466675069 C10.8619195,-0.155816755 11.8876132,-0.155816755 12.5260183,0.469568675 C13.1582383,1.0952488 13.1582383,2.1051411 12.5245507,2.73226987 L7.64673876,7.53497972 C7.33802629,7.83583835 6.92590837,8 6.49990828,8 C6.0739082,8 5.66179027,7.83583835 5.3501239,7.53208616 Z" id="path-1"></path>
|
<path id="arrow-path" d="M5.3501239,7.53208616 L0.473798314,2.73082122 C-0.158421727,2.1051411 -0.158421727,1.0952488 0.476737158,0.466675069 C1.11220338,-0.155816755 2.1378971,-0.155816755 2.77494316,0.468226909 L6.49990857,4.13723769 L10.2264532,0.466675069 C10.8619195,-0.155816755 11.8876132,-0.155816755 12.5260183,0.469568675 C13.1582383,1.0952488 13.1582383,2.1051411 12.5245507,2.73226987 L7.64673876,7.53497972 C7.33802629,7.83583835 6.92590837,8 6.49990828,8 C6.0739082,8 5.66179027,7.83583835 5.3501239,7.53208616 Z"></path>
|
||||||
</defs>
|
</defs>
|
||||||
<g id="Styleguide" stroke="none" stroke-width="1" fill-rule="evenodd">
|
<g id="Styleguide" stroke="none" stroke-width="1" fill-rule="evenodd">
|
||||||
<g id="icon/action/arrow/regular">
|
<g id="icon/action/arrow/regular">
|
||||||
<mask id="mask-2" fill="white">
|
<mask id="mask-2" fill="white">
|
||||||
<use xlink:href="#path-1"></use>
|
<use xlink:href="#path-1"></use>
|
||||||
</mask>
|
</mask>
|
||||||
<use id="Mask" fill-rule="nonzero" xlink:href="#path-1"></use>
|
<use id="Mask" fill-rule="nonzero" xlink:href="#arrow-path"></use>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
|
@ -1,15 +1,15 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<svg
|
||||||
<svg width="13px" height="8px" viewBox="0 0 13 8" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
width="13"
|
||||||
<!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
|
height="8"
|
||||||
<title>icon/action/arrow/regular/zodiac</title>
|
viewBox="0 0 13 8"
|
||||||
<desc>Created with Sketch.</desc>
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
<defs>
|
<defs>
|
||||||
<path d="M5.3501239,7.53208616 L0.473798314,2.73082122 C-0.158421727,2.1051411 -0.158421727,1.0952488 0.476737158,0.466675069 C1.11220338,-0.155816755 2.1378971,-0.155816755 2.77494316,0.468226909 L6.49990857,4.13723769 L10.2264532,0.466675069 C10.8619195,-0.155816755 11.8876132,-0.155816755 12.5260183,0.469568675 C13.1582383,1.0952488 13.1582383,2.1051411 12.5245507,2.73226987 L7.64673876,7.53497972 C7.33802629,7.83583835 6.92590837,8 6.49990828,8 C6.0739082,8 5.66179027,7.83583835 5.3501239,7.53208616 Z" id="path-1"></path>
|
<path id="path-2" d="M5.4 7.5L0.5 2.7C-0.2 2.1-0.2 1.1 0.5 0.5 1.1-0.2 2.1-0.2 2.8 0.5L6.5 4.1 10.2 0.5C10.9-0.2 11.9-0.2 12.5 0.5 13.2 1.1 13.2 2.1 12.5 2.7L7.6 7.5C7.3 7.8 6.9 8 6.5 8 6.1 8 5.7 7.8 5.4 7.5Z" />
|
||||||
</defs>
|
</defs>
|
||||||
<g id="icon/action/arrow/regular/zodiac" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
<g fill="none">
|
||||||
<mask id="mask-2" fill="white">
|
<mask fill="white">
|
||||||
<use xlink:href="#path-1"></use>
|
<use xlink:href="#path-1" />
|
||||||
</mask>
|
</mask>
|
||||||
<use id="Mask" fill="#1B2559" fill-rule="nonzero" xlink:href="#path-1"></use>
|
<use xlink:href="#path-1" fill="#1B2559" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 458 B |
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
|
||||||
|
<title>icon/sf-small/share/zodiac</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<g id="icon/sf-small/share/zodiac" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<g id="share" transform="translate(4.000000, 4.000000)" stroke="white">
|
||||||
|
<polygon id="Combined-Shape" points="7.57894737 12 12 0 0 3.52941176 5.05263158 6.35294118"></polygon>
|
||||||
|
<path d="M5.05263158,6.35294118 L12,-1.60760294e-13 L5.05263158,6.35294118 Z" id="Stroke-3"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 818 B |
43
new-lamassu-admin/src/utils/apollo.js
Normal file
43
new-lamassu-admin/src/utils/apollo.js
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { InMemoryCache } from 'apollo-cache-inmemory'
|
||||||
|
import { ApolloClient } from 'apollo-client'
|
||||||
|
import { ApolloLink } from 'apollo-link'
|
||||||
|
import { onError } from 'apollo-link-error'
|
||||||
|
import { HttpLink } from 'apollo-link-http'
|
||||||
|
|
||||||
|
const URI =
|
||||||
|
process.env.NODE_ENV === 'development' ? 'https://localhost:8070' : ''
|
||||||
|
|
||||||
|
const client = new ApolloClient({
|
||||||
|
link: ApolloLink.from([
|
||||||
|
onError(({ graphQLErrors, networkError }) => {
|
||||||
|
if (graphQLErrors)
|
||||||
|
graphQLErrors.forEach(({ message, locations, path }) =>
|
||||||
|
console.log(
|
||||||
|
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (networkError) console.log(`[Network error]: ${networkError}`)
|
||||||
|
}),
|
||||||
|
new HttpLink({
|
||||||
|
credentials: 'include',
|
||||||
|
uri: `${URI}/graphql`
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
cache: new InMemoryCache(),
|
||||||
|
defaultOptions: {
|
||||||
|
watchQuery: {
|
||||||
|
fetchPolicy: 'no-cache',
|
||||||
|
errorPolicy: 'ignore'
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
fetchPolicy: 'no-cache',
|
||||||
|
errorPolicy: 'all'
|
||||||
|
},
|
||||||
|
mutate: {
|
||||||
|
errorPolicy: 'all'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default client
|
||||||
|
export { URI }
|
||||||
|
|
@ -1,28 +1,74 @@
|
||||||
UI:
|
Main menu:
|
||||||
- Large input fields wiggle on editable table edit/non-edit mode change.
|
- do not change fonts on hover in the main menu
|
||||||
- all (machines/coins/...) should be a option on some overrides
|
- make the clickable button bigger, not just text
|
||||||
|
|
||||||
Compliance:
|
Overall:
|
||||||
- Reject Address Reuse missing
|
- caching the page
|
||||||
|
- coin dropdown should show all coins
|
||||||
|
- validation is bad rn, negatives being allowed
|
||||||
|
- input number should only allow numbers
|
||||||
|
- right aligned numbers on all tables
|
||||||
|
- locale based mil separators 1.000 1,000
|
||||||
|
|
||||||
|
Cashboxes:
|
||||||
|
- right aligned number (SAME EVERYWHERE)
|
||||||
|
|
||||||
|
UI:
|
||||||
|
- replace all the tooltips with new component
|
||||||
|
- tooltip like components should close on esc
|
||||||
|
- saving should be a one time thing. disable buttons so user doesnt spam it
|
||||||
|
- transitions
|
||||||
|
- error handling
|
||||||
|
- should all (machines/coins/...) be a option on some overrides?
|
||||||
|
- select components
|
||||||
|
- talk with nunu + neal: Hover css for edit buttons + first first cancel later
|
||||||
|
- filter countries by code as well, US should go to United States
|
||||||
|
- filter prioritize the start of words(not alphabetically)
|
||||||
|
- dropdown should have everythihg selected on the top
|
||||||
|
- disable edit on non-everrides => overrides
|
||||||
|
- remove the broswer default tooltip
|
||||||
|
|
||||||
|
|
||||||
|
Machine status:
|
||||||
|
- legend colors are different from the spec
|
||||||
|
- action Error/Success indication
|
||||||
|
- load machine model from l-m
|
||||||
|
- align popup title with content
|
||||||
|
- talk with neal to see if the actions should be consistent
|
||||||
|
- font-size of the 'write to confirm'
|
||||||
|
- reboot icon cut off
|
||||||
|
- ask neal for the support articles
|
||||||
|
- stop line breaking on multi select
|
||||||
|
|
||||||
|
Commissions:
|
||||||
|
- overrides can be tighter. Hide coins already used by the same machine on another line.
|
||||||
|
- no negative values
|
||||||
|
- autoselect not getting errored when tabbed out
|
||||||
|
|
||||||
|
Operator Info:
|
||||||
|
- That should be paginated with routes!
|
||||||
|
|
||||||
|
Terms and Conditions:
|
||||||
|
- default values are not working properly
|
||||||
|
|
||||||
|
Contact information:
|
||||||
|
- When the fields are empty, should there be a warning somewhere? Or maybe we could create an exception that if the fields are empty they shouldn't show up
|
||||||
|
- l-m uses name, email, phone. The rest is just used for the receipt printing for now
|
||||||
|
|
||||||
CoinATMRadar:
|
CoinATMRadar:
|
||||||
- We now have photo, should we relay that info?
|
- We now have photo, should we relay that info?
|
||||||
|
|
||||||
Locale:
|
|
||||||
- should we show the user wallet needs to be configured after adding in a new coin?
|
|
||||||
- check if coin is active before considering it active on other screens
|
|
||||||
|
|
||||||
Commission:
|
|
||||||
- overrides can be tighter. Hide coins already used by the same machine on another line.
|
|
||||||
|
|
||||||
Sms/email:
|
Sms/email:
|
||||||
- There's no place to pick a third party provider anymore.
|
- There's no place to pick a third party provider anymore. (sms.js, email.js)
|
||||||
|
|
||||||
Cashout:
|
|
||||||
- I've just added a zero conf limit column. Should it be like this?
|
|
||||||
|
|
||||||
Notifications:
|
Notifications:
|
||||||
- cashInAlertThreshold missing, used to warn to full cashbox
|
- cash out 500 notes max top 500 max bottom
|
||||||
|
- crypto balance alerts input width (CHECK FOR ALL)
|
||||||
|
|
||||||
|
Locale:
|
||||||
|
- limit languages
|
||||||
|
- search crypto per name as well
|
||||||
|
- show full name on the dropdown
|
||||||
|
|
||||||
Machine name:
|
Machine name:
|
||||||
- Previously we were grabbing that from the config, but since new admin still cant change the name i`m now grabbing it from the db. Possible issues if users change the machine name from the initial one. Investivate alternatives.
|
- Previously we were grabbing that from the config, but since new admin still cant change the name i`m now grabbing it from the db. Possible issues if users change the machine name from the initial one. Investivate alternatives.
|
||||||
|
|
@ -33,9 +79,32 @@ Migrate:
|
||||||
- remove apply defaults
|
- remove apply defaults
|
||||||
|
|
||||||
Compliance:
|
Compliance:
|
||||||
|
- Reject Address Reuse missing
|
||||||
- Currently admin only handles { type: 'volume', direction: 'both' }
|
- Currently admin only handles { type: 'volume', direction: 'both' }
|
||||||
- Sanctions should have more care in customers.js, currently just looking if is active as if old config
|
- Sanctions should have more care in customers.js, currently just looking if is active as if old config
|
||||||
|
|
||||||
Other stuff:
|
Customers:
|
||||||
- sms.js
|
- Should add id and make it main part of the table? Name is not common at all
|
||||||
- email.js
|
|
||||||
|
Logs:
|
||||||
|
- the new functionality that saves server logs to a db breaks initial install chicken-egg with db-logger
|
||||||
|
|
||||||
|
Downloading (logs and tx):
|
||||||
|
- They are always downloading from the local data, should be from server
|
||||||
|
|
||||||
|
Cash out:
|
||||||
|
- On off should have a fixed sized so things dont move a lot
|
||||||
|
- separate text from the first screen
|
||||||
|
- auto focus on fields after clicking next
|
||||||
|
- improve spacing around paragraphs
|
||||||
|
- button is on a wrong place on steps 2 and 3
|
||||||
|
- make it a dropdown based on the machine denomimnations settings
|
||||||
|
- ask nuno about zero conf limit
|
||||||
|
- USD should show as a suffix (validate all screens)
|
||||||
|
- Splash image for wizard
|
||||||
|
|
||||||
|
Server:
|
||||||
|
- Takes too long to load. Investigate
|
||||||
|
|
||||||
|
Review slow internet loading:
|
||||||
|
- Table should be loaded
|
||||||
Loading…
Add table
Add a link
Reference in a new issue