import { useEffect, useState } from 'react'; import { HardDrive, Activity, Wifi, Radio, Clock, RefreshCw, Loader2, Printer, Power, Computer, Download, Trash2, Eye } from 'lucide-react'; import { listDirectory, deletePath, getFileContents, getWebDAVBaseUrl, humanFileSize, type EntryInfo } from '../webdav'; import { toast } from 'sonner'; import { MediaEntry } from './MediaEntry'; import { useWs } from '../ws'; import DeviceDetailOverlay from './DeviceDetailOverlay'; import MediaSet from './MediaSet'; import { ImageWithFallback } from './figma/ImageWithFallback'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from './ui/dialog'; interface StatusPageProps { config: any; setConfig: (config: any) => void; } export default function StatusPage({ config, setConfig }: StatusPageProps) { const { status: wsStatus, send: wsSend } = useWs(); // 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) { for (const [num, device] of Object.entries(config.iec.devices)) { const d = device as any; if (d.type === 'drive' && d.enabled) { return { number: num, ...d }; } } } return null; }; const activeDevice = findActiveDevice(); const mediaSetFiles: string[] | null = (() => { if (!activeDevice?.url) return null; if (Array.isArray(activeDevice.media_set) && activeDevice.media_set.length > 0) return activeDevice.media_set as string[]; const match = (activeDevice.url as string).match(/^(.+?)(\d+)(\.[^.]+)$/); if (!match) return null; const [, prefix, , ext] = match; return Array.from({ length: 10 }, (_, i) => `${prefix}${i + 1}${ext}`); })(); const switchActiveMedia = (file: string) => { const newConfig = JSON.parse(JSON.stringify(config)); if (newConfig.iec?.devices?.[activeDevice!.number]) { newConfig.iec.devices[activeDevice!.number].url = file; setConfig(newConfig); } }; // Mock activity log - in a real app this would come from device monitoring const activityLog = [ { time: '14:32:15', event: 'File opened: game.d64', type: 'info' }, { time: '14:32:14', event: 'Device mounted', type: 'success' }, { time: '14:32:10', event: 'Read sector 18/0', type: 'info' }, { time: '14:32:08', event: 'Directory listing requested', type: 'info' }, { time: '14:32:05', event: 'Connection established', type: 'success' }, { time: '14:31:58', event: 'Device reset', type: 'warning' } ]; // Mock loading/progress state const [loading, setLoading] = useState(false); const [progress, setProgress] = useState(0.0); // 0.0 to 1.0 // Mock file info (replace with real data if available) const lastFile = 'MEATLOAF MANIACS.PRG'; const fileSize = '1.44 MB'; // Replace with real size if available const transferSpeed = '250 KB/s'; // Replace with real speed if available // Mock image association (replace with real logic if available) const imageUrl = lastFile.endsWith('.d64') ? '/assets/floppy.png' : undefined; // Dialog/modal state for reset actions const [showResetModal, setShowResetModal] = useState(null); const [resetStatus, setResetStatus] = useState('idle'); // 'idle' | 'in-progress' | 'done' // Print Log const [printFiles, setPrintFiles] = useState([]); const [printLoading, setPrintLoading] = useState(true); const [printActionEntry, setPrintActionEntry] = useState(null); const loadPrintFiles = () => { setPrintLoading(true); listDirectory('/sd/.print') .then(entries => setPrintFiles(entries.filter(e => e.type === 'file'))) .catch(() => setPrintFiles([])) .finally(() => setPrintLoading(false)); }; useEffect(() => { loadPrintFiles(); }, []); const printFileUrl = (entry: EntryInfo) => getWebDAVBaseUrl() + entry.path; const downloadPrintFile = async (entry: EntryInfo) => { try { const blob = await getFileContents(entry.path); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = entry.name; a.click(); setTimeout(() => URL.revokeObjectURL(url), 1000); } catch (e: any) { toast.error(`Download failed: ${e?.message ?? e}`); } }; const deletePrintFile = async (entry: EntryInfo) => { if (!window.confirm(`Delete "${entry.name}"?`)) return; try { await deletePath(entry.path); toast.success(`Deleted ${entry.name}`); loadPrintFiles(); } catch (e: any) { toast.error(`Delete failed: ${e?.message ?? e}`); } }; return (
{activeDevice && ( <>

Active Device

{mediaSetFiles && (
)} {/* New device info cards */}
Last File
{lastFile}
Size
{fileSize}
Transfer Speed
{transferSpeed}
{/* Progress bar (shows when loading) */} {loading && (
)} {/* Image placeholder if associated */} {imageUrl && (
)}
{showDeviceOverlay && ( setShowDeviceOverlay(false)} onNavigate={() => {}} hasPrev={false} hasNext={false} /> )} {/* Reset Activity Modal */} !open && setShowResetModal(null)}> {showResetModal === 'meatloaf' ? 'Reset Meatloaf' : 'Reset Host'} {resetStatus === 'idle' && ( <> Are you sure you want to reset {showResetModal === 'meatloaf' ? 'the Meatloaf device' : 'the Host'}?
)} {resetStatus === 'in-progress' && (
Resetting...
)} {resetStatus === 'done' && (
Reset complete!
)}
)} {!activeDevice && (
No active device
Enable a device to see activity
)}

Print Log

{printLoading ? (
Loading…
) : printFiles.length === 0 ? (
No print files found in /sd/.print
) : ( printFiles.map(entry => ( window.open(printFileUrl(entry), '_blank')} onActionsClick={e => { e.stopPropagation(); setPrintActionEntry(entry); }} className="last:border-b-0" /> )) )}
{/* Print Log action modal */} !open && setPrintActionEntry(null)}> {printActionEntry?.name} {humanFileSize(printActionEntry?.size ?? 0)} {printActionEntry && (
)}

Activity Log

{activityLog.map((entry, index) => (
{entry.time}
{entry.event}
))}

System Status
Uptime
3h 24m

{/* System Status Action Buttons at bottom */}
Memory Utilization
{/* Heap Graph */}
Heap {memory.heap.free} KB free / {memory.heap.total} KB
{/* PSRAM Graph */}
PSRAM {memory.psram.free} KB free / {memory.psram.total} KB
WiFi
Connected
WebSocket
{wsStatus === 'connecting' && <>Connecting} {wsStatus === 'connected' && <>Connected} {wsStatus === 'disconnected' && <>Disconnected}
IP Address
192.168.1.100
MAC Address
AA:BB:CC:DD:EE:FF
); }