first commit

This commit is contained in:
2025-06-10 15:28:23 +05:30
commit e22a2dc275
699 changed files with 100382 additions and 0 deletions

View File

@@ -0,0 +1,300 @@
import React, { useMemo, useState } from "react";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import { ArrowIcon } from "../../../icons/ExportCommonIcons";
// image imports
import Arc from "../../../../assets/image/aisleTypes/Arc.png";
import Arrow from "../../../../assets/image/aisleTypes/Arrow.png";
import Arrows from "../../../../assets/image/aisleTypes/Arrows.png";
import Circle from "../../../../assets/image/aisleTypes/Circle.png";
import Dashed from "../../../../assets/image/aisleTypes/Dashed.png";
import Directional from "../../../../assets/image/aisleTypes/Directional.png";
import Dotted from "../../../../assets/image/aisleTypes/Dotted.png";
import Solid from "../../../../assets/image/aisleTypes/Solid.png";
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
import InputToggle from "../../../ui/inputs/InputToggle";
interface TextureItem {
color: string;
id: AisleColors;
brief: string;
texture: string;
}
const AisleProperties: React.FC = () => {
const [collapsePresets, setCollapsePresets] = useState(false);
const [collapseTexture, setCollapseTexture] = useState(true);
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, isFlipped, setAisleType, setAisleColor, setAisleWidth, setDashLength, setGapLength, setDotRadius, setAisleLength, setIsFlipped } = useBuilderStore();
const aisleTextureList: TextureItem[] = [
{ color: "yellow", id: "yellow", brief: "pedestrian walkways", texture: "" },
{ color: "gray", id: "gray", brief: "basic", texture: "" },
{ color: "green", id: "green", brief: "pedestrian walkways", texture: "" },
{ color: "orange", id: "orange", brief: "material flow", texture: "" },
{ color: "blue", id: "blue", brief: "vehicle paths", texture: "" },
{ color: "purple", id: "purple", brief: "material flow", texture: "" },
{ color: "red", id: "red", brief: "safety zone", texture: "" },
{ color: "bright green", id: "#66FF00", brief: "safety zone", texture: "" },
{ color: "yellow-black", id: "yellow-black", brief: "utility aisles", texture: "" },
{ color: "white-black", id: "white-black", brief: "utility aisles", texture: "" },
];
const aisleTypes: {
name: string;
type: AisleTypes;
id: string;
thumbnail: string;
}[] = [
{ name: "Solid", type: "solid-aisle", id: "1", thumbnail: Solid },
{ name: "Dotted", type: "dotted-aisle", id: "2", thumbnail: Dotted },
{ name: "Dashed", type: "dashed-aisle", id: "3", thumbnail: Dashed },
{ name: "Arrow", type: "arrow-aisle", id: "4", thumbnail: Arrow },
{ name: "Continuous Arrows", type: "arrows-aisle", id: "5", thumbnail: Arrows },
{ name: "Directional", type: "junction-aisle", id: "6", thumbnail: Directional },
{ name: "Arc", type: "arc-aisle", id: "7", thumbnail: Arc },
{ name: "Circle", type: "circle-aisle", id: "8", thumbnail: Circle },
];
const handleAisleWidthChange = (value: string) => {
const width = parseFloat(value);
if (!isNaN(width)) {
setAisleWidth(width);
}
};
const handleDashLengthChange = (value: string) => {
const length = parseFloat(value);
if (!isNaN(length)) {
setDashLength(length);
}
};
const handleGapLengthChange = (value: string) => {
const length = parseFloat(value);
if (!isNaN(length)) {
setGapLength(length);
}
};
const handleDotRadiusChange = (value: string) => {
const radius = parseFloat(value);
if (!isNaN(radius)) {
setDotRadius(radius);
}
};
const handleAisleLengthChange = (value: string) => {
const length = parseFloat(value);
if (!isNaN(length)) {
setAisleLength(length);
}
};
const handleIsFlippedChange = () => {
setIsFlipped(!aisleIsFlipped)
};
const dashLengthValue = useMemo(() => {
return dashLength.toString();
}, [aisleType, dashLength]);
const dotRadiusValue = useMemo(() => {
return dotRadius.toString();
}, [aisleType, dotRadius]);
const gapLengthValue = useMemo(() => {
return gapLength.toString();
}, [aisleType, gapLength]);
const aisleWidthValue = useMemo(() => {
return aisleWidth.toString();
}, [aisleType, aisleWidth]);
const aisleLengthValue = useMemo(() => {
return aisleLength.toString();
}, [aisleType, aisleLength]);
const aisleIsFlipped = useMemo(() => {
return isFlipped;
}, [aisleType, isFlipped]);
const renderAdvancedProperties = () => {
switch (aisleType) {
case 'dashed-aisle':
return (
<>
{aisleType &&
<>
<InputWithDropDown
label="Dash Length"
value={`${dashLengthValue}`}
min={0.1}
step={0.1}
max={2}
onChange={handleDashLengthChange}
/>
<InputWithDropDown
label="Gap Length"
value={`${gapLengthValue}`}
min={0.1}
step={0.1}
max={2}
onChange={handleGapLengthChange}
/>
</>
}
</>
);
case 'dotted-aisle':
return (
<>
{aisleType &&
<>
<InputWithDropDown
label="Dot Radius"
value={`${dotRadiusValue}`}
min={0.1}
step={0.1}
max={2}
onChange={handleDotRadiusChange}
/>
<InputWithDropDown
label="Gap Length"
value={`${gapLengthValue}`}
min={0.1}
step={0.1}
max={2}
onChange={handleGapLengthChange}
/>
</>
}
</>
);
case 'arrows-aisle':
return (
<>
{aisleType &&
<>
<InputWithDropDown
label="Arrow Length"
value={`${aisleLengthValue}`}
min={0.1}
step={0.1}
max={2}
onChange={handleAisleLengthChange}
/>
<InputWithDropDown
label="Gap Length"
value={`${gapLengthValue}`}
min={0.1}
step={0.1}
max={2}
onChange={handleGapLengthChange}
/>
</>
}
</>
);
case 'junction-aisle': case 'arc-aisle':
return (
<>
{aisleType &&
<InputToggle
inputKey="Flip Ailse"
label="Flip Aisle"
value={aisleIsFlipped}
onClick={handleIsFlippedChange}
/>
}
</>
);
default:
return null;
}
};
return (
<div className="aisle-properties-container">
<div className="header">Properties</div>
{/* Basic Properties */}
<section>
{aisleType !== 'dotted-aisle' &&
<InputWithDropDown
label="Aisle Width"
value={`${aisleWidthValue}`}
min={0.1}
step={0.1}
max={2}
onChange={handleAisleWidthChange}
/>
}
{renderAdvancedProperties()}
</section>
{/* Presets */}
<section>
<button
className="header"
onClick={() => setCollapsePresets(!collapsePresets)}
aria-expanded={!collapsePresets}
>
<div className="value">Presets</div>
<div className="icon">
<ArrowIcon />
</div>
</button>
{!collapsePresets && (
<div className="presets-list-container">
{aisleTypes.map((val) => (
<div className="preset-list" key={val.id}>
<button
className={`thumbnail ${aisleType === val.type ? "selected" : ""}`}
title={val.name}
onClick={() => setAisleType(val.type)}
>
<img src={val.thumbnail} alt="" />
</button>
</div>
))}
</div>
)}
</section>
{/* Texture */}
<section>
<button
className="header"
onClick={() => setCollapseTexture(!collapseTexture)}
aria-expanded={!collapseTexture}
>
<div className="value">Aisle Texture</div>
<div className="icon" style={{ rotate: collapseTexture ? "" : "-90deg" }}>
<ArrowIcon />
</div>
</button>
{collapseTexture && (
<div className="aisle-texture-container">
{aisleTextureList.map((val) => (
<button
key={val.id}
title={val.brief || val.id}
className={`aisle-list ${aisleColor === val.color ? "selected" : ""}`}
onClick={() => setAisleColor(val.id)}
aria-pressed={aisleColor === val.id}
>
<div className="texture-display">{val.texture}</div>
<div className="aisle-color">{val.color}</div>
<div className="aisle-brief">{`( ${val.brief} )`}</div>
</button>
))}
</div>
)}
</section>
</div>
);
};
export default AisleProperties;

View File

@@ -0,0 +1,103 @@
import React, { useState } from "react";
import InputToggle from "../../../ui/inputs/InputToggle";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import { RemoveIcon } from "../../../icons/ExportCommonIcons";
import PositionInput from "../customInput/PositionInputs";
import RotationInput from "../customInput/RotationInput";
import { useSelectedFloorItem, useObjectPosition, useObjectRotation } from "../../../../store/builder/store";
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
}
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();
// Function to handle adding new user data
const handleAddUserData = () => {
const newUserData: UserData = {
id: nextId,
label: `Property ${nextId}`,
value: "",
};
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
)
);
};
// Remove user data
const handleRemoveUserData = (id: number) => {
setUserData((prevUserData) =>
prevUserData.filter((data) => data.id !== id)
);
};
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>
<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>
))}
{/* Add new user data */}
<div className="optimize-button" onClick={handleAddUserData}>
+ Add
</div>
</section>
</div>
);
};
export default AssetProperties;

View File

@@ -0,0 +1,314 @@
import React, { useEffect, useState } from "react";
import InputRange from "../../../ui/inputs/InputRange";
import InputToggle from "../../../ui/inputs/InputToggle";
import { AIIcon } from "../../../icons/ExportCommonIcons";
import LabeledButton from "../../../ui/inputs/LabledButton";
import {
useAzimuth,
useElevation,
useLimitDistance,
useRenderDistance,
useResetCamera,
useRoofVisibility,
useSelectedWallItem,
useShadows,
useSocketStore,
useTileDistance,
useToggleView,
useWallVisibility,
} from "../../../../store/builder/store";
import { setEnvironment } from "../../../../services/factoryBuilder/environment/setEnvironment";
import * as CONSTANTS from "../../../../types/world/worldConstants";
import { useParams } from "react-router-dom";
const GlobalProperties: React.FC = () => {
const { toggleView, setToggleView } = useToggleView();
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
const { roofVisibility, setRoofVisibility } = useRoofVisibility();
const { wallVisibility, setWallVisibility } = useWallVisibility();
const { shadows, setShadows } = useShadows();
const { resetCamera, setResetCamera } = useResetCamera();
const { elevation, setElevation } = useElevation();
const { azimuth, setAzimuth } = useAzimuth();
const { renderDistance, setRenderDistance } = useRenderDistance();
const { setPlaneValue, setGridValue, planeValue, gridValue } = useTileDistance();
const { socket } = useSocketStore();
const { limitDistance, setLimitDistance } = useLimitDistance();
const [distance, setDistance] = useState<number>(40);
const [limitGridDistance, setLimitGridDistance] = useState(false);
const [gridDistance, setGridDistance] = useState<number>(3);
const { projectId } = useParams();
const optimizeScene = async (value: any) => {
const email = localStorage.getItem("email");
const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg";
setEnvironment(
organization,
localStorage.getItem("userId")!,
wallVisibility,
roofVisibility,
shadows,
30,
true,
projectId
);
setRenderDistance(30);
setLimitDistance(true);
};
const limitRenderDistance = async () => {
const email = localStorage.getItem("email");
const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg";
if (limitDistance) {
setEnvironment(
organization,
localStorage.getItem("userId")!,
wallVisibility,
roofVisibility,
shadows,
75,
!limitDistance,
projectId
);
setRenderDistance(75);
} else {
setEnvironment(
organization,
localStorage.getItem("userId")!,
wallVisibility,
roofVisibility,
shadows,
renderDistance,
!limitDistance,
projectId
);
}
setLimitDistance(!limitDistance);
};
function updateDistance(value: number) {
setDistance(value);
setRenderDistance(value);
}
function updateGridDistance(value: number) {
setGridDistance(value);
// setGridValue({ size: value * 100, divisions: (value * 100) / 4 });
// setPlaneValue({ height: value * 100, width: value * 100 });
}
function updatedGrid(value: number) {
// console.log(" (value * 100) / 4 : ", (value * 100) / 4);
setGridValue({ size: value * 100, divisions: (value * 100) / 4 });
setPlaneValue({ height: value * 100, width: value * 100 });
}
const updatedDist = async (value: number) => {
const email = localStorage.getItem("email");
const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg";
setRenderDistance(value);
// setDistance(value);
const data = await setEnvironment(
organization,
localStorage.getItem("userId")!,
wallVisibility,
roofVisibility,
shadows,
value,
limitDistance,
projectId
);
};
// Function to toggle roof visibility
const changeRoofVisibility = async () => {
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
//using REST
const data = await setEnvironment(
organization,
localStorage.getItem("userId")!,
wallVisibility,
!roofVisibility,
shadows,
renderDistance,
limitDistance,
projectId
);
//
//using Socket
// const visData = {
// organization: organization,
// userId: localStorage.getItem('userId')!,
// wallVisibility: wallVisibility,
// roofVisibility: !roofVisibility,
// shadowVisibility: shadows,
// socketId: socket.id
// };
// socket.emit('v1:Environment:set', visData)
setRoofVisibility(!roofVisibility); // Toggle roof visibility
};
// Function to toggle wall visibility
const changeWallVisibility = async () => {
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
//using REST
const data = await setEnvironment(
organization,
localStorage.getItem("userId")!,
!wallVisibility,
roofVisibility,
shadows,
renderDistance,
limitDistance, projectId
);
//
//using Socket
// const visData = {
// organization: organization,
// userId: localStorage.getItem('userId')!,
// wallVisibility: !wallVisibility,
// roofVisibility: roofVisibility,
// shadowVisibility: shadows,
// socketId: socket.id
// };
// socket.emit('v1:Environment:set', visData)
setWallVisibility(!wallVisibility); // Toggle wall visibility
};
const shadowVisibility = async () => {
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
//using REST
const data = await setEnvironment(
organization,
localStorage.getItem("userId")!,
wallVisibility,
roofVisibility,
!shadows,
renderDistance,
limitDistance,
projectId
);
//
//using Socket
// const visData = {
// organization: organization,
// userId: localStorage.getItem('userId')!,
// wallVisibility: wallVisibility,
// roofVisibility: roofVisibility,
// shadowVisibility: !shadows,
// socketId: socket.id
// };
// socket.emit('v1:Environment:set', visData)
setShadows(!shadows);
};
const toggleResetCamera = () => {
if (!toggleView) {
setResetCamera(true); // Trigger reset camera action
}
};
// function changeRenderDistance(e: any) {
// if (parseInt(e.target.value) < 20) {
// setRenderDistance(20);
// } else if (parseInt(e.target.value) > 75) {
// setRenderDistance(75);
// } else {
// setRenderDistance(parseInt(e.target.value));
// }
// }
return (
<div className="global-properties-container">
<section>
<div className="header">Environment</div>
<div className="optimize-button" onClick={optimizeScene}>
<AIIcon />
Optimize
</div>
<div className="split"></div>
<InputToggle
value={roofVisibility}
inputKey="1"
label="Roof Visibility"
onClick={changeRoofVisibility}
/>
<InputToggle
value={wallVisibility}
inputKey="2"
label="Wall Visibility"
onClick={changeWallVisibility}
/>
{/* <InputToggle
value={shadows}
inputKey="3"
label="Shadows Visibility"
onClick={shadowVisibility}
/> */}
<LabeledButton
label="Reset Camera"
onClick={toggleResetCamera}
value="Reset"
/>
<div className="split"></div>
{/* //visibleEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor} */}
<InputToggle
inputKey="4"
label="Limit Render Distance"
value={limitDistance}
// onClick={() => {
// setLimitDistance(!limitDistance);
// // setDistance(75);
// // setRenderDistance(75);
// }}
onClick={async () => {
await limitRenderDistance(); // Call the function here
}}
/>
<InputRange
label="Distance"
disabled={!limitDistance}
value={renderDistance}
min={CONSTANTS.distanceConfig.minDistance}
max={CONSTANTS.distanceConfig.maxDistance}
onChange={(value: number) => updateDistance(value)}
onPointerUp={updatedDist}
key={"6"}
/>
{/* <div className="split"></div>
<InputToggle
inputKey="6"
label="Display Grid"
value={limitGridDistance}
onClick={() => {
setLimitGridDistance(!limitGridDistance);
}}
/>
<InputRange
label="Tile Distance"
disabled={!limitGridDistance}
value={gridDistance}
key={"7"}
min={1}
max={5}
onChange={(value: number) => updateGridDistance(value)}
onPointerUp={updatedGrid}
/> */}
</section>
</div>
);
};
export default GlobalProperties;

View File

