chore: use monorepo organization

This commit is contained in:
Rafael Taranto 2025-05-12 10:52:54 +01:00
parent deaf7d6ecc
commit a687827f7e
1099 changed files with 8184 additions and 11535 deletions

View file

@ -0,0 +1,62 @@
import { useQuery, useMutation, gql } from '@apollo/client'
import * as R from 'ramda'
import React from 'react'
import Section from 'src/components/layout/Section'
import TitleSection from 'src/components/layout/TitleSection'
import { mainFields, defaults, getSchema } from 'src/pages/Commissions/helper'
import { Table as EditableTable } from 'src/components/editableTable'
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
const GET_DATA = gql`
query getData {
config
}
`
const SAVE_CONFIG = gql`
mutation Save($config: JSONObject) {
saveConfig(config: $config)
}
`
function Commissions({ isActive, doContinue }) {
const { data } = useQuery(GET_DATA)
const [saveConfig] = useMutation(SAVE_CONFIG, {
onCompleted: doContinue
})
const save = it => {
const config = toNamespace('commissions')(it.commissions[0])
return saveConfig({ variables: { config } })
}
const currency = R.path(['fiatCurrency'])(
fromNamespace(namespaces.LOCALE)(data?.config)
)
const locale = fromNamespace(namespaces.LOCALE)(data?.config)
return (
<div className="w-[1132px] h-full mx-auto flex-1 flex flex-col">
<TitleSection title="Commissions" />
<Section>
<EditableTable
title="Default setup"
rowSize="lg"
titleLg
name="commissions"
initialValues={defaults}
enableEdit
forceAdd={isActive}
save={save}
validationSchema={getSchema(locale)}
data={[]}
elements={mainFields(currency)}
/>
</Section>
</div>
)
}
export default Commissions

View file

@ -0,0 +1,104 @@
import Drawer from '@mui/material/Drawer'
import Grid from '@mui/material/Grid'
import React, { useState } from 'react'
import Modal from 'src/components/Modal'
import Stepper from 'src/components/Stepper'
import { P, H2, Info2 } from 'src/components/typography'
import { Button, Link } from 'src/components/buttons'
function Footer({ currentStep, steps, subtitle, text, exImage, open, start }) {
const [fullExample, setFullExample] = useState(false)
return (
<Drawer
anchor={'bottom'}
open={true}
variant={'persistent'}
classes={{ paperAnchorDockedBottom: 'border-t-0 shadow-sm' }}>
<div className={`py-8 flex-grow ${open ? 'h-[264px]' : 'h-[84px]'}`}>
<Grid
container
direction="row"
justifyContent="center"
alignItems="baseline">
<Grid
item
xs={5}
container
direction={open ? 'column' : 'row'}
justifyContent="flex-start"
alignItems="baseline">
<H2 className="m-0 mr-8">Setup Lamassu Admin</H2>
<Info2 className="mt-2 mb-2 leading-tight inline">{subtitle}</Info2>
{open && <P>{text}</P>}
</Grid>
<Grid
item
xs={4}
container
direction="column"
justifyContent="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"
justifyContent="center"
alignItems="baseline">
<Grid
item
xs={5}
container
direction="column"
justifyContent="flex-start"
alignItems="flex-start">
<Link
onClick={() => {
setFullExample(true)
}}>
See full example
</Link>
</Grid>
<Grid
item
xs={4}
container
direction="column"
justifyContent="flex-start"
alignItems="flex-end"
spacing={5}>
<Grid item>
<Button size="lg" onClick={start}>
Get Started
</Button>
</Grid>
</Grid>
</Grid>
)}
</div>
<Modal
closeOnEscape={true}
closeOnBackdropClick={true}
className="bg-transparent shadow-none"
xl={true}
width={1152 + 120 + 56}
handleClose={() => {
setFullExample(false)
}}
open={fullExample}>
<img width={1152} src={exImage} alt="" />
</Modal>
</Drawer>
)
}
export default Footer

