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"
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "walletSettings",
|
||||
"fields": [
|
||||
"ticker",
|
||||
"wallet",
|
||||
"layer2",
|
||||
"exchange",
|
||||
"zeroConf"
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "notifications",
|
||||
"fields": [
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ function post (machineTx, pi) {
|
|||
.then(([{ config }, blacklistItems]) => {
|
||||
// TODO new-admin: addressReuse doesnt exist
|
||||
// const rejectAddressReuseActive = configManager.unscoped(config).rejectAddressReuseActive
|
||||
const rejectAddressReuseActive = true
|
||||
const rejectAddressReuseActive = false
|
||||
|
||||
if (_.some(it => it.created_by_operator === true)(blacklistItems)) {
|
||||
blacklisted = true
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ function mapCoin (rates, deviceId, settings, cryptoCode) {
|
|||
|
||||
const cashInFee = showCommissions ? commissions.cashIn / 100 : null
|
||||
const cashOutFee = showCommissions ? commissions.cashOut / 100 : null
|
||||
const cashInFixedFee = showCommissions ? commissions.fixedFee : null
|
||||
const cashInRate = showCommissions ? _.invoke('cashIn.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
|
||||
// need to get in touch with coinatmradar before updating this
|
||||
// TODO all directions and max between them instead of min
|
||||
const cashLimit = showLimitsAndVerification ? (
|
||||
!!compatTriggers.block
|
||||
? compatTriggers.block
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
const _ = require('lodash/fp')
|
||||
|
||||
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)
|
||||
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,
|
||||
count(0) over (partition by c.id) as total_txs,
|
||||
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
|
||||
from cash_in_txs where send_confirmed = true union
|
||||
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.query) {
|
||||
logger.error(e.query)
|
||||
logger.error(e.params)
|
||||
e.params && logger.error(e.params)
|
||||
}
|
||||
logger.error(err)
|
||||
}
|
||||
|
|
@ -21,11 +21,12 @@ const db = pgp(psqlUrl)
|
|||
|
||||
eventBus.subscribe('log', args => {
|
||||
const { level, message, meta } = args
|
||||
const msgToSave = message ? message : _.get('message', meta)
|
||||
|
||||
const sql = `insert into server_logs
|
||||
(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))
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
const _ = require('lodash/fp')
|
||||
const axios = require('axios')
|
||||
|
||||
const logger = require('./logger')
|
||||
const db = require('./db')
|
||||
const pairing = require('./pairing')
|
||||
const configManager = require('./new-config-manager')
|
||||
|
|
@ -15,8 +16,10 @@ function getMachines () {
|
|||
cashbox: r.cashbox,
|
||||
cassette1: r.cassette1,
|
||||
cassette2: r.cassette2,
|
||||
pairedAt: new Date(r.created).valueOf(),
|
||||
lastPing: new Date(r.last_online).valueOf(),
|
||||
version: r.version,
|
||||
model: r.model,
|
||||
pairedAt: new Date(r.created),
|
||||
lastPing: new Date(r.last_online),
|
||||
name: r.name,
|
||||
// TODO: we shall start using this JSON field at some point
|
||||
// location: r.location,
|
||||
|
|
@ -36,19 +39,12 @@ function getMachineNames (config) {
|
|||
const addName = r => {
|
||||
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
|
||||
const machineModel = ''
|
||||
const machineLocation = ''
|
||||
|
||||
// TODO: obtain next fields from somewhere
|
||||
const printer = null
|
||||
const pingTime = null
|
||||
// TODO new-admin actually load status based on ping.
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
const _ = require('lodash/fp')
|
||||
const { COINS, ALL_CRYPTOS } = require('./coins')
|
||||
|
||||
const { BTC, BCH, DASH, ETH, LTC, ZEC } = COINS
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const configManager = require('../new-config-manager')
|
|||
const wallet = require('../wallet')
|
||||
const ticker = require('../ticker')
|
||||
const coinUtils = require('../coin-utils')
|
||||
const logger = require('../logger')
|
||||
|
||||
function allScopes (cryptoScopes, machineScopes) {
|
||||
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 () {
|
||||
return settingsLoader.loadLatest().then(settings => {
|
||||
const cryptoCodes = configManager.getAllCryptoCurrencies(settings.config)
|
||||
|
|
@ -77,9 +81,10 @@ function getFunding () {
|
|||
const cryptoDisplays = _.filter(pareCoins, cryptoCurrencies)
|
||||
|
||||
const promises = cryptoDisplays.map(it => getSingleCoinFunding(settings, fiatCode, it.cryptoCode))
|
||||
return Promise.all(promises)
|
||||
return Promise.all(promises.map(reflect))
|
||||
.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 { accounts: accountsConfig, coins, countries, currencies, languages } = require('../config')
|
||||
|
||||
// TODO why does server logs messages can be null?
|
||||
const typeDefs = gql`
|
||||
scalar JSON
|
||||
scalar JSONObject
|
||||
|
|
@ -54,6 +53,10 @@ const typeDefs = gql`
|
|||
name: String!
|
||||
deviceId: ID!
|
||||
paired: Boolean!
|
||||
lastPing: Date
|
||||
pairedAt: Date
|
||||
version: String
|
||||
model: String
|
||||
cashbox: Int
|
||||
cassette1: Int
|
||||
cassette2: Int
|
||||
|
|
@ -123,21 +126,22 @@ const typeDefs = gql`
|
|||
|
||||
type CoinFunds {
|
||||
cryptoCode: String!
|
||||
fundingAddress: String!
|
||||
fundingAddressUrl: String!
|
||||
confirmedBalance: String!
|
||||
pending: String!
|
||||
fiatConfirmedBalance: String!
|
||||
fiatPending: String!
|
||||
fiatCode: String!
|
||||
display: String!
|
||||
unitScale: String!
|
||||
errorMsg: String
|
||||
fundingAddress: String
|
||||
fundingAddressUrl: String
|
||||
confirmedBalance: String
|
||||
pending: String
|
||||
fiatConfirmedBalance: String
|
||||
fiatPending: String
|
||||
fiatCode: String
|
||||
display: String
|
||||
unitScale: String
|
||||
}
|
||||
|
||||
type ProcessStatus {
|
||||
name: String!
|
||||
state: String!
|
||||
uptime: Date!
|
||||
uptime: Int!
|
||||
}
|
||||
|
||||
type Transaction {
|
||||
|
|
@ -156,6 +160,7 @@ const typeDefs = gql`
|
|||
created: Date
|
||||
send: Boolean
|
||||
sendConfirmed: Boolean
|
||||
dispense: Boolean
|
||||
timedout: Boolean
|
||||
sendTime: Date
|
||||
errorCode: String
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@ const xmlrpc = require('xmlrpc')
|
|||
const logger = require('../logger')
|
||||
const { promisify } = require('util')
|
||||
|
||||
// TODO new-admin: add the following to supervisor config
|
||||
// [inet_http_server]
|
||||
// port = 127.0.0.1:9001
|
||||
|
||||
function getAllProcessInfo () {
|
||||
const convertStates = (state) => {
|
||||
// From http://supervisord.org/subprocess.html#process-states
|
||||
|
|
@ -45,7 +49,7 @@ function getAllProcessInfo () {
|
|||
{
|
||||
name: process.name,
|
||||
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')
|
||||
let db = null
|
||||
|
||||
// TODO new-admin save to actual db, like the old settings
|
||||
low(adapter).then(it => {
|
||||
db = it
|
||||
})
|
||||
|
|
@ -54,6 +55,7 @@ function loadLatest () {
|
|||
})
|
||||
}
|
||||
|
||||
// TODO new-admin: grab correct version
|
||||
function load (versionId) {
|
||||
return new Promise((resolve) => {
|
||||
if (!db) {
|
||||
|
|
|
|||
|
|
@ -185,7 +185,6 @@ function plugins (settings, deviceId) {
|
|||
const commissions = configManager.getCommissions(cryptoCode, deviceId, settings.config)
|
||||
const minimumTx = BN(commissions.minimumTx)
|
||||
const cashInFee = BN(commissions.fixedFee)
|
||||
logger.info('FEE', cashInFee)
|
||||
const cashInCommission = BN(commissions.cashIn)
|
||||
const cashOutCommission = _.isNumber(commissions.cashOut) ? BN(commissions.cashOut) : null
|
||||
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 fiatCode = localeConfig.fiatCurrency
|
||||
|
|
@ -210,7 +209,7 @@ function plugins (settings, deviceId) {
|
|||
const tickerPromises = cryptoCodes.map(c => ticker.getRates(settings, fiatCode, c))
|
||||
const balancePromises = cryptoCodes.map(c => fiatBalance(fiatCode, c))
|
||||
const testnetPromises = cryptoCodes.map(c => wallet.cryptoNetwork(settings, c))
|
||||
const pingPromise = recordPing(deviceTime)
|
||||
const pingPromise = recordPing(deviceTime, machineVersion, machineModel)
|
||||
const currentConfigVersionPromise = fetchCurrentConfigVersion()
|
||||
|
||||
const promises = [
|
||||
|
|
@ -244,8 +243,10 @@ function plugins (settings, deviceId) {
|
|||
return wallet.sendCoins(settings, tx.toAddress, tx.cryptoAtoms, tx.cryptoCode)
|
||||
}
|
||||
|
||||
function recordPing (deviceTime) {
|
||||
function recordPing (deviceTime, version, model) {
|
||||
const devices = {
|
||||
version,
|
||||
model,
|
||||
last_online: deviceTime
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ const logger = require('./logger')
|
|||
const configManager = require('./new-config-manager')
|
||||
const complianceTriggers = require('./compliance-triggers')
|
||||
const pairing = require('./pairing')
|
||||
// TODO new-admin: remove old settings loader from here.
|
||||
const newSettingsLoader = require('./new-settings-loader')
|
||||
const plugins = require('./plugins')
|
||||
const helpers = require('./route-helpers')
|
||||
|
|
@ -51,6 +50,8 @@ function checkHasLightning (settings) {
|
|||
|
||||
function poll (req, res, next) {
|
||||
const machineVersion = req.query.version
|
||||
// TODO new-admin: pass this field from the machines
|
||||
const machineModel = req.query.model
|
||||
const deviceId = req.deviceId
|
||||
const deviceTime = req.deviceTime
|
||||
const serialNumber = req.query.sn
|
||||
|
|
@ -64,13 +65,12 @@ function poll (req, res, next) {
|
|||
const compatTriggers = complianceTriggers.getBackwardsCompatibleTriggers(triggers)
|
||||
|
||||
const operatorInfo = configManager.getOperatorInfo(settings.config)
|
||||
const terms = configManager.getTermsConditions(settings.config)
|
||||
const cashOutConfig = configManager.getCashOut(deviceId, settings.config)
|
||||
const receipt = configManager.getReceipt(settings.config)
|
||||
|
||||
pids[deviceId] = { pid, ts: Date.now() }
|
||||
|
||||
return pi.pollQueries(serialNumber, deviceTime, req.query)
|
||||
return pi.pollQueries(serialNumber, deviceTime, req.query, machineVersion, machineModel)
|
||||
.then(results => {
|
||||
const cassettes = results.cassettes
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ function poll (req, res, next) {
|
|||
sanctionsVerificationThreshold: compatTriggers.sancations,
|
||||
frontCameraVerificationActive: !!compatTriggers.facephoto,
|
||||
frontCameraVerificationThreshold: compatTriggers.facephoto,
|
||||
receiptPrintingActive: receipt.active,
|
||||
receiptPrintingActive: receipt.active === "on",
|
||||
cassettes,
|
||||
twoWayMode: cashOutConfig.active,
|
||||
zeroConfLimit: cashOutConfig.zeroConfLimit,
|
||||
|
|
@ -389,7 +389,7 @@ const localApp = express()
|
|||
app.use(compression({ threshold: 500 }))
|
||||
app.use(helmet({ noCache: true }))
|
||||
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
|
||||
app.post('/pair', populateDeviceId, pair)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ function _getRates (settings, fiatCode, cryptoCode) {
|
|||
const config = settings.config
|
||||
const plugin = configManager.getWalletSettings(cryptoCode, config).ticker
|
||||
|
||||
logger.info(plugin)
|
||||
const account = settings.accounts[plugin]
|
||||
const ticker = ph.load(ph.TICKER, plugin)
|
||||
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ function mergeStatusMode (a, b) {
|
|||
}
|
||||
|
||||
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 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": {
|
||||
"version": "1.6.5",
|
||||
"resolved": "https://registry.npmjs.org/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.5.tgz",
|
||||
"integrity": "sha512-koB76JUDJaycfejHmrXBbWIN9pRKM0Z9CJGQcBzIOtmte1JhEBSuzsOUu7NQgiXKYI4iGoMREcnaWffsosZynA==",
|
||||
"version": "1.6.6",
|
||||
"resolved": "https://registry.npmjs.org/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.6.tgz",
|
||||
"integrity": "sha512-L8pToTW/+Xru2FFAhkZ1OA9q4V4nuvfoPecBM34DecAugUZEBhI2Hmpgnzq2hTKZ60LAMrlqiASm0aqAY6F8/A==",
|
||||
"requires": {
|
||||
"apollo-cache": "^1.3.4",
|
||||
"apollo-utilities": "^1.3.3",
|
||||
"apollo-cache": "^1.3.5",
|
||||
"apollo-utilities": "^1.3.4",
|
||||
"optimism": "^0.10.0",
|
||||
"ts-invariant": "^0.4.0",
|
||||
"tslib": "^1.10.0"
|
||||
},
|
||||
"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": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
|
||||
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
|
||||
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"apollo-client": {
|
||||
"version": "2.6.8",
|
||||
"resolved": "https://registry.npmjs.org/apollo-client/-/apollo-client-2.6.8.tgz",
|
||||
"integrity": "sha512-0zvJtAcONiozpa5z5zgou83iEKkBaXhhSSXJebFHRXs100SecDojyUWKjwTtBPn9HbM6o5xrvC5mo9VQ5fgAjw==",
|
||||
"version": "2.6.10",
|
||||
"resolved": "https://registry.npmjs.org/apollo-client/-/apollo-client-2.6.10.tgz",
|
||||
"integrity": "sha512-jiPlMTN6/5CjZpJOkGeUV0mb4zxx33uXWdj/xQCfAMkuNAC3HN7CvYDyMHHEzmcQ5GV12LszWoQ/VlxET24CtA==",
|
||||
"requires": {
|
||||
"@types/zen-observable": "^0.8.0",
|
||||
"apollo-cache": "1.3.4",
|
||||
"apollo-cache": "1.3.5",
|
||||
"apollo-link": "^1.0.0",
|
||||
"apollo-utilities": "1.3.3",
|
||||
"apollo-utilities": "1.3.4",
|
||||
"symbol-observable": "^1.0.2",
|
||||
"ts-invariant": "^0.4.0",
|
||||
"tslib": "^1.10.0",
|
||||
"zen-observable": "^0.8.0"
|
||||
},
|
||||
"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": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
|
||||
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
|
||||
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"apollo-link": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.13.tgz",
|
||||
"integrity": "sha512-+iBMcYeevMm1JpYgwDEIDt/y0BB7VWyvlm/7x+TIPNLHCTCMgcEgDuW5kH86iQZWo0I7mNwQiTOz+/3ShPFmBw==",
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.14.tgz",
|
||||
"integrity": "sha512-p67CMEFP7kOG1JZ0ZkYZwRDa369w5PIjtMjvrQd/HnIV8FRsHRqLqK+oAZQnFa1DDdZtOtHTi+aMIW6EatC2jg==",
|
||||
"requires": {
|
||||
"apollo-utilities": "^1.3.0",
|
||||
"ts-invariant": "^0.4.0",
|
||||
"tslib": "^1.9.3",
|
||||
"zen-observable-ts": "^0.8.20"
|
||||
"zen-observable-ts": "^0.8.21"
|
||||
}
|
||||
},
|
||||
"apollo-link-error": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/apollo-link-error/-/apollo-link-error-1.1.12.tgz",
|
||||
"integrity": "sha512-psNmHyuy3valGikt/XHJfe0pKJnRX19tLLs6P6EHRxg+6q6JMXNVLYPaQBkL0FkwdTCB0cbFJAGRYCBviG8TDA==",
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/apollo-link-error/-/apollo-link-error-1.1.13.tgz",
|
||||
"integrity": "sha512-jAZOOahJU6bwSqb2ZyskEK1XdgUY9nkmeclCrW7Gddh1uasHVqmoYc4CKdb0/H0Y1J9lvaXKle2Wsw/Zx1AyUg==",
|
||||
"requires": {
|
||||
"apollo-link": "^1.2.13",
|
||||
"apollo-link-http-common": "^0.2.15",
|
||||
"apollo-link": "^1.2.14",
|
||||
"apollo-link-http-common": "^0.2.16",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"apollo-link-http": {
|
||||
"version": "1.5.16",
|
||||
"resolved": "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.16.tgz",
|
||||
"integrity": "sha512-IA3xA/OcrOzINRZEECI6IdhRp/Twom5X5L9jMehfzEo2AXdeRwAMlH5LuvTZHgKD8V1MBnXdM6YXawXkTDSmJw==",
|
||||
"version": "1.5.17",
|
||||
"resolved": "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.17.tgz",
|
||||
"integrity": "sha512-uWcqAotbwDEU/9+Dm9e1/clO7hTB2kQ/94JYcGouBVLjoKmTeJTUPQKcJGpPwUjZcSqgYicbFqQSoJIW0yrFvg==",
|
||||
"requires": {
|
||||
"apollo-link": "^1.2.13",
|
||||
"apollo-link-http-common": "^0.2.15",
|
||||
"apollo-link": "^1.2.14",
|
||||
"apollo-link-http-common": "^0.2.16",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"apollo-link-http-common": {
|
||||
"version": "0.2.15",
|
||||
"resolved": "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.15.tgz",
|
||||
"integrity": "sha512-+Heey4S2IPsPyTf8Ag3PugUupASJMW894iVps6hXbvwtg1aHSNMXUYO5VG7iRHkPzqpuzT4HMBanCTXPjtGzxg==",
|
||||
"version": "0.2.16",
|
||||
"resolved": "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.16.tgz",
|
||||
"integrity": "sha512-2tIhOIrnaF4UbQHf7kjeQA/EmSorB7+HyJIIrUjJOKBgnXwuexi8aMecRlqTIDWcyVXCeqLhUnztMa6bOH/jTg==",
|
||||
"requires": {
|
||||
"apollo-link": "^1.2.13",
|
||||
"apollo-link": "^1.2.14",
|
||||
"ts-invariant": "^0.4.0",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
|
|
@ -11762,9 +11763,9 @@
|
|||
}
|
||||
},
|
||||
"graphql-tag": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.1.tgz",
|
||||
"integrity": "sha512-jApXqWBzNXQ8jYa/HLkZJaVw9jgwNqZkywa2zfFn16Iv1Zb7ELNHkJaXHR7Quvd5SIGsy6Ny7SUKATgnu05uEg=="
|
||||
"version": "2.10.3",
|
||||
"resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.3.tgz",
|
||||
"integrity": "sha512-4FOv3ZKfA4WdOKJeHdz6B3F/vxBLSgmBcGeAFPf4n1F64ltJUvOOerNj0rsJxONQGdhUMynQIvd6LzB+1J5oKA=="
|
||||
},
|
||||
"growly": {
|
||||
"version": "1.3.0",
|
||||
|
|
@ -26657,9 +26658,9 @@
|
|||
"integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ=="
|
||||
},
|
||||
"zen-observable-ts": {
|
||||
"version": "0.8.20",
|
||||
"resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.20.tgz",
|
||||
"integrity": "sha512-2rkjiPALhOtRaDX6pWyNqK1fnP5KkJJybYebopNSn6wDG1lxBoFs2+nwwXKoA6glHIrtwrfBBy6da0stkKtTAA==",
|
||||
"version": "0.8.21",
|
||||
"resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.21.tgz",
|
||||
"integrity": "sha512-Yj3yXweRc8LdRMrCC8nIc4kkjWecPAUVh0TI0OUrWXx6aX790vLcDlWca6I4vsyCGH3LpWxq0dJRcMOFoVqmeg==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.3",
|
||||
"zen-observable": "^0.8.0"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,11 @@
|
|||
"@material-ui/icons": "4.9.1",
|
||||
"@material-ui/lab": "^4.0.0-alpha.47",
|
||||
"@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",
|
||||
"bignumber.js": "9.0.0",
|
||||
"classnames": "2.2.6",
|
||||
|
|
@ -17,6 +21,7 @@
|
|||
"formik": "2.1.4",
|
||||
"fuse.js": "^3.4.6",
|
||||
"graphql": "^14.5.8",
|
||||
"graphql-tag": "^2.10.3",
|
||||
"jss-plugin-extend": "^10.0.0",
|
||||
"libphonenumber-js": "^1.7.50",
|
||||
"moment": "2.24.0",
|
||||
|
|
|
|||
|
|
@ -6,42 +6,20 @@ import {
|
|||
MuiThemeProvider,
|
||||
makeStyles
|
||||
} from '@material-ui/core/styles'
|
||||
import ApolloClient from 'apollo-boost'
|
||||
import { setAutoFreeze } from 'immer'
|
||||
import { create } from 'jss'
|
||||
import extendJss from 'jss-plugin-extend'
|
||||
import React from 'react'
|
||||
import { BrowserRouter as Router } from 'react-router-dom'
|
||||
|
||||
import client from 'src/utils/apollo'
|
||||
|
||||
import Header from './components/layout/Header'
|
||||
import { tree, Routes } from './routing/routes'
|
||||
import global from './styling/global'
|
||||
import theme from './styling/theme'
|
||||
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') {
|
||||
const whyDidYouRender = require('@welldone-software/why-did-you-render')
|
||||
whyDidYouRender(React)
|
||||
|
|
|
|||
|
|
@ -3,41 +3,53 @@ import {
|
|||
DialogActions,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle as MuiDialogTitle,
|
||||
IconButton,
|
||||
makeStyles
|
||||
} from '@material-ui/core'
|
||||
import React, { useState, memo } from 'react'
|
||||
import React, { useEffect, useState, memo } from 'react'
|
||||
|
||||
import { Button } from '../components/buttons'
|
||||
import { ReactComponent as CloseIcon } from '../styling/icons/action/close/zodiac.svg'
|
||||
import { spacer } from '../styling/variables'
|
||||
import { Button, IconButton } from 'src/components/buttons'
|
||||
import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.svg'
|
||||
|
||||
import { TextInput } from './inputs'
|
||||
import { H4, P } from './typography'
|
||||
|
||||
const useStyles = makeStyles({
|
||||
label: {
|
||||
fontSize: 16
|
||||
},
|
||||
spacing: {
|
||||
padding: 32
|
||||
},
|
||||
wrapper: {
|
||||
display: 'flex'
|
||||
},
|
||||
title: {
|
||||
margin: [[20, 0, 24, 16]]
|
||||
},
|
||||
closeButton: {
|
||||
position: 'absolute',
|
||||
right: spacer,
|
||||
top: spacer
|
||||
padding: 0,
|
||||
margin: [[12, 12, 'auto', 'auto']]
|
||||
// position: 'absolute',
|
||||
// right: spacer,
|
||||
// top: spacer
|
||||
}
|
||||
})
|
||||
|
||||
export const DialogTitle = ({ children, onClose }) => {
|
||||
const classes = useStyles()
|
||||
return (
|
||||
<MuiDialogTitle>
|
||||
<div className={classes.wrapper}>
|
||||
{children}
|
||||
{onClose && (
|
||||
<IconButton
|
||||
size={16}
|
||||
aria-label="close"
|
||||
className={classes.closeButton}
|
||||
onClick={onClose}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</MuiDialogTitle>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -49,9 +61,12 @@ export const ConfirmDialog = memo(
|
|||
toBeConfirmed,
|
||||
onConfirmed,
|
||||
onDissmised,
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
const classes = useStyles()
|
||||
const [value, setValue] = useState('')
|
||||
useEffect(() => setValue(''), [open])
|
||||
const handleChange = event => {
|
||||
setValue(event.target.value)
|
||||
}
|
||||
|
|
@ -59,14 +74,14 @@ export const ConfirmDialog = memo(
|
|||
return (
|
||||
<Dialog open={open} aria-labelledby="form-dialog-title" {...props}>
|
||||
<DialogTitle id="customized-dialog-title" onClose={onDissmised}>
|
||||
<H4>{title}</H4>
|
||||
<H4 className={classes.title}>{title}</H4>
|
||||
{subtitle && (
|
||||
<DialogContentText>
|
||||
<P>{subtitle}</P>
|
||||
</DialogContentText>
|
||||
)}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContent className={className}>
|
||||
<TextInput
|
||||
label={`Write '${toBeConfirmed}' to confirm`}
|
||||
name="confirm-input"
|
||||
|
|
@ -78,11 +93,11 @@ export const ConfirmDialog = memo(
|
|||
value={value}
|
||||
touched={{}}
|
||||
error={toBeConfirmed !== value}
|
||||
InputLabelProps={{ shrink: true }}
|
||||
InputLabelProps={{ shrink: true, className: classes.label }}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<DialogActions classes={{ spacing: classes.spacing }}>
|
||||
<Button
|
||||
color="green"
|
||||
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 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 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 Popper from './Popper'
|
||||
import { Link } from './buttons'
|
||||
import DateRangePicker from './date-range-picker/DateRangePicker'
|
||||
import { RadioGroup } from './inputs'
|
||||
import typographyStyles from './typography/styles'
|
||||
|
|
@ -123,30 +125,26 @@ const styles = {
|
|||
}
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
const ALL = 'all'
|
||||
const RANGE = 'range'
|
||||
|
||||
const LogsDownloaderPopover = ({
|
||||
id,
|
||||
name,
|
||||
open,
|
||||
anchorEl,
|
||||
getTimestamp,
|
||||
logs,
|
||||
title
|
||||
}) => {
|
||||
const radioButtonAll = 'all'
|
||||
const radioButtonRange = 'range'
|
||||
|
||||
const [selectedRadio, setSelectedRadio] = useState(radioButtonAll)
|
||||
const [range, setRange] = useState(null)
|
||||
const LogsDownloaderPopover = ({ name, getTimestamp, logs, title }) => {
|
||||
const [selectedRadio, setSelectedRadio] = useState(ALL)
|
||||
const [range, setRange] = useState({ from: null, to: null })
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
|
||||
const classes = useStyles()
|
||||
|
||||
const dateRangePickerClasses = {
|
||||
[classes.dateRangePickerShowing]: selectedRadio === radioButtonRange,
|
||||
[classes.dateRangePickerHidden]: selectedRadio === radioButtonAll
|
||||
[classes.dateRangePickerShowing]: selectedRadio === RANGE,
|
||||
[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(
|
||||
(from, to) => {
|
||||
|
|
@ -164,7 +162,7 @@ const LogsDownloaderPopover = ({
|
|||
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 blob = new window.Blob([text], {
|
||||
type: 'text/plain;charset=utf-8'
|
||||
|
|
@ -173,7 +171,7 @@ const LogsDownloaderPopover = ({
|
|||
return
|
||||
}
|
||||
|
||||
if (selectedRadio === radioButtonRange) {
|
||||
if (selectedRadio === RANGE) {
|
||||
const text = logs
|
||||
.filter(log =>
|
||||
moment(getTimestamp(log)).isBetween(range.from, range.to, 'day', '[]')
|
||||
|
|
@ -190,12 +188,27 @@ const LogsDownloaderPopover = ({
|
|||
}
|
||||
}
|
||||
|
||||
const handleOpenRangePicker = event => {
|
||||
setAnchorEl(anchorEl ? null : event.currentTarget)
|
||||
}
|
||||
|
||||
const radioButtonOptions = [
|
||||
{ display: 'All logs', code: radioButtonAll },
|
||||
{ display: 'Date range', code: radioButtonRange }
|
||||
{ display: 'All logs', code: ALL },
|
||||
{ display: 'Date range', code: RANGE }
|
||||
]
|
||||
|
||||
const open = Boolean(anchorEl)
|
||||
const id = open ? 'date-range-popover' : undefined
|
||||
|
||||
return (
|
||||
<>
|
||||
<FeatureButton
|
||||
Icon={Download}
|
||||
InverseIcon={DownloadInverseIcon}
|
||||
aria-describedby={id}
|
||||
variant="contained"
|
||||
onClick={handleOpenRangePicker}
|
||||
/>
|
||||
<Popper id={id} open={open} anchorEl={anchorEl} placement="bottom">
|
||||
<div className={classes.popoverContent}>
|
||||
<div className={classes.popoverHeader}>{title}</div>
|
||||
|
|
@ -209,7 +222,7 @@ const LogsDownloaderPopover = ({
|
|||
className={classes.radioButtons}
|
||||
/>
|
||||
</div>
|
||||
{selectedRadio === radioButtonRange && (
|
||||
{selectedRadio === RANGE && (
|
||||
<div className={classnames(dateRangePickerClasses)}>
|
||||
<div className={classes.dateContainerWrapper}>
|
||||
{range && (
|
||||
|
|
@ -235,6 +248,7 @@ const LogsDownloaderPopover = ({
|
|||
</div>
|
||||
</div>
|
||||
</Popper>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import classnames from 'classnames'
|
|||
import React from 'react'
|
||||
|
||||
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'
|
||||
|
||||
const styles = {
|
||||
|
|
@ -18,29 +18,29 @@ const styles = {
|
|||
height,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
minHeight: 400,
|
||||
minHeight: height ?? 400,
|
||||
maxHeight: '90vh',
|
||||
overflowY: 'auto',
|
||||
borderRadius: 8,
|
||||
outline: 0
|
||||
}),
|
||||
content: {
|
||||
content: ({ small }) => ({
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flex: 1,
|
||||
padding: [[0, 32]]
|
||||
},
|
||||
button: {
|
||||
padding: small ? [[0, 16]] : [[0, 32]]
|
||||
}),
|
||||
button: ({ small }) => ({
|
||||
padding: 0,
|
||||
margin: [[20, 20, 'auto', 'auto']]
|
||||
},
|
||||
margin: small ? [[12, 12, 'auto', 'auto']] : [[16, 16, 'auto', 'auto']]
|
||||
}),
|
||||
header: {
|
||||
display: 'flex'
|
||||
},
|
||||
title: {
|
||||
margin: [[28, 0, 8, 32]]
|
||||
}
|
||||
title: ({ small }) => ({
|
||||
margin: small ? [[20, 0, 8, 16]] : [[28, 0, 8, 32]]
|
||||
})
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
|
@ -49,7 +49,7 @@ const Modal = ({
|
|||
width,
|
||||
height,
|
||||
title,
|
||||
titleSmall,
|
||||
small,
|
||||
infoPanel,
|
||||
handleClose,
|
||||
children,
|
||||
|
|
@ -58,9 +58,9 @@ const Modal = ({
|
|||
closeOnBackdropClick,
|
||||
...props
|
||||
}) => {
|
||||
const classes = useStyles({ width, height })
|
||||
const TitleCase = titleSmall ? H2 : H1
|
||||
const closeSize = titleSmall ? 16 : 20
|
||||
const classes = useStyles({ width, height, small })
|
||||
const TitleCase = small ? H4 : H1
|
||||
const closeSize = small ? 16 : 20
|
||||
|
||||
const innerClose = (evt, reason) => {
|
||||
if (!closeOnBackdropClick && reason === 'backdropClick') return
|
||||
|
|
|
|||
|
|
@ -5,7 +5,13 @@ import React, { useState } from 'react'
|
|||
|
||||
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 styles = {
|
||||
|
|
@ -27,7 +33,24 @@ const Popover = ({ children, bgColor = white, arrowSize = 7, ...props }) => {
|
|||
borderLeft: [['2em', 'solid', 'transparent']],
|
||||
borderRight: [['2em', 'solid', 'transparent']],
|
||||
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: {
|
||||
bottom: 0,
|
||||
|
|
@ -36,7 +59,24 @@ const Popover = ({ children, bgColor = white, arrowSize = 7, ...props }) => {
|
|||
borderLeft: [['2em', 'solid', 'transparent']],
|
||||
borderRight: [['2em', 'solid', 'transparent']],
|
||||
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: {
|
||||
left: 0,
|
||||
|
|
@ -81,6 +121,10 @@ const Popover = ({ children, bgColor = white, arrowSize = 7, ...props }) => {
|
|||
enabled: true,
|
||||
boundariesElement: 'scrollParent'
|
||||
},
|
||||
offset: {
|
||||
enabled: true,
|
||||
offset: '0, 10'
|
||||
},
|
||||
arrow: {
|
||||
enabled: true,
|
||||
element: arrowRef
|
||||
|
|
@ -94,7 +138,7 @@ const Popover = ({ children, bgColor = white, arrowSize = 7, ...props }) => {
|
|||
modifiers={modifiers}
|
||||
className={classes.popover}
|
||||
{...props}>
|
||||
<Paper className={classes.root}>
|
||||
<Paper className={classnames(classes.root, className)}>
|
||||
<span className={classnames(arrowClasses)} ref={setArrowRef} />
|
||||
{children}
|
||||
</Paper>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react'
|
||||
import Chip from '@material-ui/core/Chip'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import React from 'react'
|
||||
|
||||
import {
|
||||
tomato,
|
||||
|
|
@ -40,15 +40,21 @@ const useStyles = makeStyles({
|
|||
fontSize: smallestFontSize,
|
||||
fontWeight: inputFontWeight,
|
||||
fontFamily: inputFontFamily,
|
||||
paddingRight: spacer / 2,
|
||||
paddingLeft: spacer / 2,
|
||||
padding: [[spacer / 2, spacer]],
|
||||
color: ({ type }) => colors[type]
|
||||
}
|
||||
})
|
||||
|
||||
const Status = ({ status }) => {
|
||||
const Status = ({ status, className }) => {
|
||||
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 }) => {
|
||||
|
|
@ -59,7 +65,7 @@ const MainStatus = ({ statuses }) => {
|
|||
const plus = { label: `+${statuses.length - 1}`, type: mainStatus.type }
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{ marginLeft: -3 }}>
|
||||
<Status status={mainStatus} />
|
||||
{statuses.length > 1 && <Status status={plus} />}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { H1 } from './typography'
|
|||
|
||||
const useStyles = makeStyles({
|
||||
title: {
|
||||
marginTop: spacer * 3.5,
|
||||
marginTop: spacer * 3,
|
||||
marginBottom: spacer * 3
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import typographyStyles from 'src/components/typography/styles'
|
||||
import {
|
||||
white,
|
||||
fontColor,
|
||||
|
|
@ -6,7 +7,6 @@ import {
|
|||
offColor,
|
||||
offDarkColor
|
||||
} from 'src/styling/variables'
|
||||
import typographyStyles from 'src/components/typography/styles'
|
||||
|
||||
const { p } = typographyStyles
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ export default {
|
|||
extend: p,
|
||||
cursor: 'pointer',
|
||||
border: 'none',
|
||||
height: 24,
|
||||
height: 28,
|
||||
outline: 0,
|
||||
borderRadius: 6,
|
||||
padding: '0 8px',
|
||||
|
|
|
|||
|
|
@ -11,13 +11,19 @@ const styles = {
|
|||
extend: baseButton,
|
||||
width: baseButton.height,
|
||||
borderRadius: baseButton.height / 2,
|
||||
display: 'flex'
|
||||
display: 'flex',
|
||||
padding: 0
|
||||
},
|
||||
primary,
|
||||
buttonIcon: {
|
||||
margin: 'auto',
|
||||
'& svg': {
|
||||
overflow: 'visible'
|
||||
width: 16,
|
||||
height: 16,
|
||||
overflow: 'visible',
|
||||
'& g': {
|
||||
strokeWidth: 1.8
|
||||
}
|
||||
}
|
||||
},
|
||||
buttonIconActive: {} // required to extend primary
|
||||
|
|
@ -25,7 +31,8 @@ const styles = {
|
|||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const FeatureButton = memo(({ className, Icon, InverseIcon, ...props }) => {
|
||||
const FeatureButton = memo(
|
||||
({ className, Icon, InverseIcon, children, ...props }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
const classNames = {
|
||||
|
|
@ -42,12 +49,17 @@ const FeatureButton = memo(({ className, Icon, InverseIcon, ...props }) => {
|
|||
)}
|
||||
{InverseIcon && (
|
||||
<div
|
||||
className={classnames(classes.buttonIcon, classes.buttonIconActive)}>
|
||||
className={classnames(
|
||||
classes.buttonIcon,
|
||||
classes.buttonIconActive
|
||||
)}>
|
||||
<InverseIcon />
|
||||
</div>
|
||||
)}
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
export default FeatureButton
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ import { makeStyles } from '@material-ui/core/styles'
|
|||
import classnames from 'classnames'
|
||||
import React, { useState, memo } from 'react'
|
||||
|
||||
import Popover from 'src/components/Popper'
|
||||
import typographyStyles from 'src/components/typography/styles'
|
||||
import {
|
||||
subheaderColor,
|
||||
subheaderDarkColor,
|
||||
offColor
|
||||
} from 'src/styling/variables'
|
||||
import Popover from 'src/components/Popper'
|
||||
import typographyStyles from 'src/components/typography/styles'
|
||||
|
||||
const { info2 } = typographyStyles
|
||||
|
||||
|
|
@ -70,6 +70,7 @@ const IDButton = memo(
|
|||
InverseIcon,
|
||||
popoverWidth = 152,
|
||||
children,
|
||||
popoverClassname,
|
||||
...props
|
||||
}) => {
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
|
|
@ -117,6 +118,7 @@ const IDButton = memo(
|
|||
)}
|
||||
</button>
|
||||
<Popover
|
||||
className={popoverClassname}
|
||||
id={id}
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import React, { memo } from 'react'
|
|||
|
||||
import baseButtonStyles from './BaseButton.styles'
|
||||
|
||||
const { baseButton } = baseButtonStyles
|
||||
const { baseButton, primary } = baseButtonStyles
|
||||
|
||||
const styles = {
|
||||
button: {
|
||||
|
|
@ -12,19 +12,48 @@ const styles = {
|
|||
borderRadius: baseButton.height / 2,
|
||||
outline: 0,
|
||||
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 SimpleButton = memo(({ className, children, color, size, ...props }) => {
|
||||
const SimpleButton = memo(
|
||||
({ className, Icon, InverseIcon, children, color, size, ...props }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<button className={classnames(classes.button, className)} {...props}>
|
||||
<button
|
||||
className={classnames(classes.primary, classes.button, className)}
|
||||
{...props}>
|
||||
{Icon && (
|
||||
<div className={classes.buttonIcon}>
|
||||
<Icon />
|
||||
</div>
|
||||
)}
|
||||
{InverseIcon && (
|
||||
<div
|
||||
className={classnames(
|
||||
classes.buttonIcon,
|
||||
classes.buttonIconActive
|
||||
)}>
|
||||
<InverseIcon />
|
||||
</div>
|
||||
)}
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
export default SimpleButton
|
||||
|
|
|
|||
|
|
@ -1,10 +1,35 @@
|
|||
import * as R from 'ramda'
|
||||
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 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 {
|
||||
elements,
|
||||
|
|
@ -16,15 +41,35 @@ const Header = () => {
|
|||
toggleWidth,
|
||||
DEFAULT_COL_SIZE
|
||||
} = useContext(TableCtx)
|
||||
|
||||
const mapElement2 = (it, idx) => {
|
||||
const { width, elements, name } = it
|
||||
|
||||
if (elements && elements.length) {
|
||||
return (
|
||||
<THead>
|
||||
{elements.map(
|
||||
({ name, width = DEFAULT_COL_SIZE, header, textAlign }, idx) => (
|
||||
<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 (
|
||||
<HeaderElement>
|
||||
{innerElements.map(mapElement2)}
|
||||
{enableEdit && (
|
||||
<Td header width={editWidth} textAlign="center">
|
||||
Edit
|
||||
|
|
@ -40,7 +85,7 @@ const Header = () => {
|
|||
Enable
|
||||
</Td>
|
||||
)}
|
||||
</THead>
|
||||
</HeaderElement>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ const ActionCol = ({ disabled, editing }) => {
|
|||
)
|
||||
}
|
||||
|
||||
const ECol = ({ editing, config }) => {
|
||||
const ECol = ({ editing, config, extraPaddingRight, extraPaddingLeft }) => {
|
||||
const {
|
||||
name,
|
||||
input,
|
||||
|
|
@ -115,7 +115,11 @@ const ECol = ({ editing, config }) => {
|
|||
|
||||
return (
|
||||
<Td
|
||||
className={{ [classes.withSuffix]: suffix }}
|
||||
className={{
|
||||
[classes.extraPaddingRight]: extraPaddingRight,
|
||||
[classes.extraPaddingLeft]: extraPaddingLeft,
|
||||
[classes.withSuffix]: suffix
|
||||
}}
|
||||
width={width}
|
||||
size={size}
|
||||
bold={bold}
|
||||
|
|
@ -162,13 +166,31 @@ const ERow = ({ editing, disabled }) => {
|
|||
const shouldStripe = stripeWhen && stripeWhen(values) && !editing
|
||||
|
||||
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 (
|
||||
<Tr
|
||||
size={rowSize}
|
||||
error={errors && errors.length}
|
||||
errorMessage={errors && errors.toString()}>
|
||||
{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) && (
|
||||
<ActionCol disabled={disabled} editing={editing} />
|
||||
|
|
|
|||
|
|
@ -4,11 +4,20 @@ export default {
|
|||
cancelButton: {
|
||||
marginRight: 20
|
||||
},
|
||||
withSuffix: ({ textAlign }) => ({
|
||||
extraPaddingLeft: {
|
||||
paddingLeft: 35
|
||||
},
|
||||
extraPaddingRight: {
|
||||
paddingRight: 45
|
||||
},
|
||||
withSuffix: ({ textAlign }) => {
|
||||
const justifyContent = textAlign === 'right' ? 'end' : textAlign
|
||||
return {
|
||||
display: 'flex',
|
||||
alignItems: 'baseline',
|
||||
justifyContent: textAlign === 'right' && 'end'
|
||||
}),
|
||||
justifyContent
|
||||
}
|
||||
},
|
||||
suffix: {
|
||||
marginLeft: 7
|
||||
},
|
||||
|
|
|
|||
|
|
@ -65,8 +65,8 @@ const Th = ({ children, ...props }) => {
|
|||
)
|
||||
}
|
||||
|
||||
const ThDoubleLevel = ({ title, children, className }) => {
|
||||
const classes = useStyles()
|
||||
const ThDoubleLevel = ({ title, children, className, width }) => {
|
||||
const classes = useStyles({ width })
|
||||
|
||||
return (
|
||||
<div className={classnames(className, classes.thDoubleLevel)}>
|
||||
|
|
|
|||
|
|
@ -31,11 +31,13 @@ export default {
|
|||
color: white,
|
||||
display: 'table-row'
|
||||
},
|
||||
thDoubleLevel: {
|
||||
padding: [[0, spacer * 2]],
|
||||
thDoubleLevel: ({ width }) => ({
|
||||
width,
|
||||
display: 'table-cell',
|
||||
'& > :first-child': {
|
||||
margin: [[0, 10]],
|
||||
extend: label1,
|
||||
fontWeight: 700,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
|
|
@ -45,14 +47,12 @@ export default {
|
|||
height: 28
|
||||
},
|
||||
'& > :last-child': {
|
||||
padding: [[0, 11]],
|
||||
display: 'table-cell',
|
||||
verticalAlign: 'middle',
|
||||
height: tableDoubleHeaderHeight - 28,
|
||||
'& > div': {
|
||||
display: 'inline-block'
|
||||
height: tableDoubleHeaderHeight - 28
|
||||
}
|
||||
}
|
||||
},
|
||||
}),
|
||||
cellDoubleLevel: {
|
||||
display: 'flex',
|
||||
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 { useSelect } from 'downshift'
|
||||
import React from 'react'
|
||||
|
||||
import { ReactComponent as Arrowdown } from '../../../styling/icons/action/arrow/regular.svg'
|
||||
import { startCase } from '../../../utils/string'
|
||||
import { ReactComponent as Arrowdown } from 'src/styling/icons/action/arrow/regular.svg'
|
||||
import { startCase } from 'src/utils/string'
|
||||
|
||||
import styles from './Select.styles'
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ import { bySize, bold } from 'src/styling/helpers'
|
|||
import { secondaryColor } from 'src/styling/variables'
|
||||
|
||||
export default {
|
||||
size: ({ size }) => bySize(size),
|
||||
size: ({ size }) => ({
|
||||
marginTop: size === 'lg' ? -2 : 0,
|
||||
...bySize(size)
|
||||
}),
|
||||
bold,
|
||||
root: ({ width, textAlign }) => ({
|
||||
width,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ const RadioGroupFormik = memo(({ label, ...props }) => {
|
|||
options={props.options}
|
||||
ariaLabel={name}
|
||||
onChange={e => {
|
||||
console.log(e)
|
||||
onChange(e)
|
||||
props.resetError && props.resetError()
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import Autocomplete from './Autocomplete'
|
||||
import Checkbox from './Checkbox'
|
||||
import RadioGroup from './RadioGroup'
|
||||
import SecretInput from './SecretInput'
|
||||
import TextInput from './TextInput'
|
||||
|
||||
export { Autocomplete, Checkbox, TextInput, RadioGroup }
|
||||
export { Autocomplete, Checkbox, TextInput, SecretInput, RadioGroup }
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ const Sidebar = ({
|
|||
[classes.link]: true
|
||||
})}
|
||||
onClick={() => onClick(it)}>
|
||||
{itemRender ? itemRender(it) : displayName(it)}
|
||||
{itemRender ? itemRender(it, isSelected(it)) : displayName(it)}
|
||||
</div>
|
||||
))}
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { makeStyles } from '@material-ui/core'
|
||||
import classnames from 'classnames'
|
||||
import React from 'react'
|
||||
|
||||
import ErrorMessage from 'src/components/ErrorMessage'
|
||||
|
|
@ -8,10 +9,10 @@ import styles from './TitleSection.styles'
|
|||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const TitleSection = ({ title, error, labels }) => {
|
||||
const TitleSection = ({ className, title, error, labels, children }) => {
|
||||
const classes = useStyles()
|
||||
return (
|
||||
<div className={classes.titleWrapper}>
|
||||
<div className={classnames(classes.titleWrapper, className)}>
|
||||
<div className={classes.titleAndButtonsContainer}>
|
||||
<Title>{title}</Title>
|
||||
{error && (
|
||||
|
|
@ -19,6 +20,7 @@ const TitleSection = ({ title, error, labels }) => {
|
|||
)}
|
||||
</div>
|
||||
<div className={classes.headerLabels}>{labels}</div>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { makeStyles } from '@material-ui/core/styles'
|
|||
import classnames from 'classnames'
|
||||
import React, { memo } from 'react'
|
||||
|
||||
import typographyStyles from 'src/components/typography/styles'
|
||||
import {
|
||||
tableCellColor,
|
||||
tableCellHeight,
|
||||
|
|
@ -10,7 +11,6 @@ import {
|
|||
tableErrorColor,
|
||||
tableSuccessColor
|
||||
} from 'src/styling/variables'
|
||||
import typographyStyles from 'src/components/typography/styles'
|
||||
|
||||
const { info2, p } = typographyStyles
|
||||
|
||||
|
|
@ -19,8 +19,7 @@ const useStyles = makeStyles({
|
|||
extend: p,
|
||||
padding: 4,
|
||||
height: tableCellHeight,
|
||||
backgroundColor: tableCellColor,
|
||||
boxShadow: '0 0 4px 0 rgba(0, 0, 0, 0.08)'
|
||||
backgroundColor: tableCellColor
|
||||
},
|
||||
lg: {
|
||||
extend: info2,
|
||||
|
|
|
|||
|
|
@ -6,37 +6,62 @@ import styles from './styles'
|
|||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
function H1({ children, className, ...props }) {
|
||||
function H1({ children, noMargin, className, ...props }) {
|
||||
const classes = useStyles()
|
||||
const classNames = {
|
||||
[classes.h1]: true,
|
||||
[classes.noMargin]: noMargin,
|
||||
[className]: !!className
|
||||
}
|
||||
|
||||
return (
|
||||
<h1 className={classnames(classes.h1, className)} {...props}>
|
||||
<h1 className={classnames(classNames)} {...props}>
|
||||
{children}
|
||||
</h1>
|
||||
)
|
||||
}
|
||||
|
||||
function H2({ children, className, ...props }) {
|
||||
function H2({ children, noMargin, className, ...props }) {
|
||||
const classes = useStyles()
|
||||
const classNames = {
|
||||
[classes.h2]: true,
|
||||
[classes.noMargin]: noMargin,
|
||||
[className]: !!className
|
||||
}
|
||||
|
||||
return (
|
||||
<h2 className={classnames(classes.h2, className)} {...props}>
|
||||
<h2 className={classnames(classNames)} {...props}>
|
||||
{children}
|
||||
</h2>
|
||||
)
|
||||
}
|
||||
|
||||
function H3({ children, className, ...props }) {
|
||||
function H3({ children, noMargin, className, ...props }) {
|
||||
const classes = useStyles()
|
||||
const classNames = {
|
||||
[classes.h3]: true,
|
||||
[classes.noMargin]: noMargin,
|
||||
[className]: !!className
|
||||
}
|
||||
|
||||
return (
|
||||
<h3 className={classnames(classes.h3, className)} {...props}>
|
||||
<h3 className={classnames(classNames)} {...props}>
|
||||
{children}
|
||||
</h3>
|
||||
)
|
||||
}
|
||||
|
||||
function H4({ children, className, ...props }) {
|
||||
function H4({ children, noMargin, className, ...props }) {
|
||||
const classes = useStyles()
|
||||
console.log(className)
|
||||
const classNames = {
|
||||
[classes.h4]: true,
|
||||
[classes.noMargin]: noMargin,
|
||||
[className]: !!className
|
||||
}
|
||||
|
||||
return (
|
||||
<h4 className={classnames(classes.h4, className)} {...props}>
|
||||
<h4 className={classnames(classNames)} {...props}>
|
||||
{children}
|
||||
</h4>
|
||||
)
|
||||
|
|
@ -57,13 +82,13 @@ function pBuilder(elementClass) {
|
|||
return ({ inline, noMargin, className, children, ...props }) => {
|
||||
const classes = useStyles()
|
||||
const classNames = {
|
||||
[className]: !!className,
|
||||
[classes[elementClass]]: elementClass,
|
||||
className: true,
|
||||
[classes.inline]: inline,
|
||||
[classes.noMarginP]: noMargin
|
||||
[classes.noMargin]: noMargin
|
||||
}
|
||||
return (
|
||||
<p className={classnames(classNames, className)} {...props}>
|
||||
<p className={classnames(classNames)} {...props}>
|
||||
{children}
|
||||
</p>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ export default {
|
|||
inline: {
|
||||
display: 'inline'
|
||||
},
|
||||
noMarginP: {
|
||||
noMargin: {
|
||||
margin: 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { useMutation } from '@apollo/react-hooks'
|
||||
import { Dialog, DialogContent, SvgIcon, IconButton } from '@material-ui/core'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { gql } from 'apollo-boost'
|
||||
import classnames from 'classnames'
|
||||
import { Form, Formik, FastField } from 'formik'
|
||||
import gql from 'graphql-tag'
|
||||
import QRCode from 'qrcode.react'
|
||||
import React, { memo, useState } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
|
|
|||
|
|
@ -1,15 +1,30 @@
|
|||
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 React, { useState } from 'react'
|
||||
|
||||
import HelpTooltip from 'src/components/HelpTooltip'
|
||||
import { NamespacedTable as EditableTable } from 'src/components/editableTable'
|
||||
import { Switch } from 'src/components/inputs'
|
||||
import TitleSection from 'src/components/layout/TitleSection'
|
||||
import { P, Label2 } from 'src/components/typography'
|
||||
import { fromNamespace, toNamespace } from 'src/utils/config'
|
||||
|
||||
import Wizard from './Wizard'
|
||||
import { DenominationsSchema, getElements } from './helper'
|
||||
|
||||
const useStyles = makeStyles({
|
||||
fudgeFactor: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginRight: 156
|
||||
},
|
||||
switchLabel: {
|
||||
margin: 6
|
||||
}
|
||||
})
|
||||
|
||||
const SAVE_CONFIG = gql`
|
||||
mutation Save($config: JSONObject) {
|
||||
saveConfig(config: $config)
|
||||
|
|
@ -30,6 +45,7 @@ const GET_INFO = gql`
|
|||
`
|
||||
|
||||
const CashOut = ({ name: SCREEN_KEY }) => {
|
||||
const classes = useStyles()
|
||||
const [wizard, setWizard] = useState(false)
|
||||
const [error, setError] = useState(false)
|
||||
const { data } = useQuery(GET_INFO)
|
||||
|
|
@ -47,6 +63,7 @@ const CashOut = ({ name: SCREEN_KEY }) => {
|
|||
}
|
||||
|
||||
const config = data?.config && fromNamespace(SCREEN_KEY)(data.config)
|
||||
const fudgeFactorActive = config?.fudgeFactorActive ?? false
|
||||
const locale = data?.config && fromNamespace('locale')(data.config)
|
||||
const machines = data?.machines ?? []
|
||||
|
||||
|
|
@ -58,7 +75,31 @@ const CashOut = ({ name: SCREEN_KEY }) => {
|
|||
|
||||
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
|
||||
name="test"
|
||||
namespaces={R.map(R.path(['deviceId']))(machines)}
|
||||
|
|
|
|||
|
|
@ -57,14 +57,13 @@ const WizardStep = ({
|
|||
</div>
|
||||
</Form>
|
||||
</Formik>
|
||||
// TODO: there was a disabled link here showing the currency code; restore it
|
||||
)}
|
||||
|
||||
{lastStep && (
|
||||
<>
|
||||
<P>
|
||||
When enabling cash out, your bill count will be authomatically set
|
||||
to zero. Make sure you physically put cash inside the cashboxes to
|
||||
When enabling cash out, your bill count will be automatically set 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,
|
||||
make sure you set the correct cash out bill count for this machine
|
||||
on your Cashboxes tab under Maintenance.
|
||||
|
|
@ -72,7 +71,7 @@ const WizardStep = ({
|
|||
<P>
|
||||
When enabling cash out, default commissions will be set. To change
|
||||
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.
|
||||
</P>
|
||||
<div className={classes.submit}>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const getElements = (machines, { fiatCurrency } = {}) => {
|
|||
{
|
||||
name: 'id',
|
||||
header: 'Machine',
|
||||
width: 254,
|
||||
width: 200,
|
||||
view: it => machines.find(({ deviceId }) => deviceId === it).name,
|
||||
size: 'sm',
|
||||
editable: false
|
||||
|
|
@ -24,7 +24,8 @@ const getElements = (machines, { fiatCurrency } = {}) => {
|
|||
view: it => `${it} ${fiatCurrency}`,
|
||||
size: 'sm',
|
||||
stripe: true,
|
||||
width: 265,
|
||||
width: 200,
|
||||
textAlign: 'right',
|
||||
input: TextInput
|
||||
},
|
||||
{
|
||||
|
|
@ -33,7 +34,8 @@ const getElements = (machines, { fiatCurrency } = {}) => {
|
|||
view: it => `${it} ${fiatCurrency}`,
|
||||
size: 'sm',
|
||||
stripe: true,
|
||||
width: 265,
|
||||
textAlign: 'right',
|
||||
width: 200,
|
||||
input: TextInput
|
||||
},
|
||||
{
|
||||
|
|
@ -41,6 +43,7 @@ const getElements = (machines, { fiatCurrency } = {}) => {
|
|||
header: '0-conf Limit',
|
||||
size: 'sm',
|
||||
stripe: true,
|
||||
textAlign: 'right',
|
||||
width: 200,
|
||||
input: TextInput
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { gql } from 'apollo-boost'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React from 'react'
|
||||
|
||||
import { Table as EditableTable } from 'src/components/editableTable'
|
||||
import Section from 'src/components/layout/Section'
|
||||
import TitleSection from 'src/components/layout/TitleSection'
|
||||
import { fromNamespace, toNamespace } from 'src/utils/config'
|
||||
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
||||
|
||||
import {
|
||||
mainFields,
|
||||
|
|
@ -44,6 +44,9 @@ const Commissions = ({ name: SCREEN_KEY }) => {
|
|||
})
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -71,7 +74,7 @@ const Commissions = ({ name: SCREEN_KEY }) => {
|
|||
save={save}
|
||||
validationSchema={schema}
|
||||
data={R.of(commission)}
|
||||
elements={mainFields(data)}
|
||||
elements={mainFields(currency)}
|
||||
/>
|
||||
</Section>
|
||||
<Section>
|
||||
|
|
@ -86,7 +89,7 @@ const Commissions = ({ name: SCREEN_KEY }) => {
|
|||
save={saveOverrides}
|
||||
validationSchema={OverridesSchema}
|
||||
data={commission.overrides ?? []}
|
||||
elements={overrides(data)}
|
||||
elements={overrides(data, currency)}
|
||||
/>
|
||||
</Section>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import * as Yup from 'yup'
|
|||
import { TextInput } from 'src/components/inputs/formik'
|
||||
import Autocomplete from 'src/components/inputs/formik/Autocomplete.js'
|
||||
|
||||
const getOverridesFields = getData => {
|
||||
const getOverridesFields = (getData, currency) => {
|
||||
const getView = (data, code, compare) => it => {
|
||||
if (!data) return ''
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ const getOverridesFields = getData => {
|
|||
},
|
||||
{
|
||||
name: 'cryptoCurrencies',
|
||||
width: 270,
|
||||
width: 250,
|
||||
size: 'sm',
|
||||
view: displayCodeArray(cryptoData),
|
||||
input: Autocomplete,
|
||||
|
|
@ -54,69 +54,91 @@ const getOverridesFields = getData => {
|
|||
name: 'cashIn',
|
||||
display: 'Cash-in',
|
||||
width: 140,
|
||||
input: TextInput
|
||||
input: TextInput,
|
||||
textAlign: 'right',
|
||||
suffix: '%'
|
||||
},
|
||||
{
|
||||
name: 'cashOut',
|
||||
display: 'Cash-out',
|
||||
width: 140,
|
||||
input: TextInput
|
||||
input: TextInput,
|
||||
textAlign: 'right',
|
||||
suffix: '%'
|
||||
},
|
||||
{
|
||||
name: 'fixedFee',
|
||||
display: 'Fixed fee',
|
||||
width: 140,
|
||||
input: TextInput
|
||||
input: TextInput,
|
||||
doubleHeader: 'Cash-in only',
|
||||
textAlign: 'right',
|
||||
suffix: currency
|
||||
},
|
||||
{
|
||||
name: 'minimumTx',
|
||||
display: 'Minimun Tx',
|
||||
width: 140,
|
||||
input: TextInput
|
||||
input: TextInput,
|
||||
doubleHeader: 'Cash-in only',
|
||||
textAlign: 'right',
|
||||
suffix: currency
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const mainFields = auxData => [
|
||||
const mainFields = currency => [
|
||||
{
|
||||
name: 'cashIn',
|
||||
display: 'Cash-in',
|
||||
width: 169,
|
||||
size: 'lg',
|
||||
input: TextInput
|
||||
input: TextInput,
|
||||
suffix: '%'
|
||||
},
|
||||
{
|
||||
name: 'cashOut',
|
||||
display: 'Cash-out',
|
||||
width: 169,
|
||||
size: 'lg',
|
||||
input: TextInput
|
||||
input: TextInput,
|
||||
suffix: '%'
|
||||
},
|
||||
{
|
||||
name: 'fixedFee',
|
||||
display: 'Fixed fee',
|
||||
width: 169,
|
||||
size: 'lg',
|
||||
input: TextInput
|
||||
doubleHeader: 'Cash-in only',
|
||||
textAlign: 'center',
|
||||
input: TextInput,
|
||||
suffix: currency
|
||||
},
|
||||
{
|
||||
name: 'minimumTx',
|
||||
display: 'Minimun Tx',
|
||||
width: 169,
|
||||
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)
|
||||
|
||||
return getOverridesFields(getData)
|
||||
return getOverridesFields(getData, currency)
|
||||
}
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
cashIn: Yup.number().required('Required'),
|
||||
cashOut: Yup.number().required('Required'),
|
||||
cashIn: Yup.number()
|
||||
.max(100)
|
||||
.required('Required'),
|
||||
cashOut: Yup.number()
|
||||
.max(100)
|
||||
.required('Required'),
|
||||
fixedFee: Yup.number().required('Required'),
|
||||
minimumTx: Yup.number().required('Required')
|
||||
})
|
||||
|
|
@ -124,8 +146,12 @@ const schema = Yup.object().shape({
|
|||
const OverridesSchema = Yup.object().shape({
|
||||
machine: Yup.string().required('Required'),
|
||||
cryptoCurrencies: Yup.array().required('Required'),
|
||||
cashIn: Yup.number().required('Required'),
|
||||
cashOut: Yup.number().required('Required'),
|
||||
cashIn: Yup.number()
|
||||
.max(100)
|
||||
.required('Required'),
|
||||
cashOut: Yup.number()
|
||||
.max(100)
|
||||
.required('Required'),
|
||||
fixedFee: 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 React, { memo } from 'react'
|
||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { gql } from 'apollo-boost'
|
||||
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 {
|
||||
OVERRIDE_AUTHORIZED,
|
||||
OVERRIDE_REJECTED
|
||||
} 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 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 {
|
||||
CustomerDetails,
|
||||
IdDataCard,
|
||||
|
|
@ -25,9 +25,8 @@ import {
|
|||
IdCardPhotoCard,
|
||||
TransactionsList
|
||||
} from './components'
|
||||
import { mainStyles } from './Customers.styles'
|
||||
|
||||
const useStyles = makeStyles(mainStyles)
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const GET_CUSTOMER = gql`
|
||||
query customer($customerId: ID!) {
|
||||
|
|
@ -96,8 +95,7 @@ const CustomerProfile = memo(() => {
|
|||
const { data: customerResponse, refetch: getCustomer } = useQuery(
|
||||
GET_CUSTOMER,
|
||||
{
|
||||
variables: { customerId },
|
||||
fetchPolicy: 'no-cache'
|
||||
variables: { customerId }
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -125,24 +123,25 @@ const CustomerProfile = memo(() => {
|
|||
return (
|
||||
<>
|
||||
<Breadcrumbs
|
||||
classes={{ root: classes.breadcrumbs }}
|
||||
separator={<NavigateNextIcon fontSize="small" />}
|
||||
aria-label="breadcrumb">
|
||||
<Label1
|
||||
noMargin
|
||||
className={classes.labelLink}
|
||||
onClick={() => history.push('/compliance/customers')}>
|
||||
Customers
|
||||
</Label1>
|
||||
<Label1 className={classes.bold}>
|
||||
{R.path(['name'])(customerData)}
|
||||
</Label1>
|
||||
<Label2 noMargin className={classes.labelLink}>
|
||||
Rafael{R.path(['name'])(customerData)}
|
||||
</Label2>
|
||||
</Breadcrumbs>
|
||||
<div>
|
||||
<div className={classes.header}>
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
<CustomerDetails customer={customerData} />
|
||||
<div className={classes.rightAligned}>
|
||||
<Label1 className={classes.label1}>Actions</Label1>
|
||||
<div>
|
||||
<Label1 className={classes.actionLabel}>Actions</Label1>
|
||||
<ActionButton
|
||||
className={classes.actionButton}
|
||||
color="primary"
|
||||
Icon={blocked ? AuthorizeIcon : BlockIcon}
|
||||
InverseIcon={blocked ? AuthorizeReversedIcon : BlockReversedIcon}
|
||||
|
|
@ -156,8 +155,8 @@ const CustomerProfile = memo(() => {
|
|||
{`${blocked ? 'Authorize' : 'Block'} customer`}
|
||||
</ActionButton>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.rowCenterAligned}>
|
||||
</Box>
|
||||
<Box display="flex">
|
||||
<IdDataCard
|
||||
customerData={customerData}
|
||||
updateCustomer={updateCustomer}
|
||||
|
|
@ -170,7 +169,7 @@ const CustomerProfile = memo(() => {
|
|||
customerData={customerData}
|
||||
updateCustomer={updateCustomer}
|
||||
/>
|
||||
</div>
|
||||
</Box>
|
||||
</div>
|
||||
<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 { gql } from 'apollo-boost'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React from 'react'
|
||||
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 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 classes = useStyles()
|
||||
|
|
|
|||
|
|
@ -1,39 +1,17 @@
|
|||
import typographyStyles from 'src/components/typography/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 { titleWrapper, titleAndButtonsContainer } = baseStyles
|
||||
|
||||
const mainStyles = {
|
||||
rightAligned: {
|
||||
display: 'flex',
|
||||
flexFlow: 'column nowrap',
|
||||
right: 0
|
||||
},
|
||||
actionButton: {
|
||||
height: 28
|
||||
},
|
||||
export default {
|
||||
titleWrapper,
|
||||
titleAndButtonsContainer,
|
||||
header: {
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
justifyContent: 'space-between'
|
||||
},
|
||||
customerDetails: {
|
||||
display: 'flex',
|
||||
flex: 1
|
||||
},
|
||||
row: {
|
||||
display: 'flex',
|
||||
flexFlow: 'row nowrap'
|
||||
},
|
||||
rowCenterAligned: {
|
||||
display: 'flex',
|
||||
flexFlow: 'row nowrap',
|
||||
alignItems: 'center'
|
||||
},
|
||||
rowSpaceBetween: {
|
||||
display: 'flex',
|
||||
flexFlow: 'row nowrap',
|
||||
|
|
@ -50,17 +28,6 @@ const mainStyles = {
|
|||
textInput: {
|
||||
width: 144
|
||||
},
|
||||
label1: {
|
||||
fontFamily: 'MuseoSans',
|
||||
fontSize: 12,
|
||||
fontWeight: 500,
|
||||
fontStretch: 'normal',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.33,
|
||||
letterSpacing: 'normal',
|
||||
color: comet,
|
||||
margin: [[4, 0]]
|
||||
},
|
||||
p: {
|
||||
fontFamily: 'MuseoSans',
|
||||
fontSize: 14,
|
||||
|
|
@ -71,9 +38,6 @@ const mainStyles = {
|
|||
letterSpacing: 'normal',
|
||||
color: primaryColor
|
||||
},
|
||||
bold: {
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
txId: {
|
||||
fontFamily: 'MuseoSans',
|
||||
whiteSpace: 'nowrap',
|
||||
|
|
@ -106,7 +70,7 @@ const mainStyles = {
|
|||
height: 92,
|
||||
borderRadius: 8,
|
||||
backgroundColor: zircon,
|
||||
margin: [[15, 28, 0, 0]],
|
||||
margin: [[0, 28, 0, 0]],
|
||||
padding: [[30]],
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between'
|
||||
|
|
@ -124,9 +88,6 @@ const mainStyles = {
|
|||
height: 240,
|
||||
margin: [[32, 0, 0, 0]]
|
||||
},
|
||||
labelLink: {
|
||||
cursor: 'pointer'
|
||||
},
|
||||
field: {
|
||||
position: 'relative',
|
||||
width: 144,
|
||||
|
|
@ -158,5 +119,3 @@ const mainStyles = {
|
|||
maxHeight: 97
|
||||
}
|
||||
}
|
||||
|
||||
export { mainStyles }
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import * as R from 'ramda'
|
||||
import { makeStyles, Box } from '@material-ui/core'
|
||||
import moment from 'moment'
|
||||
import * as R from 'ramda'
|
||||
import React, { memo } from 'react'
|
||||
|
||||
import { H2 } from 'src/components/typography'
|
||||
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 { mainStyles } from '../Customers.styles'
|
||||
import { ifNotNull } from '../../../utils/nullCheck'
|
||||
import styles from '../CustomersList.styles'
|
||||
|
||||
import FrontCameraPhoto from './FrontCameraPhoto'
|
||||
|
||||
const useStyles = makeStyles(mainStyles)
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const CustomerDetails = memo(({ customer }) => {
|
||||
const classes = useStyles()
|
||||
|
|
@ -61,30 +61,30 @@ const CustomerDetails = memo(({ customer }) => {
|
|||
]
|
||||
|
||||
return (
|
||||
<div className={classes.customerDetails}>
|
||||
<Box display="flex">
|
||||
<FrontCameraPhoto
|
||||
frontCameraPath={R.path(['frontCameraPath'])(customer)}
|
||||
/>
|
||||
<div>
|
||||
<div className={classes.rowCenterAligned}>
|
||||
<H2 className={classes.customerName}>{R.path(['name'])(customer)}</H2>
|
||||
</div>
|
||||
<div className={classes.rowCenterAligned}>
|
||||
<Box display="flex">
|
||||
<H2 noMargin>Rafael{R.path(['name'])(customer)}</H2>
|
||||
</Box>
|
||||
<Box display="flex">
|
||||
{elements.map(({ size, header }, idx) => (
|
||||
<div key={idx} className={classes.label1} style={{ width: size }}>
|
||||
{header}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className={classes.rowCenterAligned}>
|
||||
</Box>
|
||||
<Box display="flex">
|
||||
{elements.map(({ size, value }, idx) => (
|
||||
<div key={idx} className={classes.p} style={{ width: size }}>
|
||||
{value}
|
||||
</div>
|
||||
))}
|
||||
</Box>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Box>
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import React, { memo } from 'react'
|
|||
|
||||
import { Info3, Label1 } from 'src/components/typography'
|
||||
|
||||
import { mainStyles } from '../Customers.styles'
|
||||
import mainStyles from '../CustomersList.styles'
|
||||
|
||||
const useStyles = makeStyles(mainStyles)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import { Paper } from '@material-ui/core'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
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 { URI } from 'src/utils/apollo'
|
||||
|
||||
import { mainStyles } from '../Customers.styles'
|
||||
|
||||
import { IMAGES_URI } from './variables'
|
||||
import mainStyles from '../CustomersList.styles'
|
||||
|
||||
const useStyles = makeStyles(mainStyles)
|
||||
|
||||
|
|
@ -16,10 +15,7 @@ const FrontCameraPhoto = memo(({ frontCameraPath }) => {
|
|||
return (
|
||||
<Paper className={classes.photo} elevation={0}>
|
||||
{frontCameraPath ? (
|
||||
<img
|
||||
src={`${IMAGES_URI}/front-camera-photo/${frontCameraPath}`}
|
||||
alt=""
|
||||
/>
|
||||
<img src={`${URI}/front-camera-photo/${frontCameraPath}`} alt="" />
|
||||
) : (
|
||||
<CrossedCameraIcon />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import * as R from 'ramda'
|
||||
import moment from 'moment'
|
||||
import * as R from 'ramda'
|
||||
import React, { memo } from 'react'
|
||||
|
||||
import { ReactComponent as CrossedCameraIcon } from 'src/styling/icons/ID/photo/crossed-camera.svg'
|
||||
import {
|
||||
PropertyCard,
|
||||
OVERRIDE_AUTHORIZED,
|
||||
OVERRIDE_REJECTED
|
||||
} 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 { IMAGES_URI } from './variables'
|
||||
|
||||
const useStyles = makeStyles(mainStyles)
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ const IdCardPhotoCard = memo(({ customerData, updateCustomer }) => {
|
|||
{customerData.idCardPhotoPath ? (
|
||||
<img
|
||||
className={classes.idCardPhoto}
|
||||
src={`${IMAGES_URI}/id-card-photo/${R.path(['idCardPhotoPath'])(
|
||||
src={`${URI}/id-card-photo/${R.path(['idCardPhotoPath'])(
|
||||
customerData
|
||||
)}`}
|
||||
alt=""
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import * as R from 'ramda'
|
||||
import moment from 'moment'
|
||||
import * as R from 'ramda'
|
||||
import React, { memo } from 'react'
|
||||
|
||||
import {
|
||||
|
|
@ -9,7 +9,7 @@ import {
|
|||
OVERRIDE_REJECTED
|
||||
} from 'src/pages/Customers/components/propertyCard'
|
||||
|
||||
import { mainStyles } from '../Customers.styles'
|
||||
import mainStyles from '../CustomersList.styles'
|
||||
|
||||
import Field from './Field'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { parsePhoneNumberFromString } from 'libphonenumber-js'
|
||||
import * as R from 'ramda'
|
||||
import React, { memo } from 'react'
|
||||
import { parsePhoneNumberFromString } from 'libphonenumber-js'
|
||||
|
||||
import {
|
||||
PropertyCard,
|
||||
|
|
@ -9,7 +9,7 @@ import {
|
|||
OVERRIDE_REJECTED
|
||||
} from 'src/pages/Customers/components/propertyCard'
|
||||
|
||||
import { mainStyles } from '../Customers.styles'
|
||||
import mainStyles from '../CustomersList.styles'
|
||||
|
||||
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 CopyToClipboard from '../../Transactions/CopyToClipboard'
|
||||
import { mainStyles } from '../Customers.styles'
|
||||
import mainStyles from '../CustomersList.styles'
|
||||
|
||||
const useStyles = makeStyles(mainStyles)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Paper } from '@material-ui/core'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import classnames from 'classnames'
|
||||
import React, { memo } from 'react'
|
||||
import { Paper } from '@material-ui/core'
|
||||
|
||||
import { ActionButton } from 'src/components/buttons'
|
||||
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 { makeStyles } from '@material-ui/core/styles'
|
||||
import { gql } from 'apollo-boost'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import classnames from 'classnames'
|
||||
import gql from 'graphql-tag'
|
||||
import moment from 'moment'
|
||||
import QRCode from 'qrcode.react'
|
||||
import React, { useState } from 'react'
|
||||
|
|
@ -35,6 +35,7 @@ const GET_FUNDING = gql`
|
|||
{
|
||||
funding {
|
||||
cryptoCode
|
||||
errorMsg
|
||||
fundingAddress
|
||||
fundingAddressUrl
|
||||
confirmedBalance
|
||||
|
|
@ -55,6 +56,7 @@ const formatNumber = it => new BigNumber(it).toFormat(2)
|
|||
const getConfirmedTotal = list => {
|
||||
return formatNumber(
|
||||
list
|
||||
.filter(it => !it.errorMsg)
|
||||
.map(it => new BigNumber(it.fiatConfirmedBalance))
|
||||
.reduce(sumReducer, new BigNumber(0))
|
||||
)
|
||||
|
|
@ -63,6 +65,7 @@ const getConfirmedTotal = list => {
|
|||
const getPendingTotal = list => {
|
||||
return formatNumber(
|
||||
list
|
||||
.filter(it => !it.errorMsg)
|
||||
.map(it => new BigNumber(it.fiatPending))
|
||||
.reduce(sumReducer, new BigNumber(0))
|
||||
)
|
||||
|
|
@ -107,16 +110,29 @@ const Funding = () => {
|
|||
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 (
|
||||
<div className={classes.itemWrapper}>
|
||||
<div className={classnames(wrapperClass)}>
|
||||
<div className={classes.firstItem}>{it.display}</div>
|
||||
<div className={classes.item}>
|
||||
{!it.errorMsg && (
|
||||
<>
|
||||
<div className={classnames(itemClass)}>
|
||||
{it.fiatConfirmedBalance} {it.fiatCode}
|
||||
</div>
|
||||
<div className={classes.item}>
|
||||
<div className={classnames(itemClass)}>
|
||||
{it.confirmedBalance} {it.cryptoCode}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -149,7 +165,14 @@ const Funding = () => {
|
|||
</div>
|
||||
)}
|
||||
</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.firstSide}>
|
||||
<H3>Balance ({selected.display})</H3>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ import {
|
|||
disabledColor2,
|
||||
spacer,
|
||||
subheaderColor,
|
||||
placeholderColor
|
||||
errorColor,
|
||||
placeholderColor,
|
||||
comet
|
||||
} from 'src/styling/variables'
|
||||
|
||||
const { label1 } = typographyStyles
|
||||
|
|
@ -25,6 +27,9 @@ export default {
|
|||
secondSide: {
|
||||
marginTop: -29
|
||||
},
|
||||
error: {
|
||||
color: errorColor
|
||||
},
|
||||
coinTotal: {
|
||||
margin: `${spacer * 1.5}px 0`
|
||||
},
|
||||
|
|
@ -51,7 +56,11 @@ export default {
|
|||
extend: label1,
|
||||
margin: 2
|
||||
},
|
||||
inactiveItem: {
|
||||
color: comet
|
||||
},
|
||||
firstItem: {
|
||||
fontWeight: 700,
|
||||
margin: 2
|
||||
},
|
||||
total: {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { gql } from 'apollo-boost'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React from 'react'
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,16 @@ export default {
|
|||
fillColumn: {
|
||||
width: '100%'
|
||||
},
|
||||
shareButton: {
|
||||
margin: 8,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontSize: 12,
|
||||
padding: [[0, 12]]
|
||||
},
|
||||
shareIcon: {
|
||||
marginRight: 6
|
||||
},
|
||||
button: {
|
||||
margin: 8
|
||||
},
|
||||
|
|
@ -45,9 +55,9 @@ export default {
|
|||
},
|
||||
buttonsWrapper: {
|
||||
display: 'flex',
|
||||
marginLeft: 10,
|
||||
marginLeft: 16,
|
||||
'& > *': {
|
||||
margin: 'auto 10px'
|
||||
margin: 'auto 6px'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { gql } from 'apollo-boost'
|
||||
import gql from 'graphql-tag'
|
||||
import moment from 'moment'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper'
|
||||
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 {
|
||||
Table,
|
||||
|
|
@ -18,8 +18,8 @@ import {
|
|||
TableCell
|
||||
} from 'src/components/table'
|
||||
import { Info3 } from 'src/components/typography'
|
||||
import { ReactComponent as DownloadActive } from 'src/styling/icons/button/download/white.svg'
|
||||
import { ReactComponent as Download } from 'src/styling/icons/button/download/zodiac.svg'
|
||||
import { ReactComponent as WhiteShareIcon } from 'src/styling/icons/circle buttons/share/white.svg'
|
||||
import { ReactComponent as ShareIcon } from 'src/styling/icons/circle buttons/share/zodiac.svg'
|
||||
|
||||
import styles from './Logs.styles'
|
||||
|
||||
|
|
@ -62,7 +62,6 @@ const Logs = () => {
|
|||
|
||||
const [selected, setSelected] = useState(null)
|
||||
const [saveMessage, setSaveMessage] = useState(null)
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
|
||||
const deviceId = selected?.deviceId
|
||||
|
||||
|
|
@ -76,7 +75,6 @@ const Logs = () => {
|
|||
|
||||
const { data: logsResponse } = useQuery(GET_MACHINE_LOGS, {
|
||||
variables: { deviceId },
|
||||
fetchPolicy: 'no-cache',
|
||||
skip: !selected,
|
||||
onCompleted: () => setSaveMessage('')
|
||||
})
|
||||
|
|
@ -89,13 +87,6 @@ const Logs = () => {
|
|||
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 (
|
||||
<>
|
||||
<div className={classes.titleWrapper}>
|
||||
|
|
@ -103,25 +94,17 @@ const Logs = () => {
|
|||
<Title>Machine Logs</Title>
|
||||
{logsResponse && (
|
||||
<div className={classes.buttonsWrapper}>
|
||||
<FeatureButton
|
||||
Icon={Download}
|
||||
InverseIcon={DownloadActive}
|
||||
aria-describedby={id}
|
||||
variant="contained"
|
||||
onClick={handleOpenRangePicker}
|
||||
/>
|
||||
<LogsDowloaderPopover
|
||||
title="Download logs"
|
||||
name="machine-logs"
|
||||
id={id}
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
logs={logsResponse.machineLogs}
|
||||
getTimestamp={log => log.timestamp}
|
||||
/>
|
||||
<SimpleButton
|
||||
className={classes.button}
|
||||
className={classes.shareButton}
|
||||
disabled={loading}
|
||||
Icon={ShareIcon}
|
||||
InverseIcon={WhiteShareIcon}
|
||||
onClick={sendSnapshot}>
|
||||
Share with Lamassu
|
||||
</SimpleButton>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { gql } from 'apollo-boost'
|
||||
import gql from 'graphql-tag'
|
||||
import React from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,29 +1,20 @@
|
|||
import { useMutation } from '@apollo/react-hooks'
|
||||
import { Dialog, DialogContent } from '@material-ui/core'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { gql } from 'apollo-boost'
|
||||
import classnames from 'classnames'
|
||||
import gql from 'graphql-tag'
|
||||
import moment from 'moment'
|
||||
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 { 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'
|
||||
import styles from './MachineDetailsCard.styles'
|
||||
|
||||
const MACHINE_ACTION = gql`
|
||||
mutation MachineAction($deviceId: ID!, $action: MachineAction!) {
|
||||
|
|
@ -33,30 +24,18 @@ const MACHINE_ACTION = gql`
|
|||
}
|
||||
`
|
||||
|
||||
const colDivider = {
|
||||
background: zircon,
|
||||
width: 2
|
||||
}
|
||||
|
||||
const inlineChip = {
|
||||
marginInlineEnd: '0.25em'
|
||||
}
|
||||
|
||||
const useLStyles = makeStyles(labelStyles)
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const Label = ({ children }) => {
|
||||
const classes = useLStyles()
|
||||
|
||||
return <div className={classes.label}>{children}</div>
|
||||
const classes = useStyles()
|
||||
return <Label1 className={classes.label}>{children}</Label1>
|
||||
}
|
||||
|
||||
const useMDStyles = makeStyles({ ...detailsRowStyles, colDivider, inlineChip })
|
||||
|
||||
const MachineDetailsRow = ({ it: machine }) => {
|
||||
const [errorDialog, setErrorDialog] = useState(false)
|
||||
const [dialogOpen, setOpen] = useState(false)
|
||||
const [actionMessage, setActionMessage] = useState(null)
|
||||
const classes = useMDStyles()
|
||||
const classes = useStyles()
|
||||
|
||||
const unpairDialog = () => setOpen(true)
|
||||
|
||||
|
|
@ -79,16 +58,17 @@ const MachineDetailsRow = ({ it: machine }) => {
|
|||
<DialogContent>{actionMessage}</DialogContent>
|
||||
</Dialog>
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classnames(classes.row)}>
|
||||
<div className={classnames(classes.col)}>
|
||||
<div className={classnames(classes.row)}>
|
||||
<div className={classnames(classes.col, classes.col2)}>
|
||||
<div className={classes.innerRow}>
|
||||
<div>
|
||||
<div className={classes.column1}>
|
||||
<div className={classes.lastRow}>
|
||||
<div className={classes.status}>
|
||||
<Label>Statuses</Label>
|
||||
<div>
|
||||
{machine.statuses.map((status, index) => (
|
||||
<Status status={status} key={index} />
|
||||
<Status
|
||||
className={classes.chips}
|
||||
status={status}
|
||||
key={index}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -96,80 +76,35 @@ const MachineDetailsRow = ({ it: machine }) => {
|
|||
<Label>Lamassu Support article</Label>
|
||||
<div>
|
||||
{machine.statuses.map((...[, index]) => (
|
||||
<span key={index} />
|
||||
// TODO support articles
|
||||
<span key={index}></span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.separator} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={classnames(
|
||||
classes.col,
|
||||
classes.col2,
|
||||
classes.colDivider
|
||||
)}
|
||||
/>
|
||||
<div className={classnames(classes.col)}>
|
||||
<div className={classnames(classes.row)}>
|
||||
<div className={classnames(classes.col, classes.col2)}>
|
||||
<div className={classes.innerRow}>
|
||||
<div>
|
||||
<div className={classes.column2}>
|
||||
<div className={classes.row}>
|
||||
<div className={classes.machineModel}>
|
||||
<Label>Machine Model</Label>
|
||||
<div>{machine.machineModel}</div>
|
||||
<div>{machine.model ?? 'unknown'}</div>
|
||||
</div>
|
||||
<div className={classes.commissionWrapper}>
|
||||
<Label>Address</Label>
|
||||
<div>{machine.machineLocation}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classnames(classes.row)}>
|
||||
<div className={classnames(classes.col, classes.col2)}>
|
||||
<div className={classes.innerRow}>
|
||||
<div>
|
||||
<Label>Paired at</Label>
|
||||
<div>
|
||||
{moment(machine.pairedAt).format('YYYY-MM-DD HH:mm:ss')}
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.commissionWrapper}>
|
||||
<Label>Software update</Label>
|
||||
<div className={classes.innerRow}>
|
||||
{machine.softwareVersion && (
|
||||
<span className={classes.inlineChip}>
|
||||
{machine.softwareVersion}
|
||||
</span>
|
||||
)}
|
||||
<ActionButton
|
||||
className={classes.inlineChip}
|
||||
disabled
|
||||
color="primary"
|
||||
Icon={DownloadIcon}
|
||||
InverseIcon={DownloadReversedIcon}>
|
||||
Update
|
||||
</ActionButton>
|
||||
{machine.pairedAt
|
||||
? moment(machine.pairedAt).format('YYYY-MM-DD HH:mm:ss')
|
||||
: 'N/A'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classnames(classes.row)}>
|
||||
<div className={classnames(classes.col, classes.col2)}>
|
||||
<div className={classes.innerRow}>
|
||||
<div className={classes.lastRow}>
|
||||
<div>
|
||||
<Label>Printer</Label>
|
||||
<div>{machine.printer || 'unknown'}</div>
|
||||
</div>
|
||||
<div className={classes.commissionWrapper}>
|
||||
<Label>Actions</Label>
|
||||
<div className={classes.innerRow}>
|
||||
<div className={classes.actionRow}>
|
||||
<ActionButton
|
||||
className={classes.inlineChip}
|
||||
className={classes.action}
|
||||
color="primary"
|
||||
Icon={UnpairIcon}
|
||||
InverseIcon={UnpairReversedIcon}
|
||||
|
|
@ -179,6 +114,7 @@ const MachineDetailsRow = ({ it: machine }) => {
|
|||
</ActionButton>
|
||||
<ConfirmDialog
|
||||
open={dialogOpen}
|
||||
className={classes.dialog}
|
||||
title="Unpair this machine?"
|
||||
subtitle={false}
|
||||
toBeConfirmed={machine.name}
|
||||
|
|
@ -196,7 +132,7 @@ const MachineDetailsRow = ({ it: machine }) => {
|
|||
}}
|
||||
/>
|
||||
<ActionButton
|
||||
className={classes.inlineChip}
|
||||
className={classes.action}
|
||||
color="primary"
|
||||
Icon={RebootIcon}
|
||||
InverseIcon={RebootReversedIcon}
|
||||
|
|
@ -212,29 +148,26 @@ const MachineDetailsRow = ({ it: machine }) => {
|
|||
Reboot
|
||||
</ActionButton>
|
||||
<ActionButton
|
||||
className={classes.inlineChip}
|
||||
className={classes.action}
|
||||
disabled={loading}
|
||||
color="primary"
|
||||
Icon={ShutdownIcon}
|
||||
InverseIcon={ShutdownReversedIcon}
|
||||
Icon={RebootIcon}
|
||||
InverseIcon={RebootReversedIcon}
|
||||
onClick={() => {
|
||||
machineAction({
|
||||
variables: {
|
||||
deviceId: machine.deviceId,
|
||||
action: 'shutdown'
|
||||
action: 'restartServices'
|
||||
}
|
||||
})
|
||||
}}>
|
||||
Shutdown
|
||||
Restart Services
|
||||
</ActionButton>
|
||||
</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 { makeStyles } from '@material-ui/core'
|
||||
import { gql } from 'apollo-boost'
|
||||
import gql from 'graphql-tag'
|
||||
import moment from 'moment'
|
||||
import * as R from 'ramda'
|
||||
import React from 'react'
|
||||
|
|
@ -20,6 +20,8 @@ const GET_MACHINES = gql`
|
|||
machines {
|
||||
name
|
||||
deviceId
|
||||
lastPing
|
||||
pairedAt
|
||||
paired
|
||||
cashbox
|
||||
cassette1
|
||||
|
|
@ -42,35 +44,28 @@ const MachineStatus = () => {
|
|||
const elements = [
|
||||
{
|
||||
header: 'Machine Name',
|
||||
width: 232,
|
||||
width: 250,
|
||||
size: 'sm',
|
||||
textAlign: 'left',
|
||||
view: m => m.name
|
||||
},
|
||||
{
|
||||
header: 'Status',
|
||||
width: 349,
|
||||
width: 350,
|
||||
size: 'sm',
|
||||
textAlign: 'left',
|
||||
view: m => <MainStatus statuses={m.statuses} />
|
||||
},
|
||||
{
|
||||
header: 'Last ping',
|
||||
width: 192,
|
||||
width: 200,
|
||||
size: 'sm',
|
||||
textAlign: 'left',
|
||||
view: m => moment(m.lastPing).fromNow()
|
||||
},
|
||||
{
|
||||
header: 'Ping Time',
|
||||
width: 155,
|
||||
size: 'sm',
|
||||
textAlign: 'left',
|
||||
view: m => m.pingTime || 'unknown'
|
||||
view: m => (m.lastPing ? moment(m.lastPing).fromNow() : 'unknown')
|
||||
},
|
||||
{
|
||||
header: 'Software Version',
|
||||
width: 201,
|
||||
width: 200,
|
||||
size: 'sm',
|
||||
textAlign: 'left',
|
||||
view: m => m.softwareVersion || 'unknown'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { gql } from 'apollo-boost'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ const FiatBalanceOverrides = ({ section }) => {
|
|||
display: 'Cash-out 1',
|
||||
width: 155,
|
||||
textAlign: 'right',
|
||||
doubleHeader: 'Cash-out (Cassette Empty)',
|
||||
bold: true,
|
||||
input: TextInputFormik,
|
||||
suffix: 'notes'
|
||||
|
|
@ -81,6 +82,7 @@ const FiatBalanceOverrides = ({ section }) => {
|
|||
display: 'Cash-out 2',
|
||||
width: 155,
|
||||
textAlign: 'right',
|
||||
doubleHeader: 'Cash-out (Cassette Empty)',
|
||||
bold: true,
|
||||
input: TextInputFormik,
|
||||
suffix: 'notes'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { gql } from 'apollo-boost'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState, memo } from 'react'
|
||||
|
||||
|
|
@ -47,7 +47,6 @@ const CoinATMRadar = memo(() => {
|
|||
|
||||
const classes = useStyles()
|
||||
|
||||
// TODO: treat errors on useMutation and useQuery
|
||||
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
||||
onCompleted: configResponse =>
|
||||
setCoinAtmRadarConfig(
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import { gql } from 'apollo-boost'
|
||||
import classnames from 'classnames'
|
||||
import { Form, Formik, Field as FormikField } from 'formik'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
|
@ -22,14 +22,14 @@ import {
|
|||
} from './OperatorInfo.styles'
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
active: Yup.boolean().required(),
|
||||
name: Yup.string().required(),
|
||||
phone: Yup.string().required(),
|
||||
active: Yup.boolean(),
|
||||
name: Yup.string(),
|
||||
phone: Yup.string(),
|
||||
email: Yup.string()
|
||||
.email('Please enter a valid email address')
|
||||
.required(),
|
||||
website: Yup.string().required(),
|
||||
companyNumber: Yup.string().required()
|
||||
website: Yup.string(),
|
||||
companyNumber: Yup.string()
|
||||
})
|
||||
|
||||
const fieldStyles = {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ const styles = {
|
|||
height: '100%'
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
marginLeft: 48,
|
||||
paddingTop: 15
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// import { makeStyles } from '@material-ui/core/styles'
|
||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { gql } from 'apollo-boost'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState, memo } from 'react'
|
||||
|
||||
|
|
@ -49,10 +49,10 @@ const receiptPrintingOptions = [
|
|||
code: 'off',
|
||||
display: 'Off'
|
||||
},
|
||||
{
|
||||
code: 'optional',
|
||||
display: 'Optional (ask user)'
|
||||
},
|
||||
// {
|
||||
// code: 'optional',
|
||||
// display: 'Optional (ask user)'
|
||||
// },
|
||||
{
|
||||
code: 'on',
|
||||
display: 'On'
|
||||
|
|
@ -64,10 +64,8 @@ const ReceiptPrinting = memo(() => {
|
|||
|
||||
// const classes = useStyles()
|
||||
|
||||
// TODO: treat errors on useMutation and useQuery
|
||||
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
||||
onCompleted: configResponse => {
|
||||
console.log(configResponse.saveConfig)
|
||||
return setReceiptPrintingConfig(
|
||||
fromNamespace(namespaces.RECEIPT, configResponse.saveConfig)
|
||||
)
|
||||
|
|
@ -87,7 +85,6 @@ const ReceiptPrinting = memo(() => {
|
|||
})
|
||||
|
||||
if (!receiptPrintingConfig) return null
|
||||
console.log(receiptPrintingConfig)
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import { gql } from 'apollo-boost'
|
||||
import classnames from 'classnames'
|
||||
import { Form, Formik, Field } from 'formik'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
|
@ -83,25 +83,30 @@ const TermsConditions = () => {
|
|||
{
|
||||
name: 'title',
|
||||
label: 'Screen title',
|
||||
value: formData.title ?? ''
|
||||
value: formData.title ?? '',
|
||||
width: 282
|
||||
},
|
||||
{
|
||||
name: 'text',
|
||||
label: 'Text content',
|
||||
value: formData.text ?? '',
|
||||
multiline: true
|
||||
width: 502,
|
||||
multiline: true,
|
||||
rows: 6
|
||||
},
|
||||
{
|
||||
name: 'acceptButtonText',
|
||||
label: 'Accept button text',
|
||||
value: formData.acceptButtonText ?? '',
|
||||
placeholder: 'I accept'
|
||||
placeholder: 'I accept',
|
||||
width: 282
|
||||
},
|
||||
{
|
||||
name: 'cancelButtonText',
|
||||
label: 'Cancel button text',
|
||||
value: formData.cancelButtonText ?? '',
|
||||
placeholder: 'Cancel'
|
||||
placeholder: 'Cancel',
|
||||
width: 282
|
||||
}
|
||||
]
|
||||
|
||||
|
|
@ -145,10 +150,12 @@ const TermsConditions = () => {
|
|||
id={f.name}
|
||||
name={f.name}
|
||||
component={TextInputFormik}
|
||||
width={f.width}
|
||||
placeholder={f.placeholder}
|
||||
type="text"
|
||||
label={f.label}
|
||||
multiline={f.multiline}
|
||||
rows={f.rows}
|
||||
rowsMax="6"
|
||||
onFocus={() => setError(null)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import { gql } from 'apollo-boost'
|
||||
import gql from 'graphql-tag'
|
||||
import moment from 'moment'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
|
|
@ -8,7 +8,7 @@ import React, { useState } from 'react'
|
|||
import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper'
|
||||
import Title from 'src/components/Title'
|
||||
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 {
|
||||
Table,
|
||||
|
|
@ -20,8 +20,8 @@ import {
|
|||
} from 'src/components/table'
|
||||
import { Info3 } from 'src/components/typography'
|
||||
import typographyStyles from 'src/components/typography/styles'
|
||||
import { ReactComponent as DownloadActive } from 'src/styling/icons/button/download/white.svg'
|
||||
import { ReactComponent as Download } from 'src/styling/icons/button/download/zodiac.svg'
|
||||
import { ReactComponent as WhiteShareIcon } from 'src/styling/icons/circle buttons/share/white.svg'
|
||||
import { ReactComponent as ShareIcon } from 'src/styling/icons/circle buttons/share/zodiac.svg'
|
||||
import { offColor } from 'src/styling/variables'
|
||||
|
||||
import logsStyles from './Logs.styles'
|
||||
|
|
@ -41,7 +41,6 @@ const localStyles = {
|
|||
margin: 'auto 0 auto 0'
|
||||
},
|
||||
headerLine2: {
|
||||
height: 60,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: 24
|
||||
|
|
@ -61,24 +60,14 @@ const formatDate = date => {
|
|||
return moment(date).format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
|
||||
const GET_VERSION = gql`
|
||||
query {
|
||||
serverVersion
|
||||
}
|
||||
`
|
||||
|
||||
const GET_UPTIME = gql`
|
||||
const GET_DATA = gql`
|
||||
{
|
||||
serverVersion
|
||||
uptime {
|
||||
name
|
||||
state
|
||||
uptime
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const GET_SERVER_LOGS = gql`
|
||||
{
|
||||
serverLogs {
|
||||
logLevel
|
||||
id
|
||||
|
|
@ -87,7 +76,6 @@ const GET_SERVER_LOGS = gql`
|
|||
}
|
||||
}
|
||||
`
|
||||
|
||||
const SUPPORT_LOGS = gql`
|
||||
mutation ServerSupportLogs {
|
||||
serverSupportLogs {
|
||||
|
|
@ -101,30 +89,19 @@ const Logs = () => {
|
|||
|
||||
const [saveMessage, setSaveMessage] = useState(null)
|
||||
const [logLevel, setLogLevel] = useState(SHOW_ALL)
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
|
||||
const { data: version } = useQuery(GET_VERSION)
|
||||
const serverVersion = version?.serverVersion
|
||||
|
||||
const { data: uptimeResponse } = useQuery(GET_UPTIME)
|
||||
const processStates = uptimeResponse?.uptime ?? []
|
||||
|
||||
const { data: logsResponse } = useQuery(GET_SERVER_LOGS, {
|
||||
fetchPolicy: 'no-cache',
|
||||
const { data } = useQuery(GET_DATA, {
|
||||
onCompleted: () => setSaveMessage('')
|
||||
})
|
||||
|
||||
const serverVersion = data?.serverVersion
|
||||
const processStates = data?.uptime ?? []
|
||||
|
||||
const [sendSnapshot, { loading }] = useMutation(SUPPORT_LOGS, {
|
||||
onError: () => setSaveMessage('Failure saving 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(
|
||||
R.prepend(SHOW_ALL),
|
||||
R.uniq,
|
||||
|
|
@ -137,27 +114,19 @@ const Logs = () => {
|
|||
<div className={classes.titleWrapper}>
|
||||
<div className={classes.titleAndButtonsContainer}>
|
||||
<Title>Server</Title>
|
||||
{logsResponse && (
|
||||
{data && (
|
||||
<div className={classes.buttonsWrapper}>
|
||||
<FeatureButton
|
||||
Icon={Download}
|
||||
InverseIcon={DownloadActive}
|
||||
aria-describedby={id}
|
||||
variant="contained"
|
||||
onClick={handleOpenRangePicker}
|
||||
/>
|
||||
<LogsDowloaderPopover
|
||||
title="Download logs"
|
||||
name="server-logs"
|
||||
id={id}
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
logs={logsResponse.serverLogs}
|
||||
logs={data.serverLogs}
|
||||
getTimestamp={log => log.timestamp}
|
||||
/>
|
||||
<SimpleButton
|
||||
className={classes.button}
|
||||
className={classes.shareButton}
|
||||
disabled={loading}
|
||||
Icon={ShareIcon}
|
||||
InverseIcon={WhiteShareIcon}
|
||||
onClick={sendSnapshot}>
|
||||
Share with Lamassu
|
||||
</SimpleButton>
|
||||
|
|
@ -170,11 +139,11 @@ const Logs = () => {
|
|||
</div>
|
||||
</div>
|
||||
<div className={classes.headerLine2}>
|
||||
{logsResponse && (
|
||||
{data && (
|
||||
<Select
|
||||
onSelectedItemChange={setLogLevel}
|
||||
label="Level"
|
||||
items={getLogLevels(logsResponse)}
|
||||
items={getLogLevels(data)}
|
||||
default={SHOW_ALL}
|
||||
selectedItem={logLevel}
|
||||
/>
|
||||
|
|
@ -197,8 +166,8 @@ const Logs = () => {
|
|||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{logsResponse &&
|
||||
logsResponse.serverLogs
|
||||
{data &&
|
||||
data.serverLogs
|
||||
.filter(
|
||||
log => logLevel === SHOW_ALL || log.logLevel === logLevel
|
||||
)
|
||||
|
|
|
|||
|
|
@ -45,10 +45,11 @@ const FormRenderer = ({
|
|||
onSubmit={save}>
|
||||
<Form className={classes.form}>
|
||||
<Grid container spacing={3} className={classes.grid}>
|
||||
{elements.map(({ component, code, display }) => (
|
||||
{elements.map(({ component, code, display, inputProps }) => (
|
||||
<Grid item xs={12} key={code}>
|
||||
<FastField
|
||||
component={component}
|
||||
{...inputProps}
|
||||
name={code}
|
||||
label={display}
|
||||
fullWidth={true}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { makeStyles, Grid } from '@material-ui/core'
|
||||
import { gql } from 'apollo-boost'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import * as Yup from 'yup'
|
||||
|
||||
import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
|
||||
import TextInputFormik from 'src/components/inputs/formik/TextInput'
|
||||
import {
|
||||
TextInput,
|
||||
SecretInput,
|
||||
Autocomplete
|
||||
} from 'src/components/inputs/formik'
|
||||
|
||||
const isDefined = it => it && it.length
|
||||
|
||||
|
|
@ -13,65 +16,71 @@ export default {
|
|||
{
|
||||
code: 'token',
|
||||
display: 'API Token',
|
||||
component: TextInputFormik,
|
||||
component: TextInput,
|
||||
face: true,
|
||||
long: true
|
||||
},
|
||||
{
|
||||
code: 'environment',
|
||||
display: 'Environment',
|
||||
component: TextInputFormik,
|
||||
component: Autocomplete,
|
||||
inputProps: {
|
||||
options: ['prod', 'test']
|
||||
// valueProp: 'deviceId',
|
||||
// getLabel: R.path(['name']),
|
||||
// limit: null
|
||||
},
|
||||
face: true
|
||||
},
|
||||
{
|
||||
code: 'btcWalletId',
|
||||
display: 'BTC Wallet ID',
|
||||
component: TextInputFormik
|
||||
component: TextInput
|
||||
},
|
||||
{
|
||||
code: 'btcWalletPassphrase',
|
||||
display: 'BTC Wallet Passphrase',
|
||||
component: SecretInputFormik
|
||||
component: SecretInput
|
||||
},
|
||||
{
|
||||
code: 'ltcWalletId',
|
||||
display: 'LTC Wallet ID',
|
||||
component: TextInputFormik
|
||||
component: TextInput
|
||||
},
|
||||
{
|
||||
code: 'ltcWalletPassphrase',
|
||||
display: 'LTC Wallet Passphrase',
|
||||
component: SecretInputFormik
|
||||
component: SecretInput
|
||||
},
|
||||
{
|
||||
code: 'zecWalletId',
|
||||
display: 'ZEC Wallet ID',
|
||||
component: TextInputFormik
|
||||
component: TextInput
|
||||
},
|
||||
{
|
||||
code: 'zecWalletPassphrase',
|
||||
display: 'ZEC Wallet Passphrase',
|
||||
component: SecretInputFormik
|
||||
component: SecretInput
|
||||
},
|
||||
{
|
||||
code: 'bchWalletId',
|
||||
display: 'BCH Wallet ID',
|
||||
component: TextInputFormik
|
||||
component: TextInput
|
||||
},
|
||||
{
|
||||
code: 'bchWalletPassphrase',
|
||||
display: 'BCH Wallet Passphrase',
|
||||
component: SecretInputFormik
|
||||
component: SecretInput
|
||||
},
|
||||
{
|
||||
code: 'dashWalletId',
|
||||
display: 'DASH Wallet ID',
|
||||
component: TextInputFormik
|
||||
component: TextInput
|
||||
},
|
||||
{
|
||||
code: 'dashWalletPassphrase',
|
||||
display: 'DASH Wallet Passphrase',
|
||||
component: SecretInputFormik
|
||||
component: SecretInput
|
||||
}
|
||||
],
|
||||
validationSchema: Yup.object().shape({
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ export default {
|
|||
elements: [
|
||||
{
|
||||
code: 'apiKey',
|
||||
display: 'API Key',
|
||||
display: 'Project ID',
|
||||
component: TextInputFormik,
|
||||
face: true,
|
||||
long: true
|
||||
},
|
||||
{
|
||||
code: 'apiSecret',
|
||||
display: 'API Secret',
|
||||
display: 'Project Secret',
|
||||
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 classnames from 'classnames'
|
||||
import moment from 'moment'
|
||||
import React, { memo } from 'react'
|
||||
|
||||
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 CardIdIcon } from 'src/styling/icons/ID/card/zodiac.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 TxInIcon } from 'src/styling/icons/direction/cash-in.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 { onlyFirstToUpper } from 'src/utils/string'
|
||||
|
||||
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 classes = labelUseStyles()
|
||||
const formatAddress = (address = '') => address.replace(/(.{5})/g, '$1 ')
|
||||
|
||||
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 classes = detailsUseStyles()
|
||||
const getStatus = it => {
|
||||
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 crypto = toUnit(new BigNumber(tx.cryptoAtoms), tx.cryptoCode).toFormat(
|
||||
5
|
||||
)
|
||||
const crypto = toUnit(new BigNumber(tx.cryptoAtoms), tx.cryptoCode)
|
||||
const commissionPercentage = Number.parseFloat(tx.commissionPercentage, 2)
|
||||
const commission = fiat * commissionPercentage
|
||||
const exchangeRate = Number(fiat / crypto).toFixed(3)
|
||||
const displayExRate = `1 ${tx.cryptoCode} = ${exchangeRate} ${tx.fiatCode}`
|
||||
|
||||
const customer = tx.customerIdCardData && {
|
||||
name: `${onlyFirstToUpper(
|
||||
tx.customerIdCardData.firstName
|
||||
|
|
@ -52,33 +74,25 @@ const DetailsRow = ({ it: tx, ...props }) => {
|
|||
)
|
||||
}
|
||||
|
||||
const formatAddress = (address = '') => {
|
||||
return address.replace(/(.{5})/g, '$1 ')
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classnames(classes.row)}>
|
||||
<div className={classnames(classes.col, classes.col1)}>
|
||||
{/* Column 1 */}
|
||||
<div className={classes.innerRow}>
|
||||
<div>
|
||||
<div className={classes.row}>
|
||||
<div className={classes.direction}>
|
||||
<Label>Direction</Label>
|
||||
<div>
|
||||
<span className={classes.txIcon}>
|
||||
{tx.txClass === 'cashOut' ? <TxOutIcon /> : <TxInIcon />}
|
||||
</span>
|
||||
<span>
|
||||
{tx.txClass === 'cashOut' ? 'Cash-out' : 'Cash-in'}
|
||||
</span>
|
||||
<span>{tx.txClass === 'cashOut' ? 'Cash-out' : 'Cash-in'}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.availableIds}>
|
||||
<Label>Available IDs</Label>
|
||||
<div>
|
||||
<Box display="flex" flexDirection="row">
|
||||
{tx.customerPhone && (
|
||||
<IDButton
|
||||
className={classes.idButton}
|
||||
name="phone"
|
||||
Icon={PhoneIdIcon}
|
||||
InverseIcon={PhoneIdInverseIcon}>
|
||||
|
|
@ -87,14 +101,21 @@ const DetailsRow = ({ it: tx, ...props }) => {
|
|||
)}
|
||||
{tx.customerIdCardPhotoPath && !tx.customerIdCardData && (
|
||||
<IDButton
|
||||
popoverClassname={classes.popover}
|
||||
className={classes.idButton}
|
||||
name="card"
|
||||
Icon={CardIdIcon}
|
||||
InverseIcon={CardIdInverseIcon}>
|
||||
<img alt="" src={tx.customerIdCardPhotoPath} />
|
||||
<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}>
|
||||
|
|
@ -118,10 +139,6 @@ const DetailsRow = ({ it: tx, ...props }) => {
|
|||
<Label>ID number</Label>
|
||||
<div>{customer.idCardNumber}</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Gender</Label>
|
||||
<div />
|
||||
</div>
|
||||
<div>
|
||||
<Label>Expiration date</Label>
|
||||
<div>{customer.idCardExpirationDate}</div>
|
||||
|
|
@ -135,87 +152,62 @@ const DetailsRow = ({ it: tx, ...props }) => {
|
|||
name="cam"
|
||||
Icon={CamIdIcon}
|
||||
InverseIcon={CamIdInverseIcon}>
|
||||
<img alt="" src={tx.customerFrontCameraPath} />
|
||||
<img
|
||||
src={`${URI}/front-camera-photo/${tx.customerFrontCameraPath}`}
|
||||
alt=""
|
||||
/>
|
||||
</IDButton>
|
||||
)}
|
||||
</Box>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classnames(classes.col, classes.col2)}>
|
||||
{/* Column 2 */}
|
||||
<div className={classes.innerRow}>
|
||||
<div>
|
||||
<div className={classes.exchangeRate}>
|
||||
<Label>Exchange rate</Label>
|
||||
<div>
|
||||
{`1 ${tx.cryptoCode} = ${Number(fiat / crypto).toFixed(3)} ${
|
||||
tx.fiatCode
|
||||
}`}
|
||||
<div>{crypto > 0 ? displayExRate : '-'}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.commissionWrapper}>
|
||||
<div className={classes.commission}>
|
||||
<Label>Commission</Label>
|
||||
<div>
|
||||
{`${commission} ${tx.fiatCode} (${commissionPercentage *
|
||||
100} %)`}
|
||||
{`${commission} ${tx.fiatCode} (${commissionPercentage * 100} %)`}
|
||||
</div>
|
||||
</div>
|
||||
{tx.txClass === 'cashIn' && (
|
||||
<div className={classes.innerRow}>
|
||||
<div>
|
||||
<Label>Fixed fee</Label>
|
||||
<div>{Number.parseFloat(tx.cashInFee)}</div>
|
||||
<div>
|
||||
{tx.txClass === 'cashIn'
|
||||
? `${Number.parseFloat(tx.cashInFee)} ${tx.fiatCode}`
|
||||
: 'N/A'}
|
||||
</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={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 className={classnames(classes.row)}>
|
||||
<div className={classnames(classes.col, classes.col1)}>
|
||||
{/* Column 1 */}
|
||||
<div className={classes.innerRow}>
|
||||
<div>
|
||||
<Label>BTC address</Label>
|
||||
<div>
|
||||
<CopyToClipboard className={classes.cryptoAddr}>
|
||||
{formatAddress(addr)}
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classnames(classes.col, classes.col2)}>
|
||||
{/* Column 2 */}
|
||||
<div className={classes.innerRow}>
|
||||
<div>
|
||||
<Label>Transaction ID</Label>
|
||||
<div>
|
||||
<CopyToClipboard className={classes.txId}>
|
||||
{txHash}
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classnames(classes.col, classes.col3)}>
|
||||
{/* Column 3 */}
|
||||
<div className={classes.innerRow}>
|
||||
<div>
|
||||
<div className={classes.sessionId}>
|
||||
<Label>Session ID</Label>
|
||||
<CopyToClipboard className={classes.sessionId}>
|
||||
{tx.id}
|
||||
</CopyToClipboard>
|
||||
<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>
|
||||
</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 { makeStyles } from '@material-ui/core/styles'
|
||||
import { gql } from 'apollo-boost'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import gql from 'graphql-tag'
|
||||
import moment from 'moment'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
import React from 'react'
|
||||
|
||||
import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper'
|
||||
import Title from 'src/components/Title'
|
||||
import { FeatureButton } from 'src/components/buttons'
|
||||
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 TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
|
||||
import { toUnit } from 'src/utils/coin'
|
||||
|
|
@ -21,7 +18,6 @@ import { mainStyles } from './Transactions.styles'
|
|||
|
||||
const useStyles = makeStyles(mainStyles)
|
||||
|
||||
// TODO customerIdCardData
|
||||
const GET_TRANSACTIONS = gql`
|
||||
{
|
||||
transactions {
|
||||
|
|
@ -30,7 +26,12 @@ const GET_TRANSACTIONS = gql`
|
|||
txHash
|
||||
toAddress
|
||||
commissionPercentage
|
||||
expired
|
||||
machineName
|
||||
operatorCompleted
|
||||
sendConfirmed
|
||||
dispense
|
||||
hasError: error
|
||||
deviceId
|
||||
fiat
|
||||
cashInFee
|
||||
|
|
@ -49,8 +50,6 @@ const GET_TRANSACTIONS = gql`
|
|||
`
|
||||
|
||||
const Transactions = () => {
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
|
||||
const classes = useStyles()
|
||||
|
||||
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 (
|
||||
<>
|
||||
<div className={classes.titleWrapper}>
|
||||
|
|
@ -145,22 +133,11 @@ const Transactions = () => {
|
|||
<Title>Transactions</Title>
|
||||
{txResponse && (
|
||||
<div className={classes.buttonsWrapper}>
|
||||
<FeatureButton
|
||||
Icon={Download}
|
||||
InverseIcon={DownloadInverseIcon}
|
||||
aria-describedby={id}
|
||||
variant="contained"
|
||||
onClick={handleOpenRangePicker}
|
||||
/>
|
||||
<LogsDowloaderPopover
|
||||
title="Download logs"
|
||||
name="transactions"
|
||||
id={id}
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
logs={txResponse.transactions}
|
||||
getTimestamp={tx => tx.created}
|
||||
onClose={handleCloseRangePicker}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import typographyStyles from 'src/components/typography/styles'
|
||||
import { offColor, white } from 'src/styling/variables'
|
||||
import baseStyles from 'src/pages/Logs.styles'
|
||||
import { offColor, white } from 'src/styling/variables'
|
||||
|
||||
const { label1, mono, p } = typographyStyles
|
||||
const { titleWrapper, titleAndButtonsContainer, buttonsWrapper } = baseStyles
|
||||
|
|
@ -34,66 +34,6 @@ const cpcStyles = {
|
|||
}
|
||||
|
||||
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: {
|
||||
extend: p,
|
||||
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 { makeStyles } from '@material-ui/core'
|
||||
import { gql } from 'apollo-boost'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
import { v4 } from 'uuid'
|
||||
|
||||
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 { 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 Wizard from './Wizard'
|
||||
|
|
@ -60,13 +58,6 @@ const Triggers = () => {
|
|||
<div className={classes.titleWrapper}>
|
||||
<div className={classes.titleAndButtonsContainer}>
|
||||
<Title>Compliance Triggers</Title>
|
||||
<div className={classes.buttonsWrapper}>
|
||||
<FeatureButton
|
||||
Icon={Configure}
|
||||
InverseIcon={ConfigureInverseIcon}
|
||||
variant="contained"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.headerLabels}>
|
||||
<Link color="primary" onClick={() => setWizard(true)}>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { gql } from 'apollo-boost'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
|
|
|
|||
|
|
@ -36,10 +36,10 @@ const tree = [
|
|||
},
|
||||
children: [
|
||||
{
|
||||
key: 'logs',
|
||||
label: 'Logs',
|
||||
route: '/maintenance/logs',
|
||||
component: MachineLogs
|
||||
key: 'cashboxes',
|
||||
label: 'Cashboxes',
|
||||
route: '/maintenance/cashboxes',
|
||||
component: Cashboxes
|
||||
},
|
||||
{
|
||||
key: 'funding',
|
||||
|
|
@ -48,10 +48,10 @@ const tree = [
|
|||
component: Funding
|
||||
},
|
||||
{
|
||||
key: 'server-logs',
|
||||
label: 'Server',
|
||||
route: '/maintenance/server-logs',
|
||||
component: ServerLogs
|
||||
key: 'logs',
|
||||
label: 'Machine logs',
|
||||
route: '/maintenance/logs',
|
||||
component: MachineLogs
|
||||
},
|
||||
{
|
||||
key: 'machine-status',
|
||||
|
|
@ -60,10 +60,10 @@ const tree = [
|
|||
component: MachineStatus
|
||||
},
|
||||
{
|
||||
key: 'cashboxes',
|
||||
label: 'Cashboxes',
|
||||
route: '/maintenance/cashboxes',
|
||||
component: Cashboxes
|
||||
key: 'server-logs',
|
||||
label: 'Server',
|
||||
route: '/maintenance/server-logs',
|
||||
component: ServerLogs
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -83,7 +83,7 @@ const tree = [
|
|||
},
|
||||
{
|
||||
key: namespaces.LOCALE,
|
||||
label: 'Locale',
|
||||
label: 'Locales',
|
||||
route: '/settings/locale',
|
||||
component: Locales
|
||||
},
|
||||
|
|
@ -93,12 +93,6 @@ const tree = [
|
|||
route: '/settings/cash-out',
|
||||
component: Cashout
|
||||
},
|
||||
{
|
||||
key: 'services',
|
||||
label: '3rd party services',
|
||||
route: '/settings/3rd-party-services',
|
||||
component: Services
|
||||
},
|
||||
{
|
||||
key: namespaces.NOTIFICATIONS,
|
||||
label: 'Notifications',
|
||||
|
|
@ -106,16 +100,22 @@ const tree = [
|
|||
component: Notifications
|
||||
},
|
||||
{
|
||||
key: namespaces.OPERATOR_INFO,
|
||||
label: 'Operator Info',
|
||||
route: '/settings/operator-info',
|
||||
component: OperatorInfo
|
||||
key: 'services',
|
||||
label: '3rd party services',
|
||||
route: '/settings/3rd-party-services',
|
||||
component: Services
|
||||
},
|
||||
{
|
||||
key: namespaces.WALLETS,
|
||||
label: 'Wallet',
|
||||
route: '/settings/wallet-settings',
|
||||
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>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<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>
|
||||
<g id="Styleguide" stroke="none" stroke-width="1" fill-rule="evenodd">
|
||||
<g id="icon/action/arrow/regular">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</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>
|
||||
</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 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">
|
||||
<!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
|
||||
<title>icon/action/arrow/regular/zodiac</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<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>
|
||||
</defs>
|
||||
<g id="icon/action/arrow/regular/zodiac" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
<svg
|
||||
width="13"
|
||||
height="8"
|
||||
viewBox="0 0 13 8"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<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>
|
||||
<g fill="none">
|
||||
<mask fill="white">
|
||||
<use xlink:href="#path-1" />
|
||||
</mask>
|
||||
<use id="Mask" fill="#1B2559" fill-rule="nonzero" xlink:href="#path-1"></use>
|
||||
</g>
|
||||
<use xlink:href="#path-1" fill="#1B2559" />
|
||||
</g>
|
||||
</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:
|
||||
- Large input fields wiggle on editable table edit/non-edit mode change.
|
||||
- all (machines/coins/...) should be a option on some overrides
|
||||
Main menu:
|
||||
- do not change fonts on hover in the main menu
|
||||
- make the clickable button bigger, not just text
|
||||
|
||||
Compliance:
|
||||
- Reject Address Reuse missing
|
||||
Overall:
|
||||
- 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:
|
||||
- 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:
|
||||
- There's no place to pick a third party provider anymore.
|
||||
|
||||
Cashout:
|
||||
- I've just added a zero conf limit column. Should it be like this?
|
||||
- There's no place to pick a third party provider anymore. (sms.js, email.js)
|
||||
|
||||
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:
|
||||
- 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
|
||||
|
||||
Compliance:
|
||||
- Reject Address Reuse missing
|
||||
- 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
|
||||
|
||||
Other stuff:
|
||||
- sms.js
|
||||
- email.js
|
||||
Customers:
|
||||
- Should add id and make it main part of the table? Name is not common at all
|
||||
|
||||
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