chore: use monorepo organization
This commit is contained in:
parent
deaf7d6ecc
commit
a687827f7e
1099 changed files with 8184 additions and 11535 deletions
|
|
@ -0,0 +1,106 @@
|
|||
import { useMutation, gql } from '@apollo/client'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
import DataTable from 'src/components/tables/DataTable'
|
||||
import CashUnitDetails from 'src/pages/Maintenance/CashUnitDetails'
|
||||
import Wizard from 'src/pages/Maintenance/Wizard/Wizard'
|
||||
import helper from 'src/pages/Maintenance/helper'
|
||||
|
||||
import { fromNamespace } from 'src/utils/config'
|
||||
|
||||
const SET_CASSETTE_BILLS = gql`
|
||||
mutation MachineAction(
|
||||
$deviceId: ID!
|
||||
$action: MachineAction!
|
||||
$cashUnits: CashUnitsInput
|
||||
) {
|
||||
machineAction(deviceId: $deviceId, action: $action, cashUnits: $cashUnits) {
|
||||
deviceId
|
||||
cashUnits {
|
||||
cashbox
|
||||
cassette1
|
||||
cassette2
|
||||
cassette3
|
||||
cassette4
|
||||
recycler1
|
||||
recycler2
|
||||
recycler3
|
||||
recycler4
|
||||
recycler5
|
||||
recycler6
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const widths = {
|
||||
name: 0,
|
||||
cashbox: 175,
|
||||
cassettes: 585,
|
||||
edit: 90
|
||||
}
|
||||
|
||||
const CashCassettes = ({ machine, config, refetchData, bills }) => {
|
||||
const [wizard, setWizard] = useState(false)
|
||||
|
||||
const cashout = config && fromNamespace('cashOut')(config)
|
||||
const locale = config && fromNamespace('locale')(config)
|
||||
const fiatCurrency = locale?.fiatCurrency
|
||||
|
||||
const getCashoutSettings = deviceId => fromNamespace(deviceId)(cashout)
|
||||
|
||||
const elements = R.filter(it => it.name !== 'name')(
|
||||
helper.getElements(config, bills, setWizard, widths)
|
||||
)
|
||||
|
||||
const [setCassetteBills, { error }] = useMutation(SET_CASSETTE_BILLS, {
|
||||
refetchQueries: () => refetchData()
|
||||
})
|
||||
|
||||
const onSave = (_, cashUnits) =>
|
||||
setCassetteBills({
|
||||
variables: {
|
||||
action: 'setCassetteBills',
|
||||
deviceId: machine.deviceId,
|
||||
cashUnits
|
||||
}
|
||||
})
|
||||
|
||||
const InnerCashUnitDetails = ({ it }) => (
|
||||
<CashUnitDetails
|
||||
machine={it}
|
||||
bills={bills[it.deviceId] ?? []}
|
||||
currency={fiatCurrency}
|
||||
config={config}
|
||||
hideMachineData
|
||||
widths
|
||||
/>
|
||||
)
|
||||
|
||||
return machine.name ? (
|
||||
<>
|
||||
<DataTable
|
||||
elements={elements}
|
||||
data={[machine]}
|
||||
Details={InnerCashUnitDetails}
|
||||
emptyText="No machines so far"
|
||||
initialExpanded={0}
|
||||
tableClassName="min-h-72"
|
||||
/>
|
||||
{wizard && (
|
||||
<Wizard
|
||||
machine={machine}
|
||||
cashoutSettings={getCashoutSettings(machine.deviceId)}
|
||||
onClose={() => {
|
||||
setWizard(false)
|
||||
}}
|
||||
error={error?.message}
|
||||
save={onSave}
|
||||
locale={locale}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
|
||||
export default CashCassettes
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import Cassettes from './Cassettes'
|
||||
export default Cassettes
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
import { useQuery, useMutation, gql } from "@apollo/client";
|
||||
import * as R from 'ramda'
|
||||
import React from 'react'
|
||||
|
||||
import { Table as EditableTable } from 'src/components/editableTable'
|
||||
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
||||
|
||||
import { overrides } from './helper'
|
||||
|
||||
const GET_DATA = gql`
|
||||
query getData {
|
||||
config
|
||||
cryptoCurrencies {
|
||||
code
|
||||
display
|
||||
}
|
||||
machines {
|
||||
name
|
||||
deviceId
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const SAVE_CONFIG = gql`
|
||||
mutation Save($config: JSONObject) {
|
||||
saveConfig(config: $config)
|
||||
}
|
||||
`
|
||||
|
||||
const Commissions = ({ name: SCREEN_KEY, id: deviceId }) => {
|
||||
const { data, loading } = useQuery(GET_DATA)
|
||||
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
||||
refetchQueries: () => ['getData']
|
||||
})
|
||||
|
||||
const config = data?.config && fromNamespace(SCREEN_KEY)(data.config)
|
||||
const currency = R.path(['fiatCurrency'])(
|
||||
fromNamespace(namespaces.LOCALE)(data?.config)
|
||||
)
|
||||
|
||||
const saveOverrides = it => {
|
||||
const config = toNamespace(SCREEN_KEY)(it)
|
||||
return saveConfig({ variables: { config } })
|
||||
}
|
||||
|
||||
const getMachineCommissions = () => {
|
||||
if (loading || !deviceId || !config) return []
|
||||
|
||||
const overrides = config.overrides
|
||||
? R.concat(
|
||||
R.filter(R.propEq('machine', 'ALL_MACHINES'), config.overrides),
|
||||
R.filter(R.propEq('machine', deviceId), config.overrides)
|
||||
)
|
||||
: []
|
||||
|
||||
return R.map(
|
||||
coin =>
|
||||
R.reduce(
|
||||
R.mergeDeepRight,
|
||||
{
|
||||
code: coin.code,
|
||||
name: coin.display,
|
||||
cashIn: config.cashIn,
|
||||
cashOut: config.cashOut,
|
||||
fixedFee: config.fixedFee,
|
||||
minimumTx: config.minimumTx,
|
||||
cashOutFixedFee: config.cashOutFixedFee
|
||||
},
|
||||
R.project(
|
||||
['cashIn', 'cashOut', 'fixedFee', 'minimumTx', 'cashOutFixedFee'],
|
||||
R.filter(
|
||||
o =>
|
||||
R.includes(coin.code, o.cryptoCurrencies) ||
|
||||
R.includes('ALL_COINS', o.cryptoCurrencies),
|
||||
overrides
|
||||
)
|
||||
)
|
||||
),
|
||||
data.cryptoCurrencies
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<EditableTable
|
||||
name="overrides"
|
||||
save={saveOverrides}
|
||||
data={getMachineCommissions()}
|
||||
elements={overrides(currency)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default Commissions
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
import React from 'react'
|
||||
import TxInIcon from 'src/styling/icons/direction/cash-in.svg?react'
|
||||
import TxOutIcon from 'src/styling/icons/direction/cash-out.svg?react'
|
||||
|
||||
const cashInAndOutHeaderStyle = { marginLeft: 6 }
|
||||
|
||||
const cashInHeader = (
|
||||
<div>
|
||||
<TxInIcon />
|
||||
<span style={cashInAndOutHeaderStyle}>Cash-in</span>
|
||||
</div>
|
||||
)
|
||||
|
||||
const cashOutHeader = (
|
||||
<div>
|
||||
<TxOutIcon />
|
||||
<span style={cashInAndOutHeaderStyle}>Cash-out</span>
|
||||
</div>
|
||||
)
|
||||
|
||||
const getOverridesFields = currency => {
|
||||
return [
|
||||
{
|
||||
name: 'name',
|
||||
width: 280,
|
||||
size: 'sm',
|
||||
view: it => `${it}`
|
||||
},
|
||||
{
|
||||
header: cashInHeader,
|
||||
name: 'cashIn',
|
||||
display: 'Cash-in',
|
||||
width: 130,
|
||||
textAlign: 'right',
|
||||
suffix: '%'
|
||||
},
|
||||
{
|
||||
header: cashOutHeader,
|
||||
name: 'cashOut',
|
||||
display: 'Cash-out',
|
||||
width: 130,
|
||||
textAlign: 'right',
|
||||
suffix: '%',
|
||||
inputProps: {
|
||||
decimalPlaces: 3
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'fixedFee',
|
||||
display: 'Fixed fee',
|
||||
width: 155,
|
||||
doubleHeader: 'Cash-in only',
|
||||
textAlign: 'right',
|
||||
suffix: currency
|
||||
},
|
||||
{
|
||||
name: 'minimumTx',
|
||||
display: 'Minimum Tx',
|
||||
width: 155,
|
||||
doubleHeader: 'Cash-in only',
|
||||
textAlign: 'right',
|
||||
suffix: currency
|
||||
},
|
||||
{
|
||||
name: 'cashOutFixedFee',
|
||||
display: 'Fixed fee',
|
||||
width: 155,
|
||||
doubleHeader: 'Cash-out only',
|
||||
textAlign: 'right',
|
||||
suffix: currency
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const overrides = currency => {
|
||||
return getOverridesFields(currency)
|
||||
}
|
||||
|
||||
export { overrides }
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import Commissions from './Commissions'
|
||||
|
||||
export default Commissions
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import React from 'react'
|
||||
import { Label1, P } from 'src/components/typography'
|
||||
|
||||
import { modelPrettifier } from 'src/utils/machine'
|
||||
import { formatDate } from 'src/utils/timezones'
|
||||
|
||||
const Details = ({ data, timezone }) => {
|
||||
return (
|
||||
<div className="flex gap-30">
|
||||
<div>
|
||||
<Label1 className="text-comet mt-0">Paired at</Label1>
|
||||
<P>
|
||||
{data.pairedAt
|
||||
? formatDate(data.pairedAt, timezone, 'yyyy-MM-dd HH:mm:ss')
|
||||
: ''}
|
||||
</P>
|
||||
</div>
|
||||
<div>
|
||||
<Label1 className="text-comet mt-0">Machine model</Label1>
|
||||
<P>{modelPrettifier[data.model]}</P>
|
||||
</div>
|
||||
<div>
|
||||
<Label1 className="text-comet mt-0">Software version</Label1>
|
||||
<P>{data.version}</P>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Details
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
import BigNumber from 'bignumber.js'
|
||||
import { formatDistance } from 'date-fns'
|
||||
import React from 'react'
|
||||
import { Status } from 'src/components/Status'
|
||||
import MachineActions from 'src/components/machineActions/MachineActions'
|
||||
import { H3, Label1, P } from 'src/components/typography'
|
||||
import CopyToClipboard from 'src/components/CopyToClipboard.jsx'
|
||||
|
||||
const Overview = ({ data, onActionSuccess }) => {
|
||||
return (
|
||||
<div className="flex flex-col gap-8">
|
||||
<H3>{data.name}</H3>
|
||||
<div>
|
||||
<Label1 className="text-comet mt-0">Status</Label1>
|
||||
{data && data.statuses ? <Status status={data.statuses[0]} /> : null}
|
||||
</div>
|
||||
<div className="flex gap-6">
|
||||
<div>
|
||||
<Label1 className="text-comet mt-0">Ping</Label1>
|
||||
<P noMargin>
|
||||
{data.responseTime
|
||||
? new BigNumber(data.responseTime).toFixed(3).toString() + ' ms'
|
||||
: 'unavailable'}
|
||||
</P>
|
||||
</div>
|
||||
<div>
|
||||
<Label1 className="text-comet mt-0">Last ping</Label1>
|
||||
<P noMargin>
|
||||
{data.lastPing
|
||||
? formatDistance(new Date(data.lastPing), new Date(), {
|
||||
addSuffix: true
|
||||
})
|
||||
: 'unknown'}
|
||||
</P>
|
||||
</div>
|
||||
<div>
|
||||
<Label1 className="text-comet mt-0">Network speed</Label1>
|
||||
<P noMargin>
|
||||
{data.downloadSpeed
|
||||
? new BigNumber(data.downloadSpeed)
|
||||
.toFixed(data.downloadSpeed < 10 ? 2 : 0)
|
||||
.toString() + ' MB/s'
|
||||
: 'unavailable'}
|
||||
</P>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<Label1 className="text-comet mt-0">Device ID</Label1>
|
||||
<P className="wrap-anywhere" noMargin>
|
||||
<CopyToClipboard>{data.deviceId}</CopyToClipboard>
|
||||
</P>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<MachineActions
|
||||
machine={data}
|
||||
onActionSuccess={onActionSuccess}></MachineActions>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Overview
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
import { useQuery, useLazyQuery, gql } from '@apollo/client'
|
||||
import { toUnit, formatCryptoAddress } from '@lamassu/coins/lightUtils'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import * as R from 'ramda'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import DetailsRow from 'src/pages/Transactions/DetailsCard'
|
||||
import TxInIcon from 'src/styling/icons/direction/cash-in.svg?react'
|
||||
import TxOutIcon from 'src/styling/icons/direction/cash-out.svg?react'
|
||||
|
||||
import { getStatus } from 'src/pages/Transactions/helper'
|
||||
import * as Customer from 'src/utils/customer'
|
||||
import { formatDate } from 'src/utils/timezones'
|
||||
|
||||
import DataTable from 'src/components/tables/DataTable'
|
||||
|
||||
const NUM_LOG_RESULTS = 5
|
||||
|
||||
const GET_TRANSACTIONS = gql`
|
||||
query transactions(
|
||||
$limit: Int
|
||||
$from: DateTimeISO
|
||||
$until: DateTimeISO
|
||||
$deviceId: String
|
||||
) {
|
||||
transactions(
|
||||
limit: $limit
|
||||
from: $from
|
||||
until: $until
|
||||
deviceId: $deviceId
|
||||
) {
|
||||
id
|
||||
txClass
|
||||
txHash
|
||||
toAddress
|
||||
commissionPercentage
|
||||
expired
|
||||
machineName
|
||||
operatorCompleted
|
||||
sendConfirmed
|
||||
dispense
|
||||
hasError: error
|
||||
deviceId
|
||||
fiat
|
||||
fixedFee
|
||||
fiatCode
|
||||
cryptoAtoms
|
||||
cryptoCode
|
||||
toAddress
|
||||
created
|
||||
customerName
|
||||
customerIdCardData
|
||||
customerIdCardPhotoPath
|
||||
customerFrontCameraPath
|
||||
customerPhone
|
||||
customerEmail
|
||||
discount
|
||||
customerId
|
||||
isAnonymous
|
||||
rawTickerPrice
|
||||
profit
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const GET_DATA = gql`
|
||||
query getData {
|
||||
config
|
||||
}
|
||||
`
|
||||
|
||||
const Transactions = ({ id }) => {
|
||||
const [extraHeight, setExtraHeight] = useState(0)
|
||||
const [clickedId, setClickedId] = useState('')
|
||||
|
||||
const [getTx, { data: txResponse, loading: txLoading }] = useLazyQuery(
|
||||
GET_TRANSACTIONS,
|
||||
{
|
||||
variables: {
|
||||
limit: NUM_LOG_RESULTS,
|
||||
deviceId: id
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const { data: configData, loading: configLoading } = useQuery(GET_DATA)
|
||||
const timezone = R.path(['config', 'locale_timezone'], configData)
|
||||
|
||||
const loading = txLoading || configLoading
|
||||
|
||||
if (!loading && txResponse) {
|
||||
txResponse.transactions = txResponse.transactions.splice(0, 5)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (id !== null) {
|
||||
getTx()
|
||||
}
|
||||
}, [getTx, id])
|
||||
|
||||
const elements = [
|
||||
{
|
||||
header: '',
|
||||
width: 0,
|
||||
size: 'sm',
|
||||
view: it => (it.txClass === 'cashOut' ? <TxOutIcon /> : <TxInIcon />)
|
||||
},
|
||||
{
|
||||
header: 'Customer',
|
||||
width: 122,
|
||||
size: 'sm',
|
||||
view: Customer.displayName
|
||||
},
|
||||
{
|
||||
header: 'Cash',
|
||||
width: 144,
|
||||
textAlign: 'right',
|
||||
size: 'sm',
|
||||
view: it => `${Number.parseFloat(it.fiat)} ${it.fiatCode}`
|
||||
},
|
||||
{
|
||||
header: 'Crypto',
|
||||
width: 164,
|
||||
textAlign: 'right',
|
||||
size: 'sm',
|
||||
view: it =>
|
||||
`${toUnit(new BigNumber(it.cryptoAtoms), it.cryptoCode).toFormat(5)} ${
|
||||
it.cryptoCode
|
||||
}`
|
||||
},
|
||||
{
|
||||
header: 'Address',
|
||||
view: it => formatCryptoAddress(it.cryptoCode, it.toAddress),
|
||||
className: 'overflow-hidden whitespace-nowrap text-ellipsis',
|
||||
size: 'sm',
|
||||
textAlign: 'left',
|
||||
width: 140
|
||||
},
|
||||
{
|
||||
header: 'Date',
|
||||
view: it => formatDate(it.created, timezone, 'yyyy‑MM‑dd'),
|
||||
textAlign: 'left',
|
||||
size: 'sm',
|
||||
width: 140
|
||||
},
|
||||
{
|
||||
header: 'Status',
|
||||
view: it => getStatus(it),
|
||||
size: 'sm',
|
||||
width: 20
|
||||
}
|
||||
]
|
||||
|
||||
const handleClick = e => {
|
||||
if (clickedId === e.id) {
|
||||
setClickedId('')
|
||||
setExtraHeight(0)
|
||||
} else {
|
||||
setClickedId(e.id)
|
||||
setExtraHeight(310)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
extraHeight={extraHeight}
|
||||
onClick={handleClick}
|
||||
maxWidth="950"
|
||||
className="min-h-90"
|
||||
loading={loading || id === null}
|
||||
emptyText="No transactions so far"
|
||||
elements={elements}
|
||||
data={R.path(['transactions'])(txResponse)}
|
||||
Details={DetailsRow}
|
||||
expandable
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default Transactions
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import Transactions from './Transactions'
|
||||
export default Transactions
|
||||
150
packages/admin-ui/src/pages/Machines/Machines.jsx
Normal file
150
packages/admin-ui/src/pages/Machines/Machines.jsx
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
import { useQuery, gql } from '@apollo/client'
|
||||
import Breadcrumbs from '@mui/material/Breadcrumbs'
|
||||
import NavigateNextIcon from '@mui/icons-material/NavigateNext'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
import { Link, useLocation, useHistory } from 'react-router-dom'
|
||||
import { TL1, TL2, Label3 } from 'src/components/typography'
|
||||
|
||||
import Cassettes from './MachineComponents/Cassettes'
|
||||
import Commissions from './MachineComponents/Commissions'
|
||||
import Details from './MachineComponents/Details'
|
||||
import Overview from './MachineComponents/Overview'
|
||||
import Transactions from './MachineComponents/Transactions'
|
||||
|
||||
const GET_INFO = gql`
|
||||
query getMachine($deviceId: ID!, $billFilters: JSONObject) {
|
||||
machine(deviceId: $deviceId) {
|
||||
name
|
||||
deviceId
|
||||
paired
|
||||
lastPing
|
||||
pairedAt
|
||||
version
|
||||
model
|
||||
cashUnits {
|
||||
cashbox
|
||||
cassette1
|
||||
cassette2
|
||||
cassette3
|
||||
cassette4
|
||||
recycler1
|
||||
recycler2
|
||||
recycler3
|
||||
recycler4
|
||||
recycler5
|
||||
recycler6
|
||||
}
|
||||
numberOfCassettes
|
||||
numberOfRecyclers
|
||||
statuses {
|
||||
label
|
||||
type
|
||||
}
|
||||
downloadSpeed
|
||||
responseTime
|
||||
packetLoss
|
||||
latestEvent {
|
||||
note
|
||||
}
|
||||
}
|
||||
bills(filters: $billFilters) {
|
||||
id
|
||||
fiat
|
||||
deviceId
|
||||
created
|
||||
}
|
||||
config
|
||||
}
|
||||
`
|
||||
|
||||
const getMachineID = path => path.slice(path.lastIndexOf('/') + 1)
|
||||
|
||||
const MachineRoute = () => {
|
||||
const location = useLocation()
|
||||
const history = useHistory()
|
||||
|
||||
const id = getMachineID(location.pathname)
|
||||
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
const { data, refetch } = useQuery(GET_INFO, {
|
||||
onCompleted: data => {
|
||||
if (data.machine === null)
|
||||
return history.push('/maintenance/machine-status')
|
||||
|
||||
setLoading(false)
|
||||
},
|
||||
variables: {
|
||||
deviceId: id,
|
||||
billFilters: {
|
||||
deviceId: id,
|
||||
batch: 'none'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const reload = () => {
|
||||
return history.push(location.pathname)
|
||||
}
|
||||
|
||||
return (
|
||||
!loading && (
|
||||
<Machines data={data} refetch={refetch} reload={reload}></Machines>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const Machines = ({ data, refetch, reload }) => {
|
||||
const timezone = R.path(['config', 'locale_timezone'], data) ?? {}
|
||||
|
||||
const machine = R.path(['machine'])(data) ?? {}
|
||||
const config = R.path(['config'])(data) ?? {}
|
||||
const bills = R.groupBy(bill => bill.deviceId)(R.path(['bills'])(data) ?? [])
|
||||
|
||||
const machineName = R.path(['name'])(machine) ?? null
|
||||
const machineID = R.path(['deviceId'])(machine) ?? null
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 h-full gap-12">
|
||||
<div className="basis-1/4 min-w-1/4 pt-8">
|
||||
<Breadcrumbs separator={<NavigateNextIcon fontSize="small" />}>
|
||||
<Link to="/dashboard" className="no-underline">
|
||||
<Label3 noMargin className="text-comet mt-[1px]">
|
||||
Dashboard
|
||||
</Label3>
|
||||
</Link>
|
||||
<TL2 noMargin className="text-comet">
|
||||
{machineName}
|
||||
</TL2>
|
||||
</Breadcrumbs>
|
||||
<Overview data={machine} onActionSuccess={reload} />
|
||||
</div>
|
||||
<div className="basis-3/4 max-w-3/4 flex flex-col mt-6">
|
||||
<div>
|
||||
<TL1 className="text-comet">Details</TL1>
|
||||
<Details data={machine} timezone={timezone} />
|
||||
</div>
|
||||
<div>
|
||||
<TL1 className="text-comet">Cash box & cassettes</TL1>
|
||||
<Cassettes
|
||||
refetchData={refetch}
|
||||
machine={machine}
|
||||
config={config ?? false}
|
||||
bills={bills}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<TL1 className="text-comet">Latest transactions</TL1>
|
||||
<Transactions id={machineID} />
|
||||
</div>
|
||||
<div>
|
||||
<TL1 className="text-comet">Commissions</TL1>
|
||||
<Commissions name={'commissions'} id={machineID} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MachineRoute
|
||||
3
packages/admin-ui/src/pages/Machines/index.js
Normal file
3
packages/admin-ui/src/pages/Machines/index.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import Machines from './Machines'
|
||||
|
||||
export default Machines
|
||||
Loading…
Add table
Add a link
Reference in a new issue