From cacdfca77c0348feccc77ff9bdefebefcbfba42a Mon Sep 17 00:00:00 2001 From: fairlighteth <31534717+fairlighteth@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:17:22 +0000 Subject: [PATCH 1/2] fix: add new tabs and proper filtering --- .../src/modules/ordersTable/const/tabs.ts | 16 ++- .../hooks/useOrdersTableList.ts | 40 ++++++-- .../containers/OrdersTableWidget/index.tsx | 50 ++++++---- .../OrdersTableContainer/OrderRow/index.tsx | 21 +++- .../pure/OrdersTableContainer/OrdersTable.tsx | 19 ++-- .../pure/OrdersTableContainer/index.tsx | 98 +++++++++++++------ 6 files changed, 174 insertions(+), 70 deletions(-) diff --git a/apps/cowswap-frontend/src/modules/ordersTable/const/tabs.ts b/apps/cowswap-frontend/src/modules/ordersTable/const/tabs.ts index 72e324a5be..fe4fdacbf4 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/const/tabs.ts +++ b/apps/cowswap-frontend/src/modules/ordersTable/const/tabs.ts @@ -5,9 +5,21 @@ export interface OrderTab { isActive?: boolean } +export const ALL_ORDERS_TAB: OrderTab = { + id: 'all', + title: 'All orders', + count: 0, +} + +export const UNFILLABLE_TAB: OrderTab = { + id: 'unfillable', + title: 'Unfillable', + count: 0, +} + export const OPEN_TAB: OrderTab = { id: 'open', - title: 'Open orders', + title: 'Open', count: 0, } @@ -17,7 +29,7 @@ export const HISTORY_TAB: OrderTab = { count: 0, } -export const ORDERS_TABLE_TABS: OrderTab[] = [OPEN_TAB, HISTORY_TAB] +export const ORDERS_TABLE_TABS: OrderTab[] = [ALL_ORDERS_TAB, UNFILLABLE_TAB, OPEN_TAB, HISTORY_TAB] export const ORDERS_TABLE_PAGE_SIZE = 10 diff --git a/apps/cowswap-frontend/src/modules/ordersTable/containers/OrdersTableWidget/hooks/useOrdersTableList.ts b/apps/cowswap-frontend/src/modules/ordersTable/containers/OrdersTableWidget/hooks/useOrdersTableList.ts index e62855cdb8..853aab2b91 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/containers/OrdersTableWidget/hooks/useOrdersTableList.ts +++ b/apps/cowswap-frontend/src/modules/ordersTable/containers/OrdersTableWidget/hooks/useOrdersTableList.ts @@ -6,12 +6,15 @@ import { getIsComposableCowOrder } from 'utils/orderUtils/getIsComposableCowOrde import { getIsNotComposableCowOrder } from 'utils/orderUtils/getIsNotComposableCowOrder' import { TabOrderTypes } from '../../../types' +import { getOrderParams } from '../../../utils/getOrderParams' import { groupOrdersTable } from '../../../utils/groupOrdersTable' import { getParsedOrderFromTableItem, isParsedOrder, OrderTableItem } from '../../../utils/orderTableGroupUtils' export interface OrdersTableList { pending: OrderTableItem[] history: OrderTableItem[] + unfillable: OrderTableItem[] + all: OrderTableItem[] } const ordersSorter = (a: OrderTableItem, b: OrderTableItem) => { @@ -23,13 +26,18 @@ const ordersSorter = (a: OrderTableItem, b: OrderTableItem) => { const ORDERS_LIMIT = 100 -export function useOrdersTableList(allOrders: Order[], orderType: TabOrderTypes): OrdersTableList { +export function useOrdersTableList( + allOrders: Order[], + orderType: TabOrderTypes, + chainId: number, + balancesAndAllowances: any, +): OrdersTableList { const allSortedOrders = useMemo(() => { return groupOrdersTable(allOrders).sort(ordersSorter) }, [allOrders]) return useMemo(() => { - const { pending, history } = allSortedOrders.reduce( + const { pending, history, unfillable, all } = allSortedOrders.reduce( (acc, item) => { const order = isParsedOrder(item) ? item : item.parent @@ -41,7 +49,22 @@ export function useOrdersTableList(allOrders: Order[], orderType: TabOrderTypes) return acc } - if (PENDING_STATES.includes(order.status)) { + // Add to 'all' list regardless of status + acc.all.push(item) + + const isPending = PENDING_STATES.includes(order.status) + + // Check if order is unfillable (insufficient balance or allowance) + const params = getOrderParams(chainId, balancesAndAllowances, order) + const isUnfillable = params.hasEnoughBalance === false || params.hasEnoughAllowance === false + + // Only add to unfillable if the order is both pending and unfillable + if (isPending && isUnfillable) { + acc.unfillable.push(item) + } + + // Add to pending or history based on status + if (isPending) { acc.pending.push(item) } else { acc.history.push(item) @@ -49,9 +72,14 @@ export function useOrdersTableList(allOrders: Order[], orderType: TabOrderTypes) return acc }, - { pending: [], history: [] } as OrdersTableList, + { pending: [], history: [], unfillable: [], all: [] } as OrdersTableList, ) - return { pending: pending.slice(0, ORDERS_LIMIT), history: history.slice(0, ORDERS_LIMIT) } - }, [allSortedOrders, orderType]) + return { + pending: pending.slice(0, ORDERS_LIMIT), + history: history.slice(0, ORDERS_LIMIT), + unfillable: unfillable.slice(0, ORDERS_LIMIT), + all: all.slice(0, ORDERS_LIMIT), + } + }, [allSortedOrders, orderType, chainId, balancesAndAllowances]) } diff --git a/apps/cowswap-frontend/src/modules/ordersTable/containers/OrdersTableWidget/index.tsx b/apps/cowswap-frontend/src/modules/ordersTable/containers/OrdersTableWidget/index.tsx index 8b5a2854ef..ab493401ca 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/containers/OrdersTableWidget/index.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/containers/OrdersTableWidget/index.tsx @@ -1,7 +1,6 @@ import { useAtomValue, useSetAtom } from 'jotai' import { useCallback, useEffect, useMemo, useState } from 'react' - import { useTokensAllowances, useTokensBalances } from '@cowprotocol/balances-and-allowances' import { useIsSafeViaWc, useWalletDetails, useWalletInfo } from '@cowprotocol/wallet' @@ -74,7 +73,18 @@ const SearchInput = styled.input` ` function getOrdersListByIndex(ordersList: OrdersTableList, id: string): OrderTableItem[] { - return id === OPEN_TAB.id ? ordersList.pending : ordersList.history + switch (id) { + case 'all': + return ordersList.all + case 'unfillable': + return ordersList.unfillable + case 'open': + return ordersList.pending + case 'history': + return ordersList.history + default: + return ordersList.pending + } } function toggleOrderInCancellationList(state: CancellableOrder[], order: CancellableOrder): CancellableOrder[] { @@ -102,7 +112,6 @@ export function OrdersTableWidget({ const location = useLocation() const navigate = useNavigate() const cancelOrder = useCancelOrder() - const ordersList = useOrdersTableList(allOrders, orderType) const { allowsOffchainSigning } = useWalletDetails() const pendingOrdersPrices = useAtomValue(pendingOrdersPricesAtom) const ordersToCancel = useAtomValue(ordersToCancelAtom) @@ -114,6 +123,21 @@ export function OrdersTableWidget({ const injectedWidgetParams = useInjectedWidgetParams() const [searchTerm, setSearchTerm] = useState('') + const balancesState = useTokensBalances() + const allowancesState = useTokensAllowances() + + const balancesAndAllowances: BalancesAndAllowances = useMemo(() => { + const { isLoading: balancesLoading, values: balances } = balancesState + const { isLoading: allowancesLoading, values: allowances } = allowancesState + return { + isLoading: balancesLoading || allowancesLoading, + balances, + allowances, + } + }, [balancesState, allowancesState]) + + const ordersList = useOrdersTableList(allOrders, orderType, chainId, balancesAndAllowances) + const { currentTabId, currentPageNumber } = useMemo(() => { const params = parseOrdersTableUrl(location.search) @@ -133,21 +157,6 @@ export function OrdersTableWidget({ }) }, [currentTabId, ordersList]) - const isOpenOrdersTab = useMemo(() => OPEN_TAB.id === currentTabId, [currentTabId]) - - const balancesState = useTokensBalances() - const allowancesState = useTokensAllowances() - - const balancesAndAllowances: BalancesAndAllowances = useMemo(() => { - const { isLoading: balancesLoading, values: balances } = balancesState - const { isLoading: allowancesLoading, values: allowances } = allowancesState - return { - isLoading: balancesLoading || allowancesLoading, - balances, - allowances, - } - }, [balancesState, allowancesState]) - const { pendingActivity } = useCategorizeRecentActivity() const toggleOrdersForCancellation = useCallback( @@ -244,7 +253,6 @@ export function OrdersTableWidget({ orders={filteredOrders} displayOrdersOnlyForSafeApp={displayOrdersOnlyForSafeApp} isSafeViaWc={isSafeViaWc} - isOpenOrdersTab={isOpenOrdersTab} currentPageNumber={currentPageNumber} pendingOrdersPrices={pendingOrdersPrices} balancesAndAllowances={balancesAndAllowances} @@ -258,7 +266,9 @@ export function OrdersTableWidget({ ordersPermitStatus={ordersPermitStatus} injectedWidgetParams={injectedWidgetParams} > - {isOpenOrdersTab && orders.length && } + {currentTabId === OPEN_TAB.id && orders.length > 0 && ( + + )} diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx index 7eaabe638c..d359e82a4f 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx @@ -25,6 +25,7 @@ import { isOrderCancellable } from 'common/utils/isOrderCancellable' import { calculatePercentageInRelationToReference } from 'utils/orderUtils/calculatePercentageInRelationToReference' import { calculatePriceDifference, PriceDifference } from 'utils/orderUtils/calculatePriceDifference' import { getIsComposableCowParentOrder } from 'utils/orderUtils/getIsComposableCowParentOrder' +import { getIsFinalizedOrder } from 'utils/orderUtils/getIsFinalizedOrder' import { getSellAmountWithFee } from 'utils/orderUtils/getSellAmountWithFee' import { getUiOrderType } from 'utils/orderUtils/getUiOrderType' import { ParsedOrder } from 'utils/orderUtils/parseOrder' @@ -195,13 +196,25 @@ export function OrderRow({ [order, orderActions], ) - const withAllowanceWarning = hasEnoughAllowance === false && hasValidPendingPermit === false + const withAllowanceWarning = + hasEnoughAllowance === false && (hasValidPendingPermit === false || hasValidPendingPermit === undefined) const withWarning = (hasEnoughBalance === false || withAllowanceWarning) && // show the warning only for pending and scheduled orders (status === OrderStatus.PENDING || status === OrderStatus.SCHEDULED) const isOrderScheduled = order.status === OrderStatus.SCHEDULED + console.log('Order warning debug:', { + orderId: order.id, + hasEnoughBalance, + hasEnoughAllowance, + hasValidPendingPermit, + status, + withAllowanceWarning, + withWarning, + isOrderScheduled, + }) + const isScheduledCreating = isOrderScheduled && Date.now() > creationTime.getTime() const expirationTimeAgo = useTimeAgo(expirationTime, TIME_AGO_UPDATE_INTERVAL) const creationTimeAgo = useTimeAgo(creationTime, TIME_AGO_UPDATE_INTERVAL) @@ -286,7 +299,9 @@ export function OrderRow({ ) : ( <> - {prices && estimatedExecutionPrice ? ( + {getIsFinalizedOrder(order) ? ( + '-' + ) : prices && estimatedExecutionPrice ? ( {priceDiffs?.percentage && Number(priceDiffs.percentage.toFixed(4)) >= MIN_PERCENTAGE_TO_DISPLAY ? ( - {priceDiffs.percentage.toSignificant(4)}% + {priceDiffs.percentage.toFixed(2)}% ) : ( '-' diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTable.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTable.tsx index 409a999ce8..27fb18d58d 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTable.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTable.tsx @@ -20,7 +20,7 @@ import { TableGroup } from './TableGroup' import { createTableHeaders } from './tableHeaders' import { OrderActions } from './types' -import { ORDERS_TABLE_PAGE_SIZE } from '../../const/tabs' +import { ALL_ORDERS_TAB, HISTORY_TAB, OPEN_TAB, UNFILLABLE_TAB, ORDERS_TABLE_PAGE_SIZE } from '../../const/tabs' import { useGetBuildOrdersTableUrl } from '../../hooks/useGetBuildOrdersTableUrl' import { getOrderParams } from '../../utils/getOrderParams' import { @@ -96,7 +96,7 @@ const Rows = styled.div` ` export interface OrdersTableProps { - isOpenOrdersTab: boolean + currentTab: string allowsOffchainSigning: boolean currentPageNumber: number chainId: SupportedChainId @@ -110,7 +110,7 @@ export interface OrdersTableProps { } export function OrdersTable({ - isOpenOrdersTab, + currentTab, selectedOrders, allowsOffchainSigning, chainId, @@ -143,7 +143,6 @@ export function OrdersTable({ return selectedOrders.reduce( (acc, val) => { acc[val.id] = true - return acc }, {} as { [key: string]: true }, @@ -178,20 +177,20 @@ export function OrdersTable({ const visibleHeaders = useMemo(() => { return tableHeaders.filter((header) => { - if (isOpenOrdersTab) { + if (currentTab === OPEN_TAB.id || currentTab === UNFILLABLE_TAB.id || currentTab === ALL_ORDERS_TAB.id) { return header.showInOpenOrders } return header.showInClosedOrders }) - }, [tableHeaders, isOpenOrdersTab]) + }, [tableHeaders, currentTab]) return ( <> - + {visibleHeaders.map((header) => { - if (header.id === 'checkbox' && (!isRowSelectable || !isOpenOrdersTab)) { + if (header.id === 'checkbox' && (!isRowSelectable || currentTab === HISTORY_TAB.id)) { return null } @@ -245,7 +244,7 @@ export function OrdersTable({ key={order.id} isRowSelectable={isRowSelectable} isRowSelected={!!selectedOrdersMap[order.id]} - isOpenOrdersTab={isOpenOrdersTab} + isOpenOrdersTab={currentTab !== HISTORY_TAB.id} order={order} spotPrice={spotPrice} prices={pendingOrdersPrices[order.id]} @@ -266,7 +265,7 @@ export function OrdersTable({ key={item.parent.id} isRowSelectable={isRowSelectable} isRowSelected={!!selectedOrdersMap[item.parent.id]} - isOpenOrdersTab={isOpenOrdersTab} + isOpenOrdersTab={currentTab !== HISTORY_TAB.id} spotPrice={spotPrice} prices={pendingOrdersPrices[item.parent.id]} isRateInverted={false} diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/index.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/index.tsx index a9bfdad7e5..a438eda7a3 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/index.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/index.tsx @@ -1,9 +1,9 @@ -import { ReactNode } from 'react' +import { ReactNode, useMemo } from 'react' import cowMeditatingV2 from '@cowprotocol/assets/cow-swap/meditating-cow-v2.svg' import imageConnectWallet from '@cowprotocol/assets/cow-swap/wallet-plus.svg' import { isInjectedWidget } from '@cowprotocol/common-utils' -import { CowSwapSafeAppLink, ExternalLink, Media, MY_ORDERS_ID, UI } from '@cowprotocol/ui' +import { CowSwapSafeAppLink, ExternalLink, Media, UI } from '@cowprotocol/ui' import type { CowSwapWidgetAppParams } from '@cowprotocol/widget-lib' import { Trans } from '@lingui/macro' @@ -12,11 +12,20 @@ import styled, { css } from 'styled-components/macro' import { Web3Status } from 'modules/wallet/containers/Web3Status' -import { OrdersTable, OrdersTableProps } from './OrdersTable' -import { OrdersTabs, OrdersTabsProps } from './OrdersTabs' +import { OrdersTable } from './OrdersTable' +import { OrdersTabs } from './OrdersTabs' +import { OrderActions } from './types' +import { ALL_ORDERS_TAB, HISTORY_TAB, OPEN_TAB, UNFILLABLE_TAB } from '../../const/tabs' import { TabOrderTypes } from '../../types' +const Wrapper = styled.div` + display: flex; + flex-flow: column wrap; + gap: 16px; + width: 100%; +` + const Content = styled.div` display: flex; flex-flow: column wrap; @@ -156,15 +165,25 @@ const RightContainer = styled.div` flex-flow: row wrap; ` -interface OrdersProps extends OrdersTabsProps, OrdersTableProps { +interface OrdersProps { isWalletConnected: boolean - isOpenOrdersTab: boolean isSafeViaWc: boolean displayOrdersOnlyForSafeApp: boolean pendingActivities: string[] children?: ReactNode orderType: TabOrderTypes injectedWidgetParams: Partial + tabs: Array<{ id: string; title: string; count: number; isActive?: boolean }> + chainId: number + orders: any[] + selectedOrders: any[] + allowsOffchainSigning: boolean + balancesAndAllowances: any + orderActions: OrderActions + currentPageNumber: number + pendingOrdersPrices: any + getSpotPrice: any + ordersPermitStatus: any } export function OrdersTableContainer({ @@ -175,7 +194,6 @@ export function OrdersTableContainer({ isSafeViaWc, displayOrdersOnlyForSafeApp, selectedOrders, - isOpenOrdersTab, allowsOffchainSigning, balancesAndAllowances, orderActions, @@ -188,6 +206,11 @@ export function OrdersTableContainer({ ordersPermitStatus, injectedWidgetParams, }: OrdersProps) { + const currentTab = useMemo(() => { + const activeTab = tabs.find((tab) => tab.isActive) + return activeTab?.id || ALL_ORDERS_TAB.id + }, [tabs]) + const content = () => { const emptyOrdersImage = injectedWidgetParams.images?.emptyOrders @@ -227,17 +250,30 @@ export function OrdersTableContainer({ )}

