mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-18 21:47:39 +02:00
feat(inventory): sort-by-colour toggle in label-print modal (#1410)
Reporter asked for an option to order printed label sheets by colour instead of spool number so multi-colour rolls group related colours together physically on the sheet. Backend (labels.py) already preserves caller order, so this is frontend-only. LabelTemplatePickerModal gains a "Sort: By ID / By colour" chip pair next to the material filter. Colour mode converts each spool's rgba to HSL: chromatic colours (s >= 0.1) cluster in bucket 0 ordered by hue 0..360, achromatic colours go in bucket 1 ordered by lightness so neutrals trail the rainbow black -> white. Stable tiebreaker on spool ID. Also fixes a latent issue exposed by the same code: the submit was always re-sorting selected IDs ascending, which would have clobbered any frontend order. Submit now uses sortedSpools.filter().map() so the visible order flows through to the PDF. Session-only state; toggle resets to "By ID" each time the modal opens. 3 new i18n keys translated across all 8 locales (parity 4852 leaves). 2 new modal tests pin the colour-sort payload order and the unchanged ID-default. 17 modal tests + i18n parity + build all green.
This commit is contained in:
@@ -319,4 +319,64 @@ describe('LabelTemplatePickerModal', () => {
|
||||
expect(spoolListScroller!.className).toContain('min-h-0');
|
||||
expect(spoolListScroller!.className).not.toMatch(/min-h-\[\d/);
|
||||
});
|
||||
|
||||
// #1410: an "ID | colour" sort toggle in the modal must flow through to the
|
||||
// PDF — the backend (labels.py) prints in the order it receives spool_ids,
|
||||
// so the modal's "submit in ID order" default was forcing every PDF to
|
||||
// appear in spool-number order regardless of user choice. Toggling to
|
||||
// colour mode must reorder both the visible list AND the payload so the
|
||||
// printed sheet groups colours together.
|
||||
it('sorts the submit payload by HSL hue when sort mode is "By colour" (#1410)', async () => {
|
||||
vi.mocked(api.printSpoolLabels).mockResolvedValue(PDF_BLOB);
|
||||
render(
|
||||
<LabelTemplatePickerModal
|
||||
isOpen={true}
|
||||
onClose={vi.fn()}
|
||||
availableSpools={SPOOLS}
|
||||
initialSelectedIds={[1, 2, 3, 4]} // Red / Blue / Black / Ivory all picked
|
||||
spoolmanMode={false}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Default is ID-sorted; flip to colour.
|
||||
fireEvent.click(screen.getByRole('button', { name: 'By colour' }));
|
||||
fireEvent.click(screen.getByText(/Box label \(62 × 29 mm\)/i));
|
||||
|
||||
await waitFor(() => {
|
||||
// Expected colour-sort order for the SPOOLS fixture:
|
||||
// Red (1) — hue 0° — chromatic
|
||||
// Ivory (4) — hue ≈34° — chromatic
|
||||
// Blue (2) — hue 240° — chromatic
|
||||
// Black (3) — saturation ≈0 → neutrals bucket, lightness 0 → last
|
||||
// Rainbow first, then neutrals (dark→light) per design choice for #1410.
|
||||
expect(api.printSpoolLabels).toHaveBeenCalledWith({
|
||||
spool_ids: [1, 4, 2, 3],
|
||||
template: 'box_62x29',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps ID-order submission by default (#1410 regression guard)', async () => {
|
||||
// Adding the sort toggle must NOT change the default behaviour — IDs go
|
||||
// in ascending order unless the user explicitly clicks "By colour".
|
||||
vi.mocked(api.printSpoolLabels).mockResolvedValue(PDF_BLOB);
|
||||
render(
|
||||
<LabelTemplatePickerModal
|
||||
isOpen={true}
|
||||
onClose={vi.fn()}
|
||||
availableSpools={SPOOLS}
|
||||
initialSelectedIds={[1, 2, 3, 4]}
|
||||
spoolmanMode={false}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByText(/Box label \(40 × 30 mm\)/i));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(api.printSpoolLabels).toHaveBeenCalledWith({
|
||||
spool_ids: [1, 2, 3, 4],
|
||||
template: 'box_40x30',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user