View file

@ -0,0 +1,94 @@
import { useQuery, useMutation, gql } from '@apollo/client'
import * as R from 'ramda'
import React from 'react'
import Section from 'src/components/layout/Section'
import TitleSection from 'src/components/layout/TitleSection'
import { Table as EditableTable } from 'src/components/editableTable'
import {
mainFields,
localeDefaults as defaults,
LocaleSchema as schema
} from 'src/pages/Locales/helper'
import { toNamespace } from 'src/utils/config'
import { getConfiguredCoins } from '../helper'
const GET_DATA = gql`
query getData {
config
accounts
currencies {
code
display
}
countries {
code
display
}
cryptoCurrencies {
code
display
}
languages {
code
display
}
machines {
name
deviceId
}
}
`
const SAVE_CONFIG = gql`
mutation Save($config: JSONObject) {
saveConfig(config: $config)
}
`
function Locales({ isActive, doContinue }) {
const { data } = useQuery(GET_DATA)
const [saveConfig] = useMutation(SAVE_CONFIG, {
onCompleted: doContinue
})
const save = it => {
const config = toNamespace('locale')(it.locale[0])
return saveConfig({ variables: { config } })
}
const cryptoCurrencies = getConfiguredCoins(
data?.config || {},
data?.cryptoCurrencies || []
)
const onChangeCoin = (prev, curr, setValue) => setValue(curr)
return (
<div className="w-[1132px] h-full mx-auto flex-1 flex flex-col">
<TitleSection title="Locales" />
<Section>
<EditableTable
title="Default settings"
rowSize="lg"
titleLg
name="locale"
initialValues={defaults}
forceAdd={isActive}
enableEdit
save={save}
validationSchema={schema}
data={[]}
elements={mainFields(
R.mergeRight(data, { cryptoCurrencies }),
onChangeCoin
)}
/>
</Section>
</div>
)
}
export default Locales

View file

@ -0,0 +1,123 @@
import { useMutation, useQuery, gql } from "@apollo/client";
import React, { useState, useEffect } from 'react'
import { H4, Info3 } from 'src/components/typography'
import FormRenderer from 'src/pages/Services/FormRenderer'
import InverseLinkIcon from 'src/styling/icons/action/external link/white.svg?react'
import LinkIcon from 'src/styling/icons/action/external link/zodiac.svg?react'
import WarningIcon from 'src/styling/icons/warning-icon/comet.svg?react'
import { ActionButton } from 'src/components/buttons'
import { RadioGroup } from 'src/components/inputs'
import mailgunSchema from 'src/pages/Services/schemas/mailgun'
import classes from 'src/pages/Wizard/Radio.module.css'
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
const GET_CONFIG = gql`
{
config
accounts
}
`
const SAVE_CONFIG = gql`
mutation Save($config: JSONObject) {
saveConfig(config: $config)
}
`
const SAVE_ACCOUNTS = gql`
mutation Save($accounts: JSONObject) {
saveAccounts(accounts: $accounts)
}
`
const options = [
{
code: 'enabled',
display: 'Yes, send notifications to my email'
},
{
code: 'disabled',
display: "No, don't send email notifications"
}
]
const Mailgun = () => {
const { data } = useQuery(GET_CONFIG)
const [saveConfig] = useMutation(SAVE_CONFIG)
const [saveAccounts] = useMutation(SAVE_ACCOUNTS)
const [emailActive, setEmailActive] = useState(false)
const accounts = data?.accounts ?? []
const emailConfig =
data?.config &&
fromNamespace(namespaces.NOTIFICATIONS + '_email')(data.config)
useEffect(() => {
if (emailActive) return
emailConfig && setEmailActive(emailConfig?.active ? 'enabled' : 'disabled')
}, [emailActive, emailConfig])
const handleRadio = enabled => {
setEmailActive(enabled)
save(enabled === 'enabled')
}
const save = active => {
const config = toNamespace(`notifications_email`)({ active })
return saveConfig({ variables: { config } })
}
const saveAccount = mailgun => {
const accounts = { mailgun }
return saveAccounts({ variables: { accounts } })
}
return (
<div className={classes.mdForm}>
<H4>Do you want to get notifications via email?</H4>
<RadioGroup
labelClassName={classes.mailgunRadioLabel}
className={classes.mailgunRadioGroup}
options={options}
value={emailActive}
onChange={event => handleRadio(event.target.value)}
/>
<div className={classes.infoMessage}>
<WarningIcon />
<Info3>
To get email notifications, youll need to set up Mailgun. Check out
our article on how to set it up.
</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/115001203991-Email-notifications-with-Mailgun">
Email notifications with Mailgun
</a>
</ActionButton>
{emailActive === 'enabled' && (
<>
<H4>Mailgun credentials</H4>
<FormRenderer
value={accounts.mailgun}
save={saveAccount}
elements={mailgunSchema.elements}
validationSchema={mailgunSchema.getValidationSchema(accounts.mailgun)}
buttonLabel={'Save'}
/>
</>
)}
</div>
)
}
export default Mailgun

