feat(trading): split orders into tabs
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
|
||||
describe('market oracle', { tags: '@smoke' }, () => {
|
||||
it('current fees displayed', () => {
|
||||
cy.mockTradingPage(
|
||||
MarketState.STATE_ACTIVE,
|
||||
undefined,
|
||||
undefined,
|
||||
'COMPROMISED'
|
||||
);
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@MarketInfo');
|
||||
cy.getByTestId('oracle-status').should('contain.text', 'COMPROMISED');
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
|
||||
import { MarketInfoAccordionContainer } from '@vegaprotocol/market-info';
|
||||
import { OrderbookContainer } from '@vegaprotocol/market-depth';
|
||||
import { OrderListContainer } from '@vegaprotocol/orders';
|
||||
import { OrderListContainer, Filter } from '@vegaprotocol/orders';
|
||||
import { FillsContainer } from '@vegaprotocol/fills';
|
||||
import { PositionsContainer } from '@vegaprotocol/positions';
|
||||
import { TradesContainer } from '@vegaprotocol/trades';
|
||||
@@ -153,7 +153,40 @@ const MarketBottomPanel = memo(
|
||||
<TradingViews.Positions onMarketClick={onMarketClick} />
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="orders" name={t('Orders')}>
|
||||
<Tab id="open-orders" name={t('Open')}>
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.Orders
|
||||
marketId={marketId}
|
||||
filter={Filter.Open}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="closed-orders" name={t('Closed')}>
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.Orders
|
||||
marketId={marketId}
|
||||
filter={Filter.Closed}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="rejected-orders" name={t('Rejected')}>
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.Orders
|
||||
marketId={marketId}
|
||||
filter={Filter.Rejected}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="orders" name={t('All')}>
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.Orders
|
||||
marketId={marketId}
|
||||
|
||||
@@ -28,6 +28,11 @@ export type Order = Omit<OrderFieldsFragment, 'market'> & {
|
||||
};
|
||||
export type OrderEdge = Edge<Order>;
|
||||
|
||||
const liveOnlyOrderStatuses = [
|
||||
OrderStatus.STATUS_ACTIVE,
|
||||
OrderStatus.STATUS_PARKED,
|
||||
];
|
||||
|
||||
const orderMatchFilters = (
|
||||
order: OrderUpdateFieldsFragment,
|
||||
variables: OrdersQueryVariables
|
||||
@@ -41,6 +46,12 @@ const orderMatchFilters = (
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
variables?.filter?.liveOnly &&
|
||||
!(order.status && liveOnlyOrderStatuses.includes(order.status))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
variables?.filter?.types &&
|
||||
!(order.type && variables.filter.types.includes(order.type))
|
||||
@@ -204,7 +215,7 @@ export const activeOrdersProvider = makeDerivedDataProvider<
|
||||
ordersProvider(callback, client, {
|
||||
partyId: variables.partyId,
|
||||
filter: {
|
||||
status: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
|
||||
liveOnly: true,
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -2,17 +2,20 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { OrderListManager } from './order-list-manager';
|
||||
import type { Filter } from './order-list-manager/use-order-list-data';
|
||||
|
||||
export const OrderListContainer = ({
|
||||
marketId,
|
||||
onMarketClick,
|
||||
onOrderTypeClick,
|
||||
enforceBottomPlaceholder,
|
||||
filter,
|
||||
}: {
|
||||
marketId?: string;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
enforceBottomPlaceholder?: boolean;
|
||||
filter?: Filter;
|
||||
}) => {
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
|
||||
@@ -24,6 +27,7 @@ export const OrderListContainer = ({
|
||||
<OrderListManager
|
||||
partyId={pubKey}
|
||||
marketId={marketId}
|
||||
filter={filter}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
isReadOnly={isReadOnly}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import type { FilterChangedEvent, SortChangedEvent } from 'ag-grid-community';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { GridReadyEvent } from 'ag-grid-community';
|
||||
@@ -9,14 +8,13 @@ import type { GridReadyEvent } from 'ag-grid-community';
|
||||
import { OrderListTable } from '../order-list/order-list';
|
||||
import { useOrderListData } from './use-order-list-data';
|
||||
import { useHasAmendableOrder } from '../../order-hooks/use-has-amendable-order';
|
||||
import type { Filter, Sort } from './use-order-list-data';
|
||||
import type { Filter } from './use-order-list-data';
|
||||
import { FilterStatus } from './use-order-list-data';
|
||||
import { useBottomPlaceholder } from '@vegaprotocol/react-helpers';
|
||||
import { OrderStatus } from '@vegaprotocol/types';
|
||||
import {
|
||||
normalizeOrderAmendment,
|
||||
useVegaTransactionStore,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import type { OrderTxUpdateFieldsFragment } from '@vegaprotocol/wallet';
|
||||
import { OrderEditDialog } from '../order-list/order-edit-dialog';
|
||||
import type { Order, OrderEdge } from '../order-data-provider';
|
||||
@@ -28,6 +26,7 @@ export interface OrderListManagerProps {
|
||||
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
isReadOnly: boolean;
|
||||
enforceBottomPlaceholder?: boolean;
|
||||
filter?: Filter;
|
||||
}
|
||||
|
||||
const CancelAllOrdersButton = ({ onClick }: { onClick: () => void }) => (
|
||||
@@ -43,12 +42,6 @@ const CancelAllOrdersButton = ({ onClick }: { onClick: () => void }) => (
|
||||
</div>
|
||||
);
|
||||
|
||||
const initialFilter: Filter = {
|
||||
status: {
|
||||
value: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
|
||||
},
|
||||
};
|
||||
|
||||
export const OrderListManager = ({
|
||||
partyId,
|
||||
marketId,
|
||||
@@ -56,20 +49,16 @@ export const OrderListManager = ({
|
||||
onOrderTypeClick,
|
||||
isReadOnly,
|
||||
enforceBottomPlaceholder,
|
||||
filter,
|
||||
}: OrderListManagerProps) => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const [dataCount, setDataCount] = useState(0);
|
||||
const scrolledToTop = useRef(false);
|
||||
const [sort, setSort] = useState<Sort[] | undefined>();
|
||||
const [filter, setFilter] = useState<Filter | undefined>(initialFilter);
|
||||
const filterRef = useRef(initialFilter);
|
||||
const [editOrder, setEditOrder] = useState<Order | null>(null);
|
||||
const create = useVegaTransactionStore((state) => state.create);
|
||||
const hasAmendableOrder = useHasAmendableOrder(marketId);
|
||||
|
||||
const { data, error, loading, reload } = useOrderListData({
|
||||
partyId,
|
||||
sort,
|
||||
filter,
|
||||
gridRef,
|
||||
scrolledToTop,
|
||||
@@ -84,42 +73,6 @@ export const OrderListManager = ({
|
||||
disabled: !enforceBottomPlaceholder && !isReadOnly && !hasAmendableOrder,
|
||||
});
|
||||
|
||||
const onFilterChanged = useCallback(
|
||||
(event: FilterChangedEvent) => {
|
||||
const updatedFilter = event.api.getFilterModel();
|
||||
if (isEqual(updatedFilter, filterRef.current)) {
|
||||
return;
|
||||
}
|
||||
filterRef.current = updatedFilter;
|
||||
if (Object.keys(updatedFilter).length) {
|
||||
setFilter(updatedFilter);
|
||||
} else {
|
||||
setFilter(undefined);
|
||||
}
|
||||
setDataCount(gridRef.current?.api?.getModel().getRowCount() ?? 0);
|
||||
bottomPlaceholderOnFilterChanged?.();
|
||||
},
|
||||
[setFilter, bottomPlaceholderOnFilterChanged]
|
||||
);
|
||||
|
||||
const onSortChange = useCallback(
|
||||
(event: SortChangedEvent) => {
|
||||
const sort = event.columnApi
|
||||
.getColumnState()
|
||||
.sort((a, b) => (a.sortIndex || 0) - (b.sortIndex || 0))
|
||||
.reduce((acc, col) => {
|
||||
if (col.sort) {
|
||||
const { colId, sort } = col;
|
||||
acc.push({ colId, sort });
|
||||
}
|
||||
return acc;
|
||||
}, [] as { colId: string; sort: string }[]);
|
||||
setSort(sort.length > 0 ? sort : undefined);
|
||||
bottomPlaceholderOnSortChanged?.();
|
||||
},
|
||||
[setSort, bottomPlaceholderOnSortChanged]
|
||||
);
|
||||
|
||||
const cancel = useCallback(
|
||||
(order: Order) => {
|
||||
if (!order.market) return;
|
||||
@@ -133,9 +86,18 @@ export const OrderListManager = ({
|
||||
[create]
|
||||
);
|
||||
|
||||
const onGridReady = useCallback(({ api }: GridReadyEvent) => {
|
||||
api.setFilterModel(initialFilter);
|
||||
}, []);
|
||||
const onGridReady = useCallback(
|
||||
({ api }: GridReadyEvent) => {
|
||||
if (filter) {
|
||||
api.setFilterModel({
|
||||
status: {
|
||||
value: FilterStatus[filter],
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
[filter]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setDataCount(gridRef.current?.api?.getModel().getRowCount() ?? 0);
|
||||
@@ -162,8 +124,6 @@ export const OrderListManager = ({
|
||||
rowData={extractedData}
|
||||
ref={gridRef}
|
||||
onGridReady={onGridReady}
|
||||
onFilterChanged={onFilterChanged}
|
||||
onSortChanged={onSortChange}
|
||||
cancel={cancel}
|
||||
setEditOrder={setEditOrder}
|
||||
onMarketClick={onMarketClick}
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { RefObject } from 'react';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { makeInfiniteScrollGetRows } from '@vegaprotocol/utils';
|
||||
import { useDataProvider, updateGridData } from '@vegaprotocol/react-helpers';
|
||||
import { OrderStatus } from '@vegaprotocol/types';
|
||||
import { ordersWithMarketProvider } from '../order-data-provider/order-data-provider';
|
||||
import type {
|
||||
OrderEdge,
|
||||
@@ -12,38 +13,33 @@ import type {
|
||||
OrdersQueryVariables,
|
||||
OrdersUpdateSubscriptionVariables,
|
||||
} from '../order-data-provider/__generated__/Orders';
|
||||
import type * as Types from '@vegaprotocol/types';
|
||||
export interface Sort {
|
||||
colId: string;
|
||||
sort: string;
|
||||
}
|
||||
export interface Filter {
|
||||
updatedAt?: {
|
||||
value: Types.DateRange;
|
||||
};
|
||||
type?: {
|
||||
value: Types.OrderType[];
|
||||
};
|
||||
status?: {
|
||||
value: Types.OrderStatus[];
|
||||
};
|
||||
timeInForce?: {
|
||||
value: Types.OrderTimeInForce[];
|
||||
};
|
||||
|
||||
export enum Filter {
|
||||
'Open' = 1,
|
||||
'Closed',
|
||||
'Rejected',
|
||||
}
|
||||
|
||||
const FilterStatus = {
|
||||
[Filter.Open]: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
|
||||
[Filter.Closed]: [
|
||||
OrderStatus.STATUS_CANCELLED,
|
||||
OrderStatus.STATUS_EXPIRED,
|
||||
OrderStatus.STATUS_FILLED,
|
||||
OrderStatus.STATUS_PARTIALLY_FILLED,
|
||||
OrderStatus.STATUS_STOPPED,
|
||||
],
|
||||
[Filter.Rejected]: [OrderStatus.STATUS_REJECTED],
|
||||
};
|
||||
interface Props {
|
||||
partyId: string;
|
||||
marketId?: string;
|
||||
filter?: Filter;
|
||||
sort?: Sort[];
|
||||
gridRef: RefObject<AgGridReact>;
|
||||
scrolledToTop: RefObject<boolean>;
|
||||
}
|
||||
|
||||
export const useOrderListData = ({
|
||||
partyId,
|
||||
marketId,
|
||||
sort,
|
||||
filter,
|
||||
gridRef,
|
||||
scrolledToTop,
|
||||
@@ -72,25 +68,10 @@ export const useOrderListData = ({
|
||||
const allVars: OrdersQueryVariables & OrdersUpdateSubscriptionVariables = {
|
||||
partyId,
|
||||
};
|
||||
if (
|
||||
filter?.updatedAt?.value ||
|
||||
filter?.status?.value.length ||
|
||||
filter?.timeInForce?.value.length ||
|
||||
filter?.type?.value.length
|
||||
) {
|
||||
allVars.filter = {};
|
||||
if (filter?.updatedAt?.value) {
|
||||
allVars.filter.dateRange = filter?.updatedAt?.value;
|
||||
}
|
||||
if (filter?.status?.value.length) {
|
||||
allVars.filter.status = filter?.status?.value;
|
||||
}
|
||||
if (filter?.timeInForce?.value.length) {
|
||||
allVars.filter.timeInForce = filter?.timeInForce?.value;
|
||||
}
|
||||
if (filter?.type?.value.length) {
|
||||
allVars.filter.types = filter?.type?.value;
|
||||
}
|
||||
if (filter === Filter.Open) {
|
||||
allVars.filter = {
|
||||
liveOnly: true,
|
||||
};
|
||||
}
|
||||
return allVars;
|
||||
}, [partyId, filter]);
|
||||
|
||||
Generated
+1
@@ -2279,6 +2279,7 @@ export type OrderFilter = {
|
||||
/** Date range to retrieve orders from/to. Start and end time should be expressed as an integer value of nano-seconds past the Unix epoch */
|
||||
dateRange?: InputMaybe<DateRange>;
|
||||
excludeLiquidity?: InputMaybe<Scalars['Boolean']>;
|
||||
liveOnly?: InputMaybe<Scalars['Boolean']>;
|
||||
status?: InputMaybe<Array<OrderStatus>>;
|
||||
timeInForce?: InputMaybe<Array<OrderTimeInForce>>;
|
||||
types?: InputMaybe<Array<OrderType>>;
|
||||
|
||||
Reference in New Issue
Block a user