import { useLazyQuery, useMutation } from '@apollo/react-hooks'
import { makeStyles, Box } from '@material-ui/core'
import BigNumber from 'bignumber.js'
import FileSaver from 'file-saver'
import gql from 'graphql-tag'
import JSZip from 'jszip'
import { utils as coinUtils } from 'lamassu-coins'
import moment from 'moment'
import * as R from 'ramda'
import React, { memo, useState } from 'react'
import { ConfirmDialog } from 'src/components/ConfirmDialog'
import { HoverableTooltip } from 'src/components/Tooltip'
import { IDButton, ActionButton } from 'src/components/buttons'
import { P, Label1 } from 'src/components/typography'
import { ReactComponent as CardIdInverseIcon } from 'src/styling/icons/ID/card/white.svg'
import { ReactComponent as CardIdIcon } from 'src/styling/icons/ID/card/zodiac.svg'
import { ReactComponent as PhoneIdInverseIcon } from 'src/styling/icons/ID/phone/white.svg'
import { ReactComponent as PhoneIdIcon } from 'src/styling/icons/ID/phone/zodiac.svg'
import { ReactComponent as CamIdInverseIcon } from 'src/styling/icons/ID/photo/white.svg'
import { ReactComponent as CamIdIcon } from 'src/styling/icons/ID/photo/zodiac.svg'
import { ReactComponent as CancelInverseIcon } from 'src/styling/icons/button/cancel/white.svg'
import { ReactComponent as CancelIcon } from 'src/styling/icons/button/cancel/zodiac.svg'
import { ReactComponent as DownloadInverseIcon } from 'src/styling/icons/button/download/white.svg'
import { ReactComponent as Download } from 'src/styling/icons/button/download/zodiac.svg'
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
import { URI } from 'src/utils/apollo'
import { onlyFirstToUpper } from 'src/utils/string'
import CopyToClipboard from './CopyToClipboard'
import styles from './DetailsCard.styles'
import { getStatus, getStatusDetails } from './helper'
const useStyles = makeStyles(styles)
const MINUTES_OFFSET = 3
const TX_SUMMARY = gql`
query txSummaryAndLogs(
$txId: ID!
$deviceId: ID!
$limit: Int
$from: Date
$until: Date
$txClass: String
$timezone: String
) {
serverLogsCsv(
limit: $limit
from: $from
until: $until
timezone: $timezone
)
machineLogsCsv(
deviceId: $deviceId
limit: $limit
from: $from
until: $until
timezone: $timezone
)
transactionCsv(id: $txId, txClass: $txClass, timezone: $timezone)
txAssociatedDataCsv(id: $txId, txClass: $txClass, timezone: $timezone)
}
`
const CANCEL_TRANSACTION = gql`
mutation cancelCashOutTransaction($id: ID!) {
cancelCashOutTransaction(id: $id) {
id
}
}
`
const formatAddress = (cryptoCode = '', address = '') =>
coinUtils.formatCryptoAddress(cryptoCode, address).replace(/(.{5})/g, '$1 ')
const Label = ({ children }) => {
const classes = useStyles()
return {children}
}
const DetailsRow = ({ it: tx, timezone }) => {
const classes = useStyles()
const [action, setAction] = useState({ command: null })
const [errorMessage, setErrorMessage] = useState('')
const zip = new JSZip()
const [fetchSummary] = useLazyQuery(TX_SUMMARY, {
onCompleted: data => createCsv(data)
})
const [cancelCashOutTransaction] = useMutation(CANCEL_TRANSACTION, {
onError: ({ message }) => setErrorMessage(message ?? 'An error occurred.'),
refetchQueries: () => ['transactions']
})
const fiat = Number.parseFloat(tx.fiat)
const crypto = coinUtils.toUnit(new BigNumber(tx.cryptoAtoms), tx.cryptoCode)
const commissionPercentage = Number.parseFloat(tx.commissionPercentage, 2)
const commission = Number(fiat * commissionPercentage).toFixed(2)
const discount = tx.discount ? `-${tx.discount}%` : null
const exchangeRate = BigNumber(fiat / crypto).toFormat(2)
const displayExRate = `1 ${tx.cryptoCode} = ${exchangeRate} ${tx.fiatCode}`
const customer = tx.customerIdCardData && {
name: `${onlyFirstToUpper(
tx.customerIdCardData.firstName
)} ${onlyFirstToUpper(tx.customerIdCardData.lastName)}`,
age: moment().diff(moment(tx.customerIdCardData.dateOfBirth), 'years'),
country: tx.customerIdCardData.country,
idCardNumber: tx.customerIdCardData.documentNumber,
idCardExpirationDate: moment(tx.customerIdCardData.expirationDate).format(
'DD-MM-YYYY'
)
}
const from = moment(tx.created)
.subtract(MINUTES_OFFSET, 'm')
.format()
const until = moment(tx.created)
.add(MINUTES_OFFSET, 'm')
.format()
const downloadRawLogs = ({ id: txId, deviceId, txClass }, timezone) => {
fetchSummary({
variables: { txId, from, until, deviceId, txClass, timezone }
})
}
const createCsv = async logs => {
const zipFilename = `tx_${tx.id}_summary.zip`
const filesNames = R.keys(logs)
R.map(name => zip.file(name + '.csv', logs[name]), filesNames)
const content = await zip.generateAsync({ type: 'blob' })
FileSaver.saveAs(content, zipFilename)
}
const errorElements = (
<>
{getStatus(tx)}
>
)
return (
{tx.txClass === 'cashOut' ? : }
{tx.txClass === 'cashOut' ? 'Cash-out' : 'Cash-in'}
{tx.customerPhone && (
{tx.customerPhone}
)}
{tx.customerIdCardPhotoPath && !tx.customerIdCardData && (
)}
{tx.customerIdCardData && (
{customer.country}
{customer.idCardNumber}
{customer.idCardExpirationDate}
)}
{tx.customerFrontCameraPath && (
)}
{crypto > 0 ? displayExRate : '-'}
{`${commission} ${tx.fiatCode} (${commissionPercentage * 100} %)`}
{discount && (
{discount}
)}
{tx.txClass === 'cashIn'
? `${Number.parseFloat(tx.cashInFee)} ${tx.fiatCode}`
: 'N/A'}
{formatAddress(tx.cryptoCode, tx.toAddress)}
{tx.txClass === 'cashOut' ? (
'N/A'
) : (
{tx.txHash}
)}
{tx.id}
{getStatusDetails(tx) ? (
{getStatusDetails(tx)}
) : (
errorElements
)}
{tx.txClass === 'cashOut' && getStatus(tx) === 'Pending' && (
setAction({
command: 'cancelTx'
})
}>
Cancel transaction
)}
downloadRawLogs(tx, timezone)}>
Download raw logs
{
setErrorMessage(null)
setAction({ command: null })
cancelCashOutTransaction({
variables: {
id: tx.id
}
})
}}
onDissmised={() => {
setAction({ command: null })
setErrorMessage(null)
}}
/>
)
}
export default memo(
DetailsRow,
(prev, next) =>
prev.it.id === next.it.id && prev.it.hasError === next.it.hasError
)