View file

@ -0,0 +1,89 @@
import Grid from '@mui/material/Grid'
import React, { useState } from 'react'
import Sidebar from 'src/components/layout/Sidebar'
import TitleSection from 'src/components/layout/TitleSection'
import Notifications from 'src/pages/Notifications/Notifications'
import { namespaces } from 'src/utils/config'
import Mailgun from './Mailgun'
const EMAIL = 'Email'
const SETUP_CHANNELS = 'Setup channels'
const TRANSACTION_ALERTS = 'Transaction alerts'
const FIAT_BALANCE_ALERTS = 'Fiat balance alerts'
const CRYPTO_BALANCE_ALERTS = 'Crypto balance alerts'
const pages = [
EMAIL,
SETUP_CHANNELS,
TRANSACTION_ALERTS,
FIAT_BALANCE_ALERTS,
CRYPTO_BALANCE_ALERTS
]
const N = () => {
const [selected, setSelected] = useState(EMAIL)
const isSelected = it => selected === it
return (
<div className="w-[1132px] h-full mx-auto flex-1 flex flex-col">
<TitleSection title="Notifications"></TitleSection>
<Grid container className="flex-1 h-full">
<Sidebar
data={pages}
isSelected={isSelected}
displayName={it => it}
onClick={it => setSelected(it)}
/>
<div className="ml-12 pt-4">
{isSelected(EMAIL) && <Mailgun />}
{isSelected(SETUP_CHANNELS) && (
<Notifications
name={namespaces.NOTIFICATIONS}
wizard={true}
displayCryptoAlerts={false}
displayOverrides={false}
displayTitle={false}
displayTransactionAlerts={false}
displayFiatAlerts={false}
/>
)}
{isSelected(TRANSACTION_ALERTS) && (
<Notifications
name={namespaces.NOTIFICATIONS}
displayCryptoAlerts={false}
displayOverrides={false}
displayTitle={false}
displaySetup={false}
displayFiatAlerts={false}
/>
)}
{isSelected(FIAT_BALANCE_ALERTS) && (
<Notifications
name={namespaces.NOTIFICATIONS}
displayCryptoAlerts={false}
displayOverrides={false}
displayTitle={false}
displayTransactionAlerts={false}
displaySetup={false}
/>
)}
{isSelected(CRYPTO_BALANCE_ALERTS) && (
<Notifications
name={namespaces.NOTIFICATIONS}
displaySetup={false}
displayOverrides={false}
displayTitle={false}
displayTransactionAlerts={false}
displayFiatAlerts={false}
/>
)}
</div>
</Grid>
</div>
)
}
export default N

View file

