diff --git a/packages/app/src/pages/dashboard/history.tsx b/packages/app/src/pages/dashboard/history.tsx index 01bc45ef1..e78b19c2d 100644 --- a/packages/app/src/pages/dashboard/history.tsx +++ b/packages/app/src/pages/dashboard/history.tsx @@ -5,15 +5,13 @@ import styled from 'styled-components' import UploadIcon from 'assets/svg/futures/upload-icon.svg' import { FlexDivCol, FlexDivRowCentered } from 'components/layout/flex' -import { MobileHiddenView, MobileOnlyView } from 'components/Media' -import Spacer from 'components/Spacer' import { Body, Heading } from 'components/Text' import DashboardLayout from 'sections/dashboard/DashboardLayout' import HistoryTabs, { HistoryTab } from 'sections/dashboard/HistoryTabs' -import TradesTab from 'sections/futures/MobileTrade/UserTabs/TradesTab' import { usePollDashboardFuturesData } from 'state/futures/hooks' import { selectPositionsCsvData, selectTradesCsvData } from 'state/futures/selectors' import { useAppSelector } from 'state/hooks' +import media from 'styles/media' type HistoryPageProps = React.FC & { getLayout: (page: ReactNode) => JSX.Element } @@ -46,33 +44,36 @@ const HistoryPage: HistoryPageProps = () => { ) return ( - <> + {t('dashboard-history.page-title')} - - - - - {t('dashboard-history.main-title')} - {t('dashboard-history.subtitle')} - - - {t('dashboard-history.export-btn')} - - - - - - - - - - - + + + + {t('dashboard-history.main-title')} + {t('dashboard-history.subtitle')} + + + {t('dashboard-history.export-btn')} + + + + + + ) } +const TitleContainer = styled.div` + ${media.lessThan('lg')` + margin: 15px 15px 0 15px; + `} + ${media.lessThan('sm')` + margin: 130px 15px 0 15px; + `} +` + const ExportButton = styled.a` gap: 8px; height: 36px; diff --git a/packages/app/src/sections/dashboard/HistoryTabs.tsx b/packages/app/src/sections/dashboard/HistoryTabs.tsx index eeed3b00e..6d65d2bcd 100644 --- a/packages/app/src/sections/dashboard/HistoryTabs.tsx +++ b/packages/app/src/sections/dashboard/HistoryTabs.tsx @@ -10,7 +10,6 @@ import { fetchPositionHistoryForTrader } from 'state/futures/actions' import { selectPositionsHistoryTableData } from 'state/futures/selectors' import { useAppDispatch, useAppSelector } from 'state/hooks' import { selectWallet } from 'state/wallet/selectors' -import media from 'styles/media' export enum HistoryTab { Positions = 'positions', @@ -33,23 +32,21 @@ const HistoryTabs: React.FC = ({ currentTab, onChangeTab }) => }, [dispatch, walletAddress]) return ( - - - - - - - + <> + + + +
= ({ currentTab, onChangeTab }) =>
-
+ ) } -const HistoryTabsHeader = styled.div` - display: flex; - justify-content: space-between; - margin-bottom: 15px; - - ${media.lessThan('md')` - flex-direction: column; - row-gap: 10px; - margin-bottom: 25px; - margin-top: 0px; - `} -` - -const HistoryTabsContainer = styled.div` - ${media.lessThan('md')` - padding: 15px; - `} -` - const TabButtons = styled.div` display: flex; - - & > button:not(:last-of-type) { - margin-right: 25px; - } - - ${media.lessThan('md')` - justify-content: flex-start; - `} + gap: 25px; + margin-bottom: 25px; ` export default HistoryTabs diff --git a/packages/app/src/sections/futures/TraderHistory.tsx b/packages/app/src/sections/futures/TraderHistory.tsx index 8c5e6c213..53c299d35 100644 --- a/packages/app/src/sections/futures/TraderHistory.tsx +++ b/packages/app/src/sections/futures/TraderHistory.tsx @@ -1,17 +1,16 @@ import { FuturesMarketKey, FuturesPositionHistory } from '@kwenta/sdk/types' import Wei, { wei, WeiSource } from '@synthetixio/wei' -import router from 'next/router' import { FC, memo, useMemo } from 'react' import { useTranslation } from 'react-i18next' import styled, { css } from 'styled-components' import Currency from 'components/Currency' import CurrencyIcon from 'components/Currency/CurrencyIcon' -import { FlexDiv } from 'components/layout/flex' +import { FlexDiv, FlexDivCol } from 'components/layout/flex' import { DesktopOnlyView, MobileOrTabletView } from 'components/Media' import Table, { TableHeader, TableNoResults } from 'components/Table' +import { TableCell } from 'components/Table/TableBodyRow' import { Body } from 'components/Text' -import ROUTES from 'constants/routes' import TimeDisplay from 'sections/futures/Trades/TimeDisplay' import { selectQueryStatuses } from 'state/futures/selectors' import { useAppSelector } from 'state/hooks' @@ -192,7 +191,7 @@ const TraderHistory: FC = memo( /> - = memo( autoResetPageIndex={false} columns={[ { - header: () => ( - - { - resetSelection() - router.push(ROUTES.Leaderboard.Home) - }} - > - {t('leaderboard.leaderboard.table.title')} - - > - - {traderEns ?? trader} - - + accessorKey: 'asset', + cell: (cellProps) => ( + <> + {t('leaderboard.trader-history.mobile-table.market')} + + + {cellProps.row.original.marketShortName} + + + ), + }, + { + accessorKey: 'totalVolume', + cell: (cellProps) => ( + <> + {t('leaderboard.trader-history.mobile-table.volume')} + + + + + ), + }, + { + accessorKey: 'status', + cell: (cellProps) => ( + <> + {t('leaderboard.trader-history.mobile-table.status')} + {cellProps.row.original.status} + ), - accessorKey: 'title', - enableSorting: false, - columns: [ - { - header: () => ( - {t('leaderboard.trader-history.table.market')} - ), - accessorKey: 'asset', - cell: (cellProps) => ( - - - {cellProps.row.original.marketShortName} - - ), - size: 50, - }, - { - header: () => ( - {t('leaderboard.trader-history.table.status')} - ), - accessorKey: 'status', - cell: (cellProps) => { - return {cellProps.row.original.status} - }, - size: 30, - }, - { - header: () => ( - {t('leaderboard.trader-history.table.total-pnl')} - ), - accessorKey: 'pnl', - cell: (cellProps) => ( - - - - {cellProps.row.original.pnlPct} - - - ), - size: 40, - }, - ], + }, + { + accessorKey: 'funding', + cell: (cellProps) => ( + <> + + {t('leaderboard.trader-history.mobile-table.funding')} + + + + + + ), + }, + { + accessorKey: 'pnl', + cell: (cellProps) => ( + <> + {t('leaderboard.trader-history.mobile-table.pnl')} + + + + {cellProps.row.original.pnlPct} + + + + ), + }, + { + accessorKey: 'openTimestamp', + cell: (cellProps) => { + return ( + <> + + {t('leaderboard.trader-history.mobile-table.timestamp')} + + + + + + ) + }, }, ]} /> @@ -296,6 +306,33 @@ const StyledTable = styled(Table)<{ compact?: boolean; height?: number }>` `} ` as typeof Table +const MobileTable = styled(Table)` + .table-row:first-child { + display: none; + } + + .table-body-row { + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto; + grid-column-gap: 15px; + grid-row-gap: 15px; + padding: 15px; + } + + ${TableCell}:first-child, + ${TableCell}:last-child { + padding: 0; + } + ${TableCell} { + height: 100%; + width: 100% !important; + display: flex; + align-items: start; + justify-content: space-between; + } +` as typeof Table + const TableTitle = styled.div` width: 100%; display: flex; @@ -331,6 +368,13 @@ const StyledCurrencyIcon = styled(CurrencyIcon)` margin-right: 5px; ` +const MobileCurrencyIcon = styled(CurrencyIcon)` + & > img { + width: 20px; + height: 20px; + } +` + const CurrencyInfo = styled(FlexDiv)` align-items: center; ` diff --git a/packages/app/src/sections/futures/Trades/Trades.tsx b/packages/app/src/sections/futures/Trades/Trades.tsx index 6d7fe26b8..4b739a5ce 100644 --- a/packages/app/src/sections/futures/Trades/Trades.tsx +++ b/packages/app/src/sections/futures/Trades/Trades.tsx @@ -1,19 +1,22 @@ import { formatDollars, formatNumber } from '@kwenta/sdk/utils' +import { wei, WeiSource } from '@synthetixio/wei' import { useRouter } from 'next/router' import { FC, memo, useMemo } from 'react' import { useTranslation } from 'react-i18next' -import styled from 'styled-components' +import styled, { css } from 'styled-components' import ColoredPrice from 'components/ColoredPrice' import Currency from 'components/Currency' -import { FlexDivCol } from 'components/layout/flex' +import CurrencyIcon from 'components/Currency/CurrencyIcon' +import { FlexDiv, FlexDivCol } from 'components/layout/flex' +import { DesktopOnlyView, MobileOrTabletView } from 'components/Media' import Table, { TableHeader, TableNoResults } from 'components/Table' +import { TableCell } from 'components/Table/TableBodyRow' import { Body } from 'components/Text' import ROUTES from 'constants/routes' import { blockExplorer } from 'containers/Connector/Connector' import useIsL2 from 'hooks/useIsL2' import useNetworkSwitcher from 'hooks/useNetworkSwitcher' -import useWindowSize from 'hooks/useWindowSize' import { selectFuturesType } from 'state/futures/common/selectors' import { selectAllTradesForAccountType, @@ -23,6 +26,7 @@ import { selectSmartMarginQueryStatuses } from 'state/futures/smartMargin/select import { useAppSelector } from 'state/hooks' import { FetchStatus } from 'state/types' +import PositionType from '../PositionType' import TableMarketDetails from '../UserInfo/TableMarketDetails' import TimeDisplay from './TimeDisplay' @@ -35,7 +39,6 @@ const Trades: FC = memo(({ rounded = false, noBottom = true }) => { const { t } = useTranslation() const { switchToL2 } = useNetworkSwitcher() const router = useRouter() - const { lessThanWidth } = useWindowSize() const accountType = useAppSelector(selectFuturesType) const history = useAppSelector(selectAllTradesForAccountType) const historyData = useAppSelector(selectTradesHistoryTableData) @@ -48,300 +51,265 @@ const Trades: FC = memo(({ rounded = false, noBottom = true }) => { const columnsDeps = useMemo(() => [historyData], [historyData]) - return lessThanWidth('xl') ? ( - ( - {t('futures.market.user.trades.table.market-side')} - ), - accessorKey: 'market', - cell: (cellProps) => { - return ( - { - cellProps.row.original.market && - router.push( - ROUTES.Markets.MarketPair(cellProps.row.original.market.asset, accountType) - ) - e.stopPropagation() - }} - > - {cellProps.row.original.market ? ( - + return ( + <> + +
( + {t('futures.market.user.trades.table.market-side')} + ), + accessorKey: 'market', + cell: (cellProps) => { + return ( + { + cellProps.row.original.market && + router.push( + ROUTES.Markets.MarketPair( + cellProps.row.original.market.asset, + accountType + ) + ) + e.stopPropagation() + }} + > + {cellProps.row.original.market ? ( + + ) : ( + '-' + )} + + ) + }, + }, + { + header: () => {t('futures.market.user.trades.table.date')}, + accessorKey: 'time', + cell: (cellProps) => , + enableSorting: true, + }, + { + header: () => ( + + {t('futures.market.user.trades.table.price-type')} + + ), + accessorKey: 'value', + sortingFn: 'basic', + cell: (cellProps) => { + return ( + + + {cellProps.row.original.type} + + ) + }, + enableSorting: true, + }, + { + header: () => ( + + {t('futures.market.user.trades.table.size')} + + ), + accessorKey: 'amount', + sortingFn: 'basic', + cell: (cellProps) => { + return ( + + {formatNumber(cellProps.getValue(), { suggestDecimals: true })} + + + ) + }, + enableSorting: true, + }, + { + header: () => ( + + {t('futures.market.user.trades.table.pnl')} + + ), + accessorKey: 'netPnl', + sortingFn: 'basic', + cell: (cellProps) => { + return cellProps.getValue().eq(0) ? ( + '--' ) : ( - '-' - )} - - ) - }, - }, - { - header: () => {t('futures.market.user.trades.table.date')}, - accessorKey: 'time', - cell: (cellProps) => , - enableSorting: true, - }, - { - header: () => ( - - {t('futures.market.user.trades.table.price-type')} - - ), - accessorKey: 'value', - sortingFn: 'basic', - cell: (cellProps) => { - return ( - - - {cellProps.row.original.type} - - ) - }, - enableSorting: true, - }, - { - header: () => ( - - {t('futures.market.user.trades.table.size')} - - ), - accessorKey: 'amount', - sortingFn: 'basic', - cell: (cellProps) => { - return ( - - {formatNumber(cellProps.getValue(), { suggestDecimals: true })} - - - ) - }, - enableSorting: true, - }, - { - header: () => ( - - {t('futures.market.user.trades.table.pnl')} - - ), - accessorKey: 'netPnl', - sortingFn: 'basic', - cell: (cellProps) => { - return cellProps.getValue().eq(0) ? ( - '--' - ) : ( - - {formatDollars(cellProps.getValue(), { maxDecimals: 2 })} - - ) - }, - enableSorting: true, - }, - { - header: () => ( - - {t('futures.market.user.trades.table.fees')} - - ), - sortingFn: 'basic', - accessorKey: 'feesPaid', - cell: (cellProps) => ( -
- -
- ), - enableSorting: true, - }, - ]} - columnsDeps={columnsDeps} - data={historyData} - isLoading={isLoading && isLoaded} - onTableRowClick={(row) => - window.open(blockExplorer.txLink(row.original.txnHash), '_blank', 'noopener noreferrer') - } - noResultsMessage={ - !isL2 ? ( - - {t('common.l2-cta')} -
{t('homepage.l2.cta-buttons.switch-l2')}
-
- ) : isLoaded && historyData?.length === 0 ? ( - {t('futures.market.user.trades.table.no-results')} - ) : undefined - } - /> - ) : ( -
( - {t('futures.market.user.trades.table.market-side')} - ), - accessorKey: 'market', - cell: (cellProps) => { - return ( - { - cellProps.row.original.market && - router.push( - ROUTES.Markets.MarketPair(cellProps.row.original.market.asset, accountType) - ) - e.stopPropagation() - }} - > - {cellProps.row.original.market ? ( - - ) : ( - '-' - )} - - ) - }, - size: 90, - }, - { - header: () => {t('futures.market.user.trades.table.date')}, - accessorKey: 'time', - cell: (cellProps) => , - enableSorting: true, - size: 90, - }, - { - header: () => ( - - {t('futures.market.user.trades.table.price')} - - ), - accessorKey: 'value', - sortingFn: 'basic', - cell: (cellProps) => { - return ( -
- - {formatDollars(cellProps.getValue(), { suggestDecimals: true })} - -
- ) - }, - enableSorting: true, - size: 125, - }, - { - header: () => ( - - {t('futures.market.user.trades.table.size')} - - ), - accessorKey: 'amount', - sortingFn: 'basic', - cell: (cellProps) => { - return ( - - {formatNumber(cellProps.getValue(), { suggestDecimals: true })} - - - ) - }, - enableSorting: true, - size: 100, - }, - { - header: () => ( - - {t('futures.market.user.trades.table.pnl')} - - ), - accessorKey: 'netPnl', - sortingFn: 'basic', - cell: (cellProps) => { - return cellProps.getValue().eq(0) ? ( - '--' - ) : ( - - {formatDollars(cellProps.getValue(), { maxDecimals: 2 })} - - ) - }, - enableSorting: true, - size: 100, - }, - { - header: () => ( - - {t('futures.market.user.trades.table.fees')} - - ), - sortingFn: 'basic', - accessorKey: 'feesPaid', - cell: (cellProps) => { - return cellProps.getValue().eq(0) ? ( - '--' - ) : ( - - - - ) - }, - enableSorting: true, - size: 100, - }, - { - header: () => ( - {t('futures.market.user.trades.table.order-type')} - ), - accessorKey: 'type', - sortingFn: 'basic', - cell: (cellProps) => <>{cellProps.getValue()}, - size: 60, - }, - ]} - columnsDeps={columnsDeps} - data={historyData} - isLoading={isLoading && isLoaded} - onTableRowClick={(row) => - window.open(blockExplorer.txLink(row.original.txnHash), '_blank', 'noopener noreferrer') - } - noResultsMessage={ - !isL2 ? ( - - {t('common.l2-cta')} -
{t('homepage.l2.cta-buttons.switch-l2')}
-
- ) : isLoaded && historyData?.length === 0 ? ( - {t('futures.market.user.trades.table.no-results')} - ) : undefined - } - /> + + {formatDollars(cellProps.getValue(), { maxDecimals: 2 })} + + ) + }, + enableSorting: true, + }, + { + header: () => ( + + {t('futures.market.user.trades.table.fees')} + + ), + sortingFn: 'basic', + accessorKey: 'feesPaid', + cell: (cellProps) => ( +
+ +
+ ), + enableSorting: true, + }, + ]} + columnsDeps={columnsDeps} + data={historyData} + isLoading={isLoading && isLoaded} + onTableRowClick={(row) => + window.open(blockExplorer.txLink(row.original.txnHash), '_blank', 'noopener noreferrer') + } + noResultsMessage={ + !isL2 ? ( + + {t('common.l2-cta')} +
{t('homepage.l2.cta-buttons.switch-l2')}
+
+ ) : isLoaded && historyData?.length === 0 ? ( + {t('futures.market.user.trades.table.no-results')} + ) : undefined + } + /> + + + + ( + <> + {t('futures.market.user.trades.mobile-table.market')} + + + {cellProps.row.original.market!.marketName} + + + ), + }, + { + accessorKey: 'price', + cell: (cellProps) => ( + <> + {t('futures.market.user.trades.mobile-table.price')} + + + ), + }, + { + accessorKey: 'side', + cell: (cellProps) => ( + <> + {t('futures.market.user.trades.mobile-table.side')} + + + ), + }, + { + accessorKey: 'fees', + cell: (cellProps) => ( + <> + {t('futures.market.user.trades.mobile-table.fees')} + + + ), + }, + { + accessorKey: 'date', + cell: (cellProps) => ( + <> + {t('futures.market.user.trades.mobile-table.date')} + + + ), + }, + { + accessorKey: 'type', + cell: (cellProps) => ( + <> + {t('futures.market.user.trades.mobile-table.type')} +
{cellProps.row.original.orderType}
+ + ), + }, + { + accessorKey: 'size', + cell: (cellProps) => ( + <> + {t('futures.market.user.trades.mobile-table.size')} + +
+ {formatNumber(cellProps.row.original.amount, { suggestDecimals: true })} + {' ETH'} +
+ +
+ + ), + }, + { + accessorKey: 'pnl', + cell: (cellProps) => ( + <> + {t('futures.market.user.trades.mobile-table.pnl')} + + + + {formatNumber(cellProps.row.original.pnlPct)}% + + + + ), + }, + ]} + columnsDeps={columnsDeps} + data={historyData} + isLoading={isLoading && isLoaded} + onTableRowClick={(row) => + window.open(blockExplorer.txLink(row.original.txnHash), '_blank', 'noopener noreferrer') + } + noResultsMessage={ + !isL2 ? ( + + {t('common.l2-cta')} +
{t('homepage.l2.cta-buttons.switch-l2')}
+
+ ) : isLoaded && historyData?.length === 0 ? ( + {t('futures.market.user.trades.table.no-results')} + ) : undefined + } + /> +
+ ) }) @@ -350,3 +318,61 @@ export default Trades const MarketDetailsContainer = styled.div` cursor: pointer; ` +const MobileTable = styled(Table)` + .table-row:first-child { + display: none; + } + + .table-body-row { + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto; + grid-column-gap: 15px; + grid-row-gap: 15px; + padding: 15px; + } + + ${TableCell}:first-child, + ${TableCell}:last-child { + padding: 0; + } + ${TableCell} { + height: 100%; + width: 100% !important; + display: flex; + align-items: start; + justify-content: space-between; + } +` as typeof Table + +const CurrencyInfo = styled(FlexDiv)` + align-items: center; +` + +const MobileCurrencyIcon = styled(CurrencyIcon)` + & > img { + width: 20px; + height: 20px; + } +` + +const StyledSubtitle = styled(Body)` + color: ${(props) => props.theme.colors.selectedTheme.button.text.primary}; + text-transform: capitalize; +` + +const valueColor = css<{ $value: WeiSource }>` + color: ${(props) => + wei(props.$value).gt(0) + ? props.theme.colors.selectedTheme.green + : wei(props.$value).lt(0) + ? props.theme.colors.selectedTheme.red + : props.theme.colors.selectedTheme.button.text.primary}; +` + +const StyledValue = styled.div<{ $value: WeiSource }>` + font-family: ${(props) => props.theme.fonts.mono}; + font-size: 13px; + margin: 0; + ${valueColor} +` diff --git a/packages/app/src/state/futures/selectors.ts b/packages/app/src/state/futures/selectors.ts index c591a38b7..cd4c25baa 100644 --- a/packages/app/src/state/futures/selectors.ts +++ b/packages/app/src/state/futures/selectors.ts @@ -913,13 +913,15 @@ export const selectTradesHistoryTableData = createSelector( const pnl = trade?.pnl const feesPaid = trade?.feesPaid const netPnl = pnl.sub(feesPaid) + const notionalValue = trade?.price.mul(trade?.size.abs()) return { ...trade, pnl, + pnlPct: netPnl.div(notionalValue), feesPaid, netPnl, - notionalValue: trade?.price.mul(trade?.size.abs()), + notionalValue, value: Number(trade?.price), funding: Number(trade?.fundingAccrued), amount: trade?.size.abs(), diff --git a/packages/app/src/translations/en.json b/packages/app/src/translations/en.json index f4b221bbd..6a9726197 100644 --- a/packages/app/src/translations/en.json +++ b/packages/app/src/translations/en.json @@ -1147,6 +1147,16 @@ "liquidated": "liquidated", "entry": "entry" } + }, + "mobile-table": { + "market": "Market", + "price": "Price", + "side": "Side", + "fees": "Fees", + "date": "Date", + "type": "Order Type", + "size": "Size", + "pnl": "PnL" } }, "transfers": { @@ -1241,6 +1251,14 @@ "total-volume": "Total Volume", "total-pnl": "Realized P&L", "funding": "Funding" + }, + "mobile-table": { + "market": "Market", + "volume": "Volume", + "status": "Status", + "funding": "Funding", + "pnl": "PnL", + "timestamp": "Date" } }, "competition": {