Merge remote-tracking branch 'origin/main-dev' into feature/layout-comparison-version

This commit is contained in:
2025-10-15 16:31:51 +05:30
11 changed files with 107 additions and 66 deletions

View File

@@ -70,12 +70,19 @@ function StorageMechanics() {
}; };
const handleCapacityChange = (value: string) => { const handleCapacityChange = (value: string) => {
if (!selectedEventData) return; if (!selectedEventData || selectedEventData.data.type !== "storageUnit") return;
const numericValue = parseInt(value); const numericValue = parseInt(value);
if (isNaN(numericValue)) return; if (isNaN(numericValue)) return;
const event = peekUpdateEvent(selectedProduct.productUuid, selectedEventData.data.modelUuid, { storageCapacity: numericValue } as StorageEventSchema); const currentCount = selectedEventData.data.storageCount;
const updatedEventData: Partial<StorageEventSchema> = { storageCapacity: numericValue };
if (currentCount > numericValue) {
updatedEventData.storageCount = numericValue;
}
const event = peekUpdateEvent(selectedProduct.productUuid, selectedEventData.data.modelUuid, updatedEventData as StorageEventSchema);
if (event) { if (event) {
updateBackend(event); updateBackend(event);
@@ -83,11 +90,15 @@ function StorageMechanics() {
}; };
const handleSpawnCountChange = (value: string) => { const handleSpawnCountChange = (value: string) => {
if (!selectedEventData) return; if (!selectedEventData || selectedEventData.data.type !== "storageUnit") return;
const numericValue = parseInt(value); const numericValue = parseInt(value);
if (isNaN(numericValue)) return; if (isNaN(numericValue)) return;
const { storageCapacity } = selectedEventData.data;
if (numericValue > storageCapacity) return;
const event = peekUpdateEvent(selectedProduct.productUuid, selectedEventData.data.modelUuid, { storageCount: numericValue } as StorageEventSchema); const event = peekUpdateEvent(selectedProduct.productUuid, selectedEventData.data.modelUuid, { storageCount: numericValue } as StorageEventSchema);
if (event) { if (event) {

View File

@@ -92,9 +92,9 @@ const Tools: React.FC = () => {
// 3. Update tools behavior based on selected tool and view mode // 3. Update tools behavior based on selected tool and view mode
useEffect(() => { useEffect(() => {
resetTools(); resetTools();
updateToolBehavior(activeTool, toggleView); updateToolBehavior(activeTool, toggleView, activeModule);
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeTool, toggleView]); }, [activeTool, toggleView, activeModule]);
// 4. Dropdown auto-close // 4. Dropdown auto-close
useEffect(() => { useEffect(() => {
@@ -111,7 +111,7 @@ const Tools: React.FC = () => {
setToolMode(null); setToolMode(null);
}; };
const updateToolBehavior = (tool: string, is2D: boolean) => { const updateToolBehavior = (tool: string, is2D: boolean, activeModule: string) => {
switch (tool) { switch (tool) {
case "cursor": case "cursor":
is2D ? setToolMode("move") : setToolMode("cursor"); is2D ? setToolMode("move") : setToolMode("cursor");
@@ -129,10 +129,27 @@ const Tools: React.FC = () => {
is2D && setToolMode("Floor"); is2D && setToolMode("Floor");
break; break;
case "move": 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; break;
case "rotate": 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; break;
case "measure": case "measure":
setToolMode("MeasurementScale"); setToolMode("MeasurementScale");

View File

@@ -528,12 +528,12 @@ export const AssetOutline = () => {
if (isGroup(draggedItem)) { if (isGroup(draggedItem)) {
addChildToGroup(targetGroupUuid, { addChildToGroup(targetGroupUuid, {
type: "Group", type: "Group",
childrenUuid: draggedItem.groupUuid, childUuid: draggedItem.groupUuid,
}); });
} else { } else {
addChildToGroup(targetGroupUuid, { addChildToGroup(targetGroupUuid, {
type: "Asset", type: "Asset",
childrenUuid: draggedItem.modelUuid, childUuid: draggedItem.modelUuid,
}); });
} }
} }

View File

@@ -107,6 +107,11 @@ function SimulationResponses() {
if (hasSelectedAction && hasNewAction && newActions.length > 0 && diffActions.length > 0) { if (hasSelectedAction && hasNewAction && newActions.length > 0 && diffActions.length > 0) {
setSelectedAction(diffActions[0].actionUuid, diffActions[0].actionName); 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);
} }
} }
} }

View File

@@ -61,7 +61,7 @@ function GroupControls() {
isLocked: false, isLocked: false,
isExpanded: true, isExpanded: true,
children: assetUuids.map((assetUuid) => { children: assetUuids.map((assetUuid) => {
return { type: "Asset", childrenUuid: assetUuid }; return { type: "Asset", childUuid: assetUuid };
}), }),
}; };

View File

@@ -3,7 +3,7 @@ import * as THREE from "three";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { useThree } from "@react-three/fiber"; import { useThree } from "@react-three/fiber";
import useModuleStore, { useSubModuleStore } from "../../../../../store/ui/useModuleStore"; 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 { TransformControls } from "@react-three/drei";
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys"; import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
import { useSelectedEventSphere, useSelectedEventData, useDeletableEventSphere } from "../../../../../store/simulation/useSimulationStore"; import { useSelectedEventSphere, useSelectedEventData, useDeletableEventSphere } from "../../../../../store/simulation/useSimulationStore";
@@ -18,6 +18,7 @@ function PointsCreator() {
const { gl, raycaster, scene, pointer, camera } = useThree(); const { gl, raycaster, scene, pointer, camera } = useThree();
const { subModule } = useSubModuleStore(); const { subModule } = useSubModuleStore();
const { toolMode } = useToolMode(); const { toolMode } = useToolMode();
const { setActiveTool } = useActiveTool();
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { simulationSocket } = useSocketStore(); const { simulationSocket } = useSocketStore();
const { eventStore, productStore, versionStore } = useSceneContext(); const { eventStore, productStore, versionStore } = useSceneContext();
@@ -41,9 +42,11 @@ function PointsCreator() {
const { projectId } = useParams(); const { projectId } = useParams();
useEffect(() => { useEffect(() => {
clearSelectedEventSphere(); if (toolMode !== "Move-Point" && toolMode !== "Rotate-Point" && activeModule !== "simulation") {
clearSelectedEventData(); clearSelectedEventSphere();
clearDeletableEventSphere(); clearSelectedEventData();
clearDeletableEventSphere();
}
}, [toolMode, activeModule]); }, [toolMode, activeModule]);
const updateBackend = (eventData: EventsSchema) => { const updateBackend = (eventData: EventsSchema) => {
@@ -81,13 +84,28 @@ function PointsCreator() {
useEffect(() => { useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {
const keyCombination = detectModifierKeys(e); const keyCombination = detectModifierKeys(e);
if (!selectedEventSphere) return; if (!selectedEventSphere || e.repeat) return;
if (keyCombination === "G") { 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") { 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") { if (keyCombination === "DELETE") {
deletePointfromConveyor(selectedEventSphere); deletePointfromConveyor(selectedEventSphere);
} }
@@ -97,6 +115,16 @@ function PointsCreator() {
return () => window.removeEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown);
}, [selectedEventSphere]); }, [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 deletePointfromConveyor = (selectedEventSphere: THREE.Mesh) => {
const eventModel = getEventByModelUuidFromProduct(selectedProduct.productUuid, selectedEventSphere.userData.modelUuid); const eventModel = getEventByModelUuidFromProduct(selectedProduct.productUuid, selectedEventSphere.userData.modelUuid);
if (!eventModel || eventModel.type !== "transfer" || eventModel.points.length < 2) return; if (!eventModel || eventModel.type !== "transfer" || eventModel.points.length < 2) return;

View File

@@ -25,7 +25,7 @@ export default function PointInstance({ point, modelUuid, color, ...meshProps }:
const { deletableEventSphere, setDeletableEventSphere, clearDeletableEventSphere } = useDeletableEventSphere(); const { deletableEventSphere, setDeletableEventSphere, clearDeletableEventSphere } = useDeletableEventSphere();
const { toolMode } = useToolMode(); const { toolMode } = useToolMode();
const { productStore, versionStore } = useSceneContext(); const { productStore, versionStore } = useSceneContext();
const { getEventByModelUuid, getTriggersByTriggeredPointUuid, peekRemoveTriggersAndPoints, peekRemovePoint, selectedProduct, updateEvent } = productStore(); const { getEventByModelUuid, getTriggersByTriggeredPointUuid, peekRemoveTriggersAndPoints, selectedProduct, updateEvent } = productStore();
const { selectedVersion } = versionStore(); const { selectedVersion } = versionStore();
const { projectId } = useParams(); const { projectId } = useParams();
@@ -69,7 +69,7 @@ export default function PointInstance({ point, modelUuid, color, ...meshProps }:
position={new THREE.Vector3(...point.position)} position={new THREE.Vector3(...point.position)}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
if (toolMode === "cursor") { if (toolMode === "cursor" || toolMode === "Move-Point" || toolMode === "Rotate-Point") {
setSelectedEventSphere(ref.current); setSelectedEventSphere(ref.current);
} else if (toolMode === "3D-Delete") { } else if (toolMode === "3D-Delete") {
handleEventPointDelete(); handleEventPointDelete();

View File

@@ -19,7 +19,7 @@ export const createAssetGroupApi = async ({
isLocked: boolean; isLocked: boolean;
children: { children: {
type: "Asset" | "Group"; type: "Asset" | "Group";
childrenUuid: string; childUuid: string;
}[]; }[];
}) => { }) => {
try { try {

View File

@@ -21,9 +21,9 @@ interface AssetGroupStore {
hasSelectedGroup: (groupUuid: string) => boolean; hasSelectedGroup: (groupUuid: string) => boolean;
// Group children management // 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; removeChildFromGroup: (groupUuid: string, childUuid: string) => void;
getGroupChildren: (groupUuid: string) => { type: "Asset" | "Group"; childrenUuid: string }[]; getGroupChildren: (groupUuid: string) => { type: "Asset" | "Group"; childUuid: string }[];
// Group properties // Group properties
setGroupName: (groupUuid: string, newName: string) => void; setGroupName: (groupUuid: string, newName: string) => void;
@@ -71,14 +71,14 @@ export const createAssetGroupStore = () => {
// Find all asset children in the new group // Find all asset children in the new group
const assetChildren = group.children.filter((child) => child.type === "Asset"); 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 // Remove these assets from existing groups and track updated groups
const updatedGroups: AssetGroup[] = []; const updatedGroups: AssetGroup[] = [];
state.assetGroups.forEach((existingGroup) => { state.assetGroups.forEach((existingGroup) => {
const originalLength = existingGroup.children.length; 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 group was modified, add to updated groups
if (existingGroup.children.length !== originalLength) { if (existingGroup.children.length !== originalLength) {
@@ -99,7 +99,7 @@ export const createAssetGroupStore = () => {
set((state) => { set((state) => {
// First remove this group from any parent groups // First remove this group from any parent groups
state.assetGroups.forEach((group) => { 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 // Then remove the group itself
@@ -181,7 +181,7 @@ export const createAssetGroupStore = () => {
if (group.groupUuid === groupUuid) return; // skip target group if (group.groupUuid === groupUuid) return; // skip target group
const originalLength = group.children.length; 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) { if (group.children.length !== originalLength) {
updatedGroups.push({ ...group }); updatedGroups.push({ ...group });
@@ -189,7 +189,7 @@ export const createAssetGroupStore = () => {
}); });
// 2⃣ Add the child to the target group (if not already present) // 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); targetGroup.children.push(child);
updatedGroups.push({ ...targetGroup }); updatedGroups.push({ ...targetGroup });
} }
@@ -207,7 +207,7 @@ export const createAssetGroupStore = () => {
set((state) => { set((state) => {
const group = state.assetGroups.find((g) => g.groupUuid === groupUuid); const group = state.assetGroups.find((g) => g.groupUuid === groupUuid);
if (group) { 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); state.groupHierarchy = get().buildHierarchy([], state.assetGroups);
} }
}); });
@@ -274,14 +274,14 @@ export const createAssetGroupStore = () => {
group.children.forEach((child) => { group.children.forEach((child) => {
if (child.type === "Asset") { if (child.type === "Asset") {
const asset = assetMap.get(child.childrenUuid); const asset = assetMap.get(child.childUuid);
if (asset) { if (asset) {
children.push(asset); children.push(asset);
// Remove from assetMap so we know it's been processed // Remove from assetMap so we know it's been processed
assetMap.delete(child.childrenUuid); assetMap.delete(child.childUuid);
} }
} else if (child.type === "Group") { } else if (child.type === "Group") {
const childGroup = groupMap.get(child.childrenUuid); const childGroup = groupMap.get(child.childUuid);
if (childGroup) { if (childGroup) {
children.push(buildNode(childGroup)); children.push(buildNode(childGroup));
} }
@@ -303,7 +303,7 @@ export const createAssetGroupStore = () => {
groups.forEach((group) => { groups.forEach((group) => {
group.children.forEach((child) => { group.children.forEach((child) => {
if (child.type === "Group") { if (child.type === "Group") {
childGroupUuids.add(child.childrenUuid); childGroupUuids.add(child.childUuid);
} }
}); });
}); });
@@ -347,7 +347,7 @@ export const createAssetGroupStore = () => {
isLocked: node.isLocked, isLocked: node.isLocked,
isExpanded: node.isExpanded, isExpanded: node.isExpanded,
children: node.children.map((child) => 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); processedGroups.add(node.groupUuid);
@@ -408,11 +408,11 @@ export const createAssetGroupStore = () => {
const allChildren: string[] = []; const allChildren: string[] = [];
const collectChildren = (children: { type: "Asset" | "Group"; childrenUuid: string }[]) => { const collectChildren = (children: { type: "Asset" | "Group"; childUuid: string }[]) => {
children.forEach((child) => { children.forEach((child) => {
allChildren.push(child.childrenUuid); allChildren.push(child.childUuid);
if (child.type === "Group") { 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) { if (childGroup) {
collectChildren(childGroup.children); collectChildren(childGroup.children);
} }
@@ -430,11 +430,11 @@ export const createAssetGroupStore = () => {
}, },
getGroupsContainingAsset: (assetUuid) => { 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) => { 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) => { getParentGroup: (item) => {

View File

@@ -575,32 +575,12 @@ export const createProductStore = () => {
} }
} else if ("point" in eventClone) { } else if ("point" in eventClone) {
const point = (eventClone as any).point; const point = (eventClone as any).point;
if (eventClone.type === "roboticArm") { if ("actions" in point) {
if ("actions" in point) { const index = point.actions.findIndex((a: any) => a.actionUuid === actionUuid);
const index = point.actions.findIndex((a: any) => a.actionUuid === actionUuid); if (index !== -1) {
if (index !== -1) { point.actions.splice(index, 1);
point.actions.splice(index, 1); updatedEvent = eventClone;
updatedEvent = eventClone; return updatedEvent;
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;
}
} }
} else if ("action" in point && point.action?.actionUuid === actionUuid) { } else if ("action" in point && point.action?.actionUuid === actionUuid) {
point.action = undefined; point.action = undefined;

View File

@@ -58,7 +58,7 @@ interface AssetGroup {
isExpanded: boolean; isExpanded: boolean;
children: { children: {
type: "Asset" | "Group"; type: "Asset" | "Group";
childrenUuid: string; childUuid: string;
}[]; }[];
} }