Explorar el Código

Fix queue badge showing on printers without matching filament (#486)

The purple queue counter badge in the printer card header used the raw
unfiltered queue item count, so it appeared on ALL printers of the same
model when a job was scheduled for "any [model]" — even printers without
the matching filament color. The PrinterQueueWidget below it (which
shows "Clear Plate & Start") already filtered by filament type + color.

Apply the same filament compatibility filter (type check + color override
check) to the badge count so it only shows on printers that can actually
run the queued jobs.
maziggy hace 2 meses
padre
commit
7eb7bf7b93

+ 1 - 0
CHANGELOG.md

@@ -13,6 +13,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **SpoolBuddy Kiosk Auth Bypass via API Key** — When Bambuddy auth is enabled, the SpoolBuddy kiosk (Chromium on RPi) was redirected to the login page because the `ProtectedRoute` requires a user object from `GET /auth/me`, which only accepted JWT tokens. The `/auth/me` endpoint now also accepts API keys (via `Authorization: Bearer bb_xxx` or `X-API-Key` header) and returns a synthetic admin user with all permissions. The frontend's `AuthContext` reads an optional `?token=` URL parameter on first load, stores it in localStorage, and strips it from the URL to prevent leakage via browser history or referrer. The install script now includes the API key in the kiosk URL (`/spoolbuddy?token=${API_KEY}`), so the device authenticates automatically on boot without manual login.
 - **SpoolBuddy Kiosk Auth Bypass via API Key** — When Bambuddy auth is enabled, the SpoolBuddy kiosk (Chromium on RPi) was redirected to the login page because the `ProtectedRoute` requires a user object from `GET /auth/me`, which only accepted JWT tokens. The `/auth/me` endpoint now also accepts API keys (via `Authorization: Bearer bb_xxx` or `X-API-Key` header) and returns a synthetic admin user with all permissions. The frontend's `AuthContext` reads an optional `?token=` URL parameter on first load, stores it in localStorage, and strips it from the URL to prevent leakage via browser history or referrer. The install script now includes the API key in the kiosk URL (`/spoolbuddy?token=${API_KEY}`), so the device authenticates automatically on boot without manual login.
 
 
 ### Fixed
 ### Fixed
+- **Queue Badge Shows on Incompatible Printers** ([#486](https://github.com/maziggy/bambuddy/issues/486)) — The purple queue counter badge in the printer card header showed on all printers of the same model when a job was scheduled for "any [model]", even if the printer didn't have the matching filament color loaded. The `PrinterQueueWidget` (which shows "Clear Plate & Start") already filtered by filament type and color, but the badge count used the raw unfiltered queue length. Now applies the same filament compatibility filter to the badge count.
 - **SpoolBuddy Daemon Can't Find Hardware Drivers** — The daemon's `nfc_reader.py` and `scale_reader.py` import `read_tag` and `scale_diag` as bare modules, but these files live in `spoolbuddy/scripts/` which isn't on Python's module search path. The systemd service sets `WorkingDirectory` to `spoolbuddy/` and runs `python -m daemon.main`, so only the `spoolbuddy/` and `daemon/` directories are on `sys.path`. Added `scripts/` to `sys.path` at daemon startup, resolved relative to the module file so it works regardless of install path. Also moved the `read_tag` import inside `NFCReader.__init__`'s try/except block — it was previously outside, so a missing module crashed the entire daemon instead of gracefully skipping NFC polling. Demoted hardware-not-available log messages from ERROR to INFO since missing modules are expected when hardware isn't connected.
 - **SpoolBuddy Daemon Can't Find Hardware Drivers** — The daemon's `nfc_reader.py` and `scale_reader.py` import `read_tag` and `scale_diag` as bare modules, but these files live in `spoolbuddy/scripts/` which isn't on Python's module search path. The systemd service sets `WorkingDirectory` to `spoolbuddy/` and runs `python -m daemon.main`, so only the `spoolbuddy/` and `daemon/` directories are on `sys.path`. Added `scripts/` to `sys.path` at daemon startup, resolved relative to the module file so it works regardless of install path. Also moved the `read_tag` import inside `NFCReader.__init__`'s try/except block — it was previously outside, so a missing module crashed the entire daemon instead of gracefully skipping NFC polling. Demoted hardware-not-available log messages from ERROR to INFO since missing modules are expected when hardware isn't connected.
 
 
 ### Improved
 ### Improved

+ 21 - 1
frontend/src/pages/PrintersPage.tsx

@@ -1672,7 +1672,27 @@ function PrinterCard({
     queryKey: ['queue', printer.id, 'pending'],
     queryKey: ['queue', printer.id, 'pending'],
     queryFn: () => api.getQueue(printer.id, 'pending'),
     queryFn: () => api.getQueue(printer.id, 'pending'),
   });
   });
-  const queueCount = queueItems?.length || 0;
+  // Filter queue items by filament compatibility (same logic as PrinterQueueWidget)
+  // so the badge only shows on printers that can actually run the queued jobs.
+  const queueCount = useMemo(() => {
+    if (!queueItems?.length) return 0;
+    return queueItems.filter(item => {
+      if (item.required_filament_types?.length && loadedFilamentTypes?.size) {
+        if (!item.required_filament_types.every((t: string) => loadedFilamentTypes.has(t.toUpperCase()))) {
+          return false;
+        }
+      }
+      if (item.filament_overrides?.length && loadedFilaments?.size) {
+        const hasColorMatch = item.filament_overrides.some((o: { type?: string; color?: string }) => {
+          const oType = (o.type || '').toUpperCase();
+          const oColor = (o.color || '').replace('#', '').toLowerCase().slice(0, 6);
+          return loadedFilaments.has(`${oType}:${oColor}`);
+        });
+        if (!hasColorMatch) return false;
+      }
+      return true;
+    }).length;
+  }, [queueItems, loadedFilamentTypes, loadedFilaments]);
 
 
   // Fetch currently printing queue item to show who started it (Issue #206)
   // Fetch currently printing queue item to show who started it (Issue #206)
   const { data: printingQueueItems } = useQuery({
   const { data: printingQueueItems } = useQuery({

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
static/assets/index-BKdrvUCu.css


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
static/assets/index-CS9we2y6.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 1
static/assets/index-CxFtC4Kb.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
static/assets/index-DJjXosw8.css


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
static/assets/index-OUDlnhQe.css


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
static/assets/index-tKUolDFn.js


+ 2 - 2
static/index.html

@@ -23,8 +23,8 @@
 
 
     <!-- Splash screens for iOS -->
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-CS9we2y6.js"></script>
-    <link rel="stylesheet" crossorigin href="/assets/index-OUDlnhQe.css">
+    <script type="module" crossorigin src="/assets/index-tKUolDFn.js"></script>
+    <link rel="stylesheet" crossorigin href="/assets/index-BKdrvUCu.css">
   </head>
   </head>
   <body>
   <body>
     <div id="root"></div>
     <div id="root"></div>

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio