Compact SpoolBuddy settings Device tab to avoid scrolling

Remove the branding/about card, fold Device ID into the Device Info
  card, make diagnostic buttons a horizontal 3-column row, and put the
  API token input and Save button on the same line. Tighten spacing
  throughout so the tab fits on the small touchscreen without scrolling.
This commit is contained in:
maziggy
2026-03-25 12:52:43 +01:00
parent 3c1fdd13b9
commit 73aeee7bdf
6 changed files with 72 additions and 118 deletions
+1
View File
@@ -11,6 +11,7 @@ All notable changes to Bambuddy will be documented in this file.
- **External Folder Mounting for File Manager** ([#124](https://github.com/maziggy/bambuddy/issues/124)) — Host directories (NAS shares, USB drives, network storage) can now be mounted into the File Manager without copying files. Click "Link External" to point at a Docker bind-mounted path. Files are indexed into the database on scan but accessed directly from their original location — nothing is copied. Supports read-only mode (default, blocks uploads/moves/deletes), hidden file filtering, and automatic thumbnail extraction for 3MF, STL, gcode, and image files. External folders show a distinct icon and info bar with a rescan button. Deleting an external folder only removes the database index, never the actual files. Requested by @S1N4X.
### Improved
- **SpoolBuddy Settings Device Tab No Longer Scrolls** — Removed the branding card, folded Device ID into the Device Info card, made diagnostic buttons a horizontal 3-column row, and put the API token input and Save button on the same line. The Device tab now fits on the small SpoolBuddy touchscreen without scrolling.
- **Spool Notes in Assign Spool Modal** ([#793](https://github.com/maziggy/bambuddy/issues/793)) — Spool cards in the Assign Spool modal now show the spool's note as a hover tooltip, making it easier to identify spools by tracking IDs or other metadata stored in notes. Works with both internal inventory and Spoolman-synced spools. Requested by @LegionCanadian.
- **WiFi Safeguard for SpoolBuddy Pi** — The install script now drops an APT hook (`/etc/apt/apt.conf.d/80-preserve-wifi`) that backs up NetworkManager WiFi connections before every `apt upgrade` and restores them if they get wiped. Prevents headless SpoolBuddy Pis from losing WiFi connectivity after Raspberry Pi OS package upgrades (observed with Bookworm kernel/raspi-config updates that clear `/etc/NetworkManager/system-connections/`).
- **SpoolBuddy Install Script Now Upgrades System Packages** — The install script now runs `apt-get upgrade -y` after installing required packages and the WiFi safeguard. This ensures the Pi is fully up to date before SpoolBuddy is deployed, and the WiFi safeguard protects connectivity during the upgrade.
@@ -75,18 +75,9 @@ function DeviceTab({ device }: { device: SpoolBuddyDevice }) {
};
return (
<div className="space-y-4">
{/* About */}
<div className="bg-zinc-800 rounded-lg p-4">
<div className="flex items-center gap-3 mb-2">
<img src="/img/spoolbuddy_logo_dark_small.png" alt="SpoolBuddy" className="h-7 w-auto" />
</div>
<p className="text-xs text-zinc-500 mb-1">Part of Bambuddy</p>
<span className="text-xs text-zinc-500">github.com/maziggy/bambuddy</span>
</div>
<div className="space-y-2">
{/* NFC Reader + Device Info side by side */}
<div className="grid grid-cols-2 gap-3">
<div className="grid grid-cols-2 gap-2">
{/* NFC Reader */}
<div className="bg-zinc-800 rounded-lg p-3">
<h3 className="text-sm font-semibold text-zinc-300 mb-2">
@@ -152,115 +143,77 @@ function DeviceTab({ device }: { device: SpoolBuddyDevice }) {
</span>
</div>
</div>
<div className="flex justify-between">
<span className="text-zinc-500">ID</span>
<span className="text-zinc-400 font-mono truncate ml-2">{device.device_id}</span>
</div>
</div>
</div>
</div>
{/* Device ID (full width, below cards) */}
<div className="bg-zinc-800 rounded-lg px-3 py-2 flex justify-between items-center text-xs">
<span className="text-zinc-500">Device ID</span>
<span className="text-zinc-400 font-mono">{device.device_id}</span>
{/* Backend/Auth Config */}
<div className="bg-zinc-800 rounded-lg p-3 space-y-2">
<h3 className="text-sm font-semibold text-zinc-300">
{t('spoolbuddy.settings.systemConfig', 'Backend & Auth')}
</h3>
<input
value={backendUrl}
onChange={(e) => setBackendUrl(e.target.value)}
placeholder="http://192.168.1.100:5000"
className="w-full px-2 py-1.5 rounded bg-zinc-900 border border-zinc-700 text-zinc-100 text-xs"
/>
<div className="flex gap-2">
<input
type="password"
value={apiToken}
onChange={(e) => setApiToken(e.target.value)}
placeholder={t('spoolbuddy.settings.apiTokenPlaceholder', 'Enter API token')}
className="flex-1 px-2 py-1.5 rounded bg-zinc-900 border border-zinc-700 text-zinc-100 text-xs"
/>
<button
onClick={saveConfig}
disabled={systemBusy}
className="px-3 py-1.5 rounded bg-green-700 hover:bg-green-600 disabled:bg-zinc-700 text-xs font-medium text-zinc-100"
>
{t('spoolbuddy.settings.saveConfig', 'Save')}
</button>
</div>
{systemMsg && (
<div className={`text-xs ${systemMsg.type === 'ok' ? 'text-green-400' : 'text-red-400'}`}>
{systemMsg.text}
</div>
)}
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-3">
{/* Backend/Auth Config */}
<div className="bg-zinc-800 rounded-lg p-4 space-y-3">
<h3 className="text-sm font-semibold text-zinc-300">
{t('spoolbuddy.settings.systemConfig', 'Backend & Auth')}
</h3>
<div className="space-y-2">
<label className="text-xs text-zinc-500 block">
{t('spoolbuddy.settings.backendUrl', 'Bambuddy Backend URL')}
</label>
<input
value={backendUrl}
onChange={(e) => setBackendUrl(e.target.value)}
placeholder="http://192.168.1.100:5000"
className="w-full px-3 py-2 rounded bg-zinc-900 border border-zinc-700 text-zinc-100 text-sm"
/>
</div>
<div className="space-y-2">
<label className="text-xs text-zinc-500 block">
{t('spoolbuddy.settings.apiToken', 'API Token')}
</label>
<input
type="password"
value={apiToken}
onChange={(e) => setApiToken(e.target.value)}
placeholder={t('spoolbuddy.settings.apiTokenPlaceholder', 'Enter API token')}
className="w-full px-3 py-2 rounded bg-zinc-900 border border-zinc-700 text-zinc-100 text-sm"
/>
</div>
<div className="flex gap-2">
<button
onClick={saveConfig}
disabled={systemBusy}
className="px-3 py-2 rounded bg-green-700 hover:bg-green-600 disabled:bg-zinc-700 text-sm font-medium text-zinc-100"
>
{t('spoolbuddy.settings.saveConfig', 'Save Config')}
</button>
</div>
{systemMsg && (
<div className={`text-xs ${systemMsg.type === 'ok' ? 'text-green-400' : 'text-red-400'}`}>
{systemMsg.text}
</div>
)}
</div>
{/* Diagnostic Buttons */}
<div className="bg-zinc-800 rounded-lg p-4 space-y-3">
{/* NFC Diagnostic Button */}
<button
onClick={() => setDiagnosticOpen('nfc')}
className="w-full bg-blue-700 hover:bg-blue-600 transition-colors rounded-lg p-3 text-left"
>
<div className="flex items-center gap-2 mb-1">
<Wand2 className="w-4 h-4 text-blue-300" />
<span className="text-sm font-semibold text-blue-100">
{t('spoolbuddy.settings.nfcDiagnostic', 'NFC Diagnostic')}
</span>
</div>
<p className="text-xs text-blue-200/70">
{t('spoolbuddy.settings.testNfc', 'Test reader')}
</p>
</button>
{/* Scale Diagnostic Button */}
<button
onClick={() => setDiagnosticOpen('scale')}
className="w-full bg-yellow-700 hover:bg-yellow-600 transition-colors rounded-lg p-3 text-left"
>
<div className="flex items-center gap-2 mb-1">
<Zap className="w-4 h-4 text-yellow-300" />
<span className="text-sm font-semibold text-yellow-100">
{t('spoolbuddy.settings.scaleDiagnostic', 'Scale Diagnostic')}
</span>
</div>
<p className="text-xs text-yellow-200/70">
{t('spoolbuddy.settings.testScale', 'Test accuracy')}
</p>
</button>
{/* Read Tag Diagnostic Button */}
<button
onClick={() => setDiagnosticOpen('read_tag')}
className="w-full bg-emerald-700 hover:bg-emerald-600 transition-colors rounded-lg p-3 text-left"
>
<div className="flex items-center gap-2 mb-1">
<FileText className="w-4 h-4 text-emerald-300" />
<span className="text-sm font-semibold text-emerald-100">
{t('spoolbuddy.settings.readTagDiagnostic', 'Read Tag Diagnostic')}
</span>
</div>
<p className="text-xs text-emerald-200/70">
{t('spoolbuddy.settings.testReadTag', 'Run read_tag.py')}
</p>
</button>
</div>
{/* Diagnostic Buttons — horizontal row */}
<div className="grid grid-cols-3 gap-2">
<button
onClick={() => setDiagnosticOpen('nfc')}
className="bg-blue-700 hover:bg-blue-600 transition-colors rounded-lg p-2 text-center"
>
<Wand2 className="w-4 h-4 text-blue-300 mx-auto mb-1" />
<span className="text-xs font-medium text-blue-100">
{t('spoolbuddy.settings.nfcDiagnostic', 'NFC Diagnostic')}
</span>
</button>
<button
onClick={() => setDiagnosticOpen('scale')}
className="bg-yellow-700 hover:bg-yellow-600 transition-colors rounded-lg p-2 text-center"
>
<Zap className="w-4 h-4 text-yellow-300 mx-auto mb-1" />
<span className="text-xs font-medium text-yellow-100">
{t('spoolbuddy.settings.scaleDiagnostic', 'Scale Diagnostic')}
</span>
</button>
<button
onClick={() => setDiagnosticOpen('read_tag')}
className="bg-emerald-700 hover:bg-emerald-600 transition-colors rounded-lg p-2 text-center"
>
<FileText className="w-4 h-4 text-emerald-300 mx-auto mb-1" />
<span className="text-xs font-medium text-emerald-100">
{t('spoolbuddy.settings.readTagDiagnostic', 'Read Tag')}
</span>
</button>
</div>
{/* Diagnostic Modal */}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -23,8 +23,8 @@
<!-- Splash screens for iOS -->
<link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
<script type="module" crossorigin src="/assets/index-UcfHURVe.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-_-nC2GSJ.css">
<script type="module" crossorigin src="/assets/index-7pAdVCMH.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DTsgpL4e.css">
</head>
<body>
<div id="root"></div>