@@ -0,0 +1,128 @@
import React, { useEffect, useState } from "react";
import RenameInput from "../../../ui/inputs/RenameInput";
import Vector3Input from "../customInput/Vector3Input";
import { useSelectedZoneStore } from "../../../../store/visualization/useZoneStore";
import {
useEditPosition,
usezonePosition,
useZones,
usezoneTarget,
} from "../../../../store/builder/store";
import { zoneCameraUpdate } from "../../../../services/visulization/zone/zoneCameraUpdation";
import { useParams } from "react-router-dom";
const ZoneProperties: React.FC = () => {
const { Edit, setEdit } = useEditPosition();
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
const { zonePosition, setZonePosition } = usezonePosition();
const { zoneTarget, setZoneTarget } = usezoneTarget();
const { zones, setZones } = useZones();
const { projectId } = useParams()
useEffect(() => {
setZonePosition(selectedZone.zoneViewPortPosition);
setZoneTarget(selectedZone.zoneViewPortTarget);
}, [selectedZone?.zoneViewPortPosition, selectedZone?.zoneViewPortTarget]);
async function handleSetView() {
try {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
let zonesdata = {
zoneUuid: selectedZone.zoneUuid,
viewPortposition: zonePosition,
viewPortCenter: zoneTarget,
};
let response = await zoneCameraUpdate(zonesdata, organization, projectId);
console.log('response: ', response);
if (response.message === "zone updated") {
setEdit(false);
} else {
// console.log(response);
}
} catch (error) {
echo.error("Failed to set zone view");
}
}
function handleEditView() {
setEdit(!Edit); // This will toggle the `Edit` state correctly
}
async function handleZoneNameChange(newName: string) {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
const zonesdata = {
zoneUuid: selectedZone.zoneUuid,
zoneName: newName,
};
// Call your API to update the zone
let response = await zoneCameraUpdate(zonesdata, organization, projectId);
if (response.message === "zone updated") {
setSelectedZone((prev) => ({ ...prev, zoneName: newName }));
setZones((prevZones: any[]) =>
prevZones.map((zone) =>
zone.zoneUuid === selectedZone.zoneUuid
? { ...zone, zoneName: newName }
: zone
)
);
} else {
// console.log(response?.message);
}
}
function handleVectorChange(
key: "zoneViewPortTarget" | "zoneViewPortPosition",
newValue: [number, number, number]
) {
setSelectedZone((prev) => ({ ...prev, [key]: newValue }));
}
const checkZoneNameDuplicate = (name: string) => {
return zones.some(
(zone: any) =>
zone.zoneName?.trim().toLowerCase() === name?.trim().toLowerCase() &&
zone.zoneUuid !== selectedZone.zoneUuid
);
};
return (
<div className="zone-properties-container">
<section>
<div className="header">
<RenameInput
value={selectedZone.zoneName}
onRename={handleZoneNameChange}
checkDuplicate={checkZoneNameDuplicate}
/>
<div className="button" onClick={handleEditView}>
{Edit ? "Cancel" : "Edit"}
</div>
</div>
<Vector3Input
onChange={(value) => handleVectorChange("zoneViewPortTarget", value)}
header="Viewport Target"
value={zoneTarget as [number, number, number]}
disabled={!Edit}
/>
<Vector3Input
onChange={(value) =>
handleVectorChange("zoneViewPortPosition", value)
}
header="Viewport Position"
value={zonePosition as [number, number, number]}
disabled={!Edit}
/>
{Edit && (
<div className="button-save" onClick={handleSetView}>
Set View
</div>
)}
</section>
</div>
);
};
export default ZoneProperties;

View File

@@ -0,0 +1,137 @@
import React, { useEffect, useState } from "react";
import {
useSelectedEventData,
useSelectedEventSphere,
} from "../../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../../store/simulation/useProductStore";
import ConveyorMechanics from "./mechanics/conveyorMechanics";
import VehicleMechanics from "./mechanics/vehicleMechanics";
import RoboticArmMechanics from "./mechanics/roboticArmMechanics";
import MachineMechanics from "./mechanics/machineMechanics";
import StorageMechanics from "./mechanics/storageMechanics";
import { AddIcon } from "../../../../icons/ExportCommonIcons";
import { handleAddEventToProduct } from "../../../../../modules/simulation/events/points/functions/handleAddEventToProduct";
import { useEventsStore } from "../../../../../store/simulation/useEventsStore";
import { useProductContext } from "../../../../../modules/simulation/products/productContext";
import { useParams } from "react-router-dom";
const EventProperties: React.FC = () => {
const { selectedEventData } = useSelectedEventData();
const { getEventByModelUuid } = useProductStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const [currentEventData, setCurrentEventData] = useState<EventsSchema | null>(
null
);
const [assetType, setAssetType] = useState<string | null>(null);
const { products, addEvent } = useProductStore();
const { selectedEventSphere } = useSelectedEventSphere();
const { projectId } = useParams();
useEffect(() => {
const event = getCurrentEventData();
setCurrentEventData(event);
const type = determineAssetType(event);
setAssetType(type);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedEventData, selectedProduct]);
const getCurrentEventData = () => {
if (!selectedEventData?.data || !selectedProduct) return null;
return (
getEventByModelUuid(
selectedProduct.productUuid,
selectedEventData.data.modelUuid
) ?? null
);
};
const determineAssetType = (event: EventsSchema | null) => {
if (!event) return null;
switch (event.type) {
case "transfer":
return "conveyor";
case "vehicle":
return "vehicle";
case "roboticArm":
return "roboticArm";
case "machine":
return "machine";
case "storageUnit":
return "storageUnit";
default:
return null;
}
};
return (
<div className="event-proprties-wrapper">
{currentEventData && (
<>
<div className="header">
<div className="header-value">
{selectedEventData?.data.modelName}
</div>
</div>
{assetType === "conveyor" && <ConveyorMechanics />}
{assetType === "vehicle" && <VehicleMechanics />}
{assetType === "roboticArm" && <RoboticArmMechanics />}
{assetType === "machine" && <MachineMechanics />}
{assetType === "storageUnit" && <StorageMechanics />}
</>
)}
{!currentEventData && selectedEventSphere && (
<div className="no-event-selected">
<p>
<strong>Oops!</strong> It looks like this object doesn't have an
event assigned yet. To continue, please link it to one of the
products below.
</p>
<div className="products-list">
<p>
<strong>Here are some products you can add it to:</strong>
</p>
<div className="product-item">
{products.map((product) => (
<button
id="add-event-to-product-button"
key={product.productUuid}
onClick={() => {
if (selectedEventData) {
handleAddEventToProduct({
event: useEventsStore
.getState()
.getEventByModelUuid(
selectedEventData?.data.modelUuid
),
addEvent,
selectedProduct,
projectId: projectId || ''
});
}
}}
>
<AddIcon />
{product.productName}
</button>
))}
</div>
</div>
</div>
)}
{!selectedEventSphere && (
<div className="no-event-selected">
<p>
<strong>Oops!</strong> It looks like you haven't selected an event
point yet. Please select an event to view its properties.
</p>
</div>
)}
</div>
);
};
export default EventProperties;

View File

