mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-20 17:50:12 +02:00
Keep the tag when a spool is lifted during a Spoolman link or quick-add
In Spoolman mode, Assign Spool and Add to Inventory on the SpoolBuddy kiosk took the tag UID from the live reader state, which is cleared when the spool leaves the reader. Lifting it while a dialog was open made the link fail, and quick-add left an untagged spool behind. Use the tag on the card, as local inventory already does.
This commit is contained in:
@@ -846,6 +846,83 @@ describe('SpoolBuddyDashboard', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Spoolman mode, spool lifted while a dialog is open', () => {
|
||||
// The reader state is cleared when the tag leaves the reader; the card and
|
||||
// its dialogs stay. Linking must use the tag the card shows.
|
||||
function renderLiftable() {
|
||||
const setterRef: { current: React.Dispatch<React.SetStateAction<typeof mockOutletContext.sbState>> | null } = { current: null };
|
||||
function DynWrapper() {
|
||||
const [sbState, setSbState] = React.useState({ ...mockOutletContext.sbState, unknownTagUid: 'AABB1122334455FF' });
|
||||
setterRef.current = setSbState;
|
||||
return <Outlet context={{ ...mockOutletContext, sbState }} />;
|
||||
}
|
||||
const qc = new QueryClient({ defaultOptions: { queries: { retry: false, gcTime: 0 } } });
|
||||
render(
|
||||
<ToastProvider>
|
||||
<QueryClientProvider client={qc}>
|
||||
<MemoryRouter initialEntries={['/spoolbuddy']}>
|
||||
<Routes>
|
||||
<Route element={<DynWrapper />}>
|
||||
<Route path="spoolbuddy" element={<SpoolBuddyDashboard />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
</QueryClientProvider>
|
||||
</ToastProvider>
|
||||
);
|
||||
return () => act(() => setterRef.current!((prev) => ({ ...prev, unknownTagUid: null, unknownTrayUuid: null })));
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
const { api } = await import('../../api/client');
|
||||
(api.getSpoolmanSettings as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
spoolman_enabled: 'true',
|
||||
spoolman_url: 'http://localhost:7912',
|
||||
spoolman_sync_mode: 'off',
|
||||
spoolman_disable_weight_sync: 'false',
|
||||
spoolman_report_partial_usage: 'false',
|
||||
});
|
||||
(api.getSpoolmanInventorySpools as ReturnType<typeof vi.fn>).mockResolvedValue([
|
||||
{ id: 30, material: 'TPU', brand: 'Polymaker', tag_uid: null, tray_uuid: null, archived_at: null, color_name: 'Orange', rgba: 'FF6600FF', subtype: null, label_weight: 1000, core_weight: 250, weight_used: 0 },
|
||||
]);
|
||||
});
|
||||
|
||||
it('links the tag shown on the card', async () => {
|
||||
const { api } = await import('../../api/client');
|
||||
const lift = renderLiftable();
|
||||
|
||||
fireEvent.click(await waitFor(() => screen.getByText('Assign Spool')));
|
||||
fireEvent.click(await waitFor(() => screen.getByText('Orange')));
|
||||
await waitFor(() => screen.getByText('Link Tag'));
|
||||
lift();
|
||||
fireEvent.click(screen.getByText('Link Tag'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(api.linkTagToSpoolmanSpool).toHaveBeenCalledWith(30, {
|
||||
tag_uid: 'AABB1122334455FF',
|
||||
tray_uuid: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('quick-adds the spool with the tag shown on the card', async () => {
|
||||
const { api } = await import('../../api/client');
|
||||
const lift = renderLiftable();
|
||||
|
||||
fireEvent.click(await waitFor(() => screen.getAllByText('Add to Inventory')[0]));
|
||||
await waitFor(() => screen.getByText('Add Anyway'));
|
||||
lift();
|
||||
fireEvent.click(screen.getByText('Add Anyway'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(api.linkTagToSpoolmanSpool).toHaveBeenCalledWith(4, {
|
||||
tag_uid: 'AABB1122334455FF',
|
||||
tray_uuid: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Spoolman mode', () => {
|
||||
const SPOOLMAN_SPOOL = {
|
||||
id: 42, material: 'PLA', subtype: null, brand: 'Bambu',
|
||||
|
||||
@@ -366,13 +366,12 @@ export function SpoolBuddyDashboard() {
|
||||
if (spoolmanMode) {
|
||||
// The tray UUID wins when both are sent: it is what the AMS stores in
|
||||
// extra.tag, and the same on both tags of a Bambu spool (#984).
|
||||
const tag_uid = sbState.unknownTagUid || undefined;
|
||||
const tray_uuid = displayedTrayUuid || undefined;
|
||||
if (!tag_uid && !tray_uuid) {
|
||||
showToast(t('spoolman.linkFailed'), 'error');
|
||||
return;
|
||||
}
|
||||
const raw = await api.linkTagToSpoolmanSpool(spool.id, { tray_uuid, tag_uid });
|
||||
// Both come from the card, not the reader: the live tag state is
|
||||
// cleared when the spool is lifted while the dialog is open.
|
||||
const raw = await api.linkTagToSpoolmanSpool(spool.id, {
|
||||
tag_uid: displayedTagId,
|
||||
tray_uuid: displayedTrayUuid || undefined,
|
||||
});
|
||||
const updated = raw as InventorySpool | undefined;
|
||||
if (!updated) {
|
||||
showToast(t('spoolman.linkFailed'), 'error');
|
||||
@@ -463,7 +462,7 @@ export function SpoolBuddyDashboard() {
|
||||
low_stock_threshold_pct: null,
|
||||
} as Parameters<typeof api.createSpoolmanInventorySpool>[0]);
|
||||
await api.linkTagToSpoolmanSpool(created.id, {
|
||||
tag_uid: sbState.unknownTagUid || undefined,
|
||||
tag_uid: displayedTagId,
|
||||
tray_uuid: displayedTrayUuid || undefined,
|
||||
});
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user