bugfix dashboard empty state (#709)

This commit is contained in:
Ember
2025-11-09 14:44:42 +08:00
committed by GitHub
parent b8b8feb5fb
commit a4d3cb41f1
3 changed files with 140 additions and 6 deletions

View File

@@ -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">

View File

@@ -28,6 +28,7 @@ import {
AlertTriangle,
BookOpen,
HelpCircle,
Radio,
} from 'lucide-react'
// 获取友好的AI模型名称
@@ -702,7 +703,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
</div>
</div>
<div className="flex gap-2 md:gap-3 w-full md:w-auto overflow-x-auto flex-wrap md:flex-nowrap">
<div className="flex gap-2 md:gap-3 w-full md:w-auto overflow-hidden flex-wrap md:flex-nowrap">
<button
onClick={handleAddModel}
className="px-3 md:px-4 py-2 rounded text-xs md:text-sm font-semibold transition-all hover:scale-105 flex items-center gap-1 md:gap-2 whitespace-nowrap"
@@ -731,14 +732,15 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
<button
onClick={() => setShowSignalSourceModal(true)}
className="px-3 md:px-4 py-2 rounded text-xs md:text-sm font-semibold transition-all hover:scale-105 whitespace-nowrap"
className="px-3 md:px-4 py-2 rounded text-xs md:text-sm font-semibold transition-all hover:scale-105 flex items-center gap-1 md:gap-2 whitespace-nowrap"
style={{
background: '#2B3139',
color: '#EAECEF',
border: '1px solid #474D57',
}}
>
📡 {t('signalSource', language)}
<Radio className="w-3 h-3 md:w-4 md:h-4" />
{t('signalSource', language)}
</button>
<button
@@ -793,7 +795,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
<strong>{t('solutions', language)}</strong>
</p>
<ul className="list-disc list-inside space-y-1 ml-2 mt-1">
<li>"📡 {t('signalSource', language)}"API地址</li>
<li>"{t('signalSource', language)}"API地址</li>
<li>"使用币种池""使用OI Top"</li>
<li></li>
</ul>
@@ -1292,7 +1294,7 @@ function SignalSourceModal({
style={{ background: '#1E2329' }}
>
<h3 className="text-xl font-bold mb-4" style={{ color: '#EAECEF' }}>
📡 {t('signalSourceConfig', language)}
{t('signalSourceConfig', language)}
</h3>
<form onSubmit={handleSubmit} className="space-y-4">

View File

@@ -146,6 +146,10 @@ export const translations = {
currentTraders: 'Current Traders',
noTraders: 'No AI Traders',
createFirstTrader: 'Create your first AI trader to get started',
dashboardEmptyTitle: 'No Traders Configured',
dashboardEmptyDescription:
"You haven't created any AI traders yet. Create your first trader to start automated trading.",
goToTradersPage: 'Go to Traders Page',
configureModelsFirst: 'Please configure AI models first',
configureExchangesFirst: 'Please configure exchanges first',
configureModelsAndExchangesFirst:
@@ -915,6 +919,10 @@ export const translations = {
currentTraders: '当前交易员',
noTraders: '暂无AI交易员',
createFirstTrader: '创建您的第一个AI交易员开始使用',
dashboardEmptyTitle: '暂无交易员',
dashboardEmptyDescription:
'您还未创建任何AI交易员创建您的第一个交易员以开始自动化交易。',
goToTradersPage: '前往交易员页面',
configureModelsFirst: '请先配置AI模型',
configureExchangesFirst: '请先配置交易所',
configureModelsAndExchangesFirst: '请先配置AI模型和交易所',