Refactor UI to light theme, move controls to header, and add collapsible sidebar
This commit is contained in:
105
App.tsx
105
App.tsx
@@ -6,7 +6,7 @@ import { BMSBasicInfo, BMSCellInfo, ConnectionState } from './types';
|
||||
import Dashboard from './components/Dashboard';
|
||||
import Settings from './components/Settings';
|
||||
import Terminal from './components/Terminal';
|
||||
import { LayoutDashboard, Settings as SettingsIcon, Usb, AlertCircle, RefreshCw } from 'lucide-react';
|
||||
import { LayoutDashboard, Settings as SettingsIcon, Usb, AlertCircle, RefreshCw, PanelRightClose, PanelRightOpen } from 'lucide-react';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [connectionState, setConnectionState] = useState<ConnectionState>(ConnectionState.DISCONNECTED);
|
||||
@@ -15,6 +15,7 @@ const App: React.FC = () => {
|
||||
const [hwVersion, setHwVersion] = useState<string | null>(null);
|
||||
const [activeTab, setActiveTab] = useState<'dashboard' | 'settings'>('dashboard');
|
||||
const [errorMsg, setErrorMsg] = useState<string | null>(null);
|
||||
const [isTerminalOpen, setIsTerminalOpen] = useState(true);
|
||||
|
||||
// Polling Logic
|
||||
useEffect(() => {
|
||||
@@ -131,15 +132,15 @@ const App: React.FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen w-full bg-gray-950 text-gray-100 overflow-hidden">
|
||||
<div className="flex h-screen w-full bg-gray-50 text-gray-900 overflow-hidden">
|
||||
|
||||
{/* Sidebar */}
|
||||
<div className="w-20 lg:w-64 flex-shrink-0 bg-gray-900 border-r border-gray-800 flex flex-col z-20">
|
||||
<div className="w-20 lg:w-64 flex-shrink-0 bg-white border-r border-gray-200 flex flex-col z-20">
|
||||
<div className="p-6 flex items-center justify-center lg:justify-start">
|
||||
<div className="h-8 w-8 bg-blue-600 rounded-lg flex items-center justify-center font-bold shadow-lg shadow-blue-900/50">
|
||||
J
|
||||
</div>
|
||||
<span className="ml-3 font-bold text-xl hidden lg:block tracking-tight text-gray-100">JBD Tool</span>
|
||||
<span className="ml-3 font-bold text-xl hidden lg:block tracking-tight text-gray-800">JBD Tool</span>
|
||||
</div>
|
||||
|
||||
<nav className="flex-1 px-4 space-y-2 mt-4">
|
||||
@@ -147,21 +148,21 @@ const App: React.FC = () => {
|
||||
onClick={() => setActiveTab('dashboard')}
|
||||
className={`w-full flex items-center p-3 rounded-xl transition-all ${activeTab === 'dashboard'
|
||||
? 'bg-blue-600 text-white shadow-lg shadow-blue-900/20'
|
||||
: 'text-gray-400 hover:bg-gray-800 hover:text-gray-200'
|
||||
: 'text-gray-600 hover:bg-gray-100 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<LayoutDashboard size={20} />
|
||||
<span className="ml-3 hidden lg:block font-medium">대시보드</span>
|
||||
<span className="ml-3 hidden lg:block font-medium">Dashboard</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('settings')}
|
||||
className={`w-full flex items-center p-3 rounded-xl transition-all ${activeTab === 'settings'
|
||||
? 'bg-blue-600 text-white shadow-lg shadow-blue-900/20'
|
||||
: 'text-gray-400 hover:bg-gray-800 hover:text-gray-200'
|
||||
: 'text-gray-600 hover:bg-gray-100 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<SettingsIcon size={20} />
|
||||
<span className="ml-3 hidden lg:block font-medium">설정 (EEPROM)</span>
|
||||
<span className="ml-3 hidden lg:block font-medium">EEPROM</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
@@ -179,57 +180,53 @@ const App: React.FC = () => {
|
||||
|
||||
</div>
|
||||
|
||||
<div className="p-4 border-t border-gray-800">
|
||||
<div className="flex flex-col gap-2">
|
||||
{connectionState === ConnectionState.CONNECTED ? (
|
||||
<button
|
||||
onClick={handleDisconnect}
|
||||
className="w-full py-3 px-4 bg-red-900/30 text-red-400 border border-red-900/50 rounded-xl hover:bg-red-900/50 transition-colors flex items-center justify-center font-semibold"
|
||||
>
|
||||
<Usb size={18} className="mr-2" />
|
||||
<span className="hidden lg:inline">연결 해제</span>
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={handleConnect}
|
||||
disabled={connectionState === ConnectionState.CONNECTING}
|
||||
className="w-full py-3 px-4 bg-green-600 text-white rounded-xl hover:bg-green-500 transition-colors flex items-center justify-center font-semibold shadow-lg shadow-green-900/20"
|
||||
>
|
||||
{connectionState === ConnectionState.CONNECTING ? <RefreshCw className="animate-spin" size={18} /> : <Usb size={18} className="mr-2" />}
|
||||
<span className="hidden lg:inline">BMS 연결</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="p-4 border-t border-gray-200 flex-1 flex flex-col justify-end">
|
||||
{/* AdSense Area - Expanded */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Content Area */}
|
||||
<div className="flex-1 flex flex-col h-full overflow-hidden relative">
|
||||
{/* Header */}
|
||||
<header className="h-16 bg-gray-900/50 backdrop-blur border-b border-gray-800 flex items-center justify-between px-6 z-10 flex-shrink-0">
|
||||
<h1 className="text-xl font-semibold text-gray-200">
|
||||
{activeTab === 'dashboard' ? '개요 (Overview)' : '설정 (Configuration)'}
|
||||
<header className="h-16 bg-white/80 backdrop-blur border-b border-gray-200 flex items-center justify-between px-6 z-10 flex-shrink-0">
|
||||
<h1 className="text-xl font-semibold text-gray-800">
|
||||
{activeTab === 'dashboard' ? 'Overview' : 'Configuration'}
|
||||
</h1>
|
||||
<div className="flex items-center gap-4">
|
||||
{connectionState === ConnectionState.CONNECTED && (
|
||||
<div className="flex items-center text-xs font-mono text-green-400 bg-green-900/20 px-3 py-1 rounded-full border border-green-900/50">
|
||||
<div className="w-2 h-2 rounded-full bg-green-500 mr-2 animate-pulse"></div>
|
||||
CONNECTED
|
||||
</div>
|
||||
)}
|
||||
{connectionState === ConnectionState.DISCONNECTED && (
|
||||
<div className="flex items-center text-xs font-mono text-gray-500 bg-gray-800 px-3 py-1 rounded-full">
|
||||
<div className="w-2 h-2 rounded-full bg-gray-500 mr-2"></div>
|
||||
OFFLINE
|
||||
</div>
|
||||
{connectionState === ConnectionState.CONNECTED ? (
|
||||
<button
|
||||
onClick={handleDisconnect}
|
||||
className="py-1.5 px-3 bg-red-100 text-red-600 border border-red-200 rounded-lg hover:bg-red-200 transition-colors flex items-center text-sm font-semibold"
|
||||
>
|
||||
<Usb size={16} className="mr-2" />
|
||||
Disconnect
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={handleConnect}
|
||||
disabled={connectionState === ConnectionState.CONNECTING}
|
||||
className="py-1.5 px-3 bg-green-600 text-white rounded-lg hover:bg-green-500 transition-colors flex items-center text-sm font-semibold shadow-sm"
|
||||
>
|
||||
{connectionState === ConnectionState.CONNECTING ? <RefreshCw className="animate-spin mr-2" size={16} /> : <Usb size={16} className="mr-2" />}
|
||||
Connect
|
||||
</button>
|
||||
)}
|
||||
|
||||
|
||||
<button
|
||||
onClick={() => setIsTerminalOpen(!isTerminalOpen)}
|
||||
className="p-2 text-gray-500 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
title={isTerminalOpen ? "Hide Terminal" : "Show Terminal"}
|
||||
>
|
||||
{isTerminalOpen ? <PanelRightClose size={20} /> : <PanelRightOpen size={20} />}
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Content Body with Right Sidebar */}
|
||||
<div className="flex-1 flex overflow-hidden">
|
||||
{/* Main View */}
|
||||
<main className="flex-1 overflow-hidden bg-gray-950 relative">
|
||||
<main className="flex-1 overflow-hidden bg-gray-50 relative">
|
||||
{errorMsg && (
|
||||
<div className="bg-red-900/80 text-white px-6 py-2 flex items-center justify-center text-sm font-medium backdrop-blur absolute top-0 left-0 right-0 z-30 animate-in slide-in-from-top-2">
|
||||
<AlertCircle size={16} className="mr-2" />
|
||||
@@ -241,27 +238,23 @@ const App: React.FC = () => {
|
||||
|
||||
{/* Empty State Overlay */}
|
||||
{connectionState !== ConnectionState.CONNECTED && (
|
||||
<div className="absolute inset-0 bg-gray-950/80 backdrop-blur-sm z-10 flex flex-col items-center justify-center p-6 text-center">
|
||||
<div className="w-16 h-16 bg-gray-800 rounded-2xl flex items-center justify-center mb-6 shadow-xl border border-gray-700">
|
||||
<div className="absolute inset-0 bg-white/80 backdrop-blur-sm z-10 flex flex-col items-center justify-center p-6 text-center">
|
||||
<div className="w-16 h-16 bg-gray-100 rounded-2xl flex items-center justify-center mb-6 shadow-xl border border-gray-200">
|
||||
<Usb size={32} className="text-gray-500" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-white mb-2">장치가 연결되지 않았습니다</h2>
|
||||
<p className="text-gray-400 max-w-md mb-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-2">장치가 연결되지 않았습니다</h2>
|
||||
<p className="text-gray-500 max-w-md mb-8">
|
||||
JBD BMS를 UART-to-USB 어댑터로 연결하여 실시간 상태를 확인하고 설정을 변경하세요.
|
||||
</p>
|
||||
<button
|
||||
onClick={handleConnect}
|
||||
className="py-3 px-8 bg-blue-600 hover:bg-blue-500 text-white font-semibold rounded-xl transition-all shadow-lg shadow-blue-900/30 flex items-center"
|
||||
>
|
||||
<Usb size={18} className="mr-2" /> 장치 연결하기
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
|
||||
{/* Right Terminal Panel */}
|
||||
<aside className="w-80 lg:w-96 border-l border-gray-800 bg-gray-950 flex flex-col flex-shrink-0 z-20">
|
||||
<Terminal />
|
||||
<aside className={`${isTerminalOpen ? 'w-80 lg:w-96 border-l' : 'w-0 border-l-0'} transition-all duration-300 ease-in-out border-gray-200 bg-white flex flex-col flex-shrink-0 z-20 overflow-hidden`}>
|
||||
<div className="w-80 lg:w-96 h-full flex flex-col">
|
||||
<Terminal />
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user