diff --git a/lib/new-admin/filters.js b/lib/new-admin/filters.js index d6a37922..d9b6c32d 100644 --- a/lib/new-admin/filters.js +++ b/lib/new-admin/filters.js @@ -21,7 +21,8 @@ function transaction () { SELECT 'address' AS type, to_address AS value FROM cash_in_txs UNION SELECT 'address' AS type, to_address AS value FROM cash_out_txs UNION SELECT 'status' AS type, ${cashInTx.TRANSACTION_STATES} AS value FROM cash_in_txs UNION - SELECT 'status' AS type, ${CASH_OUT_TRANSACTION_STATES} AS value FROM cash_out_txs + SELECT 'status' AS type, ${CASH_OUT_TRANSACTION_STATES} AS value FROM cash_out_txs UNION + SELECT 'sweep status' AS type, CASE WHEN swept THEN 'Swept' WHEN NOT swept THEN 'Unswept' END AS value FROM cash_out_txs ) f` return db.any(sql) diff --git a/lib/new-admin/graphql/resolvers/transaction.resolver.js b/lib/new-admin/graphql/resolvers/transaction.resolver.js index e41c17f9..f92a8b69 100644 --- a/lib/new-admin/graphql/resolvers/transaction.resolver.js +++ b/lib/new-admin/graphql/resolvers/transaction.resolver.js @@ -19,11 +19,11 @@ const resolvers = { isAnonymous: parent => (parent.customerId === anonymous.uuid) }, Query: { - transactions: (...[, { from, until, limit, offset, deviceId, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, excludeTestingCustomers }]) => - transactions.batch(from, until, limit, offset, deviceId, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, excludeTestingCustomers), - transactionsCsv: (...[, { from, until, limit, offset, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, timezone, excludeTestingCustomers, simplified }]) => - transactions.batch(from, until, limit, offset, null, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, excludeTestingCustomers, simplified) - .then(data => parseAsync(logDateFormat(timezone, data, ['created', 'sendTime']))), + transactions: (...[, { from, until, limit, offset, deviceId, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, swept, excludeTestingCustomers }]) => + transactions.batch(from, until, limit, offset, deviceId, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, swept, excludeTestingCustomers), + transactionsCsv: (...[, { from, until, limit, offset, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, swept, timezone, excludeTestingCustomers, simplified }]) => + transactions.batch(from, until, limit, offset, null, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, swept, excludeTestingCustomers, simplified) + .then(data => parseAsync(logDateFormat(timezone, data, ['created', 'sendTime', 'publishedAt']))), transactionCsv: (...[, { id, txClass, timezone }]) => transactions.getTx(id, txClass).then(data => parseAsync(logDateFormat(timezone, [data], ['created', 'sendTime'])) diff --git a/lib/new-admin/graphql/types/transaction.type.js b/lib/new-admin/graphql/types/transaction.type.js index a7385eda..2d95d751 100644 --- a/lib/new-admin/graphql/types/transaction.type.js +++ b/lib/new-admin/graphql/types/transaction.type.js @@ -50,6 +50,7 @@ const typeDef = gql` batchError: String walletScore: Int profit: String + swept: Boolean } type Filter { @@ -58,8 +59,8 @@ const typeDef = gql` } type Query { - transactions(from: Date, until: Date, limit: Int, offset: Int, deviceId: ID, txClass: String, machineName: String, customerName: String, fiatCode: String, cryptoCode: String, toAddress: String, status: String, excludeTestingCustomers: Boolean): [Transaction] @auth - transactionsCsv(from: Date, until: Date, limit: Int, offset: Int, txClass: String, machineName: String, customerName: String, fiatCode: String, cryptoCode: String, toAddress: String, status: String, timezone: String, excludeTestingCustomers: Boolean, simplified: Boolean): String @auth + transactions(from: Date, until: Date, limit: Int, offset: Int, deviceId: ID, txClass: String, machineName: String, customerName: String, fiatCode: String, cryptoCode: String, toAddress: String, status: String, swept: Boolean, excludeTestingCustomers: Boolean): [Transaction] @auth + transactionsCsv(from: Date, until: Date, limit: Int, offset: Int, txClass: String, machineName: String, customerName: String, fiatCode: String, cryptoCode: String, toAddress: String, status: String, swept: Boolean, timezone: String, excludeTestingCustomers: Boolean, simplified: Boolean): String @auth transactionCsv(id: ID, txClass: String, timezone: String): String @auth txAssociatedDataCsv(id: ID, txClass: String, timezone: String): String @auth transactionFilters: [Filter] @auth diff --git a/lib/new-admin/services/transactions.js b/lib/new-admin/services/transactions.js index 0f111937..3dc2679a 100644 --- a/lib/new-admin/services/transactions.js +++ b/lib/new-admin/services/transactions.js @@ -46,6 +46,7 @@ function batch ( cryptoCode = null, toAddress = null, status = null, + swept = null, excludeTestingCustomers = false, simplified ) { @@ -109,14 +110,33 @@ function batch ( AND ($11 is null or txs.crypto_code = $11) AND ($12 is null or txs.to_address = $12) AND ($13 is null or txs.txStatus = $13) + AND ($14 is null or txs.swept = $14) ${excludeTestingCustomers ? `AND c.is_test_customer is false` : ``} AND (fiat > 0) ORDER BY created DESC limit $4 offset $5` - return Promise.all([ - db.any(cashInSql, [cashInTx.PENDING_INTERVAL, from, until, limit, offset, id, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status]), - db.any(cashOutSql, [REDEEMABLE_AGE, from, until, limit, offset, id, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status]) - ]) + // The swept filter is cash-out only, so omit the cash-in query entirely + const hasCashInOnlyFilters = false + const hasCashOutOnlyFilters = !_.isNil(swept) + + let promises + + if (hasCashInOnlyFilters && hasCashOutOnlyFilters) { + throw new Error('Trying to filter transactions with mutually exclusive filters') + } + + if (hasCashInOnlyFilters) { + promises = [db.any(cashInSql, [cashInTx.PENDING_INTERVAL, from, until, limit, offset, id, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status])] + } else if (hasCashOutOnlyFilters) { + promises = [db.any(cashOutSql, [REDEEMABLE_AGE, from, until, limit, offset, id, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, swept])] + } else { + promises = [ + db.any(cashInSql, [cashInTx.PENDING_INTERVAL, from, until, limit, offset, id, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status]), + db.any(cashOutSql, [REDEEMABLE_AGE, from, until, limit, offset, id, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status, swept]) + ] + } + + return Promise.all(promises) .then(packager) .then(res => { if (simplified) return simplifiedBatch(res) diff --git a/new-lamassu-admin/src/components/SearchFilter.js b/new-lamassu-admin/src/components/SearchFilter.js index 0f5abba1..adb16647 100644 --- a/new-lamassu-admin/src/components/SearchFilter.js +++ b/new-lamassu-admin/src/components/SearchFilter.js @@ -7,7 +7,7 @@ import { P, Label3 } from 'src/components/typography' import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.svg' import { ReactComponent as FilterIcon } from 'src/styling/icons/button/filter/white.svg' import { ReactComponent as ReverseFilterIcon } from 'src/styling/icons/button/filter/zodiac.svg' -import { onlyFirstToUpper } from 'src/utils/string' +import { onlyFirstToUpper, singularOrPlural } from 'src/utils/string' import { chipStyles, styles } from './SearchFilter.styles' @@ -18,7 +18,7 @@ const SearchFilter = ({ filters, onFilterDelete, deleteAllFilters, - entries + entries = 0 }) => { const chipClasses = useChipStyles() const classes = useStyles() @@ -40,8 +40,11 @@ const SearchFilter = ({
{ - {`${entries ?? - 0} entries`} + {`${entries} ${singularOrPlural( + entries, + `entry`, + `entries` + )}`} } { )}
+ {!R.isNil(tx.swept) && ( +
+ + + {tx.swept ? `Swept` : `Unswept`} + +
+ )}
diff --git a/new-lamassu-admin/src/pages/Transactions/DetailsCard.styles.js b/new-lamassu-admin/src/pages/Transactions/DetailsCard.styles.js index 18cef9ad..536bf0ca 100644 --- a/new-lamassu-admin/src/pages/Transactions/DetailsCard.styles.js +++ b/new-lamassu-admin/src/pages/Transactions/DetailsCard.styles.js @@ -131,5 +131,8 @@ export default { }, error: { color: tomato + }, + swept: { + width: 250 } } diff --git a/new-lamassu-admin/src/pages/Transactions/Transactions.js b/new-lamassu-admin/src/pages/Transactions/Transactions.js index 8e459994..0bd530b7 100644 --- a/new-lamassu-admin/src/pages/Transactions/Transactions.js +++ b/new-lamassu-admin/src/pages/Transactions/Transactions.js @@ -75,6 +75,7 @@ const GET_TRANSACTIONS = gql` $cryptoCode: String $toAddress: String $status: String + $swept: Boolean ) { transactions( limit: $limit @@ -87,6 +88,7 @@ const GET_TRANSACTIONS = gql` cryptoCode: $cryptoCode toAddress: $toAddress status: $status + swept: $swept ) { id txClass @@ -121,6 +123,7 @@ const GET_TRANSACTIONS = gql` rawTickerPrice batchError walletScore + swept } } ` @@ -246,7 +249,8 @@ const Transactions = () => { fiatCode: filtersObject.fiat, cryptoCode: filtersObject.crypto, toAddress: filtersObject.address, - status: filtersObject.status + status: filtersObject.status, + swept: filtersObject.swept === 'Swept' }) refetch && refetch() @@ -269,7 +273,8 @@ const Transactions = () => { fiatCode: filtersObject.fiat, cryptoCode: filtersObject.crypto, toAddress: filtersObject.address, - status: filtersObject.status + status: filtersObject.status, + swept: filtersObject.swept === 'Swept' }) refetch && refetch() @@ -287,7 +292,8 @@ const Transactions = () => { fiatCode: filtersObject.fiat, cryptoCode: filtersObject.crypto, toAddress: filtersObject.address, - status: filtersObject.status + status: filtersObject.status, + swept: filtersObject.swept === 'Swept' }) refetch && refetch()