- {isOpenOrdersTab ? 'No open orders' : 'No orders history'} + + {currentTab === ALL_ORDERS_TAB.id + ? 'No orders' + : currentTab === UNFILLABLE_TAB.id + ? 'No unfillable orders' + : currentTab === OPEN_TAB.id + ? 'No open orders' + : 'No orders history'} +

{displayOrdersOnlyForSafeApp && isSafeViaWc ? ( - Use the to see {isOpenOrdersTab ? 'open orders' : 'orders history'} + Use the to see {currentTab === HISTORY_TAB.id ? 'orders history' : 'your orders'} ) : ( <> - You don't have any {isOpenOrdersTab ? 'open' : ''} orders at the moment.
- Time to create a new one! {/* TODO: add link for Advanced orders also */} + + You don't have any{' '} + {currentTab === UNFILLABLE_TAB.id ? 'unfillable' : currentTab === OPEN_TAB.id ? 'open' : ''} orders at + the moment. + {' '} +
+ Time to create a new one!{' '} {orderType === TabOrderTypes.LIMIT ? ( Learn more @@ -252,32 +288,36 @@ export function OrdersTableContainer({ } return ( - + <> + + ) } return ( - <> - + + +

+ Orders +

+ {children && {children}} - {children ||
} - {content()} - + ) } From d0f80ecafd6198cc0d5123773b3694e1796ab9f4 Mon Sep 17 00:00:00 2001 From: fairlighteth <31534717+fairlighteth@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:18:27 +0000 Subject: [PATCH 2/2] fix: remove debug --- .../pure/OrdersTableContainer/OrderRow/index.tsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx index d359e82a4f..5303ca82be 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx @@ -204,17 +204,6 @@ export function OrderRow({ (status === OrderStatus.PENDING || status === OrderStatus.SCHEDULED) const isOrderScheduled = order.status === OrderStatus.SCHEDULED - console.log('Order warning debug:', { - orderId: order.id, - hasEnoughBalance, - hasEnoughAllowance, - hasValidPendingPermit, - status, - withAllowanceWarning, - withWarning, - isOrderScheduled, - }) - const isScheduledCreating = isOrderScheduled && Date.now() > creationTime.getTime() const expirationTimeAgo = useTimeAgo(expirationTime, TIME_AGO_UPDATE_INTERVAL) const creationTimeAgo = useTimeAgo(creationTime, TIME_AGO_UPDATE_INTERVAL)