feat: add services page
fix: change styles, fix hook trigger, add ux feat: setup custom error messages refactor: conform to new style guide refactor: migrate to graphql refactor: migrate to Ramda fix: update state on mutation refactor: migrate error ux to graphql fix: change structure of accounts config fix: use absolute imports fix: move makeStyles out of components fix: correct Strike behaviour
This commit is contained in:
parent
1dba321052
commit
b9d2341cd1
30 changed files with 2579 additions and 306 deletions
0
bin/insecure-dev.sh
Normal file → Executable file
0
bin/insecure-dev.sh
Normal file → Executable file
124
lib/new-admin/accounts.js
Normal file
124
lib/new-admin/accounts.js
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
const _ = require('lodash/fp')
|
||||
|
||||
const db = require('../db')
|
||||
const config = require('./config')
|
||||
const ph = require('../plugin-helper')
|
||||
|
||||
const schemas = ph.loadSchemas()
|
||||
|
||||
function fetchAccounts () {
|
||||
return db.oneOrNone('select data from user_config where type=$1', ['accounts'])
|
||||
.then(row => {
|
||||
// Hard code this for now
|
||||
const accounts = [{
|
||||
code: 'blockcypher',
|
||||
display: 'Blockcypher',
|
||||
fields: [
|
||||
{ code: 'confidenceFactor', display: 'Confidence Factor', fieldType: 'integer', required: true, value: 40 }
|
||||
]
|
||||
}]
|
||||
|
||||
return row
|
||||
? Promise.resolve(row.data.accounts)
|
||||
: db.none('insert into user_config (type, data, valid) values ($1, $2, $3)', ['accounts', { accounts }, true])
|
||||
.then(fetchAccounts)
|
||||
})
|
||||
}
|
||||
|
||||
function selectedAccounts () {
|
||||
const mapAccount = v => v.fieldLocator.fieldType === 'account' &&
|
||||
v.fieldValue.value
|
||||
|
||||
const mapSchema = code => schemas[code]
|
||||
return config.fetchConfig()
|
||||
.then(conf => {
|
||||
const accountCodes = _.uniq(conf.map(mapAccount)
|
||||
.filter(_.identity))
|
||||
|
||||
return _.sortBy(_.get('display'), accountCodes.map(mapSchema)
|
||||
.filter(_.identity))
|
||||
})
|
||||
}
|
||||
|
||||
function fetchAccountSchema (account) {
|
||||
return schemas[account]
|
||||
}
|
||||
|
||||
function mergeAccount (oldAccount, newAccount) {
|
||||
if (!newAccount) return oldAccount
|
||||
|
||||
const newFields = newAccount.fields
|
||||
|
||||
const updateWithData = oldField => {
|
||||
const newField = _.find(r => r.code === oldField.code, newFields)
|
||||
const newValue = _.isUndefined(newField) ? oldField.value : newField.value
|
||||
return _.set('value', newValue, oldField)
|
||||
}
|
||||
|
||||
const updatedFields = oldAccount.fields.map(updateWithData)
|
||||
|
||||
return _.set('fields', updatedFields, oldAccount)
|
||||
}
|
||||
|
||||
function getAccounts (accountCode) {
|
||||
const schema = fetchAccountSchema(accountCode)
|
||||
if (!schema) return Promise.reject(new Error('No schema for: ' + accountCode))
|
||||
|
||||
return fetchAccounts()
|
||||
.then(accounts => {
|
||||
if (_.isEmpty(accounts)) return [schema]
|
||||
const account = _.find(r => r.code === accountCode, accounts)
|
||||
const mergedAccount = mergeAccount(schema, account)
|
||||
|
||||
return updateAccounts(mergedAccount, accounts)
|
||||
})
|
||||
}
|
||||
|
||||
function elideSecrets (account) {
|
||||
const elideSecret = field => {
|
||||
return field.fieldType === 'password'
|
||||
? _.set('value', !_.isEmpty(field.value), field)
|
||||
: field
|
||||
}
|
||||
|
||||
return _.set('fields', account.fields.map(elideSecret), account)
|
||||
}
|
||||
|
||||
function getAccount (accountCode) {
|
||||
return getAccounts(accountCode)
|
||||
.then(accounts => _.find(r => r.code === accountCode, accounts))
|
||||
.then(elideSecrets)
|
||||
}
|
||||
|
||||
function save (accounts) {
|
||||
return db.none('update user_config set data=$1 where type=$2', [{ accounts: accounts }, 'accounts'])
|
||||
}
|
||||
|
||||
function updateAccounts (newAccount, accounts) {
|
||||
const accountCode = newAccount.code
|
||||
const isPresent = _.some(_.matchesProperty('code', accountCode), accounts)
|
||||
const updateAccount = r => r.code === accountCode
|
||||
? newAccount
|
||||
: r
|
||||
|
||||
return isPresent
|
||||
? _.map(updateAccount, accounts)
|
||||
: _.concat(accounts, newAccount)
|
||||
}
|
||||
|
||||
function updateAccount (account) {
|
||||
return getAccounts(account.code)
|
||||
.then(accounts => {
|
||||
const merged = mergeAccount(_.find(_.matchesProperty('code', account.code), accounts), account)
|
||||
return save(updateAccounts(merged, accounts))
|
||||
})
|
||||
.then(() => getAccount(account.code))
|
||||
.catch((err) => console.log(err))
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
selectedAccounts,
|
||||
getAccount,
|
||||
updateAccount,
|
||||
fetchAccounts
|
||||
}
|
||||
654
new-lamassu-admin/package-lock.json
generated
654
new-lamassu-admin/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -71,9 +71,9 @@ const Table = ({ children, className, ...props }) => (
|
|||
</div>
|
||||
)
|
||||
|
||||
const THead = ({ children }) => {
|
||||
const THead = ({ children, className }) => {
|
||||
const classes = useStyles()
|
||||
return <div className={classes.header}>{children}</div>
|
||||
return <div className={classnames(className, classes.header)}>{children}</div>
|
||||
}
|
||||
|
||||
const TBody = ({ children, className }) => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
import React, { memo, useState } from 'react'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
|
||||
import TextInputFormik from './TextInput'
|
||||
import { styles } from './TextInput.styles'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const mask = /(\+9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)(\d{1,3}){0,1}(\d{1,3}){0,1}(\d{1,3}){0,1}(\d{1,3}){0,1}(\d{1,2}){0,1}$/
|
||||
const maskValue = value => value.replace(mask, '$1 $2 $3 $4 $5 $6')
|
||||
|
||||
const PhoneNumberInputFormik = memo(({ ...props }) => {
|
||||
const { onChange, value } = props.field
|
||||
|
||||
const classes = useStyles()
|
||||
|
||||
// Regex adapted from http://phoneregex.com/
|
||||
|
||||
const [maskedValue, setMaskedValue] = useState(maskValue(value))
|
||||
|
||||
const handleChange = event => {
|
||||
setMaskedValue(maskValue(event.target.value))
|
||||
|
||||
onChange(event)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className={classes.masked} aria-hidden="true">
|
||||
{maskedValue}
|
||||
</span>
|
||||
<TextInputFormik
|
||||
inputProps={{ maxLength: 17 }}
|
||||
className={classes.maskedInput}
|
||||
onChange={handleChange}
|
||||
{...props}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
export { PhoneNumberInputFormik, mask, maskValue }
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import React, { memo, useState } from 'react'
|
||||
import classnames from 'classnames'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
|
||||
import TextInputFormik from './TextInput'
|
||||
import { styles } from './TextInput.styles'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const SecretInputFormik = memo(({ ...props }) => {
|
||||
const { value } = props.field
|
||||
|
||||
const classes = useStyles()
|
||||
|
||||
const [localTouched, setLocalTouched] = useState(false)
|
||||
|
||||
const handleFocus = event => {
|
||||
setLocalTouched(true)
|
||||
props.onFocus()
|
||||
}
|
||||
|
||||
const spanClass = {
|
||||
[classes.secretSpan]: true,
|
||||
[classes.masked]: value && !localTouched,
|
||||
[classes.hideSpan]: !value || localTouched
|
||||
}
|
||||
|
||||
const inputClass = {
|
||||
[classes.maskedInput]: value && !localTouched
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className={classnames(spanClass)} aria-hidden="true">
|
||||
⚬ ⚬ ⚬ This field is set ⚬ ⚬ ⚬
|
||||
</span>
|
||||
<TextInputFormik
|
||||
{...props}
|
||||
onFocus={handleFocus}
|
||||
className={classnames(inputClass)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
export default SecretInputFormik
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import { fontColor, offColor } from 'src/styling/variables'
|
||||
import typographyStyles from 'src/components/typography/styles'
|
||||
|
||||
const { info3 } = typographyStyles
|
||||
|
||||
const styles = {
|
||||
masked: {
|
||||
position: 'absolute',
|
||||
bottom: 5,
|
||||
color: fontColor
|
||||
},
|
||||
secretSpan: {
|
||||
extend: info3,
|
||||
color: offColor
|
||||
},
|
||||
hideSpan: {
|
||||
display: 'none'
|
||||
},
|
||||
maskedInput: {
|
||||
'& input': {
|
||||
pointerEvents: 'none',
|
||||
backgroundColor: 'transparent',
|
||||
zIndex: -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { styles }
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
import React from 'react'
|
||||
import classnames from 'classnames'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
|
||||
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/white.svg'
|
||||
import { ReactComponent as DeleteIcon } from 'src/styling/icons/action/delete/enabled.svg'
|
||||
import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
||||
import {
|
||||
offColor,
|
||||
tableDisabledHeaderColor,
|
||||
tableNewDisabledHeaderColor,
|
||||
secondaryColorDarker
|
||||
} from 'src/styling/variables'
|
||||
import { Table, THead, TBody, Td, Th } from 'src/components/fake-table/Table'
|
||||
import typographyStyles from 'src/components/typography/styles'
|
||||
|
||||
const { label1, p } = typographyStyles
|
||||
|
||||
const SingleRowTable = ({
|
||||
width = 380,
|
||||
height = 160,
|
||||
title,
|
||||
items,
|
||||
onEdit,
|
||||
disabled,
|
||||
newService,
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
const editButtonSize = 54
|
||||
|
||||
const styles = {
|
||||
wrapper: {
|
||||
width: width,
|
||||
boxShadow: '0 0 4px 0 rgba(0, 0, 0, 0.08)'
|
||||
},
|
||||
buttonTh: {
|
||||
padding: [[0, 16]]
|
||||
},
|
||||
disabledHeader: {
|
||||
backgroundColor: tableDisabledHeaderColor,
|
||||
color: offColor
|
||||
},
|
||||
newDisabledHeader: {
|
||||
backgroundColor: tableNewDisabledHeaderColor
|
||||
},
|
||||
disabledBody: {
|
||||
extend: p,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
height: 104
|
||||
},
|
||||
itemWrapper: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
marginTop: 16,
|
||||
minHeight: 40,
|
||||
'& > div:last-child': {}
|
||||
},
|
||||
disabledWrapper: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
'& > span:first-child': {
|
||||
display: 'flex'
|
||||
},
|
||||
'& > span:last-child': {
|
||||
paddingLeft: 16
|
||||
}
|
||||
},
|
||||
label: {
|
||||
extend: label1,
|
||||
color: offColor,
|
||||
marginBottom: 4
|
||||
},
|
||||
item: {
|
||||
extend: p
|
||||
},
|
||||
editButton: {
|
||||
border: 'none',
|
||||
backgroundColor: 'transparent',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
padding: 0
|
||||
},
|
||||
spanNew: {
|
||||
color: secondaryColorDarker,
|
||||
marginLeft: 12
|
||||
}
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const classes = useStyles()
|
||||
|
||||
const headerClasses = {
|
||||
[classes.disabledHeader]: disabled,
|
||||
[classes.newDisabledHeader]: newService && disabled
|
||||
}
|
||||
|
||||
const bodyClasses = {
|
||||
[classes.disabledBody]: disabled
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{items && (
|
||||
<Table className={classnames(className, classes.wrapper)}>
|
||||
<THead className={classnames(headerClasses)}>
|
||||
<Th size={width - editButtonSize}>
|
||||
{title}
|
||||
{newService && <span className={classes.spanNew}>New</span>}
|
||||
</Th>
|
||||
<Th size={editButtonSize} className={classes.buttonTh}>
|
||||
{!disabled && (
|
||||
<button className={classes.editButton} onClick={onEdit}>
|
||||
<EditIcon />
|
||||
</button>
|
||||
)}
|
||||
{disabled && (
|
||||
<button className={classes.editButton}>
|
||||
<DeleteIcon />
|
||||
</button>
|
||||
)}
|
||||
</Th>
|
||||
</THead>
|
||||
<TBody className={classnames(bodyClasses)}>
|
||||
<Td size={width}>
|
||||
{!disabled && (
|
||||
<>
|
||||
{items[0] && (
|
||||
<div className={classes.itemWrapper}>
|
||||
<div className={classes.label}>{items[0].label}</div>
|
||||
<div className={classes.item}>{items[0].value}</div>
|
||||
</div>
|
||||
)}
|
||||
{items[1] && (
|
||||
<div className={classes.itemWrapper}>
|
||||
<div className={classes.label}>{items[1].label}</div>
|
||||
<div className={classes.item}>{items[1].value}</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{disabled && (
|
||||
<div className={classes.disabledWrapper}>
|
||||
<span>
|
||||
<WarningIcon />
|
||||
</span>
|
||||
<span>This service is not being used</span>
|
||||
</div>
|
||||
)}
|
||||
</Td>
|
||||
</TBody>
|
||||
</Table>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SingleRowTable
|
||||
250
new-lamassu-admin/src/pages/Services/Bitgo.js
Normal file
250
new-lamassu-admin/src/pages/Services/Bitgo.js
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
import React, { memo } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
import TextInputFormik from 'src/components/inputs/formik/TextInput'
|
||||
import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
|
||||
|
||||
import { Card, getValue as getValueAux, formatLong } from './aux'
|
||||
import EditService from './EditService'
|
||||
|
||||
const schema = {
|
||||
token: {
|
||||
code: 'token',
|
||||
display: 'API Token'
|
||||
},
|
||||
btcWalletId: {
|
||||
code: 'BTCWalletId',
|
||||
display: 'BTC Wallet ID'
|
||||
},
|
||||
btcWalletPassphrase: {
|
||||
code: 'BTCWalletPassphrase',
|
||||
display: 'BTC Wallet Passphrase'
|
||||
},
|
||||
ltcWalletId: {
|
||||
code: 'LTCWalletId',
|
||||
display: 'LTC Wallet ID'
|
||||
},
|
||||
ltcWalletPassphrase: {
|
||||
code: 'LTCWalletPassphrase',
|
||||
display: 'LTC Wallet Passphrase'
|
||||
},
|
||||
zecWalletId: {
|
||||
code: 'ZECWalletId',
|
||||
display: 'ZEC Wallet ID'
|
||||
},
|
||||
zecWalletPassphrase: {
|
||||
code: 'ZECWalletPassphrase',
|
||||
display: 'ZEC Wallet Passphrase'
|
||||
},
|
||||
bchWalletId: {
|
||||
code: 'BCHWalletId',
|
||||
display: 'BCH Wallet ID'
|
||||
},
|
||||
bchWalletPassphrase: {
|
||||
code: 'BCHWalletPassphrase',
|
||||
display: 'BCH Wallet Passphrase'
|
||||
},
|
||||
dashWalletId: {
|
||||
code: 'DASHWalletId',
|
||||
display: 'DASH Wallet ID'
|
||||
},
|
||||
dashWalletPassphrase: {
|
||||
code: 'DASHWalletPassphrase',
|
||||
display: 'DASH Wallet Passphrase'
|
||||
},
|
||||
environment: {
|
||||
code: 'environment',
|
||||
display: 'Environment'
|
||||
}
|
||||
}
|
||||
|
||||
const BitgoCard = memo(({ account, onEdit, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const token = schema.token
|
||||
const tokenValue = getValue(token.code)
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: token.display,
|
||||
value: formatLong(tokenValue)
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<Card
|
||||
account={account}
|
||||
title="BitGo (Wallet)"
|
||||
items={items}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
const BitgoForm = ({ account, handleSubmit, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const { code } = account
|
||||
const token = getValue(schema.token.code)
|
||||
const btcWalletId = getValue(schema.btcWalletId.code)
|
||||
const btcWalletPassphrase = getValue(schema.btcWalletPassphrase.code)
|
||||
const ltcWalletId = getValue(schema.ltcWalletId.code)
|
||||
const ltcWalletPassphrase = getValue(schema.ltcWalletPassphrase.code)
|
||||
const zecWalletId = getValue(schema.zecWalletId.code)
|
||||
const zecWalletPassphrase = getValue(schema.zecWalletPassphrase.code)
|
||||
const bchWalletId = getValue(schema.bchWalletId.code)
|
||||
const bchWalletPassphrase = getValue(schema.bchWalletPassphrase.code)
|
||||
const dashWalletId = getValue(schema.dashWalletId.code)
|
||||
const dashWalletPassphrase = getValue(schema.dashWalletPassphrase.code)
|
||||
const environment = getValue(schema.environment.code)
|
||||
|
||||
const formik = {
|
||||
initialValues: {
|
||||
token: token,
|
||||
BTCWalletId: btcWalletId,
|
||||
BTCWalletPassphrase: btcWalletPassphrase,
|
||||
LTCWalletId: ltcWalletId,
|
||||
LTCWalletPassphrase: ltcWalletPassphrase,
|
||||
ZECWalletId: zecWalletId,
|
||||
ZECWalletPassphrase: zecWalletPassphrase,
|
||||
BCHWalletId: bchWalletId,
|
||||
BCHWalletPassphrase: bchWalletPassphrase,
|
||||
DASHWalletId: dashWalletId,
|
||||
DASHWalletPassphrase: dashWalletPassphrase,
|
||||
environment: environment
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
token: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required'),
|
||||
btcWalletId: Yup.string().max(100, 'Too long'),
|
||||
btcWalletPassphrase: Yup.string().max(100, 'Too long'),
|
||||
ltcWalletId: Yup.string().max(100, 'Too long'),
|
||||
ltcWalletPassphrase: Yup.string().max(100, 'Too long'),
|
||||
zecWalletId: Yup.string().max(100, 'Too long'),
|
||||
zecWalletPassphrase: Yup.string().max(100, 'Too long'),
|
||||
bchWalletId: Yup.string().max(100, 'Too long'),
|
||||
bchWalletPassphrase: Yup.string().max(100, 'Too long'),
|
||||
dashWalletId: Yup.string().max(100, 'Too long'),
|
||||
dashWalletPassphrase: Yup.string().max(100, 'Too long'),
|
||||
environment: Yup.string()
|
||||
.matches(/(prod|test)/)
|
||||
.required('Required')
|
||||
}),
|
||||
validate: values => {
|
||||
const errors = {}
|
||||
|
||||
if (values.btcWalletId && !values.btcWalletPassphrase) {
|
||||
errors.btcWalletPassphrase = 'Required'
|
||||
}
|
||||
|
||||
if (values.ltcWalletId && !values.ltcWalletPassphrase) {
|
||||
errors.ltcWalletPassphrase = 'Required'
|
||||
}
|
||||
|
||||
if (values.zecWalletId && !values.zecWalletPassphrase) {
|
||||
errors.zecWalletPassphrase = 'Required'
|
||||
}
|
||||
|
||||
if (values.bchWalletId && !values.bchWalletPassphrase) {
|
||||
errors.bchWalletPassphrase = 'Required'
|
||||
}
|
||||
|
||||
if (values.dashWalletId && !values.dashWalletPassphrase) {
|
||||
errors.dashWalletPassphrase = 'Required'
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
}
|
||||
|
||||
const fields = [
|
||||
{
|
||||
name: schema.token.code,
|
||||
label: schema.token.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.btcWalletId.code,
|
||||
label: schema.btcWalletId.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.btcWalletPassphrase.code,
|
||||
label: schema.btcWalletPassphrase.display,
|
||||
type: 'text',
|
||||
component: SecretInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.ltcWalletId.code,
|
||||
label: schema.ltcWalletId.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.ltcWalletPassphrase.code,
|
||||
label: schema.ltcWalletPassphrase.display,
|
||||
type: 'text',
|
||||
component: SecretInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.zecWalletId.code,
|
||||
label: schema.zecWalletId.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.zecWalletPassphrase.code,
|
||||
label: schema.zecWalletPassphrase.display,
|
||||
type: 'text',
|
||||
component: SecretInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.bchWalletId.code,
|
||||
label: schema.bchWalletId.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.bchWalletPassphrase.code,
|
||||
label: schema.bchWalletPassphrase.display,
|
||||
type: 'text',
|
||||
component: SecretInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.dashWalletId.code,
|
||||
label: schema.dashWalletId.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.dashWalletPassphrase.code,
|
||||
label: schema.dashWalletPassphrase.display,
|
||||
type: 'text',
|
||||
component: SecretInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.environment.code,
|
||||
label: schema.environment.display,
|
||||
placeholder: 'prod or test',
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<EditService
|
||||
title="Bitgo"
|
||||
formik={formik}
|
||||
code={code}
|
||||
fields={fields}
|
||||
{...props}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export { BitgoForm, BitgoCard }
|
||||
116
new-lamassu-admin/src/pages/Services/Bitstamp.js
Normal file
116
new-lamassu-admin/src/pages/Services/Bitstamp.js
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
import React, { memo } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
import TextInputFormik from 'src/components/inputs/formik/TextInput'
|
||||
import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
|
||||
|
||||
import { Card, getValue as getValueAux, formatLong } from './aux'
|
||||
import EditService from './EditService'
|
||||
|
||||
const schema = {
|
||||
clientId: {
|
||||
code: 'clientId',
|
||||
display: 'Client ID'
|
||||
},
|
||||
key: {
|
||||
code: 'key',
|
||||
display: 'API Key'
|
||||
},
|
||||
secret: {
|
||||
code: 'secret',
|
||||
display: 'API Secret'
|
||||
}
|
||||
}
|
||||
|
||||
const BitstampCard = memo(({ account, onEdit, ...props }) => {
|
||||
const findValue = getValueAux(account)
|
||||
|
||||
const clientId = schema.clientId
|
||||
const key = schema.key
|
||||
|
||||
const clientIdValue = findValue(clientId.code)
|
||||
const keyValue = findValue(key.code)
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: clientId.display,
|
||||
value: formatLong(clientIdValue)
|
||||
},
|
||||
{
|
||||
label: key.display,
|
||||
value: formatLong(keyValue)
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<Card
|
||||
account={account}
|
||||
title="Bitstamp (Exchange)"
|
||||
items={items}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
const BitstampForm = ({ account, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const { code } = account
|
||||
const clientId = getValue(schema.clientId.code)
|
||||
const key = getValue(schema.key.code)
|
||||
const secret = getValue(schema.secret.code)
|
||||
|
||||
const formik = {
|
||||
initialValues: {
|
||||
clientId: clientId,
|
||||
key: key,
|
||||
secret: secret
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
clientId: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required'),
|
||||
key: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required'),
|
||||
secret: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required')
|
||||
})
|
||||
}
|
||||
|
||||
const fields = [
|
||||
{
|
||||
name: schema.clientId.code,
|
||||
label: schema.clientId.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.key.code,
|
||||
label: schema.key.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.secret.code,
|
||||
label: schema.secret.display,
|
||||
type: 'text',
|
||||
component: SecretInputFormik
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<EditService
|
||||
title="Bitstamp"
|
||||
formik={formik}
|
||||
code={code}
|
||||
fields={fields}
|
||||
{...props}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export { BitstampForm, BitstampCard }
|
||||
101
new-lamassu-admin/src/pages/Services/Blockcypher.js
Normal file
101
new-lamassu-admin/src/pages/Services/Blockcypher.js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import React, { memo } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
import TextInputFormik from 'src/components/inputs/formik/TextInput'
|
||||
|
||||
import { Card, getValue as getValueAux, formatLong } from './aux'
|
||||
import EditService from './EditService'
|
||||
|
||||
const schema = {
|
||||
token: {
|
||||
code: 'token',
|
||||
display: 'API Token'
|
||||
},
|
||||
confidenceFactor: {
|
||||
code: 'confidenceFactor',
|
||||
display: 'Confidence Factor'
|
||||
}
|
||||
}
|
||||
|
||||
const BlockcypherCard = memo(({ account, onEdit, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const token = schema.token
|
||||
const confidenceFactor = schema.confidenceFactor
|
||||
|
||||
const tokenValue = getValue(token.code)
|
||||
const confidenceFactorValue = getValue(confidenceFactor.code)
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: token.display,
|
||||
value: formatLong(tokenValue)
|
||||
},
|
||||
{
|
||||
label: confidenceFactor.display,
|
||||
value: confidenceFactorValue
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<Card
|
||||
account={account}
|
||||
title="Blockcypher (Payments)"
|
||||
items={items}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
const BlockcypherForm = ({ account, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const { code } = account
|
||||
const token = getValue(schema.token.code)
|
||||
const confidenceFactor = getValue(schema.confidenceFactor.code)
|
||||
|
||||
const formik = {
|
||||
initialValues: {
|
||||
token: token,
|
||||
confidenceFactor: confidenceFactor
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
token: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required'),
|
||||
confidenceFactor: Yup.number()
|
||||
.integer('Please input a positive integer')
|
||||
.positive('Please input a positive integer')
|
||||
.required('Required')
|
||||
})
|
||||
}
|
||||
|
||||
const fields = [
|
||||
{
|
||||
name: schema.token.code,
|
||||
label: schema.token.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.confidenceFactor.code,
|
||||
label: schema.confidenceFactor.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<EditService
|
||||
title="Blockcypher"
|
||||
code={code}
|
||||
formik={formik}
|
||||
fields={fields}
|
||||
{...props}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export { BlockcypherForm, BlockcypherCard }
|
||||
94
new-lamassu-admin/src/pages/Services/EditService.js
Normal file
94
new-lamassu-admin/src/pages/Services/EditService.js
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import React, { useState } from 'react'
|
||||
import { Form, Formik, Field } from 'formik'
|
||||
import classnames from 'classnames'
|
||||
import { makeStyles, Paper } from '@material-ui/core'
|
||||
|
||||
import { H2, Info3 } from 'src/components/typography'
|
||||
import { Button } from 'src/components/buttons'
|
||||
import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.svg'
|
||||
import { ReactComponent as ErrorIcon } from 'src/styling/icons/warning-icon/tomato.svg'
|
||||
|
||||
import { editServiceStyles as styles } from './Services.styles'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const DEFAULT_ERROR_MESSAGE = 'Something went wrong. Please contact support.'
|
||||
|
||||
const EditService = ({
|
||||
title,
|
||||
code,
|
||||
formik,
|
||||
fields,
|
||||
handleClose,
|
||||
save,
|
||||
...props
|
||||
}) => {
|
||||
const [error, setError] = useState(props.error)
|
||||
|
||||
const classes = useStyles()
|
||||
|
||||
const submitWrapperClasses = {
|
||||
[classes.submitWrapper]: true,
|
||||
[classes.submitError]: error
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper className={classes.paper}>
|
||||
<button onClick={() => handleClose()}>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
<div className={classes.modalHeader}>
|
||||
<H2>{`Edit ${title}`}</H2>
|
||||
</div>
|
||||
<div className={classes.modalBody}>
|
||||
<Formik
|
||||
initialValues={formik.initialValues}
|
||||
validate={formik.validate}
|
||||
validationSchema={formik.validationSchema}
|
||||
onSubmit={values => {
|
||||
save(code, values)
|
||||
.then(m => handleClose())
|
||||
.catch(err => {
|
||||
if (err) setError(true)
|
||||
})
|
||||
}}>
|
||||
<Form>
|
||||
<div className={classes.formBody}>
|
||||
{fields &&
|
||||
fields.map((field, idx) => (
|
||||
<div key={idx} className={classes.field}>
|
||||
<Field
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
component={field.component}
|
||||
placeholder={field.placeholder}
|
||||
type={field.type}
|
||||
label={field.label}
|
||||
onFocus={() => {
|
||||
setError(null)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className={classnames(submitWrapperClasses)}>
|
||||
<div className={classes.messageWrapper}>
|
||||
{error && (
|
||||
<div>
|
||||
<ErrorIcon />
|
||||
<Info3 className={classes.message}>
|
||||
{DEFAULT_ERROR_MESSAGE}
|
||||
</Info3>
|
||||
</div>
|
||||
)}
|
||||
<Button type="submit">Save changes</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</Formik>
|
||||
</div>
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
|
||||
export default EditService
|
||||
117
new-lamassu-admin/src/pages/Services/Infura.js
Normal file
117
new-lamassu-admin/src/pages/Services/Infura.js
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
import React, { memo } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
import TextInputFormik from 'src/components/inputs/formik/TextInput'
|
||||
import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
|
||||
|
||||
import { Card, getValue as getValueAux, formatLong } from './aux'
|
||||
import EditService from './EditService'
|
||||
|
||||
const schema = {
|
||||
apiKey: {
|
||||
code: 'apiKey',
|
||||
display: 'API Key'
|
||||
},
|
||||
apiSecret: {
|
||||
code: 'apiSecret',
|
||||
display: 'API Secret'
|
||||
},
|
||||
endpoint: {
|
||||
code: 'endpoint',
|
||||
display: 'Endpoint'
|
||||
}
|
||||
}
|
||||
|
||||
const InfuraCard = memo(({ account, onEdit, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const apiKey = schema.apiKey
|
||||
const apiSecret = schema.apiSecret
|
||||
|
||||
const apiKeyValue = getValue(apiKey.code)
|
||||
const apiSecretValue = getValue(apiSecret.code)
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: apiKey.display,
|
||||
value: formatLong(apiKeyValue)
|
||||
},
|
||||
{
|
||||
label: apiSecret.display,
|
||||
value: formatLong(apiSecretValue)
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<Card
|
||||
account={account}
|
||||
title="Infura (Wallet)"
|
||||
items={items}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
const InfuraForm = ({ account, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const { code } = account
|
||||
const apiKey = getValue(schema.apiKey.code)
|
||||
const apiSecret = getValue(schema.apiSecret.code)
|
||||
const endpoint = getValue(schema.endpoint.code)
|
||||
|
||||
const formik = {
|
||||
initialValues: {
|
||||
apiKey: apiKey,
|
||||
apiSecret: apiSecret,
|
||||
endpoint: endpoint
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
apiKey: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required'),
|
||||
apiSecret: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required'),
|
||||
endpoint: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.url('Please input a valid url')
|
||||
.required('Required')
|
||||
})
|
||||
}
|
||||
|
||||
const fields = [
|
||||
{
|
||||
name: schema.apiKey.code,
|
||||
label: schema.apiKey.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.apiSecret.code,
|
||||
label: schema.apiSecret.display,
|
||||
type: 'text',
|
||||
component: SecretInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.endpoint.code,
|
||||
label: schema.endpoint.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<EditService
|
||||
title="Infura"
|
||||
formik={formik}
|
||||
code={code}
|
||||
fields={fields}
|
||||
{...props}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export { InfuraCard, InfuraForm }
|
||||
126
new-lamassu-admin/src/pages/Services/Itbit.js
Normal file
126
new-lamassu-admin/src/pages/Services/Itbit.js
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
import React, { memo } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
import TextInputFormik from 'src/components/inputs/formik/TextInput'
|
||||
import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
|
||||
|
||||
import { Card, getValue as getValueAux, formatLong } from './aux'
|
||||
import EditService from './EditService'
|
||||
|
||||
const schema = {
|
||||
userId: {
|
||||
code: 'userId',
|
||||
display: 'User ID'
|
||||
},
|
||||
walletId: {
|
||||
code: 'walletId',
|
||||
display: 'Wallet ID'
|
||||
},
|
||||
clientKey: {
|
||||
code: 'clientKey',
|
||||
display: 'Client Key'
|
||||
},
|
||||
clientSecret: {
|
||||
code: 'clientSecret',
|
||||
display: 'Client Secret'
|
||||
}
|
||||
}
|
||||
|
||||
const ItbitCard = memo(({ account, onEdit, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const userId = schema.userId
|
||||
const walletId = schema.walletId
|
||||
|
||||
const userIdValue = getValue(userId.code)
|
||||
const walletIdValue = getValue(walletId.code)
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: userId.display,
|
||||
value: formatLong(userIdValue)
|
||||
},
|
||||
{
|
||||
label: walletId.display,
|
||||
value: formatLong(walletIdValue)
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<Card account={account} title="itBit ()" items={items} onEdit={onEdit} />
|
||||
)
|
||||
})
|
||||
|
||||
const ItbitForm = ({ account, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const { code } = account
|
||||
const userId = getValue(schema.userId.code)
|
||||
const walletId = getValue(schema.walletId.code)
|
||||
const clientKey = getValue(schema.clientKey.code)
|
||||
const clientSecret = getValue(schema.clientSecret.code)
|
||||
|
||||
const formik = {
|
||||
initialValues: {
|
||||
userId: userId,
|
||||
walletId: walletId,
|
||||
clientKey: clientKey,
|
||||
clientSecret: clientSecret
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
userId: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required'),
|
||||
walletId: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required'),
|
||||
clientKey: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required'),
|
||||
clientSecret: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required')
|
||||
})
|
||||
}
|
||||
|
||||
const fields = [
|
||||
{
|
||||
name: schema.userId.code,
|
||||
label: schema.userId.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.walletId.code,
|
||||
label: schema.walletId.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.clientKey.code,
|
||||
label: schema.clientKey.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.clientSecret.code,
|
||||
label: schema.clientSecret.display,
|
||||
type: 'text',
|
||||
component: SecretInputFormik
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<EditService
|
||||
title="itBit"
|
||||
formik={formik}
|
||||
code={code}
|
||||
fields={fields}
|
||||
{...props}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export { ItbitCard, ItbitForm }
|
||||
101
new-lamassu-admin/src/pages/Services/Kraken.js
Normal file
101
new-lamassu-admin/src/pages/Services/Kraken.js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import React, { memo } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
import TextInputFormik from 'src/components/inputs/formik/TextInput'
|
||||
import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
|
||||
|
||||
import { Card, getValue as getValueAux, formatLong } from './aux'
|
||||
import EditService from './EditService'
|
||||
|
||||
const schema = {
|
||||
apiKey: {
|
||||
code: 'apiKey',
|
||||
display: 'API Key'
|
||||
},
|
||||
privateKey: {
|
||||
code: 'privateKey',
|
||||
display: 'Private Key'
|
||||
}
|
||||
}
|
||||
|
||||
const KrakenCard = memo(({ account, onEdit, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const apiKey = schema.apiKey
|
||||
const privateKey = schema.privateKey
|
||||
|
||||
const apiKeyValue = getValue(apiKey.code)
|
||||
const privateKeyValue = getValue(privateKey.code)
|
||||
|
||||
const items = [
|
||||
apiKey && {
|
||||
label: apiKey.display,
|
||||
value: formatLong(apiKeyValue)
|
||||
},
|
||||
privateKey && {
|
||||
label: privateKey.display,
|
||||
value: formatLong(privateKeyValue)
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<Card
|
||||
account={account}
|
||||
title="Kraken (Exchange)"
|
||||
items={items}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
const KrakenForm = ({ account, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const { code } = account
|
||||
const apiKey = getValue(schema.apiKey.code)
|
||||
const privateKey = getValue(schema.privateKey.code)
|
||||
|
||||
const formik = {
|
||||
initialValues: {
|
||||
apiKey: apiKey,
|
||||
privateKey: privateKey
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
apiKey: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required'),
|
||||
privateKey: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required')
|
||||
})
|
||||
}
|
||||
|
||||
const fields = [
|
||||
{
|
||||
name: schema.apiKey.code,
|
||||
label: schema.apiKey.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.privateKey.code,
|
||||
label: schema.privateKey.display,
|
||||
type: 'text',
|
||||
component: SecretInputFormik
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<EditService
|
||||
title="Kraken"
|
||||
formik={formik}
|
||||
code={code}
|
||||
fields={fields}
|
||||
{...props}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export { KrakenCard, KrakenForm }
|
||||
132
new-lamassu-admin/src/pages/Services/Mailgun.js
Normal file
132
new-lamassu-admin/src/pages/Services/Mailgun.js
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import React, { memo } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
import TextInputFormik from 'src/components/inputs/formik/TextInput'
|
||||
|
||||
import { Card, getValue as getValueAux } from './aux'
|
||||
import EditService from './EditService'
|
||||
|
||||
const schema = {
|
||||
apiKey: {
|
||||
code: 'apiKey',
|
||||
display: 'API Key'
|
||||
},
|
||||
domain: {
|
||||
code: 'domain',
|
||||
display: 'Domain'
|
||||
},
|
||||
fromEmail: {
|
||||
code: 'fromEmail',
|
||||
display: 'From Email'
|
||||
},
|
||||
toEmail: {
|
||||
code: 'toEmail',
|
||||
display: 'To Email'
|
||||
}
|
||||
}
|
||||
|
||||
const MailgunCard = memo(({ account, onEdit, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const fromEmail = schema.fromEmail
|
||||
const toEmail = schema.toEmail
|
||||
|
||||
const fromEmailValue = getValue(fromEmail.code)
|
||||
const toEmailValue = getValue(toEmail.code)
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: fromEmail.display,
|
||||
value: fromEmailValue
|
||||
},
|
||||
{
|
||||
label: toEmail.display,
|
||||
value: toEmailValue
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<Card
|
||||
account={account}
|
||||
title="Mailgun (Email)"
|
||||
items={items}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
const MailgunForm = ({ account, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const { code } = account
|
||||
const apiKey = getValue(schema.apiKey.code)
|
||||
const domain = getValue(schema.domain.code)
|
||||
const fromEmail = getValue(schema.fromEmail.code)
|
||||
const toEmail = getValue(schema.toEmail.code)
|
||||
|
||||
const formik = {
|
||||
initialValues: {
|
||||
apiKey: apiKey,
|
||||
domain: domain,
|
||||
fromEmail: fromEmail,
|
||||
toEmail: toEmail
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
apiKey: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required'),
|
||||
domain: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required'),
|
||||
fromEmail: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.email('Please input a valid email address')
|
||||
.required('Required'),
|
||||
toEmail: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.email('Please input a valid email address')
|
||||
.required('Required')
|
||||
})
|
||||
}
|
||||
|
||||
const fields = [
|
||||
{
|
||||
name: schema.apiKey.code,
|
||||
label: schema.apiKey.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.domain.code,
|
||||
label: schema.domain.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.fromEmail.code,
|
||||
label: schema.fromEmail.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.toEmail.code,
|
||||
label: schema.toEmail.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<EditService
|
||||
title="Mailgun"
|
||||
formik={formik}
|
||||
code={code}
|
||||
fields={fields}
|
||||
{...props}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export { MailgunCard, MailgunForm }
|
||||
242
new-lamassu-admin/src/pages/Services/Services.js
Normal file
242
new-lamassu-admin/src/pages/Services/Services.js
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
import React, { useState } from 'react'
|
||||
import * as R from 'ramda'
|
||||
import { gql } from 'apollo-boost'
|
||||
import { makeStyles, Modal } from '@material-ui/core'
|
||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
|
||||
import Title from 'src/components/Title'
|
||||
|
||||
import { BitgoCard, BitgoForm } from './Bitgo'
|
||||
import { BitstampCard, BitstampForm } from './Bitstamp'
|
||||
import { BlockcypherCard, BlockcypherForm } from './Blockcypher'
|
||||
import { InfuraCard, InfuraForm } from './Infura'
|
||||
import { ItbitCard, ItbitForm } from './Itbit'
|
||||
import { KrakenCard, KrakenForm } from './Kraken'
|
||||
import { MailgunCard, MailgunForm } from './Mailgun'
|
||||
import { StrikeCard, StrikeForm } from './Strike'
|
||||
import { TwilioCard, TwilioForm } from './Twilio'
|
||||
import { servicesStyles as styles } from './Services.styles'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const GET_CONFIG = gql`
|
||||
{
|
||||
config
|
||||
}
|
||||
`
|
||||
|
||||
const GET_ACCOUNTS = gql`
|
||||
{
|
||||
accounts {
|
||||
code
|
||||
display
|
||||
class
|
||||
cryptos
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const SAVE_CONFIG = gql`
|
||||
mutation Save($config: JSONObject) {
|
||||
saveConfig(config: $config)
|
||||
}
|
||||
`
|
||||
|
||||
const Services = () => {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [modalContent, setModalContent] = useState(null)
|
||||
const [accountsConfig, setAccountsConfig] = useState(null)
|
||||
const [saveConfig, { loading }] = useMutation(SAVE_CONFIG, {
|
||||
onCompleted: data => setAccountsConfig(data.saveConfig.accounts)
|
||||
})
|
||||
|
||||
const classes = useStyles()
|
||||
|
||||
useQuery(GET_CONFIG, {
|
||||
onCompleted: data => setAccountsConfig(data.config.accounts ?? {})
|
||||
})
|
||||
const { data: accountsResponse } = useQuery(GET_ACCOUNTS)
|
||||
|
||||
const accounts = accountsResponse?.accounts
|
||||
|
||||
const save = (code, it) => {
|
||||
const newAccounts = R.clone(accountsConfig)
|
||||
newAccounts[code] = it
|
||||
return saveConfig({ variables: { config: { accounts: newAccounts } } })
|
||||
}
|
||||
|
||||
const getAccount = code => {
|
||||
return R.mergeDeepLeft(
|
||||
R.find(R.propEq('code', code))(accounts) ?? {},
|
||||
accountsConfig[code] ?? {}
|
||||
)
|
||||
}
|
||||
|
||||
const handleOpen = content => {
|
||||
setOpen(true)
|
||||
setModalContent(content)
|
||||
}
|
||||
|
||||
const handleClose = (canClose = true) => {
|
||||
if (canClose && !loading) {
|
||||
setOpen(false)
|
||||
setModalContent(null)
|
||||
}
|
||||
}
|
||||
|
||||
if (!accounts || !accountsConfig) return null
|
||||
|
||||
const codes = {
|
||||
bitgo: 'bitgo',
|
||||
bitstamp: 'bitstamp',
|
||||
blockcypher: 'blockcypher',
|
||||
infura: 'infura',
|
||||
itbit: 'itbit',
|
||||
kraken: 'kraken',
|
||||
mailgun: 'mailgun',
|
||||
strike: 'strike',
|
||||
twilio: 'twilio'
|
||||
}
|
||||
|
||||
const bitgo = getAccount(codes.bitgo)
|
||||
const bitstamp = getAccount(codes.bitstamp)
|
||||
const blockcypher = getAccount(codes.blockcypher)
|
||||
const infura = getAccount(codes.infura)
|
||||
const itbit = getAccount(codes.itbit)
|
||||
const kraken = getAccount(codes.kraken)
|
||||
const mailgun = getAccount(codes.mailgun)
|
||||
const strike = getAccount(codes.strike)
|
||||
const twilio = getAccount(codes.twilio)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={classes.titleWrapper}>
|
||||
<div className={classes.titleContainer}>
|
||||
<Title>3rd Party Services</Title>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.mainWrapper}>
|
||||
<BitgoCard
|
||||
account={bitgo}
|
||||
onEdit={() =>
|
||||
handleOpen(
|
||||
<BitgoForm
|
||||
account={bitgo}
|
||||
handleClose={handleClose}
|
||||
save={save}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<BitstampCard
|
||||
account={bitstamp}
|
||||
onEdit={() =>
|
||||
handleOpen(
|
||||
<BitstampForm
|
||||
account={bitstamp}
|
||||
handleClose={handleClose}
|
||||
save={save}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<BlockcypherCard
|
||||
account={blockcypher}
|
||||
onEdit={() =>
|
||||
handleOpen(
|
||||
<BlockcypherForm
|
||||
account={blockcypher}
|
||||
handleClose={handleClose}
|
||||
save={save}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<InfuraCard
|
||||
account={infura}
|
||||
onEdit={() =>
|
||||
handleOpen(
|
||||
<InfuraForm
|
||||
account={infura}
|
||||
handleClose={handleClose}
|
||||
save={save}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<ItbitCard
|
||||
account={itbit}
|
||||
onEdit={() =>
|
||||
handleOpen(
|
||||
<ItbitForm
|
||||
account={itbit}
|
||||
handleClose={handleClose}
|
||||
save={save}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<KrakenCard
|
||||
account={kraken}
|
||||
onEdit={() =>
|
||||
handleOpen(
|
||||
<KrakenForm
|
||||
account={kraken}
|
||||
handleClose={handleClose}
|
||||
save={save}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<MailgunCard
|
||||
account={mailgun}
|
||||
onEdit={() =>
|
||||
handleOpen(
|
||||
<MailgunForm
|
||||
account={mailgun}
|
||||
handleClose={handleClose}
|
||||
save={save}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<StrikeCard
|
||||
account={strike}
|
||||
onEdit={() =>
|
||||
handleOpen(
|
||||
<StrikeForm
|
||||
account={strike}
|
||||
handleClose={handleClose}
|
||||
save={save}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<TwilioCard
|
||||
account={twilio}
|
||||
onEdit={() =>
|
||||
handleOpen(
|
||||
<TwilioForm
|
||||
account={twilio}
|
||||
handleClose={handleClose}
|
||||
save={save}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{modalContent && (
|
||||
<Modal
|
||||
aria-labelledby="simple-modal-title"
|
||||
aria-describedby="simple-modal-description"
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
className={classes.modal}>
|
||||
<div>{modalContent}</div>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Services
|
||||
177
new-lamassu-admin/src/pages/Services/Services.styles.js
Normal file
177
new-lamassu-admin/src/pages/Services/Services.styles.js
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
import { white, offColor, errorColor } from 'src/styling/variables'
|
||||
import typographyStyles from 'src/components/typography/styles'
|
||||
import baseStyles from 'src/pages/Logs.styles'
|
||||
|
||||
const { titleWrapper } = baseStyles
|
||||
const { label1, p } = typographyStyles
|
||||
|
||||
const servicesStyles = {
|
||||
titleWrapper,
|
||||
titleContainer: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
width: '100%'
|
||||
},
|
||||
addServiceMenu: {
|
||||
width: 215,
|
||||
'& > ul': {
|
||||
padding: [[18, 16, 21, 16]],
|
||||
'& > li': {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
listStyle: 'none',
|
||||
marginBottom: 23,
|
||||
cursor: 'pointer',
|
||||
'& > span:first-child': {
|
||||
extend: p,
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
'& > span:last-child': {
|
||||
extend: label1,
|
||||
color: offColor
|
||||
},
|
||||
'&:last-child': {
|
||||
marginBottom: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mainWrapper: {
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap'
|
||||
},
|
||||
modal: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
'& > div': {
|
||||
outline: 'none'
|
||||
}
|
||||
},
|
||||
modalHeader: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
'& button': {
|
||||
border: 'none',
|
||||
backgroundColor: 'transparent',
|
||||
cursor: 'pointer'
|
||||
}
|
||||
},
|
||||
modalBody: {
|
||||
'& > form': {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
position: 'relative',
|
||||
minHeight: 400,
|
||||
'& > div:last-child': {
|
||||
display: 'flex',
|
||||
alignItems: 'flex-end',
|
||||
flex: 'auto',
|
||||
alignSelf: 'flex-end',
|
||||
'& > button': {
|
||||
marginTop: 32
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
paper: {
|
||||
position: 'absolute',
|
||||
backgroundColor: white,
|
||||
outline: '0 none',
|
||||
padding: [[16, 20, 32, 24]]
|
||||
},
|
||||
inputField: {
|
||||
width: 434
|
||||
},
|
||||
formLabel: {
|
||||
extend: label1
|
||||
}
|
||||
}
|
||||
|
||||
const editServiceStyles = {
|
||||
paper: {
|
||||
padding: [[5, 20, 32, 24]],
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
minHeight: 524,
|
||||
overflow: 'hidden',
|
||||
'& > button': {
|
||||
position: 'absolute',
|
||||
top: 16,
|
||||
right: 16,
|
||||
border: 'none',
|
||||
backgroundColor: 'transparent',
|
||||
cursor: 'pointer',
|
||||
'& svg': {
|
||||
width: 18
|
||||
}
|
||||
},
|
||||
'& form': {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 2
|
||||
}
|
||||
},
|
||||
modalHeader: {
|
||||
display: 'flex',
|
||||
marginBottom: 14
|
||||
},
|
||||
modalBody: {
|
||||
display: 'flex',
|
||||
flexGrow: 2
|
||||
},
|
||||
formBody: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
field: {
|
||||
position: 'relative',
|
||||
'& > div': {
|
||||
width: 434
|
||||
}
|
||||
},
|
||||
submitWrapper: {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'flex-end',
|
||||
flexGrow: 2,
|
||||
marginTop: 32,
|
||||
'& > div': {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
justifyContent: 'flex-end',
|
||||
'& > button': {
|
||||
'&:active': {
|
||||
marginTop: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
submitError: {
|
||||
'& > div': {
|
||||
justifyContent: 'space-between'
|
||||
}
|
||||
},
|
||||
messageWrapper: {
|
||||
'& > div': {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
'& > svg': {
|
||||
marginRight: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
message: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
color: errorColor,
|
||||
margin: 0,
|
||||
whiteSpace: 'break-spaces',
|
||||
width: 250
|
||||
}
|
||||
}
|
||||
|
||||
export { servicesStyles, editServiceStyles }
|
||||
79
new-lamassu-admin/src/pages/Services/Strike.js
Normal file
79
new-lamassu-admin/src/pages/Services/Strike.js
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
import React, { memo } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
|
||||
|
||||
import { Card, getValue as getValueAux, formatLong } from './aux'
|
||||
import EditService from './EditService'
|
||||
|
||||
const schema = {
|
||||
token: {
|
||||
code: 'token',
|
||||
display: 'API Token'
|
||||
}
|
||||
}
|
||||
|
||||
const StrikeCard = memo(({ account, onEdit, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const token = schema.token
|
||||
|
||||
const tokenValue = getValue(token.code)
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: token.display,
|
||||
value: formatLong(tokenValue)
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<Card
|
||||
account={account}
|
||||
title="Strike (Lightning Payments)"
|
||||
items={items}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
const StrikeForm = ({ account, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const code = 'strike'
|
||||
const token = getValue(schema.token.code)
|
||||
|
||||
const formik = {
|
||||
initialValues: {
|
||||
token: token
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
token: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required')
|
||||
})
|
||||
}
|
||||
|
||||
const fields = [
|
||||
{
|
||||
name: schema.token.code,
|
||||
label: schema.token.display,
|
||||
type: 'text',
|
||||
component: SecretInputFormik
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<EditService
|
||||
title="Strike"
|
||||
formik={formik}
|
||||
code={code}
|
||||
fields={fields}
|
||||
{...props}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export { StrikeCard, StrikeForm }
|
||||
131
new-lamassu-admin/src/pages/Services/Twilio.js
Normal file
131
new-lamassu-admin/src/pages/Services/Twilio.js
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
import React, { memo } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
import TextInputFormik from 'src/components/inputs/formik/TextInput'
|
||||
import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
|
||||
|
||||
import { Card, getValue as getValueAux } from './aux'
|
||||
import EditService from './EditService'
|
||||
|
||||
const schema = {
|
||||
accountSid: {
|
||||
code: 'accountSid',
|
||||
display: 'Account SID'
|
||||
},
|
||||
authToken: {
|
||||
code: 'authToken',
|
||||
display: 'Auth Token'
|
||||
},
|
||||
fromNumber: {
|
||||
code: 'fromNumber',
|
||||
display: 'From Number'
|
||||
},
|
||||
toNumber: {
|
||||
code: 'toNumber',
|
||||
display: 'To Number'
|
||||
}
|
||||
}
|
||||
|
||||
const TwilioCard = memo(({ account, onEdit, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const fromNumber = schema.fromNumber
|
||||
const toNumber = schema.toNumber
|
||||
|
||||
const fromNumberValue = getValue(fromNumber.code)
|
||||
const toNumberValue = getValue(toNumber.code)
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: fromNumber.display,
|
||||
value: fromNumberValue
|
||||
},
|
||||
{
|
||||
label: toNumber.display,
|
||||
value: toNumberValue
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<Card
|
||||
account={account}
|
||||
title="Twilio (SMS)"
|
||||
items={items}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
const TwilioForm = ({ account, ...props }) => {
|
||||
const getValue = getValueAux(account)
|
||||
|
||||
const { code } = account
|
||||
const accountSid = getValue(schema.accountSid.code)
|
||||
const authToken = getValue(schema.authToken.code)
|
||||
const fromNumber = getValue(schema.fromNumber.code)
|
||||
const toNumber = getValue(schema.toNumber.code)
|
||||
|
||||
const formik = {
|
||||
initialValues: {
|
||||
accountSid: accountSid,
|
||||
authToken: authToken,
|
||||
fromNumber: fromNumber,
|
||||
toNumber: toNumber
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
accountSid: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required'),
|
||||
authToken: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required'),
|
||||
fromNumber: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required'),
|
||||
toNumber: Yup.string()
|
||||
.max(100, 'Too long')
|
||||
.required('Required')
|
||||
})
|
||||
}
|
||||
|
||||
const fields = [
|
||||
{
|
||||
name: schema.accountSid.code,
|
||||
label: schema.accountSid.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.authToken.code,
|
||||
label: schema.authToken.display,
|
||||
type: 'text',
|
||||
component: SecretInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.fromNumber.code,
|
||||
label: schema.fromNumber.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
},
|
||||
{
|
||||
name: schema.toNumber.code,
|
||||
label: schema.toNumber.display,
|
||||
type: 'text',
|
||||
component: TextInputFormik
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<EditService
|
||||
title="Twilio"
|
||||
formik={formik}
|
||||
code={code}
|
||||
fields={fields}
|
||||
{...props}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export { TwilioCard, TwilioForm }
|
||||
44
new-lamassu-admin/src/pages/Services/aux.js
Normal file
44
new-lamassu-admin/src/pages/Services/aux.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import React from 'react'
|
||||
import * as R from 'ramda'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
|
||||
import SingleRowTable from 'src/components/single-row-table/SingleRowTable'
|
||||
|
||||
const getValue = R.curry((account, code) => account[code] ?? '')
|
||||
|
||||
const formatLong = value => {
|
||||
if (!value) return ''
|
||||
if (value.length <= 20) return value
|
||||
|
||||
return `${value.slice(0, 8)}(...)${value.slice(
|
||||
value.length - 8,
|
||||
value.length
|
||||
)}`
|
||||
}
|
||||
|
||||
const styles = {
|
||||
card: {
|
||||
margin: [[0, 30, 32, 0]],
|
||||
paddingBottom: 24,
|
||||
'&:nth-child(3n+3)': {
|
||||
marginRight: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const Card = ({ account, title, items, onEdit, ...props }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<SingleRowTable
|
||||
title={title}
|
||||
items={items}
|
||||
className={classes.card}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Card, getValue, formatLong }
|
||||
|
|
@ -10,6 +10,8 @@ import { comet } from 'src/styling/variables'
|
|||
|
||||
import { cpcStyles } from './Transactions.styles'
|
||||
|
||||
const useStyles = makeStyles(cpcStyles)
|
||||
|
||||
const CopyToClipboard = ({ className, children, ...props }) => {
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
|
||||
|
|
@ -17,8 +19,6 @@ const CopyToClipboard = ({ className, children, ...props }) => {
|
|||
if (anchorEl) setTimeout(() => setAnchorEl(null), 3000)
|
||||
}, [anchorEl])
|
||||
|
||||
const useStyles = makeStyles(cpcStyles)
|
||||
|
||||
const classes = useStyles()
|
||||
|
||||
const handleClick = event => {
|
||||
|
|
|
|||
|
|
@ -19,18 +19,18 @@ import { onlyFirstToUpper } from 'src/utils/string'
|
|||
import CopyToClipboard from './CopyToClipboard'
|
||||
import { detailsRowStyles, labelStyles } from './Transactions.styles'
|
||||
|
||||
const Label = ({ children }) => {
|
||||
const useStyles = makeStyles(labelStyles)
|
||||
const labelUseStyles = makeStyles(labelStyles)
|
||||
|
||||
const classes = useStyles()
|
||||
const Label = ({ children }) => {
|
||||
const classes = labelUseStyles()
|
||||
|
||||
return <div className={classes.label}>{children}</div>
|
||||
}
|
||||
|
||||
const DetailsRow = ({ it: tx, ...props }) => {
|
||||
const useStyles = makeStyles(detailsRowStyles)
|
||||
const detailsUseStyles = makeStyles(detailsRowStyles)
|
||||
|
||||
const classes = useStyles()
|
||||
const DetailsRow = ({ it: tx, ...props }) => {
|
||||
const classes = detailsUseStyles()
|
||||
|
||||
const addr = tx.toAddress
|
||||
const txHash = tx.txHash
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ import { toUnit } from 'src/utils/coin'
|
|||
import DetailsRow from './DetailsCard'
|
||||
import { mainStyles } from './Transactions.styles'
|
||||
|
||||
const useStyles = makeStyles(mainStyles)
|
||||
|
||||
// TODO customerIdCardData
|
||||
const GET_TRANSACTIONS = gql`
|
||||
{
|
||||
|
|
@ -49,8 +51,6 @@ const GET_TRANSACTIONS = gql`
|
|||
const Transactions = () => {
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
|
||||
const useStyles = makeStyles(mainStyles)
|
||||
|
||||
const classes = useStyles()
|
||||
|
||||
const { data: txResponse } = useQuery(GET_TRANSACTIONS)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import Locales from 'src/pages/Locales'
|
|||
import Logs from 'src/pages/Logs'
|
||||
import ServerLogs from 'src/pages/ServerLogs'
|
||||
import Transactions from 'src/pages/Transactions/Transactions'
|
||||
import Services from 'src/pages/Services/Services'
|
||||
import AuthRegister from 'src/pages/AuthRegister'
|
||||
|
||||
const tree = [
|
||||
|
|
@ -38,7 +39,12 @@ const tree = [
|
|||
label: 'Commissions',
|
||||
route: '/settings/commissions'
|
||||
},
|
||||
{ key: 'locale', label: 'Locale', route: '/settings/locale' }
|
||||
{ key: 'locale', label: 'Locale', route: '/settings/locale' },
|
||||
{
|
||||
key: 'services',
|
||||
label: '3rd party services',
|
||||
route: '/settings/3rd-party-services'
|
||||
}
|
||||
]
|
||||
}
|
||||
// compliance: { label: 'Compliance', children: [{ label: 'Locale', route: '/locale' }] }
|
||||
|
|
@ -65,6 +71,7 @@ const Routes = () => (
|
|||
/>
|
||||
<Route path="/settings/commissions" component={Commissions} />
|
||||
<Route path="/settings/locale" component={Locales} />
|
||||
<Route path="/settings/3rd-party-services" component={Services} />
|
||||
<Route path="/maintenance/logs" component={Logs} />
|
||||
<Route path="/maintenance/funding" component={Funding} />
|
||||
<Route path="/maintenance/server-logs" component={ServerLogs} />
|
||||
|
|
|
|||
|
|
@ -1,24 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="18px" height="18px" viewBox="0 0 18 18" 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/close/zodiac</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<path d="M11.10567,8.99966734 L17.5616855,15.4562245 C18.1453995,16.0371533 18.1457502,16.9810221 17.5632744,17.5635465 C17.2826878,17.8441564 16.9043531,17.9996031 16.509308,17.9996031 C16.1150437,17.9996031 15.7367759,17.8439272 15.4563692,17.5634971 L8.99995005,11.1055617 L2.54567824,17.5603569 C2.26578723,17.8430155 1.88614994,17.9996031 1.48961505,17.9996031 C1.09473832,17.9996031 0.717380733,17.844225 0.436725633,17.5635465 C-0.145575211,16.9811971 -0.145575211,16.0373273 0.436725633,15.4578096 L6.89433001,8.99966707 L0.438314479,2.54310994 C-0.145399464,1.96218116 -0.145750215,1.01831232 0.436725633,0.435787934 C1.01746304,-0.144997872 1.95893893,-0.144997872 2.54250446,0.435787934 L8.99995074,6.89377234 L15.4580075,0.434202817 C16.0398949,-0.144908147 16.9801496,-0.144559181 17.5632744,0.435787934 C18.1455752,1.0181373 18.1455752,1.96200713 17.5632744,2.54152483 L11.10567,8.99966734 Z" id="path-1"></path>
|
||||
<rect id="path-3" x="0" y="0" width="18" height="18"></rect>
|
||||
</defs>
|
||||
<g id="icon/action/close/zodiac" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="color/primary/zodiac" transform="translate(-0.000000, 0.000000)">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<use id="Mask" fill="#1B2559" xlink:href="#path-1"></use>
|
||||
<g mask="url(#mask-2)">
|
||||
<mask id="mask-4" fill="white">
|
||||
<use xlink:href="#path-3"></use>
|
||||
</mask>
|
||||
<use id="Background" fill="#1B2559" fill-rule="evenodd" xlink:href="#path-3"></use>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 18 18" style="enable-background:new 0 0 18 18;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#1B2559;}
|
||||
</style>
|
||||
<title>icon/action/close/zodiac</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="color_x2F_primary_x2F_zodiac" transform="translate(-0.000000, 0.000000)">
|
||||
<g id="Mask">
|
||||
<path id="path-1_1_" class="st0" d="M11.1,9l6.5,6.5c0.6,0.6,0.6,1.5,0,2.1c-0.3,0.3-0.7,0.4-1.1,0.4c-0.4,0-0.8-0.2-1.1-0.4
|
||||
L9,11.1l-6.5,6.5C2.3,17.8,1.9,18,1.5,18c-0.4,0-0.8-0.2-1.1-0.4c-0.6-0.6-0.6-1.5,0-2.1L6.9,9L0.4,2.5C-0.1,2-0.1,1,0.4,0.4
|
||||
C1-0.1,2-0.1,2.5,0.4L9,6.9l6.5-6.5c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1L11.1,9z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 964 B |
12
new-lamassu-admin/src/styling/icons/warning-icon/comet.svg
Normal file
12
new-lamassu-admin/src/styling/icons/warning-icon/comet.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" 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/warning-icon/comet</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="icon/warning-icon/comet">
|
||||
<rect id="Rectangle" stroke="#5F668A" stroke-width="2" x="1" y="1" width="22" height="22" rx="11"></rect>
|
||||
<path d="M11.2971429,14.4857143 L11.1085714,5.82857143 L13.3714286,5.82857143 L13.2,14.4857143 L11.2971429,14.4857143 Z M11.1942857,18 L11.1942857,15.9771429 L13.3028571,15.9771429 L13.3028571,18 L11.1942857,18 Z" id="!" fill="#5F668A" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 877 B |
12
new-lamassu-admin/src/styling/icons/warning-icon/tomato.svg
Normal file
12
new-lamassu-admin/src/styling/icons/warning-icon/tomato.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" 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/warning-icon/tomato</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="icon/warning-icon/tomato">
|
||||
<rect id="Rectangle" stroke="#FF584A" stroke-width="2" x="1" y="1" width="22" height="22" rx="11"></rect>
|
||||
<path d="M11.2971429,14.4857143 L11.1085714,5.82857143 L13.3714286,5.82857143 L13.2,14.4857143 L11.2971429,14.4857143 Z M11.1942857,18 L11.1942857,15.9771429 L13.3028571,15.9771429 L13.3028571,18 L11.1942857,18 Z" id="!" fill="#FF584A" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 879 B |
|
|
@ -103,6 +103,8 @@ const tableSmCellHeight = 30
|
|||
const tableLgCellHeight = 76
|
||||
|
||||
const tableHeaderColor = primaryColor
|
||||
const tableDisabledHeaderColor = zircon
|
||||
const tableNewDisabledHeaderColor = spring3
|
||||
const tableCellColor = white
|
||||
const tableErrorColor = mistyRose
|
||||
const tableSuccessColor = spring3
|
||||
|
|
@ -172,6 +174,8 @@ export {
|
|||
tableSmCellHeight,
|
||||
tableLgCellHeight,
|
||||
tableHeaderColor,
|
||||
tableDisabledHeaderColor,
|
||||
tableNewDisabledHeaderColor,
|
||||
tableCellColor,
|
||||
tableErrorColor,
|
||||
tableSuccessColor
|
||||
|
|
|
|||
19
package-lock.json
generated
19
package-lock.json
generated
|
|
@ -58,7 +58,7 @@
|
|||
"@ava/babel-preset-stage-4": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@ava/babel-preset-stage-4/-/babel-preset-stage-4-1.1.0.tgz",
|
||||
"integrity": "sha1-rmC+iBoLq/fTX1Krp3DR9hlPdr0=",
|
||||
"integrity": "sha512-oWqTnIGXW3k72UFidXzW0ONlO7hnO9x02S/QReJ7NBGeiBH9cUHY9+EfV6C8PXC6YJH++WrliEq03wMSJGNZFg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-plugin-check-es2015-constants": "^6.8.0",
|
||||
|
|
@ -4070,7 +4070,7 @@
|
|||
},
|
||||
"dotenv": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "http://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz",
|
||||
"integrity": "sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0="
|
||||
},
|
||||
"drbg.js": {
|
||||
|
|
@ -6230,7 +6230,7 @@
|
|||
"globals": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
|
||||
"integrity": "sha1-qjiWs+abSH8X4x7SFD1pqOMMLYo=",
|
||||
"integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
|
||||
"dev": true
|
||||
},
|
||||
"globby": {
|
||||
|
|
@ -6257,7 +6257,7 @@
|
|||
"got": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz",
|
||||
"integrity": "sha1-BUUP2ECU5rvqVvRRpDqcKJFmOFo=",
|
||||
"integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==",
|
||||
"requires": {
|
||||
"decompress-response": "^3.2.0",
|
||||
"duplexer3": "^0.1.4",
|
||||
|
|
@ -7146,7 +7146,7 @@
|
|||
"hullabaloo-config-manager": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/hullabaloo-config-manager/-/hullabaloo-config-manager-1.1.1.tgz",
|
||||
"integrity": "sha1-HZEXgTEprQNf2ehHfq8GaREmn+M=",
|
||||
"integrity": "sha512-ztKnkZV0TmxnumCDHHgLGNiDnotu4EHCp9YMkznWuo4uTtCyJ+cu+RNcxUeXYKTllpvLFWnbfWry09yzszgg+A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"dot-prop": "^4.1.0",
|
||||
|
|
@ -7668,7 +7668,7 @@
|
|||
"isurl": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz",
|
||||
"integrity": "sha1-sn9PSfPNqj6kSgpbfzRi5u3DnWc=",
|
||||
"integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==",
|
||||
"requires": {
|
||||
"has-to-string-tag-x": "^1.2.0",
|
||||
"is-object": "^1.0.1"
|
||||
|
|
@ -8586,7 +8586,7 @@
|
|||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
|
@ -9163,7 +9163,7 @@
|
|||
"p-cancelable": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz",
|
||||
"integrity": "sha1-ueEjgAvOu3rBOkeb4ZW1B7mNMPo="
|
||||
"integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw=="
|
||||
},
|
||||
"p-each-series": {
|
||||
"version": "1.0.0",
|
||||
|
|
@ -12642,7 +12642,6 @@
|
|||
"resolved": "https://registry.npmjs.org/web3/-/web3-0.20.6.tgz",
|
||||
"integrity": "sha1-PpcwauAk+yThCj11yIQwJWIhUSA=",
|
||||
"requires": {
|
||||
"bignumber.js": "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934",
|
||||
"crypto-js": "^3.1.4",
|
||||
"utf8": "^2.1.1",
|
||||
"xhr2": "*",
|
||||
|
|
@ -12651,7 +12650,7 @@
|
|||
"dependencies": {
|
||||
"bignumber.js": {
|
||||
"version": "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934",
|
||||
"from": "git+https://github.com/frozeman/bignumber.js-nolookahead.git"
|
||||
"from": "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue