mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 09:58:22 +08:00
fix: improve mobile responsive layout for header and comparison chart
This is a partial fix for issue #311 mobile display problems. Changes in this commit: - Add responsive header layout with separate mobile/desktop views in App.tsx - Fix language selector visibility on mobile (no longer hidden by menu) - Add responsive breakpoints to ComparisonChart stats grid (2 cols on mobile, 4 on desktop) - Adjust padding and text sizes for mobile screens - Preserve all i18n (internationalization) functionality from upstream Note: Additional components (CompetitionPage, AITradersPage) will need similar mobile responsive improvements in follow-up commits. Related to #311 Co-Authored-By: tinkle-community <tinklefund@gmail.com>
This commit is contained in:
+114
-6
@@ -194,8 +194,9 @@ function App() {
|
||||
<div className="min-h-screen" style={{ background: '#0B0E11', color: '#EAECEF' }}>
|
||||
{/* Header - Binance Style */}
|
||||
<header className="glass sticky top-0 z-50 backdrop-blur-xl">
|
||||
<div className="max-w-[1920px] mx-auto px-6 py-4">
|
||||
<div className="relative flex items-center">
|
||||
<div className="max-w-[1920px] mx-auto px-3 md:px-6 py-4">
|
||||
{/* Desktop Layout */}
|
||||
<div className="hidden md:flex relative items-center">
|
||||
{/* Left - Logo and Title */}
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 flex items-center justify-center">
|
||||
@@ -210,7 +211,7 @@ function App() {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Center - Page Toggle (absolutely positioned) */}
|
||||
<div className="absolute left-1/2 transform -translate-x-1/2 flex gap-1 rounded p-1" style={{ background: '#1E2329' }}>
|
||||
<button
|
||||
@@ -244,7 +245,7 @@ function App() {
|
||||
{t('tradingPanel', language)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Right - Actions */}
|
||||
<div className="ml-auto flex items-center gap-3">
|
||||
|
||||
@@ -257,7 +258,7 @@ function App() {
|
||||
<span className="text-sm" style={{ color: '#EAECEF' }}>{user.email}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{/* Admin Mode Indicator */}
|
||||
{systemConfig?.admin_mode && (
|
||||
<div className="flex items-center gap-2 px-3 py-2 rounded" style={{ background: '#1E2329', border: '1px solid #2B3139' }}>
|
||||
@@ -302,11 +303,118 @@ function App() {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Layout */}
|
||||
<div className="flex md:hidden flex-col gap-3">
|
||||
{/* Top Row - Logo, Title and Language */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<img src="/icons/nofx.svg?v=2" alt="NOFX" className="w-7 h-7" />
|
||||
<div>
|
||||
<h1 className="text-base font-bold" style={{ color: '#EAECEF' }}>
|
||||
{t('appTitle', language)}
|
||||
</h1>
|
||||
<p className="text-xs mono" style={{ color: '#848E9C' }}>
|
||||
{t('subtitle', language)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Language Toggle - Right side on mobile */}
|
||||
<div className="flex gap-1 rounded p-0.5" style={{ background: '#1E2329' }}>
|
||||
<button
|
||||
onClick={() => setLanguage('zh')}
|
||||
className="px-2 py-1 rounded text-xs font-semibold transition-all"
|
||||
style={language === 'zh'
|
||||
? { background: '#F0B90B', color: '#000' }
|
||||
: { background: 'transparent', color: '#848E9C' }
|
||||
}
|
||||
>
|
||||
中文
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setLanguage('en')}
|
||||
className="px-2 py-1 rounded text-xs font-semibold transition-all"
|
||||
style={language === 'en'
|
||||
? { background: '#F0B90B', color: '#000' }
|
||||
: { background: 'transparent', color: '#848E9C' }
|
||||
}
|
||||
>
|
||||
EN
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Second Row - Page Toggle */}
|
||||
<div className="flex gap-1 rounded p-1" style={{ background: '#1E2329' }}>
|
||||
<button
|
||||
onClick={() => setCurrentPage('competition')}
|
||||
className={`flex-1 px-2 py-1.5 rounded text-xs font-semibold transition-all`}
|
||||
style={currentPage === 'competition'
|
||||
? { background: '#F0B90B', color: '#000' }
|
||||
: { background: 'transparent', color: '#848E9C' }
|
||||
}
|
||||
>
|
||||
{t('aiCompetition', language)}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setCurrentPage('traders')}
|
||||
className={`flex-1 px-2 py-1.5 rounded text-xs font-semibold transition-all`}
|
||||
style={currentPage === 'traders'
|
||||
? { background: '#F0B90B', color: '#000' }
|
||||
: { background: 'transparent', color: '#848E9C' }
|
||||
}
|
||||
>
|
||||
{t('aiTraders', language)}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setCurrentPage('trader')}
|
||||
className={`flex-1 px-2 py-1.5 rounded text-xs font-semibold transition-all`}
|
||||
style={currentPage === 'trader'
|
||||
? { background: '#F0B90B', color: '#000' }
|
||||
: { background: 'transparent', color: '#848E9C' }
|
||||
}
|
||||
>
|
||||
{t('tradingPanel', language)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Third Row - User Info and Logout */}
|
||||
<div className="flex items-center gap-2">
|
||||
{/* User Info or Admin Mode */}
|
||||
{!systemConfig?.admin_mode && user && (
|
||||
<div className="flex-1 flex items-center gap-2 px-2 py-1.5 rounded" style={{ background: '#1E2329', border: '1px solid #2B3139' }}>
|
||||
<div className="w-5 h-5 rounded-full flex items-center justify-center text-xs font-bold" style={{ background: '#F0B90B', color: '#000' }}>
|
||||
{user.email[0].toUpperCase()}
|
||||
</div>
|
||||
<span className="text-xs truncate" style={{ color: '#EAECEF' }}>{user.email}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{systemConfig?.admin_mode && (
|
||||
<div className="flex-1 flex items-center gap-2 px-2 py-1.5 rounded" style={{ background: '#1E2329', border: '1px solid #2B3139' }}>
|
||||
<Zap className="w-4 h-4" style={{ color: '#F0B90B' }} />
|
||||
<span className="text-xs font-semibold" style={{ color: '#F0B90B' }}>{t('adminMode', language)}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Logout Button */}
|
||||
{!systemConfig?.admin_mode && (
|
||||
<button
|
||||
onClick={logout}
|
||||
className="px-3 py-1.5 rounded text-xs font-semibold transition-all"
|
||||
style={{ background: 'rgba(246, 70, 93, 0.1)', color: '#F6465D', border: '1px solid rgba(246, 70, 93, 0.2)' }}
|
||||
>
|
||||
{t('logout', language)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="max-w-[1920px] mx-auto px-6 py-6">
|
||||
<main className="max-w-[1920px] mx-auto px-3 md:px-6 py-4 md:py-6">
|
||||
{currentPage === 'competition' ? (
|
||||
<CompetitionPage />
|
||||
) : currentPage === 'traders' ? (
|
||||
|
||||
@@ -313,24 +313,24 @@ export function ComparisonChart({ traders }: ComparisonChartProps) {
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="mt-6 grid grid-cols-4 gap-4 pt-5" style={{ borderTop: '1px solid #2B3139' }}>
|
||||
<div className="p-3 rounded transition-all hover:bg-opacity-50" style={{ background: 'rgba(240, 185, 11, 0.05)' }}>
|
||||
<div className="mt-6 grid grid-cols-2 md:grid-cols-4 gap-3 md:gap-4 pt-5" style={{ borderTop: '1px solid #2B3139' }}>
|
||||
<div className="p-2 md:p-3 rounded transition-all hover:bg-opacity-50" style={{ background: 'rgba(240, 185, 11, 0.05)' }}>
|
||||
<div className="text-xs mb-1 uppercase tracking-wider" style={{ color: '#848E9C' }}>{t('comparisonMode', language)}</div>
|
||||
<div className="text-base font-bold" style={{ color: '#EAECEF' }}>PnL %</div>
|
||||
<div className="text-sm md:text-base font-bold" style={{ color: '#EAECEF' }}>PnL %</div>
|
||||
</div>
|
||||
<div className="p-3 rounded transition-all hover:bg-opacity-50" style={{ background: 'rgba(240, 185, 11, 0.05)' }}>
|
||||
<div className="p-2 md:p-3 rounded transition-all hover:bg-opacity-50" style={{ background: 'rgba(240, 185, 11, 0.05)' }}>
|
||||
<div className="text-xs mb-1 uppercase tracking-wider" style={{ color: '#848E9C' }}>{t('dataPoints', language)}</div>
|
||||
<div className="text-base font-bold mono" style={{ color: '#EAECEF' }}>{t('count', language, {count: combinedData.length})}</div>
|
||||
<div className="text-sm md:text-base font-bold mono" style={{ color: '#EAECEF' }}>{t('count', language, {count: combinedData.length})}</div>
|
||||
</div>
|
||||
<div className="p-3 rounded transition-all hover:bg-opacity-50" style={{ background: 'rgba(240, 185, 11, 0.05)' }}>
|
||||
<div className="p-2 md:p-3 rounded transition-all hover:bg-opacity-50" style={{ background: 'rgba(240, 185, 11, 0.05)' }}>
|
||||
<div className="text-xs mb-1 uppercase tracking-wider" style={{ color: '#848E9C' }}>{t('currentGap', language)}</div>
|
||||
<div className="text-base font-bold mono" style={{ color: currentGap > 1 ? '#F0B90B' : '#EAECEF' }}>
|
||||
<div className="text-sm md:text-base font-bold mono" style={{ color: currentGap > 1 ? '#F0B90B' : '#EAECEF' }}>
|
||||
{currentGap.toFixed(2)}%
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-3 rounded transition-all hover:bg-opacity-50" style={{ background: 'rgba(240, 185, 11, 0.05)' }}>
|
||||
<div className="p-2 md:p-3 rounded transition-all hover:bg-opacity-50" style={{ background: 'rgba(240, 185, 11, 0.05)' }}>
|
||||
<div className="text-xs mb-1 uppercase tracking-wider" style={{ color: '#848E9C' }}>{t('displayRange', language)}</div>
|
||||
<div className="text-base font-bold mono" style={{ color: '#EAECEF' }}>
|
||||
<div className="text-sm md:text-base font-bold mono" style={{ color: '#EAECEF' }}>
|
||||
{combinedData.length > MAX_DISPLAY_POINTS
|
||||
? `${t('recent', language)} ${MAX_DISPLAY_POINTS}`
|
||||
: t('allData', language)}
|
||||
|
||||
Reference in New Issue
Block a user