@@ -0,0 +1,201 @@
import { useEffect, useState } from "react";
import InputWithDropDown from "../../../../ui/inputs/InputWithDropDown";
import { AddIcon, RemoveIcon } from "../../../../icons/ExportCommonIcons";
// Texture Imports
import wallTexture1 from "../../../../../assets/image/wallTextures/wallTexture.png";
import defaultTexture from "../../../../../assets/image/wallTextures/defaultTexture.jpg";
import { useBuilderStore } from "../../../../../store/builder/useBuilderStore";
// Define Material type
type Material = {
texture: string;
textureName: string;
};
// Initial and default material
const initialMaterial: Material = {
texture: wallTexture1,
textureName: "Grunge Concrete Wall",
};
const defaultMaterial: Material = {
texture: defaultTexture,
textureName: "Default Material",
};
const WallProperties = () => {
const { wallHeight, wallThickness, setWallHeight, setWallThickness } = useBuilderStore();
const [activeSide, setActiveSide] = useState<"side1" | "side2">("side1");
const [materials, setMaterials] = useState<Material[]>([initialMaterial]);
const [selectedMaterials, setSelectedMaterials] = useState<{
side1: Material | null;
side2: Material | null;
}>({
side1: null,
side2: null,
});
// Select initial material for both sides on mount
useEffect(() => {
setSelectedMaterials({
side1: initialMaterial,
side2: initialMaterial,
});
}, []);
const handleHeightChange = (newValue: string) => {
setWallHeight(parseFloat(newValue));
};
const handleThicknessChange = (newValue: string) => {
setWallThickness(parseFloat(newValue));
};
const handleAddMaterial = () => {
const newMaterial: Material = {
texture: defaultMaterial.texture,
textureName: `Material ${materials.length + 1}`,
};
setMaterials([...materials, newMaterial]);
};
const handleSelectMaterial = (material: Material) => {
setSelectedMaterials((prev) => ({
...prev,
[activeSide]: material,
}));
};
const handleRemoveMaterial = (index: number) => {
const updatedMaterials = materials.filter((_, i) => i !== index);
// Ensure there's always at least one material
const newMaterials =
updatedMaterials.length === 0 ? [defaultMaterial] : updatedMaterials;
setMaterials(newMaterials);
// Deselect the material if it's the one removed
setSelectedMaterials((prev) => {
const updated = { ...prev };
["side1", "side2"].forEach((side) => {
if (
updated[side as "side1" | "side2"]?.texture ===
materials[index].texture
) {
updated[side as "side1" | "side2"] = defaultMaterial;
}
});
return updated;
});
};
return (
<div className="wall-properties-container">
<div className="header">Wall</div>
<div className="wall-properties">
<InputWithDropDown
label="Height"
value={`${wallHeight}`}
onChange={(val) => handleHeightChange(val)}
/>
<InputWithDropDown
label="Thickness"
value={`${wallThickness}`}
onChange={(val) => handleThicknessChange(val)}
/>
</div>
<section>
<div className="header-wrapper">
<div className="header">Materials</div>
<button className="addMaterial" onClick={handleAddMaterial}>
<AddIcon />
</button>
</div>
<div className="material-preview">
<div className="sides-wrapper">
<div
className={`side-wrapper ${activeSide === "side1" ? "active" : ""
}`}
onClick={() => setActiveSide("side1")}
>
<div className="label">Side 1</div>
<div className="texture-image">
{selectedMaterials.side1 && (
<img
src={selectedMaterials.side1.texture}
alt={selectedMaterials.side1.textureName}
/>
)}
</div>
</div>
<div
className={`side-wrapper ${activeSide === "side2" ? "active" : ""
}`}
onClick={() => setActiveSide("side2")}
>
<div className="label">Side 2</div>
<div className="texture-image">
{selectedMaterials.side2 && (
<img
src={selectedMaterials.side2.texture}
alt={selectedMaterials.side2.textureName}
/>
)}
</div>
</div>
</div>
<div className="preview">
{selectedMaterials[activeSide] && (
<img
src={selectedMaterials[activeSide]!.texture}
alt={selectedMaterials[activeSide]!.textureName}
/>
)}
</div>
</div>
<div className="materials">
{materials.length === 0 ? (
<div className="no-materials">No materials added yet.</div>
) : (
<div className="material-container">
{materials.map((material, index) => (
<div
className="material-wrapper"
key={`${material.textureName}_${index}`}
onClick={() => handleSelectMaterial(material)}
>
<div className="material-property">
<div className="material-image">
<img src={material.texture} alt={material.textureName} />
</div>
<div className="material-name">{material.textureName}</div>
</div>
<div
className="delete-material"
onClick={(e) => {
e.stopPropagation();
handleRemoveMaterial(index);
}}
>
<RemoveIcon />
</div>
</div>
))}
</div>
)}
</div>
</section>
</div>
);
};
export default WallProperties;

View File

@@ -0,0 +1,7 @@
import React from "react";
const DefaultAction:React.FC = () => {
return <></>;
};
export default DefaultAction;

View File

@@ -0,0 +1,34 @@
import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
interface DelayActionProps {
value: string;
defaultValue: string;
min: number;
max: number;
onChange: (value: string) => void;
}
const DelayAction: React.FC<DelayActionProps> = ({
value,
defaultValue,
min,
max,
onChange,
}) => {
return (
<InputWithDropDown
label="Delay"
value={value}
min={min}
step={0.1}
defaultValue={defaultValue}
max={max}
activeOption="s"
onClick={() => {}}
onChange={onChange}
/>
);
};
export default DelayAction;

View File

@@ -0,0 +1,10 @@
import React from "react";
const DespawnAction: React.FC = () => {
return (
<>
</>
);
};
export default DespawnAction;

View File

@@ -0,0 +1,29 @@
import React from "react";
interface PickAndPlaceActionProps {
clearPoints: () => void;
}
const PickAndPlaceAction: React.FC<PickAndPlaceActionProps> = ({
clearPoints,
}) => {
return (
<div className="selected-actions-list">
<div className="value-field-container">
<div className="label">Reset</div>
<button
id="pick-and-place-action-clear-button"
type="button"
className="regularDropdown-container"
onClick={() => {
clearPoints();
}}
>
Clear
</button>
</div>
</div>
);
};
export default PickAndPlaceAction;

View File

@@ -0,0 +1,48 @@
import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import SwapAction from "./SwapAction";
interface ProcessActionProps {
value: string;
min: number;
max: number;
defaultValue: string;
onChange: (value: string) => void;
swapOptions: string[];
swapDefaultOption: string;
onSwapSelect: (value: string) => void;
}
const ProcessAction: React.FC<ProcessActionProps> = ({
value,
min,
max,
defaultValue,
onChange,
swapOptions,
swapDefaultOption,
onSwapSelect,
}) => {
return (
<>
<InputWithDropDown
label="Process Time"
value={value}
min={min}
step={1}
max={max}
defaultValue={defaultValue}
activeOption="s"
onClick={() => { }}
onChange={onChange}
/>
<SwapAction
options={swapOptions}
defaultOption={swapDefaultOption}
onSelect={onSwapSelect}
/>
</>
);
};
export default ProcessAction;

View File

@@ -0,0 +1,72 @@
import React from "react";
import PreviewSelectionWithUpload from "../../../../../ui/inputs/PreviewSelectionWithUpload";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
interface SpawnActionProps {
onChangeInterval: (value: string) => void;
onChangeCount: (value: string) => void;
defaultOption: string;
options: string[];
onSelect: (option: string) => void;
intervalValue: string;
countValue: string;
intervalMin: number;
intervalMax: number;
intervalDefaultValue: string;
countMin: number;
countMax: number;
countDefaultValue: string;
}
const SpawnAction: React.FC<SpawnActionProps> = ({
onChangeInterval,
onChangeCount,
defaultOption,
options,
onSelect,
intervalValue,
countValue,
intervalMin,
intervalMax,
intervalDefaultValue,
countMin,
countMax,
countDefaultValue,
}) => {
return (
<>
<InputWithDropDown
label="Spawn interval"
value={intervalValue}
min={intervalMin}
step={1}
defaultValue={intervalDefaultValue}
max={intervalMax}
activeOption="s"
onClick={() => { }}
onChange={onChangeInterval}
/>
<InputWithDropDown
label="Spawn count"
value={countValue}
min={countMin}
step={1}
defaultValue={countDefaultValue}
max={countMax}
activeOption="s"
onClick={() => { }}
onChange={onChangeCount}
/>
{/* <PreviewSelectionWithUpload /> */}
<LabledDropdown
label="Presets"
defaultOption={defaultOption}
options={options}
onSelect={onSelect}
/>
</>
);
};
export default SpawnAction;

View File

@@ -0,0 +1,57 @@
import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
interface StorageActionProps {
type: "store" | "spawn" | "default";
value: string;
min: number;
max?: number;
defaultValue: string;
currentMaterialType: string;
handleCapacityChange: (value: string) => void;
handleMaterialTypeChange: (value: string) => void;
}
const StorageAction: React.FC<StorageActionProps> = ({ type, value, min, max, defaultValue, currentMaterialType, handleCapacityChange, 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}
/>
</>
}
</>
);
};
export default StorageAction;

View File

@@ -0,0 +1,25 @@
import React from "react";
import PreviewSelectionWithUpload from "../../../../../ui/inputs/PreviewSelectionWithUpload";
interface SwapActionProps {
onSelect: (option: string) => void;
defaultOption: string;
options: string[];
}
const SwapAction: React.FC<SwapActionProps> = ({
onSelect,
defaultOption,
options,
}) => {
return (
<PreviewSelectionWithUpload
label="Presets"
defaultOption={defaultOption}
options={options}
onSelect={onSelect}
/>
);
};
export default SwapAction;

View File

@@ -0,0 +1,95 @@
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;
}
const TravelAction: React.FC<TravelActionProps> = ({
loadCapacity,
unloadDuration,
pickPoint,
unloadPoint,
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}
/>
)}
</>
);
};
export default TravelAction;

View File

