add: fixes

This commit is contained in:
AzizbekFayziyev 2025-08-26 16:58:03 +05:00
parent 947880da9e
commit b0a5482146
18 changed files with 139 additions and 238 deletions

View file

@ -1 +1 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1.688 6.75h13.5l-3.376-3.375M16.313 11.25h-13.5l3.374 3.375" stroke="#1F8FEB" stroke-width="1.5" stroke-linecap="square"></path></svg>
<svg viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1.688 6.75h13.5l-3.376-3.375M16.313 11.25h-13.5l3.374 3.375" stroke="#1F8FEB" stroke-width="1.5" stroke-linecap="square"></path></svg>

Before

Width:  |  Height:  |  Size: 239 B

After

Width:  |  Height:  |  Size: 216 B

View file

@ -1,5 +1,3 @@
import { Dispatch, SetStateAction } from 'react';
export type tabsType = {
title: string;
type: string;
@ -8,6 +6,6 @@ export type tabsType = {
export interface TabsProps {
value: tabsType;
setValue: Dispatch<SetStateAction<tabsType>>;
setValue: (_next: tabsType) => void;
data: tabsType[];
}

View file

@ -1,6 +1,6 @@
import { useEffect, useRef, useState } from 'react';
import Tooltip from '@/components/UI/Tooltip/Tooltip';
import { cutAddress } from '@/utils/utils';
import { classes, cutAddress } from '@/utils/utils';
import MatrixConnectionBadge from '@/components/dex/MatrixConnectionBadge';
import BadgeStatus from '@/components/dex/BadgeStatus';
import { createPortal } from 'react-dom';
@ -12,6 +12,7 @@ export default function AliasCell({
address,
matrixAddresses,
isInstant,
isSm,
max = 12,
}: AliasCellProps) {
const display = alias ? cutAddress(alias, max) : 'no alias';
@ -59,6 +60,7 @@ export default function AliasCell({
userAdress={address}
userAlias={alias}
matrixAddresses={matrixAddresses}
isSm={isSm}
/>
{isInstant && (

View file

@ -5,5 +5,6 @@ export interface AliasCellProps {
address?: string;
matrixAddresses: MatrixAddress[];
isInstant?: boolean;
isSm?: boolean;
max?: number;
}

View file

@ -1,82 +0,0 @@
import React from 'react';
import { classes, formatTime } from '@/utils/utils';
import ContentPreloader from '@/components/UI/ContentPreloader/ContentPreloader';
import EmptyMessage from '@/components/UI/EmptyMessage';
import styles from './styles.module.scss';
import { AllTradesProps } from './types';
const AllTrades = ({
setTradesType,
tradesType,
filteredTrades,
tradesLoading,
currencyNames,
}: AllTradesProps) => {
return (
<div className={styles.allTrades}>
<div className={styles.allTrades__header}>
<button
onClick={() => setTradesType('all')}
className={classes(
styles.allTrades__header_btn,
tradesType === 'all' && styles.active,
)}
>
All Trades
</button>
<button
onClick={() => setTradesType('my')}
className={classes(
styles.allTrades__header_btn,
tradesType === 'my' && styles.active,
)}
>
My Trades
</button>
</div>
<div className={styles.allTrades__content}>
<table>
<thead>
<tr>
<th>Price ({currencyNames.secondCurrencyName})</th>
<th>Amount ({currencyNames.firstCurrencyName})</th>
<th>Time</th>
</tr>
</thead>
{!tradesLoading && !!filteredTrades.length && (
<tbody className="orders-scroll">
{filteredTrades.map((trade) => (
<tr key={trade.id}>
<td>
<p
style={{
color: trade.id % 2 === 0 ? '#16D1D6' : '#FF6767',
}}
>
{trade.price}
</p>
</td>
<td>
<p>{trade.amount}</p>
</td>
<td>
<p>{formatTime(trade.timestamp)}</p>
</td>
</tr>
))}
</tbody>
)}
</table>
{!filteredTrades.length && !tradesLoading && <EmptyMessage text="No trades" />}
{tradesLoading && <ContentPreloader style={{ marginTop: 40 }} />}
</div>
</div>
);
};
export default AllTrades;

View file

@ -1,116 +0,0 @@
.allTrades {
max-width: 415px;
width: 100%;
padding: 5px;
background: var(--window-bg-color);
border: 1px solid var(--delimiter-color);
border-radius: 10px;
@media screen and (max-width: 1480px) {
max-width: 340px;
}
&__header {
border-bottom: 1px solid var(--delimiter-color);
display: flex;
align-items: center;
gap: 22px;
padding: 10px;
padding-bottom: 0;
&_btn {
padding-bottom: 7px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
font-size: 16px;
border-bottom: 2px solid transparent;
font-weight: 600;
background-color: transparent !important;
cursor: pointer;
&.active {
border-color: #1f8feb;
}
&:hover {
color: #1f8feb;
}
}
}
&__content {
display: flex;
flex-direction: column;
padding-top: 10px;
table {
width: 100%;
thead {
display: flex;
width: 100%;
padding-inline: 10px;
margin-bottom: 9px;
tr {
width: 100%;
display: flex;
justify-content: space-between;
th {
min-width: 80px;
font-size: 11px;
font-weight: 700;
text-align: start;
color: var(--table-th-color);
&:last-child {
text-align: right;
}
}
}
}
tbody {
height: 29dvh;
min-height: 265px;
max-height: 380px;
display: flex;
flex-direction: column;
overflow: auto;
padding: 10px;
padding-bottom: 20px;
tr {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 4px 0;
&:nth-child(even) {
background-color: var(--table-even-bg);
}
td {
position: relative;
&:last-child {
> p {
text-align: right;
}
}
> p {
min-width: 80px;
width: 100%;
font-size: 12px;
font-weight: 400;
}
}
}
}
}
}
}

View file

@ -1,15 +0,0 @@
import { Trade } from '@/interfaces/responses/trades/GetTradeRes';
import { Dispatch, SetStateAction } from 'react';
type tradeType = 'all' | 'my';
export interface AllTradesProps {
setTradesType: Dispatch<SetStateAction<tradeType>>;
tradesType: tradeType;
filteredTrades: Trade[];
tradesLoading: boolean;
currencyNames: {
firstCurrencyName: string;
secondCurrencyName: string;
};
}

View file

@ -14,6 +14,7 @@ import infoIcon from '@/assets/images/UI/info_alert_icon.svg';
import Image from 'next/image';
import { useAlert } from '@/hook/useAlert';
import { buySellValues } from '@/constants';
import { usePathname, useSearchParams } from 'next/navigation';
import styles from './styles.module.scss';
import LabeledInput from './components/LabeledInput';
@ -40,6 +41,8 @@ function InputPanelItem(props: InputPanelItemProps) {
const { state } = useContext(Store);
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const { setAlertState, setAlertSubtitle } = useAlert();
const [creatingState, setCreatingState] = useState(false);
const { firstCurrencyName, secondCurrencyName } = currencyNames;
@ -47,6 +50,16 @@ function InputPanelItem(props: InputPanelItemProps) {
const [hasImmediateMatch, setHasImmediateMatch] = useState(false);
const isBuy = buySellState?.code === 'buy';
function goToSuitableTab() {
const params = new URLSearchParams(searchParams.toString());
params.set('tab', 'suitable');
router.replace(`${pathname}?${params.toString()}`, undefined, {
shallow: true,
scroll: false,
});
}
function resetForm() {
setPriceFunction('');
setAmountFunction('');
@ -126,6 +139,7 @@ function InputPanelItem(props: InputPanelItemProps) {
className={styles.applyAlert__button}
onClick={() => {
scrollToOrderList();
goToSuitableTab();
setHasImmediateMatch(false);
}}
>

View file

@ -10,7 +10,7 @@ function MatrixConnectionBadge({
userAdress,
userAlias,
matrixAddresses,
className,
isSm,
}: MatrixConnectionBadgeProps) {
const hasConnection = (address: string) =>
matrixAddresses.some((item) => item.address === address && item.registered);
@ -44,7 +44,7 @@ function MatrixConnectionBadge({
if (!userAdress || !hasConnection(userAdress)) return <></>;
return (
<div className={classes(styles.badge, className)}>
<div className={classes(styles.badge, isSm && styles.sm)}>
<p
ref={anchorRef}
onClick={(e) => {

View file

@ -1,9 +1,21 @@
.badge {
position: relative;
&.sm {
.badge__link svg {
width: 14px;
height: 14px;
}
}
&__link {
margin-top: 4px;
cursor: pointer;
svg {
width: 16px;
height: 16px;
}
}
&__tooltip {

View file

@ -1,7 +1,7 @@
import MatrixAddress from '@/interfaces/common/MatrixAddress';
export interface MatrixConnectionBadgeProps {
className?: string;
isSm?: boolean;
userAdress?: string;
userAlias?: string;
matrixAddresses: MatrixAddress[];

View file

@ -19,6 +19,7 @@ export function buildOrderPoolColumns({
className: 'alias',
cell: (row) => (
<AliasCell
isSm={true}
alias={row.user?.alias}
address={row.user?.address}
matrixAddresses={matrixAddresses}
@ -47,6 +48,7 @@ export function buildOrderPoolColumns({
key: 'total',
header: <>Total ({secondCurrencyName})</>,
width: '80px',
align: 'right',
cell: (row) => <TotalUsdCell amount={row.left} price={row.price} fixed={8} />,
},
];
@ -75,6 +77,7 @@ export function buildTradesColumns({
},
{
key: 'time',
align: 'right',
header: <>Time</>,
width: '80px',
cell: (row) => <p>{formatTimestamp(row.timestamp)}</p>,

View file

@ -145,12 +145,12 @@ const OrdersPool = (props: OrdersPoolProps) => {
<>
{!tradesLoading ? (
<GenericTable
className={styles.ordersPool__content_orders}
className={classes(styles.ordersPool__content_orders, styles.full)}
tableClassName={styles.table}
tbodyClassName={styles.table__body}
theadClassName={styles.table__header}
columns={tradeOrders}
data={trades}
data={trades.slice(0, 100)}
getRowKey={(r) => r.id}
/>
) : (

View file

@ -60,6 +60,10 @@
width: 100%;
height: 410px;
&.full {
height: 480px;
}
.table {
width: 100%;
@ -229,4 +233,4 @@
font-size: 11px;
font-weight: 400;
}
}
}

View file

@ -1,4 +1,3 @@
import { classes } from '@/utils/utils';
import ContentPreloader from '@/components/UI/ContentPreloader/ContentPreloader';
import useUpdateUser from '@/hook/useUpdateUser';
import EmptyMessage from '@/components/UI/EmptyMessage';
@ -13,6 +12,9 @@ import { useAlert } from '@/hook/useAlert';
import Alert from '@/components/UI/Alert/Alert';
import { tabsType } from '@/components/UI/Tabs/types';
import Tabs from '@/components/UI/Tabs';
import { createOrderSorter } from '@/utils/utils';
import ApplyTip from '@/interfaces/common/ApplyTip';
import { useQuerySyncedTab } from '@/hook/useQuerySyncedTab';
import { UserOrdersProps } from './types';
import styles from './styles.module.scss';
import {
@ -80,9 +82,15 @@ const UserOrders = ({
],
);
const [ordersType, setOrdersType] = useState<tabsType>(tabsData[0]);
const { active: ordersType, setActiveTab } = useQuerySyncedTab({
tabs: tabsData,
defaultType: 'opened',
queryKey: 'tab',
});
useEffect(() => {
if (!loggedIn) return;
(async () => {
const requestsData = await getUserPendings();
@ -204,6 +212,11 @@ const UserOrders = ({
[firstCurrencyName, secondCurrencyName, secondAssetUsdPrice],
);
const sortedSuitables = createOrderSorter<ApplyTip>({
getPrice: (e) => e.price,
getSide: (e) => e.type,
});
const renderTable = () => {
switch (ordersType.type) {
case 'opened':
@ -221,7 +234,7 @@ const UserOrders = ({
<GenericTable
className={styles.userOrders__body}
columns={columnsSuitables}
data={suitables}
data={suitables.sort(sortedSuitables)}
getRowKey={(r) => r.id}
emptyMessage="No suitables"
/>
@ -265,7 +278,11 @@ const UserOrders = ({
<>
<div ref={orderListRef} className={styles.userOrders}>
<div className={styles.userOrders__header}>
<Tabs data={tabsData} value={ordersType} setValue={setOrdersType} />
<Tabs
data={tabsData}
value={ordersType}
setValue={(t) => setActiveTab(t.type)}
/>
{ordersType?.type === 'opened' && userOrders.length > 0 && (
<ActionBtn

View file

@ -0,0 +1,71 @@
import { useEffect, useMemo, useState } from 'react';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
type TabType = string;
type TabItem<T extends TabType> = {
title: string;
type: T;
length?: number;
};
type Options<T extends TabType> = {
tabs: TabItem<T>[];
queryKey?: string;
defaultType?: T;
replace?: boolean;
};
export function useQuerySyncedTab<T extends TabType>({
tabs,
queryKey = 'tab',
defaultType,
replace = true,
}: Options<T>) {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const urlValue = searchParams.get(queryKey) as T | null;
const initialTab = useMemo(() => {
const fallback = (defaultType ?? tabs[0]?.type) as T;
if (!urlValue) return tabs.find((t) => t.type === fallback) ?? tabs[0];
return (
tabs.find((t) => t.type === urlValue) ??
tabs.find((t) => t.type === fallback) ??
tabs[0]
);
}, [tabs, urlValue, defaultType]);
const [active, setActive] = useState<TabItem<T>>(initialTab);
useEffect(() => {
setActive(initialTab);
}, [initialTab]);
const setActiveTab = (next: TabItem<T> | T) => {
const nextType = (typeof next === 'string' ? next : next.type) as T;
const found = tabs.find((t) => t.type === nextType);
if (found) setActive(found);
const params = new URLSearchParams(searchParams.toString());
const def = (defaultType ?? tabs[0]?.type) as T;
if (nextType === def) {
params.delete(queryKey);
} else {
params.set(queryKey, nextType);
}
const url = params.toString() ? `${pathname}?${params}` : pathname;
if (replace) {
router.replace(url, { scroll: false });
} else {
router.push(url, { scroll: false });
}
};
return { active, setActiveTab };
}

View file

@ -23,7 +23,6 @@ import TradingHeader from '@/components/dex/TradingHeader';
import UserOrders from '@/components/dex/UserOrders';
import OrdersPool from '@/components/dex/OrdersPool';
import CandleChart from '@/components/dex/CandleChart';
import AllTrades from '@/components/dex/AllTrades';
import { useSocketListeners } from '@/hook/useSocketListeners';
import { useTradingData } from '@/hook/useTradingData';
import useFilteredData from '@/hook/useFilteredData';
@ -216,13 +215,6 @@ function Trading() {
onAfter={onAfter}
/>
</div>
{/* <AllTrades
currencyNames={currencyNames}
filteredTrades={filteredTrades}
setTradesType={setTradesType}
tradesLoading={tradesLoading}
tradesType={tradesType}
/> */}
</div>
<div className={styles.trading__info}>

View file

@ -154,10 +154,10 @@ export function formatTimestamp(ms: number | string) {
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 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}`;
return `${hh}:${mm}:${ss} ${DD}-${MM}-${YYYY}`;
}
export function classes(...classes: (string | boolean | undefined)[]): string {