diff --git a/src/api/transfers.js b/src/api/transfers.js index e9dc0fd..ccca7e3 100644 --- a/src/api/transfers.js +++ b/src/api/transfers.js @@ -1,7 +1,8 @@ import apiClient from '../utils/apiClient'; import { makeQueryString } from '../utils/formatting'; +import secureLocalStorage from 'react-secure-storage'; -export const getTransfers = async (token, { pagination, filter }) => { +export const getTransfers = async (token, { pagination, filter, sorting }) => { try { const where = filter.getWhereObj(); // pagination: limit, offset @@ -11,6 +12,14 @@ export const getTransfers = async (token, { pagination, filter }) => { ...where, }; + if (sorting) { + const { sort_by, order } = sorting; + if (sort_by) { + transferFilter.sort_by = sort_by; + transferFilter.order = order; + } + } + const queryString = makeQueryString(transferFilter); const response = await apiClient @@ -20,4 +29,72 @@ export const getTransfers = async (token, { pagination, filter }) => { } catch (error) { console.error(error); } +}; + +export const getPendingTransfers = async (token) => { + try { + const response = await apiClient + .setAuthHeader(token) + .get(`/transfers?state=pending`); + return response.data; + } catch (error) { + console.error(error); + } +}; + +export const acceptTransfer = async ({ id, token }) => { + try { + const response = await fetch( + `${process.env.REACT_APP_WALLET_API_ROOT}/transfers/${id}/accept`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'TREETRACKER-API-KEY': secureLocalStorage.getItem('api-key') || '', + Authorization: token ? `Bearer ${token}` : '', + }, + } + ); + return response; + } catch (error) { + console.error(error); + } +}; + +export const declineTransfer = async ({ id, token }) => { + try { + const response = await fetch( + `${process.env.REACT_APP_WALLET_API_ROOT}/transfers/${id}/decline`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'TREETRACKER-API-KEY': secureLocalStorage.getItem('api-key') || '', + Authorization: token ? `Bearer ${token}` : '', + }, + } + ); + return response; + } catch (error) { + console.error(error); + } +}; + +export const cancelTransfer = async ({ id, token }) => { + try { + const response = await fetch( + `${process.env.REACT_APP_WALLET_API_ROOT}/transfers/${id}`, + { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + 'TREETRACKER-API-KEY': secureLocalStorage.getItem('api-key') || '', + Authorization: token ? `Bearer ${token}` : '', + }, + } + ); + return response; + } catch (error) { + console.error(error); + } }; \ No newline at end of file diff --git a/src/components/Routes/ClientRoutes.js b/src/components/Routes/ClientRoutes.js index 3788289..e6eb57f 100644 --- a/src/components/Routes/ClientRoutes.js +++ b/src/components/Routes/ClientRoutes.js @@ -33,9 +33,11 @@ const ClientRoutes = () => { exact element={ - - - + + + + + } /> @@ -44,11 +46,11 @@ const ClientRoutes = () => { exact element={ - - + + - - + + } /> @@ -57,9 +59,11 @@ const ClientRoutes = () => { exact element={ - - - + + + + + } /> @@ -68,11 +72,13 @@ const ClientRoutes = () => { exact element={ - - - - - + + + + + + + } /> @@ -81,9 +87,11 @@ const ClientRoutes = () => { exact element={ - + + - + + } /> @@ -92,9 +100,11 @@ const ClientRoutes = () => { exact element={ - - - + + + + + } /> @@ -102,9 +112,11 @@ const ClientRoutes = () => { path="*" element={ - - - + + + + + } /> diff --git a/src/components/layout/Layout.js b/src/components/layout/Layout.js index 1fd0d57..4915209 100644 --- a/src/components/layout/Layout.js +++ b/src/components/layout/Layout.js @@ -5,12 +5,14 @@ import { useLocation } from "react-router-dom"; import { StyledContent } from "./LayoutStyled"; import Menu from "./Menu/Menu"; import { useTrustRelationshipsContext } from "../../store/TrustRelationshipsContext"; +import { useTransfersContext } from "../../store/TransfersContext"; const Layout = ({ children }) => { const [open, setOpen] = useState(false); - const { count } = useTrustRelationshipsContext(); + const { count: trustRelationshipCount } = useTrustRelationshipsContext(); + const { count: transfersCount } = useTransfersContext(); const handleDrawerOpen = () => { setOpen(true); @@ -44,7 +46,8 @@ const Layout = ({ children }) => { diff --git a/src/components/layout/Menu/Menu.js b/src/components/layout/Menu/Menu.js index 29ba576..2bd83f0 100644 --- a/src/components/layout/Menu/Menu.js +++ b/src/components/layout/Menu/Menu.js @@ -7,7 +7,7 @@ import MenuItem from './MenuItem/MenuItem'; import { DrawerHeaderStyled, DrawerStyled } from './MenuStyled'; import TopMenu from './TopMenu/TopMenu'; -const Menu = ({ open, handleDrawerClose, handleDrawerOpen, count }) => { +const Menu = ({ open, handleDrawerClose, handleDrawerOpen, trustRelationshipCount, transfersCount }) => { const theme = useTheme(); return ( @@ -23,7 +23,11 @@ const Menu = ({ open, handleDrawerClose, handleDrawerOpen, count }) => { )} - + ); diff --git a/src/components/layout/Menu/MenuItem/MenuItem.js b/src/components/layout/Menu/MenuItem/MenuItem.js index 469c28a..bb60948 100644 --- a/src/components/layout/Menu/MenuItem/MenuItem.js +++ b/src/components/layout/Menu/MenuItem/MenuItem.js @@ -8,7 +8,7 @@ import * as React from 'react'; import LinkItem from './LinkItem'; import { useState } from 'react'; -const MenuItem = ({ open, count }) => { +const MenuItem = ({ open, trustRelationshipCount, transfersCount }) => { const [isHovered, setIsHovered] = useState(false); return ( @@ -27,13 +27,34 @@ const MenuItem = ({ open, count }) => { isActive={location.pathname === '/send-tokens'} open={open} /> - } - isActive={location.pathname === '/my-transfers'} - open={open} - /> + {transfersCount > 0 && open ? ( +
setIsHovered(true)} + onMouseOut={() => setIsHovered(false)} + > + } + isActive={location.pathname === '/my-transfers'} + open={open} + pendingCount={transfersCount} + /> +
+ ) : ( + } + isActive={location.pathname === '/my-transfers'} + open={open} + /> + )} { isActive={location.pathname === '/list-wallets'} open={open} /> - {count > 0 && open ? ( + {trustRelationshipCount > 0 && open ? (
{ itemIcon={} isActive={location.pathname === '/trust-relationship'} open={open} - pendingCount={count} + pendingCount={trustRelationshipCount} />
) : ( diff --git a/src/pages/MyTransfers/MyTransfers.js b/src/pages/MyTransfers/MyTransfers.js index 702217a..47700c9 100644 --- a/src/pages/MyTransfers/MyTransfers.js +++ b/src/pages/MyTransfers/MyTransfers.js @@ -1,10 +1,8 @@ -import React, { useContext, useEffect, useState } from 'react'; +import React from 'react'; import { Grid } from '@mui/material'; import TransfersTable from './TransfersTable'; import Message from '../../components/UI/components/Message/Message'; -import { getTransfers } from '../../api/transfers'; import { useTransfersContext } from '../../store/TransfersContext'; -import AuthContext from '../../store/auth-context'; /**@function * @name MyTransfers @@ -14,39 +12,8 @@ import AuthContext from '../../store/auth-context'; * */ const MyTransfers = () => { // get data from context - const { pagination, filter, setIsLoading, prepareRows } = + const { message, tableRows, totalRowCount, setMessage } = useTransfersContext(); - // error - const [message, setMessage] = useState(''); - // data to be displayed in the table - const [tableRows, setTableRows] = useState([]); - // total rows count for pagination - const [totalRowCount, setTotalRowCount] = useState(null); - - const authContext = useContext(AuthContext); - - // load data - useEffect(() => { - const loadData = async () => { - try { - setIsLoading(true); - const data = await getTransfers(authContext.token, { - pagination, - filter, - }); - const preparedRows = prepareRows(await data.transfers); - - setTableRows(preparedRows); - setTotalRowCount(data.total); - } catch (error) { - console.error(error); - setMessage('An error occurred while fetching the table data'); - } finally { - setIsLoading(false); - } - }; - loadData(); - }, [pagination, filter]); return (
{ + try { + await acceptTransfer({ id, token }); + onClose(); + setRefetch(true); + } catch (error) { + console.error('Error accepting transfer:', error); + } + }; + + const handleDecline = async (id) => { + try { + await declineTransfer({ id, token }); + onClose(); + setRefetch(true); + } catch (error) { + console.error('Error declining transfer:', error); + } + }; + + const handleCancel = async (id) => { + try { + await cancelTransfer({ id, token }); + onClose(); + setRefetch(true); + } catch (error) { + console.error('Error cancelling transfer:', error); + } + }; + + const managedWalletsWithDefault = managedWallets.wallets ? managedWallets : { ...managedWallets, wallets: [] }; + + const receiverWallet = rowInfo?.receiver_wallet || rowInfo?.destination_wallet; + const senderWallet = rowInfo?.sender_wallet || rowInfo?.originating_wallet; + const canAcceptDecline = + receiverWallet && + (wallet.name === receiverWallet || + managedWalletsWithDefault.wallets.some(w => w.name === receiverWallet)); + + const canCancel = + senderWallet && + rowInfo?.status === 'pending' && + (wallet.name === senderWallet || + managedWalletsWithDefault.wallets.some(w => w.name === senderWallet)); + + return ( + +
+ + + + + + + + Transfer Request + +
+
+ Transfer ID: + + + +
+ +
+ Sender Wallet: + + + +
+ +
+ Receiver Wallet: + + + +
+ +
+ Initiated By: + + + +
+ +
+ Created Date: + + {rowInfo?.created_date.split('T')[0] || '--'} + +
+ +
+ Token Amount: + + {rowInfo?.token_amount || '--'} + +
+
+ + {rowInfo?.status === 'pending' && canAcceptDecline && ( +
+ handleAccept(rowInfo.id || rowInfo.transfer_id)} + > + Accept Transfer + + handleDecline(rowInfo.id || rowInfo.transfer_id)}> + Decline + +
+ )} + + {rowInfo?.status === 'pending' && canCancel && ( +
+ handleCancel(rowInfo.id || rowInfo.transfer_id)} + > + Cancel Transfer + +
+ )} +
+
+ ); +} + +export default TransferSidePanel; + diff --git a/src/pages/MyTransfers/TransferSidePanel.styled.js b/src/pages/MyTransfers/TransferSidePanel.styled.js new file mode 100644 index 0000000..ef9b6a1 --- /dev/null +++ b/src/pages/MyTransfers/TransferSidePanel.styled.js @@ -0,0 +1,114 @@ +import MuiDrawer from "@mui/material/Drawer"; +import { styled } from "@mui/system"; +import { Typography, Button } from "@mui/material"; + + +const drawerWidth = 320; +const mobileDrawerWidth = 140; + +const openedMixin = (theme) => ({ + width: mobileDrawerWidth, + [theme.breakpoints.up("sm")]: { + width: drawerWidth, + }, + transition: theme.transitions.create("width", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + overflowX: "hidden", +}); + +const closedMixin = (theme) => ({ + transition: theme.transitions.create("width", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + overflowX: "hidden", + width: `calc(${theme.spacing(8)} + 1px)`, + [theme.breakpoints.up("sm")]: { + width: `calc(${theme.spacing(8)} + 1px)`, + }, +}); + +const DrawerHeaderStyled = styled("div")(({ theme }) => ({ + display: "flex", + alignItems: "center", + justifyContent: "flex-end", + padding: theme.spacing(0, 1), + // necessary for content to be below app bar + ...theme.mixins.toolbar, +})); + +const DrawerStyled = styled(MuiDrawer, { + shouldForwardProp: (prop) => prop !== "open", +})(({ theme, open }) => ({ + width: mobileDrawerWidth, + [theme.breakpoints.up("sm")]: { + width: drawerWidth, + }, + flexShrink: 0, + whiteSpace: "nowrap", + boxSizing: "border-box", + ...(open && { + ...openedMixin(theme), + "& .MuiDrawer-paper": openedMixin(theme), + }), + ...(!open && { + ...closedMixin(theme), + "& .MuiDrawer-paper": closedMixin(theme), + }), +})); + +const BoldTypography = styled(Typography)({ + fontWeight: 'bold', + fontSize: '.9rem' +}); + +const NormalTypography = styled(Typography)({ + fontWeight: 400, + fontSize: '.9rem', + textTransform: 'capitalize' +}); + +const DeclineButton = styled(Button)({ + textTransform: 'none', + padding: 0, + minWidth: 0, + border: 'none', + fontWeight: 700, + fontSize: '1rem', + margin: '0 10px', + color: '#FF7A00', + '&:hover': { + border: 'none', + } +}); + +const AcceptButton = styled(Button)({ + textTransform: 'none', + minWidth: 0, + borderRadius: '20px', + fontWeight: 500, + fontSize: '1rem', + padding: '6px 15px', + margin: '0 10px', +}); + +const CancelButton = styled(Button)({ + textTransform: 'none', + minWidth: 0, + borderRadius: '20px', + fontWeight: 500, + fontSize: '1rem', + padding: '6px 15px', + margin: '0 10px', + backgroundColor: '#FF7A00', + color: '#fff', + '&:hover': { + backgroundColor: '#e66a00', + } +}); + + +export { DrawerHeaderStyled, DrawerStyled, BoldTypography, NormalTypography, DeclineButton, AcceptButton, CancelButton }; + diff --git a/src/pages/MyTransfers/TransfersTable.js b/src/pages/MyTransfers/TransfersTable.js index 5ddd087..cc2e693 100644 --- a/src/pages/MyTransfers/TransfersTable.js +++ b/src/pages/MyTransfers/TransfersTable.js @@ -11,10 +11,13 @@ import { Typography, } from '@mui/material'; import React, { useEffect, useRef, useState } from 'react'; + import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; + import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; import { DateRangeFilter, ResetButton, TransferSelectFilter } from './TableFilters'; import { TableCellStyled, TooltipStyled } from './TransfersTable.styled'; import { useTransfersContext } from '../../store/TransfersContext'; import { Loader } from '../../components/UI/components/Loader/Loader'; + import TransferSidePanel from './TransferSidePanel'; /**@function * @name TableHeader @@ -95,10 +98,47 @@ import { * @param tableColumns * @param tableRows * @param getStatusColor + * @param selectedRowIndex + * @param setSelectedRowIndex * @return {JSX.Element} - Table body component */ - const TransfersTableBody = ({ tableColumns, tableRows, getStatusColor }) => { - const { isLoading } = useTransfersContext(); + const TransfersTableBody = ({ + tableColumns, + tableRows, + getStatusColor, + selectedRowIndex, + setSelectedRowIndex, + }) => { + const { isLoading, managedWallets } = useTransfersContext(); + const [isSidePanelOpen, setIsSidePanelOpen] = useState(false); + const [rowInfo, setRowInfo] = useState(null); + const wallet = JSON.parse(localStorage.getItem('wallet') || '{}'); + + const handleClosePanel = () => { + setIsSidePanelOpen(false); + setSelectedRowIndex(null); + }; + + const handleRowClick = (rowIndex, row) => { + setRowInfo(row); + setSelectedRowIndex(rowIndex); + // Only open side panel for pending transfers + if (row.status === 'pending') { + setIsSidePanelOpen(true); + } + }; + + // Check if a row requires user action (pending transfer where user can accept/decline) + const requiresAction = (row) => { + if (row.status !== 'pending') return false; + const managedWalletsWithDefault = managedWallets.wallets ? managedWallets : { ...managedWallets, wallets: [] }; + const receiverWallet = row.receiver_wallet || row.destination_wallet; + return ( + receiverWallet && + (wallet.name === receiverWallet || + managedWalletsWithDefault.wallets.some(w => w.name === receiverWallet)) + ); + }; if (isLoading) return ( @@ -123,37 +163,66 @@ import { ); return ( - - {tableRows && - tableRows.map((row, rowIndex) => { - return ( - - {tableColumns.map((column, colIndex) => { - const cellKey = `${rowIndex}-${colIndex}-${column.description}`; - const cellColor = - column.name === 'status' - ? getStatusColor(row[column.name]) - : ''; - const cellValue = row[column.name] - ? column.renderer - ? column.renderer(row[column.name]) - : row[column.name] - : '--'; + <> + + {tableRows && + tableRows.map((row, rowIndex) => { + const isSelected = rowIndex === selectedRowIndex; + const needsAction = requiresAction(row); + return ( + handleRowClick(rowIndex, row)} + sx={{ + transition: 'all 0.3s ease', + cursor: 'pointer', + }} + style={{ + backgroundColor: + isSelected && needsAction + ? 'rgba(135, 195, 46, .4)' + : isSelected + ? 'rgba(135, 195, 46, .4)' + : needsAction + ? 'rgba(255, 122, 0, .1)' + : null, + border: needsAction ? '2px solid rgba(255, 122, 0, .5)' : 'none', + }} + > + {tableColumns.map((column, colIndex) => { + const cellKey = `${rowIndex}-${colIndex}-${column.description}`; + const cellColor = + column.name === 'status' + ? getStatusColor(row[column.name]) + : ''; + const cellValue = row[column.name] || row[column.name] === 0 + ? column.renderer + ? column.renderer(row[column.name]) + : row[column.name] + : '--'; - return ( - - {cellValue} - - ); - })} - - ); - })} - + return ( + + {cellValue} + + ); + })} + + ); + })} + + {isSidePanelOpen && ( + + )} + ); }; @@ -168,9 +237,12 @@ import { */ const TransfersTable = ({ tableTitle, tableRows, totalRowCount }) => { // get data from context - const { pagination, setPagination, statusList, tableColumns } = + const { pagination, setPagination, statusList, tableColumns, sorting, setSorting } = useTransfersContext(); + // State to track the index of the selected row + const [selectedRowIndex, setSelectedRowIndex] = useState(null); + // pagination const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(10); @@ -191,10 +263,72 @@ import { const newPagination = { ...pagination, offset: newPage * rowsPerPage }; setPagination(newPagination); }; + + // Sorting - initialize from context + const [sortBy, setSortBy] = useState(sorting?.sort_by || 'created_at'); + const [order, setOrder] = useState(sorting?.order || 'desc'); + + // Sync local sorting state with context + useEffect(() => { + if (sorting) { + setSortBy(sorting.sort_by); + setOrder(sorting.order); + } + }, [sorting]); + + const getColumnNames = (columnName) => { + let newSortBy = columnName; + switch (columnName) { + case 'created_date': + newSortBy = 'created_at'; + break; + case 'closed_date': + newSortBy = 'closed_at'; + break; + case 'status': + newSortBy = 'state'; + break; + default: + newSortBy = columnName; + } + return newSortBy; + }; + + const mapSortBy = (columnName) => { + let newSortBy = getColumnNames(columnName); + setSortBy(newSortBy); + return newSortBy; + }; + + const handleSort = (column) => { + if (!column.sortable) return; + + let newOrder = 'asc'; + + if ( + (sortBy === getColumnNames(column.name) || + (column.name === 'created_date' && sortBy === 'created_at') || + (column.name === 'closed_date' && sortBy === 'closed_at') || + (column.name === 'status' && sortBy === 'state')) && + order === 'asc' + ) { + newOrder = 'desc'; + } + + setOrder(newOrder); + + let newSortBy = mapSortBy(column.name); + setSortBy(newSortBy); + + setSorting({ + sort_by: newSortBy, + order: newOrder, + }); + }; // get color corresponding to the status value, else default color const getStatusColor = (status) => { - const color = statusList.find((x) => x.value === status).color; + const color = statusList.find((x) => x.value === status)?.color; return color ? color : '#585B5D'; }; @@ -217,10 +351,26 @@ import { return ( column.sortable && handleSort(column)} > {column.description} + {column.sortable && + sortBy === getColumnNames(column.name) && ( + <> + {order === 'asc' && ( + + )} + {order === 'desc' && ( + + )} + + )} ); })} @@ -230,6 +380,8 @@ import { tableColumns={tableColumns} tableRows={tableRows} getStatusColor={getStatusColor} + selectedRowIndex={selectedRowIndex} + setSelectedRowIndex={setSelectedRowIndex} /> diff --git a/src/store/TransfersContext.js b/src/store/TransfersContext.js index 12a77c2..6b5b98e 100644 --- a/src/store/TransfersContext.js +++ b/src/store/TransfersContext.js @@ -1,7 +1,10 @@ -import { createContext, useContext, useState } from 'react'; +import { createContext, useContext, useState, useEffect } from 'react'; import TransferFilter from '../models/TransferFilter'; import { formatWithCommas, getDateText } from '../utils/formatting'; import { capitalize } from '@mui/material'; +import AuthContext from './auth-context'; +import { getTransfers, getPendingTransfers } from '../api/transfers'; +import { getWallets } from '../api/wallets'; const TransfersContext = createContext(); @@ -24,16 +27,34 @@ const TransfersProvider = ({ children }) => { }); const [filter, setFilter] = useState(defaultFilter); - // Loader + const defaultSorting = { + sort_by: 'created_at', + order: 'desc', + }; + const [sorting, setSorting] = useState(defaultSorting); + const [isLoading, setIsLoading] = useState(false); + const [count, setCount] = useState(0); + + const [refetch, setRefetch] = useState(false); + + const [managedWallets, setManagedWallets] = useState([]); + + const [tableRows, setTableRows] = useState([]); + const [totalRowCount, setTotalRowCount] = useState(null); + const [message, setMessage] = useState(''); + + const authContext = useContext(AuthContext); + const wallet = JSON.parse(localStorage.getItem('wallet') || '{}'); + // transfer statuses const statusList = [ - { - label: 'Requested', - value: 'requested', - color: 'black', - }, + // { + // label: 'Requested', + // value: 'requested', + // color: 'black', + // }, { label: 'Pending', value: 'pending', @@ -49,11 +70,11 @@ const TransfersProvider = ({ children }) => { value: 'cancelled', color: 'red', }, - { - label: 'Failed', - value: 'failed', - color: 'red', - }, + // { + // label: 'Failed', + // value: 'failed', + // color: 'red', + // }, ]; // transfers table columns @@ -61,26 +82,26 @@ const TransfersProvider = ({ children }) => { { description: 'Transfer ID', name: 'transfer_id', - sortable: true, + sortable: false, showInfoIcon: false, }, { description: 'Sender Wallet', name: 'sender_wallet', - sortable: true, + sortable: false, showInfoIcon: false, }, { description: 'Token Amount', name: 'token_amount', - sortable: true, + sortable: false, showInfoIcon: false, renderer: (val) => formatWithCommas(val), }, { description: 'Receiver Wallet', name: 'receiver_wallet', - sortable: true, + sortable: false, showInfoIcon: false, }, { @@ -93,7 +114,7 @@ const TransfersProvider = ({ children }) => { { description: 'Initiated By', name: 'initiated_by', - sortable: true, + sortable: false, showInfoIcon: false, }, { @@ -118,17 +139,127 @@ const TransfersProvider = ({ children }) => { return returnedRows.map(row => { return { transfer_id: row.id, + id: row.id, sender_wallet: row.source_wallet, token_amount: row.token_count, receiver_wallet: row.destination_wallet, created_date: row.created_at, + created_at: row.created_at, initiated_by: row.originating_wallet, closed_date: row.closed_at, + closed_at: row.closed_at, status: row.state, + state: row.state, + source_wallet: row.source_wallet, + destination_wallet: row.destination_wallet, + originating_wallet: row.originating_wallet, + token_count: row.token_count, }; }); }; + const getStatusPriority = (status) => { + switch (status) { + case 'pending': + return 1; + case 'completed': + return 2; + case 'cancelled': + return 3; + default: + return 4; + } + }; + + const sortRowsByDefaultOrder = (rows) => { + return [...rows].sort((a, b) => { + const statusPriorityA = getStatusPriority(a.status); + const statusPriorityB = getStatusPriority(b.status); + + if (statusPriorityA !== statusPriorityB) { + return statusPriorityA - statusPriorityB; + } + + const dateA = new Date(a.created_at || a.created_date || 0); + const dateB = new Date(b.created_at || b.created_date || 0); + return dateB - dateA; + }); + }; + + const loadData = async () => { + try { + setIsLoading(true); + + const data = await getTransfers(authContext.token, { + pagination, + filter, + sorting, + }); + let preparedRows = prepareRows(await data.transfers); + + const isDefaultSort = + sorting.sort_by === defaultSorting.sort_by && + sorting.order === defaultSorting.order; + + if (isDefaultSort) { + preparedRows = sortRowsByDefaultOrder(preparedRows); + } + + setTableRows(preparedRows); + setTotalRowCount(data.total); + } catch (error) { + console.error(error); + setMessage('An error occurred while fetching the table data'); + } finally { + setIsLoading(false); + setRefetch(false); + } + }; + + const loadPendingTransfersData = async () => { + try { + setIsLoading(true); + + const allWalletsData = await getWallets(authContext.token, '', { + pagination: { limit: 1000 }, + }); + setManagedWallets(allWalletsData); + + let local_count = 0; + const pendingTransfers = await getPendingTransfers(authContext.token); + + if (pendingTransfers && pendingTransfers.transfers) { + for (const item of pendingTransfers.transfers) { + if (wallet.name === item.destination_wallet) { + local_count++; + } else if ( + allWalletsData.wallets && + allWalletsData.wallets.some( + (wallet) => wallet.name === item.destination_wallet + ) + ) { + local_count++; + } + } + } + setCount(local_count); + } catch (error) { + console.error( + 'An error occurred fetching the managed wallets and/or pending transfers', + error + ); + } finally { + setIsLoading(false); + } + }; + + useEffect(() => { + loadPendingTransfersData(); + }, [refetch]); + + useEffect(() => { + loadData(); + }, [pagination, filter, sorting, refetch]); const value = { pagination, @@ -141,6 +272,17 @@ const TransfersProvider = ({ children }) => { setIsLoading, tableColumns, prepareRows, + sorting, + setSorting, + count, + refetch, + setRefetch, + managedWallets, + tableRows, + totalRowCount, + message, + setMessage, + loadData, }; return ( @@ -150,7 +292,6 @@ const TransfersProvider = ({ children }) => { ); }; -// hook to return transfers context const useTransfersContext = () => { const context = useContext(TransfersContext); if (!context) throw new Error('useTransfersContext must be used within TransfersProvider');