@@ -0,0 +1,160 @@
import React, { useRef } from "react";
import {
AddIcon,
RemoveIcon,
ResizeHeightIcon,
} from "../../../../../icons/ExportCommonIcons";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../../../functions/handleResizePannel";
import {
useSelectedAction,
} from "../../../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
import { useParams } from "react-router-dom";
interface ActionsListProps {
selectedPointData: any;
multipleAction?: boolean;
handleAddAction?: () => void;
handleDeleteAction?: (actionUuid: string) => void;
}
const ActionsList: React.FC<ActionsListProps> = ({
selectedPointData,
multipleAction = false,
handleAddAction,
handleDeleteAction,
}) => {
const actionsContainerRef = useRef<HTMLDivElement>(null);
// store
const { renameAction } = useProductStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { selectedAction, setSelectedAction } = useSelectedAction();
const { projectId } = useParams();
const handleRenameAction = (newName: string) => {
if (!selectedAction.actionId) return;
const event = renameAction(
selectedProduct.productUuid,
selectedAction.actionId,
newName
);
setSelectedAction(selectedAction.actionId, newName);
if (event) {
upsertProductOrEventApi({
productName: selectedProduct.productName,
productUuid: selectedProduct.productUuid,
projectId,
eventDatas: event,
});
}
};
const handleActionSelect = (actionUuid: string, actionName: string) => {
setSelectedAction(actionUuid, actionName);
};
return (
<div className="actions-list-container">
<div className="actions">
<div className="header">
<div className="header-value">Actions</div>
<button
id="add-action-button"
className="add-button"
onClick={() => {
if (handleAddAction) {
handleAddAction();
}
}}
disabled={!multipleAction}
>
<AddIcon /> Add
</button>
</div>
<div
className="lists-main-container"
ref={actionsContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{multipleAction &&
selectedPointData?.actions?.map((action: any) => (
<div
key={action.actionUuid}
className={`list-item ${selectedAction.actionId === action.actionUuid
? "active"
: ""
}`}
>
<button
id="action-button"
className="value"
onClick={() =>
handleActionSelect(action.actionUuid, action.actionName)
}
>
<RenameInput
value={action.actionName}
onRename={(value) => handleRenameAction(value)}
/>
</button>
{selectedPointData?.actions?.length > 1 && (
<button
id="remove-action-button"
className="remove-button"
onClick={() => {
if (handleDeleteAction) {
handleDeleteAction(action.actionUuid);
}
}}
>
<RemoveIcon />
</button>
)}
</div>
))}
{!multipleAction && selectedPointData?.action && (
<div
key={selectedPointData.action.actionUuid}
className={`list-item active`}
>
<button
id="action-button"
className="value"
onClick={() =>
handleActionSelect(
selectedPointData.action.actionUuid,
selectedPointData.action.actionName
)
}
>
<RenameInput
value={selectedPointData.action.actionName}
onRename={handleRenameAction}
/>
</button>
</div>
)}
</div>
{multipleAction && (
<button
className="resize-icon"
id="action-resize"
onMouseDown={(e: any) => handleResize(e, actionsContainerRef)}
>
<ResizeHeightIcon />
</button>
)}
</div>
</div>
</div>
);
};
export default ActionsList;

View File

@@ -0,0 +1,6 @@
export function handleActionToggle(uuid: string) {
// This function handles the action toggle for the event properties.
// It updates the selected action and its properties based on the provided UUID.
// The function is currently empty and needs to be implemented.
// You can add your logic here to handle the action toggle.
}

View File

@@ -0,0 +1,6 @@
export function handleDeleteAction(uuid: string) {
// This function handles the action toggle for the event properties.
// It updates the selected action and its properties based on the provided UUID.
// The function is currently empty and needs to be implemented.
// You can add your logic here to handle the action toggle.
}

View File

@@ -0,0 +1,286 @@
import { useEffect, useState } from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import DelayAction from "../actions/DelayAction";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import DespawnAction from "../actions/DespawnAction";
import SwapAction from "../actions/SwapAction";
import SpawnAction from "../actions/SpawnAction";
import DefaultAction from "../actions/DefaultAction";
import Trigger from "../trigger/Trigger";
import { useSelectedAction, useSelectedEventData } from "../../../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import ActionsList from "../components/ActionsList";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
import { useParams } from "react-router-dom";
function ConveyorMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "spawn" | "swap" | "delay" | "despawn">("default");
const [selectedPointData, setSelectedPointData] = useState<ConveyorPointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction } = useProductStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { setSelectedAction, clearSelectedAction } = useSelectedAction();
const { projectId } = useParams();
useEffect(() => {
if (selectedEventData) {
const point = getPointByUuid(
selectedProduct.productUuid,
selectedEventData?.data.modelUuid,
selectedEventData?.selectedPoint
) as ConveyorPointSchema | undefined;
if (point && "action" in point) {
setSelectedPointData(point);
setActiveOption(point.action.actionType as | "default" | "spawn" | "swap" | "delay" | "despawn");
setSelectedAction(point.action.actionUuid, point.action.actionName);
}
} else {
clearSelectedAction();
}
}, [selectedProduct, selectedEventData]);
const updateBackend = (
productName: string,
productUuid: string,
projectId: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productUuid: productUuid,
projectId: projectId,
eventDatas: eventData
})
}
const handleSpeedChange = (value: string) => {
if (!selectedEventData) return;
const event = updateEvent(selectedProduct.productUuid, selectedEventData.data.modelUuid, {
speed: parseFloat(value),
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId ||'',
event
);
}
};
const handleActionTypeChange = (option: string) => {
if (!selectedEventData || !selectedPointData) return;
const validOption = option as | "default" | "spawn" | "swap" | "delay" | "despawn";
setActiveOption(validOption);
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
actionType: validOption,
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId ||'',
event
);
}
};
const handleRenameAction = (newName: string) => {
if (!selectedEventData || !selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, { actionName: newName });
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId ||'',
event
);
}
};
const handleSpawnCountChange = (value: string) => {
if (!selectedEventData || !selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
spawnCount: parseFloat(value),
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId ||'',
event
);
}
};
const handleSpawnIntervalChange = (value: string) => {
if (!selectedEventData || !selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
spawnInterval: parseFloat(value),
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId ||'',
event
);
}
};
const handleMaterialSelect = (material: string) => {
if (!selectedEventData || !selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, { material });
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId ||'',
event
);
}
};
const handleDelayChange = (value: string) => {
if (!selectedEventData || !selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
delay: parseFloat(value),
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId ||'',
event
);
}
};
const availableActions = {
defaultOption: "default",
options: ["default", "spawn", "swap", "delay", "despawn"],
};
// Get current values from store
const currentSpeed = (getEventByModelUuid(
selectedProduct.productUuid, selectedEventData?.data.modelUuid || ""
) as ConveyorEventSchema | undefined)?.speed?.toString() || "0.5";
const currentActionName = selectedPointData
? selectedPointData.action.actionName
: "Action Name";
const currentMaterial = selectedPointData
? selectedPointData.action.material
: "Default material";
const currentSpawnCount = selectedPointData
? selectedPointData.action.spawnCount?.toString() || "1"
: "1";
const currentSpawnInterval = selectedPointData
? selectedPointData.action.spawnInterval?.toString() || "1"
: "1";
const currentDelay = selectedPointData
? selectedPointData.action.delay?.toString() || "0"
: "0";
return (
<>
<div key={selectedPointData?.uuid} className="global-props section">
<div className="property-list-container">
<div className="property-item">
<InputWithDropDown
label="Speed"
value={currentSpeed}
min={0}
step={0.1}
defaultValue={"0.5"}
max={10}
activeOption="m/s"
onClick={() => { }}
onChange={handleSpeedChange}
/>
</div>
</div>
</div>
<section>
<ActionsList
selectedPointData={selectedPointData}
/>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption={
selectedPointData
? selectedPointData.action.actionType
: "default"
}
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
{activeOption === "default" && <DefaultAction />}
{activeOption === "spawn" && (
<SpawnAction
onChangeCount={handleSpawnCountChange}
options={["Default material", "Material 1", "Material 2", "Material 3"]}
defaultOption={currentMaterial}
onSelect={handleMaterialSelect}
onChangeInterval={handleSpawnIntervalChange}
intervalValue={currentSpawnInterval}
countValue={currentSpawnCount}
intervalMin={1}
intervalMax={60}
intervalDefaultValue="1"
countMin={1}
countMax={100}
countDefaultValue="1"
/>
)}
{activeOption === "swap" && (
<SwapAction
options={["Default material", "Material 1", "Material 2", "Material 3"]}
defaultOption={currentMaterial}
onSelect={handleMaterialSelect}
/>
)}
{activeOption === "despawn" && <DespawnAction />}
{activeOption === "delay" && (
<DelayAction
value={currentDelay}
defaultValue="0"
min={0}
max={60}
onChange={handleDelayChange}
/>
)}
</div>
</div>
<div className="tirgger">
<Trigger selectedPointData={selectedPointData as any} type={'Conveyor'} />
</div>
</section>
</>
);
}
export default ConveyorMechanics;

