mirror of
https://github.com/Qortal/q-trade.git
synced 2025-06-14 18:31:22 +00:00
added history table
This commit is contained in:
parent
8a8239f5bb
commit
37099e90c8
110
src/components/history/History.tsx
Normal file
110
src/components/history/History.tsx
Normal file
@ -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 (
|
||||
<div style={{
|
||||
width: '100%',
|
||||
display: show ? 'block' : 'none'
|
||||
}}>
|
||||
<Button variant='outlined' onClick={()=> {
|
||||
setMode('buyHistory')
|
||||
}}>Buy History</Button>
|
||||
<Button onClick={()=> {
|
||||
setMode('sellHistory')
|
||||
}} variant='outlined'>Sell History</Button>
|
||||
<ButtonBase onClick={()=> {
|
||||
getBuyHistory(qortAddress, selectedCoin, mode)
|
||||
}}>
|
||||
<RefreshIcon />
|
||||
</ButtonBase>
|
||||
<Typography>Showing most recent 20 results</Typography>
|
||||
<HistoryList qortAddress={qortAddress} historyList={selectedHistory} />
|
||||
<Snackbar
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
|
||||
open={open}
|
||||
onClose={()=> {
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<Alert
|
||||
onClose={()=> setOpen(false)}
|
||||
severity="info"
|
||||
variant="filled"
|
||||
sx={{ width: "100%" }}
|
||||
>
|
||||
{'Fetching History'}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
</div>
|
||||
)
|
||||
}
|
190
src/components/history/HistoryList.tsx
Normal file
190
src/components/history/HistoryList.tsx
Normal file
@ -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<any>(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 (
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="ag-theme-alpine-dark"
|
||||
style={{ height: 400, width: "100%" }}
|
||||
>
|
||||
<AgGridReact
|
||||
ref={gridRef}
|
||||
columnDefs={columnDefs}
|
||||
defaultColDef={defaultColDef}
|
||||
rowData={historyList}
|
||||
// onRowClicked={onRowClicked}
|
||||
// onSelectionChanged={onSelectionChanged}
|
||||
// getRowStyle={getRowStyle}
|
||||
autoSizeStrategy={autoSizeStrategy}
|
||||
rowSelection="single" // Enable multi-select
|
||||
suppressHorizontalScroll={false} // Allow horizontal scroll on mobile if needed
|
||||
suppressCellFocus={true} // Prevents cells from stealing focus in mobile
|
||||
// pagination={true}
|
||||
// paginationPageSize={10}
|
||||
onGridReady={onGridReady}
|
||||
// domLayout='autoHeight'
|
||||
// getRowId={(params) => params.data.qortalAtAddress} // Ensure rows have unique IDs
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -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([]);
|
||||
|
@ -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<IsInstalledProps> = ({}) => {
|
||||
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<IsInstalledProps> = ({}) => {
|
||||
</div>
|
||||
|
||||
<CreateSell show={mode === "sell"} qortAddress={userInfo?.address} />
|
||||
<History show={mode === "history"} qortAddress={userInfo?.address} />
|
||||
</AppContainer>
|
||||
</>
|
||||
);
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user