diff --git a/new-lamassu-admin/src/components/ErrorMessage.js b/new-lamassu-admin/src/components/ErrorMessage.js
index 2d95bbb4..8a1c891b 100644
--- a/new-lamassu-admin/src/components/ErrorMessage.js
+++ b/new-lamassu-admin/src/components/ErrorMessage.js
@@ -1,6 +1,6 @@
-import React from 'react'
-import classnames from 'classnames'
import { makeStyles } from '@material-ui/core'
+import classnames from 'classnames'
+import React from 'react'
import { ReactComponent as ErrorIcon } from 'src/styling/icons/warning-icon/tomato.svg'
import { errorColor } from 'src/styling/variables'
@@ -20,8 +20,7 @@ const styles = {
alignItems: 'center',
color: errorColor,
margin: 0,
- whiteSpace: 'break-spaces',
- width: 250
+ whiteSpace: 'break-spaces'
}
}
diff --git a/new-lamassu-admin/src/components/Modal.js b/new-lamassu-admin/src/components/Modal.js
new file mode 100644
index 00000000..048586d1
--- /dev/null
+++ b/new-lamassu-admin/src/components/Modal.js
@@ -0,0 +1,54 @@
+import { makeStyles, Modal as MaterialModal, Paper } from '@material-ui/core'
+import classnames from 'classnames'
+import React from 'react'
+
+import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.svg'
+
+const styles = {
+ modal: {
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center'
+ },
+ modalContentWrapper: {
+ display: 'flex',
+ position: 'relative',
+ minHeight: 400,
+ maxHeight: '90vh',
+ overflowY: 'auto',
+ borderRadius: 8,
+ outline: 0,
+ '& > div': {
+ width: '100%'
+ }
+ },
+ closeIcon: {
+ position: 'absolute',
+ width: 18,
+ height: 18,
+ padding: 0,
+ top: 20,
+ right: 20
+ }
+}
+
+const useStyles = makeStyles(styles)
+
+const Modal = ({ handleClose, children, className, ...props }) => {
+ const classes = useStyles()
+
+ return (
+
+
+ handleClose()}>
+
+
+ {children}
+
+
+ )
+}
+
+export default Modal
diff --git a/new-lamassu-admin/src/components/Stage.js b/new-lamassu-admin/src/components/Stage.js
new file mode 100644
index 00000000..916daf4e
--- /dev/null
+++ b/new-lamassu-admin/src/components/Stage.js
@@ -0,0 +1,112 @@
+import { makeStyles } from '@material-ui/core'
+import classnames from 'classnames'
+import * as R from 'ramda'
+import React, { memo } from 'react'
+
+import { ReactComponent as CompleteStageIconSpring } from 'src/styling/icons/stage/spring/complete.svg'
+import { ReactComponent as CurrentStageIconSpring } from 'src/styling/icons/stage/spring/current.svg'
+import { ReactComponent as EmptyStageIconSpring } from 'src/styling/icons/stage/spring/empty.svg'
+import { ReactComponent as CompleteStageIconZodiac } from 'src/styling/icons/stage/zodiac/complete.svg'
+import { ReactComponent as CurrentStageIconZodiac } from 'src/styling/icons/stage/zodiac/current.svg'
+import { ReactComponent as EmptyStageIconZodiac } from 'src/styling/icons/stage/zodiac/empty.svg'
+import {
+ primaryColor,
+ secondaryColor,
+ offColor,
+ disabledColor
+} from 'src/styling/variables'
+
+const styles = {
+ stages: {
+ display: 'flex',
+ alignItems: 'center'
+ },
+ wrapper: {
+ display: 'flex',
+ alignItems: 'center',
+ margin: 0
+ },
+ stage: {
+ display: 'flex',
+ height: 28,
+ width: 28,
+ zIndex: 2,
+ '& > svg': {
+ height: '100%',
+ width: '100%',
+ overflow: 'visible'
+ }
+ },
+ separator: {
+ width: 28,
+ height: 2,
+ border: [[2, 'solid']],
+ zIndex: 1
+ },
+ separatorSpring: {
+ borderColor: secondaryColor
+ },
+ separatorZodiac: {
+ borderColor: primaryColor
+ },
+ separatorSpringEmpty: {
+ borderColor: disabledColor
+ },
+ separatorZodiacEmpty: {
+ borderColor: offColor
+ }
+}
+
+const useStyles = makeStyles(styles)
+
+const Stage = memo(({ stages, currentStage, color = 'spring', className }) => {
+ if (currentStage < 1 || currentStage > stages)
+ throw Error('Value of currentStage is invalid')
+ if (stages < 1) throw Error('Value of stages is invalid')
+
+ const classes = useStyles()
+
+ const separatorClasses = {
+ [classes.separator]: true,
+ [classes.separatorSpring]: color === 'spring',
+ [classes.separatorZodiac]: color === 'zodiac'
+ }
+
+ const separatorEmptyClasses = {
+ [classes.separator]: true,
+ [classes.separatorSpringEmpty]: color === 'spring',
+ [classes.separatorZodiacEmpty]: color === 'zodiac'
+ }
+
+ return (
+
+ {R.range(1, currentStage).map(idx => (
+
+ {idx > 1 &&
}
+
+ {color === 'spring' && }
+ {color === 'zodiac' && }
+
+
+ ))}
+
+ {currentStage > 1 &&
}
+
+ {color === 'spring' && }
+ {color === 'zodiac' && }
+
+
+ {R.range(currentStage + 1, stages + 1).map(idx => (
+
+
+
+ {color === 'spring' && }
+ {color === 'zodiac' && }
+
+
+ ))}
+
+ )
+})
+
+export default Stage
diff --git a/new-lamassu-admin/src/components/inputs/autocomplete/Autocomplete.js b/new-lamassu-admin/src/components/inputs/autocomplete/Autocomplete.js
new file mode 100644
index 00000000..4f5a5d5c
--- /dev/null
+++ b/new-lamassu-admin/src/components/inputs/autocomplete/Autocomplete.js
@@ -0,0 +1,121 @@
+import Paper from '@material-ui/core/Paper'
+import Popper from '@material-ui/core/Popper'
+import { withStyles } from '@material-ui/core/styles'
+import Downshift from 'downshift'
+import * as R from 'ramda'
+import React, { memo, useState } from 'react'
+
+import {
+ renderInput,
+ renderSuggestion,
+ filterSuggestions,
+ styles
+} from './commons'
+
+const Autocomplete = memo(
+ ({
+ suggestions,
+ classes,
+ placeholder,
+ label,
+ itemToString,
+ code = 'code',
+ display = 'display',
+ ...props
+ }) => {
+ const { name, value, onBlur } = props.field
+ const { touched, errors, setFieldValue } = props.form
+
+ const [popperNode, setPopperNode] = useState(null)
+
+ return (
+ {
+ if (itemToString) return itemToString(it)
+ if (it) return it[display]
+ return undefined
+ }}
+ onChange={it => setFieldValue(name, it)}
+ defaultHighlightedIndex={0}
+ selectedItem={value}>
+ {({
+ getInputProps,
+ getItemProps,
+ getMenuProps,
+ isOpen,
+ inputValue: inputValue2,
+ selectedItem: selectedItem2,
+ highlightedIndex,
+ inputValue,
+ toggleMenu,
+ clearSelection
+ }) => (
+
+ {renderInput({
+ name,
+ fullWidth: true,
+ error:
+ (touched[`${name}-input`] || touched[name]) && errors[name],
+ success:
+ (touched[`${name}-input`] || touched[name] || value) &&
+ !errors[name],
+ InputProps: getInputProps({
+ value: inputValue2 || '',
+ placeholder,
+ onBlur,
+ onClick: event => {
+ setPopperNode(event.currentTarget.parentElement)
+ toggleMenu()
+ },
+ onChange: it => {
+ if (it.target.value === '') {
+ clearSelection()
+ }
+ inputValue = it.target.value
+ }
+ }),
+ label
+ })}
+
+
+
+ {filterSuggestions(
+ suggestions,
+ inputValue2,
+ value ? R.of(value) : [],
+ code,
+ display
+ ).map((suggestion, index) =>
+ renderSuggestion({
+ suggestion,
+ index,
+ itemProps: getItemProps({ item: suggestion }),
+ highlightedIndex,
+ selectedItem: selectedItem2,
+ code,
+ display
+ })
+ )}
+
+
+
+
+ )}
+
+ )
+ }
+)
+
+export default withStyles(styles)(Autocomplete)
diff --git a/new-lamassu-admin/src/components/inputs/autocomplete/AutocompleteSelect.js b/new-lamassu-admin/src/components/inputs/autocomplete/AutocompleteSelect.js
new file mode 100644
index 00000000..e67c78f5
--- /dev/null
+++ b/new-lamassu-admin/src/components/inputs/autocomplete/AutocompleteSelect.js
@@ -0,0 +1,121 @@
+import Paper from '@material-ui/core/Paper'
+import Popper from '@material-ui/core/Popper'
+import { withStyles } from '@material-ui/core/styles'
+import classnames from 'classnames'
+import Downshift from 'downshift'
+import * as R from 'ramda'
+import React, { memo, useState } from 'react'
+
+import {
+ renderInput,
+ renderSuggestion,
+ filterSuggestions,
+ styles
+} from './commons'
+
+const AutocompleteSelect = memo(
+ ({
+ suggestions,
+ classes,
+ placeholder,
+ label,
+ itemToString,
+ code = 'code',
+ display = 'display',
+ name,
+ value,
+ touched,
+ error,
+ handleChange,
+ className,
+ ...props
+ }) => {
+ const [popperNode, setPopperNode] = useState(null)
+
+ return (
+ {
+ if (itemToString) return itemToString(it)
+ if (it) return it[display]
+ return undefined
+ }}
+ onChange={handleChange}
+ defaultHighlightedIndex={0}
+ selectedItem={value}>
+ {({
+ getInputProps,
+ getItemProps,
+ getMenuProps,
+ isOpen,
+ inputValue: inputValue2,
+ selectedItem: selectedItem2,
+ highlightedIndex,
+ inputValue,
+ toggleMenu,
+ clearSelection
+ }) => (
+
+ {renderInput({
+ name,
+ fullWidth: true,
+ error: touched && error,
+ success: touched && !error,
+ InputProps: getInputProps({
+ value: inputValue2 || '',
+ placeholder,
+ onClick: event => {
+ setPopperNode(event.currentTarget.parentElement)
+ toggleMenu()
+ },
+ onChange: it => {
+ if (it.target.value === '') {
+ clearSelection()
+ }
+ inputValue = it.target.value
+ }
+ }),
+ label
+ })}
+
+
+
+ {filterSuggestions(
+ suggestions,
+ inputValue2,
+ value ? R.of(value) : [],
+ code,
+ display
+ ).map((suggestion, index) =>
+ renderSuggestion({
+ suggestion,
+ index,
+ itemProps: getItemProps({ item: suggestion }),
+ highlightedIndex,
+ selectedItem: selectedItem2,
+ code,
+ display
+ })
+ )}
+
+
+
+
+ )}
+
+ )
+ }
+)
+
+export default withStyles(styles)(AutocompleteSelect)
diff --git a/new-lamassu-admin/src/components/inputs/autocomplete/commons.js b/new-lamassu-admin/src/components/inputs/autocomplete/commons.js
new file mode 100644
index 00000000..7f0353c7
--- /dev/null
+++ b/new-lamassu-admin/src/components/inputs/autocomplete/commons.js
@@ -0,0 +1,132 @@
+import MenuItem from '@material-ui/core/MenuItem'
+import { withStyles } from '@material-ui/core/styles'
+import Fuse from 'fuse.js'
+import React from 'react'
+import slugify from 'slugify'
+
+import {
+ fontColor,
+ inputFontSize,
+ inputFontWeight,
+ zircon
+} from 'src/styling/variables'
+import S from 'src/utils/sanctuary'
+
+import { TextInput } from '../base'
+
+function renderInput({ InputProps, error, name, success, ...props }) {
+ const { onChange, onBlur, value } = InputProps
+
+ return (
+
+ )
+}
+
+function renderSuggestion({
+ suggestion,
+ index,
+ itemProps,
+ highlightedIndex,
+ selectedItem,
+ code,
+ display
+}) {
+ const isHighlighted = highlightedIndex === index
+
+ const StyledMenuItem = withStyles(theme => ({
+ root: {
+ fontSize: 14,
+ fontWeight: 400,
+ color: fontColor
+ },
+ selected: {
+ '&.Mui-selected, &.Mui-selected:hover': {
+ fontWeight: 500,
+ backgroundColor: zircon
+ }
+ }
+ }))(MenuItem)
+
+ return (
+
+ {suggestion[display]}
+
+ )
+}
+
+function filterSuggestions(
+ suggestions = [],
+ value = '',
+ currentValues = [],
+ code,
+ display
+) {
+ const options = {
+ shouldSort: true,
+ threshold: 0.2,
+ location: 0,
+ distance: 100,
+ maxPatternLength: 32,
+ minMatchCharLength: 1,
+ keys: [code, display]
+ }
+
+ const fuse = new Fuse(suggestions, options)
+ const result = value ? fuse.search(slugify(value, ' ')) : suggestions
+
+ const currentCodes = S.map(S.prop(code))(currentValues)
+ const filtered = S.filter(it => !S.elem(it[code])(currentCodes))(result)
+
+ const amountToTake = S.min(filtered.length)(5)
+
+ return S.compose(S.fromMaybe([]))(S.take(amountToTake))(filtered)
+}
+
+const styles = theme => ({
+ root: {
+ flexGrow: 1,
+ height: 250
+ },
+ container: {
+ flexGrow: 1,
+ position: 'relative'
+ },
+ paper: {
+ // position: 'absolute',
+ zIndex: 1,
+ marginTop: theme.spacing(1),
+ left: 0,
+ right: 0
+ },
+ inputRoot: {
+ fontSize: inputFontSize,
+ color: fontColor,
+ fontWeight: inputFontWeight,
+ flexWrap: 'wrap'
+ },
+ inputInput: {
+ flex: 1
+ },
+ success: {
+ '&:after': {
+ transform: 'scaleX(1)'
+ }
+ },
+ divider: {
+ height: theme.spacing(2)
+ }
+})
+
+export { renderInput, renderSuggestion, filterSuggestions, styles }
diff --git a/new-lamassu-admin/src/components/inputs/base/RadioGroup.js b/new-lamassu-admin/src/components/inputs/base/RadioGroup.js
index a6953dd9..f1635634 100644
--- a/new-lamassu-admin/src/components/inputs/base/RadioGroup.js
+++ b/new-lamassu-admin/src/components/inputs/base/RadioGroup.js
@@ -15,6 +15,7 @@ const { p } = typographyStyles
const GreenRadio = withStyles({
root: {
color: secondaryColor,
+ padding: [[9, 8, 9, 9]],
'&$checked': {
color: secondaryColor
}
diff --git a/new-lamassu-admin/src/components/inputs/formik/SecretInput.js b/new-lamassu-admin/src/components/inputs/formik/SecretInput.js
index 214da002..9fac6534 100644
--- a/new-lamassu-admin/src/components/inputs/formik/SecretInput.js
+++ b/new-lamassu-admin/src/components/inputs/formik/SecretInput.js
@@ -1,13 +1,13 @@
-import React, { memo, useState } from 'react'
-import classnames from 'classnames'
import { makeStyles } from '@material-ui/core'
+import classnames from 'classnames'
+import React, { memo, useState } from 'react'
import TextInputFormik from './TextInput'
import { styles } from './TextInput.styles'
const useStyles = makeStyles(styles)
-const SecretInputFormik = memo(({ ...props }) => {
+const SecretInputFormik = memo(({ className, ...props }) => {
const { value } = props.field
const classes = useStyles()
@@ -37,7 +37,7 @@ const SecretInputFormik = memo(({ ...props }) => {
>
)
diff --git a/new-lamassu-admin/src/components/inputs/index.js b/new-lamassu-admin/src/components/inputs/index.js
index 3bb66f0e..44389d60 100644
--- a/new-lamassu-admin/src/components/inputs/index.js
+++ b/new-lamassu-admin/src/components/inputs/index.js
@@ -1,3 +1,4 @@
+import AutocompleteSelect from './autocomplete/AutocompleteSelect'
import Checkbox from './base/Checkbox'
import Radio from './base/Radio'
import RadioGroup from './base/RadioGroup'
@@ -5,4 +6,12 @@ import Select from './base/Select'
import Switch from './base/Switch'
import TextInput from './base/TextInput'
-export { TextInput, Radio, Checkbox, Switch, Select, RadioGroup }
+export {
+ AutocompleteSelect,
+ TextInput,
+ Radio,
+ Checkbox,
+ Switch,
+ Select,
+ RadioGroup
+}
diff --git a/new-lamassu-admin/src/components/layout/TitleSection.styles.js b/new-lamassu-admin/src/components/layout/TitleSection.styles.js
index d36ac905..db9958b5 100644
--- a/new-lamassu-admin/src/components/layout/TitleSection.styles.js
+++ b/new-lamassu-admin/src/components/layout/TitleSection.styles.js
@@ -7,5 +7,11 @@ export default {
},
titleAndButtonsContainer: {
display: 'flex'
+ },
+ iconButton: {
+ border: 'none',
+ outline: 0,
+ backgroundColor: 'transparent',
+ cursor: 'pointer'
}
}
diff --git a/new-lamassu-admin/src/pages/Services/Bitgo.js b/new-lamassu-admin/src/pages/Services/Bitgo.js
index db92be4a..133d5705 100644
--- a/new-lamassu-admin/src/pages/Services/Bitgo.js
+++ b/new-lamassu-admin/src/pages/Services/Bitgo.js
@@ -81,10 +81,9 @@ const BitgoCard = memo(({ account, onEdit, ...props }) => {
)
})
-const BitgoForm = ({ account, handleSubmit, ...props }) => {
+const getBitgoFormik = account => {
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)
@@ -98,7 +97,7 @@ const BitgoForm = ({ account, handleSubmit, ...props }) => {
const dashWalletPassphrase = getValue(schema.dashWalletPassphrase.code)
const environment = getValue(schema.environment.code)
- const formik = {
+ return {
initialValues: {
token: token,
BTCWalletId: btcWalletId,
@@ -157,82 +156,90 @@ const BitgoForm = ({ account, handleSubmit, ...props }) => {
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
- }
- ]
+const getBitgoFields = () => [
+ {
+ 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
+ }
+]
+
+const BitgoForm = ({ account, handleSubmit, ...props }) => {
+ const { code } = account
+
+ const formik = getBitgoFormik(account)
+
+ const fields = getBitgoFields()
return (
<>
@@ -247,4 +254,4 @@ const BitgoForm = ({ account, handleSubmit, ...props }) => {
)
}
-export { BitgoForm, BitgoCard }
+export { BitgoForm, BitgoCard, getBitgoFormik, getBitgoFields }
diff --git a/new-lamassu-admin/src/pages/Services/Bitstamp.js b/new-lamassu-admin/src/pages/Services/Bitstamp.js
index b50e8023..8216f632 100644
--- a/new-lamassu-admin/src/pages/Services/Bitstamp.js
+++ b/new-lamassu-admin/src/pages/Services/Bitstamp.js
@@ -52,15 +52,14 @@ const BitstampCard = memo(({ account, onEdit, ...props }) => {
)
})
-const BitstampForm = ({ account, ...props }) => {
+const getBitstampFormik = account => {
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 = {
+ return {
initialValues: {
clientId: clientId,
key: key,
@@ -78,27 +77,35 @@ const BitstampForm = ({ account, ...props }) => {
.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
- }
- ]
+const getBitstampFields = () => [
+ {
+ 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
+ }
+]
+
+const BitstampForm = ({ account, ...props }) => {
+ const { code } = account
+
+ const formik = getBitstampFormik(account)
+
+ const fields = getBitstampFields()
return (
<>
@@ -113,4 +120,4 @@ const BitstampForm = ({ account, ...props }) => {
)
}
-export { BitstampForm, BitstampCard }
+export { BitstampForm, BitstampCard, getBitstampFormik, getBitstampFields }
diff --git a/new-lamassu-admin/src/pages/Services/Blockcypher.js b/new-lamassu-admin/src/pages/Services/Blockcypher.js
index e64b004f..cb3ad18d 100644
--- a/new-lamassu-admin/src/pages/Services/Blockcypher.js
+++ b/new-lamassu-admin/src/pages/Services/Blockcypher.js
@@ -47,14 +47,13 @@ const BlockcypherCard = memo(({ account, onEdit, ...props }) => {
)
})
-const BlockcypherForm = ({ account, ...props }) => {
+const getBlockcypherFormik = account => {
const getValue = getValueAux(account)
- const { code } = account
const token = getValue(schema.token.code)
const confidenceFactor = getValue(schema.confidenceFactor.code)
- const formik = {
+ return {
initialValues: {
token: token,
confidenceFactor: confidenceFactor
@@ -69,21 +68,29 @@ const BlockcypherForm = ({ account, ...props }) => {
.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
- }
- ]
+const getBlockcypherFields = () => [
+ {
+ name: schema.token.code,
+ label: schema.token.display,
+ type: 'text',
+ component: TextInputFormik
+ },
+ {
+ name: schema.confidenceFactor.code,
+ label: schema.confidenceFactor.display,
+ type: 'text',
+ component: TextInputFormik
+ }
+]
+
+const BlockcypherForm = ({ account, ...props }) => {
+ const { code } = account
+
+ const formik = getBlockcypherFormik(account)
+
+ const fields = getBlockcypherFields()
return (
<>
@@ -98,4 +105,9 @@ const BlockcypherForm = ({ account, ...props }) => {
)
}
-export { BlockcypherForm, BlockcypherCard }
+export {
+ BlockcypherForm,
+ BlockcypherCard,
+ getBlockcypherFormik,
+ getBlockcypherFields
+}
diff --git a/new-lamassu-admin/src/pages/Services/Infura.js b/new-lamassu-admin/src/pages/Services/Infura.js
index 76987c2f..47e3aa9b 100644
--- a/new-lamassu-admin/src/pages/Services/Infura.js
+++ b/new-lamassu-admin/src/pages/Services/Infura.js
@@ -52,15 +52,14 @@ const InfuraCard = memo(({ account, onEdit, ...props }) => {
)
})
-const InfuraForm = ({ account, ...props }) => {
+const getInfuraFormik = account => {
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 = {
+ return {
initialValues: {
apiKey: apiKey,
apiSecret: apiSecret,
@@ -79,8 +78,10 @@ const InfuraForm = ({ account, ...props }) => {
.required('Required')
})
}
+}
- const fields = [
+const getInfuraFields = () => {
+ return [
{
name: schema.apiKey.code,
label: schema.apiKey.display,
@@ -100,6 +101,14 @@ const InfuraForm = ({ account, ...props }) => {
component: TextInputFormik
}
]
+}
+
+const InfuraForm = ({ account, ...props }) => {
+ const { code } = account
+
+ const formik = getInfuraFormik(account)
+
+ const fields = getInfuraFields()
return (
<>
@@ -114,4 +123,4 @@ const InfuraForm = ({ account, ...props }) => {
)
}
-export { InfuraCard, InfuraForm }
+export { InfuraCard, InfuraForm, getInfuraFormik, getInfuraFields }
diff --git a/new-lamassu-admin/src/pages/Services/Itbit.js b/new-lamassu-admin/src/pages/Services/Itbit.js
index 7b7dee6c..23371a4e 100644
--- a/new-lamassu-admin/src/pages/Services/Itbit.js
+++ b/new-lamassu-admin/src/pages/Services/Itbit.js
@@ -51,16 +51,15 @@ const ItbitCard = memo(({ account, onEdit, ...props }) => {
)
})
-const ItbitForm = ({ account, ...props }) => {
+const getItbitFormik = account => {
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 = {
+ return {
initialValues: {
userId: userId,
walletId: walletId,
@@ -82,33 +81,41 @@ const ItbitForm = ({ account, ...props }) => {
.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
- }
- ]
+const getItbitFields = () => [
+ {
+ 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
+ }
+]
+
+const ItbitForm = ({ account, ...props }) => {
+ const { code } = account
+
+ const formik = getItbitFormik(account)
+
+ const fields = getItbitFields()
return (
<>
@@ -123,4 +130,4 @@ const ItbitForm = ({ account, ...props }) => {
)
}
-export { ItbitCard, ItbitForm }
+export { ItbitCard, ItbitForm, getItbitFormik, getItbitFields }
diff --git a/new-lamassu-admin/src/pages/Services/Kraken.js b/new-lamassu-admin/src/pages/Services/Kraken.js
index 7b1fa2dc..7587d849 100644
--- a/new-lamassu-admin/src/pages/Services/Kraken.js
+++ b/new-lamassu-admin/src/pages/Services/Kraken.js
@@ -48,14 +48,13 @@ const KrakenCard = memo(({ account, onEdit, ...props }) => {
)
})
-const KrakenForm = ({ account, ...props }) => {
+const getKrakenFormik = account => {
const getValue = getValueAux(account)
- const { code } = account
const apiKey = getValue(schema.apiKey.code)
const privateKey = getValue(schema.privateKey.code)
- const formik = {
+ return {
initialValues: {
apiKey: apiKey,
privateKey: privateKey
@@ -69,21 +68,29 @@ const KrakenForm = ({ account, ...props }) => {
.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
- }
- ]
+const getKrakenFields = () => [
+ {
+ name: schema.apiKey.code,
+ label: schema.apiKey.display,
+ type: 'text',
+ component: TextInputFormik
+ },
+ {
+ name: schema.privateKey.code,
+ label: schema.privateKey.display,
+ type: 'text',
+ component: SecretInputFormik
+ }
+]
+
+const KrakenForm = ({ account, ...props }) => {
+ const { code } = account
+
+ const formik = getKrakenFormik(account)
+
+ const fields = getKrakenFields()
return (
<>
@@ -98,4 +105,4 @@ const KrakenForm = ({ account, ...props }) => {
)
}
-export { KrakenCard, KrakenForm }
+export { KrakenCard, KrakenForm, getKrakenFormik, getKrakenFields }
diff --git a/new-lamassu-admin/src/pages/Services/Mailgun.js b/new-lamassu-admin/src/pages/Services/Mailgun.js
index d822f839..1347e43f 100644
--- a/new-lamassu-admin/src/pages/Services/Mailgun.js
+++ b/new-lamassu-admin/src/pages/Services/Mailgun.js
@@ -55,16 +55,15 @@ const MailgunCard = memo(({ account, onEdit, ...props }) => {
)
})
-const MailgunForm = ({ account, ...props }) => {
+const getMailgunFormik = account => {
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 = {
+ return {
initialValues: {
apiKey: apiKey,
domain: domain,
@@ -88,33 +87,41 @@ const MailgunForm = ({ account, ...props }) => {
.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
- }
- ]
+const getMailgunFields = () => [
+ {
+ 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
+ }
+]
+
+const MailgunForm = ({ account, ...props }) => {
+ const { code } = account
+
+ const formik = getMailgunFormik(account)
+
+ const fields = getMailgunFields()
return (
<>
@@ -129,4 +136,4 @@ const MailgunForm = ({ account, ...props }) => {
)
}
-export { MailgunCard, MailgunForm }
+export { MailgunCard, MailgunForm, getMailgunFormik, getMailgunFields }
diff --git a/new-lamassu-admin/src/pages/Services/Strike.js b/new-lamassu-admin/src/pages/Services/Strike.js
index d8979cec..e8eef813 100644
--- a/new-lamassu-admin/src/pages/Services/Strike.js
+++ b/new-lamassu-admin/src/pages/Services/Strike.js
@@ -37,13 +37,12 @@ const StrikeCard = memo(({ account, onEdit, ...props }) => {
)
})
-const StrikeForm = ({ account, ...props }) => {
+const getStrikeFormik = account => {
const getValue = getValueAux(account)
- const code = 'strike'
const token = getValue(schema.token.code)
- const formik = {
+ return {
initialValues: {
token: token
},
@@ -53,15 +52,23 @@ const StrikeForm = ({ account, ...props }) => {
.required('Required')
})
}
+}
- const fields = [
- {
- name: schema.token.code,
- label: schema.token.display,
- type: 'text',
- component: SecretInputFormik
- }
- ]
+const getStrikeFields = () => [
+ {
+ name: schema.token.code,
+ label: schema.token.display,
+ type: 'text',
+ component: SecretInputFormik
+ }
+]
+
+const StrikeForm = ({ account, ...props }) => {
+ const code = 'strike'
+
+ const formik = getStrikeFormik(account)
+
+ const fields = getStrikeFields()
return (
<>
@@ -76,4 +83,4 @@ const StrikeForm = ({ account, ...props }) => {
)
}
-export { StrikeCard, StrikeForm }
+export { StrikeCard, StrikeForm, getStrikeFormik, getStrikeFields }
diff --git a/new-lamassu-admin/src/pages/Services/Twilio.js b/new-lamassu-admin/src/pages/Services/Twilio.js
index 16e16616..15a6c5ae 100644
--- a/new-lamassu-admin/src/pages/Services/Twilio.js
+++ b/new-lamassu-admin/src/pages/Services/Twilio.js
@@ -56,16 +56,15 @@ const TwilioCard = memo(({ account, onEdit, ...props }) => {
)
})
-const TwilioForm = ({ account, ...props }) => {
+const getTwilioFormik = account => {
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 = {
+ return {
initialValues: {
accountSid: accountSid,
authToken: authToken,
@@ -87,33 +86,41 @@ const TwilioForm = ({ account, ...props }) => {
.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
- }
- ]
+const getTwilioFields = () => [
+ {
+ 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
+ }
+]
+
+const TwilioForm = ({ account, ...props }) => {
+ const { code } = account
+
+ const formik = getTwilioFormik(account)
+
+ const fields = getTwilioFields()
return (
<>
@@ -128,4 +135,4 @@ const TwilioForm = ({ account, ...props }) => {
)
}
-export { TwilioCard, TwilioForm }
+export { TwilioCard, TwilioForm, getTwilioFormik, getTwilioFields }
diff --git a/new-lamassu-admin/src/pages/Services/aux.js b/new-lamassu-admin/src/pages/Services/aux.js
index 032534a9..bdb03ddf 100644
--- a/new-lamassu-admin/src/pages/Services/aux.js
+++ b/new-lamassu-admin/src/pages/Services/aux.js
@@ -1,10 +1,10 @@
-import React from 'react'
-import * as R from 'ramda'
import { makeStyles } from '@material-ui/core'
+import * as R from 'ramda'
+import React from 'react'
import SingleRowTable from 'src/components/single-row-table/SingleRowTable'
-const getValue = R.curry((account, code) => account[code] ?? '')
+const getValue = R.curry((account, code) => (account ? account[code] : ''))
const formatLong = value => {
if (!value) return ''
diff --git a/new-lamassu-admin/src/pages/Wallet/WalletSettings.js b/new-lamassu-admin/src/pages/Wallet/WalletSettings.js
new file mode 100644
index 00000000..4baed6e4
--- /dev/null
+++ b/new-lamassu-admin/src/pages/Wallet/WalletSettings.js
@@ -0,0 +1,451 @@
+import { useQuery, useMutation } from '@apollo/react-hooks'
+import { makeStyles } from '@material-ui/core'
+import { gql } from 'apollo-boost'
+import * as R from 'ramda'
+import React, { useState } from 'react'
+
+import ErrorMessage from 'src/components/ErrorMessage'
+import Modal from 'src/components/Modal'
+import Title from 'src/components/Title'
+import {
+ Table,
+ THead,
+ Th,
+ TBody,
+ Tr,
+ Td
+} from 'src/components/fake-table/Table'
+import { Switch } from 'src/components/inputs'
+import { ReactComponent as DisabledEditIcon } from 'src/styling/icons/action/edit/disabled.svg'
+import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
+import { zircon } from 'src/styling/variables'
+
+import Wizard from './Wizard'
+import WizardSplash from './WizardSplash'
+import {
+ CRYPTOCURRENCY_KEY,
+ TICKER_KEY,
+ WALLET_KEY,
+ EXCHANGE_KEY,
+ ZERO_CONF_KEY,
+ EDIT_KEY,
+ ENABLE_KEY,
+ SIZE_KEY,
+ TEXT_ALIGN_KEY
+} from './aux.js'
+
+const styles = {
+ disabledDrawing: {
+ position: 'relative',
+ display: 'flex',
+ alignItems: 'center',
+ '& > div': {
+ position: 'absolute',
+ backgroundColor: zircon,
+ height: 36,
+ width: 678
+ }
+ },
+ modal: {
+ width: 544
+ },
+ switchErrorMessage: {
+ margin: [['auto', 0, 'auto', 20]]
+ }
+}
+
+const useStyles = makeStyles(styles)
+
+const columns = {
+ [CRYPTOCURRENCY_KEY]: {
+ [SIZE_KEY]: 182,
+ [TEXT_ALIGN_KEY]: 'left'
+ },
+ [TICKER_KEY]: {
+ [SIZE_KEY]: 182,
+ [TEXT_ALIGN_KEY]: 'left'
+ },
+ [WALLET_KEY]: {
+ [SIZE_KEY]: 182,
+ [TEXT_ALIGN_KEY]: 'left'
+ },
+ [EXCHANGE_KEY]: {
+ [SIZE_KEY]: 182,
+ [TEXT_ALIGN_KEY]: 'left'
+ },
+ [ZERO_CONF_KEY]: {
+ [SIZE_KEY]: 229,
+ [TEXT_ALIGN_KEY]: 'left'
+ },
+ [EDIT_KEY]: {
+ [SIZE_KEY]: 134,
+ [TEXT_ALIGN_KEY]: 'center'
+ },
+ [ENABLE_KEY]: {
+ [SIZE_KEY]: 109,
+ [TEXT_ALIGN_KEY]: 'center'
+ }
+}
+
+const GET_INFO = gql`
+ {
+ config
+ accounts {
+ code
+ display
+ class
+ cryptos
+ }
+ cryptoCurrencies {
+ code
+ display
+ }
+ }
+`
+
+const SAVE_CONFIG = gql`
+ mutation Save($config: JSONObject) {
+ saveConfig(config: $config)
+ }
+`
+
+const schema = {
+ [TICKER_KEY]: '',
+ [WALLET_KEY]: '',
+ [EXCHANGE_KEY]: '',
+ [ZERO_CONF_KEY]: '',
+ [ENABLE_KEY]: false
+}
+
+const WalletSettings = () => {
+ const [cryptoCurrencies, setCryptoCurrencies] = useState(null)
+ const [accounts, setAccounts] = useState(null)
+ const [services, setServices] = useState(null)
+ const [state, setState] = useState(null)
+ const [modalContent, setModalContent] = useState(null)
+ const [modalOpen, setModalOpen] = useState(false)
+ const [error, setError] = useState(null)
+ const [saveConfig] = useMutation(SAVE_CONFIG, {
+ onCompleted: data => {
+ setServices(data.saveConfig.accounts)
+ setError(null)
+ }
+ })
+
+ useQuery(GET_INFO, {
+ onCompleted: data => {
+ const { cryptoCurrencies, config, accounts } = data
+
+ const wallet = config?.wallet ?? []
+ const services = config?.accounts ?? {}
+
+ const newState = R.map(crypto => {
+ const el = R.find(R.propEq(CRYPTOCURRENCY_KEY, crypto.code))(wallet)
+ if (!el) return R.assoc(CRYPTOCURRENCY_KEY, crypto.code)(schema)
+ return el
+ })(cryptoCurrencies)
+
+ setState(newState)
+ setCryptoCurrencies(cryptoCurrencies)
+ setAccounts(accounts)
+ setServices(services)
+ },
+ onError: error => console.error(error)
+ })
+
+ const classes = useStyles()
+
+ const getSize = key => columns[key][SIZE_KEY]
+ const getTextAlign = key => columns[key][TEXT_ALIGN_KEY]
+
+ const getDisplayName = list => code =>
+ R.path(['display'], R.find(R.propEq('code', code), list))
+
+ const getCryptoDisplayName = row =>
+ getDisplayName(cryptoCurrencies)(row[CRYPTOCURRENCY_KEY])
+
+ const getNoSetUpNeeded = accounts => {
+ const needs = [
+ 'bitgo',
+ 'bitstamp',
+ 'blockcypher',
+ 'infura',
+ 'kraken',
+ 'strike'
+ ]
+ return R.filter(account => !R.includes(account.code, needs), accounts)
+ }
+ const getAlreadySetUp = serviceClass => cryptocode => {
+ const possible = R.filter(
+ service => R.includes(cryptocode, service.cryptos),
+ R.filter(R.propEq('class', serviceClass), accounts)
+ )
+ const isSetUp = service => R.includes(service.code, R.keys(services))
+ const alreadySetUp = R.filter(isSetUp, possible)
+ const join = [...alreadySetUp, ...getNoSetUpNeeded(possible)]
+ return R.isEmpty(join) ? null : join
+ }
+ const getNotSetUp = serviceClass => cryptocode => {
+ const possible = R.filter(
+ service => R.includes(cryptocode, service.cryptos),
+ R.filter(R.propEq('class', serviceClass), accounts)
+ )
+ const without = R.without(
+ getAlreadySetUp(serviceClass)(cryptocode) ?? [],
+ possible
+ )
+ return R.isEmpty(without) ? null : without
+ }
+
+ const saveNewService = (code, it) => {
+ const newAccounts = R.clone(services)
+ newAccounts[code] = it
+ return saveConfig({ variables: { config: { accounts: newAccounts } } })
+ }
+ const save = it => {
+ const idx = R.findIndex(
+ R.propEq(CRYPTOCURRENCY_KEY, it[CRYPTOCURRENCY_KEY]),
+ state
+ )
+ const merged = R.mergeDeepRight(state[idx], it)
+ const updated = R.update(idx, merged, state)
+ return saveConfig({
+ variables: { config: { wallet: updated } }
+ })
+ }
+
+ const isSet = crypto =>
+ crypto[TICKER_KEY] &&
+ crypto[WALLET_KEY] &&
+ crypto[EXCHANGE_KEY] &&
+ crypto[ZERO_CONF_KEY]
+
+ const handleEnable = row => event => {
+ if (!isSet(row)) {
+ setModalContent(
+
+ )
+ setModalOpen(true)
+ setError(null)
+ return
+ }
+
+ save(R.assoc(ENABLE_KEY, event.target.checked, row)).catch(error =>
+ setError(error)
+ )
+ }
+
+ const handleEditClick = row => {
+ setModalOpen(true)
+ handleModalNavigation(row)(1)
+ }
+
+ const handleModalClose = () => {
+ setModalOpen(false)
+ setModalContent(null)
+ }
+ const handleModalNavigation = row => currentPage => {
+ const cryptocode = row[CRYPTOCURRENCY_KEY]
+
+ switch (currentPage) {
+ case 1:
+ setModalContent(
+ R.includes(cryptocode, ticker.cryptos),
+ R.filter(R.propEq('class', 'ticker'), accounts)
+ )}
+ />
+ )
+ break
+ case 2:
+ setModalContent(
+
+ )
+ break
+ case 3:
+ setModalContent(
+
+ )
+ break
+ case 4:
+ setModalContent(
+
+ )
+ break
+ case 5:
+ // Zero Conf
+ return save(R.assoc(ENABLE_KEY, true, row)).then(m => {
+ setModalOpen(false)
+ setModalContent(null)
+ })
+ default:
+ break
+ }
+
+ return new Promise(() => {})
+ }
+
+ if (!state) return null
+
+ return (
+ <>
+
+
+
Wallet Settings
+ {error && !modalOpen && (
+
+ Failed to save
+
+ )}
+
+
+
+
+
+
+ Cryptocurrency
+
+
+ Ticker
+
+
+ Wallet
+
+
+ Exchange
+
+
+ Zero Conf
+
+
+ Edit
+
+
+ Enable
+
+
+
+ {state.map((row, idx) => (
+
+
+ {getCryptoDisplayName(row)}
+
+ {!isSet(row) && (
+
+
+
+ )}
+ {isSet(row) && (
+ <>
+
+ {getDisplayName(accounts)(row[TICKER_KEY])}
+
+
+ {getDisplayName(accounts)(row[WALLET_KEY])}
+
+
+ {getDisplayName(accounts)(row[EXCHANGE_KEY])}
+
+
+ {getDisplayName(accounts)(row[ZERO_CONF_KEY])}
+
+ >
+ )}
+
+ {!isSet(row) && }
+ {isSet(row) && (
+ handleEditClick(row)}>
+
+
+ )}
+
+
+
+
+
+ ))}
+
+
+
+
+ {modalContent}
+
+ >
+ )
+}
+
+export default WalletSettings
diff --git a/new-lamassu-admin/src/pages/Wallet/Wizard.js b/new-lamassu-admin/src/pages/Wallet/Wizard.js
new file mode 100644
index 00000000..109d72bb
--- /dev/null
+++ b/new-lamassu-admin/src/pages/Wallet/Wizard.js
@@ -0,0 +1,307 @@
+import { makeStyles } from '@material-ui/core'
+import classnames from 'classnames'
+import { Formik, Field as FormikField } from 'formik'
+import * as R from 'ramda'
+import React, { useState, useEffect } from 'react'
+
+import ErrorMessage from 'src/components/ErrorMessage'
+import Stage from 'src/components/Stage'
+import { Button } from 'src/components/buttons'
+import { RadioGroup, AutocompleteSelect } from 'src/components/inputs'
+import { H1, Info2, H4 } from 'src/components/typography'
+import { startCase } from 'src/utils/string'
+
+import { getBitgoFields, getBitgoFormik } from '../Services/Bitgo'
+import { getBitstampFields, getBitstampFormik } from '../Services/Bitstamp'
+import {
+ getBlockcypherFields,
+ getBlockcypherFormik
+} from '../Services/Blockcypher'
+import { getInfuraFields, getInfuraFormik } from '../Services/Infura'
+import { getKrakenFields, getKrakenFormik } from '../Services/Kraken'
+import { getStrikeFields, getStrikeFormik } from '../Services/Strike'
+
+const styles = {
+ modalContent: {
+ display: 'flex',
+ flexDirection: 'column',
+ padding: [[24, 32, 0]],
+ '& > h1': {
+ margin: [[0, 0, 10]]
+ },
+ '& > h4': {
+ margin: [[32, 0, 32 - 9, 0]]
+ },
+ '& > p': {
+ margin: 0
+ }
+ },
+ submitButtonWrapper: {
+ display: 'flex',
+ alignSelf: 'flex-end',
+ margin: [['auto', 0, 0]]
+ },
+ submitButton: {
+ width: 67,
+ padding: [[0, 0]],
+ margin: [['auto', 0, 24, 20]],
+ '&:active': {
+ margin: [['auto', 0, 24, 20]]
+ }
+ },
+ stages: {
+ marginTop: 10
+ },
+ radios: {
+ display: 'flex'
+ },
+ radiosAsColumn: {
+ flexDirection: 'column'
+ },
+ radiosAsRow: {
+ flexDirection: 'row'
+ },
+ alreadySetupRadioButtons: {
+ display: 'flex',
+ flexDirection: 'row'
+ },
+ selectNewWrapper: {
+ display: 'flex',
+ alignItems: 'center'
+ },
+ selectNew: {
+ width: 204,
+ flexGrow: 0,
+ bottom: 7
+ },
+ newServiceForm: {
+ display: 'flex',
+ flexDirection: 'column'
+ },
+ newServiceFormFields: {
+ marginTop: 20,
+ marginBottom: 48
+ },
+ field: {
+ '&:not(:last-child)': {
+ marginBottom: 20
+ }
+ },
+ formInput: {
+ '& .MuiInputBase-input': {
+ width: 426
+ }
+ }
+}
+
+const getNewServiceForm = serviceName => {
+ switch (serviceName) {
+ case 'bitgo':
+ return { fields: getBitgoFields(), formik: getBitgoFormik() }
+ case 'bitstamp':
+ return { fields: getBitstampFields(), formik: getBitstampFormik() }
+ case 'blockcypher':
+ return { fields: getBlockcypherFields(), formik: getBlockcypherFormik() }
+ case 'infura':
+ return { fields: getInfuraFields(), formik: getInfuraFormik() }
+ case 'kraken':
+ return { fields: getKrakenFields(), formik: getKrakenFormik() }
+ case 'strike':
+ return { fields: getStrikeFields(), formik: getStrikeFormik() }
+ default:
+ }
+}
+
+const useStyles = makeStyles(styles)
+
+const SubmitButton = ({ error, ...props }) => {
+ const classes = useStyles()
+
+ return (
+
+ {error && Failed to save }
+ Next
+
+ )
+}
+
+const Wizard = ({
+ crypto,
+ coinName,
+ pageName,
+ currentStage,
+ alreadySetUp,
+ notSetUp,
+ handleModalNavigation,
+ saveNewService
+}) => {
+ const [selectedRadio, setSelectedRadio] = useState(
+ crypto[pageName] !== '' ? crypto[pageName] : null
+ )
+ useEffect(() => {
+ setFormContent(null)
+ setSelectedFromDropdown(null)
+ setSetUpNew('')
+ setSelectedRadio(crypto[pageName] !== '' ? crypto[pageName] : null)
+ }, [crypto, pageName])
+ const [setUpNew, setSetUpNew] = useState(null)
+ const [selectedFromDropdown, setSelectedFromDropdown] = useState(null)
+ const [formContent, setFormContent] = useState(null)
+ const [error, setError] = useState(null)
+
+ const classes = useStyles()
+
+ const radiosClassNames = {
+ [classes.radios]: true,
+ [classes.radiosAsColumn]: !selectedFromDropdown,
+ [classes.radiosAsRow]: selectedFromDropdown
+ }
+
+ const radioButtonOptions =
+ alreadySetUp &&
+ R.map(el => {
+ return { label: el.display, value: el.code }
+ })(alreadySetUp)
+
+ const handleRadioButtons = event => {
+ R.o(setSelectedRadio, R.path(['target', 'value']))(event)
+ setSetUpNew('')
+ setFormContent(null)
+ setSelectedFromDropdown(null)
+ setError(null)
+ }
+
+ const handleSetUpNew = event => {
+ R.o(setSetUpNew, R.path(['target', 'value']))(event)
+ setSelectedRadio('')
+ setFormContent(null)
+ setSelectedFromDropdown(null)
+ setError(null)
+ }
+
+ const handleNext = value => event => {
+ const nav = handleModalNavigation(
+ R.mergeDeepRight(crypto, { [pageName]: value })
+ )(currentStage + 1)
+
+ nav.catch(error => setError(error))
+ }
+
+ const handleSelectFromDropdown = it => {
+ setSelectedFromDropdown(it)
+ setFormContent(getNewServiceForm(it?.code))
+ setError(null)
+ }
+
+ const isSubmittable = () => {
+ if (selectedRadio) return true
+ if (!selectedRadio && selectedFromDropdown && !formContent) return true
+ return false
+ }
+
+ console.log(formContent)
+
+ return (
+
+
Enable {coinName}
+
{startCase(pageName)}
+
+
{`Select a ${pageName} or set up a new one`}
+
+ {alreadySetUp && (
+
+ )}
+ {notSetUp && (
+
+
+ {setUpNew && (
+
+ )}
+
+ )}
+
+ {formContent && (
+
+ saveNewService(selectedFromDropdown.code, values)
+ .then(m => {
+ handleNext(selectedFromDropdown.code)()
+ })
+ .catch(error => setError(error))
+ }>
+ {props => (
+
+ )}
+
+ )}
+ {!formContent && (
+
+ )}
+
+ )
+}
+
+export default Wizard
diff --git a/new-lamassu-admin/src/pages/Wallet/WizardSplash.js b/new-lamassu-admin/src/pages/Wallet/WizardSplash.js
new file mode 100644
index 00000000..40bfa2e8
--- /dev/null
+++ b/new-lamassu-admin/src/pages/Wallet/WizardSplash.js
@@ -0,0 +1,85 @@
+import { makeStyles } from '@material-ui/core'
+import React from 'react'
+
+import { Button } from 'src/components/buttons'
+import { H1, P } from 'src/components/typography'
+import { ReactComponent as BitcoinLogo } from 'src/styling/logos/icon-bitcoin-colour.svg'
+import { ReactComponent as BitcoinCashLogo } from 'src/styling/logos/icon-bitcoincash-colour.svg'
+import { ReactComponent as DashLogo } from 'src/styling/logos/icon-dash-colour.svg'
+import { ReactComponent as EthereumLogo } from 'src/styling/logos/icon-ethereum-colour.svg'
+import { ReactComponent as LitecoinLogo } from 'src/styling/logos/icon-litecoin-colour.svg'
+import { ReactComponent as ZCashLogo } from 'src/styling/logos/icon-zcash-colour.svg'
+
+const styles = {
+ logoWrapper: {
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ height: 80,
+ margin: [[40, 0, 24]],
+ '& > svg': {
+ maxHeight: '100%',
+ width: '100%'
+ }
+ },
+ modalContent: {
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ padding: [[0, 66]],
+ '& > h1': {
+ margin: [[0, 0, 32]]
+ },
+ '& > p': {
+ margin: 0
+ },
+ '& > button': {
+ margin: [['auto', 0, 56]],
+ '&:active': {
+ margin: [['auto', 0, 56]]
+ }
+ }
+ }
+}
+
+const useStyles = makeStyles(styles)
+
+const renderLogo = code => {
+ switch (code) {
+ case 'BTC':
+ return
+ case 'BCH':
+ return
+ case 'DASH':
+ return
+ case 'ETH':
+ return
+ case 'LTC':
+ return
+ case 'ZEC':
+ return
+ default:
+ return null
+ }
+}
+
+const WizardSplash = ({ code, coinName, handleModalNavigation }) => {
+ const classes = useStyles()
+
+ return (
+
+
{renderLogo(code)}
+
Enable {coinName}
+
+ You are about to enable {coinName} on your system. This will allow you
+ to use this cryptocurrency on your machines. To able to do that, you’ll
+ have to setup all the necessary 3rd party services.
+
+
handleModalNavigation(1)}>
+ Start configuration
+
+
+ )
+}
+
+export default WizardSplash
diff --git a/new-lamassu-admin/src/pages/Wallet/aux.js b/new-lamassu-admin/src/pages/Wallet/aux.js
new file mode 100644
index 00000000..d8706744
--- /dev/null
+++ b/new-lamassu-admin/src/pages/Wallet/aux.js
@@ -0,0 +1,21 @@
+const CRYPTOCURRENCY_KEY = 'cryptocurrency'
+const TICKER_KEY = 'ticker'
+const WALLET_KEY = 'wallet'
+const EXCHANGE_KEY = 'exchange'
+const ZERO_CONF_KEY = 'zeroConf'
+const EDIT_KEY = 'edit'
+const ENABLE_KEY = 'enabled'
+const SIZE_KEY = 'size'
+const TEXT_ALIGN_KEY = 'textAlign'
+
+export {
+ CRYPTOCURRENCY_KEY,
+ TICKER_KEY,
+ WALLET_KEY,
+ EXCHANGE_KEY,
+ ZERO_CONF_KEY,
+ EDIT_KEY,
+ ENABLE_KEY,
+ SIZE_KEY,
+ TEXT_ALIGN_KEY
+}
diff --git a/new-lamassu-admin/src/routing/routes.js b/new-lamassu-admin/src/routing/routes.js
index 465bba12..3dc6c9e0 100644
--- a/new-lamassu-admin/src/routing/routes.js
+++ b/new-lamassu-admin/src/routing/routes.js
@@ -13,6 +13,7 @@ import OperatorInfo from 'src/pages/OperatorInfo/OperatorInfo'
import ServerLogs from 'src/pages/ServerLogs'
import Services from 'src/pages/Services/Services'
import Transactions from 'src/pages/Transactions/Transactions'
+import WalletSettings from 'src/pages/Wallet/WalletSettings'
import MachineStatus from 'src/pages/maintenance/MachineStatus'
const tree = [
@@ -93,6 +94,12 @@ const tree = [
label: 'Operator Info',
route: '/settings/operator-info',
component: OperatorInfo
+ },
+ {
+ key: 'wallet',
+ label: 'Wallet',
+ route: '/settings/wallet',
+ component: WalletSettings
}
]
},
diff --git a/new-lamassu-admin/src/styling/logos/icon-bitcoin-colour.svg b/new-lamassu-admin/src/styling/logos/icon-bitcoin-colour.svg
new file mode 100644
index 00000000..5b3af8c7
--- /dev/null
+++ b/new-lamassu-admin/src/styling/logos/icon-bitcoin-colour.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/new-lamassu-admin/src/styling/logos/icon-bitcoincash-colour.svg b/new-lamassu-admin/src/styling/logos/icon-bitcoincash-colour.svg
new file mode 100644
index 00000000..abc16f63
--- /dev/null
+++ b/new-lamassu-admin/src/styling/logos/icon-bitcoincash-colour.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/new-lamassu-admin/src/styling/logos/icon-dash-colour.svg b/new-lamassu-admin/src/styling/logos/icon-dash-colour.svg
new file mode 100644
index 00000000..73da05d9
--- /dev/null
+++ b/new-lamassu-admin/src/styling/logos/icon-dash-colour.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/new-lamassu-admin/src/styling/logos/icon-ethereum-colour.svg b/new-lamassu-admin/src/styling/logos/icon-ethereum-colour.svg
new file mode 100644
index 00000000..f095d8c0
--- /dev/null
+++ b/new-lamassu-admin/src/styling/logos/icon-ethereum-colour.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/new-lamassu-admin/src/styling/logos/icon-litecoin-colour.svg b/new-lamassu-admin/src/styling/logos/icon-litecoin-colour.svg
new file mode 100644
index 00000000..0e25829b
--- /dev/null
+++ b/new-lamassu-admin/src/styling/logos/icon-litecoin-colour.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/new-lamassu-admin/src/styling/logos/icon-zcash-colour.svg b/new-lamassu-admin/src/styling/logos/icon-zcash-colour.svg
new file mode 100644
index 00000000..7e7655dc
--- /dev/null
+++ b/new-lamassu-admin/src/styling/logos/icon-zcash-colour.svg
@@ -0,0 +1,15 @@
+
+
+
+
+headerArtboard 7
+
+
+
+