refactor: update App and StatusPage components for improved styling and functionality

This commit is contained in:
Jaime Idolpx 2026-04-14 00:22:43 -04:00
parent 9eeffde903
commit 5e329a7f39
2 changed files with 141 additions and 53 deletions

View File

@ -33,28 +33,30 @@ export default function App() {
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" />
<header className="bg-[#4d4d4d] px-0 py-0 flex-shrink-0">
<div className="flex items-stretch justify-between min-h-[56px]">
<div className="flex items-center h-full">
<img src={logoSvg} alt="Meatloaf" className="h-full max-h-[56px] w-auto object-contain" />
</div>
<div className="flex items-center gap-3">
<button
onClick={() => setShowSearch(true)}
className="p-2 hover:bg-neutral-100 rounded-lg"
className="p-2 hover:bg-[#5e5e5e] rounded-lg"
>
<Search className="w-5 h-5" />
<Search className="w-5 h-5 text-white" />
</button>
<button
onClick={() => setCurrentPage('tools')}
className="p-2 hover:bg-neutral-100 rounded-lg"
className="p-2 hover:bg-[#5e5e5e] rounded-lg"
>
<Wrench className="w-5 h-5" />
<Wrench className="w-5 h-5 text-white" />
</button>
<div className="relative">
<button
onClick={() => setShowProfileMenu(!showProfileMenu)}
className="p-2 hover:bg-neutral-100 rounded-lg"
className="p-2 hover:bg-[#5e5e5e] rounded-lg"
>
<User className="w-5 h-5" />
<User className="w-5 h-5 text-white" />
</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">
@ -65,21 +67,21 @@ export default function App() {
}}
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 className="w-4 h-4 text-[#4d4d4d]" />
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" />
<Bell className="w-4 h-4 text-[#4d4d4d]" />
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" />
<FileText className="w-4 h-4 text-[#4d4d4d]" />
Documentation
</button>
<div className="border-t border-neutral-200 my-2" />
@ -87,7 +89,7 @@ export default function App() {
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 className="w-4 h-4 text-[#4d4d4d]" />
Logout
</button>
</div>
@ -101,61 +103,49 @@ export default function App() {
{pages[currentPage]}
</main>
<nav className="bg-white border-t border-neutral-200 flex-shrink-0">
<nav className="bg-[#4d4d4d] 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'
}`}
className="flex-1 flex flex-col items-center gap-1 py-2"
>
<Activity className="w-5 h-5" />
<span className="text-xs">Status</span>
<Activity className="w-5 h-5 text-white" />
<span className="text-xs text-white">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'
}`}
className="flex-1 flex flex-col items-center gap-1 py-2"
>
<HardDrive className="w-5 h-5" />
<span className="text-xs">Devices</span>
<HardDrive className="w-5 h-5 text-white" />
<span className="text-xs text-white">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'
}`}
className="flex-1 flex flex-col items-center gap-1 py-2"
>
<Cpu className="w-5 h-5" />
<span className="text-xs">IEC</span>
<Cpu className="w-5 h-5 text-white" />
<span className="text-xs text-white">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'
}`}
className="flex-1 flex flex-col items-center gap-1 py-2"
>
<Network className="w-5 h-5" />
<span className="text-xs">Network</span>
<Network className="w-5 h-5 text-white" />
<span className="text-xs text-white">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'
}`}
className="flex-1 flex flex-col items-center gap-1 py-2"
>
<MoreHorizontal className="w-5 h-5" />
<span className="text-xs">More</span>
<MoreHorizontal className="w-5 h-5 text-white" />
<span className="text-xs text-white">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'
}`}
className="flex-1 flex flex-col items-center gap-1 py-2"
>
<Settings className="w-5 h-5" />
<span className="text-xs">General</span>
<Settings className="w-5 h-5 text-white" />
<span className="text-xs text-white">General</span>
</button>
</div>
</nav>

View File

