172 lines
6.8 KiB
TypeScript
172 lines
6.8 KiB
TypeScript
import { useState } from 'react';
|
|
import { Cpu, Settings, Wifi, Network, HardDrive, Activity, MoreHorizontal, Search, Wrench, User, LogOut, Bell, FileText } from 'lucide-react';
|
|
import { Toaster } from 'sonner';
|
|
import StatusPage from './components/StatusPage';
|
|
import DevicesPage from './components/DevicesPage';
|
|
import GeneralPage from './components/GeneralPage';
|
|
import NetworkPage from './components/NetworkPage';
|
|
import IECPage from './components/IECPage';
|
|
import OtherPage from './components/OtherPage';
|
|
import ToolsPage from './components/ToolsPage';
|
|
import SearchOverlay from './components/SearchOverlay';
|
|
import logoSvg from '../imports/logo.svg';
|
|
import configData from '../imports/config.json';
|
|
|
|
type Page = 'status' | 'devices' | 'iec' | 'network' | 'other' | 'general' | 'tools';
|
|
|
|
export default function App() {
|
|
const [currentPage, setCurrentPage] = useState<Page>('status');
|
|
const [config, setConfig] = useState(configData);
|
|
const [showSearch, setShowSearch] = useState(false);
|
|
const [showProfileMenu, setShowProfileMenu] = useState(false);
|
|
|
|
const pages = {
|
|
status: <StatusPage config={config} setConfig={setConfig} />,
|
|
devices: <DevicesPage config={config} setConfig={setConfig} />,
|
|
iec: <IECPage config={config} setConfig={setConfig} />,
|
|
network: <NetworkPage config={config} setConfig={setConfig} />,
|
|
other: <OtherPage config={config} setConfig={setConfig} />,
|
|
general: <GeneralPage config={config} setConfig={setConfig} />,
|
|
tools: <ToolsPage config={config} setConfig={setConfig} />
|
|
};
|
|
|
|
return (
|
|
<div className="size-full flex flex-col bg-neutral-50">
|
|
<Toaster position="top-center" />
|
|
<header className="bg-white border-b border-neutral-200 px-4 py-3 flex-shrink-0">
|
|
<div className="flex items-center justify-between">
|
|
<img src={logoSvg} alt="Meatloaf" className="h-8" />
|
|
<div className="flex items-center gap-3">
|
|
<button
|
|
onClick={() => setShowSearch(true)}
|
|
className="p-2 hover:bg-neutral-100 rounded-lg"
|
|
>
|
|
<Search className="w-5 h-5" />
|
|
</button>
|
|
<button
|
|
onClick={() => setCurrentPage('tools')}
|
|
className="p-2 hover:bg-neutral-100 rounded-lg"
|
|
>
|
|
<Wrench className="w-5 h-5" />
|
|
</button>
|
|
<div className="relative">
|
|
<button
|
|
onClick={() => setShowProfileMenu(!showProfileMenu)}
|
|
className="p-2 hover:bg-neutral-100 rounded-lg"
|
|
>
|
|
<User className="w-5 h-5" />
|
|
</button>
|
|
{showProfileMenu && (
|
|
<div className="absolute right-0 top-12 bg-white rounded-lg shadow-lg border border-neutral-200 py-2 min-w-[200px] z-20">
|
|
<button
|
|
onClick={() => {
|
|
setShowProfileMenu(false);
|
|
setCurrentPage('general');
|
|
}}
|
|
className="w-full px-4 py-2 text-left hover:bg-neutral-50 flex items-center gap-2"
|
|
>
|
|
<Settings className="w-4 h-4" />
|
|
Settings
|
|
</button>
|
|
<button
|
|
onClick={() => setShowProfileMenu(false)}
|
|
className="w-full px-4 py-2 text-left hover:bg-neutral-50 flex items-center gap-2"
|
|
>
|
|
<Bell className="w-4 h-4" />
|
|
Notifications
|
|
</button>
|
|
<button
|
|
onClick={() => setShowProfileMenu(false)}
|
|
className="w-full px-4 py-2 text-left hover:bg-neutral-50 flex items-center gap-2"
|
|
>
|
|
<FileText className="w-4 h-4" />
|
|
Documentation
|
|
</button>
|
|
<div className="border-t border-neutral-200 my-2" />
|
|
<button
|
|
onClick={() => setShowProfileMenu(false)}
|
|
className="w-full px-4 py-2 text-left hover:bg-neutral-50 flex items-center gap-2 text-red-600"
|
|
>
|
|
<LogOut className="w-4 h-4" />
|
|
Logout
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="flex-1 overflow-y-auto">
|
|
{pages[currentPage]}
|
|
</main>
|
|
|
|
<nav className="bg-white border-t border-neutral-200 flex-shrink-0">
|
|
<div className="flex">
|
|
<button
|
|
onClick={() => setCurrentPage('status')}
|
|
className={`flex-1 flex flex-col items-center gap-1 py-2 ${
|
|
currentPage === 'status' ? 'text-blue-600' : 'text-neutral-600'
|
|
}`}
|
|
>
|
|
<Activity className="w-5 h-5" />
|
|
<span className="text-xs">Status</span>
|
|
</button>
|
|
<button
|
|
onClick={() => setCurrentPage('devices')}
|
|
className={`flex-1 flex flex-col items-center gap-1 py-2 ${
|
|
currentPage === 'devices' ? 'text-blue-600' : 'text-neutral-600'
|
|
}`}
|
|
>
|
|
<HardDrive className="w-5 h-5" />
|
|
<span className="text-xs">Devices</span>
|
|
</button>
|
|
<button
|
|
onClick={() => setCurrentPage('iec')}
|
|
className={`flex-1 flex flex-col items-center gap-1 py-2 ${
|
|
currentPage === 'iec' ? 'text-blue-600' : 'text-neutral-600'
|
|
}`}
|
|
>
|
|
<Cpu className="w-5 h-5" />
|
|
<span className="text-xs">IEC</span>
|
|
</button>
|
|
<button
|
|
onClick={() => setCurrentPage('network')}
|
|
className={`flex-1 flex flex-col items-center gap-1 py-2 ${
|
|
currentPage === 'network' ? 'text-blue-600' : 'text-neutral-600'
|
|
}`}
|
|
>
|
|
<Network className="w-5 h-5" />
|
|
<span className="text-xs">Network</span>
|
|
</button>
|
|
<button
|
|
onClick={() => setCurrentPage('other')}
|
|
className={`flex-1 flex flex-col items-center gap-1 py-2 ${
|
|
currentPage === 'other' ? 'text-blue-600' : 'text-neutral-600'
|
|
}`}
|
|
>
|
|
<MoreHorizontal className="w-5 h-5" />
|
|
<span className="text-xs">More</span>
|
|
</button>
|
|
<button
|
|
onClick={() => setCurrentPage('general')}
|
|
className={`flex-1 flex flex-col items-center gap-1 py-2 ${
|
|
currentPage === 'general' ? 'text-blue-600' : 'text-neutral-600'
|
|
}`}
|
|
>
|
|
<Settings className="w-5 h-5" />
|
|
<span className="text-xs">General</span>
|
|
</button>
|
|
</div>
|
|
</nav>
|
|
|
|
{showSearch && (
|
|
<SearchOverlay
|
|
config={config}
|
|
setConfig={setConfig}
|
|
onClose={() => setShowSearch(false)}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
} |