chore: pazuz removal

This commit is contained in:
Rafael 2024-11-29 19:25:05 +00:00
parent 770fa13ea0
commit f936386712
21 changed files with 14 additions and 1477 deletions

View file

@ -20,7 +20,6 @@ const buildApolloContext = async ({ req, res }) => {
req.session.user.role = user.role
res.set('lamassu_role', user.role)
res.cookie('pazuz_operatoridentifier', base64.encode(user.username))
res.set('Access-Control-Expose-Headers', 'lamassu_role')
return { req, res }

View file

@ -2,4 +2,3 @@ SKIP_PREFLIGHT_CHECK=true
HTTPS=true
REACT_APP_TYPE_CHECK_SANCTUARY=false
PORT=3001
REACT_APP_BUILD_TARGET=LAMASSU

View file

@ -92,9 +92,7 @@
"analyze": "source-map-explorer 'build/static/js/*.js'",
"test": "react-scripts test",
"eject": "react-scripts eject",
"postinstall": "patch-package",
"lamassu": "REACT_APP_BUILD_TARGET=LAMASSU react-scripts start",
"pazuz": "REACT_APP_BUILD_TARGET=PAZUZ react-scripts start"
"postinstall": "patch-package"
},
"browserslist": {
"production": [

View file

@ -2,30 +2,10 @@ import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import * as serviceWorker from './serviceWorker'
function checkBuildTarget() {
const buildTarget = process.env.REACT_APP_BUILD_TARGET
if (buildTarget !== 'LAMASSU' && buildTarget !== 'PAZUZ') {
return Promise.reject(
new Error('No such build target: ' + process.env.REACT_APP_BUILD_TARGET)
)
}
return Promise.resolve()
}
checkBuildTarget().then(() =>
ReactDOM.render(
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
)
)
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister()

View file

@ -1,258 +0,0 @@
import { useQuery } from '@apollo/react-hooks'
import { Paper } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles'
import BigNumber from 'bignumber.js'
import classnames from 'classnames'
import gql from 'graphql-tag'
import * as R from 'ramda'
import React, { useContext } from 'react'
import AppContext from 'src/AppContext'
import TitleSection from 'src/components/layout/TitleSection'
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 BitcoinCashLogo } from 'src/styling/logos/icon-bitcoincash-colour.svg'
import { ReactComponent as DashLogo } from 'src/styling/logos/icon-dash-colour.svg'
import { ReactComponent as EthereumLogo } from 'src/styling/logos/icon-ethereum-colour.svg'
import { ReactComponent as LitecoinLogo } from 'src/styling/logos/icon-litecoin-colour.svg'
import { ReactComponent as ZCashLogo } from 'src/styling/logos/icon-zcash-colour.svg'
import { numberToFiatAmount, numberToCryptoAmount } from 'src/utils/number'
import styles from './ATMWallet.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
assets
preferredFiatCurrency
contactInfo {
name
email
}
fundings {
id
origin
destination
fiatAmount
fiatBalanceAfter
fiatCurrency
created
status
description
}
}
}
`
const CHIPS_PER_ROW = 6
const Assets = ({ balance, wallets, currency }) => {
const classes = useStyles({ numberOfChips: CHIPS_PER_ROW })
const walletFiatSum = R.sum(R.map(it => it.fiatValue, wallets))
const totalValue = BigNumber(balance)
.plus(walletFiatSum)
.toNumber()
return (
<div className={classes.totalAssetWrapper}>
<div className={classes.totalAssetFieldWrapper}>
<P className={classes.fieldHeader}>Available balance</P>
<div className={classes.totalAssetWrapper}>
<Info2 noMargin className={classes.fieldValue}>
{numberToFiatAmount(balance)}
</Info2>
<Info2 noMargin className={classes.fieldCurrency}>
{R.toUpper(currency)}
</Info2>
</div>
</div>
<Info2 className={classes.separator}>+</Info2>
<div className={classes.totalAssetFieldWrapper}>
<P className={classes.fieldHeader}>Total balance in wallets</P>
<div className={classes.totalAssetWrapper}>
<Info2 noMargin className={classes.fieldValue}>
{numberToFiatAmount(walletFiatSum)}
</Info2>
<Info2 noMargin className={classes.fieldCurrency}>
{R.toUpper(currency)}
</Info2>
</div>
</div>
<Info2 className={classes.separator}>=</Info2>
<div className={classes.totalAssetFieldWrapper}>
<P className={classes.fieldHeader}>Total assets</P>
<div className={classes.totalAssetWrapper}>
<Info2 noMargin className={classes.fieldValue}>
{numberToFiatAmount(totalValue)}
</Info2>
<Info2 noMargin className={classes.fieldCurrency}>
{R.toUpper(currency)}
</Info2>
</div>
</div>
</div>
)
}
const WalletInfoChip = ({ wallet, currency }) => {
const classes = useStyles({ numberOfChips: CHIPS_PER_ROW })
const getLogo = cryptoCode => {
switch (cryptoCode) {
case 'BTC':
return <BitcoinLogo className={classes.logo} />
case 'ETH':
return <EthereumLogo className={classes.logo} />
case 'LTC':
return <LitecoinLogo className={classes.logo} />
case 'ZEC':
return (
<ZCashLogo className={classnames(classes.logo, classes.zecLogo)} />
)
case 'BCH':
return (
<BitcoinCashLogo
className={classnames(classes.logo, classes.bchLogo)}
/>
)
case 'DASH':
return <DashLogo className={classes.logo} />
default:
return <BitcoinLogo className={classes.logo} />
}
}
return (
<div className={classes.walletChipWrapper}>
<Paper className={classes.walletChip}>
<div className={classes.walletHeader}>
{getLogo(wallet.cryptoCode)}
<Label3 className={classes.hedgedText}>
{wallet.isHedged ? 'Hedged' : 'Not hedged'}
</Label3>
</div>
<div className={classes.walletValueWrapper}>
<Label2 className={classes.fieldHeader}>{wallet.name} value</Label2>
<Label2 className={classes.walletValue}>
{numberToCryptoAmount(wallet.amount)} {wallet.cryptoCode}
</Label2>
<Label2 className={classes.fieldHeader}>Hedged value</Label2>
<Label2 className={classes.walletValue}>
{numberToFiatAmount(wallet.hedgedFiatValue)} {currency}
</Label2>
</div>
</Paper>
</div>
)
}
const ATMWallet = () => {
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 = [
{
cryptoCode: 'BTC',
name: 'Bitcoin',
amount: operatorData?.cryptoBalances.xbt ?? 0,
fiatValue: operatorData?.assets.values.cryptoBalancesInFiat.xbt ?? 0,
hedgedFiatValue: operatorData?.assets.values.hedgedContracts.xbt ?? 0,
isHedged: BigNumber(operatorData?.assets.values.hedgedCrypto.xbt ?? 0).gt(
0
)
},
{
cryptoCode: 'ETH',
name: 'Ethereum',
amount: operatorData?.cryptoBalances.eth ?? 0,
fiatValue: operatorData?.assets.values.cryptoBalancesInFiat.eth ?? 0,
hedgedFiatValue: operatorData?.assets.values.hedgedContracts.eth ?? 0,
isHedged: BigNumber(operatorData?.assets.values.hedgedCrypto.eth ?? 0).gt(
0
)
},
{
cryptoCode: 'LTC',
name: 'Litecoin',
amount: operatorData?.cryptoBalances.ltc ?? 0,
fiatValue: operatorData?.assets.values.cryptoBalancesInFiat.ltc ?? 0,
hedgedFiatValue: operatorData?.assets.values.hedgedContracts.ltc ?? 0,
isHedged: BigNumber(operatorData?.assets.values.hedgedCrypto.ltc ?? 0).gt(
0
)
},
{
cryptoCode: 'ZEC',
name: 'Zcash',
amount: operatorData?.cryptoBalances.zec ?? 0,
fiatValue: operatorData?.assets.values.cryptoBalancesInFiat.zec ?? 0,
hedgedFiatValue: operatorData?.assets.values.hedgedContracts.zec ?? 0,
isHedged: BigNumber(operatorData?.assets.values.hedgedCrypto.zec ?? 0).gt(
0
)
},
{
cryptoCode: 'BCH',
name: 'Bitcoin Cash',
amount: operatorData?.cryptoBalances.bch ?? 0,
fiatValue: operatorData?.assets.values.cryptoBalancesInFiat.bch ?? 0,
hedgedFiatValue: operatorData?.assets.values.hedgedContracts.bch ?? 0,
isHedged: BigNumber(operatorData?.assets.values.hedgedCrypto.bch ?? 0).gt(
0
)
},
{
cryptoCode: 'DASH',
name: 'Dash',
amount: operatorData?.cryptoBalances.dash ?? 0,
fiatValue: operatorData?.assets.values.cryptoBalancesInFiat.dash ?? 0,
hedgedFiatValue: operatorData?.assets.values.hedgedContracts.dash ?? 0,
isHedged: BigNumber(
operatorData?.assets.values.hedgedCrypto.dash ?? 0
).gt(0)
}
]
return (
!loading && (
<>
<TitleSection title="ATM Wallets" />
<Assets
balance={
operatorData?.fiatBalances[operatorData.preferredFiatCurrency] ?? 0
}
wallets={wallets}
currency={operatorData?.preferredFiatCurrency ?? ''}
/>
<H3 className={classes.walletChipTitle}>ATM Wallets</H3>
<div className={classes.walletChipList}>
{R.map(
it => (
<WalletInfoChip wallet={it} currency={'USD'} />
),
wallets
)}
</div>
</>
)
)
}
export default ATMWallet

View file

@ -1,90 +0,0 @@
import { offColor } from 'src/styling/variables'
const styles = ({ numberOfChips }) => ({
totalAssetWrapper: {
display: 'flex',
flexDirection: 'row'
},
totalAssetFieldWrapper: {
display: 'flex',
flexDirection: 'column'
},
fieldHeader: {
color: offColor,
marginBottom: 5
},
fieldValue: {
fontSize: 36
},
fieldCurrency: {
fontSize: 20,
alignSelf: 'flex-end',
margin: [[0, 0, 5, 5]]
},
separator: {
fontSize: 32,
alignSelf: 'center',
margin: [[25, 20, 0, 20]]
},
walletChipList: {
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap'
},
walletChipWrapper: {
flexGrow: 0,
flexShrink: 0,
flexBasis: `16.66667%`,
'&:nth-child(6n+1)': {
'& > div': {
margin: [[0, 10, 0, 0]]
}
},
'&:nth-child(6n)': {
'& > div': {
margin: [[0, 0, 0, 10]]
}
},
margin: [[10, 0]]
},
walletChip: {
height: 200,
margin: [[0, 10]]
},
walletHeader: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
height: 50
},
logo: {
transform: `scale(0.4, 0.4)`,
height: 80,
maxWidth: 110,
margin: [[-14, 0, 0, -26]]
},
zecLogo: {
margin: [[-15, 0, 0, -10]]
},
bchLogo: {
margin: [[-12, 0, 0, -18]]
},
hedgedText: {
color: offColor,
margin: [[13, 12, 0, 0]]
},
walletValueWrapper: {
display: 'flex',
flexDirection: 'column',
margin: [[0, 0, 0, 15]]
},
walletValue: {
fontSize: 18,
margin: [[0, 0, 10, 0]]
},
walletChipTitle: {
marginTop: 50
}
})
export default styles

View file

@ -1,203 +0,0 @@
import { useQuery } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core/styles'
import BigNumber from 'bignumber.js'
import gql from 'graphql-tag'
import * as R from 'ramda'
import React, { useContext } from 'react'
import AppContext from 'src/AppContext'
import { HelpTooltip } from 'src/components/Tooltip'
import TitleSection from 'src/components/layout/TitleSection'
import DataTable from 'src/components/tables/DataTable'
import { H4, Info2, P } from 'src/components/typography'
import { numberToFiatAmount } from 'src/utils/number'
import { formatDate } from 'src/utils/timezones'
import styles from './Accounting.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
assets
preferredFiatCurrency
contactInfo {
name
email
}
fundings {
id
origin
destination
fiatAmount
fiatBalanceAfter
fiatCurrency
created
status
description
}
}
}
`
const GET_DATA = gql`
query getData {
config
}
`
const Assets = ({ balance, hedgingReserve, currency }) => {
const classes = useStyles()
return (
<div className={classes.totalAssetWrapper}>
<div className={classes.totalAssetFieldWrapper}>
<P className={classes.fieldHeader}>Pazuz fiat balance</P>
<div className={classes.totalAssetWrapper}>
<Info2 noMargin className={classes.fieldValue}>
{numberToFiatAmount(balance)}
</Info2>
<Info2 noMargin className={classes.fieldCurrency}>
{R.toUpper(currency)}
</Info2>
</div>
</div>
<Info2 className={classes.separator}>-</Info2>
<div className={classes.totalAssetFieldWrapper}>
<P className={classes.fieldHeader}>Hedging reserve</P>
<div className={classes.totalAssetWrapper}>
<Info2 noMargin className={classes.fieldValue}>
{numberToFiatAmount(hedgingReserve)}
</Info2>
<Info2 noMargin className={classes.fieldCurrency}>
{R.toUpper(currency)}
</Info2>
</div>
</div>
<Info2 className={classes.separator}>=</Info2>
<div className={classes.totalAssetFieldWrapper}>
<P className={classes.fieldHeader}>Available balance</P>
<div className={classes.totalAssetWrapper}>
<Info2 noMargin className={classes.fieldValue}>
{numberToFiatAmount(balance - hedgingReserve)}
</Info2>
<Info2 noMargin className={classes.fieldCurrency}>
{R.toUpper(currency)}
</Info2>
</div>
</div>
</div>
)
}
const Accounting = () => {
const classes = useStyles()
const { userData } = useContext(AppContext)
const { data: opData, loading: operatorLoading } = useQuery(
GET_OPERATOR_BY_USERNAME,
{
context: { clientName: 'pazuz' },
variables: { username: userData?.username }
}
)
const { data: configResponse, loading: configLoading } = useQuery(GET_DATA)
const timezone = R.path(['config', 'locale_timezone'], configResponse)
const loading = operatorLoading || configLoading
const operatorData = R.path(['operatorByUsername'], opData)
const elements = [
{
header: 'Operation',
width: 500,
size: 'sm',
textAlign: 'left',
view: it => {
return (
<span className={classes.operation}>
{it.description}
{!!it.extraInfo && (
<HelpTooltip width={175}>
<P>{it.extraInfo}</P>
</HelpTooltip>
)}
</span>
)
}
},
{
header: 'Amount',
width: 147,
size: 'sm',
textAlign: 'right',
view: it =>
`${numberToFiatAmount(it.fiatAmount)} ${R.toUpper(it.fiatCurrency)}`
},
{
header: 'Balance after operation',
width: 250,
size: 'sm',
textAlign: 'right',
view: it =>
`${numberToFiatAmount(it.fiatBalanceAfter)} ${R.toUpper(
it.fiatCurrency
)}`
},
{
header: 'Date',
width: 150,
size: 'sm',
textAlign: 'right',
view: it => formatDate(it.created, timezone, 'yyyy-MM-dd')
},
{
header: 'Time',
width: 150,
size: 'sm',
textAlign: 'right',
view: it => formatDate(it.created, timezone, 'yyyy-MM-dd')
}
]
const hedgingReserve = BigNumber(
R.reduce(
(acc, value) => acc.plus(value),
BigNumber(0),
R.values(operatorData?.assets.values.hedgedContracts) ?? []
) ?? 0
).toNumber()
return (
!loading && (
<>
<TitleSection title="Accounting" />
<Assets
balance={operatorData?.assets.total ?? 0}
hedgingReserve={hedgingReserve}
currency={operatorData?.preferredFiatCurrency ?? ''}
/>
<H4 className={classes.tableTitle}>Fiat balance history</H4>
<DataTable
loading={loading}
emptyText="No transactions so far"
elements={elements}
data={operatorData?.fundings ?? []}
rowSize="sm"
/>
</>
)
)
}
export default Accounting