@ -0,0 +1,12 @@
import React from 'react'
// import OperatorInfo from 'src/pages/OperatorInfo'
function WizardOperatorInfo() {
return (
<div className="w-[1132px] h-full mx-auto flex-1 flex flex-col">
{/* <OperatorInfo wizard={true}></OperatorInfo> */}
</div>
)
}
export default WizardOperatorInfo

View file

@ -0,0 +1,138 @@
import { useMutation, useQuery, gql } from '@apollo/client'
import classnames from 'classnames'
import React, { useState } from 'react'
import { HelpTooltip } from 'src/components/Tooltip'
import { H1, H4, Label1, P } from 'src/components/typography'
import FormRenderer from 'src/pages/Services/FormRenderer'
import WarningIcon from 'src/styling/icons/warning-icon/comet.svg?react'
import { Button, SupportLinkButton } from 'src/components/buttons'
import { RadioGroup } from 'src/components/inputs'
import twilio from 'src/pages/Services/schemas/twilio'
import sharedClasses from './Wallet/Shared.module.css'
import classes from './Twilio.module.css'
const GET_CONFIG = gql`
{
config
accounts
}
`
const SAVE_ACCOUNTS = gql`
mutation Save($accounts: JSONObject) {
saveAccounts(accounts: $accounts)
}
`
const options = [
{
code: 'enable',
display: 'Yes, I will'
},
{
code: 'disable',
display: 'No, not for now'
}
]
function Twilio({ doContinue }) {
const [selected, setSelected] = useState(null)
const [error, setError] = useState(false)
const { data, refetch } = useQuery(GET_CONFIG)
const [saveAccounts] = useMutation(SAVE_ACCOUNTS, {
onCompleted: doContinue
})
const accounts = data?.accounts ?? []
const onSelect = e => {
setSelected(e.target.value)
setError(false)
}
const clickContinue = () => {
if (!selected) return setError(true)
doContinue()
}
const save = twilio => {
const accounts = { twilio }
return saveAccounts({ variables: { accounts } }).then(() => refetch())
}
const titleClasses = {
'ml-2 mb-2': true,
[sharedClasses.error]: error
}
return (
<div className={classes.wrapper}>
<div className={classes.content}>
<H1>Twilio (SMS service)</H1>
<div className="flex items-end">
<H4 noMargin className={classnames(titleClasses)}>
Will you setup a two way machine or compliance?
</H4>
<HelpTooltip width={304}>
<P>
Two-way machines allow your customers not only to buy (cash-in)
but also sell cryptocurrencies (cash-out).
</P>
<P>
Youll need an SMS service for cash-out transactions and for any
compliance triggers
</P>
</HelpTooltip>
</div>
<RadioGroup
labelClassName={classes.radioLabel}
className={sharedClasses.radioGroup}
options={options}
value={selected}
onChange={onSelect}
/>
<div className="flex gap-4 mt-5 mb-8 items-center">
<WarningIcon />
<Label1 noMargin>
To set up Twilio please read the instructions from our support
portal.
</Label1>
</div>
<SupportLinkButton
link="https://support.lamassu.is/hc/en-us/articles/115001203951-Twilio-for-SMS"
label="Twilio for SMS"
/>
{selected === 'enable' && (
<>
<H4 noMargin>Enter credentials</H4>
<FormRenderer
xs={6}
save={save}
value={accounts.twilio}
elements={twilio.elements}
validationSchema={twilio.getValidationSchema(accounts.twilio)}
buttonLabel={'Continue'}
buttonClass={sharedClasses.formButton}
/>
</>
)}
{selected !== 'enable' && (
<Button
size="lg"
onClick={clickContinue}
className={sharedClasses.button}>
Continue
</Button>
)}
</div>
</div>
)
}
export default Twilio

View file

@ -0,0 +1,24 @@
.content {
width: 820px;
}
.radioLabel {
width: 280px;
height: 48px;
}
.wrapper {
width: 1200px;
height: 100px;
margin: 0 auto;
}
.title {
margin-left: 8px;
margin-bottom: 5px;
}
.info {
margin-top: 20px;
margin-bottom: 20px;
}

