fix: explain hidden map zones

This commit is contained in:
Nico 2026-06-03 18:44:29 -07:00
parent aaf0876b63
commit e09a95e172
4 changed files with 107 additions and 3 deletions

View File

@ -20,6 +20,7 @@ export default function LocationMapCanvas({
svgRef,
dragState,
editingLocked = false,
onShowZones,
setDragState,
setSelectedObjectKey,
remember,
@ -27,6 +28,18 @@ export default function LocationMapCanvas({
}) {
const canEditObjects = !editingLocked && mode === "edit" && editorTool === "edit";
const canDragPan = mode === "view" || editorTool === "pan";
const hiddenByZonesLayer = objects.length > 0 && !filters.showZones;
const visibleObjectCount = filters.showZones
? objects.filter((object) => object.visible).length
: 0;
const hasNoVisibleObjects = objects.length > 0 && filters.showZones && visibleObjectCount === 0;
const emptyCanvasMessage = objects.length === 0
? "No map areas yet"
: hiddenByZonesLayer
? "Zones hidden in Layers"
: hasNoVisibleObjects
? "No visible map areas"
: "";
const scrollRef = useRef(null);
const panDragRef = useRef(null);
const objectDragHistoryCapturedRef = useRef(false);
@ -113,6 +126,11 @@ export default function LocationMapCanvas({
selectObject(object);
};
const handleShowZones = (event) => {
event.stopPropagation();
onShowZones?.();
};
const clearSelection = () => {
if (suppressPanClickRef.current) {
suppressPanClickRef.current = false;
@ -339,9 +357,23 @@ export default function LocationMapCanvas({
) : null}
{objects.map(renderMapObject)}
</svg>
{objects.length === 0 ? (
<div className="location-map-empty-canvas">
No map areas yet
{emptyCanvasMessage ? (
<div
className={[
"location-map-empty-canvas",
hiddenByZonesLayer ? "has-action" : "",
].filter(Boolean).join(" ")}
>
<span>{emptyCanvasMessage}</span>
{hiddenByZonesLayer ? (
<button
type="button"
onPointerDown={(event) => event.stopPropagation()}
onClick={handleShowZones}
>
Show Zones
</button>
) : null}
</div>
) : null}
</div>

View File

@ -528,6 +528,12 @@ export default function LocationMapManager() {
setSelectedObjectKey={setSelectedObjectKey}
remember={remember}
updateObjects={updateObjects}
onShowZones={() =>
setFilters((current) => ({
...current,
showZones: true,
}))
}
/>
<LocationMapBottomSheet
mode={mode}

View File

@ -325,6 +325,25 @@
pointer-events: none;
}
.location-map-empty-canvas.has-action {
display: inline-flex;
align-items: center;
gap: 0.55rem;
padding: 0.4rem 0.45rem 0.4rem 0.7rem;
pointer-events: auto;
}
.location-map-empty-canvas button {
min-height: 40px;
padding: 0 0.75rem;
border: 1px solid rgba(96, 165, 250, 0.52);
border-radius: 999px;
background: rgba(59, 130, 246, 0.24);
color: #f8fafc;
font-weight: 900;
cursor: pointer;
}
.location-map-bottom-sheet {
max-height: 43vh;
overflow-y: auto;

View File

@ -924,6 +924,53 @@ test("viewer wraps long zone labels and keeps item counts visible", async ({ pag
await expect(produceArea.locator(".location-map-count")).toHaveAttribute("y", "116");
});
test("viewer explains when the zones layer hides the map", async ({ page }) => {
await mockMapShell(page);
const mapState = publishedMapState([
{
id: 1521,
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);
await page.route("**/households/1/locations/10/map", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(mapState),
});
});
await page.goto("/stores/100/locations/10/map");
await expect(page.getByRole("button", { name: "Map area Bakery" })).toBeVisible();
await page.getByRole("button", { name: "Layers" }).click();
await page.getByLabel("Zones").uncheck();
await expect(page.getByRole("button", { name: "Map area Bakery" })).toHaveCount(0);
await expect(page.getByText("Zones hidden in Layers")).toBeVisible();
await expect(page.getByRole("button", { name: "Show Zones" })).toBeVisible();
await expect(page.getByRole("button", { name: "Layers, 1 changed" })).toBeVisible();
await page.getByRole("button", { name: "Show Zones" }).click();
await expect(page.getByRole("button", { name: "Map area Bakery" })).toBeVisible();
await expect(page.getByText("Zones hidden in Layers")).toHaveCount(0);
await expect(page.getByRole("button", { name: "Layers" })).toBeVisible();
});
test("viewer shows a compact overflow cue for long unmapped lists", async ({ page }) => {
await mockMapShell(page);