fix: wizard rework

This commit is contained in:
Taranto 2020-10-11 18:04:46 +01:00 committed by Josh Harvey
parent 0f4350f99c
commit 3a6f3a2915
40 changed files with 12622 additions and 9244 deletions

View file

@ -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)
}

View file

@ -1,4 +0,0 @@
import '@storybook/addon-actions/register'
import '@storybook/addon-links/register'
import '@storybook/addon-knobs/register'
import '@storybook/addon-backgrounds/register'

View file

@ -1,7 +0,0 @@
import { configure } from '@storybook/react'
function loadStories () {
require('../src/stories')
}
configure(loadStories, module)

View 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'
]
}

File diff suppressed because it is too large Load diff

View file

@ -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": {

View file

@ -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"

View file

@ -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 classes = useStyles()
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 (
<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 />
<div className={classes.root}>
<Router>
<Header tree={tree} />
<main className={classes.wrapper}>
<Routes />
</main>
</Router>
</div>
<Main />
</MuiThemeProvider>
</StylesProvider>
</ApolloProvider>

View 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

View file

@ -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>
<Link color="secondary" onClick={resetForm}>
Cancel
</Link>
{!forceAdd && (
<Link color="secondary" onClick={resetForm}>
Cancel
</Link>
)}
</Td>
)}
{!editing && enableEdit && (

View file

@ -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
}

View file

@ -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 }

View file

@ -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]]
}
}

View file

@ -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

View file

@ -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]]
}
}

View file

@ -38,7 +38,6 @@ const SAVE_CONFIG = gql`
const FIELDS_WIDTH = 130
// TODO: what about 'onlySetup' 'onlyFiat'?
const Notifications = ({
name: SCREEN_KEY,
displaySetup = true,

View file

@ -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>

View file

@ -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')
})
})

View file

@ -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>
)
}

View file

@ -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

View file

@ -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

View file

@ -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>

View file

@ -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,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 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)}>
<Drawer
anchor={'bottom'}
open={true}
variant={'persistent'}
classes={{ paperAnchorDockedBottom: classes.drawer }}>
<div className={classnames(wrapperClassNames)}>
<Grid container direction="row" justify="center" alignItems="baseline">
<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
container
direction="row"
@ -80,12 +89,15 @@ function Footer({ next, current, steps: collection, path, tag, p }) {
item
xs={5}
container
direction={open ? 'column' : 'row'}
direction="column"
justify="flex-start"
alignItems="baseline">
<H2 className={classes.title}>Setup Lamassu Admin</H2>
<Info2 className={classes.subtitle}>{tag}</Info2>
{open && <P>{p}</P>}
alignItems="flex-start">
<Link
onClick={() => {
setFullExample(true)
}}>
See full example
</Link>
</Grid>
<Grid
item
@ -95,68 +107,28 @@ function Footer({ next, current, steps: collection, path, tag, p }) {
justify="flex-start"
alignItems="flex-end"
spacing={5}>
<Grid item xs={12}>
{steps && currentStep && (
<Stepper {...{ currentStep, steps }}></Stepper>
)}
<Grid item>
<Button size="lg" onClick={start}>
Get Started
</Button>
</Grid>
</Grid>
</Grid>
{open && (
<Grid
container
direction="row"
justify="center"
alignItems="baseline">
<Grid
item
xs={5}
container
direction="column"
justify="flex-start"
alignItems="flex-start">
<Link
onClick={() => {
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>
)}
</div>
<Modal
closeOnEscape={true}
closeOnBackdropClick={true}
className={classes.modal}
xl={true}
width={1152 + 120 + 56}
handleClose={() => {
setFullExample(false)
}}
open={fullExample}>
<img width={1152} src={exImage} alt="" />
</Modal>
</Drawer>
)
}

View file

@ -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>

View file

@ -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()

View file

@ -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}>

View file

@ -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, youll only need an SMS service
for cash-out transactions. If youre using one-way machines,
select No to skip this step for now. You can later set it up
within the Lamassu Admin.
Youll 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>
Before configuring Twilio, create an account and phone number to use
the Admin.
</Label1>
</div>
{enable === 'enable' && (
<FormRenderer
xs={6}
save={save}
value={accounts.twilio}
elements={twilio.elements}
validationSchema={twilio.validationSchema}
buttonLabel={'Save'}
/>
<InfoMessage className={classes.info}>
Before configuring Twilio, create an account and phone number to use
the Admin.
</InfoMessage>
{selected === 'enable' && (
<>
<H4 noMargin>Enter credentials</H4>
<FormRenderer
xs={6}
save={save}
value={accounts.twilio}
elements={twilio.elements}
validationSchema={twilio.validationSchema}
buttonLabel={'Continue'}
buttonClass={classes.formButton}
/>
</>
)}
{selected !== 'enable' && (
<Button size="lg" onClick={clickContinue} className={classes.button}>
Continue
</Button>
)}
</div>
</div>

View file

@ -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

View file

@ -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

View file

@ -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>

View file

@ -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

View file

@ -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 havent.
Make sure you set up {selected} to enter the necessary information
below. Please follow the instructions on our support page if you
havent.
</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}
/>
</>
)}

View file

@ -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

View file

@ -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 havent.
</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">
<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>
</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. Well 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

View file

@ -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]]
}
}

View file

@ -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

View file

@ -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>

View 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 theres 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 }

View file

@ -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>)