View file

@ -0,0 +1,98 @@
import { useQuery, useMutation, gql } from "@apollo/client";
import * as R from 'ramda'
import React, { useState } from 'react'
import { P, H4 } from 'src/components/typography'
import { getElements, WalletSchema } from 'src/pages/Wallet/helper'
import { Button } from 'src/components/buttons'
import { NamespacedTable as EditableTable } from 'src/components/editableTable'
import { toNamespace, namespaces } from 'src/utils/config'
import classes from './Shared.module.css'
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 { 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 = () => {
const adjustedData = {
zeroConfLimit: 0,
...currentData
}
if (!WalletSchema.isValidSync(adjustedData)) return setError(true)
const withCoin = toNamespace(coin, R.omit('coin', adjustedData))
const config = toNamespace(namespaces.WALLETS)(withCoin)
setError(false)
return saveConfig({ variables: { config } })
}
const presentableData = R.pipe(
R.omit(['coin', 'zeroConf', 'zeroConfLimit']),
toNamespace(coin)
)(currentData)
const presentableElements = R.filter(
R.pipe(
R.prop('name'),
R.flip(R.includes)(['zeroConf', 'zeroConfLimit']),
R.not()
),
getElements(cryptoCurrencies, accountsConfig, null, true)
)
return (
<>
<H4 className={error && classes.error}>All set</H4>
<P>
These are your wallet settings. You can later edit these and add
additional coins.
</P>
<EditableTable
rowSize="lg"
titleLg
name="All set"
namespaces={[coin]}
data={presentableData}
elements={presentableElements}
/>
<Button size="lg" onClick={save} className={classes.button}>
Continue
</Button>
</>
)
}
export default AllSet

View file

@ -0,0 +1,96 @@
import { useMutation, useQuery, gql } from "@apollo/client";
import React, { useState } from 'react'
import { P, H4 } from 'src/components/typography'
import FormRenderer from 'src/pages/Services/FormRenderer'
import { SupportLinkButton, Button } from 'src/components/buttons'
import { RadioGroup } from 'src/components/inputs'
import blockcypherSchema from 'src/pages/Services/schemas/blockcypher'
import classes from './Shared.module.css'
const GET_CONFIG = gql`
{
accounts
}
`
const SAVE_ACCOUNTS = gql`
mutation SaveAccountsBC($accounts: JSONObject) {
saveAccounts(accounts: $accounts)
}
`
const options = [
{
code: 'enable',
display: 'I will enable cash-out'
},
{
code: 'disable',
display: "I won't enable cash-out"
}
]
const Blockcypher = ({ addData }) => {
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 onSelect = e => {
setSelected(e.target.value)
setError(false)
}
const save = blockcypher => {
const accounts = { blockcypher }
return saveConfig({ variables: { accounts } })
}
return (
<>
<H4 className={error && classes.error}>Blockcypher</H4>
<P>
If you are enabling cash-out services, create a Blockcypher account.
</P>
<SupportLinkButton
link="https://support.lamassu.is/hc/en-us/articles/115001209472-Blockcypher"
label="Configuring Blockcypher"
/>
<RadioGroup
labelClassName={classes.radioLabel}
className={classes.radioGroup}
options={options}
value={selected}
onChange={onSelect}
/>
<div className={classes.mdForm}>
{selected === 'disable' && (
<Button
size="lg"
onClick={() => addData({ zeroConf: 'none', zeroConfLimit: 0 })}
className={classes.button}>
Continue
</Button>
)}
{selected === 'enable' && (
<FormRenderer
value={accounts.blockcypher}
save={save}
elements={blockcypherSchema.elements}
validationSchema={blockcypherSchema.getValidationSchema}
buttonLabel={'Continue'}
buttonClass={classes.formButton}
/>
)}
</div>
</>
)
}
export default Blockcypher

View file

