From 37099e90c8f040791fb52ab82f69cb6c2fea5fd6 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 25 Dec 2024 10:42:48 +0200 Subject: [PATCH] added history table --- src/components/history/History.tsx | 110 ++++++++++++++ src/components/history/HistoryList.tsx | 190 +++++++++++++++++++++++++ src/components/sell/TradeBotList.tsx | 42 +----- src/pages/Home/Home.tsx | 4 +- src/utils/formatTime.ts | 19 +++ 5 files changed, 323 insertions(+), 42 deletions(-) create mode 100644 src/components/history/History.tsx create mode 100644 src/components/history/HistoryList.tsx diff --git a/src/components/history/History.tsx b/src/components/history/History.tsx new file mode 100644 index 0000000..6cb056d --- /dev/null +++ b/src/components/history/History.tsx @@ -0,0 +1,110 @@ +import { Alert, Box, Button, ButtonBase, DialogActions, DialogContent, DialogTitle, IconButton, InputLabel, Snackbar, SnackbarCloseReason, TextField, Typography, styled } from '@mui/material' +import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react' +import { BootstrapDialog } from '../Terms' +import CloseIcon from '@mui/icons-material/Close'; +import { Spacer } from '../common/Spacer'; +import gameContext from '../../contexts/gameContext'; +import HistoryList from './HistoryList'; +import RefreshIcon from "@mui/icons-material/Refresh"; + + + + +export const History = ({qortAddress, show}) => { + const [buyHistory, setBuyHistory] = useState({}) + const [sellHistory, setSellHistory] = useState({}) + + const { getCoinLabel, selectedCoin} = useContext(gameContext) + const [mode, setMode] = useState('buyHistory') + const [open, setOpen] = useState(false) + + const selectedHistory = useMemo(()=> { + if(mode === 'buyHistory') return buyHistory[selectedCoin] || [] + if(mode === 'sellHistory') return sellHistory[selectedCoin] || [] + }, [selectedCoin, buyHistory, sellHistory, mode]) + const getBuyHistory = useCallback((address, foreignBlockchain, mode, limit = 20)=> { + setOpen(true) + let historyUrl + if(mode === 'buyHistory'){ + historyUrl = `/crosschain/trades?foreignBlockchain=${foreignBlockchain}&buyerAddress=${address}&limit=${limit}&reverse=true`; + + } + if(mode === 'sellHistory'){ + historyUrl = `/crosschain/trades?foreignBlockchain=${foreignBlockchain}&sellerAddress=${address}&limit=${limit}&reverse=true`; + + } + + + + fetch(historyUrl) + .then((response) => { + return response.json(); + }) + .then((data) => { + if(mode === 'buyHistory'){ + setBuyHistory((prev)=> { + return { + ...prev, + [foreignBlockchain]: data + } + }) + } + if(mode === 'sellHistory'){ + setSellHistory((prev)=> { + return { + ...prev, + [foreignBlockchain]: data + } + }) + } + + }).catch(()=> {}).finally(()=> { + setOpen(false) + }) + }, []) + + useEffect(()=> { + if(!qortAddress || !selectedCoin) return + if(mode === 'buyHistory' && buyHistory[selectedCoin])return + if(mode === 'sellHistory' && sellHistory[selectedCoin])return + + getBuyHistory(qortAddress, selectedCoin, mode) + }, [qortAddress, selectedCoin, buyHistory, mode]) + + return ( +
+ + + { + getBuyHistory(qortAddress, selectedCoin, mode) + }}> + + + Showing most recent 20 results + + { + setOpen(false) + }} + > + setOpen(false)} + severity="info" + variant="filled" + sx={{ width: "100%" }} + > + {'Fetching History'} + + +
+ ) +} diff --git a/src/components/history/HistoryList.tsx b/src/components/history/HistoryList.tsx new file mode 100644 index 0000000..dd680a3 --- /dev/null +++ b/src/components/history/HistoryList.tsx @@ -0,0 +1,190 @@ +import { ColDef } from "ag-grid-community"; +import { AgGridReact } from "ag-grid-react"; +import React, { + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import { autoSizeStrategy, baseLocalHost } from "../Grids/TradeOffers"; +import { Alert, Box, Snackbar, SnackbarCloseReason, Typography } from "@mui/material"; +import gameContext from "../../contexts/gameContext"; +import { formatTimestampForum } from "../../utils/formatTime"; + +const defaultColDef = { + resizable: true, // Make columns resizable by default + sortable: true, // Make columns sortable by default + suppressMovable: true, // Prevent columns from being movable +}; + + + +export default function HistoryList({ qortAddress, historyList }) { + const gridRef = useRef(null); + console.log('historyList', historyList) + const { getCoinLabel, selectedCoin} = useContext(gameContext) + const [qortalNames, setQortalNames] = useState({}); + + + const onGridReady = useCallback((params: any) => { + params.api.sizeColumnsToFit(); // Adjust columns to fit the grid width + const allColumnIds = params.columnApi + .getAllColumns() + .map((col: any) => col.getColId()); + params.columnApi.autoSizeColumns(allColumnIds); // Automatically adjust the width to fit content + }, []); + + const getName = async (address) => { + try { + const response = await fetch("/names/address/" + address); + const nameData = await response.json(); + if (nameData?.length > 0) { + setQortalNames((prev) => { + return { + ...prev, + [address]: nameData[0].name, + }; + }); + } else { + setQortalNames((prev) => { + return { + ...prev, + [address]: null, + }; + }); + } + } catch (error) { + // error + } + }; + + + const columnDefs: ColDef[] = useMemo(()=> { + return [ + { + headerName: "QORT AMOUNT", + field: "qortAmount", + flex: 1, // Flex makes this column responsive + minWidth: 150, // Ensure it doesn't shrink too much + resizable: true, + }, + { + headerName: `${getCoinLabel()}/QORT`, + valueGetter: (params) => + +params.data.foreignAmount / +params.data.qortAmount, + sortable: true, + flex: 1, // Flex makes this column responsive + minWidth: 150, // Ensure it doesn't shrink too much + resizable: true, + }, + { + headerName: `Total ${getCoinLabel()} Value`, + field: "foreignAmount", + flex: 1, // Flex makes this column responsive + minWidth: 150, // Ensure it doesn't shrink too much + resizable: true, + }, + { + headerName: "Time", + field: "tradeTimestamp", + valueGetter: (params) => + formatTimestampForum(params.data.tradeTimestamp), + flex: 1, // Flex makes this column responsive + minWidth: 200, // Ensure it doesn't shrink too much + resizable: true, + }, + { + headerName: "Buyer", + field: "buyerReceivingAddress", + flex: 1, // Flex makes this column responsive + minWidth: 200, // Ensure it doesn't shrink too much + resizable: true, + valueGetter: (params) => { + if (params?.data?.buyerReceivingAddress) { + if (qortalNames[params?.data?.buyerReceivingAddress]) { + return qortalNames[params?.data?.buyerReceivingAddress]; + } else if (qortalNames[params?.data?.buyerReceivingAddress] === undefined) { + getName(params?.data?.buyerReceivingAddress); + + return params?.data?.buyerReceivingAddress; + } else { + return params?.data?.buyerReceivingAddress; + } + } + }, + }, + { + headerName: "Seller", + field: "sellerAddress", + flex: 1, // Flex makes this column responsive + minWidth: 200, // Ensure it doesn't shrink too much + resizable: true, + valueGetter: (params) => { + if (params?.data?.sellerAddress) { + if (qortalNames[params?.data?.sellerAddress]) { + return qortalNames[params?.data?.sellerAddress]; + } else if (qortalNames[params?.data?.sellerAddress] === undefined) { + getName(params?.data?.sellerAddress); + + return params?.data?.sellerAddress; + } else { + return params?.data?.sellerAddress; + } + } + }, + }, + ]; + + }, [selectedCoin, qortalNames]) + + + + // const onSelectionChanged = (event: any) => { + // const selectedRows = event.api.getSelectedRows(); + // if(selectedRows[0]){ + // setSelectedTrade(selectedRows[0]) + // } else { + // setSelectedTrade(null) + // } + // }; + + + + + + + + return ( +
+
+ params.data.qortalAtAddress} // Ensure rows have unique IDs + /> +
+
+ ); +} diff --git a/src/components/sell/TradeBotList.tsx b/src/components/sell/TradeBotList.tsx index c317868..c619787 100644 --- a/src/components/sell/TradeBotList.tsx +++ b/src/components/sell/TradeBotList.tsx @@ -18,47 +18,7 @@ const defaultColDef = { suppressMovable: true, // Prevent columns from being movable }; -// const columnDefs: ColDef[] = [ -// { -// headerCheckboxSelection: false, // Adds a checkbox in the header for selecting all rows -// checkboxSelection: true, // Adds checkboxes in each row for selection -// headerName: "Select", // You can customize the header name -// width: 50, // Adjust the width as needed -// pinned: "left", // Optional, to pin this column on the left -// resizable: false, -// }, -// { -// headerName: "QORT AMOUNT", -// field: "qortAmount", -// flex: 1, // Flex makes this column responsive -// minWidth: 150, // Ensure it doesn't shrink too much -// resizable: true, -// }, -// { -// headerName: "LTC/QORT", -// valueGetter: (params) => -// +params.data.foreignAmount / +params.data.qortAmount, -// sortable: true, -// sort: "asc", -// flex: 1, // Flex makes this column responsive -// minWidth: 150, // Ensure it doesn't shrink too much -// resizable: true, -// }, -// { -// headerName: "Total LTC Value", -// field: "foreignAmount", -// flex: 1, // Flex makes this column responsive -// minWidth: 150, // Ensure it doesn't shrink too much -// resizable: true, -// }, -// { -// headerName: "Status", -// field: "status", -// flex: 1, // Flex makes this column responsive -// minWidth: 300, // Ensure it doesn't shrink too much -// resizable: true, -// }, -// ]; + export default function TradeBotList({ qortAddress, failedTradeBots }) { const [tradeBotList, setTradeBotList] = useState([]); diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index bdac02b..4a75776 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -16,6 +16,7 @@ import { Spacer } from "../../components/common/Spacer"; import { ReusableModal } from "../../components/common/reusable-modal/ReusableModal"; import { OAuthButton, OAuthButtonRow } from "./Home-Styles"; import { CreateSell } from "../../components/sell/CreateSell"; +import { History } from "../../components/history/History"; interface IsInstalledProps {} @@ -34,7 +35,7 @@ export const HomePage: FC = ({}) => { selectedCoin } = useContext(gameContext); const { setNotification } = useContext(NotificationContext); - const [mode, setMode] = useState("buy"); + const [mode, setMode] = useState("history"); const filteredOngoingTrades = useMemo(()=> { return onGoingTrades?.filter((item)=> item?.tradeInfo?.foreignBlockchain === selectedCoin) }, [onGoingTrades, selectedCoin]) @@ -108,6 +109,7 @@ export const HomePage: FC = ({}) => { + ); diff --git a/src/utils/formatTime.ts b/src/utils/formatTime.ts index bc26d72..6bc2c01 100644 --- a/src/utils/formatTime.ts +++ b/src/utils/formatTime.ts @@ -1,6 +1,25 @@ +import moment from "moment"; + export function formatTime(seconds: number): string { const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; // Pad the seconds with a leading zero if less than 10 return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`; } + + +export function formatTimestampForum(timestamp: number): string { + const now = moment(); + const timestampMoment = moment(timestamp); + const elapsedTime = now.diff(timestampMoment, 'minutes'); + + if (elapsedTime < 1) { + return `Just now - ${timestampMoment.format('h:mm A')}`; + } else if (elapsedTime < 60) { + return `${elapsedTime}m ago - ${timestampMoment.format('h:mm A')}`; + } else if (elapsedTime < 1440) { + return `${Math.floor(elapsedTime / 60)}h ago - ${timestampMoment.format('h:mm A')}`; + } else { + return timestampMoment.format('MMM D, YYYY - h:mm A'); + } +} \ No newline at end of file