Merge remote-tracking branch 'origin/dev-storageunit-refactor' into main-demo

This commit is contained in:
2025-08-22 09:55:21 +05:30
33 changed files with 632 additions and 424 deletions

View File

@@ -3,53 +3,51 @@ import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
interface StorageActionProps {
type: "store" | "spawn" | "default";
value: string;
maxCapacity: string;
spawnedCount: string;
min: number;
max?: number;
defaultValue: string;
maxCapacityDefault: string;
spawnedCountCefault: string;
currentMaterialType: string;
handleCapacityChange: (value: string) => void;
handleSpawnCountChange: (value: string) => void;
handleMaterialTypeChange: (value: string) => void;
}
const StorageAction: React.FC<StorageActionProps> = ({ type, value, min, max, defaultValue, currentMaterialType, handleCapacityChange, handleMaterialTypeChange }) => {
const StorageAction: React.FC<StorageActionProps> = ({ maxCapacity, spawnedCount, min, max, maxCapacityDefault, spawnedCountCefault, currentMaterialType, handleCapacityChange, handleSpawnCountChange, handleMaterialTypeChange }) => {
return (
<>
{type === 'store' &&
<InputWithDropDown
label="Storage Capacity"
value={value}
min={min}
step={1}
max={max}
defaultValue={defaultValue}
activeOption="unit"
onClick={() => { }}
onChange={handleCapacityChange}
/>
}
{type === 'spawn' &&
<>
<InputWithDropDown
label="Spawn Capacity"
value={value}
min={min}
step={1}
max={max}
defaultValue={defaultValue}
activeOption="unit"
onClick={() => { }}
onChange={handleCapacityChange}
/>
<LabledDropdown
label={"Material Type"}
defaultOption={currentMaterialType}
options={["Default material", "Material 1", "Material 2", "Material 3"]}
onSelect={handleMaterialTypeChange}
/>
</>
}
<InputWithDropDown
key={'Storage Capacity'}
label="Storage Capacity"
value={maxCapacity}
min={min}
step={1}
max={max}
defaultValue={maxCapacityDefault}
activeOption="unit"
onClick={() => { }}
onChange={handleCapacityChange}
/>
<InputWithDropDown
key={"Spawn Count"}
label="Spawn Count"
value={spawnedCount}
min={min}
step={1}
max={max}
defaultValue={spawnedCountCefault}
activeOption="unit"
onClick={() => { }}
onChange={handleSpawnCountChange}
/>
<LabledDropdown
label={"Material Type"}
defaultOption={currentMaterialType}
options={["Default material", "Material 1", "Material 2", "Material 3"]}
onSelect={handleMaterialTypeChange}
/>
</>
);
};

View File

@@ -125,26 +125,6 @@ function ConveyorMechanics() {
}
};
const handleRenameAction = (newName: string) => {
if (!selectedPointData) return;
setActionName(newName);
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{ actionName: newName }
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
};
const handleSpawnCountChange = (value: string) => {
if (!selectedPointData) return;

View File

@@ -62,36 +62,6 @@ function CraneMechanics() {
});
};
const handleRenameAction = (newName: string) => {
if (!selectedAction.actionId || !selectedPointData) return;
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
{ actionName: newName }
);
const updatedActions = selectedPointData.actions.map(action =>
action.actionUuid === selectedAction.actionId
? { ...action, actionName: newName }
: action
);
setSelectedPointData({
...selectedPointData,
actions: updatedActions,
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
};
const handleAddAction = () => {
if (!selectedEventData || !selectedPointData) return;

View File

@@ -142,7 +142,6 @@ function HumanMechanics() {
if (isNaN(numericValue)) return;
const updatedEvent = {
...selectedEventData.data,
speed: numericValue
} as HumanEventSchema;

View File

@@ -71,36 +71,6 @@ function RoboticArmMechanics() {
});
};
const handleRenameAction = (newName: string) => {
if (!selectedAction.actionId || !selectedPointData) return;
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
{ actionName: newName }
);
const updatedActions = selectedPointData.actions.map(action =>
action.actionUuid === selectedAction.actionId
? { ...action, actionName: newName }
: action
);
setSelectedPointData({
...selectedPointData,
actions: updatedActions,
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
};
const handleSpeedChange = (value: string) => {
if (!selectedEventData) return;

View File

@@ -1,4 +1,5 @@
import { useEffect, useMemo, useState } from "react";
import { useEffect, useState } from "react";
import { MathUtils } from "three";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import Trigger from "../trigger/Trigger";
@@ -6,58 +7,85 @@ import StorageAction from "../actions/StorageAction";
import ActionsList from "../components/ActionsList";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
import { useSelectedAction, useSelectedEventData } from "../../../../../../store/simulation/useSimulationStore";
import * as THREE from 'three';
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
import { useParams } from "react-router-dom";
import { useVersionContext } from "../../../../../../modules/builder/version/versionContext";
import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
function StorageMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "store" | "spawn">("default");
const [activeOption, setActiveOption] = useState<"store" | "spawn">("store");
const [currentCapacity, setCurrentCapacity] = useState("1");
const [spawnedCount, setSpawnedCount] = useState("0");
const [spawnedMaterial, setSpawnedMaterial] = useState("Default material");
const [selectedPointData, setSelectedPointData] = useState<StoragePointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { productStore } = useSceneContext();
const { getPointByUuid, updateAction } = productStore();
const { getPointByUuid, updateAction, updateEvent, getEventByModelUuid, getActionByUuid, addAction, removeAction } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { setSelectedAction, clearSelectedAction } = useSelectedAction();
const { selectedAction, setSelectedAction, clearSelectedAction } = useSelectedAction();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const updateSelectedPointData = () => {
if (selectedEventData && selectedProduct) {
const point = getPointByUuid(
selectedProduct.productUuid,
selectedEventData?.data.modelUuid,
selectedEventData?.selectedPoint
) as StoragePointSchema | undefined;
if (point && "action" in point) {
setSelectedPointData(point);
const uiOption = point.action.actionType === "retrieve" ? "spawn" : point.action.actionType;
setActiveOption(uiOption as "store" | "spawn");
setSelectedAction(point.action.actionUuid, point.action.actionName);
}
}
};
useEffect(() => {
if (selectedEventData) {
if (selectedEventData && selectedEventData.data.type === "storageUnit") {
const point = getPointByUuid(
selectedProduct.productUuid,
selectedEventData?.data.modelUuid,
selectedEventData?.selectedPoint
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint
) as StoragePointSchema | undefined;
if (point && "action" in point) {
if (point?.actions?.length) {
setSelectedPointData(point);
const uiOption = point.action.actionType === "retrieve" ? "spawn" : point.action.actionType;
setActiveOption(uiOption as "store" | "spawn");
setSelectedAction(point.action.actionUuid, point.action.actionName);
const firstAction = point.actions[0];
const eventData = getEventByModelUuid(
selectedProduct.productUuid,
selectedEventData.data.modelUuid
) as StorageEventSchema | undefined;
setCurrentCapacity(eventData?.storageCapacity?.toString() || "1");
setSpawnedCount(eventData?.storageCount?.toString() || "0");
setSpawnedMaterial(eventData?.materialType?.toString() || "Default material");
const actionUuid = selectedAction.actionId || firstAction.actionUuid;
const newCurrentAction = getActionByUuid(selectedProduct.productUuid, actionUuid);
if (newCurrentAction) {
const uiOption = newCurrentAction.actionType === "retrieve" ? "spawn" : "store";
setActiveOption(uiOption);
setSelectedAction(newCurrentAction.actionUuid, newCurrentAction.actionName);
}
}
} else {
clearSelectedAction();
}
}, [selectedProduct, selectedEventData]);
}, [selectedEventData, selectedProduct]);
useEffect(() => {
if (selectedEventData && selectedEventData.data.type === "storageUnit" && selectedAction.actionId) {
const point = getPointByUuid(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint
) as StoragePointSchema | undefined;
const actionUuid = selectedAction.actionId || point?.actions[0].actionUuid || '';
const newCurrentAction = getActionByUuid(selectedProduct.productUuid, actionUuid);
if (newCurrentAction && (newCurrentAction.actionType === 'store' || newCurrentAction.actionType === 'retrieve')) {
if (!selectedAction.actionId) {
setSelectedAction(newCurrentAction.actionUuid, newCurrentAction.actionName);
}
const uiOption = newCurrentAction.actionType === "retrieve" ? "spawn" : "store";
setActiveOption(uiOption);
} else {
clearSelectedAction();
}
}
}, [selectedAction, selectedProduct, selectedEventData]);
const updateBackend = (
productName: string,
@@ -75,48 +103,63 @@ function StorageMechanics() {
}
const handleActionTypeChange = (option: string) => {
if (!selectedEventData || !selectedPointData) return;
const internalOption = actionTypeMap[option as keyof typeof actionTypeMap] as "store" | "retrieve";
if (!selectedAction.actionId || !selectedPointData) return;
const internalOption = option === "spawn" ? "retrieve" : "store";
const updatedAction = {
actionType: internalOption as "store" | "retrieve"
};
const updatedActions = selectedPointData.actions.map(action =>
action.actionUuid === selectedAction.actionId ? {
...action,
actionType: updatedAction.actionType
} : action
);
const updatedPoint = { ...selectedPointData, actions: updatedActions };
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
updatedAction
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
setSelectedPointData(updatedPoint);
setActiveOption(option as "store" | "spawn");
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
actionType: internalOption,
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
updateSelectedPointData();
}
};
const handleRenameAction = (newName: string) => {
if (!selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, { actionName: newName });
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
updateSelectedPointData();
}
};
const handleCapacityChange = (value: string) => {
if (!selectedEventData || !selectedPointData) return;
const newCapacity = parseInt(value);
if (!selectedEventData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
storageCapacity: newCapacity,
});
const numericValue = parseInt(value);
if (isNaN(numericValue)) return;
const updatedEvent = {
storageCapacity: numericValue
} as StorageEventSchema;
const currentCount = parseInt(spawnedCount);
if (currentCount > numericValue) {
updatedEvent.storageCount = numericValue;
setSpawnedCount(numericValue.toString());
}
const event = updateEvent(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
updatedEvent
);
if (event) {
updateBackend(
@@ -125,25 +168,54 @@ function StorageMechanics() {
projectId || '',
event
);
updateSelectedPointData();
}
setCurrentCapacity(value);
};
const createNewMaterial = (materialType: string): { materialType: string; materialId: string } | null => {
if (!selectedEventData || !selectedPointData) return null;
const materialId = THREE.MathUtils.generateUUID();
return {
materialType,
materialId
};
const handleSpawnCountChange = (value: string) => {
if (!selectedEventData) return;
const numericValue = parseInt(value);
if (isNaN(numericValue)) return;
const maxCapacity = parseInt(currentCapacity);
if (numericValue > maxCapacity) return;
const updatedEvent = {
storageCount: numericValue
} as StorageEventSchema;
const event = updateEvent(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
updatedEvent
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
setSpawnedCount(value);
};
const handleMaterialTypeChange = (value: string) => {
if (!selectedEventData || !selectedPointData) return;
if (!selectedEventData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
materialType: value,
});
const updatedEvent = {
materialType: value
} as StorageEventSchema;
const event = updateEvent(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
updatedEvent
);
if (event) {
updateBackend(
@@ -152,70 +224,114 @@ function StorageMechanics() {
projectId || '',
event
);
updateSelectedPointData();
}
setSpawnedMaterial(value);
};
const currentActionName = useMemo(() =>
selectedPointData ? selectedPointData.action.actionName : "Action Name",
[selectedPointData]
);
const handleAddAction = () => {
if (!selectedEventData || !selectedPointData) return;
const currentCapacity = useMemo(() =>
selectedPointData ? selectedPointData.action.storageCapacity.toString() : "0",
[selectedPointData]
);
const newAction: StorageAction = {
actionUuid: MathUtils.generateUUID(),
actionName: `Action ${selectedPointData.actions.length + 1}`,
actionType: "store",
triggers: [],
};
const currentMaterialType = useMemo(() =>
selectedPointData?.action.materialType || "Default material",
[selectedPointData]
);
const updatedActions = [...(selectedPointData.actions || []), newAction];
const updatedPoint = { ...selectedPointData, actions: updatedActions };
const availableActions = {
defaultOption: "store",
options: ["store", "spawn"],
const event = addAction(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint,
newAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setSelectedPointData(updatedPoint);
setSelectedAction(newAction.actionUuid, newAction.actionName);
};
const actionTypeMap = {
spawn: "retrieve",
store: "store"
const handleDeleteAction = (actionUuid: string) => {
if (!selectedPointData || !actionUuid) return;
const updatedActions = selectedPointData.actions.filter(action => action.actionUuid !== actionUuid);
const updatedPoint = { ...selectedPointData, actions: updatedActions };
const event = removeAction(
selectedProduct.productUuid,
actionUuid
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setSelectedPointData(updatedPoint);
const index = selectedPointData.actions.findIndex((a) => a.actionUuid === selectedAction.actionId);
const nextAction = updatedPoint.actions[index] || updatedPoint.actions[index - 1];
if (nextAction) {
setSelectedAction(nextAction.actionUuid, nextAction.actionName);
} else {
clearSelectedAction();
}
};
return (
<>
{selectedEventData && (
<section>
<ActionsList
selectedPointData={selectedPointData}
/>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
canEdit={false}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption={activeOption}
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
<StorageAction
type={activeOption}
value={currentCapacity}
defaultValue="0"
min={0}
currentMaterialType={currentMaterialType}
handleCapacityChange={handleCapacityChange}
handleMaterialTypeChange={handleMaterialTypeChange}
/>
</div>
</div>
<div className="tirgger">
<Trigger selectedPointData={selectedPointData as any} type={'StorageUnit'} />
</div>
</section>
{selectedEventData && selectedEventData.data.type === "storageUnit" && (
<>
<section>
<StorageAction
maxCapacity={currentCapacity}
spawnedCount={spawnedCount}
maxCapacityDefault="0"
spawnedCountCefault="0"
min={0}
currentMaterialType={spawnedMaterial}
handleCapacityChange={handleCapacityChange}
handleSpawnCountChange={handleSpawnCountChange}
handleMaterialTypeChange={handleMaterialTypeChange}
/>
</section>
<section>
<ActionsList
selectedPointData={selectedPointData}
multipleAction={true}
handleAddAction={handleAddAction}
handleDeleteAction={handleDeleteAction}
/>
{selectedAction.actionId && (
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={selectedAction.actionName || ""}
canEdit={false}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
label="Action Type"
defaultOption={activeOption}
options={["store", "spawn"]}
onSelect={handleActionTypeChange}
disabled={false}
/>
</div>
<div className="tirgger">
<Trigger selectedPointData={selectedPointData as any} type={'StorageUnit'} />
</div>
</div>
)}
</section>
</>
)}
</>
);

View File

@@ -119,26 +119,6 @@ function VehicleMechanics() {
}
};
const handleRenameAction = (newName: string) => {
if (!selectedPointData) return;
setActionName(newName);
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{ actionName: newName }
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
};
const handleLoadCapacityChange = (value: string) => {
if (!selectedPointData) return;

View File

@@ -13,7 +13,7 @@ import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
type TriggerProps = {
selectedPointData?: PointsScheme | undefined;
type?: "Conveyor" | "Vehicle" | "RoboticArm" | "Machine" | "StorageUnit" | "Human";
type?: "Conveyor" | "Vehicle" | "RoboticArm" | "Machine" | "StorageUnit" | "Human" | "Crane";
};
const Trigger = ({ selectedPointData, type }: TriggerProps) => {
@@ -36,9 +36,9 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
let actionUuid: string | undefined;
if (type === "Conveyor" || type === "Vehicle" || type === "Machine" || type === "StorageUnit") {
actionUuid = (selectedPointData as | ConveyorPointSchema | VehiclePointSchema | MachinePointSchema | StoragePointSchema).action?.actionUuid;
} else if ((type === "RoboticArm" || type === "Human") && selectedAction.actionId) {
if (type === "Conveyor" || type === "Vehicle" || type === "Machine") {
actionUuid = (selectedPointData as | ConveyorPointSchema | VehiclePointSchema | MachinePointSchema).action?.actionUuid;
} else if ((type === "RoboticArm" || type === "Human" || type === "StorageUnit" || type === 'Crane') && selectedAction.actionId) {
actionUuid = selectedAction.actionId;
}