Forráskód Böngészése

fix(spoolbuddy): inventory search matches spool ID + storage location (#1738)

  The SpoolBuddy inventory page reimplemented its filter inline and only
  matched material/subtype/brand/color_name/note, while Bambuddy's main
  inventory uses the shared filterSpoolsByQuery helper which also matches
  spool ID, slicer_filament_name, and storage_location. Delegate to the
  shared helper so both pages stay in lockstep.

  - frontend/src/pages/spoolbuddy/SpoolBuddyInventoryPage.tsx: replace
    inline filter with filterSpoolsByQuery
  - frontend/src/__tests__/pages/SpoolBuddyInventorySearch.test.ts: lock
    in ID / partial ID / pre-fix fields / parity-gain fields
maziggy 2 hónapja
szülő
commit
b97f25d819

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2 - 0
CHANGELOG.md


+ 88 - 0
frontend/src/__tests__/pages/SpoolBuddyInventorySearch.test.ts

@@ -0,0 +1,88 @@
+/**
+ * Regression test for #1738 — SpoolBuddy's inventory search must match by
+ * numeric spool ID, just like Bambuddy's main InventoryPage. Both pages now
+ * share `filterSpoolsByQuery` so behaviour stays in lockstep; this test fails
+ * loudly if SpoolBuddyInventoryPage ever re-inlines its filter and drops
+ * fields.
+ */
+
+import { describe, it, expect } from 'vitest';
+import type { InventorySpool } from '../../api/client';
+import { filterSpoolsByQuery } from '../../utils/inventorySearch';
+
+function makeSpool(overrides: Partial<InventorySpool> & { id: number }): InventorySpool {
+  return {
+    material: 'PLA',
+    subtype: 'Basic',
+    brand: 'Bambu Lab',
+    color_name: 'White',
+    rgba: 'FFFFFFFF',
+    label_weight: 1000,
+    core_weight: 250,
+    core_weight_catalog_id: null,
+    weight_used: 0,
+    weight_locked: false,
+    slicer_filament: null,
+    slicer_filament_name: null,
+    nozzle_temp_min: null,
+    nozzle_temp_max: null,
+    note: null,
+    added_full: null,
+    last_used: null,
+    encode_time: null,
+    tag_uid: null,
+    tray_uuid: null,
+    data_origin: null,
+    tag_type: null,
+    archived_at: null,
+    created_at: '2025-01-01T00:00:00Z',
+    updated_at: '2025-01-01T00:00:00Z',
+    k_profiles: [],
+    cost_per_kg: null,
+    last_scale_weight: null,
+    last_weighed_at: null,
+    storage_location: null,
+    ...overrides,
+  };
+}
+
+describe('SpoolBuddyInventoryPage search filter (#1738)', () => {
+  it('matches an exact spool ID', () => {
+    const spools = [
+      makeSpool({ id: 1336 }),
+      makeSpool({ id: 1337 }),
+      makeSpool({ id: 42 }),
+    ];
+    const result = filterSpoolsByQuery(spools, '1336');
+    expect(result.map((s) => s.id)).toEqual([1336]);
+  });
+
+  it('matches a partial spool ID', () => {
+    const spools = [
+      makeSpool({ id: 100 }),
+      makeSpool({ id: 200 }),
+      makeSpool({ id: 1001 }),
+    ];
+    const result = filterSpoolsByQuery(spools, '00');
+    expect(result.map((s) => s.id).sort((a, b) => a - b)).toEqual([100, 200, 1001]);
+  });
+
+  it('still matches by the existing fields SpoolBuddy supported pre-fix', () => {
+    const spools = [
+      makeSpool({ id: 1, material: 'PLA', brand: 'Bambu Lab', color_name: 'Red' }),
+      makeSpool({ id: 2, material: 'PETG', brand: 'Polymaker', color_name: 'Blue' }),
+    ];
+    expect(filterSpoolsByQuery(spools, 'polymaker').map((s) => s.id)).toEqual([2]);
+    expect(filterSpoolsByQuery(spools, 'PLA').map((s) => s.id)).toEqual([1]);
+    expect(filterSpoolsByQuery(spools, 'red').map((s) => s.id)).toEqual([1]);
+  });
+
+  it('also matches by storage_location and slicer_filament_name (parity gain)', () => {
+    const spools = [
+      makeSpool({ id: 1, storage_location: 'IKEA Regal' }),
+      makeSpool({ id: 2, slicer_filament_name: 'Generic PLA Matte' }),
+    ];
+    expect(filterSpoolsByQuery(spools, 'IKEA').map((s) => s.id)).toEqual([1]);
+    expect(filterSpoolsByQuery(spools, 'Matte').map((s) => s.id)).toEqual([2]);
+  });
+});

+ 2 - 10
frontend/src/pages/spoolbuddy/SpoolBuddyInventoryPage.tsx

@@ -7,6 +7,7 @@ import { api } from '../../api/client';
 import type { InventorySpool } from '../../api/client';
 import { resolveSpoolColorName, getSwatchStyle, spoolColorString } from '../../utils/colors';
 import { formatSlotLabel } from '../../utils/amsHelpers';
+import { filterSpoolsByQuery } from '../../utils/inventorySearch';
 import { InventorySpoolInfoCard } from '../../components/spoolbuddy/InventorySpoolInfoCard';
 import { AssignToAmsModal } from '../../components/spoolbuddy/AssignToAmsModal';
 import type { SpoolBuddyOutletContext } from '../../components/spoolbuddy/SpoolBuddyLayout';
@@ -144,16 +145,7 @@ export function SpoolBuddyInventoryPage() {
       list = list.filter(s => s.material === filterMode);
     }
 
-    if (searchQuery.trim()) {
-      const q = searchQuery.toLowerCase().trim();
-      list = list.filter(s =>
-        s.material.toLowerCase().includes(q) ||
-        (s.subtype && s.subtype.toLowerCase().includes(q)) ||
-        (s.brand && s.brand.toLowerCase().includes(q)) ||
-        (s.color_name && s.color_name.toLowerCase().includes(q)) ||
-        (s.note && s.note.toLowerCase().includes(q))
-      );
-    }
+    list = filterSpoolsByQuery(list, searchQuery.trim());
 
     // Sort: assigned spools first (by slot label), then by most recently updated
     return [...list].sort((a, b) => {

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
static/assets/index-lB37rzBj.js


+ 1 - 1
static/index.html

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

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott