Merge branch 'dev-simulation/human' into main-dev

This commit is contained in:
2025-07-04 17:16:24 +05:30
44 changed files with 3127 additions and 497 deletions

View File

@@ -6,145 +6,128 @@ import PositionInput from "../customInput/PositionInputs";
import RotationInput from "../customInput/RotationInput";
import { useSelectedFloorItem, useObjectPosition, useObjectRotation } from "../../../../store/builder/store";
import { useSceneContext } from "../../../../modules/scene/sceneContext";
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
interface UserData {
id: number; // Unique identifier for the user data
label: string; // Label of the user data field
value: string; // Value of the user data field
id: number;
label: string;
value: string;
}
const AssetProperties: React.FC = () => {
const [userData, setUserData] = useState<UserData[]>([]); // State to track user data
const [nextId, setNextId] = useState(1); // Unique ID for new entries
const { selectedFloorItem } = useSelectedFloorItem();
const { objectPosition } = useObjectPosition();
const { objectRotation } = useObjectRotation();
const { assetStore } = useSceneContext();
const { assets, setCurrentAnimation } = assetStore()
const [hoveredIndex, setHoveredIndex] = useState<any>(null);
const [isPlaying, setIsplaying] = useState(false);
// Function to handle adding new user data
const handleAddUserData = () => {
const newUserData: UserData = {
id: nextId,
label: `Property ${nextId}`,
value: "",
const [userData, setUserData] = useState<UserData[]>([]);
const { selectedFloorItem } = useSelectedFloorItem();
const { objectPosition } = useObjectPosition();
const { objectRotation } = useObjectRotation();
const { assetStore } = useSceneContext();
const { assets, setCurrentAnimation } = assetStore();
const { loopAnimation } = useBuilderStore();
const [hoveredIndex, setHoveredIndex] = useState<any>(null);
const handleAddUserData = () => {
};
setUserData([...userData, newUserData]);
setNextId(nextId + 1); // Increment the ID for the next entry
};
// Function to update the value of a user data entry
const handleUserDataChange = (id: number, newValue: string) => {
setUserData((prevUserData) =>
prevUserData.map((data) =>
data.id === id ? { ...data, value: newValue } : data
)
);
};
const handleUserDataChange = (id: number, newValue: string) => {
};
// Remove user data
const handleRemoveUserData = (id: number) => {
setUserData((prevUserData) =>
prevUserData.filter((data) => data.id !== id)
);
};
const handleRemoveUserData = (id: number) => {
};
const handleAnimationClick = (animation: string) => {
if (selectedFloorItem) {
setCurrentAnimation(selectedFloorItem.uuid, animation, true);
const handleAnimationClick = (animation: string) => {
if (selectedFloorItem) {
setCurrentAnimation(selectedFloorItem.uuid, animation, true, loopAnimation, true);
}
}
}
if (!selectedFloorItem) return null;
if (!selectedFloorItem) return null;
return (
<div className="asset-properties-container">
{/* Name */}
<div className="header">{selectedFloorItem.userData.modelName}</div>
<section>
{objectPosition.x && objectPosition.z &&
<PositionInput
onChange={() => { }}
value1={parseFloat(objectPosition.x.toFixed(5))}
value2={parseFloat(objectPosition.z.toFixed(5))}
/>
}
{objectRotation.y &&
<RotationInput
onChange={() => { }}
value={parseFloat(objectRotation.y.toFixed(5))}
/>
}
</section>
return (
<div className="asset-properties-container">
{/* Name */}
<div className="header">{selectedFloorItem.userData.modelName}</div>
<section>
{objectPosition &&
<PositionInput
onChange={() => { }}
value1={parseFloat(objectPosition.x.toFixed(5))}
value2={parseFloat(objectPosition.z.toFixed(5))}
/>
}
{objectRotation &&
<RotationInput
onChange={() => { }}
value={parseFloat(objectRotation.y.toFixed(5))}
/>
}
</section>
<section>
<div className="header">Render settings</div>
<InputToggle inputKey="visible" label="Visible" />
<InputToggle inputKey="frustumCull" label="Frustum cull" />
</section>
<section>
<div className="header">Render settings</div>
<InputToggle inputKey="visible" label="Visible" />
<InputToggle inputKey="frustumCull" label="Frustum cull" />
</section>
<section>
<div className="header">User Data</div>
{userData.map((data) => (
<div className="input-container">
<InputWithDropDown
key={data.id}
label={data.label}
value={data.value}
editableLabel
onChange={(newValue) => handleUserDataChange(data.id, newValue)} // Pass the change handler
/>
<div
className="remove-button"
onClick={() => handleRemoveUserData(data.id)}
>
<RemoveIcon />
</div>
</div>
))}
<section>
<div className="header">User Data</div>
{userData.map((data) => (
<div className="input-container">
<InputWithDropDown
key={data.id}
label={data.label}
value={data.value}
editableLabel
onChange={(newValue) => handleUserDataChange(data.id, newValue)}
/>
<div
className="remove-button"
onClick={() => handleRemoveUserData(data.id)}
>
<RemoveIcon />
</div>
</div>
))}
{/* Add new user data */}
<div className="optimize-button" onClick={handleAddUserData}>
+ Add
</div>
</section>
<div style={{ display: "flex", flexDirection: "column", outline: "1px solid var(--border-color)" }}>
{selectedFloorItem.uuid && <div style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>Animations</div>}
{assets.map((asset) => (
<div key={asset.modelUuid} className="asset-item">
{asset.modelUuid === selectedFloorItem.uuid &&
asset.animations &&
asset.animations.length > 0 &&
asset.animations.map((animation, index) => (
<div
key={index}
style={{ gap: "15px", cursor: "pointer", padding: "5px" }}
>
<div
onClick={() => handleAnimationClick(animation)}
onMouseEnter={() => setHoveredIndex(index)}
onMouseLeave={() => setHoveredIndex(null)}
style={{
height: "20px",
width: "100%",
borderRadius: "5px",
background:
hoveredIndex === index
? "#7b4cd3"
: "transparent",
}}
>
{animation.charAt(0).toUpperCase() +
animation.slice(1).toLowerCase()}
</div>
{/* Add new user data */}
<div className="optimize-button" onClick={handleAddUserData}>
+ Add
</div>
))}
</div>
))}
</div>
</div>
);
</section>
<div style={{ display: "flex", flexDirection: "column", outline: "1px solid var(--border-color)" }}>
{selectedFloorItem.uuid && <div style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>Animations</div>}
{assets.map((asset) => (
<div key={asset.modelUuid} className="asset-item">
{asset.modelUuid === selectedFloorItem.uuid &&
asset.animations &&
asset.animations.length > 0 &&
asset.animations.map((animation, index) => (
<div
key={index}
style={{ gap: "15px", cursor: "pointer", padding: "5px" }}
>
<div
onClick={() => handleAnimationClick(animation)}
onMouseEnter={() => setHoveredIndex(index)}
onMouseLeave={() => setHoveredIndex(null)}
style={{
height: "20px",
width: "100%",
borderRadius: "5px",
background:
hoveredIndex === index
? "#7b4cd3"
: "transparent",
}}
>
{animation.charAt(0).toUpperCase() +
animation.slice(1).toLowerCase()}
</div>
</div>
))}
</div>
))}
</div>
</div>
);
};
export default AssetProperties;

View File

@@ -8,6 +8,7 @@ import VehicleMechanics from "./mechanics/vehicleMechanics";
import RoboticArmMechanics from "./mechanics/roboticArmMechanics";
import MachineMechanics from "./mechanics/machineMechanics";
import StorageMechanics from "./mechanics/storageMechanics";
import HumanMechanics from "./mechanics/humanMechanics";
import { AddIcon } from "../../../../icons/ExportCommonIcons";
import { handleAddEventToProduct } from "../../../../../modules/simulation/events/points/functions/handleAddEventToProduct";
import { useProductContext } from "../../../../../modules/simulation/products/productContext";
@@ -61,6 +62,8 @@ const EventProperties: React.FC = () => {
return "machine";
case "storageUnit":
return "storageUnit";
case "human":
return "human";
default:
return null;
}
@@ -80,6 +83,7 @@ const EventProperties: React.FC = () => {
{assetType === "roboticArm" && <RoboticArmMechanics />}
{assetType === "machine" && <MachineMechanics />}
{assetType === "storageUnit" && <StorageMechanics />}
{assetType === "human" && <HumanMechanics />}
</>
)}
{!currentEventData && selectedEventSphere && (

View File

@@ -1,95 +1,70 @@
import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import EyeDropInput from "../../../../../ui/inputs/EyeDropInput";
interface TravelActionProps {
loadCapacity: {
value: string;
min: number;
max: number;
defaultValue: string;
onChange: (value: string) => void;
};
unloadDuration: {
value: string;
min: number;
max: number;
defaultValue: string;
onChange: (value: string) => void;
};
pickPoint?: {
value: string;
onChange: (value: string) => void;
};
unloadPoint?: {
value: string;
onChange: (value: string) => void;
};
clearPoints: () => void;
loadCapacity: {
value: string;
min: number;
max: number;
defaultValue: string;
onChange: (value: string) => void;
};
unloadDuration: {
value: string;
min: number;
max: number;
defaultValue: string;
onChange: (value: string) => void;
};
clearPoints: () => void;
}
const TravelAction: React.FC<TravelActionProps> = ({
loadCapacity,
unloadDuration,
pickPoint,
unloadPoint,
clearPoints,
loadCapacity,
unloadDuration,
clearPoints,
}) => {
return (
<>
<InputWithDropDown
label="Load Capacity"
value={loadCapacity.value}
min={loadCapacity.min}
max={loadCapacity.max}
defaultValue={loadCapacity.defaultValue}
step={1}
activeOption="s"
onClick={() => {}}
onChange={loadCapacity.onChange}
/>
<InputWithDropDown
label="Unload Duration"
value={unloadDuration.value}
min={unloadDuration.min}
max={unloadDuration.max}
defaultValue={unloadDuration.defaultValue}
step={0.1}
activeOption="s"
onClick={() => {}}
onChange={unloadDuration.onChange}
/>
<div className="selected-actions-list">
<div className="value-field-container">
<div className="label">Reset</div>
<button
id="rest-button"
type="button"
className="regularDropdown-container"
onClick={() => {
clearPoints();
}}
>
Clear
</button>
</div>
</div>
{pickPoint && (
<EyeDropInput
label="Pick Point"
value={pickPoint.value}
onChange={pickPoint.onChange}
/>
)}
{unloadPoint && (
<EyeDropInput
label="Unload Point"
value={unloadPoint.value}
onChange={unloadPoint.onChange}
/>
)}
</>
);
return (
<>
<InputWithDropDown
label="Load Capacity"
value={loadCapacity.value}
min={loadCapacity.min}
max={loadCapacity.max}
defaultValue={loadCapacity.defaultValue}
step={1}
activeOption="unit"
onClick={() => { }}
onChange={loadCapacity.onChange}
/>
<InputWithDropDown
label="Unload Duration"
value={unloadDuration.value}
min={unloadDuration.min}
max={unloadDuration.max}
defaultValue={unloadDuration.defaultValue}
step={0.1}
activeOption="s"
onClick={() => { }}
onChange={unloadDuration.onChange}
/>
<div className="selected-actions-list">
<div className="value-field-container">
<div className="label">Reset</div>
<button
id="rest-button"
type="button"
className="regularDropdown-container"
onClick={() => {
clearPoints();
}}
>
Clear
</button>
</div>
</div>
</>
);
};
export default TravelAction;

View File

@@ -0,0 +1,54 @@
import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
interface WorkerActionProps {
loadCapacity: {
value: string;
min: number;
max: number;
step: number;
defaultValue: string;
disabled?: boolean,
onChange: (value: string) => void;
};
clearPoints: () => void;
}
const WorkerAction: React.FC<WorkerActionProps> = ({
loadCapacity,
clearPoints,
}) => {
return (
<>
<InputWithDropDown
label="Load Capacity"
value={loadCapacity.value}
min={loadCapacity.min}
max={loadCapacity.max}
disabled={loadCapacity.disabled}
defaultValue={loadCapacity.defaultValue}
step={loadCapacity.step}
activeOption="unit"
onClick={() => { }}
onChange={loadCapacity.onChange}
/>
<div className="selected-actions-list">
<div className="value-field-container">
<div className="label">Reset</div>
<button
id="rest-button"
type="button"
className="regularDropdown-container"
onClick={() => {
clearPoints();
}}
>
Clear
</button>
</div>
</div>
</>
);
};
export default WorkerAction;

View File

@@ -0,0 +1,292 @@
import { useEffect, useState } from "react";
import { MathUtils } from "three";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import Trigger from "../trigger/Trigger";
import ActionsList from "../components/ActionsList";
import { useSelectedEventData, useSelectedAction, useSelectedAnimation } from "../../../../../../store/simulation/useSimulationStore";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
import { useVersionContext } from "../../../../../../modules/builder/version/versionContext";
import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
import { useParams } from "react-router-dom";
import WorkerAction from "../actions/workerAction";
function HumanMechanics() {
const [activeOption, setActiveOption] = useState<"worker">("worker");
const [speed, setSpeed] = useState("0.5");
const [loadCapacity, setLoadCapacity] = useState("1");
const [currentAction, setCurrentAction] = useState<HumanAction | undefined>();
const [selectedPointData, setSelectedPointData] = useState<HumanPointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { productStore } = useSceneContext();
const { getPointByUuid, updateEvent, updateAction, addAction, removeAction, getEventByModelUuid } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { selectedAction, setSelectedAction, clearSelectedAction } = useSelectedAction();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
useEffect(() => {
if (selectedEventData && selectedEventData.data.type === "human") {
const point = getPointByUuid(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint
) as HumanPointSchema | undefined;
if (point?.action) {
setSelectedPointData(point);
setCurrentAction(point.action);
setSelectedAction(point.action.actionUuid, point.action.actionName);
setSpeed((
getEventByModelUuid(
selectedProduct.productUuid,
selectedEventData?.data.modelUuid || ""
) as HumanEventSchema | undefined
)?.speed?.toString() || "1");
setLoadCapacity(point.action.loadCapacity.toString());
}
} else {
clearSelectedAction();
}
}, [selectedEventData, selectedProduct]);
useEffect(() => {
if (selectedEventData && selectedProduct.productUuid) {
const point = getPointByUuid(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint
) as HumanPointSchema | undefined;
if (point?.action) {
setSelectedPointData(point);
setCurrentAction(point.action);
setActiveOption(point.action.actionType);
setSelectedAction(point.action.actionUuid, point.action.actionName);
}
} else {
clearSelectedAction();
setCurrentAction(undefined);
setSpeed("0.5");
setLoadCapacity("1");
}
}, [selectedEventData, selectedProduct, selectedAction]);
const updateBackend = (
productName: string,
productUuid: string,
projectId: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName,
productUuid,
projectId,
eventDatas: eventData,
versionId: selectedVersion?.versionId || "",
});
};
const handleSelectActionType = (actionType: string) => {
if (!selectedAction.actionId || !currentAction || !selectedPointData) return;
const updatedAction = { ...currentAction, actionType: actionType as "worker" };
const updatedPoint = { ...selectedPointData, action: updatedAction };
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
updatedAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setCurrentAction(updatedAction);
setSelectedPointData(updatedPoint);
};
const handleSpeedChange = (value: string) => {
if (!selectedEventData) return;
const numericValue = parseFloat(value);
if (isNaN(numericValue)) return;
const updatedEvent = {
...selectedEventData.data,
speed: numericValue
} as HumanEventSchema;
const event = updateEvent(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
updatedEvent
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setSpeed(value);
};
const handleLoadCapacityChange = (value: string) => {
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
const updatedAction = { ...currentAction };
updatedAction.loadCapacity = parseInt(value)
const updatedPoint = { ...selectedPointData, action: updatedAction };
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
updatedAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setCurrentAction(updatedAction);
setSelectedPointData(updatedPoint);
setLoadCapacity(value);
};
const handleClearPoints = () => {
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
const updatedAction = { ...currentAction };
delete updatedAction.pickUpPoint;
delete updatedAction.dropPoint;
const updatedPoint = { ...selectedPointData, action: updatedAction };
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
updatedAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setCurrentAction(updatedAction);
setSelectedPointData(updatedPoint);
};
const handleAddAction = () => {
if (!selectedEventData || !selectedPointData) return;
const newAction: HumanAction = {
actionUuid: MathUtils.generateUUID(),
actionName: `Action`,
actionType: "worker",
loadCapacity: 1,
triggers: [],
};
const event = addAction(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint,
newAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
const updatedPoint = { ...selectedPointData, action: newAction };
setSelectedPointData(updatedPoint);
setSelectedAction(newAction.actionUuid, newAction.actionName);
};
const handleDeleteAction = () => {
if (!selectedPointData) return;
const event = removeAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
const updatedPoint = { ...selectedPointData, action: undefined as any };
setSelectedPointData(updatedPoint);
clearSelectedAction();
setCurrentAction(undefined);
};
return (
<>
<div className="global-props section">
<div className="property-list-container">
<div className="property-item">
<InputWithDropDown
label="Speed"
value={speed}
min={0}
step={0.1}
defaultValue="0.5"
max={10}
activeOption="m/s"
onClick={() => { }}
onChange={handleSpeedChange}
/>
</div>
</div>
</div>
<section>
<ActionsList
selectedPointData={selectedPointData}
multipleAction={false}
handleAddAction={handleAddAction}
handleDeleteAction={handleDeleteAction}
/>
{selectedAction.actionId && currentAction && (
<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={["worker"]}
onSelect={handleSelectActionType}
disabled={true}
/>
</div>
<WorkerAction
loadCapacity={{
value: loadCapacity,
min: 1,
max: 5,
step: 1,
defaultValue: "1",
disabled: true,
onChange: handleLoadCapacityChange,
}}
clearPoints={handleClearPoints}
/>
<div className="tirgger">
<Trigger selectedPointData={selectedPointData as any} type="Human" />
</div>
</div>
)}
</section>
</>
);
}
export default HumanMechanics;

View File

@@ -272,14 +272,6 @@ function VehicleMechanics() {
onChange: handleUnloadDurationChange,
}}
clearPoints={handleClearPoints}
// pickPoint={{
// value: currentPickPoint,
// onChange: handlePickPointChange,
// }}
// unloadPoint={{
// value: currentUnloadPoint,
// onChange: handleUnloadPointChange,
// }}
/>
)}
</div>

