fix: wizard rework
This commit is contained in:
parent
0f4350f99c
commit
3a6f3a2915
40 changed files with 12622 additions and 9244 deletions
|
|
@ -31,6 +31,7 @@ const getCommissions = (cryptoCode, deviceId, config) => {
|
||||||
const commissions = fromNamespace(namespaces.COMMISSIONS)(config)
|
const commissions = fromNamespace(namespaces.COMMISSIONS)(config)
|
||||||
|
|
||||||
const filter = it => it.machine === deviceId && _.includes(cryptoCode)(it.cryptoCurrencies)
|
const filter = it => it.machine === deviceId && _.includes(cryptoCode)(it.cryptoCurrencies)
|
||||||
|
// TODO new-admin. We have a all machines override now
|
||||||
return resolveOverrides(commissions, filter, commissions.overrides)
|
return resolveOverrides(commissions, filter, commissions.overrides)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
import '@storybook/addon-actions/register'
|
|
||||||
import '@storybook/addon-links/register'
|
|
||||||
import '@storybook/addon-knobs/register'
|
|
||||||
import '@storybook/addon-backgrounds/register'
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
import { configure } from '@storybook/react'
|
|
||||||
|
|
||||||
function loadStories () {
|
|
||||||
require('../src/stories')
|
|
||||||
}
|
|
||||||
|
|
||||||
configure(loadStories, module)
|
|
||||||
10
new-lamassu-admin/.storybook/main.js
Normal file
10
new-lamassu-admin/.storybook/main.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
module.exports = {
|
||||||
|
stories: ['../src/stories/index.js'],
|
||||||
|
addons: [
|
||||||
|
'@storybook/addon-actions',
|
||||||
|
'@storybook/addon-links',
|
||||||
|
'@storybook/addon-knobs',
|
||||||
|
'@storybook/addon-backgrounds',
|
||||||
|
'@storybook/preset-create-react-app'
|
||||||
|
]
|
||||||
|
}
|
||||||
19843
new-lamassu-admin/package-lock.json
generated
19843
new-lamassu-admin/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -18,8 +18,7 @@
|
||||||
"classnames": "2.2.6",
|
"classnames": "2.2.6",
|
||||||
"downshift": "3.3.4",
|
"downshift": "3.3.4",
|
||||||
"file-saver": "2.0.2",
|
"file-saver": "2.0.2",
|
||||||
"formik": "2.1.4",
|
"formik": "2.2.0",
|
||||||
"fuse.js": "^3.4.6",
|
|
||||||
"graphql": "^14.5.8",
|
"graphql": "^14.5.8",
|
||||||
"graphql-tag": "^2.10.3",
|
"graphql-tag": "^2.10.3",
|
||||||
"jss-plugin-extend": "^10.0.0",
|
"jss-plugin-extend": "^10.0.0",
|
||||||
|
|
@ -35,17 +34,17 @@
|
||||||
"react-router-dom": "5.1.2",
|
"react-router-dom": "5.1.2",
|
||||||
"react-virtualized": "^9.21.2",
|
"react-virtualized": "^9.21.2",
|
||||||
"sanctuary": "^2.0.1",
|
"sanctuary": "^2.0.1",
|
||||||
"slugify": "^1.3.6",
|
|
||||||
"uuid": "^7.0.2",
|
"uuid": "^7.0.2",
|
||||||
"yup": "0.27.0"
|
"yup": "0.27.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@storybook/addon-actions": "^5.2.3",
|
"@storybook/addon-actions": "6.0.26",
|
||||||
"@storybook/addon-backgrounds": "^5.2.4",
|
"@storybook/addon-backgrounds": "6.0.26",
|
||||||
"@storybook/addon-knobs": "^5.2.3",
|
"@storybook/addon-knobs": "6.0.26",
|
||||||
"@storybook/addon-links": "^5.2.3",
|
"@storybook/addon-links": "6.0.26",
|
||||||
"@storybook/addons": "^5.2.3",
|
"@storybook/addons": "6.0.26",
|
||||||
"@storybook/react": "^5.2.3",
|
"@storybook/preset-create-react-app": "^3.1.4",
|
||||||
|
"@storybook/react": "6.0.26",
|
||||||
"@welldone-software/why-did-you-render": "^3.3.9",
|
"@welldone-software/why-did-you-render": "^3.3.9",
|
||||||
"eslint-config-prettier": "^6.7.0",
|
"eslint-config-prettier": "^6.7.0",
|
||||||
"eslint-config-prettier-standard": "^3.0.1",
|
"eslint-config-prettier-standard": "^3.0.1",
|
||||||
|
|
@ -61,8 +60,8 @@
|
||||||
"lint-staged": "^9.5.0",
|
"lint-staged": "^9.5.0",
|
||||||
"prettier": "1.19.1",
|
"prettier": "1.19.1",
|
||||||
"prettier-config-standard": "^1.0.1",
|
"prettier-config-standard": "^1.0.1",
|
||||||
"react-scripts": "^3.3.0",
|
"react-scripts": "^3.4.3",
|
||||||
"serve": "^11.2.0",
|
"serve": "^11.3.2",
|
||||||
"source-map-explorer": "^2.4.2"
|
"source-map-explorer": "^2.4.2"
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
with import (fetchTarball {
|
with import (fetchTarball {
|
||||||
name = "nixpkgs-19.09";
|
name = "nixpkgs-20.09";
|
||||||
url = https://github.com/NixOS/nixpkgs-channels/archive/d5291756487d70bc336e33512a9baf9fa1788faf.tar.gz;
|
url = https://github.com/NixOS/nixpkgs/archive/0b8799ecaaf0dc6b4c11583a3c96ca5b40fcfdfb.tar.gz;
|
||||||
sha256 = "0mhqhq21y5vrr1f30qd2bvydv4bbbslvyzclhw0kdxmkgg3z4c92";
|
sha256 = "11m4aig6cv0zi3gbq2xn9by29cfvnsxgzf9qsvz67qr0yq29ryyz";
|
||||||
}) {};
|
}) {};
|
||||||
|
|
||||||
stdenv.mkDerivation {
|
stdenv.mkDerivation {
|
||||||
name = "node";
|
name = "node";
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
nodejs-12_x
|
nodejs-14_x
|
||||||
];
|
];
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
export PATH="$PWD/node_modules/.bin/:$PATH"
|
export PATH="$PWD/node_modules/.bin/:$PATH"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { ApolloProvider } from '@apollo/react-hooks'
|
import { ApolloProvider, useQuery } from '@apollo/react-hooks'
|
||||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||||
import {
|
import {
|
||||||
StylesProvider,
|
StylesProvider,
|
||||||
|
|
@ -6,12 +6,14 @@ import {
|
||||||
MuiThemeProvider,
|
MuiThemeProvider,
|
||||||
makeStyles
|
makeStyles
|
||||||
} from '@material-ui/core/styles'
|
} from '@material-ui/core/styles'
|
||||||
import { setAutoFreeze } from 'immer'
|
import gql from 'graphql-tag'
|
||||||
import { create } from 'jss'
|
import { create } from 'jss'
|
||||||
import extendJss from 'jss-plugin-extend'
|
import extendJss from 'jss-plugin-extend'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { BrowserRouter as Router } from 'react-router-dom'
|
import { BrowserRouter as Router } from 'react-router-dom'
|
||||||
|
|
||||||
|
import Wizard from 'src/pages/Wizard'
|
||||||
|
import { getWizardStep } from 'src/pages/Wizard/helper'
|
||||||
import client from 'src/utils/apollo'
|
import client from 'src/utils/apollo'
|
||||||
|
|
||||||
import Header from './components/layout/Header'
|
import Header from './components/layout/Header'
|
||||||
|
|
@ -25,9 +27,6 @@ if (process.env.NODE_ENV !== 'production') {
|
||||||
whyDidYouRender(React)
|
whyDidYouRender(React)
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable immer autofreeze for performance
|
|
||||||
setAutoFreeze(false)
|
|
||||||
|
|
||||||
const jss = create({
|
const jss = create({
|
||||||
plugins: [extendJss(), ...jssPreset().plugins]
|
plugins: [extendJss(), ...jssPreset().plugins]
|
||||||
})
|
})
|
||||||
|
|
@ -54,22 +53,49 @@ const useStyles = makeStyles({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const App = () => {
|
const GET_DATA = gql`
|
||||||
const classes = useStyles()
|
query getData {
|
||||||
|
config
|
||||||
|
accounts
|
||||||
|
cryptoCurrencies {
|
||||||
|
code
|
||||||
|
display
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const Main = () => {
|
||||||
|
const classes = useStyles()
|
||||||
|
const { data, loading } = useQuery(GET_DATA, {
|
||||||
|
notifyOnNetworkStatusChange: true
|
||||||
|
})
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
const wizardStep = getWizardStep(data?.config, data?.cryptoCurrencies)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.root}>
|
||||||
|
<Router>
|
||||||
|
{wizardStep && <Wizard wizardStep={wizardStep} />}
|
||||||
|
<Header tree={tree} />
|
||||||
|
<main className={classes.wrapper}>
|
||||||
|
<Routes />
|
||||||
|
</main>
|
||||||
|
</Router>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
return (
|
return (
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={client}>
|
||||||
<StylesProvider jss={jss}>
|
<StylesProvider jss={jss}>
|
||||||
<MuiThemeProvider theme={theme}>
|
<MuiThemeProvider theme={theme}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<div className={classes.root}>
|
<Main />
|
||||||
<Router>
|
|
||||||
<Header tree={tree} />
|
|
||||||
<main className={classes.wrapper}>
|
|
||||||
<Routes />
|
|
||||||
</main>
|
|
||||||
</Router>
|
|
||||||
</div>
|
|
||||||
</MuiThemeProvider>
|
</MuiThemeProvider>
|
||||||
</StylesProvider>
|
</StylesProvider>
|
||||||
</ApolloProvider>
|
</ApolloProvider>
|
||||||
|
|
|
||||||
26
new-lamassu-admin/src/components/InfoMessage.js
Normal file
26
new-lamassu-admin/src/components/InfoMessage.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { Box, makeStyles } from '@material-ui/core'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import { Label1 } from 'src/components/typography'
|
||||||
|
import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
||||||
|
|
||||||
|
const useStyles = makeStyles({
|
||||||
|
message: ({ width }) => ({
|
||||||
|
width,
|
||||||
|
marginTop: 4,
|
||||||
|
marginLeft: 16
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const InfoMessage = ({ children, width = 330, className }) => {
|
||||||
|
const classes = useStyles({ width })
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box display="flex" className={className}>
|
||||||
|
<WarningIcon />
|
||||||
|
<Label1 className={classes.message}>{children}</Label1>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InfoMessage
|
||||||
|
|
@ -33,6 +33,7 @@ const ActionCol = ({ disabled, editing }) => {
|
||||||
enableToggle,
|
enableToggle,
|
||||||
onToggle,
|
onToggle,
|
||||||
toggleWidth,
|
toggleWidth,
|
||||||
|
forceAdd,
|
||||||
actionColSize
|
actionColSize
|
||||||
} = useContext(TableCtx)
|
} = useContext(TableCtx)
|
||||||
|
|
||||||
|
|
@ -49,9 +50,11 @@ const ActionCol = ({ disabled, editing }) => {
|
||||||
onClick={submitForm}>
|
onClick={submitForm}>
|
||||||
Save
|
Save
|
||||||
</Link>
|
</Link>
|
||||||
<Link color="secondary" onClick={resetForm}>
|
{!forceAdd && (
|
||||||
Cancel
|
<Link color="secondary" onClick={resetForm}>
|
||||||
</Link>
|
Cancel
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
</Td>
|
</Td>
|
||||||
)}
|
)}
|
||||||
{!editing && enableEdit && (
|
{!editing && enableEdit && (
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import { Form, Formik } from 'formik'
|
import { Form, Formik } from 'formik'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import { v4 } from 'uuid'
|
import { v4 } from 'uuid'
|
||||||
|
|
||||||
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
||||||
|
|
@ -51,12 +51,13 @@ const ETable = ({
|
||||||
groupBy,
|
groupBy,
|
||||||
sortBy,
|
sortBy,
|
||||||
createText = 'Add override',
|
createText = 'Add override',
|
||||||
outerEditingId = null
|
forceAdd = false
|
||||||
}) => {
|
}) => {
|
||||||
const [editingId, setEditingId] = useState(null)
|
const [editingId, setEditingId] = useState(null)
|
||||||
const [adding, setAdding] = useState(false)
|
const [adding, setAdding] = useState(false)
|
||||||
const [saving, setSaving] = useState(false)
|
const [saving, setSaving] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => setAdding(forceAdd), [forceAdd])
|
||||||
const innerSave = async value => {
|
const innerSave = async value => {
|
||||||
if (saving) return
|
if (saving) return
|
||||||
|
|
||||||
|
|
@ -88,7 +89,6 @@ const ETable = ({
|
||||||
|
|
||||||
const onReset = () => {
|
const onReset = () => {
|
||||||
setAdding(false)
|
setAdding(false)
|
||||||
setEditingId(outerEditingId)
|
|
||||||
setEditing && setEditing(false)
|
setEditing && setEditing(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,6 +136,7 @@ const ETable = ({
|
||||||
toggleWidth,
|
toggleWidth,
|
||||||
actionColSize,
|
actionColSize,
|
||||||
stripeWhen,
|
stripeWhen,
|
||||||
|
forceAdd,
|
||||||
DEFAULT_COL_SIZE
|
DEFAULT_COL_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,10 @@ import { makeStyles } from '@material-ui/core/styles'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
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 styles from './Sidebar.styles'
|
import styles from './Sidebar.styles'
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
@ -38,3 +42,35 @@ const Sidebar = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Sidebar
|
export default Sidebar
|
||||||
|
|
||||||
|
const Stepper = ({ step, it, idx, steps }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
const active = step === idx
|
||||||
|
const past = idx < step
|
||||||
|
const future = idx > step
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.item}>
|
||||||
|
<span
|
||||||
|
className={classnames({
|
||||||
|
[classes.itemText]: true,
|
||||||
|
[classes.itemTextActive]: active,
|
||||||
|
[classes.itemTextPast]: past
|
||||||
|
})}>
|
||||||
|
{it.label}
|
||||||
|
</span>
|
||||||
|
{active && <CurrentStageIconZodiac />}
|
||||||
|
{past && <CompleteStageIconZodiac />}
|
||||||
|
{future && <EmptyStageIconZodiac />}
|
||||||
|
{idx < steps.length - 1 && (
|
||||||
|
<div
|
||||||
|
className={classnames({
|
||||||
|
[classes.stepperPath]: true,
|
||||||
|
[classes.stepperPast]: past
|
||||||
|
})}></div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Stepper }
|
||||||
|
|
|
||||||
|
|
@ -73,5 +73,33 @@ export default {
|
||||||
'&::after': {
|
'&::after': {
|
||||||
height: '100%'
|
height: '100%'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
item: {
|
||||||
|
position: 'relative',
|
||||||
|
margin: '12px 0 12px 0',
|
||||||
|
display: 'flex'
|
||||||
|
},
|
||||||
|
itemText: {
|
||||||
|
extend: p,
|
||||||
|
color: placeholderColor,
|
||||||
|
marginRight: 24
|
||||||
|
},
|
||||||
|
itemTextActive: {
|
||||||
|
extend: tl2,
|
||||||
|
color: primaryColor
|
||||||
|
},
|
||||||
|
itemTextPast: {
|
||||||
|
color: primaryColor
|
||||||
|
},
|
||||||
|
stepperPath: {
|
||||||
|
position: 'absolute',
|
||||||
|
height: 25,
|
||||||
|
width: 1,
|
||||||
|
border: [[1, 'solid', placeholderColor]],
|
||||||
|
right: 8,
|
||||||
|
top: 18
|
||||||
|
},
|
||||||
|
stepperPast: {
|
||||||
|
border: [[1, 'solid', primaryColor]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { useMutation } from '@apollo/react-hooks'
|
import { useMutation } from '@apollo/react-hooks'
|
||||||
import { Dialog, DialogContent, SvgIcon, IconButton } from '@material-ui/core'
|
import { Dialog, DialogContent, SvgIcon, IconButton } from '@material-ui/core'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import classnames from 'classnames'
|
|
||||||
import { Form, Formik, FastField } from 'formik'
|
import { Form, Formik, FastField } from 'formik'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import QRCode from 'qrcode.react'
|
import QRCode from 'qrcode.react'
|
||||||
|
|
@ -11,12 +10,9 @@ import * as Yup from 'yup'
|
||||||
import Title from 'src/components/Title'
|
import Title from 'src/components/Title'
|
||||||
import { Button } from 'src/components/buttons'
|
import { Button } from 'src/components/buttons'
|
||||||
import { TextInput } from 'src/components/inputs/formik'
|
import { TextInput } from 'src/components/inputs/formik'
|
||||||
import Sidebar from 'src/components/layout/Sidebar'
|
import Sidebar, { Stepper } from 'src/components/layout/Sidebar'
|
||||||
import { Info2, P } from 'src/components/typography'
|
import { Info2, P } from 'src/components/typography'
|
||||||
import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.svg'
|
import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.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 { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
||||||
import { primaryColor } from 'src/styling/variables'
|
import { primaryColor } from 'src/styling/variables'
|
||||||
|
|
||||||
|
|
@ -126,35 +122,6 @@ const steps = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const renderStepper = (step, it, idx, classes) => {
|
|
||||||
const active = step === idx
|
|
||||||
const past = idx < step
|
|
||||||
const future = idx > step
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classes.item}>
|
|
||||||
<span
|
|
||||||
className={classnames({
|
|
||||||
[classes.itemText]: true,
|
|
||||||
[classes.itemTextActive]: active,
|
|
||||||
[classes.itemTextPast]: past
|
|
||||||
})}>
|
|
||||||
{it.label}
|
|
||||||
</span>
|
|
||||||
{active && <CurrentStageIconZodiac />}
|
|
||||||
{past && <CompleteStageIconZodiac />}
|
|
||||||
{future && <EmptyStageIconZodiac />}
|
|
||||||
{idx < steps.length - 1 && (
|
|
||||||
<div
|
|
||||||
className={classnames({
|
|
||||||
[classes.stepperPath]: true,
|
|
||||||
[classes.stepperPast]: past
|
|
||||||
})}></div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const AddMachine = memo(({ close }) => {
|
const AddMachine = memo(({ close }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const [qrCode, setQrCode] = useState(null)
|
const [qrCode, setQrCode] = useState(null)
|
||||||
|
|
@ -179,7 +146,9 @@ const AddMachine = memo(({ close }) => {
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.contentDiv}>
|
<div className={classes.contentDiv}>
|
||||||
<Sidebar>
|
<Sidebar>
|
||||||
{steps.map((it, idx) => renderStepper(step, it, idx, classes))}
|
{steps.map((it, idx) => (
|
||||||
|
<Stepper step={step} it={it} idx={idx} steps={steps} />
|
||||||
|
))}
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
<div className={classes.contentWrapper}>
|
<div className={classes.contentWrapper}>
|
||||||
<Component
|
<Component
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,4 @@
|
||||||
import typographyStyles from 'src/components/typography/styles'
|
import { backgroundColor, mainWidth } from 'src/styling/variables'
|
||||||
import {
|
|
||||||
placeholderColor,
|
|
||||||
backgroundColor,
|
|
||||||
primaryColor,
|
|
||||||
mainWidth
|
|
||||||
} from 'src/styling/variables'
|
|
||||||
|
|
||||||
const { tl2, p } = typographyStyles
|
|
||||||
|
|
||||||
const fill = '100%'
|
const fill = '100%'
|
||||||
const flexDirection = 'column'
|
const flexDirection = 'column'
|
||||||
|
|
@ -65,34 +57,6 @@ const styles = {
|
||||||
},
|
},
|
||||||
qrText: {
|
qrText: {
|
||||||
marginTop: 0
|
marginTop: 0
|
||||||
},
|
|
||||||
item: {
|
|
||||||
position: 'relative',
|
|
||||||
margin: '12px 0 12px 0',
|
|
||||||
display: 'flex'
|
|
||||||
},
|
|
||||||
itemText: {
|
|
||||||
extend: p,
|
|
||||||
color: placeholderColor,
|
|
||||||
marginRight: 24
|
|
||||||
},
|
|
||||||
itemTextActive: {
|
|
||||||
extend: tl2,
|
|
||||||
color: primaryColor
|
|
||||||
},
|
|
||||||
itemTextPast: {
|
|
||||||
color: primaryColor
|
|
||||||
},
|
|
||||||
stepperPath: {
|
|
||||||
position: 'absolute',
|
|
||||||
height: 25,
|
|
||||||
width: 1,
|
|
||||||
border: [[1, 'solid', placeholderColor]],
|
|
||||||
right: 7,
|
|
||||||
top: 15
|
|
||||||
},
|
|
||||||
stepperPast: {
|
|
||||||
border: [[1, 'solid', primaryColor]]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ const SAVE_CONFIG = gql`
|
||||||
|
|
||||||
const FIELDS_WIDTH = 130
|
const FIELDS_WIDTH = 130
|
||||||
|
|
||||||
// TODO: what about 'onlySetup' 'onlyFiat'?
|
|
||||||
const Notifications = ({
|
const Notifications = ({
|
||||||
name: SCREEN_KEY,
|
name: SCREEN_KEY,
|
||||||
displaySetup = true,
|
displaySetup = true,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { makeStyles, Grid } from '@material-ui/core'
|
import { makeStyles, Grid } from '@material-ui/core'
|
||||||
|
import classnames from 'classnames'
|
||||||
import { Formik, Form, FastField } from 'formik'
|
import { Formik, Form, FastField } from 'formik'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
@ -27,6 +28,7 @@ const FormRenderer = ({
|
||||||
value,
|
value,
|
||||||
save,
|
save,
|
||||||
buttonLabel = 'Save changes',
|
buttonLabel = 'Save changes',
|
||||||
|
buttonClass,
|
||||||
xs = 12
|
xs = 12
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
@ -58,7 +60,9 @@ const FormRenderer = ({
|
||||||
</Grid>
|
</Grid>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
<Button className={classes.button} type="submit">
|
<Button
|
||||||
|
className={classnames(classes.button, buttonClass)}
|
||||||
|
type="submit">
|
||||||
{buttonLabel}
|
{buttonLabel}
|
||||||
</Button>
|
</Button>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TextInput,
|
TextInput,
|
||||||
SecretInput,
|
SecretInput,
|
||||||
Autocomplete
|
Autocomplete
|
||||||
} from 'src/components/inputs/formik'
|
} from 'src/components/inputs/formik'
|
||||||
|
|
||||||
import bitgo from './bitgo'
|
|
||||||
|
|
||||||
export default code => ({
|
export default code => ({
|
||||||
code: 'bitgo',
|
code: 'bitgo',
|
||||||
name: 'BitGo',
|
name: 'BitGo',
|
||||||
|
|
@ -28,15 +28,28 @@ export default code => ({
|
||||||
face: true
|
face: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: `${code.toLowerCase()}WalletId`,
|
code: `${code}WalletId`,
|
||||||
display: `${code.toUpperCase()} Wallet ID`,
|
display: `${code} Wallet ID`,
|
||||||
component: TextInput
|
component: TextInput
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: `${code.toLowerCase()}WalletPassphrase`,
|
code: `${code}WalletPassphrase`,
|
||||||
display: `${code.toUpperCase()} Wallet Passphrase`,
|
display: `${code} Wallet Passphrase`,
|
||||||
component: SecretInput
|
component: SecretInput
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
validationSchema: bitgo.validationSchema
|
validationSchema: Yup.object().shape({
|
||||||
|
token: Yup.string()
|
||||||
|
.max(100, 'Too long')
|
||||||
|
.required('Required'),
|
||||||
|
environment: Yup.string()
|
||||||
|
.matches(/(prod|test)/)
|
||||||
|
.required('Required'),
|
||||||
|
[`${code}WalletId`]: Yup.string()
|
||||||
|
.max(100, 'Too long')
|
||||||
|
.required('Required'),
|
||||||
|
[`${code}WalletPassphrase`]: Yup.string()
|
||||||
|
.max(100, 'Too long')
|
||||||
|
.required('Required')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,263 +1,74 @@
|
||||||
import { useQuery } from '@apollo/react-hooks'
|
|
||||||
import { makeStyles, Dialog, DialogContent } from '@material-ui/core'
|
import { makeStyles, Dialog, DialogContent } from '@material-ui/core'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import gql from 'graphql-tag'
|
import React, { useState } from 'react'
|
||||||
import * as R from 'ramda'
|
|
||||||
import React, { useReducer, useEffect } from 'react'
|
|
||||||
import { Switch, Route, useRouteMatch } from 'react-router-dom'
|
|
||||||
|
|
||||||
import Twilio from 'src/pages/Wizard/components/Twilio'
|
|
||||||
import { backgroundColor } from 'src/styling/variables'
|
import { backgroundColor } from 'src/styling/variables'
|
||||||
import { fromNamespace, namespaces } from 'src/utils/config'
|
|
||||||
|
|
||||||
import { schema as CommissionsSchema } from '../Commissions/helper'
|
|
||||||
import { LocaleSchema } from '../Locales/helper'
|
|
||||||
import twilio from '../Services/schemas/twilio'
|
|
||||||
import { WalletSchema } from '../Wallet/helper'
|
|
||||||
|
|
||||||
import Commissions from './components/Commissions'
|
|
||||||
import Footer from './components/Footer'
|
import Footer from './components/Footer'
|
||||||
import Locales from './components/Locales'
|
import { STEPS } from './helper'
|
||||||
import N from './components/Notifications'
|
|
||||||
import WizardOperatorInfo from './components/OperatorInfo'
|
|
||||||
import Wallet from './components/Wallet'
|
|
||||||
import Welcome from './components/Welcome'
|
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
wrapper: {
|
wrapper: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexGrow: 1,
|
padding: [[16, 0]],
|
||||||
padding: '1rem 0',
|
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
justifyContent: 'space-between'
|
justifyContent: 'space-between',
|
||||||
|
backgroundColor: backgroundColor
|
||||||
},
|
},
|
||||||
welcomeBackground: {
|
welcomeBackground: {
|
||||||
background: 'url(/wizard-background.svg) no-repeat center center fixed',
|
background: 'url(/wizard-background.svg) no-repeat center center fixed',
|
||||||
backgroundSize: 'cover',
|
backgroundColor: backgroundColor,
|
||||||
backgroundColor: backgroundColor
|
backgroundSize: 'cover'
|
||||||
|
},
|
||||||
|
blurred: {
|
||||||
|
filter: 'blur(4px)',
|
||||||
|
pointerEvents: 'none'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const GET_GLOBAL_CONFIG = gql`
|
const Wizard = ({ wizardStep }) => {
|
||||||
query getData {
|
const [step, setStep] = useState(0)
|
||||||
config
|
|
||||||
accounts
|
|
||||||
}
|
|
||||||
`
|
|
||||||
const validateSteps = ({ config, accounts }) => {
|
|
||||||
const steps = [
|
|
||||||
{
|
|
||||||
namespace: 'welcome',
|
|
||||||
isComplete: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
namespace: namespaces.WALLETS,
|
|
||||||
tag: 'Wallet settings',
|
|
||||||
p: (
|
|
||||||
<>
|
|
||||||
Your wallet settings are the first step for this wizard. We'll start
|
|
||||||
by setting one of cryptocurrency to get you up and running, but you
|
|
||||||
can later setup as many cryptocurrencies as you want.
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
isComplete: (() => {
|
|
||||||
const wallets = fromNamespace(namespaces.WALLETS)(config)
|
|
||||||
const wizardCoin = Object.keys(wallets).find(
|
|
||||||
k => k.endsWith('_wizard') && wallets[k]
|
|
||||||
)
|
|
||||||
const coinCode = wizardCoin && wizardCoin.replace('_wizard', '')
|
|
||||||
const wallet = coinCode && fromNamespace(coinCode)(wallets)
|
|
||||||
return wallet && WalletSchema.isValidSync(wallet)
|
|
||||||
})()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
namespace: namespaces.LOCALE,
|
|
||||||
tag: 'Locales',
|
|
||||||
p: (
|
|
||||||
<>
|
|
||||||
From the Locales page, you can define some important default settings
|
|
||||||
of your machines. These values will be the default values of all
|
|
||||||
machines you'll later add to your network. Default settings keep you
|
|
||||||
from having to enther the same values everytime you add a new machine.
|
|
||||||
Once a machine is added, you may override some of these values in the
|
|
||||||
overrides section.
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
isComplete: LocaleSchema.isValidSync(
|
|
||||||
fromNamespace(namespaces.LOCALE)(config)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
namespace: 'twilio',
|
|
||||||
tag: 'Twilio (SMS service)',
|
|
||||||
p: (
|
|
||||||
<>
|
|
||||||
Twilio is used for SMS operator notifications, phone number collection
|
|
||||||
for compliance, and 1-confirmation redemptions on cash-out
|
|
||||||
transactions.
|
|
||||||
<br />
|
|
||||||
You'll need to configure Twilio if you're offering cash-out or any
|
|
||||||
compliance options
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
isComplete:
|
|
||||||
twilio.validationSchema.isValidSync(accounts?.twilio) ||
|
|
||||||
R.isEmpty(accounts?.twilio)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
namespace: namespaces.COMMISSIONS,
|
|
||||||
tag: 'Commissions',
|
|
||||||
p: (
|
|
||||||
<>
|
|
||||||
From the Commissions page, you can define all the commissions of your
|
|
||||||
machines. The values set here will be default values of all machines
|
|
||||||
you'll later add to your network. Default settings keep you from
|
|
||||||
having to enter the same values everytime you add a new machine. Once
|
|
||||||
a machine is added, you may override these values per machine and per
|
|
||||||
cryptocurrency in the overrides section.
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
isComplete: CommissionsSchema.isValidSync(
|
|
||||||
fromNamespace(namespaces.COMMISSIONS)(config)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
namespace: namespaces.NOTIFICATIONS,
|
|
||||||
tag: 'Notifications',
|
|
||||||
p: (
|
|
||||||
<>
|
|
||||||
Your notification settings will allow customize what notifications you
|
|
||||||
get and where. You can later override all default balance alerts setup
|
|
||||||
here.
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
isComplete: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
namespace: namespaces.OPERATOR_INFO,
|
|
||||||
tag: 'Operator info',
|
|
||||||
p: <></>,
|
|
||||||
isComplete: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
return steps
|
|
||||||
}
|
|
||||||
|
|
||||||
const findStepByName = namespace =>
|
|
||||||
R.findIndex(R.propEq('namespace', namespace))
|
|
||||||
|
|
||||||
const getNextIndex = namespace => R.compose(R.add(1), findStepByName(namespace))
|
|
||||||
|
|
||||||
const initialState = {
|
|
||||||
steps: [],
|
|
||||||
current: 'welcome',
|
|
||||||
next: namespaces.WALLETS
|
|
||||||
}
|
|
||||||
|
|
||||||
const reducer = (state, action) => {
|
|
||||||
switch (action.type) {
|
|
||||||
case 'wizard/INIT':
|
|
||||||
return { ...state, steps: validateSteps(action.payload) }
|
|
||||||
case 'wizard/VALIDATE_STEP':
|
|
||||||
return { ...state, steps: validateSteps(action.payload) }
|
|
||||||
case 'wizard/SET_STEP':
|
|
||||||
// eslint-disable-next-line no-case-declarations
|
|
||||||
const nextIndex = getNextIndex(action.payload)(state.steps)
|
|
||||||
// eslint-disable-next-line no-case-declarations
|
|
||||||
const current = findStepByName(action.payload)(state.steps)
|
|
||||||
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
...state.steps[current],
|
|
||||||
current: action.payload,
|
|
||||||
next:
|
|
||||||
state.steps[current].isComplete && state.steps[nextIndex]
|
|
||||||
? state.steps[nextIndex].namespace
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Wizard() {
|
|
||||||
const { path } = useRouteMatch()
|
|
||||||
|
|
||||||
const { data } = useQuery(GET_GLOBAL_CONFIG)
|
|
||||||
const [state, dispatch] = useReducer(reducer, initialState)
|
|
||||||
const { steps } = state
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
data &&
|
|
||||||
dispatch({
|
|
||||||
type: 'wizard/INIT',
|
|
||||||
payload: { config: data.config, accounts: data.accounts }
|
|
||||||
})
|
|
||||||
}, [data])
|
|
||||||
|
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
const [open, setOpen] = useState(true)
|
||||||
|
const [footerExp, setFooterExp] = useState(false)
|
||||||
|
|
||||||
if (!steps || !steps.length) return <div></div>
|
const isWelcome = step === 0
|
||||||
|
const classNames = {
|
||||||
|
[classes.blurred]: footerExp,
|
||||||
|
[classes.wrapper]: true,
|
||||||
|
[classes.welcomeBackground]: isWelcome
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = () => {
|
||||||
|
setFooterExp(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const doContinue = () => {
|
||||||
|
if (step >= STEPS.length - 1) return setOpen(false)
|
||||||
|
|
||||||
|
const nextStep = step === 0 && wizardStep ? wizardStep : step + 1
|
||||||
|
|
||||||
|
setFooterExp(true)
|
||||||
|
setStep(nextStep)
|
||||||
|
}
|
||||||
|
const current = STEPS[step]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog fullScreen open={true}>
|
<Dialog fullScreen open={open}>
|
||||||
<DialogContent
|
<DialogContent className={classnames(classNames)}>
|
||||||
className={classnames(
|
<current.Component doContinue={doContinue} isActive={!footerExp} />
|
||||||
classes.wrapper,
|
|
||||||
state.current === 'welcome' && classes.welcomeBackground
|
|
||||||
)}>
|
|
||||||
<Switch>
|
|
||||||
<Route exact path={path}>
|
|
||||||
<Welcome
|
|
||||||
// {...{ state, dispatch }}
|
|
||||||
dispatch={dispatch}
|
|
||||||
/>
|
|
||||||
</Route>
|
|
||||||
|
|
||||||
<Route path={`${path}/${namespaces.WALLETS}`}>
|
|
||||||
<Wallet
|
|
||||||
{...{ state }}
|
|
||||||
namespace={namespaces.WALLETS}
|
|
||||||
dispatch={dispatch}
|
|
||||||
/>
|
|
||||||
</Route>
|
|
||||||
|
|
||||||
<Route path={`${path}/${namespaces.LOCALE}`}>
|
|
||||||
<Locales
|
|
||||||
{...{ state }}
|
|
||||||
namespace={namespaces.LOCALE}
|
|
||||||
dispatch={dispatch}
|
|
||||||
/>
|
|
||||||
</Route>
|
|
||||||
<Route path={`${path}/${namespaces.COMMISSIONS}`}>
|
|
||||||
<Commissions
|
|
||||||
{...{ state }}
|
|
||||||
namespace={namespaces.COMMISSIONS}
|
|
||||||
dispatch={dispatch}
|
|
||||||
/>
|
|
||||||
</Route>
|
|
||||||
<Route path={`${path}/twilio`}>
|
|
||||||
<Twilio {...{ state }} namespace={'twilio'} dispatch={dispatch} />
|
|
||||||
</Route>
|
|
||||||
<Route path={`${path}/${namespaces.NOTIFICATIONS}`}>
|
|
||||||
<N
|
|
||||||
{...{ state }}
|
|
||||||
namespace={namespaces.NOTIFICATIONS}
|
|
||||||
dispatch={dispatch}
|
|
||||||
/>
|
|
||||||
</Route>
|
|
||||||
<Route path={`${path}/${namespaces.OPERATOR_INFO}`}>
|
|
||||||
<WizardOperatorInfo
|
|
||||||
{...{ state }}
|
|
||||||
namespace={namespaces.OPERATOR_INFO}
|
|
||||||
dispatch={dispatch}
|
|
||||||
/>
|
|
||||||
</Route>
|
|
||||||
</Switch>
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
{state.current !== 'welcome' && <Footer {...state} path={path}></Footer>}
|
{!isWelcome && (
|
||||||
|
<Footer
|
||||||
|
currentStep={step}
|
||||||
|
steps={STEPS.length - 1}
|
||||||
|
exImage={current.exImage}
|
||||||
|
subtitle={current.subtitle}
|
||||||
|
text={current.text}
|
||||||
|
open={footerExp}
|
||||||
|
start={start}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
import { useQuery } from '@apollo/react-hooks'
|
|
||||||
import gql from 'graphql-tag'
|
|
||||||
import * as R from 'ramda'
|
|
||||||
import React, { useEffect, useState } from 'react'
|
|
||||||
|
|
||||||
import { NamespacedTable as EditableTable } from 'src/components/editableTable'
|
|
||||||
import { P, H4 } from 'src/components/typography'
|
|
||||||
import { getElements } from 'src/pages/Wallet/helper'
|
|
||||||
import { fromNamespace } from 'src/utils/config'
|
|
||||||
|
|
||||||
const GET_INFO = gql`
|
|
||||||
query getData {
|
|
||||||
config
|
|
||||||
accounts
|
|
||||||
accountsConfig {
|
|
||||||
code
|
|
||||||
display
|
|
||||||
class
|
|
||||||
cryptos
|
|
||||||
}
|
|
||||||
cryptoCurrencies {
|
|
||||||
code
|
|
||||||
display
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const Wallet = ({ dispatch, namespace }) => {
|
|
||||||
const { data } = useQuery(GET_INFO)
|
|
||||||
const [dispatched, setDispatched] = useState(false)
|
|
||||||
|
|
||||||
const config = data?.config && fromNamespace('wallets')(data.config)
|
|
||||||
|
|
||||||
const wizardCoin =
|
|
||||||
config && Object.keys(config).find(k => k.endsWith('_wizard') && config[k])
|
|
||||||
const coinCode = wizardCoin && wizardCoin.replace('_wizard', '')
|
|
||||||
|
|
||||||
const accountsConfig = data?.accountsConfig
|
|
||||||
const cryptoCurrencies =
|
|
||||||
data?.cryptoCurrencies?.filter(({ code }) => code === coinCode) ?? []
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (dispatched || !data?.config) return
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: 'wizard/VALIDATE_STEP',
|
|
||||||
payload: { config: data.config, accounts: data.accounts }
|
|
||||||
})
|
|
||||||
setDispatched(true)
|
|
||||||
}, [data, dispatch, dispatched])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<H4>All set</H4>
|
|
||||||
<P>
|
|
||||||
This are your wallet settings. You can later edit these and add
|
|
||||||
additional coins.
|
|
||||||
</P>
|
|
||||||
<EditableTable
|
|
||||||
rowSize="lg"
|
|
||||||
titleLg
|
|
||||||
name="All set"
|
|
||||||
namespaces={R.map(R.path(['code']))(cryptoCurrencies)}
|
|
||||||
data={config}
|
|
||||||
elements={getElements(cryptoCurrencies, accountsConfig, true)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Wallet
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
|
||||||
import { makeStyles } from '@material-ui/core'
|
|
||||||
import gql from 'graphql-tag'
|
|
||||||
import React, { useState, useEffect } from 'react'
|
|
||||||
|
|
||||||
import { RadioGroup } from 'src/components/inputs'
|
|
||||||
import { H4 } from 'src/components/typography'
|
|
||||||
import styles from 'src/pages/Wizard/Radio.styles'
|
|
||||||
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
|
||||||
|
|
||||||
const GET_CONFIG = gql`
|
|
||||||
{
|
|
||||||
config
|
|
||||||
cryptoCurrencies {
|
|
||||||
code
|
|
||||||
display
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const SAVE_CONFIG = gql`
|
|
||||||
mutation Save($config: JSONObject) {
|
|
||||||
saveConfig(config: $config)
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const WalletCoin = () => {
|
|
||||||
const classes = useStyles()
|
|
||||||
|
|
||||||
const { data } = useQuery(GET_CONFIG)
|
|
||||||
const [saveConfig] = useMutation(SAVE_CONFIG)
|
|
||||||
const [currentCode, setCurrentCode] = useState(null)
|
|
||||||
const cryptoCurrencies = data?.cryptoCurrencies ?? []
|
|
||||||
|
|
||||||
const wallets = data?.config && fromNamespace('wallets')(data.config)
|
|
||||||
const wizardCoin =
|
|
||||||
wallets &&
|
|
||||||
Object.keys(wallets).find(k => k.endsWith('_wizard') && wallets[k])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setCurrentCode(wizardCoin && wizardCoin.replace('_wizard', ''))
|
|
||||||
}, [wizardCoin])
|
|
||||||
|
|
||||||
const save = code => {
|
|
||||||
setCurrentCode(code)
|
|
||||||
|
|
||||||
const keys = {}
|
|
||||||
for (const { code: c } of cryptoCurrencies) {
|
|
||||||
keys[`${c}_wizard`] = c === code
|
|
||||||
}
|
|
||||||
|
|
||||||
keys[`${code}_zeroConf`] = 'no-zero-conf'
|
|
||||||
keys[`${code}_ticker`] = 'kraken'
|
|
||||||
|
|
||||||
const config = toNamespace(namespaces.WALLETS)(keys)
|
|
||||||
return saveConfig({ variables: { config } })
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<H4>Choose your first cryptocurrency</H4>
|
|
||||||
|
|
||||||
<RadioGroup
|
|
||||||
labelClassName={classes.radioLabel}
|
|
||||||
className={classes.radioGroup}
|
|
||||||
options={cryptoCurrencies}
|
|
||||||
value={currentCode}
|
|
||||||
onChange={event => save(event.target.value)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default WalletCoin
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useEffect } from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { Table as EditableTable } from 'src/components/editableTable'
|
import { Table as EditableTable } from 'src/components/editableTable'
|
||||||
import Section from 'src/components/layout/Section'
|
import Section from 'src/components/layout/Section'
|
||||||
|
|
@ -16,7 +16,6 @@ const useStyles = makeStyles(styles)
|
||||||
const GET_DATA = gql`
|
const GET_DATA = gql`
|
||||||
query getData {
|
query getData {
|
||||||
config
|
config
|
||||||
accounts
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const SAVE_CONFIG = gql`
|
const SAVE_CONFIG = gql`
|
||||||
|
|
@ -25,31 +24,17 @@ const SAVE_CONFIG = gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
function Commissions({ dispatch, namespace }) {
|
function Commissions({ isActive, doContinue }) {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const { data, refetch } = useQuery(GET_DATA)
|
const { data } = useQuery(GET_DATA)
|
||||||
|
|
||||||
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
||||||
refetchQueries: () => ['getData']
|
onCompleted: doContinue
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch({ type: 'wizard/SET_STEP', payload: namespace })
|
|
||||||
}, [dispatch, namespace])
|
|
||||||
|
|
||||||
const config = data?.config && fromNamespace(namespace)(data.config)
|
|
||||||
const values = config && !R.isEmpty(config) ? config : defaults
|
|
||||||
|
|
||||||
const save = it => {
|
const save = it => {
|
||||||
const config = toNamespace(namespace)(it[namespace][0])
|
const config = toNamespace('commissions')(it.commissions[0])
|
||||||
return saveConfig({ variables: { config } })
|
return saveConfig({ variables: { config } })
|
||||||
.then(() => refetch())
|
|
||||||
.then(({ data }) => {
|
|
||||||
return dispatch({
|
|
||||||
type: 'wizard/VALIDATE_STEP',
|
|
||||||
payload: { accounts: data.accounts, config: data.config }
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const currency = R.path(['fiatCurrency'])(
|
const currency = R.path(['fiatCurrency'])(
|
||||||
|
|
@ -65,11 +50,12 @@ function Commissions({ dispatch, namespace }) {
|
||||||
rowSize="lg"
|
rowSize="lg"
|
||||||
titleLg
|
titleLg
|
||||||
name="commissions"
|
name="commissions"
|
||||||
outerEditingId={1}
|
initialValues={defaults}
|
||||||
enableEdit
|
enableEdit
|
||||||
|
forceAdd={isActive}
|
||||||
save={save}
|
save={save}
|
||||||
validationSchema={schema}
|
validationSchema={schema}
|
||||||
data={R.of({ ...values, id: 1 })}
|
data={[]}
|
||||||
elements={mainFields(currency)}
|
elements={mainFields(currency)}
|
||||||
/>
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
import { makeStyles, Drawer, Grid } from '@material-ui/core'
|
import { makeStyles, Drawer, Grid } from '@material-ui/core'
|
||||||
import ClickAwayListener from '@material-ui/core/ClickAwayListener'
|
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import * as R from 'ramda'
|
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useHistory } from 'react-router-dom'
|
|
||||||
|
|
||||||
import Modal from 'src/components/Modal'
|
import Modal from 'src/components/Modal'
|
||||||
import Stepper from 'src/components/Stepper'
|
import Stepper from 'src/components/Stepper'
|
||||||
|
|
@ -11,14 +8,6 @@ import { Button, Link } from 'src/components/buttons'
|
||||||
import { P, H2, Info2 } from 'src/components/typography'
|
import { P, H2, Info2 } from 'src/components/typography'
|
||||||
import { spacer } from 'src/styling/variables'
|
import { spacer } from 'src/styling/variables'
|
||||||
|
|
||||||
const getStepperProps = (current, steps) => ({
|
|
||||||
steps: R.length(steps),
|
|
||||||
currentStep: R.compose(
|
|
||||||
R.add(1),
|
|
||||||
R.findIndex(R.propEq('namespace', current))
|
|
||||||
)(steps)
|
|
||||||
})
|
|
||||||
|
|
||||||
const useStyles = makeStyles(() => ({
|
const useStyles = makeStyles(() => ({
|
||||||
drawer: {
|
drawer: {
|
||||||
borderTop: 'none',
|
borderTop: 'none',
|
||||||
|
|
@ -47,30 +36,50 @@ const useStyles = makeStyles(() => ({
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
function Footer({ next, current, steps: collection, path, tag, p }) {
|
function Footer({ currentStep, steps, subtitle, text, exImage, open, start }) {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const history = useHistory()
|
|
||||||
const [open, setOpen] = useState(true)
|
|
||||||
const [fullExample, setFullExample] = useState(false)
|
const [fullExample, setFullExample] = useState(false)
|
||||||
|
|
||||||
const handleClick = () => history.push(`${path}/${next}`)
|
|
||||||
const handleClickAway = () => setOpen(false)
|
|
||||||
const { currentStep, steps } = getStepperProps(current, collection)
|
|
||||||
|
|
||||||
const wrapperClassNames = {
|
const wrapperClassNames = {
|
||||||
[classes.wrapper]: true,
|
[classes.wrapper]: true,
|
||||||
[classes.smallWrapper]: !open
|
[classes.smallWrapper]: !open
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ClickAwayListener onClickAway={handleClickAway}>
|
<Drawer
|
||||||
<Drawer
|
anchor={'bottom'}
|
||||||
onClick={() => setOpen(true)}
|
open={true}
|
||||||
anchor={'bottom'}
|
variant={'persistent'}
|
||||||
open={true}
|
classes={{ paperAnchorDockedBottom: classes.drawer }}>
|
||||||
variant={'persistent'}
|
<div className={classnames(wrapperClassNames)}>
|
||||||
classes={{ paperAnchorDockedBottom: classes.drawer }}>
|
<Grid container direction="row" justify="center" alignItems="baseline">
|
||||||
<div className={classnames(wrapperClassNames)}>
|
<Grid
|
||||||
|
item
|
||||||
|
xs={5}
|
||||||
|
container
|
||||||
|
direction={open ? 'column' : 'row'}
|
||||||
|
justify="flex-start"
|
||||||
|
alignItems="baseline">
|
||||||
|
<H2 className={classes.title}>Setup Lamassu Admin</H2>
|
||||||
|
<Info2 className={classes.subtitle}>{subtitle}</Info2>
|
||||||
|
{open && <P>{text}</P>}
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
item
|
||||||
|
xs={4}
|
||||||
|
container
|
||||||
|
direction="column"
|
||||||
|
justify="flex-start"
|
||||||
|
alignItems="flex-end"
|
||||||
|
spacing={5}>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
{steps && currentStep && (
|
||||||
|
<Stepper currentStep={currentStep} steps={steps} />
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
{open && (
|
||||||
<Grid
|
<Grid
|
||||||
container
|
container
|
||||||
direction="row"
|
direction="row"
|
||||||
|
|
@ -80,12 +89,15 @@ function Footer({ next, current, steps: collection, path, tag, p }) {
|
||||||
item
|
item
|
||||||
xs={5}
|
xs={5}
|
||||||
container
|
container
|
||||||
direction={open ? 'column' : 'row'}
|
direction="column"
|
||||||
justify="flex-start"
|
justify="flex-start"
|
||||||
alignItems="baseline">
|
alignItems="flex-start">
|
||||||
<H2 className={classes.title}>Setup Lamassu Admin</H2>
|
<Link
|
||||||
<Info2 className={classes.subtitle}>{tag}</Info2>
|
onClick={() => {
|
||||||
{open && <P>{p}</P>}
|
setFullExample(true)
|
||||||
|
}}>
|
||||||
|
See full example
|
||||||
|
</Link>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
item
|
item
|
||||||
|
|
@ -95,68 +107,28 @@ function Footer({ next, current, steps: collection, path, tag, p }) {
|
||||||
justify="flex-start"
|
justify="flex-start"
|
||||||
alignItems="flex-end"
|
alignItems="flex-end"
|
||||||
spacing={5}>
|
spacing={5}>
|
||||||
<Grid item xs={12}>
|
<Grid item>
|
||||||
{steps && currentStep && (
|
<Button size="lg" onClick={start}>
|
||||||
<Stepper {...{ currentStep, steps }}></Stepper>
|
Get Started
|
||||||
)}
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
{open && (
|
)}
|
||||||
<Grid
|
</div>
|
||||||
container
|
<Modal
|
||||||
direction="row"
|
closeOnEscape={true}
|
||||||
justify="center"
|
closeOnBackdropClick={true}
|
||||||
alignItems="baseline">
|
className={classes.modal}
|
||||||
<Grid
|
xl={true}
|
||||||
item
|
width={1152 + 120 + 56}
|
||||||
xs={5}
|
handleClose={() => {
|
||||||
container
|
setFullExample(false)
|
||||||
direction="column"
|
}}
|
||||||
justify="flex-start"
|
open={fullExample}>
|
||||||
alignItems="flex-start">
|
<img width={1152} src={exImage} alt="" />
|
||||||
<Link
|
</Modal>
|
||||||
onClick={() => {
|
</Drawer>
|
||||||
setFullExample(true)
|
|
||||||
}}>
|
|
||||||
See full example
|
|
||||||
</Link>
|
|
||||||
</Grid>
|
|
||||||
<Grid
|
|
||||||
item
|
|
||||||
xs={4}
|
|
||||||
container
|
|
||||||
direction="column"
|
|
||||||
justify="flex-start"
|
|
||||||
alignItems="flex-end"
|
|
||||||
spacing={5}>
|
|
||||||
<Grid item>
|
|
||||||
<Button size="lg" disabled={!next} onClick={handleClick}>
|
|
||||||
Continue
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<Modal
|
|
||||||
closeOnEscape={true}
|
|
||||||
closeOnBackdropClick={true}
|
|
||||||
className={classes.modal}
|
|
||||||
xl={true}
|
|
||||||
width={1152 + 120 + 56}
|
|
||||||
handleClose={() => {
|
|
||||||
setFullExample(false)
|
|
||||||
}}
|
|
||||||
open={fullExample}>
|
|
||||||
<img
|
|
||||||
width={1152}
|
|
||||||
src={`/fullexample.${current}.png`}
|
|
||||||
alt={`${current} configuration example`}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
</Drawer>
|
|
||||||
</ClickAwayListener>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import React from 'react'
|
||||||
import React, { useEffect } from 'react'
|
|
||||||
|
|
||||||
import { Table as EditableTable } from 'src/components/editableTable'
|
import { Table as EditableTable } from 'src/components/editableTable'
|
||||||
import Section from 'src/components/layout/Section'
|
import Section from 'src/components/layout/Section'
|
||||||
|
|
@ -13,7 +12,7 @@ import {
|
||||||
localeDefaults as defaults,
|
localeDefaults as defaults,
|
||||||
LocaleSchema as schema
|
LocaleSchema as schema
|
||||||
} from 'src/pages/Locales/helper'
|
} from 'src/pages/Locales/helper'
|
||||||
import { fromNamespace, toNamespace } from 'src/utils/config'
|
import { toNamespace } from 'src/utils/config'
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
|
|
@ -50,31 +49,17 @@ const SAVE_CONFIG = gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
function Locales({ dispatch, namespace }) {
|
function Locales({ isActive, doContinue }) {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const { data, refetch } = useQuery(GET_DATA)
|
const { data } = useQuery(GET_DATA)
|
||||||
|
|
||||||
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
||||||
refetchQueries: () => ['getData']
|
onCompleted: doContinue
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch({ type: 'wizard/SET_STEP', payload: namespace })
|
|
||||||
}, [dispatch, namespace])
|
|
||||||
|
|
||||||
const config = data?.config && fromNamespace(namespace)(data.config)
|
|
||||||
const locale = config && !R.isEmpty(config) ? config : defaults
|
|
||||||
|
|
||||||
const save = it => {
|
const save = it => {
|
||||||
const config = toNamespace(namespace)(it[namespace][0])
|
const config = toNamespace('locale')(it.locale[0])
|
||||||
return saveConfig({ variables: { config } })
|
return saveConfig({ variables: { config } })
|
||||||
.then(() => refetch())
|
|
||||||
.then(({ data }) => {
|
|
||||||
return dispatch({
|
|
||||||
type: 'wizard/VALIDATE_STEP',
|
|
||||||
payload: { accounts: data.accounts, config: data.config }
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -86,12 +71,12 @@ function Locales({ dispatch, namespace }) {
|
||||||
rowSize="lg"
|
rowSize="lg"
|
||||||
titleLg
|
titleLg
|
||||||
name="locale"
|
name="locale"
|
||||||
initialValues={locale}
|
initialValues={defaults}
|
||||||
outerEditingId={1}
|
forceAdd={isActive}
|
||||||
enableEdit
|
enableEdit
|
||||||
save={save}
|
save={save}
|
||||||
validationSchema={schema}
|
validationSchema={schema}
|
||||||
data={R.of({ ...locale, id: 1 })}
|
data={[]}
|
||||||
elements={mainFields(data)}
|
elements={mainFields(data)}
|
||||||
/>
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import Grid from '@material-ui/core/Grid'
|
import Grid from '@material-ui/core/Grid'
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import Sidebar from 'src/components/layout/Sidebar'
|
import Sidebar from 'src/components/layout/Sidebar'
|
||||||
import TitleSection from 'src/components/layout/TitleSection'
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
|
|
@ -38,10 +38,7 @@ const pages = [
|
||||||
CRYPTO_BALANCE_ALERTS
|
CRYPTO_BALANCE_ALERTS
|
||||||
]
|
]
|
||||||
|
|
||||||
const N = ({ dispatch, namespace }) => {
|
const N = () => {
|
||||||
useEffect(() => {
|
|
||||||
dispatch({ type: 'wizard/SET_STEP', payload: namespace })
|
|
||||||
}, [dispatch, namespace])
|
|
||||||
const [selected, setSelected] = useState(EMAIL)
|
const [selected, setSelected] = useState(EMAIL)
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import React, { useEffect } from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import styles from 'src/pages/AddMachine/styles'
|
import styles from 'src/pages/AddMachine/styles'
|
||||||
import OperatorInfo from 'src/pages/OperatorInfo/OperatorInfo'
|
import OperatorInfo from 'src/pages/OperatorInfo/OperatorInfo'
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
function WizardOperatorInfo({ dispatch, namespace }) {
|
function WizardOperatorInfo() {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
useEffect(() => {
|
|
||||||
dispatch({ type: 'wizard/SET_STEP', payload: namespace })
|
|
||||||
}, [dispatch, namespace])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.wrapper}>
|
<div className={classes.wrapper}>
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,18 @@
|
||||||
import { useMutation, useQuery } from '@apollo/react-hooks'
|
import { useMutation, useQuery } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles, Box } from '@material-ui/core'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
import InfoMessage from 'src/components/InfoMessage'
|
||||||
import Tooltip from 'src/components/Tooltip'
|
import Tooltip from 'src/components/Tooltip'
|
||||||
import { IconButton } from 'src/components/buttons'
|
import { Button } from 'src/components/buttons'
|
||||||
import { RadioGroup } from 'src/components/inputs'
|
import { RadioGroup } from 'src/components/inputs'
|
||||||
import { H1, Label1, H4, P } from 'src/components/typography'
|
import { H1, H4, P } from 'src/components/typography'
|
||||||
import addMachineStyles from 'src/pages/AddMachine/styles'
|
|
||||||
import {
|
|
||||||
styles as globalStyles,
|
|
||||||
contactInfoStyles
|
|
||||||
} from 'src/pages/OperatorInfo/OperatorInfo.styles'
|
|
||||||
import FormRenderer from 'src/pages/Services/FormRenderer'
|
import FormRenderer from 'src/pages/Services/FormRenderer'
|
||||||
import twilio from 'src/pages/Services/schemas/twilio'
|
import twilio from 'src/pages/Services/schemas/twilio'
|
||||||
import styles from 'src/pages/Wizard/Radio.styles'
|
|
||||||
import { ReactComponent as HelpIcon } from 'src/styling/icons/action/help/zodiac.svg'
|
import styles from './Wallet/Shared.styles'
|
||||||
import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
|
||||||
|
|
||||||
const GET_CONFIG = gql`
|
const GET_CONFIG = gql`
|
||||||
{
|
{
|
||||||
|
|
@ -34,23 +29,32 @@ const SAVE_ACCOUNTS = gql`
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
...styles,
|
...styles,
|
||||||
...globalStyles,
|
|
||||||
...contactInfoStyles,
|
|
||||||
...addMachineStyles,
|
|
||||||
content: {
|
content: {
|
||||||
width: 820,
|
width: 820
|
||||||
flex: 0
|
|
||||||
},
|
},
|
||||||
radioLabel: {
|
radioLabel: {
|
||||||
...styles.radioLabel,
|
...styles.radioLabel,
|
||||||
width: 280
|
width: 280
|
||||||
|
},
|
||||||
|
wrapper: {
|
||||||
|
width: 1200,
|
||||||
|
height: 100,
|
||||||
|
margin: [[0, 'auto']]
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
marginLeft: 8,
|
||||||
|
marginBottom: 5
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
marginTop: 20,
|
||||||
|
marginBottom: 20
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const options = [
|
const options = [
|
||||||
{
|
{
|
||||||
code: 'enable',
|
code: 'enable',
|
||||||
display: 'Yes, I will add two-way machines'
|
display: 'Yes, I will'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'disable',
|
code: 'disable',
|
||||||
|
|
@ -58,85 +62,89 @@ const options = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
function Twilio({ dispatch, namespace }) {
|
function Twilio({ doContinue }) {
|
||||||
|
const classes = useStyles()
|
||||||
|
const [selected, setSelected] = useState(null)
|
||||||
|
const [error, setError] = useState(false)
|
||||||
|
|
||||||
const { data, refetch } = useQuery(GET_CONFIG)
|
const { data, refetch } = useQuery(GET_CONFIG)
|
||||||
const [saveAccounts] = useMutation(SAVE_ACCOUNTS)
|
const [saveAccounts] = useMutation(SAVE_ACCOUNTS, {
|
||||||
|
onCompleted: doContinue
|
||||||
|
})
|
||||||
|
|
||||||
const accounts = data?.accounts ?? []
|
const accounts = data?.accounts ?? []
|
||||||
|
|
||||||
const [enable, setEnable] = useState('disable')
|
const onSelect = e => {
|
||||||
|
setSelected(e.target.value)
|
||||||
useEffect(() => {
|
setError(false)
|
||||||
if (!accounts?.twilio) return
|
|
||||||
twilio.validationSchema.isValidSync(accounts.twilio) && setEnable('enable')
|
|
||||||
}, [accounts])
|
|
||||||
|
|
||||||
const handleRadio = enableOrNot => {
|
|
||||||
setEnable(enableOrNot)
|
|
||||||
enableOrNot === 'disable' && save({})
|
|
||||||
enableOrNot === 'enable' && save({ enable: true, ...accounts?.twilio })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
const clickContinue = () => {
|
||||||
dispatch({ type: 'wizard/SET_STEP', payload: namespace })
|
if (!selected) return setError(true)
|
||||||
}, [dispatch, namespace])
|
doContinue()
|
||||||
const classes = useStyles()
|
}
|
||||||
|
|
||||||
const save = twilio => {
|
const save = twilio => {
|
||||||
const accounts = { twilio }
|
const accounts = { twilio }
|
||||||
return saveAccounts({ variables: { accounts } })
|
return saveAccounts({ variables: { accounts } }).then(() => refetch())
|
||||||
.then(() => refetch())
|
}
|
||||||
.then(({ data }) => {
|
|
||||||
return dispatch({
|
const titleClasses = {
|
||||||
type: 'wizard/VALIDATE_STEP',
|
[classes.title]: true,
|
||||||
payload: { accounts: data.accounts, config: data.config }
|
[classes.error]: error
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.wrapper}>
|
<div className={classes.wrapper}>
|
||||||
<div className={classes.content}>
|
<div className={classes.content}>
|
||||||
<H1>Twilio (SMS service)</H1>
|
<H1>Twilio (SMS service)</H1>
|
||||||
<H4>
|
<Box display="flex" alignItems="end">
|
||||||
Will you setup a two way machine?
|
<H4 noMargin className={classnames(titleClasses)}>
|
||||||
<Tooltip width={304} enableClick Button={IconButton} Icon={HelpIcon}>
|
Will you setup a two way machine or compliance?
|
||||||
|
</H4>
|
||||||
|
<Tooltip width={304}>
|
||||||
<P>
|
<P>
|
||||||
Two-way machines allow your customers not only to buy (cash-in)
|
Two-way machines allow your customers not only to buy (cash-in)
|
||||||
but also sell cryptocurrencies (cash-out).
|
but also sell cryptocurrencies (cash-out).
|
||||||
</P>
|
</P>
|
||||||
<P>
|
<P>
|
||||||
To get your admin up and running, you’ll only need an SMS service
|
You’ll need an SMS service for cash-out transactions and for any
|
||||||
for cash-out transactions. If you’re using one-way machines,
|
complaince triggers
|
||||||
select “No” to skip this step for now. You can later set it up
|
|
||||||
within the Lamassu Admin.
|
|
||||||
</P>
|
</P>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</H4>
|
</Box>
|
||||||
|
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
labelClassName={classes.radioLabel}
|
labelClassName={classes.radioLabel}
|
||||||
className={classes.radioGroup}
|
className={classes.radioGroup}
|
||||||
options={options}
|
options={options}
|
||||||
value={enable}
|
value={selected}
|
||||||
onChange={event => handleRadio(event.target.value)}
|
onChange={onSelect}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className={classnames(classes.section, classes.infoMessage)}>
|
<InfoMessage className={classes.info}>
|
||||||
<WarningIcon />
|
Before configuring Twilio, create an account and phone number to use
|
||||||
<Label1>
|
the Admin.
|
||||||
Before configuring Twilio, create an account and phone number to use
|
</InfoMessage>
|
||||||
the Admin.
|
|
||||||
</Label1>
|
{selected === 'enable' && (
|
||||||
</div>
|
<>
|
||||||
{enable === 'enable' && (
|
<H4 noMargin>Enter credentials</H4>
|
||||||
<FormRenderer
|
<FormRenderer
|
||||||
xs={6}
|
xs={6}
|
||||||
save={save}
|
save={save}
|
||||||
value={accounts.twilio}
|
value={accounts.twilio}
|
||||||
elements={twilio.elements}
|
elements={twilio.elements}
|
||||||
validationSchema={twilio.validationSchema}
|
validationSchema={twilio.validationSchema}
|
||||||
buttonLabel={'Save'}
|
buttonLabel={'Continue'}
|
||||||
/>
|
buttonClass={classes.formButton}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{selected !== 'enable' && (
|
||||||
|
<Button size="lg" onClick={clickContinue} className={classes.button}>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
import { makeStyles } from '@material-ui/core'
|
|
||||||
import React, { useState, useEffect } from 'react'
|
|
||||||
|
|
||||||
import Sidebar from 'src/components/layout/Sidebar'
|
|
||||||
import TitleSection from 'src/components/layout/TitleSection'
|
|
||||||
import styles from 'src/pages/AddMachine/styles'
|
|
||||||
import ChooseCoin from 'src/pages/Wizard/components/ChooseCoin'
|
|
||||||
import ChooseWallet from 'src/pages/Wizard/components/ChooseWallet'
|
|
||||||
|
|
||||||
import AllSet from './AllSet'
|
|
||||||
import Blockcypher from './Blockcypher'
|
|
||||||
import ChooseExchange from './ChooseExchange'
|
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
|
||||||
|
|
||||||
const COIN = 'Choose cryptocurrency'
|
|
||||||
const WALLET = 'Choose wallet'
|
|
||||||
const EXCHANGE = 'Exchange'
|
|
||||||
const CASHOUT_OR_NOT = 'Blockcypher'
|
|
||||||
const ALL_SET = 'All set'
|
|
||||||
|
|
||||||
const pages = [COIN, WALLET, EXCHANGE, CASHOUT_OR_NOT, ALL_SET]
|
|
||||||
|
|
||||||
const Wallet = ({ dispatch, namespace }) => {
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch({ type: 'wizard/SET_STEP', payload: namespace })
|
|
||||||
}, [dispatch, namespace])
|
|
||||||
const [selected, setSelected] = useState(COIN)
|
|
||||||
const classes = useStyles()
|
|
||||||
|
|
||||||
const isSelected = it => selected === it
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classes.wrapper}>
|
|
||||||
<div className={classes.headerDiv}>
|
|
||||||
<TitleSection title="Wallet settings"></TitleSection>
|
|
||||||
</div>
|
|
||||||
<div className={classes.contentDiv}>
|
|
||||||
<Sidebar
|
|
||||||
data={pages}
|
|
||||||
isSelected={isSelected}
|
|
||||||
displayName={it => it}
|
|
||||||
onClick={it => setSelected(it)}
|
|
||||||
/>
|
|
||||||
<div className={classes.contentWrapper}>
|
|
||||||
{isSelected(COIN) && <ChooseCoin />}
|
|
||||||
{isSelected(WALLET) && <ChooseWallet />}
|
|
||||||
{isSelected(EXCHANGE) && <ChooseExchange />}
|
|
||||||
{isSelected(CASHOUT_OR_NOT) && <Blockcypher />}
|
|
||||||
{isSelected(ALL_SET) && <AllSet dispatch={dispatch} />}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Wallet
|
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
import * as R from 'ramda'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
import { Button } from 'src/components/buttons'
|
||||||
|
import { NamespacedTable as EditableTable } from 'src/components/editableTable'
|
||||||
|
import { P, H4 } from 'src/components/typography'
|
||||||
|
import { getElements, WalletSchema } from 'src/pages/Wallet/helper'
|
||||||
|
import { toNamespace, namespaces } from 'src/utils/config'
|
||||||
|
|
||||||
|
import styles from './Shared.styles'
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles)
|
||||||
|
const GET_INFO = gql`
|
||||||
|
query getData {
|
||||||
|
config
|
||||||
|
accounts
|
||||||
|
accountsConfig {
|
||||||
|
code
|
||||||
|
display
|
||||||
|
class
|
||||||
|
cryptos
|
||||||
|
}
|
||||||
|
cryptoCurrencies {
|
||||||
|
code
|
||||||
|
display
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const SAVE_CONFIG = gql`
|
||||||
|
mutation Save($config: JSONObject, $accounts: JSONObject) {
|
||||||
|
saveConfig(config: $config)
|
||||||
|
saveAccounts(accounts: $accounts)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const AllSet = ({ data: currentData, doContinue }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
console.log(currentData)
|
||||||
|
|
||||||
|
const { data } = useQuery(GET_INFO)
|
||||||
|
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
||||||
|
onCompleted: doContinue
|
||||||
|
})
|
||||||
|
|
||||||
|
const [error, setError] = useState(false)
|
||||||
|
|
||||||
|
const coin = currentData?.coin
|
||||||
|
|
||||||
|
const accountsConfig = data?.accountsConfig
|
||||||
|
const cryptoCurrencies = data?.cryptoCurrencies ?? []
|
||||||
|
|
||||||
|
const save = () => {
|
||||||
|
if (!WalletSchema.isValidSync(currentData)) return setError(true)
|
||||||
|
|
||||||
|
const withCoin = toNamespace(coin, R.omit('coin', currentData))
|
||||||
|
const config = toNamespace(namespaces.WALLETS)(withCoin)
|
||||||
|
setError(false)
|
||||||
|
return saveConfig({ variables: { config } })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<H4 className={error && classes.error}>All set</H4>
|
||||||
|
<P>
|
||||||
|
This are your wallet settings. You can later edit these and add
|
||||||
|
additional coins.
|
||||||
|
</P>
|
||||||
|
<EditableTable
|
||||||
|
rowSize="lg"
|
||||||
|
titleLg
|
||||||
|
name="All set"
|
||||||
|
namespaces={[coin]}
|
||||||
|
data={toNamespace(coin, R.omit('coin', currentData))}
|
||||||
|
elements={getElements(cryptoCurrencies, accountsConfig, true)}
|
||||||
|
/>
|
||||||
|
<Button size="lg" onClick={save} className={classes.button}>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AllSet
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
import { useMutation, useQuery } from '@apollo/react-hooks'
|
import { useMutation, useQuery } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import { Link } from 'src/components/buttons'
|
import { Link, Button } from 'src/components/buttons'
|
||||||
import { RadioGroup } from 'src/components/inputs'
|
import { RadioGroup } from 'src/components/inputs'
|
||||||
import { P, H4 } from 'src/components/typography'
|
import { P, H4 } from 'src/components/typography'
|
||||||
import FormRenderer from 'src/pages/Services/FormRenderer'
|
import FormRenderer from 'src/pages/Services/FormRenderer'
|
||||||
import schema from 'src/pages/Services/schemas'
|
import schema from 'src/pages/Services/schemas'
|
||||||
import styles from 'src/pages/Wizard/Radio.styles'
|
|
||||||
|
import styles from './Shared.styles'
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
...styles,
|
...styles,
|
||||||
|
|
@ -25,7 +26,7 @@ const GET_CONFIG = gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const SAVE_ACCOUNTS = gql`
|
const SAVE_ACCOUNTS = gql`
|
||||||
mutation Save($accounts: JSONObject) {
|
mutation SaveAccountsBC($accounts: JSONObject) {
|
||||||
saveAccounts(accounts: $accounts)
|
saveAccounts(accounts: $accounts)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
@ -41,23 +42,22 @@ const options = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const Blockcypher = () => {
|
const Blockcypher = ({ addData }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
const { data } = useQuery(GET_CONFIG)
|
const { data } = useQuery(GET_CONFIG)
|
||||||
|
const [saveConfig] = useMutation(SAVE_ACCOUNTS, {
|
||||||
|
onCompleted: () => addData({ zeroConf: 'blockcypher' })
|
||||||
|
})
|
||||||
|
|
||||||
|
const [selected, setSelected] = useState(null)
|
||||||
|
const [error, setError] = useState(false)
|
||||||
|
|
||||||
const accounts = data?.accounts ?? []
|
const accounts = data?.accounts ?? []
|
||||||
|
|
||||||
const classes = useStyles()
|
const onSelect = e => {
|
||||||
const [saveConfig] = useMutation(SAVE_ACCOUNTS)
|
setSelected(e.target.value)
|
||||||
const [currentCode, setCurrentCode] = useState('disable')
|
setError(false)
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!accounts?.blockcypher) return
|
|
||||||
schema.blockcypher.validationSchema.isValidSync(accounts.blockcypher) &&
|
|
||||||
setCurrentCode('enable')
|
|
||||||
}, [accounts])
|
|
||||||
|
|
||||||
const handleRadio = cashoutOrNot => {
|
|
||||||
setCurrentCode(cashoutOrNot)
|
|
||||||
cashoutOrNot === 'disable' && save({})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const save = blockcypher => {
|
const save = blockcypher => {
|
||||||
|
|
@ -67,7 +67,7 @@ const Blockcypher = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<H4>Blockcypher</H4>
|
<H4 className={error && classes.error}>Blockcypher</H4>
|
||||||
<P>
|
<P>
|
||||||
If you are enabling cash-out services,{' '}
|
If you are enabling cash-out services,{' '}
|
||||||
<Link>
|
<Link>
|
||||||
|
|
@ -84,17 +84,26 @@ const Blockcypher = () => {
|
||||||
labelClassName={classes.radioLabel}
|
labelClassName={classes.radioLabel}
|
||||||
className={classes.radioGroup}
|
className={classes.radioGroup}
|
||||||
options={options}
|
options={options}
|
||||||
value={currentCode}
|
value={selected}
|
||||||
onChange={event => handleRadio(event.target.value)}
|
onChange={onSelect}
|
||||||
/>
|
/>
|
||||||
<div className={classes.mdForm}>
|
<div className={classes.mdForm}>
|
||||||
{currentCode === 'enable' && (
|
{selected === 'disable' && (
|
||||||
|
<Button
|
||||||
|
size="lg"
|
||||||
|
onClick={() => addData({ zeroConf: 'all-zero-conf' })}
|
||||||
|
className={classes.button}>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{selected === 'enable' && (
|
||||||
<FormRenderer
|
<FormRenderer
|
||||||
value={accounts.blockcypher}
|
value={accounts.blockcypher}
|
||||||
save={save}
|
save={save}
|
||||||
elements={schema.blockcypher.elements}
|
elements={schema.blockcypher.elements}
|
||||||
validationSchema={schema.blockcypher.validationSchema}
|
validationSchema={schema.blockcypher.validationSchema}
|
||||||
buttonLabel={'Save'}
|
buttonLabel={'Continue'}
|
||||||
|
buttonClass={classes.formButton}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { useQuery } from '@apollo/react-hooks'
|
||||||
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import { Formik, Form, Field } from 'formik'
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
||||||
|
import { Button } from 'src/components/buttons'
|
||||||
|
import { RadioGroup } from 'src/components/inputs/formik'
|
||||||
|
import { H4 } from 'src/components/typography'
|
||||||
|
|
||||||
|
import styles from './Shared.styles'
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
|
const GET_CONFIG = gql`
|
||||||
|
{
|
||||||
|
cryptoCurrencies {
|
||||||
|
code
|
||||||
|
display
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const schema = Yup.object().shape({
|
||||||
|
coin: Yup.string().required()
|
||||||
|
})
|
||||||
|
|
||||||
|
const ChooseCoin = ({ addData }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
const [error, setError] = useState(false)
|
||||||
|
|
||||||
|
const { data } = useQuery(GET_CONFIG)
|
||||||
|
const cryptoCurrencies = data?.cryptoCurrencies ?? []
|
||||||
|
|
||||||
|
const onSubmit = it => {
|
||||||
|
if (!schema.isValidSync(it)) return setError(true)
|
||||||
|
addData(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<H4 className={error && classes.error}>
|
||||||
|
Choose your first cryptocurrency
|
||||||
|
</H4>
|
||||||
|
|
||||||
|
<Formik
|
||||||
|
enableReinitialize
|
||||||
|
initialValues={{ coin: '' }}
|
||||||
|
onSubmit={onSubmit}>
|
||||||
|
<Form onChange={() => setError(false)}>
|
||||||
|
<PromptWhenDirty />
|
||||||
|
<Field
|
||||||
|
component={RadioGroup}
|
||||||
|
name="coin"
|
||||||
|
labelClassName={classes.radioLabel}
|
||||||
|
className={classes.radioGroup}
|
||||||
|
options={cryptoCurrencies}
|
||||||
|
/>
|
||||||
|
{
|
||||||
|
<Button size="lg" type="submit" className={classes.button}>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChooseCoin
|
||||||
|
|
@ -2,26 +2,24 @@ import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState, useEffect, useCallback } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import { ActionButton } from 'src/components/buttons'
|
import { Button, ActionButton } from 'src/components/buttons'
|
||||||
import { RadioGroup } from 'src/components/inputs'
|
import { RadioGroup } from 'src/components/inputs'
|
||||||
import { H4, Info3 } from 'src/components/typography'
|
import { H4, Info3 } from 'src/components/typography'
|
||||||
import FormRenderer from 'src/pages/Services/FormRenderer'
|
import FormRenderer from 'src/pages/Services/FormRenderer'
|
||||||
import schema from 'src/pages/Services/schemas'
|
import schema from 'src/pages/Services/schemas'
|
||||||
import styles from 'src/pages/Wizard/Radio.styles'
|
|
||||||
import { ReactComponent as InverseLinkIcon } from 'src/styling/icons/action/external link/white.svg'
|
import { ReactComponent as InverseLinkIcon } from 'src/styling/icons/action/external link/white.svg'
|
||||||
import { ReactComponent as LinkIcon } from 'src/styling/icons/action/external link/zodiac.svg'
|
import { ReactComponent as LinkIcon } from 'src/styling/icons/action/external link/zodiac.svg'
|
||||||
import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
||||||
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
|
||||||
|
|
||||||
|
import styles from './Shared.styles'
|
||||||
import { getItems } from './getItems'
|
import { getItems } from './getItems'
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const GET_CONFIG = gql`
|
const GET_CONFIG = gql`
|
||||||
{
|
{
|
||||||
config
|
|
||||||
accounts
|
accounts
|
||||||
accountsConfig {
|
accountsConfig {
|
||||||
code
|
code
|
||||||
|
|
@ -36,65 +34,33 @@ const GET_CONFIG = gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const SAVE_CONFIG = gql`
|
|
||||||
mutation Save($config: JSONObject) {
|
|
||||||
saveConfig(config: $config)
|
|
||||||
}
|
|
||||||
`
|
|
||||||
const SAVE_ACCOUNTS = gql`
|
const SAVE_ACCOUNTS = gql`
|
||||||
mutation Save($accounts: JSONObject) {
|
mutation Save($accounts: JSONObject) {
|
||||||
saveAccounts(accounts: $accounts)
|
saveAccounts(accounts: $accounts)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const ChooseExchange = () => {
|
|
||||||
|
const isConfigurable = it => R.contains(it)(['kraken', 'itbit', 'bitstamp'])
|
||||||
|
|
||||||
|
const ChooseExchange = ({ data: currentData, addData }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const { data } = useQuery(GET_CONFIG)
|
const { data } = useQuery(GET_CONFIG)
|
||||||
const [saveConfig] = useMutation(SAVE_CONFIG)
|
const [saveAccounts] = useMutation(SAVE_ACCOUNTS, {
|
||||||
const [saveAccounts] = useMutation(SAVE_ACCOUNTS)
|
onCompleted: () => submit()
|
||||||
const [currentCode, setCurrentCode] = useState(null)
|
})
|
||||||
const [currentName, setCurrentName] = useState(null)
|
|
||||||
|
const [selected, setSelected] = useState(null)
|
||||||
|
const [error, setError] = useState(false)
|
||||||
|
|
||||||
const accounts = data?.accounts ?? []
|
const accounts = data?.accounts ?? []
|
||||||
const accountsConfig = data?.accountsConfig ?? []
|
const accountsConfig = data?.accountsConfig ?? []
|
||||||
|
|
||||||
const walletConfigs =
|
const coin = currentData.coin
|
||||||
data?.config && fromNamespace(namespaces.WALLETS)(data.config)
|
const exchanges = getItems(accountsConfig, accounts, 'exchange', coin)
|
||||||
const wizardCoin =
|
|
||||||
walletConfigs &&
|
|
||||||
Object.keys(walletConfigs).find(
|
|
||||||
k => k.endsWith('_wizard') && walletConfigs[k]
|
|
||||||
)
|
|
||||||
|
|
||||||
const coinCode = wizardCoin && wizardCoin.replace('_wizard', '')
|
const submit = () => {
|
||||||
const exchanges = getItems(accountsConfig, accounts, 'exchange', coinCode)
|
if (!selected) return setError(true)
|
||||||
|
addData({ exchange: selected })
|
||||||
const setCurrName = useCallback(
|
|
||||||
currentCode => {
|
|
||||||
const ex =
|
|
||||||
currentCode &&
|
|
||||||
R.union(exchanges.filled, exchanges.unfilled).find(
|
|
||||||
({ code }) => code === currentCode
|
|
||||||
)
|
|
||||||
setCurrentName(ex && ex.display)
|
|
||||||
},
|
|
||||||
[exchanges]
|
|
||||||
)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (currentCode) return
|
|
||||||
const exchange =
|
|
||||||
walletConfigs && coinCode && fromNamespace(coinCode)(walletConfigs)
|
|
||||||
setCurrentCode(exchange && exchange.exchange)
|
|
||||||
setCurrName(exchange && exchange.exchange)
|
|
||||||
}, [walletConfigs, coinCode, currentCode, setCurrName])
|
|
||||||
|
|
||||||
const save = exchange => {
|
|
||||||
setCurrentCode(exchange)
|
|
||||||
setCurrName(exchange)
|
|
||||||
const config = toNamespace(`wallets_${coinCode}`)({
|
|
||||||
active: true,
|
|
||||||
exchange
|
|
||||||
})
|
|
||||||
return saveConfig({ variables: { config } })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveExchange = name => exchange => {
|
const saveExchange = name => exchange => {
|
||||||
|
|
@ -102,6 +68,11 @@ const ChooseExchange = () => {
|
||||||
return saveAccounts({ variables: { accounts } })
|
return saveAccounts({ variables: { accounts } })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onSelect = e => {
|
||||||
|
setSelected(e.target.value)
|
||||||
|
setError(false)
|
||||||
|
}
|
||||||
|
|
||||||
const supportArticles = {
|
const supportArticles = {
|
||||||
kraken:
|
kraken:
|
||||||
'https://support.lamassu.is/hc/en-us/articles/115001206891-Kraken-trading',
|
'https://support.lamassu.is/hc/en-us/articles/115001206891-Kraken-trading',
|
||||||
|
|
@ -113,22 +84,27 @@ const ChooseExchange = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.mdForm}>
|
<div className={classes.mdForm}>
|
||||||
<H4>Choose your exchange</H4>
|
<H4 className={error && classes.error}>Choose your exchange</H4>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
labelClassName={classes.radioLabel}
|
labelClassName={classes.radioLabel}
|
||||||
className={classes.radioGroup}
|
className={classes.radioGroup}
|
||||||
options={R.union(exchanges.filled, exchanges.unfilled)}
|
options={R.union(exchanges.filled, exchanges.unfilled)}
|
||||||
value={currentCode}
|
value={selected}
|
||||||
onChange={event => save(event.target.value)}
|
onChange={onSelect}
|
||||||
/>
|
/>
|
||||||
{['kraken', 'itbit', 'bitstamp'].includes(currentCode) && (
|
{!isConfigurable(selected) && (
|
||||||
|
<Button size="lg" onClick={submit} className={classes.button}>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{isConfigurable(selected) && (
|
||||||
<>
|
<>
|
||||||
<div className={classes.infoMessage}>
|
<div className={classes.infoMessage}>
|
||||||
<WarningIcon />
|
<WarningIcon />
|
||||||
<Info3>
|
<Info3>
|
||||||
Make sure you set up {currentName} to enter the necessary
|
Make sure you set up {selected} to enter the necessary information
|
||||||
information below. Please follow the instructions on our support
|
below. Please follow the instructions on our support page if you
|
||||||
page if you haven’t.
|
haven’t.
|
||||||
</Info3>
|
</Info3>
|
||||||
</div>
|
</div>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
|
|
@ -140,18 +116,19 @@ const ChooseExchange = () => {
|
||||||
className={classes.actionButtonLink}
|
className={classes.actionButtonLink}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
href={supportArticles[currentCode]}>
|
href={supportArticles[selected]}>
|
||||||
{currentName} trading
|
{selected} trading
|
||||||
</a>
|
</a>
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
|
|
||||||
<H4 noMargin>Enter exchange information</H4>
|
<H4 noMargin>Enter exchange information</H4>
|
||||||
<FormRenderer
|
<FormRenderer
|
||||||
value={accounts[currentCode]}
|
value={accounts[selected]}
|
||||||
save={saveExchange(currentCode)}
|
save={saveExchange(selected)}
|
||||||
elements={schema[currentCode].elements}
|
elements={schema[selected].elements}
|
||||||
validationSchema={schema[currentCode].validationSchema}
|
validationSchema={schema[selected].validationSchema}
|
||||||
buttonLabel={'Confirm'}
|
buttonLabel={'Continue'}
|
||||||
|
buttonClass={classes.formButton}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
import { useQuery } from '@apollo/react-hooks'
|
||||||
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
import * as R from 'ramda'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
import { Button } from 'src/components/buttons'
|
||||||
|
import { RadioGroup } from 'src/components/inputs'
|
||||||
|
import { H4 } from 'src/components/typography'
|
||||||
|
|
||||||
|
import styles from './Shared.styles'
|
||||||
|
import { getItems } from './getItems'
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
|
const GET_CONFIG = gql`
|
||||||
|
{
|
||||||
|
accountsConfig {
|
||||||
|
code
|
||||||
|
display
|
||||||
|
class
|
||||||
|
cryptos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const ChooseTicker = ({ data: currentData, addData }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
const { data } = useQuery(GET_CONFIG)
|
||||||
|
|
||||||
|
const [selected, setSelected] = useState(null)
|
||||||
|
const [error, setError] = useState(false)
|
||||||
|
|
||||||
|
const accounts = data?.accounts ?? []
|
||||||
|
const accountsConfig = data?.accountsConfig ?? []
|
||||||
|
|
||||||
|
const coin = currentData.coin
|
||||||
|
const tickers = getItems(accountsConfig, accounts, 'ticker', coin)
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
if (!selected) return setError(true)
|
||||||
|
addData({ ticker: selected })
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSelect = e => {
|
||||||
|
setSelected(e.target.value)
|
||||||
|
setError(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.mdForm}>
|
||||||
|
<H4 className={error && classes.error}>Choose your ticker</H4>
|
||||||
|
<RadioGroup
|
||||||
|
labelClassName={classes.radioLabel}
|
||||||
|
className={classes.radioGroup}
|
||||||
|
options={R.union(tickers.filled, tickers.unfilled)}
|
||||||
|
value={selected}
|
||||||
|
onChange={onSelect}
|
||||||
|
/>
|
||||||
|
<Button size="lg" onClick={submit} className={classes.button}>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChooseTicker
|
||||||
|
|
@ -2,27 +2,25 @@ import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import { ActionButton } from 'src/components/buttons'
|
import { Button, ActionButton } from 'src/components/buttons'
|
||||||
import { RadioGroup } from 'src/components/inputs'
|
import { RadioGroup } from 'src/components/inputs'
|
||||||
import { H4, Info3 } from 'src/components/typography'
|
import { H4, Info3 } from 'src/components/typography'
|
||||||
import FormRenderer from 'src/pages/Services/FormRenderer'
|
import FormRenderer from 'src/pages/Services/FormRenderer'
|
||||||
import schema from 'src/pages/Services/schemas'
|
import schema from 'src/pages/Services/schemas'
|
||||||
import bitgo from 'src/pages/Services/schemas/singlebitgo'
|
import bitgo from 'src/pages/Services/schemas/singlebitgo'
|
||||||
import styles from 'src/pages/Wizard/Radio.styles'
|
|
||||||
import { ReactComponent as InverseLinkIcon } from 'src/styling/icons/action/external link/white.svg'
|
import { ReactComponent as InverseLinkIcon } from 'src/styling/icons/action/external link/white.svg'
|
||||||
import { ReactComponent as LinkIcon } from 'src/styling/icons/action/external link/zodiac.svg'
|
import { ReactComponent as LinkIcon } from 'src/styling/icons/action/external link/zodiac.svg'
|
||||||
import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
||||||
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
|
||||||
|
|
||||||
|
import styles from './Shared.styles'
|
||||||
import { getItems } from './getItems'
|
import { getItems } from './getItems'
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const GET_CONFIG = gql`
|
const GET_CONFIG = gql`
|
||||||
{
|
{
|
||||||
config
|
|
||||||
accounts
|
accounts
|
||||||
accountsConfig {
|
accountsConfig {
|
||||||
code
|
code
|
||||||
|
|
@ -37,68 +35,80 @@ const GET_CONFIG = gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const SAVE_CONFIG = gql`
|
|
||||||
mutation Save($config: JSONObject) {
|
|
||||||
saveConfig(config: $config)
|
|
||||||
}
|
|
||||||
`
|
|
||||||
const SAVE_ACCOUNTS = gql`
|
const SAVE_ACCOUNTS = gql`
|
||||||
mutation Save($accounts: JSONObject) {
|
mutation Save($accounts: JSONObject) {
|
||||||
saveAccounts(accounts: $accounts)
|
saveAccounts(accounts: $accounts)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const WalletCoin = () => {
|
|
||||||
|
const isConfigurable = it => R.contains(it)(['infura', 'bitgo'])
|
||||||
|
|
||||||
|
const isLocalHosted = it =>
|
||||||
|
R.contains(it)([
|
||||||
|
'bitcoind',
|
||||||
|
'geth',
|
||||||
|
'litecoind',
|
||||||
|
'dashd',
|
||||||
|
'zcashd',
|
||||||
|
'bitcoincashd'
|
||||||
|
])
|
||||||
|
|
||||||
|
const ChooseWallet = ({ data: currentData, addData }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const { data } = useQuery(GET_CONFIG)
|
const { data } = useQuery(GET_CONFIG)
|
||||||
const [saveConfig] = useMutation(SAVE_CONFIG)
|
const [saveAccounts] = useMutation(SAVE_ACCOUNTS, {
|
||||||
const [saveAccounts] = useMutation(SAVE_ACCOUNTS)
|
onCompleted: () => submit()
|
||||||
const [currentCode, setCurrentCode] = useState(null)
|
})
|
||||||
|
|
||||||
|
const [selected, setSelected] = useState(null)
|
||||||
|
const [error, setError] = useState(false)
|
||||||
|
|
||||||
const accounts = data?.accounts ?? []
|
const accounts = data?.accounts ?? []
|
||||||
const accountsConfig = data?.accountsConfig ?? []
|
const accountsConfig = data?.accountsConfig ?? []
|
||||||
|
|
||||||
const walletConfigs =
|
const coin = currentData.coin
|
||||||
data?.config && fromNamespace(namespaces.WALLETS)(data.config)
|
const wallets = getItems(accountsConfig, accounts, 'wallet', coin)
|
||||||
const wizardCoin =
|
|
||||||
walletConfigs &&
|
|
||||||
Object.keys(walletConfigs).find(
|
|
||||||
k => k.endsWith('_wizard') && walletConfigs[k]
|
|
||||||
)
|
|
||||||
|
|
||||||
const coinCode = wizardCoin && wizardCoin.replace('_wizard', '')
|
|
||||||
const wallets = getItems(accountsConfig, accounts, 'wallet', coinCode)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (currentCode) return
|
|
||||||
const wallet =
|
|
||||||
walletConfigs && coinCode && fromNamespace(coinCode)(walletConfigs)
|
|
||||||
setCurrentCode(wallet && wallet.wallet)
|
|
||||||
}, [walletConfigs, coinCode, currentCode])
|
|
||||||
|
|
||||||
const save = wallet => {
|
|
||||||
setCurrentCode(wallet)
|
|
||||||
const config = toNamespace(`wallets_${coinCode}`)({
|
|
||||||
active: true,
|
|
||||||
wallet
|
|
||||||
})
|
|
||||||
return saveConfig({ variables: { config } })
|
|
||||||
}
|
|
||||||
|
|
||||||
const saveWallet = name => wallet => {
|
const saveWallet = name => wallet => {
|
||||||
const accounts = { [name]: wallet }
|
const accounts = { [name]: wallet }
|
||||||
return saveAccounts({ variables: { accounts } })
|
return saveAccounts({ variables: { accounts } })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
if (!selected) return setError(true)
|
||||||
|
addData({ wallet: selected })
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSelect = e => {
|
||||||
|
setSelected(e.target.value)
|
||||||
|
setError(false)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.mdForm}>
|
<div className={classes.mdForm}>
|
||||||
<H4>Choose your wallet</H4>
|
<H4 className={error && classes.error}>Choose your wallet</H4>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
labelClassName={classes.radioLabel}
|
labelClassName={classes.radioLabel}
|
||||||
className={classes.radioGroup}
|
className={classes.radioGroup}
|
||||||
options={R.union(wallets.filled, wallets.unfilled)}
|
options={R.union(wallets.filled, wallets.unfilled)}
|
||||||
value={currentCode}
|
value={selected}
|
||||||
onChange={event => save(event.target.value)}
|
onChange={onSelect}
|
||||||
/>
|
/>
|
||||||
{currentCode === 'bitgo' && (
|
{isLocalHosted(selected) && (
|
||||||
|
<div className={classes.infoMessage}>
|
||||||
|
<WarningIcon />
|
||||||
|
<Info3>
|
||||||
|
To setup {selected} please read our instructions from our support
|
||||||
|
article.
|
||||||
|
</Info3>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!isConfigurable(selected) && (
|
||||||
|
<Button size="lg" onClick={submit} className={classes.button}>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{selected === 'bitgo' && (
|
||||||
<>
|
<>
|
||||||
<div className={classes.infoMessage}>
|
<div className={classes.infoMessage}>
|
||||||
<WarningIcon />
|
<WarningIcon />
|
||||||
|
|
@ -108,48 +118,40 @@ const WalletCoin = () => {
|
||||||
page if you haven’t.
|
page if you haven’t.
|
||||||
</Info3>
|
</Info3>
|
||||||
</div>
|
</div>
|
||||||
<ActionButton
|
<a
|
||||||
className={classes.actionButton}
|
className={classes.actionButtonLink}
|
||||||
color="primary"
|
target="_blank"
|
||||||
Icon={LinkIcon}
|
rel="noopener noreferrer"
|
||||||
InverseIcon={InverseLinkIcon}>
|
href="https://support.lamassu.is/hc/en-us/articles/360024455592-Setting-up-BitGo">
|
||||||
<a
|
<ActionButton
|
||||||
className={classes.actionButtonLink}
|
className={classes.actionButton}
|
||||||
target="_blank"
|
color="primary"
|
||||||
rel="noopener noreferrer"
|
Icon={LinkIcon}
|
||||||
href="https://support.lamassu.is/hc/en-us/articles/360024455592-Setting-up-BitGo">
|
InverseIcon={InverseLinkIcon}>
|
||||||
Support article
|
Support article
|
||||||
</a>
|
</ActionButton>
|
||||||
</ActionButton>
|
</a>
|
||||||
<H4 noMargin>Enter wallet information</H4>
|
<H4 noMargin>Enter wallet information</H4>
|
||||||
<FormRenderer
|
<FormRenderer
|
||||||
value={accounts.bitgo}
|
value={accounts.bitgo}
|
||||||
save={saveWallet(currentCode)}
|
save={saveWallet(selected)}
|
||||||
elements={bitgo(coinCode).elements}
|
elements={bitgo(coin).elements}
|
||||||
validationSchema={bitgo(coinCode).validationSchema}
|
validationSchema={bitgo(coin).validationSchema}
|
||||||
buttonLabel={'Confirm'}
|
buttonLabel={'Continue'}
|
||||||
|
buttonClass={classes.formButton}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{currentCode === 'bitcoind' && (
|
{selected === 'infura' && (
|
||||||
<div className={classes.infoMessage}>
|
|
||||||
<WarningIcon />
|
|
||||||
<Info3>
|
|
||||||
To setup bitcoind please read our instructions from our support
|
|
||||||
article. We’ll later verify if your wallet is correctly set up for
|
|
||||||
you to progress
|
|
||||||
</Info3>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{currentCode === 'infura' && (
|
|
||||||
<>
|
<>
|
||||||
<H4>Enter wallet information</H4>
|
<H4 noMargin>Enter wallet information</H4>
|
||||||
<FormRenderer
|
<FormRenderer
|
||||||
value={accounts.infura}
|
value={accounts.infura}
|
||||||
save={saveWallet(currentCode)}
|
save={saveWallet(selected)}
|
||||||
elements={schema.infura.elements}
|
elements={schema.infura.elements}
|
||||||
validationSchema={schema.infura.validationSchema}
|
validationSchema={schema.infura.validationSchema}
|
||||||
buttonLabel={'Confirm'}
|
buttonLabel={'Continue'}
|
||||||
|
buttonClass={classes.formButton}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
@ -157,4 +159,4 @@ const WalletCoin = () => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default WalletCoin
|
export default ChooseWallet
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { errorColor, spacer, primaryColor } from 'src/styling/variables'
|
||||||
|
|
||||||
|
const LABEL_WIDTH = 150
|
||||||
|
|
||||||
|
export default {
|
||||||
|
radioGroup: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
width: 600
|
||||||
|
},
|
||||||
|
radioLabel: {
|
||||||
|
width: LABEL_WIDTH,
|
||||||
|
height: 48
|
||||||
|
},
|
||||||
|
mdForm: {
|
||||||
|
width: 385
|
||||||
|
},
|
||||||
|
infoMessage: {
|
||||||
|
display: 'flex',
|
||||||
|
marginBottom: 20,
|
||||||
|
'& > p': {
|
||||||
|
width: 330,
|
||||||
|
marginTop: 4,
|
||||||
|
marginLeft: 16
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actionButton: {
|
||||||
|
marginBottom: spacer * 4
|
||||||
|
},
|
||||||
|
actionButtonLink: {
|
||||||
|
textDecoration: 'none',
|
||||||
|
color: primaryColor
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
color: errorColor
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
marginTop: spacer * 5
|
||||||
|
},
|
||||||
|
formButton: {
|
||||||
|
margin: [[spacer * 3, 0, 0]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import * as R from 'ramda'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
import Sidebar, { Stepper } from 'src/components/layout/Sidebar'
|
||||||
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
|
import styles from 'src/pages/AddMachine/styles'
|
||||||
|
|
||||||
|
import AllSet from './AllSet'
|
||||||
|
import Blockcypher from './Blockcypher'
|
||||||
|
import ChooseCoin from './ChooseCoin'
|
||||||
|
import ChooseExchange from './ChooseExchange'
|
||||||
|
import ChooseTicker from './ChooseTicker'
|
||||||
|
import ChooseWallet from './ChooseWallet'
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
|
const steps = [
|
||||||
|
{
|
||||||
|
label: 'Choose cryptocurrency',
|
||||||
|
component: ChooseCoin
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Choose wallet',
|
||||||
|
component: ChooseWallet
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Choose ticker',
|
||||||
|
component: ChooseTicker
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Exchange',
|
||||||
|
component: ChooseExchange
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Blockcypher',
|
||||||
|
component: Blockcypher
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'All set',
|
||||||
|
component: AllSet
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const Wallet = ({ doContinue }) => {
|
||||||
|
const [step, setStep] = useState(0)
|
||||||
|
const [data, setData] = useState({})
|
||||||
|
|
||||||
|
const classes = useStyles()
|
||||||
|
const mySteps = data?.coin === 'BTC' ? steps : R.remove(4, 1, steps)
|
||||||
|
|
||||||
|
const Component = mySteps[step].component
|
||||||
|
|
||||||
|
const addData = it => {
|
||||||
|
setData(R.merge(data, it))
|
||||||
|
setStep(step + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.wrapper}>
|
||||||
|
<div className={classes.headerDiv}>
|
||||||
|
<TitleSection title="Wallet settings"></TitleSection>
|
||||||
|
</div>
|
||||||
|
<div className={classes.contentDiv}>
|
||||||
|
<Sidebar>
|
||||||
|
{mySteps.map((it, idx) => (
|
||||||
|
<Stepper key={idx} step={step} it={it} idx={idx} steps={mySteps} />
|
||||||
|
))}
|
||||||
|
</Sidebar>
|
||||||
|
<div className={classes.contentWrapper}>
|
||||||
|
<Component data={data} addData={addData} doContinue={doContinue} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Wallet
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import React, { useEffect } from 'react'
|
import React from 'react'
|
||||||
import { useHistory } from 'react-router-dom'
|
|
||||||
|
|
||||||
import { Button } from 'src/components/buttons'
|
import { Button } from 'src/components/buttons'
|
||||||
import { H1, P } from 'src/components/typography'
|
import { H1, P } from 'src/components/typography'
|
||||||
|
|
@ -25,16 +24,9 @@ const styles = {
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
function Welcome({ dispatch }) {
|
function Welcome({ doContinue }) {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch({ type: 'wizard/SET_STEP', payload: 'welcome' })
|
|
||||||
}, [dispatch])
|
|
||||||
|
|
||||||
const history = useHistory()
|
|
||||||
const handleClick = () => history.push('/wizard/wallets')
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.welcome}>
|
<div className={classes.welcome}>
|
||||||
<H1 className={classes.title}>Welcome to Lamassu Admin</H1>
|
<H1 className={classes.title}>Welcome to Lamassu Admin</H1>
|
||||||
|
|
@ -43,7 +35,7 @@ function Welcome({ dispatch }) {
|
||||||
<br />
|
<br />
|
||||||
help set up you need before start adding machines.
|
help set up you need before start adding machines.
|
||||||
</P>
|
</P>
|
||||||
<Button size={'xl'} onClick={handleClick}>
|
<Button size="xl" onClick={doContinue}>
|
||||||
Get started
|
Get started
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
128
new-lamassu-admin/src/pages/Wizard/helper.js
Normal file
128
new-lamassu-admin/src/pages/Wizard/helper.js
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
import * as R from 'ramda'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import { schema as CommissionsSchema } from 'src/pages/Commissions/helper'
|
||||||
|
import { LocaleSchema } from 'src/pages/Locales/helper'
|
||||||
|
import { WalletSchema } from 'src/pages/Wallet/helper'
|
||||||
|
import { fromNamespace, namespaces } from 'src/utils/config'
|
||||||
|
|
||||||
|
import Commissions from './components/Commissions'
|
||||||
|
import Locale from './components/Locales'
|
||||||
|
// import Notifications from './components/Notifications'
|
||||||
|
// import WizardOperatorInfo from './components/OperatorInfo'
|
||||||
|
import Twilio from './components/Twilio'
|
||||||
|
import Wallet from './components/Wallet/Wallet'
|
||||||
|
import Welcome from './components/Welcome'
|
||||||
|
|
||||||
|
const hasValidWallet = (config, crypto) => {
|
||||||
|
const wallet = fromNamespace(namespaces.WALLETS, config)
|
||||||
|
const coins = R.map(it => fromNamespace(it.code, wallet))(crypto)
|
||||||
|
|
||||||
|
const hasValidConfig = R.compose(
|
||||||
|
R.any(R.identity),
|
||||||
|
R.map(it => WalletSchema.isValidSync(it))
|
||||||
|
)(coins)
|
||||||
|
|
||||||
|
return hasValidConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasValidLocale = config => {
|
||||||
|
const locale = fromNamespace(namespaces.LOCALE, config)
|
||||||
|
return LocaleSchema.isValidSync(locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasValidCommissions = config => {
|
||||||
|
const commission = fromNamespace(namespaces.COMMISSIONS, config)
|
||||||
|
return CommissionsSchema.isValidSync(commission)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getWizardStep = (config, crypto) => {
|
||||||
|
if (!config) return 0
|
||||||
|
|
||||||
|
const validWallet = hasValidWallet(config, crypto)
|
||||||
|
if (!validWallet) return 1
|
||||||
|
|
||||||
|
const validLocale = hasValidLocale(config)
|
||||||
|
if (!validLocale) return 2
|
||||||
|
|
||||||
|
const validCommission = hasValidCommissions(config)
|
||||||
|
if (!validCommission) return 3
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const STEPS = [
|
||||||
|
{
|
||||||
|
id: 'welcome',
|
||||||
|
Component: Welcome
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'wallet',
|
||||||
|
Component: Wallet,
|
||||||
|
exImage: '/fullexample.wallet.png',
|
||||||
|
subtitle: 'Wallet settings',
|
||||||
|
text: `Your wallet settings are the first step for this wizard. We'll start
|
||||||
|
by setting one of cryptocurrency to get you up and running, but you
|
||||||
|
can later setup as many cryptocurrencies as you want.`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'locale',
|
||||||
|
Component: Locale,
|
||||||
|
exImage: '/fullexample.locale.png',
|
||||||
|
subtitle: 'Locales',
|
||||||
|
text: `From the Locales page, you can define some important default settings
|
||||||
|
of your machines. These values will be the default values of all
|
||||||
|
machines you'll later add to your network. Default settings keep you
|
||||||
|
from having to enther the same values everytime you add a new machine.
|
||||||
|
Once a machine is added, you may override some of these values in the
|
||||||
|
overrides section.`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'twilio',
|
||||||
|
Component: Twilio,
|
||||||
|
exImage: '/fullexample.twilio.png',
|
||||||
|
subtitle: 'Twilio (SMS service)',
|
||||||
|
text: (
|
||||||
|
<>
|
||||||
|
Twilio is used for SMS operator notifications, phone number collection
|
||||||
|
for compliance, and 1-confirmation redemptions on cash-out transactions.
|
||||||
|
<br />
|
||||||
|
You'll need to configure Twilio if you're offering cash-out or any
|
||||||
|
compliance options
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'commissions',
|
||||||
|
Component: Commissions,
|
||||||
|
exImage: '/fullexample.commissions.png',
|
||||||
|
subtitle: 'Commissions',
|
||||||
|
text: `From the Commissions page, you can define all the commissions of your
|
||||||
|
machines. The values set here will be default values of all machines
|
||||||
|
you'll later add to your network. Default settings keep you from
|
||||||
|
having to enter the same values everytime you add a new machine. Once
|
||||||
|
a machine is added, you may override these values per machine and per
|
||||||
|
cryptocurrency in the overrides section.`
|
||||||
|
}
|
||||||
|
// {
|
||||||
|
// id: 'notifications',
|
||||||
|
// Component: Notifications,
|
||||||
|
// exImage: '/fullexample.notifications.png',
|
||||||
|
// subtitle: 'Notifications',
|
||||||
|
// text: `Your notification settings will allow customize what notifications you
|
||||||
|
// get and where. You can later override all default balance alerts setup
|
||||||
|
// here.`
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// id: 'operatorInfo',
|
||||||
|
// Component: WizardOperatorInfo,
|
||||||
|
// exImage: '/fullexample.operatorinfo.png',
|
||||||
|
// subtitle: 'Operator info',
|
||||||
|
// text: `Your contact information is important for your customer to be able
|
||||||
|
// to contact you in case there’s a problem with one of your machines.
|
||||||
|
// In this page, you also be able to set up what you want to share with
|
||||||
|
// Coin ATM Radar and add the Terms & Services text that is displayed by your machines.`
|
||||||
|
// }
|
||||||
|
]
|
||||||
|
|
||||||
|
export { getWizardStep, STEPS }
|
||||||
|
|
@ -13,17 +13,11 @@ import extendJss from 'jss-plugin-extend'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { ActionButton, Button, Link } from 'src/components/buttons'
|
import { ActionButton, Button, Link } from 'src/components/buttons'
|
||||||
import {
|
import { TextInput, Switch, CashIn, CashOut } from 'src/components/inputs'
|
||||||
Radio,
|
|
||||||
TextInput,
|
|
||||||
Switch,
|
|
||||||
CashIn,
|
|
||||||
CashOut
|
|
||||||
} from 'src/components/inputs'
|
|
||||||
import { ReactComponent as AuthorizeIconReversed } from 'src/styling/icons/button/authorize/white.svg'
|
import { ReactComponent as AuthorizeIconReversed } from 'src/styling/icons/button/authorize/white.svg'
|
||||||
import { ReactComponent as AuthorizeIcon } from 'src/styling/icons/button/authorize/zodiac.svg'
|
import { ReactComponent as AuthorizeIcon } from 'src/styling/icons/button/authorize/zodiac.svg'
|
||||||
|
|
||||||
import ConfirmDialog from '../components/ConfirmDialog'
|
// import ConfirmDialog from '../components/ConfirmDialog'
|
||||||
import {
|
import {
|
||||||
H1,
|
H1,
|
||||||
H2,
|
H2,
|
||||||
|
|
@ -169,21 +163,6 @@ story.add('Checkbox', () => (
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
))
|
))
|
||||||
|
|
||||||
story.add('ConfirmDialog', () => (
|
|
||||||
<Wrapper>
|
|
||||||
<ConfirmDialog
|
|
||||||
open={boolean('open', true)}
|
|
||||||
onDissmised={() => {
|
|
||||||
window.alert('dissmised')
|
|
||||||
}}
|
|
||||||
onConfirmed={() => {
|
|
||||||
window.alert('confirmed')
|
|
||||||
}}
|
|
||||||
toBeConfirmed="there-is-no-fate"
|
|
||||||
/>
|
|
||||||
</Wrapper>
|
|
||||||
))
|
|
||||||
|
|
||||||
story.add('Cashbox', () => (
|
story.add('Cashbox', () => (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -211,8 +190,6 @@ story.add('Cashbox', () => (
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
))
|
))
|
||||||
|
|
||||||
story.add('Radio', () => <Radio label="Hehe" />)
|
|
||||||
|
|
||||||
const typographyStory = storiesOf('Typography', module)
|
const typographyStory = storiesOf('Typography', module)
|
||||||
typographyStory.add('H1', () => <H1>Hehehe</H1>)
|
typographyStory.add('H1', () => <H1>Hehehe</H1>)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue