refactor: split location map manager
This commit is contained in:
parent
26b05d9729
commit
f397f19e48
48
frontend/src/components/maps/LocationMapDialogs.jsx
Normal file
48
frontend/src/components/maps/LocationMapDialogs.jsx
Normal file
@ -0,0 +1,48 @@
|
||||
import ConfirmBuyModal from "../modals/ConfirmBuyModal";
|
||||
import ConfirmSlideModal from "../modals/ConfirmSlideModal";
|
||||
import { getMapObjectDisplayLabel } from "../../lib/locationMapUtils";
|
||||
|
||||
export default function LocationMapDialogs({
|
||||
buyModalItem,
|
||||
buyModalItems,
|
||||
onBuyCancel,
|
||||
onBuyConfirm,
|
||||
onBuyNavigate,
|
||||
pendingDeleteObject,
|
||||
onDeleteClose,
|
||||
onDeleteConfirm,
|
||||
pendingLeaveTarget,
|
||||
onLeaveClose,
|
||||
onLeaveConfirm,
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
{buyModalItem ? (
|
||||
<ConfirmBuyModal
|
||||
item={buyModalItem}
|
||||
allItems={buyModalItems}
|
||||
onNavigate={onBuyNavigate}
|
||||
onCancel={onBuyCancel}
|
||||
onConfirm={onBuyConfirm}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<ConfirmSlideModal
|
||||
isOpen={Boolean(pendingDeleteObject)}
|
||||
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={onDeleteClose}
|
||||
onConfirm={onDeleteConfirm}
|
||||
/>
|
||||
<ConfirmSlideModal
|
||||
isOpen={Boolean(pendingLeaveTarget)}
|
||||
title="Leave with unsaved changes?"
|
||||
description="Your draft edits are only on this screen right now. Save the draft before leaving if you want to keep them."
|
||||
confirmLabel="Leave Map"
|
||||
onClose={onLeaveClose}
|
||||
onConfirm={onLeaveConfirm}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
145
frontend/src/hooks/useLocationMapBuying.js
Normal file
145
frontend/src/hooks/useLocationMapBuying.js
Normal file
@ -0,0 +1,145 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { getItemByName, markBought } from "../api/list";
|
||||
import getApiErrorMessage from "../lib/getApiErrorMessage";
|
||||
|
||||
const EMPTY_MAP_ITEMS = [];
|
||||
|
||||
function getNextMapBuyItem(items, currentIndex, excludedItemId) {
|
||||
const remainingItems = items.filter((item) => item.id !== excludedItemId);
|
||||
if (!remainingItems.length) return null;
|
||||
return remainingItems[Math.min(Math.max(currentIndex, 0), remainingItems.length - 1)];
|
||||
}
|
||||
|
||||
function getLocallyReducedMapItem(item, quantity) {
|
||||
const currentQuantity = Number(item.quantity || 1);
|
||||
const boughtQuantity = Math.max(1, Number(quantity || 1));
|
||||
return {
|
||||
...item,
|
||||
quantity: Math.max(1, currentQuantity - boughtQuantity),
|
||||
};
|
||||
}
|
||||
|
||||
function getLocallyBoughtMapItem(item) {
|
||||
return {
|
||||
...item,
|
||||
bought: true,
|
||||
};
|
||||
}
|
||||
|
||||
export default function useLocationMapBuying({
|
||||
activeHouseholdId,
|
||||
locationId,
|
||||
selectedObject,
|
||||
selectedZoneItems = EMPTY_MAP_ITEMS,
|
||||
setMapState,
|
||||
toast,
|
||||
visibleUnmappedItems = EMPTY_MAP_ITEMS,
|
||||
}) {
|
||||
const [buyModalItem, setBuyModalItem] = useState(null);
|
||||
const mapBuyScopeRef = useRef({ activeHouseholdId: null, locationId: null });
|
||||
const buyModalItems = useMemo(
|
||||
() => (selectedObject ? selectedZoneItems : visibleUnmappedItems).filter((item) => !item.bought),
|
||||
[selectedObject, selectedZoneItems, visibleUnmappedItems]
|
||||
);
|
||||
|
||||
const updateMapItems = useCallback((updater) => {
|
||||
setMapState((currentState) => {
|
||||
if (!currentState) return currentState;
|
||||
const currentItems = currentState.items || EMPTY_MAP_ITEMS;
|
||||
return { ...currentState, items: updater(currentItems) };
|
||||
});
|
||||
}, [setMapState]);
|
||||
|
||||
useEffect(() => {
|
||||
mapBuyScopeRef.current = {
|
||||
activeHouseholdId,
|
||||
locationId,
|
||||
};
|
||||
}, [activeHouseholdId, locationId]);
|
||||
|
||||
useEffect(() => {
|
||||
setBuyModalItem(null);
|
||||
}, [activeHouseholdId, locationId]);
|
||||
|
||||
const isCurrentMapBuyScope = useCallback((scopedHouseholdId, scopedLocationId) => (
|
||||
String(mapBuyScopeRef.current.activeHouseholdId || "") === String(scopedHouseholdId || "") &&
|
||||
String(mapBuyScopeRef.current.locationId || "") === String(scopedLocationId || "")
|
||||
), []);
|
||||
|
||||
const handleMapBuyCancel = useCallback(() => {
|
||||
setBuyModalItem(null);
|
||||
}, []);
|
||||
|
||||
const handleMapBuyNavigate = useCallback((item) => {
|
||||
setBuyModalItem(item);
|
||||
}, []);
|
||||
|
||||
const handleMapBuyConfirm = useCallback(async (quantity) => {
|
||||
if (!activeHouseholdId || !locationId || !buyModalItem) {
|
||||
setBuyModalItem(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const scopedHouseholdId = activeHouseholdId;
|
||||
const scopedLocationId = locationId;
|
||||
const item = buyModalItems.find((candidate) => candidate.id === buyModalItem.id) || buyModalItem;
|
||||
|
||||
try {
|
||||
const currentIndex = buyModalItems.findIndex((candidate) => candidate.id === item.id);
|
||||
const resolvedIndex = currentIndex >= 0 ? currentIndex : 0;
|
||||
|
||||
await markBought(scopedHouseholdId, scopedLocationId, item.item_name, quantity, true);
|
||||
if (!isCurrentMapBuyScope(scopedHouseholdId, scopedLocationId)) return;
|
||||
|
||||
let nextBuyModalItems = buyModalItems;
|
||||
if (quantity >= item.quantity) {
|
||||
nextBuyModalItems = buyModalItems.filter((candidate) => candidate.id !== item.id);
|
||||
updateMapItems((currentItems) =>
|
||||
currentItems.map((candidate) => (
|
||||
candidate.id === item.id ? getLocallyBoughtMapItem(candidate) : candidate
|
||||
))
|
||||
);
|
||||
} else {
|
||||
let updatedItem;
|
||||
try {
|
||||
const response = await getItemByName(scopedHouseholdId, scopedLocationId, item.item_name);
|
||||
if (!isCurrentMapBuyScope(scopedHouseholdId, scopedLocationId)) return;
|
||||
updatedItem = response.data || getLocallyReducedMapItem(item, quantity);
|
||||
} catch {
|
||||
if (!isCurrentMapBuyScope(scopedHouseholdId, scopedLocationId)) return;
|
||||
updatedItem = getLocallyReducedMapItem(item, quantity);
|
||||
toast.info("Updated item locally", "Latest quantity will sync when the map reloads.");
|
||||
}
|
||||
nextBuyModalItems = buyModalItems.map((candidate) => (candidate.id === item.id ? updatedItem : candidate));
|
||||
|
||||
updateMapItems((currentItems) =>
|
||||
currentItems.map((candidate) => (candidate.id === item.id ? updatedItem : candidate))
|
||||
);
|
||||
}
|
||||
setBuyModalItem(getNextMapBuyItem(nextBuyModalItems, resolvedIndex, item.id));
|
||||
|
||||
toast.success("Marked item bought", `Marked item ${item.item_name} as bought`);
|
||||
} catch (error) {
|
||||
if (!isCurrentMapBuyScope(scopedHouseholdId, scopedLocationId)) return;
|
||||
const message = getApiErrorMessage(error, "Failed to mark item as bought");
|
||||
toast.error("Mark item bought failed", `Mark item bought failed: ${message}`);
|
||||
}
|
||||
}, [
|
||||
activeHouseholdId,
|
||||
buyModalItem,
|
||||
buyModalItems,
|
||||
isCurrentMapBuyScope,
|
||||
locationId,
|
||||
toast,
|
||||
updateMapItems,
|
||||
]);
|
||||
|
||||
return {
|
||||
buyModalItem,
|
||||
buyModalItems,
|
||||
handleMapBuyCancel,
|
||||
handleMapBuyConfirm,
|
||||
handleMapBuyNavigate,
|
||||
setBuyModalItem,
|
||||
};
|
||||
}
|
||||
83
frontend/src/hooks/useLocationMapDerivedState.js
Normal file
83
frontend/src/hooks/useLocationMapDerivedState.js
Normal file
@ -0,0 +1,83 @@
|
||||
import { useMemo } from "react";
|
||||
import {
|
||||
DEFAULT_MAP_SIZE,
|
||||
getObjectKey,
|
||||
itemMatchesMapFilters,
|
||||
itemsForZone,
|
||||
mapForMode,
|
||||
unmappedItems,
|
||||
} from "../lib/locationMapUtils";
|
||||
|
||||
const EMPTY_MAP_ITEMS = [];
|
||||
|
||||
export default function useLocationMapDerivedState({
|
||||
filters,
|
||||
mapDraft,
|
||||
mapState,
|
||||
mode,
|
||||
objects,
|
||||
previewDraft,
|
||||
selectedObjectKey,
|
||||
username,
|
||||
}) {
|
||||
const activeMap = useMemo(
|
||||
() => mapForMode(mapState, mode, previewDraft),
|
||||
[mapState, mode, previewDraft]
|
||||
);
|
||||
const mapItems = mapState?.items || EMPTY_MAP_ITEMS;
|
||||
const selectedObject = useMemo(
|
||||
() => objects.find((object) => getObjectKey(object) === selectedObjectKey) || null,
|
||||
[objects, selectedObjectKey]
|
||||
);
|
||||
const selectedZoneItems = useMemo(
|
||||
() => selectedObject?.zone_id
|
||||
? itemsForZone(mapItems, selectedObject.zone_id, filters, username)
|
||||
: EMPTY_MAP_ITEMS,
|
||||
[filters, mapItems, selectedObject?.zone_id, username]
|
||||
);
|
||||
const visibleUnmappedItems = useMemo(
|
||||
() => unmappedItems(mapItems, filters, username),
|
||||
[filters, mapItems, username]
|
||||
);
|
||||
const { visibleAssignedItemCount, unmappedItemCount } = useMemo(() => {
|
||||
let nextVisibleAssignedItemCount = 0;
|
||||
let nextUnmappedItemCount = 0;
|
||||
|
||||
mapItems.forEach((item) => {
|
||||
if (!item.zone_id) {
|
||||
nextUnmappedItemCount += 1;
|
||||
return;
|
||||
}
|
||||
if (itemMatchesMapFilters(item, filters, username)) {
|
||||
nextVisibleAssignedItemCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
visibleAssignedItemCount: nextVisibleAssignedItemCount,
|
||||
unmappedItemCount: nextUnmappedItemCount,
|
||||
};
|
||||
}, [filters, mapItems, username]);
|
||||
const hiddenAreaCount = useMemo(
|
||||
() => objects.filter((object) => object.visible === false).length,
|
||||
[objects]
|
||||
);
|
||||
const mapSize = useMemo(
|
||||
() => ({
|
||||
width: mapDraft.width || activeMap?.width || DEFAULT_MAP_SIZE.width,
|
||||
height: mapDraft.height || activeMap?.height || DEFAULT_MAP_SIZE.height,
|
||||
}),
|
||||
[activeMap?.height, activeMap?.width, mapDraft.height, mapDraft.width]
|
||||
);
|
||||
|
||||
return {
|
||||
hiddenAreaCount,
|
||||
mapItems,
|
||||
mapSize,
|
||||
selectedObject,
|
||||
selectedZoneItems,
|
||||
unmappedItemCount,
|
||||
visibleAssignedItemCount,
|
||||
visibleUnmappedItems,
|
||||
};
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useCallback, useContext, useEffect, useRef } from "react";
|
||||
import { useLocation, useNavigate, useParams } from "react-router-dom";
|
||||
import { getItemByName, markBought } from "../api/list";
|
||||
import { AuthContext } from "../context/AuthContext";
|
||||
import { HouseholdContext } from "../context/HouseholdContext";
|
||||
import { StoreContext } from "../context/StoreContext";
|
||||
import LocationMapBottomSheet from "../components/maps/LocationMapBottomSheet";
|
||||
import LocationMapCanvas from "../components/maps/LocationMapCanvas";
|
||||
import LocationMapDialogs from "../components/maps/LocationMapDialogs";
|
||||
import LocationMapSetupPanel from "../components/maps/LocationMapSetupPanel";
|
||||
import {
|
||||
LocationMapLoadErrorState,
|
||||
@ -13,51 +13,17 @@ import {
|
||||
} from "../components/maps/LocationMapStateViews";
|
||||
import LocationMapToolbar from "../components/maps/LocationMapToolbar";
|
||||
import LocationMapTopbar from "../components/maps/LocationMapTopbar";
|
||||
import ConfirmBuyModal from "../components/modals/ConfirmBuyModal";
|
||||
import ConfirmSlideModal from "../components/modals/ConfirmSlideModal";
|
||||
import useActionToast from "../hooks/useActionToast";
|
||||
import useLocationMapDerivedState from "../hooks/useLocationMapDerivedState";
|
||||
import useLocationMapDraftActions from "../hooks/useLocationMapDraftActions";
|
||||
import useLocationMapDraftState from "../hooks/useLocationMapDraftState";
|
||||
import useLocationMapBuying from "../hooks/useLocationMapBuying";
|
||||
import useLocationMapObjectActions from "../hooks/useLocationMapObjectActions";
|
||||
import useLocationMapViewControls from "../hooks/useLocationMapViewControls";
|
||||
import useUnsavedMapLeaveGuard from "../hooks/useUnsavedMapLeaveGuard";
|
||||
import {
|
||||
DEFAULT_MAP_SIZE,
|
||||
getMapObjectDisplayLabel,
|
||||
getMapStatus,
|
||||
getObjectKey,
|
||||
itemMatchesMapFilters,
|
||||
itemsForZone,
|
||||
mapForMode,
|
||||
unmappedItems,
|
||||
} from "../lib/locationMapUtils";
|
||||
import getApiErrorMessage from "../lib/getApiErrorMessage";
|
||||
import { getMapStatus } from "../lib/locationMapUtils";
|
||||
import "../styles/pages/LocationMapManager.css";
|
||||
|
||||
const EMPTY_MAP_ITEMS = [];
|
||||
|
||||
function getNextMapBuyItem(items, currentIndex, excludedItemId) {
|
||||
const remainingItems = items.filter((item) => item.id !== excludedItemId);
|
||||
if (!remainingItems.length) return null;
|
||||
return remainingItems[Math.min(Math.max(currentIndex, 0), remainingItems.length - 1)];
|
||||
}
|
||||
|
||||
function getLocallyReducedMapItem(item, quantity) {
|
||||
const currentQuantity = Number(item.quantity || 1);
|
||||
const boughtQuantity = Math.max(1, Number(quantity || 1));
|
||||
return {
|
||||
...item,
|
||||
quantity: Math.max(1, currentQuantity - boughtQuantity),
|
||||
};
|
||||
}
|
||||
|
||||
function getLocallyBoughtMapItem(item) {
|
||||
return {
|
||||
...item,
|
||||
bought: true,
|
||||
};
|
||||
}
|
||||
|
||||
export default function LocationMapManager() {
|
||||
const { storeId, locationId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
@ -67,8 +33,6 @@ export default function LocationMapManager() {
|
||||
const { username } = useContext(AuthContext);
|
||||
const { activeHousehold, loading: householdLoading, hasLoaded } = useContext(HouseholdContext);
|
||||
const { stores, activeStore, setActiveStore } = useContext(StoreContext);
|
||||
const [buyModalItem, setBuyModalItem] = useState(null);
|
||||
const mapBuyScopeRef = useRef({ activeHouseholdId: null, locationId: null });
|
||||
|
||||
const {
|
||||
beginSaving,
|
||||
@ -117,146 +81,43 @@ export default function LocationMapManager() {
|
||||
|
||||
const location = mapState?.location || stores.find((store) => String(store.id) === String(locationId));
|
||||
const canManage = Boolean(mapState?.can_manage ?? ["owner", "admin"].includes(activeHousehold?.role));
|
||||
const activeMap = useMemo(
|
||||
() => mapForMode(mapState, mode, previewDraft),
|
||||
[mapState, mode, previewDraft]
|
||||
);
|
||||
const mapItems = mapState?.items || EMPTY_MAP_ITEMS;
|
||||
const selectedObject = useMemo(
|
||||
() => objects.find((object) => getObjectKey(object) === selectedObjectKey) || null,
|
||||
[objects, selectedObjectKey]
|
||||
);
|
||||
const selectedZoneItems = useMemo(
|
||||
() => selectedObject?.zone_id
|
||||
? itemsForZone(mapItems, selectedObject.zone_id, filters, username)
|
||||
: EMPTY_MAP_ITEMS,
|
||||
[filters, mapItems, selectedObject?.zone_id, username]
|
||||
);
|
||||
const visibleUnmappedItems = useMemo(
|
||||
() => unmappedItems(mapItems, filters, username),
|
||||
[filters, mapItems, username]
|
||||
);
|
||||
const buyModalItems = useMemo(
|
||||
() => (selectedObject ? selectedZoneItems : visibleUnmappedItems).filter((item) => !item.bought),
|
||||
[selectedObject, selectedZoneItems, visibleUnmappedItems]
|
||||
);
|
||||
const { visibleAssignedItemCount, unmappedItemCount } = useMemo(() => {
|
||||
let nextVisibleAssignedItemCount = 0;
|
||||
let nextUnmappedItemCount = 0;
|
||||
|
||||
mapItems.forEach((item) => {
|
||||
if (!item.zone_id) {
|
||||
nextUnmappedItemCount += 1;
|
||||
return;
|
||||
}
|
||||
if (itemMatchesMapFilters(item, filters, username)) {
|
||||
nextVisibleAssignedItemCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
visibleAssignedItemCount: nextVisibleAssignedItemCount,
|
||||
unmappedItemCount: nextUnmappedItemCount,
|
||||
};
|
||||
}, [filters, mapItems, username]);
|
||||
const hiddenAreaCount = useMemo(
|
||||
() => objects.filter((object) => object.visible === false).length,
|
||||
[objects]
|
||||
);
|
||||
const {
|
||||
hiddenAreaCount,
|
||||
mapItems,
|
||||
mapSize,
|
||||
selectedObject,
|
||||
selectedZoneItems,
|
||||
unmappedItemCount,
|
||||
visibleAssignedItemCount,
|
||||
visibleUnmappedItems,
|
||||
} = useLocationMapDerivedState({
|
||||
filters,
|
||||
mapDraft,
|
||||
mapState,
|
||||
mode,
|
||||
objects,
|
||||
previewDraft,
|
||||
selectedObjectKey,
|
||||
username,
|
||||
});
|
||||
const {
|
||||
buyModalItem,
|
||||
buyModalItems,
|
||||
handleMapBuyCancel,
|
||||
handleMapBuyConfirm,
|
||||
handleMapBuyNavigate,
|
||||
setBuyModalItem,
|
||||
} = useLocationMapBuying({
|
||||
activeHouseholdId: activeHousehold?.id,
|
||||
locationId,
|
||||
selectedObject,
|
||||
selectedZoneItems,
|
||||
setMapState,
|
||||
toast,
|
||||
visibleUnmappedItems,
|
||||
});
|
||||
const shouldGuardLeave = canManage && hasUnsavedChanges;
|
||||
|
||||
const updateMapItems = useCallback((updater) => {
|
||||
setMapState((currentState) => {
|
||||
if (!currentState) return currentState;
|
||||
const currentItems = currentState.items || EMPTY_MAP_ITEMS;
|
||||
return { ...currentState, items: updater(currentItems) };
|
||||
});
|
||||
}, [setMapState]);
|
||||
|
||||
useEffect(() => {
|
||||
mapBuyScopeRef.current = {
|
||||
activeHouseholdId: activeHousehold?.id,
|
||||
locationId,
|
||||
};
|
||||
}, [activeHousehold?.id, locationId]);
|
||||
|
||||
const isCurrentMapBuyScope = useCallback((activeHouseholdId, scopedLocationId) => (
|
||||
String(mapBuyScopeRef.current.activeHouseholdId || "") === String(activeHouseholdId || "") &&
|
||||
String(mapBuyScopeRef.current.locationId || "") === String(scopedLocationId || "")
|
||||
), []);
|
||||
|
||||
const handleMapBuyCancel = useCallback(() => {
|
||||
setBuyModalItem(null);
|
||||
}, []);
|
||||
|
||||
const handleMapBuyNavigate = useCallback((item) => {
|
||||
setBuyModalItem(item);
|
||||
}, []);
|
||||
|
||||
const handleMapBuyConfirm = useCallback(async (quantity) => {
|
||||
if (!activeHousehold?.id || !locationId || !buyModalItem) {
|
||||
setBuyModalItem(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const activeHouseholdId = activeHousehold.id;
|
||||
const scopedLocationId = locationId;
|
||||
const item = buyModalItems.find((candidate) => candidate.id === buyModalItem.id) || buyModalItem;
|
||||
|
||||
try {
|
||||
const currentIndex = buyModalItems.findIndex((candidate) => candidate.id === item.id);
|
||||
const resolvedIndex = currentIndex >= 0 ? currentIndex : 0;
|
||||
|
||||
await markBought(activeHouseholdId, scopedLocationId, item.item_name, quantity, true);
|
||||
if (!isCurrentMapBuyScope(activeHouseholdId, scopedLocationId)) return;
|
||||
|
||||
let nextBuyModalItems = buyModalItems;
|
||||
if (quantity >= item.quantity) {
|
||||
nextBuyModalItems = buyModalItems.filter((candidate) => candidate.id !== item.id);
|
||||
updateMapItems((currentItems) =>
|
||||
currentItems.map((candidate) => (
|
||||
candidate.id === item.id ? getLocallyBoughtMapItem(candidate) : candidate
|
||||
))
|
||||
);
|
||||
} else {
|
||||
let updatedItem;
|
||||
try {
|
||||
const response = await getItemByName(activeHouseholdId, scopedLocationId, item.item_name);
|
||||
if (!isCurrentMapBuyScope(activeHouseholdId, scopedLocationId)) return;
|
||||
updatedItem = response.data || getLocallyReducedMapItem(item, quantity);
|
||||
} catch {
|
||||
if (!isCurrentMapBuyScope(activeHouseholdId, scopedLocationId)) return;
|
||||
updatedItem = getLocallyReducedMapItem(item, quantity);
|
||||
toast.info("Updated item locally", "Latest quantity will sync when the map reloads.");
|
||||
}
|
||||
nextBuyModalItems = buyModalItems.map((candidate) => (candidate.id === item.id ? updatedItem : candidate));
|
||||
|
||||
updateMapItems((currentItems) =>
|
||||
currentItems.map((candidate) => (candidate.id === item.id ? updatedItem : candidate))
|
||||
);
|
||||
}
|
||||
setBuyModalItem(getNextMapBuyItem(nextBuyModalItems, resolvedIndex, item.id));
|
||||
|
||||
toast.success("Marked item bought", `Marked item ${item.item_name} as bought`);
|
||||
} catch (error) {
|
||||
if (!isCurrentMapBuyScope(activeHouseholdId, scopedLocationId)) return;
|
||||
const message = getApiErrorMessage(error, "Failed to mark item as bought");
|
||||
toast.error("Mark item bought failed", `Mark item bought failed: ${message}`);
|
||||
}
|
||||
}, [activeHousehold?.id, buyModalItem, buyModalItems, isCurrentMapBuyScope, locationId, toast, updateMapItems]);
|
||||
|
||||
const mapSize = useMemo(
|
||||
() => ({
|
||||
width: mapDraft.width || activeMap?.width || DEFAULT_MAP_SIZE.width,
|
||||
height: mapDraft.height || activeMap?.height || DEFAULT_MAP_SIZE.height,
|
||||
}),
|
||||
[activeMap?.height, activeMap?.width, mapDraft.height, mapDraft.width]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setBuyModalItem(null);
|
||||
}, [activeHousehold?.id, locationId]);
|
||||
|
||||
useEffect(() => {
|
||||
const matchingLocation = stores.find((store) => String(store.id) === String(locationId));
|
||||
if (matchingLocation && String(activeStore?.id) !== String(matchingLocation.id)) {
|
||||
@ -512,31 +373,18 @@ export default function LocationMapManager() {
|
||||
)}
|
||||
</main>
|
||||
|
||||
{buyModalItem ? (
|
||||
<ConfirmBuyModal
|
||||
item={buyModalItem}
|
||||
allItems={buyModalItems}
|
||||
onNavigate={handleMapBuyNavigate}
|
||||
onCancel={handleMapBuyCancel}
|
||||
onConfirm={handleMapBuyConfirm}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<ConfirmSlideModal
|
||||
isOpen={Boolean(pendingDeleteObject)}
|
||||
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)}
|
||||
onConfirm={handleDeleteObject}
|
||||
/>
|
||||
<ConfirmSlideModal
|
||||
isOpen={Boolean(pendingLeaveTarget)}
|
||||
title="Leave with unsaved changes?"
|
||||
description="Your draft edits are only on this screen right now. Save the draft before leaving if you want to keep them."
|
||||
confirmLabel="Leave Map"
|
||||
onClose={clearPendingLeave}
|
||||
onConfirm={confirmPendingLeave}
|
||||
<LocationMapDialogs
|
||||
buyModalItem={buyModalItem}
|
||||
buyModalItems={buyModalItems}
|
||||
onBuyNavigate={handleMapBuyNavigate}
|
||||
onBuyCancel={handleMapBuyCancel}
|
||||
onBuyConfirm={handleMapBuyConfirm}
|
||||
pendingDeleteObject={pendingDeleteObject}
|
||||
onDeleteClose={() => setPendingDeleteObject(null)}
|
||||
onDeleteConfirm={handleDeleteObject}
|
||||
pendingLeaveTarget={pendingLeaveTarget}
|
||||
onLeaveClose={clearPendingLeave}
|
||||
onLeaveConfirm={confirmPendingLeave}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user