203 lines
5.8 KiB
JavaScript
203 lines
5.8 KiB
JavaScript
const DEFAULT_LOCATION_NAME = "Default Location";
|
|
|
|
export const DEFAULT_MAP_SIZE = {
|
|
width: 1000,
|
|
height: 700,
|
|
};
|
|
|
|
export const MIN_MAP_ZOOM = 0.3;
|
|
export const MAX_MAP_ZOOM = 1.6;
|
|
|
|
export const DEFAULT_MAP_FILTERS = {
|
|
showZones: true,
|
|
showLabels: true,
|
|
showCounts: true,
|
|
showPins: false,
|
|
showMyItems: true,
|
|
showOtherItems: false,
|
|
showCompleted: false,
|
|
showUnmapped: false,
|
|
};
|
|
|
|
export const MAP_OBJECT_TYPES = [
|
|
"zone",
|
|
"aisle",
|
|
"entrance",
|
|
"checkout",
|
|
"shelf",
|
|
"wall",
|
|
"department",
|
|
"anchor",
|
|
"other",
|
|
];
|
|
|
|
export function getStoreName(location) {
|
|
return location?.name || "Store";
|
|
}
|
|
|
|
export function getLocationName(location) {
|
|
if (!location) return "Location";
|
|
if (!location.location_name || location.location_name === DEFAULT_LOCATION_NAME) {
|
|
return "Default";
|
|
}
|
|
return location.location_name;
|
|
}
|
|
|
|
export function getMapStatus(state, options = {}) {
|
|
if (options.hasUnsavedChanges) return "Unsaved Draft";
|
|
if (!state?.draft_map && !state?.published_map) return "No Map";
|
|
if (options.mode === "edit") return "Draft";
|
|
if (options.mode === "view" && options.previewDraft && state?.draft_map) return "Viewing Draft";
|
|
if (state?.published_map) return "Published";
|
|
if (state?.draft_map) return "Draft";
|
|
return "No Map";
|
|
}
|
|
|
|
export function objectZoneId(object) {
|
|
return object?.zone_id ? String(object.zone_id) : "";
|
|
}
|
|
|
|
export function mapForMode(mapState, mode, previewDraft) {
|
|
if (mode === "edit") {
|
|
return mapState?.draft_map || mapState?.published_map || null;
|
|
}
|
|
|
|
if (previewDraft && mapState?.draft_map) {
|
|
return mapState.draft_map;
|
|
}
|
|
|
|
return mapState?.published_map || mapState?.draft_map || null;
|
|
}
|
|
|
|
export function objectsForMode(mapState, mode, previewDraft) {
|
|
if (mode === "edit") {
|
|
return mapState?.draft_objects?.length
|
|
? mapState.draft_objects
|
|
: mapState?.published_objects || [];
|
|
}
|
|
|
|
if (previewDraft && mapState?.draft_map) {
|
|
return mapState.draft_objects || [];
|
|
}
|
|
|
|
return mapState?.published_objects?.length
|
|
? mapState.published_objects
|
|
: mapState?.draft_objects || [];
|
|
}
|
|
|
|
export function createClientObject(zone, index = 0) {
|
|
const offset = index * 24;
|
|
return {
|
|
client_id: `new-${Date.now()}-${index}`,
|
|
zone_id: zone?.id || null,
|
|
zone_name: zone?.name || null,
|
|
type: "zone",
|
|
label: zone?.name || "New Area",
|
|
x: 80 + offset,
|
|
y: 80 + offset,
|
|
width: 220,
|
|
height: 130,
|
|
rotation: 0,
|
|
locked: false,
|
|
visible: true,
|
|
sort_order: index + 1,
|
|
metadata_json: {},
|
|
};
|
|
}
|
|
|
|
export function normalizeMapObject(object, index = 0) {
|
|
const hasExplicitLabel = Object.prototype.hasOwnProperty.call(object, "label");
|
|
|
|
return {
|
|
...object,
|
|
client_id: object.client_id || `object-${object.id || Date.now()}-${index}`,
|
|
zone_id: object.zone_id ?? null,
|
|
zone_name: object.zone_name || null,
|
|
type: MAP_OBJECT_TYPES.includes(object.type) ? object.type : "zone",
|
|
label: hasExplicitLabel
|
|
? String(object.label ?? "").trim()
|
|
: object.zone_name || "Map Area",
|
|
x: Number(object.x) || 0,
|
|
y: Number(object.y) || 0,
|
|
width: Math.max(40, Number(object.width) || 160),
|
|
height: Math.max(40, Number(object.height) || 100),
|
|
rotation: Number(object.rotation) || 0,
|
|
locked: Boolean(object.locked),
|
|
visible: object.visible !== false,
|
|
sort_order: Number(object.sort_order ?? object.sortOrder ?? index + 1),
|
|
metadata_json: object.metadata_json || {},
|
|
};
|
|
}
|
|
|
|
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,
|
|
type: object.type || "zone",
|
|
label: String(object.label ?? "").trim(),
|
|
x: Number(object.x) || 0,
|
|
y: Number(object.y) || 0,
|
|
width: Math.max(40, Number(object.width) || 160),
|
|
height: Math.max(40, Number(object.height) || 100),
|
|
rotation: Number(object.rotation) || 0,
|
|
locked: Boolean(object.locked),
|
|
visible: object.visible !== false,
|
|
sort_order: index + 1,
|
|
metadata_json: object.metadata_json || {},
|
|
}));
|
|
}
|
|
|
|
export function itemMatchesMapFilters(item, filters, username) {
|
|
if (!filters.showCompleted && item.bought) return false;
|
|
|
|
const addedBy = Array.isArray(item.added_by_users) ? item.added_by_users : [];
|
|
const hasOwnershipData = addedBy.length > 0 && username;
|
|
const isMine = hasOwnershipData ? addedBy.includes(username) : true;
|
|
if (isMine && !filters.showMyItems) return false;
|
|
if (!isMine && !filters.showOtherItems) return false;
|
|
return true;
|
|
}
|
|
|
|
export function itemsForZone(items, zoneId, filters, username) {
|
|
return items.filter((item) => (
|
|
String(item.zone_id || "") === String(zoneId || "") &&
|
|
itemMatchesMapFilters(item, filters, username)
|
|
));
|
|
}
|
|
|
|
export function unmappedItems(items, filters, username) {
|
|
return items.filter((item) => !item.zone_id && itemMatchesMapFilters(item, filters, username));
|
|
}
|
|
|
|
export function clientPointToMap(event, svgElement, mapSize) {
|
|
const rect = svgElement.getBoundingClientRect();
|
|
const x = ((event.clientX - rect.left) / rect.width) * mapSize.width;
|
|
const y = ((event.clientY - rect.top) / rect.height) * mapSize.height;
|
|
return { x, y };
|
|
}
|
|
|
|
export function clampMapZoom(value) {
|
|
return Math.max(MIN_MAP_ZOOM, Math.min(MAX_MAP_ZOOM, value));
|
|
}
|
|
|
|
export function clampObjectToMap(object, mapSize) {
|
|
const width = Math.max(40, Math.min(object.width, mapSize.width));
|
|
const height = Math.max(40, Math.min(object.height, mapSize.height));
|
|
return {
|
|
...object,
|
|
width,
|
|
height,
|
|
x: Math.max(0, Math.min(object.x, mapSize.width - width)),
|
|
y: Math.max(0, Math.min(object.y, mapSize.height - height)),
|
|
};
|
|
}
|