Browse Source

Show inventory sidebar item for both Spoolman and internal inventory

Previously the Inventory sidebar item was hidden when Spoolman was
enabled. Now it always shows — when Spoolman is active, /inventory
embeds the Spoolman web UI in the content area via iframe; when
disabled, it renders the internal inventory page as before.
maziggy 3 months ago
parent
commit
3c72f7cb22

+ 1 - 0
CHANGELOG.md

@@ -43,6 +43,7 @@ All notable changes to Bambuddy will be documented in this file.
 
 ### Improved
 - **Clear Plate Dot Indicator on Sidebar** — When the print queue is active and a printer finishes or fails with a pending next job, a small yellow dot now appears on the Printers sidebar icon to signal that user action (clearing the build plate) is needed. The indicator reuses the existing WebSocket-driven printer status cache, so no additional API polling is required. The dot disappears once the plate is cleared or the queue empties.
+- **Inventory Sidebar Always Visible** — The Inventory sidebar item is no longer hidden when Spoolman is enabled. Instead, clicking it embeds the Spoolman web UI in the main content area via iframe (same approach as external links). When Spoolman is disabled, the internal inventory page is shown as before. Both modes use the same `/inventory` route and sidebar position.
 - **Filament Override Test Coverage** — Added 11 backend unit tests: 6 for `_count_override_color_matches` (no status, exact match, no match, partial match, color normalization, external spool) and 5 for override application in filament matching (color override, tray_info_idx clearing, type change, partial override, nozzle filtering with override). Added 12 frontend tests for the `FilamentOverride` component: 5 rendering tests (null guards, slot display, dropdown count), 2 type filtering tests (same-type only, all colors), 3 nozzle filtering tests (extruder_id matching, single-nozzle passthrough, null extruder_id inclusion), and 2 interaction tests (select override, reset to original).
 - **P2S Dual-AMS tray_now Test Coverage** — Added 14 integration tests for multi-AMS tray_now disambiguation on single-nozzle printers (resolving AMS-B slots via mapping field, AMS-A passthrough, multi-color mapping, ambiguous/missing mapping fallbacks, last_loaded_tray tracking). Added 9 unit tests for `_resolve_local_slot_from_mapping` (snow decoding, unmapped entry filtering, ambiguity detection, AMS-HT slot matching). All 66 tray_now-related tests pass.
 - **Bulk Spool, Stock & Grouping Test Coverage** — Added 13 backend unit tests covering `SpoolBulkCreate` schema validation (quantity bounds, field preservation, stock vs configured distinction) and bulk endpoint logic (correct spool count, single quantity, identical fields). Added 29 frontend tests: 13 for `SpoolFormModal` covering `validateForm` with `quickAdd` flag (6 tests), quick-add toggle visibility, PA Profile tab hiding, quantity field gating (hidden by default, visible only in quick-add, hidden in edit mode), and brand/subtype optional asterisk removal in quick-add; 16 for inventory grouping logic covering `spoolGroupKey` identity/differentiation (7 tests) and `computeDisplayItems` grouping rules (9 tests for identical/different/used/assigned/single/order/mixed/empty scenarios).

+ 0 - 12
frontend/src/components/Layout.tsx

@@ -119,13 +119,6 @@ export function Layout() {
     refetchInterval: 60 * 60 * 1000, // Check every hour
   });
 
-  // Fetch Spoolman settings to determine if inventory should be hidden
-  const { data: spoolmanSettings } = useQuery({
-    queryKey: ['spoolman-settings'],
-    queryFn: api.getSpoolmanSettings,
-    staleTime: 5 * 60 * 1000,
-  });
-
   // Fetch external links for sidebar
   const { data: externalLinks } = useQuery({
     queryKey: ['external-links'],
@@ -229,13 +222,9 @@ export function Layout() {
 
     // Determine if settings should be hidden (user role and auth enabled)
     const hideSettings = authEnabled && user?.role === 'user';
-    // Hide inventory when Spoolman mode is active
-    const hideInventory = spoolmanSettings?.spoolman_enabled === 'true';
-
     // Add items in stored order
     for (const id of sidebarOrder) {
       if (hideSettings && id === 'settings') continue;
-      if (hideInventory && id === 'inventory') continue;
       if (navItemsMap.has(id) || extLinksMap.has(id)) {
         result.push(id);
         seen.add(id);
@@ -245,7 +234,6 @@ export function Layout() {
     // Add any new internal nav items not in stored order
     for (const item of defaultNavItems) {
       if (hideSettings && item.id === 'settings') continue;
-      if (hideInventory && item.id === 'inventory') continue;
       if (!seen.has(item.id)) {
         result.push(item.id);
         seen.add(item.id);

+ 23 - 1
frontend/src/pages/InventoryPage.tsx

@@ -337,7 +337,29 @@ function saveSortState(state: SortState) {
   } catch { /* ignore */ }
 }
 
-export default function InventoryPage() {
+// Wrapper: when Spoolman is enabled, embed its UI; otherwise show internal inventory
+export default function InventoryPageRouter() {
+  const { data: spoolmanSettings } = useQuery({
+    queryKey: ['spoolman-settings'],
+    queryFn: api.getSpoolmanSettings,
+    staleTime: 5 * 60 * 1000,
+  });
+
+  if (spoolmanSettings?.spoolman_enabled === 'true' && spoolmanSettings?.spoolman_url) {
+    return (
+      <iframe
+        src={spoolmanSettings.spoolman_url}
+        className="h-full w-full border-0"
+        title="Spoolman"
+        sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"
+      />
+    );
+  }
+
+  return <InventoryPage />;
+}
+
+function InventoryPage() {
   const { t } = useTranslation();
   const queryClient = useQueryClient();
   const { showToast } = useToast();

File diff suppressed because it is too large
+ 0 - 0
static/assets/index-D_M_iIn2.js


+ 1 - 1
static/index.html

@@ -23,7 +23,7 @@
 
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-Cxub00Jo.js"></script>
+    <script type="module" crossorigin src="/assets/index-D_M_iIn2.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-COFMpymr.css">
   </head>
   <body>

Some files were not shown because too many files changed in this diff