Add store location map manager #20

Open
nalalangan wants to merge 206 commits from feature/location-map-manager into feature/store-selector-modal
4 changed files with 89 additions and 3 deletions
Showing only changes of commit 0d05bf761e - Show all commits

View File

@ -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 (
<div className="location-map-page">
<p className="location-map-loading">{message}</p>
{showTopbar ? (
<LocationMapTopbar location={location} status={status} onBack={onBack} />
) : null}
<main className="location-map-workspace location-map-message-workspace">
<p className="location-map-loading">{message}</p>
</main>
</div>
);
}

View File

@ -369,7 +369,14 @@ export default function LocationMapManager() {
});
if (!hasLoaded || householdLoading || loading) {
return <LocationMapMessageState message="Loading map..." />;
return (
<LocationMapMessageState
message="Loading map..."
location={location}
status="Loading"
onBack={requestBackNavigation}
/>
);
}
if (!activeHousehold) {

View File

@ -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;

View File

@ -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<void>((resolve) => {
resolveMapRequestStarted = resolve;
});
const mapResponseGate = new Promise<void>((resolve) => {
releaseMapResponse = resolve;
});
const mapResponseSent = new Promise<void>((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]);