diff --git a/frontend/src/components/maps/LocationMapBottomSheet.jsx b/frontend/src/components/maps/LocationMapBottomSheet.jsx
index 3d732f9..67668a7 100644
--- a/frontend/src/components/maps/LocationMapBottomSheet.jsx
+++ b/frontend/src/components/maps/LocationMapBottomSheet.jsx
@@ -84,6 +84,7 @@ export default function LocationMapBottomSheet({
const changedLayerCount = DISPLAY_CONTROLS.filter(([key]) => filters[key] !== DEFAULT_MAP_FILTERS[key]).length;
const unmappedItemPreview = visibleUnmappedItems.slice(0, 8);
const overflowUnmappedItemCount = visibleUnmappedItems.length - unmappedItemPreview.length;
+ const hiddenUnmappedItemCount = Math.max(0, unmappedItemCount - visibleUnmappedItems.length);
const hasHiddenUnmappedItems =
filters.showUnmapped && unmappedItemCount > 0 && visibleUnmappedItems.length === 0;
const selectedWidth = Number(selectedObject?.width) || 40;
@@ -438,6 +439,16 @@ export default function LocationMapBottomSheet({
) : null}
+ {hiddenUnmappedItemCount > 0 ? (
+
+
+ {hiddenUnmappedItemCount} more hidden by layer filters.
+
+
+
+ ) : null}
) : hasHiddenUnmappedItems ? (
diff --git a/frontend/tests/location-map-manager.spec.ts b/frontend/tests/location-map-manager.spec.ts
index 34658b2..4e93d2d 100644
--- a/frontend/tests/location-map-manager.spec.ts
+++ b/frontend/tests/location-map-manager.spec.ts
@@ -1539,6 +1539,57 @@ test("viewer explains when unmapped items are hidden by filters", async ({ page
await expect(page.getByRole("button", { name: "Layers, 3 changed" })).toBeVisible();
});
+test("viewer marks partial unmapped counts when filters hide some items", async ({ page }) => {
+ await mockMapShell(page);
+
+ const mapState = {
+ ...publishedMapState([], true),
+ items: [
+ {
+ id: 4110,
+ item_name: "visible batteries",
+ quantity: 1,
+ bought: false,
+ zone_id: null,
+ zone: null,
+ added_by_users: ["map-user"],
+ },
+ {
+ id: 4111,
+ item_name: "hidden batteries",
+ quantity: 1,
+ bought: false,
+ zone_id: null,
+ zone: null,
+ added_by_users: ["other-user"],
+ },
+ ],
+ unmapped_count: 2,
+ };
+
+ 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("visible batteries")).toBeVisible();
+ await expect(page.getByText("hidden batteries")).toHaveCount(0);
+ await expect(page.getByText("1 more hidden by layer filters.")).toBeVisible();
+ await expect(page.getByRole("button", { name: "Show All Unmapped Items" })).toBeVisible();
+
+ await page.getByRole("button", { name: "Show All Unmapped Items" }).click();
+
+ await expect(page.getByText("hidden batteries")).toBeVisible();
+ await expect(page.getByText("1 more hidden by layer filters.")).toHaveCount(0);
+});
+
test("viewer explains when selected zone items are hidden by filters", async ({ page }) => {
await mockMapShell(page);