diff --git a/frontend/src/components/maps/LocationMapBottomSheet.jsx b/frontend/src/components/maps/LocationMapBottomSheet.jsx
index ab8aa90..a82c5c9 100644
--- a/frontend/src/components/maps/LocationMapBottomSheet.jsx
+++ b/frontend/src/components/maps/LocationMapBottomSheet.jsx
@@ -30,6 +30,7 @@ export default function LocationMapBottomSheet({
selectedObject,
selectedZoneItems,
visibleUnmappedItems,
+ unmappedItemCount,
hiddenAreaCount,
history,
future,
@@ -81,7 +82,9 @@ export default function LocationMapBottomSheet({
const showSelectedObjectForm = Boolean(mode === "edit" && editorTool === "edit" && selectedObject);
const changedLayerCount = DISPLAY_CONTROLS.filter(([key]) => filters[key] !== DEFAULT_MAP_FILTERS[key]).length;
const unmappedItemPreview = visibleUnmappedItems.slice(0, 8);
- const hiddenUnmappedItemCount = visibleUnmappedItems.length - unmappedItemPreview.length;
+ const overflowUnmappedItemCount = visibleUnmappedItems.length - unmappedItemPreview.length;
+ const hasHiddenUnmappedItems =
+ filters.showUnmapped && unmappedItemCount > 0 && visibleUnmappedItems.length === 0;
const selectedWidth = Number(selectedObject?.width) || 40;
const selectedHeight = Number(selectedObject?.height) || 40;
const hiddenAreaLabel = `Show Hidden Areas (${hiddenAreaCount})`;
@@ -100,6 +103,15 @@ export default function LocationMapBottomSheet({
showCompleted: true,
}));
};
+ const showUnmappedItems = () => {
+ setFilters((current) => ({
+ ...current,
+ showUnmapped: true,
+ showMyItems: true,
+ showOtherItems: true,
+ showCompleted: true,
+ }));
+ };
const primaryDraftActions = showEditorControls ? (
+ ) : hasHiddenUnmappedItems ? (
+
+
Unmapped Items
+
+
+ Unmapped items are hidden by layer filters.
+
+
+
+
) : null}
);
diff --git a/frontend/src/pages/LocationMapManager.jsx b/frontend/src/pages/LocationMapManager.jsx
index a8fcd33..1e4d435 100644
--- a/frontend/src/pages/LocationMapManager.jsx
+++ b/frontend/src/pages/LocationMapManager.jsx
@@ -81,6 +81,7 @@ export default function LocationMapManager() {
? itemsForZone(mapState?.items || [], selectedObject.zone_id, filters, username)
: [];
const visibleUnmappedItems = unmappedItems(mapState?.items || [], filters, username);
+ const unmappedItemCount = (mapState?.items || []).filter((item) => !item.zone_id).length;
const hiddenAreaCount = objects.filter((object) => object.visible === false).length;
const shouldGuardLeave = canManage && hasUnsavedChanges;
@@ -579,6 +580,7 @@ export default function LocationMapManager() {
selectedObject={selectedObject}
selectedZoneItems={selectedZoneItems}
visibleUnmappedItems={visibleUnmappedItems}
+ unmappedItemCount={unmappedItemCount}
hiddenAreaCount={hiddenAreaCount}
history={history}
future={future}
diff --git a/frontend/tests/location-map-manager.spec.ts b/frontend/tests/location-map-manager.spec.ts
index ac61caf..d93d649 100644
--- a/frontend/tests/location-map-manager.spec.ts
+++ b/frontend/tests/location-map-manager.spec.ts
@@ -1367,6 +1367,48 @@ test("viewer shows a compact overflow cue for long unmapped lists", async ({ pag
await expect(page.getByLabel("3 more unmapped items")).toHaveText("+3 more");
});
+test("viewer explains when unmapped items are hidden by filters", async ({ page }) => {
+ await mockMapShell(page);
+
+ const mapState = {
+ ...publishedMapState([], true),
+ items: [
+ {
+ id: 4100,
+ item_name: "other batteries",
+ quantity: 1,
+ bought: false,
+ zone_id: null,
+ zone: null,
+ added_by_users: ["other-user"],
+ },
+ ],
+ unmapped_count: 1,
+ };
+
+ 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 page.getByRole("button", { name: "Layers" }).click();
+ await page.getByLabel("Unmapped").check();
+
+ await expect(page.getByText("other batteries")).toHaveCount(0);
+ await expect(page.getByText("Unmapped items are hidden by layer filters.")).toBeVisible();
+ await expect(page.getByRole("button", { name: "Show Unmapped Items" })).toBeVisible();
+
+ await page.getByRole("button", { name: "Show Unmapped Items" }).click();
+
+ await expect(page.getByText("other batteries")).toBeVisible();
+ await expect(page.getByText("Unmapped items are hidden by layer filters.")).toHaveCount(0);
+ await expect(page.getByRole("button", { name: "Layers, 3 changed" })).toBeVisible();
+});
+
test("viewer explains when selected zone items are hidden by filters", async ({ page }) => {
await mockMapShell(page);