From 8510c1180a16427f2d0a2c14fd85ce3483196450 Mon Sep 17 00:00:00 2001 From: Nico Date: Thu, 4 Jun 2026 13:01:22 -0700 Subject: [PATCH] fix: keep bought map items in layer state --- frontend/src/pages/LocationMapManager.jsx | 15 ++++++- frontend/tests/location-map-manager.spec.ts | 44 +++++++++++++++++---- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/frontend/src/pages/LocationMapManager.jsx b/frontend/src/pages/LocationMapManager.jsx index 532e542..e4627ad 100644 --- a/frontend/src/pages/LocationMapManager.jsx +++ b/frontend/src/pages/LocationMapManager.jsx @@ -51,6 +51,13 @@ function getLocallyReducedMapItem(item, quantity) { }; } +function getLocallyBoughtMapItem(item) { + return { + ...item, + bought: true, + }; +} + export default function LocationMapManager() { const { storeId, locationId } = useParams(); const navigate = useNavigate(); @@ -129,7 +136,7 @@ export default function LocationMapManager() { [filters, mapItems, username] ); const buyModalItems = useMemo( - () => (selectedObject ? selectedZoneItems : visibleUnmappedItems), + () => (selectedObject ? selectedZoneItems : visibleUnmappedItems).filter((item) => !item.bought), [selectedObject, selectedZoneItems, visibleUnmappedItems] ); const { visibleAssignedItemCount, unmappedItemCount } = useMemo(() => { @@ -190,7 +197,11 @@ export default function LocationMapManager() { let nextBuyModalItems = buyModalItems; if (quantity >= item.quantity) { nextBuyModalItems = buyModalItems.filter((candidate) => candidate.id !== item.id); - updateMapItems((currentItems) => currentItems.filter((candidate) => candidate.id !== item.id)); + updateMapItems((currentItems) => + currentItems.map((candidate) => ( + candidate.id === item.id ? getLocallyBoughtMapItem(candidate) : candidate + )) + ); } else { let updatedItem; try { diff --git a/frontend/tests/location-map-manager.spec.ts b/frontend/tests/location-map-manager.spec.ts index fa5cac0..37edfa6 100644 --- a/frontend/tests/location-map-manager.spec.ts +++ b/frontend/tests/location-map-manager.spec.ts @@ -2339,8 +2339,8 @@ test("viewer can buy selected zone items from the map", async ({ page }) => { quantity_bought: 1, }); await expect(page.locator(".confirm-buy-item-name")).toHaveText("sourdough"); - await expect(page.locator(".location-map-sheet-count")).toHaveText("1 item"); - await expect(bakeryArea.locator(".location-map-count")).toHaveText("1 item"); + await expect(page.locator(".location-map-sheet-count")).toHaveText("1 shown"); + await expect(bakeryArea.locator(".location-map-count")).toHaveText("1 shown"); await expect(page.locator(".location-map-zone-items")).not.toContainText("french bread"); await page.getByRole("button", { name: "Mark as Bought" }).click(); @@ -2352,9 +2352,15 @@ test("viewer can buy selected zone items from the map", async ({ page }) => { quantity_bought: 1, }); await expect(page.locator(".confirm-buy-modal")).toHaveCount(0); - await expect(page.locator(".location-map-sheet-count")).toHaveText("0 items"); - await expect(bakeryArea.locator(".location-map-count")).toHaveText("0 items"); - await expect(page.getByText("No items assigned to this zone yet.")).toBeVisible(); + await expect(page.locator(".location-map-sheet-count")).toHaveText("0 shown"); + await expect(bakeryArea.locator(".location-map-count")).toHaveText("0 shown"); + await expect(page.getByText("Items are assigned here, but hidden by layer filters.")).toBeVisible(); + + await page.getByRole("button", { name: "Layers" }).click(); + await page.getByRole("button", { name: "Bought" }).click(); + await expect(page.locator(".location-map-sheet-count")).toHaveText("2 items"); + await expect(page.getByRole("button", { name: "french bread bought" })).toBeDisabled(); + await expect(page.getByRole("button", { name: "sourdough bought" })).toBeDisabled(); }); test("viewer advances map buy modal after partial quantity buys", async ({ page }) => { @@ -2571,9 +2577,10 @@ test("viewer shows completed zone items without rebuy actions", async ({ page }) ...items[0], bought: true, }, + items[1], ], }; - let boughtRequestCount = 0; + const boughtPayloads: Array> = []; await page.route("**/households/1/locations/10/map", async (route) => { await route.fulfill({ @@ -2584,7 +2591,12 @@ test("viewer shows completed zone items without rebuy actions", async ({ page }) }); await page.route("**/households/1/locations/10/list/item", async (route) => { - boughtRequestCount += 1; + if (route.request().method() !== "PATCH") { + await route.fulfill({ status: 404, contentType: "application/json", body: "{}" }); + return; + } + + boughtPayloads.push(await route.request().postDataJSON()); await route.fulfill({ status: 200, contentType: "application/json", @@ -2605,7 +2617,23 @@ test("viewer shows completed zone items without rebuy actions", async ({ page }) await boughtRow.click({ force: true }); await expect(page.locator(".confirm-buy-modal")).toHaveCount(0); - expect(boughtRequestCount).toBe(0); + expect(boughtPayloads).toHaveLength(0); + + await page.getByRole("button", { name: "Mark sourdough bought" }).click(); + await expect(page.locator(".confirm-buy-item-name")).toHaveText("sourdough"); + await page.getByRole("button", { name: "Mark as Bought" }).click(); + + await expect.poll(() => boughtPayloads.length).toBe(1); + expect(boughtPayloads[0]).toMatchObject({ + item_name: "sourdough", + bought: true, + quantity_bought: 1, + }); + await expect(page.locator(".confirm-buy-modal")).toHaveCount(0); + const newlyBoughtRow = page.getByRole("button", { name: "sourdough bought" }); + await expect(newlyBoughtRow).toBeVisible(); + await expect(newlyBoughtRow).toBeDisabled(); + await expect(newlyBoughtRow.locator("small")).toHaveText("Bought"); }); test("viewer explains when unmapped items are hidden by filters", async ({ page }) => {