feat(trading): show full history of orders, fills and trades (#5105)
This commit is contained in:
parent
fbafc726a4
commit
c440abc77d
@ -3,7 +3,12 @@ import throttle from 'lodash/throttle';
|
|||||||
import isEqualWith from 'lodash/isEqualWith';
|
import isEqualWith from 'lodash/isEqualWith';
|
||||||
import { useApolloClient } from '@apollo/client';
|
import { useApolloClient } from '@apollo/client';
|
||||||
import type { OperationVariables } from '@apollo/client';
|
import type { OperationVariables } from '@apollo/client';
|
||||||
import type { Subscribe, Load, UpdateCallback } from './generic-data-provider';
|
import type {
|
||||||
|
Subscribe,
|
||||||
|
Load,
|
||||||
|
UpdateCallback,
|
||||||
|
PageInfo,
|
||||||
|
} from './generic-data-provider';
|
||||||
import { variablesIsEqualCustomizer } from './generic-data-provider';
|
import { variablesIsEqualCustomizer } from './generic-data-provider';
|
||||||
|
|
||||||
export interface useDataProviderParams<
|
export interface useDataProviderParams<
|
||||||
@ -12,13 +17,23 @@ export interface useDataProviderParams<
|
|||||||
Variables extends OperationVariables | undefined = undefined
|
Variables extends OperationVariables | undefined = undefined
|
||||||
> {
|
> {
|
||||||
dataProvider: Subscribe<Data, Delta, Variables>;
|
dataProvider: Subscribe<Data, Delta, Variables>;
|
||||||
update?: ({ delta, data }: { delta?: Delta; data: Data | null }) => boolean;
|
update?: ({
|
||||||
|
delta,
|
||||||
|
data,
|
||||||
|
pageInfo,
|
||||||
|
}: {
|
||||||
|
delta?: Delta;
|
||||||
|
data: Data | null;
|
||||||
|
pageInfo: PageInfo | null;
|
||||||
|
}) => boolean;
|
||||||
insert?: ({
|
insert?: ({
|
||||||
insertionData,
|
insertionData,
|
||||||
data,
|
data,
|
||||||
|
pageInfo,
|
||||||
}: {
|
}: {
|
||||||
insertionData?: Data | null;
|
insertionData?: Data | null;
|
||||||
data: Data | null;
|
data: Data | null;
|
||||||
|
pageInfo: PageInfo | null;
|
||||||
}) => boolean;
|
}) => boolean;
|
||||||
variables: Variables;
|
variables: Variables;
|
||||||
skipUpdates?: boolean;
|
skipUpdates?: boolean;
|
||||||
@ -30,7 +45,7 @@ export interface useDataProviderParams<
|
|||||||
* @param dataProvider subscribe function created by makeDataProvider
|
* @param dataProvider subscribe function created by makeDataProvider
|
||||||
* @param update optional function called on each delta received in subscription, if returns true updated data will be not passed from hook (component handles updates internally)
|
* @param update optional function called on each delta received in subscription, if returns true updated data will be not passed from hook (component handles updates internally)
|
||||||
* @param variables optional
|
* @param variables optional
|
||||||
* @returns state: data, loading, error, methods: flush (pass updated data to update function without delta), restart: () => void}};
|
* @returns state: data, loading, pageInfo, error, methods: flush (pass updated data to update function without delta), restart: () => void}};
|
||||||
*/
|
*/
|
||||||
export const useDataProvider = <
|
export const useDataProvider = <
|
||||||
Data,
|
Data,
|
||||||
@ -48,6 +63,7 @@ export const useDataProvider = <
|
|||||||
const [data, setData] = useState<Data | null>(null);
|
const [data, setData] = useState<Data | null>(null);
|
||||||
const [loading, setLoading] = useState<boolean>(!skip);
|
const [loading, setLoading] = useState<boolean>(!skip);
|
||||||
const [error, setError] = useState<Error | undefined>(undefined);
|
const [error, setError] = useState<Error | undefined>(undefined);
|
||||||
|
const [pageInfo, setPageInfo] = useState<PageInfo | null>(null);
|
||||||
const flushRef = useRef<(() => void) | undefined>(undefined);
|
const flushRef = useRef<(() => void) | undefined>(undefined);
|
||||||
const reloadRef = useRef<((force?: boolean) => void) | undefined>(undefined);
|
const reloadRef = useRef<((force?: boolean) => void) | undefined>(undefined);
|
||||||
const loadRef = useRef<Load<Data> | undefined>(undefined);
|
const loadRef = useRef<Load<Data> | undefined>(undefined);
|
||||||
@ -93,6 +109,7 @@ export const useDataProvider = <
|
|||||||
isInsert,
|
isInsert,
|
||||||
isUpdate,
|
isUpdate,
|
||||||
loaded,
|
loaded,
|
||||||
|
pageInfo,
|
||||||
} = args;
|
} = args;
|
||||||
setError(error);
|
setError(error);
|
||||||
setLoading(!loaded && loading);
|
setLoading(!loaded && loading);
|
||||||
@ -104,21 +121,22 @@ export const useDataProvider = <
|
|||||||
(skipUpdatesRef.current ||
|
(skipUpdatesRef.current ||
|
||||||
(!skipUpdatesRef.current &&
|
(!skipUpdatesRef.current &&
|
||||||
updateRef.current &&
|
updateRef.current &&
|
||||||
updateRef.current({ delta, data })))
|
updateRef.current({ delta, data, pageInfo })))
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
isInsert &&
|
isInsert &&
|
||||||
insertRef.current &&
|
insertRef.current &&
|
||||||
insertRef.current({ insertionData, data })
|
insertRef.current({ insertionData, data, pageInfo })
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setData(data);
|
setData(data);
|
||||||
|
setPageInfo(pageInfo);
|
||||||
if (!loading && !isUpdate && updateRef.current) {
|
if (!loading && !isUpdate && updateRef.current) {
|
||||||
updateRef.current({ data });
|
updateRef.current({ data, pageInfo });
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -136,15 +154,13 @@ export const useDataProvider = <
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setData(null);
|
setData(null);
|
||||||
|
setPageInfo(null);
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
if (updateRef.current) {
|
if (updateRef.current) {
|
||||||
updateRef.current({ data: null });
|
updateRef.current({ data: null, pageInfo: null });
|
||||||
}
|
}
|
||||||
if (skip) {
|
if (skip) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
if (updateRef.current) {
|
|
||||||
updateRef.current({ data: null });
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@ -165,6 +181,7 @@ export const useDataProvider = <
|
|||||||
}, [client, dataProvider, callback, variables, skip]);
|
}, [client, dataProvider, callback, variables, skip]);
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
|
pageInfo,
|
||||||
loading,
|
loading,
|
||||||
error,
|
error,
|
||||||
flush,
|
flush,
|
||||||
|
@ -109,7 +109,7 @@ describe('DealTicket', () => {
|
|||||||
variables: {
|
variables: {
|
||||||
partyId: 'pubKey',
|
partyId: 'pubKey',
|
||||||
filter: { liveOnly: true },
|
filter: { liveOnly: true },
|
||||||
pagination: { first: 5000 },
|
pagination: { first: 1000 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
result: {
|
result: {
|
||||||
|
@ -42,8 +42,12 @@ fragment FillEdge on TradeEdge {
|
|||||||
cursor
|
cursor
|
||||||
}
|
}
|
||||||
|
|
||||||
query Fills($filter: TradesFilter, $pagination: Pagination) {
|
query Fills(
|
||||||
trades(filter: $filter, pagination: $pagination) {
|
$filter: TradesFilter
|
||||||
|
$pagination: Pagination
|
||||||
|
$dateRange: DateRange
|
||||||
|
) {
|
||||||
|
trades(filter: $filter, dateRange: $dateRange, pagination: $pagination) {
|
||||||
edges {
|
edges {
|
||||||
...FillEdge
|
...FillEdge
|
||||||
}
|
}
|
||||||
|
6
libs/fills/src/lib/__generated__/Fills.ts
generated
6
libs/fills/src/lib/__generated__/Fills.ts
generated
@ -12,6 +12,7 @@ export type FillEdgeFragment = { __typename?: 'TradeEdge', cursor: string, node:
|
|||||||
export type FillsQueryVariables = Types.Exact<{
|
export type FillsQueryVariables = Types.Exact<{
|
||||||
filter?: Types.InputMaybe<Types.TradesFilter>;
|
filter?: Types.InputMaybe<Types.TradesFilter>;
|
||||||
pagination?: Types.InputMaybe<Types.Pagination>;
|
pagination?: Types.InputMaybe<Types.Pagination>;
|
||||||
|
dateRange?: Types.InputMaybe<Types.DateRange>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
@ -95,8 +96,8 @@ export const FillUpdateFieldsFragmentDoc = gql`
|
|||||||
}
|
}
|
||||||
${TradeFeeFieldsFragmentDoc}`;
|
${TradeFeeFieldsFragmentDoc}`;
|
||||||
export const FillsDocument = gql`
|
export const FillsDocument = gql`
|
||||||
query Fills($filter: TradesFilter, $pagination: Pagination) {
|
query Fills($filter: TradesFilter, $pagination: Pagination, $dateRange: DateRange) {
|
||||||
trades(filter: $filter, pagination: $pagination) {
|
trades(filter: $filter, dateRange: $dateRange, pagination: $pagination) {
|
||||||
edges {
|
edges {
|
||||||
...FillEdge
|
...FillEdge
|
||||||
}
|
}
|
||||||
@ -124,6 +125,7 @@ export const FillsDocument = gql`
|
|||||||
* variables: {
|
* variables: {
|
||||||
* filter: // value for 'filter'
|
* filter: // value for 'filter'
|
||||||
* pagination: // value for 'pagination'
|
* pagination: // value for 'pagination'
|
||||||
|
* dateRange: // value for 'dateRange'
|
||||||
* },
|
* },
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import type { AgGridReact } from 'ag-grid-react';
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
import { useRef } from 'react';
|
import { useCallback, useRef, useState } from 'react';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { FillsTable } from './fills-table';
|
import { FillsTable } from './fills-table';
|
||||||
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||||
import type * as Schema from '@vegaprotocol/types';
|
import type * as Schema from '@vegaprotocol/types';
|
||||||
import { fillsWithMarketProvider } from './fills-data-provider';
|
import { fillsWithMarketProvider } from './fills-data-provider';
|
||||||
|
import { TradingButton as Button } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
interface FillsManagerProps {
|
interface FillsManagerProps {
|
||||||
partyId: string;
|
partyId: string;
|
||||||
@ -22,26 +23,60 @@ export const FillsManager = ({
|
|||||||
const filter: Schema.TradesFilter | Schema.TradesSubscriptionFilter = {
|
const filter: Schema.TradesFilter | Schema.TradesSubscriptionFilter = {
|
||||||
partyIds: [partyId],
|
partyIds: [partyId],
|
||||||
};
|
};
|
||||||
const { data, error } = useDataProvider({
|
const [hasDisplayedRow, setHasDisplayedRow] = useState<boolean | undefined>(
|
||||||
dataProvider: fillsWithMarketProvider,
|
undefined
|
||||||
update: ({ data }) => {
|
);
|
||||||
if (data?.length && gridRef.current?.api) {
|
const { onFilterChanged, ...props } = gridProps || {};
|
||||||
gridRef.current?.api.setRowData(data);
|
const onRowDataUpdated = useCallback(
|
||||||
return true;
|
({ api }: { api: AgGridReact['api'] }) => {
|
||||||
}
|
setHasDisplayedRow(!!api.getDisplayedRowCount());
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const { data, error, load, pageInfo } = useDataProvider({
|
||||||
|
dataProvider: fillsWithMarketProvider,
|
||||||
variables: { filter },
|
variables: { filter },
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FillsTable
|
<div className="flex flex-col h-full">
|
||||||
ref={gridRef}
|
<FillsTable
|
||||||
rowData={data}
|
ref={gridRef}
|
||||||
partyId={partyId}
|
rowData={data}
|
||||||
onMarketClick={onMarketClick}
|
onFilterChanged={(event) => {
|
||||||
overlayNoRowsTemplate={error ? error.message : t('No fills')}
|
onRowDataUpdated(event);
|
||||||
{...gridProps}
|
onFilterChanged(event);
|
||||||
/>
|
}}
|
||||||
|
onRowDataUpdated={onRowDataUpdated}
|
||||||
|
partyId={partyId}
|
||||||
|
onMarketClick={onMarketClick}
|
||||||
|
overlayNoRowsTemplate={error ? error.message : t('No fills')}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex justify-between border-t border-default p-1 items-center">
|
||||||
|
<div className="text-xs">
|
||||||
|
{t(
|
||||||
|
'Depending on data node retention you may not be able see the "full" history'
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex text-xs items-center">
|
||||||
|
{data?.length && !pageInfo?.hasNextPage
|
||||||
|
? t('all %s items loaded', [data.length.toString()])
|
||||||
|
: t('%s items loaded', [
|
||||||
|
data?.length ? data.length.toString() : ' ',
|
||||||
|
])}
|
||||||
|
{pageInfo?.hasNextPage ? (
|
||||||
|
<Button size="extra-small" className="ml-1" onClick={() => load()}>
|
||||||
|
{t('Load more')}
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
{data?.length && hasDisplayedRow === false ? (
|
||||||
|
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-xs">
|
||||||
|
{t('No fills matching selected filters')}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
negativeClassNames,
|
negativeClassNames,
|
||||||
MarketNameCell,
|
MarketNameCell,
|
||||||
COL_DEFS,
|
COL_DEFS,
|
||||||
|
DateRangeFilter,
|
||||||
} from '@vegaprotocol/datagrid';
|
} from '@vegaprotocol/datagrid';
|
||||||
import type {
|
import type {
|
||||||
VegaValueFormatterParams,
|
VegaValueFormatterParams,
|
||||||
@ -120,6 +121,7 @@ export const FillsTable = forwardRef<AgGridReact, Props>(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
headerName: t('Date'),
|
headerName: t('Date'),
|
||||||
|
filter: DateRangeFilter,
|
||||||
field: 'createdAt',
|
field: 'createdAt',
|
||||||
valueFormatter: ({
|
valueFormatter: ({
|
||||||
value,
|
value,
|
||||||
|
@ -166,7 +166,7 @@ export const ordersProvider = makeDataProvider<
|
|||||||
pagination: {
|
pagination: {
|
||||||
getPageInfo,
|
getPageInfo,
|
||||||
append,
|
append,
|
||||||
first: 5000,
|
first: 1000,
|
||||||
},
|
},
|
||||||
resetDelay: 1000,
|
resetDelay: 1000,
|
||||||
additionalContext: { isEnlargedTimeout: true },
|
additionalContext: { isEnlargedTimeout: true },
|
||||||
|
@ -27,6 +27,7 @@ describe('OrderListManager', () => {
|
|||||||
flush: jest.fn(),
|
flush: jest.fn(),
|
||||||
reload: jest.fn(),
|
reload: jest.fn(),
|
||||||
load: jest.fn(),
|
load: jest.fn(),
|
||||||
|
pageInfo: null,
|
||||||
});
|
});
|
||||||
render(generateJsx());
|
render(generateJsx());
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ describe('OrderListManager', () => {
|
|||||||
flush: jest.fn(),
|
flush: jest.fn(),
|
||||||
reload: jest.fn(),
|
reload: jest.fn(),
|
||||||
load: jest.fn(),
|
load: jest.fn(),
|
||||||
|
pageInfo: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
render(generateJsx());
|
render(generateJsx());
|
||||||
@ -62,6 +64,7 @@ describe('OrderListManager', () => {
|
|||||||
flush: jest.fn(),
|
flush: jest.fn(),
|
||||||
reload: jest.fn(),
|
reload: jest.fn(),
|
||||||
load: jest.fn(),
|
load: jest.fn(),
|
||||||
|
pageInfo: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
render(
|
render(
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { useCallback, useRef, useState, useEffect } from 'react';
|
import { useCallback, useRef, useState, useEffect } from 'react';
|
||||||
import type { AgGridReact } from 'ag-grid-react';
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
import type { FilterChangedEvent } from 'ag-grid-community';
|
|
||||||
import { OrderListTable } from '../order-list';
|
import { OrderListTable } from '../order-list';
|
||||||
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||||
@ -12,7 +11,7 @@ import type { OrderTxUpdateFieldsFragment } from '@vegaprotocol/web3';
|
|||||||
import { OrderEditDialog } from '../order-list/order-edit-dialog';
|
import { OrderEditDialog } from '../order-list/order-edit-dialog';
|
||||||
import type { Order } from '../order-data-provider';
|
import type { Order } from '../order-data-provider';
|
||||||
import { OrderViewDialog } from '../order-list/order-view-dialog';
|
import { OrderViewDialog } from '../order-list/order-view-dialog';
|
||||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
import { Splash, TradingButton as Button } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
export enum Filter {
|
export enum Filter {
|
||||||
'Open' = 'Open',
|
'Open' = 'Open',
|
||||||
@ -48,33 +47,11 @@ export const OrderListManager = ({
|
|||||||
? { partyId, filter: { liveOnly: true } }
|
? { partyId, filter: { liveOnly: true } }
|
||||||
: { partyId };
|
: { partyId };
|
||||||
|
|
||||||
const { data, error } = useDataProvider({
|
const { data, error, pageInfo, load } = useDataProvider({
|
||||||
dataProvider: ordersWithMarketProvider,
|
dataProvider: ordersWithMarketProvider,
|
||||||
variables,
|
variables,
|
||||||
update: ({ data }) => {
|
|
||||||
if (data && gridRef.current?.api) {
|
|
||||||
gridRef.current.api.setRowData(data);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const onFilterChanged = useCallback(
|
|
||||||
(event: FilterChangedEvent) => {
|
|
||||||
gridProps?.onFilterChanged?.(event);
|
|
||||||
if (event.api) {
|
|
||||||
const isEmpty = event.api.getDisplayedRowCount() === 0;
|
|
||||||
if (isEmpty) {
|
|
||||||
event.api.showNoRowsOverlay();
|
|
||||||
} else {
|
|
||||||
event.api.hideOverlay();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[gridProps]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!data || !data.length) {
|
if (!data || !data.length) {
|
||||||
gridRef.current?.api?.showNoRowsOverlay();
|
gridRef.current?.api?.showNoRowsOverlay();
|
||||||
@ -96,6 +73,17 @@ export const OrderListManager = ({
|
|||||||
[create]
|
[create]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [hasDisplayedRow, setHasDisplayedRow] = useState<boolean | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
const { onFilterChanged, ...props } = gridProps || {};
|
||||||
|
const onRowDataUpdated = useCallback(
|
||||||
|
({ api }: { api: AgGridReact['api'] }) => {
|
||||||
|
setHasDisplayedRow(!!api.getDisplayedRowCount());
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return <Splash>{t(`Something went wrong: ${error.message}`)}</Splash>;
|
return <Splash>{t(`Something went wrong: ${error.message}`)}</Splash>;
|
||||||
}
|
}
|
||||||
@ -103,20 +91,60 @@ export const OrderListManager = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="h-full relative">
|
<div className="h-full relative">
|
||||||
<OrderListTable
|
<div className="flex flex-col h-full">
|
||||||
rowData={data}
|
<OrderListTable
|
||||||
ref={gridRef}
|
rowData={data}
|
||||||
filter={filter}
|
ref={gridRef}
|
||||||
onCancel={cancel}
|
filter={filter}
|
||||||
onEdit={setEditOrder}
|
onCancel={cancel}
|
||||||
onView={setViewOrder}
|
onEdit={setEditOrder}
|
||||||
onMarketClick={onMarketClick}
|
onView={setViewOrder}
|
||||||
onOrderTypeClick={onOrderTypeClick}
|
onMarketClick={onMarketClick}
|
||||||
isReadOnly={isReadOnly}
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
overlayNoRowsTemplate={noRowsMessage || t('No orders')}
|
onFilterChanged={(event) => {
|
||||||
{...gridProps}
|
onRowDataUpdated(event);
|
||||||
onFilterChanged={onFilterChanged}
|
if (onFilterChanged) {
|
||||||
/>
|
onFilterChanged(event);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onRowDataUpdated={onRowDataUpdated}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
|
overlayNoRowsTemplate={noRowsMessage || t('No orders')}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
<div className="flex justify-between border-t border-default p-1 items-center">
|
||||||
|
<div className="text-xs">
|
||||||
|
{variables.filter?.liveOnly
|
||||||
|
? null
|
||||||
|
: t(
|
||||||
|
'Depending on data node retention you may not be able see the "full" history'
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{data ? (
|
||||||
|
<div className="flex text-xs items-center">
|
||||||
|
{data?.length && !pageInfo?.hasNextPage
|
||||||
|
? t('all %s items loaded', [data.length.toString()])
|
||||||
|
: t('%s items loaded', [
|
||||||
|
data?.length ? data.length.toString() : ' ',
|
||||||
|
])}
|
||||||
|
{pageInfo?.hasNextPage ? (
|
||||||
|
<Button
|
||||||
|
size="extra-small"
|
||||||
|
className="ml-1"
|
||||||
|
onClick={() => load()}
|
||||||
|
>
|
||||||
|
{t('Load more')}
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{data?.length && hasDisplayedRow === false ? (
|
||||||
|
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-xs">
|
||||||
|
{t('No orders matching selected filters')}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{editOrder && (
|
{editOrder && (
|
||||||
<OrderEditDialog
|
<OrderEditDialog
|
||||||
|
@ -30,6 +30,7 @@ describe('StopOrdersManager', () => {
|
|||||||
flush: jest.fn(),
|
flush: jest.fn(),
|
||||||
reload: jest.fn(),
|
reload: jest.fn(),
|
||||||
load: jest.fn(),
|
load: jest.fn(),
|
||||||
|
pageInfo: null,
|
||||||
});
|
});
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(generateJsx());
|
render(generateJsx());
|
||||||
|
@ -4,10 +4,13 @@ import { TradesTable } from './trades-table';
|
|||||||
import { useDealTicketFormValues } from '@vegaprotocol/deal-ticket';
|
import { useDealTicketFormValues } from '@vegaprotocol/deal-ticket';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
|
import { TradingButton as Button } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
interface TradesContainerProps {
|
interface TradesContainerProps {
|
||||||
marketId: string;
|
marketId: string;
|
||||||
gridProps?: ReturnType<typeof useDataGridEvents>;
|
gridProps: ReturnType<typeof useDataGridEvents>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TradesManager = ({
|
export const TradesManager = ({
|
||||||
@ -16,19 +19,60 @@ export const TradesManager = ({
|
|||||||
}: TradesContainerProps) => {
|
}: TradesContainerProps) => {
|
||||||
const update = useDealTicketFormValues((state) => state.updateAll);
|
const update = useDealTicketFormValues((state) => state.updateAll);
|
||||||
|
|
||||||
const { data, error } = useDataProvider({
|
const { data, error, load, pageInfo } = useDataProvider({
|
||||||
dataProvider: tradesWithMarketProvider,
|
dataProvider: tradesWithMarketProvider,
|
||||||
variables: { marketId },
|
variables: { marketId },
|
||||||
});
|
});
|
||||||
|
const [hasDisplayedRow, setHasDisplayedRow] = useState<boolean | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
const { onFilterChanged, ...props } = gridProps || {};
|
||||||
|
const onRowDataUpdated = useCallback(
|
||||||
|
({ api }: { api: AgGridReact['api'] }) => {
|
||||||
|
setHasDisplayedRow(!!api.getDisplayedRowCount());
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TradesTable
|
<div className="flex flex-col h-full">
|
||||||
rowData={data}
|
<TradesTable
|
||||||
onClick={(price?: string) => {
|
rowData={data}
|
||||||
update(marketId, { price });
|
onClick={(price?: string) => {
|
||||||
}}
|
update(marketId, { price });
|
||||||
overlayNoRowsTemplate={error ? error.message : t('No trades')}
|
}}
|
||||||
{...gridProps}
|
onFilterChanged={(event) => {
|
||||||
/>
|
onRowDataUpdated(event);
|
||||||
|
onFilterChanged(event);
|
||||||
|
}}
|
||||||
|
onRowDataUpdated={onRowDataUpdated}
|
||||||
|
overlayNoRowsTemplate={error ? error.message : t('No trades')}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
<div className="flex justify-between border-t border-default p-1 items-center">
|
||||||
|
<div className="text-xs">
|
||||||
|
{t(
|
||||||
|
'Depending on data node retention you may not be able see the "full" history'
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex text-xs items-center">
|
||||||
|
{data?.length && !pageInfo?.hasNextPage
|
||||||
|
? t('all %s items loaded', [data.length.toString()])
|
||||||
|
: t('%s items loaded', [
|
||||||
|
data?.length ? data.length.toString() : ' ',
|
||||||
|
])}
|
||||||
|
{pageInfo?.hasNextPage ? (
|
||||||
|
<Button size="extra-small" className="ml-1" onClick={() => load()}>
|
||||||
|
{t('Load more')}
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
{data?.length && hasDisplayedRow === false ? (
|
||||||
|
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-xs">
|
||||||
|
{t('No trades matching selected filters')}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user