diff --git a/frontend/src/components/maps/LocationMapStateViews.jsx b/frontend/src/components/maps/LocationMapStateViews.jsx
index 26dd0de..38acb07 100644
--- a/frontend/src/components/maps/LocationMapStateViews.jsx
+++ b/frontend/src/components/maps/LocationMapStateViews.jsx
@@ -1,9 +1,16 @@
import LocationMapTopbar from "./LocationMapTopbar";
-export function LocationMapMessageState({ message }) {
+export function LocationMapMessageState({ message, location, status = "Loading", onBack }) {
+ const showTopbar = Boolean(location && onBack);
+
return (
-
{message}
+ {showTopbar ? (
+
+ ) : null}
+
+ {message}
+
);
}
diff --git a/frontend/src/pages/LocationMapManager.jsx b/frontend/src/pages/LocationMapManager.jsx
index 958b476..26ca2c2 100644
--- a/frontend/src/pages/LocationMapManager.jsx
+++ b/frontend/src/pages/LocationMapManager.jsx
@@ -369,7 +369,14 @@ export default function LocationMapManager() {
});
if (!hasLoaded || householdLoading || loading) {
- return ;
+ return (
+
+ );
}
if (!activeHousehold) {
diff --git a/frontend/src/styles/pages/LocationMapManager.css b/frontend/src/styles/pages/LocationMapManager.css
index 523d84d..4a878a5 100644
--- a/frontend/src/styles/pages/LocationMapManager.css
+++ b/frontend/src/styles/pages/LocationMapManager.css
@@ -76,6 +76,10 @@
background: rgba(148, 163, 184, 0.16);
}
+.location-map-status-loading {
+ background: rgba(148, 163, 184, 0.16);
+}
+
.location-map-status-draft {
background: rgba(245, 158, 11, 0.2);
border-color: rgba(245, 158, 11, 0.36);
@@ -902,6 +906,12 @@
color: #9fb3c8;
}
+.location-map-message-workspace {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
@media (min-width: 840px) {
.location-map-workspace {
grid-template-columns: minmax(0, 1fr) 360px;
diff --git a/frontend/tests/location-map-manager.spec.ts b/frontend/tests/location-map-manager.spec.ts
index 40318d0..8301734 100644
--- a/frontend/tests/location-map-manager.spec.ts
+++ b/frontend/tests/location-map-manager.spec.ts
@@ -490,6 +490,68 @@ test("load failure shows retryable error instead of no-map setup", async ({ page
expect(attempts).toBe(2);
});
+test("loading map keeps location context visible", async ({ page }) => {
+ await mockMapShell(page);
+
+ const mapState = publishedMapState([
+ {
+ id: 461,
+ location_map_id: 901,
+ zone_id: 501,
+ zone_name: "Bakery",
+ type: "zone",
+ label: "Bakery",
+ x: 40,
+ y: 40,
+ width: 260,
+ height: 160,
+ rotation: 0,
+ locked: false,
+ visible: true,
+ sort_order: 1,
+ },
+ ], true);
+ let resolveMapRequestStarted: () => void = () => {};
+ let releaseMapResponse: () => void = () => {};
+ let resolveMapResponseSent: () => void = () => {};
+ const mapRequestStarted = new Promise((resolve) => {
+ resolveMapRequestStarted = resolve;
+ });
+ const mapResponseGate = new Promise((resolve) => {
+ releaseMapResponse = resolve;
+ });
+ const mapResponseSent = new Promise((resolve) => {
+ resolveMapResponseSent = resolve;
+ });
+
+ await page.route("**/households/1/locations/10/map", async (route) => {
+ resolveMapRequestStarted();
+ await mapResponseGate;
+ await route.fulfill({
+ status: 200,
+ contentType: "application/json",
+ body: JSON.stringify(mapState),
+ });
+ resolveMapResponseSent();
+ });
+
+ const navigation = page.goto("/stores/100/locations/10/map");
+ await mapRequestStarted;
+
+ await expect(page.getByRole("heading", { name: "Costco" })).toBeVisible();
+ await expect(page.getByText("Eastvale")).toBeVisible();
+ await expect(page.getByRole("button", { name: "Back to grocery list" })).toBeVisible();
+ await expect(page.locator(".location-map-status")).toHaveText("Loading");
+ await expect(page.getByText("Loading map...")).toBeVisible();
+
+ releaseMapResponse();
+ await mapResponseSent;
+ await navigation;
+
+ await expect(page.locator(".location-map-status")).toHaveText("Published");
+ await expect(page.getByRole("button", { name: "Map area Bakery" })).toBeVisible();
+});
+
test("ignores stale map responses after switching locations", async ({ page }) => {
await mockMapShell(page, adminHousehold, [storeLocation, secondStoreLocation]);