diff --git a/CHANGELOG.md b/CHANGELOG.md index 0de8d0acf..937635dc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to Bambuddy will be documented in this file. ### Improved - **SpoolBuddy AMS Slot Action Picker** — Clicking an AMS slot on the SpoolBuddy AMS page now shows a picker with contextual actions: Configure AMS Slot (set filament preset, K-profile, color), and either Assign Spool / Link to Spoolman (when no spool is mapped) or Unassign / Unlink (when one is). Works with both internal inventory and Spoolman. Previously the slot click went straight to the configure modal with no way to manage spool assignments. +- **Unassign Button in Edit Spool Modal** — The edit spool modal now has an "Unassign" button next to "Delete Tag" that removes the spool's AMS slot assignment, clearing the location column in the inventory table. - **SpoolBuddy Settings Device Tab No Longer Scrolls** — Removed the branding card, folded Device ID into the Device Info card, placed Backend/Auth config and diagnostic buttons side by side in a 2-column layout, removed the redundant online/offline status row from Device Info, and tightened spacing throughout. The Device tab now fits on the small SpoolBuddy touchscreen without scrolling. - **Spool Notes in Assign Spool Modal** ([#793](https://github.com/maziggy/bambuddy/issues/793)) — Spool cards in the Assign Spool modal now show the spool's note as a hover tooltip, making it easier to identify spools by tracking IDs or other metadata stored in notes. Works with both internal inventory and Spoolman-synced spools. Requested by @LegionCanadian. - **WiFi Safeguard for SpoolBuddy Pi** — The install script now drops an APT hook (`/etc/apt/apt.conf.d/80-preserve-wifi`) that backs up NetworkManager WiFi connections before every `apt upgrade` and restores them if they get wiped. Prevents headless SpoolBuddy Pis from losing WiFi connectivity after Raspberry Pi OS package upgrades (observed with Bookworm kernel/raspi-config updates that clear `/etc/NetworkManager/system-connections/`). diff --git a/frontend/src/components/SpoolFormModal.tsx b/frontend/src/components/SpoolFormModal.tsx index 89b789cf9..94618e387 100644 --- a/frontend/src/components/SpoolFormModal.tsx +++ b/frontend/src/components/SpoolFormModal.tsx @@ -1,7 +1,7 @@ import { useState, useEffect, useMemo } from 'react'; -import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; -import { X, Loader2, Save, Beaker, Palette, Zap, Tag } from 'lucide-react'; +import { X, Loader2, Save, Beaker, Palette, Zap, Tag, Unlink } from 'lucide-react'; import { api } from '../api/client'; import type { InventorySpool, SlicerSetting, SpoolCatalogEntry, LocalPreset } from '../api/client'; import { Button } from './Button'; @@ -383,6 +383,29 @@ export function SpoolFormModal({ }, }); + // Fetch assignment for this spool (to show Unassign button) + const { data: assignments } = useQuery({ + queryKey: ['spool-assignments'], + queryFn: () => api.getAssignments(), + enabled: isOpen && isEditing, + }); + const spoolAssignment = spool ? assignments?.find(a => a.spool_id === spool.id) : undefined; + + const unassignMutation = useMutation({ + mutationFn: () => { + if (!spoolAssignment) throw new Error('No assignment'); + return api.unassignSpool(spoolAssignment.printer_id, spoolAssignment.ams_id, spoolAssignment.tray_id); + }, + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: ['spool-assignments'] }); + showToast(t('inventory.unassignSuccess', 'Spool unassigned'), 'success'); + onClose(); + }, + onError: (error: Error) => { + showToast(error.message, 'error'); + }, + }); + // Save K-profiles for selected calibrations const saveKProfiles = async (spoolId: number) => { if (selectedProfiles.size === 0) { @@ -487,7 +510,7 @@ export function SpoolFormModal({ } }; - const isPending = createMutation.isPending || bulkCreateMutation.isPending || updateMutation.isPending || deleteTagMutation.isPending; + const isPending = createMutation.isPending || bulkCreateMutation.isPending || updateMutation.isPending || deleteTagMutation.isPending || unassignMutation.isPending; return (
@@ -646,15 +669,24 @@ export function SpoolFormModal({ {/* Footer */}
{isEditing && ( - +
+ + +
)}