feat: downloadable tx summary and logs
This commit is contained in:
parent
bd9615c5ea
commit
92b6093a07
7 changed files with 311 additions and 37 deletions
|
|
@ -5,17 +5,17 @@ const transactions = require('../../services/transactions')
|
||||||
const anonymous = require('../../../constants').anonymousCustomer
|
const anonymous = require('../../../constants').anonymousCustomer
|
||||||
|
|
||||||
const transactionsLoader = new DataLoader(ids => transactions.getCustomerTransactionsBatch(ids))
|
const transactionsLoader = new DataLoader(ids => transactions.getCustomerTransactionsBatch(ids))
|
||||||
const tx_logFields = ["txClass", "id", "deviceId", "toAddress", "cryptoAtoms",
|
const txLogFields = ['txClass', 'id', 'deviceId', 'toAddress', 'cryptoAtoms',
|
||||||
"cryptoCode", "fiat", "fiatCode", "fee", "status",
|
'cryptoCode', 'fiat', 'fiatCode', 'fee', 'status',
|
||||||
"dispense", "notified", "redeem", "phone", "error",
|
'dispense', 'notified', 'redeem', 'phone', 'error',
|
||||||
"created", "confirmedAt", "hdIndex", "swept", "timedout",
|
'created', 'confirmedAt', 'hdIndex', 'swept', 'timedout',
|
||||||
"dispenseConfirmed", "provisioned1", "provisioned2",
|
'dispenseConfirmed', 'provisioned1', 'provisioned2',
|
||||||
"denomination1", "denomination2", "errorCode", "customerId",
|
'denomination1', 'denomination2', 'errorCode', 'customerId',
|
||||||
"txVersion", "publishedAt", "termsAccepted", "layer2Address",
|
'txVersion', 'publishedAt', 'termsAccepted', 'layer2Address',
|
||||||
"commissionPercentage", "rawTickerPrice", "receivedCryptoAtoms",
|
'commissionPercentage', 'rawTickerPrice', 'receivedCryptoAtoms',
|
||||||
"discount", "txHash", "customerPhone", "customerIdCardDataNumber",
|
'discount', 'txHash', 'customerPhone', 'customerIdCardDataNumber',
|
||||||
"customerIdCardDataExpiration", "customerIdCardData", "customerName",
|
'customerIdCardDataExpiration', 'customerIdCardData', 'customerName',
|
||||||
"customerFrontCameraPath", "customerIdCardPhotoPath", "expired", "machineName"]
|
'customerFrontCameraPath', 'customerIdCardPhotoPath', 'expired', 'machineName']
|
||||||
|
|
||||||
const resolvers = {
|
const resolvers = {
|
||||||
Customer: {
|
Customer: {
|
||||||
|
|
@ -28,7 +28,11 @@ const resolvers = {
|
||||||
transactions: (...[, { from, until, limit, offset, deviceId }]) =>
|
transactions: (...[, { from, until, limit, offset, deviceId }]) =>
|
||||||
transactions.batch(from, until, limit, offset, deviceId),
|
transactions.batch(from, until, limit, offset, deviceId),
|
||||||
transactionsCsv: (...[, { from, until, limit, offset }]) =>
|
transactionsCsv: (...[, { from, until, limit, offset }]) =>
|
||||||
transactions.batch(from, until, limit, offset).then(data => parseAsync(data, {fields: tx_logFields}))
|
transactions.batch(from, until, limit, offset).then(data => parseAsync(data, { fields: txLogFields })),
|
||||||
|
transactionCsv: (...[, { id, txClass }]) =>
|
||||||
|
transactions.getTx(id, txClass).then(parseAsync),
|
||||||
|
txAssociatedDataCsv: (...[, { id, txClass }]) =>
|
||||||
|
transactions.getTxAssociatedData(id, txClass).then(parseAsync)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,8 @@ const typeDef = gql`
|
||||||
type Query {
|
type Query {
|
||||||
transactions(from: Date, until: Date, limit: Int, offset: Int, deviceId: ID): [Transaction] @auth
|
transactions(from: Date, until: Date, limit: Int, offset: Int, deviceId: ID): [Transaction] @auth
|
||||||
transactionsCsv(from: Date, until: Date, limit: Int, offset: Int): String @auth
|
transactionsCsv(from: Date, until: Date, limit: Int, offset: Int): String @auth
|
||||||
|
transactionCsv(id: ID, txClass: String): String @auth
|
||||||
|
txAssociatedDataCsv(id: ID, txClass: String): String @auth
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -162,4 +162,33 @@ function cancel (txId) {
|
||||||
.then(() => single(txId))
|
.then(() => single(txId))
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { batch, single, cancel, getCustomerTransactionsBatch }
|
function getTx (txId, txClass) {
|
||||||
|
const cashInSql = `select 'cashIn' as tx_class, txs.*,
|
||||||
|
((not txs.send_confirmed) and (txs.created <= now() - interval $1)) as expired
|
||||||
|
from cash_in_txs as txs
|
||||||
|
where txs.id=$2`
|
||||||
|
|
||||||
|
const cashOutSql = `select 'cashOut' as tx_class,
|
||||||
|
txs.*,
|
||||||
|
(extract(epoch from (now() - greatest(txs.created, txs.confirmed_at))) * 1000) >= $2 as expired
|
||||||
|
from cash_out_txs txs
|
||||||
|
where txs.id=$1`
|
||||||
|
|
||||||
|
if (txClass === 'cashIn') {
|
||||||
|
return db.oneOrNone(cashInSql, [cashInTx.PENDING_INTERVAL, txId])
|
||||||
|
} else {
|
||||||
|
return db.oneOrNone(cashOutSql, [txId, REDEEMABLE_AGE])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTxAssociatedData (txId, txClass) {
|
||||||
|
const billsSql = `select 'bills' as bills, b.* from bills b where cash_in_txs_id = $1`
|
||||||
|
const actionsSql = `select 'cash_out_actions' as cash_out_actions, actions.* from cash_out_actions actions where tx_id = $1`
|
||||||
|
if (txClass === 'cashIn') {
|
||||||
|
return db.manyOrNone(billsSql, [txId])
|
||||||
|
} else {
|
||||||
|
return db.manyOrNone(actionsSql, [txId])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { batch, single, cancel, getCustomerTransactionsBatch, getTx, getTxAssociatedData }
|
||||||
|
|
|
||||||
207
new-lamassu-admin/package-lock.json
generated
207
new-lamassu-admin/package-lock.json
generated
|
|
@ -9513,8 +9513,7 @@
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
|
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"cosmiconfig": {
|
"cosmiconfig": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
|
|
@ -14315,6 +14314,11 @@
|
||||||
"integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
|
"integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"immediate": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||||
|
"integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
|
||||||
|
},
|
||||||
"immer": {
|
"immer": {
|
||||||
"version": "1.10.0",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz",
|
||||||
|
|
@ -14420,13 +14424,12 @@
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
|
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"inline-style-prefixer": {
|
"inline-style-prefixer": {
|
||||||
|
|
@ -14437,6 +14440,133 @@
|
||||||
"css-in-js-utils": "^2.0.0"
|
"css-in-js-utils": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"inquirer": {
|
||||||
|
"version": "7.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz",
|
||||||
|
"integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-escapes": "^4.2.1",
|
||||||
|
"chalk": "^4.1.0",
|
||||||
|
"cli-cursor": "^3.1.0",
|
||||||
|
"cli-width": "^3.0.0",
|
||||||
|
"external-editor": "^3.0.3",
|
||||||
|
"figures": "^3.0.0",
|
||||||
|
"lodash": "^4.17.19",
|
||||||
|
"mute-stream": "0.0.8",
|
||||||
|
"run-async": "^2.4.0",
|
||||||
|
"rxjs": "^6.6.0",
|
||||||
|
"string-width": "^4.1.0",
|
||||||
|
"strip-ansi": "^6.0.0",
|
||||||
|
"through": "^2.3.6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chalk": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"emoji-regex": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"eslint-scope": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
|
||||||
|
"requires": {
|
||||||
|
"esrecurse": "^4.3.0",
|
||||||
|
"estraverse": "^4.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"eslint-utils": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
|
||||||
|
"requires": {
|
||||||
|
"eslint-visitor-keys": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"has-flag": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"is-fullwidth-code-point": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"string-width": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-ansi": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-regex": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"supports-color": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"has-flag": "^4.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"internal-ip": {
|
"internal-ip": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz",
|
||||||
|
|
@ -16848,6 +16978,17 @@
|
||||||
"object.assign": "^4.1.2"
|
"object.assign": "^4.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jszip": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-jgnQoG9LKnWO3mnVNBnfhkh0QknICd1FGSrXcgrl67zioyJ4wgx25o9ZqwNtrROSflGBCGYnJfjrIyRIby1OoQ==",
|
||||||
|
"requires": {
|
||||||
|
"lie": "~3.3.0",
|
||||||
|
"pako": "~1.0.2",
|
||||||
|
"readable-stream": "~2.3.6",
|
||||||
|
"set-immediate-shim": "~1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"killable": {
|
"killable": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
|
||||||
|
|
@ -16946,14 +17087,22 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"libphonenumber-js": {
|
"libphonenumber-js": {
|
||||||
"version": "1.8.4",
|
"version": "1.9.10",
|
||||||
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.8.4.tgz",
|
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.10.tgz",
|
||||||
"integrity": "sha512-s0fTPZRB4hcsfDL9p6wUNOLngVh4y3fBPhH33dL7OfkHA2RirI8p3rlR+4f4SMxdcng9jPNOweS2Z47BivHMYw==",
|
"integrity": "sha512-XyBYwt1dQCc9emeb78uCqJv9qy9tJQsg6vYDeJt37dwBYiZga8z0rHI5dcrn3aFKz9C5Nn9azaRBC+wmW91FfQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "^1.2.5",
|
"minimist": "^1.2.5",
|
||||||
"xml2js": "^0.4.17"
|
"xml2js": "^0.4.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"lie": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||||
|
"requires": {
|
||||||
|
"immediate": "~3.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"lines-and-columns": {
|
"lines-and-columns": {
|
||||||
"version": "1.1.6",
|
"version": "1.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
|
||||||
|
|
@ -18717,8 +18866,7 @@
|
||||||
"pako": {
|
"pako": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"parallel-transform": {
|
"parallel-transform": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
|
|
@ -20369,8 +20517,7 @@
|
||||||
"process-nextick-args": {
|
"process-nextick-args": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"progress": {
|
"progress": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
|
|
@ -21102,9 +21249,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ignore": {
|
"ignore": {
|
||||||
"version": "3.3.10",
|
"version": "5.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
|
||||||
"integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
|
"integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"inquirer": {
|
"inquirer": {
|
||||||
|
|
@ -23284,9 +23431,20 @@
|
||||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"inherits": "^2.0.3",
|
"core-util-is": "~1.0.0",
|
||||||
"string_decoder": "^1.1.1",
|
"inherits": "~2.0.3",
|
||||||
"util-deprecate": "^1.0.1"
|
"isarray": "~1.0.0",
|
||||||
|
"process-nextick-args": "~2.0.0",
|
||||||
|
"safe-buffer": "~5.1.1",
|
||||||
|
"string_decoder": "~1.1.1",
|
||||||
|
"util-deprecate": "~1.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"readdirp": {
|
"readdirp": {
|
||||||
|
|
@ -24632,6 +24790,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz",
|
||||||
"integrity": "sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g=="
|
"integrity": "sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g=="
|
||||||
},
|
},
|
||||||
|
"set-immediate-shim": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E="
|
||||||
|
},
|
||||||
"set-value": {
|
"set-value": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
|
||||||
|
|
@ -26850,8 +27013,8 @@
|
||||||
"util-deprecate": {
|
"util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
|
"dev": true,
|
||||||
"dev": true
|
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||||
},
|
},
|
||||||
"util.promisify": {
|
"util.promisify": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
"graphql": "^14.5.8",
|
"graphql": "^14.5.8",
|
||||||
"graphql-tag": "^2.10.3",
|
"graphql-tag": "^2.10.3",
|
||||||
"jss-plugin-extend": "^10.0.0",
|
"jss-plugin-extend": "^10.0.0",
|
||||||
|
"jszip": "^3.6.0",
|
||||||
"libphonenumber-js": "^1.7.50",
|
"libphonenumber-js": "^1.7.50",
|
||||||
"match-sorter": "^4.2.0",
|
"match-sorter": "^4.2.0",
|
||||||
"moment": "2.24.0",
|
"moment": "2.24.0",
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
|
import { useLazyQuery } from '@apollo/react-hooks'
|
||||||
import { makeStyles, Box } from '@material-ui/core'
|
import { makeStyles, Box } from '@material-ui/core'
|
||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
|
import FileSaver from 'file-saver'
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
import JSZip from 'jszip'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
import * as R from 'ramda'
|
||||||
import React, { memo } from 'react'
|
import React, { memo } from 'react'
|
||||||
|
|
||||||
import { HoverableTooltip } from 'src/components/Tooltip'
|
import { HoverableTooltip } from 'src/components/Tooltip'
|
||||||
import { IDButton } from 'src/components/buttons'
|
import { IDButton, ActionButton } from 'src/components/buttons'
|
||||||
import { P, Label1 } from 'src/components/typography'
|
import { P, Label1 } from 'src/components/typography'
|
||||||
import { ReactComponent as CardIdInverseIcon } from 'src/styling/icons/ID/card/white.svg'
|
import { ReactComponent as CardIdInverseIcon } from 'src/styling/icons/ID/card/white.svg'
|
||||||
import { ReactComponent as CardIdIcon } from 'src/styling/icons/ID/card/zodiac.svg'
|
import { ReactComponent as CardIdIcon } from 'src/styling/icons/ID/card/zodiac.svg'
|
||||||
|
|
@ -12,6 +17,7 @@ import { ReactComponent as PhoneIdInverseIcon } from 'src/styling/icons/ID/phone
|
||||||
import { ReactComponent as PhoneIdIcon } from 'src/styling/icons/ID/phone/zodiac.svg'
|
import { ReactComponent as PhoneIdIcon } from 'src/styling/icons/ID/phone/zodiac.svg'
|
||||||
import { ReactComponent as CamIdInverseIcon } from 'src/styling/icons/ID/photo/white.svg'
|
import { ReactComponent as CamIdInverseIcon } from 'src/styling/icons/ID/photo/white.svg'
|
||||||
import { ReactComponent as CamIdIcon } from 'src/styling/icons/ID/photo/zodiac.svg'
|
import { ReactComponent as CamIdIcon } from 'src/styling/icons/ID/photo/zodiac.svg'
|
||||||
|
import { ReactComponent as Download } from 'src/styling/icons/button/download/zodiac.svg'
|
||||||
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
|
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
|
||||||
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
|
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
|
||||||
import { URI } from 'src/utils/apollo'
|
import { URI } from 'src/utils/apollo'
|
||||||
|
|
@ -23,6 +29,27 @@ import styles from './DetailsCard.styles'
|
||||||
import { getStatus, getStatusDetails } from './helper'
|
import { getStatus, getStatusDetails } from './helper'
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
const MINUTES_OFFSET = 3
|
||||||
|
const TX_SUMMARY = gql`
|
||||||
|
query txSummaryAndLogs(
|
||||||
|
$txId: ID!
|
||||||
|
$deviceId: ID!
|
||||||
|
$limit: Int
|
||||||
|
$from: Date
|
||||||
|
$until: Date
|
||||||
|
$txClass: String
|
||||||
|
) {
|
||||||
|
serverLogsCsv(limit: $limit, from: $from, until: $until)
|
||||||
|
machineLogsCsv(
|
||||||
|
deviceId: $deviceId
|
||||||
|
limit: $limit
|
||||||
|
from: $from
|
||||||
|
until: $until
|
||||||
|
)
|
||||||
|
transactionCsv(id: $txId, txClass: $txClass)
|
||||||
|
txAssociatedDataCsv(id: $txId, txClass: $txClass)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const formatAddress = (cryptoCode = '', address = '') =>
|
const formatAddress = (cryptoCode = '', address = '') =>
|
||||||
formatCryptoAddress(cryptoCode, address).replace(/(.{5})/g, '$1 ')
|
formatCryptoAddress(cryptoCode, address).replace(/(.{5})/g, '$1 ')
|
||||||
|
|
@ -35,6 +62,8 @@ const Label = ({ children }) => {
|
||||||
const DetailsRow = ({ it: tx }) => {
|
const DetailsRow = ({ it: tx }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const zip = new JSZip()
|
||||||
|
|
||||||
const fiat = Number.parseFloat(tx.fiat)
|
const fiat = Number.parseFloat(tx.fiat)
|
||||||
const crypto = toUnit(new BigNumber(tx.cryptoAtoms), tx.cryptoCode)
|
const crypto = toUnit(new BigNumber(tx.cryptoAtoms), tx.cryptoCode)
|
||||||
const commissionPercentage = Number.parseFloat(tx.commissionPercentage, 2)
|
const commissionPercentage = Number.parseFloat(tx.commissionPercentage, 2)
|
||||||
|
|
@ -55,6 +84,30 @@ const DetailsRow = ({ it: tx }) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const from = moment(tx.created)
|
||||||
|
.subtract(MINUTES_OFFSET, 'm')
|
||||||
|
.format()
|
||||||
|
const until = moment(tx.created)
|
||||||
|
.add(MINUTES_OFFSET, 'm')
|
||||||
|
.format()
|
||||||
|
|
||||||
|
const [fetchSummary] = useLazyQuery(TX_SUMMARY, {
|
||||||
|
onCompleted: data => createCsv(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
const loadAndTxSummary = ({ id: txId, deviceId, txClass }) => {
|
||||||
|
fetchSummary({ variables: { txId, from, until, deviceId, txClass } })
|
||||||
|
}
|
||||||
|
|
||||||
|
const createCsv = logs => {
|
||||||
|
const zipFilename = `tx_${tx.id}_summary.zip`
|
||||||
|
const filesNames = R.keys(logs)
|
||||||
|
R.map(name => zip.file(name + '.csv', logs[name]), filesNames)
|
||||||
|
zip.generateAsync({ type: 'blob' }).then(function(content) {
|
||||||
|
FileSaver.saveAs(content, zipFilename)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const errorElements = (
|
const errorElements = (
|
||||||
<>
|
<>
|
||||||
<Label>Transaction status</Label>
|
<Label>Transaction status</Label>
|
||||||
|
|
@ -197,7 +250,7 @@ const DetailsRow = ({ it: tx }) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.lastRow}>
|
<div className={classes.lastRow}>
|
||||||
<div>
|
<div className={classes.status}>
|
||||||
{getStatusDetails(tx) ? (
|
{getStatusDetails(tx) ? (
|
||||||
<HoverableTooltip parentElements={errorElements} width={200}>
|
<HoverableTooltip parentElements={errorElements} width={200}>
|
||||||
<P>{getStatusDetails(tx)}</P>
|
<P>{getStatusDetails(tx)}</P>
|
||||||
|
|
@ -206,6 +259,22 @@ const DetailsRow = ({ it: tx }) => {
|
||||||
errorElements
|
errorElements
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
loadAndTxSummary(tx)
|
||||||
|
}}>
|
||||||
|
<Label>Other actions</Label>
|
||||||
|
<ActionButton
|
||||||
|
color="primary"
|
||||||
|
Icon={Download}
|
||||||
|
className={classes.downloadRawLogs}>
|
||||||
|
{'Download raw logs'}
|
||||||
|
</ActionButton>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,12 @@ export default {
|
||||||
address: {
|
address: {
|
||||||
width: 280
|
width: 280
|
||||||
},
|
},
|
||||||
|
downloadRawLogs: {
|
||||||
|
width: 180
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
width: 230
|
||||||
|
},
|
||||||
transactionId: {
|
transactionId: {
|
||||||
width: 280
|
width: 280
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue