fix: ignore stale map buy responses
This commit is contained in:
parent
cfa6e70c13
commit
8f06f3e072
@ -68,6 +68,7 @@ export default function LocationMapManager() {
|
||||
const { activeHousehold, loading: householdLoading, hasLoaded } = useContext(HouseholdContext);
|
||||
const { stores, activeStore, setActiveStore } = useContext(StoreContext);
|
||||
const [buyModalItem, setBuyModalItem] = useState(null);
|
||||
const mapBuyScopeRef = useRef({ activeHouseholdId: null, locationId: null });
|
||||
|
||||
const {
|
||||
beginSaving,
|
||||
@ -172,6 +173,18 @@ export default function LocationMapManager() {
|
||||
});
|
||||
}, [setMapState]);
|
||||
|
||||
useEffect(() => {
|
||||
mapBuyScopeRef.current = {
|
||||
activeHouseholdId: activeHousehold?.id,
|
||||
locationId,
|
||||
};
|
||||
}, [activeHousehold?.id, locationId]);
|
||||
|
||||
const isCurrentMapBuyScope = useCallback((activeHouseholdId, scopedLocationId) => (
|
||||
String(mapBuyScopeRef.current.activeHouseholdId || "") === String(activeHouseholdId || "") &&
|
||||
String(mapBuyScopeRef.current.locationId || "") === String(scopedLocationId || "")
|
||||
), []);
|
||||
|
||||
const handleMapBuyCancel = useCallback(() => {
|
||||
setBuyModalItem(null);
|
||||
}, []);
|
||||
@ -186,13 +199,16 @@ export default function LocationMapManager() {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeHouseholdId = activeHousehold.id;
|
||||
const scopedLocationId = locationId;
|
||||
const item = buyModalItems.find((candidate) => candidate.id === buyModalItem.id) || buyModalItem;
|
||||
|
||||
try {
|
||||
const currentIndex = buyModalItems.findIndex((candidate) => candidate.id === item.id);
|
||||
const resolvedIndex = currentIndex >= 0 ? currentIndex : 0;
|
||||
|
||||
await markBought(activeHousehold.id, locationId, item.item_name, quantity, true);
|
||||
await markBought(activeHouseholdId, scopedLocationId, item.item_name, quantity, true);
|
||||
if (!isCurrentMapBuyScope(activeHouseholdId, scopedLocationId)) return;
|
||||
|
||||
let nextBuyModalItems = buyModalItems;
|
||||
if (quantity >= item.quantity) {
|
||||
@ -205,9 +221,11 @@ export default function LocationMapManager() {
|
||||
} else {
|
||||
let updatedItem;
|
||||
try {
|
||||
const response = await getItemByName(activeHousehold.id, locationId, item.item_name);
|
||||
const response = await getItemByName(activeHouseholdId, scopedLocationId, item.item_name);
|
||||
if (!isCurrentMapBuyScope(activeHouseholdId, scopedLocationId)) return;
|
||||
updatedItem = response.data || getLocallyReducedMapItem(item, quantity);
|
||||
} catch {
|
||||
if (!isCurrentMapBuyScope(activeHouseholdId, scopedLocationId)) return;
|
||||
updatedItem = getLocallyReducedMapItem(item, quantity);
|
||||
toast.info("Updated item locally", "Latest quantity will sync when the map reloads.");
|
||||
}
|
||||
@ -221,10 +239,11 @@ export default function LocationMapManager() {
|
||||
|
||||
toast.success("Marked item bought", `Marked item ${item.item_name} as bought`);
|
||||
} catch (error) {
|
||||
if (!isCurrentMapBuyScope(activeHouseholdId, scopedLocationId)) return;
|
||||
const message = getApiErrorMessage(error, "Failed to mark item as bought");
|
||||
toast.error("Mark item bought failed", `Mark item bought failed: ${message}`);
|
||||
}
|
||||
}, [activeHousehold?.id, buyModalItem, buyModalItems, locationId, toast, updateMapItems]);
|
||||
}, [activeHousehold?.id, buyModalItem, buyModalItems, isCurrentMapBuyScope, locationId, toast, updateMapItems]);
|
||||
|
||||
const mapSize = useMemo(
|
||||
() => ({
|
||||
|
||||
@ -845,8 +845,30 @@ test("closes stale map buy modal after switching locations", async ({ page }) =>
|
||||
location: secondStoreLocation,
|
||||
map: nextPublishedMap,
|
||||
published_map: nextPublishedMap,
|
||||
items: [],
|
||||
items: [
|
||||
{
|
||||
id: 1,
|
||||
item_name: "ontario oranges",
|
||||
quantity: 1,
|
||||
bought: false,
|
||||
zone_id: 502,
|
||||
zone: "Produce",
|
||||
added_by_users: ["map-user"],
|
||||
},
|
||||
],
|
||||
};
|
||||
let resolveBuyRequestStarted: () => void = () => {};
|
||||
let releaseBuyResponse: () => void = () => {};
|
||||
let resolveBuyResponseSent: () => void = () => {};
|
||||
const buyRequestStarted = new Promise<void>((resolve) => {
|
||||
resolveBuyRequestStarted = resolve;
|
||||
});
|
||||
const buyResponseGate = new Promise<void>((resolve) => {
|
||||
releaseBuyResponse = resolve;
|
||||
});
|
||||
const buyResponseSent = new Promise<void>((resolve) => {
|
||||
resolveBuyResponseSent = resolve;
|
||||
});
|
||||
|
||||
await page.route("**/households/1/locations/10/map", async (route) => {
|
||||
await route.fulfill({
|
||||
@ -862,11 +884,28 @@ test("closes stale map buy modal after switching locations", async ({ page }) =>
|
||||
body: JSON.stringify(nextLocationState),
|
||||
});
|
||||
});
|
||||
await page.route("**/households/1/locations/10/list/item", async (route) => {
|
||||
if (route.request().method() !== "PATCH") {
|
||||
await route.fulfill({ status: 404, contentType: "application/json", body: "{}" });
|
||||
return;
|
||||
}
|
||||
|
||||
resolveBuyRequestStarted();
|
||||
await buyResponseGate;
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({ success: true }),
|
||||
});
|
||||
resolveBuyResponseSent();
|
||||
});
|
||||
|
||||
await page.goto("/stores/100/locations/10/map");
|
||||
await page.getByRole("button", { name: "Map area Eastvale Bakery" }).click();
|
||||
await page.getByRole("button", { name: "Mark french bread bought" }).click();
|
||||
await expect(page.locator(".confirm-buy-item-name")).toHaveText("french bread");
|
||||
await page.getByRole("button", { name: "Mark as Bought" }).click();
|
||||
await buyRequestStarted;
|
||||
|
||||
await page.evaluate(() => {
|
||||
window.history.pushState({}, "", "/stores/100/locations/11/map");
|
||||
@ -877,6 +916,14 @@ test("closes stale map buy modal after switching locations", async ({ page }) =>
|
||||
await expect(page.locator(".location-map-title span")).toHaveText("Ontario");
|
||||
await expect(page.locator(".confirm-buy-modal")).toHaveCount(0);
|
||||
await expect(page.getByText("french bread")).toHaveCount(0);
|
||||
await page.getByRole("button", { name: "Map area Ontario Produce" }).click();
|
||||
await expect(page.getByRole("button", { name: "Mark ontario oranges bought" })).toBeVisible();
|
||||
|
||||
releaseBuyResponse();
|
||||
await buyResponseSent;
|
||||
|
||||
await expect(page.getByRole("button", { name: "Mark ontario oranges bought" })).toBeVisible();
|
||||
await expect(page.getByText("Marked item bought")).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("closes stale map delete confirmation after switching locations", async ({ page }) => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user