View file

@ -1,39 +0,0 @@
import { offColor } from 'src/styling/variables'
const styles = () => ({
totalAssetWrapper: {
display: 'flex',
flexDirection: 'row'
},
totalAssetFieldWrapper: {
display: 'flex',
flexDirection: 'column'
},
fieldHeader: {
color: offColor,
marginBottom: 5
},
fieldValue: {
fontSize: 36
},
fieldCurrency: {
fontSize: 20,
alignSelf: 'flex-end',
margin: [[0, 0, 5, 5]]
},
separator: {
fontSize: 32,
alignSelf: 'center',
margin: [[25, 20, 0, 20]]
},
tableTitle: {
marginTop: 35
},
operation: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center'
}
})
export default styles

View file

@ -1,258 +0,0 @@
import { useQuery } from '@apollo/react-hooks'
import Grid from '@material-ui/core/Grid'
import Table from '@material-ui/core/Table'
import TableBody from '@material-ui/core/TableBody'
import TableCell from '@material-ui/core/TableCell'
import TableContainer from '@material-ui/core/TableContainer'
import TableHead from '@material-ui/core/TableHead'
import TableRow from '@material-ui/core/TableRow'
import { makeStyles, withStyles } from '@material-ui/core/styles'
import gql from 'graphql-tag'
import * as R from 'ramda'
import React, { useContext } from 'react'
import AppContext from 'src/AppContext'
import TitleSection from 'src/components/layout/TitleSection'
import { H4, Label2, P, Info2 } from 'src/components/typography'
import { numberToFiatAmount } from 'src/utils/number'
import styles from './Assets.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
assets
preferredFiatCurrency
contactInfo {
name
email
}
fundings {
id
origin
destination
fiatAmount
fiatBalanceAfter
fiatCurrency
created
status
description
}
}
}
`
const cellStyling = {
borderBottom: '4px solid white',
padding: 0,
paddingLeft: 20,
paddingRight: 20
}
const Cell = withStyles({
root: cellStyling
})(TableCell)
const HeaderCell = withStyles({
root: {
...cellStyling,
backgroundColor: 'white'
}
})(TableCell)
const AssetsAmountTable = ({ title, data = [], numToRender }) => {
const classes = useStyles()
const totalAmount = R.compose(R.sum, R.map(R.path(['amount'])))(data) ?? 0
const currency = data[0]?.currency ?? ''
const selectAmountPrefix = it =>
it.direction === 'in' ? '+' : R.isNil(it.direction) ? '' : '-'
return (
<>
<Grid item className={classes.card} xs={12}>
<H4 className={classes.h4}>{title}</H4>
<TableContainer>
<Table>
<TableHead>
<TableRow>
<HeaderCell>
<div className={classes.asset}>
<Label2 className={classes.label}>Asset</Label2>
</div>
</HeaderCell>
<HeaderCell>
<div className={classes.amount}>
<Label2 className={classes.label}>Amount</Label2>
</div>
</HeaderCell>
</TableRow>
</TableHead>
<TableBody>
{data?.map((asset, idx) => {
if (!(idx < numToRender)) return <></>
return (
<TableRow className={classes.row} key={idx}>
<Cell align="left">
<P>{asset.display}</P>
</Cell>
<Cell align="right">
<P>{`${selectAmountPrefix(asset)}
${numberToFiatAmount(Math.abs(asset.amount))} ${
asset.currency
}`}</P>
</Cell>
</TableRow>
)
})}
<TableRow className={classes.totalRow} key={data?.length + 1}>
<Cell align="left">
<Info2>{`Total ${R.toLower(title)}`}</Info2>
</Cell>
<Cell align="right">
<Info2>{`${numberToFiatAmount(
totalAmount
)} ${currency}`}</Info2>
</Cell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
</Grid>
</>
)
}
const Assets = () => {
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 balanceData = [
{
id: 'fiatBalance',
display: 'Fiat balance',
amount: operatorData?.assets.total ?? 0,
currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''),
class: 'Available balance'
},
{
id: 'hedgingReserve',
display: 'Hedging reserve',
amount:
-R.sum(R.values(operatorData?.assets.values.hedgedContracts)) ?? 0,
currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''),
class: 'Available balance',
direction: 'out'
}
]
const walletData = [
{
id: 'hedgedWalletAssets',
display: 'Hedged wallet assets',
amount: R.sum(R.values(operatorData?.assets.values.hedgedContracts)) ?? 0,
currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''),
class: 'Wallet assets',
direction: 'in'
},
{
id: 'unhedgedWalletAssets',
display: 'Unhedged wallet assets',
amount: R.sum(R.values(operatorData?.assets.values.unhedgedFiat)) ?? 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:
-R.sum(R.values(operatorData?.assets.values.hedgedContracts)) ?? 0,
currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''),
direction: 'out'
},
{
id: 'hedgedWalletAssets',
display: 'Market value of hedged wallet assets',
amount: R.sum(R.values(operatorData?.assets.values.hedgedContracts)) ?? 0,
currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''),
direction: 'in'
},
{
id: 'unhedgedWalletAssets',
display: 'Unhedged wallet assets',
amount: R.sum(R.values(operatorData?.assets.values.unhedgedFiat)) ?? 0,
currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''),
direction: 'in'
}
]
return (
!loading && (
<>
<TitleSection title="Balance sheet" />
<div className={classes.root}>
<Grid container>
<Grid container direction="column" item xs={5}>
<Grid item xs={12}>
<div className={classes.leftSide}>
<AssetsAmountTable
title="Available balance"
data={balanceData}
numToRender={balanceData.length}
/>
</div>
<div className={classes.leftSide}>
<AssetsAmountTable
title="Wallet assets"
data={walletData}
numToRender={walletData.length}
/>
</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>
</div>
</>
)
)
}
export default Assets

View file

@ -1,45 +0,0 @@
import {
white,
offColor,
backgroundColor,
subheaderColor
} from 'src/styling/variables'
const styles = () => ({
card: {
wordWrap: 'break-word',
boxShadow: '0 0 4px 0 rgba(0, 0, 0, 0.08)',
borderRadius: 12,
padding: 24,
backgroundColor: white
},
h4: {
marginTop: 0
},
label: {
margin: 0,
color: offColor
},
asset: {
float: 'left'
},
amount: {
float: 'right'
},
row: {
backgroundColor: backgroundColor,
borderBottom: 'none'
},
totalRow: {
backgroundColor: subheaderColor,
borderBottom: 'none'
},
leftSide: {
margin: [[0, 10, 20, 0]]
},
rightSide: {
margin: [[0, 0, 0, 10]]
}
})
export default styles

View file

@ -1,6 +1,5 @@
import { useMutation, useLazyQuery } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core/styles'
import base64 from 'base-64'
import { Form, Formik } from 'formik'
import gql from 'graphql-tag'
import React, { useContext, useState } from 'react'
@ -59,14 +58,7 @@ const Input2FAState = ({ state, dispatch }) => {
const [input2FA, { error: mutationError }] = useMutation(INPUT_2FA, {
onCompleted: ({ input2FA: success }) => {
if (success) {
const options = {
context: {
headers: {
'Pazuz-Operator-Identifier': base64.encode(state.clientField)
}
}
}
return getUserData(options)
return getUserData()
}
return setInvalidToken(true)
}
@ -94,11 +86,6 @@ const Input2FAState = ({ state, dispatch }) => {
password: state.passwordField,
code: state.twoFAField,
rememberMe: state.rememberMeField
},
context: {
headers: {
'Pazuz-Operator-Identifier': base64.encode(state.clientField)
}
}
}

View file

@ -1,7 +1,6 @@
import { useMutation, useLazyQuery } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core/styles'
import { startAssertion } from '@simplewebauthn/browser'
import base64 from 'base-64'
import { Field, Form, Formik } from 'formik'
import gql from 'graphql-tag'
import React, { useContext } from 'react'
@ -84,11 +83,6 @@ const LoginState = ({ state, dispatch, strategy }) => {
variables: {
username,
password
},
context: {
headers: {
'Pazuz-Operator-Identifier': base64.encode(username)
}
}
}
const { data: loginResponse } = await login(options)

View file

@ -90,16 +90,10 @@ const Register = () => {
const classes = useStyles()
const history = useHistory()
const token = QueryParams().get('t')
const identifier = QueryParams().get('id') ?? null
const [state, dispatch] = useReducer(reducer, initialState)
const queryOptions = {
context: {
headers: {
'Pazuz-Operator-Identifier': identifier
}
},
variables: { token: token },
onCompleted: ({ validateRegisterLink: info }) => {
if (!info) {

View file

@ -1,6 +1,5 @@
import { useMutation, useQuery, useLazyQuery } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core/styles'
import base64 from 'base-64'
import { Form, Formik } from 'formik'
import gql from 'graphql-tag'
import QRCode from 'qrcode.react'
@ -71,11 +70,6 @@ const Setup2FAState = ({ state, dispatch }) => {
const queryOptions = {
variables: { username: state.clientField, password: state.passwordField },
context: {
headers: {
'Pazuz-Operator-Identifier': base64.encode(state.clientField)
}
},
onCompleted: ({ get2FASecret }) => {
setSecret(get2FASecret.secret)
setOtpauth(get2FASecret.otpauth)
@ -88,11 +82,6 @@ const Setup2FAState = ({ state, dispatch }) => {
password: state.passwordField,
rememberMe: state.rememberMeField,
codeConfirmation: twoFAConfirmation
},
context: {
headers: {
'Pazuz-Operator-Identifier': base64.encode(state.clientField)
}
}
}
@ -107,14 +96,7 @@ const Setup2FAState = ({ state, dispatch }) => {
const [setup2FA, { error: mutationError }] = useMutation(SETUP_2FA, {
onCompleted: ({ setup2FA: success }) => {
const options = {
context: {
headers: {
'Pazuz-Operator-Identifier': base64.encode(state.clientField)
}
}
}
success ? getUserData(options) : setInvalidToken(true)
success ? getUserData() : setInvalidToken(true)
}
})

View file

@ -1,6 +1,5 @@
import { useMutation } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core/styles'
import base64 from 'base-64'
import classnames from 'classnames'
import { Field, Form, Formik } from 'formik'
import gql from 'graphql-tag'
@ -75,12 +74,7 @@ const CreateUserModal = ({ state, dispatch }) => {
const [createUser, { error }] = useMutation(CREATE_USER, {
onCompleted: ({ createRegisterToken: token }) => {
const queryParams =
// Pazuz-created register tokens add a field to identify the creator
process.env.REACT_APP_BUILD_TARGET === 'LAMASSU'
? `t=${token.token}`
: `t=${token.token}&id=${base64.encode(usernameField)}`
setCreateUserURL(urlResolver(`/register?${queryParams}`))
setCreateUserURL(urlResolver(`/register?t${token.token}`))
}
})

View file

@ -2,7 +2,6 @@ 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, { useState, useContext } from 'react'
import { useHistory } from 'react-router-dom'
@ -53,18 +52,6 @@ const Wizard = ({ fromAuthRegister }) => {
const [footerExp, setFooterExp] = useState(false)
const getSteps = STEPS => {
const buildTarget = process.env.REACT_APP_BUILD_TARGET
if (buildTarget === 'PAZUZ') {
return R.filter(step => step.id !== 'wallet' && step.id !== 'twilio')(
STEPS
)
}
return STEPS
}
const steps = getSteps(STEPS)
if (loading) {
return <></>
}
@ -91,7 +78,7 @@ const Wizard = ({ fromAuthRegister }) => {
}
const doContinue = () => {
if (step >= steps.length - 1) {
if (step >= STEPS.length - 1) {
setOpen(false)
history.push('/')
}
@ -102,7 +89,7 @@ const Wizard = ({ fromAuthRegister }) => {
setStep(nextStep)
}
const current = steps[step]
const current = STEPS[step]
return (
<Dialog fullScreen open={open}>
@ -112,7 +99,7 @@ const Wizard = ({ fromAuthRegister }) => {
{!isWelcome && (
<Footer
currentStep={step}
steps={steps.length - 1}
steps={STEPS.length - 1}
exImage={current.exImage}
subtitle={current.subtitle}
text={current.text}

View file

@ -1,317 +0,0 @@
import React from 'react'
import { Redirect } from 'react-router-dom'
import ATMWallet from 'src/pages/ATMWallet/ATMWallet'
import Accounting from 'src/pages/Accounting/Accounting'
import Analytics from 'src/pages/Analytics/Analytics'
import Assets from 'src/pages/Assets/Assets'
import Blacklist from 'src/pages/Blacklist'
import Cashout from 'src/pages/Cashout'
import Commissions from 'src/pages/Commissions'
import { Customers, CustomerProfile } from 'src/pages/Customers'
import Locales from 'src/pages/Locales'
import IndividualDiscounts from 'src/pages/LoyaltyPanel/IndividualDiscounts'
import PromoCodes from 'src/pages/LoyaltyPanel/PromoCodes'
import MachineLogs from 'src/pages/MachineLogs'
import CashUnits from 'src/pages/Maintenance/CashUnits'
import MachineStatus from 'src/pages/Maintenance/MachineStatus'
import Notifications from 'src/pages/Notifications/Notifications'
import CoinAtmRadar from 'src/pages/OperatorInfo/CoinATMRadar'
import ContactInfo from 'src/pages/OperatorInfo/ContactInfo'
import MachineScreens from 'src/pages/OperatorInfo/MachineScreens'
import ReceiptPrinting from 'src/pages/OperatorInfo/ReceiptPrinting'
import SMSNotices from 'src/pages/OperatorInfo/SMSNotices/SMSNotices'
import TermsConditions from 'src/pages/OperatorInfo/TermsConditions'
import ServerLogs from 'src/pages/ServerLogs'
import SessionManagement from 'src/pages/SessionManagement/SessionManagement'
import Transactions from 'src/pages/Transactions/Transactions'
import Triggers from 'src/pages/Triggers'
import UserManagement from 'src/pages/UserManagement/UserManagement'
import { namespaces } from 'src/utils/config'
import { ROLES } from './utils'
const getPazuzRoutes = () => [
{
key: 'transactions',
label: 'Transactions',
route: '/transactions',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: Transactions
},
{
key: 'maintenance',
label: 'Maintenance',
route: '/maintenance',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
get component() {
return () => <Redirect to={this.children[0].route} />
},
children: [
{
key: 'cash_units',
label: 'Cash Units',
route: '/maintenance/cash-units',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: CashUnits
},
{
key: 'logs',
label: 'Machine logs',
route: '/maintenance/logs',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: MachineLogs
},
{
key: 'machine-status',
label: 'Machine status',
route: '/maintenance/machine-status',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: MachineStatus
},
{
key: 'server-logs',
label: 'Server',
route: '/maintenance/server-logs',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: ServerLogs
}
]
},
{
key: 'analytics',
label: 'Analytics',
route: '/analytics',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: Analytics
},
{
key: 'settings',
label: 'Settings',
route: '/settings',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
get component() {
return () => <Redirect to={this.children[0].route} />
},
children: [
{
key: namespaces.COMMISSIONS,
label: 'Commissions',
route: '/settings/commissions',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: Commissions
},
{
key: namespaces.LOCALE,
label: 'Locales',
route: '/settings/locale',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: Locales
},
{
key: namespaces.CASH_OUT,
label: 'Cash-out',
route: '/settings/cash-out',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: Cashout
},
{
key: namespaces.NOTIFICATIONS,
label: 'Notifications',
route: '/settings/notifications',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: Notifications
},
{
key: namespaces.OPERATOR_INFO,
label: 'Operator info',
route: '/settings/operator-info',
title: 'Operator information',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
get component() {
return () => (
<Redirect
to={{
pathname: this.children[0].route,
state: { prev: this.state?.prev }
}}
/>
)
},
children: [
{
key: 'contact-info',
label: 'Contact information',
route: '/settings/operator-info/contact-info',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: ContactInfo
},
{
key: 'receipt-printing',
label: 'Receipt',
route: '/settings/operator-info/receipt-printing',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: ReceiptPrinting
},
{
key: 'sms-notices',
label: 'SMS notices',
route: '/settings/operator-info/sms-notices',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: SMSNotices
},
{
key: 'coin-atm-radar',
label: 'Coin ATM Radar',
route: '/settings/operator-info/coin-atm-radar',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: CoinAtmRadar
},
{
key: 'terms-conditions',
label: 'Terms & Conditions',
route: '/settings/operator-info/terms-conditions',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: TermsConditions
},
{
key: 'machine-screens',
label: 'Machine screens',
route: '/settings/operator-info/machine-screens',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: MachineScreens
}
]
}
]
},
{
key: 'compliance',
label: 'Compliance',
route: '/compliance',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
get component() {
return () => <Redirect to={this.children[0].route} />
},
children: [
{
key: 'triggers',
label: 'Triggers',
route: '/compliance/triggers',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: Triggers
},
{
key: 'customers',
label: 'Customers',
route: '/compliance/customers',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: Customers
},
{
key: 'blacklist',
label: 'Blacklist',
route: '/compliance/blacklist',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: Blacklist
},
{
key: 'loyalty',
label: 'Loyalty',
route: '/compliance/loyalty',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
get component() {
return () => (
<Redirect
to={{
pathname: this.children[0].route,
state: { prev: this.state?.prev }
}}
/>
)
},
children: [
{
key: 'individual-discounts',
label: 'Individual discounts',
route: '/compliance/loyalty/individual-discounts',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: IndividualDiscounts
},
{
key: 'promo-codes',
label: 'Promo codes',
route: '/compliance/loyalty/codes',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: PromoCodes
}
]
},
{
key: 'customer',
route: '/compliance/customer/:id',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: CustomerProfile
}
]
},
{
key: 'accounting',
label: 'Accounting',
route: '/accounting',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
get component() {
return () => <Redirect to={this.children[0].route} />
},
children: [
{
key: 'accountingpage',
label: 'Accounting',
route: '/accounting/accounting',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: Accounting
},
{
key: 'atmwallets',
label: 'ATM Wallets',
route: '/accounting/wallets',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: ATMWallet
},
{
key: 'assetspage',
label: 'Assets',
route: '/accounting/assets',
allowedRoles: [ROLES.USER, ROLES.SUPERUSER],
component: Assets
}
]
},
{
key: 'system',
label: 'System',
route: '/system',
allowedRoles: [ROLES.SUPERUSER],
get component() {
return () => <Redirect to={this.children[0].route} />
},
children: [
{
key: 'user-management',
label: 'User management',
route: '/system/user-management',
allowedRoles: [ROLES.SUPERUSER],
component: UserManagement
},
{
key: 'session-management',
label: 'Session management',
route: '/system/session-management',
allowedRoles: [ROLES.SUPERUSER],
component: SessionManagement
}
]
}
]
export default getPazuzRoutes

View file

@ -23,7 +23,6 @@ import Wizard from 'src/pages/Wizard'
import PrivateRoute from './PrivateRoute'
import PublicRoute from './PublicRoute'
import getLamassuRoutes from './lamassu.routes'
import getPazuzRoutes from './pazuz.routes'
const useStyles = makeStyles({
wrapper: {
@ -34,19 +33,7 @@ const useStyles = makeStyles({
}
})
const getTree = () => {
const buildTarget = process.env.REACT_APP_BUILD_TARGET
if (buildTarget === 'LAMASSU') {
return getLamassuRoutes()
}
if (buildTarget === 'PAZUZ') {
return getPazuzRoutes()
}
}
const tree = getTree()
const tree = getLamassuRoutes()
const map = R.map(R.when(R.has('children'), R.prop('children')))
const mappedRoutes = R.compose(R.flatten, map)(tree)

View file

@ -9,8 +9,3 @@ export const ROLES = {
USER: 'user',
SUPERUSER: 'superuser'
}
export const BUILD_TARGETS = {
LAMASSU: 'LAMASSU',
PAZUZ: 'PAZUZ'
}

View file

@ -1,135 +0,0 @@
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://bit.ly/CRA-PWA
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
)
export function register(config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href)
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config)
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://bit.ly/CRA-PWA'
)
})
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config)
}
})
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing
if (installingWorker == null) {
return
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
)
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration)
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.')
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration)
}
}
}
}
}
})
.catch(error => {
console.error('Error during service worker registration:', error)
})
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type')
if (
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload()
})
})
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config)
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
)
})
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister()
})
}
}

View file

@ -12,21 +12,11 @@ import AppContext from 'src/AppContext'
const URI =
process.env.NODE_ENV === 'development' ? 'https://localhost:8070' : ''
const ALT_URI =
process.env.NODE_ENV === 'development'
? 'https://localhost:4001'
: `https://${window.location.hostname}:4001`
const uploadLink = createUploadLink({
credentials: 'include',
uri: `${URI}/graphql`
})
const uploadLinkALT = createUploadLink({
credentials: 'include',
uri: `${ALT_URI}/graphql`
})
const getClient = (history, location, getUserData, setUserData, setRole) =>
new ApolloClient({
link: ApolloLink.from([
@ -58,11 +48,7 @@ const getClient = (history, location, getUserData, setUserData, setRole) =>
return response
})
}),
ApolloLink.split(
operation => operation.getContext().clientName === 'pazuz',
uploadLinkALT,
uploadLink
)
]),
cache: new InMemoryCache(),
defaultOptions: {