diff --git a/src/screens/ActivePickListScreen.test.tsx b/src/screens/ActivePickListScreen.test.tsx index c228748..20a8ee9 100644 --- a/src/screens/ActivePickListScreen.test.tsx +++ b/src/screens/ActivePickListScreen.test.tsx @@ -174,6 +174,66 @@ describe('ActivePickListScreen product search', () => { ]); }); + it('deduplicates product options with the same id', async () => { + const duplicateProducts = [...defaultProducts, { ...defaultProducts[0] }]; + productsMock.mockReturnValue(duplicateProducts); + + const user = userEvent.setup(); + + render( + + + } /> + + , + ); + + const combobox = screen.getByRole('combobox'); + await user.click(combobox); + + const listbox = await screen.findByRole('listbox'); + const options = within(listbox).getAllByRole('option'); + + expect(options).toHaveLength(3); + expect(options.map((option) => option.textContent)).toEqual([ + 'Apple Juice (Drinks)', + 'Chips (Snacks)', + 'Cola (Drinks)', + ]); + }); + + it('deduplicates product options with the same name', async () => { + const duplicateNameProducts: Product[] = [ + ...defaultProducts, + { ...defaultProducts[0], id: 'prod-duplicate', barcode: '999', updated_at: 5 }, + ]; + + productsMock.mockReturnValue(duplicateNameProducts); + + const user = userEvent.setup(); + + render( + + + } /> + + , + ); + + const combobox = screen.getByRole('combobox'); + await user.click(combobox); + + const listbox = await screen.findByRole('listbox'); + const options = within(listbox).getAllByRole('option'); + + expect(options).toHaveLength(3); + expect(options.map((option) => option.textContent)).toEqual([ + 'Apple Juice (Drinks)', + 'Chips (Snacks)', + 'Cola (Drinks)', + ]); + }); + it('updates an existing pick item when the same packaging is selected', async () => { pickItemsMock.mockReturnValue([ { diff --git a/src/screens/ActivePickListScreen.tsx b/src/screens/ActivePickListScreen.tsx index 6c243ce..cf0fd4b 100644 --- a/src/screens/ActivePickListScreen.tsx +++ b/src/screens/ActivePickListScreen.tsx @@ -48,35 +48,22 @@ export const ActivePickListScreen = () => { [areas, pickList?.area_id], ); - const productMap = useMemo( - () => new Map(products.map((product) => [product.id, product])), - [products], - ); + const sortedProducts = useMemo(() => { + const uniqueProducts = new Map(); - const sortedProducts = useMemo( - () => - [...products].sort((a, b) => - a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }), - ), - [products], - ); + products.forEach((product) => { + const normalizedName = product.name.trim().toLowerCase(); + const existing = uniqueProducts.get(normalizedName); - const sortedItems = useMemo(() => { - return [...items].sort((a, b) => { - const productA = productMap.get(a.product_id); - const productB = productMap.get(b.product_id); - - const nameA = productA?.name.toLowerCase() ?? ''; - const nameB = productB?.name.toLowerCase() ?? ''; - - const nameComparison = nameA.localeCompare(nameB); - if (nameComparison !== 0) return nameComparison; - - if (a.is_carton !== b.is_carton) return Number(a.is_carton) - Number(b.is_carton); - - return (a.created_at ?? 0) - (b.created_at ?? 0); + if (!existing || product.updated_at > existing.updated_at) { + uniqueProducts.set(normalizedName, product); + } }); - }, [items, productMap]); + + return Array.from(uniqueProducts.values()).sort((a, b) => + a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }), + ); + }, [products]); const filteredProducts = useMemo(() => { const normalizedQuery = query.trim().toLowerCase();