From 4d33a83ceda9fdf4fece0a22c60cf7ee3e3a702b Mon Sep 17 00:00:00 2001 From: Andrew Besedin Date: Mon, 16 Feb 2026 23:57:55 +0300 Subject: [PATCH 01/10] update: update get-user-orders fetch interface --- .../get-user-orders/GetUserOrdersData.ts | 23 +++++++++++++++++++ .../responses/orders/GetUserOrdersRes.ts | 1 + src/utils/methods.ts | 22 ++++++++++++++++-- 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 src/interfaces/fetch-data/get-user-orders/GetUserOrdersData.ts diff --git a/src/interfaces/fetch-data/get-user-orders/GetUserOrdersData.ts b/src/interfaces/fetch-data/get-user-orders/GetUserOrdersData.ts new file mode 100644 index 0000000..e8fa4f8 --- /dev/null +++ b/src/interfaces/fetch-data/get-user-orders/GetUserOrdersData.ts @@ -0,0 +1,23 @@ +export enum GetUserOrdersBodyStatus { + ACTIVE = 'active', + FINISHED = 'finished', +} + +export enum GetUserOrdersBodyType { + BUY = 'buy', + SELL = 'sell', +} + +export type GetUserOrdersData = { + limit: number; + offset: number; + filterInfo: { + status?: GetUserOrdersBodyStatus; + type?: GetUserOrdersBodyType; + date?: { + // UNIX timestamps in milliseconds + from: number; + to: number; + }; + }; +}; diff --git a/src/interfaces/responses/orders/GetUserOrdersRes.ts b/src/interfaces/responses/orders/GetUserOrdersRes.ts index 4ac61ac..fca3f67 100644 --- a/src/interfaces/responses/orders/GetUserOrdersRes.ts +++ b/src/interfaces/responses/orders/GetUserOrdersRes.ts @@ -19,6 +19,7 @@ interface UserOrderData { interface GetUserOrdersRes { success: true; + totalItemsCount: number; data: UserOrderData[]; } diff --git a/src/utils/methods.ts b/src/utils/methods.ts index 531fe08..ff26583 100644 --- a/src/utils/methods.ts +++ b/src/utils/methods.ts @@ -25,6 +25,7 @@ import axios from 'axios'; import GetPairsPagesAmountRes from '@/interfaces/responses/dex/GetPairsPagesAmountRes'; import { PairSortOption } from '@/interfaces/enum/pair'; import { API_URL } from '@/constants'; +import { GetUserOrdersData } from '@/interfaces/fetch-data/get-user-orders/GetUserOrdersData'; const isServer = typeof window === 'undefined'; const baseUrl = isServer ? API_URL : ''; @@ -246,10 +247,27 @@ export async function getUserOrdersPage(pairId: string): Promise res.data); } -export async function getUserOrders(): Promise { +export async function getUserOrders({ + limit, + offset, + filterInfo: { status, type, date }, +}: GetUserOrdersData): Promise { return axios - .post('/api/orders/get', { + .patch('/api/orders/get', { token: sessionStorage.getItem('token'), + + limit, + offset, + filterInfo: { + status, + type, + date: date + ? { + from: date.from, + to: date.to, + } + : undefined, + }, }) .then((res) => res.data); } From c0e6fa930876c262e06fae9c73c0055c22596a50 Mon Sep 17 00:00:00 2001 From: Andrew Besedin Date: Tue, 17 Feb 2026 00:24:13 +0300 Subject: [PATCH 02/10] add: add get-user-orders-all-pairs endpoint method --- .../orders/GetUserOrdersAllPairsRes.ts | 18 ++++++++++++++++++ src/utils/methods.ts | 9 +++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/interfaces/responses/orders/GetUserOrdersAllPairsRes.ts diff --git a/src/interfaces/responses/orders/GetUserOrdersAllPairsRes.ts b/src/interfaces/responses/orders/GetUserOrdersAllPairsRes.ts new file mode 100644 index 0000000..65c8b29 --- /dev/null +++ b/src/interfaces/responses/orders/GetUserOrdersAllPairsRes.ts @@ -0,0 +1,18 @@ +export type GetUserOrdersAllPairsResPair = { + id: number; + firstCurrency: { + id: number; + ticker: string | null; + }; + secondCurrency: { + id: number; + ticker: string | null; + }; +}; + +type GetUserOrdersAllPairsRes = { + success: true; + data: GetUserOrdersAllPairsResPair[]; +}; + +export default GetUserOrdersAllPairsRes; diff --git a/src/utils/methods.ts b/src/utils/methods.ts index ff26583..01aa03b 100644 --- a/src/utils/methods.ts +++ b/src/utils/methods.ts @@ -26,6 +26,7 @@ import GetPairsPagesAmountRes from '@/interfaces/responses/dex/GetPairsPagesAmou import { PairSortOption } from '@/interfaces/enum/pair'; import { API_URL } from '@/constants'; import { GetUserOrdersData } from '@/interfaces/fetch-data/get-user-orders/GetUserOrdersData'; +import GetUserOrdersAllPairsRes from '@/interfaces/responses/orders/GetUserOrdersAllPairsRes'; const isServer = typeof window === 'undefined'; const baseUrl = isServer ? API_URL : ''; @@ -272,6 +273,14 @@ export async function getUserOrders({ .then((res) => res.data); } +export async function getUserOrdersAllPairs(): Promise { + return axios + .patch('/api/orders/get-user-orders-pairs', { + token: sessionStorage.getItem('token'), + }) + .then((res) => res.data); +} + export async function cancelOrder(id: string): Promise { return axios .post('/api/orders/cancel', { From a7ec28fa0b3b7c539bfefef23ab3e72c4830bc25 Mon Sep 17 00:00:00 2001 From: Andrew Besedin Date: Tue, 17 Feb 2026 02:48:53 +0300 Subject: [PATCH 03/10] wip: add paginated get-user-orders basic usage --- src/pages/dex/orders/index.tsx | 114 +++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 48 deletions(-) diff --git a/src/pages/dex/orders/index.tsx b/src/pages/dex/orders/index.tsx index 66b44bd..ec7d990 100644 --- a/src/pages/dex/orders/index.tsx +++ b/src/pages/dex/orders/index.tsx @@ -6,7 +6,7 @@ import { useEffect, useState } from 'react'; import Dropdown from '@/components/UI/Dropdown/Dropdown'; import DateRangeSelector from '@/components/UI/DateRangeSelector/DateRangeSelector'; import Button from '@/components/UI/Button/Button'; -import { cancelOrder, getUserOrders } from '@/utils/methods'; +import * as fetchMethods from '@/utils/methods'; import Alert from '@/components/UI/Alert/Alert'; import AlertType from '@/interfaces/common/AlertType'; import { UserOrderData } from '@/interfaces/responses/orders/GetUserOrdersRes'; @@ -14,8 +14,12 @@ import PairValue from '@/interfaces/props/pages/dex/orders/PairValue'; import DateState from '@/interfaces/common/DateState'; import useUpdateUser from '@/hook/useUpdateUser'; import { Footer } from '@/zano_ui/src'; +import { GetUserOrdersBodyStatus } from '@/interfaces/fetch-data/get-user-orders/GetUserOrdersData'; +import Decimal from 'decimal.js'; import OrdersTable from './OrdersTable/OrdersTable'; +const ORDERS_PER_PAGE = 10; + function Orders() { const fetchUser = useUpdateUser(); @@ -42,6 +46,8 @@ function Orders() { }, ]; + const [initialized, setInitialized] = useState(false); + const [pairsValues, setPairsValues] = useState([{ name: 'All pairs', code: '' }]); const [pairDropdownValue, setPairDropdownState] = useState(pairsValues[0]); @@ -62,61 +68,73 @@ function Orders() { const [orders, setOrders] = useState([]); useEffect(() => { - async function getOrders() { - setAlertState('loading'); - setAlertSubtitle('Loading orders data...'); + async function initPairsDropdown() { + try { + const getUserOrdersAllPairsRes = await fetchMethods.getUserOrdersAllPairs(); - const result = await getUserOrders(); + if (!getUserOrdersAllPairsRes.success) { + throw new Error('Error fetching pairs for orders'); + } + + const ordersPairs = getUserOrdersAllPairsRes.data; + + const statePairs = ordersPairs.map((e) => ({ + name: `${e.firstCurrency.ticker}/${e.secondCurrency.ticker}`, + code: new Decimal(e.id).toFixed(), + })); + + setPairsValues([{ name: 'All pairs', code: '' }, ...statePairs]); + } catch (error) { + console.error('Error while initPairsDropdown:', error); + } + } + + async function initOrders() { + const getUserOrdersRes = await fetchMethods.getUserOrders({ + limit: ORDERS_PER_PAGE, + offset: 0, + filterInfo: { + status: GetUserOrdersBodyStatus.ACTIVE, + }, + }); + + if (!getUserOrdersRes.success) { + throw new Error('Error fetching user orders'); + } + + setOrders(getUserOrdersRes.data); + return getUserOrdersRes.data; + } + + async function initialize() { + try { + setAlertState('loading'); + setAlertSubtitle('Loading orders data...'); + + // Simulate loading time + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await fetchUser(); + + await initPairsDropdown(); + + await initOrders(); + + setInitialized(true); + setAlertState(null); + setAlertSubtitle(''); + } catch (error) { + console.error('Error during initialization:', error); - if (!result.success) { setAlertState('error'); setAlertSubtitle('Error loading orders data'); await new Promise((resolve) => setTimeout(resolve, 2000)); setAlertState(null); setAlertSubtitle(''); - return; - } - - fetchUser(); - - setOrders(result.data); - - function getPairsFromOrders(orders: UserOrderData[]) { - const pairs = [ - { - code: '', - name: 'All pairs', - }, - ]; - - for (let i = 0; i < orders.length; i++) { - const pair = { - name: `${orders[i].first_currency.name}/${orders[i].second_currency.name}`, - code: orders[i].pair_id, - }; - - if (!pairs.find((e) => e.code === pair.code)) pairs.push(pair); - } - - return pairs; - } - - const pairs = getPairsFromOrders(result.data); - - setPairsValues(pairs); - setPairDropdownState(pairs[0]); - - setAlertState(null); - setAlertSubtitle(''); - - const { success, data } = await getUserOrders(); - - if (success) { - setOrders(data); } } - getOrders(); + initialize(); }, []); function buySellFilter(e: UserOrderData) { @@ -174,7 +192,7 @@ function Orders() { const results = await (async () => { const res = []; for (const order of activeOrders) { - res.push(await cancelOrder(order.id).catch(() => null)); + res.push(await fetchMethods.cancelOrder(order.id).catch(() => null)); } return res; })(); @@ -192,7 +210,7 @@ function Orders() { setAlertSubtitle(''); }, 2000); - const { success, data } = await getUserOrders(); + const { success, data } = await fetchMethods.getUserOrders(); if (success) { setOrders(data); From abb9e0a47688a62e1f8c4a3480c3b8dc1361d5aa Mon Sep 17 00:00:00 2001 From: Andrew Besedin Date: Tue, 17 Feb 2026 03:36:47 +0300 Subject: [PATCH 04/10] wip: add user orders page useInView and preloader --- src/pages/dex/orders/index.tsx | 13 +++++++++++++ src/styles/Orders.module.scss | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/src/pages/dex/orders/index.tsx b/src/pages/dex/orders/index.tsx index ec7d990..5afaa02 100644 --- a/src/pages/dex/orders/index.tsx +++ b/src/pages/dex/orders/index.tsx @@ -16,6 +16,8 @@ import useUpdateUser from '@/hook/useUpdateUser'; import { Footer } from '@/zano_ui/src'; import { GetUserOrdersBodyStatus } from '@/interfaces/fetch-data/get-user-orders/GetUserOrdersData'; import Decimal from 'decimal.js'; +import { useInView } from 'react-intersection-observer'; +import Preloader from '@/components/UI/Preloader/Preloader'; import OrdersTable from './OrdersTable/OrdersTable'; const ORDERS_PER_PAGE = 10; @@ -23,6 +25,13 @@ const ORDERS_PER_PAGE = 10; function Orders() { const fetchUser = useUpdateUser(); + const { ref: inViewRef } = useInView({ + threshold: 0, + onChange: (inView) => { + console.log('In view:', inView); + }, + }); + const ordersCategories = [ { name: 'Active orders', @@ -278,6 +287,10 @@ function Orders() { setOrders={setOrders} category={categoryState.code} /> + +
+ +
{alertState && ( Date: Tue, 17 Feb 2026 11:05:59 +0300 Subject: [PATCH 05/10] update: update trading page history to use updated getUserOrders endpoint --- src/components/dex/UserOrders/index.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/dex/UserOrders/index.tsx b/src/components/dex/UserOrders/index.tsx index 89e1a9b..b454f4e 100644 --- a/src/components/dex/UserOrders/index.tsx +++ b/src/components/dex/UserOrders/index.tsx @@ -16,6 +16,7 @@ import { countByKeyRecord, createOrderSorter } from '@/utils/utils'; import ApplyTip from '@/interfaces/common/ApplyTip'; import { useQuerySyncedTab } from '@/hook/useQuerySyncedTab'; import { useMediaQuery } from '@/hook/useMediaQuery'; +import { GetUserOrdersBodyStatus } from '@/interfaces/fetch-data/get-user-orders/GetUserOrdersData'; import { UserOrdersProps } from './types'; import styles from './styles.module.scss'; import { @@ -73,7 +74,7 @@ const UserOrders = ({ length: offers.length, }, { - title: 'History', + title: 'History - Last 100 Items', type: 'history', length: ordersHistory.length, }, @@ -105,7 +106,13 @@ const UserOrders = ({ })(); (async () => { - const result = await getUserOrders(); + const result = await getUserOrders({ + limit: 100, + offset: 0, + filterInfo: { + status: GetUserOrdersBodyStatus.FINISHED, + }, + }); if (!result.success) { setAlertState('error'); @@ -116,9 +123,7 @@ const UserOrders = ({ return; } - const filteredOrdersHistory = result.data - .filter((s) => s.pair_id === pairData?.id) - .filter((s) => s.status === 'finished'); + const filteredOrdersHistory = result.data.filter((s) => s.status === 'finished'); fetchUser(); From daecf49bc0d652cfbbc6edc86e9cb71484d8ab18 Mon Sep 17 00:00:00 2001 From: Andrew Besedin Date: Tue, 17 Feb 2026 23:05:45 +0300 Subject: [PATCH 06/10] wip: add My Orders fetching multiple pages & filtering --- .../get-user-orders/GetUserOrdersData.ts | 1 + src/pages/dex/orders/index.tsx | 301 ++++++++++++------ 2 files changed, 196 insertions(+), 106 deletions(-) diff --git a/src/interfaces/fetch-data/get-user-orders/GetUserOrdersData.ts b/src/interfaces/fetch-data/get-user-orders/GetUserOrdersData.ts index e8fa4f8..ad917db 100644 --- a/src/interfaces/fetch-data/get-user-orders/GetUserOrdersData.ts +++ b/src/interfaces/fetch-data/get-user-orders/GetUserOrdersData.ts @@ -12,6 +12,7 @@ export type GetUserOrdersData = { limit: number; offset: number; filterInfo: { + pairId?: number; status?: GetUserOrdersBodyStatus; type?: GetUserOrdersBodyType; date?: { diff --git a/src/pages/dex/orders/index.tsx b/src/pages/dex/orders/index.tsx index 5afaa02..71b9fab 100644 --- a/src/pages/dex/orders/index.tsx +++ b/src/pages/dex/orders/index.tsx @@ -14,7 +14,10 @@ import PairValue from '@/interfaces/props/pages/dex/orders/PairValue'; import DateState from '@/interfaces/common/DateState'; import useUpdateUser from '@/hook/useUpdateUser'; import { Footer } from '@/zano_ui/src'; -import { GetUserOrdersBodyStatus } from '@/interfaces/fetch-data/get-user-orders/GetUserOrdersData'; +import { + GetUserOrdersBodyStatus, + GetUserOrdersBodyType, +} from '@/interfaces/fetch-data/get-user-orders/GetUserOrdersData'; import Decimal from 'decimal.js'; import { useInView } from 'react-intersection-observer'; import Preloader from '@/components/UI/Preloader/Preloader'; @@ -25,13 +28,6 @@ const ORDERS_PER_PAGE = 10; function Orders() { const fetchUser = useUpdateUser(); - const { ref: inViewRef } = useInView({ - threshold: 0, - onChange: (inView) => { - console.log('In view:', inView); - }, - }); - const ordersCategories = [ { name: 'Active orders', @@ -75,117 +71,214 @@ function Orders() { }); const [orders, setOrders] = useState([]); + const [lastOrderOffset, setLastOrderOffset] = useState(0); + const [totalOrdersCount, setTotalOrdersCount] = useState(undefined); + const [orderPageLoading, setOrderPageLoading] = useState(false); - useEffect(() => { - async function initPairsDropdown() { - try { - const getUserOrdersAllPairsRes = await fetchMethods.getUserOrdersAllPairs(); + function deriveFiltersFromState() { + const status = + categoryState.code === 'active-orders' + ? GetUserOrdersBodyStatus.ACTIVE + : GetUserOrdersBodyStatus.FINISHED; - if (!getUserOrdersAllPairsRes.success) { - throw new Error('Error fetching pairs for orders'); - } - - const ordersPairs = getUserOrdersAllPairsRes.data; - - const statePairs = ordersPairs.map((e) => ({ - name: `${e.firstCurrency.ticker}/${e.secondCurrency.ticker}`, - code: new Decimal(e.id).toFixed(), - })); - - setPairsValues([{ name: 'All pairs', code: '' }, ...statePairs]); - } catch (error) { - console.error('Error while initPairsDropdown:', error); - } - } - - async function initOrders() { - const getUserOrdersRes = await fetchMethods.getUserOrders({ - limit: ORDERS_PER_PAGE, - offset: 0, - filterInfo: { - status: GetUserOrdersBodyStatus.ACTIVE, - }, - }); - - if (!getUserOrdersRes.success) { - throw new Error('Error fetching user orders'); + const type = (() => { + if (buyDropdownValue.name === 'Buy & Sell') { + return undefined; } - setOrders(getUserOrdersRes.data); - return getUserOrdersRes.data; + return buyDropdownValue.name === 'Buy' + ? GetUserOrdersBodyType.BUY + : GetUserOrdersBodyType.SELL; + })(); + + const pairId = + pairDropdownValue.code === '' + ? undefined + : new Decimal(pairDropdownValue.code).toNumber(); + + const date = (() => { + if (!dateRange.first || !dateRange.last) return undefined; + + const firstDate = new Date(dateRange.first); + const lastDate = new Date(dateRange.last); + + firstDate.setHours(0, 0, 0, 0); + lastDate.setHours(23, 59, 59, 999); + + return { + from: firstDate.getTime(), + to: lastDate.getTime(), + }; + })(); + + return { + status, + type, + pairId, + date, + }; + } + + async function addNewOrdersPage() { + const { status, type, pairId, date } = deriveFiltersFromState(); + + const getUserOrdersRes = await fetchMethods.getUserOrders({ + limit: ORDERS_PER_PAGE, + offset: lastOrderOffset, + filterInfo: { + status, + type, + pairId, + date, + }, + }); + + if (!getUserOrdersRes.success) { + throw new Error('Error fetching user orders'); } - async function initialize() { + const newOrders = getUserOrdersRes.data; + const newOrdersAmount = newOrders.length; + + setOrders((prev) => [...prev, ...newOrders]); + setLastOrderOffset((prev) => prev + newOrdersAmount); + setTotalOrdersCount(getUserOrdersRes.totalItemsCount); + } + + const { ref: inViewRef } = useInView({ + threshold: 0, + onChange: async (inView) => { + if (!inView || !initialized) { + return; + } + + if (totalOrdersCount !== undefined && lastOrderOffset >= totalOrdersCount) { + return; + } + + if (orderPageLoading) { + return; + } + + setOrderPageLoading(true); + try { - setAlertState('loading'); - setAlertSubtitle('Loading orders data...'); - - // Simulate loading time - await new Promise((resolve) => setTimeout(resolve, 1000)); - - await fetchUser(); - - await initPairsDropdown(); - - await initOrders(); - - setInitialized(true); - setAlertState(null); - setAlertSubtitle(''); + await addNewOrdersPage(); } catch (error) { - console.error('Error during initialization:', error); + console.error('Error fetching new orders page:', error); setAlertState('error'); - setAlertSubtitle('Error loading orders data'); + setAlertSubtitle('Error loading more orders'); + await new Promise((resolve) => setTimeout(resolve, 2000)); + setAlertState(null); setAlertSubtitle(''); + } finally { + setOrderPageLoading(false); } + }, + }); + + async function initPairsDropdown() { + try { + const getUserOrdersAllPairsRes = await fetchMethods.getUserOrdersAllPairs(); + + if (!getUserOrdersAllPairsRes.success) { + throw new Error('Error fetching pairs for orders'); + } + + const ordersPairs = getUserOrdersAllPairsRes.data; + + const statePairs = ordersPairs.map((e) => ({ + name: `${e.firstCurrency.ticker}/${e.secondCurrency.ticker}`, + code: new Decimal(e.id).toFixed(), + })); + + setPairsValues([{ name: 'All pairs', code: '' }, ...statePairs]); + } catch (error) { + console.error('Error while initPairsDropdown:', error); + } + } + + async function initOrders() { + const { status, type, pairId, date } = deriveFiltersFromState(); + + const getUserOrdersRes = await fetchMethods.getUserOrders({ + limit: ORDERS_PER_PAGE, + offset: 0, + filterInfo: { + status, + type, + pairId, + date, + }, + }); + + if (!getUserOrdersRes.success) { + throw new Error('Error fetching user orders'); } + const newOrders = getUserOrdersRes.data; + const newOrdersAmount = newOrders.length; + + setOrders(newOrders); + setLastOrderOffset(newOrdersAmount); + setTotalOrdersCount(getUserOrdersRes.totalItemsCount); + + return newOrders; + } + + async function initialize() { + try { + setAlertState('loading'); + setAlertSubtitle('Loading orders data...'); + + setOrders([]); + setLastOrderOffset(0); + setTotalOrdersCount(undefined); + + // Simulate loading time + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await fetchUser(); + + await initPairsDropdown(); + + await initOrders(); + + setInitialized(true); + setAlertState(null); + setAlertSubtitle(''); + } catch (error) { + console.error('Error during initialization:', error); + + setAlertState('error'); + setAlertSubtitle('Error loading orders data'); + await new Promise((resolve) => setTimeout(resolve, 2000)); + setAlertState(null); + setAlertSubtitle(''); + } + } + + useEffect(() => { + async function onFilterChange() { + if (!initialized) { + return; + } + + await initialize(); + } + + onFilterChange(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [buyDropdownValue, pairDropdownValue, dateRange, categoryState]); + + useEffect(() => { initialize(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - function buySellFilter(e: UserOrderData) { - if (buyDropdownValue.name === 'Buy & Sell') return true; - - if (buyDropdownValue.name === 'Buy') return e.type === 'buy'; - - if (buyDropdownValue.name === 'Sell') return e.type === 'sell'; - } - - function pairFilter(e: UserOrderData) { - if (!pairDropdownValue) return true; - - return !pairDropdownValue.code || e.pair_id === pairDropdownValue.code; - } - - function dateFilter(e: UserOrderData) { - if (!dateRange.first || !dateRange.last) return true; - const firstDate = new Date(dateRange.first); - const lastDate = new Date(dateRange.last); - - const timestamp = parseInt(e.timestamp, 10); - - firstDate.setHours(0, 0, 0, 0); - lastDate.setHours(24, 0, 0, 0); - - if (!dateRange.first && !dateRange.last) return true; - - if (dateRange.first && !dateRange.last) return timestamp >= firstDate.getTime(); - - if (!dateRange.first && dateRange.last) return timestamp <= lastDate.getTime(); - - return timestamp >= firstDate.getTime() && timestamp <= lastDate.getTime(); - } - - function categoryFilter(e: UserOrderData) { - if (categoryState.code === 'active-orders') { - return e.status === 'active'; - } - return e.status === 'finished'; - } - const activeOrders = orders.filter((e) => e.status === 'active'); async function cancelAllOrders() { @@ -277,11 +370,7 @@ function Orders() {
- + {orderPageLoading && }
{alertState && ( From 09750ccdaa1f6aa1607c76bdbb75cc65e37b8bb3 Mon Sep 17 00:00:00 2001 From: Andrew Besedin Date: Wed, 18 Feb 2026 00:32:43 +0300 Subject: [PATCH 07/10] wip: update My Orders order delete --- .../orders/OrdersTable/OrdersTableProps.ts | 6 +--- .../dex/orders/OrdersTable/OrdersTable.tsx | 30 ++-------------- src/pages/dex/orders/index.tsx | 35 +++++++++++++++++-- 3 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/interfaces/props/pages/dex/orders/OrdersTable/OrdersTableProps.ts b/src/interfaces/props/pages/dex/orders/OrdersTable/OrdersTableProps.ts index 203e7d7..d48f40a 100644 --- a/src/interfaces/props/pages/dex/orders/OrdersTable/OrdersTableProps.ts +++ b/src/interfaces/props/pages/dex/orders/OrdersTable/OrdersTableProps.ts @@ -1,13 +1,9 @@ -import AlertType from '@/interfaces/common/AlertType'; import { UserOrderData } from '@/interfaces/responses/orders/GetUserOrdersRes'; -import { Dispatch, SetStateAction } from 'react'; interface OrdersTableProps { value: UserOrderData[]; category: string; - setAlertState: Dispatch>; - setAlertSubtitle: Dispatch>; - setOrders: Dispatch>; + deleteOrder: (orderId: string) => Promise; } export default OrdersTableProps; diff --git a/src/pages/dex/orders/OrdersTable/OrdersTable.tsx b/src/pages/dex/orders/OrdersTable/OrdersTable.tsx index dda4b21..dee1ef8 100644 --- a/src/pages/dex/orders/OrdersTable/OrdersTable.tsx +++ b/src/pages/dex/orders/OrdersTable/OrdersTable.tsx @@ -4,7 +4,6 @@ import DeleteIcon from '@/assets/images/UI/delete.svg'; import NoOffersIcon from '@/assets/images/UI/no_offers.svg'; import EmptyLink from '@/components/UI/EmptyLink/EmptyLink'; import { notationToString, toStandardDateString } from '@/utils/utils'; -import { cancelOrder, getUserOrders } from '@/utils/methods'; import OrdersTableProps from '@/interfaces/props/pages/dex/orders/OrdersTable/OrdersTableProps'; import { UserOrderData } from '@/interfaces/responses/orders/GetUserOrdersRes'; import Decimal from 'decimal.js'; @@ -15,7 +14,7 @@ import styles from './OrdersTable.module.scss'; function OrdersTable(props: OrdersTableProps) { const orders = props.value || []; - const { setAlertState, setAlertSubtitle, setOrders, category } = props; + const { deleteOrder, category } = props; const isActive = category === 'active-orders'; @@ -27,31 +26,6 @@ function OrdersTable(props: OrdersTableProps) { const timestampDate = new Date(parseInt(orderData.timestamp, 10)); - async function deleteOrder() { - setAlertState('loading'); - setAlertSubtitle('Canceling order...'); - const result = await cancelOrder(orderData.id); - - if (result.success) { - setAlertState('success'); - setAlertSubtitle('Order canceled'); - } else { - setAlertState('error'); - setAlertSubtitle('Error canceling order'); - } - - setTimeout(() => { - setAlertState(null); - setAlertSubtitle(''); - }, 2000); - - const { success, data } = await getUserOrders(); - - if (success) { - setOrders(data); - } - } - const amount = ( isActive ? new Decimal(orderData.amount) @@ -150,7 +124,7 @@ function OrdersTable(props: OrdersTableProps) { diff --git a/src/pages/dex/orders/index.tsx b/src/pages/dex/orders/index.tsx index 71b9fab..4f0c85b 100644 --- a/src/pages/dex/orders/index.tsx +++ b/src/pages/dex/orders/index.tsx @@ -281,6 +281,37 @@ function Orders() { const activeOrders = orders.filter((e) => e.status === 'active'); + async function deleteOrder(orderId: string) { + setAlertState('loading'); + setAlertSubtitle('Canceling order...'); + + const result = await fetchMethods.cancelOrder(orderId); + + if (!result.success) { + setAlertState('error'); + setAlertSubtitle('Error canceling order'); + + setTimeout(() => { + setAlertState(null); + setAlertSubtitle(''); + }, 2000); + + return; + } + + setAlertState('success'); + setAlertSubtitle('Order canceled'); + + setTimeout(() => { + setAlertState(null); + setAlertSubtitle(''); + }, 2000); + + setOrders((prev) => prev.filter((e) => e.id !== orderId)); + setLastOrderOffset((prev) => Math.max(prev - 1, 0)); + setTotalOrdersCount((prev) => (prev !== undefined ? prev - 1 : prev)); + } + async function cancelAllOrders() { setAlertState('loading'); setAlertSubtitle('Canceling all orders...'); @@ -371,10 +402,8 @@ function Orders() {
From 5fdfe59737fa30b710fdfa31e1f76234d03fa494 Mon Sep 17 00:00:00 2001 From: Andrew Besedin Date: Wed, 18 Feb 2026 03:16:32 +0300 Subject: [PATCH 08/10] wip: add Cancel All endpoint usage --- .../cancel-all-orders/CancelAllData.ts | 16 ++ .../responses/orders/CancelAllRes.ts | 5 + src/pages/dex/orders/index.tsx | 144 +++++++++++------- src/utils/methods.ts | 22 +++ 4 files changed, 136 insertions(+), 51 deletions(-) create mode 100644 src/interfaces/fetch-data/cancel-all-orders/CancelAllData.ts create mode 100644 src/interfaces/responses/orders/CancelAllRes.ts diff --git a/src/interfaces/fetch-data/cancel-all-orders/CancelAllData.ts b/src/interfaces/fetch-data/cancel-all-orders/CancelAllData.ts new file mode 100644 index 0000000..dfacd38 --- /dev/null +++ b/src/interfaces/fetch-data/cancel-all-orders/CancelAllData.ts @@ -0,0 +1,16 @@ +export enum CancelAllBodyOrderType { + BUY = 'buy', + SELL = 'sell', +} + +export type CancelAllData = { + filterInfo: { + pairId?: number; + type?: CancelAllBodyOrderType; + date?: { + // UNIX timestamps in milliseconds + from: number; + to: number; + }; + }; +}; diff --git a/src/interfaces/responses/orders/CancelAllRes.ts b/src/interfaces/responses/orders/CancelAllRes.ts new file mode 100644 index 0000000..073718d --- /dev/null +++ b/src/interfaces/responses/orders/CancelAllRes.ts @@ -0,0 +1,5 @@ +interface CancelAllRes { + success: true; +} + +export default CancelAllRes; diff --git a/src/pages/dex/orders/index.tsx b/src/pages/dex/orders/index.tsx index 4f0c85b..b2ac204 100644 --- a/src/pages/dex/orders/index.tsx +++ b/src/pages/dex/orders/index.tsx @@ -21,6 +21,7 @@ import { import Decimal from 'decimal.js'; import { useInView } from 'react-intersection-observer'; import Preloader from '@/components/UI/Preloader/Preloader'; +import { CancelAllBodyOrderType } from '@/interfaces/fetch-data/cancel-all-orders/CancelAllData'; import OrdersTable from './OrdersTable/OrdersTable'; const ORDERS_PER_PAGE = 10; @@ -75,7 +76,7 @@ function Orders() { const [totalOrdersCount, setTotalOrdersCount] = useState(undefined); const [orderPageLoading, setOrderPageLoading] = useState(false); - function deriveFiltersFromState() { + function deriveGetUserOrdersFiltersFromState() { const status = categoryState.code === 'active-orders' ? GetUserOrdersBodyStatus.ACTIVE @@ -119,8 +120,46 @@ function Orders() { }; } + function deriveCancelAllOrdersFiltersFromState() { + const type = (() => { + if (buyDropdownValue.name === 'Buy & Sell') { + return undefined; + } + + return buyDropdownValue.name === 'Buy' + ? CancelAllBodyOrderType.BUY + : CancelAllBodyOrderType.SELL; + })(); + + const pairId = + pairDropdownValue.code === '' + ? undefined + : new Decimal(pairDropdownValue.code).toNumber(); + + const date = (() => { + if (!dateRange.first || !dateRange.last) return undefined; + + const firstDate = new Date(dateRange.first); + const lastDate = new Date(dateRange.last); + + firstDate.setHours(0, 0, 0, 0); + lastDate.setHours(23, 59, 59, 999); + + return { + from: firstDate.getTime(), + to: lastDate.getTime(), + }; + })(); + + return { + type, + pairId, + date, + }; + } + async function addNewOrdersPage() { - const { status, type, pairId, date } = deriveFiltersFromState(); + const { status, type, pairId, date } = deriveGetUserOrdersFiltersFromState(); const getUserOrdersRes = await fetchMethods.getUserOrders({ limit: ORDERS_PER_PAGE, @@ -202,7 +241,7 @@ function Orders() { } async function initOrders() { - const { status, type, pairId, date } = deriveFiltersFromState(); + const { status, type, pairId, date } = deriveGetUserOrdersFiltersFromState(); const getUserOrdersRes = await fetchMethods.getUserOrders({ limit: ORDERS_PER_PAGE, @@ -279,15 +318,34 @@ function Orders() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const activeOrders = orders.filter((e) => e.status === 'active'); - async function deleteOrder(orderId: string) { - setAlertState('loading'); - setAlertSubtitle('Canceling order...'); + try { + setAlertState('loading'); + setAlertSubtitle('Canceling order...'); - const result = await fetchMethods.cancelOrder(orderId); + // Simulate loading time + await new Promise((resolve) => setTimeout(resolve, 500)); + + const result = await fetchMethods.cancelOrder(orderId); + + if (!result.success) { + throw new Error('ERROR_CANCELING_ORDER'); + } + + setAlertState('success'); + setAlertSubtitle('Order canceled'); + + setTimeout(() => { + setAlertState(null); + setAlertSubtitle(''); + }, 2000); + + setOrders((prev) => prev.filter((e) => e.id !== orderId)); + setLastOrderOffset((prev) => Math.max(prev - 1, 0)); + setTotalOrdersCount((prev) => (prev !== undefined ? prev - 1 : prev)); + } catch (error) { + console.error('Error canceling order:', error); - if (!result.success) { setAlertState('error'); setAlertSubtitle('Error canceling order'); @@ -295,58 +353,42 @@ function Orders() { setAlertState(null); setAlertSubtitle(''); }, 2000); - - return; } - - setAlertState('success'); - setAlertSubtitle('Order canceled'); - - setTimeout(() => { - setAlertState(null); - setAlertSubtitle(''); - }, 2000); - - setOrders((prev) => prev.filter((e) => e.id !== orderId)); - setLastOrderOffset((prev) => Math.max(prev - 1, 0)); - setTotalOrdersCount((prev) => (prev !== undefined ? prev - 1 : prev)); } async function cancelAllOrders() { - setAlertState('loading'); - setAlertSubtitle('Canceling all orders...'); + try { + setAlertState('loading'); + setAlertSubtitle('Canceling all orders...'); - // const results = await Promise.allSettled( - // activeOrders.map(async (e) => { - // await cancelOrder(e.id); - // }), - // ); + // Simulate loading time + await new Promise((resolve) => setTimeout(resolve, 500)); - const results = await (async () => { - const res = []; - for (const order of activeOrders) { - res.push(await fetchMethods.cancelOrder(order.id).catch(() => null)); + const { type, pairId, date } = deriveCancelAllOrdersFiltersFromState(); + + const cancelAllRes = await fetchMethods.cancelAllOrders({ + filterInfo: { + type, + pairId, + date, + }, + }); + + if (!cancelAllRes.success) { + throw new Error('Error canceling all orders'); } - return res; - })(); - if (results.some((e) => e === null)) { + await initialize(); + } catch (error) { + console.error('Error canceling all orders:', error); + setAlertState('error'); - setAlertSubtitle('Some of the orders were not canceled'); - } else { - setAlertState('success'); - setAlertSubtitle('All orders canceled'); - } + setAlertSubtitle('Error canceling all orders'); - setTimeout(() => { - setAlertState(null); - setAlertSubtitle(''); - }, 2000); - - const { success, data } = await fetchMethods.getUserOrders(); - - if (success) { - setOrders(data); + setTimeout(() => { + setAlertState(null); + setAlertSubtitle(''); + }, 2000); } } diff --git a/src/utils/methods.ts b/src/utils/methods.ts index 01aa03b..b07aac1 100644 --- a/src/utils/methods.ts +++ b/src/utils/methods.ts @@ -27,6 +27,8 @@ import { PairSortOption } from '@/interfaces/enum/pair'; import { API_URL } from '@/constants'; import { GetUserOrdersData } from '@/interfaces/fetch-data/get-user-orders/GetUserOrdersData'; import GetUserOrdersAllPairsRes from '@/interfaces/responses/orders/GetUserOrdersAllPairsRes'; +import { CancelAllData } from '@/interfaces/fetch-data/cancel-all-orders/CancelAllData'; +import CancelAllRes from '@/interfaces/responses/orders/CancelAllRes'; const isServer = typeof window === 'undefined'; const baseUrl = isServer ? API_URL : ''; @@ -336,6 +338,26 @@ export async function applyOrder(orderData: ApplyOrderData): Promise res.data); } +export async function cancelAllOrders({ + filterInfo: { pairId, type, date }, +}: CancelAllData): Promise { + return axios + .patch('/api/orders/cancel-all', { + token: sessionStorage.getItem('token'), + filterInfo: { + pairId, + type, + date: date + ? { + from: date.from, + to: date.to, + } + : undefined, + }, + }) + .then((res) => res.data); +} + export async function confirmTransaction( transactionId: string, ): Promise { From e9ad4f04a58566496c4185e8f8267c1332e8a770 Mon Sep 17 00:00:00 2001 From: Andrew Besedin Date: Wed, 18 Feb 2026 03:28:02 +0300 Subject: [PATCH 09/10] fix: fix pair filter --- src/utils/methods.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/methods.ts b/src/utils/methods.ts index b07aac1..57b5072 100644 --- a/src/utils/methods.ts +++ b/src/utils/methods.ts @@ -253,7 +253,7 @@ export async function getUserOrdersPage(pairId: string): Promise { return axios .patch('/api/orders/get', { @@ -262,6 +262,7 @@ export async function getUserOrders({ limit, offset, filterInfo: { + pairId, status, type, date: date From 5f9fffae932dc6b79efa16dc5ecb91805a4fc817 Mon Sep 17 00:00:00 2001 From: Andrew Besedin Date: Wed, 18 Feb 2026 17:02:59 +0300 Subject: [PATCH 10/10] update: finish cancel all orders feature --- src/pages/dex/orders/index.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/pages/dex/orders/index.tsx b/src/pages/dex/orders/index.tsx index b2ac204..ec3e688 100644 --- a/src/pages/dex/orders/index.tsx +++ b/src/pages/dex/orders/index.tsx @@ -76,6 +76,8 @@ function Orders() { const [totalOrdersCount, setTotalOrdersCount] = useState(undefined); const [orderPageLoading, setOrderPageLoading] = useState(false); + const isFinishedCategory = categoryState.code === 'history'; + function deriveGetUserOrdersFiltersFromState() { const status = categoryState.code === 'active-orders' @@ -436,9 +438,11 @@ function Orders() {
- + {!isFinishedCategory && ( + + )}