Просмотр исходного кода

Name a spool by its subtype on the slot it is assigned to

    A spool's subtype is half of what it is called: "PLA" and "PLA Wood" are
    different filaments. The AMS slot's hover card built the assigned-spool
    line out of brand, material and colour name and left the subtype out, so
    a roll of Bambu PLA Wood Classic Birch in an H2C's A4 was announced as
    "Bambu Lab PLA - Classic Birch".

    Everything else named it correctly at the same moment -- the RFID read,
    the inventory row, the slot's own profile line, which is built from the
    spool's slicer preset rather than reassembled, and Bambu Studio -- so the
    one wrong line read like a bad tag read rather than a display fault.

    It was not only the render. The card's assignedSpool prop had no subtype
    field at all, and the six places the printer card fills it in -- regular
    AMS, AMS-HT and external spool, each in both Spoolman and internal-
    inventory mode -- never passed one, so the value could not reach the
    component. The field is required rather than optional, which is what
    stops the next call site from quietly omitting it; that omission is the
    whole of this bug.

    Three more surfaces rebuilt the name the same way and are fixed with it:
    the SpoolBuddy AMS slot panel in both inventory modes, and the write-tag
    confirmation. Every other place a spool is named -- the assign dialogs,
    the inventory cards, the forecast rows, the label picker -- already
    included the subtype, so these four were the outliers.

    This is the display-side half of #2902, which stopped the backend
    reducing a filled or foamed filament onto its base material. The card was
    doing the same thing to the same spools, one layer further out.
maziggy 2 недель назад
Родитель
Сommit
87e0a4c3b6

Разница между файлами не показана из-за своего большого размера
+ 1 - 0
CHANGELOG.md


+ 62 - 5
frontend/src/__tests__/components/FilamentHoverCard.test.tsx

