diff --git a/frontend/src/components/maps/LocationMapObject.jsx b/frontend/src/components/maps/LocationMapObject.jsx index ce276f1..02e0605 100644 --- a/frontend/src/components/maps/LocationMapObject.jsx +++ b/frontend/src/components/maps/LocationMapObject.jsx @@ -62,6 +62,8 @@ export default function LocationMapObject({ const countY = filters.showLabels && labelLines.length > 1 ? object.y + 76 : object.y + 52; const resizeHandleSize = Math.min(96, Math.max(44, Math.ceil(48 / zoom))); const resizeHandleInset = 6; + const lockBadgeSize = Math.min(52, Math.max(30, Math.ceil(34 / zoom))); + const lockBadgeInset = 8; const resizeHandleX = Math.min( Math.max(0, mapSize.width - resizeHandleSize), Math.max( @@ -76,6 +78,17 @@ export default function LocationMapObject({ object.y + object.height - resizeHandleSize - resizeHandleInset ) ); + const lockBadgeX = Math.min( + Math.max(0, mapSize.width - lockBadgeSize), + Math.max( + object.x + lockBadgeInset, + object.x + object.width - lockBadgeSize - lockBadgeInset + ) + ); + const lockBadgeY = Math.min( + Math.max(0, mapSize.height - lockBadgeSize), + object.y + lockBadgeInset + ); const pinDots = filters.showPins ? zoneItems.slice(0, PIN_PREVIEW_LIMIT).map((item, itemIndex) => { const column = itemIndex % 4; @@ -99,6 +112,7 @@ export default function LocationMapObject({ "location-map-object", isSelected ? "is-selected" : "", canEditObjects ? "is-editable" : "", + object.locked ? "is-locked" : "", ].filter(Boolean).join(" ")} transform={`rotate(${object.rotation || 0} ${object.x + object.width / 2} ${object.y + object.height / 2})`} > @@ -131,6 +145,31 @@ export default function LocationMapObject({ ) : null} {pinDots} + {canEditObjects && isSelected && object.locked ? ( + + ) : null} {canEditObjects && isSelected && !object.locked ? ( { await page.getByRole("button", { name: "Continue Editing" }).click(); await page.getByRole("button", { name: "Edit Areas" }).click(); const bakeryArea = page.getByRole("button", { name: "Map area Bakery" }); + const bakeryObject = page.locator(".location-map-object", { hasText: "Bakery" }); await bakeryArea.click(); await expect(page.locator(".location-map-resize-handle")).toBeVisible(); + await expect(page.locator(".location-map-lock-badge")).toHaveCount(0); await expect(page.getByRole("spinbutton", { name: "X" })).toHaveCount(0); const lockedToggle = page.getByRole("button", { name: "Locked" }); const visibleToggle = page.getByRole("button", { name: "Visible" }); @@ -1820,6 +1822,8 @@ test("admin locked map areas hide resize affordances", async ({ page }) => { await expect(page.getByRole("spinbutton", { name: "W" })).toHaveCount(0); await expect(page.getByRole("spinbutton", { name: "H" })).toHaveCount(0); await expect(page.locator(".location-map-resize-handle")).toHaveCount(0); + await expect(page.locator(".location-map-lock-badge")).toBeVisible(); + await expect(bakeryObject).toHaveClass(/is-locked/); await expect(page.getByText("Unlock this area before moving or resizing it.")).toHaveCount(0); await expect(page.locator(".location-map-status")).toHaveText("Unsaved Draft"); @@ -1830,6 +1834,7 @@ test("admin locked map areas hide resize affordances", async ({ page }) => { await lockedToggle.click(); await expect(lockedToggle).toHaveAttribute("aria-pressed", "false"); await expect(page.locator(".location-map-resize-handle")).toBeVisible(); + await expect(page.locator(".location-map-lock-badge")).toHaveCount(0); }); test("admin selected overlapping map area renders above other areas", async ({ page }) => {