feat: added the compliance/customers route
feat: added customers list page feat: created the Customer type on the gql server and consume it Currently only with the 'name' property feat: added query on gql to get the customers list with the needed props feat: added the currently available props to the front end table fix: consider only sent txs for the aggregations on the customers list fix: replace ExpTable with a non-expandable one fix: remove unused properties from gql and front-end fix: fixed the customers list columns width fix: the last active table column was reading the wrong property chore: remove debug logging fix: use the correct table columns to check for txs that should be considered on the customers list page fix: use the international format for phone numbers feat: added the search box fix: remove ordering from the gql customers list query and moved it to the front-end) fix: removed the search box chore: refactor the customers list table into a new component chore: cleanup code fix: fixed styles from customer list page header
This commit is contained in:
parent
4320df2d61
commit
507027cdee
10 changed files with 327 additions and 4 deletions
|
|
@ -377,6 +377,37 @@ function batch () {
|
||||||
}, customers)))
|
}, customers)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query all customers, ordered by last activity
|
||||||
|
* and with aggregate columns based on their
|
||||||
|
* transactions
|
||||||
|
*
|
||||||
|
* @returns {array} Array of customers with it's
|
||||||
|
*/
|
||||||
|
function getCustomersList () {
|
||||||
|
const sql = `select name, phone, total_txs, total_spent,
|
||||||
|
created as last_active, fiat as last_tx_fiat, fiat_code as last_tx_fiat_code,
|
||||||
|
tx_class as last_tx_class
|
||||||
|
from (
|
||||||
|
select c.name, c.phone, t.tx_class, t.fiat, t.fiat_code, t.created,
|
||||||
|
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 (
|
||||||
|
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
|
||||||
|
from cash_out_txs where confirmed_at is not null) t on c.id = t.customer_id
|
||||||
|
where c.id != $1
|
||||||
|
) as cl where rn = 1
|
||||||
|
limit $2`
|
||||||
|
return db.any(sql, [ anonymous.uuid, NUM_RESULTS ])
|
||||||
|
.then(customers => Promise.all(_.map(customer => {
|
||||||
|
return populateOverrideUsernames(customer)
|
||||||
|
.then(camelize)
|
||||||
|
}, customers)))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String} id customer id
|
* @param {String} id customer id
|
||||||
* @param {Object} patch customer update record
|
* @param {Object} patch customer update record
|
||||||
|
|
@ -482,4 +513,4 @@ function updateFrontCamera (id, patch) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { add, get, batch, getById, update, updatePhotoCard, updateFrontCamera }
|
module.exports = { add, get, batch, getCustomersList, getById, update, updatePhotoCard, updateFrontCamera }
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ const { GraphQLJSON, GraphQLJSONObject } = require('graphql-type-json')
|
||||||
const got = require('got')
|
const got = require('got')
|
||||||
|
|
||||||
const machineLoader = require('../../machine-loader')
|
const machineLoader = require('../../machine-loader')
|
||||||
|
const customers = require('../../customers')
|
||||||
const { machineAction } = require('../machines')
|
const { machineAction } = require('../machines')
|
||||||
const logs = require('../../logs')
|
const logs = require('../../logs')
|
||||||
const supportLogs = require('../../support_logs')
|
const supportLogs = require('../../support_logs')
|
||||||
|
|
@ -58,6 +59,17 @@ const typeDefs = gql`
|
||||||
statuses: [MachineStatus]
|
statuses: [MachineStatus]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Customer {
|
||||||
|
name: String!
|
||||||
|
phone: String
|
||||||
|
totalTxs: Int
|
||||||
|
totalSpent: String
|
||||||
|
lastActive: Date
|
||||||
|
lastTxFiat: String
|
||||||
|
lastTxFiatCode: String
|
||||||
|
lastTxClass: String
|
||||||
|
}
|
||||||
|
|
||||||
type Account {
|
type Account {
|
||||||
code: String!
|
code: String!
|
||||||
display: String!
|
display: String!
|
||||||
|
|
@ -146,6 +158,7 @@ const typeDefs = gql`
|
||||||
accounts: [Account]
|
accounts: [Account]
|
||||||
cryptoCurrencies: [CryptoCurrency]
|
cryptoCurrencies: [CryptoCurrency]
|
||||||
machines: [Machine]
|
machines: [Machine]
|
||||||
|
customers: [Customer]
|
||||||
machineLogs(deviceId: ID!): [MachineLog]
|
machineLogs(deviceId: ID!): [MachineLog]
|
||||||
funding: [CoinFunds]
|
funding: [CoinFunds]
|
||||||
serverVersion: String!
|
serverVersion: String!
|
||||||
|
|
@ -190,6 +203,7 @@ const resolvers = {
|
||||||
accounts: () => accounts,
|
accounts: () => accounts,
|
||||||
cryptoCurrencies: () => coins,
|
cryptoCurrencies: () => coins,
|
||||||
machines: () => machineLoader.getMachineNames(),
|
machines: () => machineLoader.getMachineNames(),
|
||||||
|
customers: () => customers.getCustomersList(),
|
||||||
funding: () => funding.getFunding(),
|
funding: () => funding.getFunding(),
|
||||||
machineLogs: (...[, { deviceId }]) => logs.simpleGetMachineLogs(deviceId),
|
machineLogs: (...[, { deviceId }]) => logs.simpleGetMachineLogs(deviceId),
|
||||||
serverVersion: () => serverVersion,
|
serverVersion: () => serverVersion,
|
||||||
|
|
|
||||||
86
new-lamassu-admin/src/components/dataTable/DataTable.js
Normal file
86
new-lamassu-admin/src/components/dataTable/DataTable.js
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
import React, { memo } from 'react'
|
||||||
|
import {
|
||||||
|
AutoSizer,
|
||||||
|
List,
|
||||||
|
CellMeasurer,
|
||||||
|
CellMeasurerCache
|
||||||
|
} from 'react-virtualized'
|
||||||
|
|
||||||
|
import { THead, Th, Tr, Td } from 'src/components/fake-table/Table'
|
||||||
|
import { mainWidth } from 'src/styling/variables'
|
||||||
|
|
||||||
|
const DataTable = memo(({ elements, data }) => {
|
||||||
|
const cache = new CellMeasurerCache({
|
||||||
|
defaultHeight: 62,
|
||||||
|
fixedWidth: true
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<THead>
|
||||||
|
{elements.map(({ size, className, textAlign, header }, idx) => (
|
||||||
|
<Th
|
||||||
|
key={idx}
|
||||||
|
size={size}
|
||||||
|
className={className}
|
||||||
|
textAlign={textAlign}>
|
||||||
|
{header}
|
||||||
|
</Th>
|
||||||
|
))}
|
||||||
|
</THead>
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: '1 1 auto' }}>
|
||||||
|
<AutoSizer disableWidth>
|
||||||
|
{({ height }) => (
|
||||||
|
<List
|
||||||
|
height={height}
|
||||||
|
width={mainWidth}
|
||||||
|
rowCount={data.length}
|
||||||
|
rowHeight={cache.rowHeight}
|
||||||
|
rowRenderer={({ index, isScrolling, key, parent, style }) => (
|
||||||
|
<CellMeasurer
|
||||||
|
cache={cache}
|
||||||
|
columnIndex={0}
|
||||||
|
key={key}
|
||||||
|
parent={parent}
|
||||||
|
rowIndex={index}>
|
||||||
|
<div style={style}>
|
||||||
|
<Tr
|
||||||
|
error={data[index].error}
|
||||||
|
errorMessage={data[index].errorMessage}>
|
||||||
|
{elements.map(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
header,
|
||||||
|
size,
|
||||||
|
className,
|
||||||
|
textAlign,
|
||||||
|
view = it => it?.toString()
|
||||||
|
},
|
||||||
|
idx
|
||||||
|
) => (
|
||||||
|
<Td
|
||||||
|
key={idx}
|
||||||
|
size={size}
|
||||||
|
className={className}
|
||||||
|
textAlign={textAlign}>
|
||||||
|
{view(data[index])}
|
||||||
|
</Td>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</Tr>
|
||||||
|
</div>
|
||||||
|
</CellMeasurer>
|
||||||
|
)}
|
||||||
|
overscanRowCount={50}
|
||||||
|
deferredMeasurementCache={cache}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</AutoSizer>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default DataTable
|
||||||
3
new-lamassu-admin/src/components/dataTable/index.js
Normal file
3
new-lamassu-admin/src/components/dataTable/index.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import DataTable from './DataTable'
|
||||||
|
|
||||||
|
export { DataTable }
|
||||||
106
new-lamassu-admin/src/pages/Customers/Customers.js
Normal file
106
new-lamassu-admin/src/pages/Customers/Customers.js
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
|
import moment from 'moment'
|
||||||
|
import * as R from 'ramda'
|
||||||
|
import React from 'react'
|
||||||
|
import { useQuery } from '@apollo/react-hooks'
|
||||||
|
import { gql } from 'apollo-boost'
|
||||||
|
import { parsePhoneNumberFromString } from 'libphonenumber-js'
|
||||||
|
|
||||||
|
import Title from 'src/components/Title'
|
||||||
|
import { DataTable } from 'src/components/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'
|
||||||
|
|
||||||
|
const useStyles = makeStyles(mainStyles)
|
||||||
|
|
||||||
|
const GET_CUSTOMERS = gql`
|
||||||
|
{
|
||||||
|
customers {
|
||||||
|
name
|
||||||
|
phone
|
||||||
|
totalTxs
|
||||||
|
totalSpent
|
||||||
|
lastActive
|
||||||
|
lastTxFiat
|
||||||
|
lastTxFiatCode
|
||||||
|
lastTxClass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const Customers = () => {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const { data: customersResponse } = useQuery(GET_CUSTOMERS)
|
||||||
|
|
||||||
|
const elements = [
|
||||||
|
{
|
||||||
|
header: 'Name',
|
||||||
|
size: 277,
|
||||||
|
view: R.path(['name'])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Phone',
|
||||||
|
size: 166,
|
||||||
|
view: it => parsePhoneNumberFromString(it.phone).formatInternational()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Total TXs',
|
||||||
|
size: 174,
|
||||||
|
textAlign: 'right',
|
||||||
|
view: it => `${Number.parseInt(it.totalTxs)}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Total spent',
|
||||||
|
size: 188,
|
||||||
|
textAlign: 'right',
|
||||||
|
view: it => `${Number.parseFloat(it.totalSpent)} ${it.lastTxFiatCode}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Last active',
|
||||||
|
size: 197,
|
||||||
|
view: it => moment.utc(it.lastActive).format('YYYY-MM-D')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Last transaction',
|
||||||
|
size: 198,
|
||||||
|
textAlign: 'right',
|
||||||
|
view: it => (
|
||||||
|
<>
|
||||||
|
{`${Number.parseFloat(it.lastTxFiat)} ${it.lastTxFiatCode} `}
|
||||||
|
{it.lastTxClass === 'cashOut' ? <TxOutIcon /> : <TxInIcon />}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={classes.titleWrapper}>
|
||||||
|
<div className={classes.titleAndButtonsContainer}>
|
||||||
|
<Title>Customers</Title>
|
||||||
|
</div>
|
||||||
|
<div className={classes.headerLabels}>
|
||||||
|
<div>
|
||||||
|
<TxOutIcon />
|
||||||
|
<span>Cash-out</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<TxInIcon />
|
||||||
|
<span>Cash-in</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DataTable
|
||||||
|
elements={elements}
|
||||||
|
data={R.sortWith([R.descend('lastActive')])(
|
||||||
|
R.path(['customers'])(customersResponse) ?? []
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Customers
|
||||||
28
new-lamassu-admin/src/pages/Customers/Customers.styles.js
Normal file
28
new-lamassu-admin/src/pages/Customers/Customers.styles.js
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import typographyStyles from 'src/components/typography/styles'
|
||||||
|
import baseStyles from 'src/pages/Logs.styles'
|
||||||
|
|
||||||
|
const { label1 } = typographyStyles
|
||||||
|
const { titleWrapper, titleAndButtonsContainer, buttonsWrapper } = baseStyles
|
||||||
|
|
||||||
|
const mainStyles = {
|
||||||
|
titleWrapper,
|
||||||
|
titleAndButtonsContainer,
|
||||||
|
buttonsWrapper,
|
||||||
|
headerLabels: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
'& div': {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center'
|
||||||
|
},
|
||||||
|
'& > div:first-child': {
|
||||||
|
marginRight: 24
|
||||||
|
},
|
||||||
|
'& span': {
|
||||||
|
extend: label1,
|
||||||
|
marginLeft: 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { mainStyles }
|
||||||
3
new-lamassu-admin/src/pages/Customers/index.js
Normal file
3
new-lamassu-admin/src/pages/Customers/index.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import Customers from './Customers'
|
||||||
|
|
||||||
|
export default Customers
|
||||||
|
|
@ -13,6 +13,7 @@ import AuthRegister from 'src/pages/AuthRegister'
|
||||||
import OperatorInfo from 'src/pages/OperatorInfo/OperatorInfo'
|
import OperatorInfo from 'src/pages/OperatorInfo/OperatorInfo'
|
||||||
|
|
||||||
import MachineStatus from '../pages/maintenance/MachineStatus'
|
import MachineStatus from '../pages/maintenance/MachineStatus'
|
||||||
|
import Customers from '../pages/Customers'
|
||||||
|
|
||||||
const tree = [
|
const tree = [
|
||||||
{ key: 'transactions', label: 'Transactions', route: '/transactions' },
|
{ key: 'transactions', label: 'Transactions', route: '/transactions' },
|
||||||
|
|
@ -55,6 +56,28 @@ const tree = [
|
||||||
},
|
},
|
||||||
{ key: 'info', label: 'Operator Info', route: '/settings/operator-info' }
|
{ key: 'info', label: 'Operator Info', route: '/settings/operator-info' }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'compliance',
|
||||||
|
label: 'Compliance',
|
||||||
|
route: '/compliance',
|
||||||
|
children: [
|
||||||
|
// {
|
||||||
|
// key: 'triggers',
|
||||||
|
// label: 'Triggers',
|
||||||
|
// route: '/compliance/triggers'
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
key: 'customers',
|
||||||
|
label: 'Customers',
|
||||||
|
route: '/compliance/customers'
|
||||||
|
}
|
||||||
|
// {
|
||||||
|
// key: 'blacklist',
|
||||||
|
// label: 'Blacklist',
|
||||||
|
// route: '/compliance/blacklist'
|
||||||
|
// }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
// compliance: { label: 'Compliance', children: [{ label: 'Locale', route: '/locale' }] }
|
// compliance: { label: 'Compliance', children: [{ label: 'Locale', route: '/locale' }] }
|
||||||
]
|
]
|
||||||
|
|
@ -88,6 +111,7 @@ const Routes = () => (
|
||||||
<Route path="/transactions" component={Transactions} />
|
<Route path="/transactions" component={Transactions} />
|
||||||
<Route path="/register" component={AuthRegister} />
|
<Route path="/register" component={AuthRegister} />
|
||||||
<Route path="/maintenance/machine-status" component={MachineStatus} />
|
<Route path="/maintenance/machine-status" component={MachineStatus} />
|
||||||
|
<Route path="/compliance/customers" component={Customers} />
|
||||||
</Switch>
|
</Switch>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
33
package-lock.json
generated
33
package-lock.json
generated
|
|
@ -2640,7 +2640,8 @@
|
||||||
"bn.js": {
|
"bn.js": {
|
||||||
"version": "4.11.8",
|
"version": "4.11.8",
|
||||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
|
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
|
||||||
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
|
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"bs58": {
|
"bs58": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
|
|
@ -8132,6 +8133,15 @@
|
||||||
"type-check": "~0.3.2"
|
"type-check": "~0.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"libphonenumber-js": {
|
||||||
|
"version": "1.7.38",
|
||||||
|
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.7.38.tgz",
|
||||||
|
"integrity": "sha512-3NMPjWl15E51vWtXYdhVXo+DtZcR35OmXfJQWcwQ28XEbpSWOnQXbkaIZq8RmMv5jiY4fMQxBY7MUXvioApOHg==",
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.0",
|
||||||
|
"xml2js": "^0.4.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
"libpq": {
|
"libpq": {
|
||||||
"version": "1.8.7",
|
"version": "1.8.7",
|
||||||
"resolved": "https://registry.npmjs.org/libpq/-/libpq-1.8.7.tgz",
|
"resolved": "https://registry.npmjs.org/libpq/-/libpq-1.8.7.tgz",
|
||||||
|
|
@ -8545,7 +8555,8 @@
|
||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
|
||||||
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
|
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
|
||||||
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -12728,7 +12739,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/web3/-/web3-0.20.6.tgz",
|
"resolved": "https://registry.npmjs.org/web3/-/web3-0.20.6.tgz",
|
||||||
"integrity": "sha1-PpcwauAk+yThCj11yIQwJWIhUSA=",
|
"integrity": "sha1-PpcwauAk+yThCj11yIQwJWIhUSA=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bignumber.js": "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934",
|
"bignumber.js": "git+https://github.com/frozeman/bignumber.js-nolookahead.git",
|
||||||
"crypto-js": "^3.1.4",
|
"crypto-js": "^3.1.4",
|
||||||
"utf8": "^2.1.1",
|
"utf8": "^2.1.1",
|
||||||
"xhr2": "*",
|
"xhr2": "*",
|
||||||
|
|
@ -12999,6 +13010,22 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"xml2js": {
|
||||||
|
"version": "0.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
|
||||||
|
"integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
|
||||||
|
"requires": {
|
||||||
|
"sax": ">=0.6.0",
|
||||||
|
"xmlbuilder": "~11.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"xmlbuilder": {
|
||||||
|
"version": "11.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||||
|
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"xmlbuilder": {
|
"xmlbuilder": {
|
||||||
"version": "8.2.2",
|
"version": "8.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz",
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
"helmet": "^3.8.1",
|
"helmet": "^3.8.1",
|
||||||
"inquirer": "^5.2.0",
|
"inquirer": "^5.2.0",
|
||||||
"kraken-api": "github:DeX3/npm-kraken-api",
|
"kraken-api": "github:DeX3/npm-kraken-api",
|
||||||
|
"libphonenumber-js": "^1.7.38",
|
||||||
"lnd-async": "^1.2.0",
|
"lnd-async": "^1.2.0",
|
||||||
"lodash": "^4.17.10",
|
"lodash": "^4.17.10",
|
||||||
"longjohn": "^0.2.12",
|
"longjohn": "^0.2.12",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue