From 3bce9c0a15dc361c217ce15c0edfad8d57a4d897 Mon Sep 17 00:00:00 2001 From: Sanidhya <151203073+Sanidhyavijay24@users.noreply.github.com> Date: Sat, 13 Jun 2026 09:00:42 +0530 Subject: [PATCH] Remember last used label template and printer plugin per model type (#12146) * Remember last used label template and printer plugin per model type * Added playwright test for printing preference persistence and format code * Used waitFor for react-select assertions for test case fixing and persist plugin key correctly * Updated the problem resolution code and the tests * Fixed printing test assertion to avoid word boundary failure with concatenated text --- .../components/buttons/PrintingActions.tsx | 28 ++++++++++++-- src/frontend/src/states/LocalState.tsx | 25 +++++++++++++ src/frontend/tests/pui_printing.spec.ts | 37 ++++++++++++++++++- 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/src/frontend/src/components/buttons/PrintingActions.tsx b/src/frontend/src/components/buttons/PrintingActions.tsx index 19e8b08e9d..678a336f33 100644 --- a/src/frontend/src/components/buttons/PrintingActions.tsx +++ b/src/frontend/src/components/buttons/PrintingActions.tsx @@ -11,6 +11,7 @@ import { api } from '../../App'; import { extractAvailableFields } from '../../functions/forms'; import useDataOutput from '../../hooks/UseDataOutput'; import { useCreateApiFormModal } from '../../hooks/UseForm'; +import { useLocalState } from '../../states/LocalState'; import { useGlobalSettingsState, useUserSettingsState @@ -32,6 +33,11 @@ export function PrintingActions({ }) { const userSettings = useUserSettingsState(); const globalSettings = useGlobalSettingsState(); + const localState = useLocalState(); + + const lastUsedPrinting = useMemo(() => { + return modelType ? localState.lastUsedPrinting[modelType] : undefined; + }, [localState.lastUsedPrinting, modelType]); const enabled = useMemo(() => items.length > 0, [items]); @@ -118,6 +124,7 @@ export function PrintingActions({ fields.template = { ...fields.template, autoFill: true, + value: lastUsedPrinting?.template, filters: { enabled: true, model_type: modelType, @@ -147,7 +154,14 @@ export function PrintingActions({ }; return fields; - }, [defaultLabelPlugin, pluginKey, printingFields.data, itemIdList]); + }, [ + defaultLabelPlugin, + pluginKey, + printingFields.data, + itemIdList, + lastUsedPrinting, + modelType + ]); const labelModal = useCreateApiFormModal({ url: apiUrl(ApiEndpoints.label_print), @@ -158,15 +172,21 @@ export function PrintingActions({ onOpen: () => { setLabelDialogOpen(true); setItemIdList(items); + setPluginKey(lastUsedPrinting?.plugin ?? null); }, onClose: () => { setLabelDialogOpen(false); - setPluginKey(''); }, submitText: t`Print`, successMessage: null, - onFormSuccess: (response: any) => { - setPluginKey(''); + onFormSuccess: (response: any, form: any) => { + if (modelType) { + const values = form?.getValues?.(); + localState.setLastUsedPrinting(modelType, { + plugin: pluginKey || undefined, + template: values?.template ? Number(values.template) : undefined + }); + } setLabelId(response.pk); } }); diff --git a/src/frontend/src/states/LocalState.tsx b/src/frontend/src/states/LocalState.tsx index f5c09b0751..5b043d44c0 100644 --- a/src/frontend/src/states/LocalState.tsx +++ b/src/frontend/src/states/LocalState.tsx @@ -33,6 +33,12 @@ interface LocalStateProps { setLayouts: (layouts: any, noPatch?: boolean) => void; showSampleDashboard: boolean; setShowSampleDashboard: (value: boolean) => void; + // printing + lastUsedPrinting: Record; + setLastUsedPrinting: ( + modelType: string, + values: { plugin?: string; template?: number } + ) => void; // panels lastUsedPanels: Record; setLastUsedPanel: (panelKey: string) => (value: string) => void; @@ -122,6 +128,25 @@ export const useLocalState = create()( setShowSampleDashboard: (value) => { set({ showSampleDashboard: value }); }, + // printing + lastUsedPrinting: {}, + setLastUsedPrinting: (modelType, values) => { + const current = get().lastUsedPrinting[modelType] || {}; + if ( + current.plugin !== values.plugin || + current.template !== values.template + ) { + set({ + lastUsedPrinting: { + ...get().lastUsedPrinting, + [modelType]: { + ...current, + ...values + } + } + }); + } + }, // panels lastUsedPanels: {}, setLastUsedPanel: (panelKey) => (value) => { diff --git a/src/frontend/tests/pui_printing.spec.ts b/src/frontend/tests/pui_printing.spec.ts index cbd99ae3f4..e7941fdde8 100644 --- a/src/frontend/tests/pui_printing.spec.ts +++ b/src/frontend/tests/pui_printing.spec.ts @@ -65,7 +65,42 @@ test('Printing - Label Printing', async ({ browser }) => { await page.getByRole('button', { name: 'Print', exact: true }).isEnabled(); await page.getByRole('button', { name: 'Print', exact: true }).click(); - await page.getByText('Process completed successfully').first().waitFor(); + const successMessage = page + .getByText('Process completed successfully') + .first(); + await successMessage.waitFor(); + await successMessage.waitFor({ state: 'hidden' }); + + // Re-open print dialog to verify persistence (issue #12129) + await page + .getByLabel('Stock Items') + .getByLabel('action-menu-printing-actions') + .click(); + await page.getByLabel('action-menu-printing-actions-print-labels').click(); + + const labelDialog = page.getByRole('dialog', { name: 'Print Label' }); + + // Wait for the dialog to fully load + await labelDialog.getByLabel('related-field-template').waitFor(); + await labelDialog.getByLabel('related-field-plugin').waitFor(); + + // Verify the last-used template is preselected + await expect(labelDialog).toContainText('InvenTree Stock Item Label'); + + // Verify the last-used plugin is preselected + await expect(labelDialog).toContainText('InvenTreeLabel'); + + // Submit again without re-selecting template or plugin + const printResponse = page.waitForResponse( + (response) => + response.url().includes('/api/label/print/') && + response.request().method() === 'POST' && + response.ok() + ); + await labelDialog.getByRole('button', { name: 'Print', exact: true }).click(); + + await printResponse; + await page.context().close(); });