diff --git a/src/components/common/table-container-with-header.tsx b/src/components/common/table-container-with-header.tsx index 60164e9a..31034cdf 100644 --- a/src/components/common/table-container-with-header.tsx +++ b/src/components/common/table-container-with-header.tsx @@ -36,7 +36,7 @@ export function TableContainerWithHeader({ title, actions, children, className =

{title}

{actions &&
{actions}
} -
{children}
+
{children}
); } diff --git a/src/features/autovault/components/vault-detail/allocations/allocations/collateral-view.tsx b/src/features/autovault/components/vault-detail/allocations/allocations/collateral-view.tsx index d4c8651b..6ae303fa 100644 --- a/src/features/autovault/components/vault-detail/allocations/allocations/collateral-view.tsx +++ b/src/features/autovault/components/vault-detail/allocations/allocations/collateral-view.tsx @@ -52,7 +52,7 @@ export function CollateralView({ allocations, totalAllocation, vaultAssetSymbol, {item.collateralSymbol} - + {liquidity} {/* Allocation */} - + { - if (mergedVaultAddresses.length > 0) { + const handleManageVault = (vaultAddress?: string, networkId?: number) => { + if (vaultAddress && networkId) { + router.push(`/autovault/${networkId}/${vaultAddress}`); + } else if (mergedVaultAddresses.length > 0) { const firstVault = mergedVaultAddresses[0]; router.push(`/autovault/${firstVault.networkId}/${firstVault.address}`); } }; const hasVaults = mergedVaultAddresses.length > 0; + const hasSingleVault = mergedVaultAddresses.length === 1; + const hasMultipleVaults = mergedVaultAddresses.length > 1; return (
@@ -177,21 +184,64 @@ export default function AutovaultListContent() { {/* Actions for users with existing vaults */} {isConnected && hasVaults && (
- + {/* Single vault - show avatar with address */} + {hasSingleVault && ( + + )} + + {/* Multiple vaults - show dropdown */} + {hasMultipleVaults && ( + + + + + + {mergedVaultAddresses.map((vault) => ( + handleManageVault(vault.address, vault.networkId)} + className="cursor-pointer" + > + + {vault.address.slice(0, 6)} + + ))} + + + )} + @@ -480,7 +471,7 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His )} {!isVaultAdapter && (
)} - {isGroupedView && ( -
-
- -

- Grouped view may show incomplete groups near page boundaries. Increase entries per page for more accurate grouping. -

-
-
- )} - - {!isVaultAdapter && ( - - Loan Asset - - )} - Market + Action - Side + Market Amount Tx Hash Time @@ -677,7 +646,7 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His ) : (isGroupedView ? groupedHistory : ungroupedHistory).length === 0 ? ( No transactions found @@ -687,8 +656,21 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His (isGroupedView ? groupedHistory : ungroupedHistory).map((group, index) => { // Handle rebalances (expandable) if (isGroupedView && group.isMetaAction && group.metaActionType === 'rebalance') { - const firstTx = group.transactions[0]; - const market = allMarkets.find((m) => m.uniqueKey === firstTx.data.market.uniqueKey) as Market | undefined; + const withdrawals = getWithdrawals(group.transactions); + const supplies = getSupplies(group.transactions); + const firstWithdrawal = withdrawals[0]; + const firstSupply = supplies[0]; + const fromMarket = firstWithdrawal + ? (allMarkets.find((m) => m.uniqueKey === firstWithdrawal.data.market.uniqueKey) as Market | undefined) + : undefined; + const toMarket = firstSupply + ? (allMarkets.find((m) => m.uniqueKey === firstSupply.data.market.uniqueKey) as Market | undefined) + : undefined; + const market = fromMarket ?? toMarket; + const loanAssetDecimals = fromMarket?.loanAsset.decimals ?? toMarket?.loanAsset.decimals ?? 18; + const _loanAssetSymbol = fromMarket?.loanAsset.symbol ?? toMarket?.loanAsset.symbol ?? ''; + const hasMoreWithdrawals = withdrawals.length > 1; + const hasMoreSupplies = supplies.length > 1; const rowKey = `rebalance-${group.hash}`; const isExpanded = expandedRows.has(rowKey); @@ -698,59 +680,75 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His className="cursor-pointer hover:bg-hovered" onClick={() => toggleRow(rowKey)} > - {/* Loan Asset - only show if not vault adapter */} - {!isVaultAdapter && ( - -
- {market && ( - <> - - {getTruncatedAssetName(market.loanAsset.symbol)} - - )} -
-
- )} - - {/* Market - show count of markets */} + {/* Action - Rebalance badge */} - {group.transactions.length} markets + + Rebalance + - {/* Side - Rebalance badge */} + {/* Market - show from -> to markets, left-aligned */} - Rebalance +
+ {fromMarket ? ( +
+ + {hasMoreWithdrawals && +{withdrawals.length - 1}} +
+ ) : ( + From + )} + + {toMarket ? ( +
+ + {hasMoreSupplies && +{supplies.length - 1}} +
+ ) : ( + To + )} +
{/* Amount - show rebalance amount */} {group.amount && market ? ( - - {formatReadable(Number(formatUnits(group.amount, market.loanAsset.decimals)))}{' '} - {getTruncatedAssetName(market.loanAsset.symbol)} - +
+ {formatReadable(Number(formatUnits(group.amount, loanAssetDecimals)))} + +
) : ( {group.transactions.length} actions )} @@ -759,7 +757,7 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His {/* Transaction Hash */}
@@ -773,7 +771,7 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His {/* Time */} {formatTimeAgo(group.timestamp)} @@ -785,7 +783,7 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His {isExpanded && ( m.uniqueKey === firstTx.data.market.uniqueKey) as Market | undefined; const marketCount = new Set(group.transactions.map((t) => t.data.market.uniqueKey)).size; + const hasMoreMarkets = marketCount > 1; return ( - {!isVaultAdapter && market && ( - -
- - {getTruncatedAssetName(market.loanAsset.symbol)} -
-
- )} + {/* Action - Deposits badge */} - - {marketCount} {marketCount === 1 ? 'market' : 'markets'} + + Deposits + + {/* Market - show market identity, left-aligned */} - Deposits +
+ {market && ( + <> + + {hasMoreMarkets && +{marketCount - 1} more} + + )} +
+ + {/* Amount */} {group.amount && market ? ( - - +{formatReadable(Number(formatUnits(group.amount, market.loanAsset.decimals)))}{' '} - {getTruncatedAssetName(market.loanAsset.symbol)} - +
+ +{formatReadable(Number(formatUnits(group.amount, market.loanAsset.decimals)))} + +
) : ( {group.transactions.length} actions )}
+ + {/* Transaction Hash */}
@@ -871,8 +884,11 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His />
+ + {/* Time */} {formatTimeAgo(group.timestamp)} @@ -886,58 +902,73 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His const firstTx = group.transactions[0]; const market = allMarkets.find((m) => m.uniqueKey === firstTx.data.market.uniqueKey) as Market | undefined; const marketCount = new Set(group.transactions.map((t) => t.data.market.uniqueKey)).size; + const hasMoreMarkets = marketCount > 1; return ( - {!isVaultAdapter && market && ( - -
- - {getTruncatedAssetName(market.loanAsset.symbol)} -
-
- )} + {/* Action - Withdrawals badge */} - - {marketCount} {marketCount === 1 ? 'market' : 'markets'} + + Withdrawals + + {/* Market - show market identity, left-aligned */} - Withdrawals +
+ {market && ( + <> + + {hasMoreMarkets && +{marketCount - 1} more} + + )} +
+ + {/* Amount */} {group.amount && market ? ( - - -{formatReadable(Number(formatUnits(group.amount, market.loanAsset.decimals)))}{' '} - {getTruncatedAssetName(market.loanAsset.symbol)} - +
+ -{formatReadable(Number(formatUnits(group.amount, market.loanAsset.decimals)))} + +
) : ( {group.transactions.length} actions )}
+ + {/* Transaction Hash */}
@@ -947,8 +978,11 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His />
+ + {/* Time */} {formatTimeAgo(group.timestamp)} @@ -973,6 +1007,7 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His pageSize={pageSize} onPageChange={setCurrentPage} isLoading={loading} + showEntryCount={false} /> )} @@ -1012,6 +1047,14 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His size="xs" /> + {isGroupedView && ( +
+

+ Note: Grouped view may show incomplete groups near page boundaries. Increase entries per page for more accurate + grouping. +

+
+ )} diff --git a/src/features/history/components/transaction-history-preview.tsx b/src/features/history/components/transaction-history-preview.tsx index d4321852..4f76735d 100644 --- a/src/features/history/components/transaction-history-preview.tsx +++ b/src/features/history/components/transaction-history-preview.tsx @@ -3,13 +3,16 @@ import { useEffect, useState, useMemo } from 'react'; import Link from 'next/link'; import { formatUnits } from 'viem'; +import { motion } from 'framer-motion'; +import { IoIosArrowRoundForward } from 'react-icons/io'; import { Table, TableHeader, TableBody, TableRow, TableCell, TableHead } from '@/components/ui/table'; import { TransactionIdentity } from '@/components/shared/transaction-identity'; import { TableContainerWithDescription } from '@/components/common/table-container-with-header'; +import { MarketIdentity, MarketIdentityMode } from '@/features/markets/components/market-identity'; import { useMarkets } from '@/contexts/MarketsContext'; import useUserTransactions from '@/hooks/useUserTransactions'; import { formatReadable } from '@/utils/balance'; -import { groupTransactionsByHash } from '@/utils/transactionGrouping'; +import { groupTransactionsByHash, getWithdrawals, getSupplies } from '@/utils/transactionGrouping'; import { getTruncatedAssetName } from '@/utils/oracle'; import type { Market } from '@/utils/types'; @@ -54,6 +57,7 @@ export function TransactionHistoryPreview({ const { loading, fetchTransactions } = useUserTransactions(); const [history, setHistory] = useState>([]); const [isInitialized, setIsInitialized] = useState(false); + const [isViewAllHovered, setIsViewAllHovered] = useState(false); const { allMarkets } = useMarkets(); useEffect(() => { @@ -87,9 +91,26 @@ export function TransactionHistoryPreview({ const actions = ( setIsViewAllHovered(true)} + onMouseLeave={() => setIsViewAllHovered(false)} > - View All + + View All + + + + ); @@ -145,15 +166,71 @@ export function TransactionHistoryPreview({ // Handle rebalances if (group.isMetaAction && group.metaActionType === 'rebalance') { + const withdrawals = getWithdrawals(group.transactions); + const supplies = getSupplies(group.transactions); + const firstWithdrawal = withdrawals[0]; + const firstSupply = supplies[0]; + const fromMarket = firstWithdrawal + ? (allMarkets.find((m) => m.uniqueKey === firstWithdrawal.data.market.uniqueKey) as Market | undefined) + : undefined; + const toMarket = firstSupply + ? (allMarkets.find((m) => m.uniqueKey === firstSupply.data.market.uniqueKey) as Market | undefined) + : undefined; + const loanAssetDecimals = fromMarket?.loanAsset.decimals ?? toMarket?.loanAsset.decimals ?? 18; + const loanAssetSymbol = fromMarket?.loanAsset.symbol ?? toMarket?.loanAsset.symbol ?? ''; + const hasMoreWithdrawals = withdrawals.length > 1; + const hasMoreSupplies = supplies.length > 1; + return ( - Rebalance +
+ + Rebalance + +
+ {fromMarket ? ( +
+ + {hasMoreWithdrawals && +{withdrawals.length - 1}} +
+ ) : ( + From + )} + + {toMarket ? ( +
+ + {hasMoreSupplies && +{supplies.length - 1}} +
+ ) : ( + To + )} +
+
+
+ + {group.amount ? ( + + {formatReadable(Number(formatUnits(group.amount, loanAssetDecimals)))} {getTruncatedAssetName(loanAssetSymbol)} + + ) : ( + {group.transactions.length} actions + )} - {group.transactions.length} actions m.uniqueKey === firstTx.data.market.uniqueKey) as Market | undefined; + const marketCount = new Set(group.transactions.map((t) => t.data.market.uniqueKey)).size; + const hasMoreMarkets = marketCount > 1; return ( - - Deposits ({group.transactions.length}) - +
+ + Deposits + + {market && ( +
+ + {hasMoreMarkets && +{marketCount - 1} more} +
+ )} +
- - {group.amount && market - ? `+${formatReadable(Number(formatUnits(group.amount, market.loanAsset.decimals)))} ${getTruncatedAssetName(market.loanAsset.symbol)}` - : `${group.transactions.length} actions`} + + {group.amount && market ? ( + + +{formatReadable(Number(formatUnits(group.amount, market.loanAsset.decimals)))}{' '} + {getTruncatedAssetName(market.loanAsset.symbol)} + + ) : ( + {group.transactions.length} actions + )} m.uniqueKey === firstTx.data.market.uniqueKey) as Market | undefined; + const marketCount = new Set(group.transactions.map((t) => t.data.market.uniqueKey)).size; + const hasMoreMarkets = marketCount > 1; return ( - - Withdrawals ({group.transactions.length}) - +
+ + Withdrawals + + {market && ( +
+ + {hasMoreMarkets && +{marketCount - 1} more} +
+ )} +
- - {group.amount && market - ? `-${formatReadable(Number(formatUnits(group.amount, market.loanAsset.decimals)))} ${getTruncatedAssetName(market.loanAsset.symbol)}` - : `${group.transactions.length} actions`} + + {group.amount && market ? ( + + -{formatReadable(Number(formatUnits(group.amount, market.loanAsset.decimals)))}{' '} + {getTruncatedAssetName(market.loanAsset.symbol)} + + ) : ( + {group.transactions.length} actions + )}
-
+

Transaction History

diff --git a/src/features/markets/components/market-identity.tsx b/src/features/markets/components/market-identity.tsx index 81ac2744..c23b4df3 100644 --- a/src/features/markets/components/market-identity.tsx +++ b/src/features/markets/components/market-identity.tsx @@ -77,32 +77,65 @@ export function MarketIdentity({ const tokenStack = (
-
- -
- {collateralAsset ? ( -
- -
- ) : null} + {focus === MarketIdentityFocus.Loan ? ( + <> + {collateralAsset && ( +
+ +
+ )} +
+ +
+ + ) : ( + <> +
+ +
+ {collateralAsset && ( +
+ +
+ )} + + )}
);