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 { activeHousehold, loading: householdLoading, hasLoaded } = useContext(HouseholdContext);
|
||||||
const { stores, activeStore, setActiveStore } = useContext(StoreContext);
|
const { stores, activeStore, setActiveStore } = useContext(StoreContext);
|
||||||
const [buyModalItem, setBuyModalItem] = useState(null);
|
const [buyModalItem, setBuyModalItem] = useState(null);
|
||||||
|
const mapBuyScopeRef = useRef({ activeHouseholdId: null, locationId: null });
|
||||||
|
|
||||||
const {
|
const {
|
||||||
beginSaving,
|
beginSaving,
|
||||||
@ -172,6 +173,18 @@ export default function LocationMapManager() {
|
|||||||
});
|
});
|
||||||
}, [setMapState]);
|
}, [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(() => {
|
const handleMapBuyCancel = useCallback(() => {
|
||||||
setBuyModalItem(null);
|
setBuyModalItem(null);
|
||||||
}, []);
|
}, []);
|
||||||
@ -186,13 +199,16 @@ export default function LocationMapManager() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const activeHouseholdId = activeHousehold.id;
|
||||||
|
const scopedLocationId = locationId;
|
||||||
const item = buyModalItems.find((candidate) => candidate.id === buyModalItem.id) || buyModalItem;
|
const item = buyModalItems.find((candidate) => candidate.id === buyModalItem.id) || buyModalItem;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentIndex = buyModalItems.findIndex((candidate) => candidate.id === item.id);
|
const currentIndex = buyModalItems.findIndex((candidate) => candidate.id === item.id);
|
||||||
const resolvedIndex = currentIndex >= 0 ? currentIndex : 0;
|
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;
|
let nextBuyModalItems = buyModalItems;
|
||||||
if (quantity >= item.quantity) {
|
if (quantity >= item.quantity) {
|
||||||
@ -205,9 +221,11 @@ export default function LocationMapManager() {
|
|||||||
} else {
|
} else {
|
||||||
let updatedItem;
|
let updatedItem;
|
||||||
try {
|
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);
|
updatedItem = response.data || getLocallyReducedMapItem(item, quantity);
|
||||||
} catch {
|
} catch {
|
||||||
|
if (!isCurrentMapBuyScope(activeHouseholdId, scopedLocationId)) return;
|
||||||
updatedItem = getLocallyReducedMapItem(item, quantity);
|
updatedItem = getLocallyReducedMapItem(item, quantity);
|
||||||
toast.info("Updated item locally", "Latest quantity will sync when the map reloads.");
|
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`);
|
toast.success("Marked item bought", `Marked item ${item.item_name} as bought`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (!isCurrentMapBuyScope(activeHouseholdId, scopedLocationId)) return;
|
||||||
const message = getApiErrorMessage(error, "Failed to mark item as bought");
|
const message = getApiErrorMessage(error, "Failed to mark item as bought");
|
||||||
toast.error("Mark item bought failed", `Mark item bought failed: ${message}`);
|
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(
|
const mapSize = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
|||||||
@ -845,8 +845,30 @@ test("closes stale map buy modal after switching locations", async ({ page }) =>
|
|||||||
location: secondStoreLocation,
|
location: secondStoreLocation,
|
||||||
map: nextPublishedMap,
|
map: nextPublishedMap,
|
||||||
published_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 page.route("**/households/1/locations/10/map", async (route) => {
|
||||||
await route.fulfill({
|
await route.fulfill({
|
||||||
@ -862,11 +884,28 @@ test("closes stale map buy modal after switching locations", async ({ page }) =>
|
|||||||
body: JSON.stringify(nextLocationState),
|
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.goto("/stores/100/locations/10/map");
|
||||||
await page.getByRole("button", { name: "Map area Eastvale Bakery" }).click();
|
await page.getByRole("button", { name: "Map area Eastvale Bakery" }).click();
|
||||||
await page.getByRole("button", { name: "Mark french bread bought" }).click();
|
await page.getByRole("button", { name: "Mark french bread bought" }).click();
|
||||||
await expect(page.locator(".confirm-buy-item-name")).toHaveText("french bread");
|
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(() => {
|
await page.evaluate(() => {
|
||||||
window.history.pushState({}, "", "/stores/100/locations/11/map");
|
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(".location-map-title span")).toHaveText("Ontario");
|
||||||
await expect(page.locator(".confirm-buy-modal")).toHaveCount(0);
|
await expect(page.locator(".confirm-buy-modal")).toHaveCount(0);
|
||||||
await expect(page.getByText("french bread")).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 }) => {
|
test("closes stale map delete confirmation after switching locations", async ({ page }) => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user