mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-10 06:20:58 +08:00
fix: improve frontend UX and fix OKX close position
Frontend improvements: - Replace window.location.reload() with SWR mutate() for data refresh - Replace native alert/confirm with toast notifications (confirmToast, notify) - Add loading skeletons to AITradersPage and EquityChart - Fix flash of empty state during initial load OKX fixes: - Fix proxy issue in Docker by using explicit no-proxy function - Fix CloseShort sz parameter error - ensure quantity is always positive - Fix GetPositions to return absolute value for positionAmt
This commit is contained in:
@@ -87,7 +87,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
|
||||
oiTopUrl: '',
|
||||
})
|
||||
|
||||
const { data: traders, mutate: mutateTraders } = useSWR<TraderInfo[]>(
|
||||
const { data: traders, mutate: mutateTraders, isLoading: isTradersLoading } = useSWR<TraderInfo[]>(
|
||||
user && token ? 'traders' : null,
|
||||
api.getTraders,
|
||||
{ refreshInterval: 5000 }
|
||||
@@ -1047,7 +1047,31 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{traders && traders.length > 0 ? (
|
||||
{isTradersLoading ? (
|
||||
/* Loading Skeleton */
|
||||
<div className="space-y-3 md:space-y-4">
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex flex-col md:flex-row md:items-center justify-between p-3 md:p-4 rounded gap-3 md:gap-4 animate-pulse"
|
||||
style={{ background: '#0B0E11', border: '1px solid #2B3139' }}
|
||||
>
|
||||
<div className="flex items-center gap-3 md:gap-4">
|
||||
<div className="w-10 h-10 md:w-12 md:h-12 rounded-full skeleton"></div>
|
||||
<div className="min-w-0 space-y-2">
|
||||
<div className="skeleton h-5 w-32"></div>
|
||||
<div className="skeleton h-3 w-24"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 md:gap-4">
|
||||
<div className="skeleton h-6 w-16"></div>
|
||||
<div className="skeleton h-6 w-16"></div>
|
||||
<div className="skeleton h-8 w-20"></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : traders && traders.length > 0 ? (
|
||||
<div className="space-y-3 md:space-y-4">
|
||||
{traders.map((trader) => (
|
||||
<div
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import { api } from '../lib/api'
|
||||
import { useLanguage } from '../contexts/LanguageContext'
|
||||
import { t } from '../i18n/translations'
|
||||
import { confirmToast } from '../lib/notify'
|
||||
import { DecisionCard } from './DecisionCard'
|
||||
import type {
|
||||
BacktestStatusPayload,
|
||||
@@ -308,12 +309,17 @@ export function BacktestPage() {
|
||||
|
||||
const handleDeleteRun = async () => {
|
||||
if (!selectedRunId) return
|
||||
if (
|
||||
typeof window !== 'undefined' &&
|
||||
!window.confirm(tr('toasts.confirmDelete', { id: selectedRunId }))
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
const confirmed = await confirmToast(
|
||||
tr('toasts.confirmDelete', { id: selectedRunId }),
|
||||
{
|
||||
title: language === 'zh' ? '确认删除' : 'Confirm Delete',
|
||||
okText: language === 'zh' ? '删除' : 'Delete',
|
||||
cancelText: language === 'zh' ? '取消' : 'Cancel',
|
||||
}
|
||||
)
|
||||
if (!confirmed) return
|
||||
|
||||
try {
|
||||
await api.deleteBacktestRun(selectedRunId)
|
||||
setToast({ text: tr('toasts.deleteSuccess'), tone: 'success' })
|
||||
|
||||
@@ -41,7 +41,7 @@ export function EquityChart({ traderId, embedded = false }: EquityChartProps) {
|
||||
const { user, token } = useAuth()
|
||||
const [displayMode, setDisplayMode] = useState<'dollar' | 'percent'>('dollar')
|
||||
|
||||
const { data: history, error } = useSWR<EquityPoint[]>(
|
||||
const { data: history, error, isLoading } = useSWR<EquityPoint[]>(
|
||||
user && token && traderId ? `equity-history-${traderId}` : null,
|
||||
() => api.getEquityHistory(traderId),
|
||||
{
|
||||
@@ -61,6 +61,22 @@ export function EquityChart({ traderId, embedded = false }: EquityChartProps) {
|
||||
}
|
||||
)
|
||||
|
||||
// Loading state - show skeleton
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className={embedded ? 'p-6' : 'binance-card p-6'}>
|
||||
{!embedded && (
|
||||
<h3 className="text-lg font-semibold mb-6" style={{ color: '#EAECEF' }}>
|
||||
{t('accountEquityCurve', language)}
|
||||
</h3>
|
||||
)}
|
||||
<div className="animate-pulse">
|
||||
<div className="skeleton h-64 w-full rounded"></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className={embedded ? 'p-6' : 'binance-card p-6'}>
|
||||
|
||||
Reference in New Issue
Block a user