feat: add apollolink split to query both lamassu and pazuz apollo servers

feat: integrate accounting with pazuz related screens
This commit is contained in:
Sérgio Salgado 2021-10-12 19:03:41 +01:00
parent 16513a8238
commit 9260e4a698
5 changed files with 314 additions and 193 deletions

View file

@ -97,8 +97,8 @@
"storybook": "start-storybook -p 9009 -s public", "storybook": "start-storybook -p 9009 -s public",
"postinstall": "patch-package", "postinstall": "patch-package",
"build-storybook": "build-storybook -s public", "build-storybook": "build-storybook -s public",
"start-lamassu": "REACT_APP_BUILD_TARGET=LAMASSU react-scripts start", "lamassu": "REACT_APP_BUILD_TARGET=LAMASSU react-scripts start",
"start-pazuz": "REACT_APP_BUILD_TARGET=PAZUZ react-scripts start" "pazuz": "REACT_APP_BUILD_TARGET=PAZUZ react-scripts start"
}, },
"browserslist": { "browserslist": {
"production": [ "production": [

View file

@ -1,9 +1,12 @@
import { useQuery } from '@apollo/react-hooks'
import { Paper } from '@material-ui/core' import { Paper } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import classnames from 'classnames' import classnames from 'classnames'
import gql from 'graphql-tag'
import * as R from 'ramda' import * as R from 'ramda'
import React from 'react' import React, { useContext } from 'react'
import AppContext from 'src/AppContext'
import TitleSection from 'src/components/layout/TitleSection' import TitleSection from 'src/components/layout/TitleSection'
import { H3, Info2, Label2, Label3, P } from 'src/components/typography' import { H3, Info2, Label2, Label3, P } from 'src/components/typography'
import { ReactComponent as BitcoinLogo } from 'src/styling/logos/icon-bitcoin-colour.svg' import { ReactComponent as BitcoinLogo } from 'src/styling/logos/icon-bitcoin-colour.svg'
@ -17,6 +20,37 @@ import styles from './ATMWallet.styles'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
const GET_OPERATOR_BY_USERNAME = gql`
query operatorByUsername($username: String) {
operatorByUsername(username: $username) {
id
entityId
name
fiatBalances
cryptoBalances
machines
joined
assetValue
preferredFiatCurrency
contactInfo {
name
email
}
fundings {
id
origin
destination
fiatAmount
fiatBalanceAfter
fiatCurrency
created
status
description
}
}
}
`
const CHIPS_PER_ROW = 6 const CHIPS_PER_ROW = 6
const Assets = ({ balance, wallets, currency }) => { const Assets = ({ balance, wallets, currency }) => {
@ -35,7 +69,7 @@ const Assets = ({ balance, wallets, currency }) => {
{balance.toLocaleString('en-US', { maximumFractionDigits: 2 })} {balance.toLocaleString('en-US', { maximumFractionDigits: 2 })}
</Info2> </Info2>
<Info2 noMargin className={classes.fieldCurrency}> <Info2 noMargin className={classes.fieldCurrency}>
{currency} {R.toUpper(currency)}
</Info2> </Info2>
</div> </div>
</div> </div>
@ -49,7 +83,7 @@ const Assets = ({ balance, wallets, currency }) => {
})} })}
</Info2> </Info2>
<Info2 noMargin className={classes.fieldCurrency}> <Info2 noMargin className={classes.fieldCurrency}>
{currency} {R.toUpper(currency)}
</Info2> </Info2>
</div> </div>
</div> </div>
@ -61,7 +95,7 @@ const Assets = ({ balance, wallets, currency }) => {
{balance.toLocaleString('en-US', { maximumFractionDigits: 2 })} {balance.toLocaleString('en-US', { maximumFractionDigits: 2 })}
</Info2> </Info2>
<Info2 noMargin className={classes.fieldCurrency}> <Info2 noMargin className={classes.fieldCurrency}>
{currency} {R.toUpper(currency)}
</Info2> </Info2>
</div> </div>
</div> </div>
@ -129,66 +163,82 @@ const WalletInfoChip = ({ wallet, currency }) => {
const ATMWallet = () => { const ATMWallet = () => {
const classes = useStyles({ numberOfChips: CHIPS_PER_ROW }) const classes = useStyles({ numberOfChips: CHIPS_PER_ROW })
const { userData } = useContext(AppContext)
const { data, loading } = useQuery(GET_OPERATOR_BY_USERNAME, {
context: { clientName: 'pazuz' },
variables: { username: userData?.username }
})
const operatorData = R.path(['operatorByUsername'], data)
const wallets = [ const wallets = [
{ {
cryptoCode: 'BTC', cryptoCode: 'BTC',
name: 'Bitcoin', name: 'Bitcoin',
amount: 2.7, amount: operatorData?.cryptoBalances.xbt ?? 0,
fiatValue: 81452, fiatValue: 0,
isHedged: true isHedged: true
}, },
{ {
cryptoCode: 'ETH', cryptoCode: 'ETH',
name: 'Ethereum', name: 'Ethereum',
amount: 4.1, amount: operatorData?.cryptoBalances.eth ?? 0,
fiatValue: 4924, fiatValue: 0,
isHedged: true isHedged: true
}, },
{ {
cryptoCode: 'LTC', cryptoCode: 'LTC',
name: 'Litecoin', name: 'Litecoin',
amount: 15, amount: operatorData?.cryptoBalances.ltc ?? 0,
fiatValue: 3016, fiatValue: 0,
isHedged: true isHedged: true
}, },
{ {
cryptoCode: 'ZEC', cryptoCode: 'ZEC',
name: 'Z-Cash', name: 'Z-Cash',
amount: 20, amount: operatorData?.cryptoBalances.zec ?? 0,
fiatValue: 2887, fiatValue: 0,
isHedged: false isHedged: false
}, },
{ {
cryptoCode: 'BCH', cryptoCode: 'BCH',
name: 'Bitcoin Cash', name: 'Bitcoin Cash',
amount: 10.7, amount: operatorData?.cryptoBalances.bch ?? 0,
fiatValue: 7074, fiatValue: 0,
isHedged: true isHedged: true
}, },
{ {
cryptoCode: 'DASH', cryptoCode: 'DASH',
name: 'Dash', name: 'Dash',
amount: 10.7, amount: operatorData?.cryptoBalances.dash ?? 0,
fiatValue: 1091, fiatValue: 0,
isHedged: false isHedged: false
} }
] ]
return ( return (
<> !loading && (
<TitleSection title="ATM Wallets" /> <>
<Assets balance={8952} wallets={wallets} currency={'USD'} /> <TitleSection title="ATM Wallets" />
<H3 className={classes.walletChipTitle}>ATM Wallets</H3> <Assets
<div className={classes.walletChipList}> balance={
{R.map( operatorData.fiatBalances[operatorData.preferredFiatCurrency]
it => ( }
<WalletInfoChip wallet={it} currency={'USD'} /> wallets={wallets}
), currency={operatorData.preferredFiatCurrency}
wallets />
)} <H3 className={classes.walletChipTitle}>ATM Wallets</H3>
</div> <div className={classes.walletChipList}>
</> {R.map(
it => (
<WalletInfoChip wallet={it} currency={'USD'} />
),
wallets
)}
</div>
</>
)
) )
} }

View file

@ -1,7 +1,11 @@
import { useQuery } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import gql from 'graphql-tag'
import moment from 'moment' import moment from 'moment'
import React from 'react' import * as R from 'ramda'
import React, { useContext } from 'react'
import AppContext from 'src/AppContext'
import { Tooltip } from 'src/components/Tooltip' import { Tooltip } from 'src/components/Tooltip'
import TitleSection from 'src/components/layout/TitleSection' import TitleSection from 'src/components/layout/TitleSection'
import DataTable from 'src/components/tables/DataTable' import DataTable from 'src/components/tables/DataTable'
@ -9,47 +13,42 @@ import { H4, Info2, P } from 'src/components/typography'
import styles from './Accounting.styles' import styles from './Accounting.styles'
const mockData = [
{
operation: 'Hedging summary',
direction: 'in',
extraInfo: 'This is mocked information',
amount: 486,
currency: 'USD',
balanceAfterTx: 10438,
date: '2021-02-22T20:16:12.020Z'
},
{
operation: 'Funding transaction',
direction: 'in',
amount: 2000,
currency: 'USD',
balanceAfterTx: 9952,
date: '2021-02-22T12:40:32.020Z'
},
{
operation: 'ZEC hot wallet top up',
direction: 'out',
amount: 1000,
currency: 'USD',
balanceAfterTx: 7952,
date: '2021-02-21T16:30:44.020Z'
},
{
operation: 'Funding transaction',
direction: 'in',
amount: 8000,
currency: 'USD',
balanceAfterTx: 8952,
date: '2021-02-21T08:16:20.020Z'
}
]
const formatCurrency = amount => const formatCurrency = amount =>
amount.toLocaleString('en-US', { maximumFractionDigits: 2 }) amount.toLocaleString('en-US', { maximumFractionDigits: 2 })
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
const GET_OPERATOR_BY_USERNAME = gql`
query operatorByUsername($username: String) {
operatorByUsername(username: $username) {
id
entityId
name
fiatBalances
cryptoBalances
machines
joined
assetValue
preferredFiatCurrency
contactInfo {
name
email
}
fundings {
id
origin
destination
fiatAmount
fiatBalanceAfter
fiatCurrency
created
status
description
}
}
}
`
const Assets = ({ balance, hedgingReserve, currency }) => { const Assets = ({ balance, hedgingReserve, currency }) => {
const classes = useStyles() const classes = useStyles()
@ -62,7 +61,7 @@ const Assets = ({ balance, hedgingReserve, currency }) => {
{formatCurrency(balance)} {formatCurrency(balance)}
</Info2> </Info2>
<Info2 noMargin className={classes.fieldCurrency}> <Info2 noMargin className={classes.fieldCurrency}>
{currency} {R.toUpper(currency)}
</Info2> </Info2>
</div> </div>
</div> </div>
@ -74,7 +73,7 @@ const Assets = ({ balance, hedgingReserve, currency }) => {
{formatCurrency(hedgingReserve)} {formatCurrency(hedgingReserve)}
</Info2> </Info2>
<Info2 noMargin className={classes.fieldCurrency}> <Info2 noMargin className={classes.fieldCurrency}>
{currency} {R.toUpper(currency)}
</Info2> </Info2>
</div> </div>
</div> </div>
@ -86,7 +85,7 @@ const Assets = ({ balance, hedgingReserve, currency }) => {
{formatCurrency(balance - hedgingReserve)} {formatCurrency(balance - hedgingReserve)}
</Info2> </Info2>
<Info2 noMargin className={classes.fieldCurrency}> <Info2 noMargin className={classes.fieldCurrency}>
{currency} {R.toUpper(currency)}
</Info2> </Info2>
</div> </div>
</div> </div>
@ -96,6 +95,14 @@ const Assets = ({ balance, hedgingReserve, currency }) => {
const Accounting = () => { const Accounting = () => {
const classes = useStyles() const classes = useStyles()
const { userData } = useContext(AppContext)
const { data, loading } = useQuery(GET_OPERATOR_BY_USERNAME, {
context: { clientName: 'pazuz' },
variables: { username: userData?.username }
})
const operatorData = R.path(['operatorByUsername'], data)
const elements = [ const elements = [
{ {
@ -106,7 +113,7 @@ const Accounting = () => {
view: it => { view: it => {
return ( return (
<span className={classes.operation}> <span className={classes.operation}>
{it.operation} {it.description}
{!!it.extraInfo && ( {!!it.extraInfo && (
<Tooltip width={175}> <Tooltip width={175}>
<P>{it.extraInfo}</P> <P>{it.extraInfo}</P>
@ -122,18 +129,15 @@ const Accounting = () => {
size: 'sm', size: 'sm',
textAlign: 'right', textAlign: 'right',
view: it => view: it =>
`${ `${formatCurrency(it.fiatAmount)} ${R.toUpper(it.fiatCurrency)}`
it.direction === 'in'
? formatCurrency(it.amount)
: formatCurrency(-it.amount)
} ${it.currency}`
}, },
{ {
header: 'Balance after operation', header: 'Balance after operation',
width: 250, width: 250,
size: 'sm', size: 'sm',
textAlign: 'right', textAlign: 'right',
view: it => `${formatCurrency(it.balanceAfterTx)} ${it.currency}` view: it =>
`${formatCurrency(it.fiatBalanceAfter)} ${R.toUpper(it.fiatCurrency)}`
}, },
{ {
header: 'Date', header: 'Date',
@ -152,18 +156,26 @@ const Accounting = () => {
] ]
return ( return (
<> !loading && (
<TitleSection title="Accounting" /> <>
<Assets balance={10438} hedgingReserve={1486} currency={'USD'} /> <TitleSection title="Accounting" />
<H4 className={classes.tableTitle}>Fiat balance history</H4> <Assets
<DataTable balance={
loading={false} operatorData.fiatBalances[operatorData.preferredFiatCurrency]
emptyText="No transactions so far" }
elements={elements} hedgingReserve={operatorData.hedgingReserve ?? 0}
data={mockData} currency={operatorData.preferredFiatCurrency}
rowSize="sm" />
/> <H4 className={classes.tableTitle}>Fiat balance history</H4>
</> <DataTable
loading={false}
emptyText="No transactions so far"
elements={elements}
data={operatorData.fundings ?? []}
rowSize="sm"
/>
</>
)
) )
} }

View file

@ -1,3 +1,4 @@
import { useQuery } from '@apollo/react-hooks'
import Grid from '@material-ui/core/Grid' import Grid from '@material-ui/core/Grid'
import Table from '@material-ui/core/Table' import Table from '@material-ui/core/Table'
import TableBody from '@material-ui/core/TableBody' import TableBody from '@material-ui/core/TableBody'
@ -6,78 +7,47 @@ import TableContainer from '@material-ui/core/TableContainer'
import TableHead from '@material-ui/core/TableHead' import TableHead from '@material-ui/core/TableHead'
import TableRow from '@material-ui/core/TableRow' import TableRow from '@material-ui/core/TableRow'
import { makeStyles, withStyles } from '@material-ui/core/styles' import { makeStyles, withStyles } from '@material-ui/core/styles'
import gql from 'graphql-tag'
import * as R from 'ramda' import * as R from 'ramda'
import React from 'react' import React, { useContext } from 'react'
import AppContext from 'src/AppContext'
import TitleSection from 'src/components/layout/TitleSection' import TitleSection from 'src/components/layout/TitleSection'
import { H4, Label2, P, Info2 } from 'src/components/typography' import { H4, Label2, P, Info2 } from 'src/components/typography'
import styles from './Assets.styles' import styles from './Assets.styles'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
const mockData = [ const GET_OPERATOR_BY_USERNAME = gql`
{ query operatorByUsername($username: String) {
id: 'fiatBalance', operatorByUsername(username: $username) {
display: 'Fiat balance', id
amount: 10438, entityId
currency: 'USD', name
class: 'Available balance' fiatBalances
}, cryptoBalances
{ machines
id: 'hedgingReserve', joined
display: 'Hedging reserve', assetValue
amount: -1486, preferredFiatCurrency
currency: 'USD', contactInfo {
class: 'Available balance', name
direction: 'out' email
}, }
{ fundings {
id: 'hedgedWalletAssets', id
display: 'Hedged wallet assets', origin
amount: 96446, destination
currency: 'USD', fiatAmount
class: 'Wallet assets', fiatBalanceAfter
direction: 'in' fiatCurrency
}, created
{ status
id: 'unhedgedWalletAssets', description
display: 'Unhedged wallet assets', }
amount: 3978, }
currency: 'USD',
class: 'Wallet assets',
direction: 'in'
} }
] `
const mockDataTotal = [
{
id: 'fiatBalance',
display: 'Fiat balance',
amount: 10438,
currency: 'USD'
},
{
id: 'hedgingReserve',
display: 'Hedging reserve',
amount: -1486,
currency: 'USD',
direction: 'out'
},
{
id: 'hedgedWalletAssets',
display: 'Market value of hedged wallet assets',
amount: 94980,
currency: 'USD',
direction: 'in'
},
{
id: 'unhedgedWalletAssets',
display: 'Unhedged wallet assets',
amount: 3978,
currency: 'USD',
direction: 'in'
}
]
const cellStyling = { const cellStyling = {
borderBottom: '4px solid white', borderBottom: '4px solid white',
@ -163,47 +133,126 @@ const formatCurrency = amount =>
const Assets = () => { const Assets = () => {
const classes = useStyles() const classes = useStyles()
const { userData } = useContext(AppContext)
const filterByClass = x => const { data, loading } = useQuery(GET_OPERATOR_BY_USERNAME, {
R.filter(it => R.path(['class'])(it) === x)(mockData) context: { clientName: 'pazuz' },
variables: { username: userData?.username }
})
const operatorData = R.path(['operatorByUsername'], data)
console.log('operatorData', operatorData)
const balanceData = [
{
id: 'fiatBalance',
display: 'Fiat balance',
amount:
operatorData?.fiatBalances[operatorData?.preferredFiatCurrency] ?? 0,
currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''),
class: 'Available balance'
},
{
id: 'hedgingReserve',
display: 'Hedging reserve',
amount:
operatorData?.fiatBalances[operatorData?.preferredFiatCurrency] ?? 0,
currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''),
class: 'Available balance',
direction: 'out'
}
]
const walletData = [
{
id: 'hedgedWalletAssets',
display: 'Hedged wallet assets',
amount: 0,
currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''),
class: 'Wallet assets',
direction: 'in'
},
{
id: 'unhedgedWalletAssets',
display: 'Unhedged wallet assets',
amount: 0,
currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''),
class: 'Wallet assets',
direction: 'in'
}
]
const totalData = [
{
id: 'fiatBalance',
display: 'Fiat balance',
amount:
operatorData?.fiatBalances[operatorData?.preferredFiatCurrency] ?? 0,
currency: R.toUpper(operatorData?.preferredFiatCurrency ?? '')
},
{
id: 'hedgingReserve',
display: 'Hedging reserve',
amount: 0,
currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''),
direction: 'out'
},
{
id: 'hedgedWalletAssets',
display: 'Market value of hedged wallet assets',
amount: 0,
currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''),
direction: 'in'
},
{
id: 'unhedgedWalletAssets',
display: 'Unhedged wallet assets',
amount: 0,
currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''),
direction: 'in'
}
]
return ( return (
<> !loading && (
<TitleSection title="Balance sheet" /> <>
<div className={classes.root}> <TitleSection title="Balance sheet" />
<Grid container> <div className={classes.root}>
<Grid container direction="column" item xs={5}> <Grid container>
<Grid item xs={12}> <Grid container direction="column" item xs={5}>
<div className={classes.leftSide}> <Grid item xs={12}>
<AssetsAmountTable <div className={classes.leftSide}>
title="Available balance" <AssetsAmountTable
data={filterByClass('Available balance')} title="Available balance"
numToRender={mockData.length} data={balanceData}
/> numToRender={balanceData.length}
</div> />
<div className={classes.leftSide}> </div>
<AssetsAmountTable <div className={classes.leftSide}>
title="Wallet assets" <AssetsAmountTable
data={filterByClass('Wallet assets')} title="Wallet assets"
numToRender={mockData.length} data={walletData}
/> numToRender={walletData.length}
</div> />
</div>
</Grid>
</Grid>
<Grid container direction="column" item xs={7}>
<Grid item xs={12}>
<div className={classes.rightSide}>
<AssetsAmountTable
title="Total assets"
data={totalData}
numToRender={totalData.length}
/>
</div>
</Grid>
</Grid> </Grid>
</Grid> </Grid>
<Grid container direction="column" item xs={7}> </div>
<Grid item xs={12}> </>
<div className={classes.rightSide}> )
<AssetsAmountTable
title="Total assets"
data={mockDataTotal}
numToRender={mockDataTotal.length}
/>
</div>
</Grid>
</Grid>
</Grid>
</div>
</>
) )
} }

View file

@ -12,6 +12,9 @@ import AppContext from 'src/AppContext'
const URI = const URI =
process.env.NODE_ENV === 'development' ? 'https://localhost:8070' : '' process.env.NODE_ENV === 'development' ? 'https://localhost:8070' : ''
const ALT_URI =
process.env.NODE_ENV === 'development' ? 'http://localhost:4001' : ''
const getClient = (history, location, getUserData, setUserData, setRole) => const getClient = (history, location, getUserData, setUserData, setRole) =>
new ApolloClient({ new ApolloClient({
link: ApolloLink.from([ link: ApolloLink.from([
@ -43,10 +46,17 @@ const getClient = (history, location, getUserData, setUserData, setRole) =>
return response return response
}) })
}), }),
new HttpLink({ ApolloLink.split(
credentials: 'include', operation => operation.getContext().clientName === 'pazuz',
uri: `${URI}/graphql` new HttpLink({
}) credentials: 'include',
uri: `${ALT_URI}/graphql`
}),
new HttpLink({
credentials: 'include',
uri: `${URI}/graphql`
})
)
]), ]),
cache: new InMemoryCache(), cache: new InMemoryCache(),
defaultOptions: { defaultOptions: {