diff --git a/components/TableContainer/TableContainerGetter.tsx b/components/TableContainer/TableContainerGetter.tsx
index 978c73a66..c2b4e8e6c 100644
--- a/components/TableContainer/TableContainerGetter.tsx
+++ b/components/TableContainer/TableContainerGetter.tsx
@@ -35,9 +35,11 @@ const TableContainer = ({ columns, dataGetter, opts, ssr, tableRefreshCount, emp
const [pageCount, setPageCount] = useState(0)
const [loading, setLoading] = useState(true)
const emptyMessageDisplay = emptyMessage ?? DEFAULT_EMPTY_TABLE_MESSAGE
+ const [hiddenColumns, setHiddenColumns] = useState({})
const triggerSort = (column: any): void => {
- if (column.disableSortBy === true) return
+ if (column.disableSortBy === true || hiddenColumns[column.id]) return
+
const id = column.id
if (sortColumn === id) {
setSortDesc(!sortDesc)
@@ -47,6 +49,10 @@ const TableContainer = ({ columns, dataGetter, opts, ssr, tableRefreshCount, emp
}
gotoPage(0)
}
+
+ const toggleColumn = (id: any): void => {
+ setHiddenColumns((prev) => ({ ...prev, [id]: !prev[id]}))
+ }
const {
getTableProps,
@@ -114,8 +120,15 @@ const TableContainer = ({ columns, dataGetter, opts, ssr, tableRefreshCount, emp
{headerGroup.headers.map((column: any) => (
{ triggerSort(column) }}>
+
{column.render('Header')}
- {generateSortingIndicator(column)}
+ {column.shrinkable && (
+ toggleColumn(column.id)} style={{ cursor: 'pointer' }}>
+ {hiddenColumns[column.id] ? : }
+
+ )}
+ {!column.shrinkable && generateSortingIndicator(column)}
+
|
))}
@@ -129,9 +142,9 @@ const TableContainer = ({ columns, dataGetter, opts, ssr, tableRefreshCount, emp
prepareRow(row)
return (
- {row.cells.map((cell: any) => {
- return | {cell.render('Cell')} |
- })}
+ {row.cells.map((cell: any) =>
+ hiddenColumns[cell.column.id] ? | : {cell.render('Cell')} |
+ )}
)
})
diff --git a/components/Transaction/AddressTransactions.tsx b/components/Transaction/PaybuttonTransactions.tsx
similarity index 62%
rename from components/Transaction/AddressTransactions.tsx
rename to components/Transaction/PaybuttonTransactions.tsx
index 6f169fc06..db6fa90b5 100644
--- a/components/Transaction/AddressTransactions.tsx
+++ b/components/Transaction/PaybuttonTransactions.tsx
@@ -1,12 +1,11 @@
import React, { useMemo } from 'react'
-import style from './transaction.module.css'
import Image from 'next/image'
import XECIcon from 'assets/xec-logo.png'
import BCHIcon from 'assets/bch-logo.png'
import EyeIcon from 'assets/eye-icon.png'
import CheckIcon from 'assets/check-icon.png'
import XIcon from 'assets/x-icon.png'
-import TableContainerGetter from '../../components/TableContainer/TableContainerGetter'
+import TableContainerGetter from '../TableContainer/TableContainerGetter'
import { compareNumericString } from 'utils/index'
import moment from 'moment-timezone'
import { XEC_TX_EXPLORER_URL, BCH_TX_EXPLORER_URL } from 'constants/index'
@@ -15,26 +14,29 @@ interface IProps {
addressSyncing: {
[address: string]: boolean
}
+ paybuttonId: string
tableRefreshCount: number
timezone: string
}
-function getGetterForAddress (addressString: string): Function {
+function fetchTransactionsByPaybuttonId (paybuttonId: string): Function {
return async (page: number, pageSize: number, orderBy: string, orderDesc: boolean) => {
- const ok = await fetch(`/api/address/transactions/${addressString}?page=${page}&pageSize=${pageSize}&orderBy=${orderBy}&orderDesc=${String(orderDesc)}`, {
+ const response = await fetch(`/api/paybutton/transactions/${paybuttonId}?page=${page}&pageSize=${pageSize}&orderBy=${orderBy}&orderDesc=${String(orderDesc)}`, {
headers: {
Timezone: moment.tz.guess()
}
})
- const ok2 = await fetch(`/api/address/transactions/count/${addressString}`)
+ const responseCount = await fetch(`/api/paybutton/transactions/count/${paybuttonId}`)
+ const transactions = await response.json()
+ const count = await responseCount.json()
return {
- data: await ok.json(),
- totalCount: await ok2.json()
+ data: transactions.transactions,
+ totalCount: count
}
}
}
-export default ({ addressSyncing, tableRefreshCount, timezone = moment.tz.guess() }: IProps): JSX.Element => {
+export default ({ paybuttonId, addressSyncing, tableRefreshCount, timezone = moment.tz.guess() }: IProps): JSX.Element => {
const columns = useMemo(
() => [
{
@@ -80,17 +82,31 @@ export default ({ addressSyncing, tableRefreshCount, timezone = moment.tz.guess(
}
},
{
- Header: 'TX',
+ Header: () => (TX
),
accessor: 'hash',
disableSortBy: true,
Cell: (cellProps) => {
const url = cellProps.cell.row.values['address.networkId'] === 1 ? XEC_TX_EXPLORER_URL : BCH_TX_EXPLORER_URL
return (
-
-
-
-
-
+
+ )
+ }
+ },
+ {
+ Header: () => (Address
),
+ accessor: 'address.address',
+ shrinkable: true,
+ Cell: (cellProps) => {
+ return (
+
+ {cellProps.cell.value}
+
)
}
}
@@ -99,19 +115,7 @@ export default ({ addressSyncing, tableRefreshCount, timezone = moment.tz.guess(
)
return (
<>
- {Object.keys(addressSyncing).map(transactionAddress => (
-
- ))}
+
>
)
}
diff --git a/components/Transaction/index.tsx b/components/Transaction/index.tsx
index 2cab8a8e0..193bb5bec 100644
--- a/components/Transaction/index.tsx
+++ b/components/Transaction/index.tsx
@@ -1,5 +1,5 @@
-import AddressTransactions from './AddressTransactions'
+import PaybuttonTransactions from './PaybuttonTransactions'
export {
- AddressTransactions
+ PaybuttonTransactions
}
diff --git a/pages/api/paybutton/transactions/[id].ts b/pages/api/paybutton/transactions/[id].ts
index 3a7dbd046..10c62ff6b 100644
--- a/pages/api/paybutton/transactions/[id].ts
+++ b/pages/api/paybutton/transactions/[id].ts
@@ -1,5 +1,5 @@
-import { RESPONSE_MESSAGES } from 'constants/index'
-import { fetchTransactionsByPaybuttonId } from 'services/transactionService'
+import { RESPONSE_MESSAGES, TX_PAGE_SIZE_LIMIT } from 'constants/index'
+import { fetchTransactionsByPaybuttonIdWithPagination } from 'services/transactionService'
import * as paybuttonService from 'services/paybuttonService'
import { setSession } from 'utils/setSession'
import { parseError } from 'utils/validators'
@@ -9,6 +9,17 @@ export default async (req: any, res: any): Promise => {
await setSession(req, res)
const userId = req.session.userId
const paybuttonId = req.query.id as string
+ const page = (req.query.page === '' || req.query.page === undefined) ? 0 : Number(req.query.page)
+ const pageSize = (req.query.pageSize === '' || req.query.pageSize === undefined) ? DEFAULT_TX_PAGE_SIZE : Number(req.query.pageSize)
+ const orderBy = (req.query.orderBy === '' || req.query.orderBy === undefined) ? undefined : req.query.orderBy as string
+ const orderDesc: boolean = !!(req.query.orderDesc === '' || req.query.orderDesc === undefined || req.query.orderDesc === 'true')
+
+ if (isNaN(page) || isNaN(pageSize)) {
+ throw new Error(RESPONSE_MESSAGES.PAGE_SIZE_AND_PAGE_SHOULD_BE_NUMBERS_400.message)
+ }
+ if (pageSize > TX_PAGE_SIZE_LIMIT) {
+ throw new Error(RESPONSE_MESSAGES.PAGE_SIZE_LIMIT_EXCEEDED_400.message)
+ }
try {
const paybutton = await paybuttonService.fetchPaybuttonById(paybuttonId)
@@ -16,7 +27,7 @@ export default async (req: any, res: any): Promise => {
throw new Error(RESPONSE_MESSAGES.RESOURCE_DOES_NOT_BELONG_TO_USER_400.message)
}
- const transactions = await fetchTransactionsByPaybuttonId(paybuttonId)
+ const transactions = await fetchTransactionsByPaybuttonIdWithPagination(paybuttonId, page, pageSize, orderDesc, orderBy)
res.status(200).json({ transactions })
} catch (err: any) {
diff --git a/pages/button/[id].tsx b/pages/button/[id].tsx
index 0bb50cbc2..b1729ac41 100644
--- a/pages/button/[id].tsx
+++ b/pages/button/[id].tsx
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'
import Page from 'components/Page'
import { PaybuttonDetail } from 'components/Paybutton'
import { PaybuttonWithAddresses } from 'services/paybuttonService'
-import { AddressTransactions } from 'components/Transaction'
+import { PaybuttonTransactions } from 'components/Transaction'
import supertokensNode from 'supertokens-node'
import * as SuperTokensConfig from '../../config/backendConfig'
import Session from 'supertokens-node/recipe/session'
@@ -101,7 +101,7 @@ export default function PayButton (props: PaybuttonProps): React.ReactElement {
})
socket.on(SOCKET_MESSAGES.INCOMING_TXS, (broadcastedData: BroadcastTxData) => {
- setTableRefreshCount(tableRefreshCount + 1)
+ setTableRefreshCount(tableRefreshCountCurrent => tableRefreshCountCurrent + 1)
updateIsSyncing([broadcastedData.address])
})
}
@@ -207,7 +207,7 @@ export default function PayButton (props: PaybuttonProps): React.ReactElement {
-
+
>
)
diff --git a/services/transactionService.ts b/services/transactionService.ts
index 155c4c7ee..aa910e0c3 100644
--- a/services/transactionService.ts
+++ b/services/transactionService.ts
@@ -145,6 +145,57 @@ export async function fetchTransactionsByAddressList (
})
}
+export async function fetchTransactionsByAddressListWithPagination (
+ addressIdList: string[],
+ page: number,
+ pageSize: number,
+ orderBy?: string,
+ orderDesc = true,
+ networkIdsListFilter?: number[],
+): Promise {
+
+ const orderDescString: Prisma.SortOrder = orderDesc ? 'desc' : 'asc'
+
+ // Get query for orderBy that works with nested properties (e.g. `address.networkId`)
+ let orderByQuery
+ if (orderBy !== undefined && orderBy !== '') {
+ if (orderBy.includes('.')) {
+ const [relation, property] = orderBy.split('.')
+ orderByQuery = {
+ [relation]: {
+ [property]: orderDescString
+ }
+ }
+ } else {
+ orderByQuery = {
+ [orderBy]: orderDescString
+ }
+ }
+ } else {
+ // Default orderBy
+ orderByQuery = {
+ timestamp: orderDescString
+ }
+ }
+
+ return await prisma.transaction.findMany({
+ where: {
+ addressId: {
+ in: addressIdList
+ },
+ address: {
+ networkId: {
+ in: networkIdsListFilter ?? Object.values(NETWORK_IDS)
+ }
+ }
+ },
+ include: includePaybuttonsAndPrices,
+ orderBy: orderByQuery,
+ skip: page * pageSize,
+ take: pageSize,
+ })
+}
+
export async function fetchTxCountByAddressString (addressString: string): Promise {
return await prisma.transaction.count({
where: {
@@ -513,6 +564,29 @@ export async function fetchTransactionsByPaybuttonId (paybuttonId: string, netwo
return transactions
}
+export async function fetchTransactionsByPaybuttonIdWithPagination (
+ paybuttonId: string,
+ page: number,
+ pageSize: number,
+ orderDesc: boolean,
+ orderBy?: string,
+ networkIds?: number[]): Promise {
+ const addressIdList = await fetchAddressesByPaybuttonId(paybuttonId)
+ const transactions = await fetchTransactionsByAddressListWithPagination(
+ addressIdList,
+ page,
+ pageSize,
+ orderBy,
+ orderDesc,
+ networkIds);
+
+ if (transactions.length === 0) {
+ throw new Error(RESPONSE_MESSAGES.NO_TRANSACTION_FOUND_404.message)
+ }
+
+ return transactions
+}
+
export const getTransactionValueInCurrency = (transaction: TransactionWithAddressAndPrices, currency: SupportedQuotesType): number => {
const {
prices,
@@ -661,7 +735,7 @@ export async function fetchAllPaymentsByUserIdWithPagination (
userId, page, pageSize, orderDesc, buttonIds
)
}
-
+ // Get query for orderBy that works with nested properties (e.g. `address.networkId`)
let orderByQuery
if (orderBy !== undefined && orderBy !== '') {
if (orderBy === 'values') {
diff --git a/styles/global.css b/styles/global.css
index 8bd3dae12..4ed7ce91e 100644
--- a/styles/global.css
+++ b/styles/global.css
@@ -161,6 +161,20 @@ button:enabled:hover {
background: #f4f4f4;
}
+.table-arrow-right {
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-width: 4px 5px 4px 0;
+ border-color: transparent var(--primary-text-color) transparent transparent;
+ position: absolute;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ margin: auto;
+ opacity: 0.5;
+}
+
.table-sort-arrow-down,
.table-sort-arrow-up {
width: 0;
@@ -219,12 +233,13 @@ button:enabled:hover {
}
.table-eye-ctn {
- text-align: right;
+ text-align: center;
display: flex;
align-items: center;
- justify-content: flex-end;
+ justify-content: center;
}
+
.table-eye {
width: 20px;
opacity: 0.6;