diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx index 6532424..7a6962d 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx @@ -70,12 +70,19 @@ function StorageMechanics() { }; const handleCapacityChange = (value: string) => { - if (!selectedEventData) return; + if (!selectedEventData || selectedEventData.data.type !== "storageUnit") return; const numericValue = parseInt(value); if (isNaN(numericValue)) return; - const event = peekUpdateEvent(selectedProduct.productUuid, selectedEventData.data.modelUuid, { storageCapacity: numericValue } as StorageEventSchema); + const currentCount = selectedEventData.data.storageCount; + const updatedEventData: Partial = { storageCapacity: numericValue }; + + if (currentCount > numericValue) { + updatedEventData.storageCount = numericValue; + } + + const event = peekUpdateEvent(selectedProduct.productUuid, selectedEventData.data.modelUuid, updatedEventData as StorageEventSchema); if (event) { updateBackend(event); @@ -83,11 +90,15 @@ function StorageMechanics() { }; const handleSpawnCountChange = (value: string) => { - if (!selectedEventData) return; + if (!selectedEventData || selectedEventData.data.type !== "storageUnit") return; const numericValue = parseInt(value); if (isNaN(numericValue)) return; + const { storageCapacity } = selectedEventData.data; + + if (numericValue > storageCapacity) return; + const event = peekUpdateEvent(selectedProduct.productUuid, selectedEventData.data.modelUuid, { storageCount: numericValue } as StorageEventSchema); if (event) { diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index bc4a356..8e334c5 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -92,9 +92,9 @@ const Tools: React.FC = () => { // 3. Update tools behavior based on selected tool and view mode useEffect(() => { resetTools(); - updateToolBehavior(activeTool, toggleView); + updateToolBehavior(activeTool, toggleView, activeModule); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [activeTool, toggleView]); + }, [activeTool, toggleView, activeModule]); // 4. Dropdown auto-close useEffect(() => { @@ -111,7 +111,7 @@ const Tools: React.FC = () => { setToolMode(null); }; - const updateToolBehavior = (tool: string, is2D: boolean) => { + const updateToolBehavior = (tool: string, is2D: boolean, activeModule: string) => { switch (tool) { case "cursor": is2D ? setToolMode("move") : setToolMode("cursor"); @@ -129,10 +129,27 @@ const Tools: React.FC = () => { is2D && setToolMode("Floor"); break; case "move": - if (!is2D) setToolMode("Move-Asset"); + if (!is2D) { + if (activeModule === "builder") { + setToolMode("Move-Asset"); + } else if (activeModule === "simulation") { + setToolMode("Move-Point"); + } else { + setToolMode("Move-Asset"); + } + } break; + case "rotate": - if (!is2D) setToolMode("Rotate-Asset"); + if (!is2D) { + if (activeModule === "builder") { + setToolMode("Rotate-Asset"); + } else if (activeModule === "simulation") { + setToolMode("Rotate-Point"); + } else { + setToolMode("Rotate-Asset"); + } + } break; case "measure": setToolMode("MeasurementScale"); diff --git a/app/src/components/ui/list/OutlineList/AssetOutline.tsx b/app/src/components/ui/list/OutlineList/AssetOutline.tsx index 0536262..01a6959 100644 --- a/app/src/components/ui/list/OutlineList/AssetOutline.tsx +++ b/app/src/components/ui/list/OutlineList/AssetOutline.tsx @@ -528,12 +528,12 @@ export const AssetOutline = () => { if (isGroup(draggedItem)) { addChildToGroup(targetGroupUuid, { type: "Group", - childrenUuid: draggedItem.groupUuid, + childUuid: draggedItem.groupUuid, }); } else { addChildToGroup(targetGroupUuid, { type: "Asset", - childrenUuid: draggedItem.modelUuid, + childUuid: draggedItem.modelUuid, }); } } diff --git a/app/src/modules/collaboration/socket/simulationResponses.tsx b/app/src/modules/collaboration/socket/simulationResponses.tsx index 63ab5f1..3da8393 100644 --- a/app/src/modules/collaboration/socket/simulationResponses.tsx +++ b/app/src/modules/collaboration/socket/simulationResponses.tsx @@ -107,6 +107,11 @@ function SimulationResponses() { if (hasSelectedAction && hasNewAction && newActions.length > 0 && diffActions.length > 0) { setSelectedAction(diffActions[0].actionUuid, diffActions[0].actionName); + } else if (selectedAction.actionId && selectedAction.actionName) { + const action: [string, string] = [selectedAction.actionId, selectedAction.actionName]; + setTimeout(() => { + setSelectedAction(...action); + }, 0); } } } diff --git a/app/src/modules/scene/controls/assetControls/groupControls.tsx b/app/src/modules/scene/controls/assetControls/groupControls.tsx index 6040f32..419604c 100644 --- a/app/src/modules/scene/controls/assetControls/groupControls.tsx +++ b/app/src/modules/scene/controls/assetControls/groupControls.tsx @@ -61,7 +61,7 @@ function GroupControls() { isLocked: false, isExpanded: true, children: assetUuids.map((assetUuid) => { - return { type: "Asset", childrenUuid: assetUuid }; + return { type: "Asset", childUuid: assetUuid }; }), }; diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx index e8d5536..ba83991 100644 --- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -3,7 +3,7 @@ import * as THREE from "three"; import { useParams } from "react-router-dom"; import { useThree } from "@react-three/fiber"; import useModuleStore, { useSubModuleStore } from "../../../../../store/ui/useModuleStore"; -import { useToolMode } from "../../../../../store/builder/store"; +import { useActiveTool, useToolMode } from "../../../../../store/builder/store"; import { TransformControls } from "@react-three/drei"; import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys"; import { useSelectedEventSphere, useSelectedEventData, useDeletableEventSphere } from "../../../../../store/simulation/useSimulationStore"; @@ -18,6 +18,7 @@ function PointsCreator() { const { gl, raycaster, scene, pointer, camera } = useThree(); const { subModule } = useSubModuleStore(); const { toolMode } = useToolMode(); + const { setActiveTool } = useActiveTool(); const { activeModule } = useModuleStore(); const { simulationSocket } = useSocketStore(); const { eventStore, productStore, versionStore } = useSceneContext(); @@ -41,9 +42,11 @@ function PointsCreator() { const { projectId } = useParams(); useEffect(() => { - clearSelectedEventSphere(); - clearSelectedEventData(); - clearDeletableEventSphere(); + if (toolMode !== "Move-Point" && toolMode !== "Rotate-Point" && activeModule !== "simulation") { + clearSelectedEventSphere(); + clearSelectedEventData(); + clearDeletableEventSphere(); + } }, [toolMode, activeModule]); const updateBackend = (eventData: EventsSchema) => { @@ -81,13 +84,28 @@ function PointsCreator() { useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { const keyCombination = detectModifierKeys(e); - if (!selectedEventSphere) return; + if (!selectedEventSphere || e.repeat) return; + if (keyCombination === "G") { - setTransformMode((prev) => (prev === "translate" ? null : "translate")); + setTransformMode((prev) => { + const newMode = prev === "translate" ? null : "translate"; + setTimeout(() => { + newMode === "translate" ? setActiveTool("move") : setActiveTool("cursor"); + }, 0); + return newMode; + }); } + if (keyCombination === "R") { - setTransformMode((prev) => (prev === "rotate" ? null : "rotate")); + setTransformMode((prev) => { + const newMode = prev === "rotate" ? null : "rotate"; + setTimeout(() => { + newMode === "rotate" ? setActiveTool("rotate") : setActiveTool("cursor"); + }, 0); + return newMode; + }); } + if (keyCombination === "DELETE") { deletePointfromConveyor(selectedEventSphere); } @@ -97,6 +115,16 @@ function PointsCreator() { return () => window.removeEventListener("keydown", handleKeyDown); }, [selectedEventSphere]); + useEffect(() => { + if (toolMode === "Move-Point") { + setTransformMode("translate"); + } else if (toolMode === "Rotate-Point") { + setTransformMode("rotate"); + } else { + setTransformMode(null); + } + }, [selectedEventSphere, toolMode]); + const deletePointfromConveyor = (selectedEventSphere: THREE.Mesh) => { const eventModel = getEventByModelUuidFromProduct(selectedProduct.productUuid, selectedEventSphere.userData.modelUuid); if (!eventModel || eventModel.type !== "transfer" || eventModel.points.length < 2) return; diff --git a/app/src/modules/simulation/events/points/instances/instance/pointInstance.tsx b/app/src/modules/simulation/events/points/instances/instance/pointInstance.tsx index cc8364c..587631e 100644 --- a/app/src/modules/simulation/events/points/instances/instance/pointInstance.tsx +++ b/app/src/modules/simulation/events/points/instances/instance/pointInstance.tsx @@ -25,7 +25,7 @@ export default function PointInstance({ point, modelUuid, color, ...meshProps }: const { deletableEventSphere, setDeletableEventSphere, clearDeletableEventSphere } = useDeletableEventSphere(); const { toolMode } = useToolMode(); const { productStore, versionStore } = useSceneContext(); - const { getEventByModelUuid, getTriggersByTriggeredPointUuid, peekRemoveTriggersAndPoints, peekRemovePoint, selectedProduct, updateEvent } = productStore(); + const { getEventByModelUuid, getTriggersByTriggeredPointUuid, peekRemoveTriggersAndPoints, selectedProduct, updateEvent } = productStore(); const { selectedVersion } = versionStore(); const { projectId } = useParams(); @@ -69,7 +69,7 @@ export default function PointInstance({ point, modelUuid, color, ...meshProps }: position={new THREE.Vector3(...point.position)} onClick={(e) => { e.stopPropagation(); - if (toolMode === "cursor") { + if (toolMode === "cursor" || toolMode === "Move-Point" || toolMode === "Rotate-Point") { setSelectedEventSphere(ref.current); } else if (toolMode === "3D-Delete") { handleEventPointDelete(); diff --git a/app/src/services/factoryBuilder/group/assetGroup/createAssetGroupApi.ts b/app/src/services/factoryBuilder/group/assetGroup/createAssetGroupApi.ts index db3d6fe..b43b23c 100644 --- a/app/src/services/factoryBuilder/group/assetGroup/createAssetGroupApi.ts +++ b/app/src/services/factoryBuilder/group/assetGroup/createAssetGroupApi.ts @@ -19,7 +19,7 @@ export const createAssetGroupApi = async ({ isLocked: boolean; children: { type: "Asset" | "Group"; - childrenUuid: string; + childUuid: string; }[]; }) => { try { diff --git a/app/src/store/builder/useAssetGroupStore.ts b/app/src/store/builder/useAssetGroupStore.ts index d0bd137..df4b15a 100644 --- a/app/src/store/builder/useAssetGroupStore.ts +++ b/app/src/store/builder/useAssetGroupStore.ts @@ -21,9 +21,9 @@ interface AssetGroupStore { hasSelectedGroup: (groupUuid: string) => boolean; // Group children management - addChildToGroup: (groupUuid: string, child: { type: "Asset" | "Group"; childrenUuid: string }) => { updatedGroups: AssetGroup[] }; + addChildToGroup: (groupUuid: string, child: { type: "Asset" | "Group"; childUuid: string }) => { updatedGroups: AssetGroup[] }; removeChildFromGroup: (groupUuid: string, childUuid: string) => void; - getGroupChildren: (groupUuid: string) => { type: "Asset" | "Group"; childrenUuid: string }[]; + getGroupChildren: (groupUuid: string) => { type: "Asset" | "Group"; childUuid: string }[]; // Group properties setGroupName: (groupUuid: string, newName: string) => void; @@ -71,14 +71,14 @@ export const createAssetGroupStore = () => { // Find all asset children in the new group const assetChildren = group.children.filter((child) => child.type === "Asset"); - const assetUuids = new Set(assetChildren.map((child) => child.childrenUuid)); + const assetUuids = new Set(assetChildren.map((child) => child.childUuid)); // Remove these assets from existing groups and track updated groups const updatedGroups: AssetGroup[] = []; state.assetGroups.forEach((existingGroup) => { const originalLength = existingGroup.children.length; - existingGroup.children = existingGroup.children.filter((child) => !(child.type === "Asset" && assetUuids.has(child.childrenUuid))); + existingGroup.children = existingGroup.children.filter((child) => !(child.type === "Asset" && assetUuids.has(child.childUuid))); // If group was modified, add to updated groups if (existingGroup.children.length !== originalLength) { @@ -99,7 +99,7 @@ export const createAssetGroupStore = () => { set((state) => { // First remove this group from any parent groups state.assetGroups.forEach((group) => { - group.children = group.children.filter((child) => !(child.type === "Group" && child.childrenUuid === groupUuid)); + group.children = group.children.filter((child) => !(child.type === "Group" && child.childUuid === groupUuid)); }); // Then remove the group itself @@ -181,7 +181,7 @@ export const createAssetGroupStore = () => { if (group.groupUuid === groupUuid) return; // skip target group const originalLength = group.children.length; - group.children = group.children.filter((c) => c.childrenUuid !== child.childrenUuid); + group.children = group.children.filter((c) => c.childUuid !== child.childUuid); if (group.children.length !== originalLength) { updatedGroups.push({ ...group }); @@ -189,7 +189,7 @@ export const createAssetGroupStore = () => { }); // 2️⃣ Add the child to the target group (if not already present) - if (!targetGroup.children.some((c) => c.childrenUuid === child.childrenUuid)) { + if (!targetGroup.children.some((c) => c.childUuid === child.childUuid)) { targetGroup.children.push(child); updatedGroups.push({ ...targetGroup }); } @@ -207,7 +207,7 @@ export const createAssetGroupStore = () => { set((state) => { const group = state.assetGroups.find((g) => g.groupUuid === groupUuid); if (group) { - group.children = group.children.filter((child) => child.childrenUuid !== childUuid); + group.children = group.children.filter((child) => child.childUuid !== childUuid); state.groupHierarchy = get().buildHierarchy([], state.assetGroups); } }); @@ -274,14 +274,14 @@ export const createAssetGroupStore = () => { group.children.forEach((child) => { if (child.type === "Asset") { - const asset = assetMap.get(child.childrenUuid); + const asset = assetMap.get(child.childUuid); if (asset) { children.push(asset); // Remove from assetMap so we know it's been processed - assetMap.delete(child.childrenUuid); + assetMap.delete(child.childUuid); } } else if (child.type === "Group") { - const childGroup = groupMap.get(child.childrenUuid); + const childGroup = groupMap.get(child.childUuid); if (childGroup) { children.push(buildNode(childGroup)); } @@ -303,7 +303,7 @@ export const createAssetGroupStore = () => { groups.forEach((group) => { group.children.forEach((child) => { if (child.type === "Group") { - childGroupUuids.add(child.childrenUuid); + childGroupUuids.add(child.childUuid); } }); }); @@ -347,7 +347,7 @@ export const createAssetGroupStore = () => { isLocked: node.isLocked, isExpanded: node.isExpanded, children: node.children.map((child) => - "modelUuid" in child ? { type: "Asset" as const, childrenUuid: child.modelUuid } : { type: "Group" as const, childrenUuid: child.groupUuid } + "modelUuid" in child ? { type: "Asset" as const, childUuid: child.modelUuid } : { type: "Group" as const, childUuid: child.groupUuid } ), }); processedGroups.add(node.groupUuid); @@ -408,11 +408,11 @@ export const createAssetGroupStore = () => { const allChildren: string[] = []; - const collectChildren = (children: { type: "Asset" | "Group"; childrenUuid: string }[]) => { + const collectChildren = (children: { type: "Asset" | "Group"; childUuid: string }[]) => { children.forEach((child) => { - allChildren.push(child.childrenUuid); + allChildren.push(child.childUuid); if (child.type === "Group") { - const childGroup = get().assetGroups.find((g) => g.groupUuid === child.childrenUuid); + const childGroup = get().assetGroups.find((g) => g.groupUuid === child.childUuid); if (childGroup) { collectChildren(childGroup.children); } @@ -430,11 +430,11 @@ export const createAssetGroupStore = () => { }, getGroupsContainingAsset: (assetUuid) => { - return get().assetGroups.filter((group) => group.children.some((child) => child.type === "Asset" && child.childrenUuid === assetUuid)); + return get().assetGroups.filter((group) => group.children.some((child) => child.type === "Asset" && child.childUuid === assetUuid)); }, getGroupsContainingGroup: (childGroupUuid) => { - return get().assetGroups.filter((group) => group.children.some((child) => child.type === "Group" && child.childrenUuid === childGroupUuid)); + return get().assetGroups.filter((group) => group.children.some((child) => child.type === "Group" && child.childUuid === childGroupUuid)); }, getParentGroup: (item) => { diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts index c9b1556..f8cb7be 100644 --- a/app/src/store/simulation/useProductStore.ts +++ b/app/src/store/simulation/useProductStore.ts @@ -575,32 +575,12 @@ export const createProductStore = () => { } } else if ("point" in eventClone) { const point = (eventClone as any).point; - if (eventClone.type === "roboticArm") { - if ("actions" in point) { - const index = point.actions.findIndex((a: any) => a.actionUuid === actionUuid); - if (index !== -1) { - point.actions.splice(index, 1); - updatedEvent = eventClone; - return updatedEvent; - } - } - } else if (eventClone.type === "human") { - if ("actions" in point) { - const index = point.actions.findIndex((a: any) => a.actionUuid === actionUuid); - if (index !== -1) { - point.actions.splice(index, 1); - updatedEvent = eventClone; - return updatedEvent; - } - } - } else if (eventClone.type === "crane") { - if ("actions" in point) { - const index = point.actions.findIndex((a: any) => a.actionUuid === actionUuid); - if (index !== -1) { - point.actions.splice(index, 1); - updatedEvent = eventClone; - return updatedEvent; - } + if ("actions" in point) { + const index = point.actions.findIndex((a: any) => a.actionUuid === actionUuid); + if (index !== -1) { + point.actions.splice(index, 1); + updatedEvent = eventClone; + return updatedEvent; } } else if ("action" in point && point.action?.actionUuid === actionUuid) { point.action = undefined; diff --git a/app/src/types/builderTypes.d.ts b/app/src/types/builderTypes.d.ts index ca6aedc..f6c765d 100644 --- a/app/src/types/builderTypes.d.ts +++ b/app/src/types/builderTypes.d.ts @@ -58,7 +58,7 @@ interface AssetGroup { isExpanded: boolean; children: { type: "Asset" | "Group"; - childrenUuid: string; + childUuid: string; }[]; }