fix: reuse map area display names
This commit is contained in:
parent
415a7223cd
commit
306812d692
@ -1,4 +1,8 @@
|
|||||||
import { DEFAULT_MAP_FILTERS, MAP_OBJECT_TYPES } from "../../lib/locationMapUtils";
|
import {
|
||||||
|
DEFAULT_MAP_FILTERS,
|
||||||
|
MAP_OBJECT_TYPES,
|
||||||
|
getMapObjectDisplayLabel,
|
||||||
|
} from "../../lib/locationMapUtils";
|
||||||
|
|
||||||
function objectZoneId(object) {
|
function objectZoneId(object) {
|
||||||
return object?.zone_id ? String(object.zone_id) : "";
|
return object?.zone_id ? String(object.zone_id) : "";
|
||||||
@ -48,7 +52,7 @@ export default function LocationMapBottomSheet({
|
|||||||
onDuplicateObject,
|
onDuplicateObject,
|
||||||
onDeleteObject,
|
onDeleteObject,
|
||||||
}) {
|
}) {
|
||||||
const selectedTitle = selectedObject?.label || selectedObject?.zone_name || "Map Area";
|
const selectedTitle = getMapObjectDisplayLabel(selectedObject);
|
||||||
const selectedAssignedItems = selectedObject?.zone_id
|
const selectedAssignedItems = selectedObject?.zone_id
|
||||||
? (mapState?.items || []).filter((item) => String(item.zone_id || "") === objectZoneId(selectedObject))
|
? (mapState?.items || []).filter((item) => String(item.zone_id || "") === objectZoneId(selectedObject))
|
||||||
: [];
|
: [];
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
clientPointToMap,
|
clientPointToMap,
|
||||||
|
getMapObjectDisplayLabel,
|
||||||
getObjectKey,
|
getObjectKey,
|
||||||
itemsForZone,
|
itemsForZone,
|
||||||
} from "../../lib/locationMapUtils";
|
} from "../../lib/locationMapUtils";
|
||||||
@ -8,12 +9,6 @@ import {
|
|||||||
const DRAG_CHANGE_THRESHOLD = 2;
|
const DRAG_CHANGE_THRESHOLD = 2;
|
||||||
const NUDGE_KEYS = new Set(["ArrowUp", "ArrowRight", "ArrowDown", "ArrowLeft"]);
|
const NUDGE_KEYS = new Set(["ArrowUp", "ArrowRight", "ArrowDown", "ArrowLeft"]);
|
||||||
|
|
||||||
function mapObjectDisplayLabel(object, fallback) {
|
|
||||||
const label = String(object.label || "").trim();
|
|
||||||
const zoneName = String(object.zone_name || "").trim();
|
|
||||||
return label || zoneName || fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function LocationMapCanvas({
|
export default function LocationMapCanvas({
|
||||||
mode,
|
mode,
|
||||||
editorTool,
|
editorTool,
|
||||||
@ -218,7 +213,7 @@ export default function LocationMapCanvas({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fittedLabelLines = (object, fallback) => {
|
const fittedLabelLines = (object, fallback) => {
|
||||||
const rawLabel = mapObjectDisplayLabel(object, fallback);
|
const rawLabel = getMapObjectDisplayLabel(object, fallback);
|
||||||
const maxCharacters = Math.max(8, Math.floor((Number(object.width) - 28) / 13));
|
const maxCharacters = Math.max(8, Math.floor((Number(object.width) - 28) / 13));
|
||||||
const canUseTwoLines = Number(object.height) >= 92 && rawLabel.length > maxCharacters;
|
const canUseTwoLines = Number(object.height) >= 92 && rawLabel.length > maxCharacters;
|
||||||
if (!canUseTwoLines) return [truncateLabel(rawLabel, maxCharacters)];
|
if (!canUseTwoLines) return [truncateLabel(rawLabel, maxCharacters)];
|
||||||
@ -301,7 +296,7 @@ export default function LocationMapCanvas({
|
|||||||
height={object.height}
|
height={object.height}
|
||||||
rx="12"
|
rx="12"
|
||||||
role="button"
|
role="button"
|
||||||
aria-label={`Map area ${mapObjectDisplayLabel(object, index + 1)}`}
|
aria-label={`Map area ${getMapObjectDisplayLabel(object, index + 1)}`}
|
||||||
aria-pressed={isSelected}
|
aria-pressed={isSelected}
|
||||||
tabIndex={mode === "view" || canEditObjects ? 0 : -1}
|
tabIndex={mode === "view" || canEditObjects ? 0 : -1}
|
||||||
onPointerDown={(event) => startDrag(event, object, "move")}
|
onPointerDown={(event) => startDrag(event, object, "move")}
|
||||||
|
|||||||
@ -129,6 +129,12 @@ export function getObjectKey(object) {
|
|||||||
return String(object.id || object.client_id);
|
return String(object.id || object.client_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMapObjectDisplayLabel(object, fallback = "Map Area") {
|
||||||
|
const label = String(object?.label || "").trim();
|
||||||
|
const zoneName = String(object?.zone_name || "").trim();
|
||||||
|
return label || zoneName || fallback;
|
||||||
|
}
|
||||||
|
|
||||||
export function prepareObjectsForSave(objects) {
|
export function prepareObjectsForSave(objects) {
|
||||||
return objects.map((object, index) => ({
|
return objects.map((object, index) => ({
|
||||||
zone_id: object.zone_id || null,
|
zone_id: object.zone_id || null,
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import {
|
|||||||
clampMapZoom,
|
clampMapZoom,
|
||||||
clampObjectToMap,
|
clampObjectToMap,
|
||||||
createClientObject,
|
createClientObject,
|
||||||
|
getMapObjectDisplayLabel,
|
||||||
getMapStatus,
|
getMapStatus,
|
||||||
getObjectKey,
|
getObjectKey,
|
||||||
itemsForZone,
|
itemsForZone,
|
||||||
@ -322,11 +323,12 @@ export default function LocationMapManager() {
|
|||||||
const handleDuplicateObject = () => {
|
const handleDuplicateObject = () => {
|
||||||
if (!selectedObject) return;
|
if (!selectedObject) return;
|
||||||
remember();
|
remember();
|
||||||
|
const displayLabel = getMapObjectDisplayLabel(selectedObject, "Area");
|
||||||
const nextObject = {
|
const nextObject = {
|
||||||
...selectedObject,
|
...selectedObject,
|
||||||
id: undefined,
|
id: undefined,
|
||||||
client_id: `copy-${Date.now()}`,
|
client_id: `copy-${Date.now()}`,
|
||||||
label: `${selectedObject.label || "Area"} Copy`,
|
label: `${displayLabel} Copy`,
|
||||||
x: selectedObject.x + 28,
|
x: selectedObject.x + 28,
|
||||||
y: selectedObject.y + 28,
|
y: selectedObject.y + 28,
|
||||||
sort_order: objects.length + 1,
|
sort_order: objects.length + 1,
|
||||||
@ -584,7 +586,7 @@ export default function LocationMapManager() {
|
|||||||
|
|
||||||
<ConfirmSlideModal
|
<ConfirmSlideModal
|
||||||
isOpen={Boolean(pendingDeleteObject)}
|
isOpen={Boolean(pendingDeleteObject)}
|
||||||
title={`Delete ${pendingDeleteObject?.label || "this map area"}?`}
|
title={`Delete ${getMapObjectDisplayLabel(pendingDeleteObject, "this map area")}?`}
|
||||||
description="This removes the area from the draft map. Save the draft after deleting to keep the change."
|
description="This removes the area from the draft map. Save the draft after deleting to keep the change."
|
||||||
confirmLabel="Delete Area"
|
confirmLabel="Delete Area"
|
||||||
onClose={() => setPendingDeleteObject(null)}
|
onClose={() => setPendingDeleteObject(null)}
|
||||||
|
|||||||
@ -596,7 +596,7 @@ test("admin selecting an object does not mark a draft dirty until it changes", a
|
|||||||
await expect(page.getByRole("button", { name: "Undo" })).toBeEnabled();
|
await expect(page.getByRole("button", { name: "Undo" })).toBeEnabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("admin cleared map area labels keep linked zone accessible names", async ({ page }) => {
|
test("admin cleared map area labels keep linked zone action names", async ({ page }) => {
|
||||||
await mockMapShell(page);
|
await mockMapShell(page);
|
||||||
|
|
||||||
const mapState = draftMapState([
|
const mapState = draftMapState([
|
||||||
@ -635,6 +635,15 @@ test("admin cleared map area labels keep linked zone accessible names", async ({
|
|||||||
|
|
||||||
await expect(page.getByRole("button", { name: "Map area Bakery", exact: true })).toBeVisible();
|
await expect(page.getByRole("button", { name: "Map area Bakery", exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole("button", { name: "Map area 1", exact: true })).toHaveCount(0);
|
await expect(page.getByRole("button", { name: "Map area 1", exact: true })).toHaveCount(0);
|
||||||
|
|
||||||
|
await page.getByRole("button", { name: "Delete" }).click();
|
||||||
|
await expect(page.getByRole("heading", { name: "Delete Bakery?" })).toBeVisible();
|
||||||
|
await page.getByRole("button", { name: "Cancel" }).click();
|
||||||
|
|
||||||
|
await page.getByRole("button", { name: "Duplicate" }).click();
|
||||||
|
await expect(page.getByRole("textbox", { name: "Label" })).toHaveValue("Bakery Copy");
|
||||||
|
await expect(page.getByRole("button", { name: "Map area Bakery Copy" })).toBeVisible();
|
||||||
|
await expect(page.getByRole("button", { name: "Map area Area Copy" })).toHaveCount(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("admin nudges selected map areas with arrow keys", async ({ page }) => {
|
test("admin nudges selected map areas with arrow keys", async ({ page }) => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user