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 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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
]
|
||||
}
|
||||
19997
new-lamassu-admin/package-lock.json
generated
19997
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",
|
||||
"downshift": "3.3.4",
|
||||
"file-saver": "2.0.2",
|
||||
"formik": "2.1.4",
|
||||
"fuse.js": "^3.4.6",
|
||||
"formik": "2.2.0",
|
||||
"graphql": "^14.5.8",
|
||||
"graphql-tag": "^2.10.3",
|
||||
"jss-plugin-extend": "^10.0.0",
|
||||
|
|
@ -35,17 +34,17 @@
|
|||
"react-router-dom": "5.1.2",
|
||||
"react-virtualized": "^9.21.2",
|
||||
"sanctuary": "^2.0.1",
|
||||
"slugify": "^1.3.6",
|
||||
"uuid": "^7.0.2",
|
||||
"yup": "0.27.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-actions": "^5.2.3",
|
||||
"@storybook/addon-backgrounds": "^5.2.4",
|
||||
"@storybook/addon-knobs": "^5.2.3",
|
||||
"@storybook/addon-links": "^5.2.3",
|
||||
"@storybook/addons": "^5.2.3",
|
||||
"@storybook/react": "^5.2.3",
|
||||
"@storybook/addon-actions": "6.0.26",
|
||||
"@storybook/addon-backgrounds": "6.0.26",
|
||||
"@storybook/addon-knobs": "6.0.26",
|
||||
"@storybook/addon-links": "6.0.26",
|
||||
"@storybook/addons": "6.0.26",
|
||||
"@storybook/preset-create-react-app": "^3.1.4",
|
||||
"@storybook/react": "6.0.26",
|
||||
"@welldone-software/why-did-you-render": "^3.3.9",
|
||||
"eslint-config-prettier": "^6.7.0",
|
||||
"eslint-config-prettier-standard": "^3.0.1",
|
||||
|
|
@ -61,8 +60,8 @@
|
|||
"lint-staged": "^9.5.0",
|
||||
"prettier": "1.19.1",
|
||||
"prettier-config-standard": "^1.0.1",
|
||||
"react-scripts": "^3.3.0",
|
||||
"serve": "^11.2.0",
|
||||
"react-scripts": "^3.4.3",
|
||||
"serve": "^11.3.2",
|
||||
"source-map-explorer": "^2.4.2"
|
||||
},
|
||||
"husky": {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
with import (fetchTarball {
|
||||
name = "nixpkgs-19.09";
|
||||
url = https://github.com/NixOS/nixpkgs-channels/archive/d5291756487d70bc336e33512a9baf9fa1788faf.tar.gz;
|
||||
sha256 = "0mhqhq21y5vrr1f30qd2bvydv4bbbslvyzclhw0kdxmkgg3z4c92";
|
||||
name = "nixpkgs-20.09";
|
||||
url = https://github.com/NixOS/nixpkgs/archive/0b8799ecaaf0dc6b4c11583a3c96ca5b40fcfdfb.tar.gz;
|
||||
sha256 = "11m4aig6cv0zi3gbq2xn9by29cfvnsxgzf9qsvz67qr0yq29ryyz";
|
||||
}) {};
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "node";
|
||||
buildInputs = [
|
||||
nodejs-12_x
|
||||
nodejs-14_x
|
||||
];
|
||||
shellHook = ''
|
||||
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 {
|
||||
StylesProvider,
|
||||
|
|
@ -6,12 +6,14 @@ import {
|
|||
MuiThemeProvider,
|
||||
makeStyles
|
||||
} from '@material-ui/core/styles'
|
||||
import { setAutoFreeze } from 'immer'
|
||||
import gql from 'graphql-tag'
|
||||
import { create } from 'jss'
|
||||
import extendJss from 'jss-plugin-extend'
|
||||
import React from 'react'
|
||||
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 Header from './components/layout/Header'
|
||||
|
|
@ -25,9 +27,6 @@ if (process.env.NODE_ENV !== 'production') {
|
|||
whyDidYouRender(React)
|
||||
}
|
||||
|
||||
// disable immer autofreeze for performance
|
||||
setAutoFreeze(false)
|
||||
|
||||
const jss = create({
|
||||
plugins: [extendJss(), ...jssPreset().plugins]
|
||||
})
|
||||
|
|
@ -54,22 +53,49 @@ const useStyles = makeStyles({
|
|||
}
|
||||
})
|
||||
|
||||
const App = () => {
|
||||
const GET_DATA = gql`
|
||||
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 (
|
||||
<ApolloProvider client={client}>
|
||||
<StylesProvider jss={jss}>
|
||||
<MuiThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<div className={classes.root}>
|
||||
<Router>
|
||||
{wizardStep && <Wizard wizardStep={wizardStep} />}
|
||||
<Header tree={tree} />
|
||||
<main className={classes.wrapper}>
|
||||
<Routes />
|
||||
</main>
|
||||
</Router>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<ApolloProvider client={client}>
|
||||
<StylesProvider jss={jss}>
|
||||
<MuiThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<Main />
|
||||
</MuiThemeProvider>
|
||||
</StylesProvider>
|
||||
</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,
|
||||
onToggle,
|
||||
toggleWidth,
|
||||
forceAdd,
|
||||
actionColSize
|
||||
} = useContext(TableCtx)
|
||||
|
||||
|
|
@ -49,9 +50,11 @@ const ActionCol = ({ disabled, editing }) => {
|
|||
onClick={submitForm}>
|
||||
Save
|
||||
</Link>
|
||||
{!forceAdd && (
|
||||
<Link color="secondary" onClick={resetForm}>
|
||||
Cancel
|
||||
</Link>
|
||||
)}
|
||||
</Td>
|
||||
)}
|
||||
{!editing && enableEdit && (
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { makeStyles } from '@material-ui/core'
|
||||
import { Form, Formik } from 'formik'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { v4 } from 'uuid'
|
||||
|
||||
import PromptWhenDirty from 'src/components/PromptWhenDirty'
|
||||
|
|
@ -51,12 +51,13 @@ const ETable = ({
|
|||
groupBy,
|
||||
sortBy,
|
||||
createText = 'Add override',
|
||||
outerEditingId = null
|
||||
forceAdd = false
|
||||
}) => {
|
||||
const [editingId, setEditingId] = useState(null)
|
||||
const [adding, setAdding] = useState(false)
|
||||
const [saving, setSaving] = useState(false)
|
||||
|
||||
useEffect(() => setAdding(forceAdd), [forceAdd])
|
||||
const innerSave = async value => {
|
||||
if (saving) return
|
||||
|
||||
|
|
@ -88,7 +89,6 @@ const ETable = ({
|
|||
|
||||
const onReset = () => {
|
||||
setAdding(false)
|
||||
setEditingId(outerEditingId)
|
||||
setEditing && setEditing(false)
|
||||
}
|
||||
|
||||
|
|
@ -136,6 +136,7 @@ const ETable = ({
|
|||
toggleWidth,
|
||||
actionColSize,
|
||||
stripeWhen,
|
||||
forceAdd,
|
||||
DEFAULT_COL_SIZE
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@ import { makeStyles } from '@material-ui/core/styles'
|
|||
import classnames from 'classnames'
|
||||
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'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
|
@ -38,3 +42,35 @@ const 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': {
|
||||
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 { Dialog, DialogContent, SvgIcon, IconButton } from '@material-ui/core'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import classnames from 'classnames'
|
||||
import { Form, Formik, FastField } from 'formik'
|
||||
import gql from 'graphql-tag'
|
||||
import QRCode from 'qrcode.react'
|
||||
|
|
@ -11,12 +10,9 @@ import * as Yup from 'yup'
|
|||
import Title from 'src/components/Title'
|
||||
import { Button } from 'src/components/buttons'
|
||||
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 { 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 { 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 classes = useStyles()
|
||||
const [qrCode, setQrCode] = useState(null)
|
||||
|
|
@ -179,7 +146,9 @@ const AddMachine = memo(({ close }) => {
|
|||
</div>
|
||||
<div className={classes.contentDiv}>
|
||||
<Sidebar>
|
||||
{steps.map((it, idx) => renderStepper(step, it, idx, classes))}
|
||||
{steps.map((it, idx) => (
|
||||
<Stepper step={step} it={it} idx={idx} steps={steps} />
|
||||
))}
|
||||
</Sidebar>
|
||||
<div className={classes.contentWrapper}>
|
||||
<Component
|
||||
|
|
|
|||
|
|
@ -1,12 +1,4 @@
|
|||
import typographyStyles from 'src/components/typography/styles'
|
||||
import {
|
||||
placeholderColor,
|
||||
backgroundColor,
|
||||
primaryColor,
|
||||
mainWidth
|
||||
} from 'src/styling/variables'
|
||||
|
||||
const { tl2, p } = typographyStyles
|
||||
import { backgroundColor, mainWidth } from 'src/styling/variables'
|
||||
|
||||
const fill = '100%'
|
||||
const flexDirection = 'column'
|
||||
|
|
@ -65,34 +57,6 @@ const styles = {
|
|||
},
|
||||
qrText: {
|
||||
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
|
||||
|
||||
// TODO: what about 'onlySetup' 'onlyFiat'?
|
||||
const Notifications = ({
|
||||
name: SCREEN_KEY,
|
||||
displaySetup = true,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { makeStyles, Grid } from '@material-ui/core'
|
||||
import classnames from 'classnames'
|
||||
import { Formik, Form, FastField } from 'formik'
|
||||
import * as R from 'ramda'
|
||||
import React from 'react'
|
||||
|
|
@ -27,6 +28,7 @@ const FormRenderer = ({
|
|||
value,
|
||||
save,
|
||||
buttonLabel = 'Save changes',
|
||||
buttonClass,
|
||||
xs = 12
|
||||
}) => {
|
||||
const classes = useStyles()
|
||||
|
|
@ -58,7 +60,9 @@ const FormRenderer = ({
|
|||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
<Button className={classes.button} type="submit">
|
||||
<Button
|
||||
className={classnames(classes.button, buttonClass)}
|
||||
type="submit">
|
||||
{buttonLabel}
|
||||
</Button>
|
||||
</Form>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import * as Yup from 'yup'
|
||||
|
||||
import {
|
||||
TextInput,
|
||||
SecretInput,
|
||||
Autocomplete
|
||||
} from 'src/components/inputs/formik'
|
||||
|
||||
import bitgo from './bitgo'
|
||||
|
||||
export default code => ({
|
||||
code: 'bitgo',
|
||||
name: 'BitGo',
|
||||
|
|
@ -28,15 +28,28 @@ export default code => ({
|
|||
face: true
|
||||
},
|
||||
{
|
||||
code: `${code.toLowerCase()}WalletId`,
|
||||
display: `${code.toUpperCase()} Wallet ID`,
|
||||
code: `${code}WalletId`,
|
||||
display: `${code} Wallet ID`,
|
||||
component: TextInput
|
||||
},
|
||||
{
|
||||
code: `${code.toLowerCase()}WalletPassphrase`,
|
||||
display: `${code.toUpperCase()} Wallet Passphrase`,
|
||||
code: `${code}WalletPassphrase`,
|
||||
display: `${code} Wallet Passphrase`,
|
||||
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 classnames from 'classnames'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useReducer, useEffect } from 'react'
|
||||
import { Switch, Route, useRouteMatch } from 'react-router-dom'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import Twilio from 'src/pages/Wizard/components/Twilio'
|
||||
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 Locales from './components/Locales'
|
||||
import N from './components/Notifications'
|
||||
import WizardOperatorInfo from './components/OperatorInfo'
|
||||
import Wallet from './components/Wallet'
|
||||
import Welcome from './components/Welcome'
|
||||
import { STEPS } from './helper'
|
||||
|
||||
const useStyles = makeStyles({
|
||||
wrapper: {
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
padding: '1rem 0',
|
||||
padding: [[16, 0]],
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between'
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: backgroundColor
|
||||
},
|
||||
welcomeBackground: {
|
||||
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`
|
||||
query getData {
|
||||
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 Wizard = ({ wizardStep }) => {
|
||||
const [step, setStep] = useState(0)
|
||||
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 (
|
||||
<Dialog fullScreen open={true}>
|
||||
<DialogContent
|
||||
className={classnames(
|
||||
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>
|
||||
<Dialog fullScreen open={open}>
|
||||
<DialogContent className={classnames(classNames)}>
|
||||
<current.Component doContinue={doContinue} isActive={!footerExp} />
|
||||
</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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useEffect } from 'react'
|
||||
import React from 'react'
|
||||
|
||||
import { Table as EditableTable } from 'src/components/editableTable'
|
||||
import Section from 'src/components/layout/Section'
|
||||
|
|
@ -16,7 +16,6 @@ const useStyles = makeStyles(styles)
|
|||
const GET_DATA = gql`
|
||||
query getData {
|
||||
config
|
||||
accounts
|
||||
}
|
||||
`
|
||||
const SAVE_CONFIG = gql`
|
||||
|
|
@ -25,31 +24,17 @@ const SAVE_CONFIG = gql`
|
|||
}
|
||||
`
|
||||
|
||||
function Commissions({ dispatch, namespace }) {
|
||||
function Commissions({ isActive, doContinue }) {
|
||||
const classes = useStyles()
|
||||
const { data, refetch } = useQuery(GET_DATA)
|
||||
const { data } = useQuery(GET_DATA)
|
||||
|
||||
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 config = toNamespace(namespace)(it[namespace][0])
|
||||
const config = toNamespace('commissions')(it.commissions[0])
|
||||
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'])(
|
||||
|
|
@ -65,11 +50,12 @@ function Commissions({ dispatch, namespace }) {
|
|||
rowSize="lg"
|
||||
titleLg
|
||||
name="commissions"
|
||||
outerEditingId={1}
|
||||
initialValues={defaults}
|
||||
enableEdit
|
||||
forceAdd={isActive}
|
||||
save={save}
|
||||
validationSchema={schema}
|
||||
data={R.of({ ...values, id: 1 })}
|
||||
data={[]}
|
||||
elements={mainFields(currency)}
|
||||
/>
|
||||
</Section>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
import { makeStyles, Drawer, Grid } from '@material-ui/core'
|
||||
import ClickAwayListener from '@material-ui/core/ClickAwayListener'
|
||||
import classnames from 'classnames'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
|
||||
import Modal from 'src/components/Modal'
|
||||
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 { 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(() => ({
|
||||
drawer: {
|
||||
borderTop: 'none',
|
||||
|
|
@ -47,35 +36,23 @@ 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 history = useHistory()
|
||||
const [open, setOpen] = useState(true)
|
||||
const [fullExample, setFullExample] = useState(false)
|
||||
|
||||
const handleClick = () => history.push(`${path}/${next}`)
|
||||
const handleClickAway = () => setOpen(false)
|
||||
const { currentStep, steps } = getStepperProps(current, collection)
|
||||
|
||||
const wrapperClassNames = {
|
||||
[classes.wrapper]: true,
|
||||
[classes.smallWrapper]: !open
|
||||
}
|
||||
|
||||
return (
|
||||
<ClickAwayListener onClickAway={handleClickAway}>
|
||||
<Drawer
|
||||
onClick={() => setOpen(true)}
|
||||
anchor={'bottom'}
|
||||
open={true}
|
||||
variant={'persistent'}
|
||||
classes={{ paperAnchorDockedBottom: classes.drawer }}>
|
||||
<div className={classnames(wrapperClassNames)}>
|
||||
<Grid
|
||||
container
|
||||
direction="row"
|
||||
justify="center"
|
||||
alignItems="baseline">
|
||||
<Grid container direction="row" justify="center" alignItems="baseline">
|
||||
<Grid
|
||||
item
|
||||
xs={5}
|
||||
|
|
@ -84,8 +61,8 @@ function Footer({ next, current, steps: collection, path, tag, p }) {
|
|||
justify="flex-start"
|
||||
alignItems="baseline">
|
||||
<H2 className={classes.title}>Setup Lamassu Admin</H2>
|
||||
<Info2 className={classes.subtitle}>{tag}</Info2>
|
||||
{open && <P>{p}</P>}
|
||||
<Info2 className={classes.subtitle}>{subtitle}</Info2>
|
||||
{open && <P>{text}</P>}
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
|
|
@ -97,7 +74,7 @@ function Footer({ next, current, steps: collection, path, tag, p }) {
|
|||
spacing={5}>
|
||||
<Grid item xs={12}>
|
||||
{steps && currentStep && (
|
||||
<Stepper {...{ currentStep, steps }}></Stepper>
|
||||
<Stepper currentStep={currentStep} steps={steps} />
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
|
@ -131,8 +108,8 @@ function Footer({ next, current, steps: collection, path, tag, p }) {
|
|||
alignItems="flex-end"
|
||||
spacing={5}>
|
||||
<Grid item>
|
||||
<Button size="lg" disabled={!next} onClick={handleClick}>
|
||||
Continue
|
||||
<Button size="lg" onClick={start}>
|
||||
Get Started
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
|
@ -149,14 +126,9 @@ function Footer({ next, current, steps: collection, path, tag, p }) {
|
|||
setFullExample(false)
|
||||
}}
|
||||
open={fullExample}>
|
||||
<img
|
||||
width={1152}
|
||||
src={`/fullexample.${current}.png`}
|
||||
alt={`${current} configuration example`}
|
||||
/>
|
||||
<img width={1152} src={exImage} alt="" />
|
||||
</Modal>
|
||||
</Drawer>
|
||||
</ClickAwayListener>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
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, { useEffect } from 'react'
|
||||
import React from 'react'
|
||||
|
||||
import { Table as EditableTable } from 'src/components/editableTable'
|
||||
import Section from 'src/components/layout/Section'
|
||||
|
|
@ -13,7 +12,7 @@ import {
|
|||
localeDefaults as defaults,
|
||||
LocaleSchema as schema
|
||||
} from 'src/pages/Locales/helper'
|
||||
import { fromNamespace, toNamespace } from 'src/utils/config'
|
||||
import { toNamespace } from 'src/utils/config'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
|
|
@ -50,31 +49,17 @@ const SAVE_CONFIG = gql`
|
|||
}
|
||||
`
|
||||
|
||||
function Locales({ dispatch, namespace }) {
|
||||
function Locales({ isActive, doContinue }) {
|
||||
const classes = useStyles()
|
||||
const { data, refetch } = useQuery(GET_DATA)
|
||||
const { data } = useQuery(GET_DATA)
|
||||
|
||||
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 config = toNamespace(namespace)(it[namespace][0])
|
||||
const config = toNamespace('locale')(it.locale[0])
|
||||
return saveConfig({ variables: { config } })
|
||||
.then(() => refetch())
|
||||
.then(({ data }) => {
|
||||
return dispatch({
|
||||
type: 'wizard/VALIDATE_STEP',
|
||||
payload: { accounts: data.accounts, config: data.config }
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -86,12 +71,12 @@ function Locales({ dispatch, namespace }) {
|
|||
rowSize="lg"
|
||||
titleLg
|
||||
name="locale"
|
||||
initialValues={locale}
|
||||
outerEditingId={1}
|
||||
initialValues={defaults}
|
||||
forceAdd={isActive}
|
||||
enableEdit
|
||||
save={save}
|
||||
validationSchema={schema}
|
||||
data={R.of({ ...locale, id: 1 })}
|
||||
data={[]}
|
||||
elements={mainFields(data)}
|
||||
/>
|
||||
</Section>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { makeStyles } from '@material-ui/core'
|
||||
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 TitleSection from 'src/components/layout/TitleSection'
|
||||
|
|
@ -38,10 +38,7 @@ const pages = [
|
|||
CRYPTO_BALANCE_ALERTS
|
||||
]
|
||||
|
||||
const N = ({ dispatch, namespace }) => {
|
||||
useEffect(() => {
|
||||
dispatch({ type: 'wizard/SET_STEP', payload: namespace })
|
||||
}, [dispatch, namespace])
|
||||
const N = () => {
|
||||
const [selected, setSelected] = useState(EMAIL)
|
||||
const classes = useStyles()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,13 @@
|
|||
import { makeStyles } from '@material-ui/core'
|
||||
import React, { useEffect } from 'react'
|
||||
import React from 'react'
|
||||
|
||||
import styles from 'src/pages/AddMachine/styles'
|
||||
import OperatorInfo from 'src/pages/OperatorInfo/OperatorInfo'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
function WizardOperatorInfo({ dispatch, namespace }) {
|
||||
function WizardOperatorInfo() {
|
||||
const classes = useStyles()
|
||||
useEffect(() => {
|
||||
dispatch({ type: 'wizard/SET_STEP', payload: namespace })
|
||||
}, [dispatch, namespace])
|
||||
|
||||
return (
|
||||
<div className={classes.wrapper}>
|
||||
|
|
|
|||
|
|
@ -1,23 +1,18 @@
|
|||
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 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 { IconButton } from 'src/components/buttons'
|
||||
import { Button } from 'src/components/buttons'
|
||||
import { RadioGroup } from 'src/components/inputs'
|
||||
import { H1, Label1, 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 { H1, H4, P } from 'src/components/typography'
|
||||
import FormRenderer from 'src/pages/Services/FormRenderer'
|
||||
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 { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
||||
|
||||
import styles from './Wallet/Shared.styles'
|
||||
|
||||
const GET_CONFIG = gql`
|
||||
{
|
||||
|
|
@ -34,23 +29,32 @@ const SAVE_ACCOUNTS = gql`
|
|||
|
||||
const useStyles = makeStyles({
|
||||
...styles,
|
||||
...globalStyles,
|
||||
...contactInfoStyles,
|
||||
...addMachineStyles,
|
||||
content: {
|
||||
width: 820,
|
||||
flex: 0
|
||||
width: 820
|
||||
},
|
||||
radioLabel: {
|
||||
...styles.radioLabel,
|
||||
width: 280
|
||||
},
|
||||
wrapper: {
|
||||
width: 1200,
|
||||
height: 100,
|
||||
margin: [[0, 'auto']]
|
||||
},
|
||||
title: {
|
||||
marginLeft: 8,
|
||||
marginBottom: 5
|
||||
},
|
||||
info: {
|
||||
marginTop: 20,
|
||||
marginBottom: 20
|
||||
}
|
||||
})
|
||||
|
||||
const options = [
|
||||
{
|
||||
code: 'enable',
|
||||
display: 'Yes, I will add two-way machines'
|
||||
display: 'Yes, I will'
|
||||
},
|
||||
{
|
||||
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 [saveAccounts] = useMutation(SAVE_ACCOUNTS)
|
||||
const [saveAccounts] = useMutation(SAVE_ACCOUNTS, {
|
||||
onCompleted: doContinue
|
||||
})
|
||||
|
||||
const accounts = data?.accounts ?? []
|
||||
|
||||
const [enable, setEnable] = useState('disable')
|
||||
|
||||
useEffect(() => {
|
||||
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 })
|
||||
const onSelect = e => {
|
||||
setSelected(e.target.value)
|
||||
setError(false)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch({ type: 'wizard/SET_STEP', payload: namespace })
|
||||
}, [dispatch, namespace])
|
||||
const classes = useStyles()
|
||||
const clickContinue = () => {
|
||||
if (!selected) return setError(true)
|
||||
doContinue()
|
||||
}
|
||||
|
||||
const save = twilio => {
|
||||
const accounts = { twilio }
|
||||
return saveAccounts({ variables: { accounts } })
|
||||
.then(() => refetch())
|
||||
.then(({ data }) => {
|
||||
return dispatch({
|
||||
type: 'wizard/VALIDATE_STEP',
|
||||
payload: { accounts: data.accounts, config: data.config }
|
||||
})
|
||||
})
|
||||
return saveAccounts({ variables: { accounts } }).then(() => refetch())
|
||||
}
|
||||
|
||||
const titleClasses = {
|
||||
[classes.title]: true,
|
||||
[classes.error]: error
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classes.content}>
|
||||
<H1>Twilio (SMS service)</H1>
|
||||
<H4>
|
||||
Will you setup a two way machine?
|
||||
<Tooltip width={304} enableClick Button={IconButton} Icon={HelpIcon}>
|
||||
<Box display="flex" alignItems="end">
|
||||
<H4 noMargin className={classnames(titleClasses)}>
|
||||
Will you setup a two way machine or compliance?
|
||||
</H4>
|
||||
<Tooltip width={304}>
|
||||
<P>
|
||||
Two-way machines allow your customers not only to buy (cash-in)
|
||||
but also sell cryptocurrencies (cash-out).
|
||||
</P>
|
||||
<P>
|
||||
To get your admin up and running, you’ll only need an SMS service
|
||||
for cash-out transactions. If you’re using one-way machines,
|
||||
select “No” to skip this step for now. You can later set it up
|
||||
within the Lamassu Admin.
|
||||
You’ll need an SMS service for cash-out transactions and for any
|
||||
complaince triggers
|
||||
</P>
|
||||
</Tooltip>
|
||||
</H4>
|
||||
</Box>
|
||||
|
||||
<RadioGroup
|
||||
labelClassName={classes.radioLabel}
|
||||
className={classes.radioGroup}
|
||||
options={options}
|
||||
value={enable}
|
||||
onChange={event => handleRadio(event.target.value)}
|
||||
value={selected}
|
||||
onChange={onSelect}
|
||||
/>
|
||||
|
||||
<div className={classnames(classes.section, classes.infoMessage)}>
|
||||
<WarningIcon />
|
||||
<Label1>
|
||||
<InfoMessage className={classes.info}>
|
||||
Before configuring Twilio, create an account and phone number to use
|
||||
the Admin.
|
||||
</Label1>
|
||||
</div>
|
||||
{enable === 'enable' && (
|
||||
</InfoMessage>
|
||||
|
||||
{selected === 'enable' && (
|
||||
<>
|
||||
<H4 noMargin>Enter credentials</H4>
|
||||
<FormRenderer
|
||||
xs={6}
|
||||
save={save}
|
||||
value={accounts.twilio}
|
||||
elements={twilio.elements}
|
||||
validationSchema={twilio.validationSchema}
|
||||
buttonLabel={'Save'}
|
||||
buttonLabel={'Continue'}
|
||||
buttonClass={classes.formButton}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{selected !== 'enable' && (
|
||||
<Button size="lg" onClick={clickContinue} className={classes.button}>
|
||||
Continue
|
||||
</Button>
|
||||
)}
|
||||
</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 { makeStyles } from '@material-ui/core'
|
||||
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 { P, H4 } from 'src/components/typography'
|
||||
import FormRenderer from 'src/pages/Services/FormRenderer'
|
||||
import schema from 'src/pages/Services/schemas'
|
||||
import styles from 'src/pages/Wizard/Radio.styles'
|
||||
|
||||
import styles from './Shared.styles'
|
||||
|
||||
const useStyles = makeStyles({
|
||||
...styles,
|
||||
|
|
@ -25,7 +26,7 @@ const GET_CONFIG = gql`
|
|||
}
|
||||
`
|
||||
const SAVE_ACCOUNTS = gql`
|
||||
mutation Save($accounts: JSONObject) {
|
||||
mutation SaveAccountsBC($accounts: JSONObject) {
|
||||
saveAccounts(accounts: $accounts)
|
||||
}
|
||||
`
|
||||
|
|
@ -41,23 +42,22 @@ const options = [
|
|||
}
|
||||
]
|
||||
|
||||
const Blockcypher = () => {
|
||||
const Blockcypher = ({ addData }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
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 classes = useStyles()
|
||||
const [saveConfig] = useMutation(SAVE_ACCOUNTS)
|
||||
const [currentCode, setCurrentCode] = useState('disable')
|
||||
|
||||
useEffect(() => {
|
||||
if (!accounts?.blockcypher) return
|
||||
schema.blockcypher.validationSchema.isValidSync(accounts.blockcypher) &&
|
||||
setCurrentCode('enable')
|
||||
}, [accounts])
|
||||
|
||||
const handleRadio = cashoutOrNot => {
|
||||
setCurrentCode(cashoutOrNot)
|
||||
cashoutOrNot === 'disable' && save({})
|
||||
const onSelect = e => {
|
||||
setSelected(e.target.value)
|
||||
setError(false)
|
||||
}
|
||||
|
||||
const save = blockcypher => {
|
||||
|
|
@ -67,7 +67,7 @@ const Blockcypher = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<H4>Blockcypher</H4>
|
||||
<H4 className={error && classes.error}>Blockcypher</H4>
|
||||
<P>
|
||||
If you are enabling cash-out services,{' '}
|
||||
<Link>
|
||||
|
|
@ -84,17 +84,26 @@ const Blockcypher = () => {
|
|||
labelClassName={classes.radioLabel}
|
||||
className={classes.radioGroup}
|
||||
options={options}
|
||||
value={currentCode}
|
||||
onChange={event => handleRadio(event.target.value)}
|
||||
value={selected}
|
||||
onChange={onSelect}
|
||||
/>
|
||||
<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
|
||||
value={accounts.blockcypher}
|
||||
save={save}
|
||||
elements={schema.blockcypher.elements}
|
||||
validationSchema={schema.blockcypher.validationSchema}
|
||||
buttonLabel={'Save'}
|
||||
buttonLabel={'Continue'}
|
||||
buttonClass={classes.formButton}
|
||||
/>
|
||||
)}
|
||||
</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 gql from 'graphql-tag'
|
||||
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 { H4, Info3 } from 'src/components/typography'
|
||||
import FormRenderer from 'src/pages/Services/FormRenderer'
|
||||
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 LinkIcon } from 'src/styling/icons/action/external link/zodiac.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'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const GET_CONFIG = gql`
|
||||
{
|
||||
config
|
||||
accounts
|
||||
accountsConfig {
|
||||
code
|
||||
|
|
@ -36,65 +34,33 @@ const GET_CONFIG = gql`
|
|||
}
|
||||
`
|
||||
|
||||
const SAVE_CONFIG = gql`
|
||||
mutation Save($config: JSONObject) {
|
||||
saveConfig(config: $config)
|
||||
}
|
||||
`
|
||||
const SAVE_ACCOUNTS = gql`
|
||||
mutation Save($accounts: JSONObject) {
|
||||
saveAccounts(accounts: $accounts)
|
||||
}
|
||||
`
|
||||
const ChooseExchange = () => {
|
||||
|
||||
const isConfigurable = it => R.contains(it)(['kraken', 'itbit', 'bitstamp'])
|
||||
|
||||
const ChooseExchange = ({ data: currentData, addData }) => {
|
||||
const classes = useStyles()
|
||||
const { data } = useQuery(GET_CONFIG)
|
||||
const [saveConfig] = useMutation(SAVE_CONFIG)
|
||||
const [saveAccounts] = useMutation(SAVE_ACCOUNTS)
|
||||
const [currentCode, setCurrentCode] = useState(null)
|
||||
const [currentName, setCurrentName] = useState(null)
|
||||
const [saveAccounts] = useMutation(SAVE_ACCOUNTS, {
|
||||
onCompleted: () => submit()
|
||||
})
|
||||
|
||||
const [selected, setSelected] = useState(null)
|
||||
const [error, setError] = useState(false)
|
||||
|
||||
const accounts = data?.accounts ?? []
|
||||
const accountsConfig = data?.accountsConfig ?? []
|
||||
|
||||
const walletConfigs =
|
||||
data?.config && fromNamespace(namespaces.WALLETS)(data.config)
|
||||
const wizardCoin =
|
||||
walletConfigs &&
|
||||
Object.keys(walletConfigs).find(
|
||||
k => k.endsWith('_wizard') && walletConfigs[k]
|
||||
)
|
||||
const coin = currentData.coin
|
||||
const exchanges = getItems(accountsConfig, accounts, 'exchange', coin)
|
||||
|
||||
const coinCode = wizardCoin && wizardCoin.replace('_wizard', '')
|
||||
const exchanges = getItems(accountsConfig, accounts, 'exchange', coinCode)
|
||||
|
||||
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 submit = () => {
|
||||
if (!selected) return setError(true)
|
||||
addData({ exchange: selected })
|
||||
}
|
||||
|
||||
const saveExchange = name => exchange => {
|
||||
|
|
@ -102,6 +68,11 @@ const ChooseExchange = () => {
|
|||
return saveAccounts({ variables: { accounts } })
|
||||
}
|
||||
|
||||
const onSelect = e => {
|
||||
setSelected(e.target.value)
|
||||
setError(false)
|
||||
}
|
||||
|
||||
const supportArticles = {
|
||||
kraken:
|
||||
'https://support.lamassu.is/hc/en-us/articles/115001206891-Kraken-trading',
|
||||
|
|
@ -113,22 +84,27 @@ const ChooseExchange = () => {
|
|||
|
||||
return (
|
||||
<div className={classes.mdForm}>
|
||||
<H4>Choose your exchange</H4>
|
||||
<H4 className={error && classes.error}>Choose your exchange</H4>
|
||||
<RadioGroup
|
||||
labelClassName={classes.radioLabel}
|
||||
className={classes.radioGroup}
|
||||
options={R.union(exchanges.filled, exchanges.unfilled)}
|
||||
value={currentCode}
|
||||
onChange={event => save(event.target.value)}
|
||||
value={selected}
|
||||
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}>
|
||||
<WarningIcon />
|
||||
<Info3>
|
||||
Make sure you set up {currentName} to enter the necessary
|
||||
information below. Please follow the instructions on our support
|
||||
page if you haven’t.
|
||||
Make sure you set up {selected} to enter the necessary information
|
||||
below. Please follow the instructions on our support page if you
|
||||
haven’t.
|
||||
</Info3>
|
||||
</div>
|
||||
<ActionButton
|
||||
|
|
@ -140,18 +116,19 @@ const ChooseExchange = () => {
|
|||
className={classes.actionButtonLink}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={supportArticles[currentCode]}>
|
||||
{currentName} trading
|
||||
href={supportArticles[selected]}>
|
||||
{selected} trading
|
||||
</a>
|
||||
</ActionButton>
|
||||
|
||||
<H4 noMargin>Enter exchange information</H4>
|
||||
<FormRenderer
|
||||
value={accounts[currentCode]}
|
||||
save={saveExchange(currentCode)}
|
||||
elements={schema[currentCode].elements}
|
||||
validationSchema={schema[currentCode].validationSchema}
|
||||
buttonLabel={'Confirm'}
|
||||
value={accounts[selected]}
|
||||
save={saveExchange(selected)}
|
||||
elements={schema[selected].elements}
|
||||
validationSchema={schema[selected].validationSchema}
|
||||
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 gql from 'graphql-tag'
|
||||
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 { H4, Info3 } from 'src/components/typography'
|
||||
import FormRenderer from 'src/pages/Services/FormRenderer'
|
||||
import schema from 'src/pages/Services/schemas'
|
||||
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 LinkIcon } from 'src/styling/icons/action/external link/zodiac.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'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const GET_CONFIG = gql`
|
||||
{
|
||||
config
|
||||
accounts
|
||||
accountsConfig {
|
||||
code
|
||||
|
|
@ -37,68 +35,80 @@ const GET_CONFIG = gql`
|
|||
}
|
||||
`
|
||||
|
||||
const SAVE_CONFIG = gql`
|
||||
mutation Save($config: JSONObject) {
|
||||
saveConfig(config: $config)
|
||||
}
|
||||
`
|
||||
const SAVE_ACCOUNTS = gql`
|
||||
mutation Save($accounts: JSONObject) {
|
||||
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 { data } = useQuery(GET_CONFIG)
|
||||
const [saveConfig] = useMutation(SAVE_CONFIG)
|
||||
const [saveAccounts] = useMutation(SAVE_ACCOUNTS)
|
||||
const [currentCode, setCurrentCode] = useState(null)
|
||||
const [saveAccounts] = useMutation(SAVE_ACCOUNTS, {
|
||||
onCompleted: () => submit()
|
||||
})
|
||||
|
||||
const [selected, setSelected] = useState(null)
|
||||
const [error, setError] = useState(false)
|
||||
|
||||
const accounts = data?.accounts ?? []
|
||||
const accountsConfig = data?.accountsConfig ?? []
|
||||
|
||||
const walletConfigs =
|
||||
data?.config && fromNamespace(namespaces.WALLETS)(data.config)
|
||||
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 coin = currentData.coin
|
||||
const wallets = getItems(accountsConfig, accounts, 'wallet', coin)
|
||||
|
||||
const saveWallet = name => wallet => {
|
||||
const accounts = { [name]: wallet }
|
||||
return saveAccounts({ variables: { accounts } })
|
||||
}
|
||||
|
||||
const submit = () => {
|
||||
if (!selected) return setError(true)
|
||||
addData({ wallet: selected })
|
||||
}
|
||||
|
||||
const onSelect = e => {
|
||||
setSelected(e.target.value)
|
||||
setError(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.mdForm}>
|
||||
<H4>Choose your wallet</H4>
|
||||
<H4 className={error && classes.error}>Choose your wallet</H4>
|
||||
<RadioGroup
|
||||
labelClassName={classes.radioLabel}
|
||||
className={classes.radioGroup}
|
||||
options={R.union(wallets.filled, wallets.unfilled)}
|
||||
value={currentCode}
|
||||
onChange={event => save(event.target.value)}
|
||||
value={selected}
|
||||
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}>
|
||||
<WarningIcon />
|
||||
|
|
@ -108,48 +118,40 @@ const WalletCoin = () => {
|
|||
page if you haven’t.
|
||||
</Info3>
|
||||
</div>
|
||||
<ActionButton
|
||||
className={classes.actionButton}
|
||||
color="primary"
|
||||
Icon={LinkIcon}
|
||||
InverseIcon={InverseLinkIcon}>
|
||||
<a
|
||||
className={classes.actionButtonLink}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://support.lamassu.is/hc/en-us/articles/360024455592-Setting-up-BitGo">
|
||||
<ActionButton
|
||||
className={classes.actionButton}
|
||||
color="primary"
|
||||
Icon={LinkIcon}
|
||||
InverseIcon={InverseLinkIcon}>
|
||||
Support article
|
||||
</a>
|
||||
</ActionButton>
|
||||
</a>
|
||||
<H4 noMargin>Enter wallet information</H4>
|
||||
<FormRenderer
|
||||
value={accounts.bitgo}
|
||||
save={saveWallet(currentCode)}
|
||||
elements={bitgo(coinCode).elements}
|
||||
validationSchema={bitgo(coinCode).validationSchema}
|
||||
buttonLabel={'Confirm'}
|
||||
save={saveWallet(selected)}
|
||||
elements={bitgo(coin).elements}
|
||||
validationSchema={bitgo(coin).validationSchema}
|
||||
buttonLabel={'Continue'}
|
||||
buttonClass={classes.formButton}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{currentCode === 'bitcoind' && (
|
||||
<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' && (
|
||||
{selected === 'infura' && (
|
||||
<>
|
||||
<H4>Enter wallet information</H4>
|
||||
<H4 noMargin>Enter wallet information</H4>
|
||||
<FormRenderer
|
||||
value={accounts.infura}
|
||||
save={saveWallet(currentCode)}
|
||||
save={saveWallet(selected)}
|
||||
elements={schema.infura.elements}
|
||||
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 React, { useEffect } from 'react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import React from 'react'
|
||||
|
||||
import { Button } from 'src/components/buttons'
|
||||
import { H1, P } from 'src/components/typography'
|
||||
|
|
@ -25,16 +24,9 @@ const styles = {
|
|||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
function Welcome({ dispatch }) {
|
||||
function Welcome({ doContinue }) {
|
||||
const classes = useStyles()
|
||||
|
||||
useEffect(() => {
|
||||
dispatch({ type: 'wizard/SET_STEP', payload: 'welcome' })
|
||||
}, [dispatch])
|
||||
|
||||
const history = useHistory()
|
||||
const handleClick = () => history.push('/wizard/wallets')
|
||||
|
||||
return (
|
||||
<div className={classes.welcome}>
|
||||
<H1 className={classes.title}>Welcome to Lamassu Admin</H1>
|
||||
|
|
@ -43,7 +35,7 @@ function Welcome({ dispatch }) {
|
|||
<br />
|
||||
help set up you need before start adding machines.
|
||||
</P>
|
||||
<Button size={'xl'} onClick={handleClick}>
|
||||
<Button size="xl" onClick={doContinue}>
|
||||
Get started
|
||||
</Button>
|
||||
</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 { ActionButton, Button, Link } from 'src/components/buttons'
|
||||
import {
|
||||
Radio,
|
||||
TextInput,
|
||||
Switch,
|
||||
CashIn,
|
||||
CashOut
|
||||
} from 'src/components/inputs'
|
||||
import { TextInput, Switch, CashIn, CashOut } from 'src/components/inputs'
|
||||
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 ConfirmDialog from '../components/ConfirmDialog'
|
||||
// import ConfirmDialog from '../components/ConfirmDialog'
|
||||
import {
|
||||
H1,
|
||||
H2,
|
||||
|
|
@ -169,21 +163,6 @@ story.add('Checkbox', () => (
|
|||
</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', () => (
|
||||
<Wrapper>
|
||||
<div>
|
||||
|
|
@ -211,8 +190,6 @@ story.add('Cashbox', () => (
|
|||
</Wrapper>
|
||||
))
|
||||
|
||||
story.add('Radio', () => <Radio label="Hehe" />)
|
||||
|
||||
const typographyStory = storiesOf('Typography', module)
|
||||
typographyStory.add('H1', () => <H1>Hehehe</H1>)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue