Add store location map manager #20

Open
nalalangan wants to merge 206 commits from feature/location-map-manager into feature/store-selector-modal
5 changed files with 29 additions and 13 deletions
Showing only changes of commit 306812d692 - Show all commits

View File

@ -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) {
return object?.zone_id ? String(object.zone_id) : "";
@ -48,7 +52,7 @@ export default function LocationMapBottomSheet({
onDuplicateObject,
onDeleteObject,
}) {
const selectedTitle = selectedObject?.label || selectedObject?.zone_name || "Map Area";
const selectedTitle = getMapObjectDisplayLabel(selectedObject);
const selectedAssignedItems = selectedObject?.zone_id
? (mapState?.items || []).filter((item) => String(item.zone_id || "") === objectZoneId(selectedObject))
: [];

View File

@ -1,6 +1,7 @@
import { useRef, useState } from "react";
import {
clientPointToMap,
getMapObjectDisplayLabel,
getObjectKey,
itemsForZone,
} from "../../lib/locationMapUtils";
@ -8,12 +9,6 @@ import {
const DRAG_CHANGE_THRESHOLD = 2;
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({
mode,
editorTool,
@ -218,7 +213,7 @@ export default function LocationMapCanvas({
};
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 canUseTwoLines = Number(object.height) >= 92 && rawLabel.length > maxCharacters;
if (!canUseTwoLines) return [truncateLabel(rawLabel, maxCharacters)];
@ -301,7 +296,7 @@ export default function LocationMapCanvas({
height={object.height}
rx="12"
role="button"
aria-label={`Map area ${mapObjectDisplayLabel(object, index + 1)}`}
aria-label={`Map area ${getMapObjectDisplayLabel(object, index + 1)}`}
aria-pressed={isSelected}
tabIndex={mode === "view" || canEditObjects ? 0 : -1}
onPointerDown={(event) => startDrag(event, object, "move")}

View File

@ -129,6 +129,12 @@ export function getObjectKey(object) {
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) {
return objects.map((object, index) => ({
zone_id: object.zone_id || null,

View File

@ -24,6 +24,7 @@ import {
clampMapZoom,
clampObjectToMap,
createClientObject,
getMapObjectDisplayLabel,
getMapStatus,
getObjectKey,
itemsForZone,
@ -322,11 +323,12 @@ export default function LocationMapManager() {
const handleDuplicateObject = () => {
if (!selectedObject) return;
remember();
const displayLabel = getMapObjectDisplayLabel(selectedObject, "Area");
const nextObject = {
...selectedObject,
id: undefined,
client_id: `copy-${Date.now()}`,
label: `${selectedObject.label || "Area"} Copy`,
label: `${displayLabel} Copy`,
x: selectedObject.x + 28,
y: selectedObject.y + 28,
sort_order: objects.length + 1,
@ -584,7 +586,7 @@ export default function LocationMapManager() {
<ConfirmSlideModal
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."
confirmLabel="Delete Area"
onClose={() => setPendingDeleteObject(null)}

View File

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