chore: use monorepo organization
This commit is contained in:
parent
deaf7d6ecc
commit
a687827f7e
1099 changed files with 8184 additions and 11535 deletions
303
packages/admin-ui/src/pages/Funding/Funding.jsx
Normal file
303
packages/admin-ui/src/pages/Funding/Funding.jsx
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
import { useQuery, gql } from '@apollo/client'
|
||||
import { formatCryptoAddress } from '@lamassu/coins/lightUtils'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import classnames from 'classnames'
|
||||
import { format } from 'date-fns/fp'
|
||||
import { QRCodeSVG as QRCode } from 'qrcode.react'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
import TableLabel from 'src/pages/Funding/TableLabel.jsx'
|
||||
import Title from 'src/components/Title.jsx'
|
||||
import {
|
||||
Tr,
|
||||
Td,
|
||||
THead,
|
||||
TBody,
|
||||
Table
|
||||
} from 'src/components/fake-table/Table.jsx'
|
||||
import Sidebar from 'src/components/layout/Sidebar.jsx'
|
||||
import {
|
||||
H3,
|
||||
Info1,
|
||||
Info2,
|
||||
Info3,
|
||||
Label1,
|
||||
Label3
|
||||
} from 'src/components/typography/index.jsx'
|
||||
import CopyToClipboard from 'src/components/CopyToClipboard.jsx'
|
||||
|
||||
import { primaryColor } from 'src/styling/variables.js'
|
||||
|
||||
import classes from './Funding.module.css'
|
||||
|
||||
const NODE_NOT_CONNECTED_ERR =
|
||||
"Couldn't establish connection with the node. Make sure it is installed and try again"
|
||||
|
||||
const sizes = {
|
||||
big: 165,
|
||||
time: 140,
|
||||
date: 130
|
||||
}
|
||||
|
||||
const GET_FUNDING = gql`
|
||||
{
|
||||
funding {
|
||||
cryptoCode
|
||||
errorMsg
|
||||
fundingAddress
|
||||
fundingAddressUrl
|
||||
confirmedBalance
|
||||
pending
|
||||
fiatConfirmedBalance
|
||||
fiatPending
|
||||
fiatCode
|
||||
display
|
||||
unitScale
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const formatAddress = (cryptoCode = '', address = '') =>
|
||||
formatCryptoAddress(cryptoCode, address).replace(/(.{4})/g, '$1 ')
|
||||
const sumReducer = (acc, value) => acc.plus(value)
|
||||
const formatNumber = it => new BigNumber(it).toFormat(2)
|
||||
|
||||
const getConfirmedTotal = list => {
|
||||
return formatNumber(
|
||||
list
|
||||
.filter(it => !it.errorMsg)
|
||||
.map(it => new BigNumber(it.fiatConfirmedBalance))
|
||||
.reduce(sumReducer, new BigNumber(0))
|
||||
)
|
||||
}
|
||||
|
||||
const getPendingTotal = list => {
|
||||
return formatNumber(
|
||||
list
|
||||
.filter(it => !it.errorMsg)
|
||||
.map(it => new BigNumber(it.fiatPending))
|
||||
.reduce(sumReducer, new BigNumber(0))
|
||||
)
|
||||
}
|
||||
|
||||
const Funding = () => {
|
||||
const [selected, setSelected] = useState(null)
|
||||
const [viewHistory] = useState(false)
|
||||
const fundingHistory = [
|
||||
{
|
||||
cryptoAmount: 2.0,
|
||||
balance: 10.23,
|
||||
fiatValue: 1000.0,
|
||||
date: new Date(),
|
||||
performedBy: null,
|
||||
pending: true
|
||||
},
|
||||
{
|
||||
cryptoAmount: 10.0,
|
||||
balance: 12.23,
|
||||
fiatValue: 12000.0,
|
||||
date: new Date(),
|
||||
performedBy: null
|
||||
},
|
||||
{
|
||||
cryptoAmount: 5.0,
|
||||
balance: 5.0,
|
||||
fiatValue: 50000.0,
|
||||
date: new Date(),
|
||||
performedBy: null
|
||||
}
|
||||
]
|
||||
|
||||
const isSelected = it => {
|
||||
return selected && selected.cryptoCode === it.cryptoCode
|
||||
}
|
||||
|
||||
const { data: fundingResponse, loading } = useQuery(GET_FUNDING)
|
||||
const funding = R.path(['funding'])(fundingResponse) ?? []
|
||||
|
||||
if (funding.length && !selected) {
|
||||
setSelected(funding[0])
|
||||
}
|
||||
|
||||
const itemRender = (it, active) => {
|
||||
const itemClass = {
|
||||
[classes.item]: true,
|
||||
[classes.inactiveItem]: !active
|
||||
}
|
||||
const wrapperClass = {
|
||||
[classes.itemWrapper]: true,
|
||||
[classes.error]: it.errorMsg
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classnames(wrapperClass)}>
|
||||
<div className={classes.firstItem}>{it.display}</div>
|
||||
{!it.errorMsg && (
|
||||
<>
|
||||
<div className={classnames(itemClass)}>
|
||||
{formatNumber(it.fiatConfirmedBalance)} {it.fiatCode}
|
||||
</div>
|
||||
<div className={classnames(itemClass)}>
|
||||
{it.confirmedBalance} {it.cryptoCode}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const pendingTotal = getPendingTotal(funding)
|
||||
const signIfPositive = num => (num >= 0 ? '+' : '')
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<Title>Funding</Title>
|
||||
{/* <button onClick={it => setViewHistory(!viewHistory)}>history</button> */}
|
||||
</div>
|
||||
<div className={classes.wrapper}>
|
||||
<Sidebar
|
||||
data={funding}
|
||||
isSelected={isSelected}
|
||||
onClick={setSelected}
|
||||
displayName={it => it.display}
|
||||
itemRender={itemRender}
|
||||
loading={loading}>
|
||||
{funding.length && (
|
||||
<div className={classes.total}>
|
||||
<Label1 className={classes.totalTitle}>
|
||||
Total crypto balance
|
||||
</Label1>
|
||||
<Info1 noMargin>
|
||||
{getConfirmedTotal(funding)}
|
||||
{funding[0].fiatCode}
|
||||
</Info1>
|
||||
<Label1 className={classes.totalPending}>
|
||||
({signIfPositive(pendingTotal)} {pendingTotal} pending)
|
||||
</Label1>
|
||||
</div>
|
||||
)}
|
||||
</Sidebar>
|
||||
{selected && !viewHistory && selected.errorMsg && (
|
||||
<div className={classes.main}>
|
||||
<div className={classes.firstSide}>
|
||||
<Info3 className={classes.error}>
|
||||
{R.includes('ECONNREFUSED', selected.errorMsg)
|
||||
? NODE_NOT_CONNECTED_ERR
|
||||
: selected.errorMsg}
|
||||
</Info3>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{selected && !viewHistory && !selected.errorMsg && (
|
||||
<div className={classes.main}>
|
||||
<div className={classes.firstSide}>
|
||||
<H3>Balance</H3>
|
||||
<div className={classes.coinTotal}>
|
||||
<Info1 inline noMargin>
|
||||
{`${selected.confirmedBalance} ${selected.cryptoCode}`}
|
||||
</Info1>
|
||||
<Info2 inline noMargin className="ml-2">
|
||||
{`(${signIfPositive(selected.pending)} ${
|
||||
selected.pending
|
||||
} pending)`}
|
||||
</Info2>
|
||||
</div>
|
||||
|
||||
<div className={classes.coinTotal}>
|
||||
<Info3 inline noMargin>
|
||||
{`= ${formatNumber(selected.fiatConfirmedBalance)} ${
|
||||
selected.fiatCode
|
||||
}`}
|
||||
</Info3>
|
||||
<Label3 inline noMargin className="ml-2">
|
||||
{`(${signIfPositive(selected.fiatPending)} ${formatNumber(
|
||||
selected.fiatPending
|
||||
)} pending)`}
|
||||
</Label3>
|
||||
</div>
|
||||
|
||||
<H3 className={classes.topSpacer}>Address</H3>
|
||||
<div className={classes.addressWrapper}>
|
||||
<div className={classes.mono}>
|
||||
<strong>
|
||||
<CopyToClipboard
|
||||
buttonClassname={classes.copyToClipboard}
|
||||
key={selected.cryptoCode}>
|
||||
{formatAddress(
|
||||
selected.cryptoCode,
|
||||
selected.fundingAddress
|
||||
)}
|
||||
</CopyToClipboard>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.secondSide}>
|
||||
<Label1>Scan to send {selected.display}</Label1>
|
||||
<QRCode
|
||||
size={240}
|
||||
fgColor={primaryColor}
|
||||
value={selected.fundingAddressUrl}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{selected && viewHistory && (
|
||||
<div>
|
||||
<TableLabel
|
||||
className={classes.tableLabel}
|
||||
label="Pending"
|
||||
color="#cacaca"
|
||||
/>
|
||||
<Table className={classes.table}>
|
||||
<THead>
|
||||
<Td header width={sizes.big}>
|
||||
Amount Entered
|
||||
</Td>
|
||||
<Td header width={sizes.big}>
|
||||
Balance After
|
||||
</Td>
|
||||
<Td header width={sizes.big}>
|
||||
Cash Value
|
||||
</Td>
|
||||
<Td header width={sizes.date}>
|
||||
Date
|
||||
</Td>
|
||||
<Td header width={sizes.time}>
|
||||
Time (h:m:s)
|
||||
</Td>
|
||||
<Td header width={sizes.big}>
|
||||
Performed By
|
||||
</Td>
|
||||
</THead>
|
||||
<TBody>
|
||||
{fundingHistory.map((it, idx) => (
|
||||
<Tr
|
||||
key={idx}
|
||||
className={classnames({ [classes.pending]: it.pending })}>
|
||||
<Td width={sizes.big}>
|
||||
{it.cryptoAmount} {selected.cryptoCode}
|
||||
</Td>
|
||||
<Td width={sizes.big}>
|
||||
{it.balance} {selected.cryptoCode}
|
||||
</Td>
|
||||
<Td width={sizes.big}>
|
||||
{it.fiatValue} {selected.fiatCode}
|
||||
</Td>
|
||||
<Td width={sizes.date}>{format('yyyy-MM-dd', it.date)}</Td>
|
||||
<Td width={sizes.time}>{format('hh:mm:ss', it.date)}</Td>
|
||||
<Td width={sizes.big}>add</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</TBody>
|
||||
</Table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Funding
|
||||
107
packages/admin-ui/src/pages/Funding/Funding.module.css
Normal file
107
packages/admin-ui/src/pages/Funding/Funding.module.css
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
.wrapper {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.main {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.firstSide {
|
||||
margin: 0 64px 0 48px;
|
||||
}
|
||||
|
||||
.secondSide {
|
||||
margin-top: -29px;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--tomato);
|
||||
}
|
||||
|
||||
.coinTotal {
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
.topSpacer {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.addressWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
background-color: var(--zircon);
|
||||
}
|
||||
|
||||
.address {
|
||||
width: 375px;
|
||||
margin: 12px 24px;
|
||||
}
|
||||
|
||||
.itemWrapper {
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
.item {
|
||||
font-family: var(--museo);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.inactiveItem {
|
||||
color: var(--comet);
|
||||
}
|
||||
|
||||
.firstItem {
|
||||
font-weight: 700;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.total {
|
||||
margin-top: auto;
|
||||
text-align: right;
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
.totalPending {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.totalTitle {
|
||||
color: var(--comet);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.table {
|
||||
margin-top: 8px;
|
||||
margin-left: 48px;
|
||||
}
|
||||
|
||||
.tableLabel {
|
||||
justify-content: end;
|
||||
margin-top: -38px;
|
||||
}
|
||||
|
||||
.pending {
|
||||
background-color: var(--zircon);
|
||||
}
|
||||
|
||||
.copyToClipboard {
|
||||
margin-left: auto;
|
||||
padding-top: 6px;
|
||||
padding-left: 15px;
|
||||
margin-right: -11px;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: var(--bpmono);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
width: 375px;
|
||||
margin: 12px 24px;
|
||||
}
|
||||
20
packages/admin-ui/src/pages/Funding/TableLabel.jsx
Normal file
20
packages/admin-ui/src/pages/Funding/TableLabel.jsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import classnames from 'classnames'
|
||||
import React from 'react'
|
||||
|
||||
import { Label1 } from '../../components/typography/index.jsx'
|
||||
|
||||
const TableLabel = ({ className, label, color, ...props }) => {
|
||||
return (
|
||||
<div className={classnames('flex items-center', className)} {...props}>
|
||||
{color && (
|
||||
<div
|
||||
className="rounded-sm h-3 w-3 mr-2"
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
)}
|
||||
<Label1 {...props}>{label}</Label1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default TableLabel
|
||||
Loading…
Add table
Add a link
Reference in a new issue