From b96cb65d6f2c2965501e4b83eceeb7704db1d646 Mon Sep 17 00:00:00 2001 From: Nico Date: Thu, 4 Jun 2026 15:28:43 -0700 Subject: [PATCH] fix: compact map sheet empty states --- .../maps/LocationMapBottomSheetSections.jsx | 70 ++++++++++++------- .../src/styles/pages/LocationMapManager.css | 54 ++++++++------ frontend/tests/location-map-manager.spec.ts | 41 +++++++---- 3 files changed, 102 insertions(+), 63 deletions(-) diff --git a/frontend/src/components/maps/LocationMapBottomSheetSections.jsx b/frontend/src/components/maps/LocationMapBottomSheetSections.jsx index 51b0e97..dc774f3 100644 --- a/frontend/src/components/maps/LocationMapBottomSheetSections.jsx +++ b/frontend/src/components/maps/LocationMapBottomSheetSections.jsx @@ -108,6 +108,20 @@ export function LocationMapOverview({ ariaLabel, rows }) { ); } +function InlineStateAction({ label, value, actionLabel, actionText = "Show", onAction }) { + return ( +
+ {label} + {value} + {onAction ? ( + + ) : null} +
+ ); +} + export function SelectedMapObjectForm({ selectedObject, zones = [], @@ -180,16 +194,16 @@ export function SelectedZoneItemsPanel({ return (
-

- {hasHiddenSelectedItems - ? "Items are assigned here, but hidden by layer filters." - : "No items assigned to this zone yet."} -

{hasHiddenSelectedItems ? ( - - ) : null} + + ) : ( + + )}
); @@ -204,12 +218,13 @@ export function SelectedZoneItemsPanel({ /> {hasHiddenSelectedItems ? (
-

- {hiddenSelectedItemCount} more hidden by layer filters. -

- +
) : null} @@ -228,12 +243,12 @@ export function UnmappedItemsPanel({
Unmapped Items
-

- Unmapped items are hidden by layer filters. -

- +
); @@ -253,12 +268,13 @@ export function UnmappedItemsPanel({ /> {hiddenUnmappedItemCount > 0 ? (
-

- {hiddenUnmappedItemCount} more hidden by layer filters. -

- +
) : null} diff --git a/frontend/src/styles/pages/LocationMapManager.css b/frontend/src/styles/pages/LocationMapManager.css index 63422a5..7a1dcf0 100644 --- a/frontend/src/styles/pages/LocationMapManager.css +++ b/frontend/src/styles/pages/LocationMapManager.css @@ -717,37 +717,45 @@ .location-map-zone-empty { display: grid; - gap: 0.55rem; -} - -.location-map-zone-empty button { - min-height: 40px; - width: fit-content; - padding: 0.45rem 0.7rem; - border: 1px solid var(--color-border-light); - border-radius: 8px; - background: #15283b; - color: #f8fafc; - font-weight: 800; - cursor: pointer; + gap: 0.35rem; } .location-map-zone-hidden-note { - display: grid; - gap: 0.45rem; margin-top: 0.55rem; } -.location-map-zone-hidden-note p { - margin: 0; -} - -.location-map-zone-hidden-note button { - min-height: 40px; - width: fit-content; - padding: 0.45rem 0.7rem; +.location-map-inline-state { + min-height: 42px; + display: grid; + grid-template-columns: minmax(0, 1fr) auto auto; + align-items: center; + gap: 0.5rem; + padding: 0.25rem 0.35rem 0.25rem 0.55rem; border: 1px solid var(--color-border-light); border-radius: 8px; + background: rgba(15, 23, 34, 0.56); + color: #f8fafc; +} + +.location-map-inline-state span { + min-width: 0; + color: #9fb3c8; + font-size: 0.82rem; + font-weight: 800; +} + +.location-map-inline-state strong { + color: #f8fafc; + font-weight: 900; + white-space: nowrap; +} + +.location-map-inline-state button { + min-height: 34px; + min-width: 64px; + padding: 0.35rem 0.6rem; + border: 1px solid var(--color-border-light); + border-radius: 999px; background: #15283b; color: #f8fafc; font-weight: 800; diff --git a/frontend/tests/location-map-manager.spec.ts b/frontend/tests/location-map-manager.spec.ts index 80d8701..8ed1c45 100644 --- a/frontend/tests/location-map-manager.spec.ts +++ b/frontend/tests/location-map-manager.spec.ts @@ -2914,7 +2914,10 @@ test("viewer can buy selected zone items from the map", async ({ page }) => { await expect(page.locator(".confirm-buy-modal")).toHaveCount(0); await expect(page.locator(".location-map-sheet-count")).toHaveText("0 shown"); await expect(bakeryArea.locator(".location-map-count")).toHaveText("0 shown"); - await expect(page.getByText("Items are assigned here, but hidden by layer filters.")).toBeVisible(); + const hiddenZoneState = page.locator(".location-map-zone-items .location-map-inline-state", { hasText: "Hidden" }); + await expect(hiddenZoneState).toContainText("2"); + await expect(hiddenZoneState.getByRole("button", { name: "Show Zone Items" })).toHaveText("Show"); + await expect(page.getByText("Items are assigned here, but hidden by layer filters.")).toHaveCount(0); await page.getByRole("button", { name: "Layers" }).click(); await page.getByRole("button", { name: "Bought" }).click(); @@ -3196,7 +3199,7 @@ test("viewer shows completed zone items without rebuy actions", async ({ page }) await expect(newlyBoughtRow.locator("small")).toHaveText("Bought"); }); -test("viewer explains when unmapped items are hidden by filters", async ({ page }) => { +test("viewer shows compact state when unmapped items are hidden by filters", async ({ page }) => { await mockMapShell(page); const mapState = { @@ -3228,13 +3231,15 @@ test("viewer explains when unmapped items are hidden by filters", async ({ page await page.getByRole("button", { name: "Unmapped" }).click(); 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(); + const hiddenUnmappedState = page.locator(".location-map-unmapped-list .location-map-inline-state", { hasText: "Hidden" }); + await expect(hiddenUnmappedState).toContainText("1"); + await expect(hiddenUnmappedState.getByRole("button", { name: "Show Unmapped Items" })).toHaveText("Show"); + await expect(page.getByText("Unmapped items are hidden by layer filters.")).toHaveCount(0); 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(hiddenUnmappedState).toHaveCount(0); await expect(page.getByRole("button", { name: "Layers, 3 changed" })).toBeVisible(); }); @@ -3280,16 +3285,18 @@ test("viewer marks partial unmapped counts when filters hide some items", async 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(); + const hiddenUnmappedState = page.locator(".location-map-unmapped-list .location-map-inline-state", { hasText: "Hidden" }); + await expect(hiddenUnmappedState).toContainText("1"); + await expect(hiddenUnmappedState.getByRole("button", { name: "Show All Unmapped Items" })).toHaveText("Show All"); + await expect(page.getByText("1 more hidden by layer filters.")).toHaveCount(0); 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); + await expect(hiddenUnmappedState).toHaveCount(0); }); -test("viewer explains when selected zone items are hidden by filters", async ({ page }) => { +test("viewer shows compact state when selected zone items are hidden by filters", async ({ page }) => { await mockMapShell(page); const mapState = publishedMapState([ @@ -3327,7 +3334,10 @@ test("viewer explains when selected zone items are hidden by filters", async ({ await frozenArea.locator("rect").first().click(); await expect(page.locator(".location-map-sheet-count")).toHaveText("0 shown"); - await expect(page.getByText("Items are assigned here, but hidden by layer filters.")).toBeVisible(); + const hiddenZoneState = page.locator(".location-map-zone-items .location-map-inline-state", { hasText: "Hidden" }); + await expect(hiddenZoneState).toContainText("1"); + await expect(hiddenZoneState.getByRole("button", { name: "Show Zone Items" })).toHaveText("Show"); + await expect(page.getByText("Items are assigned here, but hidden by layer filters.")).toHaveCount(0); await expect(page.getByText("frozen salmon")).toHaveCount(0); await page.getByRole("button", { name: "Show Zone Items" }).click(); @@ -3398,8 +3408,10 @@ test("viewer marks partial zone counts when filters hide some items", async ({ p await expect(page.locator(".location-map-sheet-count")).toHaveText("1 shown"); await expect(page.getByText("visible bread")).toBeVisible(); await expect(page.getByText("hidden baguette")).toHaveCount(0); - await expect(page.getByText("1 more hidden by layer filters.")).toBeVisible(); - await expect(page.getByRole("button", { name: "Show All Zone Items" })).toBeVisible(); + const hiddenZoneState = page.locator(".location-map-zone-items .location-map-inline-state", { hasText: "Hidden" }); + await expect(hiddenZoneState).toContainText("1"); + await expect(hiddenZoneState.getByRole("button", { name: "Show All Zone Items" })).toHaveText("Show All"); + await expect(page.getByText("1 more hidden by layer filters.")).toHaveCount(0); await page.getByRole("button", { name: "Show All Zone Items" }).click(); @@ -3472,7 +3484,10 @@ test("viewer handles empty and many-item zone panels without default pins", asyn await page.locator('[data-object-key="1562"]').locator("rect").first().click(); await expect(page.locator(".location-map-sheet-count")).toHaveText("0 items"); - await expect(page.getByText("No items assigned to this zone yet.")).toBeVisible(); + const emptyZoneState = page.locator(".location-map-zone-items .location-map-inline-state", { hasText: "Items" }); + await expect(emptyZoneState).toContainText("0"); + await expect(emptyZoneState.getByRole("button")).toHaveCount(0); + await expect(page.getByText("No items assigned to this zone yet.")).toHaveCount(0); await page.locator('[data-object-key="1561"]').locator("rect").first().click(); await expect(page.locator(".location-map-sheet-count")).toHaveText("14 items");