import React, { useRef, useState, useMemo, useEffect } from "react"; import { AddIcon, InfoIcon, RemoveIcon, ResizeHeightIcon, } from "../../../icons/ExportCommonIcons"; import RenameInput from "../../../ui/inputs/RenameInput"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; import LabledDropdown from "../../../ui/inputs/LabledDropdown"; import { handleResize } from "../../../../functions/handleResizePannel"; import { useFloorItems, useSelectedActionSphere, useSelectedPath, useSimulationPaths, } from "../../../../store/store"; import * as THREE from "three"; import * as Types from "../../../../types/world/worldTypes"; import InputToggle from "../../../ui/inputs/InputToggle"; import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; const ConveyorMechanics: React.FC = () => { const { selectedActionSphere } = useSelectedActionSphere(); const { selectedPath, setSelectedPath } = useSelectedPath(); const { simulationPaths, setSimulationPaths } = useSimulationPaths(); const { floorItems, setFloorItems } = useFloorItems(); const actionsContainerRef = useRef<HTMLDivElement>(null); const triggersContainerRef = useRef<HTMLDivElement>(null); const selectedPoint = useMemo(() => { if (!selectedActionSphere) return null; return simulationPaths .filter( (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" ) .flatMap((path) => path.points) .find((point) => point.uuid === selectedActionSphere.point.uuid); }, [selectedActionSphere, simulationPaths]); const updateBackend = async (updatedPath: Types.ConveyorEventsSchema | undefined) => { if (!updatedPath) return; // const email = localStorage.getItem("email"); // const organization = email ? email.split("@")[1].split(".")[0] : ""; // console.log('updatedPath: ', updatedPath); // const a = await setEventApi( // organization, // updatedPath.modeluuid, // updatedPath.points // ); // console.log('a: ', a); } const handleAddAction = () => { if (!selectedActionSphere) return; const updatedPaths = simulationPaths.map((path) => { if (path.type === "Conveyor") { return { ...path, points: path.points.map((point) => { if (point.uuid === selectedActionSphere.point.uuid) { const actionIndex = point.actions.length; const newAction = { uuid: THREE.MathUtils.generateUUID(), name: `Action ${actionIndex + 1}`, type: "Inherit", material: "Inherit", delay: "Inherit", spawnInterval: "Inherit", isUsed: false, }; return { ...point, actions: [...point.actions, newAction] }; } return point; }), }; } return path; }); const updatedPath = updatedPaths.find( (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" && path.points.some( (point) => point.uuid === selectedActionSphere.point.uuid ) ); updateBackend(updatedPath); setSimulationPaths(updatedPaths); }; const handleDeleteAction = (uuid: string) => { if (!selectedActionSphere) return; const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { ...path, points: path.points.map((point) => point.uuid === selectedActionSphere.point.uuid ? { ...point, actions: point.actions.filter( (action) => action.uuid !== uuid ), } : point ), } : path ); const updatedPath = updatedPaths.find( (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" && path.points.some( (point) => point.uuid === selectedActionSphere.point.uuid ) ); updateBackend(updatedPath); setSimulationPaths(updatedPaths); }; const handleActionSelect = (uuid: string, actionType: string) => { if (!selectedActionSphere) return; const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { ...path, points: path.points.map((point) => point.uuid === selectedActionSphere.point.uuid ? { ...point, actions: point.actions.map((action) => action.uuid === uuid ? { ...action, type: actionType, material: actionType === "Spawn" || actionType === "Swap" ? "Inherit" : action.material, delay: actionType === "Delay" ? "Inherit" : action.delay, spawnInterval: actionType === "Spawn" ? "Inherit" : action.spawnInterval, } : action ), } : point ), } : path ); const updatedPath = updatedPaths.find( (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" && path.points.some( (point) => point.uuid === selectedActionSphere.point.uuid ) ); updateBackend(updatedPath); setSimulationPaths(updatedPaths); // Update the selected item to reflect changes if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) { const updatedAction = updatedPaths .filter( (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" ) .flatMap((path) => path.points) .find((p) => p.uuid === selectedActionSphere.point.uuid) ?.actions.find((a) => a.uuid === uuid); if (updatedAction) { setSelectedItem({ type: "action", item: updatedAction, }); } } }; // Modified handleMaterialSelect to ensure it only applies to relevant action types const handleMaterialSelect = (uuid: string, material: string) => { if (!selectedActionSphere) return; const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { ...path, points: path.points.map((point) => point.uuid === selectedActionSphere.point.uuid ? { ...point, actions: point.actions.map((action) => action.uuid === uuid && (action.type === "Spawn" || action.type === "Swap") ? { ...action, material } : action ), } : point ), } : path ); const updatedPath = updatedPaths.find( (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" && path.points.some( (point) => point.uuid === selectedActionSphere.point.uuid ) ); updateBackend(updatedPath); setSimulationPaths(updatedPaths); // Update selected item if it's the current action if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) { setSelectedItem({ ...selectedItem, item: { ...selectedItem.item, material, }, }); } }; const handleDelayChange = (uuid: string, delay: number | string) => { if (!selectedActionSphere) return; const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { ...path, points: path.points.map((point) => point.uuid === selectedActionSphere.point.uuid ? { ...point, actions: point.actions.map((action) => action.uuid === uuid ? { ...action, delay } : action ), } : point ), } : path ); const updatedPath = updatedPaths.find( (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" && path.points.some( (point) => point.uuid === selectedActionSphere.point.uuid ) ); updateBackend(updatedPath); setSimulationPaths(updatedPaths); }; const handleSpawnIntervalChange = ( uuid: string, spawnInterval: number | string ) => { if (!selectedActionSphere) return; const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { ...path, points: path.points.map((point) => point.uuid === selectedActionSphere.point.uuid ? { ...point, actions: point.actions.map((action) => action.uuid === uuid ? { ...action, spawnInterval } : action ), } : point ), } : path ); const updatedPath = updatedPaths.find( (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" && path.points.some( (point) => point.uuid === selectedActionSphere.point.uuid ) ); updateBackend(updatedPath); setSimulationPaths(updatedPaths); }; const handleSpeedChange = (speed: number | string) => { if (!selectedPath) return; const updatedPaths = simulationPaths.map((path) => path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path ); const updatedPath = updatedPaths.find( (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" && path.points.some( (point) => point.uuid === selectedActionSphere.point.uuid ) ); updateBackend(updatedPath); setSimulationPaths(updatedPaths); setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } }); }; const handleAddTrigger = () => { if (!selectedActionSphere) return; const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { ...path, points: path.points.map((point) => { if (point.uuid === selectedActionSphere.point.uuid) { const triggerIndex = point.triggers.length; const newTrigger = { uuid: THREE.MathUtils.generateUUID(), name: `Trigger ${triggerIndex + 1}`, type: "", bufferTime: 0, isUsed: false, }; return { ...point, triggers: [...point.triggers, newTrigger] }; } return point; }), } : path ); const updatedPath = updatedPaths.find( (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" && path.points.some( (point) => point.uuid === selectedActionSphere.point.uuid ) ); updateBackend(updatedPath); setSimulationPaths(updatedPaths); }; const handleDeleteTrigger = (uuid: string) => { if (!selectedActionSphere) return; const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { ...path, points: path.points.map((point) => point.uuid === selectedActionSphere.point.uuid ? { ...point, triggers: point.triggers.filter( (trigger) => trigger.uuid !== uuid ), } : point ), } : path ); const updatedPath = updatedPaths.find( (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" && path.points.some( (point) => point.uuid === selectedActionSphere.point.uuid ) ); updateBackend(updatedPath); setSimulationPaths(updatedPaths); }; const handleTriggerSelect = (uuid: string, triggerType: string) => { if (!selectedActionSphere) return; const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { ...path, points: path.points.map((point) => point.uuid === selectedActionSphere.point.uuid ? { ...point, triggers: point.triggers.map((trigger) => trigger.uuid === uuid ? { ...trigger, type: triggerType } : trigger ), } : point ), } : path ); const updatedPath = updatedPaths.find( (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" && path.points.some( (point) => point.uuid === selectedActionSphere.point.uuid ) ); updateBackend(updatedPath); setSimulationPaths(updatedPaths); // Ensure the selectedItem is updated immediately const updatedTrigger = updatedPaths .flatMap((path) => (path.type === "Conveyor" ? path.points : [])) .flatMap((point) => point.triggers) .find((trigger) => trigger.uuid === uuid); if (updatedTrigger) { setSelectedItem({ type: "trigger", item: updatedTrigger }); } }; // Update the toggle handlers to immediately update the selected item const handleActionToggle = (uuid: string) => { if (!selectedActionSphere) return; const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { ...path, points: path.points.map((point) => point.uuid === selectedActionSphere.point.uuid ? { ...point, actions: point.actions.map((action) => ({ ...action, isUsed: action.uuid === uuid ? !action.isUsed : false, })), } : point ), } : path ); const updatedPath = updatedPaths.find( (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" && path.points.some( (point) => point.uuid === selectedActionSphere.point.uuid ) ); updateBackend(updatedPath); setSimulationPaths(updatedPaths); // Immediately update the selected item if it's the one being toggled if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) { setSelectedItem({ ...selectedItem, item: { ...selectedItem.item, isUsed: !selectedItem.item.isUsed, }, }); } }; // Do the same for trigger toggle const handleTriggerToggle = (uuid: string) => { if (!selectedActionSphere) return; const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { ...path, points: path.points.map((point) => point.uuid === selectedActionSphere.point.uuid ? { ...point, triggers: point.triggers.map((trigger) => ({ ...trigger, isUsed: trigger.uuid === uuid ? !trigger.isUsed : false, })), } : point ), } : path ); const updatedPath = updatedPaths.find( (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" && path.points.some( (point) => point.uuid === selectedActionSphere.point.uuid ) ); updateBackend(updatedPath); setSimulationPaths(updatedPaths); // Immediately update the selected item if it's the one being toggled if (selectedItem?.type === "trigger" && selectedItem.item.uuid === uuid) { setSelectedItem({ ...selectedItem, item: { ...selectedItem.item, isUsed: !selectedItem.item.isUsed, }, }); } }; const handleTriggerBufferTimeChange = (uuid: string, bufferTime: number) => { if (!selectedActionSphere) return; const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { ...path, points: path.points.map((point) => point.uuid === selectedActionSphere.point.uuid ? { ...point, triggers: point.triggers.map((trigger) => trigger.uuid === uuid ? { ...trigger, bufferTime } : trigger ), } : point ), } : path ); const updatedPath = updatedPaths.find( (path): path is Types.ConveyorEventsSchema => path.type === "Conveyor" && path.points.some( (point) => point.uuid === selectedActionSphere.point.uuid ) ); updateBackend(updatedPath); setSimulationPaths(updatedPaths); // Immediately update selectedItem if it's the currently selected trigger if (selectedItem?.type === "trigger" && selectedItem.item.uuid === uuid) { setSelectedItem({ ...selectedItem, item: { ...selectedItem.item, bufferTime, }, }); } }; const [selectedItem, setSelectedItem] = useState<{ type: "action" | "trigger"; item: any; } | null>(null); useEffect(() => { setSelectedItem(null); }, [selectedActionSphere]); return ( <div className="machine-mechanics-container"> {!selectedPath && ( <div className="machine-mechanics-header"> {selectedActionSphere?.path?.modelName || "point name not found"} </div> )} {selectedPath && ( <div className="machine-mechanics-header"> {selectedPath.path.modelName || "path name not found"} </div> )} <div className="machine-mechanics-content-container"> {!selectedPath && ( <> <div className="actions"> <div className="header"> <div className="header-value">Actions</div> <div className="add-button" onClick={handleAddAction}> <AddIcon /> Add </div> </div> <div className="lists-main-container" ref={actionsContainerRef} style={{ height: "120px" }} > <div className="list-container"> {selectedPoint?.actions.map((action) => ( <div key={action.uuid} className={`list-item ${selectedItem?.type === "action" && selectedItem.item?.uuid === action.uuid ? "active" : "" }`} > <div className="value" onClick={() => setSelectedItem({ type: "action", item: action }) } > <input type="radio" name="action" id="action" defaultChecked={action.isUsed} /> <RenameInput value={action.name} /> </div> <div className="remove-button" onClick={() => handleDeleteAction(action.uuid)} > <RemoveIcon /> </div> </div> ))} </div> <div className="resize-icon" id="action-resize" onMouseDown={(e) => handleResize(e, actionsContainerRef)} > <ResizeHeightIcon /> </div> </div> </div> <div className="triggers"> <div className="header"> <div className="header-value">Triggers</div> <div className="add-button" onClick={handleAddTrigger}> <AddIcon /> Add </div> </div> <div className="lists-main-container" ref={triggersContainerRef} style={{ height: "120px" }} > <div className="list-container"> {selectedPoint?.triggers.map((trigger) => ( <div key={trigger.uuid} className={`list-item ${selectedItem?.type === "trigger" && selectedItem.item?.uuid === trigger.uuid ? "active" : "" }`} > <div className="value" onClick={() => setSelectedItem({ type: "trigger", item: trigger }) } > <input type="radio" name="trigger" id="trigger" defaultChecked={trigger.isUsed} /> <RenameInput value={trigger.name} /> </div> <div className="remove-button" onClick={() => handleDeleteTrigger(trigger.uuid)} > <RemoveIcon /> </div> </div> ))} </div> <div className="resize-icon" id="trigger-resize" onMouseDown={(e) => handleResize(e, triggersContainerRef)} > <ResizeHeightIcon /> </div> </div> </div> </> )} <div className="selected-properties-container"> {selectedItem && ( <> <div className="properties-header">{selectedItem.item.name}</div> {selectedItem.type === "action" && ( <> <InputToggle inputKey="enableAction" label="Enable Action" value={selectedItem.item.isUsed} onClick={() => handleActionToggle(selectedItem.item.uuid)} /> <LabledDropdown defaultOption={selectedItem.item.type} options={["Inherit", "Spawn", "Swap", "Despawn", "Delay"]} onSelect={(option) => handleActionSelect(selectedItem.item.uuid, option) } /> {/* Only show material dropdown for Spawn/Swap actions */} {(selectedItem.item.type === "Spawn" || selectedItem.item.type === "Swap") && ( <LabledDropdown label={ selectedItem.item.type === "Spawn" ? "Spawn Material" : "Swap Material" } defaultOption={selectedItem.item.material} options={["Inherit", "Crate", "Box"]} onSelect={(option) => handleMaterialSelect(selectedItem.item.uuid, option) } /> )} {/* Only show delay input for Delay actions */} {selectedItem.item.type === "Delay" && ( <InputWithDropDown label="Delay Time" value={ selectedItem.item.delay === "Inherit" ? undefined : selectedItem.item.delay } onChange={(value) => { const numValue = parseInt(value); handleDelayChange( selectedItem.item.uuid, !value ? "Inherit" : numValue ); }} /> )} {/* Only show spawn interval for Spawn actions */} {selectedItem.item.type === "Spawn" && ( <InputWithDropDown label="Spawn Interval" min={0} defaultValue={ selectedItem.item.spawnInterval === "Inherit" ? "" : selectedItem.item.spawnInterval.toString() } value={ selectedItem.item.spawnInterval === "Inherit" ? "" : selectedItem.item.spawnInterval.toString() } onChange={(value) => { handleSpawnIntervalChange( selectedItem.item.uuid, value === "" ? "Inherit" : parseInt(value) ); }} /> )} </> )} {selectedItem.type === "trigger" && ( <> <InputToggle inputKey="enableTrigger" label="Enable Trigger" value={selectedItem.item.isUsed} onClick={() => handleTriggerToggle(selectedItem.item.uuid)} /> <LabledDropdown defaultOption={ selectedItem.item.type || "Select Trigger Type" } options={["On-Hit", "Buffer"]} onSelect={(option) => handleTriggerSelect(selectedItem.item.uuid, option) } /> {selectedItem.item.type === "Buffer" && ( <InputWithDropDown label="Buffer Time" value={selectedItem.item.bufferTime.toString()} onChange={(value) => { handleTriggerBufferTimeChange( selectedItem.item.uuid, parseInt(value) ); }} /> )} </> )} </> )} {selectedPath && !selectedItem && ( <div key={selectedPath?.path.modeluuid || "none"} className="speed-control" > <InputWithDropDown label="Conveyor Speed" min={0} value={ selectedPath.path.speed === "Inherit" ? "" : selectedPath.path.speed.toString() } onChange={(value) => handleSpeedChange(value === "" ? "Inherit" : parseInt(value)) } /> </div> )} </div> {!selectedPath && ( <div className="footer"> <InfoIcon /> Configure the point's action and trigger properties. </div> )} {selectedPath && ( <div className="footer"> <InfoIcon /> Configure the path properties. </div> )} </div> </div> ); }; export default ConveyorMechanics;