Browse Source

Allow spool assignment to empty AMS slots and fix profile label (#717)

  Previously the "Assign Spool" button only appeared on configured slots,
  forcing users to manually configure first — redundant since assignment
  auto-configures the slot. Now shows on empty slots too. Also fixed the
  AMS hover card showing generic material type instead of the spool's
  slicer preset name after assignment.
maziggy 2 months ago
parent
commit
b889de91ba

+ 1 - 0
CHANGELOG.md

@@ -11,6 +11,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **Configurable Drying Presets** ([#292](https://github.com/maziggy/bambuddy/issues/292)) — Customize temperature and duration for each filament type in Settings → Print Queue. Defaults match BambuStudio presets (PLA 55°C/8h, PETG 65°C/8h, etc.) and are used by both the manual drying popover and queue auto-drying. AMS 2 Pro and AMS-HT use separate presets reflecting their different heating capabilities.
 - **AMS PSU Detection** ([#292](https://github.com/maziggy/bambuddy/issues/292)) — The drying button is disabled with a tooltip when the AMS lacks sufficient power for drying (e.g. not connected to the external PSU). Reads `dry_sf_reason` from printer firmware and surfaces HMS error codes for AMS 2 Pro and AMS-HT power issues.
 - **Ambient Drying** ([#292](https://github.com/maziggy/bambuddy/issues/292)) — Automatically keep filament dry on idle printers based on humidity, even without queued prints. Enable "Ambient drying" in Settings → Print Queue to have the scheduler start drying on any idle printer whose AMS humidity exceeds the configured threshold — no scheduled prints required. Uses the same humidity threshold, drying presets, and power constraint detection as queue auto-drying. Both modes can be enabled simultaneously. Requested by community.
+- **Assign Spool to Empty AMS Slot** ([#717](https://github.com/maziggy/bambuddy/issues/717)) — Previously, the "Assign Spool" button only appeared on AMS slots that already had a filament profile configured, requiring users to first configure the slot manually before assigning an inventory spool — even though the assignment auto-configures the slot anyway. The "Assign Spool" option now appears on empty (unconfigured) slots as well. Selecting a spool auto-configures the slot with the correct filament profile, color, and K-profile in one step. Also fixed the AMS slot profile label showing the generic material type (e.g. "PLA") instead of the spool's actual slicer preset name (e.g. "PolyLite PLA Pro") after assignment. Requested by @RosdasHH.
 
 ### Fixed
 - **Library Upload Doesn't Show New File Until Page Reload** ([#704](https://github.com/maziggy/bambuddy/issues/704)) — After uploading a file in the Library file manager, the file list didn't update until the user reloaded the browser. The upload endpoint used `db.flush()` instead of `db.commit()`, so the new row was only written to the database *after* the response was sent to the client. The frontend immediately refetched the file list upon receiving the response, but a new database session couldn't see the uncommitted row — resulting in stale data. Fixed by committing before the response is returned. Also fixed the same race condition in folder create, folder update, and file update endpoints. Reported by @shadowjig.

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

@@ -467,12 +467,13 @@ interface EmptySlotHoverCardProps {
   children: ReactNode;
   className?: string;
   configureSlot?: ConfigureSlotConfig;
+  inventory?: InventoryConfig;
 }
 
 /**
  * Wrapper for empty slots - shows "Empty" on hover with optional configure button
  */
-export function EmptySlotHoverCard({ children, className = '', configureSlot }: EmptySlotHoverCardProps) {
+export function EmptySlotHoverCard({ children, className = '', configureSlot, inventory }: EmptySlotHoverCardProps) {
   const { t } = useTranslation();
   const [isVisible, setIsVisible] = useState(false);
   const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
@@ -529,6 +530,21 @@ export function EmptySlotHoverCard({ children, className = '', configureSlot }:
                 </button>
               </div>
             )}
+            {/* Assign spool button - allows assigning inventory spool to empty slot */}
+            {inventory?.onAssignSpool && (
+              <div className="px-2 pb-2">
+                <button
+                  onClick={(e) => {
+                    e.stopPropagation();
+                    inventory.onAssignSpool?.();
+                  }}
+                  className="w-full flex items-center justify-center gap-1.5 px-2 py-1.5 text-xs font-medium rounded transition-colors bg-bambu-blue/20 hover:bg-bambu-blue/30 text-bambu-blue"
+                >
+                  <Package className="w-3.5 h-3.5" />
+                  {t('inventory.assignSpool')}
+                </button>
+              </div>
+            )}
           </div>
           <div className="
             absolute left-1/2 -translate-x-1/2 top-full w-0 h-0

+ 39 - 3
frontend/src/pages/PrintersPage.tsx

@@ -3200,7 +3200,7 @@ function PrinterCard({
                                 // Build filament data for hover card
                                 const filamentData = tray?.tray_type ? {
                                   vendor: (isBambuLabSpool(tray) ? 'Bambu Lab' : 'Generic') as 'Bambu Lab' | 'Generic',
-                                  profile: slotPreset?.preset_name || cloudInfo?.name || tray.tray_sub_brands || tray.tray_type,
+                                  profile: slotPreset?.preset_name || cloudInfo?.name || inventoryAssignment?.spool?.slicer_filament_name || tray.tray_sub_brands || tray.tray_type,
                                   colorName: getBambuColorName(tray.tray_id_name) || hexToColorName(tray.tray_color),
                                   colorHex: tray.tray_color || null,
                                   kFactor: formatKValue(tray.k),
@@ -3367,6 +3367,18 @@ function PrinterCard({
                                             extruderId: mappedExtruderId,
                                           }),
                                         }}
+                                        inventory={spoolmanEnabled ? undefined : {
+                                          onAssignSpool: () => setAssignSpoolModal({
+                                            printerId: printer.id,
+                                            amsId: ams.id,
+                                            trayId: slotIdx,
+                                            trayInfo: {
+                                              type: '',
+                                              color: '',
+                                              location: `${getAmsLabel(ams.id, ams.tray.length)} Slot ${slotIdx + 1}`,
+                                            },
+                                          }),
+                                        }}
                                       >
                                         {slotVisual}
                                       </EmptySlotHoverCard>
@@ -3427,7 +3439,7 @@ function PrinterCard({
                         // Build filament data for hover card
                         const filamentData = tray?.tray_type ? {
                           vendor: (isBambuLabSpool(tray) ? 'Bambu Lab' : 'Generic') as 'Bambu Lab' | 'Generic',
-                          profile: slotPreset?.preset_name || cloudInfo?.name || tray.tray_sub_brands || tray.tray_type,
+                          profile: slotPreset?.preset_name || cloudInfo?.name || htInventoryAssignment?.spool?.slicer_filament_name || tray.tray_sub_brands || tray.tray_type,
                           colorName: getBambuColorName(tray.tray_id_name) || hexToColorName(tray.tray_color),
                           colorHex: tray.tray_color || null,
                           kFactor: formatKValue(tray.k),
@@ -3669,6 +3681,18 @@ function PrinterCard({
                                         extruderId: mappedExtruderId,
                                       }),
                                     }}
+                                    inventory={spoolmanEnabled ? undefined : {
+                                      onAssignSpool: () => setAssignSpoolModal({
+                                        printerId: printer.id,
+                                        amsId: ams.id,
+                                        trayId: htSlotId,
+                                        trayInfo: {
+                                          type: '',
+                                          color: '',
+                                          location: getAmsLabel(ams.id, ams.tray.length),
+                                        },
+                                      }),
+                                    }}
                                   >
                                     {slotVisual}
                                   </EmptySlotHoverCard>
@@ -3755,7 +3779,7 @@ function PrinterCard({
 
                               const extFilamentData = {
                                 vendor: (isBambuLabSpool(extTray) ? 'Bambu Lab' : 'Generic') as 'Bambu Lab' | 'Generic',
-                                profile: extSlotPreset?.preset_name || extCloudInfo?.name || extTray.tray_sub_brands || extTray.tray_type || 'Unknown',
+                                profile: extSlotPreset?.preset_name || extCloudInfo?.name || extInventoryAssignment?.spool?.slicer_filament_name || extTray.tray_sub_brands || extTray.tray_type || 'Unknown',
                                 colorName: getBambuColorName(extTray.tray_id_name) || hexToColorName(extTray.tray_color),
                                 colorHex: extTray.tray_color || null,
                                 kFactor: formatKValue(extTray.k),
@@ -3868,6 +3892,18 @@ function PrinterCard({
                                           extruderId: isDualNozzle ? (extTrayId === 254 ? 1 : 0) : undefined,
                                         }),
                                       }}
+                                      inventory={spoolmanEnabled ? undefined : {
+                                        onAssignSpool: () => setAssignSpoolModal({
+                                          printerId: printer.id,
+                                          amsId: 255,
+                                          trayId: slotTrayId,
+                                          trayInfo: {
+                                            type: '',
+                                            color: '',
+                                            location: extLabel || t('printers.external'),
+                                          },
+                                        }),
+                                      }}
                                     >
                                       {extSlotContent}
                                     </EmptySlotHoverCard>

File diff suppressed because it is too large
+ 0 - 0
static/assets/index-Ul7UFwCa.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-CyeSJFXC.js"></script>
+    <script type="module" crossorigin src="/assets/index-Ul7UFwCa.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-BgeMokkR.css">
   </head>
   <body>

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