@ -1,11 +1,21 @@
import { useState } from 'react';
import { HardDrive, Activity, Wifi, Signal, Clock } from 'lucide-react';
import DeviceDetailOverlay from './DeviceDetailOverlay';
interface StatusPageProps {
config: any;
setConfig: (config: any) => void;
}
export default function StatusPage({ config }: StatusPageProps) {
export default function StatusPage({ config, setConfig }: StatusPageProps) {
// Mock memory stats
const memory = {
heap: { total: 4096, free: 1024 }, // in KB
psram: { total: 8192, free: 4096 },
};
// Overlay state for active device
const [showDeviceOverlay, setShowDeviceOverlay] = useState(false);
// Find the first enabled device as the active device
const findActiveDevice = () => {
if (config.iec?.devices?.drive) {
@ -42,16 +52,39 @@ export default function StatusPage({ config }: StatusPageProps) {
<h2 className="text-sm text-neutral-500">System Status</h2>
<div className="bg-white border border-neutral-200 rounded-lg p-4">
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center">
<Signal className="w-5 h-5 text-green-600" />
</div>
<div>
<div className="font-medium">Online</div>
<div className="text-sm text-neutral-500">All systems operational</div>
<div className="mb-4">
<div className="text-xs text-neutral-500 mb-1">Memory Utilization</div>
<div className="flex flex-col gap-2">
{/* Heap Graph */}
<div>
<div className="flex justify-between text-xs mb-0.5">
<span>Heap</span>
<span>{memory.heap.free} KB free / {memory.heap.total} KB</span>
</div>
<div className="w-full h-3 bg-neutral-200 rounded overflow-hidden">
<div
className="h-3 bg-blue-500"
style={{ width: `${((memory.heap.total - memory.heap.free) / memory.heap.total) * 100}%` }}
/>
</div>
</div>
{/* PSRAM Graph */}
<div>
<div className="flex justify-between text-xs mb-0.5">
<span>PSRAM</span>
<span>{memory.psram.free} KB free / {memory.psram.total} KB</span>
</div>
<div className="w-full h-3 bg-neutral-200 rounded overflow-hidden">
<div
className="h-3 bg-green-500"
style={{ width: `${((memory.psram.total - memory.psram.free) / memory.psram.total) * 100}%` }}
/>
</div>
</div>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<div className="text-xs text-neutral-500">WiFi</div>
@ -59,6 +92,10 @@ export default function StatusPage({ config }: StatusPageProps) {
<Wifi className="w-3 h-3 text-green-600" />
<span>Connected</span>
</div>
<div className="text-xs text-neutral-500 mt-1">IP Address</div>
<div className="text-sm text-neutral-700">192.168.1.100</div>
<div className="text-xs text-neutral-500 mt-1">MAC Address</div>
<div className="text-sm text-neutral-700">AA:BB:CC:DD:EE:FF</div>
</div>
<div>
<div className="text-xs text-neutral-500">Uptime</div>
@ -74,7 +111,10 @@ export default function StatusPage({ config }: StatusPageProps) {
<>
<h2 className="text-sm text-neutral-500 pt-2">Active Device</h2>
<div className="bg-white border border-neutral-200 rounded-lg p-4">
<div
className="bg-white border border-neutral-200 rounded-lg p-4 cursor-pointer hover:bg-neutral-50 transition"
onClick={() => setShowDeviceOverlay(true)}
>
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center">
@ -106,8 +146,66 @@ export default function StatusPage({ config }: StatusPageProps) {
<div className="text-lg font-medium text-green-600">{stats.errors}</div>
</div>
</div>
{/* Media switch buttons if media set is detected */}
{(() => {
// Media set detection logic (copied from DeviceDetailOverlay)
const url = activeDevice.url;
if (!url) return null;
const match = url.match(/^(.+?)(\d+)(\.[^.]+)$/);
if (!match) return null;
const [, prefix, num, ext] = match;
const currentNum = parseInt(num);
const mediaSet = [];
for (let i = 1; i <= 10; i++) {
mediaSet.push(`${prefix}${i}${ext}`);
}
return (
<div className="flex flex-wrap gap-2 mt-2">
{mediaSet.map((file, idx) => (
<button
key={file}
className={`px-2 py-1 rounded text-xs border ${url === file ? 'bg-blue-600 text-white border-blue-600' : 'bg-neutral-100 text-neutral-700 border-neutral-300'}`}
onClick={e => {
e.stopPropagation();
if (setConfig) {
const newConfig = JSON.parse(JSON.stringify(config));
let current = newConfig;
if (current.iec && current.iec.devices && current.iec.devices.drive && current.iec.devices.drive[num]) {
current.iec.devices.drive[num].url = file;
setConfig(newConfig);
}
}
}}
>
{idx + 1}
</button>
))}
</div>
);
})()}
</div>
{showDeviceOverlay && (
<DeviceDetailOverlay
device={{
id: `drive-${activeDevice.number}`,
number: activeDevice.number,
type: 'drive',
name: activeDevice.name,
enabled: activeDevice.enabled,
url: activeDevice.url,
mode: activeDevice.mode
}}
config={config}
setConfig={setConfig}
onClose={() => setShowDeviceOverlay(false)}
onNavigate={() => {}}
hasPrev={false}
hasNext={false}
/>
)}
<h2 className="text-sm text-neutral-500 pt-2 flex items-center gap-2">
<Activity className="w-4 h-4" />
Activity Log