From d3194959fc8ac21aa1352eae31dbe6e9f056bfa2 Mon Sep 17 00:00:00 2001 From: Nico Date: Tue, 16 Jun 2026 00:15:03 -0700 Subject: [PATCH] fix: save map drafts with shortcut --- frontend/src/pages/LocationMapManager.jsx | 31 +++++++++++++++++++++ frontend/tests/location-map-manager.spec.ts | 16 +++++++---- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/frontend/src/pages/LocationMapManager.jsx b/frontend/src/pages/LocationMapManager.jsx index 72c2d75..1c51439 100644 --- a/frontend/src/pages/LocationMapManager.jsx +++ b/frontend/src/pages/LocationMapManager.jsx @@ -254,6 +254,37 @@ export default function LocationMapManager() { return () => window.removeEventListener("keydown", handleKeyDown); }, [buyModalItem, layersOpen, pendingDeleteObject, pendingLeaveTarget, selectedObjectKey, setSelectedObjectKey]); + useEffect(() => { + const hasOpenModal = Boolean(buyModalItem || pendingDeleteObject || pendingLeaveTarget); + if (mode !== "edit" || !canManage || hasOpenModal || typeof window === "undefined") { + return undefined; + } + + const handleKeyDown = (event) => { + const wantsSave = + (event.ctrlKey || event.metaKey) && + !event.altKey && + event.key.toLowerCase() === "s"; + + if (!wantsSave) return; + event.preventDefault(); + if (saving || !hasUnsavedChanges) return; + handleSaveDraft(); + }; + + window.addEventListener("keydown", handleKeyDown, true); + return () => window.removeEventListener("keydown", handleKeyDown, true); + }, [ + buyModalItem, + canManage, + handleSaveDraft, + hasUnsavedChanges, + mode, + pendingDeleteObject, + pendingLeaveTarget, + saving, + ]); + if (!hasLoaded || householdLoading || loading) { return ( await expect(page.locator(".location-map-sheet-header strong")).toHaveText("Edit Areas"); }); -test("admin save progress locks draft controls", async ({ page }) => { +test("admin save shortcut locks draft controls", async ({ page }) => { await mockMapShell(page); let mapState = draftMapState([ @@ -2504,9 +2504,13 @@ test("admin save progress locks draft controls", async ({ page }) => { await page.goto("/stores/100/locations/10/map"); await page.getByRole("button", { name: "Continue Editing" }).click(); await page.locator(".location-map-object", { hasText: "Bakery" }).locator("rect").first().click(); - await page.getByRole("textbox", { name: "Label" }).fill("Saving Bakery"); + const labelInput = page.getByRole("textbox", { name: "Label" }); + await labelInput.fill("Saving Bakery"); - await page.getByRole("button", { name: "Save Draft" }).click(); + await labelInput.focus(); + await page.keyboard.down("Control"); + await page.keyboard.press("KeyS"); + await page.keyboard.up("Control"); await expect.poll(() => saveStarted).toBe(true); await expect(page.getByRole("button", { name: "Saving..." })).toBeDisabled(); await expect(page.getByRole("button", { name: "Publish" })).toBeDisabled(); @@ -2515,13 +2519,13 @@ test("admin save progress locks draft controls", async ({ page }) => { await expect(page.getByRole("button", { name: "Undo" })).toBeDisabled(); await expect(page.getByRole("button", { name: "Duplicate area Saving Bakery" })).toBeDisabled(); await expect(page.getByRole("button", { name: "Delete area Saving Bakery" })).toBeDisabled(); - await expect(page.getByRole("textbox", { name: "Label" })).toBeDisabled(); + await expect(labelInput).toBeDisabled(); releaseSave(); await expect(page.locator(".location-map-status")).toHaveText("Draft"); await expect(page.getByRole("button", { name: "Save Draft" })).toHaveCount(0); - await expect(page.getByRole("textbox", { name: "Label" })).toBeEnabled(); - await expect(page.getByRole("textbox", { name: "Label" })).toHaveValue("Saving Bakery"); + await expect(labelInput).toBeEnabled(); + await expect(labelInput).toHaveValue("Saving Bakery"); await expect(page.getByRole("button", { name: "Map area Saving Bakery" })).toHaveAttribute("aria-pressed", "true"); });