refactor(converter): shared page layout + reusable conversion logic/UI

Pages:
- add WalletLayout route (WalletHeader + main + Footer via <Outlet/>),
  wrap converter/swap/bridge/transactions; thin pages, drop duplicated shell CSS
- extract SwapBridgeTabs shared between swap/bridge pages

Converter reuse (FSD layers, no widget->widget imports):
- move commission tiers to entities/commission (+ CommissionTable ui)
- shared calc hook features/payment/model/useCurrencyConversion;
  useConverterSection becomes thin wrapper; HomePage Converter reuses it
- move ConvertField/DirectionSwapButton to shared/ui; delete dead useConverter

Tooling:
- add eslint.config.js (ESLint 9 flat config); fix no-explicit-any in WalletPage

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-05-30 14:39:53 +03:00
parent bdc8bd3d93
commit 9b1d6ffb5d
45 changed files with 684 additions and 1129 deletions

View File

@@ -1,52 +1,12 @@
.page {
display: flex;
flex-direction: column;
min-height: 100vh;
background: var(--bg-deep);
}
.tabs {
display: flex;
gap: 8px;
padding: 24px 28px 0;
}
.tab {
padding: 10px 24px;
border-radius: 10px;
font-size: 14px;
font-weight: 700;
cursor: pointer;
border: none;
font-family: var(--font-sans);
letter-spacing: 0.5px;
transition: all 0.2s;
}
.active {
background: linear-gradient(135deg, var(--grad-edge), var(--grad-center));
color: var(--text-primary);
}
.inactive {
background: rgba(255, 255, 255, 0.06);
color: var(--text-secondary);
}
.inactive:hover {
color: var(--text-primary);
}
.main {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 32px 20px 48px;
.content {
display: flex;
flex-direction: column;
align-items: center;
padding: 32px 20px 48px;
}
@media (max-width: 650px) {
.main {
padding: 32px 20px;
}
.content {
padding: 32px 20px;
}
}

View File

@@ -1,37 +1,14 @@
import { useNavigate } from 'react-router-dom'
import { Footer } from '@widgets/footer'
import { BridgeForm } from '@widgets/bridge-form'
import { WalletHeader } from '@widgets/wallet-header'
import { ROUTES } from '@shared/config/routes'
import { SwapBridgeTabs } from '@widgets/swap-bridge-tabs'
import styles from './BridgePage.module.css'
export function BridgePage() {
const navigate = useNavigate()
return (
<div className={styles.page}>
<WalletHeader />
<div className={styles.tabs}>
<button
className={`${styles.tab} ${styles.inactive}`}
onClick={() => navigate(ROUTES.SWAP)}
>
СВОП
</button>
<button
className={`${styles.tab} ${styles.active}`}
onClick={() => navigate(ROUTES.BRIDGE)}
>
БРИДЖ
</button>
</div>
<main className={styles.main}>
<>
<SwapBridgeTabs active="bridge" />
<div className={styles.content}>
<BridgeForm />
</main>
<Footer />
</div>
</div>
</>
)
}

View File

@@ -1,21 +0,0 @@
.page {
min-height: 100vh;
display: flex;
flex-direction: column;
background: var(--bg-deep);
}
.main {
flex: 1;
padding: 28px 32px 40px;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
@media (max-width: 900px) {
.main {
padding: 20px 16px 32px;
}
}

View File

@@ -1,16 +1,5 @@
import { ConverterSection } from '@widgets/converter-page'
import { Footer } from '@widgets/footer'
import { WalletHeader } from '@widgets/wallet-header'
import styles from './ConverterPage.module.css'
export function ConverterPage() {
return (
<div className={styles.page}>
<WalletHeader />
<main className={styles.main}>
<ConverterSection />
</main>
<Footer />
</div>
)
return <ConverterSection />
}

View File

@@ -1,52 +1,12 @@
.page {
display: flex;
flex-direction: column;
min-height: 100vh;
background: var(--bg-deep);
}
.tabs {
display: flex;
gap: 8px;
padding: 24px 28px 0;
}
.tab {
padding: 10px 24px;
border-radius: 10px;
font-size: 14px;
font-weight: 700;
cursor: pointer;
border: none;
font-family: var(--font-sans);
letter-spacing: 0.5px;
transition: all 0.2s;
}
.active {
background: linear-gradient(135deg, var(--grad-edge), var(--grad-center));
color: var(--text-primary);
}
.inactive {
background: rgba(255, 255, 255, 0.06);
color: var(--text-secondary);
}
.inactive:hover {
color: var(--text-primary);
}
.main {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 32px 20px 48px;
.content {
display: flex;
flex-direction: column;
align-items: center;
padding: 32px 20px 48px;
}
@media (max-width: 650px) {
.main {
padding: 32px 20px;
}
}
.content {
padding: 32px 20px;
}
}

View File

@@ -1,37 +1,14 @@
import { useNavigate } from 'react-router-dom'
import { Footer } from '@widgets/footer'
import { SwapForm } from '@widgets/swap-form'
import { WalletHeader } from '@widgets/wallet-header'
import { ROUTES } from '@shared/config/routes'
import { SwapBridgeTabs } from '@widgets/swap-bridge-tabs'
import styles from './SwapPage.module.css'
export function SwapPage() {
const navigate = useNavigate()
return (
<div className={styles.page}>
<WalletHeader />
<div className={styles.tabs}>
<button
className={`${styles.tab} ${styles.active}`}
onClick={() => navigate(ROUTES.SWAP)}
>
СВОП
</button>
<button
className={`${styles.tab} ${styles.inactive}`}
onClick={() => navigate(ROUTES.BRIDGE)}
>
БРИДЖ
</button>
</div>
<main className={styles.main}>
<>
<SwapBridgeTabs active="swap" />
<div className={styles.content}>
<SwapForm />
</main>
<Footer />
</div>
</div>
</>
)
}

View File

@@ -1,12 +1,4 @@
.page {
min-height: 100vh;
display: flex;
flex-direction: column;
background: var(--bg-deep);
}
.main {
flex: 1;
.inner {
padding: 28px 32px 40px;
max-width: 1200px;
width: 100%;
@@ -36,7 +28,7 @@
}
@media (max-width: 900px) {
.main {
.inner {
padding: 20px 16px 32px;
}

View File

@@ -1,18 +1,12 @@
import { WalletHeader } from '@widgets/wallet-header'
import { Footer } from '@widgets/footer'
import { TransactionsList } from '@widgets/transactions-list'
import styles from './TransactionsPage.module.css'
export function TransactionsPage() {
return (
<div className={styles.page}>
<WalletHeader />
<main className={styles.main}>
<div className={styles.glow} />
<h1 className={styles.title}>Транзакции</h1>
<TransactionsList />
</main>
<Footer />
<div className={styles.inner}>
<div className={styles.glow} />
<h1 className={styles.title}>Транзакции</h1>
<TransactionsList />
</div>
)
}

View File

@@ -16,7 +16,7 @@ export function WalletPage() {
const navigate = useNavigate()
const { chain: chainParam } = useParams<{ chain?: string }>()
const noWallet = (portfolioError as any)?.error?.includes('No wallets')
const noWallet = (portfolioError as { error?: string } | null)?.error?.includes('No wallets')
if (isLoading) return null
if (isError) return <div className={styles.error}>Произошла ошибка. Попробуйте обновить страницу.</div>