View File

@@ -0,0 +1,181 @@
import { useEffect, useState } from "react";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import Trigger from "../trigger/Trigger";
import { useSelectedAction, useSelectedEventData } from "../../../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import ProcessAction from "../actions/ProcessAction";
import ActionsList from "../components/ActionsList";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
import { useParams } from "react-router-dom";
function MachineMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "process">("default");
const [selectedPointData, setSelectedPointData] = useState<MachinePointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { getPointByUuid, updateAction } = useProductStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { setSelectedAction, clearSelectedAction } = useSelectedAction();
const { projectId } = useParams();
useEffect(() => {
if (selectedEventData) {
const point = getPointByUuid(
selectedProduct.productUuid,
selectedEventData?.data.modelUuid,
selectedEventData?.selectedPoint
) as MachinePointSchema | undefined;
if (point && "action" in point) {
setSelectedPointData(point);
setActiveOption(point.action.actionType as "process");
setSelectedAction(point.action.actionUuid, point.action.actionName);
}
} else {
clearSelectedAction();
}
}, [selectedProduct, selectedEventData]);
const updateBackend = (
productName: string,
productUuid: string,
projectId: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productUuid: productUuid,
projectId: projectId,
eventDatas: eventData
})
}
const handleActionTypeChange = (option: string) => {
if (!selectedEventData || !selectedPointData) return;
const validOption = option as "process";
setActiveOption(validOption);
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
actionType: validOption,
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
};
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
);
}
};
const handleProcessTimeChange = (value: string) => {
if (!selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
processTime: parseFloat(value),
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
};
const handleMaterialSelect = (material: string) => {
if (!selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
swapMaterial: material,
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
};
// Get current values from store
const currentActionName = selectedPointData
? selectedPointData.action.actionName
: "Action Name";
const currentProcessTime = selectedPointData
? selectedPointData.action.processTime.toString()
: "1";
const currentMaterial = selectedPointData
? selectedPointData.action.swapMaterial
: "Default material";
const availableActions = {
defaultOption: "process",
options: ["process"],
};
return (
<>
{selectedEventData && (
<section>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
/>
</div>
<ActionsList
selectedPointData={selectedPointData}
/>
<div className="selected-actions-list">
<LabledDropdown
defaultOption="process"
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
{activeOption === "process" && (
<ProcessAction
value={currentProcessTime}
min={0.1}
max={60}
defaultValue="1"
onChange={handleProcessTimeChange}
swapOptions={["Default material", "Material 1", "Material 2", "Material 3"]}
swapDefaultOption={currentMaterial}
onSwapSelect={handleMaterialSelect}
/>
)}
</div>
</div>
<div className="tirgger">
<Trigger selectedPointData={selectedPointData as any} type={'Machine'} />
</div>
</section>
)}
</>
);
}
export default MachineMechanics;

View File

@@ -0,0 +1,276 @@
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 { useSelectedEventData, useSelectedAction } from "../../../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import PickAndPlaceAction from "../actions/PickAndPlaceAction";
import ActionsList from "../components/ActionsList";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
import { useParams } from "react-router-dom";
function RoboticArmMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "pickAndPlace">("default");
const [selectedPointData, setSelectedPointData] = useState<RoboticArmPointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction, addAction, removeAction, } = useProductStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { selectedAction, setSelectedAction, clearSelectedAction } = useSelectedAction();
const { projectId } = useParams();
useEffect(() => {
if (selectedEventData) {
const point = getPointByUuid(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint
) as RoboticArmPointSchema | undefined;
if (point?.actions) {
setSelectedPointData(point);
if (point.actions.length > 0) {
setActiveOption(
point.actions[0].actionType as "default" | "pickAndPlace"
);
setSelectedAction(
point.actions[0].actionUuid,
point.actions[0].actionName
);
}
}
} else {
clearSelectedAction();
}
}, [selectedEventData, selectedProduct]);
const updateBackend = (
productName: string,
productUuid: string,
projectId: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productUuid: productUuid,
projectId: projectId,
eventDatas: eventData,
});
};
const handleRenameAction = (newName: string) => {
if (!selectedAction.actionId) return;
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
{ actionName: newName }
);
if (selectedPointData) {
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;
const event = updateEvent(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
{ speed: parseFloat(value), }
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
};
const handleClearPoints = () => {
if (!selectedAction.actionId || !selectedPointData) return;
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
{
process: {
startPoint: null,
endPoint: null,
},
}
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
};
const handleAddAction = () => {
if (!selectedEventData || !selectedPointData) return;
const newAction = {
actionUuid: MathUtils.generateUUID(),
actionName: `Action ${selectedPointData.actions.length + 1}`,
actionType: "pickAndPlace" as const,
process: {
startPoint: null,
endPoint: null,
},
triggers: [] as TriggerSchema[],
};
const event = addAction(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint,
newAction
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
const updatedPoint = { ...selectedPointData, actions: [...selectedPointData.actions, newAction], };
setSelectedPointData(updatedPoint);
setSelectedAction(newAction.actionUuid, newAction.actionName);
};
const handleDeleteAction = (actionUuid: string) => {
if (!selectedPointData) return;
const event = removeAction(selectedProduct.productUuid, actionUuid);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
const index = selectedPointData.actions.findIndex((a) => a.actionUuid === actionUuid);
const newActions = selectedPointData.actions.filter((a) => a.actionUuid !== actionUuid);
const updatedPoint = {
...selectedPointData,
actions: newActions,
};
setSelectedPointData(updatedPoint);
if (selectedAction.actionId === actionUuid) {
const nextAction = newActions[index] || newActions[index - 1];
if (nextAction) {
setSelectedAction(nextAction.actionUuid, nextAction.actionName);
} else {
clearSelectedAction();
}
}
};
const availableActions = {
defaultOption: "pickAndPlace",
options: ["pickAndPlace"],
};
const currentSpeed = (getEventByModelUuid(selectedProduct.productUuid, selectedEventData?.data.modelUuid || "") as RoboticArmEventSchema | undefined)?.speed?.toString() || "0.5";
const currentAction = selectedPointData?.actions.find((a) => a.actionUuid === selectedAction.actionId);
const currentPickPoint = currentAction?.process.startPoint
? `${currentAction.process.startPoint[0]},${currentAction.process.startPoint[1]},${currentAction.process.startPoint[2]}`
: "";
const currentPlacePoint = currentAction?.process.endPoint
? `${currentAction.process.endPoint[0]},${currentAction.process.endPoint[1]},${currentAction.process.endPoint[2]}`
: "";
return (
<>
<div className="global-props section">
<div className="property-list-container">
<div className="property-item">
<InputWithDropDown
label="Speed"
value={currentSpeed}
min={0}
step={0.1}
defaultValue={"0.5"}
max={10}
activeOption="m/s"
onClick={() => { }}
onChange={handleSpeedChange}
/>
</div>
</div>
</div>
<section>
<ActionsList
selectedPointData={selectedPointData}
multipleAction
handleAddAction={handleAddAction}
handleDeleteAction={handleDeleteAction}
/>
{selectedAction.actionId && currentAction && (
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={selectedAction.actionName || ""}
onRename={handleRenameAction}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption={activeOption}
options={availableActions.options}
onSelect={() => { }}
disabled={true}
/>
<PickAndPlaceAction clearPoints={handleClearPoints} />
</div>
<div className="tirgger">
<Trigger
selectedPointData={selectedPointData as any}
type={"RoboticArm"}
/>
</div>
</div>
)}
</section>
</>
);
}
export default RoboticArmMechanics;

View File

@@ -0,0 +1,220 @@
import { useEffect, useMemo, useState } from "react";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import Trigger from "../trigger/Trigger";
import StorageAction from "../actions/StorageAction";
import ActionsList from "../components/ActionsList";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
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";
function StorageMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "store" | "spawn">("default");
const [selectedPointData, setSelectedPointData] = useState<StoragePointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { getPointByUuid, updateAction } = useProductStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { setSelectedAction, clearSelectedAction } = useSelectedAction();
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) {
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);
}
} else {
clearSelectedAction();
}
}, [selectedProduct, selectedEventData]);
const updateBackend = (
productName: string,
productUuid: string,
projectId: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productUuid: productUuid,
projectId: projectId,
eventDatas: eventData
})
}
const handleActionTypeChange = (option: string) => {
if (!selectedEventData || !selectedPointData) return;
const internalOption = actionTypeMap[option as keyof typeof actionTypeMap] as "store" | "retrieve";
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);
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
storageCapacity: newCapacity,
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
updateSelectedPointData();
}
};
const createNewMaterial = (materialType: string): { materialType: string; materialId: string } | null => {
if (!selectedEventData || !selectedPointData) return null;
const materialId = THREE.MathUtils.generateUUID();
return {
materialType,
materialId
};
};
const handleMaterialTypeChange = (value: string) => {
if (!selectedEventData || !selectedPointData) return;
const event = updateAction(selectedProduct.productUuid, selectedPointData.action.actionUuid, {
materialType: value,
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
updateSelectedPointData();
}
};
const currentActionName = useMemo(() =>
selectedPointData ? selectedPointData.action.actionName : "Action Name",
[selectedPointData]
);
const currentCapacity = useMemo(() =>
selectedPointData ? selectedPointData.action.storageCapacity.toString() : "0",
[selectedPointData]
);
const currentMaterialType = useMemo(() =>
selectedPointData?.action.materialType || "Default material",
[selectedPointData]
);
const availableActions = {
defaultOption: "store",
options: ["store", "spawn"],
};
const actionTypeMap = {
spawn: "retrieve",
store: "store"
};
return (
<>
{selectedEventData && (
<section>
<ActionsList
selectedPointData={selectedPointData}
/>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
/>
</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>
)}
</>
);
}
export default StorageMechanics;