View File

@@ -13,7 +13,7 @@ import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
type TriggerProps = {
selectedPointData?: PointsScheme | undefined;
type?: "Conveyor" | "Vehicle" | "RoboticArm" | "Machine" | "StorageUnit";
type?: "Conveyor" | "Vehicle" | "RoboticArm" | "Machine" | "StorageUnit" | "Human";
};
const Trigger = ({ selectedPointData, type }: TriggerProps) => {
@@ -38,7 +38,7 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
if (type === "Conveyor" || type === "Vehicle" || type === "Machine" || type === "StorageUnit") {
actionUuid = (selectedPointData as | ConveyorPointSchema | VehiclePointSchema | MachinePointSchema | StoragePointSchema).action?.actionUuid;
} else if (type === "RoboticArm" && selectedAction.actionId) {
} else if ((type === "RoboticArm" || type === "Human") && selectedAction.actionId) {
actionUuid = selectedAction.actionId;
}
@@ -365,18 +365,16 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
}
/>
</button>
{triggers.length > 1 && (
<button
id="remove-trigger-button"
className="remove-button"
onClick={(e) => {
e.stopPropagation();
handleRemoveTrigger(trigger.triggerUuid);
}}
>
<RemoveIcon />
</button>
)}
<button
id="remove-trigger-button"
className="remove-button"
onClick={(e) => {
e.stopPropagation();
handleRemoveTrigger(trigger.triggerUuid);
}}
>
<RemoveIcon />
</button>
</div>
))}
</div>