mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-16 01:06:59 +08:00
feat: comprehensive FAQ rewrite with navigation fix
- Add HeaderBar to FAQ page for proper navigation - Rewrite FAQ with 9 categories and ~50 Q&A items - Add EN/ZH translations for all FAQ content - Remove China-specific references - Update AI model names (GPT-5.2) - Update description to include crypto and US stock markets
This commit is contained in:
379
web/src/App.tsx
379
web/src/App.tsx
@@ -59,18 +59,26 @@ function getModelDisplayName(modelId: string): string {
|
||||
}
|
||||
|
||||
// Helper function to get exchange display name from exchange ID (UUID)
|
||||
function getExchangeDisplayNameFromList(exchangeId: string | undefined, exchanges: Exchange[] | undefined): string {
|
||||
function getExchangeDisplayNameFromList(
|
||||
exchangeId: string | undefined,
|
||||
exchanges: Exchange[] | undefined
|
||||
): string {
|
||||
if (!exchangeId) return 'Unknown'
|
||||
const exchange = exchanges?.find(e => e.id === exchangeId)
|
||||
const exchange = exchanges?.find((e) => e.id === exchangeId)
|
||||
if (!exchange) return exchangeId.substring(0, 8).toUpperCase() + '...'
|
||||
const typeName = exchange.exchange_type?.toUpperCase() || exchange.name
|
||||
return exchange.account_name ? `${typeName} - ${exchange.account_name}` : typeName
|
||||
return exchange.account_name
|
||||
? `${typeName} - ${exchange.account_name}`
|
||||
: typeName
|
||||
}
|
||||
|
||||
// Helper function to get exchange type from exchange ID (UUID) - for TradingView charts
|
||||
function getExchangeTypeFromList(exchangeId: string | undefined, exchanges: Exchange[] | undefined): string {
|
||||
function getExchangeTypeFromList(
|
||||
exchangeId: string | undefined,
|
||||
exchanges: Exchange[] | undefined
|
||||
): string {
|
||||
if (!exchangeId) return 'BINANCE'
|
||||
const exchange = exchanges?.find(e => e.id === exchangeId)
|
||||
const exchange = exchanges?.find((e) => e.id === exchangeId)
|
||||
if (!exchange) return 'BINANCE' // Default to BINANCE for charts
|
||||
return exchange.exchange_type?.toUpperCase() || 'BINANCE'
|
||||
}
|
||||
@@ -288,7 +296,52 @@ function App() {
|
||||
return <RegisterPage />
|
||||
}
|
||||
if (route === '/faq') {
|
||||
return <FAQPage />
|
||||
return (
|
||||
<div
|
||||
className="min-h-screen"
|
||||
style={{ background: '#0B0E11', color: '#EAECEF' }}
|
||||
>
|
||||
<HeaderBar
|
||||
isLoggedIn={!!user}
|
||||
currentPage="faq"
|
||||
language={language}
|
||||
onLanguageChange={setLanguage}
|
||||
user={user}
|
||||
onLogout={logout}
|
||||
onPageChange={(page: Page) => {
|
||||
if (page === 'competition') {
|
||||
window.history.pushState({}, '', '/competition')
|
||||
setRoute('/competition')
|
||||
setCurrentPage('competition')
|
||||
} else if (page === 'traders') {
|
||||
window.history.pushState({}, '', '/traders')
|
||||
setRoute('/traders')
|
||||
setCurrentPage('traders')
|
||||
} else if (page === 'trader') {
|
||||
window.history.pushState({}, '', '/dashboard')
|
||||
setRoute('/dashboard')
|
||||
setCurrentPage('trader')
|
||||
} else if (page === 'faq') {
|
||||
window.history.pushState({}, '', '/faq')
|
||||
setRoute('/faq')
|
||||
} else if (page === 'backtest') {
|
||||
window.history.pushState({}, '', '/backtest')
|
||||
setRoute('/backtest')
|
||||
setCurrentPage('backtest')
|
||||
} else if (page === 'strategy') {
|
||||
window.history.pushState({}, '', '/strategy')
|
||||
setRoute('/strategy')
|
||||
setCurrentPage('strategy')
|
||||
} else if (page === 'debate') {
|
||||
window.history.pushState({}, '', '/debate')
|
||||
setRoute('/debate')
|
||||
setCurrentPage('debate')
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<FAQPage />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (route === '/reset-password') {
|
||||
return <ResetPasswordPage />
|
||||
@@ -453,7 +506,13 @@ function App() {
|
||||
/>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className={currentPage === 'debate' ? 'h-[calc(100vh-64px)] mt-16' : 'max-w-[1920px] mx-auto px-6 py-6 pt-24'}>
|
||||
<main
|
||||
className={
|
||||
currentPage === 'debate'
|
||||
? 'h-[calc(100vh-64px)] mt-16'
|
||||
: 'max-w-[1920px] mx-auto px-6 py-6 pt-24'
|
||||
}
|
||||
>
|
||||
{currentPage === 'competition' ? (
|
||||
<CompetitionPage />
|
||||
) : currentPage === 'traders' ? (
|
||||
@@ -498,101 +557,118 @@ function App() {
|
||||
</main>
|
||||
|
||||
{/* Footer - Hidden on debate page */}
|
||||
{currentPage !== 'debate' && <footer
|
||||
className="mt-16"
|
||||
style={{ borderTop: '1px solid #2B3139', background: '#181A20' }}
|
||||
>
|
||||
<div
|
||||
className="max-w-[1920px] mx-auto px-6 py-6 text-center text-sm"
|
||||
style={{ color: '#5E6673' }}
|
||||
{currentPage !== 'debate' && (
|
||||
<footer
|
||||
className="mt-16"
|
||||
style={{ borderTop: '1px solid #2B3139', background: '#181A20' }}
|
||||
>
|
||||
<p>{t('footerTitle', language)}</p>
|
||||
<p className="mt-1">{t('footerWarning', language)}</p>
|
||||
<div className="mt-4 flex items-center justify-center gap-3 flex-wrap">
|
||||
{/* GitHub */}
|
||||
<a
|
||||
href={OFFICIAL_LINKS.github}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-3 py-2 rounded text-sm font-semibold transition-all hover:scale-105"
|
||||
style={{
|
||||
background: '#1E2329',
|
||||
color: '#848E9C',
|
||||
border: '1px solid #2B3139',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.background = '#2B3139'
|
||||
e.currentTarget.style.color = '#EAECEF'
|
||||
e.currentTarget.style.borderColor = '#F0B90B'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.background = '#1E2329'
|
||||
e.currentTarget.style.color = '#848E9C'
|
||||
e.currentTarget.style.borderColor = '#2B3139'
|
||||
}}
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" />
|
||||
</svg>
|
||||
GitHub
|
||||
</a>
|
||||
{/* Twitter/X */}
|
||||
<a
|
||||
href={OFFICIAL_LINKS.twitter}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-3 py-2 rounded text-sm font-semibold transition-all hover:scale-105"
|
||||
style={{
|
||||
background: '#1E2329',
|
||||
color: '#848E9C',
|
||||
border: '1px solid #2B3139',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.background = '#2B3139'
|
||||
e.currentTarget.style.color = '#EAECEF'
|
||||
e.currentTarget.style.borderColor = '#1DA1F2'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.background = '#1E2329'
|
||||
e.currentTarget.style.color = '#848E9C'
|
||||
e.currentTarget.style.borderColor = '#2B3139'
|
||||
}}
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
||||
</svg>
|
||||
Twitter
|
||||
</a>
|
||||
{/* Telegram */}
|
||||
<a
|
||||
href={OFFICIAL_LINKS.telegram}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-3 py-2 rounded text-sm font-semibold transition-all hover:scale-105"
|
||||
style={{
|
||||
background: '#1E2329',
|
||||
color: '#848E9C',
|
||||
border: '1px solid #2B3139',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.background = '#2B3139'
|
||||
e.currentTarget.style.color = '#EAECEF'
|
||||
e.currentTarget.style.borderColor = '#0088cc'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.background = '#1E2329'
|
||||
e.currentTarget.style.color = '#848E9C'
|
||||
e.currentTarget.style.borderColor = '#2B3139'
|
||||
}}
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" />
|
||||
</svg>
|
||||
Telegram
|
||||
</a>
|
||||
<div
|
||||
className="max-w-[1920px] mx-auto px-6 py-6 text-center text-sm"
|
||||
style={{ color: '#5E6673' }}
|
||||
>
|
||||
<p>{t('footerTitle', language)}</p>
|
||||
<p className="mt-1">{t('footerWarning', language)}</p>
|
||||
<div className="mt-4 flex items-center justify-center gap-3 flex-wrap">
|
||||
{/* GitHub */}
|
||||
<a
|
||||
href={OFFICIAL_LINKS.github}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-3 py-2 rounded text-sm font-semibold transition-all hover:scale-105"
|
||||
style={{
|
||||
background: '#1E2329',
|
||||
color: '#848E9C',
|
||||
border: '1px solid #2B3139',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.background = '#2B3139'
|
||||
e.currentTarget.style.color = '#EAECEF'
|
||||
e.currentTarget.style.borderColor = '#F0B90B'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.background = '#1E2329'
|
||||
e.currentTarget.style.color = '#848E9C'
|
||||
e.currentTarget.style.borderColor = '#2B3139'
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" />
|
||||
</svg>
|
||||
GitHub
|
||||
</a>
|
||||
{/* Twitter/X */}
|
||||
<a
|
||||
href={OFFICIAL_LINKS.twitter}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-3 py-2 rounded text-sm font-semibold transition-all hover:scale-105"
|
||||
style={{
|
||||
background: '#1E2329',
|
||||
color: '#848E9C',
|
||||
border: '1px solid #2B3139',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.background = '#2B3139'
|
||||
e.currentTarget.style.color = '#EAECEF'
|
||||
e.currentTarget.style.borderColor = '#1DA1F2'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.background = '#1E2329'
|
||||
e.currentTarget.style.color = '#848E9C'
|
||||
e.currentTarget.style.borderColor = '#2B3139'
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
||||
</svg>
|
||||
Twitter
|
||||
</a>
|
||||
{/* Telegram */}
|
||||
<a
|
||||
href={OFFICIAL_LINKS.telegram}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-3 py-2 rounded text-sm font-semibold transition-all hover:scale-105"
|
||||
style={{
|
||||
background: '#1E2329',
|
||||
color: '#848E9C',
|
||||
border: '1px solid #2B3139',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.background = '#2B3139'
|
||||
e.currentTarget.style.color = '#EAECEF'
|
||||
e.currentTarget.style.borderColor = '#0088cc'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.background = '#1E2329'
|
||||
e.currentTarget.style.color = '#848E9C'
|
||||
e.currentTarget.style.borderColor = '#2B3139'
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" />
|
||||
</svg>
|
||||
Telegram
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>}
|
||||
</footer>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -633,7 +709,9 @@ function TraderDetailsPage({
|
||||
exchanges?: Exchange[]
|
||||
}) {
|
||||
const [closingPosition, setClosingPosition] = useState<string | null>(null)
|
||||
const [selectedChartSymbol, setSelectedChartSymbol] = useState<string | undefined>(undefined)
|
||||
const [selectedChartSymbol, setSelectedChartSymbol] = useState<
|
||||
string | undefined
|
||||
>(undefined)
|
||||
const [chartUpdateKey, setChartUpdateKey] = useState<number>(0)
|
||||
const chartSectionRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
@@ -641,9 +719,10 @@ function TraderDetailsPage({
|
||||
const handleClosePosition = async (symbol: string, side: string) => {
|
||||
if (!selectedTraderId) return
|
||||
|
||||
const confirmMsg = language === 'zh'
|
||||
? `确定要平仓 ${symbol} ${side === 'LONG' ? '多仓' : '空仓'} 吗?`
|
||||
: `Are you sure you want to close ${symbol} ${side === 'LONG' ? 'LONG' : 'SHORT'} position?`
|
||||
const confirmMsg =
|
||||
language === 'zh'
|
||||
? `确定要平仓 ${symbol} ${side === 'LONG' ? '多仓' : '空仓'} 吗?`
|
||||
: `Are you sure you want to close ${symbol} ${side === 'LONG' ? 'LONG' : 'SHORT'} position?`
|
||||
|
||||
const confirmed = await confirmToast(confirmMsg, {
|
||||
title: language === 'zh' ? '确认平仓' : 'Confirm Close',
|
||||
@@ -656,14 +735,21 @@ function TraderDetailsPage({
|
||||
setClosingPosition(symbol)
|
||||
try {
|
||||
await api.closePosition(selectedTraderId, symbol, side)
|
||||
notify.success(language === 'zh' ? '平仓成功' : 'Position closed successfully')
|
||||
notify.success(
|
||||
language === 'zh' ? '平仓成功' : 'Position closed successfully'
|
||||
)
|
||||
// 使用 SWR mutate 刷新数据而非重新加载页面
|
||||
await Promise.all([
|
||||
mutate(`positions-${selectedTraderId}`),
|
||||
mutate(`account-${selectedTraderId}`),
|
||||
])
|
||||
} catch (err: unknown) {
|
||||
const errorMsg = err instanceof Error ? err.message : (language === 'zh' ? '平仓失败' : 'Failed to close position')
|
||||
const errorMsg =
|
||||
err instanceof Error
|
||||
? err.message
|
||||
: language === 'zh'
|
||||
? '平仓失败'
|
||||
: 'Failed to close position'
|
||||
notify.error(errorMsg)
|
||||
} finally {
|
||||
setClosingPosition(null)
|
||||
@@ -828,7 +914,10 @@ function TraderDetailsPage({
|
||||
style={{ color: '#EAECEF' }}
|
||||
>
|
||||
<PunkAvatar
|
||||
seed={getTraderAvatar(selectedTrader.trader_id, selectedTrader.trader_name)}
|
||||
seed={getTraderAvatar(
|
||||
selectedTrader.trader_id,
|
||||
selectedTrader.trader_name
|
||||
)}
|
||||
size={48}
|
||||
className="rounded-lg"
|
||||
/>
|
||||
@@ -876,7 +965,7 @@ function TraderDetailsPage({
|
||||
>
|
||||
{getModelDisplayName(
|
||||
selectedTrader.ai_model.split('_').pop() ||
|
||||
selectedTrader.ai_model
|
||||
selectedTrader.ai_model
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
@@ -884,7 +973,10 @@ function TraderDetailsPage({
|
||||
<span>
|
||||
Exchange:{' '}
|
||||
<span className="font-semibold" style={{ color: '#EAECEF' }}>
|
||||
{getExchangeDisplayNameFromList(selectedTrader.exchange_id, exchanges)}
|
||||
{getExchangeDisplayNameFromList(
|
||||
selectedTrader.exchange_id,
|
||||
exchanges
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
<span>•</span>
|
||||
@@ -961,7 +1053,10 @@ function TraderDetailsPage({
|
||||
traderId={selectedTrader.trader_id}
|
||||
selectedSymbol={selectedChartSymbol}
|
||||
updateKey={chartUpdateKey}
|
||||
exchangeId={getExchangeTypeFromList(selectedTrader.exchange_id, exchanges)}
|
||||
exchangeId={getExchangeTypeFromList(
|
||||
selectedTrader.exchange_id,
|
||||
exchanges
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1004,25 +1099,46 @@ function TraderDetailsPage({
|
||||
<th className="px-1 pb-3 font-semibold text-gray-400 whitespace-nowrap text-center">
|
||||
{language === 'zh' ? '操作' : 'Action'}
|
||||
</th>
|
||||
<th className="px-1 pb-3 font-semibold text-gray-400 whitespace-nowrap text-right" title={t('entryPrice', language)}>
|
||||
<th
|
||||
className="px-1 pb-3 font-semibold text-gray-400 whitespace-nowrap text-right"
|
||||
title={t('entryPrice', language)}
|
||||
>
|
||||
{language === 'zh' ? '入场价' : 'Entry'}
|
||||
</th>
|
||||
<th className="px-1 pb-3 font-semibold text-gray-400 whitespace-nowrap text-right" title={t('markPrice', language)}>
|
||||
<th
|
||||
className="px-1 pb-3 font-semibold text-gray-400 whitespace-nowrap text-right"
|
||||
title={t('markPrice', language)}
|
||||
>
|
||||
{language === 'zh' ? '标记价' : 'Mark'}
|
||||
</th>
|
||||
<th className="px-1 pb-3 font-semibold text-gray-400 whitespace-nowrap text-right" title={t('quantity', language)}>
|
||||
<th
|
||||
className="px-1 pb-3 font-semibold text-gray-400 whitespace-nowrap text-right"
|
||||
title={t('quantity', language)}
|
||||
>
|
||||
{language === 'zh' ? '数量' : 'Qty'}
|
||||
</th>
|
||||
<th className="px-1 pb-3 font-semibold text-gray-400 whitespace-nowrap text-right" title={t('positionValue', language)}>
|
||||
<th
|
||||
className="px-1 pb-3 font-semibold text-gray-400 whitespace-nowrap text-right"
|
||||
title={t('positionValue', language)}
|
||||
>
|
||||
{language === 'zh' ? '价值' : 'Value'}
|
||||
</th>
|
||||
<th className="px-1 pb-3 font-semibold text-gray-400 whitespace-nowrap text-center" title={t('leverage', language)}>
|
||||
<th
|
||||
className="px-1 pb-3 font-semibold text-gray-400 whitespace-nowrap text-center"
|
||||
title={t('leverage', language)}
|
||||
>
|
||||
{language === 'zh' ? '杠杆' : 'Lev.'}
|
||||
</th>
|
||||
<th className="px-1 pb-3 font-semibold text-gray-400 whitespace-nowrap text-right" title={t('unrealizedPnL', language)}>
|
||||
<th
|
||||
className="px-1 pb-3 font-semibold text-gray-400 whitespace-nowrap text-right"
|
||||
title={t('unrealizedPnL', language)}
|
||||
>
|
||||
{language === 'zh' ? '未实现盈亏' : 'uPnL'}
|
||||
</th>
|
||||
<th className="px-1 pb-3 font-semibold text-gray-400 whitespace-nowrap text-right" title={t('liqPrice', language)}>
|
||||
<th
|
||||
className="px-1 pb-3 font-semibold text-gray-400 whitespace-nowrap text-right"
|
||||
title={t('liqPrice', language)}
|
||||
>
|
||||
{language === 'zh' ? '强平价' : 'Liq.'}
|
||||
</th>
|
||||
</tr>
|
||||
@@ -1037,7 +1153,10 @@ function TraderDetailsPage({
|
||||
setChartUpdateKey(Date.now())
|
||||
// Smooth scroll to chart with ref
|
||||
if (chartSectionRef.current) {
|
||||
chartSectionRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
chartSectionRef.current.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start',
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -1050,13 +1169,13 @@ function TraderDetailsPage({
|
||||
style={
|
||||
pos.side === 'long'
|
||||
? {
|
||||
background: 'rgba(14, 203, 129, 0.1)',
|
||||
color: '#0ECB81',
|
||||
}
|
||||
background: 'rgba(14, 203, 129, 0.1)',
|
||||
color: '#0ECB81',
|
||||
}
|
||||
: {
|
||||
background: 'rgba(246, 70, 93, 0.1)',
|
||||
color: '#F6465D',
|
||||
}
|
||||
background: 'rgba(246, 70, 93, 0.1)',
|
||||
color: '#F6465D',
|
||||
}
|
||||
}
|
||||
>
|
||||
{t(
|
||||
@@ -1069,12 +1188,17 @@ function TraderDetailsPage({
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation(); // Prevent row click
|
||||
handleClosePosition(pos.symbol, pos.side.toUpperCase())
|
||||
e.stopPropagation() // Prevent row click
|
||||
handleClosePosition(
|
||||
pos.symbol,
|
||||
pos.side.toUpperCase()
|
||||
)
|
||||
}}
|
||||
disabled={closingPosition === pos.symbol}
|
||||
className="btn-danger inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-semibold transition-all hover:scale-105 disabled:opacity-50 disabled:cursor-not-allowed mx-auto"
|
||||
title={language === 'zh' ? '平仓' : 'Close Position'}
|
||||
title={
|
||||
language === 'zh' ? '平仓' : 'Close Position'
|
||||
}
|
||||
>
|
||||
{closingPosition === pos.symbol ? (
|
||||
<Loader2 className="w-3 h-3 animate-spin" />
|
||||
@@ -1227,7 +1351,6 @@ function TraderDetailsPage({
|
||||
</div>
|
||||
{/* 右侧结束 */}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user