@ -0,0 +1,74 @@
import { useQuery, gql } from "@apollo/client";
import { Formik, Form, Field } from 'formik'
import React, { useState } from 'react'
import PromptWhenDirty from 'src/components/PromptWhenDirty'
import { H4 } from 'src/components/typography'
import * as Yup from 'yup'
import { Button } from 'src/components/buttons'
import { RadioGroup } from 'src/components/inputs/formik'
import classes from './Shared.module.css'
const GET_CONFIG = gql`
{
cryptoCurrencies {
code
display
}
}
`
const schema = Yup.object().shape({
coin: Yup.string().required()
})
const ChooseCoin = ({ addData }) => {
const [error, setError] = useState(false)
const { data } = useQuery(GET_CONFIG)
const cryptoCurrencies = data?.cryptoCurrencies ?? []
const onSubmit = it => {
if (!schema.isValidSync(it)) return setError(true)
if (it.coin !== 'BTC') {
return addData({ coin: it.coin, zeroConf: 'none', zeroConfLimit: 0 })
}
addData(it)
}
return (
<>
<H4 className={error && classes.error}>
Choose your first cryptocurrency
</H4>
<Formik
validateOnBlur={false}
validateOnChange={false}
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

@ -0,0 +1,126 @@
import { useQuery, useMutation, gql } from "@apollo/client";
import { getEquivalentCode } from '@lamassu/coins/lightUtils'
import * as R from 'ramda'
import React, { useState } from 'react'
import { H4, Info3 } from 'src/components/typography'
import FormRenderer from 'src/pages/Services/FormRenderer'
import WarningIcon from 'src/styling/icons/warning-icon/comet.svg?react'
import { Button, SupportLinkButton } from 'src/components/buttons'
import { RadioGroup } from 'src/components/inputs'
import _schema from 'src/pages/Services/schemas'
import classes from './Shared.module.css'
import { getItems } from './getItems'
const GET_CONFIG = gql`
{
accounts
accountsConfig {
code
display
class
cryptos
}
cryptoCurrencies {
code
display
}
}
`
const SAVE_ACCOUNTS = gql`
mutation Save($accounts: JSONObject) {
saveAccounts(accounts: $accounts)
}
`
const isConfigurable = it =>
!R.isNil(it) && !R.contains(it)(['mock-exchange', 'no-exchange'])
const ChooseExchange = ({ data: currentData, addData }) => {
const { data } = useQuery(GET_CONFIG)
const [saveAccounts] = useMutation(SAVE_ACCOUNTS, {
onCompleted: () => submit()
})
const [selected, setSelected] = useState(null)
const [error, setError] = useState(false)
const schema = _schema()
const accounts = data?.accounts ?? []
const accountsConfig = data?.accountsConfig ?? []
const coin = getEquivalentCode(currentData.coin)
const exchanges = getItems(accountsConfig, accounts, 'exchange', coin)
const submit = () => {
if (!selected) return setError(true)
addData({ exchange: selected })
}
const saveExchange = name => exchange => {
const accounts = { [name]: exchange }
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',
itbit:
'https://support.lamassu.is/hc/en-us/articles/360026195032-itBit-trading',
bitstamp:
'https://support.lamassu.is/hc/en-us/articles/115001206911-Bitstamp-trading'
}
return (
<div className={classes.mdForm}>
<H4 className={error && classes.error}>Choose your exchange</H4>
<RadioGroup
labelClassName={classes.radioLabel}
className={classes.radioGroup}
options={R.union(exchanges.filled, exchanges.unfilled)}
value={selected}
onChange={onSelect}
/>
{!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 {schema[selected].name} to enter the
necessary information below. Please follow the instructions on our
support page if you havent.
</Info3>
</div>
<SupportLinkButton
link={supportArticles[selected]}
label={`${schema[selected].name} trading`}
/>
<H4 noMargin>Enter exchange information</H4>
<FormRenderer
value={accounts[selected]}
save={saveExchange(selected)}
elements={schema[selected].elements}
validationSchema={schema[selected].getValidationSchema(accounts[selected])}
buttonLabel={'Continue'}
buttonClass={classes.formButton}
/>
</>
)}
</div>
)
}
export default ChooseExchange

View file

@ -0,0 +1,63 @@
import { useQuery, gql } from "@apollo/client";
import { getEquivalentCode } from '@lamassu/coins/lightUtils'
import * as R from 'ramda'
import React, { useState } from 'react'
import { H4 } from 'src/components/typography'
import { Button } from 'src/components/buttons'
import { RadioGroup } from 'src/components/inputs'
import classes from './Shared.module.css'
import { getItems } from './getItems'
const GET_CONFIG = gql`
{
accountsConfig {
code
display
class
cryptos
}
}
`
const ChooseTicker = ({ data: currentData, addData }) => {
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 = getEquivalentCode(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

@ -0,0 +1,185 @@
import { useQuery, useMutation, gql } from "@apollo/client";
import * as R from 'ramda'
import React, { useState } from 'react'
import { H4, Info3 } from 'src/components/typography'
import FormRenderer from 'src/pages/Services/FormRenderer'
import WarningIcon from 'src/styling/icons/warning-icon/comet.svg?react'
import { Button, SupportLinkButton } from 'src/components/buttons'
import { RadioGroup } from 'src/components/inputs'
import _schema from 'src/pages/Services/schemas'
import bitgo from 'src/pages/Services/schemas/singlebitgo'
import classes from './Shared.module.css'
import { getItems } from './getItems'
const GET_CONFIG = gql`
{
accounts
accountsConfig {
code
display
class
cryptos
}
cryptoCurrencies {
code
display
}
}
`
const SAVE_ACCOUNTS = gql`
mutation Save($accounts: JSONObject) {
saveAccounts(accounts: $accounts)
}
`
const isConfigurable = it =>
R.contains(it)(['infura', 'bitgo', 'trongrid', 'galoy'])
const isLocalHosted = it =>
R.contains(it)([
'bitcoind',
'geth',
'litecoind',
'dashd',
'zcashd',
'bitcoincashd'
])
const ChooseWallet = ({ data: currentData, addData }) => {
// no need to fetch exchange config here
const schema = _schema()
const { data } = useQuery(GET_CONFIG)
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 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 className={error && classes.error}>Choose your wallet</H4>
<RadioGroup
labelClassName={classes.radioLabel}
className={classes.radioGroup}
options={R.union(wallets.filled, wallets.unfilled)}
value={selected}
onChange={onSelect}
/>
{isLocalHosted(selected) && (
<>
<div className={classes.infoMessage}>
<WarningIcon />
<Info3>
To set up {selected} please read the node wallet instructions from
our support portal.
</Info3>
</div>
<SupportLinkButton
link="https://support.lamassu.is/hc/en-us/articles/115001209552-Setting-up-your-node-wallets"
label="Support article"
/>
</>
)}
{!isConfigurable(selected) && (
<Button size="lg" onClick={submit} className={classes.button}>
Continue
</Button>
)}
{selected === 'bitgo' && (
<>
<div className={classes.infoMessage}>
<WarningIcon />
<Info3>
Make sure you set up a BitGo wallet to enter the necessary
information below. Please follow the instructions on our support
page if you havent.
</Info3>
</div>
<SupportLinkButton
link="https://support.lamassu.is/hc/en-us/articles/360024455592-Setting-up-BitGo"
label="Support article"
/>
<H4 noMargin>Enter wallet information</H4>
<FormRenderer
value={accounts.bitgo}
save={saveWallet(selected)}
elements={bitgo(coin).elements}
validationSchema={bitgo(coin).validationSchema}
buttonLabel={'Continue'}
buttonClass={classes.formButton}
/>
</>
)}
{selected === 'infura' && (
<>
<H4 noMargin>Enter wallet information</H4>
<FormRenderer
value={accounts.infura}
save={saveWallet(selected)}
elements={schema.infura.elements}
validationSchema={schema.infura.getValidationSchema(
accounts.infura
)}
buttonLabel={'Continue'}
buttonClass={classes.formButton}
/>
</>
)}
{selected === 'trongrid' && (
<>
<H4 noMargin>Enter wallet information</H4>
<FormRenderer
value={accounts.trongrid}
save={saveWallet(selected)}
elements={schema.trongrid.elements}
validationSchema={schema.trongrid.getValidationSchema(
accounts.trongrid
)}
buttonLabel={'Continue'}
buttonClass={classes.formButton}
/>
</>
)}
{selected === 'galoy' && (
<>
<H4 noMargin>Enter wallet information</H4>
<FormRenderer
value={accounts.galoy}
save={saveWallet(selected)}
elements={schema.galoy.elements}
validationSchema={schema.galoy.getValidationSchema(accounts.galoy)}
buttonLabel={'Continue'}
buttonClass={classes.formButton}
/>
</>
)}
</div>
)
}
export default ChooseWallet

View file

@ -0,0 +1,45 @@
.radioGroup {
flex-direction: row;
width: 600px;
}
.radioLabel {
width: 150px;
height: 48px;
}
.mdForm {
width: 385px;
}
.infoMessage {
display: flex;
margin-bottom: 20px;
}
.infoMessage > p {
width: 330px;
margin-top: 4px;
margin-left: 16px;
}
.actionButton {
margin-bottom: 32px;
}
.actionButtonLink {
text-decoration: none;
color: var(--zodiac);
}
.error {
color: var(--tomato);
}
.button {
margin-top: 40px;
}
.formButton {
margin: 24px 0 0;
}

View file

@ -0,0 +1,72 @@
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 AllSet from './AllSet'
import Blockcypher from './Blockcypher'
import ChooseCoin from './ChooseCoin'
import ChooseExchange from './ChooseExchange'
import ChooseTicker from './ChooseTicker'
import ChooseWallet from './ChooseWallet'
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 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="w-[1132px] h-full mx-auto flex-1 flex flex-col">
<div className="flex justify-between items-center">
<TitleSection title="Wallet settings"></TitleSection>
</div>
<div className="flex flex-1 flex-row">
<Sidebar>
{mySteps.map((it, idx) => (
<Stepper key={idx} step={step} it={it} idx={idx} steps={mySteps} />
))}
</Sidebar>
<div className="ml-12">
<Component data={data} addData={addData} doContinue={doContinue} />
</div>
</div>
</div>
)
}
export default Wallet

