Add store location map manager #20

Open
nalalangan wants to merge 206 commits from feature/location-map-manager into feature/store-selector-modal
2 changed files with 95 additions and 2 deletions
Showing only changes of commit 415a7223cd - Show all commits

View File

@ -270,12 +270,18 @@ export default function LocationMapManager() {
const handlePublish = async () => { const handlePublish = async () => {
if (!activeHousehold?.id || !locationId) return; if (!activeHousehold?.id || !locationId) return;
beginSaving("publish"); beginSaving("publish");
let savedPendingDraft = false;
try { try {
if (hasUnsavedChanges) { if (hasUnsavedChanges) {
await saveLocationMapDraft(activeHousehold.id, locationId, { const saveResponse = await saveLocationMapDraft(activeHousehold.id, locationId, {
map: mapDraft, map: mapDraft,
objects: prepareObjectsForSave(objects), objects: prepareObjectsForSave(objects),
}); });
savedPendingDraft = true;
setMapState(saveResponse.data);
setMode("edit");
setPreviewDraft(true);
syncMapDraftFromState(saveResponse.data, "edit", true);
} }
const response = await publishLocationMapDraft(activeHousehold.id, locationId); const response = await publishLocationMapDraft(activeHousehold.id, locationId);
@ -291,7 +297,11 @@ export default function LocationMapManager() {
: "Map is now visible to household members" : "Map is now visible to household members"
); );
} catch (error) { } catch (error) {
toast.error("Publish failed", getApiErrorMessage(error, "Failed to publish map")); const message = getApiErrorMessage(error, "Failed to publish map");
toast.error(
"Publish failed",
savedPendingDraft ? `${message}. Draft changes were saved.` : message
);
} finally { } finally {
endSaving(); endSaving();
} }

View File

@ -967,6 +967,89 @@ test("admin publish saves pending map edits before publishing", async ({ page })
await expect(page.getByRole("button", { name: "Map area Live Bakery" })).toHaveCount(0); await expect(page.getByRole("button", { name: "Map area Live Bakery" })).toHaveCount(0);
}); });
test("admin publish failure keeps successfully saved pending edits", async ({ page }) => {
await mockMapShell(page);
let mapState = publishedMapState([
{
id: 1461,
location_map_id: 901,
zone_id: 501,
zone_name: "Bakery",
type: "zone",
label: "Live Bakery",
x: 40,
y: 40,
width: 260,
height: 160,
rotation: 0,
locked: false,
visible: true,
sort_order: 1,
},
], true);
let savedPayload: Record<string, unknown> | null = null;
let publishCalled = false;
await page.route("**/households/1/locations/10/map", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(mapState),
});
});
await page.route("**/households/1/locations/10/map/draft", async (route) => {
savedPayload = await route.request().postDataJSON();
const savedObjects = (savedPayload.objects as Array<Record<string, unknown>>).map((object, index) => ({
id: 1465 + index,
location_map_id: 900,
zone_name: object.zone_id === 501 ? "Bakery" : null,
...object,
}));
mapState = draftAndPublishedMapState(savedObjects, mapState.published_objects, true);
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(mapState),
});
});
await page.route("**/households/1/locations/10/map/publish", async (route) => {
publishCalled = true;
await route.fulfill({
status: 503,
contentType: "application/json",
body: JSON.stringify({ error: { message: "Publish service unavailable" } }),
});
});
await page.goto("/stores/100/locations/10/map");
await page.getByRole("button", { name: "Edit Map" }).click();
await page.getByRole("button", { name: "Edit Objects" }).click();
await page.locator(".location-map-object", { hasText: "Live Bakery" }).locator("rect").first().click();
await page.getByRole("textbox", { name: "Label" }).fill("Saved Draft Bakery");
await expect(page.locator(".location-map-status")).toHaveText("Unsaved Draft");
await page.getByRole("button", { name: "Publish", exact: true }).click();
await expect.poll(() => publishCalled).toBe(true);
expect(savedPayload).not.toBeNull();
expect(savedPayload?.objects).toEqual(
expect.arrayContaining([
expect.objectContaining({
label: "Saved Draft Bakery",
zone_id: 501,
}),
])
);
await expect(page.locator(".location-map-status")).toHaveText("Draft");
await expect(page.getByRole("button", { name: "Save Draft" })).toBeDisabled();
await expect(page.getByRole("button", { name: "Map area Saved Draft Bakery" })).toBeVisible();
await expect(page.getByRole("button", { name: "Map area Live Bakery" })).toHaveCount(0);
});
test("viewer wraps long zone labels and keeps item counts visible", async ({ page }) => { test("viewer wraps long zone labels and keeps item counts visible", async ({ page }) => {
await mockMapShell(page); await mockMapShell(page);