View File

@@ -0,0 +1,296 @@
import { useEffect, useState } from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import Trigger from "../trigger/Trigger";
import {
useSelectedAction,
useSelectedEventData,
} from "../../../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import TravelAction from "../actions/TravelAction";
import ActionsList from "../components/ActionsList";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
import { useParams } from "react-router-dom";
function VehicleMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "travel">("default");
const [selectedPointData, setSelectedPointData] = useState<VehiclePointSchema | undefined>();
const { selectedEventData } = useSelectedEventData();
const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction } = useProductStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { setSelectedAction, clearSelectedAction } = useSelectedAction();
const { projectId } = useParams();
useEffect(() => {
if (selectedEventData && selectedEventData.data.type === "vehicle") {
const point = getPointByUuid(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
selectedEventData.selectedPoint
) as VehiclePointSchema | undefined;
if (point) {
setSelectedPointData(point);
setActiveOption(point.action.actionType as "travel");
setSelectedAction(point.action.actionUuid, point.action.actionName);
}
} else {
clearSelectedAction();
}
}, [selectedProduct, selectedEventData]);
const updateBackend = (
productName: string,
productUuid: string,
projectId: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productUuid: productUuid,
projectId: projectId,
eventDatas: eventData,
});
};
const handleSpeedChange = (value: string) => {
if (!selectedEventData) return;
const event = updateEvent(
selectedProduct.productUuid,
selectedEventData.data.modelUuid,
{
speed: parseFloat(value),
}
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
};
const handleActionTypeChange = (option: string) => {
if (!selectedEventData || !selectedPointData) return;
const validOption = option as "travel";
setActiveOption(validOption);
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{
actionType: validOption,
}
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
};
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
);
}
};
const handleLoadCapacityChange = (value: string) => {
if (!selectedPointData) return;
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{
loadCapacity: parseFloat(value),
}
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
};
const handleUnloadDurationChange = (value: string) => {
if (!selectedPointData) return;
const event = updateAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid,
{
unLoadDuration: parseFloat(value),
}
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
};
const handlePickPointChange = (value: string) => {
if (!selectedPointData) return;
};
const handleUnloadPointChange = (value: string) => {
if (!selectedPointData) return;
};
// Get current values from store
const currentSpeed =
(
getEventByModelUuid(
selectedProduct.productUuid,
selectedEventData?.data.modelUuid || ""
) as VehicleEventSchema | undefined
)?.speed?.toString() || "0.5";
const currentActionName = selectedPointData
? selectedPointData.action.actionName
: "Action Name";
const currentLoadCapacity = selectedPointData
? selectedPointData.action.loadCapacity.toString()
: "1";
const currentUnloadDuration = selectedPointData
? selectedPointData.action.unLoadDuration.toString()
: "1";
function handleClearPoints() {
if (!selectedEventData || !selectedPointData?.action.actionUuid) return;
const event = updateAction(
selectedProduct.productUuid, selectedPointData.action.actionUuid, {
pickUpPoint: null,
unLoadPoint: null,
steeringAngle: 0,
})
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
}
const availableActions = {
defaultOption: "travel",
options: ["travel"],
};
return (
<>
{selectedEventData && (
<>
<div className="global-props section">
<div className="property-list-container">
<div className="property-item">
<InputWithDropDown
label="Speed"
value={currentSpeed}
min={0}
step={0.1}
defaultValue={"0.5"}
max={10}
activeOption="m/s"
onClick={() => { }}
onChange={handleSpeedChange}
/>
</div>
</div>
</div>
<section>
<ActionsList selectedPointData={selectedPointData} />
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption="travel"
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
{activeOption === "travel" && (
<TravelAction
loadCapacity={{
value: currentLoadCapacity,
min: 1,
max: 100,
defaultValue: "1",
onChange: handleLoadCapacityChange,
}}
unloadDuration={{
value: currentUnloadDuration,
min: 1,
max: 60,
defaultValue: "1",
onChange: handleUnloadDurationChange,
}}
clearPoints={handleClearPoints}
// pickPoint={{
// value: currentPickPoint,
// onChange: handlePickPointChange,
// }}
// unloadPoint={{
// value: currentUnloadPoint,
// onChange: handleUnloadPointChange,
// }}
/>
)}
</div>
</div>
<div className="tirgger">
<Trigger
selectedPointData={selectedPointData as any}
type={"Vehicle"}
/>
</div>
</section>
</>
)}
</>
);
}
export default VehicleMechanics;

View File

