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 }) => {