View file

@ -0,0 +1,23 @@
import * as R from 'ramda'
import _schema from 'src/pages/Services/schemas'
const contains = crypto => R.compose(R.contains(crypto), R.prop('cryptos'))
const sameClass = type => R.propEq('class', type)
const filterConfig = (crypto, type) =>
R.filter(it => sameClass(type)(it) && contains(crypto)(it))
export const getItems = (accountsConfig, accounts, type, crypto) => {
const schema = _schema()
const fConfig = filterConfig(crypto, type)(accountsConfig)
const find = code => accounts && accounts[code]
const [filled, unfilled] = R.partition(({ code }) => {
const account = find(code)
if (!schema[code]) return true
const { getValidationSchema } = schema[code]
return getValidationSchema(account).isValidSync(account)
})(fConfig)
return { filled, unfilled }
}

View file

@ -0,0 +1,22 @@
import React from 'react'
import { H1, P } from 'src/components/typography'
import { Button } from 'src/components/buttons'
function Welcome({ doContinue }) {
return (
<div className="text-center flex flex-col items-center justify-center w-full h-full">
<H1 className="text-5xl">Welcome to the Lamassu Admin</H1>
<P className="text-2xl mb-14 text-comet">
To get you started, weve put together a wizard that will
<br />
help set up what you need before pairing your machines.
</P>
<Button size="xl" onClick={doContinue}>
Get started
</Button>
</div>
)
}
export default Welcome