@@ -0,0 +1,442 @@
import React, { useEffect, useMemo, useRef, useState } from "react";
import * as THREE from "three";
import { AddIcon, RemoveIcon, ResizeHeightIcon } from "../../../../../icons/ExportCommonIcons";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../../../functions/handleResizePannel";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import { useSelectedAction } from "../../../../../../store/simulation/useSimulationStore";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
import { useParams } from "react-router-dom";
type TriggerProps = {
selectedPointData?: PointsScheme | undefined;
type?: "Conveyor" | "Vehicle" | "RoboticArm" | "Machine" | "StorageUnit";
};
const Trigger = ({ selectedPointData, type }: TriggerProps) => {
const [currentAction, setCurrentAction] = useState<string | undefined>();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { getActionByUuid, getEventByModelUuid, getPointByUuid, getTriggerByUuid, addTrigger, removeTrigger, updateTrigger, renameTrigger, getProductById, } = useProductStore();
const [triggers, setTriggers] = useState<TriggerSchema[]>([]);
const [selectedTrigger, setSelectedTrigger] = useState<TriggerSchema | undefined>();
const [activeOption, setActiveOption] = useState<"onComplete" | "onStart" | "onStop" | "delay" | "onError">("onComplete");
const triggersContainerRef = useRef<HTMLDivElement>(null);
const { selectedAction } = useSelectedAction();
const { projectId } = useParams();
useEffect(() => {
if (!selectedPointData || !selectedProduct) return;
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" && selectedAction.actionId) {
actionUuid = selectedAction.actionId;
}
setCurrentAction(actionUuid);
}, [selectedPointData, selectedProduct, type, selectedAction]);
const updateBackend = (
productName: string,
productUuid: string,
projectId: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productUuid: productUuid,
projectId: projectId,
eventDatas: eventData,
});
};
useEffect(() => {
if (!currentAction || !selectedProduct) return;
const action = getActionByUuid(selectedProduct.productUuid, currentAction);
const actionTriggers = action?.triggers || [];
setTriggers(actionTriggers);
setSelectedTrigger(actionTriggers[0]);
}, [currentAction, selectedProduct]);
const triggeredModel = useMemo(() => {
if (!selectedProduct || !selectedTrigger?.triggeredAsset?.triggeredModel?.modelUuid)
return undefined;
return getEventByModelUuid(
selectedProduct.productUuid,
selectedTrigger.triggeredAsset.triggeredModel.modelUuid
);
}, [selectedProduct, selectedTrigger]);
const triggeredPoint = useMemo(() => {
if (!selectedProduct || !triggeredModel || !selectedTrigger?.triggeredAsset?.triggeredPoint?.pointUuid)
return undefined;
return getPointByUuid(
selectedProduct.productUuid,
triggeredModel.modelUuid,
selectedTrigger.triggeredAsset.triggeredPoint.pointUuid
);
}, [selectedProduct, triggeredModel, selectedTrigger]);
const triggeredAction = useMemo(() => {
if (!selectedProduct || !selectedTrigger?.triggeredAsset?.triggeredAction?.actionUuid)
return undefined;
return getActionByUuid(
selectedProduct.productUuid,
selectedTrigger.triggeredAsset.triggeredAction.actionUuid
);
}, [selectedProduct, selectedTrigger]);
const modelOptions = getProductById(selectedProduct.productUuid)?.eventDatas || [];
const pointOptions: PointsScheme[] = useMemo(() => {
if (!triggeredModel) return [];
const model = modelOptions.find((m) => m.modelUuid === triggeredModel.modelUuid);
if (!model) return [];
if ("points" in model) {
return (model).points;
} else if ("point" in model) {
return [model.point];
}
return [];
}, [triggeredModel, modelOptions]);
const actionOptions: any = useMemo(() => {
if (!triggeredPoint) return [];
const point = pointOptions.find((p) => p.uuid === triggeredPoint.uuid);
if (!point) return [];
if ("action" in point) {
const typedPoint = point;
return typedPoint.action ? [typedPoint.action] : [];
} else if ("actions" in point) {
const typedPoint = point;
return typedPoint.actions;
}
return [];
}, [triggeredPoint, pointOptions]);
const handleModelSelect = (option: string, triggerUuid: string) => {
if (!selectedProduct) return;
const selectedModel = modelOptions.find((m) => m.modelName === option);
if (!selectedModel) return;
const event = updateTrigger(selectedProduct.productUuid, triggerUuid, {
triggeredAsset: {
triggeredModel: {
modelName: selectedModel.modelName,
modelUuid: selectedModel.modelUuid,
},
triggeredPoint: null,
triggeredAction: null,
},
});
if (event) {
const updatedTrigger = getTriggerByUuid(
selectedProduct.productUuid,
triggerUuid
);
setSelectedTrigger(updatedTrigger);
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
};
const handlePointSelect = (option: string, triggerUuid: string) => {
if (!selectedProduct || !selectedTrigger) return;
const pointUuid = pointOptions.find((p) => `Point ${p.uuid.slice(0, 4)}` === option)?.uuid;
if (!pointUuid) return;
if (selectedTrigger.triggeredAsset?.triggeredModel) {
const event = updateTrigger(selectedProduct.productUuid, triggerUuid, {
triggeredAsset: {
...selectedTrigger.triggeredAsset,
triggeredPoint: {
pointName: option,
pointUuid: pointUuid,
},
triggeredAction: null,
},
});
if (event) {
const updatedTrigger = getTriggerByUuid(
selectedProduct.productUuid,
triggerUuid
);
setSelectedTrigger(updatedTrigger);
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
}
};
const handleActionSelect = (option: string, triggerUuid: string) => {
if (!selectedProduct || !selectedTrigger) return;
const selectedAction = actionOptions.find((a: any) => a.actionName === option);
if (!selectedAction) return;
if (selectedTrigger.triggeredAsset?.triggeredPoint) {
const event = updateTrigger(selectedProduct.productUuid, triggerUuid, {
triggeredAsset: {
...selectedTrigger.triggeredAsset,
triggeredAction: {
actionName: option,
actionUuid: selectedAction.actionUuid,
},
},
});
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
}
};
const handleAddTrigger = () => {
if (!selectedProduct || !currentAction) return;
const newTrigger: TriggerSchema = {
triggerUuid: THREE.MathUtils.generateUUID(),
triggerName: `New Trigger ${triggers.length + 1}`,
triggerType: activeOption,
delay: 0,
triggeredAsset: null,
};
const event = addTrigger(
selectedProduct.productUuid,
currentAction,
newTrigger
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
const updatedAction = getActionByUuid(
selectedProduct.productUuid,
currentAction
);
const updatedTriggers = updatedAction?.triggers || [];
setTriggers(updatedTriggers);
setSelectedTrigger(newTrigger);
};
const handleRemoveTrigger = (triggerUuid: string) => {
if (!selectedProduct || !currentAction) return;
const event = removeTrigger(selectedProduct.productUuid, triggerUuid);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
const index = triggers.findIndex((t) => t.triggerUuid === triggerUuid);
const newTriggers = triggers.filter((t) => t.triggerUuid !== triggerUuid);
setTriggers(newTriggers);
if (selectedTrigger?.triggerUuid === triggerUuid) {
const nextTrigger = newTriggers[index] || newTriggers[index - 1];
setSelectedTrigger(nextTrigger);
}
};
const handleTriggerRename = (triggerUuid: string, newName: string) => {
if (!selectedProduct) return;
const event = renameTrigger(
selectedProduct.productUuid,
triggerUuid,
newName
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
};
const handleTriggerTypeChange = (option: string) => {
if (!selectedTrigger || !selectedProduct) return;
const validTypes: Array<TriggerSchema["triggerType"]> = ["onComplete", "onStart", "onStop", "delay", "onError",];
if (!validTypes.includes(option as TriggerSchema["triggerType"])) return;
setActiveOption(option as TriggerSchema["triggerType"]);
const event = updateTrigger(
selectedProduct.productUuid,
selectedTrigger.triggerUuid,
{
triggerType: option as TriggerSchema["triggerType"],
}
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
event
);
}
};
return (
<div className="trigger-wrapper">
<div className="header">
<div className="title">Trigger</div>
<button
id="add-trigger-button"
className="add-button"
onClick={handleAddTrigger}
style={{ cursor: "pointer" }}
disabled={!currentAction}
>
<AddIcon /> Add
</button>
</div>
<div className="trigger-list">
<div
className="lists-main-container"
ref={triggersContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{triggers.map((trigger) => (
<div
key={trigger.triggerUuid}
className={`list-item ${selectedTrigger?.triggerUuid === trigger.triggerUuid
? "active"
: ""
}`}
onClick={() => setSelectedTrigger(trigger)}
>
<button id="trigger" className="value">
<RenameInput
value={trigger.triggerName}
onRename={(newName) =>
handleTriggerRename(trigger.triggerUuid, newName)
}
/>
</button>
{triggers.length > 1 && (
<button
id="remove-trigger-button"
className="remove-button"
onClick={(e) => {
e.stopPropagation();
handleRemoveTrigger(trigger.triggerUuid);
}}
>
<RemoveIcon />
</button>
)}
</div>
))}
</div>
<button
className="resize-icon"
id="action-resize"
onMouseDown={(e: any) => handleResize(e, triggersContainerRef)}
>
<ResizeHeightIcon />
</button>
</div>
{selectedTrigger && (
<div className="trigger-item">
<div className="trigger-name">{selectedTrigger.triggerName}</div>
<LabledDropdown
label="Trigger Type"
defaultOption={selectedTrigger.triggerType}
// options={["onComplete", "onStart", "onStop", "delay", "onError"]}
options={["onComplete"]}
onSelect={handleTriggerTypeChange}
/>
<div className="trigger-options">
<LabledDropdown
label="Triggered Object"
defaultOption={triggeredModel?.modelName || ""}
options={[...modelOptions.map((option) => option.modelName)]}
onSelect={(option) => {
handleModelSelect(option, selectedTrigger.triggerUuid);
}}
/>
<LabledDropdown
label="Triggered Point"
defaultOption={
triggeredPoint?.uuid
? `Point ${triggeredPoint?.uuid.slice(0, 4)}`
: ""
}
options={[
...pointOptions.map(
(option) => `Point ${option.uuid.slice(0, 4)}`
),
]}
onSelect={(option) => {
handlePointSelect(option, selectedTrigger.triggerUuid);
}}
/>
<LabledDropdown
label="Triggered Action"
defaultOption={triggeredAction?.actionName || ""}
options={[
...actionOptions.map((option: any) => option.actionName),
]}
onSelect={(option) => {
handleActionSelect(option, selectedTrigger.triggerUuid);
}}
/>
</div>
</div>
)}
</div>
</div>
);
};
export default Trigger;