add: fixes
This commit is contained in:
parent
947880da9e
commit
b0a5482146
18 changed files with 139 additions and 238 deletions
|
|
@ -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 |
|
|
@ -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[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 && (
|
||||
|
|
|
|||
|
|
@ -5,5 +5,6 @@ export interface AliasCellProps {
|
|||
address?: string;
|
||||
matrixAddresses: MatrixAddress[];
|
||||
isInstant?: boolean;
|
||||
isSm?: boolean;
|
||||
max?: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
@ -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);
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import MatrixAddress from '@/interfaces/common/MatrixAddress';
|
||||
|
||||
export interface MatrixConnectionBadgeProps {
|
||||
className?: string;
|
||||
isSm?: boolean;
|
||||
userAdress?: string;
|
||||
userAlias?: string;
|
||||
matrixAddresses: MatrixAddress[];
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -60,6 +60,10 @@
|
|||
width: 100%;
|
||||
height: 410px;
|
||||
|
||||
&.full {
|
||||
height: 480px;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
|
||||
|
|
@ -229,4 +233,4 @@
|
|||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
71
src/hook/useQuerySyncedTab.ts
Normal file
71
src/hook/useQuerySyncedTab.ts
Normal 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 };
|
||||
}
|
||||
|
|
@ -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}>
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue