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();