diff --git a/src/components/UI/ActionBtn/index.tsx b/src/components/UI/ActionBtn/index.tsx
new file mode 100644
index 0000000..54052ea
--- /dev/null
+++ b/src/components/UI/ActionBtn/index.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { classes } from '@/utils/utils';
+import { ActionBtnProps } from './types';
+import styles from './styles.module.scss';
+
+const ActionBtn = ({ children, variant = 'primary', className, ...props }: ActionBtnProps) => {
+ return (
+
+ );
+};
+
+export default ActionBtn;
diff --git a/src/components/UI/ActionBtn/styles.module.scss b/src/components/UI/ActionBtn/styles.module.scss
new file mode 100644
index 0000000..416c224
--- /dev/null
+++ b/src/components/UI/ActionBtn/styles.module.scss
@@ -0,0 +1,34 @@
+.btn {
+ cursor: pointer;
+ border: none;
+ outline: none;
+ padding: 6px 12px;
+ border-radius: 5px;
+ background: #273666;
+ font-size: 12px;
+ font-weight: 500;
+ line-height: 100%;
+ min-width: max-content;
+
+ &:disabled {
+ cursor: not-allowed;
+ opacity: 0.5 !important;
+ }
+
+ &:hover {
+ opacity: 0.8;
+ background: #273666;
+ }
+
+ &.primary {
+ color: #1f8feb;
+ }
+
+ &.success {
+ color: #16d1d6;
+ }
+
+ &.danger {
+ color: #ff6767;
+ }
+}
diff --git a/src/components/UI/ActionBtn/types.ts b/src/components/UI/ActionBtn/types.ts
new file mode 100644
index 0000000..2d807fd
--- /dev/null
+++ b/src/components/UI/ActionBtn/types.ts
@@ -0,0 +1,5 @@
+import { ButtonHTMLAttributes } from 'react';
+
+export interface ActionBtnProps extends ButtonHTMLAttributes {
+ variant?: 'primary' | 'success' | 'danger';
+}
diff --git a/src/components/default/GenericTable/index.tsx b/src/components/default/GenericTable/index.tsx
new file mode 100644
index 0000000..da606e8
--- /dev/null
+++ b/src/components/default/GenericTable/index.tsx
@@ -0,0 +1,95 @@
+import { classes } from '@/utils/utils';
+import React from 'react';
+import EmptyMessage from '@/components/UI/EmptyMessage';
+import { GenericTableProps } from './types';
+
+export default function GenericTable(props: GenericTableProps) {
+ const {
+ className,
+ tableClassName,
+ theadClassName,
+ tbodyClassName,
+ columns,
+ data,
+ getRowKey,
+ emptyMessage = 'No data',
+ } = props;
+
+ return (
+
+ {data.length > 0 ? (
+
+
+
+ {columns.map((col) => (
+
+ ))}
+
+
+
+
+ {columns.map((col) => (
+ |
+ {col.header}
+ |
+ ))}
+
+
+
+
+ {data.map((row, i) => (
+
+ {columns.map((col) => (
+ |
+ {col.cell(row, i)}
+ |
+ ))}
+
+ ))}
+
+
+
+ ) : (
+
+ )}
+
+ );
+}
diff --git a/src/components/default/GenericTable/types.ts b/src/components/default/GenericTable/types.ts
new file mode 100644
index 0000000..238ff86
--- /dev/null
+++ b/src/components/default/GenericTable/types.ts
@@ -0,0 +1,21 @@
+export type Align = 'left' | 'center' | 'right';
+
+export type ColumnDef = {
+ key: string;
+ header: React.ReactNode;
+ width?: string;
+ align?: Align;
+ className?: string;
+ cell: (_row: T, _rowIndex: number) => React.ReactNode;
+};
+
+export type GenericTableProps = {
+ className?: string;
+ tableClassName?: string;
+ theadClassName?: string;
+ tbodyClassName?: string;
+ columns: ColumnDef[];
+ data: T[];
+ getRowKey: (_row: T, _rowIndex: number) => React.Key;
+ emptyMessage?: string;
+};
diff --git a/src/components/dex/MatrixConnectionBadge/index.tsx b/src/components/dex/MatrixConnectionBadge/index.tsx
index 4839242..5d47033 100644
--- a/src/components/dex/MatrixConnectionBadge/index.tsx
+++ b/src/components/dex/MatrixConnectionBadge/index.tsx
@@ -1,5 +1,6 @@
import Tooltip from '@/components/UI/Tooltip/Tooltip';
-import { useState } from 'react';
+import { useEffect, useRef, useState } from 'react';
+import { createPortal } from 'react-dom';
import { ReactComponent as ConnectionIcon } from '@/assets/images/UI/connection.svg';
import styles from './styles.module.scss';
import { MatrixConnectionBadgeProps } from './types';
@@ -12,29 +13,71 @@ function MatrixConnectionBadge({
const hasConnection = (address: string) =>
matrixAddresses.some((item) => item.address === address && item.registered);
- const [connectionTooltip, setConnectionTooltip] = useState(false);
- return userAdress && hasConnection(userAdress) ? (
+ const [open, setOpen] = useState(false);
+ const [pos, setPos] = useState<{ top: number; left: number } | null>(null);
+ const anchorRef = useRef(null);
+
+ const updatePosition = () => {
+ const el = anchorRef.current;
+ if (!el) return;
+ const rect = el.getBoundingClientRect();
+ setPos({
+ top: rect.bottom + 8,
+ left: rect.left + rect.width / 2,
+ });
+ };
+
+ useEffect(() => {
+ if (!open) return;
+ updatePosition();
+ const onScrollOrResize = () => updatePosition();
+ window.addEventListener('scroll', onScrollOrResize, true);
+ window.addEventListener('resize', onScrollOrResize);
+ return () => {
+ window.removeEventListener('scroll', onScrollOrResize, true);
+ window.removeEventListener('resize', onScrollOrResize);
+ };
+ }, [open]);
+
+ if (!userAdress || !hasConnection(userAdress)) return <>>;
+
+ return (
- ) : (
- <>>
);
}
diff --git a/src/components/dex/MatrixConnectionBadge/styles.module.scss b/src/components/dex/MatrixConnectionBadge/styles.module.scss
index 9d64826..323ddc6 100644
--- a/src/components/dex/MatrixConnectionBadge/styles.module.scss
+++ b/src/components/dex/MatrixConnectionBadge/styles.module.scss
@@ -7,6 +7,7 @@
}
&__tooltip {
+ padding: 10px;
position: absolute;
top: 30px;
left: 50%;
diff --git a/src/components/dex/UserOrders/columns/index.tsx b/src/components/dex/UserOrders/columns/index.tsx
new file mode 100644
index 0000000..092a917
--- /dev/null
+++ b/src/components/dex/UserOrders/columns/index.tsx
@@ -0,0 +1,386 @@
+import { notationToString, formatTimestamp } from '@/utils/utils';
+import type OrderRow from '@/interfaces/common/OrderRow';
+import type { ColumnDef } from '@/components/default/GenericTable/types';
+import ApplyTip from '@/interfaces/common/ApplyTip';
+import UserPendingType from '@/interfaces/common/UserPendingType';
+import { UserOrderData } from '@/interfaces/responses/orders/GetUserOrdersRes';
+import CancelActionCell from '../components/CancelActionCell';
+import AliasCell from '../components/AliasCell';
+import TotalUsdCell from '../components/TotalUsdCell';
+import RequestActionCell from '../components/RequestActionCell';
+import {
+ BuildApplyTipsColumnsArgs,
+ BuildMyRequestsColumnsArgs,
+ BuildOrderHistoryColumnsArgs,
+ BuildUserColumnsArgs,
+} from './types';
+
+export function buildUserColumns({
+ firstCurrencyName,
+ secondCurrencyName,
+ secondAssetUsdPrice,
+ offersCountByOrderId,
+ onAfter,
+}: BuildUserColumnsArgs): ColumnDef[] {
+ return [
+ {
+ key: 'pair',
+ header: 'Pair',
+ width: '120px',
+ cell: (row) => (
+
+ {firstCurrencyName}/{secondCurrencyName}
+
+ ),
+ },
+ {
+ key: 'direction',
+ header: 'Direction',
+ width: '110px',
+ cell: (row) => (
+
+ {row.type}
+
+ ),
+ },
+ {
+ key: 'price',
+ header: <>Price ({secondCurrencyName})>,
+ width: '150px',
+ cell: (row) => {notationToString(row.price)}
,
+ },
+ {
+ key: 'quantity',
+ header: <>Quantity ({firstCurrencyName})>,
+ width: '160px',
+ cell: (row) => {notationToString(row.amount)}
,
+ },
+ {
+ key: 'total',
+ header: <>Total ({secondCurrencyName})>,
+ width: '180px',
+ cell: (row) => (
+
+ ),
+ },
+ {
+ key: 'offers',
+ header: 'Offers',
+ width: '90px',
+ cell: (row) => (
+
+ {offersCountByOrderId.get(String(row.id)) ?? 0}
+
+ ),
+ },
+ {
+ key: 'orderid',
+ header: 'Order ID',
+ width: '140px',
+ cell: (row) => {row.pair_id}
,
+ },
+ {
+ key: 'time',
+ header: 'Time',
+ width: '180px',
+ cell: (row) => {formatTimestamp(row.timestamp)}
,
+ },
+ {
+ key: 'action',
+ header: 'Action',
+ width: '80px',
+ align: 'left',
+ cell: (row) => ,
+ },
+ ];
+}
+
+export function buildApplyTipsColumns({
+ type,
+ firstCurrencyName,
+ secondCurrencyName,
+ matrixAddresses,
+ secondAssetUsdPrice,
+ userOrders,
+ pairData,
+ onAfter,
+}: BuildApplyTipsColumnsArgs): ColumnDef[] {
+ return [
+ {
+ key: 'pair',
+ header: 'Pair',
+ width: '120px',
+ cell: (row) => (
+
+ {firstCurrencyName}/{secondCurrencyName}
+
+ ),
+ },
+ {
+ key: 'direction',
+ header: 'Direction',
+ width: '110px',
+ cell: (row) => (
+
+ {row.type}
+
+ ),
+ },
+ {
+ key: 'alias',
+ header: 'Alias',
+ width: '180px',
+ cell: (row) => (
+
+ ),
+ },
+ {
+ key: 'price',
+ header: <>Price ({secondCurrencyName})>,
+ width: '150px',
+ cell: (row) => {notationToString(row.price)}
,
+ },
+ {
+ key: 'quantity',
+ header: <>Quantity ({firstCurrencyName})>,
+ width: '160px',
+ cell: (row) => {notationToString(row.left)}
,
+ },
+ {
+ key: 'total',
+ header: <>Total ({secondCurrencyName})>,
+ width: '180px',
+ cell: (row) => (
+
+ ),
+ },
+ {
+ key: 'orderid',
+ header: 'Order ID',
+ width: '140px',
+ cell: (row) => {row.connected_order_id}
,
+ },
+ {
+ key: 'time',
+ header: 'Time',
+ width: '180px',
+ cell: (row) => {formatTimestamp(Number(row.timestamp))}
,
+ },
+ {
+ key: 'action',
+ header: 'Action',
+ width: type === 'offers' ? '150px' : '90px',
+ cell: (row) => (
+
+ o.id === row.connected_order_id)}
+ onAfter={onAfter}
+ />
+ {type === 'offers' && (
+
+ )}
+
+ ),
+ },
+ ];
+}
+
+export function buildMyRequestsColumns({
+ firstCurrencyName,
+ secondCurrencyName,
+ onAfter,
+}: BuildMyRequestsColumnsArgs): ColumnDef[] {
+ return [
+ {
+ key: 'pair',
+ header: 'Pair',
+ width: '120px',
+ cell: (row) => (
+
+ {firstCurrencyName}/{secondCurrencyName}
+
+ ),
+ },
+ {
+ key: 'direction',
+ header: 'Direction',
+ width: '110px',
+ cell: (row) => (
+
+ {row.creator}
+
+ ),
+ },
+ {
+ key: 'quantity',
+ header: <>Quantity ({firstCurrencyName})>,
+ width: '160px',
+ cell: (row) => {notationToString(row.amount)}
,
+ },
+ {
+ key: 'sell_order_id',
+ header: 'Sell Order ID',
+ width: '140px',
+ cell: (row) => {row.sell_order_id}
,
+ },
+ {
+ key: 'buy_order_id',
+ header: 'Buy Order ID',
+ width: '140px',
+ cell: (row) => {row.buy_order_id}
,
+ },
+ {
+ key: 'status',
+ header: 'Status',
+ width: '140px',
+ cell: (row) => {row.status}
,
+ },
+ {
+ key: 'time',
+ header: 'Time',
+ width: '180px',
+ cell: (row) => {formatTimestamp(row.timestamp)}
,
+ },
+ {
+ key: 'action',
+ header: 'Action',
+ width: '80px',
+ align: 'left',
+ cell: (row) => (
+
+ ),
+ },
+ ];
+}
+
+export function buildOrderHistoryColumns({
+ firstCurrencyName,
+ secondCurrencyName,
+ secondAssetUsdPrice,
+}: BuildOrderHistoryColumnsArgs): ColumnDef[] {
+ return [
+ {
+ key: 'pair',
+ header: 'Pair',
+ width: '120px',
+ cell: (row) => (
+
+ {firstCurrencyName}/{secondCurrencyName}
+
+ ),
+ },
+ {
+ key: 'direction',
+ header: 'Direction',
+ width: '110px',
+ cell: (row) => (
+
+ {row.type}
+
+ ),
+ },
+ {
+ key: 'price',
+ header: <>Price ({secondCurrencyName})>,
+ width: '150px',
+ cell: (row) => {notationToString(row.price)}
,
+ },
+ {
+ key: 'quantity',
+ header: <>Quantity ({firstCurrencyName})>,
+ width: '160px',
+ cell: (row) => {notationToString(row.amount)}
,
+ },
+ {
+ key: 'total',
+ header: <>Total ({secondCurrencyName})>,
+ width: '180px',
+ cell: (row) => (
+
+ ),
+ },
+ {
+ key: 'orderid',
+ header: 'Order ID',
+ width: '140px',
+ cell: (row) => {row.pair_id}
,
+ },
+ {
+ key: 'time',
+ header: 'Time',
+ width: '180px',
+ cell: (row) => {formatTimestamp(row.timestamp)}
,
+ },
+ ];
+}
diff --git a/src/components/dex/UserOrders/columns/types.ts b/src/components/dex/UserOrders/columns/types.ts
new file mode 100644
index 0000000..63a6624
--- /dev/null
+++ b/src/components/dex/UserOrders/columns/types.ts
@@ -0,0 +1,34 @@
+import MatrixAddress from '@/interfaces/common/MatrixAddress';
+import OrderRow from '@/interfaces/common/OrderRow';
+import PairData from '@/interfaces/common/PairData';
+
+export interface BuildUserColumnsArgs {
+ firstCurrencyName: string;
+ secondCurrencyName: string;
+ secondAssetUsdPrice?: number;
+ offersCountByOrderId: Map;
+ onAfter: () => Promise;
+}
+
+export interface BuildApplyTipsColumnsArgs {
+ type: 'suitables' | 'offers';
+ firstCurrencyName: string;
+ secondCurrencyName: string;
+ matrixAddresses: MatrixAddress[];
+ secondAssetUsdPrice?: number;
+ userOrders: OrderRow[];
+ pairData: PairData | null;
+ onAfter: () => Promise;
+}
+
+export interface BuildMyRequestsColumnsArgs {
+ firstCurrencyName: string;
+ secondCurrencyName: string;
+ onAfter: () => Promise;
+}
+
+export interface BuildOrderHistoryColumnsArgs {
+ firstCurrencyName: string;
+ secondCurrencyName: string;
+ secondAssetUsdPrice?: number;
+}
diff --git a/src/components/dex/UserOrders/components/AliasCell/index.tsx b/src/components/dex/UserOrders/components/AliasCell/index.tsx
new file mode 100644
index 0000000..169e95b
--- /dev/null
+++ b/src/components/dex/UserOrders/components/AliasCell/index.tsx
@@ -0,0 +1,47 @@
+import { useState } from 'react';
+import Tooltip from '@/components/UI/Tooltip/Tooltip';
+import { cutAddress } from '@/utils/utils';
+import MatrixConnectionBadge from '@/components/dex/MatrixConnectionBadge';
+import BadgeStatus from '@/components/dex/BadgeStatus';
+import styles from '../../styles.module.scss';
+import { AliasCellProps } from './types';
+
+export default function AliasCell({
+ alias,
+ address,
+ matrixAddresses,
+ isInstant,
+ max = 12,
+}: AliasCellProps) {
+ const [show, setShow] = useState(false);
+ const display = alias ? cutAddress(alias, max) : 'no alias';
+
+ return (
+
+ setShow(true)}
+ onMouseLeave={() => setShow(false)}
+ className={styles.alias__text}
+ >
+ @{display}
+
+
+
+ {isInstant && (
+
+
+
+ )}
+
+ {alias && alias.length > max && (
+
+ {alias}
+
+ )}
+
+ );
+}
diff --git a/src/components/dex/UserOrders/components/AliasCell/types.ts b/src/components/dex/UserOrders/components/AliasCell/types.ts
new file mode 100644
index 0000000..c661e7b
--- /dev/null
+++ b/src/components/dex/UserOrders/components/AliasCell/types.ts
@@ -0,0 +1,9 @@
+import MatrixAddress from '@/interfaces/common/MatrixAddress';
+
+export interface AliasCellProps {
+ alias?: string;
+ address?: string;
+ matrixAddresses: MatrixAddress[];
+ isInstant?: boolean;
+ max?: number;
+}
diff --git a/src/components/dex/UserOrders/components/CancelActionCell/index.tsx b/src/components/dex/UserOrders/components/CancelActionCell/index.tsx
new file mode 100644
index 0000000..13373d3
--- /dev/null
+++ b/src/components/dex/UserOrders/components/CancelActionCell/index.tsx
@@ -0,0 +1,41 @@
+import { useState } from 'react';
+import { useAlert } from '@/hook/useAlert';
+import { cancelOrder } from '@/utils/methods';
+import ActionBtn from '@/components/UI/ActionBtn';
+import { CancelActionCellProps } from './types';
+
+export default function CancelActionCell({ type = 'cancel', id, onAfter }: CancelActionCellProps) {
+ const [loading, setLoading] = useState(false);
+ const { setAlertState, setAlertSubtitle } = useAlert();
+
+ const onClick = async () => {
+ if (loading) return;
+
+ try {
+ setLoading(true);
+ const result = await cancelOrder(id);
+ if (!result.success) {
+ setAlertState('error');
+ setAlertSubtitle('Error while cancelling order');
+ setTimeout(() => {
+ setAlertState(null);
+ setAlertSubtitle('');
+ }, 3000);
+ return;
+ }
+ await onAfter();
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+ onClick()}
+ >
+ {type === 'cancel' ? 'Cancel' : 'Reject'}
+
+ );
+}
diff --git a/src/components/dex/UserOrders/components/CancelActionCell/types.ts b/src/components/dex/UserOrders/components/CancelActionCell/types.ts
new file mode 100644
index 0000000..bad984a
--- /dev/null
+++ b/src/components/dex/UserOrders/components/CancelActionCell/types.ts
@@ -0,0 +1,5 @@
+export interface CancelActionCellProps {
+ type?: 'cancel' | 'reject';
+ id: string;
+ onAfter: () => Promise;
+}
diff --git a/src/components/dex/UserOrders/components/MyOrdersApplyRow/index.tsx b/src/components/dex/UserOrders/components/MyOrdersApplyRow/index.tsx
deleted file mode 100644
index d1b447d..0000000
--- a/src/components/dex/UserOrders/components/MyOrdersApplyRow/index.tsx
+++ /dev/null
@@ -1,231 +0,0 @@
-import React, { useContext, useState } from 'react';
-import { cutAddress, formatDollarValue, notationToString } from '@/utils/utils';
-import { nanoid } from 'nanoid';
-import Tooltip from '@/components/UI/Tooltip/Tooltip';
-import Decimal from 'decimal.js';
-import { confirmIonicSwap, ionicSwap } from '@/utils/wallet';
-import { applyOrder, confirmTransaction } from '@/utils/methods';
-import { updateAutoClosedNotification } from '@/store/actions';
-import Link from 'next/link';
-import { Store } from '@/store/store-reducer';
-import { useAlert } from '@/hook/useAlert';
-import OrderRowTooltipCell from '../../../OrderRowTooltipCell';
-import MatrixConnectionBadge from '../../../MatrixConnectionBadge';
-import styles from '../../styles.module.scss';
-import { MyOrdersApplyRowProps } from './types';
-import BadgeStatus from '../../../BadgeStatus';
-
-function MyOrdersApplyRow(props: MyOrdersApplyRowProps) {
- const {
- orderData,
- fetchUser,
- matrixAddresses,
- secondAssetUsdPrice,
- updateOrders,
- updateUserOrders,
- fetchTrades,
- pairData,
- userOrders,
- } = props;
- const e = orderData || {};
-
- const { state, dispatch } = useContext(Store);
- const { setAlertState, setAlertSubtitle } = useAlert();
- const [applyingState, setApplyingState] = useState(false);
-
- const connectedOrder = userOrders.find((order) => order.id === e.connected_order_id);
-
- const totalDecimal = new Decimal(e.left).mul(new Decimal(e.price));
- const totalValue = secondAssetUsdPrice
- ? totalDecimal.mul(secondAssetUsdPrice).toFixed(2)
- : undefined;
-
- const [showTooltip, setShowTooltip] = useState(false);
-
- async function applyClick(event: React.MouseEvent) {
- event.preventDefault();
-
- if (e.id) {
- updateAutoClosedNotification(dispatch, [
- ...state.closed_notifications,
- parseInt(e.id, 10),
- ]);
- }
-
- function alertErr(subtitle: string) {
- setAlertState('error');
- setAlertSubtitle(subtitle);
- setTimeout(() => {
- setAlertState(null);
- setAlertSubtitle('');
- }, 3000);
- }
-
- setApplyingState(true);
- interface SwapOperationResult {
- success: boolean;
- message?: string;
- errorCode?: number;
- data?: unknown;
- }
-
- let result: SwapOperationResult | null = null;
-
- await (async () => {
- if (e.transaction) {
- if (!e.hex_raw_proposal) {
- alertErr('Invalid transaction data received');
- return;
- }
-
- console.log(e.hex_raw_proposal);
-
- const confirmSwapResult = await confirmIonicSwap(e.hex_raw_proposal);
-
- console.log(confirmSwapResult);
-
- if (confirmSwapResult.data?.error?.code === -7) {
- alertErr('Insufficient funds');
- return;
- }
- if (!confirmSwapResult.data?.result) {
- alertErr('Companion responded with an error');
- return;
- }
-
- result = await confirmTransaction(e.id);
- } else {
- const firstCurrencyId = pairData?.first_currency.asset_id;
- const secondCurrencyId = pairData?.second_currency.asset_id;
-
- console.log(firstCurrencyId, secondCurrencyId);
-
- if (!(firstCurrencyId && secondCurrencyId)) {
- alertErr('Invalid transaction data received');
- return;
- }
-
- if (!connectedOrder) return;
-
- const leftDecimal = new Decimal(e.left);
- const priceDecimal = new Decimal(e.price);
-
- const params = {
- destinationAssetID: e.type === 'buy' ? secondCurrencyId : firstCurrencyId,
- destinationAssetAmount: notationToString(
- e.type === 'buy'
- ? leftDecimal.mul(priceDecimal).toString()
- : leftDecimal.toString(),
- ),
- currentAssetID: e.type === 'buy' ? firstCurrencyId : secondCurrencyId,
- currentAssetAmount: notationToString(
- e.type === 'buy'
- ? leftDecimal.toString()
- : leftDecimal.mul(priceDecimal).toString(),
- ),
-
- destinationAddress: e.user.address,
- };
-
- console.log(params);
-
- const createSwapResult = await ionicSwap(params);
-
- console.log(createSwapResult);
-
- const hex = createSwapResult?.data?.result?.hex_raw_proposal;
-
- if (createSwapResult?.data?.error?.code === -7) {
- alertErr('Insufficient funds');
- return;
- }
- if (!hex) {
- alertErr('Companion responded with an error');
- return;
- }
-
- result = await applyOrder({
- ...e,
- hex_raw_proposal: hex,
- });
- }
- })();
-
- setApplyingState(false);
-
- if (!result) {
- return;
- }
-
- if (!(result as { success: boolean }).success) {
- alertErr('Server responded with an error');
- return;
- }
-
- await updateOrders();
- await updateUserOrders();
- await fetchUser();
- await fetchTrades();
- }
-
- return (
-
- |
-
- setShowTooltip(true)}
- onMouseLeave={() => setShowTooltip(false)}
- className={styles.alias__text}
- >
- @{cutAddress(e.user.alias, 12) || 'no alias'}
-
-
-
- {e.isInstant && (
-
-
-
- )}
-
-
- {(e.isInstant || e.transaction) && }
-
- {e.user?.alias.length > 12 && (
-
- {e.user?.alias}
-
- )}
- |
-
-
- {notationToString(e.price)}
-
- {notationToString(e.left)}
-
-
- {notationToString(totalDecimal.toString())}{' '}
- ~ ${totalValue && formatDollarValue(totalValue)}
-
-
- |
-
-
- {applyingState ? 'Process' : 'Apply'}
-
- |
-
- );
-}
-
-export default MyOrdersApplyRow;
diff --git a/src/components/dex/UserOrders/components/MyOrdersApplyRow/types.ts b/src/components/dex/UserOrders/components/MyOrdersApplyRow/types.ts
deleted file mode 100644
index bec6f27..0000000
--- a/src/components/dex/UserOrders/components/MyOrdersApplyRow/types.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import ApplyTip from '@/interfaces/common/ApplyTip';
-import MatrixAddress from '@/interfaces/common/MatrixAddress';
-import OrderRow from '@/interfaces/common/OrderRow';
-import PairData from '@/interfaces/common/PairData';
-
-export interface MyOrdersApplyRowProps {
- orderData: ApplyTip;
- secondAssetUsdPrice: number | undefined;
- updateOrders: () => Promise;
- updateUserOrders: () => Promise;
- fetchUser: () => Promise;
- fetchTrades: () => Promise;
- matrixAddresses: MatrixAddress[];
- pairData: PairData | null;
- userOrders: OrderRow[];
-}
diff --git a/src/components/dex/UserOrders/components/MyOrdersRow/index.tsx b/src/components/dex/UserOrders/components/MyOrdersRow/index.tsx
deleted file mode 100644
index f697441..0000000
--- a/src/components/dex/UserOrders/components/MyOrdersRow/index.tsx
+++ /dev/null
@@ -1,140 +0,0 @@
-import Decimal from 'decimal.js';
-import { nanoid } from 'nanoid';
-import { useContext, useState } from 'react';
-import { cutAddress, formatDollarValue, notationToString } from '@/utils/utils';
-import Tooltip from '@/components/UI/Tooltip/Tooltip';
-import Link from 'next/link';
-import { cancelOrder } from '@/utils/methods';
-import { Store } from '@/store/store-reducer';
-import { useAlert } from '@/hook/useAlert';
-import BadgeStatus from '../../../BadgeStatus';
-import styles from '../../styles.module.scss';
-import MatrixConnectionBadge from '../../../MatrixConnectionBadge';
-import OrderRowTooltipCell from '../../../OrderRowTooltipCell';
-import { MyOrdersRowProps } from './types';
-
-function MyOrdersRow(props: MyOrdersRowProps) {
- const {
- orderData,
- secondAssetUsdPrice,
- fetchUser,
- updateOrders,
- updateUserOrders,
- matrixAddresses,
- applyTips,
- } = props;
-
- const e = orderData || {};
- const { state } = useContext(Store);
- const { setAlertState, setAlertSubtitle } = useAlert();
- const [cancellingState, setCancellingState] = useState(false);
-
- const totalDecimal = new Decimal(e.left).mul(new Decimal(e.price));
- const totalValue = secondAssetUsdPrice
- ? totalDecimal.mul(secondAssetUsdPrice).toFixed(2)
- : undefined;
-
- const [showTooltip, setShowTooltip] = useState(false);
-
- async function cancelClick(event: React.MouseEvent) {
- event.preventDefault();
-
- if (cancellingState) return;
-
- try {
- setCancellingState(true);
- const result = await cancelOrder(e.id);
-
- if (!result.success) {
- setAlertState('error');
- setAlertSubtitle('Error while cancelling order');
- setTimeout(() => {
- setAlertState(null);
- setAlertSubtitle('');
- }, 3000);
- return;
- }
-
- await updateOrders();
- await updateUserOrders();
- await fetchUser();
- } catch (error) {
- console.log(error);
- } finally {
- setCancellingState(false);
- }
- }
-
- return (
-
- |
-
- setShowTooltip(true)}
- onMouseLeave={() => setShowTooltip(false)}
- className={styles.alias__text}
- >
- @
- {cutAddress(
- state.wallet?.connected && state.wallet?.alias
- ? state.wallet.alias
- : 'no alias',
- 12,
- )}
-
-
-
- {e.isInstant && (
-
-
-
- )}
-
-
- {(state.wallet?.connected && state.wallet?.alias ? state.wallet?.alias : '')
- ?.length > 12 && (
-
-
- {state.wallet?.connected && state.wallet?.alias}
-
-
- )}
- |
-
-
- {notationToString(e.price)}
-
-
- {notationToString(e.amount)}
-
-
- {notationToString(totalDecimal.toString())}{' '}
- ~ ${totalValue && formatDollarValue(totalValue)}
-
-
-
-
- {applyTips?.filter((tip) => tip.connected_order_id === e.id)?.length || 0}
-
- |
-
-
- {cancellingState ? 'Process' : 'Cancel'}
-
- |
-
- );
-}
-
-export default MyOrdersRow;
diff --git a/src/components/dex/UserOrders/components/MyOrdersRow/types.ts b/src/components/dex/UserOrders/components/MyOrdersRow/types.ts
deleted file mode 100644
index 9bd8026..0000000
--- a/src/components/dex/UserOrders/components/MyOrdersRow/types.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import ApplyTip from '@/interfaces/common/ApplyTip';
-import MatrixAddress from '@/interfaces/common/MatrixAddress';
-import OrderRow from '@/interfaces/common/OrderRow';
-
-export interface MyOrdersRowProps {
- orderData: OrderRow;
- secondAssetUsdPrice: number | undefined;
- updateOrders: () => Promise;
- updateUserOrders: () => Promise;
- fetchUser: () => Promise;
- matrixAddresses: MatrixAddress[];
- applyTips: ApplyTip[];
-}
diff --git a/src/components/dex/UserOrders/components/RequestActionCell/index.tsx b/src/components/dex/UserOrders/components/RequestActionCell/index.tsx
new file mode 100644
index 0000000..8865b76
--- /dev/null
+++ b/src/components/dex/UserOrders/components/RequestActionCell/index.tsx
@@ -0,0 +1,124 @@
+import Link from 'next/link';
+import Decimal from 'decimal.js';
+import { useState, useContext } from 'react';
+import { Store } from '@/store/store-reducer';
+import { useAlert } from '@/hook/useAlert';
+import { applyOrder, confirmTransaction } from '@/utils/methods';
+import { confirmIonicSwap, ionicSwap } from '@/utils/wallet';
+import { updateAutoClosedNotification } from '@/store/actions';
+import { notationToString } from '@/utils/utils';
+import ActionBtn from '@/components/UI/ActionBtn';
+import { RequestActionCellProps } from './types';
+
+export default function RequestActionCell({
+ type = 'request',
+ row,
+ pairData,
+ onAfter,
+ connectedOrder,
+ userOrders,
+}: RequestActionCellProps) {
+ const [loading, setLoading] = useState(false);
+ const { state, dispatch } = useContext(Store);
+ const { setAlertState, setAlertSubtitle } = useAlert();
+
+ const _connectedOrder =
+ connectedOrder ?? userOrders?.find((o) => o.id === row.connected_order_id);
+
+ const alertErr = (subtitle: string) => {
+ setAlertState('error');
+ setAlertSubtitle(subtitle);
+ setTimeout(() => {
+ setAlertState(null);
+ setAlertSubtitle('');
+ }, 3000);
+ };
+
+ const onClick = async () => {
+ setLoading(true);
+
+ let result: { success: boolean } | null = null;
+
+ try {
+ if (row.id) {
+ updateAutoClosedNotification(dispatch, [
+ ...state.closed_notifications,
+ parseInt(String(row.id), 10),
+ ]);
+ }
+
+ if (row.transaction) {
+ if (!row.hex_raw_proposal) {
+ alertErr('Invalid transaction data received');
+ return;
+ }
+ const confirmSwapResult = await confirmIonicSwap(row.hex_raw_proposal);
+ if (confirmSwapResult.data?.error?.code === -7) {
+ alertErr('Insufficient funds');
+ return;
+ }
+ if (!confirmSwapResult.data?.result) {
+ alertErr('Companion responded with an error');
+ return;
+ }
+ result = await confirmTransaction(row.id);
+ } else {
+ const firstCurrencyId = pairData?.first_currency.asset_id;
+ const secondCurrencyId = pairData?.second_currency.asset_id;
+ if (!(firstCurrencyId && secondCurrencyId)) {
+ alertErr('Invalid transaction data received');
+ return;
+ }
+ if (!_connectedOrder) return;
+
+ const leftDecimal = new Decimal(row.left);
+ const priceDecimal = new Decimal(row.price);
+
+ const params = {
+ destinationAssetID: row.type === 'buy' ? secondCurrencyId : firstCurrencyId,
+ destinationAssetAmount: notationToString(
+ row.type === 'buy'
+ ? leftDecimal.mul(priceDecimal).toString()
+ : leftDecimal.toString(),
+ ),
+ currentAssetID: row.type === 'buy' ? firstCurrencyId : secondCurrencyId,
+ currentAssetAmount: notationToString(
+ row.type === 'buy'
+ ? leftDecimal.toString()
+ : leftDecimal.mul(priceDecimal).toString(),
+ ),
+ destinationAddress: row.user.address,
+ };
+
+ const createSwapResult = await ionicSwap(params);
+ const hex = createSwapResult?.data?.result?.hex_raw_proposal;
+
+ if (createSwapResult?.data?.error?.code === -7) {
+ alertErr('Insufficient funds');
+ return;
+ }
+ if (!hex) {
+ alertErr('Companion responded with an error');
+ return;
+ }
+
+ result = await applyOrder({ ...row, hex_raw_proposal: hex });
+ }
+ } finally {
+ setLoading(false);
+ }
+
+ if (!result) return;
+ if (!result.success) {
+ alertErr('Server responded with an error');
+ return;
+ }
+ await onAfter();
+ };
+
+ return (
+ onClick()}>
+ {type === 'request' ? 'Request' : 'Accept'}
+
+ );
+}
diff --git a/src/components/dex/UserOrders/components/RequestActionCell/types.ts b/src/components/dex/UserOrders/components/RequestActionCell/types.ts
new file mode 100644
index 0000000..76d9f46
--- /dev/null
+++ b/src/components/dex/UserOrders/components/RequestActionCell/types.ts
@@ -0,0 +1,12 @@
+import ApplyTip from '@/interfaces/common/ApplyTip';
+import OrderRow from '@/interfaces/common/OrderRow';
+import PairData from '@/interfaces/common/PairData';
+
+export interface RequestActionCellProps {
+ type?: 'request' | 'accept';
+ row: ApplyTip;
+ pairData: PairData | null;
+ onAfter: () => Promise;
+ connectedOrder?: OrderRow;
+ userOrders?: OrderRow[];
+}
diff --git a/src/components/dex/UserOrders/components/TotalUsdCell/index.tsx b/src/components/dex/UserOrders/components/TotalUsdCell/index.tsx
new file mode 100644
index 0000000..2f494a7
--- /dev/null
+++ b/src/components/dex/UserOrders/components/TotalUsdCell/index.tsx
@@ -0,0 +1,18 @@
+import { useMemo } from 'react';
+import Decimal from 'decimal.js';
+import { notationToString, formatDollarValue } from '@/utils/utils';
+import { TotalUsdCellProps } from './types';
+
+export default function TotalUsdCell({ amount, price, secondAssetUsdPrice }: TotalUsdCellProps) {
+ const total = useMemo(
+ () => new Decimal(amount || 0).mul(new Decimal(price || 0)),
+ [amount, price],
+ );
+ const usd = secondAssetUsdPrice ? total.mul(secondAssetUsdPrice).toFixed(2) : undefined;
+
+ return (
+
+ {notationToString(total.toString())} ~ ${usd && formatDollarValue(usd)}
+
+ );
+}
diff --git a/src/components/dex/UserOrders/components/TotalUsdCell/types.ts b/src/components/dex/UserOrders/components/TotalUsdCell/types.ts
new file mode 100644
index 0000000..b09e12c
--- /dev/null
+++ b/src/components/dex/UserOrders/components/TotalUsdCell/types.ts
@@ -0,0 +1,5 @@
+export interface TotalUsdCellProps {
+ amount: string | number;
+ price: string | number;
+ secondAssetUsdPrice?: number;
+}
diff --git a/src/components/dex/UserOrders/index.tsx b/src/components/dex/UserOrders/index.tsx
index e580296..008d241 100644
--- a/src/components/dex/UserOrders/index.tsx
+++ b/src/components/dex/UserOrders/index.tsx
@@ -2,16 +2,28 @@ import { classes } from '@/utils/utils';
import ContentPreloader from '@/components/UI/ContentPreloader/ContentPreloader';
import useUpdateUser from '@/hook/useUpdateUser';
import EmptyMessage from '@/components/UI/EmptyMessage';
+import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
+import GenericTable from '@/components/default/GenericTable';
+import ActionBtn from '@/components/UI/ActionBtn';
+import { getUserOrders, getUserPendings } from '@/utils/methods';
+import UserPendingType from '@/interfaces/common/UserPendingType';
+import { Store } from '@/store/store-reducer';
+import { UserOrderData } from '@/interfaces/responses/orders/GetUserOrdersRes';
+import { useAlert } from '@/hook/useAlert';
+import Alert from '@/components/UI/Alert/Alert';
+import { tabsType, UserOrdersProps } from './types';
import styles from './styles.module.scss';
-import { UserOrdersProps } from './types';
-import MyOrdersRow from './components/MyOrdersRow';
-import MyOrdersApplyRow from './components/MyOrdersApplyRow';
+import {
+ buildApplyTipsColumns,
+ buildMyRequestsColumns,
+ buildOrderHistoryColumns,
+ buildUserColumns,
+} from './columns';
const UserOrders = ({
userOrders,
applyTips,
myOrdersLoading,
- loggedIn,
ordersType,
setOrdersType,
handleCancelAllOrders,
@@ -23,110 +35,271 @@ const UserOrders = ({
fetchTrades,
pairData,
}: UserOrdersProps) => {
+ const { state } = useContext(Store);
+ const loggedIn = !!state.wallet?.connected;
+ const { setAlertState, setAlertSubtitle, alertState, alertSubtitle } = useAlert();
+
const fetchUser = useUpdateUser();
- const firstCurrencyName = pairData?.first_currency?.name || '';
- const secondCurrencyName = pairData?.second_currency?.name || '';
+ const suitables = applyTips.filter((s) => !s.transaction);
+ const offers = applyTips.filter((s) => s.transaction);
+ const [userRequests, setUserRequests] = useState([]);
+ const [ordersHistory, setOrdersHistory] = useState([]);
+ useEffect(() => {
+ (async () => {
+ const requestsData = await getUserPendings();
+
+ if (requestsData.success) {
+ setUserRequests(requestsData.data);
+ }
+ })();
+
+ (async () => {
+ const result = await getUserOrders();
+
+ if (!result.success) {
+ setAlertState('error');
+ setAlertSubtitle('Error loading orders data');
+ await new Promise((resolve) => setTimeout(resolve, 2000));
+ setAlertState(null);
+ setAlertSubtitle('');
+ return;
+ }
+
+ const filteredOrdersHistory = result.data
+ .filter((s) => s.pair_id === pairData?.id)
+ .filter((s) => s.status === 'finished');
+
+ fetchUser();
+
+ setOrdersHistory(filteredOrdersHistory);
+ })();
+ }, [userOrders, applyTips]);
+
+ const firstCurrencyName = pairData?.first_currency?.name ?? '';
+ const secondCurrencyName = pairData?.second_currency?.name ?? '';
+
+ const onAfter = useCallback(async () => {
+ await updateOrders();
+ await updateUserOrders();
+ await fetchUser();
+ await fetchTrades();
+ }, [updateOrders, updateUserOrders, fetchUser, fetchTrades]);
+
+ const offersCountByOrderId = useMemo(() => {
+ const map = new Map();
+ for (const tip of applyTips) {
+ const key = String(tip.connected_order_id);
+ map.set(key, (map.get(key) ?? 0) + 1);
+ }
+ return map;
+ }, [applyTips]);
+
+ const columnsOpened = useMemo(
+ () =>
+ buildUserColumns({
+ firstCurrencyName,
+ secondCurrencyName,
+ secondAssetUsdPrice,
+ offersCountByOrderId,
+ onAfter,
+ }),
+ [firstCurrencyName, secondCurrencyName, secondAssetUsdPrice, offersCountByOrderId, onAfter],
+ );
+
+ const columnsSuitables = useMemo(
+ () =>
+ buildApplyTipsColumns({
+ type: 'suitables',
+ firstCurrencyName,
+ secondCurrencyName,
+ matrixAddresses,
+ secondAssetUsdPrice,
+ userOrders,
+ pairData,
+ onAfter,
+ }),
+ [
+ firstCurrencyName,
+ secondCurrencyName,
+ matrixAddresses,
+ secondAssetUsdPrice,
+ userOrders,
+ pairData,
+ onAfter,
+ ],
+ );
+
+ const columnsMyRequests = useMemo(
+ () =>
+ buildMyRequestsColumns({
+ firstCurrencyName,
+ secondCurrencyName,
+ onAfter,
+ }),
+ [firstCurrencyName, secondCurrencyName, onAfter],
+ );
+
+ const columnsOffers = useMemo(
+ () =>
+ buildApplyTipsColumns({
+ type: 'offers',
+ firstCurrencyName,
+ secondCurrencyName,
+ matrixAddresses,
+ secondAssetUsdPrice,
+ userOrders,
+ pairData,
+ onAfter,
+ }),
+ [
+ firstCurrencyName,
+ secondCurrencyName,
+ matrixAddresses,
+ secondAssetUsdPrice,
+ userOrders,
+ pairData,
+ onAfter,
+ ],
+ );
+
+ const columnsOrderHistory = useMemo(
+ () =>
+ buildOrderHistoryColumns({
+ firstCurrencyName,
+ secondCurrencyName,
+ secondAssetUsdPrice,
+ }),
+ [firstCurrencyName, secondCurrencyName, secondAssetUsdPrice],
+ );
+
+ const renderTable = () => {
+ switch (ordersType) {
+ case 'opened':
+ return (
+ r.id}
+ emptyMessage="No orders"
+ />
+ );
+ case 'suitable':
+ return (
+ r.id}
+ emptyMessage="No suitables"
+ />
+ );
+ case 'requests':
+ return (
+ r.id}
+ emptyMessage="No requests"
+ />
+ );
+ case 'offers':
+ return (
+ r.id}
+ emptyMessage="No offers"
+ />
+ );
+ case 'history':
+ return (
+ r.id}
+ emptyMessage="No data"
+ />
+ );
+ default:
+ return null;
+ }
+ };
+
+ const tabsData: tabsType[] = [
+ {
+ title: 'Open Orders',
+ type: 'opened',
+ length: userOrders.length,
+ },
+ {
+ title: 'Suitable',
+ type: 'suitable',
+ length: suitables.length,
+ },
+ {
+ title: 'My requests',
+ type: 'requests',
+ length: userRequests.length,
+ },
+ {
+ title: 'Offers',
+ type: 'offers',
+ length: offers.length,
+ },
+ {
+ title: 'Order history',
+ type: 'history',
+ length: ordersHistory.length,
+ },
+ ];
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- | Alias |
- Price ({secondCurrencyName}) |
- Amount ({firstCurrencyName}) |
- Total ({secondCurrencyName}) |
- Offers |
- |
-
-
-
-
- {!myOrdersLoading && loggedIn && !!userOrders.length && (
-
-
-
- {userOrders.map((e) => (
-
- ))}
-
-
-
- {!!applyTips.length && (
-
-
- {applyTips.map((e) => (
-
- ))}
-
-
- )}
+ <>
+
+
+
+ {tabsData.map((tab) => (
+
+ ))}
- )}
+
+ {ordersType === 'opened' && userOrders.length > 0 && (
+
+ Cancel all
+
+ )}
+
+
+ {!myOrdersLoading && loggedIn && renderTable()}
{myOrdersLoading && loggedIn &&
}
-
{!loggedIn &&
}
-
- {loggedIn && !userOrders.length && !myOrdersLoading && (
-
- )}
-
+
+ {alertState && (
+
setAlertState(null)}
+ />
+ )}
+ >
);
};
diff --git a/src/components/dex/UserOrders/styles.module.scss b/src/components/dex/UserOrders/styles.module.scss
index cb2a2c5..a5f9d1e 100644
--- a/src/components/dex/UserOrders/styles.module.scss
+++ b/src/components/dex/UserOrders/styles.module.scss
@@ -1,22 +1,29 @@
.userOrders {
+ position: relative;
width: 100%;
padding: 5px;
background: var(--window-bg-color);
border: 1px solid var(--delimiter-color);
border-radius: 10px;
+ min-height: 310px;
- @media screen and (max-width: 1440px) {
- width: 520px;
+ &__body {
+ position: relative;
+ padding: 10px;
+ padding-top: 5px;
+ padding-bottom: 20px;
+ height: 260px;
+ overflow: auto;
}
&__header {
- border-bottom: 1px solid var(--delimiter-color);
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
- padding: 10px;
+ padding-top: 10px;
padding-bottom: 0;
+ border-bottom: 1px solid var(--delimiter-color);
&_nav {
display: flex;
@@ -24,37 +31,24 @@
gap: 22px;
.navItem {
+ cursor: pointer;
padding-bottom: 7px;
position: relative;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
- font-size: 16px;
+ font-size: 14px;
+ font-weight: 500;
border-bottom: 2px solid transparent;
- font-weight: 600;
background-color: transparent;
- cursor: pointer;
-
- span {
- top: -10px;
- right: -15px;
- position: absolute;
- display: grid;
- place-content: center;
- color: #fff;
- background-color: #ff6767;
- width: 15px;
- height: 15px;
- font-size: 10px;
- font-weight: 700;
- border-radius: 50%;
- }
+ color: #1f8feb;
&.active {
+ color: var(--text-color);
border-color: #1f8feb;
}
&:hover {
- color: #1f8feb;
+ border-color: #1f8feb;
}
}
}
@@ -63,128 +57,62 @@
position: absolute;
right: 5px;
top: 5px;
- z-index: 10;
- cursor: pointer;
- background-color: #1f8feb33;
- color: #1f8feb;
- font-size: 14px;
- font-weight: 500;
- padding: 6px 10px;
- border-radius: 8px;
-
- &:hover {
- background-color: #1f8feb43;
- }
+ z-index: 2;
}
}
table {
width: 100%;
+ border-spacing: 0;
+ border-collapse: collapse;
+ table-layout: fixed;
thead {
- display: flex;
- width: 100%;
- padding-inline: 10px;
- padding-bottom: 5px;
- margin-top: 10px;
+ th {
+ min-width: 100px;
+ font-size: 11px;
+ font-weight: 700;
+ text-align: left;
+ color: var(--table-th-color);
+ padding: 6px 10px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
- tr {
- width: 100%;
- display: flex;
- justify-content: space-between;
-
- th {
- min-width: 100px;
- font-size: 11px;
- font-weight: 700;
- text-align: left;
- color: var(--table-th-color);
-
- &:last-child {
- text-align: right;
- min-width: 50px;
- }
+ &:last-child {
+ text-align: right;
+ min-width: 50px;
}
}
}
tbody {
- a {
- display: block;
- text-align: right;
- font-size: 12px;
- font-weight: 400;
- }
- }
- }
+ td {
+ position: relative;
- &__body {
- padding: 10px;
- padding-bottom: 20px;
- height: 300px;
- overflow: auto;
-
- table {
- width: 100%;
-
- &.apply {
- border-top: 1px solid #1f8feb40;
- border-bottom: 1px solid #1f8feb40;
- background-color: var(--blur-color) !important;
-
- tr {
- &:not(:last-child) {
- border-bottom: 1px solid var(--delimiter-color);
- }
- }
- }
-
- tbody {
- display: flex;
- flex-direction: column;
-
- &.incoming {
- tr {
- &:nth-child(even) {
- background-color: var(--table-even-bg);
- }
- }
- }
-
- tr {
- display: flex;
- align-items: center;
- justify-content: space-between;
+ > p {
width: 100%;
- padding: 4px 0;
+ font-size: 12px;
+ font-weight: 400;
- td {
- position: relative;
- min-width: 100px;
-
- &:last-child {
- min-width: 50px;
+ &:first-child {
+ &::before {
+ content: '';
+ pointer-events: none;
+ position: absolute;
+ left: 0;
+ top: 50%;
+ transform: translateY(-50%);
+ height: 50%;
+ width: 2px;
+ background-color: var(--direction-color);
}
+ }
- @media screen and (max-width: 1440px) {
- min-width: 0;
-
- &:last-child {
- min-width: 0;
- }
- }
-
- > p {
- width: 100%;
- font-size: 12px;
- font-weight: 400;
-
- > span {
- line-height: 1;
- color: var(--font-dimmed-color);
- font-size: 11px;
- }
- }
+ > span {
+ line-height: 1;
+ color: var(--font-dimmed-color);
+ font-size: 11px;
}
}
}
@@ -212,7 +140,6 @@
top: 30px;
left: 5%;
background-color: var(--trade-table-tooltip);
-
z-index: 9999;
&__text {
diff --git a/src/components/dex/UserOrders/types.ts b/src/components/dex/UserOrders/types.ts
index b67eb69..92aaf97 100644
--- a/src/components/dex/UserOrders/types.ts
+++ b/src/components/dex/UserOrders/types.ts
@@ -4,14 +4,21 @@ import OrderRow from '@/interfaces/common/OrderRow';
import PairData from '@/interfaces/common/PairData';
import { Dispatch, ForwardedRef, SetStateAction } from 'react';
+export type OrderType = 'opened' | 'suitable' | 'requests' | 'offers' | 'history';
+
+export type tabsType = {
+ title: string;
+ type: OrderType;
+ length: number;
+};
+
export interface UserOrdersProps {
orderListRef: ForwardedRef;
userOrders: OrderRow[];
applyTips: ApplyTip[];
myOrdersLoading: boolean;
- loggedIn: boolean;
- ordersType: 'opened' | 'history';
- setOrdersType: Dispatch>;
+ ordersType: OrderType;
+ setOrdersType: Dispatch>;
handleCancelAllOrders: () => void;
secondAssetUsdPrice: number | undefined;
updateOrders: () => Promise;
diff --git a/src/hook/useTradeInit.ts b/src/hook/useTradeInit.ts
index f3e2732..08c7a99 100644
--- a/src/hook/useTradeInit.ts
+++ b/src/hook/useTradeInit.ts
@@ -17,7 +17,6 @@ const useTradeInit = ({ pairData, pairStats }: useTradeInitParams) => {
firstCurrencyName: pairData?.first_currency?.name || '',
secondCurrencyName: pairData?.second_currency?.name || '',
};
- const loggedIn = !!state.wallet?.connected;
const assets = state.wallet?.connected ? state.wallet?.assets || [] : [];
const balance = assets.find((e) => e.ticker === currencyNames.firstCurrencyName)?.balance;
@@ -57,7 +56,6 @@ const useTradeInit = ({ pairData, pairStats }: useTradeInitParams) => {
firstAssetLink,
secondAssetLink,
secondAssetUsdPrice,
- loggedIn,
balance,
buyForm,
sellForm,
diff --git a/src/interfaces/common/UserPendingType.ts b/src/interfaces/common/UserPendingType.ts
new file mode 100644
index 0000000..bc51be8
--- /dev/null
+++ b/src/interfaces/common/UserPendingType.ts
@@ -0,0 +1,12 @@
+interface UserPendingType {
+ id: number;
+ amount: string;
+ buy_order_id: number;
+ sell_order_id: number;
+ creator: 'sell' | 'buy';
+ hex_raw_proposal: string;
+ status: string;
+ timestamp: string;
+}
+
+export default UserPendingType;
diff --git a/src/pages/dex/trading/[id].tsx b/src/pages/dex/trading/[id].tsx
index fa1f7c3..829b827 100644
--- a/src/pages/dex/trading/[id].tsx
+++ b/src/pages/dex/trading/[id].tsx
@@ -30,6 +30,7 @@ import useFilteredData from '@/hook/useFilteredData';
import useTradeInit from '@/hook/useTradeInit';
import useMatrixAddresses from '@/hook/useMatrixAddresses';
import takeOrderClick from '@/utils/takeOrderClick';
+import { OrderType } from '@/components/dex/UserOrders/types';
const CHART_OPTIONS = [{ name: 'Zano Chart' }, { name: 'Trading View', disabled: true }];
const DEFAULT_CHART = CHART_OPTIONS[0];
@@ -50,7 +51,7 @@ function Trading() {
const [myOrdersLoading, setMyOrdersLoading] = useState(true);
const [ordersBuySell, setOrdersBuySell] = useState(buySellValues[0]);
const [tradesType, setTradesType] = useState<'all' | 'my'>('all');
- const [ordersType, setOrdersType] = useState<'opened' | 'history'>('opened');
+ const [ordersType, setOrdersType] = useState('opened');
const [pairStats, setPairStats] = useState(null);
const [applyTips, setApplyTips] = useState([]);
const matrixAddresses = useMatrixAddresses(ordersHistory);
@@ -63,7 +64,6 @@ function Trading() {
secondAssetLink,
secondAssetUsdPrice,
balance,
- loggedIn,
pairRateUsd,
} = useTradeInit({ pairData, pairStats });
@@ -204,7 +204,6 @@ function Trading() {
userOrders={userOrders}
applyTips={applyTips}
myOrdersLoading={myOrdersLoading}
- loggedIn={loggedIn}
ordersType={ordersType}
setOrdersType={setOrdersType}
handleCancelAllOrders={handleCancelAllOrders}
@@ -215,35 +214,34 @@ function Trading() {
fetchTrades={fetchTrades}
pairData={pairData}
/>
+
+
+ {['buy', 'sell'].map((type) => {
+ const isBuy = type === 'buy';
+ const form = isBuy ? buyForm : sellForm;
-
- {['buy', 'sell'].map((type) => {
- const isBuy = type === 'buy';
- const form = isBuy ? buyForm : sellForm;
-
- return (
-
- );
- })}
-
+ return (
+
+ );
+ })}
{alertState && (
diff --git a/src/styles/Trading.module.scss b/src/styles/Trading.module.scss
index aeb178e..f0a30d4 100644
--- a/src/styles/Trading.module.scss
+++ b/src/styles/Trading.module.scss
@@ -48,7 +48,6 @@
display: flex;
gap: 20px;
margin-bottom: 40px;
- height: 405px;
&_createOrders {
display: flex;
diff --git a/src/styles/globals.scss b/src/styles/globals.scss
index 601f3f7..97e3749 100644
--- a/src/styles/globals.scss
+++ b/src/styles/globals.scss
@@ -233,9 +233,8 @@ svg {
.orders-scroll::-webkit-scrollbar {
width: 6px;
-
+ height: 6px;
background-color: transparent;
- background-clip: padding-box;
}
.orders-scroll::-webkit-scrollbar-thumb {
diff --git a/src/utils/methods.ts b/src/utils/methods.ts
index 3ea4880..476bec0 100644
--- a/src/utils/methods.ts
+++ b/src/utils/methods.ts
@@ -320,14 +320,6 @@ export async function getChatChunk(
.then((res) => res.data);
}
-export async function getZanoPrice() {
- return axios
- .get(
- 'https://explorer.zano.org/api/price?asset_id=d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a',
- )
- .then((res) => res.data);
-}
-
export async function getTrades(pairId: string) {
return axios
.post(`/api/orders/get-trades`, {
@@ -336,6 +328,22 @@ export async function getTrades(pairId: string) {
.then((res) => res.data);
}
+export async function getUserPendings() {
+ return axios
+ .post('/api/transactions/get-my-pending', {
+ token: sessionStorage.getItem('token'),
+ })
+ .then((res) => res.data);
+}
+
+export async function getZanoPrice() {
+ return axios
+ .get(
+ 'https://explorer.zano.org/api/price?asset_id=d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a',
+ )
+ .then((res) => res.data);
+}
+
export async function getMatrixAddresses(addresses: string[]) {
try {
const { data } = await axios.post('https://messenger.zano.org/api/get-addresses', {
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
index 31c32e6..0daf700 100644
--- a/src/utils/utils.ts
+++ b/src/utils/utils.ts
@@ -144,6 +144,21 @@ export function formatTime(ts: string | number) {
return date.toLocaleTimeString('ru-RU', { hour12: false });
}
+export function formatTimestamp(ms: number | string) {
+ if (Number.isNaN(Number(ms))) {
+ return 0;
+ }
+
+ const date = new Date(Number(ms));
+ const YYYY = date.getFullYear();
+ const MM = String(date.getMonth() + 1).padStart(2, '0');
+ const DD = String(date.getDate()).padStart(2, '0');
+ const HH = String(date.getHours()).padStart(2, '0');
+ const mm = String(date.getMinutes()).padStart(2, '0');
+ const ss = String(date.getSeconds()).padStart(2, '0');
+ return `${YYYY}-${MM}-${DD} ${HH}:${mm}:${ss}`;
+}
+
export function classes(...classes: (string | boolean | undefined)[]): string {
// boolean for constructions like [predicate] && [className]
return classes.filter((className) => className).join(' ');