@@ -205,6 +205,7 @@ describe('FilamentHoverCard', () => {
             assignedSpool: {
               id: 1,
               material: 'PLA',
+              subtype: null,
               brand: 'Devil Design',
               color_name: 'Black',
             },
@@ -280,6 +281,7 @@ describe('FilamentHoverCard', () => {
     const inventorySpool = {
       id: 42,
       material: 'PLA',
+      subtype: null,
       brand: 'eSun',
       color_name: 'Black',
     };
@@ -420,7 +422,7 @@ describe('FilamentHoverCard', () => {
         <FilamentHoverCard
           data={baseFilamentData}
           inventory={{
-            assignedSpool: { id: 7, material: 'PLA', brand: 'eSun', color_name: 'Black' },
+            assignedSpool: { id: 7, material: 'PLA', subtype: null, brand: 'eSun', color_name: 'Black' },
             onUnassignSpool,
           }}
         >
@@ -607,7 +609,7 @@ describe('FilamentHoverCard colour name (#2875)', () => {
         data={{ ...whiteMatte, colorName: 'Jade White' }}
         inventory={{
           isAssigned: true,
-          assignedSpool: { id: 17, material: 'PLA', brand: 'Bambu Lab', color_name: 'Matte Ivory White' },
+          assignedSpool: { id: 17, material: 'PLA', subtype: null, brand: 'Bambu Lab', color_name: 'Matte Ivory White' },
         }}
       >
         <div>trigger</div>
@@ -626,7 +628,7 @@ describe('FilamentHoverCard colour name (#2875)', () => {
         data={whiteMatte}
         inventory={{
           isAssigned: true,
-          assignedSpool: { id: 17, material: 'PLA', brand: 'Bambu Lab', color_name: 'A06-D0' },
+          assignedSpool: { id: 17, material: 'PLA', subtype: null, brand: 'Bambu Lab', color_name: 'A06-D0' },
         }}
       >
         <div>trigger</div>
@@ -646,7 +648,7 @@ describe('FilamentHoverCard colour name (#2875)', () => {
         data={whiteMatte}
         inventory={{
           isAssigned: true,
-          assignedSpool: { id: 17, material: 'PLA', brand: 'Bambu Lab', color_name: colorName },
+          assignedSpool: { id: 17, material: 'PLA', subtype: null, brand: 'Bambu Lab', color_name: colorName },
         }}
       >
         <div>trigger</div>
@@ -664,7 +666,7 @@ describe('FilamentHoverCard colour name (#2875)', () => {
         data={whiteMatte}
         inventory={{
           isAssigned: true,
-          assignedSpool: { id: 17, material: 'PLA', brand: 'Bambu Lab', color_name: null },
+          assignedSpool: { id: 17, material: 'PLA', subtype: null, brand: 'Bambu Lab', color_name: null },
         }}
       >
         <div>trigger</div>
@@ -674,3 +676,58 @@ describe('FilamentHoverCard colour name (#2875)', () => {
     await waitFor(() => expect(screen.getByText('Ivory White')).toBeInTheDocument());
   });
 });
+
+// A spool's subtype is part of its name. Dropping it made a wood-filled roll
+// read as plain PLA on the slot card, which is the display-side version of
+// the mistake #2902 fixed on the backend -- and the printer, the inventory
+// page and Studio all named it correctly at the same time, so the card was
+// the only thing saying otherwise.
+describe('FilamentHoverCard assigned spool name', () => {
+  beforeEach(() => {
+    vi.useFakeTimers({ shouldAdvanceTime: true });
+    __resetColorCatalogForTests();
+  });
+
+  function showAssigned(assignedSpool: {
+    id: number;
+    material: string;
+    subtype: string | null;
+    brand: string | null;
+    color_name: string | null;
+  }) {
+    renderWithHover(
+      <FilamentHoverCard data={baseFilamentData} inventory={{ isAssigned: true, assignedSpool }}>
+        <div>trigger</div>
+      </FilamentHoverCard>
+    );
+    vi.advanceTimersByTime(100);
+  }
+
+  it('names a filled filament by its subtype, not by its base material', async () => {
+    showAssigned({
+      id: 77,
+      material: 'PLA',
+      subtype: 'Wood',
+      brand: 'Bambu Lab',
+      color_name: 'Classic Birch',
+    });
+
+    await waitFor(() =>
+      expect(screen.getByText('Bambu Lab PLA Wood - Classic Birch')).toBeInTheDocument()
+    );
+  });
+
+  it('omits the subtype entirely for a spool that has none', async () => {
+    showAssigned({
+      id: 78,
+      material: 'PLA',
+      subtype: null,
+      brand: 'Bambu Lab',
+      color_name: 'Jade White',
+    });
+
+    await waitFor(() =>
+      expect(screen.getByText('Bambu Lab PLA - Jade White')).toBeInTheDocument()
+    );
+  });
+});

+ 5 - 1
frontend/src/components/FilamentHoverCard.tsx

@@ -33,7 +33,10 @@ interface SpoolmanConfig {
 interface InventoryConfig {
   onAssignSpool?: () => void;
   onUnassignSpool?: () => void;
-  assignedSpool?: { id: number; material: string; brand: string | null; color_name: string | null; remainingWeightGrams?: number | null } | null;
+  // `subtype` is part of the spool's name, not decoration: "PLA" and "PLA Wood"
+  // are different filaments, and a card that prints only the material tells the
+  // user their wood-filled roll is plain PLA (the display-side half of #2902).
+  assignedSpool?: { id: number; material: string; subtype: string | null; brand: string | null; color_name: string | null; remainingWeightGrams?: number | null } | null;
   isAssigned?: boolean;
 }
 
@@ -401,6 +404,7 @@ export function FilamentHoverCard({ data, children, disabled, className = '', sp
                         <p className="text-xs text-white truncate">
                           {inventory.assignedSpool.brand ? `${inventory.assignedSpool.brand} ` : ''}
                           {inventory.assignedSpool.material}
+                          {inventory.assignedSpool.subtype ? ` ${inventory.assignedSpool.subtype}` : ''}
                           {inventory.assignedSpool.color_name ? ` - ${inventory.assignedSpool.color_name}` : ''}
                         </p>
                         <span className="text-[10px] font-mono text-bambu-gray shrink-0">#{inventory.assignedSpool.id}</span>

+ 6 - 0
frontend/src/pages/PrintersPage.tsx

@@ -5573,6 +5573,7 @@ function PrinterCard({
                                               assignedSpool: spoolmanSpool ? {
                                                 id: spoolmanSpool.id,
                                                 material: spoolmanSpool.material,
+                                                subtype: spoolmanSpool.subtype,
                                                 brand: spoolmanSpool.brand ?? null,
                                                 color_name: spoolmanSpool.color_name ?? null,
                                                 remainingWeightGrams: spoolmanSpool.label_weight
@@ -5600,6 +5601,7 @@ function PrinterCard({
                                             assignedSpool: assignment?.spool ? {
                                               id: assignment.spool.id,
                                               material: assignment.spool.material,
+                                              subtype: assignment.spool.subtype,
                                               brand: assignment.spool.brand,
                                               color_name: assignment.spool.color_name,
                                               remainingWeightGrams: Math.max(0, Math.round(assignment.spool.label_weight - assignment.spool.weight_used)),
@@ -5961,6 +5963,7 @@ function PrinterCard({
                                           assignedSpool: spoolmanSpool ? {
                                             id: spoolmanSpool.id,
                                             material: spoolmanSpool.material,
+                                            subtype: spoolmanSpool.subtype,
                                             brand: spoolmanSpool.brand ?? null,
                                             color_name: spoolmanSpool.color_name ?? null,
                                             remainingWeightGrams: spoolmanSpool.label_weight
@@ -5988,6 +5991,7 @@ function PrinterCard({
                                         assignedSpool: assignment?.spool ? {
                                           id: assignment.spool.id,
                                           material: assignment.spool.material,
+                                          subtype: assignment.spool.subtype,
                                           brand: assignment.spool.brand,
                                           color_name: assignment.spool.color_name,
                                           remainingWeightGrams: Math.max(0, Math.round(assignment.spool.label_weight - assignment.spool.weight_used)),
@@ -6234,6 +6238,7 @@ function PrinterCard({
                                             assignedSpool: spoolmanSpool ? {
                                               id: spoolmanSpool.id,
                                               material: spoolmanSpool.material,
+                                              subtype: spoolmanSpool.subtype,
                                               brand: spoolmanSpool.brand ?? null,
                                               color_name: spoolmanSpool.color_name ?? null,
                                               remainingWeightGrams: spoolmanSpool.label_weight
@@ -6261,6 +6266,7 @@ function PrinterCard({
                                           assignedSpool: assignment?.spool ? {
                                             id: assignment.spool.id,
                                             material: assignment.spool.material,
+                                            subtype: assignment.spool.subtype,
                                             brand: assignment.spool.brand,
                                             color_name: assignment.spool.color_name,
                                             remainingWeightGrams: Math.max(0, Math.round(assignment.spool.label_weight - assignment.spool.weight_used)),

+ 2 - 0
frontend/src/pages/spoolbuddy/SpoolBuddyAmsPage.tsx

@@ -756,6 +756,7 @@ export function SpoolBuddyAmsPage() {
                       )}
                       <span className="text-sm text-white">
                         {assignment.spool.brand ? `${assignment.spool.brand} ` : ''}{assignment.spool.material}
+                        {assignment.spool.subtype ? ` ${assignment.spool.subtype}` : ''}
                         {assignment.spool.color_name ? ` - ${assignment.spool.color_name}` : ''}
                       </span>
                       <span className="text-[10px] font-mono text-zinc-500 shrink-0 ml-auto">#{assignment.spool.id}</span>
@@ -789,6 +790,7 @@ export function SpoolBuddyAmsPage() {
                       )}
                       <span className="text-sm text-white">
                         {spoolmanAssignedSpool.brand ? `${spoolmanAssignedSpool.brand} ` : ''}{spoolmanAssignedSpool.material}
+                        {spoolmanAssignedSpool.subtype ? ` ${spoolmanAssignedSpool.subtype}` : ''}
                         {spoolmanAssignedSpool.color_name ? ` - ${spoolmanAssignedSpool.color_name}` : ''}
                       </span>
                     </div>

+ 1 - 0
frontend/src/pages/spoolbuddy/SpoolBuddyWriteTagPage.tsx

@@ -1030,6 +1030,7 @@ function NfcStatusPanel({ writeStatus, writeMessage, selectedSpool, tagOnReader,
         {selectedSpool && (
           <p className="text-zinc-400 text-sm">
             {selectedSpool.brand ? `${selectedSpool.brand} ` : ''}{selectedSpool.material}
+            {selectedSpool.subtype ? ` ${selectedSpool.subtype}` : ''}
             {selectedSpool.color_name ? ` - ${selectedSpool.color_name}` : ''}
           </p>
         )}

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
static/assets/index-B6R8pq98.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-e8_8xCtK.js"></script>
+    <script type="module" crossorigin src="/assets/index-B6R8pq98.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-DjndScv6.css">
   </head>
   <body>

Некоторые файлы не были показаны из-за большого количества измененных файлов