fix: explain hidden unmapped map items

This commit is contained in:
Nico 2026-06-03 21:53:39 -07:00
parent 0ab839ef7d
commit 214e2626fe
3 changed files with 72 additions and 4 deletions

View File

@ -30,6 +30,7 @@ export default function LocationMapBottomSheet({
selectedObject, selectedObject,
selectedZoneItems, selectedZoneItems,
visibleUnmappedItems, visibleUnmappedItems,
unmappedItemCount,
hiddenAreaCount, hiddenAreaCount,
history, history,
future, future,
@ -81,7 +82,9 @@ export default function LocationMapBottomSheet({
const showSelectedObjectForm = Boolean(mode === "edit" && editorTool === "edit" && selectedObject); const showSelectedObjectForm = Boolean(mode === "edit" && editorTool === "edit" && selectedObject);
const changedLayerCount = DISPLAY_CONTROLS.filter(([key]) => filters[key] !== DEFAULT_MAP_FILTERS[key]).length; const changedLayerCount = DISPLAY_CONTROLS.filter(([key]) => filters[key] !== DEFAULT_MAP_FILTERS[key]).length;
const unmappedItemPreview = visibleUnmappedItems.slice(0, 8); 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 selectedWidth = Number(selectedObject?.width) || 40;
const selectedHeight = Number(selectedObject?.height) || 40; const selectedHeight = Number(selectedObject?.height) || 40;
const hiddenAreaLabel = `Show Hidden Areas (${hiddenAreaCount})`; const hiddenAreaLabel = `Show Hidden Areas (${hiddenAreaCount})`;
@ -100,6 +103,15 @@ export default function LocationMapBottomSheet({
showCompleted: true, showCompleted: true,
})); }));
}; };
const showUnmappedItems = () => {
setFilters((current) => ({
...current,
showUnmapped: true,
showMyItems: true,
showOtherItems: true,
showCompleted: true,
}));
};
const primaryDraftActions = showEditorControls ? ( const primaryDraftActions = showEditorControls ? (
<div className="location-map-editor-actions location-map-primary-actions"> <div className="location-map-editor-actions location-map-primary-actions">
<button type="button" className="primary" onClick={onSaveDraft} disabled={saving || !hasUnsavedChanges}> <button type="button" className="primary" onClick={onSaveDraft} disabled={saving || !hasUnsavedChanges}>
@ -404,16 +416,28 @@ export default function LocationMapBottomSheet({
{unmappedItemPreview.map((item) => ( {unmappedItemPreview.map((item) => (
<li key={item.id}>{item.item_name}</li> <li key={item.id}>{item.item_name}</li>
))} ))}
{hiddenUnmappedItemCount > 0 ? ( {overflowUnmappedItemCount > 0 ? (
<li <li
className="location-map-unmapped-more" className="location-map-unmapped-more"
aria-label={`${hiddenUnmappedItemCount} more unmapped items`} aria-label={`${overflowUnmappedItemCount} more unmapped items`}
> >
+{hiddenUnmappedItemCount} more +{overflowUnmappedItemCount} more
</li> </li>
) : null} ) : null}
</ul> </ul>
</div> </div>
) : hasHiddenUnmappedItems ? (
<div className="location-map-unmapped-list">
<strong>Unmapped Items</strong>
<div className="location-map-zone-empty">
<p className="location-map-muted">
Unmapped items are hidden by layer filters.
</p>
<button type="button" onClick={showUnmappedItems}>
Show Unmapped Items
</button>
</div>
</div>
) : null} ) : null}
</aside> </aside>
); );

View File

@ -81,6 +81,7 @@ export default function LocationMapManager() {
? itemsForZone(mapState?.items || [], selectedObject.zone_id, filters, username) ? itemsForZone(mapState?.items || [], selectedObject.zone_id, filters, username)
: []; : [];
const visibleUnmappedItems = unmappedItems(mapState?.items || [], 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 hiddenAreaCount = objects.filter((object) => object.visible === false).length;
const shouldGuardLeave = canManage && hasUnsavedChanges; const shouldGuardLeave = canManage && hasUnsavedChanges;
@ -579,6 +580,7 @@ export default function LocationMapManager() {
selectedObject={selectedObject} selectedObject={selectedObject}
selectedZoneItems={selectedZoneItems} selectedZoneItems={selectedZoneItems}
visibleUnmappedItems={visibleUnmappedItems} visibleUnmappedItems={visibleUnmappedItems}
unmappedItemCount={unmappedItemCount}
hiddenAreaCount={hiddenAreaCount} hiddenAreaCount={hiddenAreaCount}
history={history} history={history}
future={future} future={future}

View File

@ -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"); 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 }) => { test("viewer explains when selected zone items are hidden by filters", async ({ page }) => {
await mockMapShell(page); await mockMapShell(page);