From ae28de37f5c5fd3d447b441e858199aaf00f378a Mon Sep 17 00:00:00 2001 From: Jaime Idolpx Date: Tue, 9 Jun 2026 18:43:07 -0400 Subject: [PATCH] feat(DeviceDetailOverlay, DevicesPage, MediaManager, SearchOverlay, StatusPage): refactor device handling for improved consistency and add support for new device types --- src/app/components/DeviceDetailOverlay.tsx | 3 +- src/app/components/DevicesPage.tsx | 96 ++---------- src/app/components/MediaManager.tsx | 13 +- src/app/components/SearchOverlay.tsx | 13 +- src/app/components/StatusPage.tsx | 13 +- src/imports/config.json | 172 +++++++++++---------- 6 files changed, 128 insertions(+), 182 deletions(-) diff --git a/src/app/components/DeviceDetailOverlay.tsx b/src/app/components/DeviceDetailOverlay.tsx index cc7d331..d81615a 100644 --- a/src/app/components/DeviceDetailOverlay.tsx +++ b/src/app/components/DeviceDetailOverlay.tsx @@ -94,8 +94,7 @@ export default function DeviceDetailOverlay({ }; const getDevicePath = (): string[] => { - const [type, num] = device.id.split('-'); - return ['iec', 'devices', type, num]; + return ['iec', 'devices', device.number]; }; const getDeviceData = () => { diff --git a/src/app/components/DevicesPage.tsx b/src/app/components/DevicesPage.tsx index 52bf0ae..e21509c 100644 --- a/src/app/components/DevicesPage.tsx +++ b/src/app/components/DevicesPage.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { Printer, HardDrive, Network, Box, ChevronRight, RefreshCw, FolderOpen } from 'lucide-react'; +import { Printer, HardDrive, Network, Box, ChevronRight, RefreshCw, FolderOpen, Computer } from 'lucide-react'; import DeviceDetailOverlay from './DeviceDetailOverlay'; import MediaBrowser from './MediaBrowser'; import { toast } from 'sonner'; @@ -45,77 +45,24 @@ export default function DevicesPage({ config, setConfig, openDeviceId, onClearOp const devices: Device[] = []; - // Printer devices - if (config.iec?.devices?.printer) { - Object.entries(config.iec.devices.printer).forEach(([num, device]: [string, any]) => { + if (config.iec?.devices) { + Object.entries(config.iec.devices).forEach(([num, device]: [string, any]) => { + const type = device.type as Device['type']; + const name = device.name || ( + type === 'drive' ? `Drive ${num}` : + type === 'network' ? `Network ${num}` : + type === 'meatloaf' ? `Meatloaf ${num}` : + undefined + ); devices.push({ - id: `printer-${num}`, + id: `${type}-${num}`, number: num, - type: 'printer', - name: device.name, - enabled: device.enabled - }); - }); - } - - // Drive devices - if (config.iec?.devices?.drive) { - Object.entries(config.iec.devices.drive).forEach(([key, value]: [string, any]) => { - if (key !== 'vdrive' && key !== 'rom') { - devices.push({ - id: `drive-${key}`, - number: key, - type: 'drive', - name: `Drive ${key}`, - enabled: value.enabled, - base_url: value.base_url, - url: value.url, - mode: value.mode - }); - } - }); - } - - // Network devices - if (config.iec?.devices?.network) { - Object.entries(config.iec.devices.network).forEach(([num, device]: [string, any]) => { - devices.push({ - id: `network-${num}`, - number: num, - type: 'network', - name: `Network ${num}`, - enabled: device.enabled, - base_url: device.base_url, - url: device.url - }); - }); - } - - // Other devices - if (config.iec?.devices?.other) { - Object.entries(config.iec.devices.other).forEach(([num, device]: [string, any]) => { - devices.push({ - id: `other-${num}`, - number: num, - type: 'other', - name: device.name, - enabled: device.enabled - }); - }); - } - - // Meatloaf devices - if (config.iec?.devices?.meatloaf) { - Object.entries(config.iec.devices.meatloaf).forEach(([num, device]: [string, any]) => { - devices.push({ - id: `meatloaf-${num}`, - number: num, - type: 'meatloaf', - name: `Meatloaf ${num}`, + type, + name, enabled: device.enabled, base_url: device.base_url, url: device.url, - mode: device.mode + mode: device.mode, }); }); } @@ -137,6 +84,8 @@ export default function DevicesPage({ config, setConfig, openDeviceId, onClearOp return ; case 'network': return ; + case 'meatloaf': + return ; default: return ; } @@ -177,20 +126,9 @@ export default function DevicesPage({ config, setConfig, openDeviceId, onClearOp const toggleDeviceEnabled = (device: Device, e: React.MouseEvent) => { e.stopPropagation(); - - const [type, num] = device.id.split('-'); - const path = ['iec', 'devices', type, num, 'enabled']; - const newConfig = JSON.parse(JSON.stringify(config)); - let current = newConfig; - - for (let i = 0; i < path.length - 1; i++) { - current = current[path[i]]; - } - - current[path[path.length - 1]] = device.enabled ? 0 : 1; + newConfig.iec.devices[device.number].enabled = device.enabled ? 0 : 1; setConfig(newConfig); - toast.success(`Device #${device.number} ${device.enabled ? 'disabled' : 'enabled'}`); }; diff --git a/src/app/components/MediaManager.tsx b/src/app/components/MediaManager.tsx index 05933f2..15ae647 100644 --- a/src/app/components/MediaManager.tsx +++ b/src/app/components/MediaManager.tsx @@ -738,9 +738,8 @@ export default function MediaManager({ initialPath = '/', rootPath, title, confi const newConfig = JSON.parse(JSON.stringify(config)); if (!newConfig.iec) newConfig.iec = {}; if (!newConfig.iec.devices) newConfig.iec.devices = {}; - if (!newConfig.iec.devices[deviceType]) newConfig.iec.devices[deviceType] = {}; - if (!newConfig.iec.devices[deviceType][key]) newConfig.iec.devices[deviceType][key] = {}; - const dev = newConfig.iec.devices[deviceType][key]; + if (!newConfig.iec.devices[key]) newConfig.iec.devices[key] = { type: deviceType }; + const dev = newConfig.iec.devices[key]; if (mountEntry.name.toLowerCase().endsWith('.lst')) { try { @@ -1305,10 +1304,12 @@ export default function MediaManager({ initialPath = '/', rootPath, title, confi {mountEntry?.name} {(() => { - const drives = Object.entries(config?.iec?.devices?.drive ?? {}) - .filter(([k]) => k !== 'vdrive' && k !== 'rom') + const allDevices = Object.entries(config?.iec?.devices ?? {}); + const drives = allDevices + .filter(([, v]: [string, any]) => (v as any)?.type === 'drive') .map(([k, v]: [string, any]) => ({ type: 'drive' as const, key: k, base_url: v?.base_url as string | undefined, url: v?.url as string | undefined, enabled: !!v?.enabled })); - const meatloafs = Object.entries(config?.iec?.devices?.meatloaf ?? {}) + const meatloafs = allDevices + .filter(([, v]: [string, any]) => (v as any)?.type === 'meatloaf') .map(([k, v]: [string, any]) => ({ type: 'meatloaf' as const, key: k, base_url: v?.base_url as string | undefined, url: v?.url as string | undefined, enabled: !!v?.enabled })); const devices = [...drives, ...meatloafs]; if (!devices.length) diff --git a/src/app/components/SearchOverlay.tsx b/src/app/components/SearchOverlay.tsx index 7cddd8d..2024832 100644 --- a/src/app/components/SearchOverlay.tsx +++ b/src/app/components/SearchOverlay.tsx @@ -121,8 +121,8 @@ export default function SearchOverlay({ config, setConfig, onClose }: SearchOver const handleMount = (deviceNum: string, result: SearchResult) => { const newConfig = JSON.parse(JSON.stringify(config)); - if (newConfig.iec?.devices?.drive?.[deviceNum]) { - newConfig.iec.devices.drive[deviceNum].url = result.path; + if (newConfig.iec?.devices?.[deviceNum]) { + newConfig.iec.devices[deviceNum].url = result.path; setConfig(newConfig); toast.success(`Mounted ${result.name} on Device #${deviceNum}`); setShowDeviceMenu(null); @@ -131,10 +131,11 @@ export default function SearchOverlay({ config, setConfig, onClose }: SearchOver const getAvailableDevices = () => { const devices: { number: string; name: string; url?: string }[] = []; - if (config.iec?.devices?.drive) { - for (const [num, device] of Object.entries(config.iec.devices.drive)) { - if (num !== 'vdrive' && num !== 'rom' && (device as any).enabled) { - devices.push({ number: num, name: `Drive ${num}`, url: (device as any).url }); + 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) { + devices.push({ number: num, name: `Drive ${num}`, url: d.url }); } } } diff --git a/src/app/components/StatusPage.tsx b/src/app/components/StatusPage.tsx index dede96f..22c067d 100644 --- a/src/app/components/StatusPage.tsx +++ b/src/app/components/StatusPage.tsx @@ -27,10 +27,11 @@ export default function StatusPage({ config, setConfig }: StatusPageProps) { const [showDeviceOverlay, setShowDeviceOverlay] = useState(false); // Find the first enabled device as the active device const findActiveDevice = () => { - if (config.iec?.devices?.drive) { - for (const [num, device] of Object.entries(config.iec.devices.drive)) { - if (num !== 'vdrive' && num !== 'rom' && (device as any).enabled) { - return { number: num, ...device as any, type: 'drive' }; + 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 }; } } } @@ -51,8 +52,8 @@ export default function StatusPage({ config, setConfig }: StatusPageProps) { const switchActiveMedia = (file: string) => { const newConfig = JSON.parse(JSON.stringify(config)); - if (newConfig.iec?.devices?.drive?.[activeDevice!.number]) { - newConfig.iec.devices.drive[activeDevice!.number].url = file; + if (newConfig.iec?.devices?.[activeDevice!.number]) { + newConfig.iec.devices[activeDevice!.number].url = file; setConfig(newConfig); } }; diff --git a/src/imports/config.json b/src/imports/config.json index 2b9a176..b9892fb 100644 --- a/src/imports/config.json +++ b/src/imports/config.json @@ -137,93 +137,99 @@ "bbs": 1 }, "devices": { - "printer": { - "4": { - "enabled": 1, - "type": "printer", - "name": "MPS803" - } + "4": { + "enabled": 1, + "type": "printer", + "name": "MPS803" }, - "drive": { - "8": { - "enabled": 1, - "url": "/sd", - "mode": 1 - }, - "9": { - "enabled": 1, - "url": "/", - "mode": 1 - }, - "10": { - "enabled": 1, - "url": "/", - "mode": 1 - }, - "11": { - "enabled": 1, - "url": "/", - "mode": 1 - }, - "12": { - "enabled": 1, - "url": "/", - "mode": 1 - }, - "13": { - "enabled": 1, - "url": "/", - "mode": 1 - }, - "14": { - "enabled": 1, - "url": "/", - "mode": 1 - }, - "15": { - "enabled": 1, - "url": "/", - "mode": 1 - } + "8": { + "enabled": 1, + "type": "drive", + "url": "/sd", + "mode": 1 }, - "network": { - "16": { - "enabled": 1, - "url": "/sd" - }, - "17": { - "enabled": 1, - "url": "/" - }, - "18": { - "enabled": 1, - "url": "/" - }, - "19": { - "enabled": 1, - "url": "/" - } + "9": { + "enabled": 1, + "type": "drive", + "url": "/", + "mode": 1 }, - "other": { - "20": { - "enabled": 1, - "name": "CP/m" - }, - "21": { - "enabled": 1, - "name": "S.A.M" - }, - "29": { - "enabled": 1, - "name": "Clock" - } + "10": { + "enabled": 1, + "type": "drive", + "url": "/", + "mode": 1 }, - "meatloaf": { - "30": { - "enabled": 1, - "url": "/", - "mode": 1 - } + "11": { + "enabled": 1, + "type": "drive", + "url": "/", + "mode": 1 + }, + "12": { + "enabled": 1, + "type": "drive", + "url": "/", + "mode": 1 + }, + "13": { + "enabled": 1, + "type": "drive", + "url": "/", + "mode": 1 + }, + "14": { + "enabled": 1, + "type": "drive", + "url": "/", + "mode": 1 + }, + "15": { + "enabled": 1, + "type": "drive", + "url": "/", + "mode": 1 + }, + "16": { + "enabled": 1, + "type": "network", + "url": "/sd" + }, + "17": { + "enabled": 1, + "type": "network", + "url": "/" + }, + "18": { + "enabled": 1, + "type": "network", + "url": "/" + }, + "19": { + "enabled": 1, + "type": "network", + "url": "/" + }, + "20": { + "enabled": 1, + "type": "other", + "name": "CP/m" + }, + "21": { + "enabled": 1, + "type": "other", + "name": "S.A.M" + }, + "29": { + "enabled": 1, + "type": "other", + "name": "Clock" + }, + "30": { + "enabled": 1, + "type": "meatloaf", + "url": "/", + "mode": 1 } } },