mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-10 22:36:58 +08:00
bugfix dashboard empty state (#709)
This commit is contained in:
126
web/src/App.tsx
126
web/src/App.tsx
@@ -101,11 +101,12 @@ function App() {
|
||||
// };
|
||||
|
||||
// 获取trader列表(仅在用户登录时)
|
||||
const { data: traders } = useSWR<TraderInfo[]>(
|
||||
const { data: traders, error: tradersError } = useSWR<TraderInfo[]>(
|
||||
user && token ? 'traders' : null,
|
||||
api.getTraders,
|
||||
{
|
||||
refreshInterval: 10000,
|
||||
shouldRetryOnError: false, // 避免在后端未运行时无限重试
|
||||
}
|
||||
)
|
||||
|
||||
@@ -360,8 +361,14 @@ function App() {
|
||||
lastUpdate={lastUpdate}
|
||||
language={language}
|
||||
traders={traders}
|
||||
tradersError={tradersError}
|
||||
selectedTraderId={selectedTraderId}
|
||||
onTraderSelect={setSelectedTraderId}
|
||||
onNavigateToTraders={() => {
|
||||
window.history.pushState({}, '', '/traders')
|
||||
setRoute('/traders')
|
||||
setCurrentPage('traders')
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</main>
|
||||
@@ -426,13 +433,17 @@ function TraderDetailsPage({
|
||||
lastUpdate,
|
||||
language,
|
||||
traders,
|
||||
tradersError,
|
||||
selectedTraderId,
|
||||
onTraderSelect,
|
||||
onNavigateToTraders,
|
||||
}: {
|
||||
selectedTrader?: TraderInfo
|
||||
traders?: TraderInfo[]
|
||||
tradersError?: Error
|
||||
selectedTraderId?: string
|
||||
onTraderSelect: (traderId: string) => void
|
||||
onNavigateToTraders: () => void
|
||||
status?: SystemStatus
|
||||
account?: AccountInfo
|
||||
positions?: Position[]
|
||||
@@ -441,6 +452,119 @@ function TraderDetailsPage({
|
||||
lastUpdate: string
|
||||
language: Language
|
||||
}) {
|
||||
// If API failed with error, show empty state (likely backend not running)
|
||||
if (tradersError) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[60vh]">
|
||||
<div className="text-center max-w-md mx-auto px-6">
|
||||
{/* Icon */}
|
||||
<div
|
||||
className="w-24 h-24 mx-auto mb-6 rounded-full flex items-center justify-center"
|
||||
style={{
|
||||
background: 'rgba(240, 185, 11, 0.1)',
|
||||
border: '2px solid rgba(240, 185, 11, 0.3)',
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
className="w-12 h-12"
|
||||
style={{ color: '#F0B90B' }}
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<h2 className="text-2xl font-bold mb-3" style={{ color: '#EAECEF' }}>
|
||||
{t('dashboardEmptyTitle', language)}
|
||||
</h2>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-base mb-6" style={{ color: '#848E9C' }}>
|
||||
{t('dashboardEmptyDescription', language)}
|
||||
</p>
|
||||
|
||||
{/* CTA Button */}
|
||||
<button
|
||||
onClick={onNavigateToTraders}
|
||||
className="px-6 py-3 rounded-lg font-semibold transition-all hover:scale-105 active:scale-95"
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #F0B90B 0%, #FCD535 100%)',
|
||||
color: '#0B0E11',
|
||||
boxShadow: '0 4px 12px rgba(240, 185, 11, 0.3)',
|
||||
}}
|
||||
>
|
||||
{t('goToTradersPage', language)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// If traders is loaded and empty, show empty state
|
||||
if (traders && traders.length === 0) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[60vh]">
|
||||
<div className="text-center max-w-md mx-auto px-6">
|
||||
{/* Icon */}
|
||||
<div
|
||||
className="w-24 h-24 mx-auto mb-6 rounded-full flex items-center justify-center"
|
||||
style={{
|
||||
background: 'rgba(240, 185, 11, 0.1)',
|
||||
border: '2px solid rgba(240, 185, 11, 0.3)',
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
className="w-12 h-12"
|
||||
style={{ color: '#F0B90B' }}
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<h2 className="text-2xl font-bold mb-3" style={{ color: '#EAECEF' }}>
|
||||
{t('dashboardEmptyTitle', language)}
|
||||
</h2>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-base mb-6" style={{ color: '#848E9C' }}>
|
||||
{t('dashboardEmptyDescription', language)}
|
||||
</p>
|
||||
|
||||
{/* CTA Button */}
|
||||
<button
|
||||
onClick={onNavigateToTraders}
|
||||
className="px-6 py-3 rounded-lg font-semibold transition-all hover:scale-105 active:scale-95"
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #F0B90B 0%, #FCD535 100%)',
|
||||
color: '#0B0E11',
|
||||
boxShadow: '0 4px 12px rgba(240, 185, 11, 0.3)',
|
||||
}}
|
||||
>
|
||||
{t('goToTradersPage', language)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// If traders is still loading or selectedTrader is not ready, show skeleton
|
||||
if (!selectedTrader) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
|
||||
Reference in New Issue
Block a user